/* $Id$ */
/*
* Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libman.h"
#define FL_NLINE (1 << 0)
#define FL_TLINE (1 << 1)
static int man_args(struct man *, int,
int *, char *, char **);
static int man_flags[MAN_MAX] = {
0, /* __ */
0, /* TH */
0, /* SH */
0, /* SS */
FL_TLINE, /* TP */
0, /* LP */
0, /* PP */
0, /* P */
0, /* IP */
0, /* HP */
FL_NLINE, /* SM */
FL_NLINE, /* SB */
FL_NLINE, /* BI */
FL_NLINE, /* IB */
FL_NLINE, /* BR */
FL_NLINE, /* RB */
FL_NLINE, /* R */
FL_NLINE, /* B */
FL_NLINE, /* I */
FL_NLINE, /* IR */
FL_NLINE, /* RI */
};
int
man_macro(struct man *man, int tok, int line,
int ppos, int *pos, char *buf)
{
int w, la;
char *p;
struct man_node *n;
if ( ! man_elem_alloc(man, line, ppos, tok))
return(0);
n = man->last;
man->next = MAN_NEXT_CHILD;
for (;;) {
la = *pos;
w = man_args(man, line, pos, buf, &p);
if (-1 == w)
return(0);
if (0 == w)
break;
if ( ! man_word_alloc(man, line, la, p))
return(0);
man->next = MAN_NEXT_SIBLING;
}
if (n == man->last && (FL_NLINE & man_flags[tok])) {
if (MAN_NLINE & man->flags)
return(man_verr(man, line, ppos,
"next-line scope already open"));
man->flags |= MAN_NLINE;
return(1);
}
if (FL_TLINE & man_flags[tok]) {
if (MAN_NLINE & man->flags)
return(man_verr(man, line, ppos,
"next-line scope already open"));
man->flags |= MAN_NLINE;
return(1);
}
/*
* Note that when TH is pruned, we'll be back at the root, so
* make sure that we don't clobber as its sibling.
*/
for ( ; man->last; man->last = man->last->parent) {
if (man->last == n)
break;
if (man->last->type == MAN_ROOT)
break;
if ( ! man_valid_post(man))
return(0);
if ( ! man_action_post(man))
return(0);
}
assert(man->last);
/*
* Same here regarding whether we're back at the root.
*/
if (man->last->type != MAN_ROOT && ! man_valid_post(man))
return(0);
if (man->last->type != MAN_ROOT && ! man_action_post(man))
return(0);
if (man->last->type != MAN_ROOT)
man->next = MAN_NEXT_SIBLING;
return(1);
}
int
man_macroend(struct man *m)
{
for ( ; m->last && m->last != m->first;
m->last = m->last->parent) {
if ( ! man_valid_post(m))
return(0);
if ( ! man_action_post(m))
return(0);
}
assert(m->last == m->first);
if ( ! man_valid_post(m))
return(0);
if ( ! man_action_post(m))
return(0);
return(1);
}
/* ARGSUSED */
static int
man_args(struct man *m, int line,
int *pos, char *buf, char **v)
{
if (0 == buf[*pos])
return(0);
/* First parse non-quoted strings. */
if ('\"' != buf[*pos]) {
*v = &buf[*pos];
while (buf[*pos]) {
if (' ' == buf[*pos])
if ('\\' != buf[*pos - 1])
break;
(*pos)++;
}
if (0 == buf[*pos])
return(1);
buf[(*pos)++] = 0;
if (0 == buf[*pos])
return(1);
while (buf[*pos] && ' ' == buf[*pos])
(*pos)++;
if (buf[*pos])
return(1);
if ( ! man_vwarn(m, line, *pos, "trailing spaces"))
return(-1);
return(1);
}
/*
* If we're a quoted string (and quoted strings are allowed),
* then parse ahead to the next quote. If none's found, it's an
* error. After, parse to the next word.
*/
*v = &buf[++(*pos)];
while (buf[*pos] && '\"' != buf[*pos])
(*pos)++;
if (0 == buf[*pos]) {
if ( ! man_vwarn(m, line, *pos, "unterminated quote"))
return(-1);
return(1);
}
buf[(*pos)++] = 0;
if (0 == buf[*pos])
return(1);
while (buf[*pos] && ' ' == buf[*pos])
(*pos)++;
if (buf[*pos])
return(1);
if ( ! man_vwarn(m, line, *pos, "trailing spaces"))
return(-1);
return(1);
}