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