1/* config2.help.c - config2hep Config.in .config > help.h 2 3 function parse() reads Config.in data into *sym list, then 4 we read .config and set sym->try on each enabled symbol. 5 6*/ 7 8#include <ctype.h> 9#include <stdio.h> 10#include <string.h> 11#include <stdarg.h> 12#include <stdlib.h> 13#include <sys/types.h> 14#include <sys/stat.h> 15#include <unistd.h> 16#include <regex.h> 17#include <inttypes.h> 18#include <termios.h> 19#include <poll.h> 20#include <sys/socket.h> 21 22//****************** functions copied from lib/*.c ******************** 23 24struct double_list { 25 struct double_list *next, *prev; 26 char *data; 27}; 28 29// Die unless we can allocate memory. 30void *xmalloc(size_t size) 31{ 32 void *ret = malloc(size); 33 if (!ret) { 34 fprintf(stderr, "xmalloc(%ld)", (long)size); 35 exit(1); 36 } 37 38 return ret; 39} 40 41// Die unless we can allocate enough space to sprintf() into. 42char *xmprintf(char *format, ...) 43{ 44 va_list va, va2; 45 int len; 46 char *ret; 47 48 va_start(va, format); 49 va_copy(va2, va); 50 51 // How long is it? 52 len = vsnprintf(0, 0, format, va); 53 len++; 54 va_end(va); 55 56 // Allocate and do the sprintf() 57 ret = xmalloc(len); 58 vsnprintf(ret, len, format, va2); 59 va_end(va2); 60 61 return ret; 62} 63 64// Die unless we can open/create a file, returning FILE *. 65FILE *xfopen(char *path, char *mode) 66{ 67 FILE *f = fopen(path, mode); 68 if (!f) { 69 fprintf(stderr, "No file %s", path); 70 exit(1); 71 } 72 return f; 73} 74 75void *dlist_pop(void *list) 76{ 77 struct double_list **pdlist = (struct double_list **)list, *dlist = *pdlist; 78 79 if (dlist->next == dlist) *pdlist = 0; 80 else { 81 dlist->next->prev = dlist->prev; 82 dlist->prev->next = *pdlist = dlist->next; 83 } 84 85 return dlist; 86} 87 88void dlist_add_nomalloc(struct double_list **list, struct double_list *new) 89{ 90 if (*list) { 91 new->next = *list; 92 new->prev = (*list)->prev; 93 (*list)->prev->next = new; 94 (*list)->prev = new; 95 } else *list = new->next = new->prev = new; 96} 97 98 99// Add an entry to the end of a doubly linked list 100struct double_list *dlist_add(struct double_list **list, char *data) 101{ 102 struct double_list *new = xmalloc(sizeof(struct double_list)); 103 104 new->data = data; 105 dlist_add_nomalloc(list, new); 106 107 return new; 108} 109 110//****************** end copies of lib/*.c ************* 111 112// Parse config files into data structures. 113 114struct symbol { 115 struct symbol *next; 116 int enabled, help_indent; 117 char *name, *depends; 118 struct double_list *help; 119} *sym; 120 121// remove leading spaces 122char *skip_spaces(char *s) 123{ 124 while (isspace(*s)) s++; 125 126 return s; 127} 128 129// if line starts with name (as whole word) return pointer after it, else NULL 130char *keyword(char *name, char *line) 131{ 132 int len = strlen(name); 133 134 line = skip_spaces(line); 135 if (strncmp(name, line, len)) return 0; 136 line += len; 137 if (*line && !isspace(*line)) return 0; 138 line = skip_spaces(line); 139 140 return line; 141} 142 143// dlist_pop() freeing wrapper structure for you. 144char *dlist_zap(struct double_list **help) 145{ 146 struct double_list *dd = dlist_pop(help); 147 char *s = dd->data; 148 149 free(dd); 150 151 return s; 152} 153 154int zap_blank_lines(struct double_list **help) 155{ 156 int got = 0; 157 158 while (*help) { 159 char *s; 160 161 s = skip_spaces((*help)->data); 162 163 if (*s) break; 164 got++; 165 free(dlist_zap(help)); 166 } 167 168 return got; 169} 170 171// Collect "-a blah" description lines following a blank line (or start). 172// Returns array of removed lines with *len entries (0 for none). 173 174// Moves *help to new start of text (in case dash lines were at beginning). 175// Sets *from to where dash lines removed from (in case they weren't). 176// Discards blank lines before and after dashlines. 177 178// If no prefix, *help NULL. If no postfix, *from == *help 179// if no dashlines returned *from == *help. 180 181char **grab_dashlines(struct double_list **help, struct double_list **from, 182 int *len) 183{ 184 struct double_list *dd; 185 char *s, **list; 186 int count = 0; 187 188 *len = 0; 189 zap_blank_lines(help); 190 *from = *help; 191 192 // Find start of dash block. Must be at start or after blank line. 193 for (;;) { 194 s = skip_spaces((*from)->data); 195 if (*s == '-' && s[1] != '-' && !count) break; 196 197 if (!*s) count = 0; 198 else count++; 199 200 *from = (*from)->next; 201 if (*from == *help) return 0; 202 } 203 204 // If there was whitespace before this, zap it. This can't take out *help 205 // because zap_blank_lines skipped blank lines, and we had to have at least 206 // one non-blank line (a dash line) to get this far. 207 while (!*skip_spaces((*from)->prev->data)) { 208 *from = (*from)->prev; 209 free(dlist_zap(from)); 210 } 211 212 // Count number of dashlines, copy out to array, zap trailing whitespace 213 // If *help was at start of dashblock, move it with *from 214 count = 0; 215 dd = *from; 216 if (*help == *from) *help = 0; 217 for (;;) { 218 if (*skip_spaces(dd->data) != '-') break; 219 count++; 220 if (*from == (dd = dd->next)) break; 221 } 222 223 list = xmalloc(sizeof(char *)*count); 224 *len = count; 225 while (count) list[--count] = dlist_zap(from); 226 227 return list; 228} 229 230// Read Config.in (and includes) to populate global struct symbol *sym list. 231void parse(char *filename) 232{ 233 FILE *fp = xfopen(filename, "r"); 234 struct symbol *new = 0; 235 236 for (;;) { 237 char *s, *line = NULL; 238 size_t len; 239 240 // Read line, trim whitespace at right edge. 241 if (getline(&line, &len, fp) < 1) break; 242 s = line+strlen(line); 243 while (--s >= line) { 244 if (!isspace(*s)) break; 245 *s = 0; 246 } 247 248 // source or config keyword at left edge? 249 if (*line && !isspace(*line)) { 250 if ((s = keyword("config", line))) { 251 memset(new = xmalloc(sizeof(struct symbol)), 0, sizeof(struct symbol)); 252 new->next = sym; 253 new->name = s; 254 sym = new; 255 } else if ((s = keyword("source", line))) parse(s); 256 257 continue; 258 } 259 if (!new) continue; 260 261 if (sym && sym->help_indent) { 262 dlist_add(&(new->help), line); 263 if (sym->help_indent < 0) { 264 sym->help_indent = 0; 265 while (isspace(line[sym->help_indent])) sym->help_indent++; 266 } 267 } 268 else if ((s = keyword("depends", line)) && (s = keyword("on", s))) 269 new->depends = s; 270 else if (keyword("help", line)) sym->help_indent = -1; 271 } 272 273 fclose(fp); 274} 275 276int charsort(void *a, void *b) 277{ 278 char *aa = a, *bb = b; 279 280 if (*aa < *bb) return -1; 281 if (*aa > *bb) return 1; 282 return 0; 283} 284 285int dashsort(char **a, char **b) 286{ 287 char *aa = *a, *bb = *b; 288 289 if (aa[1] < bb[1]) return -1; 290 if (aa[1] > bb[1]) return 1; 291 return 0; 292} 293 294int dashlinesort(char **a, char **b) 295{ 296 return strcmp(*a, *b); 297} 298 299// Three stages: read data, collate entries, output results. 300 301int main(int argc, char *argv[]) 302{ 303 FILE *fp; 304 305 if (argc != 3) { 306 fprintf(stderr, "usage: config2help Config.in .config\n"); 307 exit(1); 308 } 309 310 // Stage 1: read data. Read Config.in to global 'struct symbol *sym' list, 311 // then read .config to set "enabled" member of each enabled symbol. 312 313 // Read Config.in 314 parse(argv[1]); 315 316 // read .config 317 fp = xfopen(argv[2], "r"); 318 for (;;) { 319 char *line = NULL; 320 size_t len; 321 322 if (getline(&line, &len, fp) < 1) break; 323 if (!strncmp("CONFIG_", line, 7)) { 324 struct symbol *try; 325 char *s = line+7; 326 327 for (try=sym; try; try=try->next) { 328 len = strlen(try->name); 329 if (!strncmp(try->name, s, len) && s[len]=='=' && s[len+1]=='y') { 330 try->enabled++; 331 break; 332 } 333 } 334 } 335 } 336 337 // Stage 2: process data. 338 339 // Collate help according to usage, depends, and .config 340 341 // Loop through each entry, finding duplicate enabled "usage:" names 342 // This is in reverse order, so last entry gets collated with previous 343 // entry until we run out of matching pairs. 344 for (;;) { 345 struct symbol *throw = 0, *catch; 346 char *this, *that, *cusage, *tusage, *name = 0; 347 int len; 348 349 // find a usage: name and collate all enabled entries with that name 350 for (catch = sym; catch; catch = catch->next) { 351 if (catch->enabled != 1) continue; 352 if (catch->help && (that = keyword("usage:", catch->help->data))) { 353 struct double_list *cfrom, *tfrom, *anchor; 354 char *try, **cdashlines, **tdashlines, *usage; 355 int clen, tlen; 356 357 // Align usage: lines, finding a matching pair so we can suck help 358 // text out of throw into catch, copying from this to that 359 if (!throw) usage = that; 360 else if (strncmp(name, that, len) || !isspace(that[len])) continue; 361 catch->enabled++; 362 while (!isspace(*that) && *that) that++; 363 if (!throw) len = that-usage; 364 free(name); 365 name = strndup(usage, len); 366 that = skip_spaces(that); 367 if (!throw) { 368 throw = catch; 369 this = that; 370 371 continue; 372 } 373 374 // Grab option description lines to collate from catch and throw 375 tusage = dlist_zap(&throw->help); 376 tdashlines = grab_dashlines(&throw->help, &tfrom, &tlen); 377 cusage = dlist_zap(&catch->help); 378 cdashlines = grab_dashlines(&catch->help, &cfrom, &clen); 379 anchor = catch->help; 380 381 // If we've got both, collate and alphebetize 382 if (cdashlines && tdashlines) { 383 char **new = xmalloc(sizeof(char *)*(clen+tlen)); 384 385 memcpy(new, cdashlines, sizeof(char *)*clen); 386 memcpy(new+clen, tdashlines, sizeof(char *)*tlen); 387 free(cdashlines); 388 free(tdashlines); 389 qsort(new, clen+tlen, sizeof(char *), (void *)dashlinesort); 390 cdashlines = new; 391 392 // If just one, make sure it's in catch. 393 } else if (tdashlines) cdashlines = tdashlines; 394 395 // If throw had a prefix, insert it before dashlines, with a 396 // blank line if catch had a prefix. 397 if (tfrom && tfrom != throw->help) { 398 if (throw->help || catch->help) dlist_add(&cfrom, strdup("")); 399 else { 400 dlist_add(&cfrom, 0); 401 anchor = cfrom->prev; 402 } 403 while (throw->help && throw->help != tfrom) 404 dlist_add(&cfrom, dlist_zap(&throw->help)); 405 if (cfrom && cfrom->prev->data && *skip_spaces(cfrom->prev->data)) 406 dlist_add(&cfrom, strdup("")); 407 } 408 if (!anchor) { 409 dlist_add(&cfrom, 0); 410 anchor = cfrom->prev; 411 } 412 413 // Splice sorted lines back in place 414 if (cdashlines) { 415 tlen += clen; 416 417 for (clen = 0; clen < tlen; clen++) 418 dlist_add(&cfrom, cdashlines[clen]); 419 } 420 421 // If there were no dashlines, text would be considered prefix, so 422 // the list is definitely no longer empty, so discard placeholder. 423 if (!anchor->data) dlist_zap(&anchor); 424 425 // zap whitespace at end of catch help text 426 while (!*skip_spaces(anchor->prev->data)) { 427 anchor = anchor->prev; 428 free(dlist_zap(&anchor)); 429 } 430 431 // Append trailing lines. 432 while (tfrom) dlist_add(&anchor, dlist_zap(&tfrom)); 433 434 // Collate first [-abc] option block in usage: lines 435 try = 0; 436 if (*this == '[' && this[1] == '-' && this[2] != '-' && 437 *that == '[' && that[1] == '-' && that[2] != '-') 438 { 439 char *from = this+2, *to = that+2; 440 int ff = strcspn(from, " ]"), tt = strcspn(to, " ]"); 441 442 if (from[ff] == ']' && to[tt] == ']') { 443 try = xmprintf("[-%.*s%.*s] ", ff, from, tt, to); 444 qsort(try+2, ff+tt, 1, (void *)charsort); 445 this = skip_spaces(this+ff+3); 446 that = skip_spaces(that+tt+3); 447 } 448 } 449 450 // The list is definitely no longer empty, so discard placeholder. 451 if (!anchor->data) dlist_zap(&anchor); 452 453 // Add new collated line (and whitespace). 454 dlist_add(&anchor, xmprintf("%*cusage: %.*s %s%s%s%s", 455 catch->help_indent, ' ', len, name, try ? try : "", 456 this, *this ? " " : "", that)); 457 free(try); 458 dlist_add(&anchor, strdup("")); 459 free(cusage); 460 free(tusage); 461 throw->enabled = 0; 462 throw = catch; 463 throw->help = anchor->prev->prev; 464 465 throw = catch; 466 this = throw->help->data + throw->help_indent + 8 + len; 467 } 468 } 469 470 // Did we find one? 471 472 if (!throw) break; 473 } 474 475 // Stage 3: output results to stdout. 476 477 // Print out help #defines 478 while (sym) { 479 struct double_list *dd; 480 481 if (sym->help) { 482 int i, blank; 483 char *s; 484 485 strcpy(s = xmalloc(strlen(sym->name)+1), sym->name); 486 487 for (i = 0; s[i]; i++) s[i] = tolower(s[i]); 488 printf("#define HELP_%s \"", s); 489 free(s); 490 491 dd = sym->help; 492 blank = 0; 493 for (;;) { 494 495 // Trim leading whitespace 496 s = dd->data; 497 i = sym->help_indent; 498 while (isspace(*s) && i--) s++; 499 500 // Only one blank line between nonblank lines, not at start or end. 501 if (!*s) blank = 2; 502 else { 503 while (blank--) { 504 putchar('\\'); 505 putchar('n'); 506 } 507 blank = 1; 508 } 509 510 for (i=0; s[i]; i++) { 511 if (s[i] == '"' || s[i] == '\\') putchar('\\'); 512 putchar(s[i]); 513 } 514 dd = dd->next; 515 if (dd == sym->help) break; 516 } 517 printf("\"\n\n"); 518 } 519 sym = sym->next; 520 } 521 522 return 0; 523} 524