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>
33 extern char *sys_errlist[];
36 #define strerror(x) ((x) > 0 && (x) < sys_nerr) ? sys_errlist[(x)] : 0
37 #endif /* !HAVE_STRERROR */
43 #define STRLEN(s) (s ? strlen(s) : 0)
51 static int outcount = 0;
52 static var_t *outbuf = NULL;
54 static int var_cmp (const void *a, const void *b)
56 return (strcmp (((var_t *) a)->name, ((var_t *) b)->name));
59 enum output_formats_t {
60 F_CONF, F_MAN, F_SGML, F_NONE
67 #define D_TAB (1 << 4)
69 #define D_INIT (1 << 6)
73 #define D_PA (1 << 10)
95 enum output_formats_t OutputFormat = F_NONE;
98 static char *get_token (char *, size_t, char *);
99 static char *skip_ws (char *);
100 static const char *type2human (int);
101 static int buff2type (const char *);
102 static int flush_doc (int);
103 static int handle_docline (char *, int);
104 static int print_it (int, char *, int);
105 static void print_confline (const char *, int, const char *);
106 static void handle_confline (char *);
107 static void makedoc (FILE *, FILE *);
108 static int sgml_fputc (int);
109 static int sgml_fputs (const char *);
110 static int sgml_id_fputs (const char *);
111 static void add_var (const char *);
112 static int add_s (const char *);
113 static int add_c (int);
115 int main (int argc, char *argv[])
120 if ((Progname = strrchr (argv[0], '/')))
125 while ((c = getopt (argc, argv, "cmsd")) != EOF) {
128 OutputFormat = F_CONF;
131 OutputFormat = F_MAN;
134 OutputFormat = F_SGML;
138 fprintf (stderr, "%s: bad command line parameter.\n", Progname);
144 if (optind != argc) {
145 if ((f = fopen (argv[optind], "r")) == NULL) {
146 fprintf (stderr, "%s: Can't open %s (%s).\n",
147 Progname, argv[optind], strerror (errno));
154 switch (OutputFormat) {
162 fprintf (stderr, "%s: No output format specified.\n", Progname);
173 static void add_var (const char *name)
175 outbuf = realloc (outbuf, (++outcount) * sizeof (var_t));
176 outbuf[outcount - 1].seen = 0;
177 outbuf[outcount - 1].name = strdup(name);
178 outbuf[outcount - 1].descr = NULL;
181 static int add_s (const char *s)
183 size_t lnew = STRLEN (s), lold = STRLEN (outbuf[outcount - 1].descr);
187 if (!outbuf[outcount - 1].seen) {
189 outbuf[outcount - 1].seen = 1;
193 outbuf[outcount - 1].descr = strdup(s);
195 outbuf[outcount - 1].descr =
196 realloc (outbuf[outcount - 1].descr, lold + lnew + 1);
197 memcpy (&(outbuf[outcount - 1].descr[lold - 1]) + 1, s, lnew);
199 outbuf[outcount - 1].descr[lold + lnew] = '\0';
203 static int add_c (int c)
205 char buf[2] = "\0\0";
208 return (add_s (buf));
211 static void makedoc (FILE * in, FILE * out)
213 char buffer[BUFFSIZE];
214 char token[BUFFSIZE];
218 int docstat = D_INIT;
220 while ((fgets (buffer, sizeof (buffer), in))) {
222 if ((p = strchr (buffer, '\n')) == NULL) {
223 fprintf (stderr, "%s: Line %d too long. Ask a wizard to enlarge\n"
224 "%s: my buffer size.\n", Progname, line, Progname);
230 if (!(p = get_token (token, sizeof (token), buffer)))
233 if (!strcmp (token, "/*++*/"))
235 else if (!strcmp (token, "/*--*/")) {
236 docstat = flush_doc (docstat);
239 else if (active && (!strcmp (token, "/**") || !strcmp (token, "**")))
240 docstat = handle_docline (p, docstat);
241 else if (active && !strcmp (token, "{")) {
242 docstat = flush_doc (docstat);
248 qsort (outbuf, outcount, sizeof (var_t), &var_cmp);
249 for (line = 0; line < outcount; line++) {
250 if (outbuf[line].descr) {
251 fprintf (out, "%s\n", outbuf[line].descr);
252 free (outbuf[line].descr);
254 free (outbuf[line].name);
259 /* skip whitespace */
261 static char *skip_ws (char *s)
263 while (*s && isspace ((unsigned char) *s))
269 /* isolate a token */
271 static char single_char_tokens[] = "[]{},;|";
273 static char *get_token (char *d, size_t l, char *s)
285 if (strchr (single_char_tokens, *s)) {
296 for (t = s; *t && --l > 0; t++) {
297 if (*t == '\\' && !t[1])
300 if (is_quoted && *t == '\\') {
301 switch ((*d = *++t)) {
320 if (is_quoted && *t == '"') {
324 else if (!is_quoted && strchr (single_char_tokens, *t))
326 else if (!is_quoted && isspace ((unsigned char) *t))
339 ** Configuration line parser
341 ** The following code parses a line from init.h which declares
342 ** a configuration variable.
346 /* note: the following enum must be in the same order as the
347 * following string definitions!
370 "DT_NONE", "-none-"}, {
371 "DT_BOOL", "boolean"}, {
372 "DT_NUM", "number"}, {
373 "DT_STR", "string"}, {
374 "DT_PATH", "path"}, {
375 "DT_QUAD", "quadoption"}, {
376 "DT_SORT", "sort order"}, {
377 "DT_RX", "regular expression"}, {
378 "DT_MAGIC", "folder magic"}, {
380 "DT_ADDR", "e-mail address"}, {
381 "DT_SYS", "system property"}, {
386 static int buff2type (const char *s)
390 for (type = DT_NONE; types[type].machine; type++)
391 if (!strcmp (types[type].machine, s))
397 static const char *type2human (int type)
399 return types[type].human;
401 static void handle_confline (char *s)
403 char varname[BUFFSIZE];
409 /* xxx - put this into an actual state machine? */
412 if (!(s = get_token (varname, sizeof (varname), s)))
416 if (!(s = get_token (buff, sizeof (buff), s)))
420 if (!(s = get_token (buff, sizeof (buff), s)))
423 type = buff2type (buff);
425 /* possibly a "|" or comma */
426 if (!(s = get_token (buff, sizeof (buff), s)))
429 if (!strcmp (buff, "|")) {
430 /* ignore subtype and comma */
431 if (!(s = get_token (buff, sizeof (buff), s)))
433 if (!(s = get_token (buff, sizeof (buff), s)))
440 if (!(s = get_token (buff, sizeof (buff), s)))
442 if (!strcmp (buff, ","))
446 /* option name or UL &address */
447 if (!(s = get_token (buff, sizeof (buff), s)))
449 if (!strcmp (buff, "UL"))
450 if (!(s = get_token (buff, sizeof (buff), s)))
454 if (!(s = get_token (buff, sizeof (buff), s)))
457 /* <default value> or UL <default value> */
458 if (!(s = get_token (buff, sizeof (buff), s)))
460 if (!strcmp (buff, "UL")) {
461 if (!(s = get_token (buff, sizeof (buff), s)))
465 memset(val, 0, sizeof(val));
468 if (!strcmp (buff, "}"))
471 m_strcat(val, sizeof(val), buff);
473 while ((s = get_token (buff, sizeof (buff), s)));
476 print_confline (varname, type, val);
479 static void char_to_escape (char *dest, unsigned int c)
483 strcpy (dest, "\\r");
484 break; /* __STRCPY_CHECKED__ */
486 strcpy (dest, "\\n");
487 break; /* __STRCPY_CHECKED__ */
489 strcpy (dest, "\\t");
490 break; /* __STRCPY_CHECKED__ */
492 strcpy (dest, "\\f");
493 break; /* __STRCPY_CHECKED__ */
495 sprintf (dest, "\\%03o", c);
499 static void conf_char_to_escape (unsigned int c)
503 char_to_escape (buff, c);
507 static void conf_print_strval (const char *v)
510 if (*v < ' ' || *v & 0x80) {
511 conf_char_to_escape ((unsigned int) *v);
515 if (*v == '"' || *v == '\\')
521 static void man_print_strval (const char *v)
524 if (*v < ' ' || *v & 0x80) {
526 conf_char_to_escape ((unsigned int) *v);
539 static void sgml_print_strval (const char *v)
544 if (*v < ' ' || *v & 0x80) {
545 char_to_escape (buff, (unsigned int) *v);
549 sgml_fputc ((unsigned int) *v);
553 static int sgml_fputc (int c)
557 return add_s ("<");
559 return add_s (">");
562 return add_s ("$");
564 return add_s ("_");
566 return add_s ("%");
569 return add_s ("&");
572 return add_s ("\");
574 return add_s (""");
576 return add_s ("[");
578 return add_s ("]");
580 return add_s ("˜");
587 static int sgml_fputs (const char *s)
590 if (sgml_fputc ((unsigned int) *s) == EOF)
596 /* reduce CDATA to ID */
597 static int sgml_id_fputs (const char *s) {
605 if (sgml_fputc ((unsigned int) id) == EOF)
611 static void print_confline (const char *varname, int type, const char *val)
616 switch (OutputFormat) {
617 /* configuration file */
620 if (type == DT_SYS) {
627 if (type == DT_STR || type == DT_RX || type == DT_ADDR
628 || type == DT_PATH) {
632 conf_print_strval (val);
635 else if (type != DT_SYN) {
642 add_s ("\n#\n# Name: ");
644 add_s ("\n# Type: ");
645 add_s (type2human (type));
646 if (type == DT_STR || type == DT_RX || type == DT_ADDR
647 || type == DT_PATH) {
648 add_s ("\n# Default: \"");
649 conf_print_strval (val);
653 add_s ("\n# Default: ");
668 add_s (type2human (type));
670 if (type == DT_STR || type == DT_RX || type == DT_ADDR
671 || type == DT_PATH) {
672 add_s ("Default: \\(lq");
673 man_print_strval (val);
677 add_s (type == DT_SYS ? "Value: " : "Default: ");
686 /* SGML based manual */
689 add_s ("\n<madmutt-doc:vardef name=\"");
690 sgml_fputs (varname);
691 add_s ("\">\n<para>Type: <literal>");
692 add_s (type2human (type));
693 add_s ("</literal></para>\n");
695 if (type == DT_STR || type == DT_RX || type == DT_ADDR
696 || type == DT_PATH) {
697 add_s ("<para>\nDefault: <literal>"");
698 sgml_print_strval (val);
699 add_s (""</literal>");
703 add_s (type == DT_SYS ? "Value: " : "Default: ");
706 add_s ("</literal>");
718 ** Documentation line parser
720 ** The following code parses specially formatted documentation
721 ** comments in init.h.
723 ** The format is very remotely inspired by nroff. Most important, it's
724 ** easy to parse and convert, and it was easy to generate from the SGML
725 ** source of mutt's original manual.
727 ** - \fI switches to italics
728 ** - \fB switches to boldface
729 ** - \fT switches to typewriter for SGML
730 ** - \fP switches to normal display
731 ** - .dl on a line starts a definition list (name taken taken from HTML).
732 ** - .dt starts a term in a definition list.
733 ** - .dd starts a definition in a definition list.
734 ** - .de on a line finishes a definition list.
735 ** - .ts on a line starts a "tscreen" environment (name taken from SGML).
736 ** - .te on a line finishes this environment.
737 ** - .pp on a line starts a paragraph.
738 ** - \$word will be converted to a reference to word, where appropriate.
739 ** Note that \$$word is possible as well.
740 ** - '. ' in the beginning of a line expands to two space characters.
741 ** This is used to protect indentations in tables.
744 /* close eventually-open environments. */
746 static int fd_recurse = 0;
748 static int flush_doc (int docstat)
750 if (docstat & D_INIT)
754 fprintf (stderr, "%s: Internal error, recursion in flush_doc()!\n",
759 if (docstat & (D_PA))
760 docstat = print_it (SP_END_PAR, NULL, docstat);
762 if (docstat & (D_TAB))
763 docstat = print_it (SP_END_TAB, NULL, docstat);
765 if (docstat & (D_DL))
766 docstat = print_it (SP_END_DL, NULL, docstat);
768 if (docstat & (D_EM | D_BF | D_TT))
769 docstat = print_it (SP_END_FT, NULL, docstat);
771 docstat = print_it (SP_END_SECT, NULL, docstat);
773 docstat = print_it (SP_NEWLINE, NULL, 0);
779 /* print something. */
781 static int print_it (int special, char *str, int docstat)
783 int onl = docstat & (D_NL | D_NP);
785 docstat &= ~(D_NL | D_NP | D_INIT);
787 switch (OutputFormat) {
788 /* configuration file */
792 static int Continuation = 0;
795 docstat &= ~(D_EM | D_BF | D_TT);
873 if (docstat & D_DT) {
876 for (i = STRLEN (str); i < 8; i++)
894 docstat &= ~(D_EM | D_BF | D_TT);
901 docstat &= ~(D_EM | D_TT);
908 docstat &= ~(D_BF | D_TT);
914 docstat &= ~(D_BF | D_EM);
943 add_s ("\n.IP\n.DS\n.sp\n.ft CR\n.nf\n");
944 docstat |= D_TAB | D_NL;
949 add_s ("\n.fi\n.ec\n.ft P\n.sp\n");
979 for (; *str; str++) {
982 else if (*str == '\\')
984 else if (!strncmp (str, "``", 2)) {
988 else if (!strncmp (str, "''", 2)) {
1002 /* SGML based manual */
1009 add_s ("</emphasis>");
1011 add_s ("</emphasis>");
1013 add_s ("</literal>");
1014 docstat &= ~(D_EM | D_BF | D_TT);
1019 add_s ("<emphasis role=\"bold\">");
1021 docstat &= ~(D_EM | D_TT);
1026 add_s ("<emphasis>");
1028 docstat &= ~(D_BF | D_TT);
1033 add_s ("<literal>");
1035 docstat &= ~(D_EM | D_BF);
1058 add_s ("</para>\n");
1067 add_s ("\n<screen>\n");
1068 docstat |= D_TAB | D_NL;
1073 add_s ("\n</screen>");
1080 add_s ("\n<variablelist>\n");
1086 add_s ("<varlistentry><term>");
1091 add_s ("</term>\n<listitem><para>\n");
1097 add_s ("</para></listitem></varlistentry></variablelist>\n");
1098 docstat &= ~(D_DL|D_DD);
1103 add_s ("</para>\n");
1109 add_s ("</para></listitem></varlistentry>\n");
1115 add_s ("</madmutt-doc:vardef>\n");
1120 if (docstat & D_TAB)
1129 /* make gcc happy (unreached) */
1137 void print_ref (int output_dollar, const char *ref)
1139 switch (OutputFormat) {
1148 add_s ("<link linkend=\"");
1149 sgml_id_fputs (ref);
1162 static int commit_buff (char *buff, char **d, int docstat)
1166 docstat = print_it (SP_STR, buff, docstat);
1173 static int handle_docline (char *l, int docstat)
1175 char buff[BUFFSIZE];
1180 if (!strncmp (l, ".pp", 3))
1181 return print_it (SP_NEWPAR, NULL, docstat);
1182 else if (!strncmp (l, ".ts", 3))
1183 return print_it (SP_START_TAB, NULL, docstat);
1184 else if (!strncmp (l, ".te", 3))
1185 return print_it (SP_END_TAB, NULL, docstat);
1186 else if (!strncmp (l, ".dl", 3))
1187 return print_it (SP_START_DL, NULL, docstat);
1188 else if (!strncmp (l, ".de", 3))
1189 return print_it (SP_END_DL, NULL, docstat);
1190 else if (!strncmp (l, ". ", 2))
1193 for (s = l, d = buff; *s; s++) {
1194 if (!strncmp (s, "\\(as", 4)) {
1198 else if (!strncmp (s, "\\(rs", 4)) {
1202 else if (!strncmp (s, "\\fI", 3)) {
1203 docstat = commit_buff (buff, &d, docstat);
1204 docstat = print_it (SP_START_EM, NULL, docstat);
1207 else if (!strncmp (s, "\\fB", 3)) {
1208 docstat = commit_buff (buff, &d, docstat);
1209 docstat = print_it (SP_START_BF, NULL, docstat);
1212 else if (!strncmp (s, "\\fT", 3)) {
1213 docstat = commit_buff (buff, &d, docstat);
1214 docstat = print_it (SP_START_TT, NULL, docstat);
1217 else if (!strncmp (s, "\\fP", 3)) {
1218 docstat = commit_buff (buff, &d, docstat);
1219 docstat = print_it (SP_END_FT, NULL, docstat);
1222 else if (!strncmp (s, ".dt", 3)) {
1223 if (docstat & D_DD) {
1224 docstat = commit_buff (buff, &d, docstat);
1225 docstat = print_it (SP_END_DD, NULL, docstat);
1227 docstat = commit_buff (buff, &d, docstat);
1228 docstat = print_it (SP_DT, NULL, docstat);
1231 else if (!strncmp (s, ".dd", 3)) {
1232 docstat = commit_buff (buff, &d, docstat);
1233 docstat = print_it (SP_DD, NULL, docstat);
1236 else if (*s == '$') {
1237 int output_dollar = 0;
1251 while (isalnum ((unsigned char) *s) || *s == '-' || *s == '_')
1254 docstat = commit_buff (buff, &d, docstat);
1257 print_ref (output_dollar, ref);
1266 docstat = commit_buff (buff, &d, docstat);
1267 return print_it (SP_NEWLINE, NULL, docstat);