From e81807680a9a78e4658f5e00ffc42de9da9eb19d Mon Sep 17 00:00:00 2001 From: Kristaps Dzonsons Date: Thu, 19 Mar 2009 16:17:27 +0000 Subject: Split mdocterm.c -> main.c terminal.c. Abstracted output with -T selector (default ascii). Name change: mdocterm -> mandoc. Re-imported tree with -Ttree. --- Makefile | 76 ++-- main.c | 452 ++++++++++++++++++++++++ mandoc.1 | 169 +++++++++ mdoclint.1 | 156 --------- mdoclint.c | 51 --- mdocterm.1 | 182 ---------- mdocterm.c | 1132 ------------------------------------------------------------ mdoctree.1 | 162 --------- mdoctree.c | 157 --------- term.h | 9 +- terminal.c | 828 ++++++++++++++++++++++++++++++++++++++++++++ tree.c | 137 ++++++++ 12 files changed, 1629 insertions(+), 1882 deletions(-) create mode 100644 main.c create mode 100644 mandoc.1 delete mode 100644 mdoclint.1 delete mode 100644 mdoclint.c delete mode 100644 mdocterm.1 delete mode 100644 mdocterm.c delete mode 100644 mdoctree.1 delete mode 100644 mdoctree.c create mode 100644 terminal.c create mode 100644 tree.c diff --git a/Makefile b/Makefile index 7d40392e..00326f8d 100644 --- a/Makefile +++ b/Makefile @@ -16,29 +16,34 @@ VFLAGS = -DVERSION=\"$(VERSION)\" CFLAGS += -W -Wall -Wstrict-prototypes -Wno-unused-parameter -g LINTFLAGS += $(VFLAGS) CFLAGS += $(VFLAGS) + 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 -TERMLNS = mdocterm.ln term.ln ascii.ln -TERMOBJS = mdocterm.o term.o ascii.o -LLNS = llib-llibmdoc.ln llib-lmdocterm.ln -LNS = $(TERMLNS) $(LIBLNS) +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 + +MAINLNS = main.ln term.ln ascii.ln terminal.ln tree.ln +MAINOBJS = main.o term.o ascii.o terminal.o tree.o +MAINSRCS = main.c term.c ascii.c terminal.c tree.c + +LLNS = llib-llibmdoc.ln llib-lmandoc.ln +LNS = $(MAINLNS) $(LIBLNS) LIBS = libmdoc.a -OBJS = $(LIBOBJS) $(TERMOBJS) -SRCS = macro.c mdoc.c hash.c strings.c xstd.c argv.c validate.c \ - action.c term.c mdocterm.c lib.c att.c arch.c vol.c \ - msec.c st.c ascii.c +OBJS = $(LIBOBJS) $(MAINOBJS) +SRCS = $(LIBSRCS) $(MAINSRCS) DATAS = arch.in att.in lib.in msec.in st.in vol.in ascii.in HEADS = mdoc.h private.h term.h SGMLS = index.sgml HTMLS = index.html STATICS = style.css external.png -TARGZS = mdocml-$(VERSION).tar.gz mdocml-oport-$(VERSION).tar.gz \ +TARGZS = mdocml-$(VERSION).tar.gz \ + mdocml-oport-$(VERSION).tar.gz \ mdocml-nport-$(VERSION).tar.gz -MANS = mdocterm.1 mdoc.3 mdoc.7 -BINS = mdocterm +MANS = mandoc.1 mdoc.3 mdoc.7 +BINS = mandoc CLEAN = $(BINS) $(LNS) $(LLNS) $(LIBS) $(OBJS) $(HTMLS) $(TARGZS) INSTALL = $(SRCS) $(HEADS) Makefile DESCR $(MANS) $(SGMLS) \ $(STATICS) Makefile.netbsd Makefile.openbsd $(DATAS) @@ -70,25 +75,16 @@ installwww: www install: mkdir -p $(BINDIR) - mkdir -p $(INCLUDEDIR) - mkdir -p $(LIBDIR)/lib mkdir -p $(MANDIR)/man1 - mkdir -p $(MANDIR)/man3 mkdir -p $(MANDIR)/man7 - $(INSTALL_PROGRAM) mdocterm $(BINDIR) - $(INSTALL_MAN) mdocterm.1 $(MANDIR)/man1 - $(INSTALL_MAN) mdoc.3 $(MANDIR)/man3 + $(INSTALL_PROGRAM) mandoc $(BINDIR) + $(INSTALL_MAN) mandoc.1 $(MANDIR)/man1 $(INSTALL_MAN) mdoc.7 $(MANDIR)/man7 - $(INSTALL_LIB) libmdoc.a $(LIBDIR) - $(INSTALL_DATA) mdoc.h $(INCLUDEDIR) uninstall: - rm -f $(BINDIR)/mdocterm - rm -f $(MANDIR)/man1/mdocterm.1 - rm -f $(MANDIR)/man3/mdoc.3 + rm -f $(BINDIR)/mandoc + rm -f $(MANDIR)/man1/mandoc.1 rm -f $(MANDIR)/man7/mdoc.7 - rm -f $(LIBDIR)/libmdoc.a - rm -f $(INCLUDEDIR)/mdoc.h lib.ln: lib.c lib.in private.h lib.o: lib.c lib.in private.h @@ -126,8 +122,11 @@ hash.o: hash.c private.h mdoc.ln: mdoc.c private.h mdoc.o: mdoc.c private.h -mdocterm.ln: mdocterm.c -mdocterm.o: mdocterm.c +main.ln: main.c mdoc.h +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 @@ -154,11 +153,9 @@ mdocml-nport-$(VERSION).tar.gz: mdocml-$(VERSION).tar.gz Makefile.netbsd DESCR sha1 mdocml-$(VERSION).tar.gz >> .dist/mdocml/distinfo install -m 0644 DESCR .dist/mdocml/ echo @comment $$NetBSD$$ > .dist/mdocml/PLIST - echo bin/mdocterm >> .dist/mdocml/PLIST - echo lib/libmdoc.a >> .dist/mdocml/PLIST - echo include/mdoc.h >> .dist/mdocml/PLIST - echo man/man1/mdocterm.1 >> .dist/mdocml/PLIST - echo man/man3/mdoc.3 >> .dist/mdocml/PLIST + echo bin/mandoc >> .dist/mdocml/PLIST + echo man/man1/mandoc.1 >> .dist/mdocml/PLIST + echo man/man7/mdoc.7 >> .dist/mdocml/PLIST ( cd .dist/ && tar zcf ../$@ mdocml/ ) rm -rf .dist/ @@ -171,11 +168,9 @@ mdocml-oport-$(VERSION).tar.gz: mdocml-$(VERSION).tar.gz Makefile.openbsd 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/mdocterm >> .dist/mdocml/pkg/PLIST - echo lib/libmdoc.a >> .dist/mdocml/pkg/PLIST - echo include/mdoc.h >> .dist/mdocml/pkg/PLIST - echo @man man/man1/mdocterm.1 >> .dist/mdocml/pkg/PLIST - echo @man man/man3/mdoc.3 >> .dist/mdocml/pkg/PLIST + echo bin/mandoc >> .dist/mdocml/pkg/PLIST + echo @man man/man1/mandoc.1 >> .dist/mdocml/pkg/PLIST + echo @man man/man7/mdoc.7 >> .dist/mdocml/pkg/PLIST ( cd .dist/ && tar zcf ../$@ mdocml/ ) rm -rf .dist/ @@ -188,16 +183,15 @@ mdocml-$(VERSION).tar.gz: $(INSTALL) llib-llibmdoc.ln: $(LIBLNS) $(LINT) $(LINTFLAGS) -Clibmdoc $(LIBLNS) -llib-lmdocterm.ln: $(TERMLNS) llib-llibmdoc.ln - $(LINT) $(LINTFLAGS) -Cmdocterm $(TERMLNS) llib-llibmdoc.ln +llib-lmandoc.ln: $(MAINLNS) llib-llibmdoc.ln + $(LINT) $(LINTFLAGS) -Cmandoc $(MAINLNS) llib-llibmdoc.ln libmdoc.a: $(LIBOBJS) $(AR) rs $@ $(LIBOBJS) -mdocterm: $(TERMOBJS) libmdoc.a - $(CC) $(CFLAGS) -o $@ $(TERMOBJS) libmdoc.a +mandoc: $(MAINOBJS) libmdoc.a + $(CC) $(CFLAGS) -o $@ $(MAINOBJS) libmdoc.a .sgml.html: validate $< sed -e "s!@VERSION@!$(VERSION)!" -e "s!@VDATE@!$(VDATE)!" $< > $@ - diff --git a/main.c b/main.c new file mode 100644 index 00000000..396c6daf --- /dev/null +++ b/main.c @@ -0,0 +1,452 @@ +/* $Id: main.c,v 1.1 2009/03/19 16:17:27 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 + +#include "mdoc.h" + +#define WARN_WALL 0x03 /* All-warnings mask. */ +#define WARN_WCOMPAT (1 << 0) /* Compatibility warnings. */ +#define WARN_WSYNTAX (1 << 1) /* Syntax warnings. */ +#define WARN_WERR (1 << 2) /* Warnings->errors. */ + +enum outt { + OUTT_ASCII, + OUTT_LATIN1, + OUTT_UTF8, + OUTT_TREE, + OUTT_LINT +}; + +typedef int (*out_run)(void *, const struct mdoc *); +typedef void (*out_free)(void *); + +extern char *__progname; + +extern void *ascii_alloc(void); +extern void *latin1_alloc(void); +extern void *utf8_alloc(void); +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 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(char **, size_t *, char **, size_t *, + const char *, struct mdoc *); +static int fdesc(char **, size_t *, char **, size_t *, + const char *, int, struct mdoc *); + + +int +main(int argc, char *argv[]) +{ + int c, rc, fflags, wflags; + struct mdoc_cb cb; + struct mdoc *mdoc; + char *buf, *line; + size_t bufsz, linesz; + void *outdata; + enum outt outtype; + out_run outrun; + out_free outfree; + + fflags = wflags = 0; + outtype = OUTT_ASCII; + + /* LINTED */ + while (-1 != (c = getopt(argc, argv, "f:VW:T:"))) + switch (c) { + case ('f'): + if ( ! foptions(&fflags, optarg)) + return(0); + break; + case ('T'): + if ( ! toptions(&outtype, optarg)) + return(0); + break; + case ('W'): + if ( ! woptions(&wflags, optarg)) + return(0); + break; + case ('V'): + version(); + /* NOTREACHED */ + default: + usage(); + /* NOTREACHED */ + } + + argc -= optind; + argv += optind; + + /* + * Allocate the appropriate front-end. Note that utf8, ascii + * and latin1 all resolve to the terminal front-end with + * different encodings (see terminal.c). Not all frontends have + * cleanup or alloc routines. + */ + + switch (outtype) { + case (OUTT_LATIN1): + outdata = latin1_alloc(); + outrun = terminal_run; + outfree = terminal_free; + break; + case (OUTT_UTF8): + outdata = utf8_alloc(); + outrun = terminal_run; + outfree = terminal_free; + break; + case (OUTT_TREE): + outdata = NULL; + outrun = tree_run; + outfree = NULL; + break; + case (OUTT_LINT): + outdata = NULL; + outrun = NULL; + outfree = NULL; + break; + default: + outdata = ascii_alloc(); + outrun = terminal_run; + outfree = terminal_free; + break; + } + + /* + * All callbacks route into here, where we print them onto the + * screen. XXX - for now, no path for debugging messages. + */ + + cb.mdoc_msg = NULL; + cb.mdoc_err = merr; + cb.mdoc_warn = mwarn; + + buf = line = NULL; + bufsz = linesz = 0; + + mdoc = mdoc_alloc(&wflags, fflags, &cb); + + while (*argv) { + if ( ! file(&line, &linesz, &buf, &bufsz, *argv, mdoc)) + break; + if (outrun && ! (*outrun)(outdata, mdoc)) + break; + + /* Reset the parser for another file. */ + mdoc_reset(mdoc); + argv++; + } + + rc = NULL == *argv; + + if (buf) + free(buf); + if (line) + free(line); + if (outfree) + (*outfree)(outdata); + + mdoc_free(mdoc); + + return(rc ? EXIT_SUCCESS : EXIT_FAILURE); +} + + +__dead static void +version(void) +{ + + (void)printf("%s %s\n", __progname, VERSION); + exit(0); + /* NOTREACHED */ +} + + +__dead static void +usage(void) +{ + + (void)fprintf(stderr, "usage: %s\n", __progname); + exit(1); + /* NOTREACHED */ +} + + +static int +file(char **ln, size_t *lnsz, char **buf, size_t *bufsz, + const char *file, struct mdoc *mdoc) +{ + int fd, c; + + if (-1 == (fd = open(file, O_RDONLY, 0))) { + warn("%s", file); + return(0); + } + + c = fdesc(ln, lnsz, buf, bufsz, file, fd, mdoc); + + if (-1 == close(fd)) + warn("%s", file); + + return(c); +} + + +static int +fdesc(char **lnp, size_t *lnsz, char **bufp, size_t *bufsz, + const char *f, int fd, struct mdoc *mdoc) +{ + size_t sz; + ssize_t ssz; + struct stat st; + int j, i, pos, lnn; + char *ln, *buf; + + buf = *bufp; + ln = *lnp; + + /* + * Two buffers: ln and buf. buf is the input buffer, optimised + * for each file's block size. ln is a line buffer. Both + * growable, hence passed in by ptr-ptr. + */ + + if (-1 == fstat(fd, &st)) { + warnx("%s", f); + sz = BUFSIZ; + } else + sz = (unsigned)BUFSIZ > st.st_blksize ? + (size_t)BUFSIZ : st.st_blksize; + + if (sz > *bufsz) { + if (NULL == (buf = realloc(buf, sz))) + err(1, "realloc"); + *bufp = buf; + *bufsz = sz; + } + + /* + * Fill buf with file blocksize and parse newlines into ln. + */ + + for (lnn = 1, pos = 0; ; ) { + if (-1 == (ssz = read(fd, buf, sz))) { + warn("%s", f); + return(0); + } else if (0 == ssz) + break; + + for (i = 0; i < (int)ssz; i++) { + if (pos >= (int)*lnsz) { + *lnsz += 256; /* Step-size. */ + ln = realloc(ln, *lnsz); + if (NULL == ln) + err(1, "realloc"); + *lnp = ln; + } + + if ('\n' != buf[i]) { + ln[pos++] = buf[i]; + continue; + } + + /* Check for CPP-escaped newline. */ + + if (pos > 0 && '\\' == ln[pos - 1]) { + for (j = pos - 1; j >= 0; j--) + if ('\\' != ln[j]) + break; + + if ( ! ((pos - j) % 2)) { + pos--; + lnn++; + continue; + } + } + + ln[pos] = 0; + if ( ! mdoc_parseln(mdoc, lnn, ln)) + return(0); + lnn++; + pos = 0; + } + } + + return(mdoc_endparse(mdoc)); +} + + +static int +toptions(enum outt *tflags, char *arg) +{ + + if (0 == strcmp(arg, "ascii")) + *tflags = OUTT_ASCII; + else if (0 == strcmp(arg, "latin1")) + *tflags = OUTT_LATIN1; + else if (0 == strcmp(arg, "utf8")) + *tflags = OUTT_UTF8; + else if (0 == strcmp(arg, "lint")) + *tflags = OUTT_LINT; + else if (0 == strcmp(arg, "tree")) + *tflags = OUTT_TREE; + else { + warnx("bad argument: -T%s", arg); + return(0); + } + + return(1); +} + + +/* + * Parse out the options for [-fopt...] setting compiler options. These + * can be comma-delimited or called again. + */ +static int +foptions(int *fflags, char *arg) +{ + char *v; + char *toks[4]; + + toks[0] = "ign-scope"; + toks[1] = "ign-escape"; + toks[2] = "ign-macro"; + toks[3] = NULL; + + while (*arg) + switch (getsubopt(&arg, toks, &v)) { + case (0): + *fflags |= MDOC_IGN_SCOPE; + break; + case (1): + *fflags |= MDOC_IGN_ESCAPE; + break; + case (2): + *fflags |= MDOC_IGN_MACRO; + break; + default: + warnx("bad argument: -f%s", arg); + return(0); + } + + return(1); +} + + +/* + * Parse out the options for [-Werr...], which sets warning modes. + * These can be comma-delimited or called again. + */ +static int +woptions(int *wflags, char *arg) +{ + char *v; + char *toks[5]; + + toks[0] = "all"; + toks[1] = "compat"; + toks[2] = "syntax"; + toks[3] = "error"; + toks[4] = NULL; + + while (*arg) + switch (getsubopt(&arg, toks, &v)) { + case (0): + *wflags |= WARN_WALL; + break; + case (1): + *wflags |= WARN_WCOMPAT; + break; + case (2): + *wflags |= WARN_WSYNTAX; + break; + case (3): + *wflags |= WARN_WERR; + break; + default: + warnx("bad argument: -W%s", arg); + return(0); + } + + return(1); +} + + +static int +merr(void *arg, int line, int col, const char *msg) +{ + + warnx("error: %s (line %d, column %d)", msg, line, col); + return(0); +} + + +static int +mwarn(void *arg, int line, int col, + enum mdoc_warn type, const char *msg) +{ + int flags; + char *wtype; + + flags = *(int *)arg; + wtype = NULL; + + switch (type) { + case (WARN_COMPAT): + wtype = "compat"; + if (flags & WARN_WCOMPAT) + break; + return(1); + case (WARN_SYNTAX): + wtype = "syntax"; + if (flags & WARN_WSYNTAX) + break; + return(1); + } + + assert(wtype); + warnx("%s warning: %s (line %d, column %d)", + wtype, msg, line, col); + + if ( ! (flags & WARN_WERR)) + return(1); + + warnx("%s: considering warnings as errors", + __progname); + return(0); +} + + diff --git a/mandoc.1 b/mandoc.1 new file mode 100644 index 00000000..7e32f7ff --- /dev/null +++ b/mandoc.1 @@ -0,0 +1,169 @@ +.\" $Id: mandoc.1,v 1.1 2009/03/19 16:17:27 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. +.\" +.Dd $Mdocdate: March 19 2009 $ +.Dt mandoc 1 +.Os +.\" SECTION +.Sh NAME +.Nm mandoc +.Nd format and display BSD manuals +.\" SECTION +.Sh SYNOPSIS +.Nm mandoc +.Op Fl V +.Op Fl f Ns Ar option... +.Op Fl W Ns Ar err... +.Op Fl T Ns Ar output +.Op Ar infile... +.\" SECTION +.Sh DESCRIPTION +The +.Nm +utility formats a BSD +.Dq mdoc +manual page for display. The arguments are as follows: +.Bl -tag -width XXXXXXXXXXXX +.\" ITEM +.It Fl f Ns Ar option... +Override default compiler behaviour. See +.Sx Compiler Options +for details. +.\" ITEM +.It Fl T +Output format. See +.Sx Output Formats +for available formats. Defaults to +.Fl T Ns Ar ascii . +.\" ITEM +.It Fl V +Print version and exit. +.\" 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 zero or more +.Ar infile . +If unspecified, reads from stdin. +.El +.\" PARAGRAPH +.Pp +By default, +.Nm +reads from stdin and prints 78-column backspace-encoded output to stdout +as if +.Fl T Ns Ar ascii +were provided. +.\" PARAGRAPH +.Pp +.Ex -std mandoc +.\" SUB-SECTION +.Ss Output Formats +The +.Nm +utility accepts the following +.Fl T +arguments: +.Bl -tag -width XXXXXXXXXXXX -offset XXXX +.It Ar ascii +Produce 7-bit ASCII output, backspace-encoded for bold and underline +styles. This is the default. +.It Ar tree +Produce an indented parse tree. +.It Ar lint +Parse only: produce no output. +.El +.\" SUB-SECTION +.Ss Compiler Options +Default compiler behaviour may be overriden with the +.Fl f +flag. +.Bl -tag -width XXXXXXXXXXXX -offset XXXX +.It Fl f Ns Ar ign-scope +When rewinding the scope of a block macro, forces the compiler to ignore +scope violations. This can seriously mangle the resulting tree. +.It Fl f Ns Ar ign-escape +Ignore invalid escape sequences. +.It Fl f Ns Ar ign-macro +Ignore unknown macros at the start of input lines. +.El +.\" PARAGRAPH +.Pp +As with the +.Fl W +flag, multiple +.Fl f +options may be grouped and delimited with a comma. Using +.Fl f Ns Ar ign-scope,ign-escape , +for example, will try to ignore scope and character-escape errors. +.\" SECTION +.Sh EXAMPLES +To page this manual page on the terminal: +.\" PARAGRAPH +.Pp +.D1 % mandoc \-Wall,error mandoc.1 2>&1 | less +.\" SECTION +.Sh SEE ALSO +.Xr mdoc 7 +.\" +.Sh AUTHORS +The +.Nm +utility was written by +.An Kristaps Dzonsons Aq kristaps@openbsd.org . +.\" SECTION +.Sh CAVEATS +The +.Nm +utility doesn't yet know how to display the following: +.Pp +.Bl -bullet -compact +.It +The \-hang +.Sq \&Bl +list is not yet supported. +.It +The \-literal and \-unfilled +.Sq \&Bd +displays only accept text contents. +.It +The +.Sq \&Xo/Xc +pair isn't supported. +.It +The +.Sq \&Sm +macro has no effect, yet. +.El +.Pp +Other macros still aren't supported by virtue of nobody complaining +about their absence. Please report any omissions: this is a work in +progress. diff --git a/mdoclint.1 b/mdoclint.1 deleted file mode 100644 index a1e6b176..00000000 --- a/mdoclint.1 +++ /dev/null @@ -1,156 +0,0 @@ -.\" $Id: mdoclint.1,v 1.5 2009/03/16 23:37:28 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. -.\" -.Dd $Mdocdate: March 16 2009 $ -.Dt mdoclint 1 -.Os -.\" SECTION -.Sh NAME -.Nm mdoclint -.Nd mdoc macro compiler -.\" SECTION -.Sh SYNOPSIS -.Nm mdoclint -.Op Fl vV -.Op Fl f Ns Ar options... -.Op Fl W Ns Ar err... -.Op Ar infile... -.\" SECTION -.Sh DESCRIPTION -The -.Nm -utility parses a BSD -.Dq mdoc -manual pages and validates its syntax tree. The arguments are as -follows: -.Bl -tag -width XXXXXXXXXXXX -offset XXXX -.\" ITEM -.It Fl v -Print verbose parsing output. -.\" ITEM -.It Fl V -Print version and exit. -.\" ITEM -.It Fl f Ns Ar option... -Override default compiler behaviour. See -.Sx Compiler Options -for details. -.\" 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. If multiple -.Ar infile -arguments are specified, each is processed and the system will exit on -the first failure. -.El -.\" PARAGRAPH -.Pp -The -.Nm -utility is a 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. By default, it reads from stdin. -.\" PARAGRAPH -.Pp -.Ex -std mdoclint -.\" SUB-SECTION -.Ss Compiler Options -Default compiler behaviour may be overriden with the -.Fl f -flag. The available options are as follows: -.Bl -tag -width XXXXXXXXXXXX -offset XXXX -.It Fl f Ns Ar ign-scope -When rewinding the scope of a block macro, forces the compiler to ignore -scope violations. This can seriously mangle the resulting tree. -.It Fl f Ns Ar ign-escape -Ignore invalid escape sequences. -.It Fl f Ns Ar ign-macro -Ignore unknown macros at the start of input lines. -.El -.\" PARAGRAPH -.Pp -As with the -.Fl W -flag, multiple -.Fl f -options may be grouped and delimited with a comma. Using -.Fl f Ns Ar ign-scope,ign-escape , -for example, will try to ignore scope and character-escape errors. -.\" SUB-SECTION -.Ss Input Encoding -The -.Nm -utility expects its input to be 7-bit ASCII as defined in -.Xr ascii 7 . -The only non-graphing characters accepted are spaces, -.Sq \ , -and tabs, -.Sq \et . -Tabs are only accepted in literal block-displays and as column -delimiters. -.Pp -Only Unix-style newlines (\en) are accepted; if the newline is escaped, -the line is concatenated with the next. -.\" SECTION -.Sh EXAMPLES -To validate this manual page: -.\" PARAGRAPH -.Pp -.D1 % mdoclint \-Wall,error mdoclint.1 -.\" SECTION -.Sh SEE ALSO -.Xr mdocterm 1 , -.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@openbsd.org . -.\" SECTION -.Sh CAVEATS -See -.Xr mdoc 3 -for a list of bugs, caveats, and incomplete macros. diff --git a/mdoclint.c b/mdoclint.c deleted file mode 100644 index b9240d4b..00000000 --- a/mdoclint.c +++ /dev/null @@ -1,51 +0,0 @@ -/* $Id: mdoclint.c,v 1.4 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 "mmain.h" - -int -main(int argc, char *argv[]) -{ - struct mmain *p; - int c; - - p = mmain_alloc(); - - c = mmain_getopt(p, argc, argv, NULL, - "[infile...]", NULL, NULL, NULL); - - argv += c; - if (0 == (argc -= c)) - mmain_exit(p, NULL != mmain_mdoc(p, "-")); - - while (c-- > 0) { - if (NULL == mmain_mdoc(p, *argv++)) - mmain_exit(p, 1); - mmain_reset(p); - } - - mmain_exit(p, 0); - /* NOTREACHED */ -} - diff --git a/mdocterm.1 b/mdocterm.1 deleted file mode 100644 index 6922c618..00000000 --- a/mdocterm.1 +++ /dev/null @@ -1,182 +0,0 @@ -.\" $Id: mdocterm.1,v 1.26 2009/03/19 11:49:00 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. -.\" -.Dd $Mdocdate: March 19 2009 $ -.Dt mdocterm 1 -.Os -.\" SECTION -.Sh NAME -.Nm mdocterm -.Nd mdoc macro compiler -.\" SECTION -.Sh SYNOPSIS -.Nm mdocterm -.Op Fl V -.Op Fl f Ns Ar option... -.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 XXXXXXXXXXXX -.\" ITEM -.It Fl V -Print version and exit. -.\" ITEM -.It Fl f Ns Ar option... -Override default compiler behaviour. See -.Sx Compiler Options -for details. -.\" 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 zero or more -.Ar infile . -If unspecified, reads from stdin. -.El -.\" PARAGRAPH -.Pp -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 nroff -.Qq backspace -terminal-encoded output to stdout, at this time to a fixed column with -of 78 characters. -.\" PARAGRAPH -.Pp -.Ex -std mdocterm -.\" SUB-SECTION -.Ss Compiler Options -Default compiler behaviour may be overriden with the -.Fl f -flag. The available options are as follows: -.Bl -tag -width XXXXXXXXXXXX -offset XXXX -.It Fl f Ns Ar ign-scope -When rewinding the scope of a block macro, forces the compiler to ignore -scope violations. This can seriously mangle the resulting tree. -.It Fl f Ns Ar ign-escape -Ignore invalid escape sequences. -.It Fl f Ns Ar ign-macro -Ignore unknown macros at the start of input lines. -.El -.\" PARAGRAPH -.Pp -As with the -.Fl W -flag, multiple -.Fl f -options may be grouped and delimited with a comma. Using -.Fl f Ns Ar ign-scope,ign-escape , -for example, will try to ignore scope and character-escape errors. -.\" SUB-SECTION -.Ss Character Escapes -The -.Nm -utility correctly renders all -.Sx Special Characters -in -.Xr mdoc 7 -in 7-bit ASCII. -.Pp -In the general sense, -.Nm -will make a -.Pq best-effort -to render complex characters; however, if a manual is using considerable -special characters, some meaning may be lost in translation. -.\" SECTION -.Sh EXAMPLES -To display this manual page: -.\" PARAGRAPH -.Pp -.D1 % mdocterm \-Wall,error mdocterm.1 -.\" PARAGRAPH -.Pp -To pipe a manual page to the pager: -.Pp -.D1 % mdocterm mdocterm.1 | less -.\" SECTION -.Sh SEE ALSO -.Xr mdoc 7 , -.Xr mdoc 3 -.\" -.Sh AUTHORS -The -.Nm -utility was written by -.An Kristaps Dzonsons Aq kristaps@openbsd.org . -.\" SECTION -.Sh CAVEATS -See -.Xr mdoc 3 -for a list of bugs, caveats, and incomplete macros regarding the -document parse. -.Pp -The -.Nm -utility doesn't yet know how to display the following: -.Pp -.Bl -bullet -compact -.It -The \-hang -.Sq \&Bl -list is not yet supported. -.It -The \-literal and \-unfilled -.Sq \&Bd -displays only accept text contents. -.It -The -.Sq \&Xo/Xc -pair isn't supported (and never will be). -.It -The -.Sq \&Sm -macro has no effect, yet. -.El diff --git a/mdocterm.c b/mdocterm.c deleted file mode 100644 index b996fe6c..00000000 --- a/mdocterm.c +++ /dev/null @@ -1,1132 +0,0 @@ -/* $Id: mdocterm.c,v 1.49 2009/03/19 11:49:00 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 -#include -#include - -#include "term.h" - -#define WARN_WALL 0x03 /* All-warnings mask. */ -#define WARN_WCOMPAT (1 << 0) /* Compatibility warnings. */ -#define WARN_WSYNTAX (1 << 1) /* Syntax warnings. */ -#define WARN_WERR (1 << 2) /* Warnings->errors. */ - -enum termt { - TERMT_ASCII, - TERMT_LINT, - TERMT_TREE -}; - -extern char *__progname; - -__dead static void version(void); -__dead static void usage(void); -#if 0 -__dead static void punt(struct cmdargs *, char **); -#endif -static int foptions(int *, char *); -static int toptions(enum termt *, 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 void body(struct termp *, struct termpair *, - const struct mdoc_meta *, - const struct mdoc_node *); -static void header(struct termp *, - const struct mdoc_meta *); -static void footer(struct termp *, - const struct mdoc_meta *); -static int file(char **, size_t *, char **, size_t *, - const char *, struct mdoc *); -static int fdesc(char **, size_t *, char **, size_t *, - const char *, int, struct mdoc *); -static void pword(struct termp *, const char *, int); -static void pescape(struct termp *, - const char *, int *, int); -static void nescape(struct termp *, - const char *, size_t); -static void chara(struct termp *, char); -static void stringa(struct termp *, - const char *, size_t); -static void sanity(const struct mdoc_node *); - - -int -main(int argc, char *argv[]) -{ - struct termp termp; - int c, fflags, wflags; - struct mdoc_cb cb; - struct mdoc *mdoc; - char *buf, *line; - size_t bufsz, linesz; - enum termt termt; - - bzero(&termp, sizeof(struct termp)); - bzero(&cb, sizeof(struct mdoc_cb)); - - termt = TERMT_ASCII; - fflags = wflags = 0; - - /* LINTED */ - while (-1 != (c = getopt(argc, argv, "f:VW:T:"))) - switch (c) { - case ('f'): - if ( ! foptions(&fflags, optarg)) - return(0); - break; - case ('T'): - if ( ! toptions(&termt, optarg)) - return(0); - break; - case ('W'): - if ( ! woptions(&wflags, optarg)) - return(0); - break; - case ('V'): - version(); - /* NOTREACHED */ - default: - usage(); - /* NOTREACHED */ - } - - argc -= optind; - argv += optind; - - termp.maxrmargin = 78; /* FIXME */ - - cb.mdoc_err = merr; - cb.mdoc_warn = mwarn; - - /* Line and block buffers persist between parses. */ - - buf = line = NULL; - bufsz = linesz = 0; - - /* Overall mdoc persists between parses. */ - - mdoc = mdoc_alloc(&wflags, fflags, &cb); - - while (*argv) { - if ( ! file(&line, &linesz, &buf, &bufsz, *argv, mdoc)) - break; - - switch (termt) { - case (TERMT_ASCII): - if (NULL == termp.symtab) - termp.symtab = ascii2htab(); - header(&termp, mdoc_meta(mdoc)); - body(&termp, NULL, mdoc_meta(mdoc), - mdoc_node(mdoc)); - footer(&termp, mdoc_meta(mdoc)); - break; - default: - break; - } - - mdoc_reset(mdoc); - argv++; - } - - if (buf) - free(buf); - if (line) - free(line); - if (termp.buf) - free(termp.buf); - if (termp.symtab) - asciifree(termp.symtab); - - mdoc_free(mdoc); - - return(0); -} - - -__dead static void -version(void) -{ - - (void)printf("%s %s\n", __progname, VERSION); - exit(0); - /* NOTREACHED */ -} - - -__dead static void -usage(void) -{ - - (void)fprintf(stderr, "usage: %s\n", __progname); - exit(1); - /* NOTREACHED */ -} - - -static int -file(char **ln, size_t *lnsz, char **buf, size_t *bufsz, - const char *file, struct mdoc *mdoc) -{ - int fd, c; - - if (-1 == (fd = open(file, O_RDONLY, 0))) { - warn("%s", file); - return(0); - } - - c = fdesc(ln, lnsz, buf, bufsz, file, fd, mdoc); - - if (-1 == close(fd)) - warn("%s", file); - - return(c); -} - - -static int -fdesc(char **lnp, size_t *lnsz, char **bufp, size_t *bufsz, - const char *f, int fd, struct mdoc *mdoc) -{ - size_t sz; - ssize_t ssz; - struct stat st; - int j, i, pos, lnn; - char *ln, *buf; - - buf = *bufp; - ln = *lnp; - - /* - * Two buffers: ln and buf. buf is the input buffer, optimised - * for each file's block size. ln is a line buffer. Both - * growable, hence passed in by ptr-ptr. - */ - - if (-1 == fstat(fd, &st)) { - warnx("%s", f); - sz = BUFSIZ; - } else - sz = (unsigned)BUFSIZ > st.st_blksize ? - (size_t)BUFSIZ : st.st_blksize; - - if (sz > *bufsz) { - if (NULL == (buf = realloc(buf, sz))) - err(1, "realloc"); - *bufp = buf; - *bufsz = sz; - } - - /* - * Fill buf with file blocksize and parse newlines into ln. - */ - - for (lnn = 1, pos = 0; ; ) { - if (-1 == (ssz = read(fd, buf, sz))) { - warn("%s", f); - return(0); - } else if (0 == ssz) - break; - - for (i = 0; i < (int)ssz; i++) { - if (pos >= (int)*lnsz) { - *lnsz += 256; /* Step-size. */ - ln = realloc(ln, *lnsz); - if (NULL == ln) - err(1, "realloc"); - *lnp = ln; - } - - if ('\n' != buf[i]) { - ln[pos++] = buf[i]; - continue; - } - - /* Check for CPP-escaped newline. */ - - if (pos > 0 && '\\' == ln[pos - 1]) { - for (j = pos - 1; j >= 0; j--) - if ('\\' != ln[j]) - break; - - if ( ! ((pos - j) % 2)) { - pos--; - lnn++; - continue; - } - } - - ln[pos] = 0; - if ( ! mdoc_parseln(mdoc, lnn, ln)) - return(0); - lnn++; - pos = 0; - } - } - - return(mdoc_endparse(mdoc)); -} - - -static int -toptions(enum termt *tflags, char *arg) -{ - - if (0 == strcmp(arg, "ascii")) - *tflags = TERMT_ASCII; - else if (0 == strcmp(arg, "lint")) - *tflags = TERMT_LINT; - else if (0 == strcmp(arg, "tree")) - *tflags = TERMT_TREE; - else { - warnx("bad argument: -T%s", arg); - return(0); - } - - return(1); -} - - -/* - * Parse out the options for [-fopt...] setting compiler options. These - * can be comma-delimited or called again. - */ -static int -foptions(int *fflags, char *arg) -{ - char *v; - char *toks[] = { "ign-scope", "ign-escape", - "ign-macro", NULL }; - - while (*arg) - switch (getsubopt(&arg, toks, &v)) { - case (0): - *fflags |= MDOC_IGN_SCOPE; - break; - case (1): - *fflags |= MDOC_IGN_ESCAPE; - break; - case (2): - *fflags |= MDOC_IGN_MACRO; - break; - default: - warnx("bad argument: -f%s", arg); - return(0); - } - - return(1); -} - - -/* - * Parse out the options for [-Werr...], which sets warning modes. - * These can be comma-delimited or called again. XXX - should this be - * using -w like troff? - */ -static int -woptions(int *wflags, char *arg) -{ - char *v; - char *toks[] = { "all", "compat", - "syntax", "error", NULL }; - - while (*arg) - switch (getsubopt(&arg, toks, &v)) { - case (0): - *wflags |= WARN_WALL; - break; - case (1): - *wflags |= WARN_WCOMPAT; - break; - case (2): - *wflags |= WARN_WSYNTAX; - break; - case (3): - *wflags |= WARN_WERR; - break; - default: - warnx("bad argument: -W%s", arg); - return(0); - } - - return(1); -} - - -/* - * Flush a line of text. A "line" is loosely defined as being something - * that should be followed by a newline, regardless of whether it's - * broken apart by newlines getting there. A line can also be a - * fragment of a columnar list. - * - * Specifically, a line is whatever's in p->buf of length p->col, which - * is zeroed after this function returns. - * - * The variables TERMP_NOLPAD, TERMP_LITERAL and TERMP_NOBREAK are of - * critical importance here. Their behaviour follows: - * - * - TERMP_NOLPAD: when beginning to write the line, don't left-pad the - * offset value. This is useful when doing columnar lists where the - * prior column has right-padded. - * - * - TERMP_NOBREAK: this is the most important and is used when making - * columns. In short: don't print a newline and instead pad to the - * right margin. Used in conjunction with TERMP_NOLPAD. - * - * - TERMP_NONOBREAK: don't newline when TERMP_NOBREAK is specified. - * - * In-line line breaking: - * - * If TERMP_NOBREAK is specified and the line overruns the right - * margin, it will break and pad-right to the right margin after - * writing. If maxrmargin is violated, it will break and continue - * writing from the right-margin, which will lead to the above - * scenario upon exit. - * - * Otherwise, the line will break at the right margin. Extremely long - * lines will cause the system to emit a warning (TODO: hyphenate, if - * possible). - */ -void -flushln(struct termp *p) -{ - int i, j; - size_t vsz, vis, maxvis, mmax, bp; - - /* - * First, establish the maximum columns of "visible" content. - * This is usually the difference between the right-margin and - * an indentation, but can be, for tagged lists or columns, a - * small set of values. - */ - - assert(p->offset < p->rmargin); - maxvis = p->rmargin - p->offset; - mmax = p->maxrmargin - p->offset; - bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; - vis = 0; - - /* - * If in the standard case (left-justified), then begin with our - * indentation, otherwise (columns, etc.) just start spitting - * out text. - */ - - if ( ! (p->flags & TERMP_NOLPAD)) - /* LINTED */ - for (j = 0; j < (int)p->offset; j++) - putchar(' '); - - for (i = 0; i < (int)p->col; i++) { - /* - * Count up visible word characters. Control sequences - * (starting with the CSI) aren't counted. A space - * generates a non-printing word, which is valid (the - * space is printed according to regular spacing rules). - */ - - /* LINTED */ - for (j = i, vsz = 0; j < (int)p->col; j++) { - if (' ' == p->buf[j]) - break; - else if (8 == p->buf[j]) - j += 1; - else - vsz++; - } - - /* - * Do line-breaking. If we're greater than our - * break-point and already in-line, break to the next - * line and start writing. If we're at the line start, - * then write out the word (TODO: hyphenate) and break - * in a subsequent loop invocation. - */ - - if ( ! (TERMP_NOBREAK & p->flags)) { - if (vis && vis + vsz > bp) { - putchar('\n'); - for (j = 0; j < (int)p->offset; j++) - putchar(' '); - vis = 0; - } else if (vis + vsz > bp) - warnx("word breaks right margin"); - - /* TODO: hyphenate. */ - - } else { - if (vis && vis + vsz > bp) { - putchar('\n'); - for (j = 0; j < (int)p->rmargin; j++) - putchar(' '); - vis = p->rmargin - p->offset; - } else if (vis + vsz > bp) - warnx("word breaks right margin"); - - /* TODO: hyphenate. */ - } - - /* - * Write out the word and a trailing space. Omit the - * space if we're the last word in the line or beyond - * our breakpoint. - */ - - for ( ; i < (int)p->col; i++) { - if (' ' == p->buf[i]) - break; - putchar(p->buf[i]); - } - vis += vsz; - if (i < (int)p->col && vis <= bp) { - putchar(' '); - vis++; - } - } - - /* - * If we've overstepped our maximum visible no-break space, then - * cause a newline and offset at the right margin. - */ - - if ((TERMP_NOBREAK & p->flags) && vis >= maxvis) { - if ( ! (TERMP_NONOBREAK & p->flags)) { - putchar('\n'); - for (i = 0; i < (int)p->rmargin; i++) - putchar(' '); - } - p->col = 0; - return; - } - - /* - * If we're not to right-marginalise it (newline), then instead - * pad to the right margin and stay off. - */ - - if (p->flags & TERMP_NOBREAK) { - if ( ! (TERMP_NONOBREAK & p->flags)) - for ( ; vis < maxvis; vis++) - putchar(' '); - } else - putchar('\n'); - - p->col = 0; -} - - -/* - * A newline only breaks an existing line; it won't assert vertical - * space. All data in the output buffer is flushed prior to the newline - * assertion. - */ -void -newln(struct termp *p) -{ - - p->flags |= TERMP_NOSPACE; - if (0 == p->col) { - p->flags &= ~TERMP_NOLPAD; - return; - } - flushln(p); - p->flags &= ~TERMP_NOLPAD; -} - - -/* - * Asserts a vertical space (a full, empty line-break between lines). - * Note that if used twice, this will cause two blank spaces and so on. - * All data in the output buffer is flushed prior to the newline - * assertion. - */ -void -vspace(struct termp *p) -{ - - newln(p); - putchar('\n'); -} - - -/* - * Break apart a word into "pwords" (partial-words, usually from - * breaking up a phrase into individual words) and, eventually, put them - * into the output buffer. If we're a literal word, then don't break up - * the word and put it verbatim into the output buffer. - */ -void -word(struct termp *p, const char *word) -{ - int i, j, len; - - if (p->flags & TERMP_LITERAL) { - pword(p, word, (int)strlen(word)); - return; - } - - if (0 == (len = (int)strlen(word))) - errx(1, "blank line not in literal context"); - - if (mdoc_isdelim(word)) { - if ( ! (p->flags & TERMP_IGNDELIM)) - p->flags |= TERMP_NOSPACE; - p->flags &= ~TERMP_IGNDELIM; - } - - /* LINTED */ - for (j = i = 0; i < len; i++) { - if (' ' != word[i]) { - j++; - continue; - } - - /* Escaped spaces don't delimit... */ - if (i && ' ' == word[i] && '\\' == word[i - 1]) { - j++; - continue; - } - - if (0 == j) - continue; - assert(i >= j); - pword(p, &word[i - j], j); - j = 0; - } - if (j > 0) { - assert(i >= j); - pword(p, &word[i - j], j); - } -} - - -/* - * This is the main function for printing out nodes. It's constituted - * of PRE and POST functions, which correspond to prefix and infix - * processing. The termpair structure allows data to persist between - * prefix and postfix invocations. - */ -static void -body(struct termp *p, struct termpair *ppair, - const struct mdoc_meta *meta, - const struct mdoc_node *node) -{ - int dochild; - struct termpair pair; - - /* Some quick sanity-checking. */ - - sanity(node); - - /* Pre-processing. */ - - dochild = 1; - pair.ppair = ppair; - pair.type = 0; - pair.offset = pair.rmargin = 0; - pair.flag = 0; - pair.count = 0; - - if (MDOC_TEXT != node->type) { - if (termacts[node->tok].pre) - if ( ! (*termacts[node->tok].pre)(p, &pair, meta, node)) - dochild = 0; - } else /* MDOC_TEXT == node->type */ - word(p, node->string); - - /* Children. */ - - if (TERMPAIR_FLAG & pair.type) - p->flags |= pair.flag; - - if (dochild && node->child) - body(p, &pair, meta, node->child); - - if (TERMPAIR_FLAG & pair.type) - p->flags &= ~pair.flag; - - /* Post-processing. */ - - if (MDOC_TEXT != node->type) - if (termacts[node->tok].post) - (*termacts[node->tok].post)(p, &pair, meta, node); - - /* Siblings. */ - - if (node->next) - body(p, ppair, meta, node->next); -} - - -static void -footer(struct termp *p, const struct mdoc_meta *meta) -{ - struct tm *tm; - char *buf, *os; - - if (NULL == (buf = malloc(p->rmargin))) - err(1, "malloc"); - if (NULL == (os = malloc(p->rmargin))) - err(1, "malloc"); - - tm = localtime(&meta->date); - -#ifdef __OpenBSD__ - if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm)) -#else - if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm)) -#endif - err(1, "strftime"); - - (void)strlcpy(os, meta->os, p->rmargin); - - /* - * This is /slightly/ different from regular groff output - * because we don't have page numbers. Print the following: - * - * OS MDOCDATE - */ - - vspace(p); - - p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; - p->rmargin = p->maxrmargin - strlen(buf); - p->offset = 0; - - word(p, os); - flushln(p); - - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; - p->offset = p->rmargin; - p->rmargin = p->maxrmargin; - p->flags &= ~TERMP_NOBREAK; - - word(p, buf); - flushln(p); - - free(buf); - free(os); -} - - -static void -header(struct termp *p, const struct mdoc_meta *meta) -{ - char *buf, *title; - - p->rmargin = p->maxrmargin; - p->offset = 0; - - if (NULL == (buf = malloc(p->rmargin))) - err(1, "malloc"); - if (NULL == (title = malloc(p->rmargin))) - err(1, "malloc"); - - /* - * The header is strange. It has three components, which are - * really two with the first duplicated. It goes like this: - * - * IDENTIFIER TITLE IDENTIFIER - * - * The IDENTIFIER is NAME(SECTION), which is the command-name - * (if given, or "unknown" if not) followed by the manual page - * section. These are given in `Dt'. The TITLE is a free-form - * string depending on the manual volume. If not specified, it - * switches on the manual section. - */ - - assert(meta->vol); - (void)strlcpy(buf, meta->vol, p->rmargin); - - if (meta->arch) { - (void)strlcat(buf, " (", p->rmargin); - (void)strlcat(buf, meta->arch, p->rmargin); - (void)strlcat(buf, ")", p->rmargin); - } - - (void)snprintf(title, p->rmargin, "%s(%d)", - meta->title, meta->msec); - - p->offset = 0; - p->rmargin = (p->maxrmargin - strlen(buf)) / 2; - p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; - - word(p, title); - flushln(p); - - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; - p->offset = p->rmargin; - p->rmargin = p->maxrmargin - strlen(title); - - word(p, buf); - flushln(p); - - p->offset = p->rmargin; - p->rmargin = p->maxrmargin; - p->flags &= ~TERMP_NOBREAK; - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; - - word(p, title); - flushln(p); - - p->rmargin = p->maxrmargin; - p->offset = 0; - p->flags &= ~TERMP_NOSPACE; - - free(title); - free(buf); -} - - -/* - * Determine the symbol indicated by an escape sequences, that is, one - * starting with a backslash. Once done, we pass this value into the - * output buffer by way of the symbol table. - */ -static void -nescape(struct termp *p, const char *word, size_t len) -{ - const char *rhs; - size_t sz; - - if (NULL == (rhs = a2ascii(p->symtab, word, len, &sz))) - return; - stringa(p, rhs, sz); -} - - -/* - * Handle an escape sequence: determine its length and pass it to the - * escape-symbol look table. Note that we assume mdoc(3) has validated - * the escape sequence (we assert upon badly-formed escape sequences). - */ -static void -pescape(struct termp *p, const char *word, int *i, int len) -{ - int j; - - if (++(*i) >= len) - return; - - if ('(' == word[*i]) { - (*i)++; - if (*i + 1 >= len) - return; - - nescape(p, &word[*i], 2); - (*i)++; - return; - - } else if ('*' == word[*i]) { - (*i)++; - if (*i >= len) - return; - - switch (word[*i]) { - case ('('): - (*i)++; - if (*i + 1 >= len) - return; - - nescape(p, &word[*i], 2); - (*i)++; - return; - case ('['): - break; - default: - nescape(p, &word[*i], 1); - return; - } - - } else if ('[' != word[*i]) { - nescape(p, &word[*i], 1); - return; - } - - (*i)++; - for (j = 0; word[*i] && ']' != word[*i]; (*i)++, j++) - /* Loop... */ ; - - if (0 == word[*i]) - return; - - nescape(p, &word[*i - j], (size_t)j); -} - - -/* - * Handle pwords, partial words, which may be either a single word or a - * phrase that cannot be broken down (such as a literal string). This - * handles word styling. - */ -static void -pword(struct termp *p, const char *word, int len) -{ - int i; - - if ( ! (TERMP_NOSPACE & p->flags) && - ! (TERMP_LITERAL & p->flags)) - chara(p, ' '); - - if ( ! (p->flags & TERMP_NONOSPACE)) - p->flags &= ~TERMP_NOSPACE; - - /* - * If ANSI (word-length styling), then apply our style now, - * before the word. - */ - - for (i = 0; i < len; i++) { - if ('\\' == word[i]) { - pescape(p, word, &i, len); - continue; - } - - if (TERMP_STYLE & p->flags) { - if (TERMP_BOLD & p->flags) { - chara(p, word[i]); - chara(p, 8); - } - if (TERMP_UNDER & p->flags) { - chara(p, '_'); - chara(p, 8); - } - } - - chara(p, word[i]); - } -} - - -/* - * Like chara() but for arbitrary-length buffers. Resize the buffer by - * a factor of two (if the buffer is less than that) or the buffer's - * size. - */ -static void -stringa(struct termp *p, const char *c, size_t sz) -{ - size_t s; - - if (0 == sz) - return; - - assert(c); - if (p->col + sz >= p->maxcols) { - if (0 == p->maxcols) - p->maxcols = 256; - s = sz > p->maxcols * 2 ? sz : p->maxcols * 2; - p->buf = realloc(p->buf, s); - if (NULL == p->buf) - err(1, "realloc"); - p->maxcols = s; - } - - (void)memcpy(&p->buf[(int)p->col], c, sz); - p->col += sz; -} - - -/* - * Insert a single character into the line-buffer. If the buffer's - * space is exceeded, then allocate more space by doubling the buffer - * size. - */ -static void -chara(struct termp *p, char c) -{ - size_t s; - - if (p->col + 1 >= p->maxcols) { - if (0 == p->maxcols) - p->maxcols = 256; - s = p->maxcols * 2; - p->buf = realloc(p->buf, s); - if (NULL == p->buf) - err(1, "realloc"); - p->maxcols = s; - } - p->buf[(int)(p->col)++] = c; -} - - -static void -sanity(const struct mdoc_node *n) -{ - - switch (n->type) { - case (MDOC_TEXT): - if (n->child) - errx(1, "regular form violated (1)"); - if (NULL == n->parent) - errx(1, "regular form violated (2)"); - if (NULL == n->string) - errx(1, "regular form violated (3)"); - switch (n->parent->type) { - case (MDOC_TEXT): - /* FALLTHROUGH */ - case (MDOC_ROOT): - errx(1, "regular form violated (4)"); - /* NOTREACHED */ - default: - break; - } - break; - case (MDOC_ELEM): - if (NULL == n->parent) - errx(1, "regular form violated (5)"); - switch (n->parent->type) { - case (MDOC_TAIL): - /* FALLTHROUGH */ - case (MDOC_BODY): - /* FALLTHROUGH */ - case (MDOC_HEAD): - break; - default: - errx(1, "regular form violated (6)"); - /* NOTREACHED */ - } - if (n->child) switch (n->child->type) { - case (MDOC_TEXT): - break; - default: - errx(1, "regular form violated (7("); - /* NOTREACHED */ - } - break; - case (MDOC_HEAD): - /* FALLTHROUGH */ - case (MDOC_BODY): - /* FALLTHROUGH */ - case (MDOC_TAIL): - if (NULL == n->parent) - errx(1, "regular form violated (8)"); - if (MDOC_BLOCK != n->parent->type) - errx(1, "regular form violated (9)"); - if (n->child) switch (n->child->type) { - case (MDOC_BLOCK): - /* FALLTHROUGH */ - case (MDOC_ELEM): - /* FALLTHROUGH */ - case (MDOC_TEXT): - break; - default: - errx(1, "regular form violated (a)"); - /* NOTREACHED */ - } - break; - case (MDOC_BLOCK): - if (NULL == n->parent) - errx(1, "regular form violated (b)"); - if (NULL == n->child) - errx(1, "regular form violated (c)"); - switch (n->parent->type) { - case (MDOC_ROOT): - /* FALLTHROUGH */ - case (MDOC_HEAD): - /* FALLTHROUGH */ - case (MDOC_BODY): - /* FALLTHROUGH */ - case (MDOC_TAIL): - break; - default: - errx(1, "regular form violated (d)"); - /* NOTREACHED */ - } - switch (n->child->type) { - case (MDOC_ROOT): - /* FALLTHROUGH */ - case (MDOC_ELEM): - errx(1, "regular form violated (e)"); - /* NOTREACHED */ - default: - break; - } - break; - case (MDOC_ROOT): - if (n->parent) - errx(1, "regular form violated (f)"); - if (NULL == n->child) - errx(1, "regular form violated (10)"); - switch (n->child->type) { - case (MDOC_BLOCK): - break; - default: - errx(1, "regular form violated (11)"); - /* NOTREACHED */ - } - break; - } -} - - -static int -merr(void *arg, int line, int col, const char *msg) -{ - - warnx("error: %s (line %d, column %d)", msg, line, col); - return(0); -} - - -static int -mwarn(void *arg, int line, int col, - enum mdoc_warn type, const char *msg) -{ - int flags; - char *wtype; - - flags = *(int *)arg; - wtype = NULL; - - switch (type) { - case (WARN_COMPAT): - wtype = "compat"; - if (flags & WARN_WCOMPAT) - break; - return(1); - case (WARN_SYNTAX): - wtype = "syntax"; - if (flags & WARN_WSYNTAX) - break; - return(1); - } - - assert(wtype); - warnx("%s warning: %s (line %d, column %d)", - wtype, msg, line, col); - - if ( ! (flags & WARN_WERR)) - return(1); - - warnx("%s: considering warnings as errors", - __progname); - return(0); -} - - diff --git a/mdoctree.1 b/mdoctree.1 deleted file mode 100644 index e9dbaf62..00000000 --- a/mdoctree.1 +++ /dev/null @@ -1,162 +0,0 @@ -.\" $Id: mdoctree.1,v 1.7 2009/03/16 23:37:28 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. -.\" -.Dd $Mdocdate: March 16 2009 $ -.Dt mdoctree 1 -.Os -.\" SECTION -.Sh NAME -.Nm mdoctree -.Nd mdoc macro compiler -.\" SECTION -.Sh SYNOPSIS -.Nm mdoctree -.Op Fl vV -.Op Fl f Ns Ar options... -.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. It's commonly used to see the -syntax tree of a document when building new -.Xr mdoc 3 -utilities. The arguments are as follows: -.Bl -tag -width XXXXXXXXXXXX -.\" ITEM -.It Fl v -Print verbose parsing output. -.\" ITEM -.It Fl V -Print version and exit. -.\" ITEM -.It Fl f Ns Ar option... -Override default compiler behaviour. See -.Sx Compiler Options -for details. -.\" 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 -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. By default, it reads from stdin and -prints the syntax tree to stdout. -.\" PARAGRAPH -.Pp -.Ex -std mdoctree -.\" SUB-SECTION -.Ss Compiler Options -Default compiler behaviour may be overriden with the -.Fl f -flag. The available options are as follows: -.Bl -tag -width XXXXXXXXXXXX -offset XXXX -.It Fl f Ns Ar ign-scope -When rewinding the scope of a block macro, forces the compiler to ignore -scope violations. This can seriously mangle the resulting tree. -.It Fl f Ns Ar ign-escape -Ignore invalid escape sequences. -.It Fl f Ns Ar ign-macro -Ignore unknown macros at the start of input lines. -.El -.\" PARAGRAPH -.Pp -As with the -.Fl W -flag, multiple -.Fl f -options may be grouped and delimited with a comma. Using -.Fl f Ns Ar ign-scope,ign-escape , -for example, will try to ignore scope and character-escape errors. -.\" SUB-SECTION -.Ss Input Encoding -The -.Nm -utility expects its input to be 7-bit ASCII as defined in -.Xr ascii 7 . -The only non-graphing characters accepted are spaces, -.Sq \ , -and tabs, -.Sq \et . -Tabs are only accepted in literal block-displays and as column -delimiters. -.Pp -Only Unix-style newlines (\en) are accepted; if the newline is escaped, -the line is concatenated with the next. -.\" SUB-SECTION -.Ss Character Escapes -Since -.Nm -doesn't format its output, character escapes are displayed as passed -into the compiler. -.\" SECTION -.Sh EXAMPLES -To validate this manual page: -.\" PARAGRAPH -.Pp -.D1 % mdoctree \-Wall,error mdoctree.1 -.\" SECTION -.Sh SEE ALSO -.Xr mdocterm 1 , -.Xr mdoclint 1 , -.Xr mdoc.samples 7 , -.Xr mdoc 7 , -.Xr mdoc 3 -.\" -.Sh AUTHORS -The -.Nm -utility was written by -.An Kristaps Dzonsons Aq kristaps@openbsd.org . -.\" SECTION -.Sh CAVEATS -See -.Xr mdoc 3 -for a list of bugs, caveats, and incomplete macros. diff --git a/mdoctree.c b/mdoctree.c deleted file mode 100644 index 3b48b8f7..00000000 --- a/mdoctree.c +++ /dev/null @@ -1,157 +0,0 @@ -/* $Id: mdoctree.c,v 1.8 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 "mmain.h" - -#define xprintf (void)printf - -static void doprint(const struct mdoc_node *, int); - -int -main(int argc, char *argv[]) -{ - struct mmain *p; - const struct mdoc *mdoc; - int c; - char *in; - - p = mmain_alloc(); - - c = mmain_getopt(p, argc, argv, NULL, - "[infile]", NULL, NULL, NULL); - - argv += c; - if ((argc -= c) > 0) - in = *argv++; - else - in = "-"; - - if (NULL == (mdoc = mmain_mdoc(p, in))) - mmain_exit(p, 1); - - doprint(mdoc_node(mdoc), 0); - mmain_exit(p, 0); - /* NOTREACHED */ -} - - -static void -doprint(const struct mdoc_node *n, int indent) -{ - const char *p, *t; - int i, j; - size_t argc, sz; - char **params; - struct mdoc_argv *argv; - - argv = NULL; - argc = sz = 0; - params = NULL; - - switch (n->type) { - case (MDOC_ROOT): - t = "root"; - break; - case (MDOC_BLOCK): - t = "block"; - break; - case (MDOC_HEAD): - t = "block-head"; - break; - case (MDOC_BODY): - t = "block-body"; - break; - case (MDOC_TAIL): - t = "block-tail"; - break; - case (MDOC_ELEM): - t = "elem"; - break; - case (MDOC_TEXT): - t = "text"; - break; - default: - abort(); - /* NOTREACHED */ - } - - switch (n->type) { - case (MDOC_TEXT): - p = n->string; - break; - case (MDOC_BODY): - p = mdoc_macronames[n->tok]; - break; - case (MDOC_HEAD): - p = mdoc_macronames[n->tok]; - break; - case (MDOC_TAIL): - p = mdoc_macronames[n->tok]; - break; - case (MDOC_ELEM): - p = mdoc_macronames[n->tok]; - if (n->args) { - argv = n->args->argv; - argc = n->args->argc; - } - break; - case (MDOC_BLOCK): - p = mdoc_macronames[n->tok]; - if (n->args) { - argv = n->args->argv; - argc = n->args->argc; - } - break; - case (MDOC_ROOT): - p = "root"; - break; - default: - abort(); - /* NOTREACHED */ - } - - for (i = 0; i < indent; i++) - xprintf(" "); - xprintf("%s (%s)", p, t); - - for (i = 0; i < (int)argc; i++) { - xprintf(" -%s", mdoc_argnames[argv[i].arg]); - if (argv[i].sz > 0) - xprintf(" ["); - for (j = 0; j < (int)argv[i].sz; j++) - xprintf(" [%s]", argv[i].value[j]); - if (argv[i].sz > 0) - xprintf(" ]"); - } - - for (i = 0; i < (int)sz; i++) - xprintf(" [%s]", params[i]); - - xprintf(" %d:%d\n", n->line, n->pos); - - if (n->child) - doprint(n->child, indent + 1); - if (n->next) - doprint(n->next, indent); -} diff --git a/term.h b/term.h index 14112bd7..ac3699a1 100644 --- a/term.h +++ b/term.h @@ -1,4 +1,4 @@ -/* $Id: term.h,v 1.27 2009/03/19 11:49:00 kristaps Exp $ */ +/* $Id: term.h,v 1.28 2009/03/19 16:17:27 kristaps Exp $ */ /* * Copyright (c) 2008 Kristaps Dzonsons * @@ -27,6 +27,12 @@ __BEGIN_DECLS +enum termenc { + TERMENC_ASCII, + TERMENC_LATIN1, + TERMENC_UTF8 +}; + struct termp { size_t rmargin; /* Current right margin. */ size_t maxrmargin; /* Max right margin. */ @@ -45,6 +51,7 @@ struct termp { #define TERMP_BOLD (1 << 8) /* Styles... */ #define TERMP_UNDER (1 << 9) char *buf; /* Output buffer. */ + enum termenc enc; /* Type of encoding. */ void *symtab; /* Encoded-symbol table. */ }; diff --git a/terminal.c b/terminal.c new file mode 100644 index 00000000..9e63d9a9 --- /dev/null +++ b/terminal.c @@ -0,0 +1,828 @@ +/* $Id: terminal.c,v 1.1 2009/03/19 16:17:27 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 "term.h" + +static struct termp *termp_alloc(enum termenc); +static void termp_free(struct termp *); +static void termp_body(struct termp *, struct termpair *, + const struct mdoc_meta *, + const struct mdoc_node *); +static void termp_head(struct termp *, + const struct mdoc_meta *); +static void termp_foot(struct termp *, + const struct mdoc_meta *); +static void termp_pword(struct termp *, const char *, int); +static void termp_pescape(struct termp *, + const char *, int *, int); +static void termp_nescape(struct termp *, + const char *, size_t); +static void termp_chara(struct termp *, char); +static void termp_stringa(struct termp *, + const char *, size_t); +static void sanity(const struct mdoc_node *); /* XXX */ + + +void * +latin1_alloc(void) +{ + + return(termp_alloc(TERMENC_LATIN1)); +} + + +void * +utf8_alloc(void) +{ + + return(termp_alloc(TERMENC_UTF8)); +} + + +void * +ascii_alloc(void) +{ + + return(termp_alloc(TERMENC_ASCII)); +} + + +int +terminal_run(void *arg, const struct mdoc *mdoc) +{ + struct termp *p; + + p = (struct termp *)arg; + + if (NULL == p->symtab) + p->symtab = ascii2htab(); + + termp_head(p, mdoc_meta(mdoc)); + termp_body(p, NULL, mdoc_meta(mdoc), mdoc_node(mdoc)); + termp_foot(p, mdoc_meta(mdoc)); + + return(1); +} + + +void +terminal_free(void *arg) +{ + + termp_free((struct termp *)arg); +} + + +static void +termp_free(struct termp *p) +{ + + if (p->buf) + free(p->buf); + if (TERMENC_ASCII == p->enc && p->symtab) + asciifree(p->symtab); + + free(p); +} + + +static struct termp * +termp_alloc(enum termenc enc) +{ + struct termp *p; + + if (NULL == (p = malloc(sizeof(struct termp)))) + err(1, "malloc"); + bzero(p, sizeof(struct termp)); + p->maxrmargin = 78; + p->enc = enc; + return(p); +} + + +/* + * Flush a line of text. A "line" is loosely defined as being something + * that should be followed by a newline, regardless of whether it's + * broken apart by newlines getting there. A line can also be a + * fragment of a columnar list. + * + * Specifically, a line is whatever's in p->buf of length p->col, which + * is zeroed after this function returns. + * + * The variables TERMP_NOLPAD, TERMP_LITERAL and TERMP_NOBREAK are of + * critical importance here. Their behaviour follows: + * + * - TERMP_NOLPAD: when beginning to write the line, don't left-pad the + * offset value. This is useful when doing columnar lists where the + * prior column has right-padded. + * + * - TERMP_NOBREAK: this is the most important and is used when making + * columns. In short: don't print a newline and instead pad to the + * right margin. Used in conjunction with TERMP_NOLPAD. + * + * - TERMP_NONOBREAK: don't newline when TERMP_NOBREAK is specified. + * + * In-line line breaking: + * + * If TERMP_NOBREAK is specified and the line overruns the right + * margin, it will break and pad-right to the right margin after + * writing. If maxrmargin is violated, it will break and continue + * writing from the right-margin, which will lead to the above + * scenario upon exit. + * + * Otherwise, the line will break at the right margin. Extremely long + * lines will cause the system to emit a warning (TODO: hyphenate, if + * possible). + */ +void +flushln(struct termp *p) +{ + int i, j; + size_t vsz, vis, maxvis, mmax, bp; + + /* + * First, establish the maximum columns of "visible" content. + * This is usually the difference between the right-margin and + * an indentation, but can be, for tagged lists or columns, a + * small set of values. + */ + + assert(p->offset < p->rmargin); + maxvis = p->rmargin - p->offset; + mmax = p->maxrmargin - p->offset; + bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; + vis = 0; + + /* + * If in the standard case (left-justified), then begin with our + * indentation, otherwise (columns, etc.) just start spitting + * out text. + */ + + if ( ! (p->flags & TERMP_NOLPAD)) + /* LINTED */ + for (j = 0; j < (int)p->offset; j++) + putchar(' '); + + for (i = 0; i < (int)p->col; i++) { + /* + * Count up visible word characters. Control sequences + * (starting with the CSI) aren't counted. A space + * generates a non-printing word, which is valid (the + * space is printed according to regular spacing rules). + */ + + /* LINTED */ + for (j = i, vsz = 0; j < (int)p->col; j++) { + if (' ' == p->buf[j]) + break; + else if (8 == p->buf[j]) + j += 1; + else + vsz++; + } + + /* + * Do line-breaking. If we're greater than our + * break-point and already in-line, break to the next + * line and start writing. If we're at the line start, + * then write out the word (TODO: hyphenate) and break + * in a subsequent loop invocation. + */ + + if ( ! (TERMP_NOBREAK & p->flags)) { + if (vis && vis + vsz > bp) { + putchar('\n'); + for (j = 0; j < (int)p->offset; j++) + putchar(' '); + vis = 0; + } else if (vis + vsz > bp) + warnx("word breaks right margin"); + + /* TODO: hyphenate. */ + + } else { + if (vis && vis + vsz > bp) { + putchar('\n'); + for (j = 0; j < (int)p->rmargin; j++) + putchar(' '); + vis = p->rmargin - p->offset; + } else if (vis + vsz > bp) + warnx("word breaks right margin"); + + /* TODO: hyphenate. */ + } + + /* + * Write out the word and a trailing space. Omit the + * space if we're the last word in the line or beyond + * our breakpoint. + */ + + for ( ; i < (int)p->col; i++) { + if (' ' == p->buf[i]) + break; + putchar(p->buf[i]); + } + vis += vsz; + if (i < (int)p->col && vis <= bp) { + putchar(' '); + vis++; + } + } + + /* + * If we've overstepped our maximum visible no-break space, then + * cause a newline and offset at the right margin. + */ + + if ((TERMP_NOBREAK & p->flags) && vis >= maxvis) { + if ( ! (TERMP_NONOBREAK & p->flags)) { + putchar('\n'); + for (i = 0; i < (int)p->rmargin; i++) + putchar(' '); + } + p->col = 0; + return; + } + + /* + * If we're not to right-marginalise it (newline), then instead + * pad to the right margin and stay off. + */ + + if (p->flags & TERMP_NOBREAK) { + if ( ! (TERMP_NONOBREAK & p->flags)) + for ( ; vis < maxvis; vis++) + putchar(' '); + } else + putchar('\n'); + + p->col = 0; +} + + +/* + * A newline only breaks an existing line; it won't assert vertical + * space. All data in the output buffer is flushed prior to the newline + * assertion. + */ +void +newln(struct termp *p) +{ + + p->flags |= TERMP_NOSPACE; + if (0 == p->col) { + p->flags &= ~TERMP_NOLPAD; + return; + } + flushln(p); + p->flags &= ~TERMP_NOLPAD; +} + + +/* + * Asserts a vertical space (a full, empty line-break between lines). + * Note that if used twice, this will cause two blank spaces and so on. + * All data in the output buffer is flushed prior to the newline + * assertion. + */ +void +vspace(struct termp *p) +{ + + newln(p); + putchar('\n'); +} + + +/* + * Break apart a word into "pwords" (partial-words, usually from + * breaking up a phrase into individual words) and, eventually, put them + * into the output buffer. If we're a literal word, then don't break up + * the word and put it verbatim into the output buffer. + */ +void +word(struct termp *p, const char *word) +{ + int i, j, len; + + if (p->flags & TERMP_LITERAL) { + termp_pword(p, word, (int)strlen(word)); + return; + } + + if (0 == (len = (int)strlen(word))) + errx(1, "blank line not in literal context"); + + if (mdoc_isdelim(word)) { + if ( ! (p->flags & TERMP_IGNDELIM)) + p->flags |= TERMP_NOSPACE; + p->flags &= ~TERMP_IGNDELIM; + } + + /* LINTED */ + for (j = i = 0; i < len; i++) { + if (' ' != word[i]) { + j++; + continue; + } + + /* Escaped spaces don't delimit... */ + if (i && ' ' == word[i] && '\\' == word[i - 1]) { + j++; + continue; + } + + if (0 == j) + continue; + assert(i >= j); + termp_pword(p, &word[i - j], j); + j = 0; + } + if (j > 0) { + assert(i >= j); + termp_pword(p, &word[i - j], j); + } +} + + +/* + * This is the main function for printing out nodes. It's constituted + * of PRE and POST functions, which correspond to prefix and infix + * processing. The termpair structure allows data to persist between + * prefix and postfix invocations. + */ +static void +termp_body(struct termp *p, struct termpair *ppair, + const struct mdoc_meta *meta, + const struct mdoc_node *node) +{ + int dochild; + struct termpair pair; + + /* Some quick sanity-checking. */ + + sanity(node); + + /* Pre-processing. */ + + dochild = 1; + pair.ppair = ppair; + pair.type = 0; + pair.offset = pair.rmargin = 0; + pair.flag = 0; + pair.count = 0; + + if (MDOC_TEXT != node->type) { + if (termacts[node->tok].pre) + if ( ! (*termacts[node->tok].pre)(p, &pair, meta, node)) + dochild = 0; + } else /* MDOC_TEXT == node->type */ + word(p, node->string); + + /* Children. */ + + if (TERMPAIR_FLAG & pair.type) + p->flags |= pair.flag; + + if (dochild && node->child) + termp_body(p, &pair, meta, node->child); + + if (TERMPAIR_FLAG & pair.type) + p->flags &= ~pair.flag; + + /* Post-processing. */ + + if (MDOC_TEXT != node->type) + if (termacts[node->tok].post) + (*termacts[node->tok].post)(p, &pair, meta, node); + + /* Siblings. */ + + if (node->next) + termp_body(p, ppair, meta, node->next); +} + + +static void +termp_foot(struct termp *p, const struct mdoc_meta *meta) +{ + struct tm *tm; + char *buf, *os; + + if (NULL == (buf = malloc(p->rmargin))) + err(1, "malloc"); + if (NULL == (os = malloc(p->rmargin))) + err(1, "malloc"); + + tm = localtime(&meta->date); + +#ifdef __OpenBSD__ + if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm)) +#else + if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm)) +#endif + err(1, "strftime"); + + (void)strlcpy(os, meta->os, p->rmargin); + + /* + * This is /slightly/ different from regular groff output + * because we don't have page numbers. Print the following: + * + * OS MDOCDATE + */ + + vspace(p); + + p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; + p->rmargin = p->maxrmargin - strlen(buf); + p->offset = 0; + + word(p, os); + flushln(p); + + p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + p->offset = p->rmargin; + p->rmargin = p->maxrmargin; + p->flags &= ~TERMP_NOBREAK; + + word(p, buf); + flushln(p); + + free(buf); + free(os); +} + + +static void +termp_head(struct termp *p, const struct mdoc_meta *meta) +{ + char *buf, *title; + + p->rmargin = p->maxrmargin; + p->offset = 0; + + if (NULL == (buf = malloc(p->rmargin))) + err(1, "malloc"); + if (NULL == (title = malloc(p->rmargin))) + err(1, "malloc"); + + /* + * The header is strange. It has three components, which are + * really two with the first duplicated. It goes like this: + * + * IDENTIFIER TITLE IDENTIFIER + * + * The IDENTIFIER is NAME(SECTION), which is the command-name + * (if given, or "unknown" if not) followed by the manual page + * section. These are given in `Dt'. The TITLE is a free-form + * string depending on the manual volume. If not specified, it + * switches on the manual section. + */ + + assert(meta->vol); + (void)strlcpy(buf, meta->vol, p->rmargin); + + if (meta->arch) { + (void)strlcat(buf, " (", p->rmargin); + (void)strlcat(buf, meta->arch, p->rmargin); + (void)strlcat(buf, ")", p->rmargin); + } + + (void)snprintf(title, p->rmargin, "%s(%d)", + meta->title, meta->msec); + + p->offset = 0; + p->rmargin = (p->maxrmargin - strlen(buf)) / 2; + p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; + + word(p, title); + flushln(p); + + p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + p->offset = p->rmargin; + p->rmargin = p->maxrmargin - strlen(title); + + word(p, buf); + flushln(p); + + p->offset = p->rmargin; + p->rmargin = p->maxrmargin; + p->flags &= ~TERMP_NOBREAK; + p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + + word(p, title); + flushln(p); + + p->rmargin = p->maxrmargin; + p->offset = 0; + p->flags &= ~TERMP_NOSPACE; + + free(title); + free(buf); +} + + +/* + * Determine the symbol indicated by an escape sequences, that is, one + * starting with a backslash. Once done, we pass this value into the + * output buffer by way of the symbol table. + */ +static void +termp_nescape(struct termp *p, const char *word, size_t len) +{ + const char *rhs; + size_t sz; + + if (NULL == (rhs = a2ascii(p->symtab, word, len, &sz))) + return; + termp_stringa(p, rhs, sz); +} + + +/* + * Handle an escape sequence: determine its length and pass it to the + * escape-symbol look table. Note that we assume mdoc(3) has validated + * the escape sequence (we assert upon badly-formed escape sequences). + */ +static void +termp_pescape(struct termp *p, const char *word, int *i, int len) +{ + int j; + + if (++(*i) >= len) + return; + + if ('(' == word[*i]) { + (*i)++; + if (*i + 1 >= len) + return; + + termp_nescape(p, &word[*i], 2); + (*i)++; + return; + + } else if ('*' == word[*i]) { + (*i)++; + if (*i >= len) + return; + + switch (word[*i]) { + case ('('): + (*i)++; + if (*i + 1 >= len) + return; + + termp_nescape(p, &word[*i], 2); + (*i)++; + return; + case ('['): + break; + default: + termp_nescape(p, &word[*i], 1); + return; + } + + } else if ('[' != word[*i]) { + termp_nescape(p, &word[*i], 1); + return; + } + + (*i)++; + for (j = 0; word[*i] && ']' != word[*i]; (*i)++, j++) + /* Loop... */ ; + + if (0 == word[*i]) + return; + + termp_nescape(p, &word[*i - j], (size_t)j); +} + + +/* + * Handle pwords, partial words, which may be either a single word or a + * phrase that cannot be broken down (such as a literal string). This + * handles word styling. + */ +static void +termp_pword(struct termp *p, const char *word, int len) +{ + int i; + + if ( ! (TERMP_NOSPACE & p->flags) && + ! (TERMP_LITERAL & p->flags)) + termp_chara(p, ' '); + + if ( ! (p->flags & TERMP_NONOSPACE)) + p->flags &= ~TERMP_NOSPACE; + + /* + * If ANSI (word-length styling), then apply our style now, + * before the word. + */ + + for (i = 0; i < len; i++) { + if ('\\' == word[i]) { + termp_pescape(p, word, &i, len); + continue; + } + + if (TERMP_STYLE & p->flags) { + if (TERMP_BOLD & p->flags) { + termp_chara(p, word[i]); + termp_chara(p, 8); + } + if (TERMP_UNDER & p->flags) { + termp_chara(p, '_'); + termp_chara(p, 8); + } + } + + termp_chara(p, word[i]); + } +} + + +/* + * Like termp_chara() but for arbitrary-length buffers. Resize the + * buffer by a factor of two (if the buffer is less than that) or the + * buffer's size. + */ +static void +termp_stringa(struct termp *p, const char *c, size_t sz) +{ + size_t s; + + if (0 == sz) + return; + + assert(c); + if (p->col + sz >= p->maxcols) { + if (0 == p->maxcols) + p->maxcols = 256; + s = sz > p->maxcols * 2 ? sz : p->maxcols * 2; + p->buf = realloc(p->buf, s); + if (NULL == p->buf) + err(1, "realloc"); + p->maxcols = s; + } + + (void)memcpy(&p->buf[(int)p->col], c, sz); + p->col += sz; +} + + +/* + * Insert a single character into the line-buffer. If the buffer's + * space is exceeded, then allocate more space by doubling the buffer + * size. + */ +static void +termp_chara(struct termp *p, char c) +{ + size_t s; + + if (p->col + 1 >= p->maxcols) { + if (0 == p->maxcols) + p->maxcols = 256; + s = p->maxcols * 2; + p->buf = realloc(p->buf, s); + if (NULL == p->buf) + err(1, "realloc"); + p->maxcols = s; + } + p->buf[(int)(p->col)++] = c; +} + + +static void +sanity(const struct mdoc_node *n) +{ + + switch (n->type) { + case (MDOC_TEXT): + if (n->child) + errx(1, "regular form violated (1)"); + if (NULL == n->parent) + errx(1, "regular form violated (2)"); + if (NULL == n->string) + errx(1, "regular form violated (3)"); + switch (n->parent->type) { + case (MDOC_TEXT): + /* FALLTHROUGH */ + case (MDOC_ROOT): + errx(1, "regular form violated (4)"); + /* NOTREACHED */ + default: + break; + } + break; + case (MDOC_ELEM): + if (NULL == n->parent) + errx(1, "regular form violated (5)"); + switch (n->parent->type) { + case (MDOC_TAIL): + /* FALLTHROUGH */ + case (MDOC_BODY): + /* FALLTHROUGH */ + case (MDOC_HEAD): + break; + default: + errx(1, "regular form violated (6)"); + /* NOTREACHED */ + } + if (n->child) switch (n->child->type) { + case (MDOC_TEXT): + break; + default: + errx(1, "regular form violated (7("); + /* NOTREACHED */ + } + break; + case (MDOC_HEAD): + /* FALLTHROUGH */ + case (MDOC_BODY): + /* FALLTHROUGH */ + case (MDOC_TAIL): + if (NULL == n->parent) + errx(1, "regular form violated (8)"); + if (MDOC_BLOCK != n->parent->type) + errx(1, "regular form violated (9)"); + if (n->child) switch (n->child->type) { + case (MDOC_BLOCK): + /* FALLTHROUGH */ + case (MDOC_ELEM): + /* FALLTHROUGH */ + case (MDOC_TEXT): + break; + default: + errx(1, "regular form violated (a)"); + /* NOTREACHED */ + } + break; + case (MDOC_BLOCK): + if (NULL == n->parent) + errx(1, "regular form violated (b)"); + if (NULL == n->child) + errx(1, "regular form violated (c)"); + switch (n->parent->type) { + case (MDOC_ROOT): + /* FALLTHROUGH */ + case (MDOC_HEAD): + /* FALLTHROUGH */ + case (MDOC_BODY): + /* FALLTHROUGH */ + case (MDOC_TAIL): + break; + default: + errx(1, "regular form violated (d)"); + /* NOTREACHED */ + } + switch (n->child->type) { + case (MDOC_ROOT): + /* FALLTHROUGH */ + case (MDOC_ELEM): + errx(1, "regular form violated (e)"); + /* NOTREACHED */ + default: + break; + } + break; + case (MDOC_ROOT): + if (n->parent) + errx(1, "regular form violated (f)"); + if (NULL == n->child) + errx(1, "regular form violated (10)"); + switch (n->child->type) { + case (MDOC_BLOCK): + break; + default: + errx(1, "regular form violated (11)"); + /* NOTREACHED */ + } + break; + } +} diff --git a/tree.c b/tree.c new file mode 100644 index 00000000..4341077b --- /dev/null +++ b/tree.c @@ -0,0 +1,137 @@ +/* $Id: tree.c,v 1.8 2009/03/19 16:17:27 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 "mdoc.h" + +static void tree_body(const struct mdoc_node *, int); + + +int +tree_run(void *arg, const struct mdoc *mdoc) +{ + + tree_body(mdoc_node(mdoc), 0); + return(1); +} + + +static void +tree_body(const struct mdoc_node *n, int indent) +{ + const char *p, *t; + int i, j; + size_t argc, sz; + char **params; + struct mdoc_argv *argv; + + argv = NULL; + argc = sz = 0; + params = NULL; + + switch (n->type) { + case (MDOC_ROOT): + t = "root"; + break; + case (MDOC_BLOCK): + t = "block"; + break; + case (MDOC_HEAD): + t = "block-head"; + break; + case (MDOC_BODY): + t = "block-body"; + break; + case (MDOC_TAIL): + t = "block-tail"; + break; + case (MDOC_ELEM): + t = "elem"; + break; + case (MDOC_TEXT): + t = "text"; + break; + default: + abort(); + /* NOTREACHED */ + } + + switch (n->type) { + case (MDOC_TEXT): + p = n->string; + break; + case (MDOC_BODY): + p = mdoc_macronames[n->tok]; + break; + case (MDOC_HEAD): + p = mdoc_macronames[n->tok]; + break; + case (MDOC_TAIL): + p = mdoc_macronames[n->tok]; + break; + case (MDOC_ELEM): + p = mdoc_macronames[n->tok]; + if (n->args) { + argv = n->args->argv; + argc = n->args->argc; + } + break; + case (MDOC_BLOCK): + p = mdoc_macronames[n->tok]; + if (n->args) { + argv = n->args->argv; + argc = n->args->argc; + } + break; + case (MDOC_ROOT): + p = "root"; + break; + default: + abort(); + /* NOTREACHED */ + } + + for (i = 0; i < indent; i++) + (void)printf(" "); + (void)printf("%s (%s)", p, t); + + for (i = 0; i < (int)argc; i++) { + (void)printf(" -%s", mdoc_argnames[argv[i].arg]); + if (argv[i].sz > 0) + (void)printf(" ["); + for (j = 0; j < (int)argv[i].sz; j++) + (void)printf(" [%s]", argv[i].value[j]); + if (argv[i].sz > 0) + (void)printf(" ]"); + } + + for (i = 0; i < (int)sz; i++) + (void)printf(" [%s]", params[i]); + + (void)printf(" %d:%d\n", n->line, n->pos); + + if (n->child) + tree_body(n->child, indent + 1); + if (n->next) + tree_body(n->next, indent); +} + -- cgit v1.2.3-56-ge451