force rfc2047_parameters to be set to yes, and non configureable.
[apps/madmutt.git] / makedoc.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
4  *
5  * Parts were written/modified by:
6  * Rocco Rutte <pdmef@cs.tu-berlin.de>
7  *
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.
11  */
12
13 /**
14  ** This program parses mutt's init.h and generates documentation in
15  ** three different formats:
16  **
17  ** -> a commented muttrc configuration file
18  ** -> nroff, suitable for inclusion in a manual page
19  ** -> linuxdoc-sgml, suitable for inclusion in the 
20  **    SGML-based manual
21  **
22  **/
23
24 #include <lib-lib/lib-lib.h>
25
26 #ifdef HAVE_GETOPT_H
27 # include <getopt.h>
28 #endif
29
30 typedef struct {
31   short seen;
32   char *name;
33   char *descr;
34 } var_t;
35
36 static int outcount = 0;
37 static var_t *outbuf = NULL;
38
39 static int var_cmp (const void *a, const void *b)
40 {
41   return (m_strcmp (((var_t *) a)->name, ((var_t *) b)->name));
42 }
43
44 enum output_formats_t {
45   F_CONF, F_MAN, F_SGML, F_NONE
46 };
47
48 #define D_NL            (1 << 0)
49 #define D_EM            (1 << 1)
50 #define D_BF            (1 << 2)
51 #define D_TT            (1 << 3)
52 #define D_TAB           (1 << 4)
53 #define D_NP            (1 << 5)
54 #define D_INIT          (1 << 6)
55 #define D_DL            (1 << 7)
56 #define D_DT            (1 << 8)
57 #define D_DD            (1 << 9)
58 #define D_PA            (1 << 10)
59
60 enum {
61   SP_START_EM,
62   SP_START_BF,
63   SP_START_TT,
64   SP_END_FT,
65   SP_END_PAR,
66   SP_NEWLINE,
67   SP_NEWPAR,
68   SP_STR,
69   SP_START_TAB,
70   SP_END_TAB,
71   SP_START_DL,
72   SP_DT,
73   SP_DD,
74   SP_END_DD,
75   SP_END_DL,
76   SP_END_SECT,
77   SP_REFER
78 };
79
80 enum output_formats_t OutputFormat = F_NONE;
81 char *Progname;
82
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);
99
100 int main (int argc, char *argv[])
101 {
102   int c;
103   FILE *f;
104
105   if ((Progname = strrchr (argv[0], '/')))
106     Progname++;
107   else
108     Progname = argv[0];
109
110   while ((c = getopt (argc, argv, "cmsd")) != EOF) {
111     switch (c) {
112     case 'c':
113       OutputFormat = F_CONF;
114       break;
115     case 'm':
116       OutputFormat = F_MAN;
117       break;
118     case 's':
119       OutputFormat = F_SGML;
120       break;
121     default:
122       {
123         fprintf (stderr, "%s: bad command line parameter.\n", Progname);
124         exit (1);
125       }
126     }
127   }
128
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));
133       exit (1);
134     }
135   }
136   else
137     f = stdin;
138
139   switch (OutputFormat) {
140   case F_CONF:
141   case F_MAN:
142   case F_SGML:
143     makedoc (f, stdout);
144     break;
145   default:
146     {
147       fprintf (stderr, "%s: No output format specified.\n", Progname);
148       exit (1);
149     }
150   }
151
152   if (f != stdin)
153     m_fclose(&f);
154
155   exit (1);
156 }
157
158 static void add_var (const char *name)
159 {
160   outbuf = realloc (outbuf, (++outcount) * sizeof (var_t));
161   outbuf[outcount - 1].seen = 0;
162   outbuf[outcount - 1].name = strdup(name);
163   outbuf[outcount - 1].descr = NULL;
164 }
165
166 static int add_s (const char *s)
167 {
168   size_t lnew = m_strlen(s), lold = m_strlen(outbuf[outcount - 1].descr);
169
170   if (lnew == 0)
171     return (0);
172   if (!outbuf[outcount - 1].seen) {
173     lold = 0;
174     outbuf[outcount - 1].seen = 1;
175   }
176
177   if (lold == 0)
178     outbuf[outcount - 1].descr = strdup(s);
179   else {
180     outbuf[outcount - 1].descr =
181       realloc (outbuf[outcount - 1].descr, lold + lnew + 1);
182     memcpy (&(outbuf[outcount - 1].descr[lold - 1]) + 1, s, lnew);
183   }
184   outbuf[outcount - 1].descr[lold + lnew] = '\0';
185   return (1);
186 }
187
188 static int add_c (int c)
189 {
190   char buf[2] = "\0\0";
191
192   buf[0] = c;
193   return (add_s (buf));
194 }
195
196 static void makedoc (FILE * in, FILE * out)
197 {
198   char buffer[BUFSIZ];
199   char token[BUFSIZ];
200   char *p;
201   int active = 0;
202   int line = 0;
203   int docstat = D_INIT;
204
205   while ((fgets (buffer, sizeof (buffer), in))) {
206     line++;
207     if ((p = strchr (buffer, '\n')) == NULL) {
208       fprintf (stderr, "%s: Line %d too long.  Ask a wizard to enlarge\n"
209                "%s: my buffer size.\n", Progname, line, Progname);
210       exit (1);
211     }
212     else
213       *p = '\0';
214
215     if (!(p = get_token (token, sizeof (token), buffer)))
216       continue;
217
218     if (!m_strcmp (token, "/*++*/"))
219       active = 1;
220     else if (!m_strcmp (token, "/*--*/")) {
221       docstat = flush_doc (docstat);
222       active = 0;
223     }
224     else if (active && (!m_strcmp (token, "/**") || !m_strcmp (token, "**")))
225       docstat = handle_docline (p, docstat);
226     else if (active && !m_strcmp (token, "{")) {
227       docstat = flush_doc (docstat);
228       handle_confline (p);
229     }
230   }
231   flush_doc (docstat);
232   fputs ("\n", out);
233   qsort (outbuf, outcount, sizeof (var_t), &var_cmp);
234   for (line = 0; line < outcount; line++) {
235     if (outbuf[line].descr) {
236       fprintf (out, "%s\n", outbuf[line].descr);
237       free (outbuf[line].descr);
238     }
239     free (outbuf[line].name);
240   }
241   free (outbuf);
242 }
243
244 /* skip whitespace */
245
246 static char *skip_ws (char *s)
247 {
248   while (*s && isspace ((unsigned char) *s))
249     s++;
250
251   return s;
252 }
253
254 /* isolate a token */
255
256 static char single_char_tokens[] = "[]{},;|";
257
258 static char *get_token (char *d, size_t l, char *s)
259 {
260   char *t;
261   short is_quoted = 0;
262
263   s = skip_ws (s);
264
265   if (!*s) {
266     return NULL;
267   }
268
269   if (strchr (single_char_tokens, *s)) {
270     d[0] = *s++;
271     d[1] = 0;
272     return s;
273   }
274
275   if (*s == '"') {
276     s++;
277     is_quoted = 1;
278   }
279
280   for (t = s; *t && --l > 0; t++) {
281     if (*t == '\\' && !t[1])
282       break;
283
284     if (is_quoted && *t == '\\') {
285       switch ((*d = *++t)) {
286       case 'n':
287         *d = '\n';
288         break;
289       case 't':
290         *d = '\t';
291         break;
292       case 'r':
293         *d = '\r';
294         break;
295       case 'a':
296         *d = '\a';
297         break;
298       }
299
300       d++;
301       continue;
302     }
303
304     if (is_quoted && *t == '"') {
305       t++;
306       break;
307     }
308     else if (!is_quoted && strchr (single_char_tokens, *t))
309       break;
310     else if (!is_quoted && isspace ((unsigned char) *t))
311       break;
312     else
313       *d++ = *t;
314   }
315
316   *d = '\0';
317
318   return t;
319 }
320
321
322 /**
323  ** Configuration line parser
324  ** 
325  ** The following code parses a line from init.h which declares
326  ** a configuration variable.
327  **
328  **/
329
330 /* note: the following enum must be in the same order as the
331  * following string definitions!
332  */
333
334 enum {
335   DT_NONE = 0,
336   DT_BOOL,
337   DT_NUM,
338   DT_STR,
339   DT_PATH,
340   DT_QUAD,
341   DT_SORT,
342   DT_RX,
343   DT_MAGIC,
344   DT_SYN,
345   DT_ADDR,
346   DT_SYS
347 };
348
349 struct {
350   const char *machine;
351   const char *human;
352 } types[] = {
353   {"DT_NONE",  "-none-"},
354   {"DT_BOOL",  "boolean"},
355   {"DT_NUM",   "number"},
356   {"DT_STR",   "string"},
357   {"DT_PATH",  "path"},
358   {"DT_QUAD",  "quadoption"},
359   {"DT_SORT",  "sort order"},
360   {"DT_RX",    "regular expression"},
361   {"DT_MAGIC", "folder magic"},
362   {"DT_SYN",   NULL},
363   {"DT_ADDR",  "e-mail address"},
364   {"DT_SYS",   "system property"},
365   {NULL, NULL}
366 };
367
368 static int buff2type (const char *s)
369 {
370   int type;
371
372   for (type = DT_NONE; types[type].machine; type++)
373     if (!m_strcmp (types[type].machine, s))
374       return type;
375
376   return DT_NONE;
377 }
378
379 static const char *type2human (int type)
380 {
381   return types[type].human;
382 }
383 static void handle_confline (char *s)
384 {
385   char varname[BUFSIZ];
386   char buff[BUFSIZ];
387   int type;
388
389   char val[BUFSIZ];
390
391   /* xxx - put this into an actual state machine? */
392
393   /* variable name */
394   if (!(s = get_token (varname, sizeof (varname), s)))
395     return;
396
397   /* comma */
398   if (!(s = get_token (buff, sizeof (buff), s)))
399     return;
400
401   /* type */
402   if (!(s = get_token (buff, sizeof (buff), s)))
403     return;
404
405   type = buff2type (buff);
406
407   /* possibly a "|" or comma */
408   if (!(s = get_token (buff, sizeof (buff), s)))
409     return;
410
411   if (!m_strcmp (buff, "|")) {
412     /* ignore subtype and comma */
413     if (!(s = get_token (buff, sizeof (buff), s)))
414       return;
415     if (!(s = get_token (buff, sizeof (buff), s)))
416       return;
417   }
418
419   /* redraw, comma */
420
421   while (1) {
422     if (!(s = get_token (buff, sizeof (buff), s)))
423       return;
424     if (!m_strcmp (buff, ","))
425       break;
426   }
427
428   /* option name or UL &address */
429   if (!(s = get_token (buff, sizeof (buff), s)))
430     return;
431   if (!m_strcmp (buff, "UL"))
432     if (!(s = get_token (buff, sizeof (buff), s)))
433       return;
434
435   /* comma */
436   if (!(s = get_token (buff, sizeof (buff), s)))
437     return;
438
439   /* <default value> or UL <default value> */
440   if (!(s = get_token (buff, sizeof (buff), s)))
441     return;
442   if (!m_strcmp (buff, "UL")) {
443     if (!(s = get_token (buff, sizeof (buff), s)))
444       return;
445   }
446
447   memset(val, 0, sizeof(val));
448
449   do {
450     if (!m_strcmp (buff, "}"))
451       break;
452
453     m_strcat(val, sizeof(val), buff);
454   }
455   while ((s = get_token (buff, sizeof (buff), s)));
456
457   add_var (varname);
458   print_confline (varname, type, val);
459 }
460
461 static void char_to_escape (char *dest, unsigned int c)
462 {
463   switch (c) {
464   case '\r':
465     strcpy (dest, "\\r");
466     break;                      /* __STRCPY_CHECKED__ */
467   case '\n':
468     strcpy (dest, "\\n");
469     break;                      /* __STRCPY_CHECKED__ */
470   case '\t':
471     strcpy (dest, "\\t");
472     break;                      /* __STRCPY_CHECKED__ */
473   case '\f':
474     strcpy (dest, "\\f");
475     break;                      /* __STRCPY_CHECKED__ */
476   default:
477     sprintf (dest, "\\%03o", c);
478     break;
479   }
480 }
481 static void conf_char_to_escape (unsigned int c)
482 {
483   char buff[16];
484
485   char_to_escape (buff, c);
486   add_s (buff);
487 }
488
489 static void conf_print_strval (const char *v)
490 {
491   for (; *v; v++) {
492     if (*v < ' ' || *v & 0x80) {
493       conf_char_to_escape ((unsigned int) *v);
494       continue;
495     }
496
497     if (*v == '"' || *v == '\\')
498       add_c ('\\');
499     add_c (*v);
500   }
501 }
502
503 static void man_print_strval (const char *v)
504 {
505   for (; *v; v++) {
506     if (*v < ' ' || *v & 0x80) {
507       add_c ('\\');
508       conf_char_to_escape ((unsigned int) *v);
509       continue;
510     }
511
512     if (*v == '"')
513       add_s ("\\(rq");
514     else if (*v == '\\')
515       add_s ("\\\\");
516     else
517       add_c (*v);
518   }
519 }
520
521 static void sgml_print_strval (const char *v)
522 {
523   char buff[16];
524
525   for (; *v; v++) {
526     if (*v < ' ' || *v & 0x80) {
527       char_to_escape (buff, (unsigned int) *v);
528       sgml_fputs (buff);
529       continue;
530     }
531     sgml_fputc ((unsigned int) *v);
532   }
533 }
534
535 static int sgml_fputc (int c)
536 {
537   switch (c) {
538   case '<':
539     return add_s ("&lt;");
540   case '>':
541     return add_s ("&gt;");
542   case '&':
543     return add_s ("&amp;");
544   default:
545     return add_c (c);
546   }
547 }
548
549 static int sgml_fputs (const char *s)
550 {
551   for (; *s; s++)
552     if (sgml_fputc ((unsigned int) *s) == EOF)
553       return EOF;
554
555   return 0;
556 }
557
558 /* reduce CDATA to ID */
559 static int sgml_id_fputs (const char *s) {
560  char id;
561
562  for (; *s; s++) {
563    if (*s == '_')
564      id = '-';
565    else
566      id = *s;
567    if (sgml_fputc ((unsigned int) id) == EOF)
568      return EOF;
569  }
570  return 0;
571 }
572
573 static void print_confline (const char *varname, int type, const char *val)
574 {
575   if (type == DT_SYN)
576     return;
577
578   switch (OutputFormat) {
579     /* configuration file */
580   case F_CONF:
581     {
582       if (type == DT_SYS) {
583         add_s ("\n# set ?");
584         add_s (varname);
585         add_s (" prints ");
586         add_s (val);
587         break;
588       }
589       if (type == DT_STR || type == DT_RX || type == DT_ADDR
590           || type == DT_PATH) {
591         add_s ("\n# set ");
592         add_s (varname);
593         add_s ("=\"");
594         conf_print_strval (val);
595         add_s ("\"");
596       }
597       else if (type != DT_SYN) {
598         add_s ("\n# set ");
599         add_s (varname);
600         add_s ("=");
601         add_s (val);
602       }
603
604       add_s ("\n#\n# Name: ");
605       add_s (varname);
606       add_s ("\n# Type: ");
607       add_s (type2human (type));
608       if (type == DT_STR || type == DT_RX || type == DT_ADDR
609           || type == DT_PATH) {
610         add_s ("\n# Default: \"");
611         conf_print_strval (val);
612         add_s ("\"");
613       }
614       else {
615         add_s ("\n# Default: ");
616         add_s (val);
617       }
618
619       add_s ("\n# ");
620       break;
621     }
622
623     /* manual page */
624   case F_MAN:
625     {
626       add_s (".TP\n.B ");
627       add_s (varname);
628       add_s ("\n.nf\n");
629       add_s ("Type: ");
630       add_s (type2human (type));
631       add_c ('\n');
632       if (type == DT_STR || type == DT_RX || type == DT_ADDR
633           || type == DT_PATH) {
634         add_s ("Default: \\(lq");
635         man_print_strval (val);
636         add_s ("\\(rq\n");
637       }
638       else {
639         add_s (type == DT_SYS ? "Value: " : "Default: ");
640         add_s (val);
641         add_c ('\n');
642       }
643       add_s (".fi");
644
645       break;
646     }
647
648     /* SGML based manual */
649   case F_SGML:
650     {
651       add_s ("\n<madmutt-doc:vardef name=\"");
652       sgml_fputs (varname);
653       add_s ("\">\n<para>Type: <literal>");
654       add_s (type2human (type));
655       add_s ("</literal></para>\n");
656
657       if (type == DT_STR || type == DT_RX || type == DT_ADDR
658           || type == DT_PATH) {
659         add_s ("<para>\nDefault: <literal>&quot;");
660         sgml_print_strval (val);
661         add_s ("&quot;</literal>");
662       }
663       else {
664         add_s ("<para>\n"); 
665         add_s (type == DT_SYS ? "Value: " : "Default: ");
666         add_s ("<literal>");
667         add_s (val);
668         add_s ("</literal>");
669       }
670       add_s ("</para>\n");
671       break;
672     }
673     /* make gcc happy */
674   default:
675     break;
676   }
677 }
678
679 /**
680  ** Documentation line parser
681  **
682  ** The following code parses specially formatted documentation 
683  ** comments in init.h.
684  **
685  ** The format is very remotely inspired by nroff. Most important, it's
686  ** easy to parse and convert, and it was easy to generate from the SGML 
687  ** source of mutt's original manual.
688  **
689  ** - \fI switches to italics
690  ** - \fB switches to boldface
691  ** - \fT switches to typewriter for SGML
692  ** - \fP switches to normal display
693  ** - .dl on a line starts a definition list (name taken taken from HTML).
694  ** - .dt starts a term in a definition list.
695  ** - .dd starts a definition in a definition list.
696  ** - .de on a line finishes a definition list.
697  ** - .ts on a line starts a "tscreen" environment (name taken from SGML).
698  ** - .te on a line finishes this environment.
699  ** - .pp on a line starts a paragraph.
700  ** - \$word will be converted to a reference to word, where appropriate.
701  **   Note that \$$word is possible as well.
702  ** - '. ' in the beginning of a line expands to two space characters.
703  **   This is used to protect indentations in tables.
704  **/
705
706 /* close eventually-open environments. */
707
708 static int fd_recurse = 0;
709
710 static int flush_doc (int docstat)
711 {
712   if (docstat & D_INIT)
713     return D_INIT;
714
715   if (fd_recurse++) {
716     fprintf (stderr, "%s: Internal error, recursion in flush_doc()!\n",
717              Progname);
718     exit (1);
719   }
720
721   if (docstat & (D_PA))
722     docstat = print_it (SP_END_PAR, NULL, docstat);
723
724   if (docstat & (D_TAB))
725     docstat = print_it (SP_END_TAB, NULL, docstat);
726
727   if (docstat & (D_DL))
728     docstat = print_it (SP_END_DL, NULL, docstat);
729
730   if (docstat & (D_EM | D_BF | D_TT))
731     docstat = print_it (SP_END_FT, NULL, docstat);
732
733   docstat = print_it (SP_END_SECT, NULL, docstat);
734
735   docstat = print_it (SP_NEWLINE, NULL, 0);
736
737   fd_recurse--;
738   return D_INIT;
739 }
740
741 /* print something. */
742
743 static int print_it (int special, char *str, int docstat)
744 {
745   int onl = docstat & (D_NL | D_NP);
746
747   docstat &= ~(D_NL | D_NP | D_INIT);
748
749   switch (OutputFormat) {
750     /* configuration file */
751   case F_CONF:
752     {
753       switch (special) {
754         static int Continuation = 0;
755
756       case SP_END_FT:
757         docstat &= ~(D_EM | D_BF | D_TT);
758         break;
759       case SP_START_BF:
760         docstat |= D_BF;
761         break;
762       case SP_START_EM:
763         docstat |= D_EM;
764         break;
765       case SP_START_TT:
766         docstat |= D_TT;
767         break;
768       case SP_NEWLINE:
769         {
770           if (onl)
771             docstat |= onl;
772           else {
773             add_s ("\n# ");
774             docstat |= D_NL;
775           }
776           if (docstat & D_DL)
777             ++Continuation;
778           break;
779         }
780       case SP_NEWPAR:
781         {
782           if (onl & D_NP) {
783             docstat |= onl;
784             break;
785           }
786
787           if (!(onl & D_NL))
788             add_s ("\n# ");
789           add_s ("\n# ");
790           docstat |= D_NP;
791           break;
792         }
793       case SP_START_TAB:
794         {
795           if (!onl)
796             add_s ("\n# ");
797           docstat |= D_TAB;
798           break;
799         }
800       case SP_END_TAB:
801         {
802           docstat &= ~D_TAB;
803           docstat |= D_NL;
804           break;
805         }
806       case SP_START_DL:
807         {
808           docstat |= D_DL;
809           break;
810         }
811       case SP_DT:
812         {
813           Continuation = 0;
814           docstat |= D_DT;
815           break;
816         }
817       case SP_DD:
818         {
819           Continuation = 0;
820           break;
821         }
822       case SP_END_DL:
823         {
824           Continuation = 0;
825           docstat &= ~D_DL;
826           break;
827         }
828       case SP_STR:
829         {
830           if (Continuation) {
831             Continuation = 0;
832             add_s ("        ");
833           }
834           add_s (str);
835           if (docstat & D_DT) {
836             int i;
837
838             for (i = m_strlen(str); i < 8; i++)
839               add_c (' ');
840             docstat &= ~D_DT;
841             docstat |= D_NL;
842           }
843           break;
844         }
845       }
846       break;
847     }
848
849     /* manual page */
850   case F_MAN:
851     {
852       switch (special) {
853       case SP_END_FT:
854         {
855           add_s ("\\fP");
856           docstat &= ~(D_EM | D_BF | D_TT);
857           break;
858         }
859       case SP_START_BF:
860         {
861           add_s ("\\fB");
862           docstat |= D_BF;
863           docstat &= ~(D_EM | D_TT);
864           break;
865         }
866       case SP_START_EM:
867         {
868           add_s ("\\fI");
869           docstat |= D_EM;
870           docstat &= ~(D_BF | D_TT);
871           break;
872         }
873       case SP_START_TT:
874         {
875           docstat |= D_TT;
876           docstat &= ~(D_BF | D_EM);
877           break;
878         }
879       case SP_NEWLINE:
880         {
881           if (onl)
882             docstat |= onl;
883           else {
884             add_c ('\n');
885             docstat |= D_NL;
886           }
887           break;
888         }
889       case SP_NEWPAR:
890         {
891           if (onl & D_NP) {
892             docstat |= onl;
893             break;
894           }
895
896           if (!(onl & D_NL))
897             add_c ('\n');
898           add_s (".IP\n");
899
900           docstat |= D_NP;
901           break;
902         }
903       case SP_START_TAB:
904         {
905           add_s ("\n.IP\n.DS\n.sp\n.ft CR\n.nf\n");
906           docstat |= D_TAB | D_NL;
907           break;
908         }
909       case SP_END_TAB:
910         {
911           add_s ("\n.fi\n.ec\n.ft P\n.sp\n");
912           docstat &= ~D_TAB;
913           docstat |= D_NL;
914           break;
915         }
916       case SP_START_DL:
917         {
918           add_s ("\n.RS");
919           docstat |= D_DL;
920           break;
921         }
922       case SP_DT:
923         {
924           add_s ("\n.IP ");
925           break;
926         }
927       case SP_DD:
928         {
929           add_s ("\n");
930           break;
931         }
932       case SP_END_DL:
933         {
934           add_s ("\n.RE");
935           docstat &= ~D_DL;
936           break;
937         }
938       case SP_STR:
939         {
940           while (*str) {
941             for (; *str; str++) {
942               if (*str == '"')
943                 add_s ("\\(rq");
944               else if (*str == '\\')
945                 add_s ("\\\\");
946               else if (!m_strncmp (str, "``", 2)) {
947                 add_s ("\\(lq");
948                 str++;
949               }
950               else if (!m_strncmp (str, "''", 2)) {
951                 add_s ("\\(rq");
952                 str++;
953               }
954               else
955                 add_c (*str);
956             }
957           }
958           break;
959         }
960       }
961       break;
962     }
963
964     /* SGML based manual */
965   case F_SGML:
966     {
967       switch (special) {
968       case SP_END_FT:
969         {
970           if (docstat & D_EM)
971             add_s ("</emphasis>");
972           if (docstat & D_BF)
973             add_s ("</emphasis>");
974           if (docstat & D_TT)
975             add_s ("</literal>");
976           docstat &= ~(D_EM | D_BF | D_TT);
977           break;
978         }
979       case SP_START_BF:
980         {
981           add_s ("<emphasis role=\"bold\">");
982           docstat |= D_BF;
983           docstat &= ~(D_EM | D_TT);
984           break;
985         }
986       case SP_START_EM:
987         {
988           add_s ("<emphasis>");
989           docstat |= D_EM;
990           docstat &= ~(D_BF | D_TT);
991           break;
992         }
993       case SP_START_TT:
994         {
995           add_s ("<literal>");
996           docstat |= D_TT;
997           docstat &= ~(D_EM | D_BF);
998           break;
999         }
1000       case SP_NEWLINE:
1001         {
1002           if (onl)
1003             docstat |= onl;
1004           else {
1005             add_s ("\n");
1006             docstat |= D_NL;
1007           }
1008           break;
1009         }
1010       case SP_NEWPAR:
1011         {
1012           if (onl & D_NP) {
1013             docstat |= onl;
1014             break;
1015           }
1016
1017           if (!(onl & D_NL))
1018             add_s ("\n");
1019           if (docstat & D_PA)
1020             add_s ("</para>\n");
1021           add_s ("<para>\n");
1022
1023           docstat |= D_NP;
1024           docstat |= D_PA;
1025           break;
1026         }
1027       case SP_START_TAB:
1028         {
1029           add_s ("\n<screen>\n");
1030           docstat |= D_TAB | D_NL;
1031           break;
1032         }
1033       case SP_END_TAB:
1034         {
1035           add_s ("\n</screen>");
1036           docstat &= ~D_TAB;
1037           docstat |= D_NL;
1038           break;
1039         }
1040       case SP_START_DL:
1041         {
1042           add_s ("\n<variablelist>\n");
1043           docstat |= D_DL;
1044           break;
1045         }
1046       case SP_DT:
1047         {
1048           add_s ("<varlistentry><term>");
1049           break;
1050         }
1051       case SP_DD:
1052         {
1053           add_s ("</term>\n<listitem><para>\n");
1054           docstat |= D_DD;
1055           break;
1056         }
1057       case SP_END_DL:
1058         {
1059           add_s ("</para></listitem></varlistentry></variablelist>\n");
1060           docstat &= ~(D_DL|D_DD);
1061           break;
1062         }
1063       case SP_END_PAR:
1064         {
1065           add_s ("</para>\n");
1066           docstat &= ~D_PA;
1067           break;
1068         }
1069       case SP_END_DD:
1070         {
1071           add_s ("</para></listitem></varlistentry>\n");
1072           docstat &= ~D_DD;
1073           break;
1074         }
1075       case SP_END_SECT:
1076         {
1077           add_s ("</madmutt-doc:vardef>\n");
1078           break;
1079         }
1080       case SP_STR:
1081         {
1082           if (docstat & D_TAB)
1083             add_s (str);
1084           else
1085             sgml_fputs (str);
1086           break;
1087         }
1088       }
1089       break;
1090     }
1091     /* make gcc happy (unreached) */
1092   default:
1093     break;
1094   }
1095
1096   return docstat;
1097 }
1098
1099 static void print_ref (int output_dollar, const char *ref)
1100 {
1101   switch (OutputFormat) {
1102   case F_CONF:
1103   case F_MAN:
1104     if (output_dollar)
1105       add_c ('$');
1106     add_s (ref);
1107     break;
1108
1109   case F_SGML:
1110     add_s ("<link linkend=\"");
1111     sgml_id_fputs (ref);
1112     add_s ("\">\n");
1113     if (output_dollar)
1114       add_s ("$");
1115     sgml_fputs (ref);
1116     add_s ("</link>");
1117     break;
1118
1119   default:
1120     break;
1121   }
1122 }
1123
1124 static int commit_buff (char *buff, char **d, int docstat)
1125 {
1126   if (*d > buff) {
1127     **d = '\0';
1128     docstat = print_it (SP_STR, buff, docstat);
1129     *d = buff;
1130   }
1131
1132   return docstat;
1133 }
1134
1135 static int handle_docline (char *l, int docstat)
1136 {
1137   char buff[BUFSIZ];
1138   char *s, *d;
1139
1140   l = skip_ws (l);
1141
1142   if (!m_strncmp (l, ".pp", 3))
1143     return print_it (SP_NEWPAR, NULL, docstat);
1144   else if (!m_strncmp (l, ".ts", 3))
1145     return print_it (SP_START_TAB, NULL, docstat);
1146   else if (!m_strncmp (l, ".te", 3))
1147     return print_it (SP_END_TAB, NULL, docstat);
1148   else if (!m_strncmp (l, ".dl", 3))
1149     return print_it (SP_START_DL, NULL, docstat);
1150   else if (!m_strncmp (l, ".de", 3))
1151     return print_it (SP_END_DL, NULL, docstat);
1152   else if (!m_strncmp (l, ". ", 2))
1153     *l = ' ';
1154
1155   for (s = l, d = buff; *s; s++) {
1156     if (!m_strncmp (s, "\\(as", 4)) {
1157       *d++ = '*';
1158       s += 3;
1159     }
1160     else if (!m_strncmp (s, "\\(rs", 4)) {
1161       *d++ = '\\';
1162       s += 3;
1163     }
1164     else if (!m_strncmp (s, "\\fI", 3)) {
1165       docstat = commit_buff (buff, &d, docstat);
1166       docstat = print_it (SP_START_EM, NULL, docstat);
1167       s += 2;
1168     }
1169     else if (!m_strncmp (s, "\\fB", 3)) {
1170       docstat = commit_buff (buff, &d, docstat);
1171       docstat = print_it (SP_START_BF, NULL, docstat);
1172       s += 2;
1173     }
1174     else if (!m_strncmp (s, "\\fT", 3)) {
1175       docstat = commit_buff (buff, &d, docstat);
1176       docstat = print_it (SP_START_TT, NULL, docstat);
1177       s += 2;
1178     }
1179     else if (!m_strncmp (s, "\\fP", 3)) {
1180       docstat = commit_buff (buff, &d, docstat);
1181       docstat = print_it (SP_END_FT, NULL, docstat);
1182       s += 2;
1183     }
1184     else if (!m_strncmp (s, ".dt", 3)) {
1185       if (docstat & D_DD) {
1186         docstat = commit_buff (buff, &d, docstat);
1187         docstat = print_it (SP_END_DD, NULL, docstat);
1188       }
1189       docstat = commit_buff (buff, &d, docstat);
1190       docstat = print_it (SP_DT, NULL, docstat);
1191       s += 3;
1192     }
1193     else if (!m_strncmp (s, ".dd", 3)) {
1194       docstat = commit_buff (buff, &d, docstat);
1195       docstat = print_it (SP_DD, NULL, docstat);
1196       s += 3;
1197     }
1198     else if (*s == '$') {
1199       int output_dollar = 0;
1200       char *ref;
1201       char save;
1202
1203       ++s;
1204       if (*s == '$') {
1205         output_dollar = 1;
1206         ++s;
1207       }
1208       if (*s == '$') {
1209         *d++ = '$';
1210       }
1211       else {
1212         ref = s;
1213         while (isalnum ((unsigned char) *s) || *s == '-' || *s == '_')
1214           ++s;
1215
1216         docstat = commit_buff (buff, &d, docstat);
1217         save = *s;
1218         *s = 0;
1219         print_ref (output_dollar, ref);
1220         *s = save;
1221         --s;
1222       }
1223     }
1224     else
1225       *d++ = *s;
1226   }
1227
1228   docstat = commit_buff (buff, &d, docstat);
1229   return print_it (SP_NEWLINE, NULL, docstat);
1230 }