Generate HTML from the texinfo source and include it in the dist.
[pwmd.git] / doc / yat2m.c
blob5dc81bf59f54a78dd0f4463a1f5c40b190096ae1
1 /* yat2m.c - Yet Another Texi 2 Man converter
2 * Copyright (C) 2005 g10 Code GmbH
3 * Copyright (C) 2006, 2008, 2011 Free Software Foundation, Inc.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 This is a simple textinfo to man page converter. It needs some
21 special markup in th e texinfo and tries best to get a create man
22 page. It has been designed for the GnuPG man pages and thus only
23 a few texinfo commands are supported.
25 To use this you need to add the following macros into your texinfo
26 source:
28 @macro manpage {a}
29 @end macro
30 @macro mansect {a}
31 @end macro
32 @macro manpause
33 @end macro
34 @macro mancont
35 @end macro
37 They are used by yat2m to select parts of the Texinfo which should
38 go into the man page. These macros need to be used without leading
39 left space. Processing starts after a "manpage" macro has been
40 seen. "mansect" identifies the section and yat2m make sure to
41 emit the sections in the proper order. Note that @mansect skips
42 the next input line if that line begins with @section, @subsection or
43 @chapheading.
45 To insert verbatim troff markup, the following texinfo code may be
46 used:
48 @ifset manverb
49 .B whateever you want
50 @end ifset
52 alternativly a special comment may be used:
54 @c man:.B whatever you want
56 This is useful in case you need just one line. If you want to
57 include parts only in the man page but keep the texinfo
58 translation you may use:
60 @ifset isman
61 stuff to be rendered only on man pages
62 @end ifset
64 or to exclude stuff from man pages:
66 @ifclear isman
67 stuff not to be rendered on man pages
68 @end ifclear
70 the keyword @section is ignored, however @subsection gets rendered
71 as ".SS". @menu is completely skipped. Several man pages may be
72 extracted from one file, either using the --store or the --select
73 option.
75 If you want to indent tables in the source use this style:
77 @table foo
78 @item
79 @item
80 @table
81 @item
82 @end
83 @end
85 Don't change the indentation within a table and keep the same
86 number of white space at the start of the line. yat2m simply
87 detects the number of white spaces in front of an @item and remove
88 this number of spaces from all following lines until a new @item
89 is found or there are less spaces than for the last @item.
92 #include <stdio.h>
93 #include <stdlib.h>
94 #include <stddef.h>
95 #include <string.h>
96 #include <errno.h>
97 #include <stdarg.h>
98 #include <assert.h>
99 #include <ctype.h>
100 #include <time.h>
103 #define PGM "yat2m"
104 #define VERSION "1.0"
106 /* The maximum length of a line including the linefeed and one extra
107 character. */
108 #define LINESIZE 1024
110 /* Option flags. */
111 static int verbose;
112 static int quiet;
113 static int debug;
114 static const char *opt_source;
115 static const char *opt_release;
116 static const char *opt_select;
117 static const char *opt_include;
118 static int opt_store;
120 /* The only define we understand is -D gpgone. Thus we need a simple
121 boolean tro track it. */
122 static int gpgone_defined;
124 /* Flag to keep track whether any error occurred. */
125 static int any_error;
128 /* Object to keep macro definitions. */
129 struct macro_s
131 struct macro_s *next;
132 char *value; /* Malloced value. */
133 char name[1];
135 typedef struct macro_s *macro_t;
137 /* List of all defined macros. */
138 static macro_t macrolist;
141 /* Object to store one line of content. */
142 struct line_buffer_s
144 struct line_buffer_s *next;
145 int verbatim; /* True if LINE contains verbatim data. The default
146 is Texinfo source. */
147 char *line;
149 typedef struct line_buffer_s *line_buffer_t;
152 /* Object to collect the data of a section. */
153 struct section_buffer_s
155 char *name; /* Malloced name of the section. This may be
156 NULL to indicate this slot is not used. */
157 line_buffer_t lines; /* Linked list with the lines of the section. */
158 line_buffer_t *lines_tail; /* Helper for faster appending to the
159 linked list. */
160 line_buffer_t last_line; /* Points to the last line appended. */
162 typedef struct section_buffer_s *section_buffer_t;
164 /* Variable to keep info about the current page together. */
165 static struct
167 /* Filename of the current page or NULL if no page is active. Malloced. */
168 char *name;
170 /* Number of allocated elements in SECTIONS below. */
171 size_t n_sections;
172 /* Array with the data of the sections. */
173 section_buffer_t sections;
175 } thepage;
178 /* The list of standard section names. COMMANDS and ASSUAN are GnuPG
179 specific. */
180 static const char * const standard_sections[] =
181 { "NAME", "SYNOPSIS", "DESCRIPTION",
182 "RETURN VALUE", "EXIT STATUS", "ERROR HANDLING", "ERRORS",
183 "COMMANDS", "OPTIONS", "USAGE", "EXAMPLES", "FILES",
184 "ENVIRONMENT", "DIAGNOSTICS", "SECURITY", "CONFORMING TO",
185 "ASSUAN", "NOTES", "BUGS", "AUTHOR", "SEE ALSO", NULL };
188 /*-- Local prototypes. --*/
189 static void proc_texi_buffer (FILE *fp, const char *line, size_t len,
190 int *table_level, int *eol_action);
194 /* Print diagnostic message and exit with failure. */
195 static void
196 die (const char *format, ...)
198 va_list arg_ptr;
200 fflush (stdout);
201 fprintf (stderr, "%s: ", PGM);
203 va_start (arg_ptr, format);
204 vfprintf (stderr, format, arg_ptr);
205 va_end (arg_ptr);
206 putc ('\n', stderr);
208 exit (1);
212 /* Print diagnostic message. */
213 static void
214 err (const char *format, ...)
216 va_list arg_ptr;
218 fflush (stdout);
219 if (strncmp (format, "%s:%d:", 6))
220 fprintf (stderr, "%s: ", PGM);
222 va_start (arg_ptr, format);
223 vfprintf (stderr, format, arg_ptr);
224 va_end (arg_ptr);
225 putc ('\n', stderr);
226 any_error = 1;
229 /* Print diagnostic message. */
230 static void
231 inf (const char *format, ...)
233 va_list arg_ptr;
235 fflush (stdout);
236 fprintf (stderr, "%s: ", PGM);
238 va_start (arg_ptr, format);
239 vfprintf (stderr, format, arg_ptr);
240 va_end (arg_ptr);
241 putc ('\n', stderr);
245 static void *
246 xmalloc (size_t n)
248 void *p = malloc (n);
249 if (!p)
250 die ("out of core: %s", strerror (errno));
251 return p;
254 static void *
255 xcalloc (size_t n, size_t m)
257 void *p = calloc (n, m);
258 if (!p)
259 die ("out of core: %s", strerror (errno));
260 return p;
263 static void *
264 xrealloc (void *old, size_t n)
266 void *p = realloc (old, n);
267 if (!p)
268 die ("out of core: %s", strerror (errno));
269 return p;
272 static char *
273 xstrdup (const char *string)
275 void *p = malloc (strlen (string)+1);
276 if (!p)
277 die ("out of core: %s", strerror (errno));
278 strcpy (p, string);
279 return p;
283 /* Uppercase the ascii characters in STRING. */
284 static char *
285 ascii_strupr (char *string)
287 char *p;
289 for (p = string; *p; p++)
290 if (!(*p & 0x80))
291 *p = toupper (*p);
292 return string;
296 /* Return the current date as an ISO string. */
297 const char *
298 isodatestring (void)
300 static char buffer[11+5];
301 struct tm *tp;
302 time_t atime = time (NULL);
304 if (atime < 0)
305 strcpy (buffer, "????" "-??" "-??");
306 else
308 tp = gmtime (&atime);
309 sprintf (buffer,"%04d-%02d-%02d",
310 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
312 return buffer;
317 /* Return a section buffer for the section NAME. Allocate a new buffer
318 if this is a new section. Keep track of the sections in THEPAGE.
319 This function may reallocate the section array in THEPAGE. */
320 static section_buffer_t
321 get_section_buffer (const char *name)
323 int i;
324 section_buffer_t sect;
326 /* If there is no section we put everything into the required NAME
327 section. Given that this is the first one listed it is likely
328 that error are easily visible. */
329 if (!name)
330 name = "NAME";
332 for (i=0; i < thepage.n_sections; i++)
334 sect = thepage.sections + i;
335 if (sect->name && !strcmp (name, sect->name))
336 return sect;
338 for (i=0; i < thepage.n_sections; i++)
339 if (!thepage.sections[i].name)
340 break;
341 if (i < thepage.n_sections)
342 sect = thepage.sections + i;
343 else
345 /* We need to allocate or reallocate the section array. */
346 size_t old_n = thepage.n_sections;
347 size_t new_n = 20;
349 if (!old_n)
350 thepage.sections = xcalloc (new_n, sizeof *thepage.sections);
351 else
353 thepage.sections = xrealloc (thepage.sections,
354 ((old_n + new_n)
355 * sizeof *thepage.sections));
356 memset (thepage.sections + old_n, 0,
357 new_n * sizeof *thepage.sections);
359 thepage.n_sections += new_n;
361 /* Setup the tail pointers. */
362 for (i=old_n; i < thepage.n_sections; i++)
364 sect = thepage.sections + i;
365 sect->lines_tail = &sect->lines;
367 sect = thepage.sections + old_n;
370 /* Store the name. */
371 assert (!sect->name);
372 sect->name = xstrdup (name);
373 return sect;
378 /* Add the content of LINE to the section named SECTNAME. */
379 static void
380 add_content (const char *sectname, char *line, int verbatim)
382 section_buffer_t sect;
383 line_buffer_t lb;
385 sect = get_section_buffer (sectname);
386 if (sect->last_line && !sect->last_line->verbatim == !verbatim)
388 /* Lets append that line to the last one. We do this to keep
389 all lines of the same kind (i.e.verbatim or not) together in
390 one large buffer. */
391 size_t n1, n;
393 lb = sect->last_line;
394 n1 = strlen (lb->line);
395 n = n1 + 1 + strlen (line) + 1;
396 lb->line = xrealloc (lb->line, n);
397 strcpy (lb->line+n1, "\n");
398 strcpy (lb->line+n1+1, line);
400 else
402 lb = xcalloc (1, sizeof *lb);
403 lb->verbatim = verbatim;
404 lb->line = xstrdup (line);
405 sect->last_line = lb;
406 *sect->lines_tail = lb;
407 sect->lines_tail = &lb->next;
412 /* Prepare for a new man page using the filename NAME. */
413 static void
414 start_page (char *name)
416 if (verbose)
417 inf ("starting page '%s'", name);
418 assert (!thepage.name);
419 thepage.name = xstrdup (name);
420 thepage.n_sections = 0;
424 /* Write the .TH entry of the current page. Return -1 if there is a
425 problem with the page. */
426 static int
427 write_th (FILE *fp)
429 char *name, *p;
431 fputs (".\\\" Created from Texinfo source by yat2m " VERSION "\n", fp);
433 name = ascii_strupr (xstrdup (thepage.name));
434 p = strrchr (name, '.');
435 if (!p || !p[1])
437 err ("no section name in man page '%s'", thepage.name);
438 free (name);
439 return -1;
441 *p++ = 0;
442 fprintf (fp, ".TH %s %s %s \"%s\" \"%s\"\n",
443 name, p, isodatestring (), opt_release, opt_source);
444 return 0;
448 /* Process the texinfo command COMMAND (without the leading @) and
449 write output if needed to FP. REST is the remainer of the line
450 which should either point to an opening brace or to a white space.
451 The function returns the number of characters already processed
452 from REST. LEN is the usable length of REST. TABLE_LEVEL is used to
453 control the indentation of tables. */
454 static size_t
455 proc_texi_cmd (FILE *fp, const char *command, const char *rest, size_t len,
456 int *table_level, int *eol_action)
458 static struct {
459 const char *name; /* Name of the command. */
460 int what; /* What to do with this command. */
461 const char *lead_in; /* String to print with a opening brace. */
462 const char *lead_out;/* String to print with the closing brace. */
463 } cmdtbl[] = {
464 { "command", 0, "\\fB", "\\fR" },
465 { "code", 0, "\\fB", "\\fR" },
466 { "sc", 0, "\\fB", "\\fR" },
467 { "var", 0, "\\fI", "\\fR" },
468 { "samp", 0, "\\(aq", "\\(aq" },
469 { "file", 0, "\\(oq\\fI","\\fR\\(cq" },
470 { "env", 0, "\\(oq\\fI","\\fR\\(cq" },
471 { "acronym", 0 },
472 { "dfn", 0 },
473 { "option", 0, "\\fB", "\\fR" },
474 { "example", 1, ".RS 2\n.nf\n" },
475 { "smallexample", 1, ".RS 2\n.nf\n" },
476 { "asis", 7 },
477 { "anchor", 7 },
478 { "cartouche", 1 },
479 { "xref", 0, "see: [", "]" },
480 { "pxref", 0, "see: [", "]" },
481 { "uref", 0, "(\\fB", "\\fR)" },
482 { "footnote",0, " ([", "])" },
483 { "emph", 0, "\\fI", "\\fR" },
484 { "w", 1 },
485 { "c", 5 },
486 { "opindex", 1 },
487 { "cpindex", 1 },
488 { "cindex", 1 },
489 { "noindent", 0 },
490 { "section", 1 },
491 { "chapter", 1 },
492 { "subsection", 6, "\n.SS " },
493 { "chapheading", 0},
494 { "item", 2, ".TP\n.B " },
495 { "itemx", 2, ".TP\n.B " },
496 { "table", 3 },
497 { "itemize", 3 },
498 { "bullet", 0, "* " },
499 { "end", 4 },
500 { "quotation",1, ".RS\n\\fB" },
501 { NULL }
503 size_t n;
504 int i;
505 const char *s;
506 const char *lead_out = NULL;
507 int ignore_args = 0;
509 for (i=0; cmdtbl[i].name && strcmp (cmdtbl[i].name, command); i++)
511 if (cmdtbl[i].name)
513 s = cmdtbl[i].lead_in;
514 if (s)
515 fputs (s, fp);
516 lead_out = cmdtbl[i].lead_out;
517 switch (cmdtbl[i].what)
519 case 1: /* Throw away the entire line. */
520 s = memchr (rest, '\n', len);
521 return s? (s-rest)+1 : len;
522 case 2: /* Handle @item. */
523 break;
524 case 3: /* Handle table. */
525 if (++(*table_level) > 1)
526 fputs (".RS\n", fp);
527 /* Now throw away the entire line. */
528 s = memchr (rest, '\n', len);
529 return s? (s-rest)+1 : len;
530 break;
531 case 4: /* Handle end. */
532 for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
534 if (n >= 5 && !memcmp (s, "table", 5)
535 && (!n || s[5] == ' ' || s[5] == '\t' || s[5] == '\n'))
537 if ((*table_level)-- > 1)
538 fputs (".RE\n", fp);
540 else if (n >= 7 && !memcmp (s, "example", 7)
541 && (!n || s[7] == ' ' || s[7] == '\t' || s[7] == '\n'))
543 fputs (".fi\n.RE\n", fp);
545 else if (n >= 12 && !memcmp (s, "smallexample", 12)
546 && (!n || s[12] == ' ' || s[12] == '\t' || s[12] == '\n'))
548 fputs (".fi\n.RE\n", fp);
550 else if (n >= 9 && !memcmp (s, "quotation", 9)
551 && (!n || s[9] == ' ' || s[9] == '\t' || s[9] == '\n'))
553 fputs ("\\fR\n.RE\n", fp);
555 /* Now throw away the entire line. */
556 s = memchr (rest, '\n', len);
557 return s? (s-rest)+1 : len;
558 case 5: /* Handle special comments. */
559 for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
561 if (n >= 4 && !memcmp (s, "man:", 4))
563 for (s+=4, n-=4; n && *s != '\n'; n--, s++)
564 putc (*s, fp);
565 putc ('\n', fp);
567 /* Now throw away the entire line. */
568 s = memchr (rest, '\n', len);
569 return s? (s-rest)+1 : len;
570 case 6:
571 *eol_action = 1;
572 break;
573 case 7:
574 ignore_args = 1;
575 break;
576 default:
577 break;
580 else
582 macro_t m;
584 for (m = macrolist; m ; m = m->next)
585 if (!strcmp (m->name, command))
586 break;
587 if (m)
589 proc_texi_buffer (fp, m->value, strlen (m->value),
590 table_level, eol_action);
591 ignore_args = 1; /* Parameterized macros are not yet supported. */
593 else
594 inf ("texinfo command '%s' not supported (%.*s)", command,
595 ((s = memchr (rest, '\n', len)), (s? (s-rest) : len)), rest);
598 if (*rest == '{')
600 /* Find matching closing brace. */
601 for (s=rest+1, n=1, i=1; i && *s && n < len; s++, n++)
602 if (*s == '{')
603 i++;
604 else if (*s == '}')
605 i--;
606 if (i)
608 err ("closing brace for command '%s' not found", command);
609 return len;
611 if (n > 2 && !ignore_args)
612 proc_texi_buffer (fp, rest+1, n-2, table_level, eol_action);
614 else
615 n = 0;
617 if (lead_out)
618 fputs (lead_out, fp);
620 return n;
625 /* Process the string LINE with LEN bytes of Texinfo content. */
626 static void
627 proc_texi_buffer (FILE *fp, const char *line, size_t len,
628 int *table_level, int *eol_action)
630 const char *s;
631 char cmdbuf[256];
632 int cmdidx = 0;
633 int in_cmd = 0;
634 size_t n;
636 for (s=line; *s && len; s++, len--)
638 if (in_cmd)
640 if (in_cmd == 1)
642 switch (*s)
644 case '@': case '{': case '}':
645 putc (*s, fp); in_cmd = 0;
646 break;
647 case ':': /* Not ending a sentence flag. */
648 in_cmd = 0;
649 break;
650 case '.': case '!': case '?': /* Ending a sentence. */
651 putc (*s, fp); in_cmd = 0;
652 break;
653 case ' ': case '\t': case '\n': /* Non collapsing spaces. */
654 putc (*s, fp); in_cmd = 0;
655 break;
656 default:
657 cmdidx = 0;
658 cmdbuf[cmdidx++] = *s;
659 in_cmd++;
660 break;
663 else if (*s == '{' || *s == ' ' || *s == '\t' || *s == '\n')
665 cmdbuf[cmdidx] = 0;
666 n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
667 assert (n <= len);
668 s += n; len -= n;
669 s--; len++;
670 in_cmd = 0;
672 else if (cmdidx < sizeof cmdbuf -1)
673 cmdbuf[cmdidx++] = *s;
674 else
676 err ("texinfo command too long - ignored");
677 in_cmd = 0;
680 else if (*s == '@')
681 in_cmd = 1;
682 else if (*s == '\n')
684 switch (*eol_action)
686 case 1: /* Create a dummy paragraph. */
687 fputs ("\n\\ \n", fp);
688 break;
689 default:
690 putc (*s, fp);
692 *eol_action = 0;
694 else if (*s == '\\')
695 fputs ("\\\\", fp);
696 else
697 putc (*s, fp);
700 if (in_cmd > 1)
702 cmdbuf[cmdidx] = 0;
703 n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
704 assert (n <= len);
705 s += n; len -= n;
706 s--; len++;
707 in_cmd = 0;
712 /* Do something with the Texinfo line LINE. */
713 static void
714 parse_texi_line (FILE *fp, const char *line, int *table_level)
716 int eol_action = 0;
718 /* A quick test whether there are any texinfo commands. */
719 if (!strchr (line, '@'))
721 fputs (line, fp);
722 putc ('\n', fp);
723 return;
725 proc_texi_buffer (fp, line, strlen (line), table_level, &eol_action);
726 putc ('\n', fp);
730 /* Write all the lines LINES to FP. */
731 static void
732 write_content (FILE *fp, line_buffer_t lines)
734 line_buffer_t line;
735 int table_level = 0;
737 for (line = lines; line; line = line->next)
739 if (line->verbatim)
741 fputs (line->line, fp);
742 putc ('\n', fp);
744 else
746 /* fputs ("TEXI---", fp); */
747 /* fputs (line->line, fp); */
748 /* fputs ("---\n", fp); */
749 parse_texi_line (fp, line->line, &table_level);
756 static int
757 is_standard_section (const char *name)
759 int i;
760 const char *s;
762 for (i=0; (s=standard_sections[i]); i++)
763 if (!strcmp (s, name))
764 return 1;
765 return 0;
769 /* Finish a page; that is sort the data and write it out to the file. */
770 static void
771 finish_page (void)
773 FILE *fp;
774 section_buffer_t sect = NULL;
775 int idx;
776 const char *s;
777 int i;
779 if (!thepage.name)
780 return; /* No page active. */
782 if (verbose)
783 inf ("finishing page '%s'", thepage.name);
785 if (opt_select)
787 if (!strcmp (opt_select, thepage.name))
789 inf ("selected '%s'", thepage.name );
790 fp = stdout;
792 else
794 fp = fopen ( "/dev/null", "w" );
795 if (!fp)
796 die ("failed to open /dev/null: %s\n", strerror (errno));
799 else if (opt_store)
801 inf ("writing '%s'", thepage.name );
802 fp = fopen ( thepage.name, "w" );
803 if (!fp)
804 die ("failed to create '%s': %s\n", thepage.name, strerror (errno));
806 else
807 fp = stdout;
809 if (write_th (fp))
810 goto leave;
812 for (idx=0; (s=standard_sections[idx]); idx++)
814 for (i=0; i < thepage.n_sections; i++)
816 sect = thepage.sections + i;
817 if (sect->name && !strcmp (s, sect->name))
818 break;
820 if (i == thepage.n_sections)
821 sect = NULL;
823 if (sect)
825 fprintf (fp, ".SH %s\n", sect->name);
826 write_content (fp, sect->lines);
827 /* Now continue with all non standard sections directly
828 following this one. */
829 for (i++; i < thepage.n_sections; i++)
831 sect = thepage.sections + i;
832 if (sect->name && is_standard_section (sect->name))
833 break;
834 if (sect->name)
836 fprintf (fp, ".SH %s\n", sect->name);
837 write_content (fp, sect->lines);
845 leave:
846 if (fp != stdout)
847 fclose (fp);
848 free (thepage.name);
849 thepage.name = NULL;
850 /* FIXME: Cleanup the content. */
856 /* Parse one Texinfo file and create manpages according to the
857 embedded instructions. */
858 static void
859 parse_file (const char *fname, FILE *fp, char **section_name, int in_pause)
861 char *line;
862 int lnr = 0;
863 /* Fixme: The following state variables don't carry over to include
864 files. */
865 int in_verbatim = 0;
866 int skip_to_end = 0; /* Used to skip over menu entries. */
867 int skip_sect_line = 0; /* Skip after @mansect. */
868 int ifset_nesting = 0; /* How often a ifset has been seen. */
869 int ifclear_nesting = 0; /* How often a ifclear has been seen. */
870 int in_gpgone = 0; /* Keep track of "@ifset gpgone" parts. */
871 int not_in_gpgone = 0; /* Keep track of "@ifclear gpgone" parts. */
872 int not_in_man = 0; /* Keep track of "@ifclear isman" parts. */
873 int item_indent = 0; /* How far is the current @item indented. */
875 /* Helper to define a macro. */
876 char *macroname = NULL;
877 char *macrovalue = NULL;
878 size_t macrovaluesize = 0;
879 size_t macrovalueused = 0;
881 line = xmalloc (LINESIZE);
882 while (fgets (line, LINESIZE, fp))
884 size_t n = strlen (line);
885 int got_line = 0;
886 char *p;
888 lnr++;
889 if (!n || line[n-1] != '\n')
891 err ("%s:%d: trailing linefeed missing, line too long or "
892 "embedded Nul character", fname, lnr);
893 break;
895 line[--n] = 0;
897 /* Kludge to allow indentation of tables. */
898 for (p=line; *p == ' ' || *p == '\t'; p++)
900 if (*p)
902 if (*p == '@' && !strncmp (p+1, "item", 4))
903 item_indent = p - line; /* Set a new indent level. */
904 else if (p - line < item_indent)
905 item_indent = 0; /* Switch off indention. */
907 if (item_indent)
909 memmove (line, line+item_indent, n - item_indent + 1);
910 n -= item_indent;
915 if (*line == '@')
917 for (p=line+1, n=1; *p && *p != ' ' && *p != '\t'; p++)
918 n++;
919 while (*p == ' ' || *p == '\t')
920 p++;
922 else
923 p = line;
925 /* Take action on macro. */
926 if (macroname)
928 if (n == 4 && !memcmp (line, "@end", 4)
929 && (line[4]==' '||line[4]=='\t'||!line[4])
930 && !strncmp (p, "macro", 5)
931 && (p[5]==' '||p[5]=='\t'||!p[5]))
933 macro_t m;
935 if (macrovalueused)
936 macrovalue[--macrovalueused] = 0; /* Kill the last LF. */
937 macrovalue[macrovalueused] = 0; /* Terminate macro. */
938 macrovalue = xrealloc (macrovalue, macrovalueused+1);
940 for (m= macrolist; m; m = m->next)
941 if (!strcmp (m->name, macroname))
942 break;
943 if (m)
944 free (m->value);
945 else
947 m = xcalloc (1, sizeof *m + strlen (macroname));
948 strcpy (m->name, macroname);
949 m->next = macrolist;
950 macrolist = m;
952 m->value = macrovalue;
953 macrovalue = NULL;
954 free (macroname);
955 macroname = NULL;
957 else
959 if (macrovalueused + strlen (line) + 2 >= macrovaluesize)
961 macrovaluesize += strlen (line) + 256;
962 macrovalue = xrealloc (macrovalue, macrovaluesize);
964 strcpy (macrovalue+macrovalueused, line);
965 macrovalueused += strlen (line);
966 macrovalue[macrovalueused++] = '\n';
968 continue;
972 if (n >= 5 && !memcmp (line, "@node", 5)
973 && (line[5]==' '||line[5]=='\t'||!line[5]))
975 /* Completey ignore @node lines. */
976 continue;
980 if (skip_sect_line)
982 skip_sect_line = 0;
983 if (!strncmp (line, "@section", 8)
984 || !strncmp (line, "@subsection", 11)
985 || !strncmp (line, "@chapheading", 12))
986 continue;
989 /* We only parse lines we need and ignore the rest. There are a
990 few macros used to control this as well as one @ifset
991 command. Parts we know about are saved away into containers
992 separate for each section. */
994 /* First process ifset/ifclear commands. */
995 if (*line == '@')
997 if (n == 6 && !memcmp (line, "@ifset", 6)
998 && (line[6]==' '||line[6]=='\t'))
1000 ifset_nesting++;
1002 if (!strncmp (p, "manverb", 7) && (p[7]==' '||p[7]=='\t'||!p[7]))
1004 if (in_verbatim)
1005 err ("%s:%d: nested \"@ifset manverb\"", fname, lnr);
1006 else
1007 in_verbatim = ifset_nesting;
1009 else if (!strncmp (p, "gpgone", 6)
1010 && (p[6]==' '||p[6]=='\t'||!p[6]))
1012 if (in_gpgone)
1013 err ("%s:%d: nested \"@ifset gpgone\"", fname, lnr);
1014 else
1015 in_gpgone = ifset_nesting;
1017 continue;
1019 else if (n == 4 && !memcmp (line, "@end", 4)
1020 && (line[4]==' '||line[4]=='\t')
1021 && !strncmp (p, "ifset", 5)
1022 && (p[5]==' '||p[5]=='\t'||!p[5]))
1024 if (in_verbatim && ifset_nesting == in_verbatim)
1025 in_verbatim = 0;
1026 if (in_gpgone && ifset_nesting == in_gpgone)
1027 in_gpgone = 0;
1029 if (ifset_nesting)
1030 ifset_nesting--;
1031 else
1032 err ("%s:%d: unbalanced \"@end ifset\"", fname, lnr);
1033 continue;
1035 else if (n == 8 && !memcmp (line, "@ifclear", 8)
1036 && (line[8]==' '||line[8]=='\t'))
1038 ifclear_nesting++;
1040 if (!strncmp (p, "gpgone", 6)
1041 && (p[6]==' '||p[6]=='\t'||!p[6]))
1043 if (not_in_gpgone)
1044 err ("%s:%d: nested \"@ifclear gpgone\"", fname, lnr);
1045 else
1046 not_in_gpgone = ifclear_nesting;
1049 else if (!strncmp (p, "isman", 5)
1050 && (p[5]==' '||p[5]=='\t'||!p[5]))
1052 if (not_in_man)
1053 err ("%s:%d: nested \"@ifclear isman\"", fname, lnr);
1054 else
1055 not_in_man = ifclear_nesting;
1058 continue;
1060 else if (n == 4 && !memcmp (line, "@end", 4)
1061 && (line[4]==' '||line[4]=='\t')
1062 && !strncmp (p, "ifclear", 7)
1063 && (p[7]==' '||p[7]=='\t'||!p[7]))
1065 if (not_in_gpgone && ifclear_nesting == not_in_gpgone)
1066 not_in_gpgone = 0;
1067 if (not_in_man && ifclear_nesting == not_in_man)
1068 not_in_man = 0;
1070 if (ifclear_nesting)
1071 ifclear_nesting--;
1072 else
1073 err ("%s:%d: unbalanced \"@end ifclear\"", fname, lnr);
1074 continue;
1078 /* Take action on ifset/ifclear. */
1079 if ( (in_gpgone && !gpgone_defined)
1080 || (not_in_gpgone && gpgone_defined)
1081 || not_in_man)
1082 continue;
1084 /* Process commands. */
1085 if (*line == '@')
1087 if (skip_to_end
1088 && n == 4 && !memcmp (line, "@end", 4)
1089 && (line[4]==' '||line[4]=='\t'||!line[4]))
1091 skip_to_end = 0;
1093 else if (in_verbatim)
1095 got_line = 1;
1097 else if (n == 6 && !memcmp (line, "@macro", 6))
1099 macroname = xstrdup (p);
1100 macrovalue = xmalloc ((macrovaluesize = 1024));
1101 macrovalueused = 0;
1103 else if (n == 8 && !memcmp (line, "@manpage", 8))
1105 free (*section_name);
1106 *section_name = NULL;
1107 finish_page ();
1108 start_page (p);
1109 in_pause = 0;
1111 else if (n == 8 && !memcmp (line, "@mansect", 8))
1113 if (!thepage.name)
1114 err ("%s:%d: section outside of a man page", fname, lnr);
1115 else
1117 free (*section_name);
1118 *section_name = ascii_strupr (xstrdup (p));
1119 in_pause = 0;
1120 skip_sect_line = 1;
1123 else if (n == 9 && !memcmp (line, "@manpause", 9))
1125 if (!*section_name)
1126 err ("%s:%d: pausing outside of a man section", fname, lnr);
1127 else if (in_pause)
1128 err ("%s:%d: already pausing", fname, lnr);
1129 else
1130 in_pause = 1;
1132 else if (n == 8 && !memcmp (line, "@mancont", 8))
1134 if (!*section_name)
1135 err ("%s:%d: continue outside of a man section", fname, lnr);
1136 else if (!in_pause)
1137 err ("%s:%d: continue while not pausing", fname, lnr);
1138 else
1139 in_pause = 0;
1141 else if (n == 5 && !memcmp (line, "@menu", 5)
1142 && (line[5]==' '||line[5]=='\t'||!line[5]))
1144 skip_to_end = 1;
1146 else if (n == 8 && !memcmp (line, "@include", 8)
1147 && (line[8]==' '||line[8]=='\t'||!line[8]))
1149 char *incname = xstrdup (p);
1150 FILE *incfp = fopen (incname, "r");
1152 if (!incfp && opt_include && *opt_include && *p != '/')
1154 free (incname);
1155 incname = xmalloc (strlen (opt_include) + 1
1156 + strlen (p) + 1);
1157 strcpy (incname, opt_include);
1158 if ( incname[strlen (incname)-1] != '/' )
1159 strcat (incname, "/");
1160 strcat (incname, p);
1161 incfp = fopen (incname, "r");
1164 if (!incfp)
1165 err ("can't open include file '%s':%s",
1166 incname, strerror (errno));
1167 else
1169 parse_file (incname, incfp, section_name, in_pause);
1170 fclose (incfp);
1172 free (incname);
1174 else if (n == 4 && !memcmp (line, "@bye", 4)
1175 && (line[4]==' '||line[4]=='\t'||!line[4]))
1177 break;
1179 else if (!skip_to_end)
1180 got_line = 1;
1182 else if (!skip_to_end)
1183 got_line = 1;
1185 if (got_line && in_verbatim)
1186 add_content (*section_name, line, 1);
1187 else if (got_line && thepage.name && *section_name && !in_pause)
1188 add_content (*section_name, line, 0);
1191 if (ferror (fp))
1192 err ("%s:%d: read error: %s", fname, lnr, strerror (errno));
1193 free (macroname);
1194 free (macrovalue);
1195 free (line);
1199 static void
1200 top_parse_file (const char *fname, FILE *fp)
1202 char *section_name = NULL; /* Name of the current section or NULL
1203 if not in a section. */
1204 while (macrolist)
1206 macro_t next = macrolist->next;
1207 free (macrolist->value);
1208 free (macrolist);
1209 macrolist = next;
1212 parse_file (fname, fp, &section_name, 0);
1213 free (section_name);
1214 finish_page ();
1219 main (int argc, char **argv)
1221 int last_argc = -1;
1223 opt_source = "GNU";
1224 opt_release = "";
1226 if (argc)
1228 argc--; argv++;
1230 while (argc && last_argc != argc )
1232 last_argc = argc;
1233 if (!strcmp (*argv, "--"))
1235 argc--; argv++;
1236 break;
1238 else if (!strcmp (*argv, "--help"))
1240 puts (
1241 "Usage: " PGM " [OPTION] [FILE]\n"
1242 "Extract man pages from a Texinfo source.\n\n"
1243 " --source NAME use NAME as source field\n"
1244 " --release STRING use STRING as the release field\n"
1245 " --store write output using @manpage name\n"
1246 " --select NAME only output pages with @manpage NAME\n"
1247 " --verbose enable extra informational output\n"
1248 " --debug enable additional debug output\n"
1249 " --help display this help and exit\n"
1250 " -I DIR also search in include DIR\n"
1251 " -D gpgone the only useable define\n\n"
1252 "With no FILE, or when FILE is -, read standard input.\n\n"
1253 "Report bugs to <bugs@g10code.com>.");
1254 exit (0);
1256 else if (!strcmp (*argv, "--version"))
1258 puts (PGM " " VERSION "\n"
1259 "Copyright (C) 2005 g10 Code GmbH\n"
1260 "This program comes with ABSOLUTELY NO WARRANTY.\n"
1261 "This is free software, and you are welcome to redistribute it\n"
1262 "under certain conditions. See the file COPYING for details.");
1263 exit (0);
1265 else if (!strcmp (*argv, "--verbose"))
1267 verbose = 1;
1268 argc--; argv++;
1270 else if (!strcmp (*argv, "--quiet"))
1272 quiet = 1;
1273 argc--; argv++;
1275 else if (!strcmp (*argv, "--debug"))
1277 verbose = debug = 1;
1278 argc--; argv++;
1280 else if (!strcmp (*argv, "--source"))
1282 argc--; argv++;
1283 if (argc)
1285 opt_source = *argv;
1286 argc--; argv++;
1289 else if (!strcmp (*argv, "--release"))
1291 argc--; argv++;
1292 if (argc)
1294 opt_release = *argv;
1295 argc--; argv++;
1298 else if (!strcmp (*argv, "--store"))
1300 opt_store = 1;
1301 argc--; argv++;
1303 else if (!strcmp (*argv, "--select"))
1305 argc--; argv++;
1306 if (argc)
1308 opt_select = strrchr (*argv, '/');
1309 if (opt_select)
1310 opt_select++;
1311 else
1312 opt_select = *argv;
1313 argc--; argv++;
1316 else if (!strcmp (*argv, "-I"))
1318 argc--; argv++;
1319 if (argc)
1321 opt_include = *argv;
1322 argc--; argv++;
1325 else if (!strcmp (*argv, "-D"))
1327 argc--; argv++;
1328 if (argc)
1330 if (!strcmp (*argv, "gpgone"))
1331 gpgone_defined = 1;
1332 argc--; argv++;
1337 if (argc > 1)
1338 die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
1340 /* Start processing. */
1341 if (argc && strcmp (*argv, "-"))
1343 FILE *fp = fopen (*argv, "rb");
1344 if (!fp)
1345 die ("%s:0: can't open file: %s", *argv, strerror (errno));
1346 top_parse_file (*argv, fp);
1347 fclose (fp);
1349 else
1350 top_parse_file ("-", stdin);
1352 return !!any_error;
1357 Local Variables:
1358 compile-command: "gcc -Wall -g -Wall -o yat2m yat2m.c"
1359 End: