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