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