├── md5.h ├── lib ├── xmlbase.xml ├── xmldir.xml ├── xmlfile.xml ├── no-index-entry ├── toplevel.html ├── dirlist.html ├── datelist.html └── index ├── subst.h ├── parse.h ├── read.h ├── output.h ├── testsubst.c ├── md5.c ├── hash.h ├── README.md ├── subst.c ├── main.c ├── read.c ├── hash.c ├── output.c └── parse.c /md5.h: -------------------------------------------------------------------------------- 1 | char* md5(char* pathname); 2 | -------------------------------------------------------------------------------- /lib/xmlbase.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {_dirs} 5 | 6 | 7 | -------------------------------------------------------------------------------- /subst.h: -------------------------------------------------------------------------------- 1 | 2 | typedef void (*substitution_fptr)(FILE *, void *); 3 | 4 | extern void substitute(char *body, hash_t *tab, void *subrock, FILE *outfl); 5 | -------------------------------------------------------------------------------- /parse.h: -------------------------------------------------------------------------------- 1 | 2 | extern int verbose; 3 | 4 | extern int parse_master(char *master, hash_t *dirlist, char *toplevelbody, 5 | char *treedir, char *indir, int doexclude, int domd5); 6 | -------------------------------------------------------------------------------- /read.h: -------------------------------------------------------------------------------- 1 | 2 | typedef struct pfile_struct { 3 | char *filename; 4 | hash_t *tab; 5 | long bodypos; 6 | } pfile_t; 7 | 8 | extern pfile_t *read_pfile(char *flnm); 9 | extern char *pfile_getbody(pfile_t *pf); 10 | extern char *file_getbody(char *flnm); 11 | extern hash_t *parse_list(char *str); 12 | -------------------------------------------------------------------------------- /lib/xmldir.xml: -------------------------------------------------------------------------------- 1 | 2 | {dir} 3 | {xdir} 4 | {?parentdir}{parentdir}{/} 5 | {?count}{count}{:}0{/} 6 | {?subdircount}{subdircount}{:}0{/} 7 | {?hasxmldesc}{?xmlheader} 8 | 9 | {xmlheader} 10 | {/}{/} 11 | 12 | 13 | {_files} 14 | -------------------------------------------------------------------------------- /output.h: -------------------------------------------------------------------------------- 1 | 2 | extern int check_indexes(hash_t *dirlist); 3 | 4 | extern int generate_datelist(char *outdir, pfile_t *plan, hash_t *dirlist, 5 | char *datelist_body); 6 | 7 | extern int generate_dirlist(char *outdir, pfile_t *plan, hash_t *dirlist, 8 | char *dirlist_body); 9 | 10 | extern int generate_xmllist(char *outdir, char *indir, pfile_t *plan, 11 | hash_t *dirlist, char *xmllist_body); 12 | 13 | extern int generate_output(char *outdir, pfile_t *plan, hash_t *dirlist); 14 | -------------------------------------------------------------------------------- /lib/xmlfile.xml: -------------------------------------------------------------------------------- 1 | 2 | {namexml} 3 | {dir} 4 | {dir}/{namexml} 5 | {?filesize}{filesize}{/} 6 | {?date}{datestr} 7 | {date} 8 | {/} 9 | {?md5}{md5} 10 | {/} 11 | {?islinkfile} 12 | {linkpath} 13 | 14 | {/} 15 | {?islinkdir} 16 | {linkdir} 17 | {xlinkdir} 18 | 19 | {/} 20 | {?hasxmldesc} 21 | {xmldesc}{/} 22 | 23 | -------------------------------------------------------------------------------- /testsubst.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hash.h" 3 | #include "read.h" 4 | #include "subst.h" 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | int ix; 9 | pfile_t *plan; 10 | char *body; 11 | 12 | if (argc != 2) { 13 | printf("Usage: %s subst.htmls\n", argv[0]); 14 | return -1; 15 | } 16 | 17 | plan = read_pfile(argv[1]); 18 | if (!plan) { 19 | printf("Cannot read %s.\n", argv[1]); 20 | return -1; 21 | } 22 | 23 | body = pfile_getbody(plan); 24 | substitute(body, plan->tab, NULL, stdout); 25 | 26 | return 0; 27 | } 28 | 29 | void show_warning2(char *msg, char *msg2) 30 | { 31 | if (msg2) 32 | printf("Problem: %s: %s\n", msg, msg2); 33 | else 34 | printf("Problem: %s\n", msg); 35 | } 36 | -------------------------------------------------------------------------------- /lib/no-index-entry: -------------------------------------------------------------------------------- 1 | if-archive/README 2 | if-archive/directory-tree 3 | if-archive/new-since-last-post 4 | if-archive/Master-Index 5 | if-archive/md5.txt 6 | if-archive/ls-lR 7 | if-archive/games/appleII/eamon 8 | if-archive/games/competition 9 | if-archive/info/archive-stats 10 | if-archive/info/ifdb 11 | if-archive/info/ifwiki 12 | if-archive/info/intficforum 13 | if-archive/infocom/patches 14 | if-archive/infocom/compilers/zilf/old 15 | if-archive/programming/glk/old 16 | if-archive/programming/glk/implementations/old 17 | if-archive/programming/glulx/interpreters/glulxe/old 18 | if-archive/programming/glulx/interpreters/quixe/old 19 | if-archive/programming/glulx/interpreters/git/old 20 | if-archive/rec.arts.int-fiction 21 | if-archive/rec.games.int-fiction 22 | if-archive/unprocessed 23 | -------------------------------------------------------------------------------- /md5.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static char input[65536]; 5 | static char hashtext[(EVP_MAX_MD_SIZE*2)+1]; 6 | 7 | char* md5(char* pathname) 8 | { 9 | FILE* file = NULL; 10 | EVP_MD_CTX *ctx = NULL; 11 | unsigned char output[EVP_MAX_MD_SIZE]; 12 | unsigned int outlen = 0; 13 | int i; 14 | 15 | file = fopen(pathname,"rb"); 16 | if (file == NULL) 17 | return ""; 18 | 19 | ctx = EVP_MD_CTX_create(); 20 | 21 | EVP_DigestInit(ctx,EVP_md5()); 22 | 23 | while ((i = fread(input,1,sizeof input,file)) > 0) 24 | EVP_DigestUpdate(ctx,input,i); 25 | fclose(file); 26 | 27 | EVP_DigestFinal(ctx,output,&outlen); 28 | 29 | EVP_MD_CTX_destroy(ctx); 30 | ctx = NULL; 31 | 32 | for (i = 0; i < outlen; i++) 33 | sprintf(hashtext+(i*2),"%02x",output[i]); 34 | return hashtext; 35 | } 36 | -------------------------------------------------------------------------------- /lib/toplevel.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

4 | You feel somewhat disoriented as you pass through... 5 |

6 |
7 | 8 |

9 | This is the Interactive Fiction Archive at 10 | www.ifarchive.org. 11 |

12 | 13 |

14 | You may be interested in... 15 |

16 | 17 | 23 | 24 |

25 | The 26 | list of all directories 27 | is a table of contents of the entire Archive, in order. 28 | The 29 | list of files, sorted by date 30 | is a (long) index of every file. 31 | You can also see the entries of the 32 | past week, 33 | past month, 34 | past three months, 35 | or 36 | past year. 37 |

38 | 39 |

40 | Or, go delving into the hierarchical tree shown here. 41 |

42 | 43 | -------------------------------------------------------------------------------- /lib/dirlist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Index of Directories 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |

Index of Directories

14 |
15 | 16 |
17 | 18 |

19 | About the IF-Archive. 20 |

21 | 22 | 29 | 30 |

31 | Enter the Archive 32 |

33 | 34 |
35 | 36 |
37 | 38 |
    39 | {_dirs} 40 |
41 | 42 |
43 | 44 | 58 | 59 |
60 | -------------------------------------------------------------------------------- /hash.h: -------------------------------------------------------------------------------- 1 | #ifndef HASH_H 2 | #define HASH_H 3 | 4 | #ifndef TRUE 5 | #define TRUE 1 6 | #endif 7 | #ifndef FALSE 8 | #define FALSE 0 9 | #endif 10 | #ifndef NULL 11 | #define NULL 0 12 | #endif 13 | 14 | extern void show_warning2(char *msg, char *msg2); 15 | #define show_warning(msg) (show_warning2((msg), NULL)) 16 | 17 | typedef struct hash_struct hash_t; 18 | typedef struct nodepair_struct { 19 | char *id; 20 | void *val; 21 | } nodepair_t; 22 | typedef int (*iteration_fptr)(char *, void *, void *); 23 | typedef int (*hashsort_fptr)(nodepair_t *, nodepair_t *); 24 | 25 | extern hash_t *new_hash(void); 26 | extern void delete_hash(hash_t *tab); 27 | extern void *hash_get(hash_t *tab, char *id); 28 | extern void hash_put(hash_t *tab, char *id, void *val); 29 | extern void hash_put_escurl(hash_t *tab, char *idesc, char *idurl, char *val); 30 | extern void hash_put_escxml(hash_t *tab, char *id, char *val); 31 | extern int hash_length(hash_t *tab); 32 | extern void hash_iterate_sort(hash_t *tab, iteration_fptr fn, 33 | hashsort_fptr sortfn, int parity, void *rock); 34 | extern nodepair_t *hash_sort(hash_t *tab, hashsort_fptr sortfn, int *len); 35 | extern void hash_iterate_array(nodepair_t *arr, int len, iteration_fptr fn, 36 | int parity, void *rock); 37 | extern void hash_iterate(hash_t *tab, iteration_fptr fn, void *rock); 38 | extern void hash_dump(hash_t *tab, int isstring); 39 | extern char *new_string(char *str); 40 | extern char *append_string(char *str, char *str2); 41 | extern char *append_string_esc(char *str, char *str2, int xml); 42 | #define new_string_esc(str) (append_string_esc(NULL, (str), 0)) 43 | #define new_string_esc_xml(str) (append_string_esc(NULL, (str), 1)) 44 | extern char *append_spaces(char *str, int numspaces); 45 | extern char *url_escape(char *str); 46 | extern int is_string_nonwhite(char *str); 47 | extern void trim_extra_newlines(char *str); 48 | 49 | #endif /* HASH_H */ 50 | -------------------------------------------------------------------------------- /lib/datelist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {?interval}Files by Date (past {interval}){:}All Files by Date{/} 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |

{?interval}Files by Date (past {interval}){:}All Files by Date{/}

14 |
15 | 16 |
17 | 18 |

19 | {?interval} 20 | This page lists the most recent {interval} of files in the Archive, 21 | sorted by date. 22 | {:} 23 | This page lists every file in the Archive, sorted by date. 24 | {/} 25 | (The last update of the file, not the date it first entered the 26 | Archive.) Note that this list is based on filesystem timestamps, 27 | which may be inaccurate -- or just plain wrong -- for various reasons. 28 | Please do not consider them infallible. 29 |

