From eae8f4ff2cd357d92c5e6aa901cf82cc4d344470 Mon Sep 17 00:00:00 2001 From: Kristaps Dzonsons Date: Mon, 23 Mar 2009 14:22:11 +0000 Subject: First addition of -man macro support. Abstraction of mdoc. --- Makefile | 120 ++--- action.c | 13 +- arch.c | 4 +- argv.c | 19 +- att.c | 4 +- hash.c | 175 ------- lib.c | 4 +- libman.h | 66 +++ libmdoc.h | 160 +++++++ macro.c | 1446 ---------------------------------------------------------- main.c | 192 ++++---- man.c | 368 +++++++++++++++ man.h | 97 ++++ man_hash.c | 58 +++ man_macro.c | 96 ++++ mdoc.7 | 9 +- mdoc.c | 50 +- mdoc.h | 4 +- mdoc_hash.c | 175 +++++++ mdoc_macro.c | 1446 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ msec.c | 4 +- private.h | 160 ------- st.c | 4 +- strings.c | 8 +- term.h | 4 +- validate.c | 4 +- vol.c | 4 +- xstd.c | 13 +- 28 files changed, 2693 insertions(+), 2014 deletions(-) delete mode 100644 hash.c create mode 100644 libman.h create mode 100644 libmdoc.h delete mode 100644 macro.c create mode 100644 man.c create mode 100644 man.h create mode 100644 man_hash.c create mode 100644 man_macro.c create mode 100644 mdoc_hash.c create mode 100644 mdoc_macro.c delete mode 100644 private.h diff --git a/Makefile b/Makefile index 5837b991..8dfebce4 100644 --- a/Makefile +++ b/Makefile @@ -17,30 +17,29 @@ CFLAGS += -W -Wall -Wstrict-prototypes -Wno-unused-parameter -g LINTFLAGS += $(VFLAGS) CFLAGS += $(VFLAGS) -# If you want to strip `Xo/Xc' macro pairs, enable this. Really, only -# OpenBSD should be doing this while it kicks its cruft. -#CFLAGS += -DSTRIP_XO -#LINTFLAGS += -DSTRIP_XO - -LIBLNS = macro.ln mdoc.ln hash.ln strings.ln xstd.ln argv.ln \ - validate.ln action.ln lib.ln att.ln arch.ln vol.ln \ - msec.ln st.ln -LIBOBJS = macro.o mdoc.o hash.o strings.o xstd.o argv.o validate.o \ - action.o lib.o att.o arch.o vol.o msec.o st.o -LIBSRCS = macro.c mdoc.c hash.c strings.c xstd.c argv.c validate.c \ - action.c lib.c att.c arch.c vol.c msec.c st.c +MDOCLNS = mdoc_macro.ln mdoc.ln mdoc_hash.ln strings.ln xstd.ln \ + argv.ln validate.ln action.ln lib.ln att.ln arch.ln \ + vol.ln msec.ln st.ln +MDOCOBJS = mdoc_macro.o mdoc.o mdoc_hash.o strings.o xstd.o argv.o \ + validate.o action.o lib.o att.o arch.o vol.o msec.o st.o +MDOCSRCS = mdoc_macro.c mdoc.c mdoc_hash.c strings.c xstd.c argv.c \ + validate.c action.c lib.c att.c arch.c vol.c msec.c st.c + +MANLNS = man_macro.ln man.ln man_hash.ln +MANOBJS = man_macro.o man.o man_hash.o +MANSRCS = man_macro.c man.c man_hash.c MAINLNS = main.ln term.ln ascii.ln terminal.ln tree.ln compat.ln MAINOBJS = main.o term.o ascii.o terminal.o tree.o compat.o MAINSRCS = main.c term.c ascii.c terminal.c tree.c compat.c -LLNS = llib-llibmdoc.ln llib-lmandoc.ln -LNS = $(MAINLNS) $(LIBLNS) -LIBS = libmdoc.a -OBJS = $(LIBOBJS) $(MAINOBJS) -SRCS = $(LIBSRCS) $(MAINSRCS) +LLNS = llib-llibmdoc.ln llib-llibman.ln llib-lmandoc.ln +LNS = $(MAINLNS) $(MDOCLNS) +LIBS = libmdoc.a libman.a +OBJS = $(MDOCOBJS) $(MAINOBJS) +SRCS = $(MDOCSRCS) $(MAINSRCS) DATAS = arch.in att.in lib.in msec.in st.in vol.in ascii.in -HEADS = mdoc.h private.h term.h +HEADS = mdoc.h libmdoc.h man.h libman.h term.h SGMLS = index.sgml HTMLS = index.html STATICS = style.css external.png @@ -91,41 +90,50 @@ uninstall: rm -f $(MANDIR)/man1/mandoc.1 rm -f $(MANDIR)/man7/mdoc.7 -lib.ln: lib.c lib.in private.h -lib.o: lib.c lib.in private.h +man_macro.ln: man_macro.c libman.h +man_macro.o: man_macro.c libman.h -att.ln: att.c att.in private.h -att.o: att.c att.in private.h +lib.ln: lib.c lib.in libmdoc.h +lib.o: lib.c lib.in libmdoc.h -arch.ln: arch.c arch.in private.h -arch.o: arch.c arch.in private.h +att.ln: att.c att.in libmdoc.h +att.o: att.c att.in libmdoc.h -vol.ln: vol.c vol.in private.h -vol.o: vol.c vol.in private.h +arch.ln: arch.c arch.in libmdoc.h +arch.o: arch.c arch.in libmdoc.h + +vol.ln: vol.c vol.in libmdoc.h +vol.o: vol.c vol.in libmdoc.h ascii.ln: ascii.c ascii.in term.h ascii.o: ascii.c ascii.in term.h -msec.ln: msec.c msec.in private.h -msec.o: msec.c msec.in private.h +msec.ln: msec.c msec.in libmdoc.h +msec.o: msec.c msec.in libmdoc.h -st.ln: st.c st.in private.h -st.o: st.c st.in private.h +st.ln: st.c st.in libmdoc.h +st.o: st.c st.in libmdoc.h -macro.ln: macro.c private.h -macro.o: macro.c private.h +mdoc_macro.ln: mdoc_macro.c libmdoc.h +mdoc_macro.o: mdoc_macro.c libmdoc.h term.ln: term.c term.h term.o: term.c term.h -strings.ln: strings.c private.h -strings.o: strings.c private.h +strings.ln: strings.c libmdoc.h +strings.o: strings.c libmdoc.h + +man_hash.ln: man_hash.c libman.h +man_hash.o: man_hash.c libman.h + +mdoc_hash.ln: mdoc_hash.c libmdoc.h +mdoc_hash.o: mdoc_hash.c libmdoc.h -hash.ln: hash.c private.h -hash.o: hash.c private.h +mdoc.ln: mdoc.c libmdoc.h +mdoc.o: mdoc.c libmdoc.h -mdoc.ln: mdoc.c private.h -mdoc.o: mdoc.c private.h +man.ln: man.c libman.h +man.o: man.c libman.h main.ln: main.c mdoc.h main.o: main.c mdoc.h @@ -133,19 +141,19 @@ main.o: main.c mdoc.h terminal.ln: terminal.c term.h terminal.o: terminal.c term.h -xstd.ln: xstd.c private.h -xstd.o: xstd.c private.h +xstd.ln: xstd.c libmdoc.h +xstd.o: xstd.c libmdoc.h -argv.ln: argv.c private.h -argv.o: argv.c private.h +argv.ln: argv.c libmdoc.h +argv.o: argv.c libmdoc.h -validate.ln: validate.c private.h -validate.o: validate.c private.h +validate.ln: validate.c libmdoc.h +validate.o: validate.c libmdoc.h -action.ln: action.c private.h -action.o: action.c private.h +action.ln: action.c libmdoc.h +action.o: action.c libmdoc.h -private.h: mdoc.h +libmdoc.h: mdoc.h term.h: mdoc.h @@ -185,17 +193,23 @@ mdocml-$(VERSION).tar.gz: $(INSTALL) ( cd .dist/mdocml/ && tar zcf ../../$@ mdocml-$(VERSION)/ ) rm -rf .dist/ -llib-llibmdoc.ln: $(LIBLNS) - $(LINT) $(LINTFLAGS) -Clibmdoc $(LIBLNS) +llib-llibmdoc.ln: $(MDOCLNS) + $(LINT) $(LINTFLAGS) -Clibmdoc $(MDOCLNS) + +llib-llibman.ln: $(MANLNS) + $(LINT) $(LINTFLAGS) -Clibman $(MANLNS) llib-lmandoc.ln: $(MAINLNS) llib-llibmdoc.ln $(LINT) $(LINTFLAGS) -Cmandoc $(MAINLNS) llib-llibmdoc.ln -libmdoc.a: $(LIBOBJS) - $(AR) rs $@ $(LIBOBJS) +libmdoc.a: $(MDOCOBJS) + $(AR) rs $@ $(MDOCOBJS) + +libman.a: $(MANOBJS) + $(AR) rs $@ $(MANOBJS) -mandoc: $(MAINOBJS) libmdoc.a - $(CC) $(CFLAGS) -o $@ $(MAINOBJS) libmdoc.a +mandoc: $(MAINOBJS) libmdoc.a libman.a + $(CC) $(CFLAGS) -o $@ $(MAINOBJS) libmdoc.a libman.a .sgml.html: validate $< diff --git a/action.c b/action.c index 77b57e88..3b0c33c2 100644 --- a/action.c +++ b/action.c @@ -1,4 +1,4 @@ -/* $Id: action.c,v 1.49 2009/03/21 21:09:00 kristaps Exp $ */ +/* $Id: action.c,v 1.50 2009/03/23 14:22:11 kristaps Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons * @@ -24,7 +24,7 @@ #include #include -#include "private.h" +#include "libmdoc.h" /* * Actions are executed on macros after they've been post-validated: in @@ -272,7 +272,10 @@ post_std(POST_ARGS) assert(m->meta.name); - m->last->args->argv[0].value = xcalloc(1, sizeof(char *)); + m->last->args->argv[0].value = calloc(1, sizeof(char *)); + if (NULL == m->last->args->argv[0].value) + err(1, "calloc"); + m->last->args->argv[0].sz = 1; m->last->args->argv[0].value[0] = xstrdup(m->meta.name); return(1); @@ -493,7 +496,9 @@ post_bl_tagwidth(struct mdoc *m) n->args->argv[n->args->argc - 1].line = m->last->line; n->args->argv[n->args->argc - 1].pos = m->last->pos; n->args->argv[n->args->argc - 1].sz = 1; - n->args->argv[n->args->argc - 1].value = xcalloc(1, sizeof(char *)); + n->args->argv[n->args->argc - 1].value = calloc(1, sizeof(char *)); + if (NULL == n->args->argv[n->args->argc - 1].value) + err(1, "calloc"); n->args->argv[n->args->argc - 1].value[0] = xstrdup(buf); return(1); diff --git a/arch.c b/arch.c index 9fc1c4e1..64cb93fb 100644 --- a/arch.c +++ b/arch.c @@ -1,4 +1,4 @@ -/* $Id: arch.c,v 1.1 2009/03/16 22:19:19 kristaps Exp $ */ +/* $Id: arch.c,v 1.2 2009/03/23 14:22:11 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -19,7 +19,7 @@ #include #include -#include "private.h" +#include "libmdoc.h" #define LINE(x, y) \ if (0 == strcmp(p, x)) return(y); diff --git a/argv.c b/argv.c index ead3cd9a..db5b7ea4 100644 --- a/argv.c +++ b/argv.c @@ -1,4 +1,4 @@ -/* $Id: argv.c,v 1.55 2009/03/21 21:09:00 kristaps Exp $ */ +/* $Id: argv.c,v 1.56 2009/03/23 14:22:11 kristaps Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons * @@ -25,7 +25,7 @@ #include #include -#include "private.h" +#include "libmdoc.h" /* * Routines to parse arguments of macros. Arguments follow the syntax @@ -294,7 +294,8 @@ mdoc_argv(struct mdoc *mdoc, int line, int tok, return(ARGV_ERROR); if (NULL == (arg = *v)) { - *v = xcalloc(1, sizeof(struct mdoc_arg)); + if (NULL == (*v = calloc(1, sizeof(struct mdoc_arg)))) + err(1, "calloc"); arg = *v; } @@ -792,8 +793,10 @@ argv_opt_single(struct mdoc *mdoc, int line, return(1); v->sz = 1; - v->value = xcalloc(1, sizeof(char *)); - v->value[0] = xstrdup(p); + if (NULL == (v->value = calloc(1, sizeof(char *)))) + err(1, "calloc"); + if (NULL == (v->value[0] = strdup(p))) + err(1, "strdup"); return(1); } @@ -817,8 +820,10 @@ argv_single(struct mdoc *mdoc, int line, return(perr(mdoc, line, ppos, EARGVAL)); v->sz = 1; - v->value = xcalloc(1, sizeof(char *)); - v->value[0] = xstrdup(p); + if (NULL == (v->value = calloc(1, sizeof(char *)))) + err(1, "calloc"); + if (NULL == (v->value[0] = strdup(p))) + err(1, "strdup"); return(1); } diff --git a/att.c b/att.c index 573bb6cb..5d019d04 100644 --- a/att.c +++ b/att.c @@ -1,4 +1,4 @@ -/* $Id: att.c,v 1.1 2009/03/16 22:19:19 kristaps Exp $ */ +/* $Id: att.c,v 1.2 2009/03/23 14:22:11 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -19,7 +19,7 @@ #include #include -#include "private.h" +#include "libmdoc.h" #define LINE(x, y) \ if (0 == strcmp(p, x)) return(y); diff --git a/hash.c b/hash.c deleted file mode 100644 index 6301c291..00000000 --- a/hash.c +++ /dev/null @@ -1,175 +0,0 @@ -/* $Id: hash.c,v 1.11 2009/03/16 23:37:28 kristaps Exp $ */ -/* - * Copyright (c) 2008, 2009 Kristaps Dzonsons - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the - * above copyright notice and this permission notice appear in all - * copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE - * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL - * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR - * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ -#include -#include -#include -#include -#include -#include - -#include "private.h" - -/* - * Routines for the perfect-hash hashtable used by the parser to look up - * tokens by their string-ified names (`.Fl' -> MDOC_Fl). The - * allocation penalty for this is 27 * 26 * sizeof(ptr). - */ - -void -mdoc_tokhash_free(void *htab) -{ - - free(htab); -} - - -void * -mdoc_tokhash_alloc(void) -{ - int i, major, minor, ind; - const void **htab; - - htab = calloc(27 * 26 * 3, sizeof(struct mdoc_macro *)); - if (NULL == htab) - err(1, "calloc"); - - for (i = 1; i < MDOC_MAX; i++) { - major = mdoc_macronames[i][0]; - assert((major >= 65 && major <= 90) || - major == 37); - - if (major == 37) - major = 0; - else - major -= 64; - - minor = mdoc_macronames[i][1]; - assert((minor >= 65 && minor <= 90) || - (minor == 49) || - (minor >= 97 && minor <= 122)); - - if (minor == 49) - minor = 0; - else if (minor <= 90) - minor -= 65; - else - minor -= 97; - - assert(major >= 0 && major < 27); - assert(minor >= 0 && minor < 26); - - ind = (major * 27 * 3) + (minor * 3); - - if (NULL == htab[ind]) { - htab[ind] = &mdoc_macros[i]; - continue; - } - - if (NULL == htab[++ind]) { - htab[ind] = &mdoc_macros[i]; - continue; - } - - assert(NULL == htab[++ind]); - htab[ind] = &mdoc_macros[i]; - } - - return((void *)htab); -} - - -int -mdoc_tokhash_find(const void *arg, const char *tmp) -{ - int major, minor, ind, slot; - const void **htab; - - htab = /* LINTED */ - (const void **)arg; - - if (0 == tmp[0] || 0 == tmp[1]) - return(MDOC_MAX); - if (tmp[2] && tmp[3]) - return(MDOC_MAX); - - if ( ! (tmp[0] == 37 || (tmp[0] >= 65 && tmp[0] <= 90))) - return(MDOC_MAX); - - if ( ! ((tmp[1] >= 65 && tmp[1] <= 90) || - (tmp[1] == 49) || - (tmp[1] >= 97 && tmp[1] <= 122))) - return(MDOC_MAX); - - if (tmp[0] == 37) - major = 0; - else - major = tmp[0] - 64; - - if (tmp[1] == 49) - minor = 0; - else if (tmp[1] <= 90) - minor = tmp[1] - 65; - else - minor = tmp[1] - 97; - - ind = (major * 27 * 3) + (minor * 3); - if (ind < 0 || ind >= (27 * 26 * 3)) - return(MDOC_MAX); - - if (htab[ind]) { - slot = htab[ind] - /* LINTED */ - (void *)mdoc_macros; - assert(0 == (size_t)slot % sizeof(struct mdoc_macro)); - slot /= sizeof(struct mdoc_macro); - if (mdoc_macronames[slot][0] == tmp[0] && - mdoc_macronames[slot][1] == tmp[1] && - (0 == tmp[2] || - mdoc_macronames[slot][2] == tmp[2])) - return(slot); - ind++; - } - - if (htab[ind]) { - slot = htab[ind] - /* LINTED */ - (void *)mdoc_macros; - assert(0 == (size_t)slot % sizeof(struct mdoc_macro)); - slot /= sizeof(struct mdoc_macro); - if (mdoc_macronames[slot][0] == tmp[0] && - mdoc_macronames[slot][1] == tmp[1] && - (0 == tmp[2] || - mdoc_macronames[slot][2] == tmp[2])) - return(slot); - ind++; - } - - if (NULL == htab[ind]) - return(MDOC_MAX); - slot = htab[ind] - /* LINTED */ - (void *)mdoc_macros; - assert(0 == (size_t)slot % sizeof(struct mdoc_macro)); - slot /= sizeof(struct mdoc_macro); - if (mdoc_macronames[slot][0] == tmp[0] && - mdoc_macronames[slot][1] == tmp[1] && - (0 == tmp[2] || - mdoc_macronames[slot][2] == tmp[2])) - return(slot); - - return(MDOC_MAX); -} - diff --git a/lib.c b/lib.c index 28b704cf..19cfeed6 100644 --- a/lib.c +++ b/lib.c @@ -1,4 +1,4 @@ -/* $Id: lib.c,v 1.1 2009/03/16 22:19:19 kristaps Exp $ */ +/* $Id: lib.c,v 1.2 2009/03/23 14:22:11 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -19,7 +19,7 @@ #include #include -#include "private.h" +#include "libmdoc.h" #define LINE(x, y) \ if (0 == strcmp(p, x)) return(y); diff --git a/libman.h b/libman.h new file mode 100644 index 00000000..b15f6107 --- /dev/null +++ b/libman.h @@ -0,0 +1,66 @@ +/* $Id: libman.h,v 1.1 2009/03/23 14:22:11 kristaps Exp $ */ +/* + * Copyright (c) 2009 Kristaps Dzonsons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef LIBMAN_H +#define LIBMAN_H + +#include "man.h" + +enum man_next { + MAN_NEXT_SIBLING = 0, + MAN_NEXT_CHILD +}; + +struct man { + void *htab; + int flags; +#define MAN_LITERAL (1 << 1) + enum man_next next; + struct man_node *last; + struct man_node *first; + struct man_meta meta; +}; + + +#define MACRO_PROT_ARGS struct man *man, int tok, int line, \ + int ppos, int *pos, char *buf + +struct man_macro { + int (*fp)(MACRO_PROT_ARGS); + int flags; +#define MDOC_PROLOGUE (1 << 0) +}; + +extern const struct man_macro *const man_macros; + +__BEGIN_DECLS + +int man_word_alloc(struct man *, int, int, const char *); +int man_elem_alloc(struct man *, int, int, int); +int man_block_alloc(struct man *, int, int, int); +int man_head_alloc(struct man *, int, int, int); +int man_body_alloc(struct man *, int, int, int); +void man_node_free(struct man_node *); +void man_node_freelist(struct man_node *); +void *man_hash_alloc(void); +int man_hash_find(const void *, const char *); +void man_hash_free(void *); + +__END_DECLS + +#endif /*!LIBMAN_H*/ diff --git a/libmdoc.h b/libmdoc.h new file mode 100644 index 00000000..722fde26 --- /dev/null +++ b/libmdoc.h @@ -0,0 +1,160 @@ +/* $Id: libmdoc.h,v 1.1 2009/03/23 14:22:11 kristaps Exp $ */ +/* + * Copyright (c) 2008, 2009 Kristaps Dzonsons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef LIBMDOC_H +#define LIBMDOC_H + +#include "mdoc.h" + +enum mdoc_next { + MDOC_NEXT_SIBLING = 0, + MDOC_NEXT_CHILD +}; + +struct mdoc { + void *data; + struct mdoc_cb cb; + void *htab; + int flags; +#define MDOC_HALT (1 << 0) +#define MDOC_LITERAL (1 << 1) + int pflags; + enum mdoc_next next; + struct mdoc_node *last; + struct mdoc_node *first; + struct mdoc_meta meta; + enum mdoc_sec lastnamed; + enum mdoc_sec lastsec; +}; + + +#define MACRO_PROT_ARGS struct mdoc *mdoc, int tok, int line, \ + int ppos, int *pos, char *buf + +struct mdoc_macro { + int (*fp)(MACRO_PROT_ARGS); + int flags; +#define MDOC_CALLABLE (1 << 0) +#define MDOC_PARSED (1 << 1) +#define MDOC_EXPLICIT (1 << 2) +#define MDOC_PROLOGUE (1 << 3) +#define MDOC_IGNDELIM (1 << 4) + /* Reserved words in arguments treated as text. */ +}; + +#define mdoc_nwarn(mdoc, node, type, fmt, ...) \ + mdoc_vwarn((mdoc), (node)->line, \ + (node)->pos, (type), (fmt), ##__VA_ARGS__) + +#define mdoc_nerr(mdoc, node, fmt, ...) \ + mdoc_verr((mdoc), (node)->line, \ + (node)->pos, (fmt), ##__VA_ARGS__) + +#define mdoc_warn(mdoc, type, fmt, ...) \ + mdoc_vwarn((mdoc), (mdoc)->last->line, \ + (mdoc)->last->pos, (type), (fmt), ##__VA_ARGS__) + +#define mdoc_err(mdoc, fmt, ...) \ + mdoc_verr((mdoc), (mdoc)->last->line, \ + (mdoc)->last->pos, (fmt), ##__VA_ARGS__) + +#define mdoc_msg(mdoc, fmt, ...) \ + mdoc_vmsg((mdoc), (mdoc)->last->line, \ + (mdoc)->last->pos, (fmt), ##__VA_ARGS__) + +#define mdoc_pmsg(mdoc, line, pos, fmt, ...) \ + mdoc_vmsg((mdoc), (line), \ + (pos), (fmt), ##__VA_ARGS__) + +#define mdoc_pwarn(mdoc, line, pos, type, fmt, ...) \ + mdoc_vwarn((mdoc), (line), \ + (pos), (type), (fmt), ##__VA_ARGS__) + +#define mdoc_perr(mdoc, line, pos, fmt, ...) \ + mdoc_verr((mdoc), (line), \ + (pos), (fmt), ##__VA_ARGS__) + +extern const struct mdoc_macro *const mdoc_macros; + +__BEGIN_DECLS + +int mdoc_vwarn(struct mdoc *, int, int, + enum mdoc_warn, const char *, ...); +void mdoc_vmsg(struct mdoc *, int, int, + const char *, ...); +int mdoc_verr(struct mdoc *, int, int, + const char *, ...); +int mdoc_macro(MACRO_PROT_ARGS); +int mdoc_word_alloc(struct mdoc *, + int, int, const char *); +int mdoc_elem_alloc(struct mdoc *, int, int, + int, struct mdoc_arg *); +int mdoc_block_alloc(struct mdoc *, int, int, + int, struct mdoc_arg *); +int mdoc_head_alloc(struct mdoc *, int, int, int); +int mdoc_tail_alloc(struct mdoc *, int, int, int); +int mdoc_body_alloc(struct mdoc *, int, int, int); +void mdoc_node_free(struct mdoc_node *); +void mdoc_node_freelist(struct mdoc_node *); +void *mdoc_tokhash_alloc(void); +int mdoc_tokhash_find(const void *, const char *); +void mdoc_tokhash_free(void *); +int mdoc_iscdelim(char); +int mdoc_isdelim(const char *); +size_t mdoc_isescape(const char *); +enum mdoc_sec mdoc_atosec(const char *); +time_t mdoc_atotime(const char *); + +size_t mdoc_macro2len(int); +const char *mdoc_a2arch(const char *); +const char *mdoc_a2vol(const char *); +const char *mdoc_a2msec(const char *); +int mdoc_valid_pre(struct mdoc *, + const struct mdoc_node *); +int mdoc_valid_post(struct mdoc *); +int mdoc_action_pre(struct mdoc *, + const struct mdoc_node *); +int mdoc_action_post(struct mdoc *); +int mdoc_argv(struct mdoc *, int, int, + struct mdoc_arg **, int *, char *); +#define ARGV_ERROR (-1) +#define ARGV_EOLN (0) +#define ARGV_ARG (1) +#define ARGV_WORD (2) +void mdoc_argv_free(struct mdoc_arg *); +int mdoc_args(struct mdoc *, int, + int *, char *, int, char **); +#define ARGS_ERROR (-1) +#define ARGS_EOLN (0) +#define ARGS_WORD (1) +#define ARGS_PUNCT (2) +#define ARGS_QWORD (3) +#define ARGS_PHRASE (4) + +/* FIXME: get rid of these. */ +int xstrlcpys(char *, const struct mdoc_node *, size_t); +int xstrlcat(char *, const char *, size_t); +int xstrlcpy(char *, const char *, size_t); +int xstrcmp(const char *, const char *); +void *xrealloc(void *, size_t); +char *xstrdup(const char *); +int macro_end(struct mdoc *); + +__END_DECLS + +#endif /*!LIBMDOC_H*/ diff --git a/macro.c b/macro.c deleted file mode 100644 index 6b3e4fe3..00000000 --- a/macro.c +++ /dev/null @@ -1,1446 +0,0 @@ -/* $Id: macro.c,v 1.77 2009/03/22 19:01:11 kristaps Exp $ */ -/* - * Copyright (c) 2008, 2009 Kristaps Dzonsons - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the - * above copyright notice and this permission notice appear in all - * copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE - * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL - * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR - * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ -#include -#include -#include -#include -#include - -#include "private.h" - -/* - * This has scanning/parsing routines, each of which extract a macro and - * its arguments and parameters, then know how to progress to the next - * macro. - */ - -/* FIXME: .Fl, .Ar, .Cd handling of `|'. */ - -enum mwarn { - WIMPBRK, - WMACPARM, - WOBS -}; - -enum merr { - EOPEN, - EQUOT, - ENOCTX, - ENOPARMS -}; - -#define REWIND_REWIND (1 << 0) -#define REWIND_NOHALT (1 << 1) -#define REWIND_HALT (1 << 2) - -static int obsolete(MACRO_PROT_ARGS); -static int blk_part_exp(MACRO_PROT_ARGS); -static int in_line_eoln(MACRO_PROT_ARGS); -static int in_line_argn(MACRO_PROT_ARGS); -static int in_line(MACRO_PROT_ARGS); -static int blk_full(MACRO_PROT_ARGS); -static int blk_exp_close(MACRO_PROT_ARGS); -static int blk_part_imp(MACRO_PROT_ARGS); - -static int phrase(struct mdoc *, int, int, char *); -static int rew_dohalt(int, enum mdoc_type, - const struct mdoc_node *); -static int rew_alt(int); -static int rew_dobreak(int, const struct mdoc_node *); -static int rew_elem(struct mdoc *, int); -static int rew_impblock(struct mdoc *, int, int, int); -static int rew_expblock(struct mdoc *, int, int, int); -static int rew_subblock(enum mdoc_type, - struct mdoc *, int, int, int); -static int rew_last(struct mdoc *, struct mdoc_node *); -static int append_delims(struct mdoc *, int, int *, char *); -static int lookup(struct mdoc *, int, int, int, const char *); -static int pwarn(struct mdoc *, int, int, enum mwarn); -static int perr(struct mdoc *, int, int, enum merr); -static int swarn(struct mdoc *, enum mdoc_type, int, int, - const struct mdoc_node *); - -#define nerr(m, n, t) perr((m), (n)->line, (n)->pos, (t)) - -/* Central table of library: who gets parsed how. */ - -const struct mdoc_macro __mdoc_macros[MDOC_MAX] = { - { NULL, 0 }, /* \" */ - { in_line_eoln, MDOC_PROLOGUE }, /* Dd */ - { in_line_eoln, MDOC_PROLOGUE }, /* Dt */ - { in_line_eoln, MDOC_PROLOGUE }, /* Os */ - { blk_full, 0 }, /* Sh */ - { blk_full, 0 }, /* Ss */ - { in_line, 0 }, /* Pp */ - { blk_part_imp, MDOC_PARSED }, /* D1 */ - { blk_part_imp, MDOC_PARSED }, /* Dl */ - { blk_full, MDOC_EXPLICIT }, /* Bd */ - { blk_exp_close, MDOC_EXPLICIT }, /* Ed */ - { blk_full, MDOC_EXPLICIT }, /* Bl */ - { blk_exp_close, MDOC_EXPLICIT }, /* El */ - { blk_full, MDOC_PARSED }, /* It */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */ - { in_line, MDOC_PARSED }, /* An */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */ - { in_line_eoln, MDOC_CALLABLE }, /* Cd */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dv */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Er */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ev */ - { in_line_eoln, 0 }, /* Ex */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fa */ - { in_line_eoln, 0 }, /* Fd */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fn */ - { in_line, MDOC_PARSED }, /* Ft */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */ - { in_line_eoln, 0 }, /* In */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Li */ - { in_line_eoln, 0 }, /* Nd */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */ - { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Op */ - { obsolete, 0 }, /* Ot */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */ - { in_line_eoln, 0 }, /* Rv */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* St */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Va */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Vt */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Xr */ - { in_line_eoln, 0 }, /* %A */ - { in_line_eoln, 0 }, /* %B */ - { in_line_eoln, 0 }, /* %D */ - { in_line_eoln, 0 }, /* %I */ - { in_line_eoln, 0 }, /* %J */ - { in_line_eoln, 0 }, /* %N */ - { in_line_eoln, 0 }, /* %O */ - { in_line_eoln, 0 }, /* %P */ - { in_line_eoln, 0 }, /* %R */ - { in_line_eoln, 0 }, /* %T */ - { in_line_eoln, 0 }, /* %V */ - { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ac */ - { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Ao */ - { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Aq */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* At */ - { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Bc */ - { blk_full, MDOC_EXPLICIT }, /* Bf */ - { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bo */ - { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Bq */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bsx */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bx */ - { in_line_eoln, 0 }, /* Db */ - { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Dc */ - { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Do */ - { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Dq */ - { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ec */ - { blk_exp_close, MDOC_EXPLICIT }, /* Ef */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Em */ - { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Eo */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Fx */ - { in_line, MDOC_PARSED }, /* Ms */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* No */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ns */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Nx */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ox */ - { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Pc */ - { in_line_argn, MDOC_PARSED | MDOC_IGNDELIM }, /* Pf */ - { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Po */ - { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Pq */ - { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Qc */ - { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Ql */ - { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Qo */ - { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Qq */ - { blk_exp_close, MDOC_EXPLICIT }, /* Re */ - { blk_full, MDOC_EXPLICIT }, /* Rs */ - { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Sc */ - { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* So */ - { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Sq */ - { in_line_eoln, 0 }, /* Sm */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sx */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sy */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Tn */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ux */ - { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Xc */ - { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Xo */ - { blk_full, MDOC_EXPLICIT | MDOC_CALLABLE }, /* Fo */ - { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Fc */ - { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Oo */ - { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Oc */ - { blk_full, MDOC_EXPLICIT }, /* Bk */ - { blk_exp_close, MDOC_EXPLICIT }, /* Ek */ - { in_line_eoln, 0 }, /* Bt */ - { in_line_eoln, 0 }, /* Hf */ - { obsolete, 0 }, /* Fr */ - { in_line_eoln, 0 }, /* Ud */ - { in_line_eoln, 0 }, /* Lb */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ap */ - { in_line, 0 }, /* Lp */ - { in_line, MDOC_PARSED }, /* Lk */ - { in_line, MDOC_PARSED }, /* Mt */ - { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Brq */ - { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bro */ - { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Brc */ - { in_line_eoln, 0 }, /* %C */ - { obsolete, 0 }, /* Es */ - { obsolete, 0 }, /* En */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Dx */ - { in_line_eoln, 0 }, /* %Q */ -}; - -const struct mdoc_macro * const mdoc_macros = __mdoc_macros; - - -static int -perr(struct mdoc *mdoc, int line, int pos, enum merr type) -{ - char *p; - - p = NULL; - switch (type) { - case (EOPEN): - p = "explicit scope still open on exit"; - break; - case (EQUOT): - p = "unterminated quotation"; - break; - case (ENOCTX): - p = "closure has no prior context"; - break; - case (ENOPARMS): - p = "unexpect line arguments"; - break; - } - assert(p); - return(mdoc_perr(mdoc, line, pos, p)); -} - - -static int -pwarn(struct mdoc *mdoc, int line, int pos, enum mwarn type) -{ - char *p; - - p = NULL; - switch (type) { - case (WIMPBRK): - p = "crufty end-of-line scope violation"; - break; - case (WMACPARM): - p = "macro-like parameter"; - break; - case (WOBS): - p = "macro marked obsolete"; - break; - } - assert(p); - return(mdoc_pwarn(mdoc, line, pos, WARN_SYNTAX, p)); -} - - -static int -swarn(struct mdoc *mdoc, enum mdoc_type type, - int line, int pos, const struct mdoc_node *p) -{ - const char *n, *t, *tt; - - n = t = ""; - tt = "block"; - - switch (type) { - case (MDOC_BODY): - tt = "multi-line"; - break; - case (MDOC_HEAD): - tt = "line"; - break; - default: - break; - } - - switch (p->type) { - case (MDOC_BLOCK): - n = mdoc_macronames[p->tok]; - t = "block"; - break; - case (MDOC_BODY): - n = mdoc_macronames[p->tok]; - t = "multi-line"; - break; - case (MDOC_HEAD): - n = mdoc_macronames[p->tok]; - t = "line"; - break; - default: - break; - } - - if ( ! (MDOC_IGN_SCOPE & mdoc->pflags)) - return(mdoc_perr(mdoc, line, pos, - "%s scope breaks %s scope of %s", - tt, t, n)); - return(mdoc_pwarn(mdoc, line, pos, WARN_SYNTAX, - "%s scope breaks %s scope of %s", - tt, t, n)); -} - - -/* - * This is called at the end of parsing. It must traverse up the tree, - * closing out open [implicit] scopes. Obviously, open explicit scopes - * are errors. - */ -int -macro_end(struct mdoc *mdoc) -{ - struct mdoc_node *n; - - assert(mdoc->first); - assert(mdoc->last); - - /* Scan for open explicit scopes. */ - - n = MDOC_VALID & mdoc->last->flags ? - mdoc->last->parent : mdoc->last; - - for ( ; n; n = n->parent) { - if (MDOC_BLOCK != n->type) - continue; - if ( ! (MDOC_EXPLICIT & mdoc_macros[n->tok].flags)) - continue; - return(nerr(mdoc, n, EOPEN)); - } - - return(rew_last(mdoc, mdoc->first)); -} - -static int -lookup(struct mdoc *mdoc, int line, int pos, int from, const char *p) -{ - int res; - - res = mdoc_tokhash_find(mdoc->htab, p); - if (MDOC_PARSED & mdoc_macros[from].flags) - return(res); - if (MDOC_MAX == res) - return(res); - if ( ! pwarn(mdoc, line, pos, WMACPARM)) - return(-1); - return(MDOC_MAX); -} - - -static int -rew_last(struct mdoc *mdoc, struct mdoc_node *to) -{ - - assert(to); - mdoc->next = MDOC_NEXT_SIBLING; - - /* LINTED */ - while (mdoc->last != to) { - if ( ! mdoc_valid_post(mdoc)) - return(0); - if ( ! mdoc_action_post(mdoc)) - return(0); - mdoc->last = mdoc->last->parent; - assert(mdoc->last); - } - - if ( ! mdoc_valid_post(mdoc)) - return(0); - return(mdoc_action_post(mdoc)); -} - - -static int -rew_alt(int tok) -{ - switch (tok) { - case (MDOC_Ac): - return(MDOC_Ao); - case (MDOC_Bc): - return(MDOC_Bo); - case (MDOC_Brc): - return(MDOC_Bro); - case (MDOC_Dc): - return(MDOC_Do); - case (MDOC_Ec): - return(MDOC_Eo); - case (MDOC_Ed): - return(MDOC_Bd); - case (MDOC_Ef): - return(MDOC_Bf); - case (MDOC_Ek): - return(MDOC_Bk); - case (MDOC_El): - return(MDOC_Bl); - case (MDOC_Fc): - return(MDOC_Fo); - case (MDOC_Oc): - return(MDOC_Oo); - case (MDOC_Pc): - return(MDOC_Po); - case (MDOC_Qc): - return(MDOC_Qo); - case (MDOC_Re): - return(MDOC_Rs); - case (MDOC_Sc): - return(MDOC_So); - case (MDOC_Xc): - return(MDOC_Xo); - default: - break; - } - abort(); - /* NOTREACHED */ -} - - -/* - * Rewind rules. This indicates whether to stop rewinding - * (REWIND_HALT) without touching our current scope, stop rewinding and - * close our current scope (REWIND_REWIND), or continue (REWIND_NOHALT). - * The scope-closing and so on occurs in the various rew_* routines. - */ -static int -rew_dohalt(int tok, enum mdoc_type type, const struct mdoc_node *p) -{ - - if (MDOC_ROOT == p->type) - return(REWIND_HALT); - if (MDOC_VALID & p->flags) - return(REWIND_NOHALT); - - switch (tok) { - case (MDOC_Aq): - /* FALLTHROUGH */ - case (MDOC_Bq): - /* FALLTHROUGH */ - case (MDOC_Brq): - /* FALLTHROUGH */ - case (MDOC_D1): - /* FALLTHROUGH */ - case (MDOC_Dl): - /* FALLTHROUGH */ - case (MDOC_Dq): - /* FALLTHROUGH */ - case (MDOC_Op): - /* FALLTHROUGH */ - case (MDOC_Pq): - /* FALLTHROUGH */ - case (MDOC_Ql): - /* FALLTHROUGH */ - case (MDOC_Qq): - /* FALLTHROUGH */ - case (MDOC_Sq): - assert(MDOC_HEAD != type); - assert(MDOC_TAIL != type); - if (type == p->type && tok == p->tok) - return(REWIND_REWIND); - break; - case (MDOC_It): - assert(MDOC_TAIL != type); - if (type == p->type && tok == p->tok) - return(REWIND_REWIND); - if (MDOC_BODY == p->type && MDOC_Bl == p->tok) - return(REWIND_HALT); - break; - case (MDOC_Sh): - if (type == p->type && tok == p->tok) - return(REWIND_REWIND); - break; - case (MDOC_Ss): - assert(MDOC_TAIL != type); - if (type == p->type && tok == p->tok) - return(REWIND_REWIND); - if (MDOC_BODY == p->type && MDOC_Sh == p->tok) - return(REWIND_HALT); - break; - case (MDOC_Ao): - /* FALLTHROUGH */ - case (MDOC_Bd): - /* FALLTHROUGH */ - case (MDOC_Bf): - /* FALLTHROUGH */ - case (MDOC_Bk): - /* FALLTHROUGH */ - case (MDOC_Bl): - /* FALLTHROUGH */ - case (MDOC_Bo): - /* FALLTHROUGH */ - case (MDOC_Bro): - /* FALLTHROUGH */ - case (MDOC_Do): - /* FALLTHROUGH */ - case (MDOC_Eo): - /* FALLTHROUGH */ - case (MDOC_Fo): - /* FALLTHROUGH */ - case (MDOC_Oo): - /* FALLTHROUGH */ - case (MDOC_Po): - /* FALLTHROUGH */ - case (MDOC_Qo): - /* FALLTHROUGH */ - case (MDOC_Rs): - /* FALLTHROUGH */ - case (MDOC_So): - /* FALLTHROUGH */ - case (MDOC_Xo): - if (type == p->type && tok == p->tok) - return(REWIND_REWIND); - break; - - /* Multi-line explicit scope close. */ - case (MDOC_Ac): - /* FALLTHROUGH */ - case (MDOC_Bc): - /* FALLTHROUGH */ - case (MDOC_Brc): - /* FALLTHROUGH */ - case (MDOC_Dc): - /* FALLTHROUGH */ - case (MDOC_Ec): - /* FALLTHROUGH */ - case (MDOC_Ed): - /* FALLTHROUGH */ - case (MDOC_Ek): - /* FALLTHROUGH */ - case (MDOC_El): - /* FALLTHROUGH */ - case (MDOC_Fc): - /* FALLTHROUGH */ - case (MDOC_Ef): - /* FALLTHROUGH */ - case (MDOC_Oc): - /* FALLTHROUGH */ - case (MDOC_Pc): - /* FALLTHROUGH */ - case (MDOC_Qc): - /* FALLTHROUGH */ - case (MDOC_Re): - /* FALLTHROUGH */ - case (MDOC_Sc): - /* FALLTHROUGH */ - case (MDOC_Xc): - if (type == p->type && rew_alt(tok) == p->tok) - return(REWIND_REWIND); - break; - default: - abort(); - /* NOTREACHED */ - } - - return(REWIND_NOHALT); -} - - -/* - * See if we can break an encountered scope (the rew_dohalt has returned - * REWIND_NOHALT). - */ -static int -rew_dobreak(int tok, const struct mdoc_node *p) -{ - - assert(MDOC_ROOT != p->type); - if (MDOC_ELEM == p->type) - return(1); - if (MDOC_TEXT == p->type) - return(1); - if (MDOC_VALID & p->flags) - return(1); - - switch (tok) { - case (MDOC_It): - return(MDOC_It == p->tok); - case (MDOC_Ss): - return(MDOC_Ss == p->tok); - case (MDOC_Sh): - if (MDOC_Ss == p->tok) - return(1); - return(MDOC_Sh == p->tok); - case (MDOC_El): - if (MDOC_It == p->tok) - return(1); - break; - case (MDOC_Oc): - /* XXX - experimental! */ - if (MDOC_Op == p->tok) - return(1); - break; - default: - break; - } - - if (MDOC_EXPLICIT & mdoc_macros[tok].flags) - return(p->tok == rew_alt(tok)); - else if (MDOC_BLOCK == p->type) - return(1); - - return(tok == p->tok); -} - - -static int -rew_elem(struct mdoc *mdoc, int tok) -{ - struct mdoc_node *n; - - n = mdoc->last; - if (MDOC_ELEM != n->type) - n = n->parent; - assert(MDOC_ELEM == n->type); - assert(tok == n->tok); - - return(rew_last(mdoc, n)); -} - - -static int -rew_subblock(enum mdoc_type type, struct mdoc *mdoc, - int tok, int line, int ppos) -{ - struct mdoc_node *n; - int c; - - /* LINTED */ - for (n = mdoc->last; n; n = n->parent) { - c = rew_dohalt(tok, type, n); - if (REWIND_HALT == c) - return(1); - if (REWIND_REWIND == c) - break; - else if (rew_dobreak(tok, n)) - continue; - if ( ! swarn(mdoc, type, line, ppos, n)) - return(0); - } - - assert(n); - return(rew_last(mdoc, n)); -} - - -static int -rew_expblock(struct mdoc *mdoc, int tok, int line, int ppos) -{ - struct mdoc_node *n; - int c; - - /* LINTED */ - for (n = mdoc->last; n; n = n->parent) { - c = rew_dohalt(tok, MDOC_BLOCK, n); - if (REWIND_HALT == c) - return(perr(mdoc, line, ppos, ENOCTX)); - if (REWIND_REWIND == c) - break; - else if (rew_dobreak(tok, n)) - continue; - if ( ! swarn(mdoc, MDOC_BLOCK, line, ppos, n)) - return(0); - } - - assert(n); - return(rew_last(mdoc, n)); -} - - -static int -rew_impblock(struct mdoc *mdoc, int tok, int line, int ppos) -{ - struct mdoc_node *n; - int c; - - /* LINTED */ - for (n = mdoc->last; n; n = n->parent) { - c = rew_dohalt(tok, MDOC_BLOCK, n); - if (REWIND_HALT == c) - return(1); - else if (REWIND_REWIND == c) - break; - else if (rew_dobreak(tok, n)) - continue; - if ( ! swarn(mdoc, MDOC_BLOCK, line, ppos, n)) - return(0); - } - - assert(n); - return(rew_last(mdoc, n)); -} - - -static int -append_delims(struct mdoc *mdoc, int line, int *pos, char *buf) -{ - int c, lastarg; - char *p; - - if (0 == buf[*pos]) - return(1); - - for (;;) { - lastarg = *pos; - c = mdoc_args(mdoc, line, pos, buf, 0, &p); - assert(ARGS_PHRASE != c); - - if (ARGS_ERROR == c) - return(0); - else if (ARGS_EOLN == c) - break; - assert(mdoc_isdelim(p)); - if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) - return(0); - mdoc->next = MDOC_NEXT_SIBLING; - } - - return(1); -} - - -/* - * Close out block partial/full explicit. - */ -static int -blk_exp_close(MACRO_PROT_ARGS) -{ - int j, c, lastarg, maxargs, flushed; - char *p; - - switch (tok) { - case (MDOC_Ec): - maxargs = 1; - break; - default: - maxargs = 0; - break; - } - - if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) { - if (0 == buf[*pos]) { - if ( ! rew_subblock(MDOC_BODY, mdoc, - tok, line, ppos)) - return(0); - return(rew_expblock(mdoc, tok, line, ppos)); - } - return(perr(mdoc, line, ppos, ENOPARMS)); - } - - if ( ! rew_subblock(MDOC_BODY, mdoc, tok, line, ppos)) - return(0); - - if (maxargs > 0) { - if ( ! mdoc_tail_alloc(mdoc, line, - ppos, rew_alt(tok))) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - } - - for (lastarg = ppos, flushed = j = 0; ; j++) { - lastarg = *pos; - - if (j == maxargs && ! flushed) { - if ( ! rew_expblock(mdoc, tok, line, ppos)) - return(0); - flushed = 1; - } - - c = mdoc_args(mdoc, line, pos, buf, tok, &p); - - if (ARGS_ERROR == c) - return(0); - if (ARGS_PUNCT == c) - break; - if (ARGS_EOLN == c) - break; - - if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) - return(0); - else if (MDOC_MAX != c) { - if ( ! flushed) { - if ( ! rew_expblock(mdoc, tok, - line, ppos)) - return(0); - flushed = 1; - } - if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf)) - return(0); - break; - } - - if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) - return(0); - mdoc->next = MDOC_NEXT_SIBLING; - } - - if ( ! flushed && ! rew_expblock(mdoc, tok, line, ppos)) - return(0); - - if (ppos > 1) - return(1); - return(append_delims(mdoc, line, pos, buf)); -} - - -/* - * In-line macros where reserved words cause scope close-reopen. - */ -static int -in_line(MACRO_PROT_ARGS) -{ - int la, lastpunct, c, w; - struct mdoc_arg *arg; - char *p; - - for (la = ppos, arg = NULL;; ) { - la = *pos; - c = mdoc_argv(mdoc, line, tok, &arg, pos, buf); - - if (ARGV_WORD == c) { - *pos = la; - break; - } - - if (ARGV_EOLN == c) - break; - if (ARGV_ARG == c) - continue; - - mdoc_argv_free(arg); - return(0); - } - - if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - - for (lastpunct = 0;; ) { - la = *pos; - w = mdoc_args(mdoc, line, pos, buf, tok, &p); - - if (ARGS_ERROR == w) - return(0); - if (ARGS_EOLN == w) - break; - if (ARGS_PUNCT == w) - break; - - /* Quoted words shouldn't be looked-up. */ - - c = ARGS_QWORD == w ? MDOC_MAX : - lookup(mdoc, line, la, tok, p); - - /* MDOC_MAX (not a macro) or -1 (error). */ - - if (MDOC_MAX != c && -1 != c) { - if (0 == lastpunct && ! rew_elem(mdoc, tok)) - return(0); - c = mdoc_macro(mdoc, c, line, la, pos, buf); - if (0 == c) - return(0); - if (ppos > 1) - return(1); - return(append_delims(mdoc, line, pos, buf)); - } else if (-1 == c) - return(0); - - /* Non-quote-enclosed punctuation. */ - - if (ARGS_QWORD != w && mdoc_isdelim(p)) { - if (0 == lastpunct && ! rew_elem(mdoc, tok)) - return(0); - lastpunct = 1; - } else if (lastpunct) { - c = mdoc_elem_alloc(mdoc, line, ppos, tok, arg); - - if (0 == c) - return(0); - - mdoc->next = MDOC_NEXT_CHILD; - lastpunct = 0; - } - - if ( ! mdoc_word_alloc(mdoc, line, la, p)) - return(0); - mdoc->next = MDOC_NEXT_SIBLING; - } - - if (0 == lastpunct && ! rew_elem(mdoc, tok)) - return(0); - if (ppos > 1) - return(1); - return(append_delims(mdoc, line, pos, buf)); -} - - -/* - * Block full-explicit and full-implicit. - */ -static int -blk_full(MACRO_PROT_ARGS) -{ - int c, lastarg, reopen; - struct mdoc_arg *arg; - char *p; - - if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) { - if ( ! rew_subblock(MDOC_BODY, mdoc, - tok, line, ppos)) - return(0); - if ( ! rew_impblock(mdoc, tok, line, ppos)) - return(0); - } - - for (arg = NULL;; ) { - lastarg = *pos; - c = mdoc_argv(mdoc, line, tok, &arg, pos, buf); - - if (ARGV_WORD == c) { - *pos = lastarg; - break; - } - - if (ARGV_EOLN == c) - break; - if (ARGV_ARG == c) - continue; - - mdoc_argv_free(arg); - return(0); - } - - if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, arg)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - - if (0 == buf[*pos]) { - if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) - return(0); - if ( ! rew_subblock(MDOC_HEAD, mdoc, - tok, line, ppos)) - return(0); - if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - return(1); - } - - if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - - for (reopen = 0;; ) { - lastarg = *pos; - c = mdoc_args(mdoc, line, pos, buf, tok, &p); - - if (ARGS_ERROR == c) - return(0); - if (ARGS_EOLN == c) - break; - if (ARGS_PHRASE == c) { - if (reopen && ! mdoc_head_alloc - (mdoc, line, ppos, tok)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - /* - * Phrases are self-contained macro phrases used - * in the columnar output of a macro. They need - * special handling. - */ - if ( ! phrase(mdoc, line, lastarg, buf)) - return(0); - if ( ! rew_subblock(MDOC_HEAD, mdoc, - tok, line, ppos)) - return(0); - - reopen = 1; - continue; - } - - if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) - return(0); - - if (MDOC_MAX == c) { - if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) - return(0); - mdoc->next = MDOC_NEXT_SIBLING; - continue; - } - - if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf)) - return(0); - break; - } - - if (1 == ppos && ! append_delims(mdoc, line, pos, buf)) - return(0); - if ( ! rew_subblock(MDOC_HEAD, mdoc, tok, line, ppos)) - return(0); - - if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - - return(1); -} - - -/* - * Block partial-imnplicit scope. - */ -static int -blk_part_imp(MACRO_PROT_ARGS) -{ - int lastarg, c; - char *p; - struct mdoc_node *blk, *body, *n; - - if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - blk = mdoc->last; - - if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) - return(0); - mdoc->next = MDOC_NEXT_SIBLING; - - if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - body = mdoc->last; - - /* XXX - no known argument macros. */ - - for (lastarg = ppos;; ) { - lastarg = *pos; - c = mdoc_args(mdoc, line, pos, buf, tok, &p); - assert(ARGS_PHRASE != c); - - if (ARGS_ERROR == c) - return(0); - if (ARGS_PUNCT == c) - break; - if (ARGS_EOLN == c) - break; - - if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) - return(0); - else if (MDOC_MAX == c) { - if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) - return(0); - mdoc->next = MDOC_NEXT_SIBLING; - continue; - } - - if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf)) - return(0); - break; - } - - /* - * Since we know what our context is, we can rewind directly to - * it. This allows us to accomodate for our scope being - * violated by another token. - */ - - for (n = mdoc->last; n; n = n->parent) - if (body == n) - break; - - if (NULL == n && ! pwarn(mdoc, body->line, body->pos, WIMPBRK)) - return(0); - - if (n && ! rew_last(mdoc, body)) - return(0); - - if (1 == ppos && ! append_delims(mdoc, line, pos, buf)) - return(0); - - if (n && ! rew_last(mdoc, blk)) - return(0); - - return(1); -} - - -/* - * Block partial-explicit macros. - */ -static int -blk_part_exp(MACRO_PROT_ARGS) -{ - int lastarg, flushed, j, c, maxargs; - char *p; - - lastarg = ppos; - flushed = 0; - - /* - * Number of arguments (head arguments). Only `Eo' has these, - */ - - switch (tok) { - case (MDOC_Eo): - maxargs = 1; - break; - default: - maxargs = 0; - break; - } - - if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - - if (0 == maxargs) { - if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) - return(0); - if ( ! rew_subblock(MDOC_HEAD, mdoc, - tok, line, ppos)) - return(0); - if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) - return(0); - flushed = 1; - } else if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) - return(0); - - mdoc->next = MDOC_NEXT_CHILD; - - for (j = 0; ; j++) { - lastarg = *pos; - if (j == maxargs && ! flushed) { - if ( ! rew_subblock(MDOC_HEAD, mdoc, - tok, line, ppos)) - return(0); - flushed = 1; - if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - } - - c = mdoc_args(mdoc, line, pos, buf, tok, &p); - assert(ARGS_PHRASE != c); - - if (ARGS_ERROR == c) - return(0); - if (ARGS_PUNCT == c) - break; - if (ARGS_EOLN == c) - break; - - if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) - return(0); - else if (MDOC_MAX != c) { - if ( ! flushed) { - if ( ! rew_subblock(MDOC_HEAD, mdoc, - tok, line, ppos)) - return(0); - flushed = 1; - if ( ! mdoc_body_alloc(mdoc, line, - ppos, tok)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - } - if ( ! mdoc_macro(mdoc, c, line, lastarg, - pos, buf)) - return(0); - break; - } - - if ( ! flushed && mdoc_isdelim(p)) { - if ( ! rew_subblock(MDOC_HEAD, mdoc, - tok, line, ppos)) - return(0); - flushed = 1; - if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - } - - if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) - return(0); - mdoc->next = MDOC_NEXT_SIBLING; - } - - if ( ! flushed) { - if ( ! rew_subblock(MDOC_HEAD, mdoc, tok, line, ppos)) - return(0); - if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - } - - if (ppos > 1) - return(1); - return(append_delims(mdoc, line, pos, buf)); -} - - -/* - * In-line macros where reserved words signal closure of the macro. - * Macros also have a fixed number of arguments. - */ -static int -in_line_argn(MACRO_PROT_ARGS) -{ - int lastarg, flushed, j, c, maxargs; - struct mdoc_arg *arg; - char *p; - - - /* - * Fixed maximum arguments per macro. Some of these have none - * and close as soon as the invocation is parsed. - */ - - switch (tok) { - case (MDOC_Ap): - /* FALLTHROUGH */ - case (MDOC_No): - /* FALLTHROUGH */ - case (MDOC_Ns): - /* FALLTHROUGH */ - case (MDOC_Ux): - maxargs = 0; - break; - default: - maxargs = 1; - break; - } - - for (lastarg = ppos, arg = NULL;; ) { - lastarg = *pos; - c = mdoc_argv(mdoc, line, tok, &arg, pos, buf); - - if (ARGV_WORD == c) { - *pos = lastarg; - break; - } - - if (ARGV_EOLN == c) - break; - if (ARGV_ARG == c) - continue; - - mdoc_argv_free(arg); - return(0); - } - - if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg)) - return(0); - mdoc->next = MDOC_NEXT_CHILD; - - for (flushed = j = 0; ; j++) { - lastarg = *pos; - - if (j == maxargs && ! flushed) { - if ( ! rew_elem(mdoc, tok)) - return(0); - flushed = 1; - } - - c = mdoc_args(mdoc, line, pos, buf, tok, &p); - - if (ARGS_ERROR == c) - return(0); - if (ARGS_PUNCT == c) - break; - if (ARGS_EOLN == c) - break; - - if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) - return(0); - else if (MDOC_MAX != c) { - if ( ! flushed && ! rew_elem(mdoc, tok)) - return(0); - flushed = 1; - if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf)) - return(0); - break; - } - - if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) && - ! flushed && mdoc_isdelim(p)) { - if ( ! rew_elem(mdoc, tok)) - return(0); - flushed = 1; - } - - if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) - return(0); - mdoc->next = MDOC_NEXT_SIBLING; - } - - if ( ! flushed && ! rew_elem(mdoc, tok)) - return(0); - - if (ppos > 1) - return(1); - return(append_delims(mdoc, line, pos, buf)); -} - - -/* - * In-line macro that spans an entire line. May be callable, but has no - * subsequent parsed arguments. - */ -static int -in_line_eoln(MACRO_PROT_ARGS) -{ - int c, w, la; - struct mdoc_arg *arg; - char *p; - - assert( ! (MDOC_PARSED & mdoc_macros[tok].flags)); - - arg = NULL; - - for (;;) { - la = *pos; - c = mdoc_argv(mdoc, line, tok, &arg, pos, buf); - - if (ARGV_WORD == c) { - *pos = la; - break; - } - if (ARGV_EOLN == c) - break; - if (ARGV_ARG == c) - continue; - - mdoc_argv_free(arg); - return(0); - } - - if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg)) - return(0); - - mdoc->next = MDOC_NEXT_CHILD; - - for (;;) { - la = *pos; - w = mdoc_args(mdoc, line, pos, buf, tok, &p); - - if (ARGS_ERROR == w) - return(0); - if (ARGS_EOLN == w) - break; - - c = ARGS_QWORD == w ? MDOC_MAX : - lookup(mdoc, line, la, tok, p); - - if (MDOC_MAX != c && -1 != c) { - if ( ! rew_elem(mdoc, tok)) - return(0); - return(mdoc_macro(mdoc, c, line, la, pos, buf)); - } else if (-1 == c) - return(0); - - if ( ! mdoc_word_alloc(mdoc, line, la, p)) - return(0); - mdoc->next = MDOC_NEXT_SIBLING; - } - - return(rew_elem(mdoc, tok)); -} - - -/* ARGSUSED */ -static int -obsolete(MACRO_PROT_ARGS) -{ - - return(pwarn(mdoc, line, ppos, WOBS)); -} - - -static int -phrase(struct mdoc *mdoc, int line, int ppos, char *buf) -{ - int i, la, c, quoted; - - /* - * Parse over words in a phrase. We have to handle this - * specially because we assume no calling context -- in normal - * circumstances, we switch argument parsing based on whether - * the parent macro accepts quotes, tabs, etc. Here, anything - * goes. - */ - - for (i = ppos; buf[i]; ) { - assert(' ' != buf[i]); - la = i; - quoted = 0; - - /* - * Read to next token. If quoted (check not escaped), - * scan ahead to next unescaped quote. If not quoted or - * escape-quoted, then scan ahead to next space. - */ - - if ((i && '\"' == buf[i] && '\\' != buf[i - 1]) || - (0 == i && '\"' == buf[i])) { - for (la = ++i; buf[i]; i++) - if ('\"' != buf[i]) - continue; - else if ('\\' != buf[i - 1]) - break; - if (0 == buf[i]) - return(perr(mdoc, line, la, EQUOT)); - quoted = 1; - } else - for ( ; buf[i]; i++) - if (i && ' ' == buf[i]) { - if ('\\' != buf[i - 1]) - break; - } else if (' ' == buf[i]) - break; - - /* If not end-of-line, terminate argument. */ - - if (buf[i]) - buf[i++] = 0; - - /* Read to next argument. */ - - for ( ; buf[i] && ' ' == buf[i]; i++) - /* Spin. */ ; - - /* - * If we're a non-quoted string, try to look up the - * value as a macro and execute it, if found. - */ - - c = quoted ? MDOC_MAX : - mdoc_tokhash_find(mdoc->htab, &buf[la]); - - if (MDOC_MAX != c) { - if ( ! mdoc_macro(mdoc, c, line, la, &i, buf)) - return(0); - return(append_delims(mdoc, line, &i, buf)); - } - - /* A regular word or quoted string. */ - - if ( ! mdoc_word_alloc(mdoc, line, la, &buf[la])) - return(0); - mdoc->next = MDOC_NEXT_SIBLING; - } - - return(1); -} diff --git a/main.c b/main.c index 71836e3a..61ed7b72 100644 --- a/main.c +++ b/main.c @@ -1,4 +1,4 @@ -/* $Id: main.c,v 1.9 2009/03/22 19:10:48 kristaps Exp $ */ +/* $Id: main.c,v 1.10 2009/03/23 14:22:11 kristaps Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons * @@ -27,12 +27,17 @@ #include #include "mdoc.h" +#include "man.h" #ifdef __linux__ extern int getsubopt(char **, char * const *, char **); # ifndef __dead # define __dead __attribute__((__noreturn__)) # endif +#elif defined(__FreeBSD__) +# ifndef __dead +# define __dead __dead2 +# endif #endif struct buf { @@ -49,8 +54,13 @@ struct curparse { #define WARN_WERR (1 << 2) /* Warnings->errors. */ }; -enum outt { - OUTT_ASCII, +enum intt { + INTT_MDOC = 0, + INTT_MAN +}; + +enum outt { + OUTT_ASCII = 0, OUTT_LATIN1, OUTT_UTF8, OUTT_TREE, @@ -69,18 +79,22 @@ extern int terminal_run(void *, const struct mdoc *); extern int tree_run(void *, const struct mdoc *); extern void terminal_free(void *); -__dead static void version(void); -__dead static void usage(void); static int foptions(int *, char *); static int toptions(enum outt *, char *); +static int moptions(enum intt *, char *); static int woptions(int *, char *); static int merr(void *, int, int, const char *); static int mwarn(void *, int, int, enum mdoc_warn, const char *); -static int file(struct buf *, struct buf *, - const char *, struct mdoc *); +static int file(struct buf *, struct buf *, + const char *, + struct man *, struct mdoc *); static int fdesc(struct buf *, struct buf *, - const char *, int, struct mdoc *); + const char *, int, + struct man *, struct mdoc *); + +__dead static void version(void); +__dead static void usage(void); int @@ -88,9 +102,11 @@ main(int argc, char *argv[]) { int c, rc, fflags; struct mdoc_cb cb; + struct man *man; struct mdoc *mdoc; void *outdata; enum outt outtype; + enum intt inttype; struct buf ln, blk; out_run outrun; out_free outfree; @@ -98,16 +114,21 @@ main(int argc, char *argv[]) fflags = 0; outtype = OUTT_ASCII; + inttype = INTT_MDOC; bzero(&curp, sizeof(struct curparse)); /* LINTED */ - while (-1 != (c = getopt(argc, argv, "f:VW:T:"))) + while (-1 != (c = getopt(argc, argv, "f:m:VW:T:"))) switch (c) { case ('f'): if ( ! foptions(&fflags, optarg)) return(0); break; + case ('m'): + if ( ! moptions(&inttype, optarg)) + return(0); + break; case ('T'): if ( ! toptions(&outtype, optarg)) return(0); @@ -174,7 +195,17 @@ main(int argc, char *argv[]) bzero(&ln, sizeof(struct buf)); bzero(&blk, sizeof(struct buf)); - mdoc = mdoc_alloc(&curp, fflags, &cb); + man = NULL; + mdoc = NULL; + + switch (inttype) { + case (INTT_MAN): + man = man_alloc(); + break; + default: + mdoc = mdoc_alloc(&curp, fflags, &cb); + break; + } /* * Loop around available files. @@ -182,22 +213,31 @@ main(int argc, char *argv[]) if (NULL == *argv) { curp.file = ""; - c = fdesc(&blk, &ln, "stdin", STDIN_FILENO, mdoc); rc = 0; + c = fdesc(&blk, &ln, "stdin", + STDIN_FILENO, man, mdoc); + if (c && NULL == outrun) rc = 1; +#if 0 else if (c && outrun && (*outrun)(outdata, mdoc)) rc = 1; +#endif } else { while (*argv) { curp.file = *argv; - c = file(&blk, &ln, *argv, mdoc); + c = file(&blk, &ln, *argv, man, mdoc); if ( ! c) break; +#if 0 if (outrun && ! (*outrun)(outdata, mdoc)) break; - /* Reset the parser for another file. */ - mdoc_reset(mdoc); +#endif + if (man) + man_reset(man); + if (mdoc) + mdoc_reset(mdoc); + argv++; } rc = NULL == *argv; @@ -209,8 +249,10 @@ main(int argc, char *argv[]) free(ln.buf); if (outfree) (*outfree)(outdata); - - mdoc_free(mdoc); + if (mdoc) + mdoc_free(mdoc); + if (man) + man_free(man); return(rc ? EXIT_SUCCESS : EXIT_FAILURE); } @@ -237,8 +279,8 @@ usage(void) static int -file(struct buf *blk, struct buf *ln, - const char *file, struct mdoc *mdoc) +file(struct buf *blk, struct buf *ln, const char *file, + struct man *man, struct mdoc *mdoc) { int fd, c; @@ -247,7 +289,7 @@ file(struct buf *blk, struct buf *ln, return(0); } - c = fdesc(blk, ln, file, fd, mdoc); + c = fdesc(blk, ln, file, fd, man, mdoc); if (-1 == close(fd)) warn("%s", file); @@ -258,15 +300,15 @@ file(struct buf *blk, struct buf *ln, static int fdesc(struct buf *blk, struct buf *ln, - const char *f, int fd, struct mdoc *mdoc) + const char *f, int fd, + struct man *man, struct mdoc *mdoc) { size_t sz; ssize_t ssz; struct stat st; int j, i, pos, lnn; -#ifdef STRIP_XO - int macro, xo, xeoln; -#endif + + assert( ! (man && mdoc)); /* * Two buffers: ln and buf. buf is the input buffer, optimised @@ -291,9 +333,6 @@ fdesc(struct buf *blk, struct buf *ln, /* * Fill buf with file blocksize and parse newlines into ln. */ -#ifdef STRIP_XO - macro = xo = xeoln = 0; -#endif for (lnn = 1, pos = 0; ; ) { if (-1 == (ssz = read(fd, blk->buf, sz))) { @@ -311,66 +350,6 @@ fdesc(struct buf *blk, struct buf *ln, } if ('\n' != blk->buf[i]) { - /* - * Ugly of uglies. Here we handle the - * dreaded `Xo/Xc' scoping. Cover the - * eyes of any nearby children. This - * makes `Xo/Xc' enclosures look like - * one huge line. - */ -#ifdef STRIP_XO - /* - * First, note whether we're in a macro - * line. - */ - if (0 == pos && '.' == blk->buf[i]) - macro = 1; - - /* - * If we're in an `Xo' context and just - * nixed a newline, remove the control - * character for new macro lines: - * they're going to show up as all part - * of the same line. - */ - if (xo && xeoln && '.' == blk->buf[i]) { - xeoln = 0; - continue; - } - xeoln = 0; - - /* - * If we've parsed `Xo', enter an xo - * context. `Xo' must be in a parsable - * state. This is the ugly part. IT IS - * NOT SMART ENOUGH TO HANDLE ESCAPED - * WHITESPACE. - */ - if (macro && pos && 'o' == blk->buf[i]) { - if (xo && 'X' == ln->buf[pos - 1]) { - if (' ' == ln->buf[pos - 2]) - xo++; - } else if ('X' == ln->buf[pos - 1]) { - if (2 == pos && '.' == ln->buf[pos - 2]) - xo++; - else if (' ' == ln->buf[pos - 2]) - xo++; - } - } - - /* - * If we're parsed `Xc', leave an xo - * context if one's already pending. - * `Xc' must be in a parsable state. - * THIS IS NOT SMART ENOUGH TO HANDLE - * ESCAPED WHITESPACE. - */ - if (macro && pos && 'c' == blk->buf[i]) - if (xo && 'X' == ln->buf[pos - 1]) - if (' ' == ln->buf[pos - 2]) - xo--; -#endif /* STRIP_XO */ - ln->buf[pos++] = blk->buf[i]; continue; } @@ -389,30 +368,37 @@ fdesc(struct buf *blk, struct buf *ln, } } -#ifdef STRIP_XO - /* - * If we're in an xo context, put a space in - * place of the newline and continue parsing. - * Mark that we just did a newline. - */ - if (xo) { - xeoln = 1; - ln->buf[pos++] = ' '; - lnn++; - continue; - } - macro = 0; -#endif /* STRIP_XO */ - ln->buf[pos] = 0; - if ( ! mdoc_parseln(mdoc, lnn, ln->buf)) + if (mdoc && ! mdoc_parseln(mdoc, lnn, ln->buf)) + return(0); + if (man && ! man_parseln(man, lnn, ln->buf)) return(0); lnn++; pos = 0; } } - return(mdoc_endparse(mdoc)); + if (mdoc) + return(mdoc_endparse(mdoc)); + + return(man_endparse(man)); +} + + +static int +moptions(enum intt *tflags, char *arg) +{ + + if (0 == strcmp(arg, "mdoc")) + *tflags = INTT_MDOC; + else if (0 == strcmp(arg, "man")) + *tflags = INTT_MAN; + else { + warnx("bad argument: -m%s", arg); + return(0); + } + + return(1); } diff --git a/man.c b/man.c new file mode 100644 index 00000000..ec894c80 --- /dev/null +++ b/man.c @@ -0,0 +1,368 @@ +/* $Id: man.c,v 1.1 2009/03/23 14:22:11 kristaps Exp $ */ +/* + * Copyright (c) 2008, 2009 Kristaps Dzonsons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "libman.h" + +const char *const __man_macronames[MAN_MAX] = { + "\\\"", "TH", "SH", "SS", + "TP", "LP", "PP", "P", + "IP", "HP", "SM", "SB", + "BI", "IB", "BR", "RB", + "R", "B", "I" + }; + +const char * const *man_macronames = __man_macronames; + +static struct man_node *man_node_alloc(int, int, enum man_type); +static int man_node_append(struct man *, + struct man_node *); +static int man_ptext(struct man *, int, char *); +static int man_pmacro(struct man *, int, char *); + + +const struct man_node * +man_node(const struct man *man) +{ + + return(man->first); +} + + +const struct man_meta * +man_meta(const struct man *man) +{ + + return(&man->meta); +} + + +void +man_reset(struct man *man) +{ + + if (man->first) + man_node_freelist(man->first); + if (man->meta.title) + free(man->meta.title); + if (man->meta.os) + free(man->meta.os); + if (man->meta.vol) + free(man->meta.vol); + + bzero(&man->meta, sizeof(struct man_meta)); + man->flags = 0; + if (NULL == (man->last = calloc(1, sizeof(struct man_node)))) + err(1, "malloc"); + man->first = man->last; + man->last->type = MAN_ROOT; + man->next = MAN_NEXT_CHILD; +} + + +void +man_free(struct man *man) +{ + + if (man->first) + man_node_freelist(man->first); + if (man->meta.title) + free(man->meta.title); + if (man->meta.os) + free(man->meta.os); + if (man->meta.vol) + free(man->meta.vol); + if (man->htab) + man_hash_free(man->htab); + free(man); +} + + +struct man * +man_alloc(void) +{ + struct man *p; + + if (NULL == (p = calloc(1, sizeof(struct man)))) + err(1, "malloc"); + if (NULL == (p->last = calloc(1, sizeof(struct man_node)))) + err(1, "malloc"); + + p->first = p->last; + p->last->type = MAN_ROOT; + p->next = MAN_NEXT_CHILD; + p->htab = man_hash_alloc(); + return(p); +} + + +int +man_endparse(struct man *m) +{ + + return(1); +} + + +int +man_parseln(struct man *m, int ln, char *buf) +{ + + return('.' == *buf ? + man_pmacro(m, ln, buf) : + man_ptext(m, ln, buf)); +} + + +static int +man_node_append(struct man *man, struct man_node *p) +{ + + assert(man->last); + assert(man->first); + assert(MAN_ROOT != p->type); + + switch (man->next) { + case (MAN_NEXT_SIBLING): + man->last->next = p; + p->prev = man->last; + p->parent = man->last->parent; + break; + case (MAN_NEXT_CHILD): + man->last->child = p; + p->parent = man->last; + break; + default: + abort(); + /* NOTREACHED */ + } + +#if 0 + if ( ! man_valid_pre(man, p)) + return(0); + if ( ! man_action_pre(man, p)) + return(0); +#endif + + switch (p->type) { + case (MAN_HEAD): + assert(MAN_BLOCK == p->parent->type); + p->parent->head = p; + break; + case (MAN_BODY): + assert(MAN_BLOCK == p->parent->type); + p->parent->body = p; + break; + default: + break; + } + + man->last = p; + return(1); +} + + +static struct man_node * +man_node_alloc(int line, int pos, enum man_type type) +{ + struct man_node *p; + + if (NULL == (p = calloc(1, sizeof(struct man_node)))) + err(1, "malloc"); + p->line = line; + p->pos = pos; + p->type = type; + + return(p); +} + + +int +man_head_alloc(struct man *man, int line, int pos, int tok) +{ + struct man_node *p; + + p = man_node_alloc(line, pos, MAN_HEAD); + p->tok = tok; + + return(man_node_append(man, p)); +} + + +int +man_body_alloc(struct man *man, int line, int pos, int tok) +{ + struct man_node *p; + + p = man_node_alloc(line, pos, MAN_BODY); + p->tok = tok; + + return(man_node_append(man, p)); +} + + +int +man_block_alloc(struct man *man, int line, int pos, int tok) +{ + struct man_node *p; + + p = man_node_alloc(line, pos, MAN_BLOCK); + p->tok = tok; + + return(man_node_append(man, p)); +} + + +int +man_elem_alloc(struct man *man, int line, int pos, int tok) +{ + struct man_node *p; + + p = man_node_alloc(line, pos, MAN_ELEM); + p->tok = tok; + + return(man_node_append(man, p)); +} + + +int +man_word_alloc(struct man *man, + int line, int pos, const char *word) +{ + struct man_node *p; + + p = man_node_alloc(line, pos, MAN_TEXT); + if (NULL == (p->string = strdup(word))) + err(1, "strdup"); + + return(man_node_append(man, p)); +} + + +void +man_node_free(struct man_node *p) +{ + + if (p->string) + free(p->string); + free(p); +} + + +void +man_node_freelist(struct man_node *p) +{ + + if (p->child) + man_node_freelist(p->child); + if (p->next) + man_node_freelist(p->next); + + man_node_free(p); +} + + +static int +man_ptext(struct man *m, int line, char *buf) +{ + + if (0 == buf[0]) { + warnx("blank line!"); + return(1); + } + + if ( ! man_word_alloc(m, line, 0, buf)) + return(0); + + m->next = MAN_NEXT_SIBLING; + return(1); +} + + +int +man_pmacro(struct man *m, int ln, char *buf) +{ + int i, c; + char mac[5]; + + /* Comments and empties are quickly ignored. */ + + if (0 == buf[1]) + return(1); + + if (' ' == buf[1]) { + i = 2; + while (buf[i] && ' ' == buf[i]) + i++; + if (0 == buf[i]) + return(1); + warnx("invalid syntax"); + return(0); + } + + if (buf[1] && '\\' == buf[1]) + if (buf[2] && '\"' == buf[2]) + return(1); + + /* Copy the first word into a nil-terminated buffer. */ + + for (i = 1; i < 5; i++) { + if (0 == (mac[i - 1] = buf[i])) + break; + else if (' ' == buf[i]) + break; + } + + mac[i - 1] = 0; + + if (i == 5 || i <= 1) { + warnx("unknown macro: %s", mac); + goto err; + } + + if (MAN_MAX == (c = man_hash_find(m->htab, mac))) { + warnx("unknown macro: %s", mac); + goto err; + } + + /* The macro is sane. Jump to the next word. */ + + while (buf[i] && ' ' == buf[i]) + i++; + + /* Begin recursive parse sequence. */ + + if ( ! (*man_macros[c].fp)(m, c, ln, 1, &i, buf)) + goto err; + + return(1); + +err: /* Error out. */ + +#if 0 + m->flags |= MDOC_HALT; +#endif + return(0); +} diff --git a/man.h b/man.h new file mode 100644 index 00000000..3f79b10e --- /dev/null +++ b/man.h @@ -0,0 +1,97 @@ +/* $Id: man.h,v 1.1 2009/03/23 14:22:11 kristaps Exp $ */ +/* + * Copyright (c) 2009 Kristaps Dzonsons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef MAN_H +#define MAN_H + +#include + +#define MAN___ 0 +#define MAN_TH 1 +#define MAN_SH 2 +#define MAN_SS 3 +#define MAN_TP 4 +#define MAN_LP 5 +#define MAN_PP 6 +#define MAN_P 7 +#define MAN_IP 8 +#define MAN_HP 9 +#define MAN_SM 10 +#define MAN_SB 11 +#define MAN_BI 12 +#define MAN_IB 13 +#define MAN_BR 14 +#define MAN_RB 15 +#define MAN_R 16 +#define MAN_B 17 +#define MAN_I 18 +#define MAN_MAX 19 + +enum man_type { + MAN_TEXT, + MAN_ELEM, + MAN_HEAD, + MAN_BODY, + MAN_BLOCK, + MAN_ROOT +}; + +struct man_meta { + int msec; + char *vol; + time_t date; + char *title; + char *os; +}; + +struct man_node { + struct man_node *parent; + struct man_node *child; + struct man_node *next; + struct man_node *prev; + int line; + int pos; + int tok; + int flags; +#define MAN_VALID (1 << 0) +#define MAN_ACTED (1 << 1) + enum man_type type; + + struct man_node *head; + struct man_node *body; + char *string; +}; + +extern const char *const *man_macronames; + +__BEGIN_DECLS + +struct man; + +void man_free(struct man *); +struct man *man_alloc(void); +void man_reset(struct man *); +int man_parseln(struct man *, int, char *buf); +int man_endparse(struct man *); + +const struct man_node *man_node(const struct man *); +const struct man_meta *man_meta(const struct man *); + +__END_DECLS + +#endif /*!MAN_H*/ diff --git a/man_hash.c b/man_hash.c new file mode 100644 index 00000000..918258e1 --- /dev/null +++ b/man_hash.c @@ -0,0 +1,58 @@ +/* $Id: man_hash.c,v 1.1 2009/03/23 14:22:11 kristaps Exp $ */ +/* + * Copyright (c) 2008, 2009 Kristaps Dzonsons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include +#include + +#include "libman.h" + +/* ARGUSED */ +void +man_hash_free(void *htab) +{ + + /* Do nothing. */ +} + + +/* ARGUSED */ +void * +man_hash_alloc(void) +{ + + /* Do nothing. */ + return(NULL); +} + + +int +man_hash_find(const void *arg, const char *tmp) +{ + int i; + + for (i = 0; i < MAN_MAX; i++) + if (0 == strcasecmp(tmp, man_macronames[i])) + return(i); + + return(MAN_MAX); +} + diff --git a/man_macro.c b/man_macro.c new file mode 100644 index 00000000..0c35903a --- /dev/null +++ b/man_macro.c @@ -0,0 +1,96 @@ +/* $Id: man_macro.c,v 1.1 2009/03/23 14:22:11 kristaps Exp $ */ +/* + * Copyright (c) 2008, 2009 Kristaps Dzonsons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include + +#include "libman.h" + +static int in_line_eoln(MACRO_PROT_ARGS); + +const struct man_macro __man_macros[MAN_MAX] = { + { in_line_eoln, NULL }, /* MAN___ */ + { in_line_eoln, NULL }, /* MAN_TH */ + { in_line_eoln, NULL }, /* MAN_SH */ + { in_line_eoln, NULL }, /* MAN_SS */ + { in_line_eoln, NULL }, /* MAN_TP */ + { in_line_eoln, NULL }, /* MAN_LP */ + { in_line_eoln, NULL }, /* MAN_PP */ + { in_line_eoln, NULL }, /* MAN_P */ + { in_line_eoln, NULL }, /* MAN_IP */ + { in_line_eoln, NULL }, /* MAN_HP */ + { in_line_eoln, NULL }, /* MAN_SM */ + { in_line_eoln, NULL }, /* MAN_SB */ + { in_line_eoln, NULL }, /* MAN_BI */ + { in_line_eoln, NULL }, /* MAN_IB */ + { in_line_eoln, NULL }, /* MAN_BR */ + { in_line_eoln, NULL }, /* MAN_RB */ + { in_line_eoln, NULL }, /* MAN_R */ + { in_line_eoln, NULL }, /* MAN_B */ + { in_line_eoln, NULL }, /* MAN_I */ +}; + +const struct man_macro * const man_macros = __man_macros; + + +/* + * In-line macro that spans an entire line. May be callable, but has no + * subsequent parsed arguments. + */ +static int +in_line_eoln(MACRO_PROT_ARGS) +{ +#if 0 + int c, w, la; + char *p; + + if ( ! man_elem_alloc(man, line, ppos, tok, arg)) + return(0); + man->next = MDOC_NEXT_SIBLING; + + for (;;) { + la = *pos; + w = man_args(man, line, pos, buf, tok, &p); + + if (ARGS_ERROR == w) + return(0); + if (ARGS_EOLN == w) + break; + + c = ARGS_QWORD == w ? MAN_MAX : + lookup(man, line, la, tok, p); + + if (MDOC_MAX != c && -1 != c) { + if ( ! rew_elem(mdoc, tok)) + return(0); + return(mdoc_macro(mdoc, c, line, la, pos, buf)); + } else if (-1 == c) + return(0); + + if ( ! mdoc_word_alloc(mdoc, line, la, p)) + return(0); + } + + return(rew_elem(mdoc, tok)); +#endif + return(1); +} + diff --git a/mdoc.7 b/mdoc.7 index 6f0749f2..7d321aa7 100644 --- a/mdoc.7 +++ b/mdoc.7 @@ -1,4 +1,4 @@ -.\" $Id: mdoc.7,v 1.13 2009/03/22 08:52:27 kristaps Exp $ +.\" $Id: mdoc.7,v 1.14 2009/03/23 14:22:11 kristaps Exp $ .\" .\" Copyright (c) 2009 Kristaps Dzonsons .\" @@ -16,7 +16,7 @@ .\" TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR .\" PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: March 22 2009 $ +.Dd $Mdocdate: March 23 2009 $ .Dt mdoc 7 .Os .\" SECTION @@ -1016,4 +1016,9 @@ should be provided for Linux (\(`a la .Sq \&Ox , .Sq \&Nx etc.). +.\" LIST-ITEM +.It +There's no way to refer to references in +.Sq \&Rs/Re +blocks. .El diff --git a/mdoc.c b/mdoc.c index 8603fc15..395730c1 100644 --- a/mdoc.c +++ b/mdoc.c @@ -1,4 +1,4 @@ -/* $Id: mdoc.c,v 1.69 2009/03/21 09:42:07 kristaps Exp $ */ +/* $Id: mdoc.c,v 1.70 2009/03/23 14:22:11 kristaps Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons * @@ -24,7 +24,7 @@ #include #include -#include "private.h" +#include "libmdoc.h" /* * Main caller in the libmdoc library. This begins the parsing routine, @@ -32,6 +32,7 @@ * in macro.c, validate.c and action.c. */ +/* FIXME: have this accept line/pos/tok. */ static struct mdoc_node *mdoc_node_alloc(const struct mdoc *); static int mdoc_node_append(struct mdoc *, struct mdoc_node *); @@ -121,8 +122,9 @@ mdoc_meta(const struct mdoc *mdoc) /* - * Free up all resources contributed by a parse: the node tree, meta-data and - * so on. Then reallocate the root node for another parse. + * Free up all resources contributed by a parse: the node tree, + * meta-data and so on. Then reallocate the root node for another + * parse. */ void mdoc_reset(struct mdoc *mdoc) @@ -144,9 +146,10 @@ mdoc_reset(struct mdoc *mdoc) bzero(&mdoc->meta, sizeof(struct mdoc_meta)); mdoc->flags = 0; mdoc->lastnamed = mdoc->lastsec = 0; - - mdoc->first = mdoc->last = - xcalloc(1, sizeof(struct mdoc_node)); + mdoc->last = calloc(1, sizeof(struct mdoc_node)); + if (NULL == mdoc->last) + err(1, "calloc"); + mdoc->first = mdoc->last; mdoc->last->type = MDOC_ROOT; mdoc->next = MDOC_NEXT_CHILD; } @@ -184,14 +187,16 @@ mdoc_alloc(void *data, int pflags, const struct mdoc_cb *cb) { struct mdoc *p; - p = xcalloc(1, sizeof(struct mdoc)); + if (NULL == (p = calloc(1, sizeof(struct mdoc)))) + err(1, "calloc"); p->data = data; if (cb) (void)memcpy(&p->cb, cb, sizeof(struct mdoc_cb)); - p->last = p->first = - xcalloc(1, sizeof(struct mdoc_node)); + if (NULL == (p->first = calloc(1, sizeof(struct mdoc_node)))) + err(1, "calloc"); + p->last = p->first; p->last->type = MDOC_ROOT; p->pflags = pflags; p->next = MDOC_NEXT_CHILD; @@ -370,7 +375,8 @@ mdoc_node_alloc(const struct mdoc *mdoc) { struct mdoc_node *p; - p = xcalloc(1, sizeof(struct mdoc_node)); + if (NULL == (p = calloc(1, sizeof(struct mdoc_node)))) + err(1, "calloc"); p->sec = mdoc->lastsec; return(p); @@ -434,19 +440,6 @@ mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, int tok) } -int -mdoc_root_alloc(struct mdoc *mdoc) -{ - struct mdoc_node *p; - - p = mdoc_node_alloc(mdoc); - - p->type = MDOC_ROOT; - - return(mdoc_node_append(mdoc, p)); -} - - int mdoc_block_alloc(struct mdoc *mdoc, int line, int pos, int tok, struct mdoc_arg *args) @@ -500,7 +493,8 @@ mdoc_word_alloc(struct mdoc *mdoc, p->line = line; p->pos = pos; p->type = MDOC_TEXT; - p->string = xstrdup(word); + if (NULL == (p->string = strdup(word))) + err(1, "strdup"); return(mdoc_node_append(mdoc, p)); } @@ -629,12 +623,6 @@ parsemacro(struct mdoc *m, int ln, char *buf) if ( ! mdoc_macro(m, c, ln, 1, &i, buf)) goto err; - /* - * If we're in literal mode, then add a newline to the end of - * macro lines. Our frontends will interpret this correctly - * (it's documented in mdoc.3). - */ - return(1); err: /* Error out. */ diff --git a/mdoc.h b/mdoc.h index 17a94710..09578a6b 100644 --- a/mdoc.h +++ b/mdoc.h @@ -1,6 +1,6 @@ -/* $Id: mdoc.h,v 1.52 2009/03/21 13:09:29 kristaps Exp $ */ +/* $Id: mdoc.h,v 1.53 2009/03/23 14:22:11 kristaps Exp $ */ /* - * Copyright (c) 2008 Kristaps Dzonsons + * Copyright (c) 2008, 2009 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the diff --git a/mdoc_hash.c b/mdoc_hash.c new file mode 100644 index 00000000..5127095c --- /dev/null +++ b/mdoc_hash.c @@ -0,0 +1,175 @@ +/* $Id: mdoc_hash.c,v 1.1 2009/03/23 14:22:11 kristaps Exp $ */ +/* + * Copyright (c) 2008, 2009 Kristaps Dzonsons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include +#include + +#include "libmdoc.h" + +/* + * Routines for the perfect-hash hashtable used by the parser to look up + * tokens by their string-ified names (`.Fl' -> MDOC_Fl). The + * allocation penalty for this is 27 * 26 * sizeof(ptr). + */ + +void +mdoc_tokhash_free(void *htab) +{ + + free(htab); +} + + +void * +mdoc_tokhash_alloc(void) +{ + int i, major, minor, ind; + const void **htab; + + htab = calloc(27 * 26 * 3, sizeof(struct mdoc_macro *)); + if (NULL == htab) + err(1, "calloc"); + + for (i = 1; i < MDOC_MAX; i++) { + major = mdoc_macronames[i][0]; + assert((major >= 65 && major <= 90) || + major == 37); + + if (major == 37) + major = 0; + else + major -= 64; + + minor = mdoc_macronames[i][1]; + assert((minor >= 65 && minor <= 90) || + (minor == 49) || + (minor >= 97 && minor <= 122)); + + if (minor == 49) + minor = 0; + else if (minor <= 90) + minor -= 65; + else + minor -= 97; + + assert(major >= 0 && major < 27); + assert(minor >= 0 && minor < 26); + + ind = (major * 27 * 3) + (minor * 3); + + if (NULL == htab[ind]) { + htab[ind] = &mdoc_macros[i]; + continue; + } + + if (NULL == htab[++ind]) { + htab[ind] = &mdoc_macros[i]; + continue; + } + + assert(NULL == htab[++ind]); + htab[ind] = &mdoc_macros[i]; + } + + return((void *)htab); +} + + +int +mdoc_tokhash_find(const void *arg, const char *tmp) +{ + int major, minor, ind, slot; + const void **htab; + + htab = /* LINTED */ + (const void **)arg; + + if (0 == tmp[0] || 0 == tmp[1]) + return(MDOC_MAX); + if (tmp[2] && tmp[3]) + return(MDOC_MAX); + + if ( ! (tmp[0] == 37 || (tmp[0] >= 65 && tmp[0] <= 90))) + return(MDOC_MAX); + + if ( ! ((tmp[1] >= 65 && tmp[1] <= 90) || + (tmp[1] == 49) || + (tmp[1] >= 97 && tmp[1] <= 122))) + return(MDOC_MAX); + + if (tmp[0] == 37) + major = 0; + else + major = tmp[0] - 64; + + if (tmp[1] == 49) + minor = 0; + else if (tmp[1] <= 90) + minor = tmp[1] - 65; + else + minor = tmp[1] - 97; + + ind = (major * 27 * 3) + (minor * 3); + if (ind < 0 || ind >= (27 * 26 * 3)) + return(MDOC_MAX); + + if (htab[ind]) { + slot = htab[ind] - /* LINTED */ + (void *)mdoc_macros; + assert(0 == (size_t)slot % sizeof(struct mdoc_macro)); + slot /= sizeof(struct mdoc_macro); + if (mdoc_macronames[slot][0] == tmp[0] && + mdoc_macronames[slot][1] == tmp[1] && + (0 == tmp[2] || + mdoc_macronames[slot][2] == tmp[2])) + return(slot); + ind++; + } + + if (htab[ind]) { + slot = htab[ind] - /* LINTED */ + (void *)mdoc_macros; + assert(0 == (size_t)slot % sizeof(struct mdoc_macro)); + slot /= sizeof(struct mdoc_macro); + if (mdoc_macronames[slot][0] == tmp[0] && + mdoc_macronames[slot][1] == tmp[1] && + (0 == tmp[2] || + mdoc_macronames[slot][2] == tmp[2])) + return(slot); + ind++; + } + + if (NULL == htab[ind]) + return(MDOC_MAX); + slot = htab[ind] - /* LINTED */ + (void *)mdoc_macros; + assert(0 == (size_t)slot % sizeof(struct mdoc_macro)); + slot /= sizeof(struct mdoc_macro); + if (mdoc_macronames[slot][0] == tmp[0] && + mdoc_macronames[slot][1] == tmp[1] && + (0 == tmp[2] || + mdoc_macronames[slot][2] == tmp[2])) + return(slot); + + return(MDOC_MAX); +} + diff --git a/mdoc_macro.c b/mdoc_macro.c new file mode 100644 index 00000000..15fe4609 --- /dev/null +++ b/mdoc_macro.c @@ -0,0 +1,1446 @@ +/* $Id: mdoc_macro.c,v 1.1 2009/03/23 14:22:11 kristaps Exp $ */ +/* + * Copyright (c) 2008, 2009 Kristaps Dzonsons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include + +#include "libmdoc.h" + +/* + * This has scanning/parsing routines, each of which extract a macro and + * its arguments and parameters, then know how to progress to the next + * macro. + */ + +/* FIXME: .Fl, .Ar, .Cd handling of `|'. */ + +enum mwarn { + WIMPBRK, + WMACPARM, + WOBS +}; + +enum merr { + EOPEN, + EQUOT, + ENOCTX, + ENOPARMS +}; + +#define REWIND_REWIND (1 << 0) +#define REWIND_NOHALT (1 << 1) +#define REWIND_HALT (1 << 2) + +static int obsolete(MACRO_PROT_ARGS); +static int blk_part_exp(MACRO_PROT_ARGS); +static int in_line_eoln(MACRO_PROT_ARGS); +static int in_line_argn(MACRO_PROT_ARGS); +static int in_line(MACRO_PROT_ARGS); +static int blk_full(MACRO_PROT_ARGS); +static int blk_exp_close(MACRO_PROT_ARGS); +static int blk_part_imp(MACRO_PROT_ARGS); + +static int phrase(struct mdoc *, int, int, char *); +static int rew_dohalt(int, enum mdoc_type, + const struct mdoc_node *); +static int rew_alt(int); +static int rew_dobreak(int, const struct mdoc_node *); +static int rew_elem(struct mdoc *, int); +static int rew_impblock(struct mdoc *, int, int, int); +static int rew_expblock(struct mdoc *, int, int, int); +static int rew_subblock(enum mdoc_type, + struct mdoc *, int, int, int); +static int rew_last(struct mdoc *, struct mdoc_node *); +static int append_delims(struct mdoc *, int, int *, char *); +static int lookup(struct mdoc *, int, int, int, const char *); +static int pwarn(struct mdoc *, int, int, enum mwarn); +static int perr(struct mdoc *, int, int, enum merr); +static int swarn(struct mdoc *, enum mdoc_type, int, int, + const struct mdoc_node *); + +#define nerr(m, n, t) perr((m), (n)->line, (n)->pos, (t)) + +/* Central table of library: who gets parsed how. */ + +const struct mdoc_macro __mdoc_macros[MDOC_MAX] = { + { NULL, 0 }, /* \" */ + { in_line_eoln, MDOC_PROLOGUE }, /* Dd */ + { in_line_eoln, MDOC_PROLOGUE }, /* Dt */ + { in_line_eoln, MDOC_PROLOGUE }, /* Os */ + { blk_full, 0 }, /* Sh */ + { blk_full, 0 }, /* Ss */ + { in_line, 0 }, /* Pp */ + { blk_part_imp, MDOC_PARSED }, /* D1 */ + { blk_part_imp, MDOC_PARSED }, /* Dl */ + { blk_full, MDOC_EXPLICIT }, /* Bd */ + { blk_exp_close, MDOC_EXPLICIT }, /* Ed */ + { blk_full, MDOC_EXPLICIT }, /* Bl */ + { blk_exp_close, MDOC_EXPLICIT }, /* El */ + { blk_full, MDOC_PARSED }, /* It */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */ + { in_line, MDOC_PARSED }, /* An */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */ + { in_line_eoln, MDOC_CALLABLE }, /* Cd */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dv */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Er */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ev */ + { in_line_eoln, 0 }, /* Ex */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fa */ + { in_line_eoln, 0 }, /* Fd */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fn */ + { in_line, MDOC_PARSED }, /* Ft */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */ + { in_line_eoln, 0 }, /* In */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Li */ + { in_line_eoln, 0 }, /* Nd */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */ + { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Op */ + { obsolete, 0 }, /* Ot */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */ + { in_line_eoln, 0 }, /* Rv */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* St */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Va */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Vt */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Xr */ + { in_line_eoln, 0 }, /* %A */ + { in_line_eoln, 0 }, /* %B */ + { in_line_eoln, 0 }, /* %D */ + { in_line_eoln, 0 }, /* %I */ + { in_line_eoln, 0 }, /* %J */ + { in_line_eoln, 0 }, /* %N */ + { in_line_eoln, 0 }, /* %O */ + { in_line_eoln, 0 }, /* %P */ + { in_line_eoln, 0 }, /* %R */ + { in_line_eoln, 0 }, /* %T */ + { in_line_eoln, 0 }, /* %V */ + { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ac */ + { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Ao */ + { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Aq */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* At */ + { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Bc */ + { blk_full, MDOC_EXPLICIT }, /* Bf */ + { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bo */ + { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Bq */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bsx */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bx */ + { in_line_eoln, 0 }, /* Db */ + { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Dc */ + { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Do */ + { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Dq */ + { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ec */ + { blk_exp_close, MDOC_EXPLICIT }, /* Ef */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Em */ + { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Eo */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Fx */ + { in_line, MDOC_PARSED }, /* Ms */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* No */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ns */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Nx */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ox */ + { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Pc */ + { in_line_argn, MDOC_PARSED | MDOC_IGNDELIM }, /* Pf */ + { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Po */ + { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Pq */ + { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Qc */ + { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Ql */ + { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Qo */ + { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Qq */ + { blk_exp_close, MDOC_EXPLICIT }, /* Re */ + { blk_full, MDOC_EXPLICIT }, /* Rs */ + { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Sc */ + { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* So */ + { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Sq */ + { in_line_eoln, 0 }, /* Sm */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sx */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sy */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Tn */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ux */ + { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Xc */ + { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Xo */ + { blk_full, MDOC_EXPLICIT | MDOC_CALLABLE }, /* Fo */ + { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Fc */ + { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Oo */ + { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Oc */ + { blk_full, MDOC_EXPLICIT }, /* Bk */ + { blk_exp_close, MDOC_EXPLICIT }, /* Ek */ + { in_line_eoln, 0 }, /* Bt */ + { in_line_eoln, 0 }, /* Hf */ + { obsolete, 0 }, /* Fr */ + { in_line_eoln, 0 }, /* Ud */ + { in_line_eoln, 0 }, /* Lb */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ap */ + { in_line, 0 }, /* Lp */ + { in_line, MDOC_PARSED }, /* Lk */ + { in_line, MDOC_PARSED }, /* Mt */ + { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Brq */ + { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bro */ + { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Brc */ + { in_line_eoln, 0 }, /* %C */ + { obsolete, 0 }, /* Es */ + { obsolete, 0 }, /* En */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Dx */ + { in_line_eoln, 0 }, /* %Q */ +}; + +const struct mdoc_macro * const mdoc_macros = __mdoc_macros; + + +static int +perr(struct mdoc *mdoc, int line, int pos, enum merr type) +{ + char *p; + + p = NULL; + switch (type) { + case (EOPEN): + p = "explicit scope still open on exit"; + break; + case (EQUOT): + p = "unterminated quotation"; + break; + case (ENOCTX): + p = "closure has no prior context"; + break; + case (ENOPARMS): + p = "unexpect line arguments"; + break; + } + assert(p); + return(mdoc_perr(mdoc, line, pos, p)); +} + + +static int +pwarn(struct mdoc *mdoc, int line, int pos, enum mwarn type) +{ + char *p; + + p = NULL; + switch (type) { + case (WIMPBRK): + p = "crufty end-of-line scope violation"; + break; + case (WMACPARM): + p = "macro-like parameter"; + break; + case (WOBS): + p = "macro marked obsolete"; + break; + } + assert(p); + return(mdoc_pwarn(mdoc, line, pos, WARN_SYNTAX, p)); +} + + +static int +swarn(struct mdoc *mdoc, enum mdoc_type type, + int line, int pos, const struct mdoc_node *p) +{ + const char *n, *t, *tt; + + n = t = ""; + tt = "block"; + + switch (type) { + case (MDOC_BODY): + tt = "multi-line"; + break; + case (MDOC_HEAD): + tt = "line"; + break; + default: + break; + } + + switch (p->type) { + case (MDOC_BLOCK): + n = mdoc_macronames[p->tok]; + t = "block"; + break; + case (MDOC_BODY): + n = mdoc_macronames[p->tok]; + t = "multi-line"; + break; + case (MDOC_HEAD): + n = mdoc_macronames[p->tok]; + t = "line"; + break; + default: + break; + } + + if ( ! (MDOC_IGN_SCOPE & mdoc->pflags)) + return(mdoc_perr(mdoc, line, pos, + "%s scope breaks %s scope of %s", + tt, t, n)); + return(mdoc_pwarn(mdoc, line, pos, WARN_SYNTAX, + "%s scope breaks %s scope of %s", + tt, t, n)); +} + + +/* + * This is called at the end of parsing. It must traverse up the tree, + * closing out open [implicit] scopes. Obviously, open explicit scopes + * are errors. + */ +int +macro_end(struct mdoc *mdoc) +{ + struct mdoc_node *n; + + assert(mdoc->first); + assert(mdoc->last); + + /* Scan for open explicit scopes. */ + + n = MDOC_VALID & mdoc->last->flags ? + mdoc->last->parent : mdoc->last; + + for ( ; n; n = n->parent) { + if (MDOC_BLOCK != n->type) + continue; + if ( ! (MDOC_EXPLICIT & mdoc_macros[n->tok].flags)) + continue; + return(nerr(mdoc, n, EOPEN)); + } + + return(rew_last(mdoc, mdoc->first)); +} + +static int +lookup(struct mdoc *mdoc, int line, int pos, int from, const char *p) +{ + int res; + + res = mdoc_tokhash_find(mdoc->htab, p); + if (MDOC_PARSED & mdoc_macros[from].flags) + return(res); + if (MDOC_MAX == res) + return(res); + if ( ! pwarn(mdoc, line, pos, WMACPARM)) + return(-1); + return(MDOC_MAX); +} + + +static int +rew_last(struct mdoc *mdoc, struct mdoc_node *to) +{ + + assert(to); + mdoc->next = MDOC_NEXT_SIBLING; + + /* LINTED */ + while (mdoc->last != to) { + if ( ! mdoc_valid_post(mdoc)) + return(0); + if ( ! mdoc_action_post(mdoc)) + return(0); + mdoc->last = mdoc->last->parent; + assert(mdoc->last); + } + + if ( ! mdoc_valid_post(mdoc)) + return(0); + return(mdoc_action_post(mdoc)); +} + + +static int +rew_alt(int tok) +{ + switch (tok) { + case (MDOC_Ac): + return(MDOC_Ao); + case (MDOC_Bc): + return(MDOC_Bo); + case (MDOC_Brc): + return(MDOC_Bro); + case (MDOC_Dc): + return(MDOC_Do); + case (MDOC_Ec): + return(MDOC_Eo); + case (MDOC_Ed): + return(MDOC_Bd); + case (MDOC_Ef): + return(MDOC_Bf); + case (MDOC_Ek): + return(MDOC_Bk); + case (MDOC_El): + return(MDOC_Bl); + case (MDOC_Fc): + return(MDOC_Fo); + case (MDOC_Oc): + return(MDOC_Oo); + case (MDOC_Pc): + return(MDOC_Po); + case (MDOC_Qc): + return(MDOC_Qo); + case (MDOC_Re): + return(MDOC_Rs); + case (MDOC_Sc): + return(MDOC_So); + case (MDOC_Xc): + return(MDOC_Xo); + default: + break; + } + abort(); + /* NOTREACHED */ +} + + +/* + * Rewind rules. This indicates whether to stop rewinding + * (REWIND_HALT) without touching our current scope, stop rewinding and + * close our current scope (REWIND_REWIND), or continue (REWIND_NOHALT). + * The scope-closing and so on occurs in the various rew_* routines. + */ +static int +rew_dohalt(int tok, enum mdoc_type type, const struct mdoc_node *p) +{ + + if (MDOC_ROOT == p->type) + return(REWIND_HALT); + if (MDOC_VALID & p->flags) + return(REWIND_NOHALT); + + switch (tok) { + case (MDOC_Aq): + /* FALLTHROUGH */ + case (MDOC_Bq): + /* FALLTHROUGH */ + case (MDOC_Brq): + /* FALLTHROUGH */ + case (MDOC_D1): + /* FALLTHROUGH */ + case (MDOC_Dl): + /* FALLTHROUGH */ + case (MDOC_Dq): + /* FALLTHROUGH */ + case (MDOC_Op): + /* FALLTHROUGH */ + case (MDOC_Pq): + /* FALLTHROUGH */ + case (MDOC_Ql): + /* FALLTHROUGH */ + case (MDOC_Qq): + /* FALLTHROUGH */ + case (MDOC_Sq): + assert(MDOC_HEAD != type); + assert(MDOC_TAIL != type); + if (type == p->type && tok == p->tok) + return(REWIND_REWIND); + break; + case (MDOC_It): + assert(MDOC_TAIL != type); + if (type == p->type && tok == p->tok) + return(REWIND_REWIND); + if (MDOC_BODY == p->type && MDOC_Bl == p->tok) + return(REWIND_HALT); + break; + case (MDOC_Sh): + if (type == p->type && tok == p->tok) + return(REWIND_REWIND); + break; + case (MDOC_Ss): + assert(MDOC_TAIL != type); + if (type == p->type && tok == p->tok) + return(REWIND_REWIND); + if (MDOC_BODY == p->type && MDOC_Sh == p->tok) + return(REWIND_HALT); + break; + case (MDOC_Ao): + /* FALLTHROUGH */ + case (MDOC_Bd): + /* FALLTHROUGH */ + case (MDOC_Bf): + /* FALLTHROUGH */ + case (MDOC_Bk): + /* FALLTHROUGH */ + case (MDOC_Bl): + /* FALLTHROUGH */ + case (MDOC_Bo): + /* FALLTHROUGH */ + case (MDOC_Bro): + /* FALLTHROUGH */ + case (MDOC_Do): + /* FALLTHROUGH */ + case (MDOC_Eo): + /* FALLTHROUGH */ + case (MDOC_Fo): + /* FALLTHROUGH */ + case (MDOC_Oo): + /* FALLTHROUGH */ + case (MDOC_Po): + /* FALLTHROUGH */ + case (MDOC_Qo): + /* FALLTHROUGH */ + case (MDOC_Rs): + /* FALLTHROUGH */ + case (MDOC_So): + /* FALLTHROUGH */ + case (MDOC_Xo): + if (type == p->type && tok == p->tok) + return(REWIND_REWIND); + break; + + /* Multi-line explicit scope close. */ + case (MDOC_Ac): + /* FALLTHROUGH */ + case (MDOC_Bc): + /* FALLTHROUGH */ + case (MDOC_Brc): + /* FALLTHROUGH */ + case (MDOC_Dc): + /* FALLTHROUGH */ + case (MDOC_Ec): + /* FALLTHROUGH */ + case (MDOC_Ed): + /* FALLTHROUGH */ + case (MDOC_Ek): + /* FALLTHROUGH */ + case (MDOC_El): + /* FALLTHROUGH */ + case (MDOC_Fc): + /* FALLTHROUGH */ + case (MDOC_Ef): + /* FALLTHROUGH */ + case (MDOC_Oc): + /* FALLTHROUGH */ + case (MDOC_Pc): + /* FALLTHROUGH */ + case (MDOC_Qc): + /* FALLTHROUGH */ + case (MDOC_Re): + /* FALLTHROUGH */ + case (MDOC_Sc): + /* FALLTHROUGH */ + case (MDOC_Xc): + if (type == p->type && rew_alt(tok) == p->tok) + return(REWIND_REWIND); + break; + default: + abort(); + /* NOTREACHED */ + } + + return(REWIND_NOHALT); +} + + +/* + * See if we can break an encountered scope (the rew_dohalt has returned + * REWIND_NOHALT). + */ +static int +rew_dobreak(int tok, const struct mdoc_node *p) +{ + + assert(MDOC_ROOT != p->type); + if (MDOC_ELEM == p->type) + return(1); + if (MDOC_TEXT == p->type) + return(1); + if (MDOC_VALID & p->flags) + return(1); + + switch (tok) { + case (MDOC_It): + return(MDOC_It == p->tok); + case (MDOC_Ss): + return(MDOC_Ss == p->tok); + case (MDOC_Sh): + if (MDOC_Ss == p->tok) + return(1); + return(MDOC_Sh == p->tok); + case (MDOC_El): + if (MDOC_It == p->tok) + return(1); + break; + case (MDOC_Oc): + /* XXX - experimental! */ + if (MDOC_Op == p->tok) + return(1); + break; + default: + break; + } + + if (MDOC_EXPLICIT & mdoc_macros[tok].flags) + return(p->tok == rew_alt(tok)); + else if (MDOC_BLOCK == p->type) + return(1); + + return(tok == p->tok); +} + + +static int +rew_elem(struct mdoc *mdoc, int tok) +{ + struct mdoc_node *n; + + n = mdoc->last; + if (MDOC_ELEM != n->type) + n = n->parent; + assert(MDOC_ELEM == n->type); + assert(tok == n->tok); + + return(rew_last(mdoc, n)); +} + + +static int +rew_subblock(enum mdoc_type type, struct mdoc *mdoc, + int tok, int line, int ppos) +{ + struct mdoc_node *n; + int c; + + /* LINTED */ + for (n = mdoc->last; n; n = n->parent) { + c = rew_dohalt(tok, type, n); + if (REWIND_HALT == c) + return(1); + if (REWIND_REWIND == c) + break; + else if (rew_dobreak(tok, n)) + continue; + if ( ! swarn(mdoc, type, line, ppos, n)) + return(0); + } + + assert(n); + return(rew_last(mdoc, n)); +} + + +static int +rew_expblock(struct mdoc *mdoc, int tok, int line, int ppos) +{ + struct mdoc_node *n; + int c; + + /* LINTED */ + for (n = mdoc->last; n; n = n->parent) { + c = rew_dohalt(tok, MDOC_BLOCK, n); + if (REWIND_HALT == c) + return(perr(mdoc, line, ppos, ENOCTX)); + if (REWIND_REWIND == c) + break; + else if (rew_dobreak(tok, n)) + continue; + if ( ! swarn(mdoc, MDOC_BLOCK, line, ppos, n)) + return(0); + } + + assert(n); + return(rew_last(mdoc, n)); +} + + +static int +rew_impblock(struct mdoc *mdoc, int tok, int line, int ppos) +{ + struct mdoc_node *n; + int c; + + /* LINTED */ + for (n = mdoc->last; n; n = n->parent) { + c = rew_dohalt(tok, MDOC_BLOCK, n); + if (REWIND_HALT == c) + return(1); + else if (REWIND_REWIND == c) + break; + else if (rew_dobreak(tok, n)) + continue; + if ( ! swarn(mdoc, MDOC_BLOCK, line, ppos, n)) + return(0); + } + + assert(n); + return(rew_last(mdoc, n)); +} + + +static int +append_delims(struct mdoc *mdoc, int line, int *pos, char *buf) +{ + int c, lastarg; + char *p; + + if (0 == buf[*pos]) + return(1); + + for (;;) { + lastarg = *pos; + c = mdoc_args(mdoc, line, pos, buf, 0, &p); + assert(ARGS_PHRASE != c); + + if (ARGS_ERROR == c) + return(0); + else if (ARGS_EOLN == c) + break; + assert(mdoc_isdelim(p)); + if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) + return(0); + mdoc->next = MDOC_NEXT_SIBLING; + } + + return(1); +} + + +/* + * Close out block partial/full explicit. + */ +static int +blk_exp_close(MACRO_PROT_ARGS) +{ + int j, c, lastarg, maxargs, flushed; + char *p; + + switch (tok) { + case (MDOC_Ec): + maxargs = 1; + break; + default: + maxargs = 0; + break; + } + + if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) { + if (0 == buf[*pos]) { + if ( ! rew_subblock(MDOC_BODY, mdoc, + tok, line, ppos)) + return(0); + return(rew_expblock(mdoc, tok, line, ppos)); + } + return(perr(mdoc, line, ppos, ENOPARMS)); + } + + if ( ! rew_subblock(MDOC_BODY, mdoc, tok, line, ppos)) + return(0); + + if (maxargs > 0) { + if ( ! mdoc_tail_alloc(mdoc, line, + ppos, rew_alt(tok))) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + } + + for (lastarg = ppos, flushed = j = 0; ; j++) { + lastarg = *pos; + + if (j == maxargs && ! flushed) { + if ( ! rew_expblock(mdoc, tok, line, ppos)) + return(0); + flushed = 1; + } + + c = mdoc_args(mdoc, line, pos, buf, tok, &p); + + if (ARGS_ERROR == c) + return(0); + if (ARGS_PUNCT == c) + break; + if (ARGS_EOLN == c) + break; + + if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) + return(0); + else if (MDOC_MAX != c) { + if ( ! flushed) { + if ( ! rew_expblock(mdoc, tok, + line, ppos)) + return(0); + flushed = 1; + } + if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf)) + return(0); + break; + } + + if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) + return(0); + mdoc->next = MDOC_NEXT_SIBLING; + } + + if ( ! flushed && ! rew_expblock(mdoc, tok, line, ppos)) + return(0); + + if (ppos > 1) + return(1); + return(append_delims(mdoc, line, pos, buf)); +} + + +/* + * In-line macros where reserved words cause scope close-reopen. + */ +static int +in_line(MACRO_PROT_ARGS) +{ + int la, lastpunct, c, w; + struct mdoc_arg *arg; + char *p; + + for (la = ppos, arg = NULL;; ) { + la = *pos; + c = mdoc_argv(mdoc, line, tok, &arg, pos, buf); + + if (ARGV_WORD == c) { + *pos = la; + break; + } + + if (ARGV_EOLN == c) + break; + if (ARGV_ARG == c) + continue; + + mdoc_argv_free(arg); + return(0); + } + + if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + + for (lastpunct = 0;; ) { + la = *pos; + w = mdoc_args(mdoc, line, pos, buf, tok, &p); + + if (ARGS_ERROR == w) + return(0); + if (ARGS_EOLN == w) + break; + if (ARGS_PUNCT == w) + break; + + /* Quoted words shouldn't be looked-up. */ + + c = ARGS_QWORD == w ? MDOC_MAX : + lookup(mdoc, line, la, tok, p); + + /* MDOC_MAX (not a macro) or -1 (error). */ + + if (MDOC_MAX != c && -1 != c) { + if (0 == lastpunct && ! rew_elem(mdoc, tok)) + return(0); + c = mdoc_macro(mdoc, c, line, la, pos, buf); + if (0 == c) + return(0); + if (ppos > 1) + return(1); + return(append_delims(mdoc, line, pos, buf)); + } else if (-1 == c) + return(0); + + /* Non-quote-enclosed punctuation. */ + + if (ARGS_QWORD != w && mdoc_isdelim(p)) { + if (0 == lastpunct && ! rew_elem(mdoc, tok)) + return(0); + lastpunct = 1; + } else if (lastpunct) { + c = mdoc_elem_alloc(mdoc, line, ppos, tok, arg); + + if (0 == c) + return(0); + + mdoc->next = MDOC_NEXT_CHILD; + lastpunct = 0; + } + + if ( ! mdoc_word_alloc(mdoc, line, la, p)) + return(0); + mdoc->next = MDOC_NEXT_SIBLING; + } + + if (0 == lastpunct && ! rew_elem(mdoc, tok)) + return(0); + if (ppos > 1) + return(1); + return(append_delims(mdoc, line, pos, buf)); +} + + +/* + * Block full-explicit and full-implicit. + */ +static int +blk_full(MACRO_PROT_ARGS) +{ + int c, lastarg, reopen; + struct mdoc_arg *arg; + char *p; + + if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) { + if ( ! rew_subblock(MDOC_BODY, mdoc, + tok, line, ppos)) + return(0); + if ( ! rew_impblock(mdoc, tok, line, ppos)) + return(0); + } + + for (arg = NULL;; ) { + lastarg = *pos; + c = mdoc_argv(mdoc, line, tok, &arg, pos, buf); + + if (ARGV_WORD == c) { + *pos = lastarg; + break; + } + + if (ARGV_EOLN == c) + break; + if (ARGV_ARG == c) + continue; + + mdoc_argv_free(arg); + return(0); + } + + if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, arg)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + + if (0 == buf[*pos]) { + if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) + return(0); + if ( ! rew_subblock(MDOC_HEAD, mdoc, + tok, line, ppos)) + return(0); + if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + return(1); + } + + if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + + for (reopen = 0;; ) { + lastarg = *pos; + c = mdoc_args(mdoc, line, pos, buf, tok, &p); + + if (ARGS_ERROR == c) + return(0); + if (ARGS_EOLN == c) + break; + if (ARGS_PHRASE == c) { + if (reopen && ! mdoc_head_alloc + (mdoc, line, ppos, tok)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + /* + * Phrases are self-contained macro phrases used + * in the columnar output of a macro. They need + * special handling. + */ + if ( ! phrase(mdoc, line, lastarg, buf)) + return(0); + if ( ! rew_subblock(MDOC_HEAD, mdoc, + tok, line, ppos)) + return(0); + + reopen = 1; + continue; + } + + if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) + return(0); + + if (MDOC_MAX == c) { + if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) + return(0); + mdoc->next = MDOC_NEXT_SIBLING; + continue; + } + + if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf)) + return(0); + break; + } + + if (1 == ppos && ! append_delims(mdoc, line, pos, buf)) + return(0); + if ( ! rew_subblock(MDOC_HEAD, mdoc, tok, line, ppos)) + return(0); + + if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + + return(1); +} + + +/* + * Block partial-imnplicit scope. + */ +static int +blk_part_imp(MACRO_PROT_ARGS) +{ + int lastarg, c; + char *p; + struct mdoc_node *blk, *body, *n; + + if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + blk = mdoc->last; + + if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) + return(0); + mdoc->next = MDOC_NEXT_SIBLING; + + if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + body = mdoc->last; + + /* XXX - no known argument macros. */ + + for (lastarg = ppos;; ) { + lastarg = *pos; + c = mdoc_args(mdoc, line, pos, buf, tok, &p); + assert(ARGS_PHRASE != c); + + if (ARGS_ERROR == c) + return(0); + if (ARGS_PUNCT == c) + break; + if (ARGS_EOLN == c) + break; + + if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) + return(0); + else if (MDOC_MAX == c) { + if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) + return(0); + mdoc->next = MDOC_NEXT_SIBLING; + continue; + } + + if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf)) + return(0); + break; + } + + /* + * Since we know what our context is, we can rewind directly to + * it. This allows us to accomodate for our scope being + * violated by another token. + */ + + for (n = mdoc->last; n; n = n->parent) + if (body == n) + break; + + if (NULL == n && ! pwarn(mdoc, body->line, body->pos, WIMPBRK)) + return(0); + + if (n && ! rew_last(mdoc, body)) + return(0); + + if (1 == ppos && ! append_delims(mdoc, line, pos, buf)) + return(0); + + if (n && ! rew_last(mdoc, blk)) + return(0); + + return(1); +} + + +/* + * Block partial-explicit macros. + */ +static int +blk_part_exp(MACRO_PROT_ARGS) +{ + int lastarg, flushed, j, c, maxargs; + char *p; + + lastarg = ppos; + flushed = 0; + + /* + * Number of arguments (head arguments). Only `Eo' has these, + */ + + switch (tok) { + case (MDOC_Eo): + maxargs = 1; + break; + default: + maxargs = 0; + break; + } + + if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + + if (0 == maxargs) { + if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) + return(0); + if ( ! rew_subblock(MDOC_HEAD, mdoc, + tok, line, ppos)) + return(0); + if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) + return(0); + flushed = 1; + } else if ( ! mdoc_head_alloc(mdoc, line, ppos, tok)) + return(0); + + mdoc->next = MDOC_NEXT_CHILD; + + for (j = 0; ; j++) { + lastarg = *pos; + if (j == maxargs && ! flushed) { + if ( ! rew_subblock(MDOC_HEAD, mdoc, + tok, line, ppos)) + return(0); + flushed = 1; + if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + } + + c = mdoc_args(mdoc, line, pos, buf, tok, &p); + assert(ARGS_PHRASE != c); + + if (ARGS_ERROR == c) + return(0); + if (ARGS_PUNCT == c) + break; + if (ARGS_EOLN == c) + break; + + if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) + return(0); + else if (MDOC_MAX != c) { + if ( ! flushed) { + if ( ! rew_subblock(MDOC_HEAD, mdoc, + tok, line, ppos)) + return(0); + flushed = 1; + if ( ! mdoc_body_alloc(mdoc, line, + ppos, tok)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + } + if ( ! mdoc_macro(mdoc, c, line, lastarg, + pos, buf)) + return(0); + break; + } + + if ( ! flushed && mdoc_isdelim(p)) { + if ( ! rew_subblock(MDOC_HEAD, mdoc, + tok, line, ppos)) + return(0); + flushed = 1; + if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + } + + if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) + return(0); + mdoc->next = MDOC_NEXT_SIBLING; + } + + if ( ! flushed) { + if ( ! rew_subblock(MDOC_HEAD, mdoc, tok, line, ppos)) + return(0); + if ( ! mdoc_body_alloc(mdoc, line, ppos, tok)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + } + + if (ppos > 1) + return(1); + return(append_delims(mdoc, line, pos, buf)); +} + + +/* + * In-line macros where reserved words signal closure of the macro. + * Macros also have a fixed number of arguments. + */ +static int +in_line_argn(MACRO_PROT_ARGS) +{ + int lastarg, flushed, j, c, maxargs; + struct mdoc_arg *arg; + char *p; + + + /* + * Fixed maximum arguments per macro. Some of these have none + * and close as soon as the invocation is parsed. + */ + + switch (tok) { + case (MDOC_Ap): + /* FALLTHROUGH */ + case (MDOC_No): + /* FALLTHROUGH */ + case (MDOC_Ns): + /* FALLTHROUGH */ + case (MDOC_Ux): + maxargs = 0; + break; + default: + maxargs = 1; + break; + } + + for (lastarg = ppos, arg = NULL;; ) { + lastarg = *pos; + c = mdoc_argv(mdoc, line, tok, &arg, pos, buf); + + if (ARGV_WORD == c) { + *pos = lastarg; + break; + } + + if (ARGV_EOLN == c) + break; + if (ARGV_ARG == c) + continue; + + mdoc_argv_free(arg); + return(0); + } + + if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg)) + return(0); + mdoc->next = MDOC_NEXT_CHILD; + + for (flushed = j = 0; ; j++) { + lastarg = *pos; + + if (j == maxargs && ! flushed) { + if ( ! rew_elem(mdoc, tok)) + return(0); + flushed = 1; + } + + c = mdoc_args(mdoc, line, pos, buf, tok, &p); + + if (ARGS_ERROR == c) + return(0); + if (ARGS_PUNCT == c) + break; + if (ARGS_EOLN == c) + break; + + if (-1 == (c = lookup(mdoc, line, lastarg, tok, p))) + return(0); + else if (MDOC_MAX != c) { + if ( ! flushed && ! rew_elem(mdoc, tok)) + return(0); + flushed = 1; + if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf)) + return(0); + break; + } + + if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) && + ! flushed && mdoc_isdelim(p)) { + if ( ! rew_elem(mdoc, tok)) + return(0); + flushed = 1; + } + + if ( ! mdoc_word_alloc(mdoc, line, lastarg, p)) + return(0); + mdoc->next = MDOC_NEXT_SIBLING; + } + + if ( ! flushed && ! rew_elem(mdoc, tok)) + return(0); + + if (ppos > 1) + return(1); + return(append_delims(mdoc, line, pos, buf)); +} + + +/* + * In-line macro that spans an entire line. May be callable, but has no + * subsequent parsed arguments. + */ +static int +in_line_eoln(MACRO_PROT_ARGS) +{ + int c, w, la; + struct mdoc_arg *arg; + char *p; + + assert( ! (MDOC_PARSED & mdoc_macros[tok].flags)); + + arg = NULL; + + for (;;) { + la = *pos; + c = mdoc_argv(mdoc, line, tok, &arg, pos, buf); + + if (ARGV_WORD == c) { + *pos = la; + break; + } + if (ARGV_EOLN == c) + break; + if (ARGV_ARG == c) + continue; + + mdoc_argv_free(arg); + return(0); + } + + if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg)) + return(0); + + mdoc->next = MDOC_NEXT_CHILD; + + for (;;) { + la = *pos; + w = mdoc_args(mdoc, line, pos, buf, tok, &p); + + if (ARGS_ERROR == w) + return(0); + if (ARGS_EOLN == w) + break; + + c = ARGS_QWORD == w ? MDOC_MAX : + lookup(mdoc, line, la, tok, p); + + if (MDOC_MAX != c && -1 != c) { + if ( ! rew_elem(mdoc, tok)) + return(0); + return(mdoc_macro(mdoc, c, line, la, pos, buf)); + } else if (-1 == c) + return(0); + + if ( ! mdoc_word_alloc(mdoc, line, la, p)) + return(0); + mdoc->next = MDOC_NEXT_SIBLING; + } + + return(rew_elem(mdoc, tok)); +} + + +/* ARGSUSED */ +static int +obsolete(MACRO_PROT_ARGS) +{ + + return(pwarn(mdoc, line, ppos, WOBS)); +} + + +static int +phrase(struct mdoc *mdoc, int line, int ppos, char *buf) +{ + int i, la, c, quoted; + + /* + * Parse over words in a phrase. We have to handle this + * specially because we assume no calling context -- in normal + * circumstances, we switch argument parsing based on whether + * the parent macro accepts quotes, tabs, etc. Here, anything + * goes. + */ + + for (i = ppos; buf[i]; ) { + assert(' ' != buf[i]); + la = i; + quoted = 0; + + /* + * Read to next token. If quoted (check not escaped), + * scan ahead to next unescaped quote. If not quoted or + * escape-quoted, then scan ahead to next space. + */ + + if ((i && '\"' == buf[i] && '\\' != buf[i - 1]) || + (0 == i && '\"' == buf[i])) { + for (la = ++i; buf[i]; i++) + if ('\"' != buf[i]) + continue; + else if ('\\' != buf[i - 1]) + break; + if (0 == buf[i]) + return(perr(mdoc, line, la, EQUOT)); + quoted = 1; + } else + for ( ; buf[i]; i++) + if (i && ' ' == buf[i]) { + if ('\\' != buf[i - 1]) + break; + } else if (' ' == buf[i]) + break; + + /* If not end-of-line, terminate argument. */ + + if (buf[i]) + buf[i++] = 0; + + /* Read to next argument. */ + + for ( ; buf[i] && ' ' == buf[i]; i++) + /* Spin. */ ; + + /* + * If we're a non-quoted string, try to look up the + * value as a macro and execute it, if found. + */ + + c = quoted ? MDOC_MAX : + mdoc_tokhash_find(mdoc->htab, &buf[la]); + + if (MDOC_MAX != c) { + if ( ! mdoc_macro(mdoc, c, line, la, &i, buf)) + return(0); + return(append_delims(mdoc, line, &i, buf)); + } + + /* A regular word or quoted string. */ + + if ( ! mdoc_word_alloc(mdoc, line, la, &buf[la])) + return(0); + mdoc->next = MDOC_NEXT_SIBLING; + } + + return(1); +} diff --git a/msec.c b/msec.c index af22911c..227d4fcb 100644 --- a/msec.c +++ b/msec.c @@ -1,4 +1,4 @@ -/* $Id: msec.c,v 1.1 2009/03/16 22:19:19 kristaps Exp $ */ +/* $Id: msec.c,v 1.2 2009/03/23 14:22:11 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -19,7 +19,7 @@ #include #include -#include "private.h" +#include "libmdoc.h" #define LINE(x, y) \ if (0 == strcmp(p, x)) return(y); diff --git a/private.h b/private.h deleted file mode 100644 index fe620fd1..00000000 --- a/private.h +++ /dev/null @@ -1,160 +0,0 @@ -/* $Id: private.h,v 1.91 2009/03/21 13:09:29 kristaps Exp $ */ -/* - * Copyright (c) 2008 Kristaps Dzonsons - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the - * above copyright notice and this permission notice appear in all - * copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE - * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL - * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR - * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ -#ifndef PRIVATE_H -#define PRIVATE_H - -#include "mdoc.h" - -enum mdoc_next { - MDOC_NEXT_SIBLING = 0, - MDOC_NEXT_CHILD -}; - -struct mdoc { - void *data; - struct mdoc_cb cb; - void *htab; - int linetok; - int flags; -#define MDOC_HALT (1 << 0) -#define MDOC_LITERAL (1 << 1) - int pflags; - enum mdoc_next next; - struct mdoc_node *last; - struct mdoc_node *first; - struct mdoc_meta meta; - enum mdoc_sec lastnamed; - enum mdoc_sec lastsec; -}; - - -#define MACRO_PROT_ARGS struct mdoc *mdoc, int tok, int line, \ - int ppos, int *pos, char *buf - -struct mdoc_macro { - int (*fp)(MACRO_PROT_ARGS); - int flags; -#define MDOC_CALLABLE (1 << 0) -#define MDOC_PARSED (1 << 1) -#define MDOC_EXPLICIT (1 << 2) -#define MDOC_PROLOGUE (1 << 3) -#define MDOC_IGNDELIM (1 << 4) - /* Reserved words in arguments treated as text. */ -}; - -#define mdoc_nwarn(mdoc, node, type, fmt, ...) \ - mdoc_vwarn((mdoc), (node)->line, \ - (node)->pos, (type), (fmt), ##__VA_ARGS__) - -#define mdoc_nerr(mdoc, node, fmt, ...) \ - mdoc_verr((mdoc), (node)->line, \ - (node)->pos, (fmt), ##__VA_ARGS__) - -#define mdoc_warn(mdoc, type, fmt, ...) \ - mdoc_vwarn((mdoc), (mdoc)->last->line, \ - (mdoc)->last->pos, (type), (fmt), ##__VA_ARGS__) - -#define mdoc_err(mdoc, fmt, ...) \ - mdoc_verr((mdoc), (mdoc)->last->line, \ - (mdoc)->last->pos, (fmt), ##__VA_ARGS__) - -#define mdoc_msg(mdoc, fmt, ...) \ - mdoc_vmsg((mdoc), (mdoc)->last->line, \ - (mdoc)->last->pos, (fmt), ##__VA_ARGS__) - -#define mdoc_pmsg(mdoc, line, pos, fmt, ...) \ - mdoc_vmsg((mdoc), (line), \ - (pos), (fmt), ##__VA_ARGS__) - -#define mdoc_pwarn(mdoc, line, pos, type, fmt, ...) \ - mdoc_vwarn((mdoc), (line), \ - (pos), (type), (fmt), ##__VA_ARGS__) - -#define mdoc_perr(mdoc, line, pos, fmt, ...) \ - mdoc_verr((mdoc), (line), \ - (pos), (fmt), ##__VA_ARGS__) - -extern const struct mdoc_macro *const mdoc_macros; - -__BEGIN_DECLS - -int mdoc_vwarn(struct mdoc *, int, int, - enum mdoc_warn, const char *, ...); -void mdoc_vmsg(struct mdoc *, int, int, - const char *, ...); -int mdoc_verr(struct mdoc *, int, int, - const char *, ...); -int mdoc_macro(MACRO_PROT_ARGS); -int mdoc_word_alloc(struct mdoc *, - int, int, const char *); -int mdoc_elem_alloc(struct mdoc *, int, int, - int, struct mdoc_arg *); -int mdoc_block_alloc(struct mdoc *, int, int, - int, struct mdoc_arg *); -int mdoc_root_alloc(struct mdoc *); -int mdoc_head_alloc(struct mdoc *, int, int, int); -int mdoc_tail_alloc(struct mdoc *, int, int, int); -int mdoc_body_alloc(struct mdoc *, int, int, int); -void mdoc_node_free(struct mdoc_node *); -void mdoc_node_freelist(struct mdoc_node *); -void *mdoc_tokhash_alloc(void); -int mdoc_tokhash_find(const void *, const char *); -void mdoc_tokhash_free(void *); -int mdoc_iscdelim(char); -int mdoc_isdelim(const char *); -size_t mdoc_isescape(const char *); -enum mdoc_sec mdoc_atosec(const char *); -time_t mdoc_atotime(const char *); -size_t mdoc_macro2len(int); -const char *mdoc_a2arch(const char *); -const char *mdoc_a2vol(const char *); -const char *mdoc_a2msec(const char *); -int mdoc_valid_pre(struct mdoc *, - const struct mdoc_node *); -int mdoc_valid_post(struct mdoc *); -int mdoc_action_pre(struct mdoc *, - const struct mdoc_node *); -int mdoc_action_post(struct mdoc *); -int mdoc_argv(struct mdoc *, int, int, - struct mdoc_arg **, int *, char *); -#define ARGV_ERROR (-1) -#define ARGV_EOLN (0) -#define ARGV_ARG (1) -#define ARGV_WORD (2) -void mdoc_argv_free(struct mdoc_arg *); -int mdoc_args(struct mdoc *, int, - int *, char *, int, char **); -#define ARGS_ERROR (-1) -#define ARGS_EOLN (0) -#define ARGS_WORD (1) -#define ARGS_PUNCT (2) -#define ARGS_QWORD (3) -#define ARGS_PHRASE (4) -int xstrlcpys(char *, const struct mdoc_node *, size_t); -int xstrlcat(char *, const char *, size_t); -int xstrlcpy(char *, const char *, size_t); -int xstrcmp(const char *, const char *); -void *xcalloc(size_t, size_t); -void *xrealloc(void *, size_t); -char *xstrdup(const char *); -int macro_end(struct mdoc *); - -__END_DECLS - -#endif /*!PRIVATE_H*/ diff --git a/st.c b/st.c index e1447c93..deb867f5 100644 --- a/st.c +++ b/st.c @@ -1,4 +1,4 @@ -/* $Id: st.c,v 1.1 2009/03/16 22:19:19 kristaps Exp $ */ +/* $Id: st.c,v 1.2 2009/03/23 14:22:11 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -19,7 +19,7 @@ #include #include -#include "private.h" +#include "libmdoc.h" #define LINE(x, y) \ if (0 == strcmp(p, x)) return(y); diff --git a/strings.c b/strings.c index 436a7dd4..cc1c3a44 100644 --- a/strings.c +++ b/strings.c @@ -1,4 +1,4 @@ -/* $Id: strings.c,v 1.31 2009/03/21 13:47:02 kristaps Exp $ */ +/* $Id: strings.c,v 1.32 2009/03/23 14:22:11 kristaps Exp $ */ /* * Copyright (c) 2008 Kristaps Dzonsons * @@ -24,7 +24,7 @@ #include #include -#include "private.h" +#include "libmdoc.h" /* * Various string-literal operations: converting scalars to and from @@ -210,9 +210,9 @@ mdoc_atotime(const char *p) (void)memset(&tm, 0, sizeof(struct tm)); - if (xstrcmp(p, "$Mdocdate: March 21 2009 $")) + if (xstrcmp(p, "$Mdocdate: March 23 2009 $")) return(time(NULL)); - if ((pp = strptime(p, "$Mdocdate: March 21 2009 $", &tm)) && 0 == *pp) + if ((pp = strptime(p, "$Mdocdate: March 23 2009 $", &tm)) && 0 == *pp) return(mktime(&tm)); /* XXX - this matches "June 1999", which is wrong. */ if ((pp = strptime(p, "%b %d %Y", &tm)) && 0 == *pp) diff --git a/term.h b/term.h index 9a9be595..8182d8ba 100644 --- a/term.h +++ b/term.h @@ -1,6 +1,6 @@ -/* $Id: term.h,v 1.30 2009/03/21 09:48:30 kristaps Exp $ */ +/* $Id: term.h,v 1.31 2009/03/23 14:22:11 kristaps Exp $ */ /* - * Copyright (c) 2008 Kristaps Dzonsons + * Copyright (c) 2008, 2009 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the diff --git a/validate.c b/validate.c index 7c3e514d..6ee4bda7 100644 --- a/validate.c +++ b/validate.c @@ -1,4 +1,4 @@ -/* $Id: validate.c,v 1.92 2009/03/21 21:09:00 kristaps Exp $ */ +/* $Id: validate.c,v 1.93 2009/03/23 14:22:11 kristaps Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons * @@ -23,7 +23,7 @@ #include #include -#include "private.h" +#include "libmdoc.h" /* FIXME: .Bl -diag can't have non-text children in HEAD. */ /* TODO: ignoring Pp (it's superfluous in some invocations). */ diff --git a/vol.c b/vol.c index 56e915b2..362dc4b9 100644 --- a/vol.c +++ b/vol.c @@ -1,4 +1,4 @@ -/* $Id: vol.c,v 1.1 2009/03/16 22:19:19 kristaps Exp $ */ +/* $Id: vol.c,v 1.2 2009/03/23 14:22:11 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -19,7 +19,7 @@ #include #include -#include "private.h" +#include "libmdoc.h" #define LINE(x, y) \ if (0 == strcmp(p, x)) return(y); diff --git a/xstd.c b/xstd.c index 00cec8bb..c22a0ff2 100644 --- a/xstd.c +++ b/xstd.c @@ -1,4 +1,4 @@ -/* $Id: xstd.c,v 1.12 2009/03/19 16:40:49 kristaps Exp $ */ +/* $Id: xstd.c,v 1.13 2009/03/23 14:22:11 kristaps Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons * @@ -21,7 +21,7 @@ #include #include -#include "private.h" +#include "libmdoc.h" #ifdef __linux__ extern size_t strlcpy(char *, const char *, size_t); @@ -64,15 +64,6 @@ xrealloc(void *ptr, size_t sz) return(p); } -void * -xcalloc(size_t num, size_t sz) -{ - void *p; - - if (NULL == (p = calloc(num, sz))) - err(EXIT_FAILURE, "calloc"); - return(p); -} char * xstrdup(const char *p) -- cgit v1.2.3