-/* $Id: mdocml.c,v 1.2 2008/11/22 16:55:02 kristaps Exp $ */
+/* $Id: mdocml.c,v 1.46 2009/01/16 14:15:12 kristaps Exp $ */
/*
* Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
*
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
-#include <sys/param.h>
#include <sys/stat.h>
+#include <sys/param.h>
#include <assert.h>
-#include <err.h>
#include <fcntl.h>
+#include <err.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include "libmdocml.h"
-
-struct md_file {
- int fd;
- const char *name;
+#include "mdoc.h"
+
+#define xfprintf (void)fprintf
+#define xprintf (void)printf
+#define xvfprintf (void)fvprintf
+
+#define MD_LINE_SZ (256) /* Max input line size. */
+
+struct md_parse {
+ int warn; /* Warning flags. */
+#define MD_WARN_SYNTAX (1 << 0) /* Show syntax warnings. */
+#define MD_WARN_COMPAT (1 << 1) /* Show compat warnings. */
+#define MD_WARN_ALL (0x03) /* Show all warnings. */
+#define MD_WARN_ERR (1 << 2) /* Make warnings->errors. */
+ int dbg; /* Debug level. */
+ struct mdoc *mdoc; /* Active parser. */
+ char *buf; /* Input buffer. */
+ u_long bufsz; /* Input buffer size. */
+ char *name; /* Input file name. */
+ int fd; /* Input file desc. */
};
-struct md_buf {
- struct md_file *file;
- char *buf;
- size_t bufsz;
- size_t line;
-};
+extern char *__progname;
-struct md_mbuf {
- struct md_buf *buf;
- size_t pos;
-};
-
-static void usage(void);
+static void usage(void);
-static int md_begin(const char *, const char *);
-static int md_begin_io(const char *, const char *);
-static int md_begin_bufs(struct md_file *, struct md_file *);
-static int md_run(struct md_buf *, struct md_buf *);
-static int md_line(struct md_mbuf *, const struct md_buf *,
- const char *, size_t);
+static int parse_begin(struct md_parse *);
+static int parse_leave(struct md_parse *, int);
+static int io_begin(struct md_parse *);
+static int io_leave(struct md_parse *, int);
+static int buf_begin(struct md_parse *);
+static int buf_leave(struct md_parse *, int);
-static ssize_t md_buf_fill(struct md_buf *);
-static int md_buf_flush(struct md_mbuf *);
-
-static int md_buf_putchar(struct md_mbuf *, char);
-static int md_buf_puts(struct md_mbuf *,
- const char *, size_t);
+static void msg_msg(void *, int, int, const char *);
+static int msg_err(void *, int, int, const char *);
+static int msg_warn(void *, int, int,
+ enum mdoc_warn, const char *);
+#ifdef __linux__
+extern int getsubopt(char **, char *const *, char **);
+#endif
int
main(int argc, char *argv[])
{
int c;
- char *out, *in;
+ struct md_parse parser;
+ char *opts, *v;
+#define ALL 0
+#define COMPAT 1
+#define SYNTAX 2
+#define ERROR 3
+ char *toks[] = { "all", "compat", "syntax",
+ "error", NULL };
extern char *optarg;
extern int optind;
- out = NULL;
-
- while (-1 != (c = getopt(argc, argv, "o:")))
+ (void)memset(&parser, 0, sizeof(struct md_parse));
+
+ while (-1 != (c = getopt(argc, argv, "vW:")))
switch (c) {
- case ('o'):
- out = optarg;
+ case ('v'):
+ parser.dbg++;
+ break;
+ case ('W'):
+ opts = optarg;
+ while (*opts)
+ switch (getsubopt(&opts, toks, &v)) {
+ case (ALL):
+ parser.warn |= MD_WARN_ALL;
+ break;
+ case (COMPAT):
+ parser.warn |= MD_WARN_COMPAT;
+ break;
+ case (SYNTAX):
+ parser.warn |= MD_WARN_SYNTAX;
+ break;
+ case (ERROR):
+ parser.warn |= MD_WARN_ERR;
+ break;
+ default:
+ usage();
+ return(1);
+ }
break;
default:
usage();
}
argv += optind;
- if (1 != (argc -= optind)) {
- usage();
- return(1);
- }
+ argc -= optind;
- argc--;
- in = *argv++;
+ parser.name = "-";
+ if (1 == argc)
+ parser.name = *argv++;
- return(md_begin(out, in));
+ if ( ! io_begin(&parser))
+ return(EXIT_FAILURE);
+
+ return(EXIT_SUCCESS);
}
static int
-md_begin(const char *out, const char *in)
+io_leave(struct md_parse *p, int code)
{
- char buf[MAXPATHLEN];
-
- assert(in);
- if (out)
- return(md_begin_io(out, in));
- if (strlcpy(buf, in, MAXPATHLEN) >= MAXPATHLEN)
- warnx("output filename too long");
- else if (strlcat(buf, ".html", MAXPATHLEN) >= MAXPATHLEN)
- warnx("output filename too long");
- else
- return(md_begin_io(buf, in));
+ if (-1 == p->fd || STDIN_FILENO == p->fd)
+ return(code);
- return(1);
+ if (-1 == close(p->fd)) {
+ warn("%s", p->name);
+ code = 0;
+ }
+ return(code);
}
static int
-md_begin_io(const char *out, const char *in)
+io_begin(struct md_parse *p)
{
- int c;
- struct md_file fin, fout;
- assert(out);
- assert(in);
+ p->fd = STDIN_FILENO;
+ if (0 != strncmp(p->name, "-", 1))
+ if (-1 == (p->fd = open(p->name, O_RDONLY, 0))) {
+ warn("%s", p->name);
+ return(io_leave(p, 0));
+ }
+
+ return(io_leave(p, buf_begin(p)));
+}
- /* TODO: accept "-" as both input and output. */
- fin.name = in;
+static int
+buf_leave(struct md_parse *p, int code)
+{
- if (-1 == (fin.fd = open(fin.name, O_RDONLY, 0))) {
- warn("%s", fin.name);
- return(1);
- }
+ if (p->buf)
+ free(p->buf);
+ return(code);
+}
- fout.name = out;
- fout.fd = open(fout.name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
- if (-1 == fout.fd) {
- warn("%s", fout.name);
- if (-1 == close(fin.fd))
- warn("%s", fin.name);
+static int
+buf_begin(struct md_parse *p)
+{
+ struct stat st;
+
+ if (-1 == fstat(p->fd, &st)) {
+ warn("%s", p->name);
return(1);
- }
+ }
- c = md_begin_bufs(&fout, &fin);
+ p->bufsz = MAX(st.st_blksize, BUFSIZ);
- if (-1 == close(fin.fd)) {
- warn("%s", in);
- c = 1;
- }
- if (-1 == close(fout.fd)) {
- warn("%s", out);
- c = 1;
+ if (NULL == (p->buf = malloc(p->bufsz))) {
+ warn("malloc");
+ return(buf_leave(p, 0));
}
- return(c);
+ return(buf_leave(p, parse_begin(p)));
}
-static int
-md_begin_bufs(struct md_file *out, struct md_file *in)
+/* TODO: remove this to a print-tree output filter. */
+static void
+print_node(const struct mdoc_node *n, int indent)
{
- struct stat stin, stout;
- struct md_buf inbuf, outbuf;
- int c;
-
- assert(in);
- assert(out);
-
- if (-1 == fstat(in->fd, &stin)) {
- warn("fstat: %s", in->name);
- return(1);
- } else if (-1 == fstat(out->fd, &stout)) {
- warn("fstat: %s", out->name);
- return(1);
+ const char *p, *t;
+ int i, j;
+ size_t argc, sz;
+ char **params;
+ struct mdoc_arg *argv;
+
+ argv = NULL;
+ argc = sz = 0;
+ params = NULL;
+
+ t = mdoc_type2a(n->type);
+
+ switch (n->type) {
+ case (MDOC_TEXT):
+ p = n->data.text.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];
+ argv = n->data.elem.argv;
+ argc = n->data.elem.argc;
+ break;
+ case (MDOC_BLOCK):
+ p = mdoc_macronames[n->tok];
+ argv = n->data.block.argv;
+ argc = n->data.block.argc;
+ break;
+ case (MDOC_ROOT):
+ p = "root";
+ break;
+ default:
+ abort();
+ /* NOTREACHED */
}
- inbuf.file = in;
- inbuf.line = 1;
- /*inbuf.bufsz = MAX(stin.st_blksize, BUFSIZ);*/
- inbuf.bufsz = 256;
-
- outbuf.file = out;
- outbuf.line = 1;
- /*outbuf.bufsz = MAX(stout.st_blksize, BUFSIZ);*/
- outbuf.bufsz = 256;
-
- if (NULL == (inbuf.buf = malloc(inbuf.bufsz))) {
- warn("malloc");
- return(1);
- } else if (NULL == (outbuf.buf = malloc(outbuf.bufsz))) {
- warn("malloc");
- free(inbuf.buf);
- return(1);
+ 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(" ]");
}
- c = md_run(&outbuf, &inbuf);
+ for (i = 0; i < (int)sz; i++)
+ xprintf(" [%s]", params[i]);
- free(inbuf.buf);
- free(outbuf.buf);
+ xprintf(" %d:%d\n", n->line, n->pos);
- return(c);
+ if (n->child)
+ print_node(n->child, indent + 1);
+ if (n->next)
+ print_node(n->next, indent);
}
-static ssize_t
-md_buf_fill(struct md_buf *in)
+static int
+parse_leave(struct md_parse *p, int code)
{
- ssize_t ssz;
+ const struct mdoc_node *n;
- assert(in);
- assert(in->file);
- assert(in->buf);
- assert(in->bufsz > 0);
- assert(in->file->name);
+ if (NULL == p->mdoc)
+ return(code);
- if (-1 == (ssz = read(in->file->fd, in->buf, in->bufsz)))
- warn("%s", in->file->name);
- else
- (void)printf("%s: filled %zd bytes\n",
- in->file->name, ssz);
+ if ( ! mdoc_endparse(p->mdoc))
+ code = 0;
+ if ((n = mdoc_result(p->mdoc)))
+ print_node(n, 0);
- return(ssz);
+ mdoc_free(p->mdoc);
+
+ return(code);
}
static int
-md_run(struct md_buf *out, struct md_buf *in)
+parse_begin(struct md_parse *p)
{
- struct md_mbuf mbuf;
ssize_t sz, i;
- char line[BUFSIZ];
size_t pos;
-
- assert(in);
- assert(out);
-
- mbuf.buf = out;
- mbuf.pos = 0;
-
- /* LINTED */
- for (pos = 0; ; ) {
- if (-1 == (sz = md_buf_fill(in)))
- return(1);
- else if (0 == sz)
+ char line[MD_LINE_SZ];
+ struct mdoc_cb cb;
+ int lnn;
+
+ cb.mdoc_err = msg_err;
+ cb.mdoc_warn = msg_warn;
+ cb.mdoc_msg = msg_msg;
+
+ if (NULL == (p->mdoc = mdoc_alloc(p, &cb)))
+ return(parse_leave(p, 0));
+
+ for (lnn = 1, pos = 0; ; ) {
+ if (-1 == (sz = read(p->fd, p->buf, p->bufsz))) {
+ warn("%s", p->name);
+ return(parse_leave(p, 0));
+ } else if (0 == sz)
break;
for (i = 0; i < sz; i++) {
- if ('\n' == in->buf[i]) {
- if (md_line(&mbuf, in, line, pos))
- return(1);
- in->line++;
- pos = 0;
- continue;
- }
-
- if (pos < BUFSIZ) {
- /* LINTED */
- line[pos++] = in->buf[i];
- continue;
+ if ('\n' != p->buf[i]) {
+ if (pos < sizeof(line)) {
+ line[(int)pos++] = p->buf[(int)i];
+ continue;
+ }
+ warnx("%s: line %d too long",
+ p->name, lnn);
+ return(parse_leave(p, 0));
}
+
+ line[(int)pos] = 0;
+ if ( ! mdoc_parseln(p->mdoc, lnn, line))
+ return(parse_leave(p, 0));
- warnx("%s: line %zu too long",
- in->file->name, in->line);
- return(1);
+ lnn++;
+ pos = 0;
}
}
- if (0 != pos && md_line(&mbuf, in, line, pos))
- return(1);
-
- return(md_buf_flush(&mbuf) ? 0 : 1);
+ return(parse_leave(p, 1));
}
static int
-md_buf_flush(struct md_mbuf *buf)
+msg_err(void *arg, int line, int col, const char *msg)
{
- ssize_t sz;
-
- assert(buf);
- assert(buf->buf);
- assert(buf->buf->file);
- assert(buf->buf->buf);
- assert(buf->buf->file->name);
+ struct md_parse *p;
- (void)printf("%s: flushing %zu bytes\n",
- buf->buf->file->name, buf->pos);
-
- if (0 == buf->pos)
- return(1);
+ p = (struct md_parse *)arg;
- sz = write(buf->buf->file->fd, buf->buf->buf, buf->pos);
-
- if (-1 == sz) {
- warn("%s", buf->buf->file->name);
- return(0);
- } else if ((size_t)sz != buf->pos) {
- warnx("%s: short write", buf->buf->file->name);
- return(0);
- }
-
- buf->pos = 0;
- return(1);
-}
-
-
-static int
-md_buf_putchar(struct md_mbuf *buf, char c)
-{
- return(md_buf_puts(buf, &c, 1));
+ xfprintf(stderr, "%s:%d: error: %s (column %d)\n",
+ p->name, line, msg, col);
+ return(0);
}
-static int
-md_buf_puts(struct md_mbuf *buf, const char *p, size_t sz)
+static void
+msg_msg(void *arg, int line, int col, const char *msg)
{
- size_t ssz;
-
- assert(p);
- assert(buf);
- assert(buf->buf);
+ struct md_parse *p;
- while (buf->pos + sz > buf->buf->bufsz) {
- ssz = buf->buf->bufsz - buf->pos;
- (void)memcpy(buf->buf->buf + buf->pos, p, ssz);
- p += ssz;
- sz -= ssz;
- buf->pos += ssz;
+ p = (struct md_parse *)arg;
- if ( ! md_buf_flush(buf))
- return(0);
- }
+ if (0 == p->dbg)
+ return;
- (void)memcpy(buf->buf->buf + buf->pos, p, sz);
- buf->pos += sz;
- return(1);
+ xfprintf(stderr, "%s:%d: debug: %s (column %d)\n",
+ p->name, line, msg, col);
}
static int
-md_line(struct md_mbuf *out, const struct md_buf *in,
- const char *buf, size_t sz)
+msg_warn(void *arg, int line, int col,
+ enum mdoc_warn type, const char *msg)
{
+ struct md_parse *p;
- assert(buf);
- assert(out);
- assert(in);
+ p = (struct md_parse *)arg;
- if ( ! md_buf_puts(out, buf, sz))
+ switch (type) {
+ case (WARN_COMPAT):
+ if (p->warn & MD_WARN_COMPAT)
+ break;
+ return(1);
+ case (WARN_SYNTAX):
+ if (p->warn & MD_WARN_SYNTAX)
+ break;
return(1);
- if ( ! md_buf_putchar(out, '\n'))
+ }
+
+ xfprintf(stderr, "%s:%d: warning: %s (column %d)\n",
+ p->name, line, msg, col);
+
+ if ( ! (p->warn & MD_WARN_ERR))
return(1);
+ xfprintf(stderr, "%s: considering warnings as errors\n",
+ __progname);
return(0);
}
static void
usage(void)
{
- extern char *__progname;
- (void)printf("usage: %s [-o outfile] infile\n", __progname);
+ xfprintf(stderr, "usage: %s [-v] [-Wwarn...] [infile]\n",
+ __progname);
}
+