version 1.4, 2009/02/23 15:38:20 |
version 1.14, 2009/03/16 23:37:28 |
|
|
/* $Id$ */ |
/* $Id$ */ |
/* |
/* |
* Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se> |
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org> |
* |
* |
* Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
* purpose with or without fee is hereby granted, provided that the |
* purpose with or without fee is hereby granted, provided that the |
|
|
|
|
#include "mmain.h" |
#include "mmain.h" |
|
|
#define MD_LINE_SZ (256) /* Max input line size. */ |
#define MD_LINE_SZ (256) /* Input line step-size. */ |
|
|
struct mmain { |
struct mmain { |
int warn; /* Warning flags. */ |
int warn; /* Warning flags. */ |
|
|
int dbg; /* Debug level. */ |
int dbg; /* Debug level. */ |
struct mdoc *mdoc; /* Active parser. */ |
struct mdoc *mdoc; /* Active parser. */ |
char *buf; /* Input buffer. */ |
char *buf; /* Input buffer. */ |
u_long bufsz; /* Input buffer size. */ |
size_t bufsz; /* Input buffer size. */ |
char *in; /* Input file name. */ |
const char *in; /* Input file name. */ |
int fdin; /* Input file desc. */ |
int fdin; /* Input file desc. */ |
|
int pflags; /* Parse flags. */ |
}; |
}; |
|
|
extern char *__progname; |
extern char *__progname; |
|
|
static int getsopts(struct mmain *, char *); |
static void usage(const char *, const char *); |
|
static int optswarn(struct mmain *, char *); |
|
static int optsopt(struct mmain *, char *); |
static int parse(struct mmain *); |
static int parse(struct mmain *); |
static void msg_msg(void *, int, int, const char *); |
static void msg_msg(void *, int, int, const char *); |
static int msg_err(void *, int, int, const char *); |
static int msg_err(void *, int, int, const char *); |
static int msg_warn(void *, int, int, |
static int msg_warn(void *, int, int, |
enum mdoc_warn, const char *); |
enum mdoc_warn, const char *); |
|
|
#ifdef __linux__ |
|
extern int getsubopt(char **, char *const *, char **); |
|
extern size_t strlcpy(char *, const char *, size_t); |
|
extern size_t strlcat(char *, const char *, size_t); |
|
#endif |
|
|
|
|
|
/* |
/* |
* Print our and our caller's usage message. |
* Print our and our caller's usage message. |
*/ |
*/ |
void |
void |
mmain_usage(const char *help) |
usage(const char *help, const char *args) |
{ |
{ |
|
|
warnx("usage: %s %s%s[-v] [-Wwarn...] [infile]", __progname, |
warnx("usage: %s %s%s[-v] [-foption...] [-Wwarn...]%s%s", |
help ? help : "", help ? " " : ""); |
__progname, |
|
help ? help : "", help ? " " : "", |
|
args ? " " : "", args ? args : ""); |
} |
} |
|
|
|
|
Line 85 mmain_alloc(void) |
|
Line 83 mmain_alloc(void) |
|
if (NULL == (p = calloc(1, sizeof(struct mmain)))) |
if (NULL == (p = calloc(1, sizeof(struct mmain)))) |
err(1, "malloc"); |
err(1, "malloc"); |
|
|
p->in = "-"; |
|
p->fdin = STDIN_FILENO; |
|
|
|
return(p); |
return(p); |
} |
} |
|
|
Line 100 mmain_alloc(void) |
|
Line 95 mmain_alloc(void) |
|
*/ |
*/ |
int |
int |
mmain_getopt(struct mmain *p, int argc, char *argv[], |
mmain_getopt(struct mmain *p, int argc, char *argv[], |
const char *help, const char *u, void *arg, |
const char *help, const char *args, |
int (*getopt_cb)(void *, int, const char *)) |
const char *u, void *arg, |
|
int (*getopt_cb)(void *, int, char *)) |
{ |
{ |
int c; |
int c; |
char opts[32]; /* XXX */ |
char opts[32]; /* XXX */ |
Line 109 mmain_getopt(struct mmain *p, int argc, char *argv[], |
|
Line 105 mmain_getopt(struct mmain *p, int argc, char *argv[], |
|
|
|
extern int optind; |
extern int optind; |
|
|
sz = strlcpy(opts, "vW:", 32); |
sz = strlcpy(opts, "VvW:f:", 32); |
assert(sz < 32); |
assert(sz < 32); |
|
|
if (u) { |
if (u) { |
Line 122 mmain_getopt(struct mmain *p, int argc, char *argv[], |
|
Line 118 mmain_getopt(struct mmain *p, int argc, char *argv[], |
|
/* LINTED */ |
/* LINTED */ |
while (-1 != (c = getopt(argc, argv, opts))) |
while (-1 != (c = getopt(argc, argv, opts))) |
switch (c) { |
switch (c) { |
|
case ('f'): |
|
if ( ! optsopt(p, optarg)) |
|
mmain_exit(p, 1); |
|
break; |
case ('v'): |
case ('v'): |
p->dbg++; |
p->dbg++; |
break; |
break; |
|
case ('V'): |
|
(void)printf("%s %s\n", __progname, VERSION); |
|
mmain_exit(p, 0); |
|
/* NOTREACHED */ |
case ('W'): |
case ('W'): |
if ( ! getsopts(p, optarg)) |
if ( ! optswarn(p, optarg)) |
return(0); |
mmain_exit(p, 1); |
break; |
break; |
case ('?'): |
case ('?'): |
mmain_usage(help); |
usage(help, args); |
return(0); |
mmain_exit(p, 1); |
|
/* NOTREACHED */ |
default: |
default: |
assert(getopt_cb); |
assert(getopt_cb); |
if ((*getopt_cb)(arg, c, optarg)) |
if ((*getopt_cb)(arg, c, optarg)) |
break; |
break; |
return(0); |
mmain_exit(p, 1); |
|
/* NOTREACHED */ |
} |
} |
|
|
argv += optind; |
return(optind); |
if ((argc -= optind) > 0) |
} |
p->in = *argv++; |
|
|
|
return(1); |
|
|
void |
|
mmain_reset(struct mmain *p) |
|
{ |
|
|
|
if (p->mdoc) |
|
mdoc_free(p->mdoc); |
|
p->mdoc = NULL; |
} |
} |
|
|
|
|
dead_pre void |
void |
mmain_exit(struct mmain *p, int code) |
mmain_free(struct mmain *p) |
{ |
{ |
|
|
if (p->mdoc) |
if (p->mdoc) |
mdoc_free(p->mdoc); |
mdoc_free(p->mdoc); |
free(p); |
free(p); |
|
} |
|
|
|
|
|
__dead void |
|
mmain_exit(struct mmain *p, int code) |
|
{ |
|
|
|
mmain_free(p); |
exit(code); |
exit(code); |
} |
} |
|
|
|
|
struct mdoc * |
void |
mmain_mdoc(struct mmain *p) |
mmain_prepare(struct mmain *p, const char *in) |
{ |
{ |
struct stat st; |
struct stat st; |
int c; |
|
struct mdoc_cb cb; |
|
|
|
cb.mdoc_err = msg_err; |
p->in = in; |
cb.mdoc_warn = msg_warn; |
p->fdin = STDIN_FILENO; |
cb.mdoc_msg = msg_msg; |
|
|
|
if (0 != strcmp(p->in, "-")) |
if (0 != strcmp(p->in, "-")) |
if (-1 == (p->fdin = open(p->in, O_RDONLY, 0))) { |
if (-1 == (p->fdin = open(p->in, O_RDONLY, 0))) { |
warn("%s", p->in); |
warn("%s", p->in); |
return(0); |
mmain_exit(p, 1); |
} |
} |
|
|
/* Allocate a buffer to be BUFSIZ/block size. */ |
/* Allocate a buffer to be BUFSIZ/block size. */ |
Line 181 mmain_mdoc(struct mmain *p) |
|
Line 198 mmain_mdoc(struct mmain *p) |
|
warn("%s", p->in); |
warn("%s", p->in); |
p->bufsz = BUFSIZ; |
p->bufsz = BUFSIZ; |
} else |
} else |
p->bufsz = MAX(st.st_blksize, BUFSIZ); |
p->bufsz = (size_t)MAX(st.st_blksize, BUFSIZ); |
|
|
p->buf = malloc(p->bufsz); |
p->buf = malloc(p->bufsz); |
if (NULL == p->buf) |
if (NULL == p->buf) |
err(1, "malloc"); |
err(1, "malloc"); |
|
} |
|
|
|
|
|
struct mdoc * |
|
mmain_process(struct mmain *p) |
|
{ |
|
int c; |
|
struct mdoc_cb cb; |
|
|
|
/* XXX - in mmain_alloc.*/ |
|
cb.mdoc_err = msg_err; |
|
cb.mdoc_warn = msg_warn; |
|
cb.mdoc_msg = msg_msg; |
|
|
/* Allocate the parser. */ |
/* Allocate the parser. */ |
|
|
p->mdoc = mdoc_alloc(p, &cb); |
p->mdoc = mdoc_alloc(p, p->pflags, &cb); |
|
|
/* Parse the input file. */ |
/* Parse the input file. */ |
|
|
Line 204 mmain_mdoc(struct mmain *p) |
|
Line 234 mmain_mdoc(struct mmain *p) |
|
} |
} |
|
|
|
|
|
struct mdoc * |
|
mmain_mdoc(struct mmain *p, const char *in) |
|
{ |
|
|
|
mmain_prepare(p, in); |
|
return(mmain_process(p)); |
|
} |
|
|
|
|
static int |
static int |
getsopts(struct mmain *p, char *arg) |
optsopt(struct mmain *p, char *arg) |
{ |
{ |
char *v; |
char *v; |
|
char *toks[] = { "ign-scope", "ign-escape", |
|
"ign-macro", NULL }; |
|
|
|
while (*arg) |
|
switch (getsubopt(&arg, toks, &v)) { |
|
case (0): |
|
p->pflags |= MDOC_IGN_SCOPE; |
|
break; |
|
case (1): |
|
p->pflags |= MDOC_IGN_ESCAPE; |
|
break; |
|
case (2): |
|
p->pflags |= MDOC_IGN_MACRO; |
|
break; |
|
default: |
|
warnx("unknown -f argument"); |
|
return(0); |
|
} |
|
|
|
return(1); |
|
} |
|
|
|
|
|
static int |
|
optswarn(struct mmain *p, char *arg) |
|
{ |
|
char *v; |
char *toks[] = { "all", "compat", |
char *toks[] = { "all", "compat", |
"syntax", "error", NULL }; |
"syntax", "error", NULL }; |
|
|
Line 226 getsopts(struct mmain *p, char *arg) |
|
Line 292 getsopts(struct mmain *p, char *arg) |
|
p->warn |= MD_WARN_ERR; |
p->warn |= MD_WARN_ERR; |
break; |
break; |
default: |
default: |
|
warnx("unknown -W argument"); |
return(0); |
return(0); |
} |
} |
|
|
Line 236 getsopts(struct mmain *p, char *arg) |
|
Line 303 getsopts(struct mmain *p, char *arg) |
|
static int |
static int |
parse(struct mmain *p) |
parse(struct mmain *p) |
{ |
{ |
ssize_t sz, i; |
ssize_t sz; |
size_t pos; |
int j, i, pos, len, lnn; |
char line[MD_LINE_SZ]; |
char *ln; |
int lnn; |
|
|
|
/* |
for (ln = NULL, lnn = 1, len = pos = 0; ; ) { |
* This is a little more complicated than fgets. TODO: have |
|
* some benchmarks that show it's faster (note that I want to |
|
* check many, many manuals simultaneously, so speed is |
|
* important). Fill a buffer (sized to the block size) with a |
|
* single read, then parse \n-terminated lines into a line |
|
* buffer, which is passed to the parser. Hard-code the line |
|
* buffer to a particular size -- a reasonable assumption. |
|
*/ |
|
|
|
for (lnn = 1, pos = 0; ; ) { |
|
if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) { |
if (-1 == (sz = read(p->fdin, p->buf, p->bufsz))) { |
warn("%s", p->in); |
warn("%s", p->in); |
return(0); |
return(0); |
} else if (0 == sz) |
} else if (0 == sz) |
break; |
break; |
|
|
for (i = 0; i < sz; i++) { |
for (i = 0; i < (int)sz; i++) { |
|
if (pos >= len) { |
|
len += MD_LINE_SZ; |
|
ln = realloc(ln, (size_t)len); |
|
if (NULL == ln) |
|
err(1, "realloc"); |
|
} |
|
|
if ('\n' != p->buf[i]) { |
if ('\n' != p->buf[i]) { |
if (pos < sizeof(line)) { |
ln[pos++] = p->buf[i]; |
line[(int)pos++] = p->buf[(int)i]; |
continue; |
|
} |
|
|
|
/* Check for 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; |
continue; |
} |
} |
warnx("%s: line %d too long", p->in, lnn); |
|
return(0); |
|
} |
} |
|
|
line[(int)pos] = 0; |
|
if ( ! mdoc_parseln(p->mdoc, lnn, line)) |
|
return(0); |
|
|
|
|
ln[pos] = 0; |
|
if ( ! mdoc_parseln(p->mdoc, lnn, ln)) { |
|
free(ln); |
|
return(0); |
|
} |
lnn++; |
lnn++; |
pos = 0; |
pos = 0; |
} |
} |
} |
} |
|
|
|
if (ln) |
|
free(ln); |
|
if (pos > 0) |
|
warnx("%s: file not eof-terminated", p->in); |
return(mdoc_endparse(p->mdoc)); |
return(mdoc_endparse(p->mdoc)); |
} |
} |
|
|