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