man.c revision 260e9a87725c090ba5835b1f9f0b62fa2f96036f
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov/* $Id: man.c,v 1.149 2015/01/30 21:28:46 schwarze Exp $ */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore/*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Copyright (c) 2011 Joerg Sonnenberger <joerg@netbsd.org>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore *
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * copyright notice and this permission notice appear in all copies.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore *
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include "config.h"
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <sys/types.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <assert.h>
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov#include <ctype.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <stdarg.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <stdlib.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <stdio.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include <string.h>
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include "man.h"
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include "mandoc.h"
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov#include "mandoc_aux.h"
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include "libman.h"
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore#include "libmandoc.h"
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovconst char *const __man_macronames[MAN_MAX] = {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore "br", "TH", "SH", "SS",
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov "TP", "LP", "PP", "P",
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore "IP", "HP", "SM", "SB",
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore "BI", "IB", "BR", "RB",
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore "R", "B", "I", "IR",
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov "RI", "sp", "nf",
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore "fi", "RE", "RS", "DT",
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore "UC", "PD", "AT", "in",
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore "ft", "OP", "EX", "EE",
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov "UR", "UE", "ll"
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore };
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreconst char * const *man_macronames = __man_macronames;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovstatic void man_alloc1(struct man *);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovstatic void man_breakscope(struct man *, enum mant);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovstatic void man_descope(struct man *, int, int);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovstatic void man_free1(struct man *);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovstatic struct man_node *man_node_alloc(struct man *, int, int,
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore enum man_type, enum mant);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovstatic void man_node_append(struct man *, struct man_node *);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic void man_node_free(struct man_node *);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovstatic void man_node_unlink(struct man *,
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore struct man_node *);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic int man_ptext(struct man *, int, char *, int);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic int man_pmacro(struct man *, int, char *, int);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreconst struct man_node *
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_node(const struct man *man)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore return(man->first);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreconst struct man_meta *
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_meta(const struct man *man)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore return(&man->meta);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorevoid
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreman_reset(struct man *man)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore man_free1(man);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore man_alloc1(man);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorevoid
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreman_free(struct man *man)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore man_free1(man);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore free(man);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestruct man *
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovman_alloc(struct roff *roff, struct mparse *parse,
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov const char *defos, int quick)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore struct man *p;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p = mandoc_calloc(1, sizeof(struct man));
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore man_hash_init();
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->parse = parse;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov p->defos = defos;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov p->quick = quick;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->roff = roff;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore man_alloc1(p);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore return(p);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovvoid
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_endparse(struct man *man)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_macroend(man);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreint
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_parseln(struct man *man, int ln, char *buf, int offs)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (man->last->type != MAN_EQN || ln > man->last->line)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man->flags |= MAN_NEWLINE;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore return (roff_getcontrol(man->roff, buf, &offs) ?
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_pmacro(man, ln, buf, offs) :
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_ptext(man, ln, buf, offs));
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic void
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreman_free1(struct man *man)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (man->first)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore man_node_delete(man, man->first);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov free(man->meta.title);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov free(man->meta.source);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov free(man->meta.date);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov free(man->meta.vol);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov free(man->meta.msec);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic void
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_alloc1(struct man *man)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore memset(&man->meta, 0, sizeof(struct man_meta));
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->flags = 0;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->last = mandoc_calloc(1, sizeof(struct man_node));
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->first = man->last;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->last->type = MAN_ROOT;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->last->tok = MAN_MAX;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->next = MAN_NEXT_CHILD;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovstatic void
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreman_node_append(struct man *man, struct man_node *p)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore assert(man->last);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore assert(man->first);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov assert(p->type != MAN_ROOT);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore switch (man->next) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case MAN_NEXT_SIBLING:
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore man->last->next = p;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->prev = man->last;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->parent = man->last->parent;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case MAN_NEXT_CHILD:
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore man->last->child = p;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->parent = man->last;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore default:
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore abort();
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* NOTREACHED */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore assert(p->parent);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->parent->nchild++;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore switch (p->type) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case MAN_BLOCK:
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (p->tok == MAN_SH || p->tok == MAN_SS)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man->flags &= ~MAN_LITERAL;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case MAN_HEAD:
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov assert(p->parent->type == MAN_BLOCK);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov p->parent->head = p;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case MAN_BODY:
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov assert(p->parent->type == MAN_BLOCK);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->parent->body = p;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore default:
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore man->last = p;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore switch (p->type) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case MAN_TBL:
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* FALLTHROUGH */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case MAN_TEXT:
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_valid_post(man);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore default:
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore break;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic struct man_node *
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovman_node_alloc(struct man *man, int line, int pos,
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore enum man_type type, enum mant tok)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore struct man_node *p;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p = mandoc_calloc(1, sizeof(struct man_node));
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->line = line;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->pos = pos;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->type = type;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->tok = tok;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (man->flags & MAN_NEWLINE)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore p->flags |= MAN_LINE;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->flags &= ~MAN_NEWLINE;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore return(p);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovvoid
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_elem_alloc(struct man *man, int line, int pos, enum mant tok)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore struct man_node *p;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore p = man_node_alloc(man, line, pos, MAN_ELEM, tok);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_node_append(man, p);
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->next = MAN_NEXT_CHILD;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovvoid
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_head_alloc(struct man *man, int line, int pos, enum mant tok)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore struct man_node *p;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore p = man_node_alloc(man, line, pos, MAN_HEAD, tok);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_node_append(man, p);
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->next = MAN_NEXT_CHILD;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovvoid
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_body_alloc(struct man *man, int line, int pos, enum mant tok)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore struct man_node *p;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore p = man_node_alloc(man, line, pos, MAN_BODY, tok);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_node_append(man, p);
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->next = MAN_NEXT_CHILD;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovvoid
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_block_alloc(struct man *man, int line, int pos, enum mant tok)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore struct man_node *p;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore p = man_node_alloc(man, line, pos, MAN_BLOCK, tok);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_node_append(man, p);
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->next = MAN_NEXT_CHILD;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovvoid
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_word_alloc(struct man *man, int line, int pos, const char *word)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore struct man_node *n;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore n = man_node_alloc(man, line, pos, MAN_TEXT, MAN_MAX);
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore n->string = roff_strdup(man->roff, word);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_node_append(man, n);
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->next = MAN_NEXT_SIBLING;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovvoid
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovman_word_append(struct man *man, const char *word)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov{
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov struct man_node *n;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov char *addstr, *newstr;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov n = man->last;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov addstr = roff_strdup(man->roff, word);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov free(addstr);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov free(n->string);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov n->string = newstr;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man->next = MAN_NEXT_SIBLING;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore/*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Free all of the resources held by a node. This does NOT unlink a
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * node from its context; for that, see man_node_unlink().
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic void
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreman_node_free(struct man_node *p)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov free(p->string);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore free(p);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorevoid
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_node_delete(struct man *man, struct man_node *p)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore while (p->child)
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man_node_delete(man, p->child);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man_node_unlink(man, p);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore man_node_free(p);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovvoid
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_addeqn(struct man *man, const struct eqn *ep)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore struct man_node *n;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore n = man_node_alloc(man, ep->ln, ep->pos, MAN_EQN, MAN_MAX);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore n->eqn = ep;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (ep->ln > man->last->line)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov n->flags |= MAN_LINE;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_node_append(man, n);
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->next = MAN_NEXT_SIBLING;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_descope(man, ep->ln, ep->pos);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovvoid
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_addspan(struct man *man, const struct tbl_span *sp)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore struct man_node *n;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_breakscope(man, MAN_MAX);
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore n = man_node_alloc(man, sp->line, 0, MAN_TBL, MAN_MAX);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore n->span = sp;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_node_append(man, n);
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->next = MAN_NEXT_SIBLING;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_descope(man, sp->line, 0);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovstatic void
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_descope(struct man *man, int line, int offs)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Co-ordinate what happens with having a next-line scope open:
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * first close out the element scope (if applicable), then close
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * out the block scope (also if applicable).
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (man->flags & MAN_ELINE) {
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->flags &= ~MAN_ELINE;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_unscope(man, man->last->parent);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if ( ! (man->flags & MAN_BLINE))
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov return;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->flags &= ~MAN_BLINE;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_unscope(man, man->last->parent);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_body_alloc(man, line, offs, man->last->tok);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic int
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_ptext(struct man *man, int line, char *buf, int offs)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore int i;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Literal free-form text whitespace is preserved. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (man->flags & MAN_LITERAL) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_word_alloc(man, line, offs, buf + offs);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_descope(man, line, offs);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov return(1);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov for (i = offs; buf[i] == ' '; i++)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Skip leading whitespace. */ ;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore /*
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * Blank lines are ignored right after headings
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * but add a single vertical space elsewhere.
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore */
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (buf[i] == '\0') {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Allocate a blank entry. */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (man->last->tok != MAN_SH &&
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man->last->tok != MAN_SS) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_elem_alloc(man, line, offs, MAN_sp);
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->next = MAN_NEXT_SIBLING;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore }
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore return(1);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Warn if the last un-escaped character is whitespace. Then
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * strip away the remaining spaces (tabs stay!).
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore i = (int)strlen(buf);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore assert(i);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (i > 1 && '\\' != buf[i - 2])
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov mandoc_msg(MANDOCERR_SPACE_EOL, man->parse,
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov line, i - 1, NULL);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore for (--i; i && ' ' == buf[i]; i--)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Spin back to non-space. */ ;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Jump ahead of escaped whitespace. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore i += '\\' == buf[i] ? 2 : 1;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore buf[i] = '\0';
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_word_alloc(man, line, offs, buf + offs);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * End-of-sentence check. If the last character is an unescaped
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * EOS character, then flag the node as being the end of a
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * sentence. The front-end will know how to interpret this.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore assert(i);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (mandoc_eos(buf, (size_t)i))
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->last->flags |= MAN_EOS;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_descope(man, line, offs);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov return(1);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic int
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_pmacro(struct man *man, int ln, char *buf, int offs)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov struct man_node *n;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov const char *cp;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore enum mant tok;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov int i, ppos;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov int bline;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore char mac[5];
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore ppos = offs;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Copy the first word into a nil-terminated buffer.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Stop when a space, tab, escape, or eoln is encountered.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore i = 0;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov while (i < 4 && strchr(" \t\\", buf[offs]) == NULL)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore mac[i++] = buf[offs++];
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore mac[i] = '\0';
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore tok = (i > 0 && i < 4) ? man_hash_find(mac) : MAN_MAX;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (tok == MAN_MAX) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov mandoc_msg(MANDOCERR_MACRO, man->parse,
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov ln, ppos, buf + ppos - 1);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore return(1);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov /* Skip a leading escape sequence or tab. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov switch (buf[offs]) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '\\':
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov cp = buf + offs + 1;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov mandoc_escape(&cp, NULL, NULL);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov offs = cp - buf;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov case '\t':
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore offs++;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov default:
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov }
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov /* Jump to the next non-whitespace word. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov while (buf[offs] && buf[offs] == ' ')
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov offs++;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov /*
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * Trailing whitespace. Note that tabs are allowed to be passed
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * into the parser as "text", so we only warn about spaces here.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (buf[offs] == '\0' && buf[offs - 1] == ' ')
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov mandoc_msg(MANDOCERR_SPACE_EOL, man->parse,
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov ln, offs - 1, NULL);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov /*
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Some macros break next-line scopes; otherwise, remember
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * whether we are in next-line scope for a block head.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_breakscope(man, tok);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov bline = man->flags & MAN_BLINE;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov /* Call to handler... */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov assert(man_macros[tok].fp);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov (*man_macros[tok].fp)(man, tok, ln, ppos, &offs, buf);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov /* In quick mode (for mandocdb), abort after the NAME section. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (man->quick && tok == MAN_SH) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov n = man->last;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (n->type == MAN_BODY &&
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov strcmp(n->prev->child->string, "NAME"))
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov return(2);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * If we are in a next-line scope for a block head,
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * close it out now and switch to the body,
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * unless the next-line scope is allowed to continue.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if ( ! bline || man->flags & MAN_ELINE ||
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_macros[tok].flags & MAN_NSCOPED)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov return(1);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov assert(man->flags & MAN_BLINE);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man->flags &= ~MAN_BLINE;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_unscope(man, man->last->parent);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_body_alloc(man, ln, ppos, man->last->tok);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov return(1);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovvoid
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovman_breakscope(struct man *man, enum mant tok)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov{
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov struct man_node *n;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * An element next line scope is open,
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * and the new macro is not allowed inside elements.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Delete the element that is being broken.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (man->flags & MAN_ELINE && (tok == MAN_MAX ||
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov ! (man_macros[tok].flags & MAN_NSCOPED))) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov n = man->last;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov assert(n->type != MAN_TEXT);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (man_macros[n->tok].flags & MAN_NSCOPED)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov n = n->parent;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse,
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov n->line, n->pos, "%s breaks %s",
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov tok == MAN_MAX ? "TS" : man_macronames[tok],
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_macronames[n->tok]);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_node_delete(man, n);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man->flags &= ~MAN_ELINE;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * A block header next line scope is open,
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * and the new macro is not allowed inside block headers.
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov * Delete the block that is being broken.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (man->flags & MAN_BLINE && (tok == MAN_MAX ||
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_macros[tok].flags & MAN_BSCOPE)) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov n = man->last;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (n->type == MAN_TEXT)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov n = n->parent;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if ( ! (man_macros[n->tok].flags & MAN_BSCOPE))
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov n = n->parent;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov assert(n->type == MAN_HEAD);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov n = n->parent;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov assert(n->type == MAN_BLOCK);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov assert(man_macros[n->tok].flags & MAN_SCOPED);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse,
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov n->line, n->pos, "%s breaks %s",
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov tok == MAN_MAX ? "TS" : man_macronames[tok],
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_macronames[n->tok]);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_node_delete(man, n);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man->flags &= ~MAN_BLINE;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore/*
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore * Unlink a node from its context. If "man" is provided, the last parse
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore * point will also be adjusted accordingly.
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amorestatic void
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_node_unlink(struct man *man, struct man_node *n)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Adjust siblings. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (n->prev)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore n->prev->next = n->next;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (n->next)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore n->next->prev = n->prev;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Adjust parent. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (n->parent) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore n->parent->nchild--;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (n->parent->child == n)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore n->parent->child = n->prev ? n->prev : n->next;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /* Adjust parse point, if applicable. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if (man && man->last == n) {
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*XXX: this can occur when bailing from validation. */
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore /*assert(NULL == n->next);*/
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore if (n->prev) {
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->last = n->prev;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->next = MAN_NEXT_SIBLING;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore } else {
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->last = n->parent;
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->next = MAN_NEXT_CHILD;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore }
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore if (man && man->first == n)
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore man->first = NULL;
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amoreconst struct mparse *
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amoreman_mparse(const struct man *man)
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore{
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore assert(man && man->parse);
698f87a48e2e945bfe5493ce168e0d0ae1cedd5cGarrett D'Amore return(man->parse);
95c635efb7c3b86efc493e0447eaec7aecca3f0fGarrett D'Amore}
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovvoid
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankovman_deroff(char **dest, const struct man_node *n)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov{
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov char *cp;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov size_t sz;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (n->type != MAN_TEXT) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov for (n = n->child; n; n = n->next)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov man_deroff(dest, n);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov return;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov }
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov /* Skip leading whitespace and escape sequences. */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov cp = n->string;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov while ('\0' != *cp) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if ('\\' == *cp) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov cp++;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov mandoc_escape((const char **)&cp, NULL, NULL);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov } else if (isspace((unsigned char)*cp))
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov cp++;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov else
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov }
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov /* Skip trailing whitespace. */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov for (sz = strlen(cp); sz; sz--)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (0 == isspace((unsigned char)cp[sz-1]))
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov break;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov /* Skip empty strings. */
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (0 == sz)
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov return;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov if (NULL == *dest) {
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov *dest = mandoc_strndup(cp, sz);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov return;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov }
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov free(*dest);
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov *dest = cp;
260e9a87725c090ba5835b1f9f0b62fa2f96036fYuri Pankov}