summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--macro.c13
-rw-r--r--mdoc.356
-rw-r--r--mdoc.c6
-rw-r--r--mdocml.1166
-rw-r--r--mdocml.31
-rw-r--r--validate.c158
6 files changed, 233 insertions, 167 deletions
diff --git a/macro.c b/macro.c
index 967f18fb..0b202d64 100644
--- a/macro.c
+++ b/macro.c
@@ -950,6 +950,8 @@ macro_constant_delimited(MACRO_PROT_ARGS)
/* FALLTHROUGH */
case (MDOC_Ns):
/* FALLTHROUGH */
+ case (MDOC_Pf):
+ /* FALLTHROUGH */
case (MDOC_Ux):
/* FALLTHROUGH */
case (MDOC_St):
@@ -1044,8 +1046,6 @@ macro_constant(MACRO_PROT_ARGS)
struct mdoc_arg argv[MDOC_LINEARG_MAX];
char *p;
- /* FIXME: parsing macros! */
-
fl = 0;
if (MDOC_QUOTABLE & mdoc_macros[tok].flags)
fl = ARGS_QUOTED;
@@ -1084,6 +1084,15 @@ macro_constant(MACRO_PROT_ARGS)
if (ARGS_EOLN == c)
break;
+ if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
+ return(0);
+ else if (MDOC_MAX != c) {
+ if ( ! rewind_elem(mdoc, tok))
+ return(0);
+ return(mdoc_macro(mdoc, c, line,
+ lastarg, pos, buf));
+ }
+
if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
return(0);
mdoc->next = MDOC_NEXT_SIBLING;
diff --git a/mdoc.3 b/mdoc.3
new file mode 100644
index 00000000..1cc4eaca
--- /dev/null
+++ b/mdoc.3
@@ -0,0 +1,56 @@
+.\"
+.Dd $Mdocdate$
+.Dt mdoc 3
+.Os
+.\"
+.Sh NAME
+.Nm mdoc_alloc ,
+.Nm mdoc_parseln ,
+.Nm mdoc_endparse ,
+.Nm mdoc_result ,
+.Nm mdoc_free
+.Nd mdoc macro compiler
+.\"
+.Sh SYNOPSIS
+.In mdoc.h
+.Ft "struct mdoc *"
+.Fn mdoc_alloc "void *data" "const struct mdoc_cb *cb"
+.Ft void
+.Fn mdoc_free "struct mdoc *"
+.Ft int
+.Fn mdoc_parseln "struct mdoc *" "int" "char *buf"
+.Ft "const struct mdoc_node *"
+.Fn mdoc_result "struct mdoc *"
+.Ft int
+.Fn mdoc_endparse "struct mdoc *"
+.\"
+.Sh DESCRIPTION
+The
+.Nm mdoc
+library parses lines of mdoc-macro text into an abstract syntax tree.
+In general, applications initiate a parsing sequence with
+.Fn mdoc_alloc ,
+parse each line in a document with
+.Fn mdoc_parseln ,
+close the parsing session with
+.Fn mdoc_endparse ,
+operate over the syntax tree returned by
+.Fn mdoc_result ,
+then free all allocated memory with
+.Fn mdoc_free .
+See the
+.Sx EXAMPLES
+section for a full example.
+.\" The following requests should be uncommented and used where appropriate.
+.\" This next request is for sections 2, 3, and 9 function return values only.
+.\" .Sh RETURN VALUES
+.\" .Sh EXAMPLES
+.\" The next request is for sections 2, 3, and 9 error and signal handling only.
+.\" .Sh ERRORS
+.\" .Sh SEE ALSO
+.\" .Xr foobar 1
+.\" .Sh STANDARDS
+.\" .Sh HISTORY
+.\" .Sh AUTHORS
+.\" .Sh CAVEATS
+.\" .Sh BUGS
diff --git a/mdoc.c b/mdoc.c
index e3ba2b93..e51f7771 100644
--- a/mdoc.c
+++ b/mdoc.c
@@ -99,7 +99,7 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
{ macro_scoped_close, MDOC_EXPLICIT }, /* El */
{ macro_scoped, MDOC_PARSED | MDOC_TABSEP}, /* It */
{ macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */
- { macro_constant, MDOC_PARSED }, /* An */
+ { macro_text, MDOC_PARSED }, /* An */
{ macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */
{ macro_constant, MDOC_QUOTABLE }, /* Cd */
{ macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */
@@ -111,7 +111,7 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
{ macro_constant, 0 }, /* Fd */
{ macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */
{ macro_text, MDOC_CALLABLE | MDOC_QUOTABLE | MDOC_PARSED }, /* Fn */
- { macro_text, MDOC_PARSED }, /* Ft */
+ { macro_text, MDOC_PARSED | MDOC_QUOTABLE }, /* Ft */
{ macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */
{ macro_constant, 0 }, /* In */
{ macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Li */
@@ -162,7 +162,7 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
{ macro_constant_delimited, MDOC_PARSED }, /* Nx */
{ macro_constant_delimited, MDOC_PARSED }, /* Ox */
{ macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Pc */
- { macro_constant, MDOC_PARSED }, /* Pf */
+ { macro_constant_delimited, MDOC_PARSED }, /* Pf */
{ macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Po */
{ macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pq */
{ macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Qc */
diff --git a/mdocml.1 b/mdocml.1
index ce6176a1..add2cf6a 100644
--- a/mdocml.1
+++ b/mdocml.1
@@ -5,50 +5,37 @@
.\"
.Sh NAME
.Nm mdocml
-.Nd compile manpage source into mark-up language
+.Nd mdoc macro compiler
.\"
.Sh SYNOPSIS
.Nm mdocml
.Op Fl v
.Op Fl W Ns Ar err...
-.Op Fl f Ar filter
-.Op Fl o Ar outfile
.Op Ar infile
.\"
.Sh DESCRIPTION
The
.Nm
-utility parses mdoc formatted manual source and passes results into an
-output filter. The current output filters are
-.Fl f Ar html
-and
-.Fl f Ar xml .
-By default,
-.Nm
-only validates its input. This may be explicitly directed with
-.Fl f Ar noop .
-Arguments common to all filters follow:
-.Bl -tag -width "\-o outfile"
-.It Fl f Ar filter
-The output filter name.
-.It Fl o Ar outfile
-Write output to
-.Ar outfile ,
-which may be
-.Dq \-
-for stdout. This is meaningless for
-.Fl f Ar noop .
+utility interfaces the
+.Xr mdoc 3
+library to validate and parse mdoc-macro documents. Arguments follow:
+.Bl -tag -width "\-Werr... "
.It Fl W Ns Ar err...
-Print warning messages. If set to
-.Fl W Ns Ar all ,
-all warnings are printed; if
-.Fl W Ns Ar error ,
-warnings are considered errors and cause utility termination. Multiple
+Print warning messages. May be set to
+.Fl W Ns Ar all
+for all warnings,
+.Ar compat
+for groff/troff-compatibility warnings, or
+.Ar syntax
+for syntax warnings. If
+.Fl W Ns Ar error
+is specified, warnings are considered errors and cause utility
+termination. Multiple
.Fl W
arguments may be comma-separated, such as
.Fl W Ns Ar error,all .
.It Fl v
-Make warning and error messages verbose.
+Print verbose parsing output.
.It Ar infile
Read input from
.Ar infile ,
@@ -57,90 +44,29 @@ which may be
for stdin.
.El
.Pp
+Parsing and validation rules are drawn entirely from the
+.Xr mdoc 7
+and
+.Xr mdoc.samples 7
+manuals.
+.Pp
By default,
.Nm
-reads from stdin and writes to stdout.
+reads from stdin, writes messages to stdout, and writes errors and
+warnings to stderr.
.Pp
.Ex -std mdocml
-.\"
-.Ss Noop Filter
-The default noop
-.Dq validating
-filter simply validates its input; it produces no output beyond error
-and warning messages.
-.\"
-.Ss XML Filter
-The XML filter, specified by
-.Fl f Ar xml ,
-produces correctly-formatted XML output. This filter has no additional
-arguments.
-.Pp
-The XML filter creates an XML document where element names are their respective
-roff macro names. Each element name has an associated
-namespace, which is one of
-.Dq block ,
-.Dq head ,
-.Dq body ,
-or
-.Dq inline ,
-corresponding to the display mode of a node. The document root is
-always the
-.Dq mdoc
-element, in the default namespace; the
-.Dq head
-namespace is for block headers (such as
-.Sq .Ss
-and
-.Sq .Sh ) ;
-the
-.Dq body
-namespace is for block bodies; and the
-.Dq inline
-namespace is for in-line elements (such as
-.Sq .Em ) .
-.\"
-.Ss HTML Filter
-The HTML filter, specified by
-.Fl f Ar html ,
-accepts the following filter-specific arguments:
-.Bl -tag -width "\-c css"
-.It Fl c Ar css
-The CSS file location, which defaults to
-.Ar mdocml.css .
-.It Fl e
-Whether to embed the CSS file into the HTML prologue.
-.El
-.Pp
-By default, the HTML filter produces HTML-4.01 strict mark-up. The
-default CSS document styles the page as it would appear in a terminal
-window.
.\"
.Sh EXAMPLES
-To produce an HTML4-strict document
-.Pa mdocml.html
-for
-.Pa mdocml.1
-with the default, embedded style-sheet:
-.Pp
-.D1 % mdocml -fhtml -e -o mdocml.html mdocml.1
-.Pp
-To create an XML document on standard output from
-.Pa mdocml.1
-with the default namespace identifiers
-.Li head ,
-.Li body ,
-.Li block
-and
-.Li inline :
-.Pp
-.D1 % mdocml -Wall,error -fxml mdocml.1
+To validate this manual page:
.Pp
-The previous example will also halt on input document warnings.
+.D1 % mdocml \-Wall,error mdocml.1
.\"
.Sh SEE ALSO
.Xr groff 1 ,
.Xr mdoc.samples 7 ,
-.Xr mdoc 7
+.Xr mdoc 7 ,
+.Xr mdoc 3
.\" .Sh STANDARDS
.\" .Sh HISTORY
.Sh AUTHORS
@@ -150,34 +76,16 @@ utility was written by
.An Kristaps Dzonsons Aq kristaps@kth.se .
.\"
.Sh CAVEATS
-Most caveats of
+The most glaring short-coming of
.Nm
-stem from ambiguities in
-.Xr mdoc 7
-or the necessary limitations of converting an ad hoc language into
-structured ones:
-.Bl -enum -compact -offset indent
-.It
-The engine doesn't understand the
-.Sq \&No ,
-.Sq \&Db ,
-.Sq \&Xc ,
+is that it doesn't yet support the
+.Sq \&Xc
and
.Sq \&Xo
-mdoc macros.
-.It
-All macro arguments may be quoted, instead of only some.
-.It
-Blank lines raise errors.
-.It
-If terminating punctuation is found, then
-.Em all
-remaining tokens are flushed after line scope is closed, not just the
-last one.
-.El
-.Pp
-The roff engine in
-.Nm
-produces text in-line; thus, output may already be partially written by
-the time an error is encountered.
+macros when used to extend the line arguments to
+.Sq \&It ;
+in effect, trampling the body section. We note that this is explicitly
+discouraged in
+.Xr mdoc.samples 7 ,
+but in practice used quite often.
.\" .Sh BUGS
diff --git a/mdocml.3 b/mdocml.3
deleted file mode 100644
index 2a34ea6b..00000000
--- a/mdocml.3
+++ /dev/null
@@ -1 +0,0 @@
-TODO.
diff --git a/validate.c b/validate.c
index 9f9d5cde..54e57093 100644
--- a/validate.c
+++ b/validate.c
@@ -31,11 +31,20 @@ struct valids {
v_post *post;
};
+static int pre_check_parent(struct mdoc *, struct mdoc_node *,
+ int, enum mdoc_type);
+static int pre_check_msecs(struct mdoc *, struct mdoc_node *,
+ int, enum mdoc_msec *);
static int pre_display(struct mdoc *, struct mdoc_node *);
+static int pre_sh(struct mdoc *, struct mdoc_node *);
+static int pre_ss(struct mdoc *, struct mdoc_node *);
static int pre_bd(struct mdoc *, struct mdoc_node *);
static int pre_bl(struct mdoc *, struct mdoc_node *);
static int pre_it(struct mdoc *, struct mdoc_node *);
+static int pre_cd(struct mdoc *, struct mdoc_node *);
+static int pre_er(struct mdoc *, struct mdoc_node *);
+static int pre_ex(struct mdoc *, struct mdoc_node *);
static int pre_prologue(struct mdoc *, struct mdoc_node *);
static int pre_prologue(struct mdoc *, struct mdoc_node *);
static int pre_prologue(struct mdoc *, struct mdoc_node *);
@@ -58,6 +67,11 @@ static v_pre pres_d1[] = { pre_display, NULL };
static v_pre pres_bd[] = { pre_display, pre_bd, NULL };
static v_pre pres_bl[] = { pre_bl, NULL };
static v_pre pres_it[] = { pre_it, NULL };
+static v_pre pres_ss[] = { pre_ss, NULL };
+static v_pre pres_sh[] = { pre_sh, NULL };
+static v_pre pres_cd[] = { pre_cd, NULL };
+static v_pre pres_er[] = { pre_er, NULL };
+static v_pre pres_ex[] = { pre_ex, NULL };
static v_post posts_bd[] = { headchild_err_eq0, bodychild_warn_ge1, NULL };
static v_post posts_text[] = { elemchild_err_ge1, NULL };
@@ -79,11 +93,9 @@ const struct valids mdoc_valids[MDOC_MAX] = {
{ pres_prologue, NULL }, /* Os */
/* FIXME: preceding Pp. */
/* FIXME: NAME section internal ordering. */
- /* FIXME: can only be a child of root. */
- { NULL, posts_sh }, /* Sh */
+ { pres_sh, posts_sh }, /* Sh */
/* FIXME: preceding Pp. */
- /* FIXME: can only be a child of Sh. */
- { NULL, posts_ss }, /* Ss */
+ { pres_ss, posts_ss }, /* Ss */
/* FIXME: proceeding... */
{ NULL, posts_pp }, /* Pp */
{ pres_d1, posts_d1 }, /* D1 */
@@ -96,16 +108,15 @@ const struct valids mdoc_valids[MDOC_MAX] = {
{ NULL, NULL }, /* El */
{ pres_it, posts_it }, /* It */
{ NULL, posts_text }, /* Ad */
- /* FIXME */
+ /* FIXME: argument OR parameters. */
{ NULL, NULL }, /* An */
{ NULL, NULL }, /* Ar */
-
- { NULL, posts_text }, /* Cd */ /* FIXME: section 4 only. */
+ { pres_cd, posts_text }, /* Cd */
{ NULL, NULL }, /* Cm */
{ NULL, posts_text }, /* Dv */
- { NULL, posts_text }, /* Er */ /* FIXME: section 2 only. */
+ { pres_er, posts_text }, /* Er */
{ NULL, posts_text }, /* Ev */
- { NULL, posts_notext }, /* Ex */ /* FIXME: sections 1,6,8 only. */ /* -std required */
+ { pres_ex, posts_notext }, /* Ex */ /* FIXME: -std required */
{ NULL, posts_text }, /* Fa */
{ NULL, NULL }, /* Fd */ /* FIXME: SYNOPSIS section. */
{ NULL, NULL }, /* Fl */
@@ -160,7 +171,7 @@ const struct valids mdoc_valids[MDOC_MAX] = {
{ NULL, NULL }, /* Nx */
{ NULL, NULL }, /* Ox */
{ NULL, NULL }, /* Pc */
- { NULL, NULL }, /* Pf */ /* FIXME: 2 or more arguments */
+ { NULL, NULL }, /* Pf */ /* FIXME: 2 or more arguments */ /* First should be text. */
{ NULL, NULL }, /* Po */
{ NULL, posts_wline }, /* Pq */ /* FIXME: ignore following Sh/Ss */
{ NULL, NULL }, /* Qc */
@@ -193,6 +204,37 @@ const struct valids mdoc_valids[MDOC_MAX] = {
static int
+pre_check_msecs(struct mdoc *mdoc, struct mdoc_node *node,
+ int sz, enum mdoc_msec *msecs)
+{
+ int i;
+
+ for (i = 0; i < sz; i++)
+ if (msecs[i] == mdoc->meta.msec)
+ return(1);
+ return(mdoc_nwarn(mdoc, node, WARN_COMPAT,
+ "macro is not appropriate for this manual section"));
+}
+
+
+static int
+pre_check_parent(struct mdoc *mdoc, struct mdoc_node *node,
+ int tok, enum mdoc_type type)
+{
+
+ if (type != mdoc->last->parent->type)
+ return(mdoc_nerr(mdoc, node, "invalid macro parent class %s, expected %s",
+ mdoc_type2a(mdoc->last->parent->type),
+ mdoc_type2a(type)));
+ if (MDOC_ROOT != type && tok == mdoc->last->parent->tok)
+ return(mdoc_nerr(mdoc, node, "invalid macro parent `%s', expected `%s'",
+ mdoc_macronames[mdoc->last->parent->tok],
+ mdoc_macronames[tok]));
+ return(1);
+}
+
+
+static int
bodychild_err_eq0(struct mdoc *mdoc)
{
@@ -420,19 +462,67 @@ pre_bd(struct mdoc *mdoc, struct mdoc_node *node)
static int
-pre_it(struct mdoc *mdoc, struct mdoc_node *node)
+pre_ss(struct mdoc *mdoc, struct mdoc_node *node)
{
if (MDOC_BLOCK != mdoc->last->type)
return(1);
- assert(MDOC_It == mdoc->last->tok);
+ assert(MDOC_Sh == mdoc->last->tok);
+ return(pre_check_parent(mdoc, node, MDOC_Sh, MDOC_BODY));
+}
- if (MDOC_BODY != mdoc->last->parent->type)
- return(mdoc_nerr(mdoc, node, "invalid macro parent `%s'", mdoc_macronames[mdoc->last->parent->tok]));
- if (MDOC_Bl != mdoc->last->parent->tok)
- return(mdoc_nerr(mdoc, node, "invalid macro parent `%s'", mdoc_macronames[mdoc->last->parent->tok]));
- return(1);
+static int
+pre_sh(struct mdoc *mdoc, struct mdoc_node *node)
+{
+
+ if (MDOC_BLOCK != mdoc->last->type)
+ return(1);
+ assert(MDOC_Sh == mdoc->last->tok);
+ return(pre_check_parent(mdoc, node, -1, MDOC_ROOT));
+}
+
+
+static int
+pre_ex(struct mdoc *mdoc, struct mdoc_node *node)
+{
+ enum mdoc_msec msecs[3];
+
+ msecs[0] = MSEC_1;
+ msecs[1] = MSEC_6;
+ msecs[2] = MSEC_8;
+ return(pre_check_msecs(mdoc, node, 3, msecs));
+}
+
+
+static int
+pre_er(struct mdoc *mdoc, struct mdoc_node *node)
+{
+ enum mdoc_msec msecs[1];
+
+ msecs[0] = MSEC_2;
+ return(pre_check_msecs(mdoc, node, 1, msecs));
+}
+
+
+static int
+pre_cd(struct mdoc *mdoc, struct mdoc_node *node)
+{
+ enum mdoc_msec msecs[1];
+
+ msecs[0] = MSEC_4;
+ return(pre_check_msecs(mdoc, node, 1, msecs));
+}
+
+
+static int
+pre_it(struct mdoc *mdoc, struct mdoc_node *node)
+{
+
+ if (MDOC_BLOCK != mdoc->last->type)
+ return(1);
+ assert(MDOC_It == mdoc->last->tok);
+ return(pre_check_parent(mdoc, node, MDOC_Bl, MDOC_BODY));
}
@@ -552,31 +642,33 @@ post_it(struct mdoc *mdoc)
assert(TYPE_NONE != type);
if (TYPE_HEAD == type) {
- if (NULL == (n = mdoc->last->data.block.head)) {
- if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
- return(0);
- } else if (NULL == n->child)
+ n = mdoc->last->data.block.head;
+ assert(n);
+ if (NULL == n->child)
if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
return(0);
- if (NULL == (n = mdoc->last->data.block.body)) {
- if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests body children"))
- return(0);
- } else if (NULL == n->child)
+ n = mdoc->last->data.block.body;
+ assert(n);
+ if (NULL == n->child)
if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests body children"))
return(0);
return(1);
}
- if (NULL == (n = mdoc->last->data.block.head)) {
- if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
- return(0);
- } else if (NULL == n->child)
- if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests line parameters"))
+ assert(TYPE_BODY == type);
+ assert(mdoc->last->data.block.head);
+
+ n = mdoc->last->data.block.head;
+ assert(n);
+ if (n->child)
+ if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests no line parameters"))
return(0);
- if ((n = mdoc->last->data.block.body) && n->child)
+ n = mdoc->last->data.block.body;
+ assert(n);
+ if (NULL == n->child)
if ( ! mdoc_warn(mdoc, WARN_SYNTAX, "macro suggests body children"))
return(0);
@@ -686,8 +778,10 @@ mdoc_valid_post(struct mdoc *mdoc)
if (MDOC_TEXT == mdoc->last->type)
return(1);
- if (MDOC_ROOT == mdoc->last->type)
+ if (MDOC_ROOT == mdoc->last->type) {
+ /* TODO: make sure prologue is complete. */
return(1);
+ }
if (NULL == mdoc_valids[mdoc->last->tok].post)
return(1);