2 * Copyright notice from original mutt:
3 * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
5 * Parts were written/modified by:
6 * Rocco Rutte <pdmef@cs.tu-berlin.de>
8 * This file is part of mutt-ng, see http://www.muttng.org/.
9 * It's licensed under the GNU General Public License,
10 * please see the file GPL in the top level source directory.
14 ** This program parses mutt's init.h and generates documentation in
15 ** three different formats:
17 ** -> a commented muttrc configuration file
18 ** -> nroff, suitable for inclusion in a manual page
19 ** -> linuxdoc-sgml, suitable for inclusion in the
24 #include <lib-lib/lib-lib.h>
36 static int outcount = 0;
37 static var_t *outbuf = NULL;
39 static int var_cmp (const void *a, const void *b)
41 return (m_strcmp (((var_t *) a)->name, ((var_t *) b)->name));
44 enum output_formats_t {
45 F_CONF, F_MAN, F_SGML, F_NONE
52 #define D_TAB (1 << 4)
54 #define D_INIT (1 << 6)
58 #define D_PA (1 << 10)
80 enum output_formats_t OutputFormat = F_NONE;
83 static char *get_token (char *, size_t, char *);
84 static char *skip_ws (char *);
85 static const char *type2human (int);
86 static int buff2type (const char *);
87 static int flush_doc (int);
88 static int handle_docline (char *, int);
89 static int print_it (int, char *, int);
90 static void print_confline (const char *, int, const char *);
91 static void handle_confline (char *);
92 static void makedoc (FILE *, FILE *);
93 static int sgml_fputc (int);
94 static int sgml_fputs (const char *);
95 static int sgml_id_fputs (const char *);
96 static void add_var (const char *);
97 static int add_s (const char *);
98 static int add_c (int);
100 int main (int argc, char *argv[])
105 if ((Progname = strrchr (argv[0], '/')))
110 while ((c = getopt (argc, argv, "cmsd")) != EOF) {
113 OutputFormat = F_CONF;
116 OutputFormat = F_MAN;
119 OutputFormat = F_SGML;
123 fprintf (stderr, "%s: bad command line parameter.\n", Progname);
129 if (optind != argc) {
130 if ((f = fopen (argv[optind], "r")) == NULL) {
131 fprintf (stderr, "%s: Can't open %s (%s).\n",
132 Progname, argv[optind], strerror (errno));
139 switch (OutputFormat) {
147 fprintf (stderr, "%s: No output format specified.\n", Progname);
158 static void add_var (const char *name)
160 outbuf = realloc (outbuf, (++outcount) * sizeof (var_t));
161 outbuf[outcount - 1].seen = 0;
162 outbuf[outcount - 1].name = strdup(name);
163 outbuf[outcount - 1].descr = NULL;
166 static int add_s (const char *s)
168 size_t lnew = m_strlen(s), lold = m_strlen(outbuf[outcount - 1].descr);
172 if (!outbuf[outcount - 1].seen) {
174 outbuf[outcount - 1].seen = 1;
178 outbuf[outcount - 1].descr = strdup(s);
180 outbuf[outcount - 1].descr =
181 realloc (outbuf[outcount - 1].descr, lold + lnew + 1);
182 memcpy (&(outbuf[outcount - 1].descr[lold - 1]) + 1, s, lnew);
184 outbuf[outcount - 1].descr[lold + lnew] = '\0';
188 static int add_c (int c)
190 char buf[2] = "\0\0";
193 return (add_s (buf));
196 static void makedoc (FILE * in, FILE * out)
203 int docstat = D_INIT;
205 while ((fgets (buffer, sizeof (buffer), in))) {
207 if ((p = strchr (buffer, '\n')) == NULL) {
208 fprintf (stderr, "%s: Line %d too long. Ask a wizard to enlarge\n"
209 "%s: my buffer size.\n", Progname, line, Progname);
215 if (!(p = get_token (token, sizeof (token), buffer)))
218 if (!m_strcmp (token, "/*++*/"))
220 else if (!m_strcmp (token, "/*--*/")) {
221 docstat = flush_doc (docstat);
224 else if (active && (!m_strcmp (token, "/**") || !m_strcmp (token, "**")))
225 docstat = handle_docline (p, docstat);
226 else if (active && !m_strcmp (token, "{")) {
227 docstat = flush_doc (docstat);
233 qsort (outbuf, outcount, sizeof (var_t), &var_cmp);
234 for (line = 0; line < outcount; line++) {
235 if (outbuf[line].descr) {
236 fprintf (out, "%s\n", outbuf[line].descr);
237 free (outbuf[line].descr);
239 free (outbuf[line].name);
244 /* skip whitespace */
246 static char *skip_ws (char *s)
248 while (*s && isspace ((unsigned char) *s))
254 /* isolate a token */
256 static char single_char_tokens[] = "[]{},;|";
258 static char *get_token (char *d, size_t l, char *s)
269 if (strchr (single_char_tokens, *s)) {
280 for (t = s; *t && --l > 0; t++) {
281 if (*t == '\\' && !t[1])
284 if (is_quoted && *t == '\\') {
285 switch ((*d = *++t)) {
304 if (is_quoted && *t == '"') {
308 else if (!is_quoted && strchr (single_char_tokens, *t))
310 else if (!is_quoted && isspace ((unsigned char) *t))
323 ** Configuration line parser
325 ** The following code parses a line from init.h which declares
326 ** a configuration variable.
330 /* note: the following enum must be in the same order as the
331 * following string definitions!
353 {"DT_NONE", "-none-"},
354 {"DT_BOOL", "boolean"},
355 {"DT_NUM", "number"},
356 {"DT_STR", "string"},
358 {"DT_QUAD", "quadoption"},
359 {"DT_SORT", "sort order"},
360 {"DT_RX", "regular expression"},
361 {"DT_MAGIC", "folder magic"},
363 {"DT_ADDR", "e-mail address"},
364 {"DT_SYS", "system property"},
368 static int buff2type (const char *s)
372 for (type = DT_NONE; types[type].machine; type++)
373 if (!m_strcmp (types[type].machine, s))
379 static const char *type2human (int type)
381 return types[type].human;
383 static void handle_confline (char *s)
385 char varname[BUFSIZ];
391 /* xxx - put this into an actual state machine? */
394 if (!(s = get_token (varname, sizeof (varname), s)))
398 if (!(s = get_token (buff, sizeof (buff), s)))
402 if (!(s = get_token (buff, sizeof (buff), s)))
405 type = buff2type (buff);
407 /* possibly a "|" or comma */
408 if (!(s = get_token (buff, sizeof (buff), s)))
411 if (!m_strcmp (buff, "|")) {
412 /* ignore subtype and comma */
413 if (!(s = get_token (buff, sizeof (buff), s)))
415 if (!(s = get_token (buff, sizeof (buff), s)))
422 if (!(s = get_token (buff, sizeof (buff), s)))
424 if (!m_strcmp (buff, ","))
428 /* option name or UL &address */
429 if (!(s = get_token (buff, sizeof (buff), s)))
431 if (!m_strcmp (buff, "UL"))
432 if (!(s = get_token (buff, sizeof (buff), s)))
436 if (!(s = get_token (buff, sizeof (buff), s)))
439 /* <default value> or UL <default value> */
440 if (!(s = get_token (buff, sizeof (buff), s)))
442 if (!m_strcmp (buff, "UL")) {
443 if (!(s = get_token (buff, sizeof (buff), s)))
447 memset(val, 0, sizeof(val));
450 if (!m_strcmp (buff, "}"))
453 m_strcat(val, sizeof(val), buff);
455 while ((s = get_token (buff, sizeof (buff), s)));
458 print_confline (varname, type, val);
461 static void char_to_escape (char *dest, unsigned int c)
465 strcpy (dest, "\\r");
466 break; /* __STRCPY_CHECKED__ */
468 strcpy (dest, "\\n");
469 break; /* __STRCPY_CHECKED__ */
471 strcpy (dest, "\\t");
472 break; /* __STRCPY_CHECKED__ */
474 strcpy (dest, "\\f");
475 break; /* __STRCPY_CHECKED__ */
477 sprintf (dest, "\\%03o", c);
481 static void conf_char_to_escape (unsigned int c)
485 char_to_escape (buff, c);
489 static void conf_print_strval (const char *v)
492 if (*v < ' ' || *v & 0x80) {
493 conf_char_to_escape ((unsigned int) *v);
497 if (*v == '"' || *v == '\\')
503 static void man_print_strval (const char *v)
506 if (*v < ' ' || *v & 0x80) {
508 conf_char_to_escape ((unsigned int) *v);
521 static void sgml_print_strval (const char *v)
526 if (*v < ' ' || *v & 0x80) {
527 char_to_escape (buff, (unsigned int) *v);
531 sgml_fputc ((unsigned int) *v);
535 static int sgml_fputc (int c)
539 return add_s ("<");
541 return add_s (">");
543 return add_s ("&");
549 static int sgml_fputs (const char *s)
552 if (sgml_fputc ((unsigned int) *s) == EOF)
558 /* reduce CDATA to ID */
559 static int sgml_id_fputs (const char *s) {
567 if (sgml_fputc ((unsigned int) id) == EOF)
573 static void print_confline (const char *varname, int type, const char *val)
578 switch (OutputFormat) {
579 /* configuration file */
582 if (type == DT_SYS) {
589 if (type == DT_STR || type == DT_RX || type == DT_ADDR
590 || type == DT_PATH) {
594 conf_print_strval (val);
597 else if (type != DT_SYN) {
604 add_s ("\n#\n# Name: ");
606 add_s ("\n# Type: ");
607 add_s (type2human (type));
608 if (type == DT_STR || type == DT_RX || type == DT_ADDR
609 || type == DT_PATH) {
610 add_s ("\n# Default: \"");
611 conf_print_strval (val);
615 add_s ("\n# Default: ");
630 add_s (type2human (type));
632 if (type == DT_STR || type == DT_RX || type == DT_ADDR
633 || type == DT_PATH) {
634 add_s ("Default: \\(lq");
635 man_print_strval (val);
639 add_s (type == DT_SYS ? "Value: " : "Default: ");
648 /* SGML based manual */
651 add_s ("\n<madmutt-doc:vardef name=\"");
652 sgml_fputs (varname);
653 add_s ("\">\n<para>Type: <literal>");
654 add_s (type2human (type));
655 add_s ("</literal></para>\n");
657 if (type == DT_STR || type == DT_RX || type == DT_ADDR
658 || type == DT_PATH) {
659 add_s ("<para>\nDefault: <literal>"");
660 sgml_print_strval (val);
661 add_s (""</literal>");
665 add_s (type == DT_SYS ? "Value: " : "Default: ");
668 add_s ("</literal>");
680 ** Documentation line parser
682 ** The following code parses specially formatted documentation
683 ** comments in init.h.
685 ** The format is very remotely inspired by nroff. Most important, it's
686 ** easy to parse and convert, and it was easy to generate from the SGML
687 ** source of mutt's original manual.
689 ** - \fI switches to italics
690 ** - \fB switches to boldface
691 ** - \fT switches to typewriter for SGML
692 ** - \fP switches to normal display
693 ** - .dl on a line starts a definition list (name taken taken from HTML).
694 ** - .dt starts a term in a definition list.
695 ** - .dd starts a definition in a definition list.
696 ** - .de on a line finishes a definition list.
697 ** - .ts on a line starts a "tscreen" environment (name taken from SGML).
698 ** - .te on a line finishes this environment.
699 ** - .pp on a line starts a paragraph.
700 ** - \$word will be converted to a reference to word, where appropriate.
701 ** Note that \$$word is possible as well.
702 ** - '. ' in the beginning of a line expands to two space characters.
703 ** This is used to protect indentations in tables.
706 /* close eventually-open environments. */
708 static int fd_recurse = 0;
710 static int flush_doc (int docstat)
712 if (docstat & D_INIT)
716 fprintf (stderr, "%s: Internal error, recursion in flush_doc()!\n",
721 if (docstat & (D_PA))
722 docstat = print_it (SP_END_PAR, NULL, docstat);
724 if (docstat & (D_TAB))
725 docstat = print_it (SP_END_TAB, NULL, docstat);
727 if (docstat & (D_DL))
728 docstat = print_it (SP_END_DL, NULL, docstat);
730 if (docstat & (D_EM | D_BF | D_TT))
731 docstat = print_it (SP_END_FT, NULL, docstat);
733 docstat = print_it (SP_END_SECT, NULL, docstat);
735 docstat = print_it (SP_NEWLINE, NULL, 0);
741 /* print something. */
743 static int print_it (int special, char *str, int docstat)
745 int onl = docstat & (D_NL | D_NP);
747 docstat &= ~(D_NL | D_NP | D_INIT);
749 switch (OutputFormat) {
750 /* configuration file */
754 static int Continuation = 0;
757 docstat &= ~(D_EM | D_BF | D_TT);
835 if (docstat & D_DT) {
838 for (i = m_strlen(str); i < 8; i++)
856 docstat &= ~(D_EM | D_BF | D_TT);
863 docstat &= ~(D_EM | D_TT);
870 docstat &= ~(D_BF | D_TT);
876 docstat &= ~(D_BF | D_EM);
905 add_s ("\n.IP\n.DS\n.sp\n.ft CR\n.nf\n");
906 docstat |= D_TAB | D_NL;
911 add_s ("\n.fi\n.ec\n.ft P\n.sp\n");
941 for (; *str; str++) {
944 else if (*str == '\\')
946 else if (!m_strncmp (str, "``", 2)) {
950 else if (!m_strncmp (str, "''", 2)) {
964 /* SGML based manual */
971 add_s ("</emphasis>");
973 add_s ("</emphasis>");
975 add_s ("</literal>");
976 docstat &= ~(D_EM | D_BF | D_TT);
981 add_s ("<emphasis role=\"bold\">");
983 docstat &= ~(D_EM | D_TT);
988 add_s ("<emphasis>");
990 docstat &= ~(D_BF | D_TT);
997 docstat &= ~(D_EM | D_BF);
1020 add_s ("</para>\n");
1029 add_s ("\n<screen>\n");
1030 docstat |= D_TAB | D_NL;
1035 add_s ("\n</screen>");
1042 add_s ("\n<variablelist>\n");
1048 add_s ("<varlistentry><term>");
1053 add_s ("</term>\n<listitem><para>\n");
1059 add_s ("</para></listitem></varlistentry></variablelist>\n");
1060 docstat &= ~(D_DL|D_DD);
1065 add_s ("</para>\n");
1071 add_s ("</para></listitem></varlistentry>\n");
1077 add_s ("</madmutt-doc:vardef>\n");
1082 if (docstat & D_TAB)
1091 /* make gcc happy (unreached) */
1099 static void print_ref (int output_dollar, const char *ref)
1101 switch (OutputFormat) {
1110 add_s ("<link linkend=\"");
1111 sgml_id_fputs (ref);
1124 static int commit_buff (char *buff, char **d, int docstat)
1128 docstat = print_it (SP_STR, buff, docstat);
1135 static int handle_docline (char *l, int docstat)
1142 if (!m_strncmp (l, ".pp", 3))
1143 return print_it (SP_NEWPAR, NULL, docstat);
1144 else if (!m_strncmp (l, ".ts", 3))
1145 return print_it (SP_START_TAB, NULL, docstat);
1146 else if (!m_strncmp (l, ".te", 3))
1147 return print_it (SP_END_TAB, NULL, docstat);
1148 else if (!m_strncmp (l, ".dl", 3))
1149 return print_it (SP_START_DL, NULL, docstat);
1150 else if (!m_strncmp (l, ".de", 3))
1151 return print_it (SP_END_DL, NULL, docstat);
1152 else if (!m_strncmp (l, ". ", 2))
1155 for (s = l, d = buff; *s; s++) {
1156 if (!m_strncmp (s, "\\(as", 4)) {
1160 else if (!m_strncmp (s, "\\(rs", 4)) {
1164 else if (!m_strncmp (s, "\\fI", 3)) {
1165 docstat = commit_buff (buff, &d, docstat);
1166 docstat = print_it (SP_START_EM, NULL, docstat);
1169 else if (!m_strncmp (s, "\\fB", 3)) {
1170 docstat = commit_buff (buff, &d, docstat);
1171 docstat = print_it (SP_START_BF, NULL, docstat);
1174 else if (!m_strncmp (s, "\\fT", 3)) {
1175 docstat = commit_buff (buff, &d, docstat);
1176 docstat = print_it (SP_START_TT, NULL, docstat);
1179 else if (!m_strncmp (s, "\\fP", 3)) {
1180 docstat = commit_buff (buff, &d, docstat);
1181 docstat = print_it (SP_END_FT, NULL, docstat);
1184 else if (!m_strncmp (s, ".dt", 3)) {
1185 if (docstat & D_DD) {
1186 docstat = commit_buff (buff, &d, docstat);
1187 docstat = print_it (SP_END_DD, NULL, docstat);
1189 docstat = commit_buff (buff, &d, docstat);
1190 docstat = print_it (SP_DT, NULL, docstat);
1193 else if (!m_strncmp (s, ".dd", 3)) {
1194 docstat = commit_buff (buff, &d, docstat);
1195 docstat = print_it (SP_DD, NULL, docstat);
1198 else if (*s == '$') {
1199 int output_dollar = 0;
1213 while (isalnum ((unsigned char) *s) || *s == '-' || *s == '_')
1216 docstat = commit_buff (buff, &d, docstat);
1219 print_ref (output_dollar, ref);
1228 docstat = commit_buff (buff, &d, docstat);
1229 return print_it (SP_NEWLINE, NULL, docstat);