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 p_realloc(&outbuf, ++outcount);
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 p_realloc(&outbuf[outcount - 1].descr, lold + lnew + 1);
181 memcpy(&(outbuf[outcount - 1].descr[lold - 1]) + 1, s, lnew);
183 outbuf[outcount - 1].descr[lold + lnew] = '\0';
187 static int add_c (int c)
189 char buf[2] = "\0\0";
192 return (add_s (buf));
195 static void makedoc (FILE * in, FILE * out)
202 int docstat = D_INIT;
204 while ((fgets (buffer, sizeof (buffer), in))) {
206 if ((p = strchr (buffer, '\n')) == NULL) {
207 fprintf (stderr, "%s: Line %d too long. Ask a wizard to enlarge\n"
208 "%s: my buffer size.\n", Progname, line, Progname);
214 if (!(p = get_token (token, sizeof (token), buffer)))
217 if (!m_strcmp (token, "/*++*/"))
219 else if (!m_strcmp (token, "/*--*/")) {
220 docstat = flush_doc (docstat);
223 else if (active && (!m_strcmp (token, "/**") || !m_strcmp (token, "**")))
224 docstat = handle_docline (p, docstat);
225 else if (active && !m_strcmp (token, "{")) {
226 docstat = flush_doc (docstat);
232 qsort (outbuf, outcount, sizeof (var_t), &var_cmp);
233 for (line = 0; line < outcount; line++) {
234 if (outbuf[line].descr) {
235 fprintf (out, "%s\n", outbuf[line].descr);
236 p_delete(&outbuf[line].descr);
238 p_delete(&outbuf[line].name);
243 /* skip whitespace */
245 static char *skip_ws (char *s)
247 while (*s && isspace ((unsigned char) *s))
253 /* isolate a token */
255 static char single_char_tokens[] = "[]{},;|";
257 static char *get_token (char *d, size_t l, char *s)
268 if (strchr (single_char_tokens, *s)) {
279 for (t = s; *t && --l > 0; t++) {
280 if (*t == '\\' && !t[1])
283 if (is_quoted && *t == '\\') {
284 switch ((*d = *++t)) {
303 if (is_quoted && *t == '"') {
307 else if (!is_quoted && strchr (single_char_tokens, *t))
309 else if (!is_quoted && isspace ((unsigned char) *t))
322 ** Configuration line parser
324 ** The following code parses a line from init.h which declares
325 ** a configuration variable.
329 /* note: the following enum must be in the same order as the
330 * following string definitions!
352 {"DT_NONE", "-none-"},
353 {"DT_BOOL", "boolean"},
354 {"DT_NUM", "number"},
355 {"DT_STR", "string"},
357 {"DT_QUAD", "quadoption"},
358 {"DT_SORT", "sort order"},
359 {"DT_RX", "regular expression"},
360 {"DT_MAGIC", "folder magic"},
362 {"DT_ADDR", "e-mail address"},
363 {"DT_SYS", "system property"},
367 static int buff2type (const char *s)
371 for (type = DT_NONE; types[type].machine; type++)
372 if (!m_strcmp (types[type].machine, s))
378 static const char *type2human (int type)
380 return types[type].human;
382 static void handle_confline (char *s)
384 char varname[BUFSIZ];
390 /* xxx - put this into an actual state machine? */
393 if (!(s = get_token (varname, sizeof (varname), s)))
397 if (!(s = get_token (buff, sizeof (buff), s)))
401 if (!(s = get_token (buff, sizeof (buff), s)))
404 type = buff2type (buff);
406 /* possibly a "|" or comma */
407 if (!(s = get_token (buff, sizeof (buff), s)))
410 if (!m_strcmp (buff, "|")) {
411 /* ignore subtype and comma */
412 if (!(s = get_token (buff, sizeof (buff), s)))
414 if (!(s = get_token (buff, sizeof (buff), s)))
421 if (!(s = get_token (buff, sizeof (buff), s)))
423 if (!m_strcmp (buff, ","))
427 /* option name or UL &address */
428 if (!(s = get_token (buff, sizeof (buff), s)))
430 if (!m_strcmp (buff, "UL"))
431 if (!(s = get_token (buff, sizeof (buff), s)))
435 if (!(s = get_token (buff, sizeof (buff), s)))
438 /* <default value> or UL <default value> */
439 if (!(s = get_token (buff, sizeof (buff), s)))
441 if (!m_strcmp (buff, "UL")) {
442 if (!(s = get_token (buff, sizeof (buff), s)))
446 memset(val, 0, sizeof(val));
449 if (!m_strcmp (buff, "}"))
452 m_strcat(val, sizeof(val), buff);
454 while ((s = get_token (buff, sizeof (buff), s)));
457 print_confline (varname, type, val);
460 static void char_to_escape (char *dest, int len, unsigned int c)
464 m_strcpy(dest, len, "\\r");
467 m_strcpy(dest, len, "\\n");
470 m_strcpy(dest, len, "\\t");
473 m_strcpy(dest, len, "\\f");
476 sprintf (dest, len, "\\%03o", c);
480 static void conf_char_to_escape (unsigned int c)
484 char_to_escape(buff, sizeof(buff), c);
488 static void conf_print_strval (const char *v)
491 if (*v < ' ' || *v & 0x80) {
492 conf_char_to_escape ((unsigned int) *v);
496 if (*v == '"' || *v == '\\')
502 static void man_print_strval (const char *v)
505 if (*v < ' ' || *v & 0x80) {
507 conf_char_to_escape ((unsigned int) *v);
520 static void sgml_print_strval (const char *v)
525 if (*v < ' ' || *v & 0x80) {
526 char_to_escape(buff, sizeof(buff), (unsigned int) *v);
530 sgml_fputc ((unsigned int) *v);
534 static int sgml_fputc (int c)
538 return add_s ("<");
540 return add_s (">");
542 return add_s ("&");
548 static int sgml_fputs (const char *s)
551 if (sgml_fputc ((unsigned int) *s) == EOF)
557 /* reduce CDATA to ID */
558 static int sgml_id_fputs (const char *s) {
566 if (sgml_fputc ((unsigned int) id) == EOF)
572 static void print_confline (const char *varname, int type, const char *val)
577 switch (OutputFormat) {
578 /* configuration file */
581 if (type == DT_SYS) {
588 if (type == DT_STR || type == DT_RX || type == DT_ADDR
589 || type == DT_PATH) {
593 conf_print_strval (val);
596 else if (type != DT_SYN) {
603 add_s ("\n#\n# Name: ");
605 add_s ("\n# Type: ");
606 add_s (type2human (type));
607 if (type == DT_STR || type == DT_RX || type == DT_ADDR
608 || type == DT_PATH) {
609 add_s ("\n# Default: \"");
610 conf_print_strval (val);
614 add_s ("\n# Default: ");
629 add_s (type2human (type));
631 if (type == DT_STR || type == DT_RX || type == DT_ADDR
632 || type == DT_PATH) {
633 add_s ("Default: \\(lq");
634 man_print_strval (val);
638 add_s (type == DT_SYS ? "Value: " : "Default: ");
647 /* SGML based manual */
650 add_s ("\n<madmutt-doc:vardef name=\"");
651 sgml_fputs (varname);
652 add_s ("\">\n<para>Type: <literal>");
653 add_s (type2human (type));
654 add_s ("</literal></para>\n");
656 if (type == DT_STR || type == DT_RX || type == DT_ADDR
657 || type == DT_PATH) {
658 add_s ("<para>\nDefault: <literal>"");
659 sgml_print_strval (val);
660 add_s (""</literal>");
664 add_s (type == DT_SYS ? "Value: " : "Default: ");
667 add_s ("</literal>");
679 ** Documentation line parser
681 ** The following code parses specially formatted documentation
682 ** comments in init.h.
684 ** The format is very remotely inspired by nroff. Most important, it's
685 ** easy to parse and convert, and it was easy to generate from the SGML
686 ** source of mutt's original manual.
688 ** - \fI switches to italics
689 ** - \fB switches to boldface
690 ** - \fT switches to typewriter for SGML
691 ** - \fP switches to normal display
692 ** - .dl on a line starts a definition list (name taken taken from HTML).
693 ** - .dt starts a term in a definition list.
694 ** - .dd starts a definition in a definition list.
695 ** - .de on a line finishes a definition list.
696 ** - .ts on a line starts a "tscreen" environment (name taken from SGML).
697 ** - .te on a line finishes this environment.
698 ** - .pp on a line starts a paragraph.
699 ** - \$word will be converted to a reference to word, where appropriate.
700 ** Note that \$$word is possible as well.
701 ** - '. ' in the beginning of a line expands to two space characters.
702 ** This is used to protect indentations in tables.
705 /* close eventually-open environments. */
707 static int fd_recurse = 0;
709 static int flush_doc (int docstat)
711 if (docstat & D_INIT)
715 fprintf (stderr, "%s: Internal error, recursion in flush_doc()!\n",
720 if (docstat & (D_PA))
721 docstat = print_it (SP_END_PAR, NULL, docstat);
723 if (docstat & (D_TAB))
724 docstat = print_it (SP_END_TAB, NULL, docstat);
726 if (docstat & (D_DL))
727 docstat = print_it (SP_END_DL, NULL, docstat);
729 if (docstat & (D_EM | D_BF | D_TT))
730 docstat = print_it (SP_END_FT, NULL, docstat);
732 docstat = print_it (SP_END_SECT, NULL, docstat);
734 docstat = print_it (SP_NEWLINE, NULL, 0);
740 /* print something. */
742 static int print_it (int special, char *str, int docstat)
744 int onl = docstat & (D_NL | D_NP);
746 docstat &= ~(D_NL | D_NP | D_INIT);
748 switch (OutputFormat) {
749 /* configuration file */
753 static int Continuation = 0;
756 docstat &= ~(D_EM | D_BF | D_TT);
834 if (docstat & D_DT) {
837 for (i = m_strlen(str); i < 8; i++)
855 docstat &= ~(D_EM | D_BF | D_TT);
862 docstat &= ~(D_EM | D_TT);
869 docstat &= ~(D_BF | D_TT);
875 docstat &= ~(D_BF | D_EM);
904 add_s ("\n.IP\n.DS\n.sp\n.ft CR\n.nf\n");
905 docstat |= D_TAB | D_NL;
910 add_s ("\n.fi\n.ec\n.ft P\n.sp\n");
940 for (; *str; str++) {
943 else if (*str == '\\')
945 else if (!m_strncmp (str, "``", 2)) {
949 else if (!m_strncmp (str, "''", 2)) {
963 /* SGML based manual */
970 add_s ("</emphasis>");
972 add_s ("</emphasis>");
974 add_s ("</literal>");
975 docstat &= ~(D_EM | D_BF | D_TT);
980 add_s ("<emphasis role=\"bold\">");
982 docstat &= ~(D_EM | D_TT);
987 add_s ("<emphasis>");
989 docstat &= ~(D_BF | D_TT);
996 docstat &= ~(D_EM | D_BF);
1019 add_s ("</para>\n");
1028 add_s ("\n<screen>\n");
1029 docstat |= D_TAB | D_NL;
1034 add_s ("\n</screen>");
1041 add_s ("\n<variablelist>\n");
1047 add_s ("<varlistentry><term>");
1052 add_s ("</term>\n<listitem><para>\n");
1058 add_s ("</para></listitem></varlistentry></variablelist>\n");
1059 docstat &= ~(D_DL|D_DD);
1064 add_s ("</para>\n");
1070 add_s ("</para></listitem></varlistentry>\n");
1076 add_s ("</madmutt-doc:vardef>\n");
1081 if (docstat & D_TAB)
1090 /* make gcc happy (unreached) */
1098 static void print_ref (int output_dollar, const char *ref)
1100 switch (OutputFormat) {
1109 add_s ("<link linkend=\"");
1110 sgml_id_fputs (ref);
1123 static int commit_buff (char *buff, char **d, int docstat)
1127 docstat = print_it (SP_STR, buff, docstat);
1134 static int handle_docline (char *l, int docstat)
1141 if (!m_strncmp (l, ".pp", 3))
1142 return print_it (SP_NEWPAR, NULL, docstat);
1143 else if (!m_strncmp (l, ".ts", 3))
1144 return print_it (SP_START_TAB, NULL, docstat);
1145 else if (!m_strncmp (l, ".te", 3))
1146 return print_it (SP_END_TAB, NULL, docstat);
1147 else if (!m_strncmp (l, ".dl", 3))
1148 return print_it (SP_START_DL, NULL, docstat);
1149 else if (!m_strncmp (l, ".de", 3))
1150 return print_it (SP_END_DL, NULL, docstat);
1151 else if (!m_strncmp (l, ". ", 2))
1154 for (s = l, d = buff; *s; s++) {
1155 if (!m_strncmp (s, "\\(as", 4)) {
1159 else if (!m_strncmp (s, "\\(rs", 4)) {
1163 else if (!m_strncmp (s, "\\fI", 3)) {
1164 docstat = commit_buff (buff, &d, docstat);
1165 docstat = print_it (SP_START_EM, NULL, docstat);
1168 else if (!m_strncmp (s, "\\fB", 3)) {
1169 docstat = commit_buff (buff, &d, docstat);
1170 docstat = print_it (SP_START_BF, NULL, docstat);
1173 else if (!m_strncmp (s, "\\fT", 3)) {
1174 docstat = commit_buff (buff, &d, docstat);
1175 docstat = print_it (SP_START_TT, NULL, docstat);
1178 else if (!m_strncmp (s, "\\fP", 3)) {
1179 docstat = commit_buff (buff, &d, docstat);
1180 docstat = print_it (SP_END_FT, NULL, docstat);
1183 else if (!m_strncmp (s, ".dt", 3)) {
1184 if (docstat & D_DD) {
1185 docstat = commit_buff (buff, &d, docstat);
1186 docstat = print_it (SP_END_DD, NULL, docstat);
1188 docstat = commit_buff (buff, &d, docstat);
1189 docstat = print_it (SP_DT, NULL, docstat);
1192 else if (!m_strncmp (s, ".dd", 3)) {
1193 docstat = commit_buff (buff, &d, docstat);
1194 docstat = print_it (SP_DD, NULL, docstat);
1197 else if (*s == '$') {
1198 int output_dollar = 0;
1212 while (isalnum ((unsigned char) *s) || *s == '-' || *s == '_')
1215 docstat = commit_buff (buff, &d, docstat);
1218 print_ref (output_dollar, ref);
1227 docstat = commit_buff (buff, &d, docstat);
1228 return print_it (SP_NEWLINE, NULL, docstat);