From a15c35f37ed2b58805adc213029998aa3e52f038 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 28 Oct 2013 07:05:41 +0100 Subject: [PATCH] doc: Change yat2m to allow arbitrary condition names. * doc/yat2m.c (MAX_CONDITION_NESTING): New. (gpgone_defined): Remove. (condition_s, condition_stack, condition_stack_idx): New. (cond_is_active, cond_in_verbatim): New. (add_predefined_macro, set_macro, macro_set_p): New. (evaluate_conditions, push_condition, pop_condition): New. (parse_file): Rewrite to use the condition stack. (top_parse_file): Set prefined macros. (main): Change -D to define arbitrary macros. -- This change allows the use of other conditionals than "gpgone" and thus make "gpgtwoone" et al. actually work. It does now also track conditionals over included files. Signed-off-by: Werner Koch --- doc/yat2m.c | 310 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 218 insertions(+), 92 deletions(-) diff --git a/doc/yat2m.c b/doc/yat2m.c index 5dc81bf59..2ac43902a 100644 --- a/doc/yat2m.c +++ b/doc/yat2m.c @@ -1,5 +1,5 @@ /* yat2m.c - Yet Another Texi 2 Man converter - * Copyright (C) 2005 g10 Code GmbH + * Copyright (C) 2005, 2013 g10 Code GmbH * Copyright (C) 2006, 2008, 2011 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify @@ -17,7 +17,7 @@ */ /* - This is a simple textinfo to man page converter. It needs some + This is a simple texinfo to man page converter. It needs some special markup in th e texinfo and tries best to get a create man page. It has been designed for the GnuPG man pages and thus only a few texinfo commands are supported. @@ -107,6 +107,9 @@ character. */ #define LINESIZE 1024 +/* Number of allowed condition nestings. */ +#define MAX_CONDITION_NESTING 10 + /* Option flags. */ static int verbose; static int quiet; @@ -117,10 +120,6 @@ static const char *opt_select; static const char *opt_include; static int opt_store; -/* The only define we understand is -D gpgone. Thus we need a simple - boolean tro track it. */ -static int gpgone_defined; - /* Flag to keep track whether any error occurred. */ static int any_error; @@ -129,7 +128,7 @@ static int any_error; struct macro_s { struct macro_s *next; - char *value; /* Malloced value. */ + char *value; /* Malloced value. */ char name[1]; }; typedef struct macro_s *macro_t; @@ -137,6 +136,24 @@ typedef struct macro_s *macro_t; /* List of all defined macros. */ static macro_t macrolist; +/* List of global macro names. The value part is not used. */ +static macro_t predefinedmacrolist; + +/* Object to keep track of @isset and @ifclear. */ +struct condition_s +{ + int manverb; /* "manverb" needs special treatment. */ + int isset; /* This is an @isset condition. */ + char name[1]; /* Name of the condition macro. */ +}; +typedef struct condition_s *condition_t; + +/* The stack used to evaluate conditions. And the current states. */ +static condition_t condition_stack[MAX_CONDITION_NESTING]; +static int condition_stack_idx; +static int cond_is_active; /* State of ifset/ifclear */ +static int cond_in_verbatim; /* State of "manverb". */ + /* Object to store one line of content. */ struct line_buffer_s @@ -313,7 +330,158 @@ isodatestring (void) } +/* Add NAME to the list of predefined macros which are global for all + files. */ +static void +add_predefined_macro (const char *name) +{ + macro_t m; + for (m=predefinedmacrolist; m; m = m->next) + if (!strcmp (m->name, name)) + break; + if (!m) + { + m = xcalloc (1, sizeof *m + strlen (name)); + strcpy (m->name, name); + m->next = predefinedmacrolist; + predefinedmacrolist = m; + } +} + + +/* Create or update a macro with name MACRONAME and set its values TO + MACROVALUE. Note that ownership of the macro value is transferred + to this function. */ +static void +set_macro (const char *macroname, char *macrovalue) +{ + macro_t m; + + for (m=macrolist; m; m = m->next) + if (!strcmp (m->name, macroname)) + break; + if (m) + free (m->value); + else + { + m = xcalloc (1, sizeof *m + strlen (macroname)); + strcpy (m->name, macroname); + m->next = macrolist; + macrolist = m; + } + m->value = macrovalue; + macrovalue = NULL; +} + + +/* Return true if the macro NAME is set, i.e. not the empty string and + not evaluating to 0. */ +static int +macro_set_p (const char *name) +{ + macro_t m; + + for (m = macrolist; m ; m = m->next) + if (!strcmp (m->name, name)) + break; + if (!m || !m->value || !*m->value) + return 0; + if ((*m->value & 0x80) || !isdigit (*m->value)) + return 1; /* Not a digit but some other string. */ + return !!atoi (m->value); +} + + +/* Evaluate the current conditions. */ +static void +evaluate_conditions (const char *fname, int lnr) +{ + int i; + + /* for (i=0; i < condition_stack_idx; i++) */ + /* inf ("%s:%d: stack[%d] %s %s %c", */ + /* fname, lnr, i, condition_stack[i]->isset? "set":"clr", */ + /* condition_stack[i]->name, */ + /* (macro_set_p (condition_stack[i]->name) */ + /* ^ !condition_stack[i]->isset)? 't':'f'); */ + + cond_is_active = 1; + cond_in_verbatim = 0; + if (condition_stack_idx) + { + for (i=0; i < condition_stack_idx; i++) + { + if (condition_stack[i]->manverb) + cond_in_verbatim = (macro_set_p (condition_stack[i]->name) + ^ !condition_stack[i]->isset); + else if (!(macro_set_p (condition_stack[i]->name) + ^ !condition_stack[i]->isset)) + { + cond_is_active = 0; + break; + } + } + } + + /* inf ("%s:%d: active=%d verbatim=%d", */ + /* fname, lnr, cond_is_active, cond_in_verbatim); */ +} + + +/* Push a condition with condition macro NAME onto the stack. If + ISSET is true, a @isset condition is pushed. */ +static void +push_condition (const char *name, int isset, const char *fname, int lnr) +{ + condition_t cond; + int manverb = 0; + + if (condition_stack_idx >= MAX_CONDITION_NESTING) + { + err ("%s:%d: condition nested too deep", fname, lnr); + return; + } + + if (!strcmp (name, "manverb")) + { + if (!isset) + { + err ("%s:%d: using \"@ifclear manverb\" is not allowed", fname, lnr); + return; + } + manverb = 1; + } + + cond = xcalloc (1, sizeof *cond + strlen (name)); + cond->manverb = manverb; + cond->isset = isset; + strcpy (cond->name, name); + + condition_stack[condition_stack_idx++] = cond; + evaluate_conditions (fname, lnr); +} + + +/* Remove the last condition from the stack. ISSET is used for error + reporting. */ +static void +pop_condition (int isset, const char *fname, int lnr) +{ + if (!condition_stack_idx) + { + err ("%s:%d: unbalanced \"@end %s\"", + fname, lnr, isset?"isset":"isclear"); + return; + } + condition_stack_idx--; + free (condition_stack[condition_stack_idx]); + condition_stack[condition_stack_idx] = NULL; + evaluate_conditions (fname, lnr); +} + + + /* Return a section buffer for the section NAME. Allocate a new buffer if this is a new section. Keep track of the sections in THEPAGE. This function may reallocate the section array in THEPAGE. */ @@ -862,14 +1030,8 @@ parse_file (const char *fname, FILE *fp, char **section_name, int in_pause) int lnr = 0; /* Fixme: The following state variables don't carry over to include files. */ - int in_verbatim = 0; int skip_to_end = 0; /* Used to skip over menu entries. */ int skip_sect_line = 0; /* Skip after @mansect. */ - int ifset_nesting = 0; /* How often a ifset has been seen. */ - int ifclear_nesting = 0; /* How often a ifclear has been seen. */ - int in_gpgone = 0; /* Keep track of "@ifset gpgone" parts. */ - int not_in_gpgone = 0; /* Keep track of "@ifclear gpgone" parts. */ - int not_in_man = 0; /* Keep track of "@ifclear isman" parts. */ int item_indent = 0; /* How far is the current @item indented. */ /* Helper to define a macro. */ @@ -883,7 +1045,7 @@ parse_file (const char *fname, FILE *fp, char **section_name, int in_pause) { size_t n = strlen (line); int got_line = 0; - char *p; + char *p, *pend; lnr++; if (!n || line[n-1] != '\n') @@ -930,26 +1092,12 @@ parse_file (const char *fname, FILE *fp, char **section_name, int in_pause) && !strncmp (p, "macro", 5) && (p[5]==' '||p[5]=='\t'||!p[5])) { - macro_t m; - if (macrovalueused) macrovalue[--macrovalueused] = 0; /* Kill the last LF. */ macrovalue[macrovalueused] = 0; /* Terminate macro. */ macrovalue = xrealloc (macrovalue, macrovalueused+1); - for (m= macrolist; m; m = m->next) - if (!strcmp (m->name, macroname)) - break; - if (m) - free (m->value); - else - { - m = xcalloc (1, sizeof *m + strlen (macroname)); - strcpy (m->name, macroname); - m->next = macrolist; - macrolist = m; - } - m->value = macrovalue; + set_macro (macroname, macrovalue); macrovalue = NULL; free (macroname); macroname = NULL; @@ -997,23 +1145,33 @@ parse_file (const char *fname, FILE *fp, char **section_name, int in_pause) if (n == 6 && !memcmp (line, "@ifset", 6) && (line[6]==' '||line[6]=='\t')) { - ifset_nesting++; - - if (!strncmp (p, "manverb", 7) && (p[7]==' '||p[7]=='\t'||!p[7])) + for (p=line+7; *p == ' ' || *p == '\t'; p++) + ; + if (!*p) { - if (in_verbatim) - err ("%s:%d: nested \"@ifset manverb\"", fname, lnr); - else - in_verbatim = ifset_nesting; + err ("%s:%d: name missing after \"@ifset\"", fname, lnr); + continue; } - else if (!strncmp (p, "gpgone", 6) - && (p[6]==' '||p[6]=='\t'||!p[6])) + for (pend=p; *pend && *pend != ' ' && *pend != '\t'; pend++) + ; + *pend = 0; /* Ignore rest of the line. */ + push_condition (p, 1, fname, lnr); + continue; + } + else if (n == 8 && !memcmp (line, "@ifclear", 8) + && (line[8]==' '||line[8]=='\t')) + { + for (p=line+9; *p == ' ' || *p == '\t'; p++) + ; + if (!*p) { - if (in_gpgone) - err ("%s:%d: nested \"@ifset gpgone\"", fname, lnr); - else - in_gpgone = ifset_nesting; + err ("%s:%d: name missing after \"@ifsclear\"", fname, lnr); + continue; } + for (pend=p; *pend && *pend != ' ' && *pend != '\t'; pend++) + ; + *pend = 0; /* Ignore rest of the line. */ + push_condition (p, 0, fname, lnr); continue; } else if (n == 4 && !memcmp (line, "@end", 4) @@ -1021,40 +1179,7 @@ parse_file (const char *fname, FILE *fp, char **section_name, int in_pause) && !strncmp (p, "ifset", 5) && (p[5]==' '||p[5]=='\t'||!p[5])) { - if (in_verbatim && ifset_nesting == in_verbatim) - in_verbatim = 0; - if (in_gpgone && ifset_nesting == in_gpgone) - in_gpgone = 0; - - if (ifset_nesting) - ifset_nesting--; - else - err ("%s:%d: unbalanced \"@end ifset\"", fname, lnr); - continue; - } - else if (n == 8 && !memcmp (line, "@ifclear", 8) - && (line[8]==' '||line[8]=='\t')) - { - ifclear_nesting++; - - if (!strncmp (p, "gpgone", 6) - && (p[6]==' '||p[6]=='\t'||!p[6])) - { - if (not_in_gpgone) - err ("%s:%d: nested \"@ifclear gpgone\"", fname, lnr); - else - not_in_gpgone = ifclear_nesting; - } - - else if (!strncmp (p, "isman", 5) - && (p[5]==' '||p[5]=='\t'||!p[5])) - { - if (not_in_man) - err ("%s:%d: nested \"@ifclear isman\"", fname, lnr); - else - not_in_man = ifclear_nesting; - } - + pop_condition (1, fname, lnr); continue; } else if (n == 4 && !memcmp (line, "@end", 4) @@ -1062,23 +1187,13 @@ parse_file (const char *fname, FILE *fp, char **section_name, int in_pause) && !strncmp (p, "ifclear", 7) && (p[7]==' '||p[7]=='\t'||!p[7])) { - if (not_in_gpgone && ifclear_nesting == not_in_gpgone) - not_in_gpgone = 0; - if (not_in_man && ifclear_nesting == not_in_man) - not_in_man = 0; - - if (ifclear_nesting) - ifclear_nesting--; - else - err ("%s:%d: unbalanced \"@end ifclear\"", fname, lnr); + pop_condition (0, fname, lnr); continue; } } /* Take action on ifset/ifclear. */ - if ( (in_gpgone && !gpgone_defined) - || (not_in_gpgone && gpgone_defined) - || not_in_man) + if (!cond_is_active) continue; /* Process commands. */ @@ -1090,7 +1205,7 @@ parse_file (const char *fname, FILE *fp, char **section_name, int in_pause) { skip_to_end = 0; } - else if (in_verbatim) + else if (cond_in_verbatim) { got_line = 1; } @@ -1182,7 +1297,7 @@ parse_file (const char *fname, FILE *fp, char **section_name, int in_pause) else if (!skip_to_end) got_line = 1; - if (got_line && in_verbatim) + if (got_line && cond_in_verbatim) add_content (*section_name, line, 1); else if (got_line && thepage.name && *section_name && !in_pause) add_content (*section_name, line, 0); @@ -1201,6 +1316,8 @@ top_parse_file (const char *fname, FILE *fp) { char *section_name = NULL; /* Name of the current section or NULL if not in a section. */ + macro_t m; + while (macrolist) { macro_t next = macrolist->next; @@ -1208,6 +1325,10 @@ top_parse_file (const char *fname, FILE *fp) free (macrolist); macrolist = next; } + for (m=predefinedmacrolist; m; m = m->next) + set_macro (m->name, xstrdup ("1")); + cond_is_active = 1; + cond_in_verbatim = 0; parse_file (fname, fp, §ion_name, 0); free (section_name); @@ -1223,6 +1344,12 @@ main (int argc, char **argv) opt_source = "GNU"; opt_release = ""; + /* Define default macros. The trick is that these macros are not + defined when using the actual texinfo renderer. */ + add_predefined_macro ("isman"); + add_predefined_macro ("manverb"); + + /* Option parsing. */ if (argc) { argc--; argv++; @@ -1327,8 +1454,7 @@ main (int argc, char **argv) argc--; argv++; if (argc) { - if (!strcmp (*argv, "gpgone")) - gpgone_defined = 1; + add_predefined_macro (*argv); argc--; argv++; } }