-/* $Id: man.c,v 1.125 2014/03/23 11:25:26 schwarze Exp $ */
+/* $Id: man.c,v 1.139 2014/09/06 23:24:32 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifdef HAVE_CONFIG_H
#include "config.h"
-#endif
#include <sys/types.h>
#include <assert.h>
+#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include "libman.h"
#include "libmandoc.h"
-const char *const __man_macronames[MAN_MAX] = {
+const char *const __man_macronames[MAN_MAX] = {
"br", "TH", "SH", "SS",
- "TP", "LP", "PP", "P",
+ "TP", "LP", "PP", "P",
"IP", "HP", "SM", "SB",
"BI", "IB", "BR", "RB",
"R", "B", "I", "IR",
"fi", "RE", "RS", "DT",
"UC", "PD", "AT", "in",
"ft", "OP", "EX", "EE",
- "UR", "UE"
+ "UR", "UE", "ll"
};
const char * const *man_macronames = __man_macronames;
-static struct man_node *man_node_alloc(struct man *, int, int,
+static struct man_node *man_node_alloc(struct man *, int, int,
enum man_type, enum mant);
-static int man_node_append(struct man *,
+static int man_node_append(struct man *,
struct man_node *);
static void man_node_free(struct man_node *);
-static void man_node_unlink(struct man *,
+static void man_node_unlink(struct man *,
struct man_node *);
static int man_ptext(struct man *, int, char *, int);
static int man_pmacro(struct man *, int, char *, int);
man_node(const struct man *man)
{
- assert( ! (MAN_HALT & man->flags));
return(man->first);
}
-
const struct man_meta *
man_meta(const struct man *man)
{
- assert( ! (MAN_HALT & man->flags));
return(&man->meta);
}
-
void
man_reset(struct man *man)
{
man_alloc1(man);
}
-
void
man_free(struct man *man)
{
free(man);
}
-
struct man *
man_alloc(struct roff *roff, struct mparse *parse, int quick)
{
return(p);
}
-
int
man_endparse(struct man *man)
{
- assert( ! (MAN_HALT & man->flags));
- if (man_macroend(man))
- return(1);
- man->flags |= MAN_HALT;
- return(0);
+ return(man_macroend(man));
}
-
int
man_parseln(struct man *man, int ln, char *buf, int offs)
{
man->flags |= MAN_NEWLINE;
- assert( ! (MAN_HALT & man->flags));
-
return (roff_getcontrol(man->roff, buf, &offs) ?
- man_pmacro(man, ln, buf, offs) :
- man_ptext(man, ln, buf, offs));
+ man_pmacro(man, ln, buf, offs) :
+ man_ptext(man, ln, buf, offs));
}
-
static void
man_free1(struct man *man)
{
free(man->meta.msec);
}
-
static void
man_alloc1(struct man *man)
{
assert(MAN_ROOT != p->type);
switch (man->next) {
- case (MAN_NEXT_SIBLING):
+ case MAN_NEXT_SIBLING:
man->last->next = p;
p->prev = man->last;
p->parent = man->last->parent;
break;
- case (MAN_NEXT_CHILD):
+ case MAN_NEXT_CHILD:
man->last->child = p;
p->parent = man->last;
break;
abort();
/* NOTREACHED */
}
-
+
assert(p->parent);
p->parent->nchild++;
- if ( ! man_valid_pre(man, p))
- return(0);
-
switch (p->type) {
- case (MAN_HEAD):
+ case MAN_BLOCK:
+ if (p->tok == MAN_SH || p->tok == MAN_SS)
+ man->flags &= ~MAN_LITERAL;
+ break;
+ case MAN_HEAD:
assert(MAN_BLOCK == p->parent->type);
p->parent->head = p;
break;
- case (MAN_TAIL):
+ case MAN_TAIL:
assert(MAN_BLOCK == p->parent->type);
p->parent->tail = p;
break;
- case (MAN_BODY):
+ case MAN_BODY:
assert(MAN_BLOCK == p->parent->type);
p->parent->body = p;
break;
man->last = p;
switch (p->type) {
- case (MAN_TBL):
+ case MAN_TBL:
/* FALLTHROUGH */
- case (MAN_TEXT):
+ case MAN_TEXT:
if ( ! man_valid_post(man))
return(0);
break;
return(1);
}
-
static struct man_node *
-man_node_alloc(struct man *man, int line, int pos,
+man_node_alloc(struct man *man, int line, int pos,
enum man_type type, enum mant tok)
{
struct man_node *p;
return(p);
}
-
int
man_elem_alloc(struct man *man, int line, int pos, enum mant tok)
{
return(1);
}
-
int
man_tail_alloc(struct man *man, int line, int pos, enum mant tok)
{
return(1);
}
-
int
man_head_alloc(struct man *man, int line, int pos, enum mant tok)
{
return(1);
}
-
int
man_body_alloc(struct man *man, int line, int pos, enum mant tok)
{
return(1);
}
-
int
man_block_alloc(struct man *man, int line, int pos, enum mant tok)
{
return(1);
}
-
/*
* Free all of the resources held by a node. This does NOT unlink a
* node from its context; for that, see man_node_unlink().
free(p);
}
-
void
man_node_delete(struct man *man, struct man_node *p)
{
{
struct man_node *n;
- assert( ! (MAN_HALT & man->flags));
-
n = man_node_alloc(man, ep->ln, ep->pos, MAN_EQN, MAN_MAX);
n->eqn = ep;
{
struct man_node *n;
- assert( ! (MAN_HALT & man->flags));
-
n = man_node_alloc(man, sp->line, 0, MAN_TBL, MAN_MAX);
n->span = sp;
if (MAN_ELINE & man->flags) {
man->flags &= ~MAN_ELINE;
- if ( ! man_unscope(man, man->last->parent, MANDOCERR_MAX))
+ if ( ! man_unscope(man, man->last->parent))
return(0);
}
return(1);
man->flags &= ~MAN_BLINE;
- if ( ! man_unscope(man, man->last->parent, MANDOCERR_MAX))
+ if ( ! man_unscope(man, man->last->parent))
return(0);
return(man_body_alloc(man, line, offs, man->last->tok));
}
return(1);
}
- /*
+ /*
* Warn if the last un-escaped character is whitespace. Then
- * strip away the remaining spaces (tabs stay!).
+ * strip away the remaining spaces (tabs stay!).
*/
i = (int)strlen(buf);
if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
if (i > 1 && '\\' != buf[i - 2])
- man_pmsg(man, line, i - 1, MANDOCERR_EOLNSPACE);
+ mandoc_msg(MANDOCERR_SPACE_EOL, man->parse,
+ line, i - 1, NULL);
for (--i; i && ' ' == buf[i]; i--)
/* Spin back to non-space. */ ;
static int
man_pmacro(struct man *man, int ln, char *buf, int offs)
{
- int i, ppos;
- enum mant tok;
char mac[5];
struct man_node *n;
-
- if ('"' == buf[offs]) {
- man_pmsg(man, ln, offs, MANDOCERR_BADCOMMENT);
- return(1);
- } else if ('\0' == buf[offs])
- return(1);
+ enum mant tok;
+ int i, ppos;
+ int bline;
ppos = offs;
*/
i = 0;
- while (i < 4 && '\0' != buf[offs] &&
- ' ' != buf[offs] && '\t' != buf[offs])
+ while (i < 4 && '\0' != buf[offs] && ' ' != buf[offs] &&
+ '\t' != buf[offs])
mac[i++] = buf[offs++];
mac[i] = '\0';
tok = (i > 0 && i < 4) ? man_hash_find(mac) : MAN_MAX;
if (MAN_MAX == tok) {
- mandoc_vmsg(MANDOCERR_MACRO, man->parse, ln,
- ppos, "%s", buf + ppos - 1);
+ mandoc_msg(MANDOCERR_MACRO, man->parse,
+ ln, ppos, buf + ppos - 1);
return(1);
}
while (buf[offs] && ' ' == buf[offs])
offs++;
- /*
+ /*
* Trailing whitespace. Note that tabs are allowed to be passed
* into the parser as "text", so we only warn about spaces here.
*/
if ('\0' == buf[offs] && ' ' == buf[offs - 1])
- man_pmsg(man, ln, offs - 1, MANDOCERR_EOLNSPACE);
+ mandoc_msg(MANDOCERR_SPACE_EOL, man->parse,
+ ln, offs - 1, NULL);
- /*
+ /*
* Remove prior ELINE macro, as it's being clobbered by a new
* macro. Note that NSCOPED macros do not close out ELINE
* macros---they don't print text---so we let those slip by.
if (MAN_NSCOPED & man_macros[n->tok].flags)
n = n->parent;
- mandoc_vmsg(MANDOCERR_LINESCOPE, man->parse, n->line,
+ mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, n->line,
n->pos, "%s breaks %s", man_macronames[tok],
man_macronames[n->tok]);
assert(MAN_BLOCK == n->type);
assert(MAN_SCOPED & man_macros[n->tok].flags);
- mandoc_vmsg(MANDOCERR_LINESCOPE, man->parse, n->line,
+ mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, n->line,
n->pos, "%s breaks %s", man_macronames[tok],
man_macronames[n->tok]);
man->flags &= ~MAN_BLINE;
}
- /*
- * Save the fact that we're in the next-line for a block. In
- * this way, embedded roff instructions can "remember" state
- * when they exit.
- */
+ /* Remember whether we are in next-line scope for a block head. */
- if (MAN_BLINE & man->flags)
- man->flags |= MAN_BPLINE;
+ bline = man->flags & MAN_BLINE;
/* Call to handler... */
assert(man_macros[tok].fp);
if ( ! (*man_macros[tok].fp)(man, tok, ln, ppos, &offs, buf))
- goto err;
+ return(0);
/* In quick mode (for mandocdb), abort after the NAME section. */
- if (man->quick && MAN_SH == tok &&
- strcmp(man->last->prev->child->string, "NAME"))
- return(2);
-
- /*
- * We weren't in a block-line scope when entering the
- * above-parsed macro, so return.
- */
-
- if ( ! (MAN_BPLINE & man->flags)) {
- man->flags &= ~MAN_ILINE;
- return(1);
+ if (man->quick && MAN_SH == tok) {
+ n = man->last;
+ if (MAN_BODY == n->type &&
+ strcmp(n->prev->child->string, "NAME"))
+ return(2);
}
- man->flags &= ~MAN_BPLINE;
/*
- * If we're in a block scope, then allow this macro to slip by
- * without closing scope around it.
- */
-
- if (MAN_ILINE & man->flags) {
- man->flags &= ~MAN_ILINE;
- return(1);
- }
-
- /*
- * If we've opened a new next-line element scope, then return
- * now, as the next line will close out the block scope.
+ * If we are in a next-line scope for a block head,
+ * close it out now and switch to the body,
+ * unless the next-line scope is allowed to continue.
*/
- if (MAN_ELINE & man->flags)
+ if ( ! bline || man->flags & MAN_ELINE ||
+ man_macros[tok].flags & MAN_NSCOPED)
return(1);
- /* Close out the block scope opened in the prior line. */
-
assert(MAN_BLINE & man->flags);
man->flags &= ~MAN_BLINE;
- if ( ! man_unscope(man, man->last->parent, MANDOCERR_MAX))
+ if ( ! man_unscope(man, man->last->parent))
return(0);
return(man_body_alloc(man, ln, ppos, man->last->tok));
-
-err: /* Error out. */
-
- man->flags |= MAN_HALT;
- return(0);
}
/*
assert(man && man->parse);
return(man->parse);
}
+
+void
+man_deroff(char **dest, const struct man_node *n)
+{
+ char *cp;
+ size_t sz;
+
+ if (MAN_TEXT != n->type) {
+ for (n = n->child; n; n = n->next)
+ man_deroff(dest, n);
+ return;
+ }
+
+ /* Skip leading whitespace and escape sequences. */
+
+ cp = n->string;
+ while ('\0' != *cp) {
+ if ('\\' == *cp) {
+ cp++;
+ mandoc_escape((const char **)&cp, NULL, NULL);
+ } else if (isspace((unsigned char)*cp))
+ cp++;
+ else
+ break;
+ }
+
+ /* Skip trailing whitespace. */
+
+ for (sz = strlen(cp); sz; sz--)
+ if (0 == isspace((unsigned char)cp[sz-1]))
+ break;
+
+ /* Skip empty strings. */
+
+ if (0 == sz)
+ return;
+
+ if (NULL == *dest) {
+ *dest = mandoc_strndup(cp, sz);
+ return;
+ }
+
+ mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
+ free(*dest);
+ *dest = cp;
+}