2 * Copyright notice from original mutt:
3 * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
11 ** This program parses mutt's init.h and generates documentation in
12 ** three different formats:
14 ** -> a commented muttrc configuration file
15 ** -> nroff, suitable for inclusion in a manual page
16 ** -> linuxdoc-sgml, suitable for inclusion in the
43 extern char *sys_errlist[];
46 #define strerror(x) ((x) > 0 && (x) < sys_nerr) ? sys_errlist[(x)] : 0
47 #endif /* !HAVE_STRERROR */
53 #define STRLEN(s) (s ? strlen(s) : 0)
61 static int outcount = 0;
62 static var_t *outbuf = NULL;
64 static int var_cmp (const void *a, const void *b)
66 return (strcmp (((var_t *) a)->name, ((var_t *) b)->name));
69 enum output_formats_t {
70 F_CONF, F_MAN, F_SGML, F_NONE
76 #define D_TAB (1 << 3)
78 #define D_INIT (1 << 5)
98 enum output_formats_t OutputFormat = F_NONE;
102 static char *get_token (char *, size_t, char *);
103 static char *skip_ws (char *);
104 static const char *type2human (int);
105 static int buff2type (const char *);
106 static int flush_doc (int);
107 static int handle_docline (char *, int);
108 static int print_it (int, char *, int);
109 static void print_confline (const char *, int, const char *);
110 static void handle_confline (char *);
111 static void makedoc (FILE *, FILE *);
112 static void pretty_default (char *, size_t, const char *, int);
113 static int sgml_fputc (int);
114 static int sgml_fputs (const char *);
115 static void add_var (const char *);
116 static int add_s (const char *);
117 static int add_c (int);
119 int main (int argc, char *argv[])
124 if ((Progname = strrchr (argv[0], '/')))
129 while ((c = getopt (argc, argv, "cmsd")) != EOF) {
132 OutputFormat = F_CONF;
135 OutputFormat = F_MAN;
138 OutputFormat = F_SGML;
145 fprintf (stderr, "%s: bad command line parameter.\n", Progname);
151 if (optind != argc) {
152 if ((f = fopen (argv[optind], "r")) == NULL) {
153 fprintf (stderr, "%s: Can't open %s (%s).\n",
154 Progname, argv[optind], strerror (errno));
161 switch (OutputFormat) {
169 fprintf (stderr, "%s: No output format specified.\n", Progname);
180 static void add_var (const char *name)
182 outbuf = realloc (outbuf, (++outcount) * sizeof (var_t));
183 outbuf[outcount - 1].seen = 0;
184 outbuf[outcount - 1].name = strdup (name);
185 outbuf[outcount - 1].descr = NULL;
188 static int add (const char *s)
190 size_t lnew = STRLEN (s), lold = STRLEN (outbuf[outcount - 1].descr);
194 if (!outbuf[outcount - 1].seen) {
196 outbuf[outcount - 1].seen = 1;
200 outbuf[outcount - 1].descr = strdup (s);
202 outbuf[outcount - 1].descr =
203 realloc (outbuf[outcount - 1].descr, lold + lnew + 1);
204 memcpy (&(outbuf[outcount - 1].descr[lold - 1]) + 1, s, lnew);
206 outbuf[outcount - 1].descr[lold + lnew] = '\0';
210 static int add_c (int c)
212 char buf[2] = "\0\0";
218 static void makedoc (FILE * in, FILE * out)
220 char buffer[BUFFSIZE];
221 char token[BUFFSIZE];
225 int docstat = D_INIT;
227 while ((fgets (buffer, sizeof (buffer), in))) {
229 if ((p = strchr (buffer, '\n')) == NULL) {
230 fprintf (stderr, "%s: Line %d too long. Ask a wizard to enlarge\n"
231 "%s: my buffer size.\n", Progname, line, Progname);
237 if (!(p = get_token (token, sizeof (token), buffer)))
241 fprintf (stderr, "%s: line %d. first token: \"%s\".\n",
242 Progname, line, token);
245 if (!strcmp (token, "/*++*/"))
247 else if (!strcmp (token, "/*--*/")) {
248 docstat = flush_doc (docstat);
251 else if (active && (!strcmp (token, "/**") || !strcmp (token, "**")))
252 docstat = handle_docline (p, docstat);
253 else if (active && !strcmp (token, "{")) {
254 docstat = flush_doc (docstat);
260 qsort (outbuf, outcount, sizeof (var_t), &var_cmp);
261 for (line = 0; line < outcount; line++) {
262 if (outbuf[line].descr) {
263 fprintf (out, "%s\n", outbuf[line].descr);
264 free (outbuf[line].descr);
266 free (outbuf[line].name);
271 /* skip whitespace */
273 static char *skip_ws (char *s)
275 while (*s && isspace ((unsigned char) *s))
281 /* isolate a token */
283 static char single_char_tokens[] = "[]{},;|";
285 static char *get_token (char *d, size_t l, char *s)
292 fprintf (stderr, "%s: get_token called for `%s'.\n", Progname, s);
297 fprintf (stderr, "%s: argumet after skip_ws(): `%s'.\n", Progname, s);
301 fprintf (stderr, "%s: no more tokens on this line.\n", Progname);
305 if (strchr (single_char_tokens, *s)) {
307 fprintf (stderr, "%s: found single character token `%c'.\n",
317 fprintf (stderr, "%s: found quote character.\n", Progname);
324 for (t = s; *t && --l > 0; t++) {
325 if (*t == '\\' && !t[1])
328 if (is_quoted && *t == '\\') {
329 switch ((*d = *++t)) {
348 if (is_quoted && *t == '"') {
352 else if (!is_quoted && strchr (single_char_tokens, *t))
354 else if (!is_quoted && isspace ((unsigned char) *t))
363 fprintf (stderr, "%s: Got %stoken: `%s'.\n",
364 Progname, is_quoted ? "quoted " : "", dd);
365 fprintf (stderr, "%s: Remainder: `%s'.\n", Progname, t);
373 ** Configuration line parser
375 ** The following code parses a line from init.h which declares
376 ** a configuration variable.
380 /* note: the following enum must be in the same order as the
381 * following string definitions!
403 "DT_NONE", "-none-"}, {
404 "DT_BOOL", "boolean"}, {
405 "DT_NUM", "number"}, {
406 "DT_STR", "string"}, {
407 "DT_PATH", "path"}, {
408 "DT_QUAD", "quadoption"}, {
409 "DT_SORT", "sort order"}, {
410 "DT_RX", "regular expression"}, {
411 "DT_MAGIC", "folder magic"}, {
413 "DT_ADDR", "e-mail address"}, {
418 static int buff2type (const char *s)
422 for (type = DT_NONE; types[type].machine; type++)
423 if (!strcmp (types[type].machine, s))
429 static const char *type2human (int type)
431 return types[type].human;
433 static void handle_confline (char *s)
435 char varname[BUFFSIZE];
442 /* xxx - put this into an actual state machine? */
445 if (!(s = get_token (varname, sizeof (varname), s)))
449 if (!(s = get_token (buff, sizeof (buff), s)))
453 if (!(s = get_token (buff, sizeof (buff), s)))
456 type = buff2type (buff);
458 /* possibly a "|" or comma */
459 if (!(s = get_token (buff, sizeof (buff), s)))
462 if (!strcmp (buff, "|")) {
464 fprintf (stderr, "%s: Expecting <subtype> <comma>.\n", Progname);
465 /* ignore subtype and comma */
466 if (!(s = get_token (buff, sizeof (buff), s)))
468 if (!(s = get_token (buff, sizeof (buff), s)))
475 if (!(s = get_token (buff, sizeof (buff), s)))
477 if (!strcmp (buff, ","))
481 /* option name or UL &address */
482 if (!(s = get_token (buff, sizeof (buff), s)))
484 if (!strcmp (buff, "UL"))
485 if (!(s = get_token (buff, sizeof (buff), s)))
489 if (!(s = get_token (buff, sizeof (buff), s)))
493 fprintf (stderr, "%s: Expecting default value.\n", Progname);
495 /* <default value> or UL <default value> */
496 if (!(s = get_token (buff, sizeof (buff), s)))
498 if (!strcmp (buff, "UL")) {
500 fprintf (stderr, "%s: Skipping UL.\n", Progname);
501 if (!(s = get_token (buff, sizeof (buff), s)))
505 memset (tmp, 0, sizeof (tmp));
508 if (!strcmp (buff, "}"))
511 strncpy (tmp + STRLEN (tmp), buff, sizeof (tmp) - STRLEN (tmp));
513 while ((s = get_token (buff, sizeof (buff), s)));
515 pretty_default (val, sizeof (val), tmp, type);
517 print_confline (varname, type, val);
520 static void pretty_default (char *t, size_t l, const char *s, int type)
528 if (!strcasecmp (s, "M_YES"))
529 strncpy (t, "yes", l);
530 else if (!strcasecmp (s, "M_NO"))
531 strncpy (t, "no", l);
532 else if (!strcasecmp (s, "M_ASKYES"))
533 strncpy (t, "ask-yes", l);
534 else if (!strcasecmp (s, "M_ASKNO"))
535 strncpy (t, "ask-no", l);
541 strncpy (t, "yes", l);
543 strncpy (t, "no", l);
549 strncpy (t, s + 5, l);
551 *t = tolower ((unsigned char) *t);
557 strncpy (t, s + 2, l);
559 *t = tolower ((unsigned char) *t);
567 if (!strcmp (s, "0"))
579 static void char_to_escape (char *dest, unsigned int c)
583 strcpy (dest, "\\r");
584 break; /* __STRCPY_CHECKED__ */
586 strcpy (dest, "\\n");
587 break; /* __STRCPY_CHECKED__ */
589 strcpy (dest, "\\t");
590 break; /* __STRCPY_CHECKED__ */
592 strcpy (dest, "\\f");
593 break; /* __STRCPY_CHECKED__ */
595 sprintf (dest, "\\%03o", c);
599 static void conf_char_to_escape (unsigned int c)
603 char_to_escape (buff, c);
607 static void conf_print_strval (const char *v)
610 if (*v < ' ' || *v & 0x80) {
611 conf_char_to_escape ((unsigned int) *v);
615 if (*v == '"' || *v == '\\')
621 static void man_print_strval (const char *v)
624 if (*v < ' ' || *v & 0x80) {
626 conf_char_to_escape ((unsigned int) *v);
639 static void sgml_print_strval (const char *v)
644 if (*v < ' ' || *v & 0x80) {
645 char_to_escape (buff, (unsigned int) *v);
649 sgml_fputc ((unsigned int) *v);
653 static int sgml_fputc (int c)
661 return add ("$");
663 return add ("_");
665 return add ("%");
667 return add ("&");
669 return add ("\");
671 return add ("&dquot;");
673 return add ("[");
675 return add ("]");
677 return add ("˜");
683 static int sgml_fputs (const char *s)
686 if (sgml_fputc ((unsigned int) *s) == EOF)
692 static void print_confline (const char *varname, int type, const char *val)
697 switch (OutputFormat) {
698 /* configuration file */
701 if (type == DT_STR || type == DT_RX || type == DT_ADDR
702 || type == DT_PATH) {
706 conf_print_strval (val);
709 else if (type != DT_SYN) {
716 add ("\n#\n# Name: ");
719 add (type2human (type));
720 if (type == DT_STR || type == DT_RX || type == DT_ADDR
721 || type == DT_PATH) {
722 add ("\n# Default: \"");
723 conf_print_strval (val);
727 add ("\n# Default: ");
742 add (type2human (type));
744 if (type == DT_STR || type == DT_RX || type == DT_ADDR
745 || type == DT_PATH) {
746 add ("Default: \\(lq");
747 man_print_strval (val);
760 /* SGML based manual */
764 sgml_fputs (varname);
765 add ("<label id=\"");
768 add ("\n<p>\nType: <tt>");
769 add (type2human (type));
772 if (type == DT_STR || type == DT_RX || type == DT_ADDR
773 || type == DT_PATH) {
774 add ("<p>\nDefault: <tt>&dquot;");
775 sgml_print_strval (val);
776 add ("&dquot;</tt>");
779 add ("<p>\nDefault: <tt>");
792 ** Documentation line parser
794 ** The following code parses specially formatted documentation
795 ** comments in init.h.
797 ** The format is very remotely inspired by nroff. Most important, it's
798 ** easy to parse and convert, and it was easy to generate from the SGML
799 ** source of mutt's original manual.
801 ** - \fI switches to italics
802 ** - \fB switches to boldface
803 ** - \fP switches to normal display
804 ** - .dl on a line starts a definition list (name taken taken from HTML).
805 ** - .dt starts a term in a definition list.
806 ** - .dd starts a definition in a definition list.
807 ** - .de on a line finishes a definition list.
808 ** - .ts on a line starts a "tscreen" environment (name taken from SGML).
809 ** - .te on a line finishes this environment.
810 ** - .pp on a line starts a paragraph.
811 ** - \$word will be converted to a reference to word, where appropriate.
812 ** Note that \$$word is possible as well.
813 ** - '. ' in the beginning of a line expands to two space characters.
814 ** This is used to protect indentations in tables.
817 /* close eventually-open environments. */
819 static int fd_recurse = 0;
821 static int flush_doc (int docstat)
823 if (docstat & D_INIT)
827 fprintf (stderr, "%s: Internal error, recursion in flush_doc()!\n",
832 if (docstat & (D_TAB))
833 docstat = print_it (SP_END_TAB, NULL, docstat);
835 if (docstat & (D_DL))
836 docstat = print_it (SP_END_DL, NULL, docstat);
838 if (docstat & (D_EM | D_BF))
839 docstat = print_it (SP_END_FT, NULL, docstat);
841 docstat = print_it (SP_NEWLINE, NULL, 0);
847 /* print something. */
849 static int print_it (int special, char *str, int docstat)
851 int onl = docstat & (D_NL | D_NP);
853 docstat &= ~(D_NL | D_NP | D_INIT);
855 switch (OutputFormat) {
856 /* configuration file */
860 static int Continuation = 0;
863 docstat &= ~(D_EM | D_BF);
938 if (docstat & D_DT) {
941 for (i = STRLEN (str); i < 8; i++)
959 docstat &= ~(D_EM | D_BF);
1002 add ("\n.IP\n.DS\n.sp\n.ft CR\n.nf\n");
1003 docstat |= D_TAB | D_NL;
1008 add ("\n.fi\n.ec\n.ft P\n.sp\n");
1038 for (; *str; str++) {
1041 else if (*str == '\\')
1043 else if (!strncmp (str, "``", 2)) {
1047 else if (!strncmp (str, "''", 2)) {
1061 /* SGML based manual */
1071 docstat &= ~(D_EM | D_BF);
1114 add ("\n<tscreen><verb>\n");
1115 docstat |= D_TAB | D_NL;
1120 add ("\n</verb></tscreen>");
1127 add ("\n<descrip>\n");
1143 add ("</descrip>\n");
1149 if (docstat & D_TAB)
1158 /* make gcc happy (unreached) */
1166 void print_ref (int output_dollar, const char *ref)
1168 switch (OutputFormat) {
1191 static int commit_buff (char *buff, char **d, int docstat)
1195 docstat = print_it (SP_STR, buff, docstat);
1202 static int handle_docline (char *l, int docstat)
1204 char buff[BUFFSIZE];
1210 fprintf (stderr, "%s: handle_docline `%s'\n", Progname, l);
1212 if (!strncmp (l, ".pp", 3))
1213 return print_it (SP_NEWPAR, NULL, docstat);
1214 else if (!strncmp (l, ".ts", 3))
1215 return print_it (SP_START_TAB, NULL, docstat);
1216 else if (!strncmp (l, ".te", 3))
1217 return print_it (SP_END_TAB, NULL, docstat);
1218 else if (!strncmp (l, ".dl", 3))
1219 return print_it (SP_START_DL, NULL, docstat);
1220 else if (!strncmp (l, ".de", 3))
1221 return print_it (SP_END_DL, NULL, docstat);
1222 else if (!strncmp (l, ". ", 2))
1225 for (s = l, d = buff; *s; s++) {
1226 if (!strncmp (s, "\\(as", 4)) {
1230 else if (!strncmp (s, "\\(rs", 4)) {
1234 else if (!strncmp (s, "\\fI", 3)) {
1235 docstat = commit_buff (buff, &d, docstat);
1236 docstat = print_it (SP_START_EM, NULL, docstat);
1239 else if (!strncmp (s, "\\fB", 3)) {
1240 docstat = commit_buff (buff, &d, docstat);
1241 docstat = print_it (SP_START_BF, NULL, docstat);
1244 else if (!strncmp (s, "\\fP", 3)) {
1245 docstat = commit_buff (buff, &d, docstat);
1246 docstat = print_it (SP_END_FT, NULL, docstat);
1249 else if (!strncmp (s, ".dt", 3)) {
1250 docstat = commit_buff (buff, &d, docstat);
1251 docstat = print_it (SP_DT, NULL, docstat);
1254 else if (!strncmp (s, ".dd", 3)) {
1255 docstat = commit_buff (buff, &d, docstat);
1256 docstat = print_it (SP_DD, NULL, docstat);
1259 else if (*s == '$') {
1260 int output_dollar = 0;
1274 while (isalnum ((unsigned char) *s) || *s == '-' || *s == '_')
1277 docstat = commit_buff (buff, &d, docstat);
1280 print_ref (output_dollar, ref);
1289 docstat = commit_buff (buff, &d, docstat);
1290 return print_it (SP_NEWLINE, NULL, docstat);