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