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
46 extern char *sys_errlist[];
49 #define strerror(x) ((x) > 0 && (x) < sys_nerr) ? sys_errlist[(x)] : 0
50 #endif /* !HAVE_STRERROR */
56 #define STRLEN(s) (s ? strlen(s) : 0)
64 static int outcount = 0;
65 static var_t *outbuf = NULL;
67 static int var_cmp (const void *a, const void *b)
69 return (strcmp (((var_t *) a)->name, ((var_t *) b)->name));
72 enum output_formats_t {
73 F_CONF, F_MAN, F_SGML, F_NONE
80 #define D_TAB (1 << 4)
82 #define D_INIT (1 << 6)
103 enum output_formats_t OutputFormat = F_NONE;
107 static char *get_token (char *, size_t, char *);
108 static char *skip_ws (char *);
109 static const char *type2human (int);
110 static int buff2type (const char *);
111 static int flush_doc (int);
112 static int handle_docline (char *, int);
113 static int print_it (int, char *, int);
114 static void print_confline (const char *, int, const char *);
115 static void handle_confline (char *);
116 static void makedoc (FILE *, FILE *);
117 static int sgml_fputc (int);
118 static int sgml_fputs (const char *);
119 static void add_var (const char *);
120 static int add_s (const char *);
121 static int add_c (int);
123 int main (int argc, char *argv[])
128 if ((Progname = strrchr (argv[0], '/')))
133 while ((c = getopt (argc, argv, "cmsd")) != EOF) {
136 OutputFormat = F_CONF;
139 OutputFormat = F_MAN;
142 OutputFormat = F_SGML;
149 fprintf (stderr, "%s: bad command line parameter.\n", Progname);
155 if (optind != argc) {
156 if ((f = fopen (argv[optind], "r")) == NULL) {
157 fprintf (stderr, "%s: Can't open %s (%s).\n",
158 Progname, argv[optind], strerror (errno));
165 switch (OutputFormat) {
173 fprintf (stderr, "%s: No output format specified.\n", Progname);
184 static void add_var (const char *name)
186 outbuf = realloc (outbuf, (++outcount) * sizeof (var_t));
187 outbuf[outcount - 1].seen = 0;
188 outbuf[outcount - 1].name = strdup (name);
189 outbuf[outcount - 1].descr = NULL;
192 static int add_s (const char *s)
194 size_t lnew = STRLEN (s), lold = STRLEN (outbuf[outcount - 1].descr);
198 if (!outbuf[outcount - 1].seen) {
200 outbuf[outcount - 1].seen = 1;
204 outbuf[outcount - 1].descr = strdup (s);
206 outbuf[outcount - 1].descr =
207 realloc (outbuf[outcount - 1].descr, lold + lnew + 1);
208 memcpy (&(outbuf[outcount - 1].descr[lold - 1]) + 1, s, lnew);
210 outbuf[outcount - 1].descr[lold + lnew] = '\0';
214 static int add_c (int c)
216 char buf[2] = "\0\0";
219 return (add_s (buf));
222 static void makedoc (FILE * in, FILE * out)
224 char buffer[BUFFSIZE];
225 char token[BUFFSIZE];
229 int docstat = D_INIT;
231 while ((fgets (buffer, sizeof (buffer), in))) {
233 if ((p = strchr (buffer, '\n')) == NULL) {
234 fprintf (stderr, "%s: Line %d too long. Ask a wizard to enlarge\n"
235 "%s: my buffer size.\n", Progname, line, Progname);
241 if (!(p = get_token (token, sizeof (token), buffer)))
245 fprintf (stderr, "%s: line %d. first token: \"%s\".\n",
246 Progname, line, token);
249 if (!strcmp (token, "/*++*/"))
251 else if (!strcmp (token, "/*--*/")) {
252 docstat = flush_doc (docstat);
255 else if (active && (!strcmp (token, "/**") || !strcmp (token, "**")))
256 docstat = handle_docline (p, docstat);
257 else if (active && !strcmp (token, "{")) {
258 docstat = flush_doc (docstat);
264 qsort (outbuf, outcount, sizeof (var_t), &var_cmp);
265 for (line = 0; line < outcount; line++) {
266 if (outbuf[line].descr) {
267 fprintf (out, "%s\n", outbuf[line].descr);
268 free (outbuf[line].descr);
270 free (outbuf[line].name);
275 /* skip whitespace */
277 static char *skip_ws (char *s)
279 while (*s && isspace ((unsigned char) *s))
285 /* isolate a token */
287 static char single_char_tokens[] = "[]{},;|";
289 static char *get_token (char *d, size_t l, char *s)
296 fprintf (stderr, "%s: get_token called for `%s'.\n", Progname, s);
301 fprintf (stderr, "%s: argumet after skip_ws(): `%s'.\n", Progname, s);
305 fprintf (stderr, "%s: no more tokens on this line.\n", Progname);
309 if (strchr (single_char_tokens, *s)) {
311 fprintf (stderr, "%s: found single character token `%c'.\n",
321 fprintf (stderr, "%s: found quote character.\n", Progname);
328 for (t = s; *t && --l > 0; t++) {
329 if (*t == '\\' && !t[1])
332 if (is_quoted && *t == '\\') {
333 switch ((*d = *++t)) {
352 if (is_quoted && *t == '"') {
356 else if (!is_quoted && strchr (single_char_tokens, *t))
358 else if (!is_quoted && isspace ((unsigned char) *t))
367 fprintf (stderr, "%s: Got %stoken: `%s'.\n",
368 Progname, is_quoted ? "quoted " : "", dd);
369 fprintf (stderr, "%s: Remainder: `%s'.\n", Progname, t);
377 ** Configuration line parser
379 ** The following code parses a line from init.h which declares
380 ** a configuration variable.
384 /* note: the following enum must be in the same order as the
385 * following string definitions!
408 "DT_NONE", "-none-"}, {
409 "DT_BOOL", "boolean"}, {
410 "DT_NUM", "number"}, {
411 "DT_STR", "string"}, {
412 "DT_PATH", "path"}, {
413 "DT_QUAD", "quadoption"}, {
414 "DT_SORT", "sort order"}, {
415 "DT_RX", "regular expression"}, {
416 "DT_MAGIC", "folder magic"}, {
418 "DT_ADDR", "e-mail address"}, {
419 "DT_SYS", "system property"}, {
424 static int buff2type (const char *s)
428 for (type = DT_NONE; types[type].machine; type++)
429 if (!strcmp (types[type].machine, s))
435 static const char *type2human (int type)
437 return types[type].human;
439 static void handle_confline (char *s)
441 char varname[BUFFSIZE];
447 /* xxx - put this into an actual state machine? */
450 if (!(s = get_token (varname, sizeof (varname), s)))
454 if (!(s = get_token (buff, sizeof (buff), s)))
458 if (!(s = get_token (buff, sizeof (buff), s)))
461 type = buff2type (buff);
463 /* possibly a "|" or comma */
464 if (!(s = get_token (buff, sizeof (buff), s)))
467 if (!strcmp (buff, "|")) {
469 fprintf (stderr, "%s: Expecting <subtype> <comma>.\n", Progname);
470 /* ignore subtype and comma */
471 if (!(s = get_token (buff, sizeof (buff), s)))
473 if (!(s = get_token (buff, sizeof (buff), s)))
480 if (!(s = get_token (buff, sizeof (buff), s)))
482 if (!strcmp (buff, ","))
486 /* option name or UL &address */
487 if (!(s = get_token (buff, sizeof (buff), s)))
489 if (!strcmp (buff, "UL"))
490 if (!(s = get_token (buff, sizeof (buff), s)))
494 if (!(s = get_token (buff, sizeof (buff), s)))
498 fprintf (stderr, "%s: Expecting default value.\n", Progname);
500 /* <default value> or UL <default value> */
501 if (!(s = get_token (buff, sizeof (buff), s)))
503 if (!strcmp (buff, "UL")) {
505 fprintf (stderr, "%s: Skipping UL.\n", Progname);
506 if (!(s = get_token (buff, sizeof (buff), s)))
510 memset (val, 0, sizeof (val));
513 if (!strcmp (buff, "}"))
516 strncpy (val + STRLEN (val), buff, sizeof (val) - STRLEN (val));
518 while ((s = get_token (buff, sizeof (buff), s)));
521 print_confline (varname, type, val);
524 static void char_to_escape (char *dest, unsigned int c)
528 strcpy (dest, "\\r");
529 break; /* __STRCPY_CHECKED__ */
531 strcpy (dest, "\\n");
532 break; /* __STRCPY_CHECKED__ */
534 strcpy (dest, "\\t");
535 break; /* __STRCPY_CHECKED__ */
537 strcpy (dest, "\\f");
538 break; /* __STRCPY_CHECKED__ */
540 sprintf (dest, "\\%03o", c);
544 static void conf_char_to_escape (unsigned int c)
548 char_to_escape (buff, c);
552 static void conf_print_strval (const char *v)
555 if (*v < ' ' || *v & 0x80) {
556 conf_char_to_escape ((unsigned int) *v);
560 if (*v == '"' || *v == '\\')
566 static void man_print_strval (const char *v)
569 if (*v < ' ' || *v & 0x80) {
571 conf_char_to_escape ((unsigned int) *v);
584 static void sgml_print_strval (const char *v)
589 if (*v < ' ' || *v & 0x80) {
590 char_to_escape (buff, (unsigned int) *v);
594 sgml_fputc ((unsigned int) *v);
598 static int sgml_fputc (int c)
602 return add_s ("<");
604 return add_s (">");
606 return add_s ("$");
608 return add_s ("_");
610 return add_s ("%");
612 return add_s ("&");
614 return add_s ("\");
616 return add_s ("&dquot;");
618 return add_s ("[");
620 return add_s ("]");
622 return add_s ("˜");
628 static int sgml_fputs (const char *s)
631 if (sgml_fputc ((unsigned int) *s) == EOF)
637 static void print_confline (const char *varname, int type, const char *val)
642 switch (OutputFormat) {
643 /* configuration file */
646 if (type == DT_SYS) {
653 if (type == DT_STR || type == DT_RX || type == DT_ADDR
654 || type == DT_PATH) {
658 conf_print_strval (val);
661 else if (type != DT_SYN) {
668 add_s ("\n#\n# Name: ");
670 add_s ("\n# Type: ");
671 add_s (type2human (type));
672 if (type == DT_STR || type == DT_RX || type == DT_ADDR
673 || type == DT_PATH) {
674 add_s ("\n# Default: \"");
675 conf_print_strval (val);
679 add_s ("\n# Default: ");
694 add_s (type2human (type));
696 if (type == DT_STR || type == DT_RX || type == DT_ADDR
697 || type == DT_PATH) {
698 add_s ("Default: \\(lq");
699 man_print_strval (val);
703 add_s (type == DT_SYS ? "Value: " : "Default: ");
712 /* SGML based manual */
716 sgml_fputs (varname);
717 add_s ("<label id=\"");
720 add_s ("\n<p>\nType: <tt>");
721 add_s (type2human (type));
724 if (type == DT_STR || type == DT_RX || type == DT_ADDR
725 || type == DT_PATH) {
726 add_s ("<p>\nDefault: <tt>&dquot;");
727 sgml_print_strval (val);
728 add_s ("&dquot;</tt>");
732 add_s (type == DT_SYS ? "Value: " : "Default: ");
746 ** Documentation line parser
748 ** The following code parses specially formatted documentation
749 ** comments in init.h.
751 ** The format is very remotely inspired by nroff. Most important, it's
752 ** easy to parse and convert, and it was easy to generate from the SGML
753 ** source of mutt's original manual.
755 ** - \fI switches to italics
756 ** - \fB switches to boldface
757 ** - \fT switches to typewriter for SGML
758 ** - \fP switches to normal display
759 ** - .dl on a line starts a definition list (name taken taken from HTML).
760 ** - .dt starts a term in a definition list.
761 ** - .dd starts a definition in a definition list.
762 ** - .de on a line finishes a definition list.
763 ** - .ts on a line starts a "tscreen" environment (name taken from SGML).
764 ** - .te on a line finishes this environment.
765 ** - .pp on a line starts a paragraph.
766 ** - \$word will be converted to a reference to word, where appropriate.
767 ** Note that \$$word is possible as well.
768 ** - '. ' in the beginning of a line expands to two space characters.
769 ** This is used to protect indentations in tables.
772 /* close eventually-open environments. */
774 static int fd_recurse = 0;
776 static int flush_doc (int docstat)
778 if (docstat & D_INIT)
782 fprintf (stderr, "%s: Internal error, recursion in flush_doc()!\n",
787 if (docstat & (D_TAB))
788 docstat = print_it (SP_END_TAB, NULL, docstat);
790 if (docstat & (D_DL))
791 docstat = print_it (SP_END_DL, NULL, docstat);
793 if (docstat & (D_EM | D_BF | D_TT))
794 docstat = print_it (SP_END_FT, NULL, docstat);
796 docstat = print_it (SP_NEWLINE, NULL, 0);
802 /* print something. */
804 static int print_it (int special, char *str, int docstat)
806 int onl = docstat & (D_NL | D_NP);
808 docstat &= ~(D_NL | D_NP | D_INIT);
810 switch (OutputFormat) {
811 /* configuration file */
815 static int Continuation = 0;
818 docstat &= ~(D_EM | D_BF | D_TT);
896 if (docstat & D_DT) {
899 for (i = STRLEN (str); i < 8; i++)
917 docstat &= ~(D_EM | D_BF | D_TT);
924 docstat &= ~(D_EM | D_TT);
931 docstat &= ~(D_BF | D_TT);
937 docstat &= ~(D_BF | D_EM);
966 add_s ("\n.IP\n.DS\n.sp\n.ft CR\n.nf\n");
967 docstat |= D_TAB | D_NL;
972 add_s ("\n.fi\n.ec\n.ft P\n.sp\n");
1002 for (; *str; str++) {
1005 else if (*str == '\\')
1007 else if (!strncmp (str, "``", 2)) {
1011 else if (!strncmp (str, "''", 2)) {
1025 /* SGML based manual */
1037 docstat &= ~(D_EM | D_BF | D_TT);
1044 docstat &= ~(D_EM | D_TT);
1051 docstat &= ~(D_BF | D_TT);
1058 docstat &= ~(D_EM | D_BF);
1087 add_s ("\n<tscreen><verb>\n");
1088 docstat |= D_TAB | D_NL;
1093 add_s ("\n</verb></tscreen>");
1100 add_s ("\n<descrip>\n");
1116 add_s ("</descrip>\n");
1122 if (docstat & D_TAB)
1131 /* make gcc happy (unreached) */
1139 void print_ref (int output_dollar, const char *ref)
1141 switch (OutputFormat) {
1150 add_s ("<ref id=\"");
1152 add_s ("\" name=\"");
1164 static int commit_buff (char *buff, char **d, int docstat)
1168 docstat = print_it (SP_STR, buff, docstat);
1175 static int handle_docline (char *l, int docstat)
1177 char buff[BUFFSIZE];
1183 fprintf (stderr, "%s: handle_docline `%s'\n", Progname, l);
1185 if (!strncmp (l, ".pp", 3))
1186 return print_it (SP_NEWPAR, NULL, docstat);
1187 else if (!strncmp (l, ".ts", 3))
1188 return print_it (SP_START_TAB, NULL, docstat);
1189 else if (!strncmp (l, ".te", 3))
1190 return print_it (SP_END_TAB, NULL, docstat);
1191 else if (!strncmp (l, ".dl", 3))
1192 return print_it (SP_START_DL, NULL, docstat);
1193 else if (!strncmp (l, ".de", 3))
1194 return print_it (SP_END_DL, NULL, docstat);
1195 else if (!strncmp (l, ". ", 2))
1198 for (s = l, d = buff; *s; s++) {
1199 if (!strncmp (s, "\\(as", 4)) {
1203 else if (!strncmp (s, "\\(rs", 4)) {
1207 else if (!strncmp (s, "\\fI", 3)) {
1208 docstat = commit_buff (buff, &d, docstat);
1209 docstat = print_it (SP_START_EM, NULL, docstat);
1212 else if (!strncmp (s, "\\fB", 3)) {
1213 docstat = commit_buff (buff, &d, docstat);
1214 docstat = print_it (SP_START_BF, NULL, docstat);
1217 else if (!strncmp (s, "\\fT", 3)) {
1218 docstat = commit_buff (buff, &d, docstat);
1219 docstat = print_it (SP_START_TT, NULL, docstat);
1222 else if (!strncmp (s, "\\fP", 3)) {
1223 docstat = commit_buff (buff, &d, docstat);
1224 docstat = print_it (SP_END_FT, NULL, docstat);
1227 else if (!strncmp (s, ".dt", 3)) {
1228 docstat = commit_buff (buff, &d, docstat);
1229 docstat = print_it (SP_DT, NULL, docstat);
1232 else if (!strncmp (s, ".dd", 3)) {
1233 docstat = commit_buff (buff, &d, docstat);
1234 docstat = print_it (SP_DD, NULL, docstat);
1237 else if (*s == '$') {
1238 int output_dollar = 0;
1252 while (isalnum ((unsigned char) *s) || *s == '-' || *s == '_')
1255 docstat = commit_buff (buff, &d, docstat);
1258 print_ref (output_dollar, ref);
1267 docstat = commit_buff (buff, &d, docstat);
1268 return print_it (SP_NEWLINE, NULL, docstat);