mutt stable branch with some hacks
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
53extern int sys_nerr;
54extern char *sys_errlist[];
55#endif
56
57#define strerror(x) ((x) > 0 && (x) < sys_nerr) ? sys_errlist[(x)] : 0
58#endif /* !HAVE_STRERROR */
59
60extern int optind;
61
62#define BUFFSIZE 2048
63
64enum 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
82enum
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
105enum output_formats_t OutputFormat = F_NONE;
106char *Progname;
107short Debug = 0;
108
109static char *get_token (char *, size_t, char *);
110static char *skip_ws (char *);
111static const char *type2human (int);
112static int buff2type (const char *);
113static int flush_doc (int, FILE *);
114static int handle_docline (char *, FILE *, int);
115static int print_it (int, char *, FILE *, int);
116static void print_confline (const char *, int, const char *, FILE *);
117static void handle_confline (char *, FILE *);
118static void makedoc (FILE *, FILE *);
119static void pretty_default (char *, size_t, const char *, int);
120static int sgml_fputc (int, FILE *);
121static int sgml_fputs (const char *, FILE *);
122static int sgml_id_fputs (const char *, FILE *);
123
124int 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
182static 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
233static 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
243static char single_char_tokens[] = "[]{},;|";
244
245static 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: argument 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
349enum
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 DT_MBCHARTBL
363};
364
365struct
366{
367 char *machine;
368 char *human;
369}
370types[] =
371{
372 { "DT_NONE", "-none-" },
373 { "DT_BOOL", "boolean" },
374 { "DT_NUM", "number" },
375 { "DT_STR", "string" },
376 { "DT_PATH", "path" },
377 { "DT_QUAD", "quadoption" },
378 { "DT_SORT", "sort order" },
379 { "DT_RX", "regular expression" },
380 { "DT_MAGIC", "folder magic" },
381 { "DT_SYN", NULL },
382 { "DT_ADDR", "e-mail address" },
383 { "DT_MBCHARTBL", "string" },
384 { NULL, NULL }
385};
386
387
388static int buff2type (const char *s)
389{
390 int type;
391
392 for (type = DT_NONE; types[type].machine; type++)
393 if (!strcmp (types[type].machine, s))
394 return type;
395
396 return DT_NONE;
397}
398
399static const char *type2human (int type)
400{
401 return types[type].human;
402}
403static void handle_confline (char *s, FILE *out)
404{
405 char varname[BUFFSIZE];
406 char buff[BUFFSIZE];
407 char tmp[BUFFSIZE];
408 int type;
409
410 char val[BUFFSIZE];
411
412 /* xxx - put this into an actual state machine? */
413
414 /* variable name */
415 if (!(s = get_token (varname, sizeof (varname), s))) return;
416
417 /* comma */
418 if (!(s = get_token (buff, sizeof (buff), s))) return;
419
420 /* type */
421 if (!(s = get_token (buff, sizeof (buff), s))) return;
422
423 type = buff2type (buff);
424
425 /* possibly a "|" or comma */
426 if (!(s = get_token (buff, sizeof (buff), s))) return;
427
428 if (!strcmp (buff, "|"))
429 {
430 if (Debug) fprintf (stderr, "%s: Expecting <subtype> <comma>.\n", Progname);
431 /* ignore subtype and comma */
432 if (!(s = get_token (buff, sizeof (buff), s))) return;
433 if (!(s = get_token (buff, sizeof (buff), s))) return;
434 }
435
436 /* redraw, comma */
437
438 while (1)
439 {
440 if (!(s = get_token (buff, sizeof (buff), s))) return;
441 if (!strcmp (buff, ","))
442 break;
443 }
444
445 /* option name or UL &address */
446 if (!(s = get_token (buff, sizeof (buff), s))) return;
447 if (!strcmp (buff, "UL"))
448 if (!(s = get_token (buff, sizeof (buff), s))) return;
449
450 /* comma */
451 if (!(s = get_token (buff, sizeof (buff), s))) return;
452
453 if (Debug) fprintf (stderr, "%s: Expecting default value.\n", Progname);
454
455 /* <default value> or UL <default value> */
456 if (!(s = get_token (buff, sizeof (buff), s))) return;
457 if (!strcmp (buff, "UL"))
458 {
459 if (Debug) fprintf (stderr, "%s: Skipping UL.\n", Progname);
460 if (!(s = get_token (buff, sizeof (buff), s))) return;
461 }
462
463 memset (tmp, 0, sizeof (tmp));
464
465 do
466 {
467 if (!strcmp (buff, "}"))
468 break;
469
470 strncpy (tmp + strlen (tmp), buff, sizeof (tmp) - strlen (tmp));
471 }
472 while ((s = get_token (buff, sizeof (buff), s)));
473
474 pretty_default (val, sizeof (val), tmp, type);
475 print_confline (varname, type, val, out);
476}
477
478static void pretty_default (char *t, size_t l, const char *s, int type)
479{
480 memset (t, 0, l);
481 l--;
482
483 switch (type)
484 {
485 case DT_QUAD:
486 {
487 if (!strcasecmp (s, "MUTT_YES")) strncpy (t, "yes", l);
488 else if (!strcasecmp (s, "MUTT_NO")) strncpy (t, "no", l);
489 else if (!strcasecmp (s, "MUTT_ASKYES")) strncpy (t, "ask-yes", l);
490 else if (!strcasecmp (s, "MUTT_ASKNO")) strncpy (t, "ask-no", l);
491 break;
492 }
493 case DT_BOOL:
494 {
495 if (atoi (s))
496 strncpy (t, "yes", l);
497 else
498 strncpy (t, "no", l);
499 break;
500 }
501 case DT_SORT:
502 {
503 /* heuristic! */
504 if (strncmp (s, "SORT_", 5))
505 fprintf (stderr,
506 "WARNING: expected prefix of SORT_ for type DT_SORT instead of %s\n", s);
507 strncpy (t, s + 5, l);
508 for (; *t; t++) *t = tolower ((unsigned char) *t);
509 break;
510 }
511 case DT_MAGIC:
512 {
513 /* heuristic! */
514 if (strncmp (s, "MUTT_", 5))
515 fprintf (stderr,
516 "WARNING: expected prefix of MUTT_ for type DT_MAGIC instead of %s\n", s);
517 strncpy (t, s + 5, l);
518 for (; *t; t++) *t = tolower ((unsigned char) *t);
519 break;
520 }
521 case DT_STR:
522 case DT_RX:
523 case DT_ADDR:
524 case DT_PATH:
525 case DT_MBCHARTBL:
526 {
527 if (!strcmp (s, "0"))
528 break;
529 /* fallthrough */
530 }
531 default:
532 {
533 strncpy (t, s, l);
534 break;
535 }
536 }
537}
538
539static void char_to_escape (char *dest, unsigned int c)
540{
541 switch (c)
542 {
543 case '\r': strcpy (dest, "\\r"); break; /* __STRCPY_CHECKED__ */
544 case '\n': strcpy (dest, "\\n"); break; /* __STRCPY_CHECKED__ */
545 case '\t': strcpy (dest, "\\t"); break; /* __STRCPY_CHECKED__ */
546 case '\f': strcpy (dest, "\\f"); break; /* __STRCPY_CHECKED__ */
547 default: sprintf (dest, "\\%03o", c); break;
548 }
549}
550static void conf_char_to_escape (unsigned int c , FILE *out)
551{
552 char buff[16];
553 char_to_escape (buff, c);
554 fputs (buff, out);
555}
556
557static void conf_print_strval (const char *v, FILE *out)
558{
559 for (; *v; v++)
560 {
561 if (*v < ' ' || *v & 0x80)
562 {
563 conf_char_to_escape ((unsigned int) *v, out);
564 continue;
565 }
566
567 if (*v == '"' || *v == '\\')
568 fputc ('\\', out);
569 fputc (*v, out);
570 }
571}
572
573static void man_print_strval (const char *v, FILE *out)
574{
575 for (; *v; v++)
576 {
577 if (*v < ' ' || *v & 0x80)
578 {
579 fputc ('\\', out);
580 conf_char_to_escape ((unsigned int) *v, out);
581 continue;
582 }
583
584 if (*v == '"')
585 fputs ("\\(rq", out);
586 else if (*v == '\\')
587 fputs ("\\\\", out);
588 else if (*v == '-')
589 fputs ("\\-", out);
590 else
591 fputc (*v, out);
592 }
593}
594
595static void sgml_print_strval (const char *v, FILE *out)
596{
597 char buff[16];
598 for (; *v; v++)
599 {
600 if (*v < ' ' || *v & 0x80)
601 {
602 char_to_escape (buff, (unsigned int) *v);
603 sgml_fputs (buff, out);
604 continue;
605 }
606 sgml_fputc ((unsigned int) *v, out);
607 }
608}
609
610static int sgml_fputc (int c, FILE *out)
611{
612 switch (c)
613 {
614 /* the bare minimum for escaping */
615 case '<': return fputs ("<", out);
616 case '>': return fputs (">", out);
617 case '&': return fputs ("&", out);
618 default: return fputc (c, out);
619 }
620}
621
622static int sgml_fputs (const char *s, FILE *out)
623{
624 for (; *s; s++)
625 if (sgml_fputc ((unsigned int) *s, out) == EOF)
626 return EOF;
627
628 return 0;
629}
630
631/* reduce CDATA to ID */
632static 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
655static 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 type == DT_MBCHARTBL)
666 {
667 fprintf (out, "\n# set %s=\"", varname);
668 conf_print_strval (val, out);
669 fputs ("\"", out);
670 }
671 else if (type != DT_SYN)
672 fprintf (out, "\n# set %s=%s", varname, val);
673
674 fprintf (out, "\n#\n# Name: %s", varname);
675 fprintf (out, "\n# Type: %s", type2human (type));
676 if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH ||
677 type == DT_MBCHARTBL)
678 {
679 fputs ("\n# Default: \"", out);
680 conf_print_strval (val, out);
681 fputs ("\"", out);
682 }
683 else
684 fprintf (out, "\n# Default: %s", val);
685
686 fputs ("\n# ", out);
687 break;
688 }
689
690 /* manual page */
691 case F_MAN:
692 {
693 fprintf (out, "\n.TP\n.B %s\n", varname);
694 fputs (".nf\n", out);
695 fprintf (out, "Type: %s\n", type2human (type));
696 if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH ||
697 type == DT_MBCHARTBL)
698 {
699 fputs ("Default: \\(lq", out);
700 man_print_strval (val, out);
701 fputs ("\\(rq\n", out);
702 }
703 else {
704 fputs ("Default: ", out);
705 man_print_strval (val, out);
706 fputs ("\n", out);
707 }
708
709 fputs (".fi", out);
710
711 break;
712 }
713
714 /* SGML based manual */
715 case F_SGML:
716 {
717 fputs ("\n<sect2 id=\"", out);
718 sgml_id_fputs(varname, out);
719 fputs ("\">\n<title>", out);
720 sgml_fputs (varname, out);
721 fprintf (out, "</title>\n<literallayout>Type: %s", type2human (type));
722
723
724 if (type == DT_STR || type == DT_RX || type == DT_ADDR || type == DT_PATH ||
725 type == DT_MBCHARTBL)
726 {
727 if (val && *val)
728 {
729 fputs ("\nDefault: <quote><literal>", out);
730 sgml_print_strval (val, out);
731 fputs ("</literal></quote>", out);
732 }
733 else
734 {
735 fputs ("\nDefault: (empty)", out);
736 }
737 fputs ("</literallayout>\n", out);
738 }
739 else
740 fprintf (out, "\nDefault: %s</literallayout>\n", val);
741 break;
742 }
743 /* make gcc happy */
744 default:
745 break;
746 }
747}
748
749/**
750 ** Documentation line parser
751 **
752 ** The following code parses specially formatted documentation
753 ** comments in init.h.
754 **
755 ** The format is very remotely inspired by nroff. Most important, it's
756 ** easy to parse and convert, and it was easy to generate from the SGML
757 ** source of mutt's original manual.
758 **
759 ** - \fI switches to italics
760 ** - \fB switches to boldface
761 ** - \fP switches to normal display
762 ** - .dl on a line starts a definition list (name taken taken from HTML).
763 ** - .dt starts a term in a definition list.
764 ** - .dd starts a definition in a definition list.
765 ** - .de on a line finishes a definition list.
766 ** - .il on a line starts an itemized list
767 ** - .dd starts an item in an itemized list
768 ** - .ie on a line finishes an itemized list
769 ** - .ts on a line starts a "tscreen" environment (name taken from SGML).
770 ** - .te on a line finishes this environment.
771 ** - .pp on a line starts a paragraph.
772 ** - \$word will be converted to a reference to word, where appropriate.
773 ** Note that \$$word is possible as well.
774 ** - '. ' in the beginning of a line expands to two space characters.
775 ** This is used to protect indentations in tables.
776 **/
777
778/* close eventually-open environments. */
779
780static int fd_recurse = 0;
781
782static int flush_doc (int docstat, FILE *out)
783{
784 if (docstat & D_INIT)
785 return D_INIT;
786
787 if (fd_recurse++)
788 {
789 fprintf (stderr, "%s: Internal error, recursion in flush_doc()!\n", Progname);
790 exit (1);
791 }
792
793 if (docstat & (D_PA))
794 docstat = print_it (SP_END_PAR, NULL, out, docstat);
795
796 if (docstat & (D_TAB))
797 docstat = print_it (SP_END_TAB, NULL, out, docstat);
798
799 if (docstat & (D_DL))
800 docstat = print_it (SP_END_DL, NULL, out, docstat);
801
802 if (docstat & (D_EM | D_BF | D_TT))
803 docstat = print_it (SP_END_FT, NULL, out, docstat);
804
805 docstat = print_it (SP_END_SECT, NULL, out, docstat);
806
807 docstat = print_it (SP_NEWLINE, NULL, out, 0);
808
809 fd_recurse--;
810 return D_INIT;
811}
812
813/* print something. */
814
815static int print_it (int special, char *str, FILE *out, int docstat)
816{
817 int onl = docstat & (D_NL|D_NP);
818
819 docstat &= ~(D_NL|D_NP|D_INIT);
820
821 switch (OutputFormat)
822 {
823 /* configuration file */
824 case F_CONF:
825 {
826 switch (special)
827 {
828 static int Continuation = 0;
829
830 case SP_END_FT: docstat &= ~(D_EM|D_BF|D_TT); break;
831 case SP_START_BF: docstat |= D_BF; break;
832 case SP_START_EM: docstat |= D_EM; break;
833 case SP_START_TT: docstat |= D_TT; break;
834 case SP_NEWLINE:
835 {
836 if (onl)
837 docstat |= onl;
838 else
839 {
840 fputs ("\n# ", out);
841 docstat |= D_NL;
842 }
843 if (docstat & D_DL)
844 ++ Continuation;
845 break;
846 }
847 case SP_NEWPAR:
848 {
849 if (onl & D_NP)
850 {
851 docstat |= onl;
852 break;
853 }
854
855 if (!(onl & D_NL))
856 fputs ("\n# ", out);
857 fputs ("\n# ", out);
858 docstat |= D_NP;
859 break;
860 }
861 case SP_START_TAB:
862 {
863 if (!onl)
864 fputs ("\n# ", out);
865 docstat |= D_TAB;
866 break;
867 }
868 case SP_END_TAB:
869 {
870 docstat &= ~D_TAB;
871 docstat |= D_NL;
872 break;
873 }
874 case SP_START_DL:
875 {
876 docstat |= D_DL;
877 break;
878 }
879 case SP_DT:
880 {
881 Continuation = 0;
882 docstat |= D_DT;
883 break;
884 }
885 case SP_DD:
886 {
887 if (docstat & D_IL)
888 fputs ("- ", out);
889 Continuation = 0;
890 break;
891 }
892 case SP_END_DL:
893 {
894 Continuation = 0;
895 docstat &= ~D_DL;
896 break;
897 }
898 case SP_START_IL:
899 {
900 docstat |= D_IL;
901 break;
902 }
903 case SP_END_IL:
904 {
905 Continuation = 0;
906 docstat &= ~D_IL;
907 break;
908 }
909 case SP_STR:
910 {
911 if (Continuation)
912 {
913 Continuation = 0;
914 fputs (" ", out);
915 }
916 fputs (str, out);
917 if (docstat & D_DT)
918 {
919 int i;
920
921 for (i = strlen (str) ; i < 8 ; i++)
922 putc (' ', out);
923 docstat &= ~D_DT;
924 docstat |= D_NL;
925 }
926 break;
927 }
928 }
929 break;
930 }
931
932 /* manual page */
933 case F_MAN:
934 {
935 switch (special)
936 {
937 case SP_END_FT:
938 {
939 fputs ("\\fP", out);
940 docstat &= ~(D_EM|D_BF|D_TT);
941 break;
942 }
943 case SP_START_BF:
944 {
945 fputs ("\\fB", out);
946 docstat |= D_BF;
947 docstat &= ~(D_EM|D_TT);
948 break;
949 }
950 case SP_START_EM:
951 {
952 fputs ("\\fI", out);
953 docstat |= D_EM;
954 docstat &= ~(D_BF|D_TT);
955 break;
956 }
957 case SP_START_TT:
958 {
959 fputs ("\\fC", out);
960 docstat |= D_TT;
961 docstat &= ~(D_BF|D_EM);
962 break;
963 }
964 case SP_NEWLINE:
965 {
966 if (onl)
967 docstat |= onl;
968 else
969 {
970 fputc ('\n', out);
971 docstat |= D_NL;
972 }
973 break;
974 }
975 case SP_NEWPAR:
976 {
977 if (onl & D_NP)
978 {
979 docstat |= onl;
980 break;
981 }
982
983 if (!(onl & D_NL))
984 fputc ('\n', out);
985 fputs (".IP\n", out);
986
987 docstat |= D_NP;
988 break;
989 }
990 case SP_START_TAB:
991 {
992 fputs ("\n.IP\n.EX\n", out);
993 docstat |= D_TAB | D_NL;
994 break;
995 }
996 case SP_END_TAB:
997 {
998 fputs ("\n.EE\n", out);
999 docstat &= ~D_TAB;
1000 docstat |= D_NL;
1001 break;
1002 }
1003 case SP_START_DL:
1004 {
1005 fputs (".RS\n.PD 0\n", out);
1006 docstat |= D_DL;
1007 break;
1008 }
1009 case SP_DT:
1010 {
1011 fputs (".TP\n", out);
1012 break;
1013 }
1014 case SP_DD:
1015 {
1016 if (docstat & D_IL)
1017 fputs (".TP\n\\(hy ", out);
1018 else
1019 fputs ("\n", out);
1020 break;
1021 }
1022 case SP_END_DL:
1023 {
1024 fputs (".RE\n.PD 1", out);
1025 docstat &= ~D_DL;
1026 break;
1027 }
1028 case SP_START_IL:
1029 {
1030 fputs (".RS\n.PD 0\n", out);
1031 docstat |= D_IL;
1032 break;
1033 }
1034 case SP_END_IL:
1035 {
1036 fputs (".RE\n.PD 1", out);
1037 docstat &= ~D_DL;
1038 break;
1039 }
1040 case SP_STR:
1041 {
1042 while (*str)
1043 {
1044 for (; *str; str++)
1045 {
1046 if (*str == '"')
1047 fputs ("\\(rq", out);
1048 else if (*str == '\\')
1049 fputs ("\\\\", out);
1050 else if (*str == '-')
1051 fputs ("\\-", out);
1052 else if (!strncmp (str, "``", 2))
1053 {
1054 fputs ("\\(lq", out);
1055 str++;
1056 }
1057 else if (!strncmp (str, "''", 2))
1058 {
1059 fputs ("\\(rq", out);
1060 str++;
1061 }
1062 else
1063 fputc (*str, out);
1064 }
1065 }
1066 break;
1067 }
1068 }
1069 break;
1070 }
1071
1072 /* SGML based manual */
1073 case F_SGML:
1074 {
1075 switch (special)
1076 {
1077 case SP_END_FT:
1078 {
1079 if (docstat & D_EM) fputs ("</emphasis>", out);
1080 if (docstat & D_BF) fputs ("</emphasis>", out);
1081 if (docstat & D_TT) fputs ("</literal>", out);
1082 docstat &= ~(D_EM|D_BF|D_TT);
1083 break;
1084 }
1085 case SP_START_BF:
1086 {
1087 fputs ("<emphasis role=\"bold\">", out);
1088 docstat |= D_BF;
1089 docstat &= ~(D_EM|D_TT);
1090 break;
1091 }
1092 case SP_START_EM:
1093 {
1094 fputs ("<emphasis>", out);
1095 docstat |= D_EM;
1096 docstat &= ~(D_BF|D_TT);
1097 break;
1098 }
1099 case SP_START_TT:
1100 {
1101 fputs ("<literal>", out);
1102 docstat |= D_TT;
1103 docstat &= ~(D_BF|D_EM);
1104 break;
1105 }
1106 case SP_NEWLINE:
1107 {
1108 if (onl)
1109 docstat |= onl;
1110 else
1111 {
1112 fputc ('\n', out);
1113 docstat |= D_NL;
1114 }
1115 break;
1116 }
1117 case SP_NEWPAR:
1118 {
1119 if (onl & D_NP)
1120 {
1121 docstat |= onl;
1122 break;
1123 }
1124
1125 if (!(onl & D_NL))
1126 fputc ('\n', out);
1127 if (docstat & D_PA)
1128 fputs("</para>\n", out);
1129 fputs ("<para>\n", out);
1130
1131 docstat |= D_NP;
1132 docstat |= D_PA;
1133
1134 break;
1135 }
1136 case SP_END_PAR:
1137 {
1138 fputs ("</para>\n", out);
1139 docstat &= ~D_PA;
1140 break;
1141 }
1142 case SP_START_TAB:
1143 {
1144 if (docstat & D_PA)
1145 {
1146 fputs ("\n</para>\n", out);
1147 docstat &= ~D_PA;
1148 }
1149 fputs ("\n<screen>\n", out);
1150 docstat |= D_TAB | D_NL;
1151 break;
1152 }
1153 case SP_END_TAB:
1154 {
1155 fputs ("</screen>", out);
1156 docstat &= ~D_TAB;
1157 docstat |= D_NL;
1158 break;
1159 }
1160 case SP_START_DL:
1161 {
1162 if (docstat & D_PA)
1163 {
1164 fputs ("\n</para>\n", out);
1165 docstat &= ~D_PA;
1166 }
1167 fputs ("\n<informaltable>\n<tgroup cols=\"2\">\n<tbody>\n", out);
1168 docstat |= D_DL;
1169 break;
1170 }
1171 case SP_DT:
1172 {
1173 fputs ("<row><entry>", out);
1174 break;
1175 }
1176 case SP_DD:
1177 {
1178 docstat |= D_DD;
1179 if (docstat & D_DL)
1180 fputs("</entry><entry>", out);
1181 else
1182 fputs ("<listitem><para>", out);
1183 break;
1184 }
1185 case SP_END_DD:
1186 {
1187 if (docstat & D_DL)
1188 fputs ("</entry></row>\n", out);
1189 else
1190 fputs ("</para></listitem>", out);
1191 docstat &= ~D_DD;
1192 break;
1193 }
1194 case SP_END_DL:
1195 {
1196 fputs ("</entry></row></tbody></tgroup></informaltable>\n", out);
1197 docstat &= ~(D_DD|D_DL);
1198 break;
1199 }
1200 case SP_START_IL:
1201 {
1202 if (docstat & D_PA)
1203 {
1204 fputs ("\n</para>\n", out);
1205 docstat &= ~D_PA;
1206 }
1207 fputs ("\n<itemizedlist>\n", out);
1208 docstat |= D_IL;
1209 break;
1210 }
1211 case SP_END_IL:
1212 {
1213 fputs ("</para></listitem></itemizedlist>\n", out);
1214 docstat &= ~(D_DD|D_DL);
1215 break;
1216 }
1217 case SP_END_SECT:
1218 {
1219 fputs ("</sect2>", out);
1220 break;
1221 }
1222 case SP_STR:
1223 {
1224 if (docstat & D_TAB)
1225 sgml_fputs (str, out);
1226 else
1227 {
1228 while (*str)
1229 {
1230 for (; *str; str++)
1231 {
1232 if (!strncmp (str, "``", 2))
1233 {
1234 fputs ("<quote>", out);
1235 str++;
1236 }
1237 else if (!strncmp (str, "''", 2))
1238 {
1239 fputs ("</quote>", out);
1240 str++;
1241 }
1242 else
1243 sgml_fputc (*str, out);
1244 }
1245 }
1246 }
1247 break;
1248 }
1249 }
1250 break;
1251 }
1252 /* make gcc happy (unreached) */
1253 default:
1254 break;
1255 }
1256
1257 return docstat;
1258}
1259
1260void print_ref (FILE *out, int output_dollar, const char *ref)
1261{
1262 switch (OutputFormat)
1263 {
1264 case F_CONF:
1265 case F_MAN:
1266 if (output_dollar)
1267 putc ('$', out);
1268 fputs (ref, out);
1269 break;
1270
1271 case F_SGML:
1272 fputs ("<link linkend=\"", out);
1273 sgml_id_fputs (ref, out);
1274 fputs ("\">", out);
1275 if (output_dollar)
1276 fputc ('$', out);
1277 sgml_fputs (ref, out);
1278 fputs ("</link>", out);
1279 break;
1280
1281 default:
1282 break;
1283 }
1284}
1285
1286static int commit_buff (char *buff, char **d, FILE *out, int docstat)
1287{
1288 if (*d > buff)
1289 {
1290 **d = '\0';
1291 docstat = print_it (SP_STR, buff, out, docstat);
1292 *d = buff;
1293 }
1294
1295 return docstat;
1296}
1297
1298static int handle_docline (char *l, FILE *out, int docstat)
1299{
1300 char buff[BUFFSIZE];
1301 char *s, *d;
1302 l = skip_ws (l);
1303
1304 if (Debug)
1305 fprintf (stderr, "%s: handle_docline `%s'\n", Progname, l);
1306
1307 if (!strncmp (l, ".pp", 3))
1308 return print_it (SP_NEWPAR, NULL, out, docstat);
1309 else if (!strncmp (l, ".ts", 3))
1310 return print_it (SP_START_TAB, NULL, out, docstat);
1311 else if (!strncmp (l, ".te", 3))
1312 return print_it (SP_END_TAB, NULL, out, docstat);
1313 else if (!strncmp (l, ".dl", 3))
1314 return print_it (SP_START_DL, NULL, out, docstat);
1315 else if (!strncmp (l, ".de", 3))
1316 return print_it (SP_END_DL, NULL, out, docstat);
1317 else if (!strncmp (l, ".il", 3))
1318 return print_it (SP_START_IL, NULL, out, docstat);
1319 else if (!strncmp (l, ".ie", 3))
1320 return print_it (SP_END_IL, NULL, out, docstat);
1321 else if (!strncmp (l, ". ", 2))
1322 *l = ' ';
1323
1324 for (s = l, d = buff; *s; s++)
1325 {
1326 if (!strncmp (s, "\\(as", 4))
1327 {
1328 *d++ = '*';
1329 s += 3;
1330 }
1331 else if (!strncmp (s, "\\(rs", 4))
1332 {
1333 *d++ = '\\';
1334 s += 3;
1335 }
1336 else if (!strncmp (s, "\\fI", 3))
1337 {
1338 docstat = commit_buff (buff, &d, out, docstat);
1339 docstat = print_it (SP_START_EM, NULL, out, docstat);
1340 s += 2;
1341 }
1342 else if (!strncmp (s, "\\fB", 3))
1343 {
1344 docstat = commit_buff (buff, &d, out, docstat);
1345 docstat = print_it (SP_START_BF, NULL, out, docstat);
1346 s += 2;
1347 }
1348 else if (!strncmp (s, "\\fC", 3))
1349 {
1350 docstat = commit_buff (buff, &d, out, docstat);
1351 docstat = print_it (SP_START_TT, NULL, out, docstat);
1352 s += 2;
1353 }
1354 else if (!strncmp (s, "\\fP", 3))
1355 {
1356 docstat = commit_buff (buff, &d, out, docstat);
1357 docstat = print_it (SP_END_FT, NULL, out, docstat);
1358 s += 2;
1359 }
1360 else if (!strncmp (s, ".dt", 3))
1361 {
1362 if (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_DT, NULL, out, docstat);
1369 s += 3;
1370 }
1371 else if (!strncmp (s, ".dd", 3))
1372 {
1373 if ((docstat & D_IL) && (docstat & D_DD))
1374 {
1375 docstat = commit_buff (buff, &d, out, docstat);
1376 docstat = print_it (SP_END_DD, NULL, out, docstat);
1377 }
1378 docstat = commit_buff (buff, &d, out, docstat);
1379 docstat = print_it (SP_DD, NULL, out, docstat);
1380 s += 3;
1381 }
1382 else if (*s == '$')
1383 {
1384 int output_dollar = 0;
1385 char *ref;
1386 char save;
1387
1388 ++s;
1389 if (*s == '$')
1390 {
1391 output_dollar = 1;
1392 ++s;
1393 }
1394 if (*s == '$')
1395 {
1396 *d++ = '$';
1397 }
1398 else
1399 {
1400 ref = s;
1401 while (isalnum ((unsigned char) *s) || (*s && strchr("-_<>", *s)))
1402 ++s;
1403
1404 docstat = commit_buff (buff, &d, out, docstat);
1405 save = *s;
1406 *s = 0;
1407 print_ref (out, output_dollar, ref);
1408 *s = save;
1409 --s;
1410 }
1411 }
1412 else
1413 *d++ = *s;
1414 }
1415
1416 docstat = commit_buff (buff, &d, out, docstat);
1417 return print_it (SP_NEWLINE, NULL, out, docstat);
1418}