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 <wk@gnupg.org>
This commit is contained in:
Werner Koch 2013-10-28 07:05:41 +01:00
parent de7e15c6b8
commit a15c35f37e
1 changed files with 218 additions and 92 deletions

View File

@ -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, &section_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++;
}
}