/* generate pensinsulators site _ _ __ _ ___ _ __ ___(_) |_ ___ ___ / _` |/ _ \ '_ \/ __| | __/ _ \ / __| | (_| | __/ | | \__ \ | || __/ _ | (__ \__, |\___|_| |_|___/_|\__\___| (_) \___| |___/ takes set of meta-html .h files organized into a heirarchy of .ring files and generates final html for each page. also produces a manifest, site map, index, list of comparable insulator images, and list of external links. converted from awk, which program was 1/2 the size; long live awk! ian macky feb 2004 */ #include #include #include #include #include #include #include #include #include #include "jpgmean.h" /* ------------------------------- basic defines --------------------------- */ #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #define MAX_LINE 1024 /* input line */ #define MAX_PATH 512 /* filesystem path */ #define MAX_HASH 4001 /* hash table size MUST BE PRIME */ /* Title is specified as Long|Short, e.g. "Full Title about Thingy|Thingy": */ #define MAX_FIELD 256 /* "Full Title about Thingy" */ #define MAX_SHORT_FIELD 64 /* "Thingy" */ #define MAX_REL_DEPTH 16 /* max path depth for rel_path() */ #define MAX_PAGE_PATH 4 /* show this many levels up */ #define MAX_PAGE_RING 16 /* max rings introduced by page */ /* -------------------------------- site config ---------------------------- */ #ifndef GENSITE_NAME # define GENSITE_NAME "Site Name" #endif #ifndef GENSITE_WIDTH # define GENSITE_WIDTH "\"100%\"" #endif #ifndef GENSITE_HEIGHT # define GENSITE_HEIGHT 600 #endif #ifndef GENSITE_FONT # define GENSITE_FONT "helvetica" #endif #ifndef GENSITE_FIXED # define GENSITE_FIXED "monospace" #endif #ifndef GENSITE_SANS # define GENSITE_SANS "sans" #endif /* ------------------------------------------------------------------------- */ #define ROOT_PAGE "index.h" #define ROOT_HTML ROOT_PAGE "tml" #define ROOT_RING "root.ring" #define SITE_RING "site.ring" /* ring with index & sitemap */ #define WEBRING "webring" /* their code to insert */ #define IMAGES "images" /* subdir for basic images */ #define SITEMAP "sitemap.h" #define SITEMAP_HTML SITEMAP "tml" #define SITEMAP_NAV "Site Map" #define SITEMAP_LONG SITEMAP_NAV #define SITEMAP_SHORT "Map" #define SITEMAP_TITLE SITEMAP_LONG "|" SITEMAP_SHORT #define INDEX "master.h" /* "index.h" obviously taken */ #define INDEX_HTML INDEX "tml" #define INDEX_NAV "Index" #define INDEX_LONG INDEX_NAV #define INDEX_SHORT "Index" #define INDEX_TITLE INDEX_LONG "|" INDEX_SHORT #define INDEX_LETTERS 26 /* index 'A' - 'Z' */ #define INDEXBAR_PADDING 4 /* see generate_index() */ #define INDEXBAR_SPACING 2 #define XLINK "xlinks" /* external links */ #define CMP_LIST "cmplist" /* comparable images */ #define MANIFEST "manifest" /* list of all files */ #define ERRATA "errata" /* extra stuff for tarball */ #define EVERY_PAGE 0 /* 0 if don't want */ #define LR_GIF "lr.gif" /* left-right arrows */ #define LRINV_GIF "lrinv.gif" /* lr inverse video */ #define LR0_GIF "lr0.gif" /* lr grayed out */ #define LR0INV_GIF "lr0inv.gif" /* lr inverse grayed out */ #define NAV_GIF "nav.gif" /* main navigation gif */ #define INVNAV_GIF "navinv.gif" /* inverse navigation gif */ #define NAVGIF_WIDTH 64 /* all of the navigation */ #define NAVGIF_HEIGHT 34 /* gifs are this size */ #define BOOK_SEPARATION 20 /* spacing between left/right */ #define RINGSEL_PADDING 4 #define MAX_MEMBERS 300 /* see ring_navigation */ #define MAX_SECTION 30 /* max pages/section */ #define RINGNAV_ROWS 30 /* -------------------------------- meta-data ------------------------------ */ #define CONTENT_TYPE "text/html" #define LANGUAGE "en-US" #define CHARSET "ASCII" #define DOCTYPE \ "" /* ------------------------------- font sizes ------------------------------ */ #define INDEX_SIZE 3 #define NOTE_SIZE 2 #define HEADING_SIZE 3 #define RINGHEAD_SIZE 3 #define ROOTNAV_SIZE 3 #define SELECTOR_SIZE 3 #define BOOK_SIZE 4 #define CDCMP_SIZE "\"+1\"" #define RINGNAV_SIZE 3 /* ---------------------------------- colors ------------------------------- */ #define BODY_COLOR "white" #define TEXT_COLOR "black" #define LINK_COLOR "black" #define VLINK_COLOR "black" #define INVERSE_BODY_COLOR "black" #define INVERSE_TEXT_COLOR "white" #define INVERSE_LINK_COLOR "white" #define INVERSE_VLINK_COLOR "white" #define INVERSE_THEME_COLOR "black" #define THEME_COLOR "\"#CCCCCC\"" #define INDEX_COLOR "\"#FF9900\"" #define CMP_COLOR "\"#FFCC00\"" #define BOOK_BG "\"#FFCC00\"" /* ---------------------------------- style -------------------------------- */ #define PAGE_STYLE PAGE_STYLE_DEFAULT #define RINGNAV_STYLE STYLE_BULLET #define RINGNAV_BULLET1 "square" #define RINGNAV_BULLET2 "disc" #define RINGNAV_HL_BULLET1 "disc" #define RINGNAV_HL_BULLET2 "circle" #define ROOT_CELLPADDING 2 /* ------------------------------------ cd --------------------------------- */ #define CD_DATA_FORMAT "CD/%s/%s.dat" #define CD_FORMAT \ "%s%s" #define CDCMP_FORMAT \ "\n" \ "%s\n" \ "%s\n" \ "·\n" \ "%s\n" \ "\n" /* ---------------------------------- markup ------------------------------- */ #define EOG_MARKER " \n" /* End of Generated */ #define SOG_MARKER " \n" /* Start of Generated */ #define AREA_FORMAT " " /*#define BODY_FORMAT "\n"*/ #define BODY_FORMAT "\n" /* ------------------------------- meta markup ---------------------------- */ #define META_LINK_FORMAT "@@link{%s %s}" /* ---------------------------------- flags ------------------------------- */ #define BIT(n) (((unsigned) 1) << (n)) /* Next level, structs with 'flags' member */ #define ON(p, w) (((p)->flags & (w)) != 0) #define OFF(p, w) (((p)->flags & (w)) == 0) #define SET(p, w) ((p)->flags |= (w)) #define CLR(p, w) ((p)->flags &= ~(w)) #define XOR(p, w) ((p)->flags ^= (w)) /* ------------------------------- linked list ---------------------------- */ typedef struct { void *head; /* head of list */ void *tail; /* tail of list */ size_t n; /* length of list */ } list; #define LINK_GLUE(TYPE) struct TYPE *next, *prev #define LIST_HEAD(l) (l)->head #define LIST_TAIL(l) (l)->tail #define LIST_SIZE(l) (l)->n #define LINK_NEXT(thing) (thing)->next #define LINK_PREV(thing) (thing)->prev #define LIST_INIT(l) { \ LIST_HEAD(l) = LIST_TAIL(l) = NULL; \ LIST_SIZE(l) = 0; \ } #define LINK_HEAD(l, thing) { \ if ((LINK_NEXT(thing) = LIST_HEAD(l))) \ LINK_PREV(LINK_NEXT(thing)) = (thing); \ LINK_PREV(thing) = NULL; \ LIST_HEAD(l) = (thing); \ if (!LIST_TAIL(l)) \ LIST_TAIL(l) = (thing); \ LIST_SIZE(l)++; \ } #define LINK_TAIL(l, thing) { \ if ((LINK_PREV(thing) = LIST_TAIL(l))) \ LINK_NEXT(LINK_PREV(thing)) = (thing); \ LINK_NEXT(thing) = NULL; \ LIST_TAIL(l) = (thing); \ if (!LIST_HEAD(l)) \ LIST_HEAD(l) = (thing); \ LIST_SIZE(l)++; \ } #define LINK_BEFORE(l, before, thing) { \ LINK_NEXT(thing) = (before); \ if ((LINK_PREV(thing) = LINK_PREV(before))) \ LINK_NEXT(LINK_PREV(before)) = (thing); \ else \ LIST_HEAD(l) = (thing); \ LINK_PREV(before) = (thing); \ LIST_SIZE(l)++; \ } /* ----------------------------------- types ------------------------------ */ #ifndef JPEGLIB_H typedef int boolean; /* jpeglib.h defines one too */ #endif typedef enum { PAGE_STYLE_DEFAULT = 0, PAGE_STYLE_BOOK, PAGE_STYLE_VERBATIM, } pstyle; typedef enum { RING_STYLE_DEFAULT = 0, RING_STYLE_BOOK } rstyle; #define PROCESS_OPEN BIT(0) #define RING_PROCESSED BIT(0) #define STYLE_BULLET BIT(0) #define STYLE_CITE BIT(1) #define STYLE_DATE BIT(2) #define STYLE_LI BIT(3) #define STYLE_NEXTPREV BIT(4) #define STYLE_SANS BIT(5) #define STYLE_SELECT BIT(6) #define STYLE_SHORT BIT(7) #define STYLE_10OPCT BIT(8) /* ----------------------------------- ring ------------------------------ */ struct PAGE; /* forward decl */ typedef struct RING { unsigned flags; /* mask of flags */ rstyle style; /* basic style */ char path[MAX_PATH]; /* "foo.ring" */ char long_title[MAX_FIELD]; /* full long title */ char short_title[MAX_SHORT_FIELD]; /* short abbreviated title */ char *cite; /* optional citation */ char *theme; /* theme/title-bar color */ char *bookbg; /* book style body color */ char *bookpage; /* book style page background */ char *width, *height; /* override global default */ int cols; /* forced # cols for ringnav */ int size; /* font size */ list members; /* all ring member pages */ struct PAGE *page; /* page which introduces ring */ } ring; /* ----------------------------------- page ------------------------------ */ #define PAGE_IS_ROOT BIT(0) /* is this ROOT_PAGE? */ #define PAGE_PROCESSED BIT(1) /* already processed this .h? */ #define PAGE_INVERSE BIT(2) /* reverse colors? */ #define PAGE_SECTION BIT(3) /* page starts new section */ struct MEMBER; /* forward decl */ typedef struct PAGE { FILE *in; /* reading .h */ FILE *out; /* producing .html */ unsigned flags; /* page flags */ pstyle style; ring *ring; /* page belongs to this ring */ struct PAGE *up; /* up goes to this page */ char *width, *height; /* override site defaults */ int ord; /* ring ordinal */ int nms; /* next/prev map count */ int meta_lines; /* # of lines of @metadata */ /* real storage for these since every page has them */ char h[MAX_PATH]; /* foo.h */ char html[MAX_PATH]; /* foo.html */ char long_title[MAX_FIELD]; /* full (long) title */ char short_title[MAX_SHORT_FIELD]; /* short title */ /* ptr to malloc'd for less frequent stuff */ char *theme; /* theme/title-bar color */ char *bookpage; /* override ring's */ char *background; /* page background image */ char *bg; /* page background color */ char *cite; /* citation */ char *date; /* date of original */ char *keywords; /* keywords */ char *description; /* description */ char *thumb; /* thumbnail for page scan */ char *thumb_left; /* same in alt position */ char *thumb_left2; /* second one */ int n_rings; /* page introduces n rings */ ring *rings[MAX_PAGE_RING]; /* only this many allowed */ int n_ringnavs; ring *ringnavs[MAX_PAGE_RING]; /* page wants nav for these rings */ int ringnavsize[MAX_PAGE_RING];/* font size */ int ringnav_size; /* ringnav font size */ int ringnav_cols; /* ringnav # columns */ long body; /* position of body start */ struct MEMBER *member; /* backpointer */ } page; /* ------------------------------ member of ring --------------------------- */ typedef struct MEMBER { page *p; /* member may be a page... */ ring *r; /* ...or another ring */ LINK_GLUE(MEMBER); } member; /* ------------------------------------ image ------------------------------ */ /* image file and its metadata */ typedef struct IMAGE { char path[MAX_PATH]; int width, height; int cmpnum; /* comparator {number} */ char *colorname; /* name of color, if a comparable image */ JSAMPLE red, green, blue; /* average color */ FILE *f; LINK_GLUE(IMAGE); struct IMAGE *thumb_of; /* this is a thumb of... */ } image; /* ------------------------------------ index ------------------------------ */ /* index entry */ typedef struct ENTRY { char *text; /* full text */ char *primary; /* primary key */ char *primary_sort; /* text w/tags removed, all lowercase */ char *secondary; /* secondary key */ char *secondary_sort; /* text w/tags removed, all lowercase */ char *comment; /* parenthetical comment */ page *page; /* points to this page */ boolean main; /* this the main entry? */ boolean image; /* this an image entry? */ int num; /* letter number for this entry */ char *see; LINK_GLUE(ENTRY); } entry; /* ---------------------------------- CD data ------------------------------ */ /* data for a specific CD */ typedef struct { char width[MAX_SHORT_FIELD]; char height[MAX_SHORT_FIELD]; char weight[MAX_SHORT_FIELD]; char voltage[MAX_SHORT_FIELD]; char leakage[MAX_SHORT_FIELD]; char pinhole[MAX_SHORT_FIELD]; char style[MAX_SHORT_FIELD]; } cd_data; /* --------------------------------- hash table ---------------------------- */ /* hash table bucket */ typedef struct BUCKET { char *key; void *data; struct BUCKET *next; /* no need for double-link */ } bucket; typedef bucket *hash[MAX_HASH]; /* -------------------------------- @key processor ------------------------- */ #define BODY_KEYFUNC(func, p, up, args) \ boolean func(page *p, page *up, char *args) BODY_KEYFUNC(body_key_area, p, up, args); BODY_KEYFUNC(body_key_cd, p, up, args); BODY_KEYFUNC(body_key_cdcmp, p, up, args); BODY_KEYFUNC(body_key_font, p, up, args); BODY_KEYFUNC(body_key_fixed, p, up, args); BODY_KEYFUNC(body_key_sans, p, up, args); BODY_KEYFUNC(body_key_image, p, up, args); BODY_KEYFUNC(body_key_image_page, p, up, args); BODY_KEYFUNC(body_key_image_link, p, up, args); BODY_KEYFUNC(body_key_thumb_image, p, up, args); BODY_KEYFUNC(body_key_thumb, p, up, args); BODY_KEYFUNC(body_key_tthumb, p, up, args); BODY_KEYFUNC(body_key_link, p, up, args); BODY_KEYFUNC(body_key_index, p, up, args); BODY_KEYFUNC(body_key_index_main, p, up, args); BODY_KEYFUNC(body_key_index_image, p, up, args); BODY_KEYFUNC(body_key_ringnav, p, up, args); BODY_KEYFUNC(body_key_ringhead, p, up, args); BODY_KEYFUNC(body_key_webring, p, up, args); BODY_KEYFUNC(body_key_width, p, up, args); BODY_KEYFUNC(body_key_xlink, p, up, args); BODY_KEYFUNC(body_key_xlink_image, p, up, args); struct { char *key; BODY_KEYFUNC((*func), p, up, args); } body_keyfuncs[] = { { "area", body_key_area }, { "cd", body_key_cd }, { "cdcmp", body_key_cdcmp }, { "fixed", body_key_fixed }, { "font", body_key_font }, { "image", body_key_image }, { "image-page", body_key_image_page }, { "image-link", body_key_image_link }, { "index", body_key_index }, { "index-main", body_key_index_main }, { "index-image", body_key_index_image }, { "link", body_key_link }, { "ringnav", body_key_ringnav }, { "ringhead", body_key_ringhead }, { "sans", body_key_sans }, { "thumb-image", body_key_thumb_image }, { "thumb", body_key_thumb }, { "tthumb", body_key_tthumb }, { "webring", body_key_webring }, { "width", body_key_width }, { "xlink", body_key_xlink }, { "xlink-image", body_key_xlink_image } }; #define N_KEYFUNCS (sizeof(body_keyfuncs) / sizeof(body_keyfuncs[0])) /* ---------------------------------- macros ------------------------------ */ #define FCLOSE(f) { fclose(f); f = NULL; } #define STR(s) ((s) ? (s) : "NIL") #define CALLOC(what, thing) \ if (!(what = calloc(1, sizeof(thing)))) \ { puts("\nOUT OF MEMORY"); exit(2); } #define MALLOC(what, thing) \ if (!(what = malloc(sizeof(thing)))) \ { puts("\nOUT OF MEMORY"); exit(2); } /* ---------------------------------- globals ------------------------------ */ int debug; /* debug level, 0 = none */ int level; /* level in heirarchy */ boolean loading; /* doing main load? not post-processing? */ boolean single_page; /* single-page development mode */ page *root; /* root page of site, ROOT_PAGE */ hash pages; /* hash table of all pages */ int n_pages; /* total number of pages */ ring *root_ring; /* top-level site ring */ hash rings; /* hash table of all rings */ int n_rings; /* total number of rings */ list entries[INDEX_LETTERS]; /* lists of index entries, one per letter */ int n_index; /* total # of index entries */ page *index_page; /* index() is a string.h func */ hash images; /* hash table of all images */ int n_images; /* total number of images */ list cmplist; /* list of all comparable insulator images */ boolean new_colors; /* any new comparables added this run? */ int n_cds; /* number of special CD pages */ page *sitemap; /* generated sitemap */ FILE *xlinks; /* list of all external links */ FILE *manifest; /* generated list of all site files */ int n_manifest; /* total # of files in manifest */ page *cur_page; /* current page being processed */ /* ----------------------- private function declarations ------------------- */ boolean body_key_index_worker(page *p, page *up, char *args, boolean main, boolean image); boolean body_key_thumbn(page *p, page *up, char *args, int n_t); boolean body_process(page *p, page *up); boolean comparable(image *i, char *colorname); boolean do_body_key(page *p, page *up, char *key, char *args); char *find_index(char *name, char *dest); member *find_member(list *members, page *p, int *ord); int generate_index(void); boolean gif_size(image *i); void hash_add(hash h, char *key, void *data); void hash_dump(hash h); void *hash_lookup(hash h, char *key); unsigned hash_of(char *s); void hash_stats(hash h); boolean has_extension(char *s, char *ext); image *image_register(char *ref, char *image_path); boolean image_rgb(image *i); boolean image_write(page *p, char *img, char *to, char *args, int n_thumb); boolean jpg_size(image *i); boolean load_cd_data(char *cd, cd_data *cdd); boolean load_cmplist(void); char *lowercaseify(char *buf); char *next_arg(char *line, char **rest); page *page_register(char *ref, char *h_path, char *def_theme, boolean primary); boolean page_meta(page *p, boolean primary); void page_path(page *p, page *cur, char *pathbuf, int depth); boolean page_process(page *p, page *up, ring *r, unsigned flags); int path_split(char *path, char *pathbuf, char *parts[]); char *rel_path(char *from, char *to, char *buf); char *rel_expand(char *rel, char *ref, char *buf); page *ring_firstpage(ring *r); page *ring_lastpage(ring *r); boolean ring_meta(ring *r, boolean primary); boolean ring_navigation(page *p, ring *r, page *up, unsigned style, int size, int cols, char *font); boolean ring_nextprev(page *p, ring *r); boolean ring_process(ring *r, page *up); ring *ring_register(char *ref, char *ring_path, char *def_theme, boolean primary); boolean ring_selector(page *p, ring *r, page *up, int size, int cols); char *save_string(char *s); void strip_tags(char *from, char *dest); unsigned style_mask(char *s); char *sub_bigger(char *old, char *new, char *buf, char *newbuf); void sub_smaller(char *old, char *new, char *buf); boolean write_cmplist(void); boolean write_link(char *link_href, char *link_text, page *p); void write_meta(page *p, ring *r); boolean write_navigation(page *p, page *up, ring *r); /* ----------------------------------- main -------------------------------- */ int main(int argc, char *argv[]) { page *p; ring *site_ring, *r; char path[MAX_FIELD], line[MAX_LINE]; char *arg, *switches; FILE *errata; int sw, n_errata; puts("genpen.c $Revision: 1.96 $"); /* process switches */ for (argv++, argc--; (arg = *argv) && (*arg == '-'); argv++, argc--) { switches = arg + 1; while ((sw = *switches++)) { switch (sw) { case 'x': debug++; /* the more x's, the more detail */ break; default: printf("Unknown switch '%c'.\n", sw); return 1; } } } /* development shortcut to rebuild a single page; will have bogus nav */ if (argc > 1) { if (argc != 2) { puts("usage: gensite "); return 1; } single_page = TRUE; if (debug) puts("Single-page mode"); if (!(p = page_register(/* ref unknown*/NULL, argv[0], NULL, TRUE))) return 1; if (ON(p, PAGE_IS_ROOT)) r = NULL; else { if (!(r = ring_register(/* ref unknown*/NULL, argv[1], NULL, TRUE))) return 1; if (!ring_meta(r, TRUE)) return 1; } return page_process(p, /*up unknown*/NULL, r, TRUE) ? 0 : 1; } printf("Generating '%s'...\n", NAME); /* --------------------------- start site map -------------------------- */ if (!(sitemap = page_register(NULL, SITEMAP, NULL, TRUE))) return 1; strcpy(sitemap->long_title, SITEMAP_LONG); strcpy(sitemap->short_title, SITEMAP_SHORT); SET(sitemap, PAGE_PROCESSED); if (!(sitemap->out = fopen(sitemap->h, "w"))) /* note: we make a .h! */ { printf("Failed to open sitemap '%s'\n", path); return 1; } fprintf(sitemap->out, "@title %s\n\n", SITEMAP_TITLE); sitemap->body = ftell(sitemap->out); fputs("@index S Site map\n", sitemap->out); fputs("
    \n", sitemap->out); /* --------------------------- start index ----------------------------- */ if (!(index_page = page_register(NULL, INDEX, NULL, TRUE))) return 1; strcpy(index_page->long_title, INDEX_LONG); strcpy(index_page->short_title, INDEX_SHORT); SET(index_page, PAGE_PROCESSED); if (!(index_page->out = fopen(index_page->h, "w"))) { /* note: we make a .h! */ printf("Failed to open index '%s'\n", path); return 1; } fprintf(index_page->out, "@title %s\n\n", INDEX_TITLE); index_page->body = ftell(index_page->out); /* --------------------- start manifest with errata -------------------- */ if (debug) printf("Starting manifest '%s'\n", MANIFEST); if (!(manifest = fopen(MANIFEST, "w"))) { printf("Failed to open manifest '%s'\n", MANIFEST); return 1; } if (debug) printf("Reading errata file '%s'\n", ERRATA); if ((errata = fopen(ERRATA, "r"))) { for (n_errata = 0; fgets(line, sizeof(line), errata); ) { if (*line != '#') { fputs(line, manifest); n_manifest++; n_errata++; } } FCLOSE(errata) } /* ---------------------- start external links list -------------------- */ if (debug) printf("Opening external links file '%s'\n", XLINK); if (!(xlinks = fopen(XLINK, "w"))) { printf("Failed to open xlink '%s'\n", XLINK); return 1; } /* -------------------------- start images list ------------------------ */ if (!load_cmplist()) return 1; /* --------------------register the root ring -------------------------- */ if (!(root_ring = ring_register(NULL, ROOT_RING, NULL, TRUE))) { printf("Failed to load root ring '%s'\n", ROOT_RING); return 1; } /* ----------------------- ditto the site ring ------------------------- */ if (!(site_ring = ring_register(NULL, SITE_RING, NULL, TRUE))) { printf("Failed to load site ring '%s'\n", SITE_RING); return 1; } /* --------------- start recursing from the root page ------------------ */ printf("Starting at root page '%s'...\n", ROOT_PAGE); if (!(root = page_register(NULL, ROOT_PAGE, NULL, TRUE))) return 1; loading = TRUE; if (!page_process(root, NULL, NULL, FALSE)) { printf("Processing failed for '%s'\n", root->h); /* save that cmplist! computing average RGB is expensive */ if (new_colors) write_cmplist(); return 1; } loading = FALSE; /* ------------------- process finished sitemap ------------------------ */ fputs("
