1 /* yat2m.c - Yet Another Texi 2 Man converter
2 * Copyright (C) 2005, 2013, 2015, 2016, 2017 g10 Code GmbH
3 * Copyright (C) 2006, 2008, 2011 Free Software Foundation, Inc.
4 *
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.
9 *
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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <https://www.gnu.org/licenses/>.
17 */
18
19 /*
20 This is a simple texinfo 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.
24
25 To use this you need to add the following macros into your texinfo
26 source:
27
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
36
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.
44
45 To insert verbatim troff markup, the following texinfo code may be
46 used:
47
48 @ifset manverb
49 .B whateever you want
50 @end ifset
51
52 alternatively a special comment may be used:
53
54 @c man:.B whatever you want
55
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:
59
60 @ifset isman
61 stuff to be rendered only on man pages
62 @end ifset
63
64 or to exclude stuff from man pages:
65
66 @ifclear isman
67 stuff not to be rendered on man pages
68 @end ifclear
69
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.
74
75 If you want to indent tables in the source use this style:
76
77 @table foo
78 @item
79 @item
80 @table
81 @item
82 @end
83 @end
84
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.
90
91 Note that @* does only work correctly if used at the end of an
92 input line.
93
94 */
95
96 #include <stdio.h>
97 #include <stdlib.h>
98 #include <stddef.h>
99 #include <string.h>
100 #include <errno.h>
101 #include <stdarg.h>
102 #include <assert.h>
103 #include <ctype.h>
104 #include <time.h>
105
106
107 #if __GNUC__
108 # define MY_GCC_VERSION (__GNUC__ * 10000 \
109 + __GNUC_MINOR__ * 100 \
110 + __GNUC_PATCHLEVEL__)
111 #else
112 # define MY_GCC_VERSION 0
113 #endif
114
115 #if MY_GCC_VERSION >= 20500
116 # define ATTR_PRINTF(f, a) __attribute__ ((format(printf,f,a)))
117 # define ATTR_NR_PRINTF(f, a) __attribute__ ((noreturn, format(printf,f,a)))
118 #else
119 # define ATTR_PRINTF(f, a)
120 # define ATTR_NR_PRINTF(f, a)
121 #endif
122 #if MY_GCC_VERSION >= 30200
123 # define ATTR_MALLOC __attribute__ ((__malloc__))
124 #else
125 # define ATTR_MALLOC
126 #endif
127
128
129
130 #define PGM "yat2m"
131 #ifdef PACKAGE_VERSION
132 # define VERSION PACKAGE_VERSION
133 #else
134 # define VERSION "1.0"
135 #endif
136
137 /* The maximum length of a line including the linefeed and one extra
138 character. */
139 #define LINESIZE 1024
140
141 /* Number of allowed condition nestings. */
142 #define MAX_CONDITION_NESTING 10
143
144 static char const default_css[] =
145 "<style type=\"text/css\">\n"
146 " .y2m {\n"
147 " font-family: monospace;\n"
148 " }\n"
149 " .y2m u {\n"
150 " text-decoration: underline;\n"
151 " }\n"
152 " .y2m-sc {\n"
153 " font-variant: small-caps;\n"
154 " }\n"
155 " .y2m li {\n"
156 " margin-top: 1em;\n"
157 " }\n"
158 " .y2m-item {\n"
159 " display: block;\n"
160 " font-weight: bold;\n"
161 " }\n"
162 " .y2m-args {\n"
163 " font-weight: normal;\n"
164 " }\n"
165 "</style>\n";
166
167
168
169 /* Option flags. */
170 static int verbose;
171 static int quiet;
172 static int debug;
173 static int htmlmode;
174 static const char *opt_source;
175 static const char *opt_release;
176 static const char *opt_date;
177 static const char *opt_select;
178 static const char *opt_include;
179 static int opt_store;
180
181 /* Flag to keep track whether any error occurred. */
182 static int any_error;
183
184
185 /* Object to keep macro definitions. */
186 struct macro_s
187 {
188 struct macro_s *next;
189 char *value; /* Malloced value. */
190 char name[1];
191 };
192 typedef struct macro_s *macro_t;
193
194 /* List of all defined macros. */
195 static macro_t macrolist;
196
197 /* List of variables set by @set. */
198 static macro_t variablelist;
199
200 /* List of global macro names. The value part is not used. */
201 static macro_t predefinedmacrolist;
202
203 /* Object to keep track of @isset and @ifclear. */
204 struct condition_s
205 {
206 int manverb; /* "manverb" needs special treatment. */
207 int isset; /* This is an @isset condition. */
208 char name[1]; /* Name of the condition macro. */
209 };
210 typedef struct condition_s *condition_t;
211
212 /* The stack used to evaluate conditions. And the current states. */
213 static condition_t condition_stack[MAX_CONDITION_NESTING];
214 static int condition_stack_idx;
215 static int cond_is_active; /* State of ifset/ifclear */
216 static int cond_in_verbatim; /* State of "manverb". */
217
218
219 /* Object to store one line of content. */
220 struct line_buffer_s
221 {
222 struct line_buffer_s *next;
223 int verbatim; /* True if LINE contains verbatim data. The default
224 is Texinfo source. */
225 char *line;
226 };
227 typedef struct line_buffer_s *line_buffer_t;
228
229
230 /* Object to collect the data of a section. */
231 struct section_buffer_s
232 {
233 char *name; /* Malloced name of the section. This may be
234 NULL to indicate this slot is not used. */
235 line_buffer_t lines; /* Linked list with the lines of the section. */
236 line_buffer_t *lines_tail; /* Helper for faster appending to the
237 linked list. */
238 line_buffer_t last_line; /* Points to the last line appended. */
239 };
240 typedef struct section_buffer_s *section_buffer_t;
241
242 /* Variable to keep info about the current page together. */
243 static struct
244 {
245 /* Filename of the current page or NULL if no page is active. Malloced. */
246 char *name;
247
248 /* Number of allocated elements in SECTIONS below. */
249 size_t n_sections;
250 /* Array with the data of the sections. */
251 section_buffer_t sections;
252
253 } thepage;
254
255
256 /* The list of standard section names. COMMANDS and ASSUAN are GnuPG
257 specific. */
258 static const char * const standard_sections[] =
259 { "NAME", "SYNOPSIS", "DESCRIPTION",
260 "RETURN VALUE", "EXIT STATUS", "ERROR HANDLING", "ERRORS",
261 "COMMANDS", "OPTIONS", "USAGE", "EXAMPLES", "FILES",
262 "ENVIRONMENT", "DIAGNOSTICS", "SECURITY", "CONFORMING TO",
263 "ASSUAN", "NOTES", "BUGS", "AUTHOR", "SEE ALSO", NULL };
264
265
266 /*-- Local prototypes. --*/
267 static void proc_texi_buffer (FILE *fp, const char *line, size_t len,
268 int *table_level, int *eol_action);
269
270 static void die (const char *format, ...) ATTR_NR_PRINTF(1,2);
271 static void err (const char *format, ...) ATTR_PRINTF(1,2);
272 static void inf (const char *format, ...) ATTR_PRINTF(1,2);
273 static void *xmalloc (size_t n) ATTR_MALLOC;
274 static void *xcalloc (size_t n, size_t m) ATTR_MALLOC;
275
276
277
278 /*-- Functions --*/
279
280 /* Print diagnostic message and exit with failure. */
281 static void
die(const char * format,...)282 die (const char *format, ...)
283 {
284 va_list arg_ptr;
285
286 fflush (stdout);
287 fprintf (stderr, "%s: ", PGM);
288
289 va_start (arg_ptr, format);
290 vfprintf (stderr, format, arg_ptr);
291 va_end (arg_ptr);
292 putc ('\n', stderr);
293
294 exit (1);
295 }
296
297
298 /* Print diagnostic message. */
299 static void
err(const char * format,...)300 err (const char *format, ...)
301 {
302 va_list arg_ptr;
303
304 fflush (stdout);
305 if (strncmp (format, "%s:%d:", 6))
306 fprintf (stderr, "%s: ", PGM);
307
308 va_start (arg_ptr, format);
309 vfprintf (stderr, format, arg_ptr);
310 va_end (arg_ptr);
311 putc ('\n', stderr);
312 any_error = 1;
313 }
314
315 /* Print diagnostic message. */
316 static void
inf(const char * format,...)317 inf (const char *format, ...)
318 {
319 va_list arg_ptr;
320
321 fflush (stdout);
322 fprintf (stderr, "%s: ", PGM);
323
324 va_start (arg_ptr, format);
325 vfprintf (stderr, format, arg_ptr);
326 va_end (arg_ptr);
327 putc ('\n', stderr);
328 }
329
330
331 static void *
xmalloc(size_t n)332 xmalloc (size_t n)
333 {
334 void *p = malloc (n);
335 if (!p)
336 die ("out of core: %s", strerror (errno));
337 return p;
338 }
339
340 static void *
xcalloc(size_t n,size_t m)341 xcalloc (size_t n, size_t m)
342 {
343 void *p = calloc (n, m);
344 if (!p)
345 die ("out of core: %s", strerror (errno));
346 return p;
347 }
348
349 static void *
xrealloc(void * old,size_t n)350 xrealloc (void *old, size_t n)
351 {
352 void *p = realloc (old, n);
353 if (!p)
354 die ("out of core: %s", strerror (errno));
355 return p;
356 }
357
358 static char *
xstrdup(const char * string)359 xstrdup (const char *string)
360 {
361 void *p = malloc (strlen (string)+1);
362 if (!p)
363 die ("out of core: %s", strerror (errno));
364 strcpy (p, string);
365 return p;
366 }
367
368
369 /* Uppercase the ascii characters in STRING. */
370 static char *
ascii_strupr(char * string)371 ascii_strupr (char *string)
372 {
373 char *p;
374
375 for (p = string; *p; p++)
376 if (!(*p & 0x80))
377 *p = toupper (*p);
378 return string;
379 }
380
381
382 /* Return the current date as an ISO string. */
383 const char *
isodatestring(void)384 isodatestring (void)
385 {
386 static char buffer[36];
387 struct tm *tp;
388 time_t atime;
389
390 if (opt_date && *opt_date)
391 atime = strtoul (opt_date, NULL, 10);
392 else
393 atime = time (NULL);
394 if (atime < 0)
395 strcpy (buffer, "????" "-??" "-??");
396 else
397 {
398 tp = gmtime (&atime);
399 sprintf (buffer,"%04d-%02d-%02d",
400 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
401 }
402 return buffer;
403 }
404
405
406 /* Add NAME to the list of predefined macros which are global for all
407 files. */
408 static void
add_predefined_macro(const char * name)409 add_predefined_macro (const char *name)
410 {
411 macro_t m;
412
413 for (m=predefinedmacrolist; m; m = m->next)
414 if (!strcmp (m->name, name))
415 break;
416 if (!m)
417 {
418 m = xcalloc (1, sizeof *m + strlen (name));
419 strcpy (m->name, name);
420 m->next = predefinedmacrolist;
421 predefinedmacrolist = m;
422 }
423 }
424
425
426 /* Create or update a macro with name MACRONAME and set its values TO
427 MACROVALUE. Note that ownership of the macro value is transferred
428 to this function. */
429 static void
set_macro(const char * macroname,char * macrovalue)430 set_macro (const char *macroname, char *macrovalue)
431 {
432 macro_t m;
433
434 for (m=macrolist; m; m = m->next)
435 if (!strcmp (m->name, macroname))
436 break;
437 if (m)
438 free (m->value);
439 else
440 {
441 m = xcalloc (1, sizeof *m + strlen (macroname));
442 strcpy (m->name, macroname);
443 m->next = macrolist;
444 macrolist = m;
445 }
446 m->value = macrovalue;
447 macrovalue = NULL;
448 }
449
450
451 /* Create or update a variable with name and value given in NAMEANDVALUE. */
452 static void
set_variable(char * nameandvalue)453 set_variable (char *nameandvalue)
454 {
455 macro_t m;
456 const char *value;
457 char *p;
458
459 for (p = nameandvalue; *p && *p != ' ' && *p != '\t'; p++)
460 ;
461 if (!*p)
462 value = "";
463 else
464 {
465 *p++ = 0;
466 while (*p == ' ' || *p == '\t')
467 p++;
468 value = p;
469 }
470
471 for (m=variablelist; m; m = m->next)
472 if (!strcmp (m->name, nameandvalue))
473 break;
474 if (m)
475 free (m->value);
476 else
477 {
478 m = xcalloc (1, sizeof *m + strlen (nameandvalue));
479 strcpy (m->name, nameandvalue);
480 m->next = variablelist;
481 variablelist = m;
482 }
483 m->value = xstrdup (value);
484 }
485
486
487 /* Return true if the macro or variable NAME is set, i.e. not the
488 empty string and not evaluating to 0. */
489 static int
macro_set_p(const char * name)490 macro_set_p (const char *name)
491 {
492 macro_t m;
493
494 for (m = macrolist; m ; m = m->next)
495 if (!strcmp (m->name, name))
496 break;
497 if (!m)
498 for (m = variablelist; m ; m = m->next)
499 if (!strcmp (m->name, name))
500 break;
501 if (!m || !m->value || !*m->value)
502 return 0;
503 if ((*m->value & 0x80) || !isdigit (*m->value))
504 return 1; /* Not a digit but some other string. */
505 return !!atoi (m->value);
506 }
507
508
509 /* Evaluate the current conditions. */
510 static void
evaluate_conditions(const char * fname,int lnr)511 evaluate_conditions (const char *fname, int lnr)
512 {
513 int i;
514
515 (void)fname;
516 (void)lnr;
517
518 /* for (i=0; i < condition_stack_idx; i++) */
519 /* inf ("%s:%d: stack[%d] %s %s %c", */
520 /* fname, lnr, i, condition_stack[i]->isset? "set":"clr", */
521 /* condition_stack[i]->name, */
522 /* (macro_set_p (condition_stack[i]->name) */
523 /* ^ !condition_stack[i]->isset)? 't':'f'); */
524
525 cond_is_active = 1;
526 cond_in_verbatim = 0;
527 if (condition_stack_idx)
528 {
529 for (i=0; i < condition_stack_idx; i++)
530 {
531 if (condition_stack[i]->manverb)
532 cond_in_verbatim = (macro_set_p (condition_stack[i]->name)
533 ^ !condition_stack[i]->isset);
534 else if (!(macro_set_p (condition_stack[i]->name)
535 ^ !condition_stack[i]->isset))
536 {
537 cond_is_active = 0;
538 break;
539 }
540 }
541 }
542
543 /* inf ("%s:%d: active=%d verbatim=%d", */
544 /* fname, lnr, cond_is_active, cond_in_verbatim); */
545 }
546
547
548 /* Push a condition with condition macro NAME onto the stack. If
549 ISSET is true, a @isset condition is pushed. */
550 static void
push_condition(const char * name,int isset,const char * fname,int lnr)551 push_condition (const char *name, int isset, const char *fname, int lnr)
552 {
553 condition_t cond;
554 int manverb = 0;
555
556 if (condition_stack_idx >= MAX_CONDITION_NESTING)
557 {
558 err ("%s:%d: condition nested too deep", fname, lnr);
559 return;
560 }
561
562 if (!strcmp (name, "manverb"))
563 {
564 if (!isset)
565 {
566 err ("%s:%d: using \"@ifclear manverb\" is not allowed", fname, lnr);
567 return;
568 }
569 manverb = 1;
570 }
571
572 cond = xcalloc (1, sizeof *cond + strlen (name));
573 cond->manverb = manverb;
574 cond->isset = isset;
575 strcpy (cond->name, name);
576
577 condition_stack[condition_stack_idx++] = cond;
578 evaluate_conditions (fname, lnr);
579 }
580
581
582 /* Remove the last condition from the stack. ISSET is used for error
583 reporting. */
584 static void
pop_condition(int isset,const char * fname,int lnr)585 pop_condition (int isset, const char *fname, int lnr)
586 {
587 if (!condition_stack_idx)
588 {
589 err ("%s:%d: unbalanced \"@end %s\"",
590 fname, lnr, isset?"isset":"isclear");
591 return;
592 }
593 condition_stack_idx--;
594 free (condition_stack[condition_stack_idx]);
595 condition_stack[condition_stack_idx] = NULL;
596 evaluate_conditions (fname, lnr);
597 }
598
599
600
601 /* Return a section buffer for the section NAME. Allocate a new buffer
602 if this is a new section. Keep track of the sections in THEPAGE.
603 This function may reallocate the section array in THEPAGE. */
604 static section_buffer_t
get_section_buffer(const char * name)605 get_section_buffer (const char *name)
606 {
607 int i;
608 section_buffer_t sect;
609
610 /* If there is no section we put everything into the required NAME
611 section. Given that this is the first one listed it is likely
612 that error are easily visible. */
613 if (!name)
614 name = "NAME";
615
616 for (i=0; i < thepage.n_sections; i++)
617 {
618 sect = thepage.sections + i;
619 if (sect->name && !strcmp (name, sect->name))
620 return sect;
621 }
622 for (i=0; i < thepage.n_sections; i++)
623 if (!thepage.sections[i].name)
624 break;
625 if (thepage.n_sections && i < thepage.n_sections)
626 sect = thepage.sections + i;
627 else
628 {
629 /* We need to allocate or reallocate the section array. */
630 size_t old_n = thepage.n_sections;
631 size_t new_n = 20;
632
633 if (!old_n)
634 thepage.sections = xcalloc (new_n, sizeof *thepage.sections);
635 else
636 {
637 thepage.sections = xrealloc (thepage.sections,
638 ((old_n + new_n)
639 * sizeof *thepage.sections));
640 memset (thepage.sections + old_n, 0,
641 new_n * sizeof *thepage.sections);
642 }
643 thepage.n_sections += new_n;
644
645 /* Setup the tail pointers. */
646 for (i=old_n; i < thepage.n_sections; i++)
647 {
648 sect = thepage.sections + i;
649 sect->lines_tail = §->lines;
650 }
651 sect = thepage.sections + old_n;
652 }
653
654 /* Store the name. */
655 assert (!sect->name);
656 sect->name = xstrdup (name);
657 return sect;
658 }
659
660
661
662 /* Add the content of LINE to the section named SECTNAME. */
663 static void
add_content(const char * sectname,char * line,int verbatim)664 add_content (const char *sectname, char *line, int verbatim)
665 {
666 section_buffer_t sect;
667 line_buffer_t lb;
668
669 sect = get_section_buffer (sectname);
670 if (sect->last_line && !sect->last_line->verbatim == !verbatim)
671 {
672 /* Lets append that line to the last one. We do this to keep
673 all lines of the same kind (i.e.verbatim or not) together in
674 one large buffer. */
675 size_t n1, n;
676
677 lb = sect->last_line;
678 n1 = strlen (lb->line);
679 n = n1 + 1 + strlen (line) + 1;
680 lb->line = xrealloc (lb->line, n);
681 strcpy (lb->line+n1, "\n");
682 strcpy (lb->line+n1+1, line);
683 }
684 else
685 {
686 lb = xcalloc (1, sizeof *lb);
687 lb->verbatim = verbatim;
688 lb->line = xstrdup (line);
689 sect->last_line = lb;
690 *sect->lines_tail = lb;
691 sect->lines_tail = &lb->next;
692 }
693 }
694
695
696 /* Prepare for a new man page using the filename NAME. */
697 static void
start_page(char * name)698 start_page (char *name)
699 {
700 if (verbose)
701 inf ("starting page '%s'", name);
702 assert (!thepage.name);
703 thepage.name = xstrdup (name);
704 thepage.n_sections = 0;
705 }
706
707
708 /* Write a character to FP. */
709 static void
writechr(int c,FILE * fp)710 writechr (int c, FILE *fp)
711 {
712 putc (c, fp);
713 }
714
715
716 /* Write depending on HTMLMODE either ROFF or HTML to FP. */
717 static void
writestr(const char * roff,const char * html,FILE * fp)718 writestr (const char *roff, const char *html, FILE *fp)
719 {
720 const char *s = htmlmode? html : roff;
721
722 if (s)
723 fputs (s, fp);
724 }
725
726
727 /* Write the .TH entry of the current page. Return -1 if there is a
728 problem with the page. */
729 static int
write_th(FILE * fp)730 write_th (FILE *fp)
731 {
732 char *name, *p;
733
734 writestr (".\\\" Created from Texinfo source by yat2m " VERSION "\n",
735 "<!-- Created from Texinfo source by yat2m " VERSION " -->\n",
736 fp);
737
738 name = ascii_strupr (xstrdup (thepage.name));
739 p = strrchr (name, '.');
740 if (!p || !p[1])
741 {
742 err ("no section name in man page '%s'", thepage.name);
743 free (name);
744 return -1;
745 }
746 *p++ = 0;
747
748 if (htmlmode)
749 {
750 fputs ("<html>\n"
751 "<head>\n", fp);
752 fprintf (fp, " <title>%s(%s)</title>\n", name, p);
753 fputs (default_css, fp);
754 fputs ("</head>\n"
755 "<body>\n", fp);
756 fputs ("<div class=\"y2m\">\n", fp);
757 }
758
759 /* This roff source
760 * .TH GPG 1 2016-12-20 "GnuPG 2.1.17" "GNU Privacy Guard 2.1"
761 * is rendered by man like this:
762 * GPG(1) GNU Privacy Guard 2.1 GPG(1)
763 * [...]
764 * GnuPG 2.1.17 2016-12-20 GPG(1)
765 */
766 if (htmlmode)
767 {
768 fprintf (fp, "<p class=\"y2m y2m-top\">"
769 "<span class=\"y2m-left\">%s(%s)</span> "
770 "<span class=\"y2m-center\">%s</span> "
771 "<span class=\"y2m-right\">%s(%s)</span>"
772 "</p>\n",
773 name, p, opt_source, name, p);
774 /* Note that the HTML footer is written by write_bottom(). */
775
776 }
777 else
778 fprintf (fp, ".TH %s %s %s \"%s\" \"%s\"\n",
779 name, p, isodatestring (), opt_release, opt_source);
780
781 free (name);
782 return 0;
783 }
784
785
786 /* In HTML mode we need to render a footer. */
787 static int
write_bottom(FILE * fp)788 write_bottom (FILE *fp)
789 {
790 char *name, *p;
791
792 if (!htmlmode)
793 return 0;
794
795 name = ascii_strupr (xstrdup (thepage.name));
796 p = strrchr (name, '.');
797 if (!p || !p[1])
798 {
799 err ("no section name in man page '%s'", thepage.name);
800 free (name);
801 return -1;
802 }
803 *p++ = 0;
804
805 /* This roff source
806 * .TH GPG 1 2016-12-20 "GnuPG 2.1.17" "GNU Privacy Guard 2.1"
807 * is rendered by man to this footer:
808 * GnuPG 2.1.17 2016-12-20 GPG(1)
809 */
810 fprintf (fp, "<p class=\"y2m y2m-footer\">"
811 "<span class=\"y2m-left\">%s</span> "
812 "<span class=\"y2m-center\">%s</span> "
813 "<span class=\"y2m-right\">%s(%s)</span>"
814 "</p>\n",
815 opt_release, isodatestring (), name, p);
816 fputs ("</div><!-- class y2m -->\n", fp);
817 fputs ("</body>\n"
818 "</html>\n", fp);
819
820 free (name);
821 return 0;
822 }
823
824
825 /* Write the .SH header. With NULL passed for NAME just close a
826 * section in html mode if there is an open section. */
827 static void
write_sh(FILE * fp,const char * name)828 write_sh (FILE *fp, const char *name)
829 {
830 static int in_section;
831
832 if (htmlmode && in_section)
833 fprintf (fp, "</div>\n");
834 in_section = 0;
835
836 if (name)
837 {
838 if (htmlmode)
839 fprintf (fp,
840 "<div class=\"y2m-section\">\n"
841 "<p class=\"y2m-sh\">%s</p>\n", name);
842 else
843 fprintf (fp, ".SH %s\n", name);
844 in_section = 1;
845 }
846 }
847
848 /* Render a @item line to HTML. (LINE,LEN) gives the arguments of
849 * @item. Use NULL for LINE to close a possible open <li>. ITEMX
850 * flags a @itemx line. */
851 static void
write_html_item(FILE * fp,const char * line,size_t len,int itemx)852 write_html_item (FILE *fp, const char *line, size_t len, int itemx)
853 {
854 static int in_li;
855 const char *rest;
856 size_t n, n0;
857 int eol_action = 0;
858 int table_level = 0;
859
860 if (!itemx && in_li)
861 {
862 fprintf (fp, "</li>\n");
863 in_li = 0;
864 }
865
866 if (line)
867 {
868 /* Trim the LF and skip leading spaces. */
869 if (len && line[len-1] == '\n')
870 len--;
871 for (; len && (*line == ' ' || *line == '\t'); len--, line++)
872 ;
873 if (len)
874 {
875 rest = line;
876 for (n=0; n < len && !(*rest == ' ' || *rest == '\t'); n++, rest++)
877 ;
878 n0 = n;
879 for (; n < len && (*rest == ' ' || *rest == '\t'); n++, rest++)
880 ;
881 len -= n;
882 /* Now the first word is (LINE,N0) and the args are (REST,LEN) */
883 fprintf (fp, "%s<span class=\"y2m-item\">%.*s",
884 itemx? " ":"<li>", (int)n0, line);
885 if (len)
886 {
887 fputs (" <span class=\"y2m-args\">", fp);
888 proc_texi_buffer (fp, rest, len, &table_level, &eol_action);
889 fputs ("</span>", fp);
890 }
891 fputs ("</span>\n", fp);
892 in_li = 1;
893 }
894 }
895 }
896
897
898 /* Process the texinfo command COMMAND (without the leading @) and
899 write output if needed to FP. REST is the remainder of the line
900 which should either point to an opening brace or to a white space.
901 The function returns the number of characters already processed
902 from REST. LEN is the usable length of REST. TABLE_LEVEL is used to
903 control the indentation of tables. */
904 static size_t
proc_texi_cmd(FILE * fp,const char * command,const char * rest,size_t len,int * table_level,int * eol_action)905 proc_texi_cmd (FILE *fp, const char *command, const char *rest, size_t len,
906 int *table_level, int *eol_action)
907 {
908 static struct {
909 const char *name; /* Name of the command. */
910 int what; /* What to do with this command. */
911 const char *lead_in; /* String to print with a opening brace. */
912 const char *lead_out;/* String to print with the closing brace. */
913 const char *html_in; /* Same as LEAD_IN but for HTML. */
914 const char *html_out;/* Same as LEAD_OUT but for HTML. */
915 } cmdtbl[] = {
916 { "command", 0, "\\fB", "\\fR", "<i>", "</i>" },
917 { "code", 0, "\\fB", "\\fR", "<samp>", "</samp>" },
918 { "url", 0, "\\fB", "\\fR", "<strong>", "</strong>" },
919 { "sc", 0, "\\fB", "\\fR", "<span class=\"y2m-sc\">", "</span>" },
920 { "var", 0, "\\fI", "\\fR", "<u>", "</u>" },
921 { "samp", 0, "\\(oq", "\\(cq" },
922 { "kbd", 0, "\\(oq", "\\(cq" },
923 { "file", 0, "\\(oq\\fI","\\fR\\(cq" },
924 { "env", 0, "\\(oq\\fI","\\fR\\(cq" },
925 { "acronym", 0 },
926 { "dfn", 0 },
927 { "option", 0, "\\fB", "\\fR", "<samp>", "</samp>" },
928 { "example", 1, ".RS 2\n.nf\n", NULL, "\n<pre>\n", "\n</pre>\n" },
929 { "smallexample", 1, ".RS 2\n.nf\n", NULL, "\n<pre>\n", "\n</pre>\n" },
930 { "asis", 7 },
931 { "anchor", 7 },
932 { "cartouche", 1 },
933 { "ref", 0, "[", "]" },
934 { "xref", 0, "See: [", "]" },
935 { "pxref", 0, "see: [", "]" },
936 { "uref", 0, "(\\fB", "\\fR)" },
937 { "footnote",0, " ([", "])" },
938 { "emph", 0, "\\fI", "\\fR", "<em>", "</em>" },
939 { "w", 1 },
940 { "c", 5 },
941 { "efindex", 1 },
942 { "opindex", 1 },
943 { "cpindex", 1 },
944 { "cindex", 1 },
945 { "noindent", 0 },
946 { "section", 1 },
947 { "chapter", 1 },
948 { "subsection", 6, "\n.SS " },
949 { "chapheading", 0},
950 { "item", 2, ".TP\n.B " },
951 { "itemx", 2, ".TQ\n.B " },
952 { "table", 3, NULL, NULL, "<ul>\n", "</ul>\n" },
953 { "itemize", 3 },
954 { "bullet", 0, "* " },
955 { "*", 0, "\n.br"},
956 { "/", 0 },
957 { "end", 4 },
958 { "quotation",1, ".RS\n\\fB" },
959 { "value", 8 },
960 { NULL }
961 };
962 size_t n;
963 int i;
964 const char *s;
965 const char *lead_out = NULL;
966 const char *html_out = NULL;
967 int ignore_args = 0;
968
969 for (i=0; cmdtbl[i].name && strcmp (cmdtbl[i].name, command); i++)
970 ;
971 if (cmdtbl[i].name)
972 {
973 writestr (cmdtbl[i].lead_in, cmdtbl[i].html_in, fp);
974 lead_out = cmdtbl[i].lead_out;
975 html_out = cmdtbl[i].html_out;
976 switch (cmdtbl[i].what)
977 {
978 case 1: /* Throw away the entire line. */
979 s = memchr (rest, '\n', len);
980 return s? (s-rest)+1 : len;
981 case 2: /* Handle @item. */
982 if (htmlmode)
983 {
984 s = memchr (rest, '\n', len);
985 n = s? (s-rest)+1 : len;
986 write_html_item (fp, rest, n, !strcmp(cmdtbl[i].name, "itemx"));
987 return n;
988 }
989 break;
990 case 3: /* Handle table. */
991 if (++(*table_level) > 1)
992 {
993 write_html_item (fp, NULL, 0, 0);
994 writestr (".RS\n", "<ul>\n", fp);
995 }
996 /* Now throw away the entire line. */
997 s = memchr (rest, '\n', len);
998 return s? (s-rest)+1 : len;
999 break;
1000 case 4: /* Handle end. */
1001 for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
1002 ;
1003 if (n >= 5 && !memcmp (s, "table", 5)
1004 && (!n || s[5] == ' ' || s[5] == '\t' || s[5] == '\n'))
1005 {
1006 if (htmlmode)
1007 write_html_item (fp, NULL, 0, 0);
1008 if ((*table_level)-- > 1)
1009 writestr (".RE\n", "</ul>\n", fp);
1010 else
1011 writestr (".P\n", "</ul>\n", fp);
1012 }
1013 else if (n >= 7 && !memcmp (s, "example", 7)
1014 && (!n || s[7] == ' ' || s[7] == '\t' || s[7] == '\n'))
1015 {
1016 writestr (".fi\n.RE\n", "</pre>\n", fp);
1017 }
1018 else if (n >= 12 && !memcmp (s, "smallexample", 12)
1019 && (!n || s[12] == ' ' || s[12] == '\t' || s[12] == '\n'))
1020 {
1021 writestr (".fi\n.RE\n", "</pre>\n", fp);
1022 }
1023 else if (n >= 9 && !memcmp (s, "quotation", 9)
1024 && (!n || s[9] == ' ' || s[9] == '\t' || s[9] == '\n'))
1025 {
1026 writestr ("\\fR\n.RE\n", "xx", fp);
1027 }
1028 /* Now throw away the entire line. */
1029 s = memchr (rest, '\n', len);
1030 return s? (s-rest)+1 : len;
1031 case 5: /* Handle special comments. */
1032 for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
1033 ;
1034 if (n >= 4 && !memcmp (s, "man:", 4))
1035 {
1036 s += 4;
1037 n -= 4;
1038 if (htmlmode)
1039 {
1040 if (!strncmp (s, ".RE\n", 4)
1041 || !strncmp (s, ".RS\n", 4))
1042 ;
1043 else
1044 inf ("unknown special comment \"man:\"");
1045 }
1046 else
1047 {
1048 for (; n && *s != '\n'; n--, s++)
1049 writechr (*s, fp);
1050 writechr ('\n', fp);
1051 }
1052 }
1053 /* Now throw away the entire line. */
1054 s = memchr (rest, '\n', len);
1055 return s? (s-rest)+1 : len;
1056 case 6:
1057 *eol_action = 1;
1058 break;
1059 case 7:
1060 ignore_args = 1;
1061 break;
1062 case 8:
1063 ignore_args = 1;
1064 if (*rest != '{')
1065 {
1066 err ("opening brace for command '%s' missing", command);
1067 return len;
1068 }
1069 else
1070 {
1071 /* Find closing brace. */
1072 for (s=rest+1, n=1; *s && n < len; s++, n++)
1073 if (*s == '}')
1074 break;
1075 if (*s != '}')
1076 {
1077 err ("closing brace for command '%s' not found", command);
1078 return len;
1079 }
1080 else
1081 {
1082 size_t rlen = s - (rest + 1);
1083 macro_t m;
1084
1085 for (m = variablelist; m; m = m->next)
1086 {
1087 if (strlen (m->name) == rlen
1088 && !strncmp (m->name, rest+1, rlen))
1089 break;
1090 }
1091 if (m)
1092 writestr (m->value, m->value, fp);
1093 else
1094 inf ("texinfo variable '%.*s' is not set",
1095 (int)rlen, rest+1);
1096 }
1097 }
1098 break;
1099 default:
1100 break;
1101 }
1102 }
1103 else /* macro */
1104 {
1105 macro_t m;
1106
1107 for (m = macrolist; m ; m = m->next)
1108 if (!strcmp (m->name, command))
1109 break;
1110 if (m)
1111 {
1112 proc_texi_buffer (fp, m->value, strlen (m->value),
1113 table_level, eol_action);
1114 ignore_args = 1; /* Parameterized macros are not yet supported. */
1115 }
1116 else
1117 inf ("texinfo command '%s' not supported (%.*s)", command,
1118 (int)((s = memchr (rest, '\n', len)), (s? (s-rest) : len)), rest);
1119 }
1120
1121 if (*rest == '{')
1122 {
1123 /* Find matching closing brace. */
1124 for (s=rest+1, n=1, i=1; i && *s && n < len; s++, n++)
1125 if (*s == '{')
1126 i++;
1127 else if (*s == '}')
1128 i--;
1129 if (i)
1130 {
1131 err ("closing brace for command '%s' not found", command);
1132 return len;
1133 }
1134 if (n > 2 && !ignore_args)
1135 proc_texi_buffer (fp, rest+1, n-2, table_level, eol_action);
1136 }
1137 else
1138 n = 0;
1139
1140 writestr (lead_out, html_out, fp);
1141
1142 return n;
1143 }
1144
1145
1146
1147 /* Process the string LINE with LEN bytes of Texinfo content. */
1148 static void
proc_texi_buffer(FILE * fp,const char * line,size_t len,int * table_level,int * eol_action)1149 proc_texi_buffer (FILE *fp, const char *line, size_t len,
1150 int *table_level, int *eol_action)
1151 {
1152 const char *s;
1153 char cmdbuf[256];
1154 int cmdidx = 0;
1155 int in_cmd = 0;
1156 size_t n;
1157
1158 for (s=line; *s && len; s++, len--)
1159 {
1160 if (in_cmd)
1161 {
1162 if (in_cmd == 1)
1163 {
1164 switch (*s)
1165 {
1166 case '@': case '{': case '}':
1167 writechr (*s, fp); in_cmd = 0;
1168 break;
1169 case ':': /* Not ending a sentence flag. */
1170 in_cmd = 0;
1171 break;
1172 case '.': case '!': case '?': /* Ending a sentence. */
1173 writechr (*s, fp); in_cmd = 0;
1174 break;
1175 case ' ': case '\t': case '\n': /* Non collapsing spaces. */
1176 writechr (*s, fp); in_cmd = 0;
1177 break;
1178 default:
1179 cmdidx = 0;
1180 cmdbuf[cmdidx++] = *s;
1181 in_cmd++;
1182 break;
1183 }
1184 }
1185 else if (*s == '{' || *s == ' ' || *s == '\t' || *s == '\n')
1186 {
1187 cmdbuf[cmdidx] = 0;
1188 n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
1189 assert (n <= len);
1190 s += n; len -= n;
1191 s--; len++;
1192 in_cmd = 0;
1193 }
1194 else if (cmdidx < sizeof cmdbuf -1)
1195 cmdbuf[cmdidx++] = *s;
1196 else
1197 {
1198 err ("texinfo command too long - ignored");
1199 in_cmd = 0;
1200 }
1201 }
1202 else if (*s == '@')
1203 in_cmd = 1;
1204 else if (*s == '\n')
1205 {
1206 switch (*eol_action)
1207 {
1208 case 1: /* Create a dummy paragraph. */
1209 writestr ("\n\\ \n", "\n<-- dummy par -->\n", fp);
1210 break;
1211 default:
1212 writechr (*s, fp);
1213 }
1214 *eol_action = 0;
1215 }
1216 else if (*s == '\\')
1217 writestr ("\\\\", "\\\\", fp);
1218 else
1219 writechr (*s, fp);
1220 }
1221
1222 if (in_cmd > 1)
1223 {
1224 cmdbuf[cmdidx] = 0;
1225 n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
1226 assert (n <= len);
1227 s += n; len -= n;
1228 s--; len++;
1229 /* in_cmd = 0; -- doc only */
1230 }
1231 }
1232
1233
1234 /* Do something with the Texinfo line LINE. */
1235 static void
parse_texi_line(FILE * fp,const char * line,int * table_level)1236 parse_texi_line (FILE *fp, const char *line, int *table_level)
1237 {
1238 int eol_action = 0;
1239
1240 /* A quick test whether there are any texinfo commands. */
1241 if (!strchr (line, '@'))
1242 {
1243 /* FIXME: In html mode escape HTML stuff. */
1244 writestr (line, line, fp);
1245 writechr ('\n', fp);
1246 return;
1247 }
1248 proc_texi_buffer (fp, line, strlen (line), table_level, &eol_action);
1249 writechr ('\n', fp);
1250 }
1251
1252
1253 /* Write all the lines LINES to FP. */
1254 static void
write_content(FILE * fp,line_buffer_t lines)1255 write_content (FILE *fp, line_buffer_t lines)
1256 {
1257 line_buffer_t line;
1258 int table_level = 0;
1259
1260 for (line = lines; line; line = line->next)
1261 {
1262 if (line->verbatim)
1263 {
1264 /* FIXME: IN HTML mode we need to employ a parser for roff
1265 * markup. */
1266 writestr (line->line, line->line, fp);
1267 writechr ('\n', fp);
1268 }
1269 else
1270 {
1271 /* fputs ("TEXI---", fp); */
1272 /* fputs (line->line, fp); */
1273 /* fputs ("---\n", fp); */
1274 parse_texi_line (fp, line->line, &table_level);
1275 }
1276 }
1277 }
1278
1279
1280
1281 static int
is_standard_section(const char * name)1282 is_standard_section (const char *name)
1283 {
1284 int i;
1285 const char *s;
1286
1287 for (i=0; (s=standard_sections[i]); i++)
1288 if (!strcmp (s, name))
1289 return 1;
1290 return 0;
1291 }
1292
1293
1294 /* Finish a page; that is sort the data and write it out to the file. */
1295 static void
finish_page(void)1296 finish_page (void)
1297 {
1298 FILE *fp;
1299 section_buffer_t sect = NULL;
1300 int idx;
1301 const char *s;
1302 int i;
1303
1304 if (!thepage.name)
1305 return; /* No page active. */
1306
1307 if (verbose)
1308 inf ("finishing page '%s'", thepage.name);
1309
1310 if (opt_select)
1311 {
1312 if (!strcmp (opt_select, thepage.name))
1313 {
1314 inf ("selected '%s'", thepage.name );
1315 fp = stdout;
1316 }
1317 else
1318 {
1319 fp = fopen ( "/dev/null", "w" );
1320 if (!fp)
1321 die ("failed to open /dev/null: %s\n", strerror (errno));
1322 }
1323 }
1324 else if (opt_store)
1325 {
1326 inf ("writing '%s'", thepage.name );
1327 fp = fopen ( thepage.name, "w" );
1328 if (!fp)
1329 die ("failed to create '%s': %s\n", thepage.name, strerror (errno));
1330 }
1331 else
1332 fp = stdout;
1333
1334 if (write_th (fp))
1335 goto leave;
1336
1337 for (idx=0; (s=standard_sections[idx]); idx++)
1338 {
1339 for (i=0; i < thepage.n_sections; i++)
1340 {
1341 sect = thepage.sections + i;
1342 if (sect->name && !strcmp (s, sect->name))
1343 break;
1344 }
1345 if (i == thepage.n_sections)
1346 sect = NULL;
1347
1348 if (sect)
1349 {
1350 write_sh (fp, sect->name);
1351 write_content (fp, sect->lines);
1352 /* Now continue with all non standard sections directly
1353 following this one. */
1354 for (i++; i < thepage.n_sections; i++)
1355 {
1356 sect = thepage.sections + i;
1357 if (sect->name && is_standard_section (sect->name))
1358 break;
1359 if (sect->name)
1360 {
1361 write_sh (fp, sect->name);
1362 write_content (fp, sect->lines);
1363 }
1364 }
1365
1366 }
1367 }
1368
1369 write_sh (fp, NULL);
1370 if (write_bottom (fp))
1371 goto leave;
1372
1373 leave:
1374 if (fp != stdout)
1375 fclose (fp);
1376 free (thepage.name);
1377 thepage.name = NULL;
1378 /* FIXME: Cleanup the content. */
1379 }
1380
1381
1382
1383
1384 /* Parse one Texinfo file and create manpages according to the
1385 embedded instructions. */
1386 static void
parse_file(const char * fname,FILE * fp,char ** section_name,int in_pause)1387 parse_file (const char *fname, FILE *fp, char **section_name, int in_pause)
1388 {
1389 char *line;
1390 int lnr = 0;
1391 /* Fixme: The following state variables don't carry over to include
1392 files. */
1393 int skip_to_end = 0; /* Used to skip over menu entries. */
1394 int skip_sect_line = 0; /* Skip after @mansect. */
1395 int item_indent = 0; /* How far is the current @item indented. */
1396
1397 /* Helper to define a macro. */
1398 char *macroname = NULL;
1399 char *macrovalue = NULL;
1400 size_t macrovaluesize = 0;
1401 size_t macrovalueused = 0;
1402
1403 line = xmalloc (LINESIZE);
1404 while (fgets (line, LINESIZE, fp))
1405 {
1406 size_t n = strlen (line);
1407 int got_line = 0;
1408 char *p, *pend;
1409
1410 lnr++;
1411 if (!n || line[n-1] != '\n')
1412 {
1413 err ("%s:%d: trailing linefeed missing, line too long or "
1414 "embedded Nul character", fname, lnr);
1415 break;
1416 }
1417 line[--n] = 0;
1418
1419 /* Kludge to allow indentation of tables. */
1420 for (p=line; *p == ' ' || *p == '\t'; p++)
1421 ;
1422 if (*p)
1423 {
1424 if (*p == '@' && !strncmp (p+1, "item", 4))
1425 item_indent = p - line; /* Set a new indent level. */
1426 else if (p - line < item_indent)
1427 item_indent = 0; /* Switch off indention. */
1428
1429 if (item_indent)
1430 {
1431 memmove (line, line+item_indent, n - item_indent + 1);
1432 n -= item_indent;
1433 }
1434 }
1435
1436
1437 if (*line == '@')
1438 {
1439 for (p=line+1, n=1; *p && *p != ' ' && *p != '\t'; p++)
1440 n++;
1441 while (*p == ' ' || *p == '\t')
1442 p++;
1443 }
1444 else
1445 p = line;
1446
1447 /* Take action on macro. */
1448 if (macroname)
1449 {
1450 if (n == 4 && !memcmp (line, "@end", 4)
1451 && (line[4]==' '||line[4]=='\t'||!line[4])
1452 && !strncmp (p, "macro", 5)
1453 && (p[5]==' '||p[5]=='\t'||!p[5]))
1454 {
1455 if (macrovalueused)
1456 macrovalue[--macrovalueused] = 0; /* Kill the last LF. */
1457 macrovalue[macrovalueused] = 0; /* Terminate macro. */
1458 macrovalue = xrealloc (macrovalue, macrovalueused+1);
1459
1460 set_macro (macroname, macrovalue);
1461 macrovalue = NULL;
1462 free (macroname);
1463 macroname = NULL;
1464 }
1465 else
1466 {
1467 if (macrovalueused + strlen (line) + 2 >= macrovaluesize)
1468 {
1469 macrovaluesize += strlen (line) + 256;
1470 macrovalue = xrealloc (macrovalue, macrovaluesize);
1471 }
1472 strcpy (macrovalue+macrovalueused, line);
1473 macrovalueused += strlen (line);
1474 macrovalue[macrovalueused++] = '\n';
1475 }
1476 continue;
1477 }
1478
1479
1480 if (n >= 5 && !memcmp (line, "@node", 5)
1481 && (line[5]==' '||line[5]=='\t'||!line[5]))
1482 {
1483 /* Completey ignore @node lines. */
1484 continue;
1485 }
1486
1487
1488 if (skip_sect_line)
1489 {
1490 skip_sect_line = 0;
1491 if (!strncmp (line, "@section", 8)
1492 || !strncmp (line, "@subsection", 11)
1493 || !strncmp (line, "@chapheading", 12))
1494 continue;
1495 }
1496
1497 /* We only parse lines we need and ignore the rest. There are a
1498 few macros used to control this as well as one @ifset
1499 command. Parts we know about are saved away into containers
1500 separate for each section. */
1501
1502 /* First process ifset/ifclear commands. */
1503 if (*line == '@')
1504 {
1505 if (n == 6 && !memcmp (line, "@ifset", 6)
1506 && (line[6]==' '||line[6]=='\t'))
1507 {
1508 for (p=line+7; *p == ' ' || *p == '\t'; p++)
1509 ;
1510 if (!*p)
1511 {
1512 err ("%s:%d: name missing after \"@ifset\"", fname, lnr);
1513 continue;
1514 }
1515 for (pend=p; *pend && *pend != ' ' && *pend != '\t'; pend++)
1516 ;
1517 *pend = 0; /* Ignore rest of the line. */
1518 push_condition (p, 1, fname, lnr);
1519 continue;
1520 }
1521 else if (n == 8 && !memcmp (line, "@ifclear", 8)
1522 && (line[8]==' '||line[8]=='\t'))
1523 {
1524 for (p=line+9; *p == ' ' || *p == '\t'; p++)
1525 ;
1526 if (!*p)
1527 {
1528 err ("%s:%d: name missing after \"@ifsclear\"", fname, lnr);
1529 continue;
1530 }
1531 for (pend=p; *pend && *pend != ' ' && *pend != '\t'; pend++)
1532 ;
1533 *pend = 0; /* Ignore rest of the line. */
1534 push_condition (p, 0, fname, lnr);
1535 continue;
1536 }
1537 else if (n == 4 && !memcmp (line, "@end", 4)
1538 && (line[4]==' '||line[4]=='\t')
1539 && !strncmp (p, "ifset", 5)
1540 && (p[5]==' '||p[5]=='\t'||!p[5]))
1541 {
1542 pop_condition (1, fname, lnr);
1543 continue;
1544 }
1545 else if (n == 4 && !memcmp (line, "@end", 4)
1546 && (line[4]==' '||line[4]=='\t')
1547 && !strncmp (p, "ifclear", 7)
1548 && (p[7]==' '||p[7]=='\t'||!p[7]))
1549 {
1550 pop_condition (0, fname, lnr);
1551 continue;
1552 }
1553 }
1554
1555 /* Take action on ifset/ifclear. */
1556 if (!cond_is_active)
1557 continue;
1558
1559 /* Process commands. */
1560 if (*line == '@')
1561 {
1562 if (skip_to_end
1563 && n == 4 && !memcmp (line, "@end", 4)
1564 && (line[4]==' '||line[4]=='\t'||!line[4]))
1565 {
1566 skip_to_end = 0;
1567 }
1568 else if (cond_in_verbatim)
1569 {
1570 got_line = 1;
1571 }
1572 else if (n == 6 && !memcmp (line, "@macro", 6))
1573 {
1574 macroname = xstrdup (p);
1575 macrovalue = xmalloc ((macrovaluesize = 1024));
1576 macrovalueused = 0;
1577 }
1578 else if (n == 4 && !memcmp (line, "@set", 4))
1579 {
1580 set_variable (p);
1581 }
1582 else if (n == 8 && !memcmp (line, "@manpage", 8))
1583 {
1584 free (*section_name);
1585 *section_name = NULL;
1586 finish_page ();
1587 start_page (p);
1588 in_pause = 0;
1589 }
1590 else if (n == 8 && !memcmp (line, "@mansect", 8))
1591 {
1592 if (!thepage.name)
1593 err ("%s:%d: section outside of a man page", fname, lnr);
1594 else
1595 {
1596 free (*section_name);
1597 *section_name = ascii_strupr (xstrdup (p));
1598 in_pause = 0;
1599 skip_sect_line = 1;
1600 }
1601 }
1602 else if (n == 9 && !memcmp (line, "@manpause", 9))
1603 {
1604 if (!*section_name)
1605 err ("%s:%d: pausing outside of a man section", fname, lnr);
1606 else if (in_pause)
1607 err ("%s:%d: already pausing", fname, lnr);
1608 else
1609 in_pause = 1;
1610 }
1611 else if (n == 8 && !memcmp (line, "@mancont", 8))
1612 {
1613 if (!*section_name)
1614 err ("%s:%d: continue outside of a man section", fname, lnr);
1615 else if (!in_pause)
1616 err ("%s:%d: continue while not pausing", fname, lnr);
1617 else
1618 in_pause = 0;
1619 }
1620 else if (n == 5 && !memcmp (line, "@menu", 5)
1621 && (line[5]==' '||line[5]=='\t'||!line[5]))
1622 {
1623 skip_to_end = 1;
1624 }
1625 else if (n == 8 && !memcmp (line, "@include", 8)
1626 && (line[8]==' '||line[8]=='\t'||!line[8]))
1627 {
1628 char *incname = xstrdup (p);
1629 FILE *incfp = fopen (incname, "r");
1630
1631 if (!incfp && opt_include && *opt_include && *p != '/')
1632 {
1633 free (incname);
1634 incname = xmalloc (strlen (opt_include) + 1
1635 + strlen (p) + 1);
1636 strcpy (incname, opt_include);
1637 if ( incname[strlen (incname)-1] != '/' )
1638 strcat (incname, "/");
1639 strcat (incname, p);
1640 incfp = fopen (incname, "r");
1641 }
1642
1643 if (!incfp)
1644 err ("can't open include file '%s': %s",
1645 incname, strerror (errno));
1646 else
1647 {
1648 parse_file (incname, incfp, section_name, in_pause);
1649 fclose (incfp);
1650 }
1651 free (incname);
1652 }
1653 else if (n == 4 && !memcmp (line, "@bye", 4)
1654 && (line[4]==' '||line[4]=='\t'||!line[4]))
1655 {
1656 break;
1657 }
1658 else if (!skip_to_end)
1659 got_line = 1;
1660 }
1661 else if (!skip_to_end)
1662 got_line = 1;
1663
1664 if (got_line && cond_in_verbatim)
1665 add_content (*section_name, line, 1);
1666 else if (got_line && thepage.name && *section_name && !in_pause)
1667 add_content (*section_name, line, 0);
1668
1669 }
1670 if (ferror (fp))
1671 err ("%s:%d: read error: %s", fname, lnr, strerror (errno));
1672 free (macroname);
1673 free (macrovalue);
1674 free (line);
1675 }
1676
1677
1678 static void
top_parse_file(const char * fname,FILE * fp)1679 top_parse_file (const char *fname, FILE *fp)
1680 {
1681 char *section_name = NULL; /* Name of the current section or NULL
1682 if not in a section. */
1683 macro_t m;
1684
1685 while (macrolist)
1686 {
1687 macro_t next = macrolist->next;
1688 free (macrolist->value);
1689 free (macrolist);
1690 macrolist = next;
1691 }
1692 while (variablelist)
1693 {
1694 macro_t next = variablelist->next;
1695 free (variablelist->value);
1696 free (variablelist);
1697 variablelist = next;
1698 }
1699 for (m=predefinedmacrolist; m; m = m->next)
1700 set_macro (m->name, xstrdup ("1"));
1701 cond_is_active = 1;
1702 cond_in_verbatim = 0;
1703
1704 parse_file (fname, fp, §ion_name, 0);
1705 free (section_name);
1706 finish_page ();
1707 }
1708
1709
1710 int
main(int argc,char ** argv)1711 main (int argc, char **argv)
1712 {
1713 int last_argc = -1;
1714 const char *s;
1715
1716 opt_source = "GNU";
1717 opt_release = "";
1718
1719 /* Define default macros. The trick is that these macros are not
1720 defined when using the actual texinfo renderer. */
1721 add_predefined_macro ("isman");
1722 add_predefined_macro ("manverb");
1723
1724 /* Option parsing. */
1725 if (argc)
1726 {
1727 argc--; argv++;
1728 }
1729 while (argc && last_argc != argc )
1730 {
1731 last_argc = argc;
1732 if (!strcmp (*argv, "--"))
1733 {
1734 argc--; argv++;
1735 break;
1736 }
1737 else if (!strcmp (*argv, "--help"))
1738 {
1739 puts (
1740 "Usage: " PGM " [OPTION] [FILE]\n"
1741 "Extract man pages from a Texinfo source.\n\n"
1742 " --html render output as HTML\n"
1743 " --source NAME use NAME as source field\n"
1744 " --release STRING use STRING as the release field\n"
1745 " --date EPOCH use EPOCH as publication date\n"
1746 " --store write output using @manpage name\n"
1747 " --select NAME only output pages with @manpage NAME\n"
1748 " --verbose enable extra informational output\n"
1749 " --debug enable additional debug output\n"
1750 " --help display this help and exit\n"
1751 " -I DIR also search in include DIR\n"
1752 " -D MACRO define MACRO to 1\n\n"
1753 "With no FILE, or when FILE is -, read standard input.\n\n"
1754 "Report bugs to <https://bugs.gnupg.org>.");
1755 exit (0);
1756 }
1757 else if (!strcmp (*argv, "--version"))
1758 {
1759 puts (PGM " " VERSION "\n"
1760 "Copyright (C) 2005, 2017 g10 Code GmbH\n"
1761 "This program comes with ABSOLUTELY NO WARRANTY.\n"
1762 "This is free software, and you are welcome to redistribute it\n"
1763 "under certain conditions. See the file COPYING for details.");
1764 exit (0);
1765 }
1766 else if (!strcmp (*argv, "--html"))
1767 {
1768 htmlmode = 1;
1769 argc--; argv++;
1770 }
1771 else if (!strcmp (*argv, "--verbose"))
1772 {
1773 verbose = 1;
1774 argc--; argv++;
1775 }
1776 else if (!strcmp (*argv, "--quiet"))
1777 {
1778 quiet = 1;
1779 argc--; argv++;
1780 }
1781 else if (!strcmp (*argv, "--debug"))
1782 {
1783 verbose = debug = 1;
1784 argc--; argv++;
1785 }
1786 else if (!strcmp (*argv, "--source"))
1787 {
1788 argc--; argv++;
1789 if (argc)
1790 {
1791 opt_source = *argv;
1792 argc--; argv++;
1793 }
1794 }
1795 else if (!strcmp (*argv, "--release"))
1796 {
1797 argc--; argv++;
1798 if (argc)
1799 {
1800 opt_release = *argv;
1801 argc--; argv++;
1802 }
1803 }
1804 else if (!strcmp (*argv, "--date"))
1805 {
1806 argc--; argv++;
1807 if (argc)
1808 {
1809 opt_date = *argv;
1810 argc--; argv++;
1811 }
1812 }
1813 else if (!strcmp (*argv, "--store"))
1814 {
1815 opt_store = 1;
1816 argc--; argv++;
1817 }
1818 else if (!strcmp (*argv, "--select"))
1819 {
1820 argc--; argv++;
1821 if (argc)
1822 {
1823 opt_select = strrchr (*argv, '/');
1824 if (opt_select)
1825 opt_select++;
1826 else
1827 opt_select = *argv;
1828 argc--; argv++;
1829 }
1830 }
1831 else if (!strcmp (*argv, "-I"))
1832 {
1833 argc--; argv++;
1834 if (argc)
1835 {
1836 opt_include = *argv;
1837 argc--; argv++;
1838 }
1839 }
1840 else if (!strcmp (*argv, "-D"))
1841 {
1842 argc--; argv++;
1843 if (argc)
1844 {
1845 add_predefined_macro (*argv);
1846 argc--; argv++;
1847 }
1848 }
1849 }
1850
1851 if (argc > 1)
1852 die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
1853
1854 /* Take care of supplied timestamp for reproducible builds. See
1855 * https://reproducible-builds.org/specs/source-date-epoch/ */
1856 if (!opt_date && (s = getenv ("SOURCE_DATE_EPOCH")) && *s)
1857 opt_date = s;
1858
1859 /* Start processing. */
1860 if (argc && strcmp (*argv, "-"))
1861 {
1862 FILE *fp = fopen (*argv, "rb");
1863 if (!fp)
1864 die ("%s:0: can't open file: %s", *argv, strerror (errno));
1865 top_parse_file (*argv, fp);
1866 fclose (fp);
1867 }
1868 else
1869 top_parse_file ("-", stdin);
1870
1871 return !!any_error;
1872 }
1873
1874
1875 /*
1876 Local Variables:
1877 compile-command: "gcc -Wall -g -Wall -o yat2m yat2m.c"
1878 End:
1879 */
1880