30 | 31 | 38 | 39 |

40 | About the IF-Archive. 41 |

42 | 43 |

44 | List of all directories. 45 |

46 | 47 |

48 | Enter the Archive 49 |

50 | 51 |
52 | 53 |
54 | 55 |
    56 | {_files} 57 |
58 | 59 |
60 | 61 | 75 | 76 |
77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ifmap -- the index generator tool for the IF Archive 2 | 3 | - Copyright 2017 by the Interactive Fiction Technology Foundation 4 | - Distributed under the MIT license 5 | - Created by Andrew Plotkin 6 | 7 | This program has one core task: to look through all the files in the IF Archive, combine that with the contents of the Master-Index file, and generate all the index.html files in the indexes subdirectory. 8 | 9 | (The Master-Index file is created by sewing together all the Index files in all the directories of the Archive. A different script does that job.) 10 | 11 | This version has been retired in favor of [ifarchive-ifmap-py][]. 12 | 13 | [ifarchive-ifmap-py]: https://github.com/iftechfoundation/ifarchive-ifmap-py 14 | 15 | ## Arguments 16 | 17 | In normal Archive operation, this is invoked from the build-indexes script. 18 | 19 | - -index FILE: pathname of Master-Index. (Normally /var/ifarchive/htdocs/if-archive/Master-Index.) 20 | - -src DIR: Pathname of the directory full of HTML templates which control the appearance of the index files. (Normally /var/ifarchive/lib/ifmap.) 21 | - -dest DIR: Pathname of the indexes directory, where the index files are written. (Normally /var/ifarchive/htdocs/indexes.) 22 | - -tree DIR: Pathname of the root directory which the Archive serves. (Normally /var/ifarchive/htdocs.) 23 | - -v: If set, print verbose output. 24 | - -xml: If set, also create a Master-Index.xml file (in the indexes directory) which includes all the known metadata. (Normally set.) 25 | - -exclude: If set, files without index entries are excluded from index listings. (Normally *not* set.) 26 | 27 | ## History 28 | 29 | I wrote the first version of this program in 1999-ish. It was built around the original Index files, which were hand-written by Volker Blasius (the original Archive curator) for human consumption. Their format was not particularly convenient for parsing, but I parsed them anyway. 30 | 31 | I wrote the program in C because it was portable and I didn't know Python or Perl yet. C is a terrible language for this sort of thing, of course -- I started by implementing my own hash tables. And escaping strings for HTML? Yuck. 32 | 33 | There are plenty of quirks and limitations which persist because C is too hard to update. Now that [ifarchive-ifmap-py][] exists, nobody has to. 34 | 35 | 36 | -------------------------------------------------------------------------------- /lib/index: -------------------------------------------------------------------------------- 1 | Top-Level-Template: toplevel.html 2 | XML-Template: xmlbase.xml 3 | XML-Dir-Template: xmldir.xml 4 | XML-File-Template: xmlfile.xml 5 | Dir-List-Template: dirlist.html 6 | Dir-List-Entry:
  • {dir} 7 | Date-List-Template: datelist.html 8 | Date-List-Entry:
  • [{datestr}] {dir}/{name} 9 | Subdir-List-Entry:
  • {dir} 10 | File-List-Entry:
    {name}{?date} [{datestr}]{/}
    {desc} 11 | 12 | 13 | 14 | 15 | 16 | Index: {dir} 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 |

    Index: {xdirlinks}

    25 |
    26 | 27 | {?parentdir} 28 | 37 | {/} 38 | 39 | {?header} 40 |
    41 | {header} 42 |
    43 |
    44 | {/} 45 | 46 |
    47 | 48 | {?subdircount} 49 |

    {subdircount} Subdirectories

    50 |
      51 | {_subdirs} 52 |
    53 | {/} 54 | 55 | {?count} 56 |

    {count} Items

    57 |
    58 | {_files} 59 |
    60 | {/} 61 | 62 |
    63 | 64 | 78 | 79 |
    80 | -------------------------------------------------------------------------------- /subst.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "hash.h" 5 | #include "subst.h" 6 | 7 | #define MAXDEPTH (16) 8 | 9 | void substitute(char *body, hash_t *tab, void *subrock, FILE *outfl) 10 | { 11 | char *cx, *bx; 12 | char ch; 13 | int activelist[MAXDEPTH]; 14 | int depth = 0; 15 | 16 | activelist[depth] = TRUE; 17 | 18 | for (cx = body; *cx; cx++) { 19 | ch = *cx; 20 | if (ch != '{') { 21 | if (activelist[depth]) 22 | putc(ch, outfl); 23 | } 24 | else { 25 | static char tagbuf[64]; 26 | int len; 27 | 28 | bx = cx; 29 | cx++; 30 | len = 0; 31 | while (*cx && *cx != '}' && *cx != '\n' && len < 63) { 32 | tagbuf[len] = *cx; 33 | cx++; 34 | len++; 35 | } 36 | tagbuf[len] = '\0'; 37 | if (*cx != '}') { 38 | show_warning2("brace-tag unclosed", tagbuf); 39 | } 40 | 41 | if (len == 0) { 42 | /* nothing */ 43 | } 44 | else if (tagbuf[0] == '{') { 45 | if (activelist[depth]) 46 | putc('{', outfl); 47 | } 48 | else if (tagbuf[0] == '?') { 49 | if (depth >= MAXDEPTH-1) { 50 | show_warning2("brace-tags nested too deep", tagbuf); 51 | break; 52 | } 53 | if (activelist[depth]) { 54 | depth++; 55 | activelist[depth] = (tab && hash_get(tab, tagbuf+1)); 56 | } 57 | else { 58 | depth++; 59 | activelist[depth] = FALSE; 60 | } 61 | } 62 | else if (tagbuf[0] == ':') { 63 | if (depth==0 || activelist[depth-1]) { 64 | activelist[depth] = !activelist[depth]; 65 | } 66 | } 67 | else if (tagbuf[0] == '/') { 68 | depth--; 69 | } 70 | else if (activelist[depth]) { 71 | void *val; 72 | if (tab) 73 | val = hash_get(tab, tagbuf); 74 | else 75 | val = NULL; 76 | 77 | if (!val) { 78 | fputs("[UNKNOWN]", outfl); 79 | show_warning2("undefined brace-tag", tagbuf); 80 | } 81 | else if (tagbuf[0] == '_') { 82 | substitution_fptr fptr = val; 83 | (*fptr)(outfl, subrock); 84 | } 85 | else if (tagbuf[0] == '@') { 86 | fputs("[NOT-PRINTABLE]", outfl); 87 | } 88 | else { 89 | fputs((char *)val, outfl); 90 | } 91 | } 92 | else { 93 | /* normal tag, but not active. */ 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "hash.h" 7 | #include "read.h" 8 | #include "output.h" 9 | #include "subst.h" 10 | #include "parse.h" 11 | 12 | static char *master = "Master-Index"; 13 | static char *indir = "lib"; 14 | static char *outdir = "out"; 15 | static char *treedir = NULL; 16 | static int doxml = FALSE; 17 | static int doexclude = FALSE; 18 | 19 | int verbose = FALSE; 20 | 21 | int main(int argc, char *argv[]) 22 | { 23 | int ix; 24 | char pathbuf[MAXPATHLEN]; 25 | pfile_t *plan; 26 | char *toplevel_body, *dirlist_body, *datelist_body, *xmllist_body; 27 | hash_t *dirlist; 28 | char *cx; 29 | 30 | for (ix=1; ixtab, "Top-Level-Template"); 76 | if (cx) { 77 | sprintf(pathbuf, "%s/%s", indir, cx); 78 | toplevel_body = file_getbody(pathbuf); 79 | if (!toplevel_body) { 80 | printf("Cannot find top-level template file. Using default.\n"); 81 | } 82 | } 83 | if (!toplevel_body) { 84 | toplevel_body = "Welcome to the archive.\n"; 85 | } 86 | 87 | dirlist_body = NULL; 88 | cx = hash_get(plan->tab, "Dir-List-Template"); 89 | if (cx) { 90 | sprintf(pathbuf, "%s/%s", indir, cx); 91 | dirlist_body = file_getbody(pathbuf); 92 | if (!dirlist_body) { 93 | printf("Cannot find dirlist template file. Using default.\n"); 94 | } 95 | } 96 | if (!dirlist_body) { 97 | dirlist_body = "\n{_dirs}\n" 98 | "\n"; 99 | } 100 | 101 | xmllist_body = NULL; 102 | cx = hash_get(plan->tab, "XML-Template"); 103 | if (cx) { 104 | sprintf(pathbuf, "%s/%s", indir, cx); 105 | xmllist_body = file_getbody(pathbuf); 106 | if (!xmllist_body) { 107 | printf("Cannot find xmllist template file. Using default.\n"); 108 | } 109 | } 110 | if (!xmllist_body) { 111 | xmllist_body = "\n{_dirs}\n" 112 | "\n"; 113 | } 114 | 115 | datelist_body = NULL; 116 | cx = hash_get(plan->tab, "Date-List-Template"); 117 | if (cx) { 118 | sprintf(pathbuf, "%s/%s", indir, cx); 119 | datelist_body = file_getbody(pathbuf); 120 | if (!datelist_body) { 121 | printf("Cannot find datelist template file. Using default.\n"); 122 | } 123 | } 124 | if (!datelist_body) { 125 | datelist_body = "\n{_files}\n" 126 | "\n"; 127 | } 128 | 129 | dirlist = new_hash(); 130 | hash_put(plan->tab, "@dirlist", dirlist); 131 | 132 | if (!parse_master(master, dirlist, toplevel_body, treedir, indir, doexclude, doxml)) 133 | return -1; 134 | 135 | if (!check_indexes(dirlist)) 136 | return -1; 137 | 138 | if (!generate_dirlist(outdir, plan, dirlist, dirlist_body)) 139 | return -1; 140 | 141 | if (!generate_output(outdir, plan, dirlist)) 142 | return -1; 143 | 144 | if (!generate_datelist(outdir, plan, dirlist, datelist_body)) 145 | return -1; 146 | 147 | if (doxml) { 148 | if (!generate_xmllist(outdir, indir, plan, dirlist, xmllist_body)) 149 | return -1; 150 | } 151 | 152 | /* printf("No problem.\n"); */ 153 | 154 | return 0; 155 | } 156 | 157 | void show_warning2(char *msg, char *msg2) 158 | { 159 | if (msg2) 160 | printf("Problem: %s: %s\n", msg, msg2); 161 | else 162 | printf("Problem: %s\n", msg); 163 | } 164 | -------------------------------------------------------------------------------- /read.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "hash.h" 5 | #include "read.h" 6 | 7 | static long buf_size = 0; 8 | static char *buffer = NULL; 9 | 10 | static int ensure_buffer(long size); 11 | 12 | static int ensure_buffer(long size) 13 | { 14 | if (size > buf_size) { 15 | if (!buffer) { 16 | buf_size = size + 1024; 17 | buffer = (char *)malloc(sizeof(char) * buf_size); 18 | if (!buffer) { 19 | show_warning("out of memory"); 20 | return FALSE; 21 | } 22 | } 23 | else { 24 | while (size > buf_size) 25 | buf_size *= 2; 26 | buffer = (char *)realloc(buffer, sizeof(char) * buf_size); 27 | if (!buffer) { 28 | show_warning("out of memory"); 29 | return FALSE; 30 | } 31 | } 32 | } 33 | 34 | return TRUE; 35 | } 36 | 37 | static char *trim_whitespace(char *buf) 38 | { 39 | long pos; 40 | 41 | while (*buf == ' ' || *buf == '\t') 42 | buf++; 43 | 44 | pos = strlen(buf); 45 | while (pos && (buf[pos-1] == ' ' || buf[pos-1] == '\t')) 46 | pos--; 47 | buf[pos] = '\0'; 48 | 49 | return buf; 50 | } 51 | 52 | pfile_t *read_pfile(char *filename) 53 | { 54 | pfile_t *pf; 55 | hash_t *tab; 56 | FILE *fl; 57 | long pos; 58 | int stopflag; 59 | 60 | pf = (pfile_t *)malloc(sizeof(pfile_t)); 61 | tab = new_hash(); 62 | if (!pf || !tab) { 63 | show_warning("out of memory"); 64 | return NULL; 65 | } 66 | 67 | pf->filename = new_string(filename); 68 | pf->tab = tab; 69 | pf->bodypos = -1; 70 | 71 | fl = fopen(filename, "r"); 72 | if (!fl) { 73 | perror(filename); 74 | show_warning("unable to open"); 75 | return NULL; 76 | } 77 | 78 | stopflag = 0; /* 1 for blank line, 2 for EOF */ 79 | 80 | while (!stopflag) { 81 | int ch; 82 | char *cx; 83 | 84 | pos = 0; 85 | 86 | while (ch=getc(fl), ch != EOF && ch != '\n') { 87 | if (!ensure_buffer(pos+2)) { /* allow space for closing null */ 88 | return NULL; 89 | } 90 | buffer[pos] = ch; 91 | pos++; 92 | } 93 | buffer[pos] = '\0'; 94 | 95 | if (ch == EOF) { 96 | stopflag = 2; 97 | } 98 | 99 | cx = trim_whitespace(buffer); 100 | 101 | if (*cx == '\0') { 102 | /* blank line */ 103 | if (!stopflag) 104 | stopflag = 1; 105 | } 106 | else { 107 | /* header line */ 108 | char *cx2 = strchr(cx, ':'); 109 | if (!cx2) { 110 | show_warning2("no colon in header line", cx); 111 | } 112 | else { 113 | *cx2 = '\0'; 114 | cx2++; 115 | cx = trim_whitespace(cx); 116 | cx2 = trim_whitespace(cx2); 117 | hash_put(tab, new_string(cx), new_string(cx2)); 118 | } 119 | } 120 | } 121 | 122 | if (stopflag == 1) { 123 | pf->bodypos = ftell(fl); 124 | } 125 | 126 | fclose(fl); 127 | 128 | /* 129 | printf("%s: bodypos = %d, header list:\n", pf->filename, pf->bodypos); 130 | hash_dump(tab, TRUE); 131 | */ 132 | 133 | return pf; 134 | } 135 | 136 | char *file_getbody(char *filename) 137 | { 138 | FILE *fl; 139 | long pos; 140 | int ch; 141 | char *cx; 142 | 143 | fl = fopen(filename, "r"); 144 | if (!fl) 145 | return NULL; 146 | 147 | pos = 0; 148 | while (ch=getc(fl), ch != EOF) { 149 | if (!ensure_buffer(pos+2)) { /* allow space for closing null */ 150 | return NULL; 151 | } 152 | buffer[pos] = ch; 153 | pos++; 154 | } 155 | buffer[pos] = '\0'; 156 | 157 | fclose(fl); 158 | 159 | return new_string(buffer); 160 | } 161 | 162 | char *pfile_getbody(pfile_t *pf) 163 | { 164 | FILE *fl; 165 | long pos; 166 | int ch; 167 | char *cx; 168 | 169 | if (pf->bodypos < 0) 170 | return NULL; 171 | 172 | fl = fopen(pf->filename, "r"); 173 | if (!fl) 174 | return NULL; 175 | 176 | fseek(fl, pf->bodypos, 0); 177 | 178 | pos = 0; 179 | while (ch=getc(fl), ch != EOF) { 180 | if (!ensure_buffer(pos+2)) { /* allow space for closing null */ 181 | return NULL; 182 | } 183 | buffer[pos] = ch; 184 | pos++; 185 | } 186 | buffer[pos] = '\0'; 187 | 188 | fclose(fl); 189 | 190 | return new_string(buffer); 191 | } 192 | 193 | hash_t *parse_list(char *str) 194 | { 195 | char *cx, *bx; 196 | char numbuf[16]; 197 | int counter = 0; 198 | hash_t *tab = new_hash(); 199 | if (!tab) 200 | return NULL; 201 | 202 | str = new_string(str); /* for diddling */ 203 | 204 | cx = str; 205 | 206 | while (*cx) { 207 | for (bx=cx; *cx && *cx != ','; cx++) { } 208 | if (*cx) { 209 | *cx = '\0'; 210 | cx++; 211 | } 212 | 213 | bx = trim_whitespace(bx); 214 | sprintf(numbuf, "%d", counter); 215 | counter++; 216 | hash_put(tab, new_string(bx), new_string(numbuf)); 217 | } 218 | 219 | free(str); 220 | 221 | return tab; 222 | } 223 | 224 | -------------------------------------------------------------------------------- /hash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "hash.h" 5 | 6 | #define HASHSIZE (31) 7 | 8 | typedef struct hashnode_struct { 9 | char *id; 10 | void *val; 11 | struct hashnode_struct *next; 12 | } hashnode_t; 13 | 14 | struct hash_struct { 15 | hashnode_t *bucket[HASHSIZE]; 16 | }; 17 | 18 | hash_t *new_hash() 19 | { 20 | int ix; 21 | hash_t *tab = (hash_t *)malloc(sizeof(hash_t)); 22 | if (!tab) 23 | return NULL; 24 | for (ix=0; ixbucket[ix] = NULL; 26 | return tab; 27 | } 28 | 29 | void delete_hash(hash_t *tab) 30 | { 31 | int buck; 32 | hashnode_t *nod, *nod2; 33 | 34 | for (buck = 0; buck < HASHSIZE; buck++) { 35 | nod = tab->bucket[buck]; 36 | while (nod) { 37 | nod2 = nod->next; 38 | free(nod); 39 | nod = nod2; 40 | } 41 | tab->bucket[buck] = NULL; 42 | } 43 | 44 | free(tab); 45 | } 46 | 47 | static int string_to_key(char *id) 48 | { 49 | int res = 0; 50 | int ix = 0; 51 | unsigned char ch; 52 | 53 | while (*id) { 54 | ch = (*id); 55 | res += (ch << (ix & 7)); 56 | id++; 57 | ix++; 58 | } 59 | return (res % HASHSIZE); 60 | } 61 | 62 | void *hash_get(hash_t *tab, char *id) 63 | { 64 | int buck = string_to_key(id); 65 | hashnode_t *nod; 66 | for (nod = tab->bucket[buck]; nod; nod = nod->next) { 67 | if (!strcmp(id, nod->id)) 68 | return nod->val; 69 | } 70 | return NULL; 71 | } 72 | 73 | void hash_put(hash_t *tab, char *id, void *val) 74 | { 75 | int buck = string_to_key(id); 76 | hashnode_t *nod; 77 | 78 | for (nod = tab->bucket[buck]; nod; nod = nod->next) { 79 | if (!strcmp(id, nod->id)) { 80 | nod->val = val; 81 | return; 82 | } 83 | } 84 | 85 | nod = (hashnode_t *)malloc(sizeof(hashnode_t)); 86 | if (!nod) 87 | return; 88 | nod->id = id; 89 | nod->val = val; 90 | nod->next = tab->bucket[buck]; 91 | tab->bucket[buck] = nod; 92 | } 93 | 94 | void hash_put_escurl(hash_t *tab, char *idesc, char *idurl, char *val) 95 | { 96 | char *urlval = url_escape(val); 97 | if (urlval == val) { 98 | char *esc = new_string_esc(val); 99 | hash_put(tab, idesc, esc); 100 | hash_put(tab, idurl, esc); 101 | } 102 | else { 103 | char *esc = new_string_esc(val); 104 | hash_put(tab, idesc, esc); 105 | esc = new_string_esc(urlval); 106 | hash_put(tab, idurl, esc); 107 | } 108 | } 109 | 110 | void hash_put_escxml(hash_t *tab, char *id, char *val) 111 | { 112 | int len = 0; 113 | int newlen = 0; 114 | char *cx, *cx2, *str; 115 | for (cx=val; *cx; cx++) { 116 | switch (*cx) { 117 | case '&': 118 | newlen += 5; 119 | break; 120 | case '<': 121 | case '>': 122 | newlen += 4; 123 | break; 124 | default: 125 | newlen += 1; 126 | break; 127 | } 128 | len += 1; 129 | } 130 | if (len == newlen) { 131 | hash_put(tab, id, val); 132 | return; 133 | } 134 | str = (char *)malloc((1 + newlen) * sizeof(char)); 135 | cx2 = str; 136 | for (cx=val; *cx; cx++) { 137 | switch (*cx) { 138 | case '&': 139 | strcpy(cx2, "&"); 140 | cx2 += 5; 141 | break; 142 | case '<': 143 | strcpy(cx2, "<"); 144 | cx2 += 4; 145 | break; 146 | case '>': 147 | strcpy(cx2, ">"); 148 | cx2 += 4; 149 | break; 150 | default: 151 | *cx2 = *cx; 152 | cx2++; 153 | break; 154 | } 155 | } 156 | *cx2 = '\0'; 157 | hash_put(tab, id, str); 158 | } 159 | 160 | int hash_length(hash_t *tab) 161 | { 162 | int ix; 163 | int buck; 164 | hashnode_t *nod; 165 | 166 | ix = 0; 167 | for (buck = 0; buck < HASHSIZE; buck++) { 168 | for (nod = tab->bucket[buck]; nod; nod = nod->next) { 169 | ix++; 170 | } 171 | } 172 | 173 | return ix; 174 | } 175 | 176 | void hash_iterate(hash_t *tab, iteration_fptr fn, void *rock) 177 | { 178 | int buck; 179 | hashnode_t *nod; 180 | int res; 181 | 182 | for (buck = 0; buck < HASHSIZE; buck++) { 183 | for (nod = tab->bucket[buck]; nod; nod = nod->next) { 184 | res = fn(nod->id, nod->val, rock); 185 | if (res) 186 | return; 187 | } 188 | } 189 | } 190 | 191 | nodepair_t *hash_sort(hash_t *tab, hashsort_fptr sortfn, int *len) 192 | { 193 | int buck; 194 | hashnode_t *nod; 195 | nodepair_t *arr; 196 | int ix, count, res; 197 | 198 | *len = 0; 199 | 200 | ix = 0; 201 | for (buck = 0; buck < HASHSIZE; buck++) { 202 | for (nod = tab->bucket[buck]; nod; nod = nod->next) { 203 | ix++; 204 | } 205 | } 206 | count = ix; 207 | 208 | if (count <= 0) 209 | return NULL; 210 | 211 | arr = (nodepair_t *)malloc(count * sizeof(nodepair_t)); 212 | if (!arr) { 213 | return NULL; 214 | } 215 | 216 | ix = 0; 217 | for (buck = 0; buck < HASHSIZE; buck++) { 218 | for (nod = tab->bucket[buck]; nod; nod = nod->next) { 219 | arr[ix].id = nod->id; 220 | arr[ix].val = nod->val; 221 | ix++; 222 | } 223 | } 224 | 225 | qsort(arr, count, sizeof(nodepair_t), (void *)sortfn); 226 | 227 | *len = count; 228 | return arr; 229 | } 230 | 231 | void hash_iterate_array(nodepair_t *arr, int len, iteration_fptr fn, 232 | int parity, void *rock) 233 | { 234 | int ix, res; 235 | 236 | for (ix=0; ixbucket[buck]; nod; nod = nod->next) { 273 | printf(formstr, nod->id, nod->val); 274 | } 275 | } 276 | } 277 | 278 | int is_string_nonwhite(char *str) 279 | { 280 | char *cx; 281 | 282 | if (!str) 283 | return FALSE; 284 | 285 | for (cx=str; *cx; cx++) { 286 | if (!(*cx == ' ' || *cx == '\n' || *cx == '\t')) 287 | return TRUE; 288 | } 289 | 290 | return FALSE; 291 | } 292 | 293 | void trim_extra_newlines(char *str) 294 | { 295 | int len; 296 | 297 | len = strlen(str); 298 | while (len >= 2 && str[len-1] == '\n' && str[len-2] == '\n') { 299 | str[len-1] = '\0'; 300 | len--; 301 | } 302 | } 303 | 304 | char *new_string(char *str) 305 | { 306 | char *res; 307 | if (!str) 308 | return NULL; 309 | res = (char *)malloc((1 + strlen(str)) * sizeof(char)); 310 | if (!res) 311 | return NULL; 312 | strcpy(res, str); 313 | return res; 314 | } 315 | 316 | char *append_spaces(char *str, int numspaces) 317 | { 318 | int ix, pos; 319 | if (!str) 320 | str = new_string(""); 321 | if (numspaces < 0) 322 | numspaces = 0; 323 | pos = strlen(str); 324 | str = (char *)realloc(str, (1 + pos + numspaces) * sizeof(char)); 325 | for (ix=0; ix'); 351 | if (cx != NULL) { 352 | return cx-str; 353 | } 354 | } 355 | return 0; 356 | } 357 | 358 | #define IS_PRINTABLE(ch) \ 359 | (((ch) >= ' ' && (ch) <= '~') || (ch) == '\n') 360 | 361 | char *append_string_esc(char *str, char *str2, int xml) 362 | { 363 | /* str2 is effectively const */ 364 | int oldlen, newlen, urllen, esccount; 365 | char *cx, *cx2; 366 | if (!str2) 367 | return str; 368 | 369 | esccount = 0; 370 | newlen = 0; 371 | for (cx=str2; *cx; cx++) { 372 | switch (*cx) { 373 | case '<': 374 | urllen = url_length(cx+1); 375 | if (urllen > 0) { 376 | cx += urllen+1; /* */ 377 | newlen += (2*urllen)+15; /* url */ 378 | } else { 379 | newlen += 4; 380 | } 381 | esccount++; 382 | break; 383 | case '>': 384 | newlen += 4; 385 | esccount++; 386 | break; 387 | case '&': 388 | if (xml) { 389 | newlen += 5; 390 | esccount++; 391 | break; 392 | } 393 | default: 394 | if (IS_PRINTABLE(*cx)) { 395 | newlen++; 396 | } 397 | else { 398 | printf("Warning: non-printable character %d\n",(int)(unsigned char)*cx); 399 | newlen += 6; 400 | esccount++; 401 | } 402 | break; 403 | } 404 | } 405 | 406 | if (!esccount) 407 | return append_string(str, str2); 408 | 409 | if (!str) { 410 | str = (char *)malloc((1 + newlen) * sizeof(char)); 411 | cx2 = str; 412 | } 413 | else { 414 | oldlen = strlen(str); 415 | str = (char *)realloc(str, (1 + oldlen + newlen) * sizeof(char)); 416 | cx2 = str + oldlen; 417 | } 418 | 419 | for (cx=str2; *cx; cx++) { 420 | switch (*cx) { 421 | case '<': 422 | urllen = url_length(cx+1); 423 | if (urllen > 0) { 424 | sprintf(cx2,"%.*s",urllen,cx+1,urllen,cx+1); 425 | cx2 += (2*urllen)+15; 426 | cx += urllen+1; 427 | } else { 428 | strcpy(cx2, "<"); 429 | cx2 += 4; 430 | } 431 | break; 432 | case '>': 433 | strcpy(cx2, ">"); 434 | cx2 += 4; 435 | break; 436 | case '&': 437 | if (xml) { 438 | strcpy(cx2, "&"); 439 | cx2 += 5; 440 | break; 441 | } 442 | default: 443 | if (IS_PRINTABLE(*cx)) { 444 | *cx2 = *cx; 445 | cx2++; 446 | } 447 | else { 448 | sprintf(cx2, "&#x%02X;", (*cx) & 0xFF); 449 | cx2 += 6; 450 | } 451 | break; 452 | } 453 | } 454 | *cx2 = '\0'; 455 | 456 | return str; 457 | } 458 | 459 | #define IS_URLABLE(ch) \ 460 | (((ch) > '*' && (ch) <= ';') || ((ch) >= '@' && (ch) <= 'z')) 461 | 462 | char *url_escape(char *str) 463 | { 464 | int esccount, newlen, oldlen; 465 | char *cx, *cx2, *newstr; 466 | 467 | newlen = 0; 468 | esccount = 0; 469 | for (cx=str; *cx; cx++) { 470 | if (IS_URLABLE(*cx)) { 471 | newlen += 1; 472 | } 473 | else { 474 | esccount += 1; 475 | newlen += 3; 476 | } 477 | } 478 | 479 | if (!esccount) 480 | return str; 481 | 482 | newstr = (char *)malloc((1 + newlen) * sizeof(char)); 483 | for (cx=str, cx2=newstr; *cx; cx++) { 484 | if (IS_URLABLE(*cx)) { 485 | *cx2 = *cx; 486 | cx2++; 487 | } 488 | else { 489 | *cx2 = '%'; 490 | cx2++; 491 | sprintf(cx2, "%02X", (*cx) & 0xFF); 492 | cx2 += 2; 493 | } 494 | } 495 | *cx2 = '\0'; 496 | cx2++; 497 | 498 | if (newstr+newlen+1 != cx2) 499 | printf("Warning: url_escape generated wrong length.\n"); 500 | 501 | return newstr; 502 | } 503 | -------------------------------------------------------------------------------- /output.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "hash.h" 10 | #include "read.h" 11 | #include "output.h" 12 | #include "subst.h" 13 | 14 | typedef struct contextgen_struct { 15 | char *outdir; 16 | pfile_t *plan; 17 | char *elstr; 18 | } contextgen_t; 19 | 20 | typedef struct contextfil_struct { 21 | pfile_t *plan; 22 | hash_t *filelist; 23 | hash_t *subdirlist; 24 | } contextfil_t; 25 | 26 | typedef struct contextfilf_struct { 27 | FILE *outfl; 28 | char *elstr; 29 | contextfil_t *con; 30 | } contextfilf_t; 31 | 32 | typedef struct contextdir_struct { 33 | char *outdir; 34 | pfile_t *plan; 35 | hash_t *dirlist; 36 | } contextdir_t; 37 | 38 | typedef struct contextdire_struct { 39 | FILE *outfl; 40 | char *elstr; 41 | contextdir_t *con; 42 | } contextdire_t; 43 | 44 | typedef struct contextxml_struct { 45 | char *outdir; 46 | pfile_t *plan; 47 | hash_t *dirlist; 48 | char *dirbody; 49 | char *filebody; 50 | } contextxml_t; 51 | 52 | typedef struct contextxmle_struct { 53 | FILE *outfl; 54 | char *elstr; 55 | contextxml_t *con; 56 | } contextxmle_t; 57 | 58 | typedef struct contextxmled_struct { 59 | hash_t *filelist; 60 | contextxmle_t *con; 61 | } contextxmled_t; 62 | 63 | typedef struct contextxmledel_struct { 64 | FILE *outfl; 65 | char *elstr; 66 | contextxmled_t *con; 67 | } contextxmledel_t; 68 | 69 | typedef struct contextdat_struct { 70 | hash_t *filelist; 71 | pfile_t *plan; 72 | int count; 73 | nodepair_t *arr; 74 | int arrlen; 75 | time_t interval; 76 | time_t curtime; 77 | } contextdat_t; 78 | 79 | typedef struct contextdate_struct { 80 | FILE *outfl; 81 | char *elstr; 82 | contextdat_t *con; 83 | } contextdate_t; 84 | 85 | static int ensure_dir(char *path); 86 | static int check_thunk(char *id, void *dirptr, void *rock); 87 | static int checkel_thunk(char *id, void *dirptr, void *rock); 88 | static int generate_thunk(char *id, void *dirptr, void *rock); 89 | static void dirlist_thunk(FILE *outfl, void *rock); 90 | static void filelist_thunk(FILE *outfl, void *rock); 91 | static void subdirlist_thunk(FILE *outfl, void *rock); 92 | static int dirlistel_thunk(char *id, void *memptr, void *rock); 93 | static int filelistel_thunk(char *id, void *memptr, void *rock); 94 | static int subdirlistel_thunk(char *id, void *memptr, void *rock); 95 | static int datelist_thunk(char *id, void *dirptr, void *rock); 96 | static int datelistel_thunk(char *id, void *dirptr, void *rock); 97 | static void datelistfiles_thunk(FILE *outfl, void *rock); 98 | static int datelistfilesel_thunk(char *id, void *filptr, void *rock); 99 | static void xmllist_thunk(FILE *outfl, void *rock); 100 | static int xmllistel_thunk(char *id, void *memptr, void *rock); 101 | static void xmllistdir_thunk(FILE *outfl, void *rock); 102 | static int xmllistdirel_thunk(char *id, void *fileptr, void *rock); 103 | 104 | static int sort_dirs(nodepair_t *v1, nodepair_t *v2); 105 | static int sort_files(nodepair_t *v1, nodepair_t *v2); 106 | static int sort_filedates(nodepair_t *v1, nodepair_t *v2); 107 | 108 | int check_indexes(hash_t *dirlist) 109 | { 110 | hash_iterate(dirlist, &check_thunk, NULL); 111 | 112 | return TRUE; 113 | } 114 | 115 | static int check_thunk(char *id, void *dirptr, void *rock) 116 | { 117 | hash_t *dir = dirptr; 118 | hash_t *filelist = hash_get(dir, "@files"); 119 | 120 | if (filelist) { 121 | hash_iterate(filelist, checkel_thunk, NULL); 122 | } 123 | 124 | return FALSE; 125 | } 126 | 127 | static int checkel_thunk(char *id, void *fileptr, void *rock) 128 | { 129 | hash_t *file = fileptr; 130 | 131 | if (hash_get(file, "date") == 0 && hash_get(file, "xlinkdir") == 0 && hash_get(file, "islink") == 0) { 132 | fprintf(stderr,"Index entry without file: %s/%s\n", (char *)hash_get(file, "dir"), 133 | (char *)hash_get(file, "name")); 134 | } 135 | 136 | return FALSE; 137 | } 138 | 139 | int generate_output(char *outdir, pfile_t *plan, hash_t *dirlist) 140 | { 141 | contextgen_t con; 142 | con.outdir = outdir; 143 | con.plan = plan; 144 | con.elstr = pfile_getbody(plan); 145 | 146 | hash_iterate(dirlist, &generate_thunk, &con); 147 | 148 | free(con.elstr); 149 | 150 | return TRUE; 151 | } 152 | 153 | static int generate_thunk(char *id, void *dirptr, void *rock) 154 | { 155 | contextgen_t *con = rock; 156 | contextfil_t conm; 157 | hash_t *dir = dirptr; 158 | char pathbuf[MAXPATHLEN]; 159 | FILE *fl; 160 | 161 | sprintf(pathbuf, "%s/%s.html", con->outdir, 162 | (char *)hash_get(dir, "xdir")); 163 | fl = fopen(pathbuf, "w"); 164 | if (!fl) { 165 | perror(pathbuf); 166 | printf("Cannot create index file. Goodbye.\n"); 167 | return FALSE; 168 | } 169 | 170 | conm.filelist = hash_get(dir, "@files"); 171 | conm.subdirlist = hash_get(dir, "@subdirs"); 172 | conm.plan = con->plan; 173 | 174 | hash_put(dir, "_files", &filelist_thunk); 175 | hash_put(dir, "_subdirs", &subdirlist_thunk); 176 | substitute(con->elstr, dir, &conm, fl); 177 | 178 | fclose(fl); 179 | 180 | return FALSE; 181 | } 182 | 183 | static void filelist_thunk(FILE *outfl, void *rock) 184 | { 185 | contextfil_t *con = rock; 186 | contextfilf_t conm; 187 | 188 | conm.con = con; 189 | conm.outfl = outfl; 190 | conm.elstr = hash_get(con->plan->tab, "File-List-Entry"); 191 | if (!conm.elstr) 192 | conm.elstr = "
  • {name}\n{desc}"; 193 | 194 | hash_iterate_sort(con->filelist, &filelistel_thunk, sort_files, TRUE, &conm); 195 | } 196 | 197 | static int filelistel_thunk(char *id, void *fileptr, void *rock) 198 | { 199 | contextfilf_t *conm = rock; 200 | hash_t *file = fileptr; 201 | 202 | substitute(conm->elstr, file, NULL, conm->outfl); 203 | fprintf(conm->outfl, "\n"); 204 | 205 | return FALSE; 206 | } 207 | 208 | static void subdirlist_thunk(FILE *outfl, void *rock) 209 | { 210 | contextfil_t *con = rock; 211 | contextfilf_t conm; 212 | 213 | conm.con = con; 214 | conm.outfl = outfl; 215 | conm.elstr = hash_get(con->plan->tab, "Subdir-List-Entry"); 216 | if (!conm.elstr) 217 | conm.elstr = "
  • {dir}"; 218 | 219 | hash_iterate_sort(con->subdirlist, &subdirlistel_thunk, sort_dirs, TRUE, &conm); 220 | } 221 | 222 | static int subdirlistel_thunk(char *id, void *dirptr, void *rock) 223 | { 224 | contextfilf_t *conm = rock; 225 | hash_t *dir = dirptr; 226 | 227 | substitute(conm->elstr, dir, NULL, conm->outfl); 228 | fprintf(conm->outfl, "\n"); 229 | 230 | return FALSE; 231 | } 232 | 233 | #define NUMINTERVALS (5) 234 | 235 | int generate_datelist(char *outdir, pfile_t *plan, hash_t *dirlist, 236 | char *body) 237 | { 238 | contextdat_t con; 239 | char pathbuf[MAXPATHLEN]; 240 | int interval; 241 | static time_t intlist[NUMINTERVALS] = { 242 | 0, 243 | 7*24*60*60, 244 | 31*24*60*60, 245 | 93*24*60*60, 246 | 366*24*60*60 247 | }; 248 | static char *intnamelist[NUMINTERVALS] = { 249 | NULL, "week", "month", "three months", "year" 250 | }; 251 | FILE *fl; 252 | 253 | con.filelist = new_hash(); 254 | con.count = 0; 255 | con.plan = plan; 256 | con.curtime = time(NULL); 257 | 258 | hash_iterate(dirlist, datelist_thunk, &con); 259 | 260 | if (!ensure_dir(outdir)) { 261 | printf("Goodbye.\n"); 262 | return FALSE; 263 | } 264 | 265 | con.arr = hash_sort(con.filelist, sort_filedates, &con.arrlen); 266 | if (con.arr) { 267 | 268 | for (interval=0; intervaltab, "_files", &datelistfiles_thunk); 283 | if (intnamelist[interval]) 284 | hash_put(plan->tab, "interval", intnamelist[interval]); 285 | substitute(body, plan->tab, &con, fl); 286 | 287 | fclose(fl); 288 | } 289 | 290 | free(con.arr); 291 | } 292 | 293 | return TRUE; 294 | } 295 | 296 | int datelist_thunk(char *id, void *dirptr, void *rock) 297 | { 298 | contextdat_t *con = rock; 299 | hash_t *dir = dirptr; 300 | hash_t *filelist = hash_get(dir, "@files"); 301 | 302 | if (filelist) 303 | hash_iterate(filelist, datelistel_thunk, con); 304 | 305 | return FALSE; 306 | } 307 | 308 | int datelistel_thunk(char *id, void *filptr, void *rock) 309 | { 310 | contextdat_t *con = rock; 311 | hash_t *fil = filptr; 312 | 313 | if (hash_get(fil, "date")) { 314 | char countbuf[32]; 315 | sprintf(countbuf, "%d", con->count); 316 | hash_put(con->filelist, new_string(countbuf), fil); 317 | con->count++; 318 | } 319 | 320 | return FALSE; 321 | } 322 | 323 | static void datelistfiles_thunk(FILE *outfl, void *rock) 324 | { 325 | contextdat_t *con = rock; 326 | contextdate_t cone; 327 | 328 | cone.con = con; 329 | cone.outfl = outfl; 330 | cone.elstr = hash_get(con->plan->tab, "Date-List-Entry"); 331 | if (!cone.elstr) 332 | cone.elstr = "
  • {name}"; 333 | 334 | hash_iterate_array(con->arr, con->arrlen, &datelistfilesel_thunk, TRUE, &cone); 335 | } 336 | 337 | static int datelistfilesel_thunk(char *id, void *filptr, void *rock) 338 | { 339 | contextdate_t *cone = rock; 340 | hash_t *fil = filptr; 341 | time_t filedate; 342 | 343 | if (cone->con->interval) { 344 | filedate = atol(hash_get(fil, "date")); 345 | if (filedate + cone->con->interval < cone->con->curtime) 346 | return TRUE; 347 | } 348 | 349 | substitute(cone->elstr, fil, NULL, cone->outfl); 350 | fprintf(cone->outfl, "\n"); 351 | 352 | return FALSE; 353 | } 354 | 355 | int generate_dirlist(char *outdir, pfile_t *plan, hash_t *dirlist, 356 | char *body) 357 | { 358 | int ix; 359 | char pathbuf[MAXPATHLEN]; 360 | FILE *fl; 361 | contextdir_t con; 362 | 363 | if (!ensure_dir(outdir)) { 364 | printf("Goodbye.\n"); 365 | return FALSE; 366 | } 367 | 368 | sprintf(pathbuf, "%s/dirlist.html", outdir); 369 | fl = fopen(pathbuf, "w"); 370 | if (!fl) { 371 | perror(pathbuf); 372 | printf("Cannot create dirlist. Goodbye.\n"); 373 | return FALSE; 374 | } 375 | 376 | con.outdir = outdir; 377 | con.dirlist = dirlist; 378 | con.plan = plan; 379 | 380 | hash_put(plan->tab, "_dirs", &dirlist_thunk); 381 | substitute(body, plan->tab, &con, fl); 382 | 383 | fclose(fl); 384 | 385 | return TRUE; 386 | } 387 | 388 | static void dirlist_thunk(FILE *outfl, void *rock) 389 | { 390 | contextdir_t *con = rock; 391 | contextdire_t conm; 392 | 393 | conm.con = con; 394 | conm.outfl = outfl; 395 | conm.elstr = hash_get(con->plan->tab, "Dir-List-Entry"); 396 | if (!conm.elstr) 397 | conm.elstr = "
  • {dir}"; 398 | 399 | hash_iterate_sort(con->dirlist, &dirlistel_thunk, sort_dirs, TRUE, &conm); 400 | } 401 | 402 | static int dirlistel_thunk(char *id, void *dirptr, void *rock) 403 | { 404 | contextdire_t *conm = rock; 405 | hash_t *dir = dirptr; 406 | 407 | substitute(conm->elstr, dir, NULL, conm->outfl); 408 | fprintf(conm->outfl, "\n"); 409 | 410 | return FALSE; 411 | } 412 | 413 | static int sort_dirs(nodepair_t *v1, nodepair_t *v2) 414 | { 415 | hash_t *pf1 = v1->val; 416 | hash_t *pf2 = v2->val; 417 | char *c1, *c2; 418 | 419 | c1 = hash_get(pf1, "dir"); 420 | c2 = hash_get(pf2, "dir"); 421 | 422 | return strcasecmp(c1, c2); 423 | } 424 | 425 | static int sort_files(nodepair_t *v1, nodepair_t *v2) 426 | { 427 | hash_t *pf1 = v1->val; 428 | hash_t *pf2 = v2->val; 429 | char *c1, *c2; 430 | 431 | c1 = hash_get(pf1, "name"); 432 | c2 = hash_get(pf2, "name"); 433 | 434 | return strcasecmp(c1, c2); 435 | } 436 | 437 | static int sort_filedates(nodepair_t *v1, nodepair_t *v2) 438 | { 439 | hash_t *pf1 = v1->val; 440 | hash_t *pf2 = v2->val; 441 | char *c1, *c2; 442 | long dat1, dat2; 443 | 444 | c1 = hash_get(pf1, "date"); 445 | c2 = hash_get(pf2, "date"); 446 | 447 | if (c1) 448 | dat1 = atol(c1); 449 | else 450 | dat1 = 0; 451 | if (c2) 452 | dat2 = atol(c2); 453 | else 454 | dat2 = 0; 455 | 456 | if (dat1-dat2 < 0) 457 | return 1; 458 | else if (dat1-dat2 > 0) 459 | return -1; 460 | 461 | c1 = hash_get(pf1, "name"); 462 | c2 = hash_get(pf2, "name"); 463 | 464 | return strcasecmp(c1, c2); 465 | } 466 | 467 | int generate_xmllist(char *outdir, char *indir, pfile_t *plan, hash_t *dirlist, 468 | char *body) 469 | { 470 | int ix; 471 | char pathbuf[MAXPATHLEN]; 472 | char *cx; 473 | FILE *fl; 474 | char *dirbody, *filebody; 475 | contextxml_t con; 476 | 477 | if (!ensure_dir(outdir)) { 478 | printf("Goodbye.\n"); 479 | return FALSE; 480 | } 481 | 482 | dirbody = NULL; 483 | cx = hash_get(plan->tab, "XML-Dir-Template"); 484 | if (cx) { 485 | sprintf(pathbuf, "%s/%s", indir, cx); 486 | dirbody = file_getbody(pathbuf); 487 | } 488 | if (!dirbody) { 489 | dirbody = "\n{dir}\n\n"; 490 | } 491 | 492 | filebody = NULL; 493 | cx = hash_get(plan->tab, "XML-File-Template"); 494 | if (cx) { 495 | sprintf(pathbuf, "%s/%s", indir, cx); 496 | filebody = file_getbody(pathbuf); 497 | } 498 | if (!filebody) { 499 | filebody = "\n{name}\n\n"; 500 | } 501 | 502 | sprintf(pathbuf, "%s/Master-Index.xml", outdir); 503 | fl = fopen(pathbuf, "w"); 504 | if (!fl) { 505 | perror(pathbuf); 506 | printf("Cannot create xml. Goodbye.\n"); 507 | return FALSE; 508 | } 509 | 510 | con.outdir = outdir; 511 | con.dirlist = dirlist; 512 | con.plan = plan; 513 | con.dirbody = dirbody; 514 | con.filebody = filebody; 515 | 516 | hash_put(plan->tab, "_dirs", &xmllist_thunk); 517 | substitute(body, plan->tab, &con, fl); 518 | 519 | fclose(fl); 520 | 521 | return TRUE; 522 | } 523 | 524 | static void xmllist_thunk(FILE *outfl, void *rock) 525 | { 526 | contextxml_t *con = rock; 527 | contextxmle_t conm; 528 | 529 | conm.con = con; 530 | conm.outfl = outfl; 531 | conm.elstr = con->dirbody; 532 | 533 | hash_iterate_sort(con->dirlist, &xmllistel_thunk, sort_dirs, TRUE, &conm); 534 | } 535 | 536 | static int xmllistel_thunk(char *id, void *dirptr, void *rock) 537 | { 538 | contextxmle_t *conm = rock; 539 | contextxmled_t conz; 540 | hash_t *dir = dirptr; 541 | 542 | conz.con = conm; 543 | conz.filelist = hash_get(dir, "@files"); 544 | 545 | hash_put(dir, "_files", &xmllistdir_thunk); 546 | 547 | substitute(conm->elstr, dir, &conz, conm->outfl); 548 | fprintf(conm->outfl, "\n"); 549 | 550 | return FALSE; 551 | } 552 | 553 | static void xmllistdir_thunk(FILE *outfl, void *rock) 554 | { 555 | contextxmled_t *conz = rock; 556 | contextxmledel_t cony; 557 | 558 | cony.con = conz; 559 | cony.outfl = outfl; 560 | cony.elstr = conz->con->con->filebody; 561 | 562 | hash_iterate_sort(conz->filelist, &xmllistdirel_thunk, sort_files, TRUE, &cony); 563 | } 564 | 565 | static int xmllistdirel_thunk(char *id, void *fileptr, void *rock) 566 | { 567 | contextxmledel_t *cony = rock; 568 | hash_t *file = fileptr; 569 | 570 | substitute(cony->elstr, file, NULL, cony->outfl); 571 | fprintf(cony->outfl, "\n"); 572 | 573 | return FALSE; 574 | } 575 | 576 | static int ensure_dir(char *path) 577 | { 578 | int ix; 579 | struct stat sta; 580 | 581 | ix = stat(path, &sta); 582 | if (ix && errno != ENOENT) { 583 | perror(path); 584 | show_warning("Cannot find destination directory."); 585 | return FALSE; 586 | } 587 | if (!ix && !S_ISDIR(sta.st_mode)) { 588 | show_warning("Destination exists, but is not a directory."); 589 | return FALSE; 590 | } 591 | if (ix && errno == ENOENT) { 592 | ix = mkdir(path, 0755); 593 | if (ix) { 594 | perror(path); 595 | show_warning("Cannot create destination directory."); 596 | return FALSE; 597 | } 598 | } 599 | return TRUE; 600 | } 601 | -------------------------------------------------------------------------------- /parse.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "hash.h" 10 | #include "read.h" 11 | #include "output.h" 12 | #include "subst.h" 13 | #include "parse.h" 14 | #include "md5.h" 15 | 16 | #define ROOTNAME "if-archive" 17 | 18 | #define BUFSIZE (255) 19 | #define EXCLUDESIZE (64) 20 | 21 | typedef struct contextsubdir_struct { 22 | hash_t *dirlist; 23 | } contextsubdir_t; 24 | 25 | typedef struct contextsubdire_struct { 26 | contextsubdir_t *con; 27 | hash_t *dir; 28 | char *dirname; 29 | hash_t *subdirlist; 30 | } contextsubdire_t; 31 | 32 | typedef struct exclude_struct { 33 | int do_exclude; 34 | char allowed[EXCLUDESIZE][MAXPATHLEN]; 35 | } exclude_t; 36 | 37 | static void scan_directory(hash_t *dirlist, char *treedir, char *dirname, 38 | hash_t *parentlist, char *parentdir, exclude_t *exclude, int domd5); 39 | static int subdir_thunk(char *id, void *dirptr, void *rock); 40 | static int subdirel_thunk(char *id, void *dirptr, void *rock); 41 | 42 | static void tabsub(char *buf); 43 | static int bracket_count(char* buf); 44 | 45 | static char *monthlist[12] = { 46 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", 47 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 48 | }; 49 | 50 | void add_dir_links(hash_t *dir, char* path) 51 | { 52 | static char buf[65535]; 53 | int ix = 0, jx = 0, kx; 54 | 55 | buf[0] = '\0'; 56 | while (1) 57 | { 58 | if (path[jx] == '\0' || path[jx] == '/') 59 | { 60 | char link[BUFSIZE+1]; 61 | sprintf(link, "%.*s", jx, path, jx-ix, path+ix); 62 | for (kx = 0; link[kx] != '>'; kx++) 63 | { 64 | if (link[kx] == '/') 65 | link[kx] = 'X'; 66 | } 67 | kx = strlen(buf); 68 | if (kx > 0) 69 | buf[kx++] = '/'; 70 | strcpy(buf+kx, link); 71 | if (path[jx] == '\0') 72 | break; 73 | ix = jx+1; 74 | } 75 | jx++; 76 | } 77 | hash_put(dir, "xdirlinks", new_string(buf)); 78 | } 79 | 80 | int parse_master(char *master, hash_t *dirlist, char *toplevelbody, 81 | char *treedir, char *indir, int doexclude, int domd5) 82 | { 83 | int ix, jx; 84 | FILE *infl; 85 | char buf[BUFSIZE+1]; 86 | char *cx, *bx; 87 | char *headerstr = NULL; 88 | char *headerstrraw = NULL; 89 | char *filestr = NULL; 90 | char *filestrraw = NULL; 91 | int buflen, indent, firstindent; 92 | hash_t *dir, *filelist, *file; 93 | int inheader = TRUE; 94 | int headerpara = TRUE; 95 | int done = FALSE; 96 | int brackets = 0; 97 | exclude_t exclude; 98 | 99 | infl = fopen(master, "r"); 100 | if (!infl) { 101 | printf("Cannot open %s. Goodbye.\n", master); 102 | return 0; 103 | } 104 | 105 | dir = NULL; 106 | filelist = NULL; 107 | file = NULL; 108 | filestr = NULL; 109 | filestrraw = NULL; 110 | 111 | dir = new_hash(); 112 | hash_put(dir, "dir", ROOTNAME); 113 | hash_put(dir, "xdir", ROOTNAME); 114 | add_dir_links(dir, ROOTNAME); 115 | hash_put(dir, "@files", new_hash()); 116 | if (toplevelbody) { 117 | hash_put(dir, "header", toplevelbody); 118 | hash_put(dir, "hasdesc", "1"); 119 | } 120 | hash_put(dirlist, ROOTNAME, dir); 121 | dir = NULL; 122 | 123 | while (!done) { 124 | cx = fgets(buf, BUFSIZE, infl); 125 | if (!cx) { 126 | buflen = 0; 127 | buf[0] = '\0'; 128 | done = TRUE; 129 | } 130 | else { 131 | buflen = strlen(buf); 132 | if (buflen && buf[buflen-1] == '\n') { 133 | buflen--; 134 | buf[buflen] = '\0'; 135 | } 136 | } 137 | 138 | tabsub(buf); 139 | 140 | if (done || (!strncmp(buf, ROOTNAME, 10) && buf[buflen-1] == ':')) { 141 | if (dir) { 142 | int len; 143 | 144 | if (file) { 145 | if (filestr) { 146 | hash_put(file, "desc", filestr); 147 | if (is_string_nonwhite(filestr)) 148 | hash_put(file, "hasdesc", "1"); 149 | filestr = NULL; 150 | } 151 | if (filestrraw) { 152 | char *dx = new_string_esc_xml(filestrraw); 153 | trim_extra_newlines(dx); 154 | hash_put(file, "xmldesc", dx); 155 | if (is_string_nonwhite(filestrraw)) 156 | hash_put(file, "hasxmldesc", "1"); 157 | filestrraw = NULL; 158 | } 159 | hash_put(filelist, (char *)hash_get(file, "rawname"), file); 160 | file = NULL; 161 | } 162 | 163 | cx = hash_get(dir, "dir"); 164 | if (verbose) 165 | printf("Completing %s...\n", cx); 166 | hash_put(dirlist, cx, dir); 167 | if (headerstr) { 168 | hash_put(dir, "header", headerstr); 169 | if (is_string_nonwhite(headerstr)) 170 | hash_put(dir, "hasdesc", "1"); 171 | headerstr = NULL; 172 | } 173 | if (headerstrraw) { 174 | trim_extra_newlines(headerstrraw); 175 | hash_put(dir, "xmlheader", new_string_esc_xml(headerstrraw)); 176 | if (is_string_nonwhite(headerstrraw)) 177 | hash_put(dir, "hasxmldesc", "1"); 178 | headerstrraw = NULL; 179 | } 180 | dir = NULL; 181 | filelist = NULL; 182 | } 183 | 184 | if (!done) { 185 | char *cx2, *cx3; 186 | 187 | dir = new_hash(); 188 | filelist = new_hash(); 189 | file = NULL; 190 | filestr = NULL; 191 | filestrraw = NULL; 192 | hash_put(dir, "@files", filelist); 193 | buflen--; 194 | buf[buflen] = '\0'; 195 | if (verbose) 196 | printf("Starting %s:\n", buf); 197 | 198 | hash_put(dir, "dir", new_string(buf)); 199 | 200 | cx = new_string(buf); 201 | for (cx2 = cx; *cx2; cx2++) 202 | if (*cx2 == '/') 203 | *cx2 = 'X'; 204 | hash_put(dir, "xdir", cx); 205 | add_dir_links(dir, buf); 206 | 207 | cx = new_string(buf); 208 | for (cx2=cx, cx3=NULL; *cx2; cx2++) 209 | if (*cx2 == '/') 210 | cx3 = cx2; 211 | if (cx3) { 212 | *cx3 = '\0'; 213 | hash_put(dir, "parentdir", new_string(cx)); 214 | for (cx2 = cx; *cx2; cx2++) 215 | if (*cx2 == '/') 216 | *cx2 = 'X'; 217 | hash_put(dir, "xparentdir", cx); 218 | } 219 | 220 | inheader = TRUE; 221 | headerpara = TRUE; 222 | headerstr = NULL; 223 | headerstrraw = NULL; 224 | } 225 | 226 | continue; 227 | } 228 | 229 | if (!dir) { 230 | continue; 231 | } 232 | 233 | jx = 0; 234 | for (cx = buf; *cx; cx++) { 235 | if (*cx == ' ') 236 | continue; 237 | if (!(*cx >= ' ' && *cx <= '-')) 238 | break; 239 | jx++; 240 | } 241 | if (*cx == '\0' && jx) 242 | continue; 243 | 244 | for (cx = buf, indent = 0; *cx == ' '; cx++, indent++); 245 | bx = cx; 246 | 247 | if (inheader) { 248 | if (!strncmp(cx, "Index", 5)) { 249 | cx += 5; 250 | for (; *cx == ' '; cx++); 251 | if (!strncasecmp(cx, "this file", 9)) { 252 | inheader = FALSE; 253 | continue; 254 | } 255 | } 256 | } 257 | 258 | if (inheader) { 259 | if (strlen(bx)) { 260 | headerstr = append_string_esc(headerstr, bx, 0); 261 | headerstr = append_string(headerstr, "\n"); 262 | headerpara = FALSE; 263 | headerstrraw = append_string(headerstrraw, bx); 264 | headerstrraw = append_string(headerstrraw, "\n"); 265 | } 266 | else { 267 | if (!headerpara) { 268 | headerstr = append_string(headerstr, "

    \n"); 269 | headerpara = TRUE; 270 | } 271 | if (headerstrraw && headerstrraw[0]) { 272 | headerstrraw = append_string(headerstrraw, "\n"); 273 | } 274 | } 275 | continue; 276 | } 277 | 278 | if (indent == 0 && *bx) { 279 | char *cx2, *id; 280 | int len; 281 | 282 | if (file) { 283 | if (filestr) { 284 | hash_put(file, "desc", filestr); 285 | if (is_string_nonwhite(filestr)) 286 | hash_put(file, "hasdesc", "1"); 287 | filestr = NULL; 288 | } 289 | if (filestrraw) { 290 | char *dx = new_string_esc_xml(filestrraw); 291 | trim_extra_newlines(dx); 292 | hash_put(file, "xmldesc", dx); 293 | if (is_string_nonwhite(filestrraw)) 294 | hash_put(file, "hasxmldesc", "1"); 295 | filestrraw = NULL; 296 | } 297 | hash_put(filelist, (char *)hash_get(file, "rawname"), file); 298 | file = NULL; 299 | } 300 | 301 | file = new_hash(); 302 | filestr = NULL; 303 | filestrraw = NULL; 304 | firstindent = -1; 305 | 306 | for (cx2 = bx; *cx2 && *cx2 != ' '; cx2++); 307 | len = cx2-bx; 308 | id = (char *)malloc(sizeof(char) * (len+1)); 309 | strncpy(id, bx, len); 310 | id[len] = '\0'; 311 | 312 | while (*cx2 == ' ') 313 | cx2++; 314 | if (*cx2) { 315 | firstindent = (cx2-buf); 316 | filestr = append_string_esc(NULL, cx2, 0); 317 | filestr = append_string(filestr, "\n"); 318 | filestrraw = append_string(NULL, cx2); 319 | filestrraw = append_string(filestrraw, "\n"); 320 | brackets = bracket_count(cx2); 321 | } 322 | else { 323 | firstindent = -1; 324 | filestr = new_string(""); 325 | filestrraw = new_string(""); 326 | } 327 | 328 | hash_put(file, "rawname", id); 329 | hash_put_escurl(file, "name", "nameurl", id); 330 | hash_put_escxml(file, "namexml", id); 331 | hash_put(file, "dir", (char *)hash_get(dir, "dir")); 332 | } 333 | else { 334 | if (strlen(bx)) { 335 | if (firstindent < 0) { 336 | firstindent = indent; 337 | brackets = 0; 338 | } 339 | if ((firstindent != indent) && (brackets == 0)) { 340 | filestr = append_string(filestr, "
      "); 341 | filestrraw = append_spaces(filestrraw, indent-firstindent); 342 | } 343 | filestr = append_string_esc(filestr, bx, 0); 344 | filestr = append_string(filestr, "\n"); 345 | filestrraw = append_string(filestrraw, bx); 346 | brackets += bracket_count(bx); 347 | } 348 | filestrraw = append_string(filestrraw, "\n"); 349 | } 350 | } 351 | 352 | fclose(infl); 353 | 354 | exclude.do_exclude = doexclude; 355 | for (ix = 0; ix < EXCLUDESIZE; ix++) 356 | exclude.allowed[ix][0] = '\0'; 357 | 358 | sprintf(buf, "%s/no-index-entry", indir); 359 | infl = fopen(buf, "r"); 360 | if (!infl) { 361 | perror(buf); 362 | printf("Cannot open no-index-entry file. Goodbye.\n"); 363 | return FALSE; 364 | } 365 | 366 | ix = 0; 367 | while (!feof(infl) && (ix < EXCLUDESIZE)) { 368 | fgets(exclude.allowed[ix], MAXPATHLEN, infl); 369 | 370 | jx = strlen(exclude.allowed[ix]); 371 | if ((jx > 0) && exclude.allowed[ix][jx-1] == '\n') 372 | exclude.allowed[ix][jx-1] = '\0'; 373 | ix++; 374 | } 375 | fclose(infl); 376 | 377 | /* Do an actual scan of the tree and write in any directories 378 | we missed. */ 379 | if (treedir) 380 | scan_directory(dirlist, treedir, ROOTNAME, NULL, NULL, &exclude, domd5); 381 | 382 | /* Now we have to buzz through and assign subdirectories. */ 383 | { 384 | contextsubdir_t con; 385 | con.dirlist = dirlist; 386 | hash_iterate(dirlist, &subdir_thunk, &con); 387 | } 388 | 389 | return 1; 390 | } 391 | 392 | static int check_exclude(exclude_t *exclude, const char *name) 393 | { 394 | int i; 395 | 396 | for (i = 0; i < EXCLUDESIZE; i++) { 397 | if (exclude->allowed[i][0] == '\0') 398 | break; 399 | if (strncmp(name, exclude->allowed[i], strlen(exclude->allowed[i])) == 0) 400 | return 0; 401 | } 402 | 403 | fprintf(stderr,"File without index entry: %s\n", name); 404 | return (exclude->do_exclude == 0) ? 0 : 1; 405 | } 406 | 407 | static void scan_directory(hash_t *dirlist, char *treedir, char *dirname, 408 | hash_t *parentlist, char *parentdir, exclude_t *exclude, int domd5) 409 | { 410 | DIR *udir; 411 | hash_t *dir, *filelist; 412 | struct dirent *ent; 413 | struct stat sta, sta2; 414 | char pathname[MAXPATHLEN]; 415 | char dirname2[MAXPATHLEN]; 416 | 417 | if (verbose) 418 | printf("Scanning %s...\n", dirname); 419 | 420 | dir = hash_get(dirlist, dirname); 421 | if (!dir) { 422 | show_warning2("Unable to find directory.", dirname); 423 | return; 424 | } 425 | filelist = hash_get(dir, "@files"); 426 | 427 | sprintf(pathname, "%s/%s", treedir, dirname); 428 | 429 | udir = opendir(pathname); 430 | if (!udir) { 431 | show_warning2("Unable to open directory.", pathname); 432 | return; 433 | } 434 | 435 | while ((ent = readdir(udir)) != NULL) { 436 | char *fname = ent->d_name; 437 | if (*fname == '\0' || *fname == '.') 438 | continue; 439 | 440 | sprintf(dirname2, "%s/%s", dirname, fname); 441 | sprintf(pathname, "%s/%s/%s", treedir, dirname, fname); 442 | 443 | if (lstat(pathname, &sta)) { 444 | show_warning2("Unable to lstat.", pathname); 445 | continue; 446 | } 447 | 448 | if (S_ISLNK(sta.st_mode)) { 449 | /* got a symlink */ 450 | char linkname[MAXPATHLEN+1]; 451 | int linklen; 452 | linklen = readlink(pathname, linkname, MAXPATHLEN); 453 | if (linklen <= 0) { 454 | show_warning2("Unable to readlink.", pathname); 455 | } 456 | else { 457 | linkname[linklen] = '\0'; 458 | if (linklen && linkname[linklen-1] == '/') { 459 | linklen--; 460 | linkname[linklen] = '\0'; 461 | } 462 | if (!stat(pathname, &sta2)) { 463 | if (S_ISREG(sta2.st_mode)) { 464 | hash_t *file; 465 | char *cx; 466 | struct tm *tmdat; 467 | char datebuf[32]; 468 | file = hash_get(filelist, fname); 469 | if (!file) { 470 | if (check_exclude(exclude,dirname2)) 471 | continue; 472 | file = new_hash(); 473 | cx = new_string(fname); 474 | hash_put(file, "rawname", cx); 475 | hash_put_escurl(file, "name", "nameurl", cx); 476 | hash_put_escxml(file, "namexml", cx); 477 | hash_put(file, "dir", new_string_esc(dirname)); 478 | hash_put(file, "desc", " "); 479 | hash_put(filelist, cx, file); 480 | } 481 | hash_put(file, "islink", "1"); 482 | hash_put(file, "islinkfile", "1"); 483 | hash_put(file, "linkpath", new_string(linkname)); /* ### canonicalize */ 484 | sprintf(datebuf, "%ld", (long)sta2.st_mtime); 485 | hash_put(file, "date", new_string(datebuf)); 486 | tmdat = localtime(&sta2.st_mtime); 487 | sprintf(datebuf, "%02d-%s-%d", 488 | tmdat->tm_mday, monthlist[tmdat->tm_mon], tmdat->tm_year+1900); 489 | hash_put(file, "datestr", new_string(datebuf)); 490 | } 491 | else if (S_ISDIR(sta2.st_mode)) { 492 | char targetname[MAXPATHLEN+1]; 493 | int targetlen; 494 | char *cx, *cx2; 495 | hash_t *file; 496 | strcpy(targetname, dirname); 497 | targetlen = strlen(targetname); 498 | for (cx = linkname; *cx; ) { 499 | cx2 = strchr(cx, '/'); 500 | if (!cx2) { 501 | cx2 = cx + strlen(cx); 502 | } 503 | if (cx2-cx == 1 && cx[0] == '.') { 504 | /* nothing */ 505 | } 506 | else if (cx2-cx == 2 && cx[0] == '.' && cx[1] == '.') { 507 | if (targetlen) 508 | targetlen--; 509 | while (targetlen && targetname[targetlen] != '/') 510 | targetlen--; 511 | } 512 | else { 513 | targetname[targetlen] = '/'; 514 | targetlen++; 515 | memcpy(targetname+targetlen, cx, sizeof(char) * (cx2-cx)); 516 | targetlen += (cx2-cx); 517 | } 518 | if (*cx2 == '/') 519 | cx2++; 520 | cx = cx2; 521 | } 522 | targetname[targetlen] = '\0'; 523 | if (targetname[0] == '/') 524 | continue; 525 | file = hash_get(filelist, fname); 526 | if (!file) { 527 | char tempname[MAXPATHLEN+1]; 528 | sprintf(tempname, "Symlink to %s", targetname); 529 | file = new_hash(); 530 | cx = new_string(fname); 531 | hash_put(file, "name", cx); 532 | hash_put_escxml(file, "namexml", cx); 533 | hash_put(file, "dir", new_string(dirname)); 534 | hash_put(file, "desc", new_string(tempname)); 535 | hash_put(filelist, cx, file); 536 | } 537 | hash_put(file, "islink", "1"); 538 | hash_put(file, "islinkdir", "1"); 539 | cx = new_string(targetname); 540 | hash_put(file, "linkdir", cx); 541 | cx = new_string(targetname); 542 | for (cx2 = cx; *cx2; cx2++) 543 | if (*cx2 == '/') 544 | *cx2 = 'X'; 545 | hash_put(file, "xlinkdir", cx); 546 | } 547 | } 548 | } 549 | } 550 | else if (S_ISREG(sta.st_mode)) { 551 | /* regular file */ 552 | hash_t *file; 553 | char *cx; 554 | struct tm *tmdat; 555 | char datebuf[32]; 556 | if (!strcmp(fname, "Index")) 557 | continue; 558 | file = hash_get(filelist, fname); 559 | if (!file) { 560 | if (check_exclude(exclude,dirname2)) 561 | continue; 562 | file = new_hash(); 563 | cx = new_string(fname); 564 | hash_put(file, "rawname", cx); 565 | hash_put_escurl(file, "name", "nameurl", cx); 566 | hash_put_escxml(file, "namexml", cx); 567 | hash_put(file, "dir", new_string_esc(dirname)); 568 | hash_put(file, "desc", " "); 569 | hash_put(filelist, cx, file); 570 | } 571 | sprintf(datebuf, "%ld", (long)sta.st_size); 572 | hash_put(file, "filesize", new_string(datebuf)); 573 | sprintf(datebuf, "%ld", (long)sta.st_mtime); 574 | hash_put(file, "date", new_string(datebuf)); 575 | tmdat = localtime(&sta.st_mtime); 576 | sprintf(datebuf, "%02d-%s-%d", 577 | tmdat->tm_mday, monthlist[tmdat->tm_mon], tmdat->tm_year+1900); 578 | hash_put(file, "datestr", new_string(datebuf)); 579 | if (domd5) 580 | hash_put(file, "md5", new_string(md5(pathname))); 581 | } 582 | else if (S_ISDIR(sta.st_mode)) { 583 | /* directory */ 584 | hash_t *dir2, *file; 585 | char *cx, *cx2, *cx3; 586 | dir2 = hash_get(dirlist, dirname2); 587 | if (!dir2) { 588 | dir2 = new_hash(); 589 | cx = new_string(dirname2); 590 | hash_put(dir2, "dir", cx); 591 | hash_put(dirlist, cx, dir2); 592 | hash_put(dir2, "@files", new_hash()); 593 | cx = new_string(dirname2); 594 | for (cx2=cx; *cx2; cx2++) 595 | if (*cx2 == '/') 596 | *cx2 = 'X'; 597 | hash_put(dir2, "xdir", cx); 598 | add_dir_links(dir2, dirname2); 599 | 600 | cx = new_string(dirname2); 601 | for (cx2=cx, cx3=NULL; *cx2; cx2++) 602 | if (*cx2 == '/') 603 | cx3 = cx2; 604 | if (cx3) { 605 | *cx3 = '\0'; 606 | hash_put(dir2, "parentdir", new_string(cx)); 607 | for (cx2 = cx; *cx2; cx2++) 608 | if (*cx2 == '/') 609 | *cx2 = 'X'; 610 | hash_put(dir2, "xparentdir", cx); 611 | } 612 | } 613 | file = hash_get(filelist, fname); 614 | if (file) { 615 | cx = new_string(dirname2); 616 | hash_put(file, "linkdir", cx); 617 | cx = new_string(dirname2); 618 | for (cx2 = cx; *cx2; cx2++) 619 | if (*cx2 == '/') 620 | *cx2 = 'X'; 621 | hash_put(file, "xlinkdir", cx); 622 | } 623 | if (parentlist && parentdir) { 624 | hash_t *parentfile; 625 | char parentname[MAXPATHLEN]; 626 | sprintf(parentname,"%s/%s",parentdir,fname); 627 | parentfile = hash_get(parentlist, parentname); 628 | if (parentfile) { 629 | cx = new_string(dirname2); 630 | hash_put(parentfile, "linkdir", cx); 631 | cx = new_string(dirname2); 632 | for (cx2 = cx; *cx2; cx2++) 633 | if (*cx2 == '/') 634 | *cx2 = 'X'; 635 | hash_put(parentfile, "xlinkdir", cx); 636 | } 637 | } 638 | scan_directory(dirlist, treedir, dirname2, filelist, fname, exclude, domd5); 639 | } 640 | } 641 | 642 | closedir(udir); 643 | } 644 | 645 | static int subdir_thunk(char *id, void *dirptr, void *rock) 646 | { 647 | contextsubdir_t *con = rock; 648 | hash_t *dir = dirptr; 649 | hash_t *filelist; 650 | int len; 651 | contextsubdire_t conm; 652 | char numbuf[16]; 653 | 654 | filelist = hash_get(dir, "@files"); 655 | if (filelist) { 656 | len = hash_length(filelist); 657 | if (len) { 658 | sprintf(numbuf, "%d", len); 659 | hash_put(dir, "count", new_string(numbuf)); 660 | } 661 | } 662 | 663 | conm.con = con; 664 | conm.dir = dir; 665 | conm.dirname = id; 666 | conm.subdirlist = new_hash(); 667 | 668 | hash_iterate(con->dirlist, &subdirel_thunk, &conm); 669 | 670 | hash_put(dir, "@subdirs", conm.subdirlist); 671 | len = hash_length(conm.subdirlist); 672 | if (len) { 673 | sprintf(numbuf, "%d", len); 674 | hash_put(dir, "subdircount", new_string(numbuf)); 675 | } 676 | return FALSE; 677 | } 678 | 679 | static int subdirel_thunk(char *id, void *dirptr, void *rock) 680 | { 681 | contextsubdire_t *con = rock; 682 | hash_t *dir = dirptr; 683 | int idlen = strlen(con->dirname); 684 | 685 | if (!strncmp(con->dirname, id, idlen) 686 | && id[idlen] == '/' 687 | && !strchr(id+idlen+1, '/')) { 688 | hash_put(con->subdirlist, id, dir); 689 | } 690 | return FALSE; 691 | } 692 | 693 | static void tabsub(char *buf) 694 | { 695 | char buf2[BUFSIZE+1]; 696 | char *cx, *cx2; 697 | int len2 = 0, count = 0; 698 | 699 | for (cx=buf, cx2=buf2; *cx; cx++) { 700 | if (*cx == '\t') { 701 | count++; 702 | do { 703 | *cx2++ = ' '; 704 | len2++; 705 | } while ((len2 & 8) != 0); 706 | } 707 | else { 708 | *cx2++ = *cx; 709 | len2++; 710 | } 711 | } 712 | *cx2 = '\0'; 713 | 714 | if (count) { 715 | strcpy(buf, buf2); 716 | } 717 | } 718 | 719 | static int bracket_count(char* buf) 720 | { 721 | int count = 0; 722 | 723 | while (*buf != 0) 724 | { 725 | if ((*buf == '[') || (*buf == '(')) 726 | count++; 727 | if ((*buf == ']') || (*buf == ')')) 728 | count--; 729 | buf++; 730 | } 731 | 732 | return count; 733 | } 734 | 735 | --------------------------------------------------------------------------------