xref: /third_party/toybox/scripts/config2help.c (revision 0f66f451)
10f66f451Sopenharmony_ci/* config2.help.c - config2hep Config.in .config > help.h
20f66f451Sopenharmony_ci
30f66f451Sopenharmony_ci   function parse() reads Config.in data into *sym list, then
40f66f451Sopenharmony_ci   we read .config and set sym->try on each enabled symbol.
50f66f451Sopenharmony_ci
60f66f451Sopenharmony_ci*/
70f66f451Sopenharmony_ci
80f66f451Sopenharmony_ci#include <ctype.h>
90f66f451Sopenharmony_ci#include <stdio.h>
100f66f451Sopenharmony_ci#include <string.h>
110f66f451Sopenharmony_ci#include <stdarg.h>
120f66f451Sopenharmony_ci#include <stdlib.h>
130f66f451Sopenharmony_ci#include <sys/types.h>
140f66f451Sopenharmony_ci#include <sys/stat.h>
150f66f451Sopenharmony_ci#include <unistd.h>
160f66f451Sopenharmony_ci#include <regex.h>
170f66f451Sopenharmony_ci#include <inttypes.h>
180f66f451Sopenharmony_ci#include <termios.h>
190f66f451Sopenharmony_ci#include <poll.h>
200f66f451Sopenharmony_ci#include <sys/socket.h>
210f66f451Sopenharmony_ci
220f66f451Sopenharmony_ci//****************** functions copied from lib/*.c ********************
230f66f451Sopenharmony_ci
240f66f451Sopenharmony_cistruct double_list {
250f66f451Sopenharmony_ci  struct double_list *next, *prev;
260f66f451Sopenharmony_ci  char *data;
270f66f451Sopenharmony_ci};
280f66f451Sopenharmony_ci
290f66f451Sopenharmony_ci// Die unless we can allocate memory.
300f66f451Sopenharmony_civoid *xmalloc(size_t size)
310f66f451Sopenharmony_ci{
320f66f451Sopenharmony_ci  void *ret = malloc(size);
330f66f451Sopenharmony_ci  if (!ret) {
340f66f451Sopenharmony_ci    fprintf(stderr, "xmalloc(%ld)", (long)size);
350f66f451Sopenharmony_ci    exit(1);
360f66f451Sopenharmony_ci  }
370f66f451Sopenharmony_ci
380f66f451Sopenharmony_ci  return ret;
390f66f451Sopenharmony_ci}
400f66f451Sopenharmony_ci
410f66f451Sopenharmony_ci// Die unless we can allocate enough space to sprintf() into.
420f66f451Sopenharmony_cichar *xmprintf(char *format, ...)
430f66f451Sopenharmony_ci{
440f66f451Sopenharmony_ci  va_list va, va2;
450f66f451Sopenharmony_ci  int len;
460f66f451Sopenharmony_ci  char *ret;
470f66f451Sopenharmony_ci
480f66f451Sopenharmony_ci  va_start(va, format);
490f66f451Sopenharmony_ci  va_copy(va2, va);
500f66f451Sopenharmony_ci
510f66f451Sopenharmony_ci  // How long is it?
520f66f451Sopenharmony_ci  len = vsnprintf(0, 0, format, va);
530f66f451Sopenharmony_ci  len++;
540f66f451Sopenharmony_ci  va_end(va);
550f66f451Sopenharmony_ci
560f66f451Sopenharmony_ci  // Allocate and do the sprintf()
570f66f451Sopenharmony_ci  ret = xmalloc(len);
580f66f451Sopenharmony_ci  vsnprintf(ret, len, format, va2);
590f66f451Sopenharmony_ci  va_end(va2);
600f66f451Sopenharmony_ci
610f66f451Sopenharmony_ci  return ret;
620f66f451Sopenharmony_ci}
630f66f451Sopenharmony_ci
640f66f451Sopenharmony_ci// Die unless we can open/create a file, returning FILE *.
650f66f451Sopenharmony_ciFILE *xfopen(char *path, char *mode)
660f66f451Sopenharmony_ci{
670f66f451Sopenharmony_ci  FILE *f = fopen(path, mode);
680f66f451Sopenharmony_ci  if (!f) {
690f66f451Sopenharmony_ci    fprintf(stderr, "No file %s", path);
700f66f451Sopenharmony_ci    exit(1);
710f66f451Sopenharmony_ci  }
720f66f451Sopenharmony_ci  return f;
730f66f451Sopenharmony_ci}
740f66f451Sopenharmony_ci
750f66f451Sopenharmony_civoid *dlist_pop(void *list)
760f66f451Sopenharmony_ci{
770f66f451Sopenharmony_ci  struct double_list **pdlist = (struct double_list **)list, *dlist = *pdlist;
780f66f451Sopenharmony_ci
790f66f451Sopenharmony_ci  if (dlist->next == dlist) *pdlist = 0;
800f66f451Sopenharmony_ci  else {
810f66f451Sopenharmony_ci    dlist->next->prev = dlist->prev;
820f66f451Sopenharmony_ci    dlist->prev->next = *pdlist = dlist->next;
830f66f451Sopenharmony_ci  }
840f66f451Sopenharmony_ci
850f66f451Sopenharmony_ci  return dlist;
860f66f451Sopenharmony_ci}
870f66f451Sopenharmony_ci
880f66f451Sopenharmony_civoid dlist_add_nomalloc(struct double_list **list, struct double_list *new)
890f66f451Sopenharmony_ci{
900f66f451Sopenharmony_ci  if (*list) {
910f66f451Sopenharmony_ci    new->next = *list;
920f66f451Sopenharmony_ci    new->prev = (*list)->prev;
930f66f451Sopenharmony_ci    (*list)->prev->next = new;
940f66f451Sopenharmony_ci    (*list)->prev = new;
950f66f451Sopenharmony_ci  } else *list = new->next = new->prev = new;
960f66f451Sopenharmony_ci}
970f66f451Sopenharmony_ci
980f66f451Sopenharmony_ci
990f66f451Sopenharmony_ci// Add an entry to the end of a doubly linked list
1000f66f451Sopenharmony_cistruct double_list *dlist_add(struct double_list **list, char *data)
1010f66f451Sopenharmony_ci{
1020f66f451Sopenharmony_ci  struct double_list *new = xmalloc(sizeof(struct double_list));
1030f66f451Sopenharmony_ci
1040f66f451Sopenharmony_ci  new->data = data;
1050f66f451Sopenharmony_ci  dlist_add_nomalloc(list, new);
1060f66f451Sopenharmony_ci
1070f66f451Sopenharmony_ci  return new;
1080f66f451Sopenharmony_ci}
1090f66f451Sopenharmony_ci
1100f66f451Sopenharmony_ci//****************** end copies of lib/*.c *************
1110f66f451Sopenharmony_ci
1120f66f451Sopenharmony_ci// Parse config files into data structures.
1130f66f451Sopenharmony_ci
1140f66f451Sopenharmony_cistruct symbol {
1150f66f451Sopenharmony_ci  struct symbol *next;
1160f66f451Sopenharmony_ci  int enabled, help_indent;
1170f66f451Sopenharmony_ci  char *name, *depends;
1180f66f451Sopenharmony_ci  struct double_list *help;
1190f66f451Sopenharmony_ci} *sym;
1200f66f451Sopenharmony_ci
1210f66f451Sopenharmony_ci// remove leading spaces
1220f66f451Sopenharmony_cichar *skip_spaces(char *s)
1230f66f451Sopenharmony_ci{
1240f66f451Sopenharmony_ci  while (isspace(*s)) s++;
1250f66f451Sopenharmony_ci
1260f66f451Sopenharmony_ci  return s;
1270f66f451Sopenharmony_ci}
1280f66f451Sopenharmony_ci
1290f66f451Sopenharmony_ci// if line starts with name (as whole word) return pointer after it, else NULL
1300f66f451Sopenharmony_cichar *keyword(char *name, char *line)
1310f66f451Sopenharmony_ci{
1320f66f451Sopenharmony_ci  int len = strlen(name);
1330f66f451Sopenharmony_ci
1340f66f451Sopenharmony_ci  line = skip_spaces(line);
1350f66f451Sopenharmony_ci  if (strncmp(name, line, len)) return 0;
1360f66f451Sopenharmony_ci  line += len;
1370f66f451Sopenharmony_ci  if (*line && !isspace(*line)) return 0;
1380f66f451Sopenharmony_ci  line = skip_spaces(line);
1390f66f451Sopenharmony_ci
1400f66f451Sopenharmony_ci  return line;
1410f66f451Sopenharmony_ci}
1420f66f451Sopenharmony_ci
1430f66f451Sopenharmony_ci// dlist_pop() freeing wrapper structure for you.
1440f66f451Sopenharmony_cichar *dlist_zap(struct double_list **help)
1450f66f451Sopenharmony_ci{
1460f66f451Sopenharmony_ci  struct double_list *dd = dlist_pop(help);
1470f66f451Sopenharmony_ci  char *s = dd->data;
1480f66f451Sopenharmony_ci
1490f66f451Sopenharmony_ci  free(dd);
1500f66f451Sopenharmony_ci
1510f66f451Sopenharmony_ci  return s;
1520f66f451Sopenharmony_ci}
1530f66f451Sopenharmony_ci
1540f66f451Sopenharmony_ciint zap_blank_lines(struct double_list **help)
1550f66f451Sopenharmony_ci{
1560f66f451Sopenharmony_ci  int got = 0;
1570f66f451Sopenharmony_ci
1580f66f451Sopenharmony_ci  while (*help) {
1590f66f451Sopenharmony_ci    char *s;
1600f66f451Sopenharmony_ci
1610f66f451Sopenharmony_ci    s = skip_spaces((*help)->data);
1620f66f451Sopenharmony_ci
1630f66f451Sopenharmony_ci    if (*s) break;
1640f66f451Sopenharmony_ci    got++;
1650f66f451Sopenharmony_ci    free(dlist_zap(help));
1660f66f451Sopenharmony_ci  }
1670f66f451Sopenharmony_ci
1680f66f451Sopenharmony_ci  return got;
1690f66f451Sopenharmony_ci}
1700f66f451Sopenharmony_ci
1710f66f451Sopenharmony_ci// Collect "-a blah" description lines following a blank line (or start).
1720f66f451Sopenharmony_ci// Returns array of removed lines with *len entries (0 for none).
1730f66f451Sopenharmony_ci
1740f66f451Sopenharmony_ci// Moves *help to new start of text (in case dash lines were at beginning).
1750f66f451Sopenharmony_ci// Sets *from to where dash lines removed from (in case they weren't).
1760f66f451Sopenharmony_ci// Discards blank lines before and after dashlines.
1770f66f451Sopenharmony_ci
1780f66f451Sopenharmony_ci// If no prefix, *help NULL. If no postfix, *from == *help
1790f66f451Sopenharmony_ci// if no dashlines returned *from == *help.
1800f66f451Sopenharmony_ci
1810f66f451Sopenharmony_cichar **grab_dashlines(struct double_list **help, struct double_list **from,
1820f66f451Sopenharmony_ci                      int *len)
1830f66f451Sopenharmony_ci{
1840f66f451Sopenharmony_ci  struct double_list *dd;
1850f66f451Sopenharmony_ci  char *s, **list;
1860f66f451Sopenharmony_ci  int count = 0;
1870f66f451Sopenharmony_ci
1880f66f451Sopenharmony_ci  *len = 0;
1890f66f451Sopenharmony_ci  zap_blank_lines(help);
1900f66f451Sopenharmony_ci  *from = *help;
1910f66f451Sopenharmony_ci
1920f66f451Sopenharmony_ci  // Find start of dash block. Must be at start or after blank line.
1930f66f451Sopenharmony_ci  for (;;) {
1940f66f451Sopenharmony_ci    s = skip_spaces((*from)->data);
1950f66f451Sopenharmony_ci    if (*s == '-' && s[1] != '-' && !count) break;
1960f66f451Sopenharmony_ci
1970f66f451Sopenharmony_ci    if (!*s) count = 0;
1980f66f451Sopenharmony_ci    else count++;
1990f66f451Sopenharmony_ci
2000f66f451Sopenharmony_ci    *from = (*from)->next;
2010f66f451Sopenharmony_ci    if (*from == *help) return 0;
2020f66f451Sopenharmony_ci  }
2030f66f451Sopenharmony_ci
2040f66f451Sopenharmony_ci  // If there was whitespace before this, zap it. This can't take out *help
2050f66f451Sopenharmony_ci  // because zap_blank_lines skipped blank lines, and we had to have at least
2060f66f451Sopenharmony_ci  // one non-blank line (a dash line) to get this far.
2070f66f451Sopenharmony_ci  while (!*skip_spaces((*from)->prev->data)) {
2080f66f451Sopenharmony_ci    *from = (*from)->prev;
2090f66f451Sopenharmony_ci    free(dlist_zap(from));
2100f66f451Sopenharmony_ci  }
2110f66f451Sopenharmony_ci
2120f66f451Sopenharmony_ci  // Count number of dashlines, copy out to array, zap trailing whitespace
2130f66f451Sopenharmony_ci  // If *help was at start of dashblock, move it with *from
2140f66f451Sopenharmony_ci  count = 0;
2150f66f451Sopenharmony_ci  dd = *from;
2160f66f451Sopenharmony_ci  if (*help == *from) *help = 0;
2170f66f451Sopenharmony_ci  for (;;) {
2180f66f451Sopenharmony_ci   if (*skip_spaces(dd->data) != '-') break;
2190f66f451Sopenharmony_ci   count++;
2200f66f451Sopenharmony_ci   if (*from == (dd = dd->next)) break;
2210f66f451Sopenharmony_ci  }
2220f66f451Sopenharmony_ci
2230f66f451Sopenharmony_ci  list = xmalloc(sizeof(char *)*count);
2240f66f451Sopenharmony_ci  *len = count;
2250f66f451Sopenharmony_ci  while (count) list[--count] = dlist_zap(from);
2260f66f451Sopenharmony_ci
2270f66f451Sopenharmony_ci  return list;
2280f66f451Sopenharmony_ci}
2290f66f451Sopenharmony_ci
2300f66f451Sopenharmony_ci// Read Config.in (and includes) to populate global struct symbol *sym list.
2310f66f451Sopenharmony_civoid parse(char *filename)
2320f66f451Sopenharmony_ci{
2330f66f451Sopenharmony_ci  FILE *fp = xfopen(filename, "r");
2340f66f451Sopenharmony_ci  struct symbol *new = 0;
2350f66f451Sopenharmony_ci
2360f66f451Sopenharmony_ci  for (;;) {
2370f66f451Sopenharmony_ci    char *s, *line = NULL;
2380f66f451Sopenharmony_ci    size_t len;
2390f66f451Sopenharmony_ci
2400f66f451Sopenharmony_ci    // Read line, trim whitespace at right edge.
2410f66f451Sopenharmony_ci    if (getline(&line, &len, fp) < 1) break;
2420f66f451Sopenharmony_ci    s = line+strlen(line);
2430f66f451Sopenharmony_ci    while (--s >= line) {
2440f66f451Sopenharmony_ci      if (!isspace(*s)) break;
2450f66f451Sopenharmony_ci      *s = 0;
2460f66f451Sopenharmony_ci    }
2470f66f451Sopenharmony_ci
2480f66f451Sopenharmony_ci    // source or config keyword at left edge?
2490f66f451Sopenharmony_ci    if (*line && !isspace(*line)) {
2500f66f451Sopenharmony_ci      if ((s = keyword("config", line))) {
2510f66f451Sopenharmony_ci        memset(new = xmalloc(sizeof(struct symbol)), 0, sizeof(struct symbol));
2520f66f451Sopenharmony_ci        new->next = sym;
2530f66f451Sopenharmony_ci        new->name = s;
2540f66f451Sopenharmony_ci        sym = new;
2550f66f451Sopenharmony_ci      } else if ((s = keyword("source", line))) parse(s);
2560f66f451Sopenharmony_ci
2570f66f451Sopenharmony_ci      continue;
2580f66f451Sopenharmony_ci    }
2590f66f451Sopenharmony_ci    if (!new) continue;
2600f66f451Sopenharmony_ci
2610f66f451Sopenharmony_ci    if (sym && sym->help_indent) {
2620f66f451Sopenharmony_ci      dlist_add(&(new->help), line);
2630f66f451Sopenharmony_ci      if (sym->help_indent < 0) {
2640f66f451Sopenharmony_ci        sym->help_indent = 0;
2650f66f451Sopenharmony_ci        while (isspace(line[sym->help_indent])) sym->help_indent++;
2660f66f451Sopenharmony_ci      }
2670f66f451Sopenharmony_ci    }
2680f66f451Sopenharmony_ci    else if ((s = keyword("depends", line)) && (s = keyword("on", s)))
2690f66f451Sopenharmony_ci      new->depends = s;
2700f66f451Sopenharmony_ci    else if (keyword("help", line)) sym->help_indent = -1;
2710f66f451Sopenharmony_ci  }
2720f66f451Sopenharmony_ci
2730f66f451Sopenharmony_ci  fclose(fp);
2740f66f451Sopenharmony_ci}
2750f66f451Sopenharmony_ci
2760f66f451Sopenharmony_ciint charsort(void *a, void *b)
2770f66f451Sopenharmony_ci{
2780f66f451Sopenharmony_ci  char *aa = a, *bb = b;
2790f66f451Sopenharmony_ci
2800f66f451Sopenharmony_ci  if (*aa < *bb) return -1;
2810f66f451Sopenharmony_ci  if (*aa > *bb) return 1;
2820f66f451Sopenharmony_ci  return 0;
2830f66f451Sopenharmony_ci}
2840f66f451Sopenharmony_ci
2850f66f451Sopenharmony_ciint dashsort(char **a, char **b)
2860f66f451Sopenharmony_ci{
2870f66f451Sopenharmony_ci  char *aa = *a, *bb = *b;
2880f66f451Sopenharmony_ci
2890f66f451Sopenharmony_ci  if (aa[1] < bb[1]) return -1;
2900f66f451Sopenharmony_ci  if (aa[1] > bb[1]) return 1;
2910f66f451Sopenharmony_ci  return 0;
2920f66f451Sopenharmony_ci}
2930f66f451Sopenharmony_ci
2940f66f451Sopenharmony_ciint dashlinesort(char **a, char **b)
2950f66f451Sopenharmony_ci{
2960f66f451Sopenharmony_ci  return strcmp(*a, *b);
2970f66f451Sopenharmony_ci}
2980f66f451Sopenharmony_ci
2990f66f451Sopenharmony_ci// Three stages: read data, collate entries, output results.
3000f66f451Sopenharmony_ci
3010f66f451Sopenharmony_ciint main(int argc, char *argv[])
3020f66f451Sopenharmony_ci{
3030f66f451Sopenharmony_ci  FILE *fp;
3040f66f451Sopenharmony_ci
3050f66f451Sopenharmony_ci  if (argc != 3) {
3060f66f451Sopenharmony_ci    fprintf(stderr, "usage: config2help Config.in .config\n");
3070f66f451Sopenharmony_ci    exit(1);
3080f66f451Sopenharmony_ci  }
3090f66f451Sopenharmony_ci
3100f66f451Sopenharmony_ci  // Stage 1: read data. Read Config.in to global 'struct symbol *sym' list,
3110f66f451Sopenharmony_ci  // then read .config to set "enabled" member of each enabled symbol.
3120f66f451Sopenharmony_ci
3130f66f451Sopenharmony_ci  // Read Config.in
3140f66f451Sopenharmony_ci  parse(argv[1]);
3150f66f451Sopenharmony_ci
3160f66f451Sopenharmony_ci  // read .config
3170f66f451Sopenharmony_ci  fp = xfopen(argv[2], "r");
3180f66f451Sopenharmony_ci  for (;;) {
3190f66f451Sopenharmony_ci    char *line = NULL;
3200f66f451Sopenharmony_ci    size_t len;
3210f66f451Sopenharmony_ci
3220f66f451Sopenharmony_ci    if (getline(&line, &len, fp) < 1) break;
3230f66f451Sopenharmony_ci    if (!strncmp("CONFIG_", line, 7)) {
3240f66f451Sopenharmony_ci      struct symbol *try;
3250f66f451Sopenharmony_ci      char *s = line+7;
3260f66f451Sopenharmony_ci
3270f66f451Sopenharmony_ci      for (try=sym; try; try=try->next) {
3280f66f451Sopenharmony_ci        len = strlen(try->name);
3290f66f451Sopenharmony_ci        if (!strncmp(try->name, s, len) && s[len]=='=' && s[len+1]=='y') {
3300f66f451Sopenharmony_ci          try->enabled++;
3310f66f451Sopenharmony_ci          break;
3320f66f451Sopenharmony_ci        }
3330f66f451Sopenharmony_ci      }
3340f66f451Sopenharmony_ci    }
3350f66f451Sopenharmony_ci  }
3360f66f451Sopenharmony_ci
3370f66f451Sopenharmony_ci  // Stage 2: process data.
3380f66f451Sopenharmony_ci
3390f66f451Sopenharmony_ci  // Collate help according to usage, depends, and .config
3400f66f451Sopenharmony_ci
3410f66f451Sopenharmony_ci  // Loop through each entry, finding duplicate enabled "usage:" names
3420f66f451Sopenharmony_ci  // This is in reverse order, so last entry gets collated with previous
3430f66f451Sopenharmony_ci  // entry until we run out of matching pairs.
3440f66f451Sopenharmony_ci  for (;;) {
3450f66f451Sopenharmony_ci    struct symbol *throw = 0, *catch;
3460f66f451Sopenharmony_ci    char *this, *that, *cusage, *tusage, *name = 0;
3470f66f451Sopenharmony_ci    int len;
3480f66f451Sopenharmony_ci
3490f66f451Sopenharmony_ci    // find a usage: name and collate all enabled entries with that name
3500f66f451Sopenharmony_ci    for (catch = sym; catch; catch = catch->next) {
3510f66f451Sopenharmony_ci      if (catch->enabled != 1) continue;
3520f66f451Sopenharmony_ci      if (catch->help && (that = keyword("usage:", catch->help->data))) {
3530f66f451Sopenharmony_ci        struct double_list *cfrom, *tfrom, *anchor;
3540f66f451Sopenharmony_ci        char *try, **cdashlines, **tdashlines, *usage;
3550f66f451Sopenharmony_ci        int clen, tlen;
3560f66f451Sopenharmony_ci
3570f66f451Sopenharmony_ci        // Align usage: lines, finding a matching pair so we can suck help
3580f66f451Sopenharmony_ci        // text out of throw into catch, copying from this to that
3590f66f451Sopenharmony_ci        if (!throw) usage = that;
3600f66f451Sopenharmony_ci        else if (strncmp(name, that, len) || !isspace(that[len])) continue;
3610f66f451Sopenharmony_ci        catch->enabled++;
3620f66f451Sopenharmony_ci        while (!isspace(*that) && *that) that++;
3630f66f451Sopenharmony_ci        if (!throw) len = that-usage;
3640f66f451Sopenharmony_ci        free(name);
3650f66f451Sopenharmony_ci        name = strndup(usage, len);
3660f66f451Sopenharmony_ci        that = skip_spaces(that);
3670f66f451Sopenharmony_ci        if (!throw) {
3680f66f451Sopenharmony_ci          throw = catch;
3690f66f451Sopenharmony_ci          this = that;
3700f66f451Sopenharmony_ci
3710f66f451Sopenharmony_ci          continue;
3720f66f451Sopenharmony_ci        }
3730f66f451Sopenharmony_ci
3740f66f451Sopenharmony_ci        // Grab option description lines to collate from catch and throw
3750f66f451Sopenharmony_ci        tusage = dlist_zap(&throw->help);
3760f66f451Sopenharmony_ci        tdashlines = grab_dashlines(&throw->help, &tfrom, &tlen);
3770f66f451Sopenharmony_ci        cusage = dlist_zap(&catch->help);
3780f66f451Sopenharmony_ci        cdashlines = grab_dashlines(&catch->help, &cfrom, &clen);
3790f66f451Sopenharmony_ci        anchor = catch->help;
3800f66f451Sopenharmony_ci
3810f66f451Sopenharmony_ci        // If we've got both, collate and alphebetize
3820f66f451Sopenharmony_ci        if (cdashlines && tdashlines) {
3830f66f451Sopenharmony_ci          char **new = xmalloc(sizeof(char *)*(clen+tlen));
3840f66f451Sopenharmony_ci
3850f66f451Sopenharmony_ci          memcpy(new, cdashlines, sizeof(char *)*clen);
3860f66f451Sopenharmony_ci          memcpy(new+clen, tdashlines, sizeof(char *)*tlen);
3870f66f451Sopenharmony_ci          free(cdashlines);
3880f66f451Sopenharmony_ci          free(tdashlines);
3890f66f451Sopenharmony_ci          qsort(new, clen+tlen, sizeof(char *), (void *)dashlinesort);
3900f66f451Sopenharmony_ci          cdashlines = new;
3910f66f451Sopenharmony_ci
3920f66f451Sopenharmony_ci        // If just one, make sure it's in catch.
3930f66f451Sopenharmony_ci        } else if (tdashlines) cdashlines = tdashlines;
3940f66f451Sopenharmony_ci
3950f66f451Sopenharmony_ci        // If throw had a prefix, insert it before dashlines, with a
3960f66f451Sopenharmony_ci        // blank line if catch had a prefix.
3970f66f451Sopenharmony_ci        if (tfrom && tfrom != throw->help) {
3980f66f451Sopenharmony_ci          if (throw->help || catch->help) dlist_add(&cfrom, strdup(""));
3990f66f451Sopenharmony_ci          else {
4000f66f451Sopenharmony_ci            dlist_add(&cfrom, 0);
4010f66f451Sopenharmony_ci            anchor = cfrom->prev;
4020f66f451Sopenharmony_ci          }
4030f66f451Sopenharmony_ci          while (throw->help && throw->help != tfrom)
4040f66f451Sopenharmony_ci            dlist_add(&cfrom, dlist_zap(&throw->help));
4050f66f451Sopenharmony_ci          if (cfrom && cfrom->prev->data && *skip_spaces(cfrom->prev->data))
4060f66f451Sopenharmony_ci            dlist_add(&cfrom, strdup(""));
4070f66f451Sopenharmony_ci        }
4080f66f451Sopenharmony_ci        if (!anchor) {
4090f66f451Sopenharmony_ci          dlist_add(&cfrom, 0);
4100f66f451Sopenharmony_ci          anchor = cfrom->prev;
4110f66f451Sopenharmony_ci        }
4120f66f451Sopenharmony_ci
4130f66f451Sopenharmony_ci        // Splice sorted lines back in place
4140f66f451Sopenharmony_ci        if (cdashlines) {
4150f66f451Sopenharmony_ci          tlen += clen;
4160f66f451Sopenharmony_ci
4170f66f451Sopenharmony_ci          for (clen = 0; clen < tlen; clen++)
4180f66f451Sopenharmony_ci            dlist_add(&cfrom, cdashlines[clen]);
4190f66f451Sopenharmony_ci        }
4200f66f451Sopenharmony_ci
4210f66f451Sopenharmony_ci        // If there were no dashlines, text would be considered prefix, so
4220f66f451Sopenharmony_ci        // the list is definitely no longer empty, so discard placeholder.
4230f66f451Sopenharmony_ci        if (!anchor->data) dlist_zap(&anchor);
4240f66f451Sopenharmony_ci
4250f66f451Sopenharmony_ci        // zap whitespace at end of catch help text
4260f66f451Sopenharmony_ci        while (!*skip_spaces(anchor->prev->data)) {
4270f66f451Sopenharmony_ci          anchor = anchor->prev;
4280f66f451Sopenharmony_ci          free(dlist_zap(&anchor));
4290f66f451Sopenharmony_ci        }
4300f66f451Sopenharmony_ci
4310f66f451Sopenharmony_ci        // Append trailing lines.
4320f66f451Sopenharmony_ci        while (tfrom) dlist_add(&anchor, dlist_zap(&tfrom));
4330f66f451Sopenharmony_ci
4340f66f451Sopenharmony_ci        // Collate first [-abc] option block in usage: lines
4350f66f451Sopenharmony_ci        try = 0;
4360f66f451Sopenharmony_ci        if (*this == '[' && this[1] == '-' && this[2] != '-' &&
4370f66f451Sopenharmony_ci            *that == '[' && that[1] == '-' && that[2] != '-')
4380f66f451Sopenharmony_ci        {
4390f66f451Sopenharmony_ci          char *from = this+2, *to = that+2;
4400f66f451Sopenharmony_ci          int ff = strcspn(from, " ]"), tt = strcspn(to, " ]");
4410f66f451Sopenharmony_ci
4420f66f451Sopenharmony_ci          if (from[ff] == ']' && to[tt] == ']') {
4430f66f451Sopenharmony_ci            try = xmprintf("[-%.*s%.*s] ", ff, from, tt, to);
4440f66f451Sopenharmony_ci            qsort(try+2, ff+tt, 1, (void *)charsort);
4450f66f451Sopenharmony_ci            this = skip_spaces(this+ff+3);
4460f66f451Sopenharmony_ci            that = skip_spaces(that+tt+3);
4470f66f451Sopenharmony_ci          }
4480f66f451Sopenharmony_ci        }
4490f66f451Sopenharmony_ci
4500f66f451Sopenharmony_ci        // The list is definitely no longer empty, so discard placeholder.
4510f66f451Sopenharmony_ci        if (!anchor->data) dlist_zap(&anchor);
4520f66f451Sopenharmony_ci
4530f66f451Sopenharmony_ci        // Add new collated line (and whitespace).
4540f66f451Sopenharmony_ci        dlist_add(&anchor, xmprintf("%*cusage: %.*s %s%s%s%s",
4550f66f451Sopenharmony_ci                  catch->help_indent, ' ', len, name, try ? try : "",
4560f66f451Sopenharmony_ci                  this, *this ? " " : "", that));
4570f66f451Sopenharmony_ci        free(try);
4580f66f451Sopenharmony_ci        dlist_add(&anchor, strdup(""));
4590f66f451Sopenharmony_ci        free(cusage);
4600f66f451Sopenharmony_ci        free(tusage);
4610f66f451Sopenharmony_ci        throw->enabled = 0;
4620f66f451Sopenharmony_ci        throw = catch;
4630f66f451Sopenharmony_ci        throw->help = anchor->prev->prev;
4640f66f451Sopenharmony_ci
4650f66f451Sopenharmony_ci        throw = catch;
4660f66f451Sopenharmony_ci        this = throw->help->data + throw->help_indent + 8 + len;
4670f66f451Sopenharmony_ci      }
4680f66f451Sopenharmony_ci    }
4690f66f451Sopenharmony_ci
4700f66f451Sopenharmony_ci    // Did we find one?
4710f66f451Sopenharmony_ci
4720f66f451Sopenharmony_ci    if (!throw) break;
4730f66f451Sopenharmony_ci  }
4740f66f451Sopenharmony_ci
4750f66f451Sopenharmony_ci  // Stage 3: output results to stdout.
4760f66f451Sopenharmony_ci
4770f66f451Sopenharmony_ci  // Print out help #defines
4780f66f451Sopenharmony_ci  while (sym) {
4790f66f451Sopenharmony_ci    struct double_list *dd;
4800f66f451Sopenharmony_ci
4810f66f451Sopenharmony_ci    if (sym->help) {
4820f66f451Sopenharmony_ci      int i, blank;
4830f66f451Sopenharmony_ci      char *s;
4840f66f451Sopenharmony_ci
4850f66f451Sopenharmony_ci      strcpy(s = xmalloc(strlen(sym->name)+1), sym->name);
4860f66f451Sopenharmony_ci
4870f66f451Sopenharmony_ci      for (i = 0; s[i]; i++) s[i] = tolower(s[i]);
4880f66f451Sopenharmony_ci      printf("#define HELP_%s \"", s);
4890f66f451Sopenharmony_ci      free(s);
4900f66f451Sopenharmony_ci
4910f66f451Sopenharmony_ci      dd = sym->help;
4920f66f451Sopenharmony_ci      blank = 0;
4930f66f451Sopenharmony_ci      for (;;) {
4940f66f451Sopenharmony_ci
4950f66f451Sopenharmony_ci        // Trim leading whitespace
4960f66f451Sopenharmony_ci        s = dd->data;
4970f66f451Sopenharmony_ci        i = sym->help_indent;
4980f66f451Sopenharmony_ci        while (isspace(*s) && i--) s++;
4990f66f451Sopenharmony_ci
5000f66f451Sopenharmony_ci        // Only one blank line between nonblank lines, not at start or end.
5010f66f451Sopenharmony_ci        if (!*s) blank = 2;
5020f66f451Sopenharmony_ci        else {
5030f66f451Sopenharmony_ci          while (blank--) {
5040f66f451Sopenharmony_ci            putchar('\\');
5050f66f451Sopenharmony_ci            putchar('n');
5060f66f451Sopenharmony_ci          }
5070f66f451Sopenharmony_ci          blank = 1;
5080f66f451Sopenharmony_ci        }
5090f66f451Sopenharmony_ci
5100f66f451Sopenharmony_ci        for (i=0; s[i]; i++) {
5110f66f451Sopenharmony_ci          if (s[i] == '"' || s[i] == '\\') putchar('\\');
5120f66f451Sopenharmony_ci          putchar(s[i]);
5130f66f451Sopenharmony_ci        }
5140f66f451Sopenharmony_ci        dd = dd->next;
5150f66f451Sopenharmony_ci        if (dd == sym->help) break;
5160f66f451Sopenharmony_ci      }
5170f66f451Sopenharmony_ci      printf("\"\n\n");
5180f66f451Sopenharmony_ci    }
5190f66f451Sopenharmony_ci    sym = sym->next;
5200f66f451Sopenharmony_ci  }
5210f66f451Sopenharmony_ci
5220f66f451Sopenharmony_ci  return 0;
5230f66f451Sopenharmony_ci}
524