Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 1 | #include "toys.h" |
| 2 | |
| 3 | // Humor toys.h |
| 4 | struct toy_context toys; |
| 5 | char libbuf[4096], toybuf[4096]; |
| 6 | void show_help(void) {;} |
| 7 | void toy_exec(char *argv[]) {;} |
| 8 | |
| 9 | // Parse config files into data structures. |
| 10 | |
| 11 | struct symbol { |
| 12 | struct symbol *next; |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 13 | int enabled, help_indent; |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 14 | char *name, *depends; |
| 15 | struct double_list *help; |
| 16 | } *sym; |
| 17 | |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 18 | char *trim(char *s) |
| 19 | { |
| 20 | while (isspace(*s)) s++; |
| 21 | |
| 22 | return s; |
| 23 | } |
| 24 | |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 25 | char *keyword(char *name, char *line) |
| 26 | { |
| 27 | int len = strlen(name); |
| 28 | |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 29 | line = trim(line); |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 30 | if (strncmp(name, line, len)) return 0; |
| 31 | line += len; |
| 32 | if (*line && !isspace(*line)) return 0; |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 33 | line = trim(line); |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 34 | |
| 35 | return line; |
| 36 | } |
| 37 | |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 38 | char *dlist_zap(struct double_list **help) |
| 39 | { |
| 40 | struct double_list *dd = dlist_pop(help); |
| 41 | char *s = dd->data; |
| 42 | |
| 43 | free(dd); |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 44 | |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 45 | return s; |
| 46 | } |
| 47 | |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 48 | int zap_blank_lines(struct double_list **help) |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 49 | { |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 50 | int got = 0; |
| 51 | |
| 52 | while (*help) { |
| 53 | char *s; |
| 54 | |
| 55 | s = trim((*help)->data); |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 56 | |
| 57 | if (*s) break; |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 58 | got++; |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 59 | free(dlist_zap(help)); |
| 60 | } |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 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 | |
| 75 | char **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; |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 122 | } |
| 123 | |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 124 | void parse(char *filename) |
| 125 | { |
| 126 | FILE *fp = xfopen(filename, "r"); |
| 127 | struct symbol *new = 0; |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 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)) { |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 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 | |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 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 | } |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 161 | else if ((s = keyword("depends", line)) && (s = keyword("on", s))) |
| 162 | new->depends = s; |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 163 | else if (keyword("help", line)) sym->help_indent = -1; |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 164 | } |
| 165 | |
| 166 | fclose(fp); |
| 167 | } |
| 168 | |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 169 | int 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 | |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 178 | int dashsort(char **a, char **b) |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 179 | { |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 180 | char *aa = *a, *bb = *b; |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 181 | |
| 182 | if (aa[1] < bb[1]) return -1; |
| 183 | if (aa[1] > bb[1]) return 1; |
| 184 | return 0; |
| 185 | } |
| 186 | |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 187 | int dashlinesort(char **a, char **b) |
| 188 | { |
| 189 | return strcmp(*a, *b); |
| 190 | } |
| 191 | |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 192 | int main(int argc, char *argv[]) |
| 193 | { |
| 194 | FILE *fp; |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 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)) { |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 212 | struct symbol *try; |
| 213 | char *s = line+7; |
| 214 | |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 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 | |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 225 | // Collate help according to usage, depends, and .config |
| 226 | |
| 227 | // Loop through each entry, finding duplicate enabled "usage:" names |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 228 | // This is in reverse order, so last entry gets collated with previous |
| 229 | // entry until we run out of matching pairs. |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 230 | for (;;) { |
| 231 | struct symbol *throw = 0, *catch; |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 232 | char *this, *that, *cusage, *tusage, *name; |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 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; |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 238 | if (catch->help && (that = keyword("usage:", catch->help->data))) { |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 239 | struct double_list *cfrom, *tfrom, *anchor; |
| 240 | char *try, **cdashlines, **tdashlines; |
| 241 | int clen, tlen; |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 242 | |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 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 |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 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); |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 251 | if (!throw) { |
| 252 | throw = catch; |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 253 | this = that; |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 254 | |
| 255 | continue; |
| 256 | } |
| 257 | |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 258 | // Grab option description lines to collate from catch and throw |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 259 | tusage = dlist_zap(&throw->help); |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 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; |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 264 | |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 265 | // If we've got both, collate and alphebetize |
| 266 | if (cdashlines && tdashlines) { |
| 267 | char **new = xmalloc(sizeof(char *)*(clen+tlen)); |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 268 | |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 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 |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 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 | |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 334 | // The list is definitely no longer empty, so discard placeholder. |
| 335 | if (!anchor->data) dlist_zap(&anchor); |
| 336 | |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 337 | // Add new collated line (and whitespace). |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 338 | dlist_add(&anchor, xmprintf("%*cusage: %.*s %s%s%s%s", |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 339 | catch->help_indent, ' ', len, name, try ? try : "", |
| 340 | this, *this ? " " : "", that)); |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 341 | free(try); |
| 342 | dlist_add(&anchor, strdup("")); |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 343 | free(cusage); |
| 344 | free(tusage); |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 345 | throw->enabled = 0; |
Rob Landley | 0432050 | 2014-01-29 23:47:53 -0600 | [diff] [blame] | 346 | throw = catch; |
| 347 | throw->help = anchor->prev->prev; |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 348 | |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 349 | throw = catch; |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 350 | this = throw->help->data + throw->help_indent + 8 + len; |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 351 | } |
| 352 | } |
| 353 | |
| 354 | // Did we find one? |
| 355 | |
| 356 | if (!throw) break; |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 357 | } |
| 358 | |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 359 | // Print out help #defines |
| 360 | while (sym) { |
| 361 | struct double_list *dd; |
| 362 | |
| 363 | if (sym->help) { |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 364 | int i; |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 365 | char *s = xstrdup(sym->name); |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 366 | |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 367 | for (i = 0; s[i]; i++) s[i] = tolower(s[i]); |
| 368 | printf("#define help_%s \"", s); |
| 369 | free(s); |
| 370 | |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 371 | dd = sym->help; |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 372 | for (;;) { |
Rob Landley | c049bca | 2014-01-20 17:26:50 -0600 | [diff] [blame] | 373 | i = sym->help_indent; |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 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 | } |
Rob Landley | 2ded833 | 2014-01-15 09:38:31 -0600 | [diff] [blame] | 385 | putchar('\\'); |
| 386 | putchar('n'); |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 387 | dd = dd->next; |
| 388 | if (dd == sym->help) break; |
| 389 | } |
Rob Landley | e6314da | 2014-04-07 12:53:24 -0500 | [diff] [blame] | 390 | printf("\"\n\n"); |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 391 | } |
| 392 | sym = sym->next; |
| 393 | } |
Rob Landley | 1fb3ae7 | 2014-02-16 11:09:23 -0600 | [diff] [blame] | 394 | |
| 395 | return 0; |
Rob Landley | 86cafe1 | 2014-01-03 18:23:09 -0600 | [diff] [blame] | 396 | } |