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
34 #include <lib-lib/str.h>
47 extern char *sys_errlist[];
50 #define strerror(x) ((x) > 0 && (x) < sys_nerr) ? sys_errlist[(x)] : 0
51 #endif /* !HAVE_STRERROR */
57 #define STRLEN(s) (s ? strlen(s) : 0)
65 static int outcount = 0;
66 static var_t *outbuf = NULL;
68 static int var_cmp (const void *a, const void *b)
70 return (strcmp (((var_t *) a)->name, ((var_t *) b)->name));
73 enum output_formats_t {
74 F_CONF, F_MAN, F_SGML, F_NONE
81 #define D_TAB (1 << 4)
83 #define D_INIT (1 << 6)
87 #define D_PA (1 << 10)
109 enum output_formats_t OutputFormat = F_NONE;
113 static char *get_token (char *, size_t, char *);
114 static char *skip_ws (char *);
115 static const char *type2human (int);
116 static int buff2type (const char *);
117 static int flush_doc (int);
118 static int handle_docline (char *, int);
119 static int print_it (int, char *, int);
120 static void print_confline (const char *, int, const char *);
121 static void handle_confline (char *);
122 static void makedoc (FILE *, FILE *);
123 static int sgml_fputc (int);
124 static int sgml_fputs (const char *);
125 static int sgml_id_fputs (const char *);
126 static void add_var (const char *);
127 static int add_s (const char *);
128 static int add_c (int);
130 int main (int argc, char *argv[])
135 if ((Progname = strrchr (argv[0], '/')))
140 while ((c = getopt (argc, argv, "cmsd")) != EOF) {
143 OutputFormat = F_CONF;
146 OutputFormat = F_MAN;
149 OutputFormat = F_SGML;
156 fprintf (stderr, "%s: bad command line parameter.\n", Progname);
162 if (optind != argc) {
163 if ((f = fopen (argv[optind], "r")) == NULL) {
164 fprintf (stderr, "%s: Can't open %s (%s).\n",
165 Progname, argv[optind], strerror (errno));
172 switch (OutputFormat) {
180 fprintf (stderr, "%s: No output format specified.\n", Progname);
191 static void add_var (const char *name)
193 outbuf = realloc (outbuf, (++outcount) * sizeof (var_t));
194 outbuf[outcount - 1].seen = 0;
195 outbuf[outcount - 1].name = strdup(name);
196 outbuf[outcount - 1].descr = NULL;
199 static int add_s (const char *s)
201 size_t lnew = STRLEN (s), lold = STRLEN (outbuf[outcount - 1].descr);
205 if (!outbuf[outcount - 1].seen) {
207 outbuf[outcount - 1].seen = 1;
211 outbuf[outcount - 1].descr = strdup(s);
213 outbuf[outcount - 1].descr =
214 realloc (outbuf[outcount - 1].descr, lold + lnew + 1);
215 memcpy (&(outbuf[outcount - 1].descr[lold - 1]) + 1, s, lnew);
217 outbuf[outcount - 1].descr[lold + lnew] = '\0';
221 static int add_c (int c)
223 char buf[2] = "\0\0";
226 return (add_s (buf));
229 static void makedoc (FILE * in, FILE * out)
231 char buffer[BUFFSIZE];
232 char token[BUFFSIZE];
236 int docstat = D_INIT;
238 while ((fgets (buffer, sizeof (buffer), in))) {
240 if ((p = strchr (buffer, '\n')) == NULL) {
241 fprintf (stderr, "%s: Line %d too long. Ask a wizard to enlarge\n"
242 "%s: my buffer size.\n", Progname, line, Progname);
248 if (!(p = get_token (token, sizeof (token), buffer)))
252 fprintf (stderr, "%s: line %d. first token: \"%s\".\n",
253 Progname, line, token);
256 if (!strcmp (token, "/*++*/"))
258 else if (!strcmp (token, "/*--*/")) {
259 docstat = flush_doc (docstat);
262 else if (active && (!strcmp (token, "/**") || !strcmp (token, "**")))
263 docstat = handle_docline (p, docstat);
264 else if (active && !strcmp (token, "{")) {
265 docstat = flush_doc (docstat);
271 qsort (outbuf, outcount, sizeof (var_t), &var_cmp);
272 for (line = 0; line < outcount; line++) {
273 if (outbuf[line].descr) {
274 fprintf (out, "%s\n", outbuf[line].descr);
275 free (outbuf[line].descr);
277 free (outbuf[line].name);
282 /* skip whitespace */
284 static char *skip_ws (char *s)
286 while (*s && isspace ((unsigned char) *s))
292 /* isolate a token */
294 static char single_char_tokens[] = "[]{},;|";
296 static char *get_token (char *d, size_t l, char *s)
303 fprintf (stderr, "%s: get_token called for `%s'.\n", Progname, s);
308 fprintf (stderr, "%s: argumet after skip_ws(): `%s'.\n", Progname, s);
312 fprintf (stderr, "%s: no more tokens on this line.\n", Progname);
316 if (strchr (single_char_tokens, *s)) {
318 fprintf (stderr, "%s: found single character token `%c'.\n",
328 fprintf (stderr, "%s: found quote character.\n", Progname);
335 for (t = s; *t && --l > 0; t++) {
336 if (*t == '\\' && !t[1])
339 if (is_quoted && *t == '\\') {
340 switch ((*d = *++t)) {
359 if (is_quoted && *t == '"') {
363 else if (!is_quoted && strchr (single_char_tokens, *t))
365 else if (!is_quoted && isspace ((unsigned char) *t))
374 fprintf (stderr, "%s: Got %stoken: `%s'.\n",
375 Progname, is_quoted ? "quoted " : "", dd);
376 fprintf (stderr, "%s: Remainder: `%s'.\n", Progname, t);
384 ** Configuration line parser
386 ** The following code parses a line from init.h which declares
387 ** a configuration variable.
391 /* note: the following enum must be in the same order as the
392 * following string definitions!
415 "DT_NONE", "-none-"}, {
416 "DT_BOOL", "boolean"}, {
417 "DT_NUM", "number"}, {
418 "DT_STR", "string"}, {
419 "DT_PATH", "path"}, {
420 "DT_QUAD", "quadoption"}, {
421 "DT_SORT", "sort order"}, {
422 "DT_RX", "regular expression"}, {
423 "DT_MAGIC", "folder magic"}, {
425 "DT_ADDR", "e-mail address"}, {
426 "DT_SYS", "system property"}, {
431 static int buff2type (const char *s)
435 for (type = DT_NONE; types[type].machine; type++)
436 if (!strcmp (types[type].machine, s))
442 static const char *type2human (int type)
444 return types[type].human;
446 static void handle_confline (char *s)
448 char varname[BUFFSIZE];
454 /* xxx - put this into an actual state machine? */
457 if (!(s = get_token (varname, sizeof (varname), s)))
461 if (!(s = get_token (buff, sizeof (buff), s)))
465 if (!(s = get_token (buff, sizeof (buff), s)))
468 type = buff2type (buff);
470 /* possibly a "|" or comma */
471 if (!(s = get_token (buff, sizeof (buff), s)))
474 if (!strcmp (buff, "|")) {
476 fprintf (stderr, "%s: Expecting <subtype> <comma>.\n", Progname);
477 /* ignore subtype and comma */
478 if (!(s = get_token (buff, sizeof (buff), s)))
480 if (!(s = get_token (buff, sizeof (buff), s)))
487 if (!(s = get_token (buff, sizeof (buff), s)))
489 if (!strcmp (buff, ","))
493 /* option name or UL &address */
494 if (!(s = get_token (buff, sizeof (buff), s)))
496 if (!strcmp (buff, "UL"))
497 if (!(s = get_token (buff, sizeof (buff), s)))
501 if (!(s = get_token (buff, sizeof (buff), s)))
505 fprintf (stderr, "%s: Expecting default value.\n", Progname);
507 /* <default value> or UL <default value> */
508 if (!(s = get_token (buff, sizeof (buff), s)))
510 if (!strcmp (buff, "UL")) {
512 fprintf (stderr, "%s: Skipping UL.\n", Progname);
513 if (!(s = get_token (buff, sizeof (buff), s)))
517 memset(val, 0, sizeof(val));
520 if (!strcmp (buff, "}"))
523 m_strcat(val, sizeof(val), buff);
525 while ((s = get_token (buff, sizeof (buff), s)));
528 print_confline (varname, type, val);
531 static void char_to_escape (char *dest, unsigned int c)
535 strcpy (dest, "\\r");
536 break; /* __STRCPY_CHECKED__ */
538 strcpy (dest, "\\n");
539 break; /* __STRCPY_CHECKED__ */
541 strcpy (dest, "\\t");
542 break; /* __STRCPY_CHECKED__ */
544 strcpy (dest, "\\f");
545 break; /* __STRCPY_CHECKED__ */
547 sprintf (dest, "\\%03o", c);
551 static void conf_char_to_escape (unsigned int c)
555 char_to_escape (buff, c);
559 static void conf_print_strval (const char *v)
562 if (*v < ' ' || *v & 0x80) {
563 conf_char_to_escape ((unsigned int) *v);
567 if (*v == '"' || *v == '\\')
573 static void man_print_strval (const char *v)
576 if (*v < ' ' || *v & 0x80) {
578 conf_char_to_escape ((unsigned int) *v);
591 static void sgml_print_strval (const char *v)
596 if (*v < ' ' || *v & 0x80) {
597 char_to_escape (buff, (unsigned int) *v);
601 sgml_fputc ((unsigned int) *v);
605 static int sgml_fputc (int c)
609 return add_s ("<");
611 return add_s (">");
614 return add_s ("$");
616 return add_s ("_");
618 return add_s ("%");
621 return add_s ("&");
624 return add_s ("\");
626 return add_s (""");
628 return add_s ("[");
630 return add_s ("]");
632 return add_s ("˜");
639 static int sgml_fputs (const char *s)
642 if (sgml_fputc ((unsigned int) *s) == EOF)
648 /* reduce CDATA to ID */
649 static int sgml_id_fputs (const char *s) {
657 if (sgml_fputc ((unsigned int) id) == EOF)
663 static void print_confline (const char *varname, int type, const char *val)
668 switch (OutputFormat) {
669 /* configuration file */
672 if (type == DT_SYS) {
679 if (type == DT_STR || type == DT_RX || type == DT_ADDR
680 || type == DT_PATH) {
684 conf_print_strval (val);
687 else if (type != DT_SYN) {
694 add_s ("\n#\n# Name: ");
696 add_s ("\n# Type: ");
697 add_s (type2human (type));
698 if (type == DT_STR || type == DT_RX || type == DT_ADDR
699 || type == DT_PATH) {
700 add_s ("\n# Default: \"");
701 conf_print_strval (val);
705 add_s ("\n# Default: ");
720 add_s (type2human (type));
722 if (type == DT_STR || type == DT_RX || type == DT_ADDR
723 || type == DT_PATH) {
724 add_s ("Default: \\(lq");
725 man_print_strval (val);
729 add_s (type == DT_SYS ? "Value: " : "Default: ");
738 /* SGML based manual */
741 add_s ("\n<muttng-doc:vardef name=\"");
742 sgml_fputs (varname);
743 add_s ("\">\n<para>Type: <literal>");
744 add_s (type2human (type));
745 add_s ("</literal></para>\n");
747 if (type == DT_STR || type == DT_RX || type == DT_ADDR
748 || type == DT_PATH) {
749 add_s ("<para>\nDefault: <literal>"");
750 sgml_print_strval (val);
751 add_s (""</literal>");
755 add_s (type == DT_SYS ? "Value: " : "Default: ");
758 add_s ("</literal>");
770 ** Documentation line parser
772 ** The following code parses specially formatted documentation
773 ** comments in init.h.
775 ** The format is very remotely inspired by nroff. Most important, it's
776 ** easy to parse and convert, and it was easy to generate from the SGML
777 ** source of mutt's original manual.
779 ** - \fI switches to italics
780 ** - \fB switches to boldface
781 ** - \fT switches to typewriter for SGML
782 ** - \fP switches to normal display
783 ** - .dl on a line starts a definition list (name taken taken from HTML).
784 ** - .dt starts a term in a definition list.
785 ** - .dd starts a definition in a definition list.
786 ** - .de on a line finishes a definition list.
787 ** - .ts on a line starts a "tscreen" environment (name taken from SGML).
788 ** - .te on a line finishes this environment.
789 ** - .pp on a line starts a paragraph.
790 ** - \$word will be converted to a reference to word, where appropriate.
791 ** Note that \$$word is possible as well.
792 ** - '. ' in the beginning of a line expands to two space characters.
793 ** This is used to protect indentations in tables.
796 /* close eventually-open environments. */
798 static int fd_recurse = 0;
800 static int flush_doc (int docstat)
802 if (docstat & D_INIT)
806 fprintf (stderr, "%s: Internal error, recursion in flush_doc()!\n",
811 if (docstat & (D_PA))
812 docstat = print_it (SP_END_PAR, NULL, docstat);
814 if (docstat & (D_TAB))
815 docstat = print_it (SP_END_TAB, NULL, docstat);
817 if (docstat & (D_DL))
818 docstat = print_it (SP_END_DL, NULL, docstat);
820 if (docstat & (D_EM | D_BF | D_TT))
821 docstat = print_it (SP_END_FT, NULL, docstat);
823 docstat = print_it (SP_END_SECT, NULL, docstat);
825 docstat = print_it (SP_NEWLINE, NULL, 0);
831 /* print something. */
833 static int print_it (int special, char *str, int docstat)
835 int onl = docstat & (D_NL | D_NP);
837 docstat &= ~(D_NL | D_NP | D_INIT);
839 switch (OutputFormat) {
840 /* configuration file */
844 static int Continuation = 0;
847 docstat &= ~(D_EM | D_BF | D_TT);
925 if (docstat & D_DT) {
928 for (i = STRLEN (str); i < 8; i++)
946 docstat &= ~(D_EM | D_BF | D_TT);
953 docstat &= ~(D_EM | D_TT);
960 docstat &= ~(D_BF | D_TT);
966 docstat &= ~(D_BF | D_EM);
995 add_s ("\n.IP\n.DS\n.sp\n.ft CR\n.nf\n");
996 docstat |= D_TAB | D_NL;
1001 add_s ("\n.fi\n.ec\n.ft P\n.sp\n");
1031 for (; *str; str++) {
1034 else if (*str == '\\')
1036 else if (!strncmp (str, "``", 2)) {
1040 else if (!strncmp (str, "''", 2)) {
1054 /* SGML based manual */
1061 add_s ("</emphasis>");
1063 add_s ("</emphasis>");
1065 add_s ("</literal>");
1066 docstat &= ~(D_EM | D_BF | D_TT);
1071 add_s ("<emphasis role=\"bold\">");
1073 docstat &= ~(D_EM | D_TT);
1078 add_s ("<emphasis>");
1080 docstat &= ~(D_BF | D_TT);
1085 add_s ("<literal>");
1087 docstat &= ~(D_EM | D_BF);
1110 add_s ("</para>\n");
1119 add_s ("\n<screen>\n");
1120 docstat |= D_TAB | D_NL;
1125 add_s ("\n</screen>");
1132 add_s ("\n<variablelist>\n");
1138 add_s ("<varlistentry><term>");
1143 add_s ("</term>\n<listitem><para>\n");
1149 add_s ("</para></listitem></varlistentry></variablelist>\n");
1150 docstat &= ~(D_DL|D_DD);
1155 add_s ("</para>\n");
1161 add_s ("</para></listitem></varlistentry>\n");
1167 add_s ("</muttng-doc:vardef>\n");
1172 if (docstat & D_TAB)
1181 /* make gcc happy (unreached) */
1189 void print_ref (int output_dollar, const char *ref)
1191 switch (OutputFormat) {
1200 add_s ("<link linkend=\"");
1201 sgml_id_fputs (ref);
1214 static int commit_buff (char *buff, char **d, int docstat)
1218 docstat = print_it (SP_STR, buff, docstat);
1225 static int handle_docline (char *l, int docstat)
1227 char buff[BUFFSIZE];
1233 fprintf (stderr, "%s: handle_docline `%s'\n", Progname, l);
1235 if (!strncmp (l, ".pp", 3))
1236 return print_it (SP_NEWPAR, NULL, docstat);
1237 else if (!strncmp (l, ".ts", 3))
1238 return print_it (SP_START_TAB, NULL, docstat);
1239 else if (!strncmp (l, ".te", 3))
1240 return print_it (SP_END_TAB, NULL, docstat);
1241 else if (!strncmp (l, ".dl", 3))
1242 return print_it (SP_START_DL, NULL, docstat);
1243 else if (!strncmp (l, ".de", 3))
1244 return print_it (SP_END_DL, NULL, docstat);
1245 else if (!strncmp (l, ". ", 2))
1248 for (s = l, d = buff; *s; s++) {
1249 if (!strncmp (s, "\\(as", 4)) {
1253 else if (!strncmp (s, "\\(rs", 4)) {
1257 else if (!strncmp (s, "\\fI", 3)) {
1258 docstat = commit_buff (buff, &d, docstat);
1259 docstat = print_it (SP_START_EM, NULL, docstat);
1262 else if (!strncmp (s, "\\fB", 3)) {
1263 docstat = commit_buff (buff, &d, docstat);
1264 docstat = print_it (SP_START_BF, NULL, docstat);
1267 else if (!strncmp (s, "\\fT", 3)) {
1268 docstat = commit_buff (buff, &d, docstat);
1269 docstat = print_it (SP_START_TT, NULL, docstat);
1272 else if (!strncmp (s, "\\fP", 3)) {
1273 docstat = commit_buff (buff, &d, docstat);
1274 docstat = print_it (SP_END_FT, NULL, docstat);
1277 else if (!strncmp (s, ".dt", 3)) {
1278 if (docstat & D_DD) {
1279 docstat = commit_buff (buff, &d, docstat);
1280 docstat = print_it (SP_END_DD, NULL, docstat);
1282 docstat = commit_buff (buff, &d, docstat);
1283 docstat = print_it (SP_DT, NULL, docstat);
1286 else if (!strncmp (s, ".dd", 3)) {
1287 docstat = commit_buff (buff, &d, docstat);
1288 docstat = print_it (SP_DD, NULL, docstat);
1291 else if (*s == '$') {
1292 int output_dollar = 0;
1306 while (isalnum ((unsigned char) *s) || *s == '-' || *s == '_')
1309 docstat = commit_buff (buff, &d, docstat);
1312 print_ref (output_dollar, ref);
1321 docstat = commit_buff (buff, &d, docstat);
1322 return print_it (SP_NEWLINE, NULL, docstat);