diff options
-rw-r--r-- | DESCR | 2 | ||||
-rw-r--r-- | Makefile | 66 | ||||
-rw-r--r-- | action.c | 1 | ||||
-rw-r--r-- | mdoc.h | 8 | ||||
-rw-r--r-- | mdocml.1 | 100 | ||||
-rw-r--r-- | mdocterm.1 | 113 | ||||
-rw-r--r-- | mdocterm.c | 289 | ||||
-rw-r--r-- | mdoctree.1 | 113 | ||||
-rw-r--r-- | mdoctree.c (renamed from mdocml.c) | 252 | ||||
-rw-r--r-- | private.h | 6 | ||||
-rw-r--r-- | strings.c | 6 | ||||
-rw-r--r-- | term.c | 77 | ||||
-rw-r--r-- | term.h | 7 | ||||
-rw-r--r-- | termact.c | 75 | ||||
-rw-r--r-- | tree.c | 3 | ||||
-rw-r--r-- | validate.c | 2 |
16 files changed, 745 insertions, 375 deletions
@@ -1,2 +1,2 @@ -The mdocml utility interfaces with the mdoc library to compile +The mdocml utilities interface with the mdoc library to compile mdoc-roff documents into a variety of output formats. @@ -1,33 +1,37 @@ -VERSION = 1.2.0 +VERSION = 1.3.0 CFLAGS += -W -Wall -Wstrict-prototypes -Wno-unused-parameter -g LIBLNS = macro.ln mdoc.ln hash.ln strings.ln xstd.ln argv.ln \ validate.ln action.ln -BINLNS = mdocml.ln term.ln tree.ln termact.ln +TREELNS = mdoctree.ln tree.ln -LNS = $(LIBLNS) $(BINLNS) +TERMLNS = mdoctree.ln term.ln termact.ln -LLNS = llib-llibmdoc.ln llib-lmdocml.ln +LNS = $(LIBLNS) $(TREELNS) $(TERMLNS) + +LLNS = llib-llibmdoc.ln llib-lmdoctree.ln llib-lmdocterm.ln LIBS = libmdoc.a LIBOBJS = macro.o mdoc.o hash.o strings.o xstd.o argv.o \ - validate.o action.o + validate.o action.o + +TERMOBJS= mdocterm.o term.o termact.o -BINOBJS = mdocml.o term.o tree.o termact.o +TREEOBJS= mdoctree.o tree.o -OBJS = $(LIBOBJS) $(BINOBJS) +OBJS = $(LIBOBJS) $(TERMOBJS) $(TREEOBJS) -SRCS = macro.c mdoc.c mdocml.c hash.c strings.c xstd.c argv.c \ - validate.c action.c term.c tree.c termact.c +SRCS = macro.c mdoc.c hash.c strings.c xstd.c argv.c validate.c \ + action.c term.c tree.c termact.c mdoctree.c mdocterm.c HEADS = mdoc.h private.h term.h -MANS = mdocml.1 mdoc.3 +MANS = mdoctree.1 mdocterm.1 mdoc.3 -BINS = mdocml +BINS = mdocterm mdoctree CLEAN = $(BINS) $(LNS) $(LLNS) $(LIBS) $(OBJS) @@ -109,8 +113,10 @@ install: mkdir -p $(PREFIX)/include/mdoc/ mkdir -p $(PREFIX)/lib/ mkdir -p $(PREFIX)/man/man1/ - install -m 0755 mdocml $(PREFIX)/bin/ - install -m 0444 mdocml.1 $(PREFIX)/man/man1/ + install -m 0755 mdocterm $(PREFIX)/bin/ + install -m 0755 mdoctree $(PREFIX)/bin/ + install -m 0444 mdocterm.1 $(PREFIX)/man/man1/ + install -m 0444 mdocterm.1 $(PREFIX)/man/man1/ install -m 0444 mdoc.3 $(PREFIX)/man/man3/ install -m 0644 libmdoc.a $(PREFIX)/lib/ install -m 0444 mdoc.h $(PREFIX)/include/ @@ -122,8 +128,10 @@ install-dist: mdocml-$(VERSION).tar.gz mdocml-oport-$(VERSION).tar.gz install -m 0644 mdocml-oport-$(VERSION).tar.gz $(PREFIX)/mdocml-oport.tar.gz uninstall: - rm -f $(PREFIX)/bin/mdocml - rm -f $(PREFIX)/man/man1/mdocml.1 + rm -f $(PREFIX)/bin/mdocterm + rm -f $(PREFIX)/bin/mdoctree + rm -f $(PREFIX)/man/man1/mdocterm.1 + rm -f $(PREFIX)/man/man1/mdoctree.1 rm -f $(PREFIX)/man/man3/mdoc.3 rm -f $(PREFIX)/lib/libmdoc.a rm -f $(PREFIX)/include/mdoc.h @@ -156,9 +164,13 @@ mdoc.ln: mdoc.c private.h mdoc.o: mdoc.c private.h -mdocml.ln: mdocml.c mdoc.h +mdocterm.ln: mdocterm.c term.h + +mdocterm.o: mdocterm.c term.h -mdocml.o: mdocml.c mdoc.h +mdoctree.ln: mdoctree.c mdoc.h + +mdoctree.o: mdoctree.c mdoc.h xstd.ln: xstd.c private.h @@ -186,10 +198,12 @@ mdocml-oport-$(VERSION).tar.gz: Makefile.port DESCR sha1 mdocml-$(VERSION).tar.gz >> .dist/mdocml/distinfo install -m 0644 DESCR .dist/mdocml/pkg/DESCR echo @comment $$OpenBSD$$ > .dist/mdocml/pkg/PLIST - echo bin/mdocml >> .dist/mdocml/pkg/PLIST + echo bin/mdocterm >> .dist/mdocml/pkg/PLIST + echo bin/mdoctree >> .dist/mdocml/pkg/PLIST echo lib/libmdoc.a >> .dist/mdocml/pkg/PLIST echo include/mdoc.h >> .dist/mdocml/pkg/PLIST - echo @man man/man1/mdocml.1 >> .dist/mdocml/pkg/PLIST + echo @man man/man1/mdoctree.1 >> .dist/mdocml/pkg/PLIST + echo @man man/man1/mdocterm.1 >> .dist/mdocml/pkg/PLIST echo @man man/man3/mdoc.3 >> .dist/mdocml/pkg/PLIST ( cd .dist/ && tar zcf ../$@ mdocml/ ) rm -rf .dist/ @@ -203,12 +217,18 @@ mdocml-$(VERSION).tar.gz: $(INSTALL) llib-llibmdoc.ln: $(LIBLNS) $(LINT) $(LINTFLAGS) -Clibmdoc $(LIBLNS) -llib-lmdocml.ln: $(BINLNS) llib-llibmdoc.ln - $(LINT) $(LINTFLAGS) -Cmdocml $(BINLNS) llib-llibmdoc.ln +llib-lmdoctree.ln: $(TREELNS) llib-llibmdoc.ln + $(LINT) $(LINTFLAGS) -Cmdoctree $(TREELNS) llib-llibmdoc.ln + +llib-lmdocterm.ln: $(TERMLNS) llib-llibmdoc.ln + $(LINT) $(LINTFLAGS) -Cmdocterm $(TERMLNS) llib-llibmdoc.ln libmdoc.a: $(LIBOBJS) $(AR) rs $@ $(LIBOBJS) -mdocml: $(BINOBJS) libmdoc.a - $(CC) $(CFLAGS) -o $@ $(BINOBJS) libmdoc.a +mdocterm: $(TERMOBJS) libmdoc.a + $(CC) $(CFLAGS) -o $@ $(TERMOBJS) libmdoc.a + +mdoctree: $(TREEOBJS) libmdoc.a + $(CC) $(CFLAGS) -o $@ $(TREEOBJS) libmdoc.a @@ -216,6 +216,7 @@ post_dt(struct mdoc *mdoc) assert(NULL == mdoc->meta.title); + /* LINTED */ for (i = 0, n = mdoc->last->child; n; n = n->next, i++) { assert(MDOC_TEXT == n->type); p = n->data.text.string; @@ -421,6 +421,14 @@ const struct mdoc_meta *mdoc_meta(struct mdoc *); /* Signal end of parse sequence (boolean retval). */ int mdoc_endparse(struct mdoc *); +const char *mdoc_arch2a(enum mdoc_arch); + +const char *mdoc_vol2a(enum mdoc_vol); + +const char *mdoc_msec2a(enum mdoc_msec); + +int mdoc_isdelim(const char *); + __END_DECLS #endif /*!MDOC_H*/ diff --git a/mdocml.1 b/mdocml.1 deleted file mode 100644 index 2bae03d2..00000000 --- a/mdocml.1 +++ /dev/null @@ -1,100 +0,0 @@ -.\" -.Dd $Mdocdate$ -.Dt mdocml 1 -.Os -.\" SECTION -.Sh NAME -.Nm mdocml -.Nd mdoc macro compiler -.\" SECTION -.Sh SYNOPSIS -.Nm mdocml -.Op Fl f Ns Ar filter -.Op Fl v -.Op Fl W Ns Ar err... -.Op Ar infile -.\" SECTION -.Sh DESCRIPTION -The -.Nm -utility interfaces the -.Xr mdoc 3 -library to scan, parse, validate and output mdoc-macro documents. -Arguments follow: -.Bl -tag -width "\-Werr... " -.\" ITEM -.It Fl f Ns Ar filter -Pipe the parsed syntax tree into an output filter. May be either -.Ar tree -for the parse tree or -.Ar term -for a terminal-encoded, formatted manual page. -.\" ITEM -.It Fl v -Print verbose parsing output. -.\" ITEM -.It Fl W Ns Ar err... -Print warning messages. May be set to -.Fl W Ns Ar all -for all warnings, -.Ar compat -for groff/troff-compatibility warnings, or -.Ar syntax -for syntax warnings. If -.Fl W Ns Ar error -is specified, warnings are considered errors and cause utility -termination. Multiple -.Fl W -arguments may be comma-separated, such as -.Fl W Ns Ar error,all . -.\" ITEM -.It Ar infile -Read input from -.Ar infile , -which may be -.Dq \- -for stdin. -.El -.\" PARAGRAPH -.Pp -Parsing and validation rules are drawn entirely from the -.Xr mdoc 7 -and -.Xr mdoc.samples 7 -manuals. -.\" PARAGRAPH -.Pp -By default, -.Nm -reads from stdin and only validates its input. -.\" PARAGRAPH -.Pp -.Ex -std mdocml -.\" PARAGRAPH -.Pp -.Nm -is -.Ud -.\" SECTION -.Sh EXAMPLES -To validate this manual page: -.\" PARAGRAPH -.Pp -.D1 % mdocml \-Wall,error mdocml.1 -.\" SECTION -.Sh SEE ALSO -.Xr groff 1 , -.Xr mdoc.samples 7 , -.Xr mdoc 7 , -.Xr mdoc 3 -.\" -.Sh AUTHORS -The -.Nm -utility was written by -.An Kristaps Dzonsons Aq kristaps@kth.se . -.\" SECTION -.Sh CAVEATS -See -.Xr mdoc 3 -for a list of bugs, caveats, and incomplete macros. diff --git a/mdocterm.1 b/mdocterm.1 new file mode 100644 index 00000000..fd4abf63 --- /dev/null +++ b/mdocterm.1 @@ -0,0 +1,113 @@ +.\" $Id$ +.\" +.\" Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se> +.\" +.\" 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. +.\" +.Dd $Mdocdate$ +.Dt mdocmterm 1 +.Os +.\" SECTION +.Sh NAME +.Nm mdocmterm +.Nd mdoc macro compiler +.\" SECTION +.Sh SYNOPSIS +.Nm mdocmterm +.Op Fl v +.Op Fl W Ns Ar err... +.Op Ar infile +.\" SECTION +.Sh DESCRIPTION +The +.Nm +utility formats a BSD +.Dq mdoc +manual page for display on the terminal. The arguments are as follows: +.Bl -tag -width "\-Werr... " +.\" ITEM +.It Fl v +Print verbose parsing output. +.\" ITEM +.It Fl W Ns Ar err... +Print warning messages. May be set to +.Fl W Ns Ar all +for all warnings, +.Ar compat +for groff/troff-compatibility warnings, or +.Ar syntax +for syntax warnings. If +.Fl W Ns Ar error +is specified, warnings are considered errors and cause utility +termination. Multiple +.Fl W +arguments may be comma-separated, such as +.Fl W Ns Ar error,all . +.\" ITEM +.It Ar infile +Read input from +.Ar infile , +which may be +.Dq \- +for stdin. +.El +.\" PARAGRAPH +The +.Nm +utility is a formatting front-end for +.Xr mdoc 3 , +which parses the +.Dq mdoc +input, documented at +.Xr mdoc 7 +and +.Xr mdoc.samples 7 , +into an abstract syntax tree. +.\" PARAGRAPH +.Pp +By default, +.Nm +reads from stdin and prints terminal-encoded output to stdout. +.\" PARAGRAPH +.Pp +.Ex -std mdocmterm +.\" PARAGRAPH +.Pp +.Nm +is +.Ud +.\" SECTION +.Sh EXAMPLES +To display this manual page: +.\" PARAGRAPH +.Pp +.D1 % mdocmterm \-Wall,error mdocmterm.1 +.\" SECTION +.Sh SEE ALSO +.Xr mdoctree 1 , +.Xr mdoc.samples 7 , +.Xr mdoc 7 , +.Xr mdoc 3 +.\" +.Sh AUTHORS +The +.Nm +utility was written by +.An Kristaps Dzonsons Aq kristaps@kth.se . +.\" SECTION +.Sh CAVEATS +See +.Xr mdoc 3 +for a list of bugs, caveats, and incomplete macros. diff --git a/mdocterm.c b/mdocterm.c new file mode 100644 index 00000000..9dccd5c5 --- /dev/null +++ b/mdocterm.c @@ -0,0 +1,289 @@ + /* $Id$ */ +/* + * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se> + * + * 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 <sys/stat.h> +#include <sys/param.h> + +#include <assert.h> +#include <fcntl.h> +#include <err.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mdoc.h" +#include "term.h" + +#define MD_LINE_SZ (256) /* Max input line size. */ + +struct md_parse { + int warn; /* Warning flags. */ +#define MD_WARN_SYNTAX (1 << 0) /* Show syntax warnings. */ +#define MD_WARN_COMPAT (1 << 1) /* Show compat warnings. */ +#define MD_WARN_ALL (0x03) /* Show all warnings. */ +#define MD_WARN_ERR (1 << 2) /* Make warnings->errors. */ + int dbg; /* Debug level. */ + struct mdoc *mdoc; /* Active parser. */ + char *buf; /* Input buffer. */ + u_long bufsz; /* Input buffer size. */ + char *in; /* Input file name. */ + int fdin; /* Input file desc. */ +}; + +extern char *__progname; + +static void usage(void); +static int getsopts(struct md_parse *, char *); +static int parse(struct md_parse *); +static void msg_msg(void *, int, int, const char *); +static int msg_err(void *, int, int, const char *); +static int msg_warn(void *, int, int, + enum mdoc_warn, const char *); + +#ifdef __linux__ +extern int getsubopt(char **, char *const *, char **); +#endif + +int +main(int argc, char *argv[]) +{ + struct md_parse p; + struct mdoc_cb cb; + struct stat st; + int c; + extern char *optarg; + extern int optind; + + (void)memset(&p, 0, sizeof(struct md_parse)); + + while (-1 != (c = getopt(argc, argv, "vW:"))) + switch (c) { + case ('v'): + p.dbg++; + break; + case ('W'): + if ( ! getsopts(&p, optarg)) + return(0); + break; + default: + usage(); + return(0); + } + + argv += optind; + argc -= optind; + + /* Initialise the input file. */ + + p.in = "-"; + p.fdin = STDIN_FILENO; + + if (argc > 0) { + p.in = *argv++; + p.fdin = open(p.in, O_RDONLY, 0); + if (-1 == p.fdin) + err(1, "%s", p.in); + } + + /* Allocate a buffer to be BUFSIZ/block size. */ + + if (-1 == fstat(p.fdin, &st)) { + warn("%s", p.in); + p.bufsz = BUFSIZ; + } else + p.bufsz = MAX(st.st_blksize, BUFSIZ); + + p.buf = malloc(p.bufsz); + if (NULL == p.buf) + err(1, "malloc"); + + /* Allocate the parser. */ + + cb.mdoc_err = msg_err; + cb.mdoc_warn = msg_warn; + cb.mdoc_msg = msg_msg; + + p.mdoc = mdoc_alloc(&p, &cb); + + /* Parse the input file. */ + + c = parse(&p); + free(p.buf); + + if (STDIN_FILENO != p.fdin && -1 == close(p.fdin)) + warn("%s", p.in); + + if (0 == c) { + mdoc_free(p.mdoc); + return(EXIT_FAILURE); + } + + /* If the parse succeeded, print it out. */ + + termprint(mdoc_node(p.mdoc), mdoc_meta(p.mdoc)); + mdoc_free(p.mdoc); + + return(EXIT_SUCCESS); +} + + +static int +getsopts(struct md_parse *p, char *arg) +{ + char *v; + char *toks[] = { "all", "compat", + "syntax", "error", NULL }; + + while (*arg) + switch (getsubopt(&arg, toks, &v)) { + case (0): + p->warn |= MD_WARN_ALL; + break; + case (1): + p->warn |= MD_WARN_COMPAT; + break; + case (2): + p->warn |= MD_WARN_SYNTAX; + break; + case (3): + p->warn |= MD_WARN_ERR; + break; + default: + usage(); + return(0); + } + + return(1); +} + + +static int +parse(struct md_parse *p) +{ + ssize_t sz, i; + size_t pos; + char line[MD_LINE_SZ]; + int lnn; + + /* + * This is a little more complicated than fgets. TODO: have + * some benchmarks that show it's faster (note that I want to + * check many, many manuals simultaneously, so speed is + * important). Fill a buffer (sized to the block size) with a + * single read, then parse \n-terminated lines into a line + * buffer, which is passed to the parser. Hard-code the line + * buffer to a particular size -- a reasonable assumption. + */ + + for (lnn = 1, pos = 0; ; ) { + if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) { + warn("%s", p->in); + return(0); + } else if (0 == sz) + break; + + for (i = 0; i < sz; i++) { + if ('\n' != p->buf[i]) { + if (pos < sizeof(line)) { + line[(int)pos++] = p->buf[(int)i]; + continue; + } + warnx("%s: line %d too long", p->in, lnn); + return(0); + } + + line[(int)pos] = 0; + if ( ! mdoc_parseln(p->mdoc, lnn, line)) + return(0); + + lnn++; + pos = 0; + } + } + + return(mdoc_endparse(p->mdoc)); +} + + +static int +msg_err(void *arg, int line, int col, const char *msg) +{ + struct md_parse *p; + + p = (struct md_parse *)arg; + + warnx("%s:%d: error: %s (column %d)", + p->in, line, msg, col); + return(0); +} + + +static void +msg_msg(void *arg, int line, int col, const char *msg) +{ + struct md_parse *p; + + p = (struct md_parse *)arg; + + if (0 == p->dbg) + return; + + warnx("%s:%d: debug: %s (column %d)", + p->in, line, msg, col); +} + + +static int +msg_warn(void *arg, int line, int col, + enum mdoc_warn type, const char *msg) +{ + struct md_parse *p; + + p = (struct md_parse *)arg; + + switch (type) { + case (WARN_COMPAT): + if (p->warn & MD_WARN_COMPAT) + break; + return(1); + case (WARN_SYNTAX): + if (p->warn & MD_WARN_SYNTAX) + break; + return(1); + } + + warnx("%s:%d: warning: %s (column %d)", + p->in, line, msg, col); + + if ( ! (p->warn & MD_WARN_ERR)) + return(1); + + warnx("%s: considering warnings as errors", __progname); + return(0); +} + + +static void +usage(void) +{ + + warnx("usage: %s [-v] [-Wwarn...] [infile]", __progname); +} + diff --git a/mdoctree.1 b/mdoctree.1 new file mode 100644 index 00000000..0174f888 --- /dev/null +++ b/mdoctree.1 @@ -0,0 +1,113 @@ +.\" $Id$ +.\" +.\" Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se> +.\" +.\" 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. +.\" +.Dd $Mdocdate$ +.Dt mdoctree 1 +.Os +.\" SECTION +.Sh NAME +.Nm mdoctree +.Nd mdoc macro compiler +.\" SECTION +.Sh SYNOPSIS +.Nm mdoctree +.Op Fl v +.Op Fl W Ns Ar err... +.Op Ar infile +.\" SECTION +.Sh DESCRIPTION +The +.Nm +utility parses a BSD +.Dq mdoc +manual pages and prints its syntax tree. The arguments are as follows: +.Bl -tag -width "\-Werr... " +.\" ITEM +.It Fl v +Print verbose parsing output. +.\" ITEM +.It Fl W Ns Ar err... +Print warning messages. May be set to +.Fl W Ns Ar all +for all warnings, +.Ar compat +for groff/troff-compatibility warnings, or +.Ar syntax +for syntax warnings. If +.Fl W Ns Ar error +is specified, warnings are considered errors and cause utility +termination. Multiple +.Fl W +arguments may be comma-separated, such as +.Fl W Ns Ar error,all . +.\" ITEM +.It Ar infile +Read input from +.Ar infile , +which may be +.Dq \- +for stdin. +.El +.\" PARAGRAPH +The +.Nm +utility is a formatting front-end for +.Xr mdoc 3 , +which parses the +.Dq mdoc +input, documented at +.Xr mdoc 7 +and +.Xr mdoc.samples 7 , +into an abstract syntax tree. +.\" PARAGRAPH +.Pp +By default, +.Nm +reads from stdin and prints the syntax tree to stdout. +.\" PARAGRAPH +.Pp +.Ex -std mdoctree +.\" PARAGRAPH +.Pp +.Nm +is +.Ud +.\" SECTION +.Sh EXAMPLES +To validate this manual page: +.\" PARAGRAPH +.Pp +.D1 % mdoctree \-Wall,error mdoctree.1 +.\" SECTION +.Sh SEE ALSO +.Xr mdocterm 1 , +.Xr mdoc.samples 7 , +.Xr mdoc 7 , +.Xr mdoc 3 +.\" +.Sh AUTHORS +The +.Nm +utility was written by +.An Kristaps Dzonsons Aq kristaps@kth.se . +.\" SECTION +.Sh CAVEATS +See +.Xr mdoc 3 +for a list of bugs, caveats, and incomplete macros. @@ -32,9 +32,6 @@ #define MD_LINE_SZ (256) /* Max input line size. */ -typedef int (*md_print)(const struct mdoc_node *, - const struct mdoc_meta *); - struct md_parse { int warn; /* Warning flags. */ #define MD_WARN_SYNTAX (1 << 0) /* Show syntax warnings. */ @@ -47,28 +44,21 @@ struct md_parse { u_long bufsz; /* Input buffer size. */ char *in; /* Input file name. */ int fdin; /* Input file desc. */ - md_print fp; }; extern char *__progname; static void usage(void); - -static int parse_opts(struct md_parse *, int, char *[]); -static int parse_subopts(struct md_parse *, char *); - -static int parse_begin(struct md_parse *); -static int parse_leave(struct md_parse *, int); -static int io_begin(struct md_parse *); -static int io_leave(struct md_parse *, int); -static int buf_begin(struct md_parse *); -static int buf_leave(struct md_parse *, int); - +static int getsopts(struct md_parse *, char *); +static int parse(struct md_parse *); static void msg_msg(void *, int, int, const char *); static int msg_err(void *, int, int, const char *); static int msg_warn(void *, int, int, enum mdoc_warn, const char *); +extern void treeprint(const struct mdoc_node *, + const struct mdoc_meta *); + #ifdef __linux__ extern int getsubopt(char **, char *const *, char **); #endif @@ -76,102 +66,22 @@ extern int getsubopt(char **, char *const *, char **); int main(int argc, char *argv[]) { - struct md_parse parser; - - (void)memset(&parser, 0, sizeof(struct md_parse)); - - if ( ! parse_opts(&parser, argc, argv)) - return(EXIT_FAILURE); - if ( ! io_begin(&parser)) - return(EXIT_FAILURE); - - return(EXIT_SUCCESS); -} - - -static int -io_leave(struct md_parse *p, int code) -{ - - if (-1 == p->fdin || STDIN_FILENO == p->fdin) - return(code); - - if (-1 == close(p->fdin)) { - warn("%s", p->in); - code = 0; - } - return(code); -} - - -static int -parse_subopts(struct md_parse *p, char *arg) -{ - char *v; - char *toks[] = { "all", "compat", - "syntax", "error", NULL }; - - /* - * Future -Wxxx levels and so on should be here. For now we - * only recognise syntax and compat warnings as categories, - * beyond the usually "all" and "error" (make warn error out). - */ - - while (*arg) - switch (getsubopt(&arg, toks, &v)) { - case (0): - p->warn |= MD_WARN_ALL; - break; - case (1): - p->warn |= MD_WARN_COMPAT; - break; - case (2): - p->warn |= MD_WARN_SYNTAX; - break; - case (3): - p->warn |= MD_WARN_ERR; - break; - default: - usage(); - return(0); - } - - return(1); -} - - -static int -parse_opts(struct md_parse *p, int argc, char *argv[]) -{ + struct md_parse p; + struct mdoc_cb cb; + struct stat st; int c; - extern char *optarg; extern int optind; - extern int termprint(const struct mdoc_node *, - const struct mdoc_meta *); - extern int treeprint(const struct mdoc_node *, - const struct mdoc_meta *); - - p->in = "-"; + (void)memset(&p, 0, sizeof(struct md_parse)); - while (-1 != (c = getopt(argc, argv, "f:vW:"))) + while (-1 != (c = getopt(argc, argv, "vW:"))) switch (c) { - case ('f'): - if (0 == strcmp(optarg, "tree")) { - p->fp = treeprint; - break; - } else if (0 == strcmp(optarg, "term")) { - p->fp = termprint; - break; - } - warnx("unknown filter: %s", optarg); - return(0); case ('v'): - p->dbg++; + p.dbg++; break; case ('W'): - if ( ! parse_subopts(p, optarg)) + if ( ! getsopts(&p, optarg)) return(0); break; default: @@ -180,103 +90,100 @@ parse_opts(struct md_parse *p, int argc, char *argv[]) } argv += optind; - if (0 == (argc -= optind)) - return(1); - - p->in = *argv++; - return(1); -} + argc -= optind; + /* Initialise the input file. */ -static int -io_begin(struct md_parse *p) -{ + p.in = "-"; + p.fdin = STDIN_FILENO; - p->fdin = STDIN_FILENO; - if (0 != strncmp(p->in, "-", 1)) - if (-1 == (p->fdin = open(p->in, O_RDONLY, 0))) { - warn("%s", p->in); - return(io_leave(p, 0)); - } + if (argc > 0) { + p.in = *argv++; + p.fdin = open(p.in, O_RDONLY, 0); + if (-1 == p.fdin) + err(1, "%s", p.in); + } - return(io_leave(p, buf_begin(p))); -} + /* Allocate a buffer to be BUFSIZ/block size. */ + if (-1 == fstat(p.fdin, &st)) { + warn("%s", p.in); + p.bufsz = BUFSIZ; + } else + p.bufsz = MAX(st.st_blksize, BUFSIZ); -static int -buf_leave(struct md_parse *p, int code) -{ + p.buf = malloc(p.bufsz); + if (NULL == p.buf) + err(1, "malloc"); - if (p->buf) - free(p->buf); - return(code); -} + /* Allocate the parser. */ + cb.mdoc_err = msg_err; + cb.mdoc_warn = msg_warn; + cb.mdoc_msg = msg_msg; -static int -buf_begin(struct md_parse *p) -{ - struct stat st; + p.mdoc = mdoc_alloc(&p, &cb); - if (-1 == fstat(p->fdin, &st)) { - warn("%s", p->in); - return(0); - } + /* Parse the input file. */ - /* - * Try to intuit the fastest way of sucking down buffered data - * by using either the block buffer size or the hard-coded one. - * This is inspired by bin/cat.c. - */ + c = parse(&p); + free(p.buf); - p->bufsz = MAX(st.st_blksize, BUFSIZ); + if (STDIN_FILENO != p.fdin && -1 == close(p.fdin)) + warn("%s", p.in); - if (NULL == (p->buf = malloc(p->bufsz))) { - warn("malloc"); - return(buf_leave(p, 0)); + if (0 == c) { + mdoc_free(p.mdoc); + return(EXIT_FAILURE); } - return(buf_leave(p, parse_begin(p))); + /* If the parse succeeded, print it out. */ + + treeprint(mdoc_node(p.mdoc), mdoc_meta(p.mdoc)); + mdoc_free(p.mdoc); + + return(EXIT_SUCCESS); } static int -parse_leave(struct md_parse *p, int code) +getsopts(struct md_parse *p, char *arg) { - md_print fp; - - if (NULL == p->mdoc) - return(code); - - if ( ! mdoc_endparse(p->mdoc)) - code = 0; + char *v; + char *toks[] = { "all", "compat", + "syntax", "error", NULL }; - if (code && (fp = p->fp)) { - if ( ! (*fp)(mdoc_node(p->mdoc), mdoc_meta(p->mdoc))) - code = 0; - } + while (*arg) + switch (getsubopt(&arg, toks, &v)) { + case (0): + p->warn |= MD_WARN_ALL; + break; + case (1): + p->warn |= MD_WARN_COMPAT; + break; + case (2): + p->warn |= MD_WARN_SYNTAX; + break; + case (3): + p->warn |= MD_WARN_ERR; + break; + default: + usage(); + return(0); + } - mdoc_free(p->mdoc); - return(code); + return(1); } static int -parse_begin(struct md_parse *p) +parse(struct md_parse *p) { ssize_t sz, i; size_t pos; char line[MD_LINE_SZ]; - struct mdoc_cb cb; int lnn; - cb.mdoc_err = msg_err; - cb.mdoc_warn = msg_warn; - cb.mdoc_msg = msg_msg; - - if (NULL == (p->mdoc = mdoc_alloc(p, &cb))) - return(parse_leave(p, 0)); - /* * This is a little more complicated than fgets. TODO: have * some benchmarks that show it's faster (note that I want to @@ -290,7 +197,7 @@ parse_begin(struct md_parse *p) for (lnn = 1, pos = 0; ; ) { if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) { warn("%s", p->in); - return(parse_leave(p, 0)); + return(0); } else if (0 == sz) break; @@ -301,19 +208,19 @@ parse_begin(struct md_parse *p) continue; } warnx("%s: line %d too long", p->in, lnn); - return(parse_leave(p, 0)); + return(0); } line[(int)pos] = 0; if ( ! mdoc_parseln(p->mdoc, lnn, line)) - return(parse_leave(p, 0)); + return(0); lnn++; pos = 0; } } - return(parse_leave(p, 1)); + return(mdoc_endparse(p->mdoc)); } @@ -379,7 +286,6 @@ static void usage(void) { - warnx("usage: %s [-ffilter] [-v] [-Wwarn...] [infile]", - __progname); + warnx("usage: %s [-v] [-Wwarn...] [infile]", __progname); } @@ -123,7 +123,6 @@ void mdoc_sibling(struct mdoc *, int, struct mdoc_node **, void *mdoc_tokhash_alloc(void); int mdoc_tokhash_find(const void *, const char *); void mdoc_tokhash_free(void *); -int mdoc_isdelim(const char *); int mdoc_iscdelim(char); enum mdoc_sec mdoc_atosec(const char *); enum mdoc_msec mdoc_atomsec(const char *); @@ -132,11 +131,6 @@ enum mdoc_arch mdoc_atoarch(const char *); enum mdoc_att mdoc_atoatt(const char *); time_t mdoc_atotime(const char *); -/* FIXME: these three are only for output channels. */ -char *mdoc_arch2a(enum mdoc_arch); -char *mdoc_vol2a(enum mdoc_vol); -char *mdoc_msec2a(enum mdoc_msec); - char *mdoc_type2a(enum mdoc_type); char *mdoc_node2a(struct mdoc_node *); @@ -336,7 +336,7 @@ mdoc_type2a(enum mdoc_type type) } -char * +const char * mdoc_arch2a(enum mdoc_arch arch) { @@ -404,7 +404,7 @@ mdoc_arch2a(enum mdoc_arch arch) } -char * +const char * mdoc_vol2a(enum mdoc_vol vol) { @@ -438,7 +438,7 @@ mdoc_vol2a(enum mdoc_vol vol) } -char * +const char * mdoc_msec2a(enum mdoc_msec msec) { @@ -25,7 +25,6 @@ #include <unistd.h> #include "term.h" -#include "private.h" /* XXX */ enum termstyle { STYLE_CLEAR, @@ -42,8 +41,10 @@ static void termprint_footer(struct termp *, const struct mdoc_meta *); static void pword(struct termp *, const char *, size_t); +static void pescape(struct termp *, + const char *, size_t *, size_t); static void chara(struct termp *, char); -static void escape(struct termp *, enum termstyle); +static void style(struct termp *, enum termstyle); void @@ -185,7 +186,7 @@ chara(struct termp *p, char c) static void -escape(struct termp *p, enum termstyle esc) +style(struct termp *p, enum termstyle esc) { if (p->col + 4 >= p->maxcols) @@ -212,6 +213,48 @@ escape(struct termp *p, enum termstyle esc) static void +pescape(struct termp *p, const char *word, size_t *i, size_t len) +{ + + (*i)++; + assert(*i < len); + + if ('(' == word[*i]) { + /* Two-character escapes. */ + (*i)++; + assert(*i + 1 < len); + + if ('r' == word[*i] && 'B' == word[*i + 1]) + chara(p, ']'); + else if ('l' == word[*i] && 'B' == word[*i + 1]) + chara(p, '['); + + (*i)++; + return; + + } else if ('[' != word[*i]) { + /* One-character escapes. */ + switch (word[*i]) { + case ('\\'): + /* FALLTHROUGH */ + case ('\''): + /* FALLTHROUGH */ + case ('`'): + /* FALLTHROUGH */ + case ('-'): + /* FALLTHROUGH */ + case ('.'): + chara(p, word[*i]); + default: + break; + } + return; + } + /* n-character escapes. */ +} + + +static void pword(struct termp *p, const char *word, size_t len) { size_t i; @@ -224,18 +267,21 @@ pword(struct termp *p, const char *word, size_t len) p->flags &= ~TERMP_NOSPACE; if (p->flags & TERMP_BOLD) - escape(p, STYLE_BOLD); + style(p, STYLE_BOLD); if (p->flags & TERMP_UNDERLINE) - escape(p, STYLE_UNDERLINE); - - /* TODO: escape patterns. */ + style(p, STYLE_UNDERLINE); - for (i = 0; i < len; i++) + for (i = 0; i < len; i++) { + if ('\\' == word[i]) { + pescape(p, word, &i, len); + continue; + } chara(p, word[i]); + } if (p->flags & TERMP_BOLD || p->flags & TERMP_UNDERLINE) - escape(p, STYLE_CLEAR); + style(p, STYLE_CLEAR); } @@ -293,11 +339,9 @@ termprint_r(struct termp *p, const struct mdoc_meta *meta, /* Post-processing. */ - if (MDOC_TEXT != node->type) { + if (MDOC_TEXT != node->type) if (termacts[node->tok].post) - if ( ! (*termacts[node->tok].post)(p, meta, node)) - return; - } + (*termacts[node->tok].post)(p, meta, node); /* Siblings. */ @@ -351,7 +395,8 @@ termprint_footer(struct termp *p, const struct mdoc_meta *meta) static void termprint_header(struct termp *p, const struct mdoc_meta *meta) { - char *msec, *buf, *title, *pp; + char *buf, *title; + const char *pp, *msec; size_t ssz, tsz, ttsz, i;; if (NULL == (buf = malloc(p->rmargin))) @@ -439,7 +484,7 @@ termprint_header(struct termp *p, const struct mdoc_meta *meta) } -int +void termprint(const struct mdoc_node *node, const struct mdoc_meta *meta) { @@ -459,8 +504,6 @@ termprint(const struct mdoc_node *node, termprint_footer(&p, meta); free(p.buf); - - return(1); } @@ -42,15 +42,20 @@ struct termact { int (*pre)(struct termp *, const struct mdoc_meta *, const struct mdoc_node *); - int (*post)(struct termp *, + void (*post)(struct termp *, const struct mdoc_meta *, const struct mdoc_node *); }; +void termprint(const struct mdoc_node *, + const struct mdoc_meta *); + void newln(struct termp *); void vspace(struct termp *); void word(struct termp *, const char *); void flushln(struct termp *); +void transcode(struct termp *, + const char *, size_t); const struct termact *termacts; @@ -56,11 +56,10 @@ static int arg_getattr(int, size_t, const struct mdoc_meta *meta, \ const struct mdoc_node *node -#define DECL_PREPOST(name, suffix) \ -static int name##_##suffix(DECL_ARGS) - -#define DECL_PRE(name) DECL_PREPOST(name, pre) -#define DECL_POST(name) DECL_PREPOST(name, post) +#define DECL_PRE(name) \ +static int name##_pre(DECL_ARGS) +#define DECL_POST(name) \ +static void name##_post(DECL_ARGS) DECL_PRE(termp_aq); DECL_PRE(termp_ar); @@ -216,21 +215,20 @@ termp_dq_pre(DECL_ARGS) /* ARGSUSED */ -static int +static void termp_dq_post(DECL_ARGS) { if (MDOC_BODY != node->type) - return(1); + return; p->flags |= TERMP_NOSPACE; word(p, "''"); - return(1); } /* ARGSUSED */ -static int +static void termp_it_post(DECL_ARGS) { const struct mdoc_node *n, *it; @@ -251,7 +249,7 @@ termp_it_post(DECL_ARGS) case (MDOC_HEAD): break; default: - return(1); + return; } it = node->parent; @@ -283,8 +281,6 @@ termp_it_post(DECL_ARGS) p->flags &= ~TERMP_NOLPAD; } } - - return(1); } @@ -362,22 +358,20 @@ termp_it_pre(DECL_ARGS) /* ARGSUSED */ -static int +static void termp_nm_post(DECL_ARGS) { p->flags &= ~ttypes[TTYPE_PROG]; - return(1); } /* ARGSUSED */ -static int +static void termp_fl_post(DECL_ARGS) { p->flags &= ~ttypes[TTYPE_CMD_FLAG]; - return(1); } @@ -426,12 +420,11 @@ termp_pp_pre(DECL_ARGS) /* ARGSUSED */ -static int +static void termp_ar_post(DECL_ARGS) { p->flags &= ~ttypes[TTYPE_CMD_ARG]; - return(1); } @@ -466,40 +459,29 @@ termp_nd_pre(DECL_ARGS) /* ARGSUSED */ -static int +static void termp_bl_post(DECL_ARGS) { - switch (node->type) { - case (MDOC_BLOCK): + if (MDOC_BLOCK == node->type) newln(p); - break; - default: - break; - } - return(1); } /* ARGSUSED */ -static int +static void termp_op_post(DECL_ARGS) { - switch (node->type) { - case (MDOC_BODY): - p->flags |= TERMP_NOSPACE; - word(p, "\\]"); - break; - default: - break; - } - return(1); + if (MDOC_BODY != node->type) + return; + p->flags |= TERMP_NOSPACE; + word(p, "\\(rB"); } /* ARGSUSED */ -static int +static void termp_sh_post(DECL_ARGS) { @@ -515,7 +497,6 @@ termp_sh_post(DECL_ARGS) default: break; } - return(1); } @@ -536,11 +517,11 @@ termp_xr_pre(DECL_ARGS) assert(MDOC_TEXT == n->type); p->flags |= TERMP_NOSPACE; - word(p, "\\("); + word(p, "("); p->flags |= TERMP_NOSPACE; word(p, n->data.text.string); p->flags |= TERMP_NOSPACE; - word(p, "\\)"); + word(p, ")"); return(0); } @@ -573,7 +554,7 @@ termp_op_pre(DECL_ARGS) switch (node->type) { case (MDOC_BODY): - word(p, "\\["); + word(p, "\\(lB"); p->flags |= TERMP_NOSPACE; break; default: @@ -619,15 +600,14 @@ termp_d1_pre(DECL_ARGS) /* ARGSUSED */ -static int +static void termp_d1_post(DECL_ARGS) { - if (MDOC_BODY != node->type) - return(1); + if (MDOC_BODY != node->type) + return; newln(p); p->offset -= 4; - return(1); } @@ -645,15 +625,14 @@ termp_aq_pre(DECL_ARGS) /* ARGSUSED */ -static int +static void termp_aq_post(DECL_ARGS) { if (MDOC_BODY != node->type) - return(1); + return; p->flags |= TERMP_NOSPACE; word(p, "\\>"); - return(1); } @@ -123,11 +123,10 @@ treeprint_r(const struct mdoc_node *n, int indent) /* ARGSUSED */ -int +void treeprint(const struct mdoc_node *node, const struct mdoc_meta *meta) { treeprint_r(node, 0); - return(1); } @@ -35,7 +35,7 @@ typedef int (*v_post)(struct mdoc *); /* FIXME: some sections should only occur in specific msecs. */ /* FIXME: ignoring Pp. */ /* FIXME: math symbols. */ -/* FIXME: valid character-escape checks. */ +/* FIXME: valid character-escape checks!!!! */ /* FIXME: make sure required sections are included (NAME, ...). */ struct valids { |