2
* Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
** This program parses mutt's init.h and generates documentation in
21
** three different formats:
23
** -> a commented muttrc configuration file
24
** -> nroff, suitable for inclusion in a manual page
25
** -> docbook-xml, suitable for inclusion in the
52
extern char *sys_errlist[];
55
#define strerror(x) ((x) > 0 && (x) < sys_nerr) ? sys_errlist[(x)] : 0
56
#endif /* !HAVE_STRERROR */
64
F_CONF, F_MAN, F_SGML, F_NONE
70
#define D_TAB (1 << 3)
72
#define D_INIT (1 << 5)
98
enum output_formats_t OutputFormat = F_NONE;
102
static char *get_token (char *, size_t, char *);
103
static char *skip_ws (char *);
104
static const char *type2human (int);
105
static int buff2type (const char *);
106
static int flush_doc (int, FILE *);
107
static int handle_docline (char *, FILE *, int);
108
static int print_it (int, char *, FILE *, int);
109
static void print_confline (const char *, int, const char *, FILE *);
110
static void handle_confline (char *, FILE *);
111
static void makedoc (FILE *, FILE *);
112
static void pretty_default (char *, size_t, const char *, int);
113
static int sgml_fputc (int, FILE *);
114
static int sgml_fputs (const char *, FILE *);
115
static int sgml_id_fputs (const char *, FILE *);
117
int main (int argc, char *argv[])
122
if ((Progname = strrchr (argv[0], '/')))
127
while ((c = getopt (argc, argv, "cmsd")) != EOF)
131
case 'c': OutputFormat = F_CONF; break;
132
case 'm': OutputFormat = F_MAN; break;
133
case 's': OutputFormat = F_SGML; break;
134
case 'd': Debug++; break;
137
fprintf (stderr, "%s: bad command line parameter.\n", Progname);
145
if ((f = fopen (argv[optind], "r")) == NULL)
147
fprintf (stderr, "%s: Can't open %s (%s).\n",
148
Progname, argv[optind], strerror (errno));
155
switch (OutputFormat)
159
case F_SGML: makedoc (f, stdout); break;
162
fprintf (stderr, "%s: No output format specified.\n",
175
static void makedoc (FILE *in, FILE *out)
177
char buffer[BUFFSIZE];
178
char token[BUFFSIZE];
182
int docstat = D_INIT;
184
while ((fgets (buffer, sizeof (buffer), in)))
187
if ((p = strchr (buffer, '\n')) == NULL)
189
fprintf (stderr, "%s: Line %d too long. Ask a wizard to enlarge\n"
190
"%s: my buffer size.\n", Progname, line, Progname);
196
if (!(p = get_token (token, sizeof (token), buffer)))
201
fprintf (stderr, "%s: line %d. first token: \"%s\".\n",
202
Progname, line, token);
205
if (!strcmp (token, "/*++*/"))
207
else if (!strcmp (token, "/*--*/"))
209
docstat = flush_doc (docstat, out);
212
else if (active && (!strcmp (token, "/**") || !strcmp (token, "**")))
213
docstat = handle_docline (p, out, docstat);
214
else if (active && !strcmp (token, "{"))
216
docstat = flush_doc (docstat, out);
217
handle_confline (p, out);
220
flush_doc (docstat, out);
224
/* skip whitespace */
226
static char *skip_ws (char *s)
228
while (*s && isspace ((unsigned char) *s))
234
/* isolate a token */
236
static char single_char_tokens[] = "[]{},;|";
238
static char *get_token (char *d, size_t l, char *s)
245
fprintf (stderr, "%s: get_token called for `%s'.\n",
251
fprintf (stderr, "%s: argumet after skip_ws(): `%s'.\n",
257
fprintf (stderr, "%s: no more tokens on this line.\n", Progname);
261
if (strchr (single_char_tokens, *s))
265
fprintf (stderr, "%s: found single character token `%c'.\n",
277
fprintf (stderr, "%s: found quote character.\n", Progname);
284
for (t = s; *t && --l > 0; t++)
286
if (*t == '\\' && !t[1])
289
if (is_quoted && *t == '\\')
293
case 'n': *d = '\n'; break;
294
case 't': *d = '\t'; break;
295
case 'r': *d = '\r'; break;
296
case 'a': *d = '\a'; break;
303
if (is_quoted && *t == '"')
308
else if (!is_quoted && strchr (single_char_tokens, *t))
310
else if (!is_quoted && isspace ((unsigned char) *t))
320
fprintf (stderr, "%s: Got %stoken: `%s'.\n",
321
Progname, is_quoted ? "quoted " : "", dd);
322
fprintf (stderr, "%s: Remainder: `%s'.\n",
331
** Configuration line parser
333
** The following code parses a line from init.h which declares
334
** a configuration variable.
338
/* note: the following enum must be in the same order as the
339
* following string definitions!
364
{ "DT_NONE", "-none-" },
365
{ "DT_BOOL", "boolean" },
366
{ "DT_NUM", "number" },
367
{ "DT_STR", "string" },
368
{ "DT_PATH", "path" },
369
{ "DT_QUAD", "quadoption" },
370
{ "DT_SORT", "sort order" },
371
{ "DT_RX", "regular expression" },
372
{ "DT_MAGIC", "folder magic" },
374
{ "DT_ADDR", "e-mail address" },
379
static int buff2type (const char *s)
383
for (type = DT_NONE; types[type].machine; type++)
384
if (!strcmp (types[type].machine, s))
390
static const char *type2human (int type)
392
return types[type].human;
394
static void handle_confline (char *s, FILE *out)
396
char varname[BUFFSIZE];
403
/* xxx - put this into an actual state machine? */
406
if (!(s = get_token (varname, sizeof (varname), s))) return;
409
if (!(s = get_token (buff, sizeof (buff), s))) return;
412
if (!(s = get_token (buff, sizeof (buff), s))) return;
414
type = buff2type (buff);
416
/* possibly a "|" or comma */
417
if (!(s = get_token (buff, sizeof (buff), s))) return;
419
if (!strcmp (buff, "|"))
421
if (Debug) fprintf (stderr, "%s: Expecting <subtype> <comma>.\n", Progname);
422
/* ignore subtype and comma */
423
if (!(s = get_token (buff, sizeof (buff), s))) return;
424
if (!(s = get_token (buff, sizeof (buff), s))) return;
431
if (!(s = get_token (buff, sizeof (buff), s))) return;
432
if (!strcmp (buff, ","))
436
/* option name or UL &address */
437
if (!(s = get_token (buff, sizeof (buff), s))) return;
438
if (!strcmp (buff, "UL"))
439
if (!(s = get_token (buff, sizeof (buff), s))) return;
442
if (!(s = get_token (buff, sizeof (buff), s))) return;
444
if (Debug) fprintf (stderr, "%s: Expecting default value.\n", Progname);
446
/* <default value> or UL <default value> */
447
if (!(s = get_token (buff, sizeof (buff), s))) return;
448
if (!strcmp (buff, "UL"))
450
if (Debug) fprintf (stderr, "%s: Skipping UL.\n", Progname);
451
if (!(s = get_token (buff, sizeof (buff), s))) return;
454
memset (tmp, 0, sizeof (tmp));
458
if (!strcmp (buff, "}"))
461
strncpy (tmp + strlen (tmp), buff, sizeof (tmp) - strlen (tmp));
463
while ((s = get_token (buff, sizeof (buff), s)));
465
pretty_default (val, sizeof (val), tmp, type);
466
print_confline (varname, type, val, out);
469
static void pretty_default (char *t, size_t l, const char *s, int type)
478
if (!strcasecmp (s, "M_YES")) strncpy (t, "yes", l);
479
else if (!strcasecmp (s, "M_NO")) strncpy (t, "no", l);
480
else if (!strcasecmp (s, "M_ASKYES")) strncpy (t, "ask-yes", l);
481
else if (!strcasecmp (s, "M_ASKNO")) strncpy (t, "ask-no", l);
487
strncpy (t, "yes", l);
489
strncpy (t, "no", l);
495
strncpy (t, s + 5, l);
496
for (; *t; t++) *t = tolower ((unsigned char) *t);
502
strncpy (t, s + 2, l);
503
for (; *t; t++) *t = tolower ((unsigned char) *t);
511
if (!strcmp (s, "0"))
523
static void char_to_escape (char *dest, unsigned int c)
527
case '\r': strcpy (dest, "\\r"); break; /* __STRCPY_CHECKED__ */
528
case '\n': strcpy (dest, "\\n"); break; /* __STRCPY_CHECKED__ */
529
case '\t': strcpy (dest, "\\t"); break; /* __STRCPY_CHECKED__ */
530
case '\f': strcpy (dest, "\\f"); break; /* __STRCPY_CHECKED__ */
531
default: sprintf (dest, "\\%03o", c); break;
534
static void conf_char_to_escape (unsigned int c , FILE *out)
537
char_to_escape (buff, c);
541
static void conf_print_strval (const char *v, FILE *out)
545
if (*v < ' ' || *v & 0x80)
547
conf_char_to_escape ((unsigned int) *v, out);
551
if (*v == '"' || *v == '\\')
557
static void man_print_strval (const char *v, FILE *out)
561
if (*v < ' ' || *v & 0x80)
564
conf_char_to_escape ((unsigned int) *v, out);
569
fputs ("\\(rq", out);
577
static void sgml_print_strval (const char *v, FILE *out)
582
if (*v < ' ' || *v & 0x80)
584
char_to_escape (buff, (unsigned int) *v);
585
sgml_fputs (buff, out);
588
sgml_fputc ((unsigned int) *v, out);
592
static int sgml_fputc (int c, FILE *out)
596
case '<': return fputs ("<", out);
597
case '>': return fputs (">", out);
598
case '$': return fputs ("$", out);
599
case '_': return fputs ("_", out);
600
case '%': return fputs ("%", out);
601
case '&': return fputs ("&", out);
602
case '\\': return fputs ("\", out);
603
case '"': return fputs (""", out);
604
case '[': return fputs ("[", out);
605
case ']': return fputs ("]", out);
606
case '~': return fputs ("˜", out);
607
default: return fputc (c, out);
611
static int sgml_fputs (const char *s, FILE *out)
614
if (sgml_fputc ((unsigned int) *s, out) == EOF)
620
/* reduce CDATA to ID */
621
static int sgml_id_fputs (const char *s, FILE* out)
632
if (fputc ((unsigned int) id, out) == EOF)
639
static void print_confline (const char *varname, int type, const char *val, FILE *out)
641
if (type == DT_SYN) return;
643
switch (OutputFormat)
645
/* configuration file */
648
if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH)
650
fprintf (out, "\n# set %s=\"", varname);
651
conf_print_strval (val, out);
654
else if (type != DT_SYN)
655
fprintf (out, "\n# set %s=%s", varname, val);
657
fprintf (out, "\n#\n# Name: %s", varname);
658
fprintf (out, "\n# Type: %s", type2human (type));
659
if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH)
661
fputs ("\n# Default: \"", out);
662
conf_print_strval (val, out);
666
fprintf (out, "\n# Default: %s", val);
675
fprintf (out, "\n.TP\n.B %s\n", varname);
676
fputs (".nf\n", out);
677
fprintf (out, "Type: %s\n", type2human (type));
678
if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH)
680
fputs ("Default: \\(lq", out);
681
man_print_strval (val, out);
682
fputs ("\\(rq\n", out);
685
fprintf (out, "Default: %s\n", val);
692
/* SGML based manual */
695
fputs ("\n<sect2 id=\"", out);
696
sgml_id_fputs(varname, out);
697
fputs ("\">\n<title>", out);
698
sgml_fputs (varname, out);
699
fprintf (out, "</title>\n<literallayout>Type: %s", type2human (type));
701
if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH)
703
fputs ("\nDefault: "", out);
704
sgml_print_strval (val, out);
705
fputs (""</literallayout>\n", out);
708
fprintf (out, "\nDefault: %s</literallayout>\n", val);
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
** - \fP switches to normal display
730
** - .dl on a line starts a definition list (name taken taken from HTML).
731
** - .dt starts a term in a definition list.
732
** - .dd starts a definition in a definition list.
733
** - .de on a line finishes a definition list.
734
** - .ts on a line starts a "tscreen" environment (name taken from SGML).
735
** - .te on a line finishes this environment.
736
** - .pp on a line starts a paragraph.
737
** - \$word will be converted to a reference to word, where appropriate.
738
** Note that \$$word is possible as well.
739
** - '. ' in the beginning of a line expands to two space characters.
740
** This is used to protect indentations in tables.
743
/* close eventually-open environments. */
745
static int fd_recurse = 0;
747
static int flush_doc (int docstat, FILE *out)
749
if (docstat & D_INIT)
754
fprintf (stderr, "%s: Internal error, recursion in flush_doc()!\n", Progname);
758
if (docstat & (D_PA))
759
docstat = print_it (SP_END_PAR, NULL, out, docstat);
761
if (docstat & (D_TAB))
762
docstat = print_it (SP_END_TAB, NULL, out, docstat);
764
if (docstat & (D_DL))
765
docstat = print_it (SP_END_DL, NULL, out, docstat);
767
if (docstat & (D_EM | D_BF))
768
docstat = print_it (SP_END_FT, NULL, out, docstat);
770
docstat = print_it (SP_END_SECT, NULL, out, docstat);
772
docstat = print_it (SP_NEWLINE, NULL, out, 0);
778
/* print something. */
780
static int print_it (int special, char *str, FILE *out, int docstat)
782
int onl = docstat & (D_NL|D_NP);
784
docstat &= ~(D_NL|D_NP|D_INIT);
786
switch (OutputFormat)
788
/* configuration file */
793
static int Continuation = 0;
795
case SP_END_FT: docstat &= ~(D_EM|D_BF); break;
796
case SP_START_BF: docstat |= D_BF; break;
797
case SP_START_EM: docstat |= D_EM; break;
872
for (i = strlen (str) ; i < 8 ; i++)
891
docstat &= ~(D_EM|D_BF);
929
fputs (".IP\n", out);
936
fputs ("\n.IP\n.DS\n.sp\n.ft CR\n.nf\n", out);
937
docstat |= D_TAB | D_NL;
942
fputs ("\n.fi\n.ec\n.ft P\n.sp\n", out);
949
fputs ("\n.RS", out);
955
fputs ("\n.IP ", out);
965
fputs ("\n.RE", out);
976
fputs ("\\(rq", out);
977
else if (*str == '\\')
979
else if (!strncmp (str, "``", 2))
981
fputs ("\\(lq", out);
984
else if (!strncmp (str, "''", 2))
986
fputs ("\\(rq", out);
999
/* SGML based manual */
1006
if (docstat & D_EM) fputs ("</emphasis>", out);
1007
if (docstat & D_BF) fputs ("</emphasis>", out);
1008
docstat &= ~(D_EM|D_BF);
1013
fputs ("<emphasis role=\"bold\">", out);
1020
fputs ("<emphasis>", out);
1047
fputs("</para>\n", out);
1048
fputs ("<para>\n", out);
1057
fputs ("</para>\n", out);
1063
fputs ("\n<screen>\n", out);
1064
docstat |= D_TAB | D_NL;
1069
fputs ("\n</screen>", out);
1076
fputs ("\n<variablelist>\n", out);
1082
fputs ("<varlistentry><term>", out);
1088
fputs ("</term>\n<listitem><para>", out);
1094
fputs ("</para></listitem></varlistentry>\n", out);
1099
fputs ("</para></listitem></varlistentry></variablelist>\n", out);
1100
docstat &= ~(D_DD|D_DL);
1105
fputs ("</sect2>", out);
1110
if (docstat & D_TAB)
1113
sgml_fputs (str, out);
1119
/* make gcc happy (unreached) */
1127
void print_ref (FILE *out, int output_dollar, const char *ref)
1129
switch (OutputFormat)
1139
fputs ("<link linkend=\"", out);
1140
sgml_id_fputs (ref, out);
1143
fputs ("$", out);
1144
sgml_fputs (ref, out);
1145
fputs ("</link>", out);
1153
static int commit_buff (char *buff, char **d, FILE *out, int docstat)
1158
docstat = print_it (SP_STR, buff, out, docstat);
1165
static int handle_docline (char *l, FILE *out, int docstat)
1167
char buff[BUFFSIZE];
1172
fprintf (stderr, "%s: handle_docline `%s'\n", Progname, l);
1174
if (!strncmp (l, ".pp", 3))
1175
return print_it (SP_NEWPAR, NULL, out, docstat);
1176
else if (!strncmp (l, ".ts", 3))
1177
return print_it (SP_START_TAB, NULL, out, docstat);
1178
else if (!strncmp (l, ".te", 3))
1179
return print_it (SP_END_TAB, NULL, out, docstat);
1180
else if (!strncmp (l, ".dl", 3))
1181
return print_it (SP_START_DL, NULL, out, docstat);
1182
else if (!strncmp (l, ".de", 3))
1183
return print_it (SP_END_DL, NULL, out, docstat);
1184
else if (!strncmp (l, ". ", 2))
1187
for (s = l, d = buff; *s; s++)
1189
if (!strncmp (s, "\\(as", 4))
1194
else if (!strncmp (s, "\\(rs", 4))
1199
else if (!strncmp (s, "\\fI", 3))
1201
docstat = commit_buff (buff, &d, out, docstat);
1202
docstat = print_it (SP_START_EM, NULL, out, docstat);
1205
else if (!strncmp (s, "\\fB", 3))
1207
docstat = commit_buff (buff, &d, out, docstat);
1208
docstat = print_it (SP_START_BF, NULL, out, docstat);
1211
else if (!strncmp (s, "\\fP", 3))
1213
docstat = commit_buff (buff, &d, out, docstat);
1214
docstat = print_it (SP_END_FT, NULL, out, docstat);
1217
else if (!strncmp (s, ".dt", 3))
1221
docstat = commit_buff (buff, &d, out, docstat);
1222
docstat = print_it (SP_END_DD, NULL, out, docstat);
1224
docstat = commit_buff (buff, &d, out, docstat);
1225
docstat = print_it (SP_DT, NULL, out, docstat);
1228
else if (!strncmp (s, ".dd", 3))
1230
docstat = commit_buff (buff, &d, out, docstat);
1231
docstat = print_it (SP_DD, NULL, out, docstat);
1236
int output_dollar = 0;
1253
while (isalnum ((unsigned char) *s) || *s == '-' || *s == '_')
1256
docstat = commit_buff (buff, &d, out, docstat);
1259
print_ref (out, output_dollar, ref);
1268
docstat = commit_buff (buff, &d, out, docstat);
1269
return print_it (SP_NEWLINE, NULL, out, docstat);