Return to main.c CVS log | Up to [cvsweb.bsd.lv] / mandoc |
version 1.21, 2009/04/02 16:42:35 | version 1.65, 2010/05/15 04:46:10 | ||
---|---|---|---|
|
|
||
/* $Id$ */ | /* $Id$ */ | ||
/* | /* | ||
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org> | * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> | ||
* | * | ||
* 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 above | ||
* above copyright notice and this permission notice appear in all | * copyright notice and this permission notice appear in all copies. | ||
* copies. | |||
* | * | ||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
* PERFORMANCE OF THIS SOFTWARE. | |||
*/ | */ | ||
#ifdef HAVE_CONFIG_H | |||
#include "config.h" | |||
#endif | |||
#include <sys/stat.h> | #include <sys/stat.h> | ||
#include <assert.h> | #include <assert.h> | ||
#include <err.h> | |||
#include <fcntl.h> | #include <fcntl.h> | ||
#include <stdio.h> | #include <stdio.h> | ||
#include <stdint.h> | |||
#include <stdlib.h> | #include <stdlib.h> | ||
#include <string.h> | #include <string.h> | ||
#include <unistd.h> | #include <unistd.h> | ||
#include "mdoc.h" | #include "mdoc.h" | ||
#include "man.h" | #include "man.h" | ||
#include "main.h" | |||
/* Account for FreeBSD and Linux in our declarations. */ | #define UNCONST(a) ((void *)(uintptr_t)(const void *)(a)) | ||
#ifdef __linux__ | /* FIXME: Intel's compiler? LLVM? pcc? */ | ||
extern int getsubopt(char **, char * const *, char **); | |||
# ifndef __dead | #if !defined(__GNUC__) || (__GNUC__ < 2) | ||
# define __dead __attribute__((__noreturn__)) | # if !defined(lint) | ||
# define __attribute__(x) | |||
# endif | # endif | ||
#elif defined(__dead2) | #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */ | ||
# ifndef __dead | |||
# define __dead __dead2 | |||
# endif | |||
#endif | |||
typedef void (*out_mdoc)(void *, const struct mdoc *); | |||
typedef void (*out_man)(void *, const struct man *); | |||
typedef void (*out_free)(void *); | |||
struct buf { | struct buf { | ||
char *buf; | char *buf; | ||
size_t sz; | size_t sz; | ||
|
|
||
enum outt { | enum outt { | ||
OUTT_ASCII = 0, | OUTT_ASCII = 0, | ||
OUTT_TREE, | OUTT_TREE, | ||
OUTT_HTML, | |||
OUTT_XHTML, | |||
OUTT_LINT | OUTT_LINT | ||
}; | }; | ||
struct curparse { | struct curparse { | ||
const char *file; | const char *file; /* Current parse. */ | ||
int fd; | int fd; /* Current parse. */ | ||
int wflags; | int wflags; | ||
#define WARN_WALL 0x03 /* All-warnings mask. */ | #define WARN_WALL (1 << 0) /* All-warnings mask. */ | ||
#define WARN_WCOMPAT (1 << 0) /* Compatibility warnings. */ | |||
#define WARN_WSYNTAX (1 << 1) /* Syntax warnings. */ | |||
#define WARN_WERR (1 << 2) /* Warnings->errors. */ | #define WARN_WERR (1 << 2) /* Warnings->errors. */ | ||
int fflags; | int fflags; | ||
enum intt inttype; | #define FL_IGN_SCOPE (1 << 0) /* Ignore scope errors. */ | ||
#define FL_NIGN_ESCAPE (1 << 1) /* Don't ignore bad escapes. */ | |||
#define FL_NIGN_MACRO (1 << 2) /* Don't ignore bad macros. */ | |||
#define FL_IGN_ERRORS (1 << 4) /* Ignore failed parse. */ | |||
enum intt inttype; /* Input parsers... */ | |||
struct man *man; | struct man *man; | ||
struct mdoc *mdoc; | struct mdoc *mdoc; | ||
enum outt outtype; /* Output devices... */ | |||
out_mdoc outmdoc; | |||
out_man outman; | |||
out_free outfree; | |||
void *outdata; | |||
char outopts[BUFSIZ]; | |||
}; | }; | ||
#define IGN_SCOPE (1 << 0) /* Ignore scope errors. */ | #define FL_STRICT FL_NIGN_ESCAPE | \ | ||
#define IGN_ESCAPE (1 << 1) /* Ignore bad escapes. */ | FL_NIGN_MACRO | ||
#define IGN_MACRO (1 << 2) /* Ignore unknown macros. */ | |||
#define NO_IGN_MACRO (1 << 3) | |||
typedef int (*out_run)(void *, const struct man *, | |||
const struct mdoc *); | |||
typedef void (*out_free)(void *); | |||
extern char *__progname; | |||
extern void *ascii_alloc(void); | |||
extern int terminal_run(void *, const struct man *, | |||
const struct mdoc *); | |||
extern int tree_run(void *, const struct man *, | |||
const struct mdoc *); | |||
extern void terminal_free(void *); | |||
static int foptions(int *, char *); | static int foptions(int *, char *); | ||
static int toptions(enum outt *, char *); | static int toptions(struct curparse *, char *); | ||
static int moptions(enum intt *, char *); | static int moptions(enum intt *, char *); | ||
static int woptions(int *, char *); | static int woptions(int *, char *); | ||
static int merr(void *, int, int, const char *); | static int merr(void *, int, int, const char *); | ||
static int manwarn(void *, int, int, const char *); | static int mwarn(void *, int, int, const char *); | ||
static int mdocwarn(void *, int, int, | static void ffile(struct buf *, struct buf *, | ||
enum mdoc_warn, const char *); | |||
static int fstdin(struct buf *, struct buf *, | |||
struct curparse *); | |||
static int ffile(struct buf *, struct buf *, | |||
const char *, struct curparse *); | const char *, struct curparse *); | ||
static int fdesc(struct buf *, struct buf *, | static void fdesc(struct buf *, struct buf *, | ||
struct curparse *); | struct curparse *); | ||
static int pset(const char *, size_t, struct curparse *, | static int pset(const char *, int, struct curparse *, | ||
struct man **, struct mdoc **); | struct man **, struct mdoc **); | ||
static struct man *man_init(struct curparse *); | static struct man *man_init(struct curparse *); | ||
static struct mdoc *mdoc_init(struct curparse *); | static struct mdoc *mdoc_init(struct curparse *); | ||
__dead static void version(void); | static void version(void) __attribute__((noreturn)); | ||
__dead static void usage(void); | static void usage(void) __attribute__((noreturn)); | ||
static const char *progname; | |||
static int with_error, with_warning; | |||
int | int | ||
main(int argc, char *argv[]) | main(int argc, char *argv[]) | ||
{ | { | ||
int c, rc; | int c; | ||
void *outdata; | |||
enum outt outtype; | |||
struct buf ln, blk; | struct buf ln, blk; | ||
out_run outrun; | |||
out_free outfree; | |||
struct curparse curp; | struct curparse curp; | ||
outtype = OUTT_ASCII; | progname = strrchr(argv[0], '/'); | ||
if (progname == NULL) | |||
progname = argv[0]; | |||
else | |||
++progname; | |||
bzero(&curp, sizeof(struct curparse)); | memset(&curp, 0, sizeof(struct curparse)); | ||
curp.inttype = INTT_AUTO; | curp.inttype = INTT_AUTO; | ||
curp.outtype = OUTT_ASCII; | |||
/* LINTED */ | /* LINTED */ | ||
while (-1 != (c = getopt(argc, argv, "f:m:VW:T:"))) | while (-1 != (c = getopt(argc, argv, "f:m:O:T:VW:"))) | ||
switch (c) { | switch (c) { | ||
case ('f'): | case ('f'): | ||
if ( ! foptions(&curp.fflags, optarg)) | if ( ! foptions(&curp.fflags, optarg)) | ||
return(0); | return(EXIT_FAILURE); | ||
break; | break; | ||
case ('m'): | case ('m'): | ||
if ( ! moptions(&curp.inttype, optarg)) | if ( ! moptions(&curp.inttype, optarg)) | ||
return(0); | return(EXIT_FAILURE); | ||
break; | break; | ||
case ('O'): | |||
(void)strlcat(curp.outopts, optarg, BUFSIZ); | |||
(void)strlcat(curp.outopts, ",", BUFSIZ); | |||
break; | |||
case ('T'): | case ('T'): | ||
if ( ! toptions(&outtype, optarg)) | if ( ! toptions(&curp, optarg)) | ||
return(0); | return(EXIT_FAILURE); | ||
break; | break; | ||
case ('W'): | case ('W'): | ||
if ( ! woptions(&curp.wflags, optarg)) | if ( ! woptions(&curp.wflags, optarg)) | ||
return(0); | return(EXIT_FAILURE); | ||
break; | break; | ||
case ('V'): | case ('V'): | ||
version(); | version(); | ||
|
|
||
argc -= optind; | argc -= optind; | ||
argv += optind; | argv += optind; | ||
/* | memset(&ln, 0, sizeof(struct buf)); | ||
* Allocate the appropriate front-end. Note that utf8, latin1 | memset(&blk, 0, sizeof(struct buf)); | ||
* (both not yet implemented) and ascii all resolve to the | |||
* terminal front-end with different encodings (see terminal.c). | |||
* Not all frontends have cleanup or alloc routines. | |||
*/ | |||
switch (outtype) { | if (NULL == *argv) { | ||
case (OUTT_TREE): | curp.file = "<stdin>"; | ||
outdata = NULL; | curp.fd = STDIN_FILENO; | ||
outrun = tree_run; | |||
outfree = NULL; | fdesc(&blk, &ln, &curp); | ||
break; | |||
case (OUTT_LINT): | |||
outdata = NULL; | |||
outrun = NULL; | |||
outfree = NULL; | |||
break; | |||
default: | |||
outdata = ascii_alloc(); | |||
outrun = terminal_run; | |||
outfree = terminal_free; | |||
break; | |||
} | } | ||
/* Configure buffers. */ | while (*argv) { | ||
ffile(&blk, &ln, *argv, &curp); | |||
bzero(&ln, sizeof(struct buf)); | if (with_error && !(curp.fflags & FL_IGN_ERRORS)) | ||
bzero(&blk, sizeof(struct buf)); | break; | ||
++argv; | |||
/* | |||
* Main loop around available files. | |||
*/ | |||
if (NULL == *argv) { | |||
rc = 0; | |||
c = fstdin(&blk, &ln, &curp); | |||
if (c && NULL == outrun) | |||
rc = 1; | |||
/*else if (c && outrun && (*outrun)(outdata, curp.man, curp.mdoc)) | |||
rc = 1;*/ | |||
} else { | |||
while (*argv) { | |||
c = ffile(&blk, &ln, *argv, &curp); | |||
if ( ! c) | |||
break; | |||
/*if (outrun && ! (*outrun)(outdata, curp.man, curp.mdoc)) | |||
break;*/ | |||
if (curp.man) | |||
man_reset(curp.man); | |||
if (curp.mdoc && ! mdoc_reset(curp.mdoc)) { | |||
warnx("memory exhausted"); | |||
break; | |||
} | |||
argv++; | |||
} | |||
rc = NULL == *argv; | |||
} | } | ||
if (blk.buf) | if (blk.buf) | ||
free(blk.buf); | free(blk.buf); | ||
if (ln.buf) | if (ln.buf) | ||
free(ln.buf); | free(ln.buf); | ||
if (outfree) | if (curp.outfree) | ||
(*outfree)(outdata); | (*curp.outfree)(curp.outdata); | ||
if (curp.mdoc) | |||
mdoc_free(curp.mdoc); | |||
if (curp.man) | |||
man_free(curp.man); | |||
return(rc ? EXIT_SUCCESS : EXIT_FAILURE); | return((with_warning || with_error) ? EXIT_FAILURE : EXIT_SUCCESS ); | ||
} | } | ||
__dead static void | static void | ||
version(void) | version(void) | ||
{ | { | ||
(void)printf("%s %s\n", __progname, VERSION); | (void)printf("%s %s\n", progname, VERSION); | ||
exit(EXIT_SUCCESS); | exit(EXIT_SUCCESS); | ||
} | } | ||
__dead static void | static void | ||
usage(void) | usage(void) | ||
{ | { | ||
(void)fprintf(stderr, "usage: %s [-V] [-foption...] " | (void)fprintf(stderr, "usage: %s [-V] [-foption] " | ||
"[-mformat] [-Toutput] [-Werr...]\n", | "[-mformat] [-Ooption] [-Toutput] " | ||
__progname); | "[-Werr] [file...]\n", progname); | ||
exit(EXIT_FAILURE); | exit(EXIT_FAILURE); | ||
} | } | ||
|
|
||
man_init(struct curparse *curp) | man_init(struct curparse *curp) | ||
{ | { | ||
int pflags; | int pflags; | ||
struct man *man; | |||
struct man_cb mancb; | struct man_cb mancb; | ||
mancb.man_err = merr; | mancb.man_err = merr; | ||
mancb.man_warn = manwarn; | mancb.man_warn = mwarn; | ||
/* Set command defaults. */ | /* Defaults from mandoc.1. */ | ||
pflags = MAN_IGN_MACRO; | |||
if (curp->fflags & NO_IGN_MACRO) | pflags = MAN_IGN_MACRO | MAN_IGN_ESCAPE; | ||
if (curp->fflags & FL_NIGN_MACRO) | |||
pflags &= ~MAN_IGN_MACRO; | pflags &= ~MAN_IGN_MACRO; | ||
if (curp->fflags & FL_NIGN_ESCAPE) | |||
pflags &= ~MAN_IGN_ESCAPE; | |||
if (NULL == (man = man_alloc(curp, pflags, &mancb))) | return(man_alloc(curp, pflags, &mancb)); | ||
warnx("memory exhausted"); | |||
return(man); | |||
} | } | ||
|
|
||
mdoc_init(struct curparse *curp) | mdoc_init(struct curparse *curp) | ||
{ | { | ||
int pflags; | int pflags; | ||
struct mdoc *mdoc; | |||
struct mdoc_cb mdoccb; | struct mdoc_cb mdoccb; | ||
mdoccb.mdoc_msg = NULL; | |||
mdoccb.mdoc_err = merr; | mdoccb.mdoc_err = merr; | ||
mdoccb.mdoc_warn = mdocwarn; | mdoccb.mdoc_warn = mwarn; | ||
pflags = 0; | /* Defaults from mandoc.1. */ | ||
if (curp->fflags & IGN_SCOPE) | pflags = MDOC_IGN_MACRO | MDOC_IGN_ESCAPE; | ||
if (curp->fflags & FL_IGN_SCOPE) | |||
pflags |= MDOC_IGN_SCOPE; | pflags |= MDOC_IGN_SCOPE; | ||
if (curp->fflags & IGN_ESCAPE) | if (curp->fflags & FL_NIGN_ESCAPE) | ||
pflags |= MDOC_IGN_ESCAPE; | pflags &= ~MDOC_IGN_ESCAPE; | ||
if (curp->fflags & IGN_MACRO) | if (curp->fflags & FL_NIGN_MACRO) | ||
pflags |= MDOC_IGN_MACRO; | pflags &= ~MDOC_IGN_MACRO; | ||
if (NULL == (mdoc = mdoc_alloc(curp, pflags, &mdoccb))) | return(mdoc_alloc(curp, pflags, &mdoccb)); | ||
warnx("memory allocated"); | |||
return(mdoc); | |||
} | } | ||
static int | static void | ||
fstdin(struct buf *blk, struct buf *ln, struct curparse *curp) | |||
{ | |||
curp->file = "<stdin>"; | |||
curp->fd = STDIN_FILENO; | |||
return(fdesc(blk, ln, curp)); | |||
} | |||
static int | |||
ffile(struct buf *blk, struct buf *ln, | ffile(struct buf *blk, struct buf *ln, | ||
const char *file, struct curparse *curp) | const char *file, struct curparse *curp) | ||
{ | { | ||
int c; | |||
curp->file = file; | curp->file = file; | ||
if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) { | if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) { | ||
warn("%s", curp->file); | perror(curp->file); | ||
return(0); | with_error = 1; | ||
return; | |||
} | } | ||
c = fdesc(blk, ln, curp); | fdesc(blk, ln, curp); | ||
if (-1 == close(curp->fd)) | if (-1 == close(curp->fd)) | ||
warn("%s", curp->file); | perror(curp->file); | ||
return(c); | |||
} | } | ||
static int | static void | ||
fdesc(struct buf *blk, struct buf *ln, struct curparse *curp) | fdesc(struct buf *blk, struct buf *ln, struct curparse *curp) | ||
{ | { | ||
size_t sz; | size_t sz; | ||
ssize_t ssz; | ssize_t ssz; | ||
struct stat st; | struct stat st; | ||
int j, i, pos, lnn; | int j, i, pos, lnn, comment; | ||
struct man *man; | struct man *man; | ||
struct mdoc *mdoc; | struct mdoc *mdoc; | ||
|
|
||
* growable, hence passed in by ptr-ptr. | * growable, hence passed in by ptr-ptr. | ||
*/ | */ | ||
if (-1 == fstat(curp->fd, &st)) | if (-1 == fstat(curp->fd, &st)) { | ||
warnx("%s", curp->file); | perror(curp->file); | ||
else if ((size_t)st.st_blksize > sz) | with_error = 1; | ||
return; | |||
} | |||
if ((size_t)st.st_blksize > sz) | |||
sz = st.st_blksize; | sz = st.st_blksize; | ||
if (sz > blk->sz) { | if (sz > blk->sz) { | ||
blk->buf = realloc(blk->buf, sz); | void *buf = realloc(blk->buf, sz); | ||
if (NULL == blk->buf) { | |||
warn("realloc"); | if (NULL == buf) { | ||
return(0); | perror(NULL); | ||
with_error = 1; | |||
return; | |||
} | } | ||
blk->buf = buf; | |||
blk->sz = sz; | blk->sz = sz; | ||
} | } | ||
/* Fill buf with file blocksize. */ | /* Fill buf with file blocksize. */ | ||
for (lnn = 0, pos = 0; ; ) { | for (lnn = pos = comment = 0; ; ) { | ||
if (-1 == (ssz = read(curp->fd, blk->buf, sz))) { | if (-1 == (ssz = read(curp->fd, blk->buf, sz))) { | ||
warn("%s", curp->file); | perror(curp->file); | ||
return(0); | goto bailout; | ||
} else if (0 == ssz) | } else if (0 == ssz) | ||
break; | break; | ||
|
|
||
ln->sz += 256; /* Step-size. */ | ln->sz += 256; /* Step-size. */ | ||
ln->buf = realloc(ln->buf, ln->sz); | ln->buf = realloc(ln->buf, ln->sz); | ||
if (NULL == ln->buf) { | if (NULL == ln->buf) { | ||
warn("realloc"); | perror(NULL); | ||
return(0); | goto bailout; | ||
} | } | ||
} | } | ||
if ('\n' != blk->buf[i]) { | if ('\n' != blk->buf[i]) { | ||
if (comment) | |||
continue; | |||
ln->buf[pos++] = blk->buf[i]; | ln->buf[pos++] = blk->buf[i]; | ||
/* Handle in-line `\"' comments. */ | |||
if (1 == pos || '\"' != ln->buf[pos - 1]) | |||
continue; | |||
for (j = pos - 2; j >= 0; j--) | |||
if ('\\' != ln->buf[j]) | |||
break; | |||
if ( ! ((pos - 2 - j) % 2)) | |||
continue; | |||
comment = 1; | |||
pos -= 2; | |||
for (; pos > 0; --pos) { | |||
if (ln->buf[pos - 1] != ' ') | |||
break; | |||
if (pos > 2 && ln->buf[pos - 2] == '\\') | |||
break; | |||
} | |||
continue; | continue; | ||
} | } | ||
/* Check for CPP-escaped newline. */ | /* Handle escaped `\\n' newlines. */ | ||
if (pos > 0 && '\\' == ln->buf[pos - 1]) { | if (pos > 0 && 0 == comment && | ||
'\\' == ln->buf[pos - 1]) { | |||
for (j = pos - 1; j >= 0; j--) | for (j = pos - 1; j >= 0; j--) | ||
if ('\\' != ln->buf[j]) | if ('\\' != ln->buf[j]) | ||
break; | break; | ||
if ( ! ((pos - j) % 2)) { | if ( ! ((pos - j) % 2)) { | ||
pos--; | pos--; | ||
lnn++; | lnn++; | ||
|
|
||
ln->buf[pos] = 0; | ln->buf[pos] = 0; | ||
lnn++; | lnn++; | ||
/* | |||
* If no manual parser has been assigned, then | |||
* try to assign one in pset(), which may do | |||
* nothing at all. After this, parse the manual | |||
* line accordingly. | |||
*/ | |||
/* If unset, assign parser in pset(). */ | |||
if ( ! (man || mdoc) && ! pset(ln->buf, | if ( ! (man || mdoc) && ! pset(ln->buf, | ||
pos, curp, &man, &mdoc)) | pos, curp, &man, &mdoc)) | ||
return(0); | goto bailout; | ||
pos = 0; | pos = comment = 0; | ||
/* Pass down into parsers. */ | |||
if (man && ! man_parseln(man, lnn, ln->buf)) | if (man && ! man_parseln(man, lnn, ln->buf)) | ||
return(0); | goto bailout; | ||
if (mdoc && ! mdoc_parseln(mdoc, lnn, ln->buf)) | if (mdoc && ! mdoc_parseln(mdoc, lnn, ln->buf)) | ||
return(0); | goto bailout; | ||
} | } | ||
} | } | ||
/* Note that a parser may not have been assigned, yet. */ | /* NOTE a parser may not have been assigned, yet. */ | ||
if (mdoc) | if ( ! (man || mdoc)) { | ||
return(mdoc_endparse(mdoc)); | fprintf(stderr, "%s: Not a manual\n", curp->file); | ||
if (man) | goto bailout; | ||
return(man_endparse(man)); | } | ||
warnx("%s: not a manual", curp->file); | if (mdoc && ! mdoc_endparse(mdoc)) | ||
return(0); | goto bailout; | ||
if (man && ! man_endparse(man)) | |||
goto bailout; | |||
/* If unset, allocate output dev now (if applicable). */ | |||
if ( ! (curp->outman && curp->outmdoc)) { | |||
switch (curp->outtype) { | |||
case (OUTT_XHTML): | |||
curp->outdata = xhtml_alloc(curp->outopts); | |||
curp->outman = html_man; | |||
curp->outmdoc = html_mdoc; | |||
curp->outfree = html_free; | |||
break; | |||
case (OUTT_HTML): | |||
curp->outdata = html_alloc(curp->outopts); | |||
curp->outman = html_man; | |||
curp->outmdoc = html_mdoc; | |||
curp->outfree = html_free; | |||
break; | |||
case (OUTT_TREE): | |||
curp->outman = tree_man; | |||
curp->outmdoc = tree_mdoc; | |||
break; | |||
case (OUTT_LINT): | |||
break; | |||
default: | |||
curp->outdata = ascii_alloc(); | |||
curp->outman = terminal_man; | |||
curp->outmdoc = terminal_mdoc; | |||
curp->outfree = terminal_free; | |||
break; | |||
} | |||
} | |||
/* Execute the out device, if it exists. */ | |||
if (man && curp->outman) | |||
(*curp->outman)(curp->outdata, man); | |||
if (mdoc && curp->outmdoc) | |||
(*curp->outmdoc)(curp->outdata, mdoc); | |||
cleanup: | |||
if (curp->mdoc) { | |||
mdoc_free(curp->mdoc); | |||
curp->mdoc = NULL; | |||
} | |||
if (curp->man) { | |||
man_free(curp->man); | |||
curp->man = NULL; | |||
} | |||
return; | |||
bailout: | |||
with_error = 1; | |||
goto cleanup; | |||
} | } | ||
static int | static int | ||
pset(const char *buf, size_t pos, struct curparse *curp, | pset(const char *buf, int pos, struct curparse *curp, | ||
struct man **man, struct mdoc **mdoc) | struct man **man, struct mdoc **mdoc) | ||
{ | { | ||
int i; | |||
/* | /* | ||
* Try to intuit which kind of manual parser should be used. If | * Try to intuit which kind of manual parser should be used. If | ||
* passed in by command-line (-man, -mdoc), then use that | * passed in by command-line (-man, -mdoc), then use that | ||
* explicitly. If passed as -mandoc, then try to guess from the | * explicitly. If passed as -mandoc, then try to guess from the | ||
* line: either skip comments, use -mdoc when finding `.Dt', or | * line: either skip dot-lines, use -mdoc when finding `.Dt', or | ||
* default to -man, which is more lenient. | * default to -man, which is more lenient. | ||
*/ | */ | ||
if (pos >= 3 && 0 == memcmp(buf, ".\\\"", 3)) | if (buf[0] == '.') { | ||
return(1); | for (i = 1; buf[i]; i++) | ||
if (' ' != buf[i] && '\t' != buf[i]) | |||
break; | |||
if (0 == buf[i]) | |||
return(1); | |||
} | |||
switch (curp->inttype) { | switch (curp->inttype) { | ||
case (INTT_MDOC): | case (INTT_MDOC): | ||
|
|
||
curp->mdoc = mdoc_init(curp); | curp->mdoc = mdoc_init(curp); | ||
if (NULL == (*mdoc = curp->mdoc)) | if (NULL == (*mdoc = curp->mdoc)) | ||
return(0); | return(0); | ||
warnx("inheriting -mdoc parser"); | |||
return(1); | return(1); | ||
case (INTT_MAN): | case (INTT_MAN): | ||
if (NULL == curp->man) | if (NULL == curp->man) | ||
curp->man = man_init(curp); | curp->man = man_init(curp); | ||
if (NULL == (*man = curp->man)) | if (NULL == (*man = curp->man)) | ||
return(0); | return(0); | ||
warnx("inheriting -man parser"); | |||
return(1); | return(1); | ||
default: | default: | ||
break; | break; | ||
|
|
||
else if (0 == strcmp(arg, "an")) | else if (0 == strcmp(arg, "an")) | ||
*tflags = INTT_MAN; | *tflags = INTT_MAN; | ||
else { | else { | ||
warnx("bad argument: -m%s", arg); | fprintf(stderr, "%s: Bad argument\n", arg); | ||
return(0); | return(0); | ||
} | } | ||
|
|
||
static int | static int | ||
toptions(enum outt *tflags, char *arg) | toptions(struct curparse *curp, char *arg) | ||
{ | { | ||
if (0 == strcmp(arg, "ascii")) | if (0 == strcmp(arg, "ascii")) | ||
*tflags = OUTT_ASCII; | curp->outtype = OUTT_ASCII; | ||
else if (0 == strcmp(arg, "lint")) | else if (0 == strcmp(arg, "lint")) { | ||
*tflags = OUTT_LINT; | curp->outtype = OUTT_LINT; | ||
curp->wflags |= WARN_WALL; | |||
curp->fflags |= FL_STRICT; | |||
} | |||
else if (0 == strcmp(arg, "tree")) | else if (0 == strcmp(arg, "tree")) | ||
*tflags = OUTT_TREE; | curp->outtype = OUTT_TREE; | ||
else if (0 == strcmp(arg, "html")) | |||
curp->outtype = OUTT_HTML; | |||
else if (0 == strcmp(arg, "xhtml")) | |||
curp->outtype = OUTT_XHTML; | |||
else { | else { | ||
warnx("bad argument: -T%s", arg); | fprintf(stderr, "%s: Bad argument\n", arg); | ||
return(0); | return(0); | ||
} | } | ||
|
|
||
} | } | ||
/* | |||
* Parse out the options for [-fopt...] setting compiler options. These | |||
* can be comma-delimited or called again. | |||
*/ | |||
static int | static int | ||
foptions(int *fflags, char *arg) | foptions(int *fflags, char *arg) | ||
{ | { | ||
char *v; | char *v, *o; | ||
char *toks[5]; | const char *toks[8]; | ||
toks[0] = "ign-scope"; | toks[0] = "ign-scope"; | ||
toks[1] = "ign-escape"; | toks[1] = "no-ign-escape"; | ||
toks[2] = "ign-macro"; | toks[2] = "no-ign-macro"; | ||
toks[4] = "no-ign-macro"; | toks[3] = "ign-errors"; | ||
toks[5] = NULL; | toks[4] = "strict"; | ||
toks[5] = "ign-escape"; | |||
toks[6] = NULL; | |||
while (*arg) | while (*arg) { | ||
switch (getsubopt(&arg, toks, &v)) { | o = arg; | ||
switch (getsubopt(&arg, UNCONST(toks), &v)) { | |||
case (0): | case (0): | ||
*fflags |= IGN_SCOPE; | *fflags |= FL_IGN_SCOPE; | ||
break; | break; | ||
case (1): | case (1): | ||
*fflags |= IGN_ESCAPE; | *fflags |= FL_NIGN_ESCAPE; | ||
break; | break; | ||
case (2): | case (2): | ||
*fflags |= IGN_MACRO; | *fflags |= FL_NIGN_MACRO; | ||
break; | break; | ||
case (3): | case (3): | ||
*fflags |= NO_IGN_MACRO; | *fflags |= FL_IGN_ERRORS; | ||
break; | break; | ||
case (4): | |||
*fflags |= FL_STRICT; | |||
break; | |||
case (5): | |||
*fflags &= ~FL_NIGN_ESCAPE; | |||
break; | |||
default: | default: | ||
warnx("bad argument: -f%s", arg); | fprintf(stderr, "%s: Bad argument\n", o); | ||
return(0); | return(0); | ||
} | } | ||
} | |||
return(1); | return(1); | ||
} | } | ||
/* | |||
* Parse out the options for [-Werr...], which sets warning modes. | |||
* These can be comma-delimited or called again. | |||
*/ | |||
static int | static int | ||
woptions(int *wflags, char *arg) | woptions(int *wflags, char *arg) | ||
{ | { | ||
char *v; | char *v, *o; | ||
char *toks[5]; | const char *toks[3]; | ||
toks[0] = "all"; | toks[0] = "all"; | ||
toks[1] = "compat"; | toks[1] = "error"; | ||
toks[2] = "syntax"; | toks[2] = NULL; | ||
toks[3] = "error"; | |||
toks[4] = NULL; | |||
while (*arg) | while (*arg) { | ||
switch (getsubopt(&arg, toks, &v)) { | o = arg; | ||
switch (getsubopt(&arg, UNCONST(toks), &v)) { | |||
case (0): | case (0): | ||
*wflags |= WARN_WALL; | *wflags |= WARN_WALL; | ||
break; | break; | ||
case (1): | case (1): | ||
*wflags |= WARN_WCOMPAT; | |||
break; | |||
case (2): | |||
*wflags |= WARN_WSYNTAX; | |||
break; | |||
case (3): | |||
*wflags |= WARN_WERR; | *wflags |= WARN_WERR; | ||
break; | break; | ||
default: | default: | ||
warnx("bad argument: -W%s", arg); | fprintf(stderr, "%s: Bad argument\n", o); | ||
return(0); | return(0); | ||
} | } | ||
} | |||
return(1); | return(1); | ||
} | } | ||
|
|
||
curp = (struct curparse *)arg; | curp = (struct curparse *)arg; | ||
warnx("%s:%d: error: %s (column %d)", | (void)fprintf(stderr, "%s:%d:%d: error: %s\n", | ||
curp->file, line, msg, col); | curp->file, line, col + 1, msg); | ||
return(0); | |||
} | |||
with_error = 1; | |||
static int | |||
mdocwarn(void *arg, int line, int col, | |||
enum mdoc_warn type, const char *msg) | |||
{ | |||
struct curparse *curp; | |||
char *wtype; | |||
curp = (struct curparse *)arg; | |||
wtype = NULL; | |||
switch (type) { | |||
case (WARN_COMPAT): | |||
wtype = "compat"; | |||
if (curp->wflags & WARN_WCOMPAT) | |||
break; | |||
return(1); | |||
case (WARN_SYNTAX): | |||
wtype = "syntax"; | |||
if (curp->wflags & WARN_WSYNTAX) | |||
break; | |||
return(1); | |||
} | |||
assert(wtype); | |||
warnx("%s:%d: %s warning: %s (column %d)", | |||
curp->file, line, wtype, msg, col); | |||
if ( ! (curp->wflags & WARN_WERR)) | |||
return(1); | |||
warnx("%s: considering warnings as errors", | |||
__progname); | |||
return(0); | return(0); | ||
} | } | ||
static int | static int | ||
manwarn(void *arg, int line, int col, const char *msg) | mwarn(void *arg, int line, int col, const char *msg) | ||
{ | { | ||
struct curparse *curp; | struct curparse *curp; | ||
curp = (struct curparse *)arg; | curp = (struct curparse *)arg; | ||
if ( ! (curp->wflags & WARN_WSYNTAX)) | if ( ! (curp->wflags & WARN_WALL)) | ||
return(1); | return(1); | ||
warnx("%s:%d: syntax warning: %s (column %d)", | (void)fprintf(stderr, "%s:%d:%d: warning: %s\n", | ||
curp->file, line, msg, col); | curp->file, line, col + 1, msg); | ||
if ( ! (curp->wflags & WARN_WERR)) | with_warning = 1; | ||
return(1); | if (curp->wflags & WARN_WERR) { | ||
with_error = 1; | |||
return(0); | |||
} | |||
warnx("%s: considering warnings as errors", | return(1); | ||
__progname); | |||
return(0); | |||
} | } | ||