\n", sitemap->out); FCLOSE(sitemap->out) CLR(sitemap, PAGE_PROCESSED); /* force reload */ printf(" Processing site map %s\n", sitemap->h); if (!page_process(sitemap, root, site_ring, 0)) { puts("Sitemap processing failed"); return 1; } /* ----------------- generate index, then process it ------------------- */ printf(" Generating index %s\n", index_page->h); if (!generate_index()) { puts("Index generation failed."); return 1; } FCLOSE(index_page->out) CLR(index_page, PAGE_PROCESSED); /* force reload */ puts(" Processing index"); if (!page_process(index_page, root, site_ring, 0)) { puts("Index processing failed"); return 1; } /* -------------- finished with all pages, close the rest -------------- */ FCLOSE(manifest) FCLOSE(xlinks) /* write out updated color comparison list if additions */ if (new_colors) write_cmplist(); printf(" %d files: %d errata + %d pages in %d rings\n", n_manifest, n_errata, n_pages, n_rings); printf(" %d comparable images out of %d total\n", LIST_SIZE(&cmplist), n_images); if (debug) { fputs("Pages hash stats: ", stdout); hash_stats(pages); fputs("Rings hash stats: ", stdout); hash_stats(rings); fputs("Images hash stats: ", stdout); hash_stats(images); } puts("Done."); return 0; } void add_to_manifest(char *file) { if (manifest) { fputs(file, manifest); putc('\n', manifest); n_manifest++; } } /* --------------------------------- page -------------------------------- */ /* return page pointer given its path. pages are identified by the full path to their .h file. if the named page already exists, it is just returned. otherwise, adds a new page. pages names are in the global hash table 'pages'. */ page *page_register(char *ref, char *h_path, char *def_theme, boolean primary) { char pathbuf[MAX_PATH]; page *p; rel_expand(h_path, ref, pathbuf); /* cvt rel path to abs */ if ((p = hash_lookup(pages, pathbuf))) /* already exists? */ { if (debug > 4) fprintf(stderr, "page_register(ref='%s', h_path='%s') -> %s\n", ref, h_path, p->h); return p; /* yes, return old page. */ } if (debug > 3) printf("new page '%s'\n", pathbuf); CALLOC(p, page) strcpy(p->h, pathbuf); /* "foo.h" */ strcpy(p->html, pathbuf); strcat(p->html, "tml"); /* "foo.html" */ p->theme = def_theme; add_to_manifest(p->html); /* root page path only matches for top-level "index.h", not some sub-page "index.h" that's being worked on. */ if (/*!single_page &&*/ !strcmp(h_path, ROOT_PAGE)) SET(p, PAGE_IS_ROOT); hash_add(pages, p->h, (void *) p); n_pages++; /*if (!single_page || (level == 1))*/ if (!page_meta(p, primary)) /* load page's metadata */ return NULL; /* doesn't exist or bogus */ return p; /* return new page */ } /* load page metadata */ boolean page_meta(page *p, boolean primary) { char line[MAX_LINE], rp[MAX_FIELD]; char *key, *rest, *e, *bar, *width, *height, *path; int len, line_no, size; pstyle s; ring *r; if (debug > 3) fprintf(stderr, "--> page_meta(p='%s')\n", p->h); p->style = PAGE_STYLE; if (loading) { /* special cases for generated files */ if (p == sitemap) { strcpy(p->long_title, SITEMAP_LONG); strcpy(p->short_title, SITEMAP_SHORT); return TRUE; } if (p == index_page) { strcpy(p->long_title, INDEX_LONG); strcpy(p->short_title, INDEX_SHORT); return TRUE; } } /* open the .h file */ if (!(p->in = fopen(p->h, "r"))) { printf("***** ERROR: failed to open h file '%s'\n", p->h); perror("fopen"); return FALSE; } /* read the header, stop at blank line */ for (line_no = 1; fgets(line, sizeof(line), p->in); line_no++) { if (*line == '#') /* ignore #comments */ continue; /* clobber ending NL */ e = line + strlen(line) - 1; /* last char */ if (*e != '\n') { fprintf(stderr, "line %d overflow; max %d\n", line_no, sizeof(line)); return FALSE; } *e = 0; /* clobber NL */ /* blank line ends header */ if (!*line) break; key = next_arg(line, &rest); /* get first arg as */ if (!strcmp(key, "@title")) { if ((bar = strchr(rest, '|'))) /* "Long Wordy Title|Title" */ { len = bar - rest; strncpy(p->long_title, rest, len); p->long_title[len] = 0; strcpy(p->short_title, bar + 1); } else { strcpy(p->long_title, rest); strncpy(p->short_title, rest, MAX_SHORT_FIELD); p->short_title[MAX_SHORT_FIELD - 1] = 0; } } else if (!strcmp(key, "@inverse")) /* invert colors? */ SET(p, PAGE_INVERSE); else if (!strcmp(key, "@style")) { key = next_arg(rest, &rest); if (!strcmp(key, "book")) s = PAGE_STYLE_BOOK; else if (!strcmp(key, "vertical")) s = PAGE_STYLE_DEFAULT; else if (!strcmp(key, "verbatim")) s = PAGE_STYLE_VERBATIM; else { printf("***** ERROR: page=%s line %d " "unknown page style %s", p->h, line_no, key); return FALSE; } switch (p->style = s) { case PAGE_STYLE_BOOK: if (!(path = next_arg(rest, &rest))) return FALSE; rel_expand(path, p->h, rp); if (!(r = ring_register(p->h, rp, p->theme, primary))) return FALSE; if (rest) { width = next_arg(rest, &rest); if (!atoi(width)) { printf("***** ERROR: page width '%s'.\n", width); return FALSE; } p->width = save_string(width); height = next_arg(rest, &rest); if (!atoi(height)) { printf("***** ERROR: page height '%s'.\n", height); return FALSE; } p->height = save_string(height); } else { p->width = r->width; p->height = r->height; } break; default: /* no extra arguments for the rest */ break; } } else if (!strcmp(key, "@book-ringnav-size")) p->ringnav_size = atoi(rest); else if (!strcmp(key, "@book-ringnav-cols")) p->ringnav_cols = atoi(rest); else if (!strcmp(key, "@color")) p->theme = save_string(rest); else if (!strcmp(key, "@background")) p->background = save_string(rest); else if (!strcmp(key, "@bg")) p->bg = save_string(rest); else if (!strcmp(key, "@bookpage")) p->bookpage = save_string(rest); else if (!strcmp(key, "@cite")) p->cite = save_string(rest); else if (!strcmp(key, "@date")) p->date = save_string(rest); else if (!strcmp(key, "@section")) SET(p, PAGE_SECTION); else if (!strcmp(key, "@ringnav")) { if (!(path = next_arg(rest, &rest))) return FALSE; if (rest) { size = atoi(next_arg(rest, &rest)); if (rest) rel_expand(rest, p->h, rp); } else size = RINGNAV_SIZE; if (p->n_ringnavs == MAX_PAGE_RING) { printf("***** ERROR: page=%s line %d " "too many ringnavs, max %d\n", p->h, line_no, MAX_PAGE_RING); return FALSE; } if (!(p->ringnavs[p->n_ringnavs] = ring_register(p->h, path, p->theme, primary))) return FALSE; p->ringnavsize[p->n_ringnavs++] = size; } else if (!strcmp(key, "@keywords")) p->keywords = save_string(rest); else if (!strcmp(key, "@description")) p->description = save_string(rest); else if (!strcmp(key, "@ring")) { rel_expand(rest, p->h, rp); if (p->n_rings == MAX_PAGE_RING) { printf("***** ERROR: page '%s' line %d " "too many rings, max %d\n", p->h, line_no, MAX_PAGE_RING); return FALSE; } if (!(r = ring_register(p->h, rp, p->theme, TRUE))) return FALSE; if (r->page) { printf("***** ERROR: ring '%s' introduced by page '%s' " "*and* '%s'\n", r->path, r->page->h, p->h); return FALSE; } p->rings[p->n_rings++] = r; r->page = p; /* ring r introduced by p */ } else if (!strcmp(key, "@thumb")) /* thumbnail for page scan */ p->thumb = save_string(rest); else if (!strcmp(key, "@thumb-left")) /* same in alt. position */ { if (!p->thumb_left) p->thumb_left = save_string(rest); else if (!p->thumb_left2) p->thumb_left2 = save_string(rest); else { printf("***** ERROR: page '%s' max 2 thumb-lefts\n", p->h); return FALSE; } } else { printf("***** ERROR: page '%s' line %d " "unknown metadata key '%s'\n", p->h, line_no, key); return FALSE; } } if (debug > 3) fprintf(stderr, "<-- page_meta(p='%s')\n", p->h); if (p != cur_page) /* if not current page being processed */ { p->body = ftell(p->in); /* note body starts here; */ FCLOSE(p->in) /* come back to it later. */ } p->meta_lines = line_no + 1; /* for correct body line# output */ return TRUE; } /* register a new page */ boolean page_process(page *p, page *up, ring *r, unsigned flags) { ring *pr; char *title; char cd_title[MAX_FIELD]; int i; if (!p) { puts("***** ERROR: page_process: no page"); return FALSE; } if (!r && OFF(p, PAGE_IS_ROOT)) { puts("***** ERROR: page_process: no ring"); return FALSE; } if (ON(p, PAGE_PROCESSED)) return TRUE; /* already processed this page */ level++; SET(p, PAGE_PROCESSED); /* avoid loops */ cur_page = p; if (debug) fprintf(stderr, "Processing page (%d) '%s'\n", level, p->h); p->up = up; if (!p->ring) p->ring = r; if (!(p->in = fopen(p->h, "r"))) /* re-open file */ { printf("***** ERROR: failed to open h file '%s'\n", p->h); perror("fopen"); goto page_error; } if (fseek(p->in, p->body, SEEK_SET)) { printf("***** ERROR: failed to seek h file '%s' to %ld\n", p->h, p->body); goto page_error; } if (!(p->out = fopen(p->html, "w"))) { fprintf(stderr, "failed to open html file '%s'\n", p->html); goto page_error; } /* update sitemap */ if (loading) { if (OFF(p, PAGE_IS_ROOT) && (r->style != RING_STYLE_BOOK)) { boolean is_cd = !strcmp(r->long_title, "Threaded Insulators"); if (is_cd && n_cds++) fputs(" \n", sitemap->out); else fputs("
  • ", sitemap->out); if (is_cd) { strcpy(title = cd_title, p->long_title); sub_smaller("CD ", "", title); } else title = p->long_title; if (level == 2) fputs("", sitemap->out); fprintf(sitemap->out, META_LINK_FORMAT, p->h, title); if (level == 2) fputs("", sitemap->out); if (!is_cd) fputs("
    \n", sitemap->out); } } /* write basic HTML metadata */ write_meta(p, r); /* write HTML metadata to .html */ if (OFF(p, PAGE_IS_ROOT)) /* for all pages but root */ if (!write_navigation(p, up, r)) /* write nav bar and title */ goto page_error; fputs(EOG_MARKER, p->out); /* End Of Generated part */ if (!body_process(p, up)) goto page_error; FCLOSE(p->in) /* finished with .h file */ fputs(SOG_MARKER, p->out); /* Start Of Generated trailer */ if (p->style == PAGE_STYLE_BOOK) { fputs(" \n", p->out); if (p->thumb) { fprintf(p->out, "  \n", BOOK_SEPARATION); fputs(" \n", p->out); if (!body_key_thumbn(p, up, p->thumb, 1)) return FALSE; fputs(" \n", p->out); } fputs("\n", p->out); } fputs(" \n\n", p->out); /* if page introduces new rings, process them now */ if (!single_page && p->n_rings) { if (debug > 2) fprintf(stderr, " n_rings = %d\n", p->n_rings); if (loading && (p != root)) fputs("
      \n", sitemap->out); for (i = 0; i < p->n_rings; i++) { pr = p->rings[i]; if (!ring_process(pr, p)) goto page_error; } if (loading && (p != root)) fputs("
    \n", sitemap->out); } if (debug > 2) fprintf(stderr, "<-- page_process p='%s', up='%s', r=%s\n", p->h, up ? up->h : "NULL", r ? r->path : "NULL"); FCLOSE(p->out); level--; return TRUE; page_error: level--; return FALSE; } /* ------------------------------ navigation ------------------------------ */ boolean homeup_navigation(page *p, page *up, ring *r) { char rp[MAX_LINE], *up_title; if (debug > 2) fprintf(stderr, "homeup_navigation(page=%s, up=%s, ring=%s)\n", p->h, up ? up->h : "NULL", r->path); /* relative path to root directory and its images subdir */ rel_path(p->h, ROOT_HTML, rp); if (debug > 2) fprintf(stderr, "Rel path to ROOT_HTML: %s\n", rp); fputs(" \n", p->out); fprintf(p->out, " Home\n", rp); rel_path(p->h, INDEX_HTML, rp); if (debug > 2) fprintf(stderr, "Rel path to INDEX_HTML: %s\n", rp); fprintf(p->out, " \n", rp); rel_path(p->h, SITEMAP_HTML, rp); if (debug > 2) fprintf(stderr, "Rel path to SITEMAP_HTML: %s\n", rp); fprintf(p->out, " \n", rp); if (up) { rel_path(p->h, up->html, rp); up_title = up->short_title; } else { strcpy(rp, "NO_LINK"); up_title = "Unknown"; } fprintf(p->out, " \n", rp, up_title); fputs(" \n", p->out); rel_path(p->h, IMAGES, rp); if (debug > 2) fprintf(stderr, "Rel path to IMAGES: %s\n", rp); fprintf(p->out, " Navigation
    \n", rp, ON(p, PAGE_INVERSE) ? INVNAV_GIF : NAV_GIF, NAVGIF_WIDTH, NAVGIF_HEIGHT); fprintf(p->out, " Up: %s\n", GENSITE_FONT, ON(p, PAGE_INVERSE) ? "color=white " : "", NOTE_SIZE, up_title); return TRUE; } /* --------------------------------- body -------------------------------- */ /* copy input .h to output .html expanding @ tags. two forms are allowed, a full-line version where @key starts the first: '@' key [add'l args separated by spaces] EOL and an inline version ... '@@' key '{' [add'l args separated by spaces] '}' ... EOL the same output is produced, but the first consumes the EOL and the inline version does not. note multiple inlines allowed. */ boolean body_process(page *p, page *up) { char line[MAX_LINE]; char *rest, *from, *key, *e, *args, *start, *atat; int line_no, len; if (debug > 2) fprintf(stderr, "--> body_process(p='%s')\n", p->h); /* not watching for truncated lines */ for (line_no = p->meta_lines; fgets(line, sizeof(line), p->in); line_no++) { /* check for raw hrefs */ if (strstr(line, "href=") && !strstr(line, "h, line_no); return FALSE; } if (strstr(line, "helvetica")) printf("***** WARNING: page '%s' body line %d raw helvetica\n", p->h, line_no); /* clobber ending NL */ e = line + strlen(line) - 1; /* last char */ if (*e != '\n') { printf("***** ERROR: page '%s' body line %d overflow; max %d\n", p->h, line_no, sizeof(line)); return FALSE; } *e = 0; /* clobber NL */ if (debug > 3) printf(" body line: '%s'\n", line); /* scan for @key at beginning or @@key anywhere else */ if (*line == '@') { start = line + 1; if (*start == '@') atat = line; else { key = next_arg(start, &args); if (!do_body_key(p, up, key, args)) { printf("***** ERROR: at line %d of %s\n", line_no, p->h); return FALSE; } continue; } } else atat = strstr(line, "@@"); for (from = rest = line; atat; atat = strstr(from = rest, "@@")) { /* initial stuff before @key? */ if ((len = atat - from)) fwrite(from, 1, len, p->out); for (key = atat + 2; *from != '{'; from++) ; *from++ = 0; args = from; if (!(rest = strchr(from, '}'))) { printf("***** ERROR: missing close brace: " "'%s'\n", line); return FALSE; } *rest++ = 0; if (!do_body_key(p, up, key, args)) { printf("***** ERROR: at line %d of %s\n", line_no, p->h); return FALSE; } } if (rest) fputs(rest, p->out); /* emit leftover */ putc('\n', p->out); } if (debug > 2) fprintf(stderr, "<-- body_process(p='%s')\n", p->h); return TRUE; } boolean do_body_key(page *p, page *up, char *key, char *args) { int ki; /* search body keyword table for matching keyword */ for (ki = 0; ki < N_KEYFUNCS; ki++) if (!strcmp(body_keyfuncs[ki].key, key)) break; if (ki == N_KEYFUNCS) { printf("***** ERROR: page '%s' unknown body key '%s'\n", p->h, key); return FALSE; } if (debug > 3) fprintf(stderr, "=== @%s ===\n", key); if (!(*body_keyfuncs[ki].func)(p, up, args)) { printf("***** ERROR: page '%s' @%s failed\n", p->h, key); return FALSE; } return TRUE; } /* ---------- RINGNAV --------------------------------------- @ringnav ring style size - Adds extra ring navigator of same style as main page's navigator. ring - ring path (./ will be expanded) style - mask of STYLE bits seperated by | with no spaces size - font size, 0 for default cols - number of columns (if not specified, automatic) example: @ringnav ./paper.ring [style [size [cols]]] */ BODY_KEYFUNC(body_key_ringnav, p, up, s) { char *rp, *rest; ring *r; char rpath[MAX_PATH]; unsigned ps; int size, cols; if (!(rp = next_arg(s, &rest))) return FALSE; ps = RINGNAV_STYLE; size = RINGNAV_SIZE; cols = 0; if (rest) { if (!(ps = style_mask(next_arg(rest, &rest)))) return FALSE; if (rest) { if (!(size = atoi(next_arg(rest, &rest)))) size = RINGNAV_SIZE; if (rest) cols = atoi(next_arg(rest, &rest)); } } rel_expand(rp, p->h, rpath); if (!(r = ring_register(p->h, rpath, p->theme, FALSE))) return FALSE; return (ps & STYLE_NEXTPREV) ? ring_nextprev(p, r) : ring_navigation(p, r, up, ps, size, cols, (ps & STYLE_SANS) ? GENSITE_SANS : NULL); } /* ---------- RINGHEAD -------------------------------------- @ringhead ring style - Adds simple navigator to first page in ring, with ring's name and size in a banner. ring - ring path (./ will be expanded) style - style keywords; only 'short' used (or leave blank) example: @ringhead ./elliott.ring short */ BODY_KEYFUNC(body_key_ringhead, p, up, args) { char *rp, *rest, *title, *style; list *members; member *m1; ring *r; page *mp; char dest[MAX_FIELD], rpath[MAX_PATH]; unsigned m; int size; if (!(rp = next_arg(args, &rest))) return FALSE; rel_expand(rp, p->h, rpath); if (!(r = ring_register(p->h, rpath, p->theme, FALSE))) return FALSE; if (rest) { if (!(style = next_arg(rest, &rest))) return FALSE; m = style_mask(style); size = 0; if (rest) size = atoi(next_arg(rest, &rest)); if (!size) size = RINGHEAD_SIZE; } else m = 0; members = &r->members; m1 = (member *) LIST_HEAD(members); if (!(mp = m1->p)) mp = ring_firstpage(m1->r); rel_path(p->h, mp->html, dest); title = (m & STYLE_SHORT) ? r->short_title : r->long_title; if (!write_link(dest, title, p)) return FALSE; fprintf(p->out, " (%d)", (int) LIST_SIZE(members)); return TRUE; } /* ---------- WEBRING ---------------------------------------- @webring - Inserts [boilerplate] contents of webring file example: @webring */ BODY_KEYFUNC(body_key_webring, p, up, ignored_args) { char line[MAX_LINE]; FILE *f; if (!(f = fopen(WEBRING, "r"))) { printf("***** ERROR: failed to open webring file '%s'\n", WEBRING); return FALSE; } fputs("\n", p->out); /* not watching for truncated lines */ while (fgets(line, sizeof(line), f)) fputs(line, p->out); fclose(f); fputs("\n", p->out); return TRUE; } /* ---------- INDEX ------------------------------------------ @index [ '->' ] - Adds index entry @index-main [ '->' ] - Adds main index entry @index-image [ '->' ] - Adds image index entry example: @index A Arrdvarks */ BODY_KEYFUNC(body_key_index, p, up, args) { return body_key_index_worker(p, up, args, FALSE, FALSE); } BODY_KEYFUNC(body_key_index_main, p, up, args) { return body_key_index_worker(p, up, args, TRUE, FALSE); } BODY_KEYFUNC(body_key_index_image, p, up, args) { return body_key_index_worker(p, up, args, FALSE, TRUE); } boolean body_key_index_worker(page *p, page *up, char *args, boolean main, boolean image) { char sort[MAX_FIELD]; /* entry text stripped for sorting */ char *rest, *see, *letter, *op, *cp; entry *e, *f; /* index entries */ list *ll; /* entries list for letter */ int l; /* index letter, 'A' is 0 */ int s; /* sort order (strcmp retval) */ if (!(letter = next_arg(args, &rest))) return FALSE; if (strlen(letter) != 1) { printf("***** ERROR: index letter '%s' wrong length (not 1)\n", letter); return FALSE; } l = toupper(*letter); if ((l < 'A') || (l > 'Z')) { printf("***** ERROR: index letter '%c' not A-Z\n", l); return FALSE; } l -= 'A'; /* turn into 0-based index */ if ((see = strstr(rest, "->"))) { *see = 0; if (*(see - 1) == ' ') *(see - 1) = 0; see += 2; if (*see == ' ') see++; } CALLOC(e, entry) e->page = p; /* index entry points to this page */ e->text = save_string(rest); /* full text of entry */ e->main = main; /* main entry? */ e->image = image; /* image entry? */ /* split "primary|secondary (comment)" */ cp = rest + strlen(rest) - 1; if (*cp == ')') { for (op = cp - 1; (op > rest) && (*op != '('); op--) ; if (op == rest) { puts("***** ERROR: Bungled parens in index"); return FALSE; } *(op - 1) = *cp = 0; e->comment = save_string(op + 1); } if ((op = strchr(rest, '|'))) { *op = 0; e->secondary = save_string(op + 1); } e->primary = save_string(rest); strip_tags(e->primary, sort); /* strip HTML tags */ lowercaseify(sort); /* force to all lowercase */ e->primary_sort = save_string(sort);/* we'll sort using this key */ if (e->secondary) { strip_tags(e->secondary, sort); lowercaseify(sort); e->secondary_sort = save_string(sort); } if (see) e->see = save_string(see); ll = &entries[l]; /* list of entries for letter l */ if (!LIST_SIZE(ll)) /* this is first entry? */ LINK_HEAD(ll, e) /* yes, start list. */ else /* else add in sorted order */ { /* scan for 'larger' string, keep list in increasing order */ for (f = (entry *) LIST_HEAD(ll); f; f = LINK_NEXT(f)) { if (!(s = strcmp(f->primary_sort, e->primary_sort))) { if (e->secondary) { if (f->secondary) s = strcmp(f->secondary_sort, e->secondary_sort); else s = 0; } else s = f->secondary ? 1 : 0; } if (s > 0) break; /* went too far, so... */ } if (f) /* new entry comes right before f */ LINK_BEFORE(ll, f, e) else LINK_TAIL(ll, e) /* new entry comes last */ } e->num = LIST_SIZE(ll); /* A1, A2, etc */ fprintf(p->out, "", l + 'A', e->num); n_index++; /* bump total # index entries */ return TRUE; } /* ---------- AREA -------------------------------------------- @area [img tags] - Includes local image . optional img tags (identified by '=' in the middle) are followed by required alt text. example: @area ./foo.jpg hspace=4 Authentic Foo ca1960s */ BODY_KEYFUNC(body_key_area, p, up, args) { char *image, *rest; if (!(image = next_arg(args, &rest))) return FALSE; if (!image_register(p->h, image)) /* get image metadata, write manifest */ return FALSE; fprintf(p->out, AREA_FORMAT, image, rest); return TRUE; } /* ---------- IMAGE ------------------------------------------- @image [img tags] - Includes local image . optional img tags (identified by '=' in the middle) are followed by required alt text. example: @image ./foo.jpg hspace=4 Authentic Foo ca1960s */ BODY_KEYFUNC(body_key_image, p, up, args) { char *image, *rest; if (!(image = next_arg(args, &rest))) return FALSE; return image_write(p, image, NULL, rest, 0); } /* ---------- IMAGE-LINK -------------------------------------- @image-link - Makes a hypertext link to an image. example: @image-link ./foo.jpg Picture of a Foo */ BODY_KEYFUNC(body_key_image_link, p, up, args) { char *path, *rest; char rp[MAX_PATH]; image *im; if (!(path = next_arg(args, &rest))) return FALSE; if (!(im = image_register(p->h, path))) return FALSE; rel_path(p->h, im->path, rp); return write_link(rp, rest, p); } /* ---------- IMAGE-PAGE -------------------------------------- @image-page [img tags] - Makes an image hypertext linked to a page. example: @image-page ./foo.jpg Picture of a Foo */ BODY_KEYFUNC(body_key_image_page, p, up, args) { char *image, *rest, *dest; page *to; if (!(image = next_arg(args, &rest))) return FALSE; if (!image_register(p->h, image)) return FALSE; if (!(dest = next_arg(rest, &rest))) return FALSE; if (!(to = page_register(p->h, dest, up ? up->theme : NULL, FALSE))) return FALSE; return image_write(p, image, to->html, rest, 0); } /* ---------- IMAGE-THUMB ------------------------------------- @thumb-image [img tags] - Includes a thumbnail-image link which links to full-size . example: @thumb-image ./foo.jpg Top/bigfoo.jpg border=0 Major Foo Action */ BODY_KEYFUNC(body_key_thumb_image, p, up, args) { char *thumb, *image, *rest; if (!(thumb = next_arg(args, &rest))) return FALSE; if (!(image = next_arg(rest, &rest))) return FALSE; return image_write(p, thumb, image, rest, 0); } /* ---------- THUMB ------------------------------------------- @thumb [img tags] - Includes thumbnail of image linked to full-size image; path may be ./relative. optional img tags (identified by '=' in the middle) are followed by required alt text. name of thumbname is formed by t., e.g. "bar.jpg" has a thumbnail named "bart.jpg". example: @thumb ./foo.jpg hspace=4 vspace=4 Authentic Foo ca1960s */ BODY_KEYFUNC(body_key_thumb, p, up, args) { return body_key_thumbn(p, up, args, 1); } BODY_KEYFUNC(body_key_tthumb, p, up, args) { return body_key_thumbn(p, up, args, 2); } boolean body_key_thumbn(page *p, page *up, char *args, int n_t) { char thumb[MAX_PATH]; char *image, *rest, *e; int len, ext_len; if (!(image = next_arg(args, &rest))) return FALSE; len = strlen(image); strcpy(thumb, image); e = thumb + len - 1; /* point to last char */ for (ext_len = 0; (e >= thumb) && (*e != '.'); ext_len++) e--; /* back up to '.' */ while (n_t-- > 0) /* add t's to the name */ *e++ = 't'; /* "foo.jpg" -> "footjpg" */ *e++ = '.'; /* foot.pg" */ strcpy(e, image + len - ext_len); /* foot.jpg" */ return image_write(p, thumb, image, rest, n_t); } /* ---------- XLINK ------------------------------------------- @xlink - Creates absolute link to external page with given hypertext example: @xlink http://benevolent.org/permanent.resource Benevolent */ BODY_KEYFUNC(body_key_xlink, p, up, args) { char *dest, *rest; if (!(dest = next_arg(args, &rest))) return FALSE; if (!rest || !*rest) /* no text? */ return FALSE; if (xlinks) { fputs(p->h, xlinks); /* page with the link */ putc(' ', xlinks); fputs(dest, xlinks); /* write to list of all xlinks */ putc('\n', xlinks); } return write_link(dest, rest, p); } /* ---------- XLINK-IMAGE ------------------------------------- @xlink-image [img tags] - Creates absolute link to destination page with given hypertext . zero or more img tags are allowed, identified by '=' sign. An is required. example: @xlink-image http://www.sediver.fr/ ./logo.gif border=0 Sediver */ BODY_KEYFUNC(body_key_xlink_image, p, up, args) { char *dest, *rest, *image; if (!(dest = next_arg(args, &rest))) return FALSE; if (!(image = next_arg(rest, &rest))) return FALSE; if (!rest || !*rest) /* no text? */ return FALSE; if (xlinks) { fputs(p->h, xlinks); putc(' ', xlinks); fputs(dest, xlinks); /* write to list of all xlinks */ putc('\n', xlinks); } return image_write(p, image, dest, rest, 0); } /* ---------- LINK ------------------------------------------ @link - Creates relative link to destination page with given hypertext example: See the @@link{biblio.h,Bibliography} */ BODY_KEYFUNC(body_key_link, p, up, args) { char *dest, *rest; char rp[MAX_PATH], path[MAX_PATH]; struct stat s; ring *r; page *to; if (debug > 2) fprintf(stderr, "body_key_link p='%s', args='%s'\n", p->h, args); if (!(dest = next_arg(args, &rest))) { puts("***** ERROR: @link missing destination"); return FALSE; } if (!rest || !*rest) /* no text? */ { puts("***** ERROR: @link missing text"); return FALSE; } /* if linking to an image, figure the image's metadata */ if (has_extension(dest, "gif") || has_extension(dest, "jpg")) { rel_expand(dest, p->h, path); if (!image_register(p->h, path)) return FALSE; rel_path(p->h, path, rp); dest = rp; } else if (has_extension(dest, "ring")) { if (!(r = ring_register(p->h, dest, p->theme, FALSE))) return FALSE; to = ring_firstpage(r); rel_path(p->h, to->html, dest = rp); } else if (has_extension(dest, "html")) { if (p != index_page) printf("*** Warning: @link to raw HTML '%s'\n", dest); rel_expand(dest, p->h, path); rel_path(p->h, path, dest = rp); if (!strchr(path, '#')) /* not index links */ add_to_manifest(path); } else if (has_extension(dest, "h")) { if (!(to = page_register(p->h, dest, up ? up->theme : NULL, FALSE))) return FALSE; rel_path(p->h, to->html, dest = rp); } else if (has_extension(dest, "c") || has_extension(dest, "pdf")) { rel_expand(dest, p->h, path); if (stat(path, &s)) { printf("***** ERROR: linked-to file %s does not exist\n", path); return FALSE; } rel_path(p->h, path, dest = rp); add_to_manifest(path); } else if (has_extension(dest, "H")) { *(dest + strlen(dest) - 1) = 'h'; if (stat(dest, &s)) { printf("***** ERROR: linked-to header %s does not exist\n", dest); return FALSE; } rel_path(p->h, dest, rp); dest = rp; } else if (*dest != '#') { printf("***** ERROR: linked-to file %s not a .{ring,c,H,h,gif,jpg}\n", dest); return FALSE; } return write_link(dest, rest, p); } /* ---------- CD ---------------------------------------------- @cd - Create CD reference page example: @cd 102 */ BODY_KEYFUNC(body_key_cd, p, up, args) { cd_data cdd; char *cd; char *rest; if (!(cd = next_arg(args, &rest))) return FALSE; if (!load_cd_data(cd, &cdd)) return FALSE; fputs("\n", p->out); fprintf(p->out, CD_FORMAT, "Width", cdd.width); fprintf(p->out, CD_FORMAT, "Height", cdd.height); fprintf(p->out, CD_FORMAT, "Weight", cdd.weight); fprintf(p->out, CD_FORMAT, "Voltage", cdd.voltage); fprintf(p->out, CD_FORMAT, "Leakage", cdd.leakage); fprintf(p->out, CD_FORMAT, "Pinhole", cdd.pinhole); fprintf(p->out, CD_FORMAT, "Style#", cdd.style); fputs("
    \n", p->out); return TRUE; } /* ---------- CDCMP ------------------------------------------- @cdcmp - Create CD comparison page example: @cdcmp 161 162 */ BODY_KEYFUNC(body_key_cdcmp, p, up, args) { char *cd1, *cd2; char *rest; cd_data cdd1, cdd2; if (!(cd1 = next_arg(args, &rest))) return FALSE; if (!load_cd_data(cd1, &cdd1)) return FALSE; if (!(cd2 = next_arg(rest, &rest))) return FALSE; if (!load_cd_data(cd2, &cdd2)) return FALSE; fprintf(p->out, "\n", cd1, cd2); fputs("\n", p->out); fprintf(p->out, "", GENSITE_FONT, CDCMP_SIZE, cd1); fputs("", p->out); fprintf(p->out, "\n", GENSITE_FONT, CDCMP_SIZE, cd2); fputs("\n", p->out); #define CDCMP_OUT(p, label, thing1, thing2) \ fprintf(p->out, CDCMP_FORMAT, GENSITE_FONT, CDCMP_SIZE, (label), \ GENSITE_FONT, CDCMP_SIZE, (thing1), \ GENSITE_FONT, CDCMP_SIZE, (thing2)) CDCMP_OUT(p, "Width", cdd1.width, cdd2.width); CDCMP_OUT(p, "Height", cdd1.height, cdd2.height); CDCMP_OUT(p, "Weight", cdd1.weight, cdd2.weight); CDCMP_OUT(p, "Voltage", cdd1.voltage, cdd2.voltage); CDCMP_OUT(p, "Leakage", cdd1.leakage, cdd2.leakage); CDCMP_OUT(p, "Pinhole", cdd1.pinhole, cdd2.pinhole); CDCMP_OUT(p, "Style#", cdd1.style, cdd2.style); fputs("
    " "CD %sCD %s" "
    \n", p->out); return TRUE; } #define CD_FIELD(field) \ if (!(bar = strchr(from, '|'))) \ { printf("***** ERROR: bad CD %s data file '%s'\n", cd, path); \ return FALSE; } \ *bar = 0; \ if ((len = bar - from) >= MAX_SHORT_FIELD) { \ printf("***** ERROR: CD %s data file field overflow\n", cd); \ return FALSE; } \ strcpy(cdd->field, from); \ from = bar + 1; boolean load_cd_data(char *cd, cd_data *cdd) { char path[MAX_PATH], line[MAX_LINE]; char *from, *bar; FILE *f; int len; sprintf(path, CD_DATA_FORMAT, cd, cd); if (!(f = fopen(path, "r"))) { printf("***** ERROR: missing CD data file '%s'\n", path); return FALSE; } if (!(fgets(line, sizeof(line), f))) { printf("***** ERROR: empty CD data file '%s'\n", path); return FALSE; } from = line; memset(cdd, 0, sizeof(cd_data)); CD_FIELD(width) CD_FIELD(height) CD_FIELD(weight) CD_FIELD(voltage) CD_FIELD(leakage) CD_FIELD(pinhole) if (strlen(from) >= MAX_SHORT_FIELD) { puts("***** ERROR: CD data file field overflow (style)"); return FALSE; } strcpy(cdd->style, from); fclose(f); return TRUE; } /* ---------- FONT ------------------------------------------- @font - insert main font name example: */ BODY_KEYFUNC(body_key_font, p, up, args) { fputs(GENSITE_FONT, p->out); return TRUE; } /* ---------- FIXED ------------------------------------------- @font - insert fixed-width font name example: */ BODY_KEYFUNC(body_key_fixed, p, up, args) { fputs(GENSITE_FIXED, p->out); return TRUE; } /* ---------- SANS ------------------------------------------- @sans - insert sans-serif font name example: */ BODY_KEYFUNC(body_key_sans, p, up, args) { fputs(GENSITE_SANS, p->out); return TRUE; } /* ---------- WIDTH ------------------------------------------- @width - insert primary page width example: */ BODY_KEYFUNC(body_key_width, p, up, args) { fprintf(p->out, "%s", GENSITE_WIDTH); return TRUE; } /* --------------------------------- html -------------------------------- */ boolean write_link(char *link_href, char *link_text, page *p) { if (!link_text || !*link_text) { printf("***** ERROR: empty link text for '%s'\n", link_href); return FALSE; } fprintf(p->out, "%s", link_href, link_text); return TRUE; } void write_meta(page *p, ring *r) { char title[MAX_FIELD], back[MAX_FIELD]; char *desc, *keys, *bg; /* note first write uses ">" and creates the output. all subsequent writes must be >> to append */ fprintf(p->out, "%s\n\n\n", DOCTYPE, LANGUAGE); strip_tags(p->long_title, title); fprintf(p->out, " %s\n", title); fprintf(p->out, " \n", CONTENT_TYPE, CHARSET); if ((desc = p->description)) fprintf(p->out, " \n", desc); if ((keys = p->keywords)) fprintf(p->out, " \n", keys); fputs("\n", p->out); if (p->background) sprintf(back, "background=%s", p->background); else { if (ON(p, PAGE_INVERSE)) bg = INVERSE_BODY_COLOR; else if (!(bg = p->bg)) { if (r == root_ring) bg = BODY_COLOR; else if (p->style == PAGE_STYLE_BOOK) bg = r->bookbg; else bg = BODY_COLOR; p->bg = bg; } sprintf(back, "bgcolor=%s", bg); } fprintf(p->out, BODY_FORMAT, back, TEXT_COLOR, LINK_COLOR, VLINK_COLOR); } boolean write_navigation(page *p, page *up, ring *r) { char *theme, *bookpage, *font_color, *q; char pathbuf[MAX_LINE], t[MAX_FIELD]; int len, ri, hs; if (debug > 2) fprintf(stderr, "--> write_navigation(page=%s, up=%s, ring=%s)\n", p->h, up ? up->h : "NULL", r->path); switch (p->style) { case PAGE_STYLE_DEFAULT: fprintf(p->out, "
    \n", GENSITE_WIDTH); fputs(" \n", p->out); if (!(theme = p->theme)) if (!(theme = r->theme)) theme = THEME_COLOR; fprintf(p->out, " \n", p->out); fputs("
    \n", p->out); if (!homeup_navigation(p, up, r)) return FALSE; fputs(" \n", theme); strcpy(t, p->long_title); sub_smaller("·", "*", t); sub_smaller("&", "&", t); len = strlen(t); if (len > 75) hs = HEADING_SIZE - 1; else if (len > 55) hs = HEADING_SIZE; else if (len > 40) hs = HEADING_SIZE + 1; else if (len > 25) hs = HEADING_SIZE + 2; else hs = HEADING_SIZE + 3; if (p == sitemap) { fprintf(p->out, " " SITEMAP_LONG "
    " "%d pages and %d images in " "%d rings
    \n", GENSITE_FONT, hs, NOTE_SIZE, n_pages + 1, n_images, n_rings); } else if (p == index_page) { fprintf(p->out, " " INDEX_LONG "
    " "%d entries
    \n", GENSITE_FONT, hs, NOTE_SIZE, n_index); } else { fprintf(p->out, " out, " color=%s", font_color); fprintf(p->out, ">%s", p->long_title); *pathbuf = 0; page_path(p, p, pathbuf, 0); fprintf(p->out, "
    %s", pathbuf); fputs("
    \n", p->out); } fputs("
    \n", p->out); if (!ring_nextprev(p, r)) return FALSE; fputs("

    \n", p->out); break; case PAGE_STYLE_BOOK: fputs("
    \n", p->out); fputs("" "
    \n", p->out); if (!homeup_navigation(p, up, r)) return FALSE; fputs("

    \n", p->out); if (!ring_selector(p, p->ring, up, p->ringnav_size, p->ringnav_cols)) return FALSE; /* page wants addl ring navigators? */ if (p->n_ringnavs) for (ri = 0; ri < p->n_ringnavs; ri++) if (!ring_selector(p, p->ringnavs[ri], up, p->ringnavsize[ri], 0)) return FALSE; /* left page thumb? */ if (p->thumb_left) { if (!body_key_thumbn(p, up, p->thumb_left, 1)) return FALSE; if (p->thumb_left2) if (!body_key_thumbn(p, up, p->thumb_left2, 1)) return FALSE; } fputs("
    \n", p->out); if (!(bookpage = p->bookpage)) bookpage = r->bookpage; fprintf(p->out, "
     \n", BOOK_SEPARATION, bookpage, p->width, p->height); break; default: /* other styles do not get nevigation bar */ break; } if (debug > 2) fprintf(stderr, "<-- write_navigation(page=%s, up=%s, ring=%s)\n", p->h, up ? up->h : "NULL", r->path); return TRUE; } boolean ring_selector(page *p, ring *r, page *up, int size, int cols) { fputs("
    ", p->out); fprintf(p->out, "
    \n", p->bg, RINGSEL_PADDING); if (!ring_nextprev(p, r)) return FALSE; fputs("
    \n", p->out); if (!ring_navigation(p, r, up, STYLE_SELECT|STYLE_SHORT, size, cols, NULL)) return FALSE; fputs("
    \n", p->out); fputs("

    \n", p->out); return TRUE; } boolean ring_nextprev(page *p, ring *r) { list *members = &r->members; member *mm, *mp, *mn; page *pp, *pn; char *lr, *lr0, *pp_title, *pn_title, *sep; char imgbuf[MAX_FIELD], rp[MAX_FIELD], ip[MAX_FIELD], title[MAX_FIELD]; int ord, hs; if (debug > 2) fprintf(stderr, "ring_nextprev(p='%s', ring='%s')\n", p->h, r->path); if (p->ring == r) { if (debug > 3) printf(" p and r match; p's ord = %d\n", p->ord); mm = p->member; ord = p->ord; } else { if (!(mm = find_member(members, p, &ord))) { if (debug > 3) puts(" p not in r's members"); ord = 0; } } if (!ord && !single_page) { printf("***** ERROR: page '%s' is not a member of ring '%s'\n", p->h, r->path); return FALSE; } /* figures relative paths to root directory and its images subdir */ rel_path(p->h, IMAGES, ip); if (LIST_SIZE(members) > 1) { fprintf(p->out, " \n", ++p->nms); if (mm) { mp = LIST_HEAD(members); if (!(pp = mp->p)) pp = ring_firstpage(mp->r); sub_bigger("\"", """, pp->long_title, pp_title = title); rel_path(p->h, pp->html, rp); } else { pp_title = "Unknown"; strcpy(rp, "NO_LINK"); } fprintf(p->out, " \n", rp, pp_title, pp_title); if (mm) { mp = LIST_TAIL(members); if (!(pn = mp->p)) pn = ring_lastpage(mp->r); sub_bigger("\"", """, pn->long_title, pn_title = title); rel_path(p->h, pn->html, rp); } else { pn_title = "Unknown"; strcpy(rp, "NO_LINK"); } fprintf(p->out, " \n", rp, pn_title, pn_title); if (mm) { if (!(mp = LINK_PREV(mm))) mp = LIST_TAIL(members); if (!(pp = mp->p)) pp = ring_firstpage(mp->r); sub_bigger("\"", """, pp->long_title, pp_title = title); rel_path(p->h, pp->html, rp); } else { pp_title = "Unknown"; strcpy(rp, "NO_LINK"); } fprintf(p->out, " \n", rp, pp_title, pp_title); if (mm) { if (!(mn = LINK_NEXT(mm))) mn = LIST_HEAD(members); if (!(pn = mn->p)) pn = ring_firstpage(mn->r); sub_bigger("\"", """, pn->long_title, pn_title = title); rel_path(p->h, pn->html, rp); } else { pn_title = "Unknown"; strcpy(rp, "NO_LINK"); } fprintf(p->out, " \n", rp, pn_title, pn_title); fputs(" \n", p->out); lr = ON(p, PAGE_INVERSE) ? LRINV_GIF : LR_GIF; fputs(" ", p->out); sprintf(imgbuf, "Navigation", ip, lr, p->nms, NAVGIF_WIDTH, NAVGIF_HEIGHT); if (!write_link(rp, imgbuf, p)) return FALSE; } else /* nothing else in ring, just this one page, so grey out arrows */ { lr0 = ON(p, PAGE_INVERSE) ? LR0INV_GIF : LR0_GIF; fprintf(p->out, " Navigation\n", ip, lr0, NAVGIF_WIDTH, NAVGIF_HEIGHT); } hs = NOTE_SIZE; sep = ": "; if (strlen(r->short_title) > 10) { hs = NOTE_SIZE - 1; sep = "
    "; } fprintf(p->out, "
    \n %s%s", GENSITE_FONT, ON(p, PAGE_INVERSE) ? " color=white" : "", hs, r->short_title, sep); if (!ord) { printf("***** ERROR: page '%s' not in ring '%s'\n", p->h, r->path); return FALSE; } fprintf(p->out, "%d" " of %d\n", ord, LIST_SIZE(members)); return TRUE; } member *find_member(list *members, page *p, int *ord) { member *mm; page *mp; int o; for (o = 1, mm = LIST_HEAD(members); mm; mm = LINK_NEXT(mm), o++) { if (!(mp = mm->p)) mp = ring_firstpage(mm->r); if (mp == p) { *ord = o; return mm; } } *ord = 0; return NULL; } boolean ring_navigation(page *p, ring *r, page *up, unsigned style, int size, int cols, char *font) { int ci, rows, ri, i, mi, si; boolean tab; char rp[MAX_FIELD], t[MAX_FIELD]; member *m, *mc, *members[MAX_MEMBERS]; char *c, *d, *mark, *color, *title, *width; page *pm; ring *rm; if (debug > 2) fprintf(stderr, "--> ring_navigation(p='%s', r='%s', style=%u)\n", p->h, r->path, style); if (!size) { size = SELECTOR_SIZE; if (LIST_SIZE(&r->members) >= 20) size--; } if (!font) font = GENSITE_FIXED; /* scan members and form list of indexes (omit pages from long sects) */ for (m = LIST_HEAD(&r->members), i = mi = 0, si = -1; m; m = LINK_NEXT(m), i++) { if (!(pm = m->p)) pm = ring_firstpage(m->r); if (ON(pm, PAGE_SECTION)) /* reset for new section */ si = 0; if (si >= 0) /* in section? */ { ++si; if (si >= MAX_SECTION) { if (si == MAX_SECTION) /* mark omission */ members[mi++] = (member *) 0xDEADBEEF; continue; } } members[mi++] = m; } if (!cols && !(cols = r->cols)) /* automatic columnation? */ cols = ((mi - 1) / RINGNAV_ROWS) + 1; /* # of columns needed */ rows = mi / cols; /* rows per column */ if ((cols * rows) < mi) /* make sure rounding */ rows++; /* didn't lose anyone. */ if ((tab = !(style & STYLE_LI))) /* if not
  • , as table */ { if (style & STYLE_10OPCT) width = "width=\"100%\" "; else width=""; fprintf(p->out, "\n", width, r->long_title); } /* for each row */ for (m = members[0], i = ri = 0; ri < rows; m = members[++i], ri++) { if (tab) fputs("", p->out); /* for each column */ for (mc = m, ci = 0, si = i; mc && (ci < cols); ci++) { if (tab) fprintf(p->out, "\n" : "\n", p->out); if (cols > 1) { si += rows; mc = (si < mi) ? members[si] : NULL; } } if (tab) fputs("\n", p->out); } if (tab) fputs("
    \n", font, size); if (mc == (member *) 0xDEADBEEF) /* omission */ fputs(" ...", p->out); else { if ((pm = mc->p)) /* member is a page */ title = (style & STYLE_SHORT) ? pm->short_title : pm->long_title; else /* member is a ring */ { rm = mc->r; title = (style & STYLE_SHORT) ? rm->short_title : rm->long_title; pm = ring_firstpage(rm); } if (style & STYLE_SHORT) sub_bigger(" ", " ", title, t); else strcpy(t, title); if (cols > 3) { sub_smaller("Page ", "", t); sub_smaller("Front Cover", "FC", t); sub_smaller("Back Cover", "BC", t); sub_smaller("Front", "F", t); sub_smaller("Back", "B", t); sub_smaller("Gravure", "G", t); sub_smaller("Dust Jacket", "DJ", t); } /* page wants special treatment? */ if ((color = (pm == p) ? "red" : NULL)) /* current page? */ fprintf(p->out, "", color); if (tab) { mark = ON(pm, PAGE_SECTION) ? "§" : (style & STYLE_BULLET) ? "·" : "·"; fputs(mark, p->out); } else fprintf(p->out, "
  • \n", font, size); if (pm == p) fprintf(p->out, "%s", t); else { rel_path(p->h, pm->html, rp); if (tab && ON(pm, PAGE_SECTION)) fputs("", p->out); if (!write_link(rp, t, p)) return FALSE; if (tab && ON(pm, PAGE_SECTION)) fputs("", p->out); } c = pm->cite; d = pm->date; if ((c && (style & STYLE_CITE)) || (d && (style & STYLE_DATE))) { fputs(" ", p->out); if (c && (style & STYLE_CITE)) fprintf(p->out, " (%s)", c); if (d && (style & STYLE_DATE)) fprintf(p->out, " [%s]", d); fputs("", p->out); } if (color) fputs("", p->out); fputs("\n", p->out); } fputs(tab ? "
  • \n", p->out); if (debug > 2) fprintf(stderr, "<-- ring_navigation(p='%s', r='%s', style=%u)\n", p->h, r->path, style); return TRUE; } /* strip all */ void strip_tags(char *from, char *dest) { int c; while ((c = *from++)) { while (c == '<') /* start tag? */ { while ((c = *from++) != '>') ; /* skip to end */ c = *from++; /* check next char */ } *dest++ = c; /* save non-tag chars */ } *dest = 0; /* tie off dest buf */ } /* --------------------------------- image -------------------------------- */ /* return page pointer given its path. pages are identified by the full path to their .h file. if the named page already exists, it is just returned. otherwise, adds a new page. pages names are in the global hash table 'pages'. */ image *image_register(char *ref, char *image_path) { char pathbuf[MAX_PATH]; image *i; rel_expand(image_path, ref, pathbuf); if ((i = hash_lookup(images, pathbuf))) /* already exists? */ { if (debug > 4) fprintf(stderr, "image_register(ref='%s', image_path='%s') -> " "W=%d H=%d\n", ref, image_path, i->width, i->height); return i; } CALLOC(i, image); strcpy(i->path, pathbuf); /* "foo.jpg" etc */ /* ditto manifest */ add_to_manifest(pathbuf); hash_add(images, i->path, (void *) i); n_images++; /* gonna needta sniff the image eventually */ if (!(i->f = fopen(pathbuf, "rb"))) { printf("***** ERROR: failed to open image \"%s\"\n", pathbuf); perror("fopen"); return NULL; } if (has_extension(image_path, "gif")) { if (!gif_size(i)) { printf("***** ERROR: couldn't figure size of GIF \"%s\"\n", image_path); return NULL; } } else if (has_extension(image_path, "jpg")) { if (!jpg_size(i)) { printf("***** ERROR: couldn't figure size of JPEG \"%s\"\n", image_path); return NULL; } } else { printf("***** ERROR: unknown image type for \"%s\"\n", image_path); return NULL; } FCLOSE(i->f); return i; } boolean gif_size(image *i) { char gifbuf[6]; unsigned w1, w2, h1, h2; if ((fread(gifbuf, sizeof(char), 6, i->f) < 6) || (gifbuf[0] != 'G') || (gifbuf[1] != 'I') || (gifbuf[2] != 'F') || !isalnum(gifbuf[3]) || !isalnum(gifbuf[4]) || !isalnum(gifbuf[5])) { printf("***** ERROR: file \"%s\" is not a GIF\n", i->path); return FALSE; } w1 = getc(i->f); w2 = getc(i->f); i->width = (w2 << 8) + w1; h1 = getc(i->f); h2 = getc(i->f); i->height = (h2 << 8) + h1; return TRUE; } boolean jpg_size(image *i) { struct jpeg_decompress_struct jd; struct jpeg_error_mgr jerr; /* set up jpeg decompression */ jd.err = jpeg_std_error(&jerr); jpeg_create_decompress(&jd); jpeg_stdio_src(&jd, i->f); jpeg_read_header(&jd, TRUE); i->width = jd.image_width; i->height = jd.image_height; return TRUE; } /* image i [links -> to]

    page being created the image to be included if defined, image is a link to this rest of data: [img tags] */ boolean image_write(page *p, char *img, char *to, char *args, int n_thumb) { char *arg, *from, *rest, *colorname; image *id, *toi, *thumb; char alt[MAX_FIELD], alttmp[MAX_FIELD], params[MAX_FIELD]; char rp[MAX_PATH]; int n_tags, cmpnum; if (debug > 2) fprintf(stderr, "image_write(image=\"%s\", to=\"%s\", args=\"%s\")\n", img, STR(to), STR(args)); if (!args) { puts("***** ERROR: NULL args to image_write"); return FALSE; } /* if first arg is quoted string, it's a name for color comparison */ if (*args == '"') { for (colorname = ++args; *args != '"'; args++) ; *args++ = 0; } else colorname = NULL; /* skip over attr=value pairs until find beginning of alt text */ *params = 0; for (n_tags = 0, from = args; ((arg = next_arg(from, &rest))); from = rest) { if (strchr(arg, '=')) /* another "hspace=0" type tag? */ { if (n_tags++) /* count them up */ strcat(params, " "); strcat(params, arg); } else break; /* should be 1st word of alt */ } if (!arg) { printf("***** ERROR: page \"%s\" image \"%s\" has no alt\n", p->h, img); return FALSE; } strcpy(alttmp, arg); if (rest) { strcat(alttmp, " "); strcat(alttmp, rest); } sub_bigger("\"", """, alttmp, alt); /* replace " with " */ if (!(id = image_register(p->h, img))) return FALSE; if (colorname) { if (!n_thumb && !comparable(id, colorname)) return FALSE; strcat(alt, " in "); strcat(alt, colorname); } if (to) /* hyperlink image to ? */ { toi = NULL; /* to image */ if (memcmp(to, "http:", 5)) /* not an xlink? */ { if (has_extension(to, "gif") || has_extension(to, "jpg")) { if (!(toi = image_register(p->h, to))) return FALSE; if (colorname && n_thumb) if (!comparable(toi, colorname)) return FALSE; id->thumb_of = toi; /* id is a thumb of toi */ } else if (!has_extension(to, "html")) { printf("***** ERROR: bogus page \"%s\" link \"%s\"\n", p->h, to); return FALSE; } rel_path(p->h, toi ? toi->path : to, rp); to = rp; } fprintf(p->out, "", to); } rel_path(p->h, id->path, rp); fprintf(p->out, "width, id->height); if (*params) fprintf(p->out, " %s", params); fprintf(p->out, " alt=\"%s\"", alt); if ((cmpnum = id->cmpnum) || /* there a comparator number? */ ((thumb = id->thumb_of) && (cmpnum = thumb->cmpnum))) fprintf(p->out, " title=\"%s {%d}\"", alt, cmpnum); else fprintf(p->out, " title=\"%s\"", alt); putc('>', p->out); if (to) fputs("", p->out); return TRUE; } boolean comparable(image *i, char *colorname) { if (i->colorname) /* already have a color name? */ { if (strcmp(i->colorname, colorname)) /* new one better match! */ { printf("***** ERROR: image '%s' has color name '%s' " "*and* '%s'\n", i->path, i->colorname, colorname); return FALSE; } } else /* this is first naming for color */ { if (!single_page) image_rgb(i); new_colors = TRUE; i->colorname = save_string(colorname); LINK_TAIL(&cmplist, i) i->cmpnum = LIST_SIZE(&cmplist); } return TRUE; } boolean image_rgb(image *i) { char *error_string; if (!jpg_mean(i->path, NULL, &error_string, -1, -1, -1, &i->red, &i->green, &i->blue, NULL, NULL, (debug > 1))) { printf("Failed to calculate RGB average of '%s', error:", i->path); puts(error_string); return FALSE; } if (debug) fprintf(stderr, "RGB of '%s' is (%d,%d,%d)\n", i->path, i->red, i->green, i->blue); return TRUE; } /* --------------------------------- ring -------------------------------- */ /* register a new ring */ ring *ring_register(char *ref, char *ring_path, char *def_theme, boolean primary) { char ringbuf[MAX_PATH]; ring *r; rel_expand(ring_path, ref, ringbuf); if ((r = hash_lookup(rings, ringbuf))) /* already exists? */ { if (debug > 4) fprintf(stderr, "ring_register(ref='%s', ring_path='%s') -> '%s'\n", ref, ring_path, r->path); return r; } CALLOC(r, ring) if (debug > 2) fprintf(stderr, "new ring '%s' r='%p', def_theme=%s\n", ring_path, r, STR(def_theme)); strcpy(r->path, ringbuf); /* "foo.ring" */ r->width = GENSITE_WIDTH; r->height = GENSITE_HEIGHT; r->theme = def_theme; hash_add(rings, r->path, (void *) r); n_rings++; /*if (!single_page || (level == 1))*/ if (!ring_meta(r, primary)) return NULL; if (debug > 3) fprintf(stderr, "new ring '%s' title='%s'\n", ring_path, r->long_title); return r; } /* return first page of ring, drilling down as needed */ page *ring_firstpage(ring *r) { member *m1; while (r) { m1 = LIST_HEAD(&r->members); /* first member */ if (m1->p) /* if it's a page, good */ return m1->p; /* that's what we want */ r = m1->r; /* else must be a ring, try again */ } return NULL; } /* return last page of ring, drilling down as needed */ page *ring_lastpage(ring *r) { member *m1; while (r) { m1 = LIST_TAIL(&r->members); /* first member */ if (m1->p) /* if it's a page, good */ return m1->p; /* that's what we want */ r = m1->r; /* else must be a ring, try again */ } return NULL; } /* note we pass in ring here with the full extension, a complete filename, so we can read from it directly. */ boolean ring_process(ring *r, page *up) { char titlebuf[MAX_FIELD]; list *members; member *m; page *p; if (ON(r, RING_PROCESSED)) return TRUE; if (debug) fprintf(stderr, "Processing ring (%d) '%s'\n", level, r->path); if (loading) { /* see sitemap formatting */ strcpy(titlebuf, r->long_title); sub_smaller("", "", titlebuf); sub_smaller("", "", titlebuf); sub_smaller("", "", titlebuf); sub_smaller("", "", titlebuf); members = &r->members; switch (r->style) { case RING_STYLE_DEFAULT: if (r != root_ring) { fputs("

  • ", sitemap->out); if (level < 3) fprintf(sitemap->out, "", 3 - level); fprintf(sitemap->out, "%s: %d", titlebuf, LIST_SIZE(members)); if (level < 3) fputs("", sitemap->out); putc('\n', sitemap->out); fputs("
      \n", sitemap->out); } break; case RING_STYLE_BOOK: { #if EVERY_PAGE char shortname[MAX_FIELD]; char *title; fprintf(sitemap->out, "
    • %s", titlebuf, NOTE_SIZE); for (m = LIST_HEAD(members); m; m = LINK_NEXT(m)) { if (!(p = m->p)) p = ring_firstpage(m->r); putc('\n', sitemap->out); strip_tags(p->short_title, title = shortname); sub_smaller("Page ", "", shortname); sub_smaller("Pages ", "", shortname); sub_smaller("Front Cover", "FC", shortname); sub_smaller("I.F.Cover", "IFC",shortname); sub_smaller("Back Cover", "BC", shortname); sub_smaller("I.B.Cover", "IBC",shortname); sub_smaller("Inside", "I", shortname); sub_smaller("Drawing", "D", shortname); sub_smaller("Figures", "Fig", shortname); if (strstr(shortname, "Gravure")) { sub_smaller("Gravure", "G", shortname); sub_smaller("Front", "F", shortname); sub_smaller("Back", "B", shortname); } sub_smaller(" ", "", shortname); if (ON(p, PAGE_SECTION)) fputs("", sitemap->out); fprintf(sitemap->out, META_LINK_FORMAT, p->h, title); if (ON(p, PAGE_SECTION)) fputs("", sitemap->out); } fputs("\n", sitemap->out); #else m = LIST_HEAD(members); if (!(p = m->p)) p = ring_firstpage(m->r); fputs("
    • ", sitemap->out); fprintf(sitemap->out, META_LINK_FORMAT, p->h, titlebuf); fprintf(sitemap->out, ": %d\n", LIST_SIZE(members)); #endif } } } if (!up) { if (r != root_ring) { printf("***** ERROR: r='%s' has no up and not root.ring", r->path); return FALSE; } up = root; } /* process ring members */ if (!single_page) { for (m = (member *) LIST_HEAD(&r->members); m; m = LINK_NEXT(m)) if (!(m->p ? page_process(m->p, up, r, 0) : ring_process(m->r, up))) return FALSE; } if (loading) { if (r->style == RING_STYLE_DEFAULT) { if (r != root_ring) fputs("
    \n", sitemap->out); } } if (debug > 1) fprintf(stderr, "ring_process r='%s'\n", r->path); SET(r, RING_PROCESSED); return TRUE; } boolean ring_meta(ring *r, boolean primary) { char line[MAX_LINE]; char *rest, *key, *bar, *style, *e, *color, *mempath, *width, *height; list *members; member *m; page *p0; FILE *f; rstyle s; int len; if (!r) return FALSE; members = &r->members; if (LIST_SIZE(members)) /* already have ring metadata? */ return TRUE; if (debug > 2) fprintf(stderr, "--> ring_meta r='%s', primary=%s\n", r->path, primary ? "TRUE" : "FALSE"); if (!r->theme) r->theme = THEME_COLOR; r->height = GENSITE_HEIGHT; r->style = RING_STYLE_DEFAULT; if (!(f = fopen(r->path, "r"))) { fprintf(stderr, "failed to open ring file '%s'\n", r->path); return FALSE; } while (fgets(line, sizeof(line), f)) { if (*line == '#') /* comments start w/"#" */ continue; e = line + strlen(line) - 1; /* last char */ if (*e != '\n') { line[MAX_LINE - 1] = 0; printf("***** ERROR: input line too big: '%s'\n", line); return FALSE; } *e = 0; if (debug > 3) printf(" ring line: '%s'\n", line); key = next_arg(line, &rest); if (rest) while (isspace(*rest)) rest++; if (!strcmp(key, "@member")) /* most common key first */ { mempath = next_arg(rest, &rest); if (debug > 3) printf(" @member %s\n", mempath); CALLOC(m, member) /* if member is a ring, it means first member in that ring */ if (has_extension(mempath, "ring")) { if (!(m->r = ring_register(r->path,mempath,r->theme,primary))) return FALSE; } else if (has_extension(mempath, "h")) { if (!(p0 = page_register(r->path, mempath, r->theme, FALSE))) return FALSE; if (debug > 3) printf(" @member page is '%s'\n", p0->h); if (primary) /* if primary linkage */ { /* sanity check: make sure page isn't already linked up */ if (p0->ring) { printf("***** ERROR: page '%s' is in ring '%s'" " *and* '%s'\n", p0->h, r->path, p0->ring->path); return FALSE; } p0->ring = r; p0->member = m; p0->ord = LIST_SIZE(members) + 1; if (debug > 3) printf(" p0's ord is %d\n", p0->ord); } m->p = p0; } else { printf("***** ERROR: ring member '%s' not .ring or .h\n", mempath); return FALSE; } LINK_TAIL(members, m) } else if (!strcmp(key, "@title")) { if ((bar = strchr(rest, '|'))) /* "Full long title|Short" */ { len = bar - rest; strncpy(r->long_title, rest, len); r->long_title[len] = 0; strcpy(r->short_title, bar + 1); } else { strcpy(r->long_title, rest); strncpy(r->short_title, rest, MAX_SHORT_FIELD); next_arg(r->short_title, NULL); /* chop off first word */ } } else if (!strcmp(key, "@cite")) r->cite = save_string(rest); else if (!strcmp(key, "@color")) r->theme = save_string(rest); else if (!strcmp(key, "@width")) { if (!atoi(rest)) { printf("***** ERROR: Invalid ring width '%s'.\n", rest); return FALSE; } r->width = save_string(rest); } else if (!strcmp(key, "@height")) { if (!atoi(rest)) { printf("***** ERROR: Invalid ring height '%s'.\n", rest); return FALSE; } r->height = save_string(rest); } else if (!strcmp(key, "@cols")) r->cols = atoi(rest); else if (!strcmp(key, "@style")) { style = next_arg(rest, &rest); if (!strcmp(style, "book")) s = RING_STYLE_BOOK; else { printf("Unknown ring style '%s'\n", style); return FALSE; } switch (r->style = s) { case RING_STYLE_BOOK: r->bookbg = BODY_COLOR; r->bookpage = THEME_COLOR; if ((width = next_arg(rest, &rest))) { if (!atoi(width)) { printf("***** ERROR: Invalid ring width '%s'.\n", width); return FALSE; } r->width = save_string(width); if ((height = next_arg(rest, &rest))) { if (!atoi(height)) { printf("***** ERROR: Invalid ring " "height '%s'.\n", height); return FALSE; } r->height = save_string(height); if ((color = next_arg(rest, &rest))) { r->bookbg = save_string(color); if ((color = next_arg(rest, &rest))) r->bookpage = save_string(color); } } } break; default: /* no other arguments for rest */ break; } } else { printf("***** ERROR: unknown key '%s' in ring '%s'\n", key, r->path); return FALSE; } } fclose(f); if (!LIST_SIZE(&r->members)) { printf("***** ERROR: ring '%s' has no members\n", r->path); return FALSE; } return TRUE; } /* ---------------------------------- index --------------------------------- */ int generate_index() { list *l; entry *e, *last_e, *next_e; char *see, *text, *key; int li, lj, subs, eord, letter, repeat, dup; char dest[MAX_FIELD], seebuf[MAX_FIELD]; char letbuf[2], destbuf[3]; fprintf(index_page->out, "
    \n", INDEX_TITLE); letbuf[1] = destbuf[2] = 0; destbuf[0] = '#'; for (last_e = NULL, li = 0; li < INDEX_LETTERS; li++) { letter = 'A' + li; fprintf(index_page->out, "", letter); fprintf(index_page->out, "
    \n", INDEXBAR_PADDING, INDEXBAR_SPACING); fputs("\n", index_page->out); for (lj = 0; lj < INDEX_LETTERS; lj++) { letbuf[0] = destbuf[1] = 'A' + lj; if (li == lj) { fprintf(index_page->out, "\n", letbuf); } else { fprintf(index_page->out, "\n", index_page->out); } } fputs("
    " "out, " size=%d", INDEX_SIZE); #endif fprintf(index_page->out, ">%s", INDEX_COLOR); #if INDEX_SIZE != 3 fprintf(index_page->out, "", INDEX_SIZE); #endif fprintf(index_page->out, META_LINK_FORMAT, destbuf, letbuf); #if INDEX_SIZE != 3 fputs("", index_page->out); #endif fputs("
    \n\n", index_page->out); l = &entries[li]; if (!LIST_SIZE(l)) continue; fputs("
      \n", index_page->out); subs = eord = dup = 0; repeat = 1; for (e = (entry *) LIST_HEAD(l); e; last_e = e, e = next_e) { next_e = (entry *) LINK_NEXT(e); if (last_e && subs && strcmp(last_e->primary, e->primary)) { fputs("
    ", index_page->out); subs = 0; /* back to top level */ } if ((key = e->secondary)) /* key|subkey? */ { if (!subs++) /* first sub? */ { if (last_e && strcmp(last_e->primary, e->primary)) { fputs("
  • ", index_page->out); fputs(e->primary, index_page->out); } fputs("\n
      ", index_page->out); } } else key = e->primary; if (e->see) /* see also? */ { fputs("
    • ", index_page->out); fprintf(index_page->out, "%s, see ", key); if (!(see = find_index(e->see, seebuf))) { printf("***** ERROR: find_index \"%s\" failed\n", e->see); return FALSE; } fprintf(index_page->out, META_LINK_FORMAT, see, e->see); } else { sprintf(dest, "%s#%c%d", e->page->html, 'A' + li, e->num); if (e->main) fputs("", index_page->out); if (e->image) fputs("", index_page->out); if (dup) { fputs(" [", index_page->out); sprintf(text, "%d", ++repeat); fprintf(index_page->out, META_LINK_FORMAT, dest, text); fputc(']', index_page->out); } else { repeat = 1; fputs("
    • ", index_page->out); if (e->comment) /* () part? */ { fprintf(index_page->out, META_LINK_FORMAT, dest, key); fputs(" (", index_page->out); fputs(e->comment, index_page->out); fputc(')', index_page->out); } else fprintf(index_page->out, META_LINK_FORMAT, dest, key); } if (e->image) fputs("", index_page->out); if (e->main) fputs("", index_page->out); } if (!(dup = next_e && !strcmp(e->text, next_e->text))) fputs("
      \n", index_page->out); } if (subs) fputs("
    ", index_page->out); fputs("\n", index_page->out); } fputs("
  • \n", index_page->out); return TRUE; } char *find_index(char *text, char *dest) { list *l; entry *e; int li, eord; for (li = 0; li < INDEX_LETTERS; li++) { l = &entries[li]; for (e = (entry *) LIST_HEAD(l), eord = 0; e; e = (entry *) LINK_NEXT(e), eord++) { if (!strcmp(e->text, text)) { sprintf(dest, "%s#%c%d", e->page->html, 'A' + li, e->num); return dest; } } } return NULL; } /* ----------------------------- style mask ---------------------------- */ /* turn string "foo|bar|baz" into mask STYLE_FOO|STYLE_BAR|STYLE_BAZ */ unsigned style_mask(char *s) { char *next; unsigned m; for (m = 0; s; s = next) { /* if there's a |, split there and note remainder for next time */ if ((next = strchr(s, '|'))) *next++ = 0; if (!strcmp(s, "bullet")) m |= STYLE_BULLET; else if (!strcmp(s, "cite")) m |= STYLE_CITE; else if (!strcmp(s, "date")) m |= STYLE_DATE; else if (!strcmp(s, "li")) m |= STYLE_LI; else if (!strcmp(s, "nextprev")) m |= STYLE_NEXTPREV; else if (!strcmp(s, "sans")) m |= STYLE_SANS; else if (!strcmp(s, "select")) m |= STYLE_SELECT; else if (!strcmp(s, "short")) m |= STYLE_SHORT; else if (!strcmp(s, "100")) m |= STYLE_10OPCT; else { printf("***** ERROR: unknown style flag '%s'\n", s); return 0; } } return m; } /* ----------------------------- comparison list --------------------------- */ boolean load_cmplist(void) { FILE *f; image *i; char line[MAX_LINE], path[MAX_PATH], colorname[MAX_FIELD]; unsigned r, g, b; int n; if (!(f = fopen(CMP_LIST, "r"))) { printf("No cmplist '%s', creating...\n", CMP_LIST); return TRUE; } if (debug) printf("Reading image list '%s'...\n", CMP_LIST); while (fgets(line, sizeof(line), f)) { n = sscanf(line, "%s %2X%2X%2X %[^\n]\n", path, &r, &g, &b, colorname); if (n != 5) { printf("Bad cmplist line: %s\n", line); fclose(f); return FALSE; } if (!(i = image_register(NULL, path))) { fclose(f); return FALSE; } i->red = r; i->green = g; i->blue = b; i->colorname = save_string(colorname); LINK_TAIL(&cmplist, i) i->cmpnum = LIST_SIZE(&cmplist); } fclose(f); if (debug) printf("Loaded %d color-named images\n", (int) LIST_SIZE(&cmplist)); return TRUE; } boolean write_cmplist(void) { FILE *f; image *i; int n; if (!(f = fopen(CMP_LIST, "w"))) { printf("Failed to open images '%s'\n", CMP_LIST); return FALSE; } for (n = 0, i = LIST_HEAD(&cmplist); i; i = LINK_NEXT(i), n++) fprintf(f, "%s %02X%02X%02X %s\n", i->path, (unsigned) i->red, (unsigned) i->green, (unsigned) i->blue, i->colorname); fclose(f); if (debug) printf("Wrote %d color-named images\n", n); return TRUE; } /* ---------------------------------- path --------------------------------- */ /* case (1) given relative path "./arf.h" and reference path "foo/bar/baz.ring"... the '.' refers to ref's *directory*, so puts into buf: "foo/bar/arf.h" case (2) given relative path "../arf.h" and reference path "foo/bar/baz.ring"... the '..' refers to ref's *parent directory*, so puts into buf: "foo/arf.h" */ char *rel_expand(char *rel, char *ref, char *buf) { char *to, *slash, *last_slash; int c; if (debug > 3) fprintf(stderr, "rel_expand(rel='%s' ref='%s')\n", STR(rel), STR(ref)); if ((*rel == '.') && ref) /* relative to ref */ { if (rel[1] == '.') /* ../foo? */ { for (last_slash = slash = NULL, to = buf; ((c = *ref++)); ) { if ((*to++ = c) == '/') { last_slash = slash; slash = to; } } if (!last_slash) last_slash = buf; strcpy(last_slash, rel + 3); } else /* ref is "./foo" */ { for (slash = to = buf; ((c = *ref++)); ) if ((*to++ = c) == '/') slash = to; /* copy, noting last slash */ strcpy(slash, rel + 2); /* replace file part of ref */ } } else strcpy(buf, rel); /* not relative */ if (debug > 3) fprintf(stderr, " -> buf='%s'\n", buf); return buf; } /* split "a/b/foo.html" into "a", "b", "foo.html" and return 3 (#parts) */ int path_split(char *path, char *pathbuf, char *parts[]) { char *slash, *from; int n; strcpy(pathbuf, path); /* copy so can chop it up in place */ for (n = 0, from = pathbuf; ((slash = strchr(from, '/'))); n++, from = ++slash) { /* there's another slash & subpart */ *slash = 0; /* tie off part */ parts[n] = from; /* parts[] points to each piece */ } parts[n++] = from; /* final file is last part */ return n; /* return total # parts */ } /* given from="a/b/foo.gif" and to="a/c/page.html" yields "../c/page.html" */ char *rel_path(char *from, char *to, char *buf) { char frombuf[MAX_PATH], tobuf[MAX_PATH]; char *frompath[MAX_REL_DEPTH], *topath[MAX_REL_DEPTH]; int fl; /* 'from' levels */ int tl; /* 'to' levels */ int divl; /* divergence level */ int ul; /* up levels */ int dl; /* down levels */ int min_level; /* shortest depth */ int i; /* generic loop index */ fl = path_split(from, frombuf, frompath); /* split paths on slashes */ tl = path_split(to, tobuf, topath); *buf = 0; /* start off destination buffer */ min_level = (fl < tl) ? fl : tl; for (divl = 0, i = 0; i < min_level; i++) { if (strcmp(frompath[i], topath[i])) break; /* paths diverge here, else... */ divl++; /* still the same; divergence is higher */ } if ((ul = fl - divl - 1)) /* number of up levels */ for (i = 1; i <= ul; i++) strcat(buf, "../"); if ((dl = tl - divl - 1)) /* number of down levels */ { for (i = divl; dl-- > 0; i++) { strcat(buf, topath[i]); strcat(buf, "/"); } } strcat(buf, topath[tl - 1]); /* add final file component */ if (debug > 3) fprintf(stderr, " rel_path(from='%s', to='%s') = '%s'\n", from, to, buf); return buf; } void page_path(page *p, page *cur, char *pathbuf, int depth) { page *up; char rp[MAX_PATH]; if ((up = cur->up)) { if (depth >= MAX_PAGE_PATH) strcat(pathbuf, "... "); else { page_path(p, up, pathbuf, depth + 1); strcat(pathbuf, "  > "); } } if (depth) { strcat(pathbuf, "h, cur->html, rp); strcat(pathbuf, rp); strcat(pathbuf, "\">"); strcat(pathbuf, cur->short_title); strcat(pathbuf, ""); } else strcat(pathbuf, cur->short_title); } /* --------------------------------- syntax -------------------------------- */ char *next_arg(char *line, char **rest) { char *arg; int c; if (!line) return NULL; while (isspace(*line)) line++; /* skip leading white */ arg = line; /* start of arg */ while ((c = *line) && !isspace(c)) line++; /* skip to EOL or white */ if (line) *line++ = 0; /* tie off */ if (rest) /* if want rest of line */ *rest = c ? line : NULL; return arg; } char *lowercaseify(char *buf) { char *p; int c; for (p = buf; ((c = *p)); p++) if (isupper(c)) *p = tolower(c); return buf; } boolean has_extension(char *s, char *ext) { char *e = s + strlen(s) - 1; /* point to end of s */ int extlen = strlen(ext); for (e = s + strlen(s) - 1; (e >= s) && (*e != '.'); e--) ; /* back up to '.' */ return (*e == '.') && !memcmp(e + 1, ext, extlen) && (!e[extlen + 1] || (e[extlen + 1] == '#')); } char *save_string(char *s) { char *p; p = malloc(strlen(s) + 1); return strcpy(p, s); } /* sub "old" with "new" into newbuf since new is bigger than old */ char *sub_bigger(char *oldsub, char *newsub, char *buf, char *newbuf) { int newlen = strlen(newsub), oldlen = strlen(oldsub); int skip_len; char *to, *from, *p; from = buf; to = newbuf; while ((p = strstr(from, oldsub))) { if ((skip_len = p - from)) { memcpy(to, from, skip_len); to += skip_len; } memcpy(to, newsub, newlen); to += newlen; from = p + oldlen; } if (*from) /* leftover? */ strcpy(to, from); else *to = 0; /* tie off */ return newbuf; } /* sub "old" with "new" in place, since new is smaller (or same size) */ void sub_smaller(char *oldsub, char *newsub, char *buf) { int newlen = strlen(newsub), oldlen = strlen(oldsub); char *to, *from, *p, *old; to = from = buf; while ((p = strstr(from, oldsub))) { if (newlen) memcpy(p, newsub, newlen); from = to = p + newlen; old = p + oldlen; while ((*to++ = *old++)) ; } } /* ------------------------------- hash table ------------------------------ */ void *hash_lookup(hash h, char *key) { unsigned v; bucket *b; for (v = hash_of(key), b = h[v]; b; b = b->next) if (!strcmp(b->key, key)) return b->data; return NULL; } void hash_add(hash h, char *key, void *data) { unsigned v; bucket *b; v = hash_of(key); MALLOC(b, bucket); b->key = key; b->data = data; b->next = h[v]; h[v] = b; } unsigned hash_of(char *s) { unsigned v = 0; unsigned c; char *from; if ((from = s)) while ((c = (unsigned) *from++)) v = ((v << 4) ^ c) % MAX_HASH; if (debug > 4) fprintf(stderr, "hash_of('%s')=%u:%d\n", s, v, MAX_HASH); return v; } void hash_stats(hash h) { int i, len, len_total, n_chain; bucket *b; for (n_chain = len_total = i = 0; i < MAX_HASH; i++) { if ((b = (bucket *) h[i])) { n_chain++; /* something in this chain */ for (len = 0; b; b = (bucket *) b->next) len++; len_total += len; } } printf("%4d chains of %d = %2d%% full, ", n_chain, MAX_HASH, (n_chain * 100) / MAX_HASH); printf("avg = %g\n", ((float) len_total) / n_chain); } void hash_dump(hash h) { int i; bucket *b; for (i = 0; i < MAX_HASH; i++) { if ((b = (bucket *) h[i])) { printf("%04d: ", i); for (; b; b = (bucket *) b->next) printf("\"%s\" ", b->key); putchar('\n'); } } } /* $Id: gensite.c,v 1.96 2007/10/22 17:44:47 ian Exp ian $ */