]> git.llucax.com Git - software/mutt-debian.git/blob - doc/makedoc.c
fix index weirdness if mailbox is emptied while inside the pager (Closes: 375530)
[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 *, int);
121 static int sgml_fputs (const char *, FILE *, int);
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
580       fputc (*v, out);
581   }
582 }
583
584 static void sgml_print_strval (const char *v, FILE *out)
585 {
586   char buff[16];
587   for (; *v; v++)
588   {
589     if (*v <  ' ' || *v & 0x80)
590     {
591       char_to_escape (buff, (unsigned int) *v);
592       sgml_fputs (buff, out, 1);
593       continue;
594     }
595     sgml_fputc ((unsigned int) *v, out, 1);
596   }
597 }
598
599 static int sgml_fputc (int c, FILE *out, int full)
600 {
601   switch (c)
602   {
603     /* the bare minimum for escaping */
604     case '<': return fputs ("&lt;", out);
605     case '>': return fputs ("&gt;", out);
606     case '&': return fputs ("&amp;", out);
607     /* map to entities, fall-through to raw if !full */
608     case '$': if (full) return fputs ("&dollar;", out);
609     case '_': if (full) return fputs ("&lowbar;", out);
610     case '%': if (full) return fputs ("&percnt;", out);
611     case '\\': if (full) return fputs ("&bsol;", out);
612     case '"': if (full) return fputs ("&quot;", out);
613     case '[': if (full) return fputs ("&lsqb;", out);
614     case ']': if (full) return fputs ("&rsqb;", out);
615     case '~': if (full) return fputs ("&tilde;", out);
616     case '|': if (full) return fputs ("&verbar;", out);
617     case '^': if (full) return fputs ("&circ;", out);
618     default:  return fputc (c, out);
619   }
620 }
621
622 static int sgml_fputs (const char *s, FILE *out, int full)
623 {
624   for (; *s; s++)
625     if (sgml_fputc ((unsigned int) *s, out, full) == EOF)
626       return EOF;
627
628   return 0;
629 }
630
631 /* reduce CDATA to ID */
632 static int sgml_id_fputs (const char *s, FILE* out)
633 {
634   char id;
635
636   if (*s == '<')
637     s++;
638
639   for (; *s; s++)
640   {
641     if (*s == '_')
642       id = '-';
643     else
644       id = *s;
645     if (*s == '>' && !*(s+1))
646       break;
647
648     if (fputc ((unsigned int) id, out) == EOF)
649       return EOF;
650   }
651
652   return 0;
653 }
654
655 static void print_confline (const char *varname, int type, const char *val, FILE *out)
656 {
657   if (type == DT_SYN) return;
658   
659   switch (OutputFormat)
660   {
661     /* configuration file */
662     case F_CONF:
663     {
664       if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH)
665       {
666         fprintf (out, "\n# set %s=\"", varname);
667         conf_print_strval (val, out);
668         fputs ("\"", out);
669       }
670       else if (type != DT_SYN)
671         fprintf (out, "\n# set %s=%s", varname, val);
672       
673       fprintf (out, "\n#\n# Name: %s", varname);
674       fprintf (out, "\n# Type: %s", type2human (type));
675       if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH)
676       {
677         fputs ("\n# Default: \"", out);
678         conf_print_strval (val, out);
679         fputs ("\"", out);
680       }
681       else
682         fprintf (out, "\n# Default: %s", val);
683
684       fputs ("\n# ", out);
685       break;
686     }
687
688     /* manual page */
689     case F_MAN:
690     {
691       fprintf (out, "\n.TP\n.B %s\n", varname);
692       fputs (".nf\n", out);
693       fprintf (out, "Type: %s\n", type2human (type));
694       if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH)
695       {
696         fputs ("Default: \\(lq", out);
697         man_print_strval (val, out);
698         fputs ("\\(rq\n", out);
699       }
700       else
701         fprintf (out, "Default: %s\n", val);
702
703       fputs (".fi", out);
704
705       break;
706     }
707     
708     /* SGML based manual */
709     case F_SGML:
710     {
711       fputs ("\n<sect2 id=\"", out);
712       sgml_id_fputs(varname, out);
713       fputs ("\">\n<title>", out);
714       sgml_fputs (varname, out, 1);
715       fprintf (out, "</title>\n<literallayout>Type: %s", type2human (type));
716
717       
718       if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH)
719       {
720         if (val && *val)
721         {
722           fputs ("\nDefault: <quote><literal>", out);
723           sgml_print_strval (val, out);
724           fputs ("</literal></quote>", out);
725         }
726         else
727         {
728           fputs ("\nDefault: (empty)", out);
729         }
730         fputs ("</literallayout>\n", out);
731       }
732       else
733         fprintf (out, "\nDefault: %s</literallayout>\n", val);
734       break;
735     }
736     /* make gcc happy */
737     default:
738       break;
739   }
740 }
741
742 /**
743  ** Documentation line parser
744  **
745  ** The following code parses specially formatted documentation 
746  ** comments in init.h.
747  **
748  ** The format is very remotely inspired by nroff. Most important, it's
749  ** easy to parse and convert, and it was easy to generate from the SGML 
750  ** source of mutt's original manual.
751  **
752  ** - \fI switches to italics
753  ** - \fB switches to boldface
754  ** - \fP switches to normal display
755  ** - .dl on a line starts a definition list (name taken taken from HTML).
756  ** - .dt starts a term in a definition list.
757  ** - .dd starts a definition in a definition list.
758  ** - .de on a line finishes a definition list.
759  ** - .il on a line starts an itemzied list
760  ** - .dd starts an item in an itemized list
761  ** - .ie on a line finishes an itemized list
762  ** - .ts on a line starts a "tscreen" environment (name taken from SGML).
763  ** - .te on a line finishes this environment.
764  ** - .pp on a line starts a paragraph.
765  ** - \$word will be converted to a reference to word, where appropriate.
766  **   Note that \$$word is possible as well.
767  ** - '. ' in the beginning of a line expands to two space characters.
768  **   This is used to protect indentations in tables.
769  **/
770
771 /* close eventually-open environments. */
772
773 static int fd_recurse = 0;
774
775 static int flush_doc (int docstat, FILE *out)
776 {
777   if (docstat & D_INIT)
778     return D_INIT;
779
780   if (fd_recurse++)
781   {
782     fprintf (stderr, "%s: Internal error, recursion in flush_doc()!\n", Progname);
783     exit (1);
784   }
785
786   if (docstat & (D_PA))
787     docstat = print_it (SP_END_PAR, NULL, out, docstat);
788
789   if (docstat & (D_TAB))
790     docstat = print_it (SP_END_TAB, NULL, out, docstat);
791
792   if (docstat & (D_DL))
793     docstat = print_it (SP_END_DL, NULL, out, docstat);
794
795   if (docstat & (D_EM | D_BF | D_TT))
796     docstat = print_it (SP_END_FT, NULL, out, docstat);
797
798   docstat = print_it (SP_END_SECT, NULL, out, docstat);
799
800   docstat = print_it (SP_NEWLINE, NULL, out, 0);
801
802   fd_recurse--;
803   return D_INIT;
804 }
805
806 /* print something. */
807
808 static int print_it (int special, char *str, FILE *out, int docstat)
809 {
810   int onl = docstat & (D_NL|D_NP);
811   
812   docstat &= ~(D_NL|D_NP|D_INIT);
813
814   switch (OutputFormat)
815   {
816     /* configuration file */
817     case F_CONF:
818     {
819       switch (special)
820       {
821         static int Continuation = 0;
822
823         case SP_END_FT: docstat &= ~(D_EM|D_BF|D_TT); break;
824         case SP_START_BF: docstat |= D_BF; break;
825         case SP_START_EM: docstat |= D_EM; break;
826         case SP_START_TT: docstat |= D_TT; break;
827         case SP_NEWLINE: 
828         {
829           if (onl)
830             docstat |= onl;
831           else
832           {
833             fputs ("\n# ", out);
834             docstat |= D_NL;
835           }
836           if (docstat & D_DL)
837             ++ Continuation;
838           break;
839         }
840         case SP_NEWPAR:
841         {
842           if (onl & D_NP)
843           {
844             docstat |= onl;
845             break;
846           }
847
848           if (!(onl & D_NL))
849             fputs ("\n# ", out);
850           fputs ("\n# ", out);
851           docstat |= D_NP;
852           break;
853         }
854         case SP_START_TAB: 
855         {
856           if (!onl) 
857             fputs ("\n# ", out);
858           docstat |= D_TAB;
859           break;
860         }
861         case SP_END_TAB:
862         {
863           docstat &= ~D_TAB;
864           docstat |= D_NL;
865           break;
866         }
867         case SP_START_DL:
868         {
869           docstat |= D_DL;
870           break;
871         }
872         case SP_DT:
873         {
874           Continuation = 0;
875           docstat |= D_DT;
876           break;
877         }
878         case SP_DD:
879         {
880           if (docstat & D_IL)
881             fputs ("- ", out);
882           Continuation = 0;
883           break;
884         }
885         case SP_END_DL:
886         {
887           Continuation = 0;
888           docstat &= ~D_DL;
889           break;
890         }
891         case SP_START_IL:
892         {
893           docstat |= D_IL;
894           break;
895         }
896         case SP_END_IL:
897         {
898           Continuation = 0;
899           docstat &= ~D_IL;
900           break;
901         }
902         case SP_STR:
903         {
904           if (Continuation)
905           {
906             Continuation = 0;
907             fputs ("        ", out);
908           }
909           fputs (str, out);
910           if (docstat & D_DT)
911           { 
912             int i;
913
914             for (i = strlen (str) ; i < 8 ; i++)
915               putc (' ', out);
916             docstat &= ~D_DT;
917             docstat |= D_NL;
918           }
919           break;
920         }
921       }
922       break;
923     }
924
925     /* manual page */
926     case F_MAN:
927     {
928       switch (special)
929       {
930         case SP_END_FT: 
931         {
932           fputs ("\\fP", out);
933           docstat &= ~(D_EM|D_BF|D_TT);
934           break;
935         }
936         case SP_START_BF: 
937         {
938           fputs ("\\fB", out);
939           docstat |= D_BF;
940           docstat &= ~(D_EM|D_TT);
941           break;
942         }
943         case SP_START_EM:
944         {
945           fputs ("\\fI", out);
946           docstat |= D_EM;
947           docstat &= ~(D_BF|D_TT);
948           break;
949         }
950         case SP_START_TT:
951         {
952           fputs ("\\fC", out);
953           docstat |= D_TT;
954           docstat &= ~(D_BF|D_EM);
955           break;
956         }
957         case SP_NEWLINE:
958         {
959           if (onl)
960             docstat |= onl;
961           else
962           {
963             fputc ('\n', out);
964             docstat |= D_NL;
965           }
966           break;
967         }
968         case SP_NEWPAR:
969         {
970           if (onl & D_NP)
971           {
972             docstat |= onl;
973             break;
974           }
975
976           if (!(onl & D_NL))
977             fputc ('\n', out);
978           fputs (".IP\n", out);
979
980           docstat |= D_NP;
981           break;
982         }
983         case SP_START_TAB:
984         {
985           fputs ("\n.IP\n.DS\n.sp\n.ft CR\n.nf\n", out);
986           docstat |= D_TAB | D_NL;
987           break;
988         }
989         case SP_END_TAB:
990         {
991           fputs ("\n.fi\n.ec\n.ft P\n.sp\n", out);
992           docstat &= ~D_TAB;
993           docstat |= D_NL;
994           break;
995         }
996         case SP_START_DL:
997         {
998           fputs (".RS\n.PD 0\n", out);
999           docstat |= D_DL;
1000           break;
1001         }
1002         case SP_DT:
1003         {
1004           fputs (".TP\n", out);
1005           break;
1006         }
1007         case SP_DD:
1008         {
1009           if (docstat & D_IL)
1010             fputs (".TP\n\\(hy ", out);
1011           else
1012             fputs ("\n", out);
1013           break;
1014         }
1015         case SP_END_DL:
1016         {
1017           fputs (".RE\n.PD 1", out);
1018           docstat &= ~D_DL;
1019           break;
1020         }
1021         case SP_START_IL:
1022         {
1023           fputs (".RS\n.PD 0\n", out);
1024           docstat |= D_IL;
1025           break;
1026         }
1027         case SP_END_IL:
1028         {
1029           fputs (".RE\n.PD 1", out);
1030           docstat &= ~D_DL;
1031           break;
1032         }
1033         case SP_STR:
1034         {
1035           while (*str)
1036           {
1037             for (; *str; str++)
1038             {
1039               if (*str == '"')
1040                 fputs ("\\(rq", out);
1041               else if (*str == '\\')
1042                 fputs ("\\\\", out);
1043               else if (!strncmp (str, "``", 2))
1044               {
1045                 fputs ("\\(lq", out);
1046                 str++;
1047               }
1048               else if (!strncmp (str, "''", 2))
1049               {
1050                 fputs ("\\(rq", out);
1051                 str++;
1052               }
1053               else
1054                 fputc (*str, out);
1055             }
1056           }
1057           break;
1058         }
1059       }
1060       break;
1061     }
1062
1063     /* SGML based manual */
1064     case F_SGML:
1065     {
1066       switch (special)
1067       {
1068         case SP_END_FT: 
1069         {
1070           if (docstat & D_EM) fputs ("</emphasis>", out);
1071           if (docstat & D_BF) fputs ("</emphasis>", out);
1072           if (docstat & D_TT) fputs ("</literal>", out);
1073           docstat &= ~(D_EM|D_BF|D_TT);
1074           break;
1075         }
1076         case SP_START_BF: 
1077         {
1078           fputs ("<emphasis role=\"bold\">", out);
1079           docstat |= D_BF;
1080           docstat &= ~(D_EM|D_TT);
1081           break;
1082         }
1083         case SP_START_EM:
1084         {
1085           fputs ("<emphasis>", out);
1086           docstat |= D_EM;
1087           docstat &= ~(D_BF|D_TT);
1088           break;
1089         }
1090         case SP_START_TT:
1091         {
1092           fputs ("<literal>", out);
1093           docstat |= D_TT;
1094           docstat &= ~(D_BF|D_EM);
1095           break;
1096         }
1097         case SP_NEWLINE:
1098         {
1099           if (onl)
1100             docstat |= onl;
1101           else
1102           {
1103             fputc ('\n', out);
1104             docstat |= D_NL;
1105           }
1106           break;
1107         }
1108         case SP_NEWPAR:
1109         {
1110           if (onl & D_NP)
1111           {
1112             docstat |= onl;
1113             break;
1114           }
1115
1116           if (!(onl & D_NL))
1117             fputc ('\n', out);
1118           if (docstat & D_PA)
1119             fputs("</para>\n", out);
1120           fputs ("<para>\n", out);
1121
1122           docstat |= D_NP;
1123           docstat |= D_PA;
1124
1125           break;
1126         }
1127         case SP_END_PAR:
1128         {
1129           fputs ("</para>\n", out);
1130           docstat &= ~D_PA;
1131           break;
1132         }
1133         case SP_START_TAB:
1134         {
1135           if (docstat & D_PA)
1136           {
1137             fputs ("\n</para>\n", out);
1138             docstat &= ~D_PA;
1139           }
1140           fputs ("\n<screen>\n", out);
1141           docstat |= D_TAB | D_NL;
1142           break;
1143         }
1144         case SP_END_TAB:
1145         {
1146           fputs ("\n</screen>", out);
1147           docstat &= ~D_TAB;
1148           docstat |= D_NL;
1149           break;
1150         }
1151         case SP_START_DL:
1152         {
1153           if (docstat & D_PA)
1154           {
1155             fputs ("\n</para>\n", out);
1156             docstat &= ~D_PA;
1157           }
1158           fputs ("\n<variablelist>\n", out);
1159           docstat |= D_DL;
1160           break;
1161         }
1162         case SP_DT:
1163         {
1164           fputs ("<varlistentry><term>", out);
1165           break;
1166         }
1167         case SP_DD:
1168         {
1169           docstat |= D_DD;
1170           if (docstat & D_DL)
1171             fputs("</term>\n", out);
1172           fputs ("<listitem><para>", out);
1173           break;
1174         }
1175         case SP_END_DD:
1176         {
1177           docstat &= ~D_DD;
1178           fputs ("</para></listitem>", out);
1179           if (docstat & D_DL)
1180             fputs("</varlistentry>\n", out);
1181           break;
1182         }
1183         case SP_END_DL:
1184         {
1185           fputs ("</para></listitem></varlistentry></variablelist>\n", out);
1186           docstat &= ~(D_DD|D_DL);
1187           break;
1188         }
1189         case SP_START_IL:
1190         {
1191           if (docstat & D_PA)
1192           {
1193             fputs ("\n</para>\n", out);
1194             docstat &= ~D_PA;
1195           }
1196           fputs ("\n<itemizedlist>\n", out);
1197           docstat |= D_IL;
1198           break;
1199         }
1200         case SP_END_IL:
1201         {
1202           fputs ("</para></listitem></itemizedlist>\n", out);
1203           docstat &= ~(D_DD|D_DL);
1204           break;
1205         }
1206         case SP_END_SECT:
1207         {
1208           fputs ("</sect2>", out);
1209           break;
1210         }
1211         case SP_STR:
1212         {
1213           if (docstat & D_TAB)
1214             sgml_fputs (str, out, 0);
1215           else
1216           {
1217             while (*str)
1218             {
1219               for (; *str; str++)
1220               {
1221                 if (!strncmp (str, "``", 2))
1222                 {
1223                   fputs ("<quote>", out);
1224                   str++;
1225                 }
1226                 else if (!strncmp (str, "''", 2))
1227                 {
1228                   fputs ("</quote>", out);
1229                   str++;
1230                 }
1231                 else
1232                   sgml_fputc (*str, out, 1);
1233               }
1234             }
1235           }
1236           break;
1237         }
1238       }
1239       break;
1240     }
1241     /* make gcc happy (unreached) */
1242     default:
1243       break;
1244   }
1245
1246   return docstat;
1247 }
1248
1249 void print_ref (FILE *out, int output_dollar, const char *ref)
1250 {
1251   switch (OutputFormat)
1252   {
1253   case F_CONF:
1254   case F_MAN:
1255     if (output_dollar)
1256       putc ('$', out);
1257     fputs (ref, out);
1258     break;
1259
1260   case F_SGML:
1261     fputs ("<link linkend=\"", out);
1262     sgml_id_fputs (ref, out);
1263     fputs ("\">", out);
1264     if (output_dollar)
1265       fputs ("&dollar;", out);
1266     sgml_fputs (ref, out, 1);
1267     fputs ("</link>", out);
1268     break;
1269
1270   default:
1271     break;
1272   }
1273 }
1274
1275 static int commit_buff (char *buff, char **d, FILE *out, int docstat)
1276 {
1277   if (*d > buff)
1278   {
1279     **d = '\0';
1280     docstat = print_it (SP_STR, buff, out, docstat);
1281     *d = buff;
1282   }
1283
1284   return docstat;
1285 }
1286
1287 static int handle_docline (char *l, FILE *out, int docstat)
1288 {
1289   char buff[BUFFSIZE];
1290   char *s, *d;
1291   l = skip_ws (l);
1292
1293   if (Debug)
1294     fprintf (stderr, "%s: handle_docline `%s'\n", Progname, l);
1295   
1296   if (!strncmp (l, ".pp", 3))
1297     return print_it (SP_NEWPAR, NULL, out, docstat);
1298   else if (!strncmp (l, ".ts", 3))
1299     return print_it (SP_START_TAB, NULL, out, docstat);
1300   else if (!strncmp (l, ".te", 3))
1301     return print_it (SP_END_TAB, NULL, out, docstat);
1302   else if (!strncmp (l, ".dl", 3))
1303     return print_it (SP_START_DL, NULL, out, docstat);
1304   else if (!strncmp (l, ".de", 3))
1305     return print_it (SP_END_DL, NULL, out, docstat);
1306   else if (!strncmp (l, ".il", 3))
1307     return print_it (SP_START_IL, NULL, out, docstat);
1308   else if (!strncmp (l, ".ie", 3))
1309     return print_it (SP_END_IL, NULL, out, docstat);
1310   else if (!strncmp (l, ". ", 2))
1311     *l = ' ';
1312
1313   for (s = l, d = buff; *s; s++)
1314   {
1315     if (!strncmp (s, "\\(as", 4))
1316     {
1317       *d++ = '*';
1318       s += 3;
1319     }
1320     else if (!strncmp (s, "\\(rs", 4))
1321     {
1322       *d++ = '\\';
1323       s += 3;
1324     }
1325     else if (!strncmp (s, "\\fI", 3))
1326     {
1327       docstat = commit_buff (buff, &d, out, docstat);
1328       docstat = print_it (SP_START_EM, NULL, out, docstat);
1329       s += 2;
1330     }
1331     else if (!strncmp (s, "\\fB", 3))
1332     {
1333       docstat = commit_buff (buff, &d, out, docstat);
1334       docstat = print_it (SP_START_BF, NULL, out, docstat);
1335       s += 2;
1336     }
1337     else if (!strncmp (s, "\\fC", 3))
1338     {
1339       docstat = commit_buff (buff, &d, out, docstat);
1340       docstat = print_it (SP_START_TT, NULL, out, docstat);
1341       s += 2;
1342     }
1343     else if (!strncmp (s, "\\fP", 3))
1344     {
1345       docstat = commit_buff (buff, &d, out, docstat);
1346       docstat = print_it (SP_END_FT, NULL, out, docstat);
1347       s += 2;
1348     }
1349     else if (!strncmp (s, ".dt", 3))
1350     {
1351       if (docstat & D_DD)
1352       {
1353         docstat = commit_buff (buff, &d, out, docstat);
1354         docstat = print_it (SP_END_DD, NULL, out, docstat);
1355       }
1356       docstat = commit_buff (buff, &d, out, docstat);
1357       docstat = print_it (SP_DT, NULL, out, docstat);
1358       s += 3;
1359     }
1360     else if (!strncmp (s, ".dd", 3))
1361     {
1362       if ((docstat & D_IL) && (docstat & D_DD))
1363       {
1364         docstat = commit_buff (buff, &d, out, docstat);
1365         docstat = print_it (SP_END_DD, NULL, out, docstat);
1366       }
1367       docstat = commit_buff (buff, &d, out, docstat);
1368       docstat = print_it (SP_DD, NULL, out, docstat);
1369       s += 3;
1370     }
1371     else if (*s == '$')
1372     {
1373       int output_dollar = 0;
1374       char *ref;
1375       char save;
1376
1377       ++s;
1378       if (*s == '$')
1379       {
1380         output_dollar = 1;
1381         ++s;
1382       }
1383       if (*s == '$')
1384       {
1385         *d++ = '$';
1386       }
1387       else
1388       {
1389         ref = s;
1390         while (isalnum ((unsigned char) *s) || (*s && strchr("-_<>", *s)))
1391           ++s;
1392
1393         docstat = commit_buff (buff, &d, out, docstat);
1394         save = *s;
1395         *s = 0;
1396         print_ref (out, output_dollar, ref);
1397         *s = save;
1398         --s;
1399       }
1400     }
1401     else
1402       *d++ = *s;
1403   }
1404
1405   docstat = commit_buff (buff, &d, out, docstat);
1406   return print_it (SP_NEWLINE, NULL, out, docstat);
1407 }