diff options
-rw-r--r-- | macro.c | 13 | ||||
-rw-r--r-- | mdoc.3 | 56 | ||||
-rw-r--r-- | mdoc.c | 6 | ||||
-rw-r--r-- | mdocml.1 | 166 | ||||
-rw-r--r-- | mdocml.3 | 1 | ||||
-rw-r--r-- | validate.c | 158 |
6 files changed, 233 insertions, 167 deletions
@@ -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; @@ -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 @@ -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 */ @@ -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. @@ -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); |