00001
00002
00003
00004
00005 #define nil ((void*)0)
00006 #if __minix_vmd
00007 #include <minix/stubs.h>
00008 #else
00009 #define fstat _fstat
00010 #define stat _stat
00011 #endif
00012 #include <sys/types.h>
00013 #include <stdio.h>
00014 #include <stddef.h>
00015 #include <stdlib.h>
00016 #include <errno.h>
00017 #include <string.h>
00018 #include <time.h>
00019 #include <sys/stat.h>
00020 #if __minix_vmd
00021 #include <minix/asciictype.h>
00022 #else
00023 #include <ctype.h>
00024 #endif
00025 #define _c
00026 #include <configfile.h>
00027
00028 typedef struct configfile {
00029 struct configfile *next;
00030 time_t ctime;
00031 char name[1];
00032 } configfile_t;
00033
00034
00035 #define configfilesize(len) (offsetof(configfile_t, name) + 1 + (len))
00036
00037 typedef struct firstconfig {
00038 configfile_t *filelist;
00039 char new;
00040 config_t config1;
00041 } firstconfig_t;
00042
00043
00044 #define config0size() (offsetof(config_t, word))
00045 #define configsize(len) (config0size() + 1 + (len))
00046 #define firstconfigsize(len) \
00047 (offsetof(firstconfig_t, config1) + configsize(len))
00048
00049
00050 #define cfg2fcfg(p) \
00051 ((firstconfig_t *) ((char *) (p) - offsetof(firstconfig_t, config1)))
00052 #define fcfg2cfg(p) (&(p)->config1)
00053
00054
00055 static configfile_t *c_files;
00056 static int c_flags;
00057 static FILE *c_fp;
00058 static char *c_file;
00059 static unsigned c_line;
00060 static int c;
00061
00062 static void *allocate(void *mem, size_t size)
00063
00064 {
00065 if ((mem= realloc(mem, size)) == nil) {
00066 fprintf(stderr, "\"%s\", line %u: Out of memory\n", c_file, c_line);
00067 exit(1);
00068 }
00069 return mem;
00070 }
00071
00072 #define deallocate(mem) free(mem)
00073
00074 static void delete_filelist(configfile_t *cfgf)
00075
00076 {
00077 void *junk;
00078
00079 while (cfgf != nil) {
00080 junk= cfgf;
00081 cfgf= cfgf->next;
00082 deallocate(junk);
00083 }
00084 }
00085
00086 static void delete_config(config_t *cfg)
00087
00088 {
00089 config_t *next, *list, *junk;
00090
00091 next= cfg;
00092 list= nil;
00093 for (;;) {
00094 if (next != nil) {
00095
00096
00097
00098 junk= next;
00099 next= next->next;
00100 junk->next= list;
00101 list= junk;
00102 } else
00103 if (list != nil) {
00104
00105
00106
00107 junk= list;
00108 next= list->list;
00109 list= list->next;
00110 deallocate(junk);
00111 } else {
00112
00113 break;
00114 }
00115 }
00116 }
00117
00118 void config_delete(config_t *cfg1)
00119
00120 {
00121 firstconfig_t *fcfg= cfg2fcfg(cfg1);
00122
00123 delete_filelist(fcfg->filelist);
00124 delete_config(fcfg->config1.next);
00125 delete_config(fcfg->config1.list);
00126 deallocate(fcfg);
00127 }
00128
00129 static void nextc(void)
00130
00131 {
00132 if (c == '\n') c_line++;
00133 c= getc(c_fp);
00134 if (c == EOF && ferror(c_fp)) {
00135 fprintf(stderr, "\"%s\", line %u: %s\n",
00136 c_file, c_line, strerror(errno));
00137 exit(1);
00138 }
00139 }
00140
00141 static void skipwhite(void)
00142
00143 {
00144 while (isspace(c)) {
00145 nextc();
00146 if (c == '#') {
00147 do nextc(); while (c != EOF && c != '\n');
00148 }
00149 }
00150 }
00151
00152 static void parse_err(void)
00153
00154 {
00155 char sc[2];
00156
00157 sc[0]= c;
00158 sc[1]= 0;
00159 fprintf(stderr, "\"%s\", line %u: parse error at '%s'\n",
00160 c_file, c_line, c == EOF ? "EOF" : sc);
00161 exit(1);
00162 }
00163
00164 static config_t *read_word(void)
00165
00166 {
00167 config_t *w;
00168 size_t i, len;
00169 int q;
00170 static char SPECIAL[] = "!#$%&*+-./:<=>?[\\]^_|~";
00171
00172 i= 0;
00173 len= 32;
00174 w= allocate(nil, configsize(32));
00175 w->next= nil;
00176 w->list= nil;
00177 w->file= c_file;
00178 w->line= c_line;
00179 w->flags= 0;
00180
00181
00182 if (c == '\'' || c == '"') {
00183 q= c;
00184 nextc();
00185 } else {
00186 q= -1;
00187 }
00188
00189 for (;;) {
00190 if (i == len) {
00191 len+= 32;
00192 w= allocate(w, configsize(len));
00193 }
00194
00195 if (q == -1) {
00196
00197 if (!isalnum(c) && c < 0x80 && strchr(SPECIAL, c) == nil) break;
00198 } else {
00199
00200 if (c == EOF || c == '\n') {
00201 fprintf(stderr,
00202 "\"%s\", line %u: string at line %u not closed\n",
00203 c_file, c_line, w->line);
00204 exit(1);
00205 break;
00206 }
00207 if (c == q) {
00208 nextc();
00209 break;
00210 }
00211 }
00212
00213 if (c != '\\') {
00214 w->word[i++]= c;
00215 nextc();
00216 } else {
00217 nextc();
00218 if (isspace(c)) {
00219 skipwhite();
00220 continue;
00221 }
00222
00223 if (c_flags & CFG_ESCAPED) {
00224 w->word[i++]= '\\';
00225 if (i == len) {
00226 len+= 32;
00227 w= allocate(w, configsize(len));
00228 }
00229 w->flags |= CFG_ESCAPED;
00230 }
00231
00232 if (isdigit(c)) {
00233 int n= 3;
00234 int d= 0;
00235
00236 do {
00237 d= d * 010 + (c - '0');
00238 nextc();
00239 } while (--n > 0 && isdigit(c));
00240 w->word[i++]= d;
00241 } else
00242 if (c == 'x' || c == 'X') {
00243 int n= 2;
00244 int d= 0;
00245
00246 nextc();
00247 if (!isxdigit(c)) {
00248 fprintf(stderr, "\"%s\", line %u: bad hex escape\n",
00249 c_file, c_line);
00250 exit(1);
00251 }
00252 do {
00253 d= d * 0x10 + (islower(c) ? (c - 'a' + 0xa) :
00254 isupper(c) ? (c - 'A' + 0xA) :
00255 (c - '0'));
00256 nextc();
00257 } while (--n > 0 && isxdigit(c));
00258 w->word[i++]= d;
00259 } else {
00260 switch (c) {
00261 case 'a': c= '\a'; break;
00262 case 'b': c= '\b'; break;
00263 case 'e': c= '\033'; break;
00264 case 'f': c= '\f'; break;
00265 case 'n': c= '\n'; break;
00266 case 'r': c= '\r'; break;
00267 case 's': c= ' '; break;
00268 case 't': c= '\t'; break;
00269 case 'v': c= '\v'; break;
00270 default: ;
00271 }
00272 w->word[i++]= c;
00273 nextc();
00274 }
00275 }
00276 }
00277 w->word[i]= 0;
00278 if (q != -1) {
00279 w->flags |= CFG_STRING;
00280 } else {
00281 int f;
00282 char *end;
00283 static char base[]= { 0, 010, 10, 0x10 };
00284
00285 if (i == 0) parse_err();
00286
00287
00288 for (f= 0; f < 4; f++) {
00289 (void) strtol(w->word, &end, base[f]);
00290 if (*end == 0) w->flags |= 1 << (f + 0);
00291 (void) strtoul(w->word, &end, base[f]);
00292 if (*end == 0) w->flags |= 1 << (f + 4);
00293 }
00294 }
00295 return allocate(w, configsize(i));
00296 }
00297
00298 static config_t *read_file(const char *file);
00299 static config_t *read_list(void);
00300
00301 static config_t *read_line(void)
00302
00303 {
00304 config_t *cline, **pcline, *clist;
00305
00306 cline= nil;
00307 pcline= &cline;
00308
00309 for (;;) {
00310 skipwhite();
00311
00312 if (c == EOF || c == '}') {
00313 if(0) if (cline != nil) parse_err();
00314 break;
00315 } else
00316 if (c == ';') {
00317 nextc();
00318 if (cline != nil) break;
00319 } else
00320 if (cline != nil && c == '{') {
00321
00322 nextc();
00323 clist= allocate(nil, config0size());
00324 clist->next= nil;
00325 clist->file= c_file;
00326 clist->line= c_line;
00327 clist->list= read_list();
00328 clist->flags= CFG_SUBLIST;
00329 *pcline= clist;
00330 pcline= &clist->next;
00331 if (c != '}') parse_err();
00332 nextc();
00333 } else {
00334 *pcline= read_word();
00335 pcline= &(*pcline)->next;
00336 }
00337 }
00338 return cline;
00339 }
00340
00341 static config_t *read_list(void)
00342
00343 {
00344 config_t *clist, **pclist, *cline;
00345
00346 clist= nil;
00347 pclist= &clist;
00348
00349 while ((cline= read_line()) != nil) {
00350 if (strcmp(cline->word, "include") == 0) {
00351 config_t *file= cline->next;
00352 if (file == nil || file->next != nil || !config_isatom(file)) {
00353 fprintf(stderr,
00354 "\"%s\", line %u: 'include' command requires an argument\n",
00355 c_file, cline->line);
00356 exit(1);
00357 }
00358 if (file->flags & CFG_ESCAPED) {
00359 char *p, *q;
00360 p= q= file->word;
00361 for (;;) {
00362 if ((*q = *p) == '\\') *q = *++p;
00363 if (*q == 0) break;
00364 p++;
00365 q++;
00366 }
00367 }
00368 file= read_file(file->word);
00369 delete_config(cline);
00370 *pclist= file;
00371 while (*pclist != nil) pclist= &(*pclist)->next;
00372 } else {
00373 config_t *cfg= allocate(nil, config0size());
00374 cfg->next= nil;
00375 cfg->list= cline;
00376 cfg->file= cline->file;
00377 cfg->line= cline->line;
00378 cfg->flags= CFG_SUBLIST;
00379 *pclist= cfg;
00380 pclist= &cfg->next;
00381 }
00382 }
00383 return clist;
00384 }
00385
00386 static config_t *read_file(const char *file)
00387
00388 {
00389 configfile_t *cfgf;
00390 config_t *cfg;
00391 struct stat st;
00392 FILE *old_fp;
00393 char *old_file;
00394 unsigned old_line;
00395 int old_c;
00396 size_t n;
00397 char *slash;
00398
00399 old_fp= c_fp;
00400 old_file= c_file;
00401 old_line= c_line;
00402 old_c= c;
00403
00404 n= 0;
00405 if (file[0] != '/' && old_file != nil
00406 && (slash= strrchr(old_file, '/')) != nil) {
00407 n= slash - old_file + 1;
00408 }
00409 cfgf= allocate(nil, configfilesize(n + strlen(file)));
00410 memcpy(cfgf->name, old_file, n);
00411 strcpy(cfgf->name + n, file);
00412 cfgf->next= c_files;
00413 c_files= cfgf;
00414
00415 c_file= cfgf->name;
00416 c_line= 0;
00417
00418 if ((c_fp= fopen(file, "r")) == nil || fstat(fileno(c_fp), &st) < 0) {
00419 if (errno != ENOENT) {
00420 fprintf(stderr, "\"%s\", line 1: %s\n", file, strerror(errno));
00421 exit(1);
00422 }
00423 cfgf->ctime= -1;
00424 c= EOF;
00425 } else {
00426 cfgf->ctime= st.st_ctime;
00427 c= '\n';
00428 }
00429
00430 cfg= read_list();
00431 if (c != EOF) parse_err();
00432
00433 if (c_fp != nil) fclose(c_fp);
00434 c_fp= old_fp;
00435 c_file= old_file;
00436 c_line= old_line;
00437 c= old_c;
00438 return cfg;
00439 }
00440
00441 config_t *config_read(const char *file, int flags, config_t *cfg)
00442
00443 {
00444 if (cfg != nil) {
00445
00446 firstconfig_t *fcfg;
00447 configfile_t *cfgf;
00448 struct stat st;
00449
00450 fcfg= cfg2fcfg(cfg);
00451 for (cfgf= fcfg->filelist; cfgf != nil; cfgf= cfgf->next) {
00452 if (stat(cfgf->name, &st) < 0) {
00453 if (errno != ENOENT) break;
00454 st.st_ctime= -1;
00455 }
00456 if (st.st_ctime != cfgf->ctime) break;
00457 }
00458
00459 if (cfgf == nil) return cfg;
00460 config_delete(cfg);
00461 }
00462
00463 errno= 0;
00464 c_files= nil;
00465 c_flags= flags;
00466 cfg= read_file(file);
00467
00468 if (cfg != nil) {
00469
00470 size_t len= strlen(cfg->word);
00471 firstconfig_t *fcfg;
00472
00473 fcfg= allocate(cfg, firstconfigsize(len));
00474 memmove(&fcfg->config1, fcfg, configsize(len));
00475 fcfg->filelist= c_files;
00476 fcfg->new= 1;
00477 return fcfg2cfg(fcfg);
00478 }
00479
00480 delete_filelist(c_files);
00481 delete_config(cfg);
00482 return nil;
00483 }
00484
00485 int config_renewed(config_t *cfg)
00486 {
00487 int new;
00488
00489 if (cfg == nil) {
00490 new= 1;
00491 } else {
00492 new= cfg2fcfg(cfg)->new;
00493 cfg2fcfg(cfg)->new= 0;
00494 }
00495 return new;
00496 }
00497
00498 size_t config_length(config_t *cfg)
00499
00500 {
00501 size_t n= 0;
00502
00503 while (cfg != nil) {
00504 n++;
00505 cfg= cfg->next;
00506 }
00507 return n;
00508 }
00509
00510 #if TEST
00511 #include <unistd.h>
00512
00513 static void print_list(int indent, config_t *cfg);
00514
00515 static void print_words(int indent, config_t *cfg)
00516 {
00517 while (cfg != nil) {
00518 if (config_isatom(cfg)) {
00519 if (config_isstring(cfg)) fputc('"', stdout);
00520 printf("%s", cfg->word);
00521 if (config_isstring(cfg)) fputc('"', stdout);
00522 } else {
00523 printf("{\n");
00524 print_list(indent+4, cfg->list);
00525 printf("%*s}", indent, "");
00526 }
00527 cfg= cfg->next;
00528 if (cfg != nil) fputc(' ', stdout);
00529 }
00530 printf(";\n");
00531 }
00532
00533 static void print_list(int indent, config_t *cfg)
00534 {
00535 while (cfg != nil) {
00536 if (!config_issub(cfg)) {
00537 fprintf(stderr, "Cell at \"%s\", line %u is not a sublist\n");
00538 break;
00539 }
00540 printf("%*s", indent, "");
00541 print_words(indent, cfg->list);
00542 cfg= cfg->next;
00543 }
00544 }
00545
00546 static void print_config(config_t *cfg)
00547 {
00548 if (!config_renewed(cfg)) {
00549 printf("# Config didn't change\n");
00550 } else {
00551 print_list(0, cfg);
00552 }
00553 }
00554
00555 int main(int argc, char **argv)
00556 {
00557 config_t *cfg;
00558 int c;
00559
00560 if (argc != 2) {
00561 fprintf(stderr, "One config file name please\n");
00562 exit(1);
00563 }
00564
00565 cfg= nil;
00566 do {
00567 cfg= config_read(argv[1], CFG_ESCAPED, cfg);
00568 print_config(cfg);
00569 if (!isatty(0)) break;
00570 while ((c= getchar()) != EOF && c != '\n') {}
00571 } while (c != EOF);
00572 return 0;
00573 }
00574 #endif