Import mdocml 1.10.4:

- Smaller bugfixes
- Improved .ds support
- Support .Bk -words
- Improve multi-page postscript output
- Improve indentation in SYNOPSIS
- Improved support for badly nested blocks
This commit is contained in:
joerg 2010-07-25 19:10:40 +00:00
parent 0ad9aaa477
commit 82361f106b
35 changed files with 2559 additions and 936 deletions

View File

@ -12,8 +12,8 @@ INSTALL_DATA = $(INSTALL) -m 0444
INSTALL_LIB = $(INSTALL) -m 0644
INSTALL_MAN = $(INSTALL_DATA)
VERSION = 1.10.2
VDATE = 19 June 2010
VERSION = 1.10.4
VDATE = 12 July 2010
VFLAGS = -DVERSION="\"$(VERSION)\""
WFLAGS = -W -Wall -Wstrict-prototypes -Wno-unused-parameter -Wwrite-strings
@ -23,10 +23,6 @@ CFLAGS += -g $(WFLAGS) $(VFLAGS) -DHAVE_CONFIG_H
# in the lower-left hand corner of -mdoc manuals.
# CFLAGS += -DOSNAME="\"OpenBSD 4.5\""
# Unset this if you don't want Xo/Xc allowing split `It' lines, which
# breaks symmetry.
CFLAGS += -DUGLY
LINTFLAGS += $(VFLAGS)
MANDOCFLAGS = -Wall -fstrict
@ -65,7 +61,7 @@ MANLNS = man_macro.ln man.ln man_hash.ln man_validate.ln \
MANOBJS = man_macro.o man.o man_hash.o man_validate.o \
man_action.o man_argv.o
MANSRCS = man_macro.c man.c man_hash.c man_validate.c \
man_action.c mandoc.c man_argv.c
man_action.c man_argv.c
MAINLNS = main.ln mdoc_term.ln chars.ln term.ln tree.ln \
compat.ln man_term.ln html.ln mdoc_html.ln \

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: html.c,v 1.102 2010/06/19 20:46:27 kristaps Exp $ */
/* $Vendor-Id: html.c,v 1.105 2010/07/06 12:37:17 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -393,8 +393,15 @@ print_otag(struct html *h, enum htmltag tag,
t = NULL;
if ( ! (HTML_NOSPACE & h->flags))
if ( ! (HTML_CLRLINE & htmltags[tag].flags))
putchar(' ');
if ( ! (HTML_CLRLINE & htmltags[tag].flags)) {
/* Manage keeps! */
if ( ! (HTML_KEEP & h->flags)) {
if (HTML_PREKEEP & h->flags)
h->flags |= HTML_KEEP;
putchar(' ');
} else
printf("&#160;");
}
/* Print out the tag name and attributes. */
@ -484,11 +491,11 @@ print_doctype(struct html *h)
void
print_text(struct html *h, const char *p)
print_text(struct html *h, const char *word)
{
if (*p && 0 == *(p + 1))
switch (*p) {
if (word[0] && '\0' == word[1])
switch (word[0]) {
case('.'):
/* FALLTHROUGH */
case(','):
@ -511,19 +518,26 @@ print_text(struct html *h, const char *p)
break;
}
if ( ! (h->flags & HTML_NOSPACE))
putchar(' ');
if ( ! (HTML_NOSPACE & h->flags)) {
/* Manage keeps! */
if ( ! (HTML_KEEP & h->flags)) {
if (HTML_PREKEEP & h->flags)
h->flags |= HTML_KEEP;
putchar(' ');
} else
printf("&#160;");
}
assert(p);
if ( ! print_encode(h, p, 0))
assert(word);
if ( ! print_encode(h, word, 0))
h->flags &= ~HTML_NOSPACE;
/*
* Note that we don't process the pipe: the parser sees it as
* punctuation, but we don't in terms of typography.
*/
if (*p && 0 == *(p + 1))
switch (*p) {
if (word[0] && '\0' == word[1])
switch (word[0]) {
case('('):
/* FALLTHROUGH */
case('['):
@ -718,11 +732,11 @@ bufcat_su(struct html *h, const char *p, const struct roffsu *su)
break;
}
if (su->pt)
buffmt(h, "%s: %f%s;", p, v, u);
else
/* LINTED */
buffmt(h, "%s: %d%s;", p, (int)v, u);
/*
* XXX: the CSS spec isn't clear as to which types accept
* integer or real numbers, so we just make them all decimals.
*/
buffmt(h, "%s: %.2f%s;", p, v, u);
}

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: html.h,v 1.24 2010/06/19 20:46:27 kristaps Exp $ */
/* $Vendor-Id: html.h,v 1.25 2010/07/06 12:37:17 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -110,7 +110,9 @@ enum htmltype {
struct html {
int flags;
#define HTML_NOSPACE (1 << 0)
#define HTML_IGNDELIM (1 << 2)
#define HTML_IGNDELIM (1 << 1)
#define HTML_KEEP (1 << 2)
#define HTML_PREKEEP (1 << 3)
struct tagq tags;
struct ordq ords;
void *symtab;

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: libman.h,v 1.36 2010/06/19 20:46:27 kristaps Exp $ */
/* $Vendor-Id: libman.h,v 1.41 2010/07/07 15:04:54 kristaps Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -25,8 +25,8 @@ enum man_next {
};
struct man {
void *data;
mandocmsg msg;
void *data; /* private application data */
mandocmsg msg; /* output message handler */
int pflags; /* parse flags (see man.h) */
int flags; /* parse flags */
#define MAN_HALT (1 << 0) /* badness happened: die */
@ -35,14 +35,19 @@ struct man {
#define MAN_ILINE (1 << 3) /* Ignored in next-line scope. */
#define MAN_LITERAL (1 << 4) /* Literal input. */
#define MAN_BPLINE (1 << 5)
enum man_next next;
struct man_node *last;
struct man_node *first;
struct man_meta meta;
enum man_next next; /* where to put the next node */
struct man_node *last; /* the last parsed node */
struct man_node *first; /* the first parsed node */
struct man_meta meta; /* document meta-data */
struct regset *regs; /* registers */
};
#define MACRO_PROT_ARGS struct man *m, enum mant tok, int line, \
int ppos, int *pos, char *buf
#define MACRO_PROT_ARGS struct man *m, \
enum mant tok, \
int line, \
int ppos, \
int *pos, \
char *buf
struct man_macro {
int (*fp)(MACRO_PROT_ARGS);

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: libmdoc.h,v 1.53 2010/06/19 20:46:27 kristaps Exp $ */
/* $Vendor-Id: libmdoc.h,v 1.60 2010/07/07 15:04:54 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -25,8 +25,8 @@ enum mdoc_next {
};
struct mdoc {
void *data;
mandocmsg msg;
void *data; /* private application data */
mandocmsg msg; /* message callback */
int flags;
#define MDOC_HALT (1 << 0) /* error in parse: halt */
#define MDOC_LITERAL (1 << 1) /* in a literal scope */
@ -35,17 +35,23 @@ struct mdoc {
#define MDOC_PHRASELIT (1 << 4) /* literal within a partila phrase */
#define MDOC_PPHRASE (1 << 5) /* within a partial phrase */
#define MDOC_FREECOL (1 << 6) /* `It' invocation should close */
#define MDOC_SYNOPSIS (1 << 7) /* SYNOPSIS-style formatting */
int pflags;
enum mdoc_next next;
struct mdoc_node *last;
struct mdoc_node *first;
struct mdoc_meta meta;
enum mdoc_next next; /* where to put the next node */
struct mdoc_node *last; /* the last node parsed */
struct mdoc_node *first; /* the first node parsed */
struct mdoc_meta meta; /* document meta-data */
enum mdoc_sec lastnamed;
enum mdoc_sec lastsec;
struct regset *regs; /* registers */
};
#define MACRO_PROT_ARGS struct mdoc *m, enum mdoct tok, \
int line, int ppos, int *pos, char *buf
#define MACRO_PROT_ARGS struct mdoc *m, \
enum mdoct tok, \
int line, \
int ppos, \
int *pos, \
char *buf
struct mdoc_macro {
int (*fp)(MACRO_PROT_ARGS);
@ -103,6 +109,9 @@ int mdoc_block_alloc(struct mdoc *, int, int,
int mdoc_head_alloc(struct mdoc *, int, int, enum mdoct);
int mdoc_tail_alloc(struct mdoc *, int, int, enum mdoct);
int mdoc_body_alloc(struct mdoc *, int, int, enum mdoct);
int mdoc_endbody_alloc(struct mdoc *m, int line, int pos,
enum mdoct tok, struct mdoc_node *body,
enum mdoc_endbody end);
void mdoc_node_delete(struct mdoc *, struct mdoc_node *);
void mdoc_hash_init(void);
enum mdoct mdoc_hash_find(const char *);

View File

@ -1,6 +1,7 @@
/* $Vendor-Id: main.c,v 1.89 2010/06/19 20:46:27 kristaps Exp $ */
/* $Vendor-Id: main.c,v 1.98 2010/07/07 15:04:54 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -30,10 +31,10 @@
#include <unistd.h>
#include "mandoc.h"
#include "main.h"
#include "mdoc.h"
#include "man.h"
#include "roff.h"
#include "main.h"
#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
@ -87,6 +88,7 @@ struct curparse {
struct man *man; /* man parser */
struct mdoc *mdoc; /* mdoc parser */
struct roff *roff; /* roff parser (!NULL) */
struct regset regs; /* roff registers */
enum outt outtype; /* which output to use */
out_mdoc outmdoc; /* mdoc output ptr */
out_man outman; /* man output ptr */
@ -97,6 +99,9 @@ struct curparse {
static const char * const mandocerrs[MANDOCERR_MAX] = {
"ok",
"generic warning",
"text should be uppercase",
"sections out of conventional order",
"section name repeats",
@ -115,7 +120,11 @@ static const char * const mandocerrs[MANDOCERR_MAX] = {
"unknown manual section",
"section not in conventional manual section",
"end of line whitespace",
"blocks badly nested",
"scope open on exit",
"generic error",
"NAME section must come first",
"bad Boolean value",
"child violates parent syntax",
@ -135,7 +144,6 @@ static const char * const mandocerrs[MANDOCERR_MAX] = {
"bad comment style",
"unknown macro will be lost",
"line scope broken",
"scope broken",
"argument count wrong",
"request scope close w/none open",
"scope already open",
@ -145,13 +153,17 @@ static const char * const mandocerrs[MANDOCERR_MAX] = {
"no title in document",
"missing list type",
"missing display type",
"missing font type",
"line argument(s) will be lost",
"body argument(s) will be lost",
"generic fatal error",
"column syntax is inconsistent",
"missing font type",
"displays may not be nested",
"unsupported display type",
"no scope to rewind: syntax violated",
"blocks badly nested",
"no such block is open",
"scope broken, syntax violated",
"line scope broken, syntax violated",
"argument count wrong, violates syntax",
@ -180,8 +192,8 @@ static void version(void) __attribute__((noreturn));
static int woptions(int *, char *);
static const char *progname;
static int with_error;
static int with_warning;
static int with_fatal;
static int with_error;
int
main(int argc, char *argv[])
@ -244,7 +256,7 @@ main(int argc, char *argv[])
while (*argv) {
ffile(*argv, &curp);
if (with_error && !(curp.fflags & FL_IGN_ERRORS))
if (with_fatal && !(curp.fflags & FL_IGN_ERRORS))
break;
++argv;
}
@ -258,7 +270,7 @@ main(int argc, char *argv[])
if (curp.roff)
roff_free(curp.roff);
return((with_warning || with_error) ?
return((with_fatal || with_error) ?
EXIT_FAILURE : EXIT_SUCCESS);
}
@ -297,7 +309,7 @@ man_init(struct curparse *curp)
if (curp->fflags & FL_NIGN_ESCAPE)
pflags &= ~MAN_IGN_ESCAPE;
return(man_alloc(curp, pflags, mmsg));
return(man_alloc(&curp->regs, curp, pflags, mmsg));
}
@ -305,7 +317,7 @@ static struct roff *
roff_init(struct curparse *curp)
{
return(roff_alloc(mmsg, curp));
return(roff_alloc(&curp->regs, mmsg, curp));
}
@ -325,7 +337,7 @@ mdoc_init(struct curparse *curp)
if (curp->fflags & FL_NIGN_MACRO)
pflags &= ~MDOC_IGN_MACRO;
return(mdoc_alloc(curp, pflags, mmsg));
return(mdoc_alloc(&curp->regs, curp, pflags, mmsg));
}
@ -336,7 +348,7 @@ ffile(const char *file, struct curparse *curp)
curp->file = file;
if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
perror(curp->file);
with_error = 1;
with_fatal = 1;
return;
}
@ -377,7 +389,7 @@ read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap)
if (-1 == fstat(curp->fd, &st)) {
perror(curp->file);
with_error = 1;
with_fatal = 1;
return(0);
}
@ -392,7 +404,7 @@ read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap)
if (st.st_size >= (1U << 31)) {
fprintf(stderr, "%s: input too large\n",
curp->file);
with_error = 1;
with_fatal = 1;
return(0);
}
*with_mmap = 1;
@ -436,7 +448,7 @@ read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap)
free(fb->buf);
fb->buf = NULL;
with_error = 1;
with_fatal = 1;
return(0);
}
@ -454,6 +466,7 @@ fdesc(struct curparse *curp)
man = NULL;
mdoc = NULL;
roff = NULL;
memset(&ln, 0, sizeof(struct buf));
/*
@ -596,7 +609,7 @@ fdesc(struct curparse *curp)
curp->outfree = ascii_free;
break;
case (OUTT_PS):
curp->outdata = ps_alloc();
curp->outdata = ps_alloc(curp->outopts);
curp->outfree = ps_free;
break;
default:
@ -634,6 +647,7 @@ fdesc(struct curparse *curp)
(*curp->outmdoc)(curp->outdata, mdoc);
cleanup:
memset(&curp->regs, 0, sizeof(struct regset));
if (mdoc)
mdoc_reset(mdoc);
if (man)
@ -650,7 +664,7 @@ fdesc(struct curparse *curp)
return;
bailout:
with_error = 1;
with_fatal = 1;
goto cleanup;
}
@ -835,30 +849,37 @@ static int
mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg)
{
struct curparse *cp;
const char *level;
int rc;
cp = (struct curparse *)arg;
level = NULL;
rc = 1;
if (t <= MANDOCERR_ERROR) {
if ( ! (cp->wflags & WARN_WALL))
if (t >= MANDOCERR_FATAL) {
with_fatal = 1;
level = "FATAL";
rc = 0;
} else {
if ( ! (WARN_WALL & cp->wflags))
return(1);
with_warning = 1;
} else
with_error = 1;
fprintf(stderr, "%s:%d:%d: %s", cp->file,
ln, col + 1, mandocerrs[t]);
if (t >= MANDOCERR_ERROR) {
with_error = 1;
level = "ERROR";
}
if (WARN_WERR & cp->wflags) {
with_fatal = 1;
rc = 0;
}
}
fprintf(stderr, "%s:%d:%d:", cp->file, ln, col + 1);
if (level)
fprintf(stderr, " %s:", level);
fprintf(stderr, " %s", mandocerrs[t]);
if (msg)
fprintf(stderr, ": %s", msg);
fputc('\n', stderr);
/* This is superfluous, but whatever. */
if (t > MANDOCERR_ERROR)
return(0);
if (cp->wflags & WARN_WERR) {
with_error = 1;
return(0);
}
return(1);
return(rc);
}

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: main.h,v 1.7 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: main.h,v 1.8 2010/06/29 14:53:14 kristaps Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -44,7 +44,7 @@ void tree_man(void *, const struct man *);
void *ascii_alloc(char *);
void ascii_free(void *);
void *ps_alloc(void);
void *ps_alloc(char *);
void ps_free(void *);
void terminal_mdoc(void *, const struct mdoc *);

View File

@ -1,4 +1,4 @@
.\" $Vendor-Id: man.3,v 1.18 2010/05/25 22:16:59 kristaps Exp $
.\" $Vendor-Id: man.3,v 1.22 2010/07/07 15:04:54 kristaps Exp $
.\"
.\" Copyright (c) 2009-2010 Kristaps Dzonsons <kristaps@bsd.lv>
.\"
@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: May 25 2010 $
.Dd $Mdocdate: July 7 2010 $
.Dt MAN 3
.Os
.Sh NAME
@ -32,7 +32,12 @@
.In man.h
.Vt extern const char * const * man_macronames;
.Ft "struct man *"
.Fn man_alloc "void *data" "int pflags" "mandocmsg msgs"
.Fo man_alloc
.Fa "struct regset *regs"
.Fa "void *data"
.Fa "int pflags"
.Fa "mandocmsg msgs"
.Fc
.Ft int
.Fn man_endparse "struct man *man"
.Ft void
@ -42,7 +47,11 @@
.Ft "const struct man_node *"
.Fn man_node "const struct man *man"
.Ft int
.Fn man_parseln "struct man *man" "int line" "char *buf"
.Fo man_parseln
.Fa "struct man *man"
.Fa "int line"
.Fa "char *buf"
.Fc
.Ft void
.Fn man_reset "struct man *man"
.Sh DESCRIPTION
@ -281,14 +290,16 @@ on the finished parse tree with
.Fn parsed .
This example does not error-check nor free memory upon failure.
.Bd -literal -offset indent
struct regset regs;
struct man *man;
struct man_node *node;
char *buf;
size_t len;
int line;
bzero(&regs, sizeof(struct regset));
line = 1;
man = man_alloc(NULL, 0, NULL);
man = man_alloc(&regs, NULL, 0, NULL);
buf = NULL;
alloc_len = 0;

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: man.c,v 1.76 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: man.c,v 1.81 2010/07/07 15:04:54 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -94,7 +94,8 @@ man_free(struct man *man)
struct man *
man_alloc(void *data, int pflags, mandocmsg msg)
man_alloc(struct regset *regs, void *data,
int pflags, mandocmsg msg)
{
struct man *p;
@ -104,6 +105,7 @@ man_alloc(void *data, int pflags, mandocmsg msg)
p->data = data;
p->pflags = pflags;
p->msg = msg;
p->regs = regs;
man_alloc1(p);
return(p);

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: man.h,v 1.37 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: man.h,v 1.40 2010/06/27 16:18:13 kristaps Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -106,7 +106,7 @@ __BEGIN_DECLS
struct man;
void man_free(struct man *);
struct man *man_alloc(void *, int, mandocmsg);
struct man *man_alloc(struct regset *, void *, int, mandocmsg);
void man_reset(struct man *);
int man_parseln(struct man *, int, char *, int);
int man_endparse(struct man *);

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: man_html.c,v 1.37 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: man_html.c,v 1.41 2010/07/07 15:04:54 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: man_macro.c,v 1.47 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: man_macro.c,v 1.48 2010/06/26 16:07:08 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -290,6 +290,7 @@ blk_close(MACRO_PROT_ARGS)
}
/* ARGSUSED */
int
blk_exp(MACRO_PROT_ARGS)
{
@ -341,6 +342,7 @@ blk_exp(MACRO_PROT_ARGS)
* scopes, such as `SH' closing out an `SS', are defined in the rew
* routines.
*/
/* ARGSUSED */
int
blk_imp(MACRO_PROT_ARGS)
{
@ -398,6 +400,7 @@ blk_imp(MACRO_PROT_ARGS)
}
/* ARGSUSED */
int
in_line_eoln(MACRO_PROT_ARGS)
{

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: man_validate.c,v 1.44 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: man_validate.c,v 1.45 2010/06/28 14:39:17 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -223,6 +223,11 @@ check_text(CHKARGS)
return(c);
}
/*
* FIXME: we absolutely cannot let \b get through or it
* will destroy some assumptions in terms of format.
*/
if ('\t' == *p || isprint((u_char)*p) || ASCII_HYPH == *p)
continue;
if ( ! man_pmsg(m, n->line, pos, MANDOCERR_BADCHAR))

View File

@ -1,4 +1,4 @@
.\" $Vendor-Id: mandoc.1,v 1.63 2010/06/11 07:15:42 kristaps Exp $
.\" $Vendor-Id: mandoc.1,v 1.71 2010/07/04 20:06:59 kristaps Exp $
.\"
.\" Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
.\"
@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: June 11 2010 $
.Dd $Mdocdate: July 4 2010 $
.Dt MANDOC 1
.Os
.Sh NAME
@ -51,9 +51,6 @@ Defaults to
.Fl m Ns Cm andoc .
.It Fl O Ns Ar option
Comma-separated output options.
See
.Sx Output Options
for details.
.It Fl T Ns Ar output
Output format.
See
@ -139,13 +136,45 @@ specified and
or
.Fl m Ns Cm an
is specified, then this format is used exclusively.
.Ss Compiler Options
Default
.Xr mdoc 7
and
.Xr man 7
compilation behaviour may be overridden with the
.Fl f
flag.
.Bl -tag -width Ds
.It Fl f Ns Cm ign-errors
When parsing multiple files, don't halt when one errors out.
Useful with
.Fl T Ns Cm lint
over a large set of manuals passed on the command line.
.It Fl f Ns Cm ign-escape
Ignore invalid escape sequences.
This is the default, but the option can be used to override an earlier
.Fl f Ns Cm strict .
.It Fl f Ns Cm ign-scope
When rewinding the scope of a block macro, forces the compiler to ignore
scope violations.
This can seriously mangle the resulting tree.
.Pq mdoc only
.It Fl f Ns Cm no-ign-escape
Do not ignore invalid escape sequences.
.It Fl f Ns Cm no-ign-macro
Do not ignore unknown macros at the start of input lines.
.It Fl f Ns Cm strict
Implies
.Fl f Ns Cm no-ign-escape
and
.Fl f Ns Cm no-ign-macro .
.El
.Ss Output Formats
The
.Nm
utility accepts the following
.Fl T
arguments (see
.Sx OUTPUT ) :
arguments, which correspond to output modes:
.Bl -tag -width Ds
.It Fl T Ns Cm ascii
Produce 7-bit ASCII output, backspace-encoded for bold and underline
@ -177,136 +206,6 @@ See
.Pp
If multiple input files are specified, these will be processed by the
corresponding filter in-order.
.Ss Compiler Options
Default compiler behaviour may be overridden with the
.Fl f
flag.
.Bl -tag -width Ds
.It Fl f Ns Cm ign-errors
When parsing multiple files, don't halt when one errors out.
Useful with
.Fl T Ns Cm lint
over a large set of manuals passed on the command line.
.It Fl f Ns Cm ign-escape
Ignore invalid escape sequences.
This is the default, but the option can be used to override an earlier
.Fl f Ns Cm strict .
.It Fl f Ns Cm ign-scope
When rewinding the scope of a block macro, forces the compiler to ignore
scope violations.
This can seriously mangle the resulting tree.
.Pq mdoc only
.It Fl f Ns Cm no-ign-escape
Do not ignore invalid escape sequences.
.It Fl f Ns Cm no-ign-macro
Do not ignore unknown macros at the start of input lines.
.It Fl f Ns Cm strict
Implies
.Fl f Ns Cm no-ign-escape
and
.Fl f Ns Cm no-ign-macro .
.El
.Ss Output Options
The
.Fl T Ns Ar html
and
.Fl T Ns Ar xhtml
modes accept the following output options:
.Bl -tag -width Ds
.It Fl O Ns Cm includes Ns = Ns Ar fmt
The string
.Ar fmt ,
for example,
.Ar ../src/%I.html ,
is used as a template for linked header files (usually via the
.Sq \&In
macro).
Instances of
.Sq \&%I
are replaced with the include filename.
The default is not to present a
hyperlink.
.It Fl O Ns Cm man Ns = Ns Ar fmt
The string
.Ar fmt ,
for example,
.Ar ../html%S/%N.%S.html ,
is used as a template for linked manuals (usually via the
.Sq \&Xr
macro).
Instances of
.Sq \&%N
and
.Sq %S
are replaced with the linked manual's name and section, respectively.
If no section is included, section 1 is assumed.
The default is not to
present a hyperlink.
.It Fl O Ns Cm style Ns = Ns Ar style.css
The file
.Ar style.css
is used for an external style-sheet.
This must be a valid absolute or
relative URI.
.El
.Pp
The
.Fl T Ns Ar ascii
mode accepts the following output option:
.Bl -tag -width Ds
.It Fl O Ns Cm width Ns = Ns Ar width
The output width is set to
.Ar width ,
which will normalise to \(>=60.
.El
.Sh OUTPUT
This section documents output details of
.Nm .
In general, output conforms to the traditional manual style of a header,
a body composed of sections and sub-sections, and a footer.
.Pp
The text style of output characters (non-macro characters, punctuation,
and white-space) is dictated by context.
.Pp
White-space is generally stripped from input.
This can be changed with
character escapes (specified in
.Xr mandoc_char 7 )
or literal modes (specified in
.Xr mdoc 7
and
.Xr man 7 ) .
.Pp
If non-macro punctuation is set apart from words, such as in the phrase
.Dq to be \&, or not to be ,
it's processed by
.Nm ,
regardless of output format, according to the following rules: opening
punctuation
.Po
.Sq \&( ,
.Sq \&[ ,
and
.Sq \&{
.Pc
is not followed by a space; closing punctuation
.Po
.Sq \&. ,
.Sq \&, ,
.Sq \&; ,
.Sq \&: ,
.Sq \&? ,
.Sq \&! ,
.Sq \&) ,
.Sq \&]
and
.Sq \&}
.Pc
is not preceded by white-space.
.Pp
If the input is
.Xr mdoc 7 ,
however, these rules are also applied to macro arguments when appropriate.
.Ss ASCII Output
Output produced by
.Fl T Ns Cm ascii ,
@ -330,6 +229,16 @@ are rendered best-effort in an ASCII equivalent.
.Pp
Output width is limited to 78 visible columns unless literal input lines
exceed this limit.
.Pp
The following
.Fl O
arguments are accepted:
.Bl -tag -width Ds
.It Cm width Ns = Ns Ar width
The output width is set to
.Ar width ,
which will normalise to \(>=60.
.El
.Ss HTML Output
Output produced by
.Fl T Ns Cm html
@ -347,11 +256,81 @@ cause rendered documents to appear as they do in
.Fl T Ns Cm ascii .
.Pp
Special characters are rendered in decimal-encoded UTF-8.
.Pp
The following
.Fl O
arguments are accepted:
.Bl -tag -width Ds
.It Cm includes Ns = Ns Ar fmt
The string
.Ar fmt ,
for example,
.Ar ../src/%I.html ,
is used as a template for linked header files (usually via the
.Sq \&In
macro).
Instances of
.Sq \&%I
are replaced with the include filename.
The default is not to present a
hyperlink.
.It Cm man Ns = Ns Ar fmt
The string
.Ar fmt ,
for example,
.Ar ../html%S/%N.%S.html ,
is used as a template for linked manuals (usually via the
.Sq \&Xr
macro).
Instances of
.Sq \&%N
and
.Sq %S
are replaced with the linked manual's name and section, respectively.
If no section is included, section 1 is assumed.
The default is not to
present a hyperlink.
.It Cm style Ns = Ns Ar style.css
The file
.Ar style.css
is used for an external style-sheet.
This must be a valid absolute or
relative URI.
.El
.Ss PostScript Output
PostScript Level 2 pages may be generated by
PostScript
.Qq Adobe-3.0
Level-2 pages may be generated by
.Fl T Ns Cm ps .
Output pages are US-letter sized (215.9 x 279.4 mm) and rendered in
fixed, 10-point Courier font.
Output pages default to letter sized and are rendered in the Times font
family, 11-point.
Margins are calculated as 1/9 the page length and width.
Line-height is 1.4m.
.Pp
Special characters are rendered as in
.Sx ASCII Output .
.Pp
The following
.Fl O
arguments are accepted:
.Bl -tag -width Ds
.It Cm paper Ns = Ns Ar name
The paper size
.Ar name
may be one of
.Ar a3 ,
.Ar a4 ,
.Ar a5 ,
.Ar legal ,
or
.Ar letter .
You may also manually specify dimensions as
.Ar NNxNN ,
width by height in millimetres.
If an unknown value is encountered,
.Ar letter
is used.
.El
.Ss XHTML Output
Output produced by
.Fl T Ns Cm xhtml
@ -376,6 +355,10 @@ as the style-sheet:
To check over a large set of manuals:
.Pp
.Dl $ mandoc \-Tlint \-fign-errors `find /usr/src -name \e*\e.[1-9]`
.Pp
To produce a series of PostScript manuals for A4 paper:
.Pp
.D1 $ mandoc \-Tps \-Opaper=a4 mdoc.7 man.7 \*(Gt manuals.ps
.Sh COMPATIBILITY
This section summarises
.Nm

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: mandoc.c,v 1.19 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: mandoc.c,v 1.21 2010/07/06 22:04:31 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: mandoc.h,v 1.12 2010/06/12 11:41:50 kristaps Exp $ */
/* $Vendor-Id: mandoc.h,v 1.16 2010/07/05 20:10:22 kristaps Exp $ */
/*
* Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -17,14 +17,20 @@
#ifndef MANDOC_H
#define MANDOC_H
/*
* This contains declarations that are available system-wide.
*/
#define ASCII_NBRSP 31 /* non-breaking space */
#define ASCII_HYPH 30 /* breakable hyphen */
__BEGIN_DECLS
enum mandocerr {
MANDOCERR_OK,
MANDOCERR_WARNING, /* ===== end of warnings ===== */
MANDOCERR_UPPERCASE, /* text should be uppercase */
MANDOCERR_SECOOO, /* sections out of conventional order */
MANDOCERR_SECREP, /* section name repeats */
@ -38,14 +44,16 @@ enum mandocerr {
MANDOCERR_NOWIDTHARG, /* argument requires the width argument */
/* FIXME: merge with MANDOCERR_IGNARGV. */
MANDOCERR_WIDTHARG, /* superfluous width argument */
MANDOCERR_IGNARGV, /* macro ignoring argv */
MANDOCERR_IGNARGV, /* ignoring argument */
MANDOCERR_BADDATE, /* bad date argument */
MANDOCERR_BADWIDTH, /* bad width argument */
MANDOCERR_BADMSEC, /* unknown manual section */
MANDOCERR_SECMSEC, /* section not in conventional manual section */
MANDOCERR_EOLNSPACE, /* end of line whitespace */
MANDOCERR_SCOPENEST, /* blocks badly nested */
MANDOCERR_SCOPEEXIT, /* scope open on exit */
#define MANDOCERR_WARNING MANDOCERR_SCOPEEXIT
MANDOCERR_ERROR, /* ===== end of errors ===== */
MANDOCERR_NAMESECFIRST, /* NAME section must come first */
MANDOCERR_BADBOOL, /* bad Boolean value */
@ -66,9 +74,8 @@ enum mandocerr {
MANDOCERR_BADCOMMENT, /* bad comment style */
MANDOCERR_MACRO, /* unknown macro will be lost */
MANDOCERR_LINESCOPE, /* line scope broken */
MANDOCERR_SCOPE, /* scope broken */
MANDOCERR_ARGCOUNT, /* argument count wrong */
MANDOCERR_NOSCOPE, /* request scope close w/none open */
MANDOCERR_NOSCOPE, /* no such block is open */
MANDOCERR_SCOPEREP, /* scope already open */
/* FIXME: merge following with MANDOCERR_ARGCOUNT */
MANDOCERR_NOARGS, /* macro requires line argument(s) */
@ -77,17 +84,18 @@ enum mandocerr {
MANDOCERR_NOTITLE, /* no title in document */
MANDOCERR_LISTTYPE, /* missing list type */
MANDOCERR_DISPTYPE, /* missing display type */
MANDOCERR_FONTTYPE, /* missing font type */
MANDOCERR_ARGSLOST, /* line argument(s) will be lost */
MANDOCERR_BODYLOST, /* body argument(s) will be lost */
#define MANDOCERR_ERROR MANDOCERR_BODYLOST
MANDOCERR_FATAL, /* ===== end of fatal errors ===== */
MANDOCERR_COLUMNS, /* column syntax is inconsistent */
/* FIXME: this should be a MANDOCERR_ERROR */
MANDOCERR_FONTTYPE, /* missing font type */
/* FIXME: this should be a MANDOCERR_ERROR */
MANDOCERR_NESTEDDISP, /* displays may not be nested */
MANDOCERR_BADDISP, /* unsupported display type */
MANDOCERR_SYNTNOSCOPE, /* request scope close w/none open */
MANDOCERR_SCOPEFATAL, /* blocks badly nested */
MANDOCERR_SYNTNOSCOPE, /* no scope to rewind: syntax violated */
MANDOCERR_SYNTSCOPE, /* scope broken, syntax violated */
MANDOCERR_SYNTLINESCOPE, /* line scope broken, syntax violated */
MANDOCERR_SYNTARGVCOUNT, /* argument count wrong, violates syntax */
@ -95,15 +103,45 @@ enum mandocerr {
MANDOCERR_SYNTARGCOUNT, /* argument count wrong, violates syntax */
MANDOCERR_NODOCBODY, /* no document body */
MANDOCERR_NODOCPROLOG, /* no document prologue */
MANDOCERR_UTSNAME, /* utsname() system call failed */
MANDOCERR_UTSNAME, /* utsname system call failed */
MANDOCERR_MEM, /* memory exhausted */
#define MANDOCERR_FATAL MANDOCERR_MEM
MANDOCERR_MAX
};
typedef int (*mandocmsg)(enum mandocerr,
void *, int, int, const char *);
enum regs {
REG_nS = 0, /* register: nS */
REG__MAX
};
/*
* A single register entity. If "set" is zero, the value of the
* register should be the default one, which is per-register. It's
* assumed that callers know which type in "v" corresponds to which
* register value.
*/
struct reg {
int set; /* whether set or not */
union {
unsigned u; /* unsigned integer */
} v;
};
/*
* The primary interface to setting register values is in libroff,
* although libmdoc and libman from time to time will manipulate
* registers (such as `.Sh SYNOPSIS' enabling REG_nS).
*/
struct regset {
struct reg regs[REG__MAX];
};
/*
* Callback function for warnings, errors, and fatal errors as they
* occur in the compilers libroff, libmdoc, and libman.
*/
typedef int (*mandocmsg)(enum mandocerr, void *,
int, int, const char *);
__END_DECLS

View File

@ -1,6 +1,7 @@
.\" $Vendor-Id: mdoc.3,v 1.41 2010/05/30 22:56:02 kristaps Exp $
.\" $Vendor-Id: mdoc.3,v 1.48 2010/07/07 15:04:54 kristaps Exp $
.\"
.\" Copyright (c) 2009-2010 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@ -14,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: May 30 2010 $
.Dd $Mdocdate: July 7 2010 $
.Dt MDOC 3
.Os
.Sh NAME
@ -33,7 +34,12 @@
.Vt extern const char * const * mdoc_macronames;
.Vt extern const char * const * mdoc_argnames;
.Ft "struct mdoc *"
.Fn mdoc_alloc "void *data" "int pflags" "mandocmsg msgs"
.Fo mdoc_alloc
.Fa "struct regset *regs"
.Fa "void *data"
.Fa "int pflags"
.Fa "mandocmsg msgs"
.Fc
.Ft int
.Fn mdoc_endparse "struct mdoc *mdoc"
.Ft void
@ -43,7 +49,11 @@
.Ft "const struct mdoc_node *"
.Fn mdoc_node "const struct mdoc *mdoc"
.Ft int
.Fn mdoc_parseln "struct mdoc *mdoc" "int line" "char *buf"
.Fo mdoc_parseln
.Fa "struct mdoc *mdoc"
.Fa "int line"
.Fa "char *buf"
.Fc
.Ft int
.Fn mdoc_reset "struct mdoc *mdoc"
.Sh DESCRIPTION
@ -207,10 +217,14 @@ and
fields), its position in the tree (the
.Va parent ,
.Va child ,
.Va nchild ,
.Va next
and
.Va prev
fields) and some type-specific data.
fields) and some type-specific data, in particular, for nodes generated
from macros, the generating macro in the
.Va tok
field.
.Pp
The tree itself is arranged according to the following normal form,
where capitalised non-terminals represent nodes.
@ -225,11 +239,11 @@ where capitalised non-terminals represent nodes.
.It ELEMENT
\(<- TEXT*
.It HEAD
\(<- mnode+
\(<- mnode*
.It BODY
\(<- mnode+
\(<- mnode* [ENDBODY mnode*]
.It TAIL
\(<- mnode+
\(<- mnode*
.It TEXT
\(<- [[:printable:],0x1e]*
.El
@ -243,20 +257,88 @@ an empty line will produce a zero-length string.
Multiple body parts are only found in invocations of
.Sq \&Bl \-column ,
where a new body introduces a new phrase.
.Ss Badly-nested Blocks
The ENDBODY node is available to end the formatting associated
with a given block before the physical end of that block.
It has a non-null
.Va end
field, is of the BODY
.Va type ,
has the same
.Va tok
as the BLOCK it is ending, and has a
.Va pending
field pointing to that BLOCK's BODY node.
It is an indirect child of that BODY node
and has no children of its own.
.Pp
An ENDBODY node is generated when a block ends while one of its child
blocks is still open, like in the following example:
.Bd -literal -offset indent
\&.Ao ao
\&.Bo bo ac
\&.Ac bc
\&.Bc end
.Ed
.Pp
This example results in the following block structure:
.Bd -literal -offset indent
BLOCK Ao
HEAD Ao
BODY Ao
TEXT ao
BLOCK Bo, pending -> Ao
HEAD Bo
BODY Bo
TEXT bo
TEXT ac
ENDBODY Ao, pending -> Ao
TEXT bc
TEXT end
.Ed
.Pp
Here, the formatting of the
.Sq \&Ao
block extends from TEXT ao to TEXT ac,
while the formatting of the
.Sq \&Bo
block extends from TEXT bo to TEXT bc.
It renders as follows in
.Fl T Ns Cm ascii
mode:
.Pp
.Dl <ao [bo ac> bc] end
.Pp
Support for badly-nested blocks is only provided for backward
compatibility with some older
.Xr mdoc 7
implementations.
Using badly-nested blocks is
.Em strongly discouraged :
the
.Fl T Ns Cm html
and
.Fl T Ns Cm xhtml
front-ends are unable to render them in any meaningful way.
Furthermore, behaviour when encountering badly-nested blocks is not
consistent across troff implementations, especially when using multiple
levels of badly-nested blocks.
.Sh EXAMPLES
The following example reads lines from stdin and parses them, operating
on the finished parse tree with
.Fn parsed .
This example does not error-check nor free memory upon failure.
.Bd -literal -offset indent
struct regset regs;
struct mdoc *mdoc;
const struct mdoc_node *node;
char *buf;
size_t len;
int line;
bzero(&regs, sizeof(struct regset));
line = 1;
mdoc = mdoc_alloc(NULL, 0, NULL);
mdoc = mdoc_alloc(&regs, NULL, 0, NULL);
buf = NULL;
alloc_len = 0;

View File

@ -1,6 +1,7 @@
.\" $Vendor-Id: mdoc.7,v 1.126 2010/06/12 11:41:50 kristaps Exp $
.\" $Vendor-Id: mdoc.7,v 1.133 2010/07/06 11:07:21 kristaps Exp $
.\"
.\" Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@ -14,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: June 12 2010 $
.Dd $Mdocdate: July 6 2010 $
.Dt MDOC 7
.Os
.Sh NAME
@ -478,6 +479,19 @@ they are separated by a vertical space, unless in the case of
and
.Sx \&Ft ,
which are always separated by vertical space.
.Pp
When text and macros following an
.Sx \&Nm
macro starting an input line span multiple output lines,
all output lines but the first will be indented to align
with the text immediately following the
.Sx \&Nm
macro, up to the next
.Sx \&Nm ,
.Sx \&Sx ,
or
.Sx \&Ss
macro or the end of an enclosing block, whichever comes first.
.It Em DESCRIPTION
This expands upon the brief, one-line description in
.Em NAME .
@ -672,9 +686,20 @@ has multiple heads.
.It Em Macro Ta Em Callable Ta Em Parsable Ta Em Scope
.It Sx \&It Ta \&No Ta Yes Ta closed by Sx \&It , Sx \&El
.It Sx \&Nd Ta \&No Ta \&No Ta closed by Sx \&Sh
.It Sx \&Nm Ta \&No Ta Yes Ta closed by Sx \&Nm , Sx \&Sh , Sx \&Ss
.It Sx \&Sh Ta \&No Ta \&No Ta closed by Sx \&Sh
.It Sx \&Ss Ta \&No Ta \&No Ta closed by Sx \&Sh , Sx \&Ss
.El
.Pp
Note that the
.Sx \&Nm
macro is a
.Sx Block full-implicit
macro only when invoked as the first macro
in a
.Em SYNOPSIS
section line, else it is
.Sx In-line .
.Ss Block partial-explicit
Like block full-explicit, but also with single-line scope.
Each has at least a body and, in limited circumstances, a head
@ -1039,6 +1064,14 @@ Closes a
block. Does not have any tail arguments.
.Ss \&Bd
Begins a display block.
Its syntax is as follows:
.Bd -ragged -offset indent
.Pf \. Sx \&Bd
.Fl type
.Op Fl offset Ar width
.Op Fl compact
.Ed
.Pp
A display is collection of macros or text which may be collectively
offset or justified in a manner different from that
of the enclosing context.
@ -1063,9 +1096,9 @@ Centre-justify each line.
The type must be provided first.
Secondary arguments are as follows:
.Bl -tag -width 12n -offset indent
.It Fl offset Ar width
.It Fl offset Ar val
Offset by the value of
.Ar width ,
.Ar val ,
which is interpreted as one of the following, specified in order:
.Bl -item
.It
@ -1076,7 +1109,7 @@ the width of standard indentation;
twice
.Ar indent ;
.Ar left ,
which has no effect ;
which has no effect;
.Ar right ,
which justifies to the right margin; and
.Ar center ,
@ -1097,10 +1130,6 @@ As the calculated string length of the opaque string.
If not provided an argument, it will be ignored.
.It Fl compact
Do not assert a vertical space before the block.
.It Fl file Ar file
Prepend the file
.Ar file
before any text or macros within the block.
.El
.Pp
Examples:
@ -1115,9 +1144,75 @@ See also
and
.Sx \&Dl .
.Ss \&Bf
Change the font mode for a scoped block of text.
Its syntax is as follows:
.Bd -ragged -offset indent
.Pf \. Sx \&Bf
.Oo
.Fl emphasis | literal | symbolic |
.Cm \&Em | \&Li | \&Sy
.Oc
.Ed
.Pp
The
.Fl emphasis
and
.Cm \&Em
argument are equivalent, as are
.Fl symbolic
and
.Cm \&Sy,
and
.Fl literal
and
.Cm \&Li .
Without an argument, this macro does nothing.
The font mode continues until broken by a new font mode in a nested
scope or
.Sx \&Ef
is encountered.
.Pp
See also
.Sx \&Li ,
.Sx \&Ef ,
and
.Sx \&Sy .
.Ss \&Bk
Begins a collection of macros or text not breaking the line.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Bk Fl words
.Pp
Subsequent arguments are ignored.
The
.Fl words
argument is required.
.Pp
Each line within a keep block is kept intact, so the following example
will not break within each
.Sx \&Op
macro line:
.Bd -literal -offset indent
\&.Bk \-words
\&.Op Fl f Ar flags
\&.Op Fl o Ar output
\&.Ek
.Ed
.Pp
Be careful in using over-long lines within a keep block!
Doing so will clobber the right margin.
.Ss \&Bl
Begins a list composed of one or more list entries.
Its syntax is as follows:
.Bd -ragged -offset indent
.Pf \. Sx \&Bl
.Fl type
.Op Fl width Ar val
.Op Fl offset Ar val
.Op Fl compact
.Op HEAD ...
.Ed
.Pp
A list is associated with a type, which is a required argument.
Other arguments are
.Fl width ,
@ -1586,9 +1681,26 @@ See also
and
.Sx \&Ux .
.Ss \&Ec
Close a scope started by
.Sx \&Eo .
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Ec Op Cm TERM
.Pp
The
.Cm TERM
argument is used as the enclosure tail, for example, specifying \e(rq
will emulate
.Sx \&Dc .
.Ss \&Ed
End a display context started by
.Sx \&Bd .
.Ss \&Ef
Ends a font mode context started by
.Sx \&Bf .
.Ss \&Ek
Ends a keep context started by
.Sx \&Bk .
.Ss \&El
Ends a list context started by
.Sx \&Bl .
@ -1606,7 +1718,18 @@ Examples:
.D1 \&.Em Warnings!
.D1 \&.Em Remarks :
.Ss \&En
This macro is obsolete and not implemented.
.Ss \&Eo
An arbitrary enclosure.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&Eo Op Cm TERM
.Pp
The
.Cm TERM
argument is used as the enclosure head, for example, specifying \e(lq
will emulate
.Sx \&Do .
.Ss \&Er
Display error constants.
.Pp
@ -1617,6 +1740,7 @@ Examples:
See also
.Sx \&Dv .
.Ss \&Es
This macro is obsolete and not implemented.
.Ss \&Ev
Environmental variables such as those specified in
.Xr environ 7 .
@ -1667,6 +1791,8 @@ Examples:
See also
.Sx \&Fo .
.Ss \&Fc
Ends a function context started by
.Sx \&Fo .
.Ss \&Fd
Historically used to document include files.
This usage has been deprecated in favour of
@ -1788,7 +1914,24 @@ See also
and
.Sx \&Ux .
.Ss \&Hf
This macro is obsolete and not implemented.
.Ss \&Ic
Designate an internal or interactive command.
This is similar to
.Sx \&Cm
but used for instructions rather than values.
.Pp
Examples:
.D1 \&.Ic hash
.D1 \&.Ic alias
.Pp
Note that using
.Sx \&Bd No Fl literal
or
.Sx \&D1
is preferred for displaying code; the
.Sx \&Ic
macro is used when referring to specific instructions.
.Ss \&In
An
.Qq include
@ -1911,6 +2054,9 @@ Examples:
.D1 \&.Lb libz
.D1 \&.Lb mdoc
.Ss \&Li
Denotes text that should be in a literal font mode.
Note that this is a presentation term and should not be used for
stylistically decorating technical terms.
.Ss \&Lk
Format a hyperlink.
Its syntax is as follows:
@ -1924,6 +2070,8 @@ Examples:
See also
.Sx \&Mt .
.Ss \&Lp
Synonym for
.Sx \&Pp .
.Ss \&Ms
.Ss \&Mt
Format a
@ -1936,8 +2084,73 @@ Its syntax is as follows:
Examples:
.D1 \&.Mt discuss@manpages.bsd.lv
.Ss \&Nd
A one-line description of the manual's content.
This may only be invoked in the
.Em SYNOPSIS
section subsequent the
.Sx \&Nm
macro.
.Pp
Examples:
.D1 \&.Sx \&Nd mdoc language reference
.D1 \&.Sx \&Nd format and display UNIX manuals
.Pp
The
.Sx \&Nd
macro technically accepts child macros and terminates with a subsequent
.Sx \&Sh
invocation.
Do not assume this behaviour: some
.Xr whatis 1
database generators are not smart enough to parse more than the line
arguments and will display macros verbatim.
.Pp
See also
.Sx \&Nm .
.Ss \&Nm
The name of the manual page, or \(em in particular in section 1, 6,
and 8 pages \(em of an additional command or feature documented in
the manual page.
When first invoked, the
.Sx \&Nm
macro expects a single argument, the name of the manual page.
Usually, the first invocation happens in the
.Em NAME
section of the page.
The specified name will be remembered and used whenever the macro is
called again without arguments later in the page.
The
.Sx \&Nm
macro uses
.Sx Block full-implicit
semantics when invoked as the first macro on an input line in the
.Em SYNOPSIS
section; otherwise, it uses ordinary
.Sx In-line
semantics.
.Pp
Examples:
.Bd -literal -offset indent
\&.Sh SYNOPSIS
\&.Nm cat
\&.Op Fl benstuv
\&.Op Ar
.Ed
.Pp
In the
.Em SYNOPSIS
of section 2, 3 and 9 manual pages, use the
.Sx \&Fn
macro rather than
.Sx \&Nm
to mark up the name of the manual page.
.Ss \&No
A
.Qq noop
macro used to terminate prior macro contexts.
.Pp
Examples:
.D1 \&.Sx \&Fl ab \&No cd \&Fl ef
.Ss \&Ns
.Ss \&Nx
Format the NetBSD version provided as an argument, or a default value if
@ -1957,8 +2170,30 @@ See also
and
.Sx \&Ux .
.Ss \&Oc
Closes multi-line
.Sx \&Oo
context.
.Ss \&Oo
Multi-line version of
.Sx \&Op .
.Pp
Examples:
.Bd -literal -offset indent
\&.Oo
\&.Op Fl flag Ns Ar value
\&.Oc
.Ed
.Ss \&Op
Command-line option.
Used when listing options to command-line utilities.
Prints the argument(s) in brackets.
.Pp
Examples:
.D1 \&.Op \&Fl a \&Ar b
.D1 \&.Op \&Ar a | b
.Pp
See also
.Sx \&Oo .
.Ss \&Os
Document operating system version.
This is the mandatory third macro of
@ -2007,11 +2242,43 @@ See also
and
.Sx \&Ux .
.Ss \&Pa
A file-system path.
.Pp
Examples:
.D1 \&.Pa /usr/bin/mandoc
.D1 \&.Pa /usr/share/man/man7/mdoc.7
.Pp
See also
.Sx \&Lk .
.Ss \&Pc
Close parenthesised context opened by
.Sx \&Po .
.Ss \&Pf
Removes the space
.Pq Qq prefix
between its arguments.
Its syntax is as follows:
.Pp
.D1 Pf \. \&Pf Cm prefix suffix
.Pp
The
.Cm suffix
argument may be a macro.
.Pp
Examples:
.D1 \&.Pf \e. \&Sx \&Pf \&Cm prefix suffix
.Ss \&Po
Multi-line version of
.Sx \&Pq .
.Ss \&Pp
Break a paragraph.
This will assert vertical space between prior and subsequent macros
and/or text.
.Ss \&Pq
Parenthesised enclosure.
.Pp
See also
.Sx \&Po .
.Ss \&Qc
.Ss \&Ql
.Ss \&Qo
@ -2092,6 +2359,11 @@ See also
and
.Sx \&Ox .
.Ss \&Va
A variable name.
.Pp
Examples:
.D1 \&.Va foo
.D1 \&.Va const char *bar ;
.Ss \&Vt
A variable type.
This is also used for indicating global variables in the
@ -2249,9 +2521,9 @@ Display offsets
and
.Fl offset Ar right
are disregarded in mandoc.
Furthermore, the
Furthermore, troff specifies a
.Fl file Ar file
argument is not supported in mandoc.
argument that is not supported in mandoc.
Lastly, since text is not right-justified in mandoc (or even groff),
.Fl ragged
and

View File

@ -1,6 +1,7 @@
/* $Vendor-Id: mdoc.c,v 1.146 2010/06/12 11:58:22 kristaps Exp $ */
/* $Vendor-Id: mdoc.c,v 1.158 2010/07/07 15:04:54 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -191,7 +192,8 @@ mdoc_free(struct mdoc *mdoc)
* Allocate volatile and non-volatile parse resources.
*/
struct mdoc *
mdoc_alloc(void *data, int pflags, mandocmsg msg)
mdoc_alloc(struct regset *regs, void *data,
int pflags, mandocmsg msg)
{
struct mdoc *p;
@ -200,6 +202,7 @@ mdoc_alloc(void *data, int pflags, mandocmsg msg)
p->msg = msg;
p->data = data;
p->pflags = pflags;
p->regs = regs;
mdoc_hash_init();
mdoc_alloc1(p);
@ -236,6 +239,20 @@ mdoc_parseln(struct mdoc *m, int ln, char *buf, int offs)
return(0);
m->flags |= MDOC_NEWLINE;
/*
* Let the roff nS register switch SYNOPSIS mode early,
* such that the parser knows at all times
* whether this mode is on or off.
* Note that this mode is also switched by the Sh macro.
*/
if (m->regs->regs[(int)REG_nS].set) {
if (m->regs->regs[(int)REG_nS].v.u)
m->flags |= MDOC_SYNOPSIS;
else
m->flags &= ~MDOC_SYNOPSIS;
}
return(('.' == buf[offs] || '\'' == buf[offs]) ?
mdoc_pmacro(m, ln, buf, offs) :
mdoc_ptext(m, ln, buf, offs));
@ -258,8 +275,7 @@ mdoc_vmsg(struct mdoc *mdoc, enum mandocerr t,
int
mdoc_macro(struct mdoc *m, enum mdoct tok,
int ln, int pp, int *pos, char *buf)
mdoc_macro(MACRO_PROT_ARGS)
{
assert(tok < MDOC_MAX);
@ -267,13 +283,13 @@ mdoc_macro(struct mdoc *m, enum mdoct tok,
if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
MDOC_PBODY & m->flags)
return(mdoc_pmsg(m, ln, pp, MANDOCERR_BADBODY));
return(mdoc_pmsg(m, line, ppos, MANDOCERR_BADBODY));
/* If we're in the prologue, deny "body" macros. */
if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
! (MDOC_PBODY & m->flags)) {
if ( ! mdoc_pmsg(m, ln, pp, MANDOCERR_BADPROLOG))
if ( ! mdoc_pmsg(m, line, ppos, MANDOCERR_BADPROLOG))
return(0);
if (NULL == m->meta.title)
m->meta.title = mandoc_strdup("UNKNOWN");
@ -286,7 +302,7 @@ mdoc_macro(struct mdoc *m, enum mdoct tok,
m->flags |= MDOC_PBODY;
}
return((*mdoc_macros[tok].fp)(m, tok, ln, pp, pos, buf));
return((*mdoc_macros[tok].fp)(m, tok, line, ppos, pos, buf));
}
@ -330,6 +346,8 @@ node_append(struct mdoc *mdoc, struct mdoc_node *p)
p->parent->tail = p;
break;
case (MDOC_BODY):
if (p->end)
break;
assert(MDOC_BLOCK == p->parent->type);
p->parent->body = p;
break;
@ -366,9 +384,17 @@ node_alloc(struct mdoc *m, int line, int pos,
p->pos = pos;
p->tok = tok;
p->type = type;
/* Flag analysis. */
if (MDOC_SYNOPSIS & m->flags)
p->flags |= MDOC_SYNPRETTY;
else
p->flags &= ~MDOC_SYNPRETTY;
if (MDOC_NEWLINE & m->flags)
p->flags |= MDOC_LINE;
m->flags &= ~MDOC_NEWLINE;
return(p);
}
@ -415,6 +441,22 @@ mdoc_body_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
}
int
mdoc_endbody_alloc(struct mdoc *m, int line, int pos, enum mdoct tok,
struct mdoc_node *body, enum mdoc_endbody end)
{
struct mdoc_node *p;
p = node_alloc(m, line, pos, tok, MDOC_BODY);
p->pending = body;
p->end = end;
if ( ! node_append(m, p))
return(0);
m->next = MDOC_NEXT_SIBLING;
return(1);
}
int
mdoc_block_alloc(struct mdoc *m, int line, int pos,
enum mdoct tok, struct mdoc_arg *args)
@ -472,10 +514,26 @@ mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p)
}
void
static void
mdoc_node_free(struct mdoc_node *p)
{
/*
* XXX: if these end up being problematic in terms of memory
* management and dereferencing freed blocks, then make them
* into reference-counted double-pointers.
*/
if (MDOC_Bd == p->tok && MDOC_BLOCK == p->type)
if (p->data.Bd)
free(p->data.Bd);
if (MDOC_Bl == p->tok && MDOC_BLOCK == p->type)
if (p->data.Bl)
free(p->data.Bl);
if (MDOC_Bf == p->tok && MDOC_HEAD == p->type)
if (p->data.Bf)
free(p->data.Bf);
if (p->string)
free(p->string);
if (p->args)
@ -568,7 +626,7 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs)
*/
if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
LIST_column == n->data.Bl.type) {
LIST_column == n->data.Bl->type) {
/* `Bl' is open without any children. */
m->flags |= MDOC_FREECOL;
return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf));
@ -577,7 +635,7 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs)
if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
NULL != n->parent &&
MDOC_Bl == n->parent->tok &&
LIST_column == n->parent->data.Bl.type) {
LIST_column == n->parent->data.Bl->type) {
/* `Bl' has block-level `It' children. */
m->flags |= MDOC_FREECOL;
return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf));
@ -689,7 +747,7 @@ macrowarn(struct mdoc *m, int ln, const char *buf, int offs)
* Parse a macro line, that is, a line beginning with the control
* character.
*/
int
static int
mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs)
{
enum mdoct tok;
@ -783,9 +841,9 @@ mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs)
*/
if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
LIST_column == n->data.Bl.type) {
LIST_column == n->data.Bl->type) {
m->flags |= MDOC_FREECOL;
if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf))
if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf))
goto err;
return(1);
}
@ -799,7 +857,7 @@ mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs)
if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
NULL != n->parent &&
MDOC_Bl == n->parent->tok &&
LIST_column == n->parent->data.Bl.type) {
LIST_column == n->parent->data.Bl->type) {
m->flags |= MDOC_FREECOL;
if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf))
goto err;

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: mdoc.h,v 1.90 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: mdoc.h,v 1.100 2010/07/04 21:59:30 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -155,6 +155,7 @@ enum mdoct {
/* What follows is a list of ALL possible macro arguments. */
/* FIXME: make this into an enum. */
#define MDOC_Split 0
#define MDOC_Nosplit 1
#define MDOC_Ragged 2
@ -248,6 +249,12 @@ struct mdoc_arg {
unsigned int refcnt;
};
enum mdoc_endbody {
ENDBODY_NOT = 0,
ENDBODY_SPACE,
ENDBODY_NOSPACE
};
enum mdoc_list {
LIST__NONE = 0,
LIST_bullet,
@ -272,6 +279,19 @@ enum mdoc_disp {
DISP_literal
};
enum mdoc_auth {
AUTH__NONE = 0,
AUTH_split,
AUTH_nosplit
};
enum mdoc_font {
FONT__NONE = 0,
FONT_Em,
FONT_Li,
FONT_Sy
};
struct mdoc_bd {
const char *offs; /* -offset */
enum mdoc_disp type; /* -ragged, etc. */
@ -283,6 +303,16 @@ struct mdoc_bl {
const char *offs; /* -offset */
enum mdoc_list type; /* -tag, -enum, etc. */
int comp; /* -compact */
size_t ncols; /* -column arg count */
const char **cols; /* -column val ptr */
};
struct mdoc_bf {
enum mdoc_font font; /* font */
};
struct mdoc_an {
enum mdoc_auth auth; /* -split, etc. */
};
/* Node in AST. */
@ -300,20 +330,24 @@ struct mdoc_node {
#define MDOC_ACTED (1 << 1) /* has been acted upon */
#define MDOC_EOS (1 << 2) /* at sentence boundary */
#define MDOC_LINE (1 << 3) /* first macro/text on line */
#define MDOC_SYNPRETTY (1 << 4) /* SYNOPSIS-style formatting */
#define MDOC_ENDED (1 << 5) /* rendering has been ended */
enum mdoc_type type; /* AST node type */
enum mdoc_sec sec; /* current named section */
/* FIXME: these can be union'd to shave a few bytes. */
struct mdoc_arg *args; /* BLOCK/ELEM */
#ifdef UGLY
struct mdoc_node *pending; /* BLOCK */
#endif
struct mdoc_node *head; /* BLOCK */
struct mdoc_node *body; /* BLOCK */
struct mdoc_node *tail; /* BLOCK */
char *string; /* TEXT */
enum mdoc_endbody end; /* BODY */
union {
struct mdoc_bl Bl;
struct mdoc_bd Bd;
struct mdoc_an An;
struct mdoc_bd *Bd;
struct mdoc_bf *Bf;
struct mdoc_bl *Bl;
} data;
};
@ -333,7 +367,7 @@ struct mdoc;
/* See mdoc.3 for documentation. */
void mdoc_free(struct mdoc *);
struct mdoc *mdoc_alloc(void *, int, mandocmsg);
struct mdoc *mdoc_alloc(struct regset *, void *, int, mandocmsg);
void mdoc_reset(struct mdoc *);
int mdoc_parseln(struct mdoc *, int, char *, int);
const struct mdoc_node *mdoc_node(const struct mdoc *);

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: mdoc_argv.c,v 1.54 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: mdoc_argv.c,v 1.55 2010/07/01 22:56:17 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -359,7 +359,8 @@ mdoc_args(struct mdoc *m, int line, int *pos,
if (MDOC_Bl == n->tok)
break;
if (n && LIST_column == n->data.Bl.type) {
assert(n->data.Bl);
if (n && LIST_column == n->data.Bl->type) {
fl |= ARGS_TABSEP;
fl &= ~ARGS_DELIM;
}

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: mdoc_html.c,v 1.85 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: mdoc_html.c,v 1.95 2010/07/07 15:04:54 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -72,6 +72,8 @@ static int mdoc_aq_pre(MDOC_ARGS);
static int mdoc_ar_pre(MDOC_ARGS);
static int mdoc_bd_pre(MDOC_ARGS);
static int mdoc_bf_pre(MDOC_ARGS);
static void mdoc_bk_post(MDOC_ARGS);
static int mdoc_bk_pre(MDOC_ARGS);
static void mdoc_bl_post(MDOC_ARGS);
static int mdoc_bl_pre(MDOC_ARGS);
static void mdoc_bq_post(MDOC_ARGS);
@ -236,7 +238,7 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = {
{NULL, NULL}, /* Fc */
{mdoc_op_pre, mdoc_op_post}, /* Oo */
{NULL, NULL}, /* Oc */
{NULL, NULL}, /* Bk */
{mdoc_bk_pre, mdoc_bk_post}, /* Bk */
{NULL, NULL}, /* Ek */
{mdoc_bt_pre, NULL}, /* Bt */
{NULL, NULL}, /* Hf */
@ -303,7 +305,7 @@ synopsis_pre(struct html *h, const struct mdoc_node *n)
struct roffsu su;
struct htmlpair tag;
if (NULL == n->prev || SEC_SYNOPSIS != n->sec)
if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
return;
SCALE_VS_INIT(&su, 1);
@ -436,11 +438,23 @@ print_mdoc_node(MDOC_ARGS)
print_text(h, n->string);
return;
default:
if (mdocs[n->tok].pre)
if (mdocs[n->tok].pre && ENDBODY_NOT == n->end)
child = (*mdocs[n->tok].pre)(m, n, h);
break;
}
if (HTML_KEEP & h->flags) {
if (n->prev && n->prev->line != n->line) {
h->flags &= ~HTML_KEEP;
h->flags |= HTML_PREKEEP;
} else if (NULL == n->prev) {
if (n->parent && n->parent->line != n->line) {
h->flags &= ~HTML_KEEP;
h->flags |= HTML_PREKEEP;
}
}
}
if (child && n->child)
print_mdoc_nodelist(m, n->child, h);
@ -452,7 +466,7 @@ print_mdoc_node(MDOC_ARGS)
mdoc_root_post(m, n, h);
break;
default:
if (mdocs[n->tok].post)
if (mdocs[n->tok].post && ENDBODY_NOT == n->end)
(*mdocs[n->tok].post)(m, n, h);
break;
}
@ -735,17 +749,70 @@ mdoc_op_post(MDOC_ARGS)
static int
mdoc_nm_pre(MDOC_ARGS)
{
struct htmlpair tag;
struct htmlpair tag;
struct roffsu su;
const char *cp;
if (NULL == n->child && NULL == m->name)
return(1);
/*
* Accomodate for `Nm' being both an element (which may have
* NULL children AND no m->name) and a block.
*/
synopsis_pre(h, n);
cp = NULL;
if (MDOC_ELEM == n->type) {
if (NULL == n->child && NULL == m->name)
return(1);
synopsis_pre(h, n);
PAIR_CLASS_INIT(&tag, "name");
print_otag(h, TAG_SPAN, 1, &tag);
if (NULL == n->child)
print_text(h, m->name);
} else if (MDOC_BLOCK == n->type) {
synopsis_pre(h, n);
bufcat_style(h, "clear", "both");
if (n->head->child || m->name) {
if (n->head->child && MDOC_TEXT ==
n->head->child->type)
cp = n->head->child->string;
if (NULL == cp || '\0' == *cp)
cp = m->name;
SCALE_HS_INIT(&su, (double)strlen(cp));
bufcat_su(h, "padding-left", &su);
}
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
} else if (MDOC_HEAD == n->type) {
if (NULL == n->child && NULL == m->name)
return(1);
if (n->child && MDOC_TEXT == n->child->type)
cp = n->child->string;
if (NULL == cp || '\0' == *cp)
cp = m->name;
SCALE_HS_INIT(&su, (double)strlen(cp));
bufcat_style(h, "float", "left");
bufcat_su(h, "min-width", &su);
SCALE_INVERT(&su);
bufcat_su(h, "margin-left", &su);
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
if (NULL == n->child)
print_text(h, m->name);
} else if (MDOC_BODY == n->type) {
SCALE_HS_INIT(&su, 2);
bufcat_su(h, "margin-left", &su);
PAIR_STYLE_INIT(&tag, h);
print_otag(h, TAG_DIV, 1, &tag);
}
PAIR_CLASS_INIT(&tag, "name");
print_otag(h, TAG_SPAN, 1, &tag);
if (NULL == n->child)
print_text(h, m->name);
return(1);
}
@ -1020,7 +1087,7 @@ mdoc_it_head_pre(MDOC_ARGS, enum mdoc_list type, struct roffsu *width)
static int
mdoc_it_pre(MDOC_ARGS)
{
int i, wp, comp;
int i, comp;
const struct mdoc_node *bl, *nn;
struct roffsu width, offs;
enum mdoc_list type;
@ -1037,11 +1104,12 @@ mdoc_it_pre(MDOC_ARGS)
SCALE_HS_INIT(&offs, 0);
type = bl->data.Bl.type;
comp = bl->data.Bl.comp;
assert(bl->data.Bl);
type = bl->data.Bl->type;
comp = bl->data.Bl->comp;
if (bl->data.Bl.offs)
a2offs(bl->data.Bl.offs, &offs);
if (bl->data.Bl->offs)
a2offs(bl->data.Bl->offs, &offs);
switch (type) {
case (LIST_enum):
@ -1058,18 +1126,8 @@ mdoc_it_pre(MDOC_ARGS)
break;
}
if (bl->data.Bl.width)
a2width(bl->data.Bl.width, &width);
wp = -1;
for (i = 0; bl->args && i < (int)bl->args->argc; i++)
switch (bl->args->argv[i].arg) {
case (MDOC_Column):
wp = i; /* Save for later. */
break;
default:
break;
}
if (bl->data.Bl->width)
a2width(bl->data.Bl->width, &width);
/* Override width in some cases. */
@ -1094,8 +1152,8 @@ mdoc_it_pre(MDOC_ARGS)
for (i = 0; nn && nn != n; nn = nn->next)
if (MDOC_BODY == nn->type)
i++;
if (i < (int)bl->args->argv[wp].sz)
a2width(bl->args->argv[wp].value[i], &width);
if (i < (int)bl->data.Bl->ncols)
a2width(bl->data.Bl->cols[i], &width);
}
if (MDOC_HEAD == n->type)
@ -1117,7 +1175,8 @@ mdoc_bl_pre(MDOC_ARGS)
return(0);
if (MDOC_BLOCK != n->type)
return(1);
if (LIST_enum != n->data.Bl.type)
assert(n->data.Bl);
if (LIST_enum != n->data.Bl->type)
return(1);
ord = malloc(sizeof(struct ord));
@ -1141,7 +1200,7 @@ mdoc_bl_post(MDOC_ARGS)
if (MDOC_BLOCK != n->type)
return;
if (LIST_enum != n->data.Bl.type)
if (LIST_enum != n->data.Bl->type)
return;
ord = h->ords.head;
@ -1356,10 +1415,11 @@ mdoc_bd_pre(MDOC_ARGS)
SCALE_VS_INIT(&su, 0);
if (n->data.Bd.offs)
a2offs(n->data.Bd.offs, &su);
assert(n->data.Bd);
if (n->data.Bd->offs)
a2offs(n->data.Bd->offs, &su);
comp = n->data.Bd.comp;
comp = n->data.Bd->comp;
/* FIXME: -centered, etc. formatting. */
/* FIXME: does not respect -offset ??? */
@ -1386,8 +1446,8 @@ mdoc_bd_pre(MDOC_ARGS)
return(1);
}
if (DISP_unfilled != n->data.Bd.type &&
DISP_literal != n->data.Bd.type)
if (DISP_unfilled != n->data.Bd->type &&
DISP_literal != n->data.Bd->type)
return(1);
PAIR_CLASS_INIT(&tag[0], "lit");
@ -1613,7 +1673,7 @@ mdoc_fn_pre(MDOC_ARGS)
*/
#if 0
if (SEC_SYNOPSIS == n->sec) {
if (MDOC_SYNPRETTY & n->flags) {
nbuf[0] = '\0';
html_idcat(nbuf, sp, BUFSIZ);
PAIR_ID_INIT(&tag[1], nbuf);
@ -1643,7 +1703,7 @@ mdoc_fn_pre(MDOC_ARGS)
for (nn = n->child->next; nn; nn = nn->next) {
i = 1;
if (SEC_SYNOPSIS == n->sec)
if (MDOC_SYNPRETTY & n->flags)
i = 2;
t = print_otag(h, TAG_SPAN, i, tag);
print_text(h, nn->string);
@ -1653,7 +1713,7 @@ mdoc_fn_pre(MDOC_ARGS)
}
print_text(h, ")");
if (SEC_SYNOPSIS == n->sec)
if (MDOC_SYNPRETTY & n->flags)
print_text(h, ";");
return(0);
@ -1823,7 +1883,7 @@ mdoc_in_pre(MDOC_ARGS)
PAIR_CLASS_INIT(&tag[0], "includes");
print_otag(h, TAG_SPAN, 1, tag);
if (SEC_SYNOPSIS == n->sec && MDOC_LINE & n->flags)
if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags)
print_text(h, "#include");
print_text(h, "<");
@ -1958,46 +2018,33 @@ mdoc_ap_pre(MDOC_ARGS)
static int
mdoc_bf_pre(MDOC_ARGS)
{
int i;
struct htmlpair tag[2];
struct roffsu su;
if (MDOC_HEAD == n->type)
return(0);
else if (MDOC_BLOCK != n->type)
else if (MDOC_BODY != n->type)
return(1);
PAIR_CLASS_INIT(&tag[0], "lit");
assert(n->data.Bf);
if (n->head->child) {
if ( ! strcmp("Em", n->head->child->string))
PAIR_CLASS_INIT(&tag[0], "emph");
else if ( ! strcmp("Sy", n->head->child->string))
PAIR_CLASS_INIT(&tag[0], "symb");
else if ( ! strcmp("Li", n->head->child->string))
PAIR_CLASS_INIT(&tag[0], "lit");
} else {
for (i = 0; n->args && i < (int)n->args->argc; i++)
switch (n->args->argv[i].arg) {
case (MDOC_Symbolic):
PAIR_CLASS_INIT(&tag[0], "symb");
break;
case (MDOC_Literal):
PAIR_CLASS_INIT(&tag[0], "lit");
break;
case (MDOC_Emphasis):
PAIR_CLASS_INIT(&tag[0], "emph");
break;
default:
break;
}
}
/* FIXME: div's have spaces stripped--we want them. */
if (FONT_Em == n->data.Bf->font)
PAIR_CLASS_INIT(&tag[0], "emph");
else if (FONT_Sy == n->data.Bf->font)
PAIR_CLASS_INIT(&tag[0], "symb");
else if (FONT_Li == n->data.Bf->font)
PAIR_CLASS_INIT(&tag[0], "lit");
else
PAIR_CLASS_INIT(&tag[0], "none");
/*
* We want this to be inline-formatted, but needs to be div to
* accept block children.
*/
bufcat_style(h, "display", "inline");
SCALE_HS_INIT(&su, 1);
bufcat_su(h, "margin-right", &su);
/* Needs a left-margin for spacing. */
bufcat_su(h, "margin-left", &su);
PAIR_STYLE_INIT(&tag[1], h);
print_otag(h, TAG_DIV, 2, tag);
return(1);
@ -2193,3 +2240,35 @@ mdoc__x_post(MDOC_ARGS)
h->flags |= HTML_NOSPACE;
print_text(h, n->next ? "," : ".");
}
/* ARGSUSED */
static int
mdoc_bk_pre(MDOC_ARGS)
{
switch (n->type) {
case (MDOC_BLOCK):
break;
case (MDOC_HEAD):
return(0);
case (MDOC_BODY):
h->flags |= HTML_PREKEEP;
break;
default:
abort();
/* NOTREACHED */
}
return(1);
}
/* ARGSUSED */
static void
mdoc_bk_post(MDOC_ARGS)
{
if (MDOC_BODY == n->type)
h->flags &= ~(HTML_KEEP | HTML_PREKEEP);
}

View File

@ -1,6 +1,7 @@
/* $Vendor-Id: mdoc_macro.c,v 1.80 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: mdoc_macro.c,v 1.92 2010/07/04 22:04:04 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -29,10 +30,12 @@
#include "libmdoc.h"
#include "libmandoc.h"
enum rew {
REWIND_REWIND,
REWIND_NOHALT,
REWIND_HALT
enum rew { /* see rew_dohalt() */
REWIND_NONE,
REWIND_THIS,
REWIND_MORE,
REWIND_LATER,
REWIND_ERROR
};
static int blk_full(MACRO_PROT_ARGS);
@ -50,10 +53,10 @@ static int append_delims(struct mdoc *,
int, int *, char *);
static enum mdoct lookup(enum mdoct, const char *);
static enum mdoct lookup_raw(const char *);
static int make_pending(struct mdoc_node *, enum mdoct,
struct mdoc *, int, int);
static int phrase(struct mdoc *, int, int, char *);
static enum mdoct rew_alt(enum mdoct);
static int rew_dobreak(enum mdoct,
const struct mdoc_node *);
static enum rew rew_dohalt(enum mdoct, enum mdoc_type,
const struct mdoc_node *);
static int rew_elem(struct mdoc *, enum mdoct);
@ -61,8 +64,6 @@ static int rew_last(struct mdoc *,
const struct mdoc_node *);
static int rew_sub(enum mdoc_type, struct mdoc *,
enum mdoct, int, int);
static int swarn(struct mdoc *, enum mdoc_type, int,
int, const struct mdoc_node *);
const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ap */
@ -97,7 +98,7 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* In */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Li */
{ blk_full, 0 }, /* Nd */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */
{ ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */
{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Op */
{ obsolete, 0 }, /* Ot */
{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */
@ -192,53 +193,6 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
const struct mdoc_macro * const mdoc_macros = __mdoc_macros;
static int
swarn(struct mdoc *mdoc, enum mdoc_type type,
int line, int pos, const struct mdoc_node *p)
{
const char *n, *t, *tt;
enum mandocerr ec;
n = t = "<root>";
tt = "block";
switch (type) {
case (MDOC_BODY):
tt = "multi-line";
break;
case (MDOC_HEAD):
tt = "line";
break;
default:
break;
}
switch (p->type) {
case (MDOC_BLOCK):
n = mdoc_macronames[p->tok];
t = "block";
break;
case (MDOC_BODY):
n = mdoc_macronames[p->tok];
t = "multi-line";
break;
case (MDOC_HEAD):
n = mdoc_macronames[p->tok];
t = "line";
break;
default:
break;
}
ec = (MDOC_IGN_SCOPE & mdoc->pflags) ?
MANDOCERR_SCOPE : MANDOCERR_SYNTSCOPE;
return(mdoc_vmsg(mdoc, ec, line, pos,
"%s scope breaks %s of %s",
tt, t, n));
}
/*
* This is called at the end of parsing. It must traverse up the tree,
* closing out open [implicit] scopes. Obviously, open explicit scopes
@ -322,8 +276,8 @@ rew_last(struct mdoc *mdoc, const struct mdoc_node *to)
/*
* Return the opening macro of a closing one, e.g., `Ec' has `Eo' as its
* matching pair.
* For a block closing macro, return the corresponding opening one.
* Otherwise, return the macro itself.
*/
static enum mdoct
rew_alt(enum mdoct tok)
@ -362,202 +316,121 @@ rew_alt(enum mdoct tok)
case (MDOC_Xc):
return(MDOC_Xo);
default:
break;
return(tok);
}
abort();
/* NOTREACHED */
}
/*
* Rewind rules. This indicates whether to stop rewinding
* (REWIND_HALT) without touching our current scope, stop rewinding and
* close our current scope (REWIND_REWIND), or continue (REWIND_NOHALT).
* The scope-closing and so on occurs in the various rew_* routines.
/*
* Rewinding to tok, how do we have to handle *p?
* REWIND_NONE: *p would delimit tok, but no tok scope is open
* inside *p, so there is no need to rewind anything at all.
* REWIND_THIS: *p matches tok, so rewind *p and nothing else.
* REWIND_MORE: *p is implicit, rewind it and keep searching for tok.
* REWIND_LATER: *p is explicit and still open, postpone rewinding.
* REWIND_ERROR: No tok block is open at all.
*/
static enum rew
rew_dohalt(enum mdoct tok, enum mdoc_type type,
const struct mdoc_node *p)
{
/*
* No matching token, no delimiting block, no broken block.
* This can happen when full implicit macros are called for
* the first time but try to rewind their previous
* instance anyway.
*/
if (MDOC_ROOT == p->type)
return(REWIND_HALT);
if (MDOC_VALID & p->flags)
return(REWIND_NOHALT);
return(MDOC_BLOCK == type &&
MDOC_EXPLICIT & mdoc_macros[tok].flags ?
REWIND_ERROR : REWIND_NONE);
switch (tok) {
case (MDOC_Aq):
/* FALLTHROUGH */
case (MDOC_Bq):
/* FALLTHROUGH */
case (MDOC_Brq):
/* FALLTHROUGH */
case (MDOC_D1):
/* FALLTHROUGH */
case (MDOC_Dl):
/* FALLTHROUGH */
case (MDOC_Dq):
/* FALLTHROUGH */
case (MDOC_Op):
/* FALLTHROUGH */
case (MDOC_Pq):
/* FALLTHROUGH */
case (MDOC_Ql):
/* FALLTHROUGH */
case (MDOC_Qq):
/* FALLTHROUGH */
case (MDOC_Sq):
/* FALLTHROUGH */
case (MDOC_Vt):
assert(MDOC_TAIL != type);
if (type == p->type && tok == p->tok)
return(REWIND_REWIND);
break;
case (MDOC_It):
assert(MDOC_TAIL != type);
if (type == p->type && tok == p->tok)
return(REWIND_REWIND);
if (MDOC_BODY == p->type && MDOC_Bl == p->tok)
return(REWIND_HALT);
break;
case (MDOC_Sh):
if (type == p->type && tok == p->tok)
return(REWIND_REWIND);
break;
case (MDOC_Nd):
/* FALLTHROUGH */
case (MDOC_Ss):
assert(MDOC_TAIL != type);
if (type == p->type && tok == p->tok)
return(REWIND_REWIND);
if (MDOC_BODY == p->type && MDOC_Sh == p->tok)
return(REWIND_HALT);
break;
case (MDOC_Ao):
/* FALLTHROUGH */
case (MDOC_Bd):
/* FALLTHROUGH */
case (MDOC_Bf):
/* FALLTHROUGH */
case (MDOC_Bk):
/* FALLTHROUGH */
case (MDOC_Bl):
/* FALLTHROUGH */
case (MDOC_Bo):
/* FALLTHROUGH */
case (MDOC_Bro):
/* FALLTHROUGH */
case (MDOC_Do):
/* FALLTHROUGH */
case (MDOC_Eo):
/* FALLTHROUGH */
case (MDOC_Fo):
/* FALLTHROUGH */
case (MDOC_Oo):
/* FALLTHROUGH */
case (MDOC_Po):
/* FALLTHROUGH */
case (MDOC_Qo):
/* FALLTHROUGH */
case (MDOC_Rs):
/* FALLTHROUGH */
case (MDOC_So):
/* FALLTHROUGH */
case (MDOC_Xo):
if (type == p->type && tok == p->tok)
return(REWIND_REWIND);
break;
/* Multi-line explicit scope close. */
case (MDOC_Ac):
/* FALLTHROUGH */
case (MDOC_Bc):
/* FALLTHROUGH */
case (MDOC_Brc):
/* FALLTHROUGH */
case (MDOC_Dc):
/* FALLTHROUGH */
case (MDOC_Ec):
/* FALLTHROUGH */
case (MDOC_Ed):
/* FALLTHROUGH */
case (MDOC_Ek):
/* FALLTHROUGH */
case (MDOC_El):
/* FALLTHROUGH */
case (MDOC_Fc):
/* FALLTHROUGH */
case (MDOC_Ef):
/* FALLTHROUGH */
case (MDOC_Oc):
/* FALLTHROUGH */
case (MDOC_Pc):
/* FALLTHROUGH */
case (MDOC_Qc):
/* FALLTHROUGH */
case (MDOC_Re):
/* FALLTHROUGH */
case (MDOC_Sc):
/* FALLTHROUGH */
case (MDOC_Xc):
if (type == p->type && rew_alt(tok) == p->tok)
return(REWIND_REWIND);
break;
default:
abort();
/* NOTREACHED */
}
/*
* When starting to rewind, skip plain text
* and nodes that have already been rewound.
*/
if (MDOC_TEXT == p->type || MDOC_VALID & p->flags)
return(REWIND_MORE);
return(REWIND_NOHALT);
}
/*
* The easiest case: Found a matching token.
* This applies to both blocks and elements.
*/
tok = rew_alt(tok);
if (tok == p->tok)
return(p->end ? REWIND_NONE :
type == p->type ? REWIND_THIS : REWIND_MORE);
/*
* See if we can break an encountered scope (the rew_dohalt has returned
* REWIND_NOHALT).
*/
static int
rew_dobreak(enum mdoct tok, const struct mdoc_node *p)
{
assert(MDOC_ROOT != p->type);
/*
* While elements do require rewinding for themselves,
* they never affect rewinding of other nodes.
*/
if (MDOC_ELEM == p->type)
return(1);
if (MDOC_TEXT == p->type)
return(1);
if (MDOC_VALID & p->flags)
return(1);
return(REWIND_MORE);
/*
* Blocks delimited by our target token get REWIND_MORE.
* Blocks delimiting our target token get REWIND_NONE.
*/
switch (tok) {
case (MDOC_It):
return(MDOC_It == p->tok);
case (MDOC_Nd):
return(MDOC_Nd == p->tok);
case (MDOC_Ss):
return(MDOC_Ss == p->tok);
case (MDOC_Sh):
if (MDOC_Nd == p->tok)
return(1);
if (MDOC_Ss == p->tok)
return(1);
return(MDOC_Sh == p->tok);
case (MDOC_El):
case (MDOC_Bl):
if (MDOC_It == p->tok)
return(1);
return(REWIND_MORE);
break;
case (MDOC_Oc):
case (MDOC_It):
if (MDOC_BODY == p->type && MDOC_Bl == p->tok)
return(REWIND_NONE);
break;
/*
* XXX Badly nested block handling still fails badly
* when one block is breaking two blocks of the same type.
* This is an incomplete and extremely ugly workaround,
* required to let the OpenBSD tree build.
*/
case (MDOC_Oo):
if (MDOC_Op == p->tok)
return(1);
return(REWIND_MORE);
break;
case (MDOC_Nm):
return(REWIND_NONE);
case (MDOC_Nd):
/* FALLTHROUGH */
case (MDOC_Ss):
if (MDOC_BODY == p->type && MDOC_Sh == p->tok)
return(REWIND_NONE);
/* FALLTHROUGH */
case (MDOC_Sh):
if (MDOC_Nd == p->tok || MDOC_Ss == p->tok ||
MDOC_Sh == p->tok)
return(REWIND_MORE);
break;
default:
break;
}
if (MDOC_EXPLICIT & mdoc_macros[tok].flags)
return(p->tok == rew_alt(tok));
else if (MDOC_BLOCK == p->type)
return(1);
/*
* Default block rewinding rules.
* In particular, always skip block end markers,
* and let all blocks rewind Nm children.
*/
if (ENDBODY_NOT != p->end || MDOC_Nm == p->tok ||
(MDOC_BLOCK == p->type &&
! (MDOC_EXPLICIT & mdoc_macros[tok].flags)))
return(REWIND_MORE);
return(tok == p->tok);
/*
* Partial blocks allow delayed rewinding by default.
*/
if (&blk_full != mdoc_macros[tok].fp)
return (REWIND_LATER);
/*
* Full blocks can only be rewound when matching
* or when there is an explicit rule.
*/
return(REWIND_ERROR);
}
@ -576,51 +449,128 @@ rew_elem(struct mdoc *mdoc, enum mdoct tok)
}
/*
* We are trying to close a block identified by tok,
* but the child block *broken is still open.
* Thus, postpone closing the tok block
* until the rew_sub call closing *broken.
*/
static int
make_pending(struct mdoc_node *broken, enum mdoct tok,
struct mdoc *m, int line, int ppos)
{
struct mdoc_node *breaker;
/*
* Iterate backwards, searching for the block matching tok,
* that is, the block breaking the *broken block.
*/
for (breaker = broken->parent; breaker; breaker = breaker->parent) {
/*
* If the *broken block had already been broken before
* and we encounter its breaker, make the tok block
* pending on the inner breaker.
* Graphically, "[A breaker=[B broken=[C->B B] tok=A] C]"
* becomes "[A broken=[B [C->B B] tok=A] C]"
* and finally "[A [B->A [C->B B] A] C]".
*/
if (breaker == broken->pending) {
broken = breaker;
continue;
}
if (REWIND_THIS != rew_dohalt(tok, MDOC_BLOCK, breaker))
continue;
if (MDOC_BODY == broken->type)
broken = broken->parent;
/*
* Found the breaker.
* If another, outer breaker is already pending on
* the *broken block, we must not clobber the link
* to the outer breaker, but make it pending on the
* new, now inner breaker.
* Graphically, "[A breaker=[B broken=[C->A A] tok=B] C]"
* becomes "[A breaker=[B->A broken=[C A] tok=B] C]"
* and finally "[A [B->A [C->B A] B] C]".
*/
if (broken->pending) {
struct mdoc_node *taker;
/*
* If the breaker had also been broken before,
* it cannot take on the outer breaker itself,
* but must hand it on to its own breakers.
* Graphically, this is the following situation:
* "[A [B breaker=[C->B B] broken=[D->A A] tok=C] D]"
* "[A taker=[B->A breaker=[C->B B] [D->C A] C] D]"
*/
taker = breaker;
while (taker->pending)
taker = taker->pending;
taker->pending = broken->pending;
}
broken->pending = breaker;
mdoc_vmsg(m, MANDOCERR_SCOPENEST, line, ppos,
"%s breaks %s", mdoc_macronames[tok],
mdoc_macronames[broken->tok]);
return(1);
}
/*
* Found no matching block for tok.
* Are you trying to close a block that is not open?
* XXX Make this non-fatal.
*/
mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTNOSCOPE);
return(0);
}
static int
rew_sub(enum mdoc_type t, struct mdoc *m,
enum mdoct tok, int line, int ppos)
{
struct mdoc_node *n;
enum rew c;
/* LINTED */
for (n = m->last; n; n = n->parent) {
c = rew_dohalt(tok, t, n);
if (REWIND_HALT == c) {
if (MDOC_BLOCK != t)
return(1);
if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags))
return(1);
/* FIXME: shouldn't raise an error */
mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTNOSCOPE);
return(0);
}
if (REWIND_REWIND == c)
n = m->last;
while (n) {
switch (rew_dohalt(tok, t, n)) {
case (REWIND_NONE):
return(1);
case (REWIND_THIS):
break;
else if (rew_dobreak(tok, n))
case (REWIND_MORE):
n = n->parent;
continue;
if ( ! swarn(m, t, line, ppos, n))
return(0);
case (REWIND_LATER):
return(make_pending(n, tok, m, line, ppos));
case (REWIND_ERROR):
/* XXX Make this non-fatal. */
mdoc_vmsg(m, MANDOCERR_SCOPEFATAL, line, ppos,
"%s cannot break %s", mdoc_macronames[tok],
mdoc_macronames[n->tok]);
return 0;
}
break;
}
assert(n);
if ( ! rew_last(m, n))
return(0);
#ifdef UGLY
/*
* The current block extends an enclosing block beyond a line
* break. Now that the current block ends, close the enclosing
* block, too.
* The current block extends an enclosing block.
* Now that the current block ends, close the enclosing block, too.
*/
if (NULL != (n = n->pending)) {
assert(MDOC_HEAD == n->type);
while (NULL != (n = n->pending)) {
if ( ! rew_last(m, n))
return(0);
if ( ! mdoc_body_alloc(m, n->line, n->pos, n->tok))
if (MDOC_HEAD == n->type &&
! mdoc_body_alloc(m, n->line, n->pos, n->tok))
return(0);
}
#endif
return(1);
}
@ -674,9 +624,13 @@ append_delims(struct mdoc *m, int line, int *pos, char *buf)
static int
blk_exp_close(MACRO_PROT_ARGS)
{
struct mdoc_node *body; /* Our own body. */
struct mdoc_node *later; /* A sub-block starting later. */
struct mdoc_node *n; /* For searching backwards. */
int j, lastarg, maxargs, flushed, nl;
enum margserr ac;
enum mdoct ntok;
enum mdoct atok, ntok;
char *p;
nl = MDOC_NEWLINE & m->flags;
@ -690,6 +644,68 @@ blk_exp_close(MACRO_PROT_ARGS)
break;
}
/*
* Search backwards for beginnings of blocks,
* both of our own and of pending sub-blocks.
*/
atok = rew_alt(tok);
body = later = NULL;
for (n = m->last; n; n = n->parent) {
if (MDOC_VALID & n->flags)
continue;
/* Remember the start of our own body. */
if (MDOC_BODY == n->type && atok == n->tok) {
if (ENDBODY_NOT == n->end)
body = n;
continue;
}
if (MDOC_BLOCK != n->type || MDOC_Nm == n->tok)
continue;
if (atok == n->tok) {
assert(body);
/*
* Found the start of our own block.
* When there is no pending sub block,
* just proceed to closing out.
*/
if (NULL == later)
break;
/*
* When there is a pending sub block,
* postpone closing out the current block
* until the rew_sub() closing out the sub-block.
*/
if ( ! make_pending(later, tok, m, line, ppos))
return(0);
/*
* Mark the place where the formatting - but not
* the scope - of the current block ends.
*/
if ( ! mdoc_endbody_alloc(m, line, ppos,
atok, body, ENDBODY_SPACE))
return(0);
break;
}
/*
* When finding an open sub block, remember the last
* open explicit block, or, in case there are only
* implicit ones, the first open implicit block.
*/
if (later &&
MDOC_EXPLICIT & mdoc_macros[later->tok].flags)
continue;
if (MDOC_CALLABLE & mdoc_macros[n->tok].flags) {
assert( ! (MDOC_ACTED & n->flags));
later = n;
}
}
if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) {
/* FIXME: do this in validate */
if (buf[*pos])
@ -704,7 +720,7 @@ blk_exp_close(MACRO_PROT_ARGS)
if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
return(0);
if (maxargs > 0)
if (NULL == later && maxargs > 0)
if ( ! mdoc_tail_alloc(m, line, ppos, rew_alt(tok)))
return(0);
@ -936,9 +952,7 @@ blk_full(MACRO_PROT_ARGS)
struct mdoc_arg *arg;
struct mdoc_node *head; /* save of head macro */
struct mdoc_node *body; /* save of body macro */
#ifdef UGLY
struct mdoc_node *n;
#endif
enum mdoc_type mtt;
enum mdoct ntok;
enum margserr ac, lac;
@ -1012,6 +1026,9 @@ blk_full(MACRO_PROT_ARGS)
lac = ARGS_ERROR == ac ? ARGS_PEND : ac;
ac = mdoc_args(m, line, pos, buf, tok, &p);
if (ARGS_PUNCT == ac)
break;
if (ARGS_ERROR == ac)
return(0);
@ -1120,7 +1137,6 @@ blk_full(MACRO_PROT_ARGS)
if (NULL != body)
goto out;
#ifdef UGLY
/*
* If there is an open (i.e., unvalidated) sub-block requiring
* explicit close-out, postpone switching the current block from
@ -1136,7 +1152,6 @@ blk_full(MACRO_PROT_ARGS)
return(1);
}
}
#endif
/* Close out scopes to remain in a consistent state. */
@ -1261,22 +1276,36 @@ blk_part_imp(MACRO_PROT_ARGS)
body->parent->flags |= MDOC_EOS;
}
/*
* If there is an open sub-block requiring explicit close-out,
* postpone closing out the current block
* until the rew_sub() call closing out the sub-block.
*/
for (n = m->last; n && n != body && n != blk->parent; n = n->parent) {
if (MDOC_BLOCK == n->type &&
MDOC_EXPLICIT & mdoc_macros[n->tok].flags &&
! (MDOC_VALID & n->flags)) {
assert( ! (MDOC_ACTED & n->flags));
if ( ! make_pending(n, tok, m, line, ppos))
return(0);
if ( ! mdoc_endbody_alloc(m, line, ppos,
tok, body, ENDBODY_NOSPACE))
return(0);
return(1);
}
}
/*
* If we can't rewind to our body, then our scope has already
* been closed by another macro (like `Oc' closing `Op'). This
* is ugly behaviour nodding its head to OpenBSD's overwhelming
* crufty use of `Op' breakage.
*
* FIXME - this should be ifdef'd OpenBSD?
*/
for (n = m->last; n; n = n->parent)
if (body == n)
break;
if (NULL == n && ! mdoc_nmsg(m, body, MANDOCERR_SCOPE))
if (n != body && ! mdoc_vmsg(m, MANDOCERR_SCOPENEST,
line, ppos, "%s broken", mdoc_macronames[tok]))
return(0);
if (n && ! rew_last(m, body))
if (n && ! rew_sub(MDOC_BODY, m, tok, line, ppos))
return(0);
/* Standard appending of delimiters. */
@ -1286,7 +1315,7 @@ blk_part_imp(MACRO_PROT_ARGS)
/* Rewind scope, if applicable. */
if (n && ! rew_last(m, blk))
if (n && ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
return(0);
return(1);
@ -1554,6 +1583,9 @@ in_line_eoln(MACRO_PROT_ARGS)
assert( ! (MDOC_PARSED & mdoc_macros[tok].flags));
if (tok == MDOC_Pp)
rew_sub(MDOC_BLOCK, m, MDOC_Nm, line, ppos);
/* Parse macro arguments. */
for (arg = NULL; ; ) {
@ -1617,7 +1649,7 @@ ctx_synopsis(MACRO_PROT_ARGS)
nl = MDOC_NEWLINE & m->flags;
/* If we're not in the SYNOPSIS, go straight to in-line. */
if (SEC_SYNOPSIS != m->lastsec)
if ( ! (MDOC_SYNOPSIS & m->flags))
return(in_line(m, tok, line, ppos, pos, buf));
/* If we're a nested call, same place. */
@ -1629,7 +1661,9 @@ ctx_synopsis(MACRO_PROT_ARGS)
* up formatting the block scope, then child nodes will inherit
* the formatting. Be careful.
*/
if (MDOC_Nm == tok)
return(blk_full(m, tok, line, ppos, pos, buf));
assert(MDOC_Vt == tok);
return(blk_part_imp(m, tok, line, ppos, pos, buf));
}

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: mdoc_validate.c,v 1.99 2010/06/13 21:02:49 kristaps Exp $ */
/* $Vendor-Id: mdoc_validate.c,v 1.109 2010/07/04 21:59:30 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -107,7 +107,7 @@ static int pre_ss(PRE_ARGS);
static v_post posts_an[] = { post_an, NULL };
static v_post posts_at[] = { post_at, NULL };
static v_post posts_bd[] = { hwarn_eq0, bwarn_ge1, NULL };
static v_post posts_bd_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
static v_post posts_bf[] = { hwarn_le1, post_bf, NULL };
static v_post posts_bl[] = { bwarn_ge1, post_bl, NULL };
static v_post posts_bool[] = { eerr_eq1, ebool, NULL };
@ -154,7 +154,7 @@ const struct valids mdoc_valids[MDOC_MAX] = {
{ NULL, posts_notext }, /* Pp */
{ pres_d1, posts_wline }, /* D1 */
{ pres_d1, posts_wline }, /* Dl */
{ pres_bd, posts_bd }, /* Bd */
{ pres_bd, posts_bd_bk }, /* Bd */
{ NULL, NULL }, /* Ed */
{ pres_bl, posts_bl }, /* Bl */
{ NULL, NULL }, /* El */
@ -245,7 +245,7 @@ const struct valids mdoc_valids[MDOC_MAX] = {
{ NULL, NULL }, /* Fc */
{ NULL, NULL }, /* Oo */
{ NULL, NULL }, /* Oc */
{ NULL, posts_wline }, /* Bk */
{ NULL, posts_bd_bk }, /* Bk */
{ NULL, NULL }, /* Ek */
{ NULL, posts_eoln }, /* Bt */
{ NULL, NULL }, /* Hf */
@ -458,6 +458,11 @@ check_text(struct mdoc *mdoc, int line, int pos, char *p)
{
int c;
/*
* FIXME: we absolutely cannot let \b get through or it will
* destroy some assumptions in terms of format.
*/
for ( ; *p; p++, pos++) {
if ('\t' == *p) {
if ( ! (MDOC_LITERAL & mdoc->flags))
@ -532,17 +537,23 @@ pre_display(PRE_ARGS)
static int
pre_bl(PRE_ARGS)
{
int i, comp, dup;
const char *offs, *width;
enum mdoc_list lt;
int i, comp, dup;
const char *offs, *width;
enum mdoc_list lt;
struct mdoc_node *np;
if (MDOC_BLOCK != n->type) {
assert(n->parent);
assert(MDOC_BLOCK == n->parent->type);
assert(MDOC_Bl == n->parent->tok);
assert(LIST__NONE != n->parent->data.Bl.type);
memcpy(&n->data.Bl, &n->parent->data.Bl,
sizeof(struct mdoc_bl));
if (ENDBODY_NOT != n->end) {
assert(n->pending);
np = n->pending->parent;
} else
np = n->parent;
assert(np);
assert(MDOC_BLOCK == np->type);
assert(MDOC_Bl == np->tok);
assert(np->data.Bl);
n->data.Bl = np->data.Bl;
return(1);
}
@ -552,7 +563,8 @@ pre_bl(PRE_ARGS)
* ones. If we find no list type, we default to LIST_item.
*/
assert(LIST__NONE == n->data.Bl.type);
assert(NULL == n->data.Bl);
n->data.Bl = mandoc_calloc(1, sizeof(struct mdoc_bl));
/* LINTED */
for (i = 0; n->args && i < (int)n->args->argc; i++) {
@ -596,18 +608,18 @@ pre_bl(PRE_ARGS)
break;
/* Set list arguments. */
case (MDOC_Compact):
dup = n->data.Bl.comp;
dup = n->data.Bl->comp;
comp = 1;
break;
case (MDOC_Width):
dup = (NULL != n->data.Bl.width);
dup = (NULL != n->data.Bl->width);
width = n->args->argv[i].value[0];
break;
case (MDOC_Offset):
/* NB: this can be empty! */
if (n->args->argv[i].sz) {
offs = n->args->argv[i].value[0];
dup = (NULL != n->data.Bl.offs);
dup = (NULL != n->data.Bl->offs);
break;
}
if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV))
@ -621,29 +633,37 @@ pre_bl(PRE_ARGS)
return(0);
if (comp && ! dup)
n->data.Bl.comp = comp;
n->data.Bl->comp = comp;
if (offs && ! dup)
n->data.Bl.offs = offs;
n->data.Bl->offs = offs;
if (width && ! dup)
n->data.Bl.width = width;
n->data.Bl->width = width;
/* Check: multiple list types. */
if (LIST__NONE != lt && n->data.Bl.type != LIST__NONE)
if (LIST__NONE != lt && n->data.Bl->type != LIST__NONE)
if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP))
return(0);
/* Assign list type. */
if (LIST__NONE != lt && n->data.Bl.type == LIST__NONE)
n->data.Bl.type = lt;
if (LIST__NONE != lt && n->data.Bl->type == LIST__NONE) {
n->data.Bl->type = lt;
/* Set column information, too. */
if (LIST_column == lt) {
n->data.Bl->ncols =
n->args->argv[i].sz;
n->data.Bl->cols = (const char **)
n->args->argv[i].value;
}
}
/* The list type should come first. */
if (n->data.Bl.type == LIST__NONE)
if (n->data.Bl.width ||
n->data.Bl.offs ||
n->data.Bl.comp)
if (n->data.Bl->type == LIST__NONE)
if (n->data.Bl->width ||
n->data.Bl->offs ||
n->data.Bl->comp)
if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST))
return(0);
@ -652,10 +672,10 @@ pre_bl(PRE_ARGS)
/* Allow lists to default to LIST_item. */
if (LIST__NONE == n->data.Bl.type) {
if (LIST__NONE == n->data.Bl->type) {
if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE))
return(0);
n->data.Bl.type = LIST_item;
n->data.Bl->type = LIST_item;
}
/*
@ -664,9 +684,9 @@ pre_bl(PRE_ARGS)
* and must also be warned.
*/
switch (n->data.Bl.type) {
switch (n->data.Bl->type) {
case (LIST_tag):
if (n->data.Bl.width)
if (n->data.Bl->width)
break;
if (mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG))
break;
@ -680,7 +700,7 @@ pre_bl(PRE_ARGS)
case (LIST_inset):
/* FALLTHROUGH */
case (LIST_item):
if (NULL == n->data.Bl.width)
if (NULL == n->data.Bl->width)
break;
if (mdoc_nmsg(mdoc, n, MANDOCERR_WIDTHARG))
break;
@ -696,21 +716,28 @@ pre_bl(PRE_ARGS)
static int
pre_bd(PRE_ARGS)
{
int i, dup, comp;
enum mdoc_disp dt;
const char *offs;
int i, dup, comp;
enum mdoc_disp dt;
const char *offs;
struct mdoc_node *np;
if (MDOC_BLOCK != n->type) {
assert(n->parent);
assert(MDOC_BLOCK == n->parent->type);
assert(MDOC_Bd == n->parent->tok);
assert(DISP__NONE != n->parent->data.Bd.type);
memcpy(&n->data.Bd, &n->parent->data.Bd,
sizeof(struct mdoc_bd));
if (ENDBODY_NOT != n->end) {
assert(n->pending);
np = n->pending->parent;
} else
np = n->parent;
assert(np);
assert(MDOC_BLOCK == np->type);
assert(MDOC_Bd == np->tok);
assert(np->data.Bd);
n->data.Bd = np->data.Bd;
return(1);
}
assert(DISP__NONE == n->data.Bd.type);
assert(NULL == n->data.Bd);
n->data.Bd = mandoc_calloc(1, sizeof(struct mdoc_bd));
/* LINTED */
for (i = 0; n->args && i < (int)n->args->argc; i++) {
@ -741,7 +768,7 @@ pre_bd(PRE_ARGS)
/* NB: this can be empty! */
if (n->args->argv[i].sz) {
offs = n->args->argv[i].value[0];
dup = (NULL != n->data.Bd.offs);
dup = (NULL != n->data.Bd->offs);
break;
}
if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV))
@ -749,7 +776,7 @@ pre_bd(PRE_ARGS)
break;
case (MDOC_Compact):
comp = 1;
dup = n->data.Bd.comp;
dup = n->data.Bd->comp;
break;
default:
abort();
@ -764,26 +791,26 @@ pre_bd(PRE_ARGS)
/* Make our auxiliary assignments. */
if (offs && ! dup)
n->data.Bd.offs = offs;
n->data.Bd->offs = offs;
if (comp && ! dup)
n->data.Bd.comp = comp;
n->data.Bd->comp = comp;
/* Check whether a type has already been assigned. */
if (DISP__NONE != dt && n->data.Bd.type != DISP__NONE)
if (DISP__NONE != dt && n->data.Bd->type != DISP__NONE)
if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP))
return(0);
/* Make our type assignment. */
if (DISP__NONE != dt && n->data.Bd.type == DISP__NONE)
n->data.Bd.type = dt;
if (DISP__NONE != dt && n->data.Bd->type == DISP__NONE)
n->data.Bd->type = dt;
}
if (DISP__NONE == n->data.Bd.type) {
if (DISP__NONE == n->data.Bd->type) {
if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE))
return(0);
n->data.Bd.type = DISP_ragged;
n->data.Bd->type = DISP_ragged;
}
return(1);
@ -806,6 +833,8 @@ pre_sh(PRE_ARGS)
if (MDOC_BLOCK != n->type)
return(1);
mdoc->regs->regs[(int)REG_nS].set = 0;
return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
}
@ -828,13 +857,20 @@ static int
pre_an(PRE_ARGS)
{
if (NULL == n->args || 1 == n->args->argc)
if (NULL == n->args)
return(1);
mdoc_vmsg(mdoc, MANDOCERR_SYNTARGCOUNT,
n->line, n->pos,
"line arguments == 1 (have %d)",
n->args->argc);
return(0);
if (n->args->argc > 1)
if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_ARGCOUNT))
return(0);
if (MDOC_Split == n->args->argv[0].arg)
n->data.An.auth = AUTH_split;
else if (MDOC_Nosplit == n->args->argv[0].arg)
n->data.An.auth = AUTH_nosplit;
else
abort();
return(1);
}
@ -910,38 +946,74 @@ pre_dd(PRE_ARGS)
static int
post_bf(POST_ARGS)
{
char *p;
struct mdoc_node *head;
struct mdoc_node *np;
int arg;
if (MDOC_BLOCK != mdoc->last->type)
/*
* Unlike other data pointers, these are "housed" by the HEAD
* element, which contains the goods.
*/
if (MDOC_HEAD != mdoc->last->type) {
if (ENDBODY_NOT != mdoc->last->end) {
assert(mdoc->last->pending);
np = mdoc->last->pending->parent->head;
} else if (MDOC_BLOCK != mdoc->last->type) {
np = mdoc->last->parent->head;
} else
np = mdoc->last->head;
assert(np);
assert(MDOC_HEAD == np->type);
assert(MDOC_Bf == np->tok);
assert(np->data.Bf);
mdoc->last->data.Bf = np->data.Bf;
return(1);
head = mdoc->last->head;
if (mdoc->last->args && head->child) {
/* FIXME: this should provide a default. */
mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SYNTARGVCOUNT);
return(0);
} else if (mdoc->last->args)
return(1);
if (NULL == head->child || MDOC_TEXT != head->child->type) {
/* FIXME: this should provide a default. */
mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SYNTARGVCOUNT);
return(0);
}
p = head->child->string;
np = mdoc->last;
assert(MDOC_BLOCK == np->parent->type);
assert(MDOC_Bf == np->parent->tok);
np->data.Bf = mandoc_calloc(1, sizeof(struct mdoc_bf));
if (0 == strcmp(p, "Em"))
return(1);
else if (0 == strcmp(p, "Li"))
return(1);
else if (0 == strcmp(p, "Sy"))
return(1);
/*
* Cannot have both argument and parameter.
* If neither is specified, let it through with a warning.
*/
mdoc_nmsg(mdoc, head, MANDOCERR_FONTTYPE);
return(0);
if (np->parent->args && np->child) {
mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
return(0);
} else if (NULL == np->parent->args && NULL == np->child)
return(mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE));
/* Extract argument into data. */
if (np->parent->args) {
arg = np->parent->args->argv[0].arg;
if (MDOC_Emphasis == arg)
np->data.Bf->font = FONT_Em;
else if (MDOC_Literal == arg)
np->data.Bf->font = FONT_Li;
else if (MDOC_Symbolic == arg)
np->data.Bf->font = FONT_Sy;
else
abort();
return(1);
}
/* Extract parameter into data. */
if (0 == strcmp(np->child->string, "Em"))
np->data.Bf->font = FONT_Em;
else if (0 == strcmp(np->child->string, "Li"))
np->data.Bf->font = FONT_Li;
else if (0 == strcmp(np->child->string, "Sy"))
np->data.Bf->font = FONT_Sy;
else if ( ! mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE))
return(0);
return(1);
}
@ -1019,16 +1091,14 @@ post_at(POST_ARGS)
static int
post_an(POST_ARGS)
{
struct mdoc_node *np;
if (mdoc->last->args) {
if (NULL == mdoc->last->child)
return(1);
return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGCOUNT));
}
if (mdoc->last->child)
np = mdoc->last;
if (AUTH__NONE != np->data.An.auth && np->child)
return(mdoc_nmsg(mdoc, np, MANDOCERR_ARGCOUNT));
if (AUTH__NONE != np->data.An.auth || np->child)
return(1);
return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS));
return(mdoc_nmsg(mdoc, np, MANDOCERR_NOARGS));
}
@ -1044,7 +1114,8 @@ post_it(POST_ARGS)
return(1);
n = mdoc->last->parent->parent;
lt = n->data.Bl.type;
assert(n->data.Bl);
lt = n->data.Bl->type;
if (LIST__NONE == lt) {
mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
@ -1069,9 +1140,6 @@ post_it(POST_ARGS)
if (NULL == mdoc->last->head->child)
if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS))
return(0);
if (NULL == mdoc->last->body->child)
if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY))
return(0);
break;
case (LIST_bullet):
/* FALLTHROUGH */
@ -1080,24 +1148,18 @@ post_it(POST_ARGS)
case (LIST_enum):
/* FALLTHROUGH */
case (LIST_hyphen):
if (NULL == mdoc->last->body->child)
if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY))
return(0);
/* FALLTHROUGH */
case (LIST_item):
if (mdoc->last->head->child)
if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST))
return(0);
if (NULL == mdoc->last->body->child)
if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY))
return(0);
break;
case (LIST_column):
cols = -1;
for (i = 0; i < (int)n->args->argc; i++)
if (MDOC_Column == n->args->argv[i].arg) {
cols = (int)n->args->argv[i].sz;
break;
}
cols = (int)n->data.Bl->ncols;
assert(-1 != cols);
assert(NULL == mdoc->last->head->child);
if (NULL == mdoc->last->body->child)
@ -1136,13 +1198,8 @@ post_bl_head(POST_ARGS)
assert(mdoc->last->parent);
n = mdoc->last->parent;
if (LIST_column == n->data.Bl.type) {
for (i = 0; i < (int)n->args->argc; i++)
if (MDOC_Column == n->args->argv[i].arg)
break;
assert(i < (int)n->args->argc);
if (n->args->argv[i].sz && mdoc->last->nchild) {
if (LIST_column == n->data.Bl->type) {
if (n->data.Bl->ncols && mdoc->last->nchild) {
mdoc_nmsg(mdoc, n, MANDOCERR_COLUMNS);
return(0);
}

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: out.c,v 1.16 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: out.c,v 1.17 2010/06/25 19:50:23 kristaps Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -139,8 +139,6 @@ a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
if ((dst->scale = atof(buf)) < 0)
dst->scale = 0;
dst->unit = unit;
dst->pt = hasd;
return(1);
}

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: out.h,v 1.11 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: out.h,v 1.12 2010/06/25 19:50:23 kristaps Exp $ */
/*
* Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -53,7 +53,6 @@ enum roffdeco {
struct roffsu {
enum roffscale unit;
double scale;
int pt;
};
#define SCALE_INVERT(p) \
@ -62,14 +61,12 @@ struct roffsu {
#define SCALE_VS_INIT(p, v) \
do { (p)->unit = SCALE_VS; \
(p)->scale = (v); \
(p)->pt = 0; } \
(p)->scale = (v); } \
while (/* CONSTCOND */ 0)
#define SCALE_HS_INIT(p, v) \
do { (p)->unit = SCALE_BU; \
(p)->scale = (v); \
(p)->pt = 0; } \
(p)->scale = (v); } \
while (/* CONSTCOND */ 0)
int a2roffsu(const char *,

View File

@ -1,6 +1,7 @@
.\" $Vendor-Id: roff.3,v 1.1 2010/05/25 22:16:59 kristaps Exp $
.\" $Vendor-Id: roff.3,v 1.6 2010/07/07 15:04:54 kristaps Exp $
.\"
.\" Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@ -14,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: May 25 2010 $
.Dd $Mdocdate: July 7 2010 $
.Dt ROFF 3
.Os
.Sh NAME
@ -29,7 +30,11 @@
.In mandoc.h
.In roff.h
.Ft "struct roff *"
.Fn roff_alloc "mandocmsg msgs" "void *data"
.Fo roff_alloc
.Fa "struct regset *regs"
.Fa "mandocmsg msgs"
.Fa "void *data"
.Fc
.Ft int
.Fn roff_endparse "struct roff *roff"
.Ft void
@ -154,3 +159,15 @@ The
.Nm
library was written by
.An Kristaps Dzonsons Aq kristaps@bsd.lv .
.Sh BUGS
The implementation of user-defined strings needs improvement:
.Bl -dash
.It
String values are taken literally and are not interpreted.
.It
Parsing of quoted strings is incomplete.
.It
The stings are stored internally using a singly linked list,
which is fine for small numbers of strings,
but ineffient when handling many strings.
.El

View File

@ -1,6 +1,7 @@
.\" $Vendor-Id: roff.7,v 1.9 2010/06/10 21:42:02 kristaps Exp $
.\" $Vendor-Id: roff.7,v 1.13 2010/07/07 15:04:54 kristaps Exp $
.\"
.\" Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
.\" Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@ -14,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: June 10 2010 $
.Dd $Mdocdate: July 7 2010 $
.Dt ROFF 7
.Os
.Sh NAME
@ -91,11 +92,20 @@ The syntax of this macro is the same as that of
except that a leading argument must be specified.
It is ignored, as are its children.
.Ss \&ds
Define a string.
This macro is intended to have two arguments,
the name of the string to define and its content.
Currently, it is ignored including its arguments,
and the number of arguments is not checked.
Define a reserved word.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&ds No Cm key val
.Pp
The
.Cm key
and
.Cm val
strings are space-separated.
The
.Cm key
values may be invoked in subsequent text by using \e*(NN for two-letter
pairs, \e*N for one-letter, and \e*[NNN] for arbitrary-length values.
.Ss \&de1
The syntax of this macro is the same as that of
.Sx \&ig ,
@ -268,6 +278,37 @@ This macro is intended to have one argument,
the name of the request, macro or string to be undefined.
Currently, it is ignored including its arguments,
and the number of arguments is not checked.
.Ss \&nr
Define a register.
A register is an arbitrary string value that defines some sort of state,
which influences parsing and/or formatting.
Its syntax is as follows:
.Pp
.D1 Pf \. Sx \&nr Cm name value
.Pp
The
.Cm value
may, at the moment, only be an integer.
The
.Cm name
is defined up to the next whitespace.
The following register
.Cm name
requests are recognised:
.Bl -tag -width Ds
.It Cm nS
If set to a positive integer value, certain
.Xr mdoc 7
macros will behave as if they were defined in the
.Em SYNOPSIS
section.
Otherwise, this behaviour is unset (even if called within the
.Em SYNOPSIS
section itself).
Note that invoking a new
.Xr mdoc 7
section will unset this value.
.El
.Ss \&tr
Output character translation.
This macro is intended to have one argument,
@ -287,6 +328,12 @@ file re-write
.Pp
.Bl -dash -compact
.It
The
.Cm nS
request to
.Sx \&nr
is only compatible with OpenBSD's groff.
.It
Historic groff did not accept white-space buffering the custom END tag
for the
.Sx \&ig

View File

@ -1,6 +1,7 @@
/* $Vendor-Id: roff.c,v 1.88 2010/06/10 21:42:02 kristaps Exp $ */
/* $Vendor-Id: roff.c,v 1.94 2010/07/07 15:04:54 kristaps Exp $ */
/*
* Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -19,13 +20,16 @@
#endif
#include <assert.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "mandoc.h"
#include "roff.h"
#include "libmandoc.h"
#define RSTACK_MAX 128
@ -56,6 +60,7 @@ enum rofft {
ROFF_tr,
ROFF_cblock,
ROFF_ccond,
ROFF_nr,
ROFF_MAX
};
@ -64,12 +69,21 @@ enum roffrule {
ROFFRULE_DENY
};
struct roffstr {
char *name; /* key of symbol */
char *string; /* current value */
struct roffstr *next; /* next in list */
};
struct roff {
struct roffnode *last; /* leaf of stack */
mandocmsg msg; /* err/warn/fatal messages */
void *data; /* privdata for messages */
enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */
int rstackpos; /* position in rstack */
struct regset *regs; /* read/writable registers */
struct roffstr *first_string;
};
struct roffnode {
@ -111,8 +125,17 @@ static enum rofferr roff_ccond(ROFF_ARGS);
static enum rofferr roff_cond(ROFF_ARGS);
static enum rofferr roff_cond_text(ROFF_ARGS);
static enum rofferr roff_cond_sub(ROFF_ARGS);
static enum rofferr roff_ds(ROFF_ARGS);
static enum roffrule roff_evalcond(const char *, int *);
static void roff_freestr(struct roff *);
static const char *roff_getstrn(const struct roff *,
const char *, size_t);
static enum rofferr roff_line(ROFF_ARGS);
static enum rofferr roff_nr(ROFF_ARGS);
static int roff_res(struct roff *, int,
char **, size_t *, int, int *);
static void roff_setstr(struct roff *,
const char *, const char *);
/* See roff_hash_find() */
@ -129,7 +152,7 @@ static struct roffmac roffs[ROFF_MAX] = {
{ "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
{ "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
{ "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
{ "ds", roff_line, NULL, NULL, 0, NULL },
{ "ds", roff_ds, NULL, NULL, 0, NULL },
{ "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
{ "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
{ "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
@ -138,6 +161,7 @@ static struct roffmac roffs[ROFF_MAX] = {
{ "tr", roff_line, NULL, NULL, 0, NULL },
{ ".", roff_cblock, NULL, NULL, 0, NULL },
{ "\\}", roff_ccond, NULL, NULL, 0, NULL },
{ "nr", roff_nr, NULL, NULL, 0, NULL },
};
static void roff_free1(struct roff *);
@ -148,6 +172,7 @@ static int roffnode_push(struct roff *,
enum rofft, int, int);
static void roffnode_pop(struct roff *);
static enum rofft roff_parse(const char *, int *);
static int roff_parse_nat(const char *, unsigned int *);
/* See roff_hash_find() */
#define ROFF_HASH(p) (p[0] - ASCII_LO)
@ -260,6 +285,7 @@ roff_free1(struct roff *r)
while (r->last)
roffnode_pop(r);
roff_freestr(r);
}
@ -281,7 +307,7 @@ roff_free(struct roff *r)
struct roff *
roff_alloc(const mandocmsg msg, void *data)
roff_alloc(struct regset *regs, const mandocmsg msg, void *data)
{
struct roff *r;
@ -290,6 +316,7 @@ roff_alloc(const mandocmsg msg, void *data)
return(0);
}
r->regs = regs;
r->msg = msg;
r->data = data;
r->rstackpos = -1;
@ -299,13 +326,89 @@ roff_alloc(const mandocmsg msg, void *data)
}
/*
* Pre-filter each and every line for reserved words (one beginning with
* `\*', e.g., `\*(ab'). These must be handled before the actual line
* is processed.
*/
static int
roff_res(struct roff *r, int ln, char **bufp,
size_t *szp, int pos, int *offs)
{
const char *cp, *cpp, *st, *res;
int i, maxl;
size_t nsz;
char *n;
for (cp = &(*bufp)[pos]; (cpp = strstr(cp, "\\*")); cp++) {
cp = cpp + 2;
switch (*cp) {
case ('('):
cp++;
maxl = 2;
break;
case ('['):
cp++;
maxl = 0;
break;
default:
maxl = 1;
break;
}
st = cp;
for (i = 0; 0 == maxl || i < maxl; i++, cp++) {
if ('\0' == *cp)
return(1); /* Error. */
if (0 == maxl && ']' == *cp)
break;
}
res = roff_getstrn(r, st, (size_t)i);
if (NULL == res) {
cp -= maxl ? 1 : 0;
continue;
}
ROFF_DEBUG("roff: splicing reserved: [%.*s]\n", i, st);
nsz = *szp + strlen(res) + 1;
n = mandoc_malloc(nsz);
*n = '\0';
strlcat(n, *bufp, (size_t)(cpp - *bufp + 1));
strlcat(n, res, nsz);
strlcat(n, cp + (maxl ? 0 : 1), nsz);
free(*bufp);
*bufp = n;
*szp = nsz;
return(0);
}
return(1);
}
enum rofferr
roff_parseln(struct roff *r, int ln,
char **bufp, size_t *szp, int pos, int *offs)
roff_parseln(struct roff *r, int ln, char **bufp,
size_t *szp, int pos, int *offs)
{
enum rofft t;
int ppos;
/*
* Run the reserved-word filter only if we have some reserved
* words to fill in.
*/
if (r->first_string && ! roff_res(r, ln, bufp, szp, pos, offs))
return(ROFF_RERUN);
/*
* First, if a scope is open and we're not a macro, pass the
* text through the macro's filter. If a scope isn't open and
@ -318,12 +421,10 @@ roff_parseln(struct roff *r, int ln,
ROFF_DEBUG("roff: intercept scoped text: %s, [%s]\n",
roffs[t].name, &(*bufp)[pos]);
return((*roffs[t].text)
(r, t, bufp, szp, ln, pos, pos, offs));
} else if ( ! ROFF_CTL((*bufp)[pos])) {
ROFF_DEBUG("roff: pass non-scoped text: [%s]\n",
&(*bufp)[pos]);
(r, t, bufp, szp,
ln, pos, pos, offs));
} else if ( ! ROFF_CTL((*bufp)[pos]))
return(ROFF_CONT);
}
/*
* If a scope is open, go to the child handler for that macro,
@ -336,7 +437,8 @@ roff_parseln(struct roff *r, int ln,
ROFF_DEBUG("roff: intercept scoped context: %s\n",
roffs[t].name);
return((*roffs[t].sub)
(r, t, bufp, szp, ln, pos, pos, offs));
(r, t, bufp, szp,
ln, pos, pos, offs));
}
/*
@ -346,17 +448,15 @@ roff_parseln(struct roff *r, int ln,
*/
ppos = pos;
if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) {
ROFF_DEBUG("roff: pass non-scoped non-macro: [%s]\n",
&(*bufp)[pos]);
if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
return(ROFF_CONT);
}
ROFF_DEBUG("roff: intercept new-scope: %s, [%s]\n",
roffs[t].name, &(*bufp)[pos]);
assert(roffs[t].proc);
return((*roffs[t].proc)
(r, t, bufp, szp, ln, ppos, pos, offs));
(r, t, bufp, szp,
ln, ppos, pos, offs));
}
@ -412,6 +512,26 @@ roff_parse(const char *buf, int *pos)
}
static int
roff_parse_nat(const char *buf, unsigned int *res)
{
char *ep;
long lval;
errno = 0;
lval = strtol(buf, &ep, 10);
if (buf[0] == '\0' || *ep != '\0')
return(0);
if ((errno == ERANGE &&
(lval == LONG_MAX || lval == LONG_MIN)) ||
(lval > INT_MAX || lval < 0))
return(0);
*res = (unsigned int)lval;
return(1);
}
/* ARGSUSED */
static enum rofferr
roff_cblock(ROFF_ARGS)
@ -622,8 +742,8 @@ roff_block_sub(ROFF_ARGS)
return(ROFF_IGN);
assert(roffs[t].proc);
return((*roffs[t].proc)(r, t, bufp,
szp, ln, ppos, pos, offs));
return((*roffs[t].proc)(r, t, bufp, szp,
ln, ppos, pos, offs));
}
@ -672,8 +792,8 @@ roff_cond_sub(ROFF_ARGS)
return(ROFF_IGN);
assert(roffs[t].proc);
return((*roffs[t].proc)
(r, t, bufp, szp, ln, ppos, pos, offs));
return((*roffs[t].proc)(r, t, bufp, szp,
ln, ppos, pos, offs));
}
@ -730,6 +850,15 @@ roff_evalcond(const char *v, int *pos)
}
/* ARGSUSED */
static enum rofferr
roff_line(ROFF_ARGS)
{
return(ROFF_IGN);
}
/* ARGSUSED */
static enum rofferr
roff_cond(ROFF_ARGS)
@ -838,8 +967,127 @@ roff_cond(ROFF_ARGS)
/* ARGSUSED */
static enum rofferr
roff_line(ROFF_ARGS)
roff_ds(ROFF_ARGS)
{
char *name, *string, *end;
name = *bufp + pos;
if ('\0' == *name)
return(ROFF_IGN);
string = name;
while (*string && ' ' != *string)
string++;
if (*string)
*(string++) = '\0';
if (*string && '"' == *string)
string++;
while (*string && ' ' == *string)
string++;
end = string;
while (*end)
end++;
if (string < end) {
end--;
if (*end == '"')
*end = '\0';
}
roff_setstr(r, name, string);
return(ROFF_IGN);
}
/* ARGSUSED */
static enum rofferr
roff_nr(ROFF_ARGS)
{
const char *key, *val;
struct reg *rg;
key = &(*bufp)[pos];
rg = r->regs->regs;
/* Parse register request. */
while ((*bufp)[pos] && ' ' != (*bufp)[pos])
pos++;
/*
* Set our nil terminator. Because this line is going to be
* ignored anyway, we can munge it as we please.
*/
if ((*bufp)[pos])
(*bufp)[pos++] = '\0';
/* Skip whitespace to register token. */
while ((*bufp)[pos] && ' ' == (*bufp)[pos])
pos++;
val = &(*bufp)[pos];
/* Process register token. */
if (0 == strcmp(key, "nS")) {
rg[(int)REG_nS].set = 1;
if ( ! roff_parse_nat(val, &rg[(int)REG_nS].v.u))
rg[(int)REG_nS].v.u = 0;
ROFF_DEBUG("roff: register nS: %u\n",
rg[(int)REG_nS].v.u);
} else
ROFF_DEBUG("roff: ignoring register: %s\n", key);
return(ROFF_IGN);
}
static void
roff_setstr(struct roff *r, const char *name, const char *string)
{
struct roffstr *n;
char *namecopy;
n = r->first_string;
while (n && strcmp(name, n->name))
n = n->next;
if (NULL == n) {
namecopy = mandoc_strdup(name);
n = mandoc_malloc(sizeof(struct roffstr));
n->name = namecopy;
n->next = r->first_string;
r->first_string = n;
} else
free(n->string);
n->string = string ? strdup(string) : NULL;
}
static const char *
roff_getstrn(const struct roff *r, const char *name, size_t len)
{
const struct roffstr *n;
n = r->first_string;
while (n && (strncmp(name, n->name, len) || '\0' != n->name[len]))
n = n->next;
return(n ? n->string : NULL);
}
static void
roff_freestr(struct roff *r)
{
struct roffstr *n, *nn;
for (n = r->first_string; n; n = nn) {
free(n->name);
free(n->string);
nn = n->next;
free(n);
}
r->first_string = NULL;
}

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: roff.h,v 1.15 2010/05/17 00:06:36 kristaps Exp $ */
/* $Vendor-Id: roff.h,v 1.17 2010/06/27 15:52:41 kristaps Exp $ */
/*
* Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -29,7 +29,7 @@ __BEGIN_DECLS
struct roff;
void roff_free(struct roff *);
struct roff *roff_alloc(mandocmsg, void *);
struct roff *roff_alloc(struct regset *, mandocmsg, void *);
void roff_reset(struct roff *);
enum rofferr roff_parseln(struct roff *, int,
char **, size_t *, int, int *);

View File

@ -1,6 +1,7 @@
/* $Vendor-Id: term.c,v 1.148 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: term.c,v 1.160 2010/07/07 15:04:54 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -31,8 +32,6 @@
#include "chars.h"
#include "out.h"
#include "term.h"
#include "man.h"
#include "mdoc.h"
#include "main.h"
static void spec(struct termp *, const char *, size_t);
@ -87,9 +86,7 @@ term_alloc(enum termenc enc)
exit(EXIT_FAILURE);
}
p->tabwidth = 5;
p->enc = enc;
p->defrmargin = 78;
return(p);
}
@ -137,9 +134,10 @@ term_flushln(struct termp *p)
size_t vbl; /* number of blanks to prepend to output */
size_t vend; /* end of word visual position on output */
size_t bp; /* visual right border position */
int j; /* temporary loop index */
int jhy; /* last hyphen before line overflow */
size_t maxvis, mmax;
int j; /* temporary loop index for p->buf */
int jhy; /* last hyph before overflow w/r/t j */
size_t maxvis; /* output position of visible boundary */
size_t mmax; /* used in calculating bp */
/*
* First, establish the maximum columns of "visible" content.
@ -164,21 +162,17 @@ term_flushln(struct termp *p)
*/
vbl = p->flags & TERMP_NOLPAD ? 0 : p->offset;
/*
* FIXME: if bp is zero, we still output the first word before
* breaking the line.
*/
vis = vend = i = 0;
while (i < (int)p->col) {
while (i < (int)p->col) {
/*
* Handle literal tab characters.
* Handle literal tab characters: collapse all
* subsequent tabs into a single huge set of spaces.
*/
for (j = i; j < (int)p->col; j++) {
if ('\t' != p->buf[j])
break;
vend = (vis/p->tabwidth+1)*p->tabwidth;
vend = (vis / p->tabwidth + 1) * p->tabwidth;
vbl += vend - vis;
vis = vend;
}
@ -194,13 +188,21 @@ term_flushln(struct termp *p)
for (jhy = 0; j < (int)p->col; j++) {
if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j])
break;
if (8 != p->buf[j]) {
if (vend > vis && vend < bp &&
ASCII_HYPH == p->buf[j])
jhy = j;
vend++;
} else
vend--;
/* Back over the the last printed character. */
if (8 == p->buf[j]) {
assert(j);
vend -= (*p->width)(p, p->buf[j - 1]);
continue;
}
/* Regular word. */
/* Break at the hyphen point if we overrun. */
if (vend > vis && vend < bp &&
ASCII_HYPH == p->buf[j])
jhy = j;
vend += (*p->width)(p, p->buf[j]);
}
/*
@ -240,13 +242,13 @@ term_flushln(struct termp *p)
break;
if (' ' == p->buf[i]) {
while (' ' == p->buf[i]) {
vbl++;
vbl += (*p->width)(p, p->buf[i]);
i++;
}
break;
}
if (ASCII_NBRSP == p->buf[i]) {
vbl++;
vbl += (*p->width)(p, ' ');
continue;
}
@ -261,12 +263,13 @@ term_flushln(struct termp *p)
vbl = 0;
}
if (ASCII_HYPH == p->buf[i])
if (ASCII_HYPH == p->buf[i]) {
(*p->letter)(p, '-');
else
p->viscol += (*p->width)(p, '-');
} else {
(*p->letter)(p, p->buf[i]);
p->viscol += 1;
p->viscol += (*p->width)(p, p->buf[i]);
}
}
vend += vbl;
vis = vend;
@ -284,7 +287,7 @@ term_flushln(struct termp *p)
if (TERMP_HANG & p->flags) {
/* We need one blank after the tag. */
p->overstep = /* LINTED */
vis - maxvis + 1;
vis - maxvis + (*p->width)(p, ' ');
/*
* Behave exactly the same way as groff:
@ -308,7 +311,8 @@ term_flushln(struct termp *p)
/* Right-pad. */
if (maxvis > vis + /* LINTED */
((TERMP_TWOSPACE & p->flags) ? 1 : 0)) {
((TERMP_TWOSPACE & p->flags) ?
(*p->width)(p, ' ') : 0)) {
p->viscol += maxvis - vis;
(*p->advance)(p, maxvis - vis);
vis += (maxvis - vis);
@ -484,9 +488,14 @@ term_word(struct termp *p, const char *word)
}
if ( ! (TERMP_NOSPACE & p->flags)) {
bufferc(p, ' ');
if (TERMP_SENTENCE & p->flags)
if ( ! (TERMP_KEEP & p->flags)) {
if (TERMP_PREKEEP & p->flags)
p->flags |= TERMP_KEEP;
bufferc(p, ' ');
if (TERMP_SENTENCE & p->flags)
bufferc(p, ' ');
} else
bufferc(p, ASCII_NBRSP);
}
if ( ! (p->flags & TERMP_NONOSPACE))
@ -626,7 +635,28 @@ encode(struct termp *p, const char *word, size_t sz)
size_t
term_vspan(const struct roffsu *su)
term_len(const struct termp *p, size_t sz)
{
return((*p->width)(p, ' ') * sz);
}
size_t
term_strlen(const struct termp *p, const char *cp)
{
size_t sz;
for (sz = 0; *cp; cp++)
sz += (*p->width)(p, *cp);
return(sz);
}
/* ARGSUSED */
size_t
term_vspan(const struct termp *p, const struct roffsu *su)
{
double r;
@ -662,41 +692,13 @@ term_vspan(const struct roffsu *su)
size_t
term_hspan(const struct roffsu *su)
term_hspan(const struct termp *p, const struct roffsu *su)
{
double r;
double v;
/* XXX: CM, IN, and PT are approximations. */
switch (su->unit) {
case (SCALE_CM):
r = 4 * su->scale;
break;
case (SCALE_IN):
/* XXX: this is an approximation. */
r = 10 * su->scale;
break;
case (SCALE_PC):
r = (10 * su->scale) / 6;
break;
case (SCALE_PT):
r = (10 * su->scale) / 72;
break;
case (SCALE_MM):
r = su->scale / 1000; /* FIXME: double-check. */
break;
case (SCALE_VS):
r = su->scale * 2 - 1; /* FIXME: double-check. */
break;
default:
r = su->scale;
break;
}
if (r < 0.0)
r = 0.0;
return((size_t)/* LINTED */
r);
v = ((*p->hspan)(p, su));
if (v < 0.0)
v = 0.0;
return((size_t) /* LINTED */
v);
}

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: term.h,v 1.64 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: term.h,v 1.73 2010/07/04 19:42:25 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -33,7 +33,8 @@ enum termtype {
enum termfont {
TERMFONT_NONE = 0,
TERMFONT_BOLD,
TERMFONT_UNDER
TERMFONT_UNDER,
TERMFONT__MAX
};
#define TERM_MAXMARGIN 100000 /* FIXME */
@ -41,17 +42,27 @@ enum termfont {
typedef void (*term_margin)(struct termp *, const void *);
struct termp_ps {
int psstate; /* state of ps output */
int flags;
#define PS_INLINE (1 << 0) /* we're in a word */
#define PS_MARGINS (1 << 1) /* we're in the margins */
size_t pscol; /* visible column */
size_t psrow; /* visible row */
#define PS_NEWPAGE (1 << 2) /* new page, no words yet */
size_t pscol; /* visible column (AFM units) */
size_t psrow; /* visible row (AFM units) */
char *psmarg; /* margin buf */
size_t psmargsz; /* margin buf size */
size_t psmargcur; /* current pos in margin buf */
size_t pspage; /* current page */
size_t psmargcur; /* cur index in margin buf */
char last; /* character buffer */
enum termfont lastf; /* last set font */
size_t scale; /* font scaling factor */
size_t pages; /* number of pages shown */
size_t lineheight; /* line height (AFM units) */
size_t top; /* body top (AFM units) */
size_t bottom; /* body bottom (AFM units) */
size_t height; /* page height (AFM units */
size_t width; /* page width (AFM units) */
size_t left; /* body left (AFM units) */
size_t header; /* header pos (AFM units) */
size_t footer; /* footer pos (AFM units) */
};
struct termp {
@ -78,6 +89,8 @@ struct termp {
#define TERMP_NOSPLIT (1 << 11) /* See termp_an_pre/post(). */
#define TERMP_SPLIT (1 << 12) /* See termp_an_pre/post(). */
#define TERMP_ANPREC (1 << 13) /* See termp_an_pre(). */
#define TERMP_KEEP (1 << 14) /* Keep words together. */
#define TERMP_PREKEEP (1 << 15) /* ...starting with the next one. */
char *buf; /* Output buffer. */
enum termenc enc; /* Type of encoding. */
void *symtab; /* Encoded-symbol table. */
@ -91,6 +104,9 @@ struct termp {
void (*end)(struct termp *);
void (*endline)(struct termp *);
void (*advance)(struct termp *, size_t);
size_t (*width)(const struct termp *, char);
double (*hspan)(const struct termp *,
const struct roffsu *);
const void *argf; /* arg for headf/footf */
union {
struct termp_ps ps;
@ -107,8 +123,12 @@ void term_begin(struct termp *, term_margin,
term_margin, const void *);
void term_end(struct termp *);
size_t term_hspan(const struct roffsu *);
size_t term_vspan(const struct roffsu *);
size_t term_hspan(const struct termp *,
const struct roffsu *);
size_t term_vspan(const struct termp *,
const struct roffsu *);
size_t term_strlen(const struct termp *, const char *);
size_t term_len(const struct termp *, size_t);
enum termfont term_fonttop(struct termp *);
const void *term_fontq(struct termp *);

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: term_ascii.c,v 1.4 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: term_ascii.c,v 1.8 2010/06/30 12:30:36 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -30,11 +30,14 @@
#include "term.h"
#include "main.h"
static double ascii_hspan(const struct termp *,
const struct roffsu *);
static size_t ascii_width(const struct termp *, char);
static void ascii_advance(struct termp *, size_t);
static void ascii_begin(struct termp *);
static void ascii_end(struct termp *);
static void ascii_endline(struct termp *);
static void ascii_letter(struct termp *, char);
static void ascii_begin(struct termp *);
static void ascii_advance(struct termp *, size_t);
static void ascii_end(struct termp *);
void *
@ -47,12 +50,17 @@ ascii_alloc(char *outopts)
if (NULL == (p = term_alloc(TERMENC_ASCII)))
return(NULL);
p->type = TERMTYPE_CHAR;
p->letter = ascii_letter;
p->tabwidth = 5;
p->defrmargin = 78;
p->advance = ascii_advance;
p->begin = ascii_begin;
p->end = ascii_end;
p->endline = ascii_endline;
p->advance = ascii_advance;
p->hspan = ascii_hspan;
p->letter = ascii_letter;
p->type = TERMTYPE_CHAR;
p->width = ascii_width;
toks[0] = "width";
toks[1] = NULL;
@ -74,6 +82,15 @@ ascii_alloc(char *outopts)
}
/* ARGSUSED */
static size_t
ascii_width(const struct termp *p, char c)
{
return(1);
}
void
ascii_free(void *arg)
{
@ -126,3 +143,43 @@ ascii_advance(struct termp *p, size_t len)
for (i = 0; i < len; i++)
putchar(' ');
}
/* ARGSUSED */
static double
ascii_hspan(const struct termp *p, const struct roffsu *su)
{
double r;
/*
* Approximate based on character width. These are generated
* entirely by eyeballing the screen, but appear to be correct.
*/
switch (su->unit) {
case (SCALE_CM):
r = 4 * su->scale;
break;
case (SCALE_IN):
r = 10 * su->scale;
break;
case (SCALE_PC):
r = (10 * su->scale) / 6;
break;
case (SCALE_PT):
r = (10 * su->scale) / 72;
break;
case (SCALE_MM):
r = su->scale / 1000;
break;
case (SCALE_VS):
r = su->scale * 2 - 1;
break;
default:
r = su->scale;
break;
}
return(r);
}

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: term_ps.c,v 1.10 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: term_ps.c,v 1.33 2010/07/05 08:46:09 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -22,22 +22,336 @@
#include <assert.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "out.h"
#include "main.h"
#include "term.h"
#define PS_CHAR_WIDTH 6
#define PS_CHAR_HEIGHT 12
#define PS_CHAR_TOPMARG (792 - 24)
#define PS_CHAR_TOP (PS_CHAR_TOPMARG - 36)
#define PS_CHAR_LEFT 36
#define PS_CHAR_BOTMARG 24
#define PS_CHAR_BOT (PS_CHAR_BOTMARG + 36)
/* Convert PostScript point "x" to an AFM unit. */
#define PNT2AFM(p, x) /* LINTED */ \
(size_t)((double)(x) * (1000.0 / (double)(p)->engine.ps.scale))
/* Convert an AFM unit "x" to a PostScript points */
#define AFM2PNT(p, x) /* LINTED */ \
(size_t)((double)(x) / (1000.0 / (double)(p)->engine.ps.scale))
struct glyph {
size_t wx; /* WX in AFM */
};
struct font {
const char *name; /* FontName in AFM */
#define MAXCHAR 95 /* total characters we can handle */
struct glyph gly[MAXCHAR]; /* glyph metrics */
};
/*
* We define, for the time being, three fonts: bold, oblique/italic, and
* normal (roman). The following table hard-codes the font metrics for
* ASCII, i.e., 32--127.
*/
static const struct font fonts[TERMFONT__MAX] = {
{ "Times-Roman", {
{ 250 },
{ 333 },
{ 408 },
{ 500 },
{ 500 },
{ 833 },
{ 778 },
{ 333 },
{ 333 },
{ 333 },
{ 500 },
{ 564 },
{ 250 },
{ 333 },
{ 250 },
{ 278 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 278 },
{ 278 },
{ 564 },
{ 564 },
{ 564 },
{ 444 },
{ 921 },
{ 722 },
{ 667 },
{ 667 },
{ 722 },
{ 611 },
{ 556 },
{ 722 },
{ 722 },
{ 333 },
{ 389 },
{ 722 },
{ 611 },
{ 889 },
{ 722 },
{ 722 },
{ 556 },
{ 722 },
{ 667 },
{ 556 },
{ 611 },
{ 722 },
{ 722 },
{ 944 },
{ 722 },
{ 722 },
{ 611 },
{ 333 },
{ 278 },
{ 333 },
{ 469 },
{ 500 },
{ 333 },
{ 444 },
{ 500 },
{ 444 },
{ 500},
{ 444},
{ 333},
{ 500},
{ 500},
{ 278},
{ 278},
{ 500},
{ 278},
{ 778},
{ 500},
{ 500},
{ 500},
{ 500},
{ 333},
{ 389},
{ 278},
{ 500},
{ 500},
{ 722},
{ 500},
{ 500},
{ 444},
{ 480},
{ 200},
{ 480},
{ 541},
} },
{ "Times-Bold", {
{ 250 },
{ 333 },
{ 555 },
{ 500 },
{ 500 },
{ 1000 },
{ 833 },
{ 333 },
{ 333 },
{ 333 },
{ 500 },
{ 570 },
{ 250 },
{ 333 },
{ 250 },
{ 278 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 333 },
{ 333 },
{ 570 },
{ 570 },
{ 570 },
{ 500 },
{ 930 },
{ 722 },
{ 667 },
{ 722 },
{ 722 },
{ 667 },
{ 611 },
{ 778 },
{ 778 },
{ 389 },
{ 500 },
{ 778 },
{ 667 },
{ 944 },
{ 722 },
{ 778 },
{ 611 },
{ 778 },
{ 722 },
{ 556 },
{ 667 },
{ 722 },
{ 722 },
{ 1000 },
{ 722 },
{ 722 },
{ 667 },
{ 333 },
{ 278 },
{ 333 },
{ 581 },
{ 500 },
{ 333 },
{ 500 },
{ 556 },
{ 444 },
{ 556 },
{ 444 },
{ 333 },
{ 500 },
{ 556 },
{ 278 },
{ 333 },
{ 556 },
{ 278 },
{ 833 },
{ 556 },
{ 500 },
{ 556 },
{ 556 },
{ 444 },
{ 389 },
{ 333 },
{ 556 },
{ 500 },
{ 722 },
{ 500 },
{ 500 },
{ 444 },
{ 394 },
{ 220 },
{ 394 },
{ 520 },
} },
{ "Times-Italic", {
{ 250 },
{ 333 },
{ 420 },
{ 500 },
{ 500 },
{ 833 },
{ 778 },
{ 333 },
{ 333 },
{ 333 },
{ 500 },
{ 675 },
{ 250 },
{ 333 },
{ 250 },
{ 278 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 333 },
{ 333 },
{ 675 },
{ 675 },
{ 675 },
{ 500 },
{ 920 },
{ 611 },
{ 611 },
{ 667 },
{ 722 },
{ 611 },
{ 611 },
{ 722 },
{ 722 },
{ 333 },
{ 444 },
{ 667 },
{ 556 },
{ 833 },
{ 667 },
{ 722 },
{ 611 },
{ 722 },
{ 611 },
{ 500 },
{ 556 },
{ 722 },
{ 611 },
{ 833 },
{ 611 },
{ 556 },
{ 556 },
{ 389 },
{ 278 },
{ 389 },
{ 422 },
{ 500 },
{ 333 },
{ 500 },
{ 500 },
{ 444 },
{ 500 },
{ 444 },
{ 278 },
{ 500 },
{ 500 },
{ 278 },
{ 278 },
{ 444 },
{ 278 },
{ 722 },
{ 500 },
{ 500 },
{ 500 },
{ 500 },
{ 389 },
{ 389 },
{ 278 },
{ 500 },
{ 444 },
{ 667 },
{ 444 },
{ 444 },
{ 389 },
{ 400 },
{ 275 },
{ 400 },
{ 541 },
} },
};
/* These work the buffer used by the header and footer. */
#define PS_BUFSLOP 128
#define PS_GROWBUF(p, sz) \
do if ((p)->engine.ps.psmargcur + (sz) > \
@ -54,33 +368,120 @@
} while (/* CONSTCOND */ 0)
static void ps_letter(struct termp *, char);
static double ps_hspan(const struct termp *,
const struct roffsu *);
static size_t ps_width(const struct termp *, char);
static void ps_advance(struct termp *, size_t);
static void ps_begin(struct termp *);
static void ps_end(struct termp *);
static void ps_advance(struct termp *, size_t);
static void ps_endline(struct termp *);
static void ps_fclose(struct termp *);
static void ps_letter(struct termp *, char);
static void ps_pclose(struct termp *);
static void ps_pletter(struct termp *, char);
static void ps_pletter(struct termp *, int);
static void ps_printf(struct termp *, const char *, ...);
static void ps_putchar(struct termp *, char);
static void ps_setfont(struct termp *, enum termfont);
void *
ps_alloc(void)
ps_alloc(char *outopts)
{
struct termp *p;
size_t pagex, pagey, marginx, marginy, lineheight;
const char *toks[2];
const char *pp;
char *v;
if (NULL == (p = term_alloc(TERMENC_ASCII)))
return(NULL);
p->type = TERMTYPE_PS;
p->letter = ps_letter;
p->advance = ps_advance;
p->begin = ps_begin;
p->end = ps_end;
p->advance = ps_advance;
p->endline = ps_endline;
p->hspan = ps_hspan;
p->letter = ps_letter;
p->type = TERMTYPE_PS;
p->width = ps_width;
toks[0] = "paper";
toks[1] = NULL;
pp = NULL;
while (outopts && *outopts)
switch (getsubopt(&outopts, UNCONST(toks), &v)) {
case (0):
pp = v;
break;
default:
break;
}
/* Default to US letter (millimetres). */
pagex = 216;
pagey = 279;
/*
* The ISO-269 paper sizes can be calculated automatically, but
* it would require bringing in -lm for pow() and I'd rather not
* do that. So just do it the easy way for now. Since this
* only happens once, I'm not terribly concerned.
*/
if (pp && strcasecmp(pp, "letter")) {
if (0 == strcasecmp(pp, "a3")) {
pagex = 297;
pagey = 420;
} else if (0 == strcasecmp(pp, "a4")) {
pagex = 210;
pagey = 297;
} else if (0 == strcasecmp(pp, "a5")) {
pagex = 148;
pagey = 210;
} else if (0 == strcasecmp(pp, "legal")) {
pagex = 216;
pagey = 356;
} else if (2 != sscanf(pp, "%zux%zu", &pagex, &pagey))
fprintf(stderr, "%s: Unknown paper\n", pp);
} else if (NULL == pp)
pp = "letter";
/*
* This MUST be defined before any PNT2AFM or AFM2PNT
* calculations occur.
*/
p->engine.ps.scale = 11;
/* Remember millimetres -> AFM units. */
pagex = PNT2AFM(p, ((double)pagex * 2.834));
pagey = PNT2AFM(p, ((double)pagey * 2.834));
/* Margins are 1/9 the page x and y. */
marginx = /* LINTED */
(size_t)((double)pagex / 9.0);
marginy = /* LINTED */
(size_t)((double)pagey / 9.0);
/* Line-height is 1.4em. */
lineheight = PNT2AFM(p, ((double)p->engine.ps.scale * 1.4));
p->engine.ps.width = pagex;
p->engine.ps.height = pagey;
p->engine.ps.header = pagey - (marginy / 2) - (lineheight / 2);
p->engine.ps.top = pagey - marginy;
p->engine.ps.footer = (marginy / 2) - (lineheight / 2);
p->engine.ps.bottom = marginy;
p->engine.ps.left = marginx;
p->engine.ps.lineheight = lineheight;
p->defrmargin = pagex - (marginx * 2);
return(p);
}
@ -113,7 +514,7 @@ ps_printf(struct termp *p, const char *fmt, ...)
* into our growable margin buffer.
*/
if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
if ( ! (PS_MARGINS & p->engine.ps.flags)) {
vprintf(fmt, ap);
va_end(ap);
return;
@ -142,7 +543,7 @@ ps_putchar(struct termp *p, char c)
/* See ps_printf(). */
if ( ! (PS_MARGINS & p->engine.ps.psstate)) {
if ( ! (PS_MARGINS & p->engine.ps.flags)) {
putchar(c);
return;
}
@ -166,18 +567,26 @@ ps_end(struct termp *p)
* well as just one.
*/
assert(0 == p->engine.ps.psstate);
assert('\0' == p->engine.ps.last);
assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
printf("%s", p->engine.ps.psmarg);
printf("showpage\n");
printf("%s\n", "%%EOF");
if ( ! (PS_NEWPAGE & p->engine.ps.flags)) {
assert(0 == p->engine.ps.flags);
assert('\0' == p->engine.ps.last);
assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
printf("%s", p->engine.ps.psmarg);
p->engine.ps.pages++;
printf("showpage\n");
}
printf("%%%%Trailer\n");
printf("%%%%Pages: %zu\n", p->engine.ps.pages);
printf("%%%%EOF\n");
}
static void
ps_begin(struct termp *p)
{
time_t t;
int i;
/*
* Print margins into margin buffer. Nothing gets output to the
@ -190,24 +599,24 @@ ps_begin(struct termp *p)
}
p->engine.ps.psmargcur = 0;
p->engine.ps.psstate = PS_MARGINS;
p->engine.ps.pscol = PS_CHAR_LEFT;
p->engine.ps.psrow = PS_CHAR_TOPMARG;
p->engine.ps.flags = PS_MARGINS;
p->engine.ps.pscol = p->engine.ps.left;
p->engine.ps.psrow = p->engine.ps.header;
ps_setfont(p, TERMFONT_NONE);
(*p->headf)(p, p->argf);
(*p->endline)(p);
p->engine.ps.pscol = PS_CHAR_LEFT;
p->engine.ps.psrow = PS_CHAR_BOTMARG;
p->engine.ps.pscol = p->engine.ps.left;
p->engine.ps.psrow = p->engine.ps.footer;
(*p->footf)(p, p->argf);
(*p->endline)(p);
p->engine.ps.psstate &= ~PS_MARGINS;
p->engine.ps.flags &= ~PS_MARGINS;
assert(0 == p->engine.ps.psstate);
assert(0 == p->engine.ps.flags);
assert(p->engine.ps.psmarg);
assert('\0' != p->engine.ps.psmarg[0]);
@ -216,29 +625,64 @@ ps_begin(struct termp *p)
* stuff gets printed to the screen, so make sure we're sane.
*/
printf("%s\n", "%!PS");
t = time(NULL);
printf("%%!PS-Adobe-3.0\n");
printf("%%%%Creator: mandoc-%s\n", VERSION);
printf("%%%%CreationDate: %s", ctime(&t));
printf("%%%%DocumentData: Clean7Bit\n");
printf("%%%%Orientation: Portrait\n");
printf("%%%%Pages: (atend)\n");
printf("%%%%PageOrder: Ascend\n");
printf("%%%%DocumentMedia: Default %zu %zu 0 () ()\n",
AFM2PNT(p, p->engine.ps.width),
AFM2PNT(p, p->engine.ps.height));
printf("%%%%DocumentNeededResources: font");
for (i = 0; i < (int)TERMFONT__MAX; i++)
printf(" %s", fonts[i].name);
printf("\n%%%%EndComments\n");
p->engine.ps.pscol = p->engine.ps.left;
p->engine.ps.psrow = p->engine.ps.top;
p->engine.ps.flags |= PS_NEWPAGE;
ps_setfont(p, TERMFONT_NONE);
p->engine.ps.pscol = PS_CHAR_LEFT;
p->engine.ps.psrow = PS_CHAR_TOP;
}
static void
ps_pletter(struct termp *p, char c)
ps_pletter(struct termp *p, int c)
{
int f;
/*
* If we haven't opened a page context, then output that we're
* in a new page and make sure the font is correctly set.
*/
if (PS_NEWPAGE & p->engine.ps.flags) {
printf("%%%%Page: %zu %zu\n",
p->engine.ps.pages + 1,
p->engine.ps.pages + 1);
ps_printf(p, "/%s %zu selectfont\n",
fonts[(int)p->engine.ps.lastf].name,
p->engine.ps.scale);
p->engine.ps.flags &= ~PS_NEWPAGE;
}
/*
* If we're not in a PostScript "word" context, then open one
* now at the current cursor.
*/
if ( ! (PS_INLINE & p->engine.ps.psstate)) {
if ( ! (PS_INLINE & p->engine.ps.flags)) {
ps_printf(p, "%zu %zu moveto\n(",
p->engine.ps.pscol,
p->engine.ps.psrow);
p->engine.ps.psstate |= PS_INLINE;
AFM2PNT(p, p->engine.ps.pscol),
AFM2PNT(p, p->engine.ps.psrow));
p->engine.ps.flags |= PS_INLINE;
}
assert( ! (PS_NEWPAGE & p->engine.ps.flags));
/*
* We need to escape these characters as per the PostScript
* specification. We would also escape non-graphable characters
@ -260,8 +704,17 @@ ps_pletter(struct termp *p, char c)
/* Write the character and adjust where we are on the page. */
ps_putchar(p, c);
p->engine.ps.pscol += PS_CHAR_WIDTH;
f = (int)p->engine.ps.lastf;
if (c <= 32 || (c - 32 > MAXCHAR)) {
ps_putchar(p, ' ');
p->engine.ps.pscol += fonts[f].gly[0].wx;
return;
}
ps_putchar(p, (char)c);
c -= 32;
p->engine.ps.pscol += fonts[f].gly[c].wx;
}
@ -275,11 +728,11 @@ ps_pclose(struct termp *p)
* or anything).
*/
if ( ! (PS_INLINE & p->engine.ps.psstate))
if ( ! (PS_INLINE & p->engine.ps.flags))
return;
ps_printf(p, ") show\n");
p->engine.ps.psstate &= ~PS_INLINE;
p->engine.ps.flags &= ~PS_INLINE;
}
@ -304,7 +757,7 @@ ps_fclose(struct termp *p)
p->engine.ps.last = '\0';
}
if ( ! (PS_INLINE & p->engine.ps.psstate))
if ( ! (PS_INLINE & p->engine.ps.flags))
return;
ps_pclose(p);
@ -371,7 +824,7 @@ ps_advance(struct termp *p, size_t len)
*/
ps_fclose(p);
p->engine.ps.pscol += len ? len * PS_CHAR_WIDTH : 0;
p->engine.ps.pscol += len;
}
@ -389,7 +842,16 @@ ps_endline(struct termp *p)
* lines, we'll do nasty stuff.
*/
if (PS_MARGINS & p->engine.ps.psstate)
if (PS_MARGINS & p->engine.ps.flags)
return;
/* Left-justify. */
p->engine.ps.pscol = p->engine.ps.left;
/* If we haven't printed anything, return. */
if (PS_NEWPAGE & p->engine.ps.flags)
return;
/*
@ -397,16 +859,19 @@ ps_endline(struct termp *p)
* showpage and restart our row.
*/
p->engine.ps.pscol = PS_CHAR_LEFT;
if (p->engine.ps.psrow >= PS_CHAR_HEIGHT + PS_CHAR_BOT) {
p->engine.ps.psrow -= PS_CHAR_HEIGHT;
if (p->engine.ps.psrow >= p->engine.ps.lineheight +
p->engine.ps.bottom) {
p->engine.ps.psrow -= p->engine.ps.lineheight;
return;
}
assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]);
printf("%s", p->engine.ps.psmarg);
printf("showpage\n");
p->engine.ps.psrow = PS_CHAR_TOP;
p->engine.ps.pages++;
p->engine.ps.psrow = p->engine.ps.top;
assert( ! (PS_NEWPAGE & p->engine.ps.flags));
p->engine.ps.flags |= PS_NEWPAGE;
}
@ -414,14 +879,77 @@ static void
ps_setfont(struct termp *p, enum termfont f)
{
if (TERMFONT_BOLD == f)
ps_printf(p, "/Courier-Bold\n");
else if (TERMFONT_UNDER == f)
ps_printf(p, "/Courier-Oblique\n");
else
ps_printf(p, "/Courier\n");
ps_printf(p, "10 selectfont\n");
assert(f < TERMFONT__MAX);
p->engine.ps.lastf = f;
/*
* If we're still at the top of the page, let the font-setting
* be delayed until we actually have stuff to print.
*/
if (PS_NEWPAGE & p->engine.ps.flags)
return;
ps_printf(p, "/%s %zu selectfont\n",
fonts[(int)f].name, p->engine.ps.scale);
}
/* ARGSUSED */
static size_t
ps_width(const struct termp *p, char c)
{
if (c <= 32 || c - 32 >= MAXCHAR)
return(fonts[(int)TERMFONT_NONE].gly[0].wx);
c -= 32;
return(fonts[(int)TERMFONT_NONE].gly[(int)c].wx);
}
static double
ps_hspan(const struct termp *p, const struct roffsu *su)
{
double r;
/*
* All of these measurements are derived by converting from the
* native measurement to AFM units.
*/
switch (su->unit) {
case (SCALE_CM):
r = PNT2AFM(p, su->scale * 28.34);
break;
case (SCALE_IN):
r = PNT2AFM(p, su->scale * 72);
break;
case (SCALE_PC):
r = PNT2AFM(p, su->scale * 12);
break;
case (SCALE_PT):
r = PNT2AFM(p, su->scale * 100);
break;
case (SCALE_EM):
r = su->scale *
fonts[(int)TERMFONT_NONE].gly[109 - 32].wx;
break;
case (SCALE_MM):
r = PNT2AFM(p, su->scale * 2.834);
break;
case (SCALE_EN):
r = su->scale *
fonts[(int)TERMFONT_NONE].gly[110 - 32].wx;
break;
case (SCALE_VS):
r = su->scale * p->engine.ps.lineheight;
break;
default:
r = su->scale;
break;
}
return(r);
}

View File

@ -1,4 +1,4 @@
/* $Vendor-Id: tree.c,v 1.21 2010/06/19 20:46:28 kristaps Exp $ */
/* $Vendor-Id: tree.c,v 1.24 2010/07/07 15:04:54 kristaps Exp $ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
*
@ -74,7 +74,10 @@ print_mdoc(const struct mdoc_node *n, int indent)
t = "block-head";
break;
case (MDOC_BODY):
t = "block-body";
if (n->end)
t = "body-end";
else
t = "block-body";
break;
case (MDOC_TAIL):
t = "block-tail";