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