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