#include <sys/types.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "mandoc_aux.h"
#include "mandoc.h"
#include "roff.h"
#include "mdoc.h"
#include "libmandoc.h"
#include "roff_int.h"
#include "libmdoc.h"
#define MULTI_STEP 5
#define DELIMSZ 6
enum argsflag {
ARGSFL_NONE = 0,
ARGSFL_DELIM,
ARGSFL_TABSEP
};
enum argvflag {
ARGV_NONE,
ARGV_SINGLE,
ARGV_MULTI
};
struct mdocarg {
enum argsflag flags;
const enum mdocargt *argvs;
};
static void argn_free(struct mdoc_arg *, int);
static enum margserr args(struct roff_man *, int, int *,
char *, enum argsflag, char **);
static int args_checkpunct(const char *, int);
static void argv_multi(struct roff_man *, int,
struct mdoc_argv *, int *, char *);
static void argv_single(struct roff_man *, int,
struct mdoc_argv *, int *, char *);
static const enum argvflag argvflags[MDOC_ARG_MAX] = {
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_SINGLE,
ARGV_SINGLE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_MULTI,
ARGV_SINGLE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE,
ARGV_NONE
};
static const enum mdocargt args_Ex[] = {
MDOC_Std,
MDOC_ARG_MAX
};
static const enum mdocargt args_An[] = {
MDOC_Split,
MDOC_Nosplit,
MDOC_ARG_MAX
};
static const enum mdocargt args_Bd[] = {
MDOC_Ragged,
MDOC_Unfilled,
MDOC_Filled,
MDOC_Literal,
MDOC_File,
MDOC_Offset,
MDOC_Compact,
MDOC_Centred,
MDOC_ARG_MAX
};
static const enum mdocargt args_Bf[] = {
MDOC_Emphasis,
MDOC_Literal,
MDOC_Symbolic,
MDOC_ARG_MAX
};
static const enum mdocargt args_Bk[] = {
MDOC_Words,
MDOC_ARG_MAX
};
static const enum mdocargt args_Bl[] = {
MDOC_Bullet,
MDOC_Dash,
MDOC_Hyphen,
MDOC_Item,
MDOC_Enum,
MDOC_Tag,
MDOC_Diag,
MDOC_Hang,
MDOC_Ohang,
MDOC_Inset,
MDOC_Column,
MDOC_Width,
MDOC_Offset,
MDOC_Compact,
MDOC_Nested,
MDOC_ARG_MAX
};
static const struct mdocarg mdocargs[MDOC_MAX - MDOC_Dd] = {
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, args_Bd },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, args_Bl },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, args_An },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, args_Ex },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, args_Ex },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, args_Bf },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, args_Bk },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_DELIM, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
{ ARGSFL_NONE, NULL },
};
void
mdoc_argv(struct roff_man *mdoc, int line, enum roff_tok tok,
struct mdoc_arg **reta, int *pos, char *buf)
{
struct mdoc_argv tmpv;
struct mdoc_argv **retv;
const enum mdocargt *argtable;
char *argname;
int ipos, retc;
char savechar;
*reta = NULL;
assert(tok >= MDOC_Dd && tok < MDOC_MAX);
argtable = mdocargs[tok - MDOC_Dd].argvs;
if (argtable == NULL)
return;
ipos = *pos;
while (buf[ipos] == '-') {
for (argname = buf + ++ipos; buf[ipos] != '\0'; ipos++)
if (buf[ipos] == ' ' && buf[ipos - 1] != '\\')
break;
if ((savechar = buf[ipos]) != '\0')
buf[ipos++] = '\0';
while ((tmpv.arg = *argtable++) != MDOC_ARG_MAX)
if ( ! strcmp(argname, mdoc_argnames[tmpv.arg]))
break;
if (tmpv.arg == MDOC_ARG_MAX) {
if (savechar != '\0')
buf[ipos - 1] = savechar;
break;
}
while (buf[ipos] == ' ')
ipos++;
tmpv.line = line;
tmpv.pos = *pos;
tmpv.sz = 0;
tmpv.value = NULL;
switch (argvflags[tmpv.arg]) {
case ARGV_SINGLE:
argv_single(mdoc, line, &tmpv, &ipos, buf);
break;
case ARGV_MULTI:
argv_multi(mdoc, line, &tmpv, &ipos, buf);
break;
case ARGV_NONE:
break;
}
if (*reta == NULL)
*reta = mandoc_calloc(1, sizeof(**reta));
retc = ++(*reta)->argc;
retv = &(*reta)->argv;
*retv = mandoc_reallocarray(*retv, retc, sizeof(**retv));
memcpy(*retv + retc - 1, &tmpv, sizeof(**retv));
*pos = ipos;
argtable = mdocargs[tok - MDOC_Dd].argvs;
}
}
void
mdoc_argv_free(struct mdoc_arg *p)
{
int i;
if (NULL == p)
return;
if (p->refcnt) {
--(p->refcnt);
if (p->refcnt)
return;
}
assert(p->argc);
for (i = (int)p->argc - 1; i >= 0; i--)
argn_free(p, i);
free(p->argv);
free(p);
}
static void
argn_free(struct mdoc_arg *p, int iarg)
{
struct mdoc_argv *arg;
int j;
arg = &p->argv[iarg];
if (arg->sz && arg->value) {
for (j = (int)arg->sz - 1; j >= 0; j--)
free(arg->value[j]);
free(arg->value);
}
for (--p->argc; iarg < (int)p->argc; iarg++)
p->argv[iarg] = p->argv[iarg+1];
}
enum margserr
mdoc_args(struct roff_man *mdoc, int line, int *pos,
char *buf, enum roff_tok tok, char **v)
{
struct roff_node *n;
enum argsflag fl;
fl = tok == TOKEN_NONE ? ARGSFL_NONE : mdocargs[tok - MDOC_Dd].flags;
if (tok == MDOC_It) {
for (n = mdoc->last; n != NULL; n = n->parent) {
if (n->tok != MDOC_Bl)
continue;
if (n->norm->Bl.type == LIST_column)
fl = ARGSFL_TABSEP;
break;
}
}
return args(mdoc, line, pos, buf, fl, v);
}
static enum margserr
args(struct roff_man *mdoc, int line, int *pos,
char *buf, enum argsflag fl, char **v)
{
char *p;
char *v_local;
int pairs;
if (buf[*pos] == '\0') {
if (mdoc->flags & MDOC_PHRASELIT &&
! (mdoc->flags & MDOC_PHRASE)) {
mandoc_msg(MANDOCERR_ARG_QUOTE, line, *pos, NULL);
mdoc->flags &= ~MDOC_PHRASELIT;
}
mdoc->flags &= ~MDOC_PHRASEQL;
return ARGS_EOLN;
}
if (v == NULL)
v = &v_local;
*v = buf + *pos;
if (fl == ARGSFL_DELIM && args_checkpunct(buf, *pos))
return ARGS_PUNCT;
if (fl == ARGSFL_TABSEP) {
if ((p = strchr(*v, '\t')) != NULL) {
if (p > buf && p[-1] != ' ')
mdoc->flags |= MDOC_PHRASEQL;
if (p[1] != ' ')
mdoc->flags |= MDOC_PHRASEQN;
*pos += (int)(p - *v) + 1;
while (buf[*pos] == ' ' && buf[*pos + 1] == ' ')
(*pos)++;
if (buf[*pos] == '\0' || buf[*pos + 1] == '\0')
mdoc->flags |= MDOC_PHRASEQN;
} else {
p = strchr(*v, '\0');
if (p[-1] == ' ')
mandoc_msg(MANDOCERR_SPACE_EOL,
line, *pos, NULL);
*pos += (int)(p - *v);
}
while (p > *v && p[-1] == ' ' &&
(p - 1 == *v || p[-2] != '\\'))
p--;
*p = '\0';
return ARGS_PHRASE;
}
if (mdoc->flags & MDOC_PHRASELIT ||
(mdoc->flags & MDOC_PHRASE && buf[*pos] == '\"')) {
if ((mdoc->flags & MDOC_PHRASELIT) == 0) {
*v = &buf[++(*pos)];
mdoc->flags |= MDOC_PHRASELIT;
}
pairs = 0;
for ( ; buf[*pos]; (*pos)++) {
if (pairs)
buf[*pos - pairs] = buf[*pos];
if ('\"' != buf[*pos])
continue;
if ('\"' != buf[*pos + 1])
break;
pairs++;
(*pos)++;
}
if (pairs)
buf[*pos - pairs] = '\0';
if (buf[*pos] == '\0') {
if ( ! (mdoc->flags & MDOC_PHRASE))
mandoc_msg(MANDOCERR_ARG_QUOTE,
line, *pos, NULL);
return ARGS_WORD;
}
mdoc->flags &= ~MDOC_PHRASELIT;
buf[(*pos)++] = '\0';
if ('\0' == buf[*pos])
return ARGS_WORD;
while (' ' == buf[*pos])
(*pos)++;
if ('\0' == buf[*pos])
mandoc_msg(MANDOCERR_SPACE_EOL, line, *pos, NULL);
return ARGS_WORD;
}
p = &buf[*pos];
*v = roff_getarg(mdoc->roff, &p, line, pos);
if (v == &v_local)
free(*v);
if (*p == '\0' && mdoc->flags & MDOC_PHRASEQL) {
mdoc->flags &= ~MDOC_PHRASEQL;
mdoc->flags |= MDOC_PHRASEQF;
}
return ARGS_ALLOC;
}
static int
args_checkpunct(const char *buf, int i)
{
int j;
char dbuf[DELIMSZ];
enum mdelim d;
for (j = 0; buf[i] && ' ' != buf[i] && j < DELIMSZ; j++, i++)
dbuf[j] = buf[i];
if (DELIMSZ == j)
return 0;
dbuf[j] = '\0';
if (DELIM_CLOSE != mdoc_isdelim(dbuf))
return 0;
while (' ' == buf[i])
i++;
while (buf[i]) {
j = 0;
while (buf[i] && ' ' != buf[i] && j < DELIMSZ)
dbuf[j++] = buf[i++];
if (DELIMSZ == j)
return 0;
dbuf[j] = '\0';
d = mdoc_isdelim(dbuf);
if (DELIM_NONE == d || DELIM_OPEN == d)
return 0;
while (' ' == buf[i])
i++;
}
return '\0' == buf[i];
}
static void
argv_multi(struct roff_man *mdoc, int line,
struct mdoc_argv *v, int *pos, char *buf)
{
enum margserr ac;
char *p;
for (v->sz = 0; ; v->sz++) {
if (buf[*pos] == '-')
break;
ac = args(mdoc, line, pos, buf, ARGSFL_NONE, &p);
if (ac == ARGS_EOLN)
break;
if (v->sz % MULTI_STEP == 0)
v->value = mandoc_reallocarray(v->value,
v->sz + MULTI_STEP, sizeof(char *));
if (ac != ARGS_ALLOC)
p = mandoc_strdup(p);
v->value[(int)v->sz] = p;
}
}
static void
argv_single(struct roff_man *mdoc, int line,
struct mdoc_argv *v, int *pos, char *buf)
{
enum margserr ac;
char *p;
ac = args(mdoc, line, pos, buf, ARGSFL_NONE, &p);
if (ac == ARGS_EOLN)
return;
if (ac != ARGS_ALLOC)
p = mandoc_strdup(p);
v->sz = 1;
v->value = mandoc_malloc(sizeof(char *));
v->value[0] = p;
}