diff options
author | Ingo Schwarze <schwarze@cvs.openbsd.org> | 2010-03-29 22:56:53 +0000 |
---|---|---|
committer | Ingo Schwarze <schwarze@cvs.openbsd.org> | 2010-03-29 22:56:53 +0000 |
commit | 0181b3616d9227302ce4911ebb960a51c11b31ae (patch) | |
tree | 8c7104781fe73b13f0fb5255b77f13dab2ba7d45 /usr.bin | |
parent | 61cad722963e27909ab453356961232cfbdf00c3 (diff) |
merge 1.9.19, keeping local patches
* scoping fixes for roff instructions
* accept apostroph in place of dot as a macro control character
* accept tabs between the control character and the macro name
* check that man(7) .TH titles use capital letters
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/mandoc/Makefile | 4 | ||||
-rw-r--r-- | usr.bin/mandoc/libman.h | 7 | ||||
-rw-r--r-- | usr.bin/mandoc/man.3 | 120 | ||||
-rw-r--r-- | usr.bin/mandoc/man.7 | 23 | ||||
-rw-r--r-- | usr.bin/mandoc/man.c | 20 | ||||
-rw-r--r-- | usr.bin/mandoc/man_macro.c | 108 | ||||
-rw-r--r-- | usr.bin/mandoc/man_validate.c | 25 | ||||
-rw-r--r-- | usr.bin/mandoc/mandoc.1 | 13 |
8 files changed, 248 insertions, 72 deletions
diff --git a/usr.bin/mandoc/Makefile b/usr.bin/mandoc/Makefile index cbbf8c7e74f..a159e0f08ab 100644 --- a/usr.bin/mandoc/Makefile +++ b/usr.bin/mandoc/Makefile @@ -1,8 +1,8 @@ -# $OpenBSD: Makefile,v 1.29 2010/03/26 01:22:05 schwarze Exp $ +# $OpenBSD: Makefile,v 1.30 2010/03/29 22:56:52 schwarze Exp $ .include <bsd.own.mk> -VERSION=1.9.17 +VERSION=1.9.19 CFLAGS+=-DVERSION=\"${VERSION}\" CFLAGS+=-W -Wall -Wstrict-prototypes .if ${USE_GCC3:L} != "no" diff --git a/usr.bin/mandoc/libman.h b/usr.bin/mandoc/libman.h index 1bb92ebf14d..2e2998b1b18 100644 --- a/usr.bin/mandoc/libman.h +++ b/usr.bin/mandoc/libman.h @@ -1,4 +1,4 @@ -/* $Id: libman.h,v 1.13 2010/03/26 01:22:05 schwarze Exp $ */ +/* $Id: libman.h,v 1.14 2010/03/29 22:56:52 schwarze Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se> * @@ -61,6 +61,8 @@ enum merr { WOLITERAL, WNLITERAL, WROFFNEST, + WROFFSCOPE, + WTITLECASE, WERRMAX }; @@ -111,7 +113,8 @@ int man_valid_post(struct man *); int man_valid_pre(struct man *, const struct man_node *); int man_action_post(struct man *); int man_action_pre(struct man *, struct man_node *); -int man_unscope(struct man *, const struct man_node *); +int man_unscope(struct man *, + const struct man_node *, enum merr); __END_DECLS diff --git a/usr.bin/mandoc/man.3 b/usr.bin/mandoc/man.3 index 2630278fad4..f6eb59bb35f 100644 --- a/usr.bin/mandoc/man.3 +++ b/usr.bin/mandoc/man.3 @@ -1,4 +1,4 @@ -.\" $Id: man.3,v 1.7 2010/02/18 02:11:26 schwarze Exp $ +.\" $Id: man.3,v 1.8 2010/03/29 22:56:52 schwarze Exp $ .\" .\" Copyright (c) 2009-2010 Kristaps Dzonsons <kristaps@bsd.lv> .\" @@ -14,11 +14,13 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: February 18 2010 $ +.Dd $Mdocdate: March 29 2010 $ .Dt MAN 3 .Os -.\" SECTION +. +. .Sh NAME +.Nm man , .Nm man_alloc , .Nm man_parseln , .Nm man_endparse , @@ -27,7 +29,8 @@ .Nm man_free , .Nm man_reset .Nd man macro compiler library -.\" SECTION +. +. .Sh SYNOPSIS .In man.h .Vt extern const char * const * man_macronames; @@ -45,16 +48,17 @@ .Fn man_meta "const struct man *man" .Ft int .Fn man_endparse "struct man *man" -.\" SECTION +. +. .Sh DESCRIPTION The -.Nm man +.Nm library parses lines of .Xr man 7 input (and .Em only man) into an abstract syntax tree (AST). -.\" PARAGRAPH +. .Pp In general, applications initiate a parsing sequence with .Fn man_alloc , @@ -74,8 +78,58 @@ function may be used in order to reset the parser for another input sequence. See the .Sx EXAMPLES section for a full example. -.\" PARAGRAPH +. +.Pp +Beyond the full set of macros defined in +.Xr man 7 , +the +.Nm +library also accepts the following macros: +. .Pp +.Bl -tag -width Ds -compact +.It am +.It ami +.It de +.It dei +.It ig +Instructional macros in the original roff language. Blocks begun by +these macros end with +.Sq .. +and may begin anywhere, although they may not break the next-line +scoping rules specified in +.Xr man 7 . +These blocks are discarded. +. +.It PD +Has no effect. Handled as a current-scope line macro. +. +.It Sp +A synonym for +.Sq sp 0.5v +.Pq part of the standard preamble for Perl documentation . +Handled as a line macro. +. +.It UC +Has no effect. Handled as a current-scope line macro. +. +.It Vb +A synonym for +.Sq nf +.Pq part of the standard preamble for Perl documentation . +Handled as a current-scope line macro. +. +.It Ve +A synonym for +.Sq fi , +closing +.Sq Vb +.Pq part of the standard preamble for Perl documentation . +Handled as a current-scope line macro. +.El +. +. +.Sh REFERENCE This section further defines the .Sx Types , .Sx Functions @@ -84,7 +138,8 @@ and available to programmers. Following that, the .Sx Abstract Syntax Tree section documents the output tree. -.\" SUBSECTION +. +. .Ss Types Both functions (see .Sx Functions ) @@ -92,16 +147,16 @@ and variables (see .Sx Variables ) may use the following types: .Bl -ohang -.\" LIST-ITEM +. .It Vt struct man An opaque type defined in .Pa man.c . Its values are only used privately within the library. -.\" LIST-ITEM +. .It Vt struct man_cb A set of message callbacks defined in .Pa man.h . -.\" LIST-ITEM +. .It Vt struct man_node A parsed node. Defined in .Pa man.h . @@ -109,11 +164,12 @@ See .Sx Abstract Syntax Tree for details. .El -.\" SUBSECTION +. +. .Ss Functions Function descriptions follow: .Bl -ohang -.\" LIST-ITEM +. .It Fn man_alloc Allocates a parsing structure. The .Fa data @@ -126,29 +182,29 @@ arguments are defined in .Pa man.h . Returns NULL on failure. If non-NULL, the pointer must be freed with .Fn man_free . -.\" LIST-ITEM +. .It Fn man_reset Reset the parser for another parse routine. After its use, .Fn man_parseln behaves as if invoked for the first time. -.\" LIST-ITEM +. .It Fn man_free Free all resources of a parser. The pointer is no longer valid after invocation. -.\" LIST-ITEM +. .It Fn man_parseln Parse a nil-terminated line of input. This line should not contain the trailing newline. Returns 0 on failure, 1 on success. The input buffer .Fa buf is modified by this function. -.\" LIST-ITEM +. .It Fn man_endparse Signals that the parse is complete. Note that if .Fn man_endparse is called subsequent to .Fn man_node , the resulting tree is incomplete. Returns 0 on failure, 1 on success. -.\" LIST-ITEM +. .It Fn man_node Returns the first node of the parse. Note that if .Fn man_parseln @@ -163,15 +219,17 @@ or .Fn man_endparse return 0, the data will be incomplete. .El -.\" SUBSECTION +. +. .Ss Variables The following variables are also defined: .Bl -ohang -.\" LIST-ITEM +. .It Va man_macronames An array of string-ified token names. .El -.\" SUBSECTION +. +. .Ss Abstract Syntax Tree The .Nm @@ -185,13 +243,13 @@ or after or .Fn man_parseln fail, it may be incomplete. -.\" PARAGRAPH +. .Pp This AST is governed by the ontological rules dictated in .Xr man 7 and derives its terminology accordingly. -.\" PARAGRAPH +. .Pp The AST is composed of .Vt struct man_node @@ -210,13 +268,12 @@ fields), its position in the tree (the and .Va prev fields) and some type-specific data. -.\" PARAGRAPH +. .Pp The tree itself is arranged according to the following normal form, where capitalised non-terminals represent nodes. .Pp .Bl -tag -width "ELEMENTXX" -compact -.\" LIST-ITEM .It ROOT \(<- mnode+ .It mnode @@ -232,12 +289,13 @@ where capitalised non-terminals represent nodes. .It TEXT \(<- [[:alpha:]]* .El -.\" PARAGRAPH +. .Pp The only elements capable of nesting other elements are those with next-lint scope as documented in .Xr man 7 . -.\" SECTION +. +. .Sh EXAMPLES The following example reads lines from stdin and parses them, operating on the finished parse tree with @@ -273,11 +331,13 @@ if (NULL == (node = man_node(man))) parsed(man, node); man_free(man); .Ed -.\" SECTION +. +. .Sh SEE ALSO .Xr mandoc 1 , .Xr man 7 -.\" SECTION +. +. .Sh AUTHORS The .Nm diff --git a/usr.bin/mandoc/man.7 b/usr.bin/mandoc/man.7 index 860013dc77a..93c98c2f8ad 100644 --- a/usr.bin/mandoc/man.7 +++ b/usr.bin/mandoc/man.7 @@ -1,4 +1,4 @@ -.\" $Id: man.7,v 1.18 2010/03/26 01:22:05 schwarze Exp $ +.\" $Id: man.7,v 1.19 2010/03/29 22:56:52 schwarze Exp $ .\" .\" Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se> .\" @@ -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: March 26 2010 $ +.Dd $Mdocdate: March 29 2010 $ .Dt MAN 7 .Os . @@ -403,9 +403,11 @@ Documents any security precautions that operators should consider. Macros are one to three three characters in length and begin with a control character , .Sq \&. , -at the beginning of the line. An arbitrary amount of whitespace may -sit between the control character and the macro name. Thus, the -following are equivalent: +at the beginning of the line. The +.Sq \(aq +macro control character is also accepted. An arbitrary amount of +whitespace (spaces or tabs) may sit between the control character and +the macro name. Thus, the following are equivalent: .Bd -literal -offset indent \&.PP \&.\ \ \ PP @@ -999,22 +1001,33 @@ This section documents areas of questionable portability between implementations of the .Nm language. +. .Pp .Bl -dash -compact .It In quoted literals, GNU troff allowed pair-wise double-quotes to produce a standalone double-quote in formatted output. It is not known whether this behaviour is exhibited by other formatters. +. .It Blocks of whitespace are stripped from macro and free-form text lines (except when in literal mode) in mandoc. This is not the case for GNU troff: for maximum portability, whitespace sensitive blocks should be enclosed in literal contexts. +. .It The .Sx \&sp macro does not accept negative values in mandoc. In GNU troff, this would result in strange behaviour. +. +.It +The +.Sq \(aq +macro control character, in GNU troff (and prior troffs) suppresses a +newline before macro output; in mandoc, it is an alias for the standard +.Sq \&. +control character. .El . . diff --git a/usr.bin/mandoc/man.c b/usr.bin/mandoc/man.c index ec14a091747..90a42429aaf 100644 --- a/usr.bin/mandoc/man.c +++ b/usr.bin/mandoc/man.c @@ -1,4 +1,4 @@ -/* $Id: man.c,v 1.22 2010/03/26 01:22:05 schwarze Exp $ */ +/* $Id: man.c,v 1.23 2010/03/29 22:56:52 schwarze Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> * @@ -47,6 +47,8 @@ const char *const __man_merrnames[WERRMAX] = { "literal context already open", /* WOLITERAL */ "no literal context open", /* WNLITERAL */ "invalid nesting of roff declarations", /* WROFFNEST */ + "scope in roff instructions broken", /* WROFFSCOPE */ + "document title should be uppercase", /* WTITLECASE */ }; const char *const __man_macronames[MAN_MAX] = { @@ -151,7 +153,7 @@ int man_parseln(struct man *m, int ln, char *buf) { - return('.' == *buf ? + return('.' == *buf || '\'' == *buf ? man_pmacro(m, ln, buf) : man_ptext(m, ln, buf)); } @@ -443,7 +445,7 @@ descope: if (MAN_ELINE & m->flags) { m->flags &= ~MAN_ELINE; - if ( ! man_unscope(m, m->last->parent)) + if ( ! man_unscope(m, m->last->parent, WERRMAX)) return(0); } @@ -451,7 +453,7 @@ descope: return(1); m->flags &= ~MAN_BLINE; - if ( ! man_unscope(m, m->last->parent)) + if ( ! man_unscope(m, m->last->parent, WERRMAX)) return(0); return(man_body_alloc(m, line, 0, m->last->tok)); } @@ -486,9 +488,13 @@ man_pmacro(struct man *m, int ln, char *buf) i = 1; - if (' ' == buf[i]) { + /* + * Skip whitespace between the control character and initial + * text. "Whitespace" is both spaces and tabs. + */ + if (' ' == buf[i] || '\t' == buf[i]) { i++; - while (buf[i] && ' ' == buf[i]) + while (buf[i] && (' ' == buf[i] || '\t' == buf[i])) i++; if ('\0' == buf[i]) goto out; @@ -619,7 +625,7 @@ out: assert(MAN_BLINE & m->flags); m->flags &= ~MAN_BLINE; - if ( ! man_unscope(m, m->last->parent)) + if ( ! man_unscope(m, m->last->parent, WERRMAX)) return(0); return(man_body_alloc(m, ln, 0, m->last->tok)); diff --git a/usr.bin/mandoc/man_macro.c b/usr.bin/mandoc/man_macro.c index 8e62c005f22..94780ade071 100644 --- a/usr.bin/mandoc/man_macro.c +++ b/usr.bin/mandoc/man_macro.c @@ -1,4 +1,4 @@ -/* $Id: man_macro.c,v 1.12 2010/03/26 01:22:05 schwarze Exp $ */ +/* $Id: man_macro.c,v 1.13 2010/03/29 22:56:52 schwarze Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> * @@ -24,7 +24,7 @@ enum rew { REW_REWIND, REW_NOHALT, - REW_HALT, + REW_HALT }; static int blk_close(MACRO_PROT_ARGS); @@ -39,6 +39,8 @@ static enum rew rew_dohalt(enum mant, enum man_type, const struct man_node *); static enum rew rew_block(enum mant, enum man_type, const struct man_node *); +static int rew_warn(struct man *, + struct man_node *, enum merr); const struct man_macro __man_macros[MAN_MAX] = { { in_line_eoln, MAN_NSCOPED }, /* br */ @@ -87,14 +89,39 @@ const struct man_macro __man_macros[MAN_MAX] = { const struct man_macro * const man_macros = __man_macros; +/* + * Warn when "n" is an explicit non-roff macro. + */ +static int +rew_warn(struct man *m, struct man_node *n, enum merr er) +{ + + if (er == WERRMAX || MAN_BLOCK != n->type) + return(1); + if (MAN_VALID & n->flags) + return(1); + if ( ! (MAN_EXPLICIT & man_macros[n->tok].flags)) + return(1); + if (MAN_NOCLOSE & man_macros[n->tok].flags) + return(1); + return(man_nwarn(m, n, er)); +} + + +/* + * Rewind scope. If a code "er" != WERRMAX has been provided, it will + * be used if an explicit block scope is being closed out. + */ int -man_unscope(struct man *m, const struct man_node *n) +man_unscope(struct man *m, const struct man_node *n, enum merr er) { assert(n); /* LINTED */ while (m->last != n) { + if ( ! rew_warn(m, m->last, er)) + return(0); if ( ! man_valid_post(m)) return(0); if ( ! man_action_post(m)) @@ -103,6 +130,8 @@ man_unscope(struct man *m, const struct man_node *n) assert(m->last); } + if ( ! rew_warn(m, m->last, er)) + return(0); if ( ! man_valid_post(m)) return(0); if ( ! man_action_post(m)) @@ -136,18 +165,47 @@ rew_dohalt(enum mant tok, enum man_type type, const struct man_node *n) { enum rew c; + /* We cannot progress beyond the root ever. */ if (MAN_ROOT == n->type) return(REW_HALT); + assert(n->parent); + + /* Normal nodes shouldn't go to the level of the root. */ if (MAN_ROOT == n->parent->type) return(REW_REWIND); + + /* Already-validated nodes should be closed out. */ if (MAN_VALID & n->flags) return(REW_NOHALT); - /* Rewind to ourselves, first. */ + /* First: rewind to ourselves. */ if (type == n->type && tok == n->tok) return(REW_REWIND); + /* + * If we're a roff macro, then we can close out anything that + * stands between us and our parent context. + */ + if (MAN_NOCLOSE & man_macros[tok].flags) + return(REW_NOHALT); + + /* + * Don't clobber roff macros: this is a bit complicated. If the + * current macro is a roff macro, halt immediately and don't + * rewind. If it's not, and the parent is, then close out the + * current scope and halt at the parent. + */ + if (MAN_NOCLOSE & man_macros[n->tok].flags) + return(REW_HALT); + if (MAN_NOCLOSE & man_macros[n->parent->tok].flags) + return(REW_REWIND); + + /* + * Next follow the implicit scope-smashings as defined by man.7: + * section, sub-section, etc. + */ + switch (tok) { case (MAN_SH): break; @@ -206,10 +264,15 @@ rew_scope(enum man_type type, struct man *m, enum mant tok) break; } - /* Rewind until the current point. */ - + /* + * Rewind until the current point. Warn if we're a roff + * instruction that's mowing over explicit scopes. + */ assert(n); - return(man_unscope(m, n)); + if (MAN_NOCLOSE & man_macros[tok].flags) + return(man_unscope(m, n, WROFFSCOPE)); + + return(man_unscope(m, n, WERRMAX)); } @@ -225,6 +288,8 @@ blk_dotted(MACRO_PROT_ARGS) enum mant ntok; struct man_node *nn; + /* Check for any of the following parents... */ + for (nn = m->last->parent; nn; nn = nn->parent) if (nn->tok == MAN_de || nn->tok == MAN_dei || nn->tok == MAN_am || @@ -245,6 +310,20 @@ blk_dotted(MACRO_PROT_ARGS) if ( ! rew_scope(MAN_BLOCK, m, ntok)) return(0); + /* + * XXX: manually adjust our next-line status. roff macros are, + * for the moment, ignored, so we don't want to close out bodies + * and so on. + */ + + switch (m->last->type) { + case (MAN_BODY): + m->next = MAN_NEXT_CHILD; + break; + default: + break; + } + return(1); } @@ -476,20 +555,7 @@ in_line_eoln(MACRO_PROT_ARGS) int man_macroend(struct man *m) { - struct man_node *n; - - n = MAN_VALID & m->last->flags ? - m->last->parent : m->last; - - for ( ; n; n = n->parent) { - if (MAN_BLOCK != n->type) - continue; - if ( ! (MAN_EXPLICIT & man_macros[n->tok].flags)) - continue; - if ( ! man_nwarn(m, n, WEXITSCOPE)) - return(0); - } - return(man_unscope(m, m->first)); + return(man_unscope(m, m->first, WEXITSCOPE)); } diff --git a/usr.bin/mandoc/man_validate.c b/usr.bin/mandoc/man_validate.c index 4630457a58a..826df3c3138 100644 --- a/usr.bin/mandoc/man_validate.c +++ b/usr.bin/mandoc/man_validate.c @@ -1,4 +1,4 @@ -/* $Id: man_validate.c,v 1.14 2010/03/26 01:22:05 schwarze Exp $ */ +/* $Id: man_validate.c,v 1.15 2010/03/29 22:56:52 schwarze Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> * @@ -46,9 +46,10 @@ static int check_roff(CHKARGS); static int check_root(CHKARGS); static int check_sec(CHKARGS); static int check_text(CHKARGS); +static int check_title(CHKARGS); static v_check posts_eq0[] = { check_eq0, NULL }; -static v_check posts_ge2_le5[] = { check_ge2, check_le5, NULL }; +static v_check posts_th[] = { check_ge2, check_le5, check_title, NULL }; static v_check posts_par[] = { check_par, NULL }; static v_check posts_part[] = { check_part, NULL }; static v_check posts_sec[] = { check_sec, NULL }; @@ -58,7 +59,7 @@ static v_check pres_roff[] = { check_bline, check_roff, NULL }; static const struct man_valid man_valids[MAN_MAX] = { { NULL, posts_eq0 }, /* br */ - { pres_bline, posts_ge2_le5 }, /* TH */ /* FIXME: make sure capitalised. */ + { pres_bline, posts_th }, /* TH */ { pres_bline, posts_sec }, /* SH */ { pres_bline, posts_sec }, /* SS */ { pres_bline, posts_par }, /* TP */ @@ -170,6 +171,24 @@ check_root(CHKARGS) static int +check_title(CHKARGS) +{ + const char *p; + + assert(n->child); + if ('\0' == *n->child->string) + return(man_nerr(m, n, WNOTITLE)); + + for (p = n->child->string; '\0' != *p; p++) + if (isalpha((u_char)*p) && ! isupper((u_char)*p)) + if ( ! man_nwarn(m, n, WTITLECASE)) + return(0); + + return(1); +} + + +static int check_text(CHKARGS) { const char *p; diff --git a/usr.bin/mandoc/mandoc.1 b/usr.bin/mandoc/mandoc.1 index 4285e14d7c8..6f93c741698 100644 --- a/usr.bin/mandoc/mandoc.1 +++ b/usr.bin/mandoc/mandoc.1 @@ -1,4 +1,4 @@ -.\" $Id: mandoc.1,v 1.23 2010/03/26 01:22:05 schwarze Exp $ +.\" $Id: mandoc.1,v 1.24 2010/03/29 22:56:52 schwarze Exp $ .\" .\" Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se> .\" @@ -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: March 26 2010 $ +.Dd $Mdocdate: March 29 2010 $ .Dt MANDOC 1 .Os . @@ -510,6 +510,7 @@ CSS2 styling used for .Fl m Ns Ar doc input lists does not render properly in older browsers, such as Internet Explorer 6 and earlier. +. .Pp In .Fl T Ns Ar html @@ -520,6 +521,7 @@ the maximum size of an element attribute is determined by which is usually 1024 bytes. Be aware of this when setting long link formats, e.g., .Fl O Ns Ar style=really/long/link . +. .Pp The .Fl T Ns Ar html @@ -531,6 +533,7 @@ font size escape documented in .Xr mdoc 7 and .Xr man 7 . +. .Pp Nesting elements within next-line element scopes of .Fl m Ns Ar an , @@ -543,6 +546,7 @@ will confuse and .Fl T Ns Ar xhtml and cause them to forget the formatting of the prior next-line scope. +. .Pp The .Sq i @@ -550,3 +554,8 @@ macro in .Fl m Ns Ar an should italicise all subsequent text if a line argument is not provided. This behaviour is not implemented. +. +The +.Sq \(aq +control character is an alias for the standard macro control character +and does not emit a line-break as stipulated in GNU troff. |