config2help.c 13 KB

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