diff options
author | Werner Koch <wk@gnupg.org> | 2011-09-15 11:43:10 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2011-09-15 11:43:10 +0200 |
commit | f054da9ac1ad7e08c4520a3e0fcf94c25aeffba4 (patch) | |
tree | 830a7c468ad3d487782b04179def9a89a45c13d2 /doc | |
parent | 4bd0620ef61e705f64eb43e43ddaf91b89459bd3 (diff) | |
download | libgcrypt-f054da9ac1ad7e08c4520a3e0fcf94c25aeffba4.tar.gz |
Add a man page for hmac256.
We also include the man page in the manual.
Diffstat (limited to 'doc')
-rw-r--r-- | doc/ChangeLog | 6 | ||||
-rw-r--r-- | doc/Makefile.am | 39 | ||||
-rw-r--r-- | doc/gcrypt.texi | 87 | ||||
-rw-r--r-- | doc/yat2m.c | 1327 |
4 files changed, 1457 insertions, 2 deletions
diff --git a/doc/ChangeLog b/doc/ChangeLog index 992c63bd..15c65a76 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,9 @@ +2011-09-15 Werner Koch <wk@g10code.com> + + * Makefile.am: Add code to build a man page for hmac256. + * yat2m.c: New. Taken from GnuPG. + * gcrypt.text (hmac256): New section. + 2009-10-28 Werner Koch <wk@g10code.com> * gcrypt.texi (Multi-Threading): Add examples. diff --git a/doc/Makefile.am b/doc/Makefile.am index fc12745a..a6bd2ae4 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -20,9 +20,11 @@ EXTRA_DIST = README.apichanges HACKING \ libgcrypt-modules.eps fips-fsm.eps \ libgcrypt-modules.png fips-fsm.png \ - libgcrypt-modules.pdf fips-fsm.pdf + libgcrypt-modules.pdf fips-fsm.pdf \ + yat2m.c -DISTCLEANFILES = gcrypt.cps +DISTCLEANFILES = gcrypt.cps yat2m-stamp.tmp yat2m-stamp $(myman_pages) +CLEANFILES = yat2m BUILT_SOURCES = libgcrypt-modules.eps fips-fsm.eps \ libgcrypt-modules.png fips-fsm.png \ @@ -31,6 +33,16 @@ BUILT_SOURCES = libgcrypt-modules.eps fips-fsm.eps \ info_TEXINFOS = gcrypt.texi gcrypt_TEXINFOS = lgpl.texi gpl.texi libgcrypt-modules.fig fips-fsm.fig +YAT2M_OPTIONS = -I $(srcdir) \ + --release "Libgcrypt @PACKAGE_VERSION@" --source "Libgcrypt" + +myman_sources = gcrypt.texi +myman_pages = hmac256.1 + +man_MANS = $(myman_pages) + +yat2m: yat2m.c + $(CC_FOR_BUILD) -o $@ $(srcdir)/yat2m.c .fig.png: fig2dev -L png `test -f '$<' || echo '$(srcdir)/'`$< $@ @@ -44,6 +56,29 @@ gcrypt_TEXINFOS = lgpl.texi gpl.texi libgcrypt-modules.fig fips-fsm.fig .fig.pdf: fig2dev -L pdf `test -f '$<' || echo '$(srcdir)/'`$< $@ +yat2m-stamp: $(myman_sources) + @rm -f yat2m-stamp.tmp + @touch yat2m-stamp.tmp + for file in $(myman_sources) ; do \ + ./yat2m $(YAT2M_OPTIONS) --store \ + `test -f '$$file' || echo '$(srcdir)/'`$$file ; done + @mv -f yat2m-stamp.tmp $@ + +yat2m-stamp: yat2m + +$(myman_pages) : yat2m-stamp + @if test -f $@; then :; else \ + trap 'rm -rf yat2m-stamp yat2m-lock' 1 2 13 15; \ + if mkdir yat2m-lock 2>/dev/null; then \ + rm -f yat2m-stamp; \ + $(MAKE) $(AM_MAKEFLAGS) yat2m-stamp; \ + rmdir yat2m-lock; \ + else \ + while test -d yat2m-lock; do sleep 1; done; \ + test -f yat2m-stamp; exit $$?; \ + fi; \ + fi + # Make sure that gcrypt.texi is touched if any other source file has # been modified. This is required so that the version.texi magic diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi index 561a8261..adfef27e 100644 --- a/doc/gcrypt.texi +++ b/doc/gcrypt.texi @@ -28,6 +28,23 @@ section entitled ``GNU General Public License''. * libgcrypt: (gcrypt). Cryptographic function library. @end direntry +@c A couple of macros with no effect on texinfo +@c but used by the yat2m processor. +@macro manpage {a} +@end macro +@macro mansect {a} +@end macro +@macro manpause +@end macro +@macro mancont +@end macro + +@c +@c Printing stuff taken from gcc. +@c +@macro gnupgtabopt{body} +@code{\body\} +@end macro @c @@ -74,6 +91,7 @@ section entitled ``GNU General Public License''. * MPI library:: How to work with multi-precision-integers. * Prime numbers:: How to use the Prime number related functions. * Utilities:: Utility functions. +* Tools:: Utility tools * Architecture:: How Libgcrypt works internally. Appendices @@ -4856,6 +4874,75 @@ Release the memory area pointed to by @var{p}. @end deftypefun @c ********************************************************** +@c ********************* Tools **************************** +@c ********************************************************** +@node Tools +@chapter Tools + +@menu +* hmac256:: A standalone HMAC-SHA-256 implementation +@end menu + +@manpage hmac256.1 +@node hmac256 +@section A HMAC-SHA-256 tool +@ifset manverb +.B hmac256 +\- Compute an HMAC-SHA-256 MAC +@end ifset + +@mansect synopsis +@ifset manverb +.B hmac256 +.RB [ \-\-binary ] +.I key +.I [FILENAME] +@end ifset + +@mansect description +This is a standalone HMAC-SHA-256 implementation used to compute an +HMAC-SHA-256 message authentication code. The tool has originally +been developed as a second implementation for Libgcrypt to allow +comparing against the primary implementation and to be used for +internal consistency checks. It should not be used for sensitive data +because no mechanisms to clear the stack etc are used. + +The code has been written in a highly portable manner and requires +only a few standard definitions to be provided in a config.h file. + +@noindent +@command{hmac256} is commonly invoked as + +@example +hmac256 "This is my key" foo.txt +@end example + +@noindent +This compute the MAC on the file @file{foo.txt} using the key given on +the command line. + +@mansect options +@noindent +@command{hmac256} understands these options: + +@table @gnupgtabopt + +@item --binary +Print the MAC as a binary string. The default is to print the MAC +encoded has lower case hex digits. + +@item --version +Print version of the program and exit. + +@end table + +@mansect see also +@ifset isman +@command{sha256sum}(1) +@end ifset +@manpause + +@c ********************************************************** @c ***************** Architecure Overview ***************** @c ********************************************************** @node Architecture diff --git a/doc/yat2m.c b/doc/yat2m.c new file mode 100644 index 00000000..9d7bdec9 --- /dev/null +++ b/doc/yat2m.c @@ -0,0 +1,1327 @@ +/* yat2m.c - Yet Another Texi 2 Man converter + * Copyright (C) 2005 g10 Code GmbH + * Copyright (C) 2006, 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + This is a simple textinfo 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. + + To use this you need to add the following macros into your texinfo + source: + + @macro manpage {a} + @end macro + @macro mansect {a} + @end macro + @macro manpause + @end macro + @macro mancont + @end macro + + They are used by yat2m to select parts of the Texinfo which should + go into the man page. These macros need to be used without leading + left space. Processing starts after a "manpage" macro has been + seen. "mansect" identifies the section and yat2m make sure to + emit the sections in the proper order. Note that @mansect skips + the next input line if that line begins with @section, @subsection or + @chapheading. + + To insert verbatim troff markup, the following texinfo code may be + used: + + @ifset manverb + .B whateever you want + @end ifset + + alternativly a special comment may be used: + + @c man:.B whatever you want + + This is useful in case you need just one line. If you want to + include parts only in the man page but keep the texinfo + translation you may use: + + @ifset isman + stuff to be rendered only on man pages + @end ifset + + or to exclude stuff from man pages: + + @ifclear isman + stuff not to be rendered on man pages + @end ifclear + + the keyword @section is ignored, however @subsection gets rendered + as ".SS". @menu is completely skipped. Several man pages may be + extracted from one file, either using the --store or the --select + option. + + +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> +#include <assert.h> +#include <ctype.h> +#include <time.h> + + +#define PGM "yat2m" +#define VERSION "1.0" + +/* The maximum length of a line including the linefeed and one extra + character. */ +#define LINESIZE 1024 + +/* Option flags. */ +static int verbose; +static int quiet; +static int debug; +static const char *opt_source; +static const char *opt_release; +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; + + +/* Object to keep macro definitions. */ +struct macro_s +{ + struct macro_s *next; + char *value; /* Malloced value. */ + char name[1]; +}; +typedef struct macro_s *macro_t; + +/* List of all defined macros. */ +static macro_t macrolist; + + +/* Object to store one line of content. */ +struct line_buffer_s +{ + struct line_buffer_s *next; + int verbatim; /* True if LINE contains verbatim data. The default + is Texinfo source. */ + char *line; +}; +typedef struct line_buffer_s *line_buffer_t; + + +/* Object to collect the data of a section. */ +struct section_buffer_s +{ + char *name; /* Malloced name of the section. This may be + NULL to indicate this slot is not used. */ + line_buffer_t lines; /* Linked list with the lines of the section. */ + line_buffer_t *lines_tail; /* Helper for faster appending to the + linked list. */ + line_buffer_t last_line; /* Points to the last line appended. */ +}; +typedef struct section_buffer_s *section_buffer_t; + +/* Variable to keep info about the current page together. */ +static struct +{ + /* Filename of the current page or NULL if no page is active. Malloced. */ + char *name; + + /* Number of allocated elements in SECTIONS below. */ + size_t n_sections; + /* Array with the data of the sections. */ + section_buffer_t sections; + +} thepage; + + +/* The list of standard section names. COMMANDS and ASSUAN are GnuPG + specific. */ +static const char * const standard_sections[] = + { "NAME", "SYNOPSIS", "DESCRIPTION", + "RETURN VALUE", "EXIT STATUS", "ERROR HANDLING", "ERRORS", + "COMMANDS", "OPTIONS", "USAGE", "EXAMPLES", "FILES", + "ENVIRONMENT", "DIAGNOSTICS", "SECURITY", "CONFORMING TO", + "ASSUAN", "NOTES", "BUGS", "AUTHOR", "SEE ALSO", NULL }; + + +/*-- Local prototypes. --*/ +static void proc_texi_buffer (FILE *fp, const char *line, size_t len, + int *table_level, int *eol_action); + + + +/* Print diagnostic message and exit with failure. */ +static void +die (const char *format, ...) +{ + va_list arg_ptr; + + fflush (stdout); + fprintf (stderr, "%s: ", PGM); + + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + putc ('\n', stderr); + + exit (1); +} + + +/* Print diagnostic message. */ +static void +err (const char *format, ...) +{ + va_list arg_ptr; + + fflush (stdout); + if (strncmp (format, "%s:%d:", 6)) + fprintf (stderr, "%s: ", PGM); + + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + putc ('\n', stderr); + any_error = 1; +} + +/* Print diagnostic message. */ +static void +inf (const char *format, ...) +{ + va_list arg_ptr; + + fflush (stdout); + fprintf (stderr, "%s: ", PGM); + + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + putc ('\n', stderr); +} + + +static void * +xmalloc (size_t n) +{ + void *p = malloc (n); + if (!p) + die ("out of core: %s", strerror (errno)); + return p; +} + +static void * +xcalloc (size_t n, size_t m) +{ + void *p = calloc (n, m); + if (!p) + die ("out of core: %s", strerror (errno)); + return p; +} + +static void * +xrealloc (void *old, size_t n) +{ + void *p = realloc (old, n); + if (!p) + die ("out of core: %s", strerror (errno)); + return p; +} + +static char * +xstrdup (const char *string) +{ + void *p = malloc (strlen (string)+1); + if (!p) + die ("out of core: %s", strerror (errno)); + strcpy (p, string); + return p; +} + + +/* Uppercase the ascii characters in STRING. */ +static char * +ascii_strupr (char *string) +{ + char *p; + + for (p = string; *p; p++) + if (!(*p & 0x80)) + *p = toupper (*p); + return string; +} + + +/* Return the current date as an ISO string. */ +const char * +isodatestring (void) +{ + static char buffer[11+5]; + struct tm *tp; + time_t atime = time (NULL); + + if (atime < 0) + strcpy (buffer, "????" "-??" "-??"); + else + { + tp = gmtime (&atime); + sprintf (buffer,"%04d-%02d-%02d", + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); + } + return buffer; +} + + + +/* 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. */ +static section_buffer_t +get_section_buffer (const char *name) +{ + int i; + section_buffer_t sect; + + /* If there is no section we put everything into the required NAME + section. Given that this is the first one listed it is likely + that error are easily visible. */ + if (!name) + name = "NAME"; + + for (i=0; i < thepage.n_sections; i++) + { + sect = thepage.sections + i; + if (sect->name && !strcmp (name, sect->name)) + return sect; + } + for (i=0; i < thepage.n_sections; i++) + if (!thepage.sections[i].name) + break; + if (i < thepage.n_sections) + sect = thepage.sections + i; + else + { + /* We need to allocate or reallocate the section array. */ + size_t old_n = thepage.n_sections; + size_t new_n = 20; + + if (!old_n) + thepage.sections = xcalloc (new_n, sizeof *thepage.sections); + else + { + thepage.sections = xrealloc (thepage.sections, + ((old_n + new_n) + * sizeof *thepage.sections)); + memset (thepage.sections + old_n, 0, + new_n * sizeof *thepage.sections); + } + thepage.n_sections += new_n; + + /* Setup the tail pointers. */ + for (i=old_n; i < thepage.n_sections; i++) + { + sect = thepage.sections + i; + sect->lines_tail = §->lines; + } + sect = thepage.sections + old_n; + } + + /* Store the name. */ + assert (!sect->name); + sect->name = xstrdup (name); + return sect; +} + + + +/* Add the content of LINE to the section named SECTNAME. */ +static void +add_content (const char *sectname, char *line, int verbatim) +{ + section_buffer_t sect; + line_buffer_t lb; + + sect = get_section_buffer (sectname); + if (sect->last_line && !sect->last_line->verbatim == !verbatim) + { + /* Lets append that line to the last one. We do this to keep + all lines of the same kind (i.e.verbatim or not) together in + one large buffer. */ + size_t n1, n; + + lb = sect->last_line; + n1 = strlen (lb->line); + n = n1 + 1 + strlen (line) + 1; + lb->line = xrealloc (lb->line, n); + strcpy (lb->line+n1, "\n"); + strcpy (lb->line+n1+1, line); + } + else + { + lb = xcalloc (1, sizeof *lb); + lb->verbatim = verbatim; + lb->line = xstrdup (line); + sect->last_line = lb; + *sect->lines_tail = lb; + sect->lines_tail = &lb->next; + } +} + + +/* Prepare for a new man page using the filename NAME. */ +static void +start_page (char *name) +{ + if (verbose) + inf ("starting page `%s'", name); + assert (!thepage.name); + thepage.name = xstrdup (name); + thepage.n_sections = 0; +} + + +/* Write the .TH entry of the current page. Return -1 if there is a + problem with the page. */ +static int +write_th (FILE *fp) +{ + char *name, *p; + + fputs (".\\\" Created from Texinfo source by yat2m " VERSION "\n", fp); + + name = ascii_strupr (xstrdup (thepage.name)); + p = strrchr (name, '.'); + if (!p || !p[1]) + { + err ("no section name in man page `%s'", thepage.name); + free (name); + return -1; + } + *p++ = 0; + fprintf (fp, ".TH %s %s %s \"%s\" \"%s\"\n", + name, p, isodatestring (), opt_release, opt_source); + return 0; +} + + +/* Process the texinfo command COMMAND (without the leading @) and + write output if needed to FP. REST is the remainer of the line + which should either point to an opening brace or to a white space. + The function returns the number of characters already processed + from REST. LEN is the usable length of REST. TABLE_LEVEL is used to + control the indentation of tables. */ +static size_t +proc_texi_cmd (FILE *fp, const char *command, const char *rest, size_t len, + int *table_level, int *eol_action) +{ + static struct { + const char *name; /* Name of the command. */ + int what; /* What to do with this command. */ + const char *lead_in; /* String to print with a opening brace. */ + const char *lead_out;/* String to print with the closing brace. */ + } cmdtbl[] = { + { "command", 0, "\\fB", "\\fR" }, + { "code", 0, "\\fB", "\\fR" }, + { "sc", 0, "\\fB", "\\fR" }, + { "var", 0, "\\fI", "\\fR" }, + { "samp", 0, "\\(aq", "\\(aq" }, + { "file", 0, "\\(oq\\fI","\\fR\\(cq" }, + { "env", 0, "\\(oq\\fI","\\fR\\(cq" }, + { "acronym", 0 }, + { "dfn", 0 }, + { "option", 0, "\\fB", "\\fR" }, + { "example", 1, ".RS 2\n.nf\n" }, + { "smallexample", 1, ".RS 2\n.nf\n" }, + { "asis", 7 }, + { "anchor", 7 }, + { "cartouche", 1 }, + { "xref", 0, "see: [", "]" }, + { "pxref", 0, "see: [", "]" }, + { "uref", 0, "(\\fB", "\\fR)" }, + { "footnote",0, " ([", "])" }, + { "emph", 0, "\\fI", "\\fR" }, + { "w", 1 }, + { "c", 5 }, + { "opindex", 1 }, + { "cpindex", 1 }, + { "cindex", 1 }, + { "noindent", 0 }, + { "section", 1 }, + { "chapter", 1 }, + { "subsection", 6, "\n.SS " }, + { "chapheading", 0}, + { "item", 2, ".TP\n.B " }, + { "itemx", 2, ".TP\n.B " }, + { "table", 3 }, + { "itemize", 3 }, + { "bullet", 0, "* " }, + { "end", 4 }, + { "quotation",1, ".RS\n\\fB" }, + { NULL } + }; + size_t n; + int i; + const char *s; + const char *lead_out = NULL; + int ignore_args = 0; + + for (i=0; cmdtbl[i].name && strcmp (cmdtbl[i].name, command); i++) + ; + if (cmdtbl[i].name) + { + s = cmdtbl[i].lead_in; + if (s) + fputs (s, fp); + lead_out = cmdtbl[i].lead_out; + switch (cmdtbl[i].what) + { + case 1: /* Throw away the entire line. */ + s = memchr (rest, '\n', len); + return s? (s-rest)+1 : len; + case 2: /* Handle @item. */ + break; + case 3: /* Handle table. */ + if (++(*table_level) > 1) + fputs (".RS\n", fp); + /* Now throw away the entire line. */ + s = memchr (rest, '\n', len); + return s? (s-rest)+1 : len; + break; + case 4: /* Handle end. */ + for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--) + ; + if (n >= 5 && !memcmp (s, "table", 5) + && (!n || s[5] == ' ' || s[5] == '\t' || s[5] == '\n')) + { + if ((*table_level)-- > 1) + fputs (".RE\n", fp); + } + else if (n >= 7 && !memcmp (s, "example", 7) + && (!n || s[7] == ' ' || s[7] == '\t' || s[7] == '\n')) + { + fputs (".fi\n.RE\n", fp); + } + else if (n >= 12 && !memcmp (s, "smallexample", 12) + && (!n || s[12] == ' ' || s[12] == '\t' || s[12] == '\n')) + { + fputs (".fi\n.RE\n", fp); + } + else if (n >= 9 && !memcmp (s, "quotation", 9) + && (!n || s[9] == ' ' || s[9] == '\t' || s[9] == '\n')) + { + fputs ("\\fR\n.RE\n", fp); + } + /* Now throw away the entire line. */ + s = memchr (rest, '\n', len); + return s? (s-rest)+1 : len; + case 5: /* Handle special comments. */ + for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--) + ; + if (n >= 4 && !memcmp (s, "man:", 4)) + { + for (s+=4, n-=4; n && *s != '\n'; n--, s++) + putc (*s, fp); + putc ('\n', fp); + } + /* Now throw away the entire line. */ + s = memchr (rest, '\n', len); + return s? (s-rest)+1 : len; + case 6: + *eol_action = 1; + break; + case 7: + ignore_args = 1; + break; + default: + break; + } + } + else + { + macro_t m; + + for (m = macrolist; m ; m = m->next) + if (!strcmp (m->name, command)) + break; + if (m) + { + proc_texi_buffer (fp, m->value, strlen (m->value), + table_level, eol_action); + ignore_args = 1; /* Parameterized macros are not yet supported. */ + } + else + inf ("texinfo command `%s' not supported (%.*s)", command, + ((s = memchr (rest, '\n', len)), (s? (s-rest) : len)), rest); + } + + if (*rest == '{') + { + /* Find matching closing brace. */ + for (s=rest+1, n=1, i=1; i && *s && n < len; s++, n++) + if (*s == '{') + i++; + else if (*s == '}') + i--; + if (i) + { + err ("closing brace for command `%s' not found", command); + return len; + } + if (n > 2 && !ignore_args) + proc_texi_buffer (fp, rest+1, n-2, table_level, eol_action); + } + else + n = 0; + + if (lead_out) + fputs (lead_out, fp); + + return n; +} + + + +/* Process the string LINE with LEN bytes of Texinfo content. */ +static void +proc_texi_buffer (FILE *fp, const char *line, size_t len, + int *table_level, int *eol_action) +{ + const char *s; + char cmdbuf[256]; + int cmdidx = 0; + int in_cmd = 0; + size_t n; + + for (s=line; *s && len; s++, len--) + { + if (in_cmd) + { + if (in_cmd == 1) + { + switch (*s) + { + case '@': case '{': case '}': + putc (*s, fp); in_cmd = 0; + break; + case ':': /* Not ending a sentence flag. */ + in_cmd = 0; + break; + case '.': case '!': case '?': /* Ending a sentence. */ + putc (*s, fp); in_cmd = 0; + break; + case ' ': case '\t': case '\n': /* Non collapsing spaces. */ + putc (*s, fp); in_cmd = 0; + break; + default: + cmdidx = 0; + cmdbuf[cmdidx++] = *s; + in_cmd++; + break; + } + } + else if (*s == '{' || *s == ' ' || *s == '\t' || *s == '\n') + { + cmdbuf[cmdidx] = 0; + n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action); + assert (n <= len); + s += n; len -= n; + s--; len++; + in_cmd = 0; + } + else if (cmdidx < sizeof cmdbuf -1) + cmdbuf[cmdidx++] = *s; + else + { + err ("texinfo command too long - ignored"); + in_cmd = 0; + } + } + else if (*s == '@') + in_cmd = 1; + else if (*s == '\n') + { + switch (*eol_action) + { + case 1: /* Create a dummy paragraph. */ + fputs ("\n\\ \n", fp); + break; + default: + putc (*s, fp); + } + *eol_action = 0; + } + else if (*s == '\\') + fputs ("\\\\", fp); + else + putc (*s, fp); + } + + if (in_cmd > 1) + { + cmdbuf[cmdidx] = 0; + n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action); + assert (n <= len); + s += n; len -= n; + s--; len++; + in_cmd = 0; + } +} + + +/* Do something with the Texinfo line LINE. */ +static void +parse_texi_line (FILE *fp, const char *line, int *table_level) +{ + int eol_action = 0; + + /* A quick test whether there are any texinfo commands. */ + if (!strchr (line, '@')) + { + fputs (line, fp); + putc ('\n', fp); + return; + } + proc_texi_buffer (fp, line, strlen (line), table_level, &eol_action); + putc ('\n', fp); +} + + +/* Write all the lines LINES to FP. */ +static void +write_content (FILE *fp, line_buffer_t lines) +{ + line_buffer_t line; + int table_level = 0; + + for (line = lines; line; line = line->next) + { + if (line->verbatim) + { + fputs (line->line, fp); + putc ('\n', fp); + } + else + { +/* fputs ("TEXI---", fp); */ +/* fputs (line->line, fp); */ +/* fputs ("---\n", fp); */ + parse_texi_line (fp, line->line, &table_level); + } + } +} + + + +static int +is_standard_section (const char *name) +{ + int i; + const char *s; + + for (i=0; (s=standard_sections[i]); i++) + if (!strcmp (s, name)) + return 1; + return 0; +} + + +/* Finish a page; that is sort the data and write it out to the file. */ +static void +finish_page (void) +{ + FILE *fp; + section_buffer_t sect = NULL; + int idx; + const char *s; + int i; + + if (!thepage.name) + return; /* No page active. */ + + if (verbose) + inf ("finishing page `%s'", thepage.name); + + if (opt_select) + { + if (!strcmp (opt_select, thepage.name)) + { + inf ("selected `%s'", thepage.name ); + fp = stdout; + } + else + { + fp = fopen ( "/dev/null", "w" ); + if (!fp) + die ("failed to open /dev/null: %s\n", strerror (errno)); + } + } + else if (opt_store) + { + inf ("writing `%s'", thepage.name ); + fp = fopen ( thepage.name, "w" ); + if (!fp) + die ("failed to create `%s': %s\n", thepage.name, strerror (errno)); + } + else + fp = stdout; + + if (write_th (fp)) + goto leave; + + for (idx=0; (s=standard_sections[idx]); idx++) + { + for (i=0; i < thepage.n_sections; i++) + { + sect = thepage.sections + i; + if (sect->name && !strcmp (s, sect->name)) + break; + } + if (i == thepage.n_sections) + sect = NULL; + + if (sect) + { + fprintf (fp, ".SH %s\n", sect->name); + write_content (fp, sect->lines); + /* Now continue with all non standard sections directly + following this one. */ + for (i++; i < thepage.n_sections; i++) + { + sect = thepage.sections + i; + if (sect->name && is_standard_section (sect->name)) + break; + if (sect->name) + { + fprintf (fp, ".SH %s\n", sect->name); + write_content (fp, sect->lines); + } + } + + } + } + + + leave: + if (fp != stdout) + fclose (fp); + free (thepage.name); + thepage.name = NULL; + /* FIXME: Cleanup the content. */ +} + + + + +/* Parse one Texinfo file and create manpages according to the + embedded instructions. */ +static void +parse_file (const char *fname, FILE *fp, char **section_name, int in_pause) +{ + char *line; + 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. */ + + /* Helper to define a macro. */ + char *macroname = NULL; + char *macrovalue = NULL; + size_t macrovaluesize = 0; + size_t macrovalueused = 0; + + line = xmalloc (LINESIZE); + while (fgets (line, LINESIZE, fp)) + { + size_t n = strlen (line); + int got_line = 0; + char *p; + + lnr++; + if (!n || line[n-1] != '\n') + { + err ("%s:%d: trailing linefeed missing, line too long or " + "embedded Nul character", fname, lnr); + break; + } + line[--n] = 0; + + if (*line == '@') + { + for (p=line+1, n=1; *p && *p != ' ' && *p != '\t'; p++) + n++; + while (*p == ' ' || *p == '\t') + p++; + } + else + p = line; + + /* Take action on macro. */ + if (macroname) + { + if (n == 4 && !memcmp (line, "@end", 4) + && (line[4]==' '||line[4]=='\t'||!line[4]) + && !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; + macrovalue = NULL; + free (macroname); + macroname = NULL; + } + else + { + if (macrovalueused + strlen (line) + 2 >= macrovaluesize) + { + macrovaluesize += strlen (line) + 256; + macrovalue = xrealloc (macrovalue, macrovaluesize); + } + strcpy (macrovalue+macrovalueused, line); + macrovalueused += strlen (line); + macrovalue[macrovalueused++] = '\n'; + } + continue; + } + + + if (n >= 5 && !memcmp (line, "@node", 5) + && (line[5]==' '||line[5]=='\t'||!line[5])) + { + /* Completey ignore @node lines. */ + continue; + } + + + if (skip_sect_line) + { + skip_sect_line = 0; + if (!strncmp (line, "@section", 8) + || !strncmp (line, "@subsection", 11) + || !strncmp (line, "@chapheading", 12)) + continue; + } + + /* We only parse lines we need and ignore the rest. There are a + few macros used to control this as well as one @ifset + command. Parts we know about are saved away into containers + separate for each section. */ + + /* First process ifset/ifclear commands. */ + if (*line == '@') + { + 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])) + { + if (in_verbatim) + err ("%s:%d: nested \"@ifset manverb\"", fname, lnr); + else + in_verbatim = ifset_nesting; + } + else if (!strncmp (p, "gpgone", 6) + && (p[6]==' '||p[6]=='\t'||!p[6])) + { + if (in_gpgone) + err ("%s:%d: nested \"@ifset gpgone\"", fname, lnr); + else + in_gpgone = ifset_nesting; + } + continue; + } + else if (n == 4 && !memcmp (line, "@end", 4) + && (line[4]==' '||line[4]=='\t') + && !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; + } + + continue; + } + else if (n == 4 && !memcmp (line, "@end", 4) + && (line[4]==' '||line[4]=='\t') + && !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); + continue; + } + } + + /* Take action on ifset/ifclear. */ + if ( (in_gpgone && !gpgone_defined) + || (not_in_gpgone && gpgone_defined) + || not_in_man) + continue; + + /* Process commands. */ + if (*line == '@') + { + if (skip_to_end + && n == 4 && !memcmp (line, "@end", 4) + && (line[4]==' '||line[4]=='\t'||!line[4])) + { + skip_to_end = 0; + } + else if (in_verbatim) + { + got_line = 1; + } + else if (n == 6 && !memcmp (line, "@macro", 6)) + { + macroname = xstrdup (p); + macrovalue = xmalloc ((macrovaluesize = 1024)); + macrovalueused = 0; + } + else if (n == 8 && !memcmp (line, "@manpage", 8)) + { + free (*section_name); + *section_name = NULL; + finish_page (); + start_page (p); + in_pause = 0; + } + else if (n == 8 && !memcmp (line, "@mansect", 8)) + { + if (!thepage.name) + err ("%s:%d: section outside of a man page", fname, lnr); + else + { + free (*section_name); + *section_name = ascii_strupr (xstrdup (p)); + in_pause = 0; + skip_sect_line = 1; + } + } + else if (n == 9 && !memcmp (line, "@manpause", 9)) + { + if (!*section_name) + err ("%s:%d: pausing outside of a man section", fname, lnr); + else if (in_pause) + err ("%s:%d: already pausing", fname, lnr); + else + in_pause = 1; + } + else if (n == 8 && !memcmp (line, "@mancont", 8)) + { + if (!*section_name) + err ("%s:%d: continue outside of a man section", fname, lnr); + else if (!in_pause) + err ("%s:%d: continue while not pausing", fname, lnr); + else + in_pause = 0; + } + else if (n == 5 && !memcmp (line, "@menu", 5) + && (line[5]==' '||line[5]=='\t'||!line[5])) + { + skip_to_end = 1; + } + else if (n == 8 && !memcmp (line, "@include", 8) + && (line[8]==' '||line[8]=='\t'||!line[8])) + { + char *incname = xstrdup (p); + FILE *incfp = fopen (incname, "r"); + + if (!incfp && opt_include && *opt_include && *p != '/') + { + free (incname); + incname = xmalloc (strlen (opt_include) + 1 + + strlen (p) + 1); + strcpy (incname, opt_include); + if ( incname[strlen (incname)-1] != '/' ) + strcat (incname, "/"); + strcat (incname, p); + incfp = fopen (incname, "r"); + } + + if (!incfp) + err ("can't open include file `%s':%s", + incname, strerror (errno)); + else + { + parse_file (incname, incfp, section_name, in_pause); + fclose (incfp); + } + free (incname); + } + else if (n == 4 && !memcmp (line, "@bye", 4) + && (line[4]==' '||line[4]=='\t'||!line[4])) + { + break; + } + else if (!skip_to_end) + got_line = 1; + } + else if (!skip_to_end) + got_line = 1; + + if (got_line && in_verbatim) + add_content (*section_name, line, 1); + else if (got_line && thepage.name && *section_name && !in_pause) + add_content (*section_name, line, 0); + + } + if (ferror (fp)) + err ("%s:%d: read error: %s", fname, lnr, strerror (errno)); + free (macroname); + free (macrovalue); + free (line); +} + + +static void +top_parse_file (const char *fname, FILE *fp) +{ + char *section_name = NULL; /* Name of the current section or NULL + if not in a section. */ + while (macrolist) + { + macro_t m = macrolist->next; + free (m->value); + free (m); + macrolist = m; + } + + parse_file (fname, fp, §ion_name, 0); + free (section_name); + finish_page (); +} + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + + opt_source = "GNU"; + opt_release = ""; + + if (argc) + { + argc--; argv++; + } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + puts ( + "Usage: " PGM " [OPTION] [FILE]\n" + "Extract man pages from a Texinfo source.\n\n" + " --source NAME use NAME as source field\n" + " --release STRING use STRING as the release field\n" + " --store write output using @manpage name\n" + " --select NAME only output pages with @manpage NAME\n" + " --verbose enable extra informational output\n" + " --debug enable additional debug output\n" + " --help display this help and exit\n" + " -I DIR also search in include DIR\n" + " -D gpgone the only useable define\n\n" + "With no FILE, or when FILE is -, read standard input.\n\n" + "Report bugs to <bugs@g10code.com>."); + exit (0); + } + else if (!strcmp (*argv, "--version")) + { + puts (PGM " " VERSION "\n" + "Copyright (C) 2005 g10 Code GmbH\n" + "This program comes with ABSOLUTELY NO WARRANTY.\n" + "This is free software, and you are welcome to redistribute it\n" + "under certain conditions. See the file COPYING for details."); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--quiet")) + { + quiet = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose = debug = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--source")) + { + argc--; argv++; + if (argc) + { + opt_source = *argv; + argc--; argv++; + } + } + else if (!strcmp (*argv, "--release")) + { + argc--; argv++; + if (argc) + { + opt_release = *argv; + argc--; argv++; + } + } + else if (!strcmp (*argv, "--store")) + { + opt_store = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--select")) + { + argc--; argv++; + if (argc) + { + opt_select = strrchr (*argv, '/'); + if (opt_select) + opt_select++; + else + opt_select = *argv; + argc--; argv++; + } + } + else if (!strcmp (*argv, "-I")) + { + argc--; argv++; + if (argc) + { + opt_include = *argv; + argc--; argv++; + } + } + else if (!strcmp (*argv, "-D")) + { + argc--; argv++; + if (argc) + { + if (!strcmp (*argv, "gpgone")) + gpgone_defined = 1; + argc--; argv++; + } + } + } + + if (argc > 1) + die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n"); + + /* Start processing. */ + if (argc && strcmp (*argv, "-")) + { + FILE *fp = fopen (*argv, "rb"); + if (!fp) + die ("%s:0: can't open file: %s", *argv, strerror (errno)); + top_parse_file (*argv, fp); + fclose (fp); + } + else + top_parse_file ("-", stdin); + + return !!any_error; +} + + +/* +Local Variables: +compile-command: "gcc -Wall -g -Wall -o yat2m yat2m.c" +End: +*/ |