aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/mdocterm.c
diff options
context:
space:
mode:
Diffstat (limited to 'mdocterm.c')
-rw-r--r--mdocterm.c552
1 files changed, 378 insertions, 174 deletions
diff --git a/mdocterm.c b/mdocterm.c
index 9742dbef..b996fe6c 100644
--- a/mdocterm.c
+++ b/mdocterm.c
@@ -1,4 +1,4 @@
-/* $Id: mdocterm.c,v 1.48 2009/03/17 13:35:46 kristaps Exp $ */
+/* $Id: mdocterm.c,v 1.49 2009/03/19 11:49:00 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
*
@@ -17,45 +17,57 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
+#include <sys/stat.h>
#include <assert.h>
-#include <ctype.h>
#include <err.h>
+#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include "mmain.h"
#include "term.h"
-struct nroffopt {
- int fl_h;
- int fl_i;
- char *arg_m;
- char *arg_n;
- char *arg_o;
- char *arg_r;
- char *arg_T;
- struct termp *termp; /* Ephemeral. */
+#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
};
-__dead void punt(struct nroffopt *, char *);
-static int option(void *, int, char *);
-static int optsopt(struct termp *, char *);
-static void body(struct termp *,
- struct termpair *,
+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 void pword(struct termp *, const char *, size_t);
-static void pescape(struct termp *, const char *,
- size_t *, size_t);
+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);
@@ -63,76 +75,267 @@ static void stringa(struct termp *,
const char *, size_t);
static void sanity(const struct mdoc_node *);
+
int
main(int argc, char *argv[])
{
- struct mmain *p;
- const struct mdoc *mdoc;
- struct nroffopt nroff;
- struct termp termp;
- int c;
- char *in;
-
- (void)memset(&termp, 0, sizeof(struct termp));
- (void)memset(&nroff, 0, sizeof(struct nroffopt));
-
- termp.maxrmargin = termp.rmargin = 78; /* FIXME */
- termp.maxcols = 1024; /* FIXME */
- termp.flags = TERMP_NOSPACE;
- termp.symtab = ascii2htab();
-
- nroff.termp = &termp;
-
- p = mmain_alloc();
-
- c = mmain_getopt(p, argc, argv, "[-Ooption...]",
- "[infile]", "him:n:o:r:T:O:", &nroff, option);
-
- /* FIXME: this needs to accept multiple outputs. */
- argv += c;
- if ((argc -= c) > 0)
- in = *argv++;
- else
- in = "-";
-
- mmain_prepare(p, in);
-
- if (NULL == (mdoc = mmain_process(p))) {
- if (TERMP_NOPUNT & termp.iflags)
- mmain_exit(p, 1);
- mmain_free(p);
- punt(&nroff, in);
- /* NOTREACHED */
+ 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 (NULL == (termp.buf = malloc(termp.maxcols)))
- err(1, "malloc");
+ if (buf)
+ free(buf);
+ if (line)
+ free(line);
+ if (termp.buf)
+ free(termp.buf);
+ if (termp.symtab)
+ asciifree(termp.symtab);
- header(&termp, mdoc_meta(mdoc));
- body(&termp, NULL, mdoc_meta(mdoc), mdoc_node(mdoc));
- footer(&termp, mdoc_meta(mdoc));
+ mdoc_free(mdoc);
+
+ return(0);
+}
- free(termp.buf);
- asciifree(termp.symtab);
- mmain_exit(p, 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
-optsopt(struct termp *p, char *arg)
+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[] = { "nopunt", NULL };
+ char *toks[] = { "ign-scope", "ign-escape",
+ "ign-macro", NULL };
while (*arg)
switch (getsubopt(&arg, toks, &v)) {
case (0):
- p->iflags |= TERMP_NOPUNT;
+ *fflags |= MDOC_IGN_SCOPE;
+ break;
+ case (1):
+ *fflags |= MDOC_IGN_ESCAPE;
+ break;
+ case (2):
+ *fflags |= MDOC_IGN_MACRO;
break;
default:
- warnx("unknown -O argument");
+ warnx("bad argument: -f%s", arg);
return(0);
}
@@ -140,42 +343,36 @@ optsopt(struct termp *p, char *arg)
}
+/*
+ * 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
-option(void *ptr, int c, char *arg)
+woptions(int *wflags, char *arg)
{
- struct termp *termp;
- struct nroffopt *nroff;
-
- nroff = (struct nroffopt *)ptr;
- termp = nroff->termp;
+ char *v;
+ char *toks[] = { "all", "compat",
+ "syntax", "error", NULL };
- switch (c) {
- case ('h'):
- nroff->fl_h = 1;
- break;
- case ('i'):
- nroff->fl_i = 1;
- break;
- case ('m'):
- nroff->arg_m = arg;
- break;
- case ('n'):
- nroff->arg_n = arg;
- break;
- case ('o'):
- nroff->arg_o = arg;
- break;
- case ('r'):
- nroff->arg_r = arg;
- break;
- case ('T'):
- nroff->arg_T = arg;
- break;
- case ('O'):
- return(optsopt(termp, arg));
- default:
- break;
- }
+ 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);
}
@@ -201,6 +398,8 @@ option(void *ptr, int c, char *arg)
* 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
@@ -216,7 +415,8 @@ option(void *ptr, int c, char *arg)
void
flushln(struct termp *p)
{
- size_t i, j, vsz, vis, maxvis, mmax, bp;
+ int i, j;
+ size_t vsz, vis, maxvis, mmax, bp;
/*
* First, establish the maximum columns of "visible" content.
@@ -239,10 +439,10 @@ flushln(struct termp *p)
if ( ! (p->flags & TERMP_NOLPAD))
/* LINTED */
- for (j = 0; j < p->offset; j++)
+ for (j = 0; j < (int)p->offset; j++)
putchar(' ');
- for (i = 0; i < p->col; i++) {
+ for (i = 0; i < (int)p->col; i++) {
/*
* Count up visible word characters. Control sequences
* (starting with the CSI) aren't counted. A space
@@ -251,7 +451,7 @@ flushln(struct termp *p)
*/
/* LINTED */
- for (j = i, vsz = 0; j < p->col; j++) {
+ for (j = i, vsz = 0; j < (int)p->col; j++) {
if (' ' == p->buf[j])
break;
else if (8 == p->buf[j])
@@ -271,7 +471,7 @@ flushln(struct termp *p)
if ( ! (TERMP_NOBREAK & p->flags)) {
if (vis && vis + vsz > bp) {
putchar('\n');
- for (j = 0; j < p->offset; j++)
+ for (j = 0; j < (int)p->offset; j++)
putchar(' ');
vis = 0;
} else if (vis + vsz > bp)
@@ -282,7 +482,7 @@ flushln(struct termp *p)
} else {
if (vis && vis + vsz > bp) {
putchar('\n');
- for (j = 0; j < p->rmargin; j++)
+ for (j = 0; j < (int)p->rmargin; j++)
putchar(' ');
vis = p->rmargin - p->offset;
} else if (vis + vsz > bp)
@@ -297,13 +497,13 @@ flushln(struct termp *p)
* our breakpoint.
*/
- for ( ; i < p->col; i++) {
+ for ( ; i < (int)p->col; i++) {
if (' ' == p->buf[i])
break;
putchar(p->buf[i]);
}
vis += vsz;
- if (i < p->col && vis <= bp) {
+ if (i < (int)p->col && vis <= bp) {
putchar(' ');
vis++;
}
@@ -317,7 +517,7 @@ flushln(struct termp *p)
if ((TERMP_NOBREAK & p->flags) && vis >= maxvis) {
if ( ! (TERMP_NONOBREAK & p->flags)) {
putchar('\n');
- for (i = 0; i < p->rmargin; i++)
+ for (i = 0; i < (int)p->rmargin; i++)
putchar(' ');
}
p->col = 0;
@@ -383,14 +583,14 @@ vspace(struct termp *p)
void
word(struct termp *p, const char *word)
{
- size_t i, j, len;
+ int i, j, len;
if (p->flags & TERMP_LITERAL) {
- pword(p, word, strlen(word));
+ pword(p, word, (int)strlen(word));
return;
}
- if (0 == (len = strlen(word)))
+ if (0 == (len = (int)strlen(word)))
errx(1, "blank line not in literal context");
if (mdoc_isdelim(word)) {
@@ -537,7 +737,7 @@ footer(struct termp *p, const struct mdoc_meta *meta)
static void
header(struct termp *p, const struct mdoc_meta *meta)
{
- char *buf, *title, *bufp;
+ char *buf, *title;
p->rmargin = p->maxrmargin;
p->offset = 0;
@@ -572,9 +772,6 @@ header(struct termp *p, const struct mdoc_meta *meta)
(void)snprintf(title, p->rmargin, "%s(%d)",
meta->title, meta->msec);
- for (bufp = title; *bufp; bufp++)
- *bufp = toupper((u_char)*bufp);
-
p->offset = 0;
p->rmargin = (p->maxrmargin - strlen(buf)) / 2;
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
@@ -617,11 +814,8 @@ 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))) {
- warnx("unsupported %zu-byte escape sequence", len);
+ if (NULL == (rhs = a2ascii(p->symtab, word, len, &sz)))
return;
- }
-
stringa(p, rhs, sz);
}
@@ -632,38 +826,33 @@ nescape(struct termp *p, const char *word, size_t len)
* the escape sequence (we assert upon badly-formed escape sequences).
*/
static void
-pescape(struct termp *p, const char *word, size_t *i, size_t len)
+pescape(struct termp *p, const char *word, int *i, int len)
{
- size_t j;
+ int j;
- if (++(*i) >= len) {
- warnx("ignoring bad escape sequence");
+ if (++(*i) >= len)
return;
- }
if ('(' == word[*i]) {
(*i)++;
- if (*i + 1 >= len) {
- warnx("ignoring bad escape sequence");
+ if (*i + 1 >= len)
return;
- }
+
nescape(p, &word[*i], 2);
(*i)++;
return;
} else if ('*' == word[*i]) {
(*i)++;
- if (*i >= len) {
- warnx("ignoring bad escape sequence");
+ if (*i >= len)
return;
- }
+
switch (word[*i]) {
case ('('):
(*i)++;
- if (*i + 1 >= len) {
- warnx("ignoring bad escape sequence");
+ if (*i + 1 >= len)
return;
- }
+
nescape(p, &word[*i], 2);
(*i)++;
return;
@@ -683,11 +872,10 @@ pescape(struct termp *p, const char *word, size_t *i, size_t len)
for (j = 0; word[*i] && ']' != word[*i]; (*i)++, j++)
/* Loop... */ ;
- if (0 == word[*i]) {
- warnx("ignoring bad escape sequence");
+ if (0 == word[*i])
return;
- }
- nescape(p, &word[*i - j], j);
+
+ nescape(p, &word[*i - j], (size_t)j);
}
@@ -697,9 +885,9 @@ pescape(struct termp *p, const char *word, size_t *i, size_t len)
* handles word styling.
*/
static void
-pword(struct termp *p, const char *word, size_t len)
+pword(struct termp *p, const char *word, int len)
{
- size_t i;
+ int i;
if ( ! (TERMP_NOSPACE & p->flags) &&
! (TERMP_LITERAL & p->flags))
@@ -748,17 +936,18 @@ stringa(struct termp *p, const char *c, size_t sz)
if (0 == sz)
return;
- s = sz > p->maxcols * 2 ? sz : p->maxcols * 2;
-
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[p->col], c, sz);
+ (void)memcpy(&p->buf[(int)p->col], c, sz);
p->col += sz;
}
@@ -771,14 +960,18 @@ stringa(struct termp *p, const char *c, size_t sz)
static void
chara(struct termp *p, char c)
{
+ size_t s;
if (p->col + 1 >= p->maxcols) {
- p->buf = realloc(p->buf, p->maxcols * 2);
+ if (0 == p->maxcols)
+ p->maxcols = 256;
+ s = p->maxcols * 2;
+ p->buf = realloc(p->buf, s);
if (NULL == p->buf)
- err(1, "malloc");
- p->maxcols *= 2;
+ err(1, "realloc");
+ p->maxcols = s;
}
- p->buf[(p->col)++] = c;
+ p->buf[(int)(p->col)++] = c;
}
@@ -892,37 +1085,48 @@ sanity(const struct mdoc_node *n)
}
-__dead void
-punt(struct nroffopt *nroff, char *in)
+static int
+merr(void *arg, int line, int col, const char *msg)
{
- char *args[32];
- char arg0[32], argm[32];
- int i;
- warnx("punting to nroff!");
+ warnx("error: %s (line %d, column %d)", msg, line, col);
+ return(0);
+}
+
- i = 0;
+static int
+mwarn(void *arg, int line, int col,
+ enum mdoc_warn type, const char *msg)
+{
+ int flags;
+ char *wtype;
- (void)strlcpy(arg0, "nroff", 32);
- args[i++] = arg0;
+ flags = *(int *)arg;
+ wtype = NULL;
- if (nroff->fl_h)
- args[i++] = "-h";
- if (nroff->fl_i)
- args[i++] = "-i";
+ 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);
+ }
- if (nroff->arg_m) {
- (void)strlcpy(argm, "-m", 32);
- (void)strlcat(argm, nroff->arg_m, 32);
- args[i++] = argm;
- } else
- args[i++] = "-mandoc";
+ assert(wtype);
+ warnx("%s warning: %s (line %d, column %d)",
+ wtype, msg, line, col);
- args[i++] = in;
- args[i++] = (char *)NULL;
+ if ( ! (flags & WARN_WERR))
+ return(1);
- (void)execvp("nroff", args);
- errx(1, "exec");
- /* NOTREACHED */
+ warnx("%s: considering warnings as errors",
+ __progname);
+ return(0);
}
+