1294113Sbapt/*	$Id: mdoc_argv.c,v 1.107 2015/10/17 00:21:07 schwarze Exp $ */
2241675Suqs/*
3241675Suqs * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4294113Sbapt * Copyright (c) 2012, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
5241675Suqs *
6241675Suqs * Permission to use, copy, modify, and distribute this software for any
7241675Suqs * purpose with or without fee is hereby granted, provided that the above
8241675Suqs * copyright notice and this permission notice appear in all copies.
9241675Suqs *
10294113Sbapt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12294113Sbapt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17241675Suqs */
18241675Suqs#include "config.h"
19241675Suqs
20241675Suqs#include <sys/types.h>
21241675Suqs
22241675Suqs#include <assert.h>
23241675Suqs#include <stdlib.h>
24241675Suqs#include <stdio.h>
25241675Suqs#include <string.h>
26241675Suqs
27294113Sbapt#include "mandoc_aux.h"
28294113Sbapt#include "mandoc.h"
29294113Sbapt#include "roff.h"
30241675Suqs#include "mdoc.h"
31294113Sbapt#include "libmandoc.h"
32241675Suqs#include "libmdoc.h"
33241675Suqs
34241675Suqs#define	MULTI_STEP	 5 /* pre-allocate argument values */
35274880Sbapt#define	DELIMSZ		 6 /* max possible size of a delimiter */
36241675Suqs
37241675Suqsenum	argsflag {
38241675Suqs	ARGSFL_NONE = 0,
39241675Suqs	ARGSFL_DELIM, /* handle delimiters of [[::delim::][ ]+]+ */
40241675Suqs	ARGSFL_TABSEP /* handle tab/`Ta' separated phrases */
41241675Suqs};
42241675Suqs
43241675Suqsenum	argvflag {
44241675Suqs	ARGV_NONE, /* no args to flag (e.g., -split) */
45241675Suqs	ARGV_SINGLE, /* one arg to flag (e.g., -file xxx)  */
46261344Suqs	ARGV_MULTI /* multiple args (e.g., -column xxx yyy) */
47241675Suqs};
48241675Suqs
49241675Suqsstruct	mdocarg {
50241675Suqs	enum argsflag	 flags;
51241675Suqs	const enum mdocargt *argvs;
52241675Suqs};
53241675Suqs
54241675Suqsstatic	void		 argn_free(struct mdoc_arg *, int);
55294113Sbaptstatic	enum margserr	 args(struct roff_man *, int, int *,
56241675Suqs				char *, enum argsflag, char **);
57241675Suqsstatic	int		 args_checkpunct(const char *, int);
58294113Sbaptstatic	void		 argv_multi(struct roff_man *, int,
59241675Suqs				struct mdoc_argv *, int *, char *);
60294113Sbaptstatic	void		 argv_single(struct roff_man *, int,
61241675Suqs				struct mdoc_argv *, int *, char *);
62241675Suqs
63241675Suqsstatic	const enum argvflag argvflags[MDOC_ARG_MAX] = {
64241675Suqs	ARGV_NONE,	/* MDOC_Split */
65241675Suqs	ARGV_NONE,	/* MDOC_Nosplit */
66241675Suqs	ARGV_NONE,	/* MDOC_Ragged */
67241675Suqs	ARGV_NONE,	/* MDOC_Unfilled */
68241675Suqs	ARGV_NONE,	/* MDOC_Literal */
69241675Suqs	ARGV_SINGLE,	/* MDOC_File */
70261344Suqs	ARGV_SINGLE,	/* MDOC_Offset */
71241675Suqs	ARGV_NONE,	/* MDOC_Bullet */
72241675Suqs	ARGV_NONE,	/* MDOC_Dash */
73241675Suqs	ARGV_NONE,	/* MDOC_Hyphen */
74241675Suqs	ARGV_NONE,	/* MDOC_Item */
75241675Suqs	ARGV_NONE,	/* MDOC_Enum */
76241675Suqs	ARGV_NONE,	/* MDOC_Tag */
77241675Suqs	ARGV_NONE,	/* MDOC_Diag */
78241675Suqs	ARGV_NONE,	/* MDOC_Hang */
79241675Suqs	ARGV_NONE,	/* MDOC_Ohang */
80241675Suqs	ARGV_NONE,	/* MDOC_Inset */
81241675Suqs	ARGV_MULTI,	/* MDOC_Column */
82261344Suqs	ARGV_SINGLE,	/* MDOC_Width */
83241675Suqs	ARGV_NONE,	/* MDOC_Compact */
84241675Suqs	ARGV_NONE,	/* MDOC_Std */
85241675Suqs	ARGV_NONE,	/* MDOC_Filled */
86241675Suqs	ARGV_NONE,	/* MDOC_Words */
87241675Suqs	ARGV_NONE,	/* MDOC_Emphasis */
88241675Suqs	ARGV_NONE,	/* MDOC_Symbolic */
89241675Suqs	ARGV_NONE	/* MDOC_Symbolic */
90241675Suqs};
91241675Suqs
92241675Suqsstatic	const enum mdocargt args_Ex[] = {
93241675Suqs	MDOC_Std,
94241675Suqs	MDOC_ARG_MAX
95241675Suqs};
96241675Suqs
97241675Suqsstatic	const enum mdocargt args_An[] = {
98241675Suqs	MDOC_Split,
99241675Suqs	MDOC_Nosplit,
100241675Suqs	MDOC_ARG_MAX
101241675Suqs};
102241675Suqs
103241675Suqsstatic	const enum mdocargt args_Bd[] = {
104241675Suqs	MDOC_Ragged,
105241675Suqs	MDOC_Unfilled,
106241675Suqs	MDOC_Filled,
107241675Suqs	MDOC_Literal,
108241675Suqs	MDOC_File,
109241675Suqs	MDOC_Offset,
110241675Suqs	MDOC_Compact,
111241675Suqs	MDOC_Centred,
112241675Suqs	MDOC_ARG_MAX
113241675Suqs};
114241675Suqs
115241675Suqsstatic	const enum mdocargt args_Bf[] = {
116241675Suqs	MDOC_Emphasis,
117241675Suqs	MDOC_Literal,
118241675Suqs	MDOC_Symbolic,
119241675Suqs	MDOC_ARG_MAX
120241675Suqs};
121241675Suqs
122241675Suqsstatic	const enum mdocargt args_Bk[] = {
123241675Suqs	MDOC_Words,
124241675Suqs	MDOC_ARG_MAX
125241675Suqs};
126241675Suqs
127241675Suqsstatic	const enum mdocargt args_Bl[] = {
128241675Suqs	MDOC_Bullet,
129241675Suqs	MDOC_Dash,
130241675Suqs	MDOC_Hyphen,
131241675Suqs	MDOC_Item,
132241675Suqs	MDOC_Enum,
133241675Suqs	MDOC_Tag,
134241675Suqs	MDOC_Diag,
135241675Suqs	MDOC_Hang,
136241675Suqs	MDOC_Ohang,
137241675Suqs	MDOC_Inset,
138241675Suqs	MDOC_Column,
139241675Suqs	MDOC_Width,
140241675Suqs	MDOC_Offset,
141241675Suqs	MDOC_Compact,
142241675Suqs	MDOC_Nested,
143241675Suqs	MDOC_ARG_MAX
144241675Suqs};
145241675Suqs
146241675Suqsstatic	const struct mdocarg mdocargs[MDOC_MAX] = {
147261344Suqs	{ ARGSFL_DELIM, NULL }, /* Ap */
148241675Suqs	{ ARGSFL_NONE, NULL }, /* Dd */
149241675Suqs	{ ARGSFL_NONE, NULL }, /* Dt */
150241675Suqs	{ ARGSFL_NONE, NULL }, /* Os */
151241675Suqs	{ ARGSFL_NONE, NULL }, /* Sh */
152274880Sbapt	{ ARGSFL_NONE, NULL }, /* Ss */
153274880Sbapt	{ ARGSFL_NONE, NULL }, /* Pp */
154241675Suqs	{ ARGSFL_DELIM, NULL }, /* D1 */
155241675Suqs	{ ARGSFL_DELIM, NULL }, /* Dl */
156241675Suqs	{ ARGSFL_NONE, args_Bd }, /* Bd */
157241675Suqs	{ ARGSFL_NONE, NULL }, /* Ed */
158241675Suqs	{ ARGSFL_NONE, args_Bl }, /* Bl */
159241675Suqs	{ ARGSFL_NONE, NULL }, /* El */
160241675Suqs	{ ARGSFL_NONE, NULL }, /* It */
161274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Ad */
162241675Suqs	{ ARGSFL_DELIM, args_An }, /* An */
163241675Suqs	{ ARGSFL_DELIM, NULL }, /* Ar */
164261344Suqs	{ ARGSFL_DELIM, NULL }, /* Cd */
165241675Suqs	{ ARGSFL_DELIM, NULL }, /* Cm */
166274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Dv */
167274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Er */
168274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Ev */
169241675Suqs	{ ARGSFL_NONE, args_Ex }, /* Ex */
170274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Fa */
171274880Sbapt	{ ARGSFL_NONE, NULL }, /* Fd */
172241675Suqs	{ ARGSFL_DELIM, NULL }, /* Fl */
173274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Fn */
174274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Ft */
175274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Ic */
176274880Sbapt	{ ARGSFL_DELIM, NULL }, /* In */
177241675Suqs	{ ARGSFL_DELIM, NULL }, /* Li */
178274880Sbapt	{ ARGSFL_NONE, NULL }, /* Nd */
179274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Nm */
180241675Suqs	{ ARGSFL_DELIM, NULL }, /* Op */
181274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Ot */
182241675Suqs	{ ARGSFL_DELIM, NULL }, /* Pa */
183241675Suqs	{ ARGSFL_NONE, args_Ex }, /* Rv */
184274880Sbapt	{ ARGSFL_DELIM, NULL }, /* St */
185241675Suqs	{ ARGSFL_DELIM, NULL }, /* Va */
186274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Vt */
187241675Suqs	{ ARGSFL_DELIM, NULL }, /* Xr */
188241675Suqs	{ ARGSFL_NONE, NULL }, /* %A */
189241675Suqs	{ ARGSFL_NONE, NULL }, /* %B */
190241675Suqs	{ ARGSFL_NONE, NULL }, /* %D */
191241675Suqs	{ ARGSFL_NONE, NULL }, /* %I */
192241675Suqs	{ ARGSFL_NONE, NULL }, /* %J */
193241675Suqs	{ ARGSFL_NONE, NULL }, /* %N */
194241675Suqs	{ ARGSFL_NONE, NULL }, /* %O */
195241675Suqs	{ ARGSFL_NONE, NULL }, /* %P */
196241675Suqs	{ ARGSFL_NONE, NULL }, /* %R */
197241675Suqs	{ ARGSFL_NONE, NULL }, /* %T */
198241675Suqs	{ ARGSFL_NONE, NULL }, /* %V */
199241675Suqs	{ ARGSFL_DELIM, NULL }, /* Ac */
200241675Suqs	{ ARGSFL_NONE, NULL }, /* Ao */
201241675Suqs	{ ARGSFL_DELIM, NULL }, /* Aq */
202241675Suqs	{ ARGSFL_DELIM, NULL }, /* At */
203241675Suqs	{ ARGSFL_DELIM, NULL }, /* Bc */
204274880Sbapt	{ ARGSFL_NONE, args_Bf }, /* Bf */
205241675Suqs	{ ARGSFL_NONE, NULL }, /* Bo */
206241675Suqs	{ ARGSFL_DELIM, NULL }, /* Bq */
207241675Suqs	{ ARGSFL_DELIM, NULL }, /* Bsx */
208241675Suqs	{ ARGSFL_DELIM, NULL }, /* Bx */
209241675Suqs	{ ARGSFL_NONE, NULL }, /* Db */
210241675Suqs	{ ARGSFL_DELIM, NULL }, /* Dc */
211241675Suqs	{ ARGSFL_NONE, NULL }, /* Do */
212241675Suqs	{ ARGSFL_DELIM, NULL }, /* Dq */
213241675Suqs	{ ARGSFL_DELIM, NULL }, /* Ec */
214241675Suqs	{ ARGSFL_NONE, NULL }, /* Ef */
215274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Em */
216241675Suqs	{ ARGSFL_NONE, NULL }, /* Eo */
217241675Suqs	{ ARGSFL_DELIM, NULL }, /* Fx */
218241675Suqs	{ ARGSFL_DELIM, NULL }, /* Ms */
219241675Suqs	{ ARGSFL_DELIM, NULL }, /* No */
220241675Suqs	{ ARGSFL_DELIM, NULL }, /* Ns */
221241675Suqs	{ ARGSFL_DELIM, NULL }, /* Nx */
222241675Suqs	{ ARGSFL_DELIM, NULL }, /* Ox */
223241675Suqs	{ ARGSFL_DELIM, NULL }, /* Pc */
224241675Suqs	{ ARGSFL_DELIM, NULL }, /* Pf */
225241675Suqs	{ ARGSFL_NONE, NULL }, /* Po */
226241675Suqs	{ ARGSFL_DELIM, NULL }, /* Pq */
227241675Suqs	{ ARGSFL_DELIM, NULL }, /* Qc */
228241675Suqs	{ ARGSFL_DELIM, NULL }, /* Ql */
229241675Suqs	{ ARGSFL_NONE, NULL }, /* Qo */
230241675Suqs	{ ARGSFL_DELIM, NULL }, /* Qq */
231241675Suqs	{ ARGSFL_NONE, NULL }, /* Re */
232241675Suqs	{ ARGSFL_NONE, NULL }, /* Rs */
233241675Suqs	{ ARGSFL_DELIM, NULL }, /* Sc */
234241675Suqs	{ ARGSFL_NONE, NULL }, /* So */
235241675Suqs	{ ARGSFL_DELIM, NULL }, /* Sq */
236241675Suqs	{ ARGSFL_NONE, NULL }, /* Sm */
237241675Suqs	{ ARGSFL_DELIM, NULL }, /* Sx */
238241675Suqs	{ ARGSFL_DELIM, NULL }, /* Sy */
239241675Suqs	{ ARGSFL_DELIM, NULL }, /* Tn */
240241675Suqs	{ ARGSFL_DELIM, NULL }, /* Ux */
241241675Suqs	{ ARGSFL_DELIM, NULL }, /* Xc */
242241675Suqs	{ ARGSFL_NONE, NULL }, /* Xo */
243274880Sbapt	{ ARGSFL_NONE, NULL }, /* Fo */
244274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Fc */
245241675Suqs	{ ARGSFL_NONE, NULL }, /* Oo */
246241675Suqs	{ ARGSFL_DELIM, NULL }, /* Oc */
247241675Suqs	{ ARGSFL_NONE, args_Bk }, /* Bk */
248241675Suqs	{ ARGSFL_NONE, NULL }, /* Ek */
249241675Suqs	{ ARGSFL_NONE, NULL }, /* Bt */
250241675Suqs	{ ARGSFL_NONE, NULL }, /* Hf */
251274880Sbapt	{ ARGSFL_DELIM, NULL }, /* Fr */
252241675Suqs	{ ARGSFL_NONE, NULL }, /* Ud */
253261344Suqs	{ ARGSFL_DELIM, NULL }, /* Lb */
254241675Suqs	{ ARGSFL_NONE, NULL }, /* Lp */
255241675Suqs	{ ARGSFL_DELIM, NULL }, /* Lk */
256241675Suqs	{ ARGSFL_DELIM, NULL }, /* Mt */
257241675Suqs	{ ARGSFL_DELIM, NULL }, /* Brq */
258241675Suqs	{ ARGSFL_NONE, NULL }, /* Bro */
259241675Suqs	{ ARGSFL_DELIM, NULL }, /* Brc */
260241675Suqs	{ ARGSFL_NONE, NULL }, /* %C */
261241675Suqs	{ ARGSFL_NONE, NULL }, /* Es */
262274880Sbapt	{ ARGSFL_DELIM, NULL }, /* En */
263261344Suqs	{ ARGSFL_DELIM, NULL }, /* Dx */
264241675Suqs	{ ARGSFL_NONE, NULL }, /* %Q */
265241675Suqs	{ ARGSFL_NONE, NULL }, /* br */
266241675Suqs	{ ARGSFL_NONE, NULL }, /* sp */
267241675Suqs	{ ARGSFL_NONE, NULL }, /* %U */
268241675Suqs	{ ARGSFL_NONE, NULL }, /* Ta */
269274880Sbapt	{ ARGSFL_NONE, NULL }, /* ll */
270241675Suqs};
271241675Suqs
272241675Suqs
273241675Suqs/*
274275432Sbapt * Parse flags and their arguments from the input line.
275275432Sbapt * These come in the form -flag [argument ...].
276275432Sbapt * Some flags take no argument, some one, some multiple.
277241675Suqs */
278275432Sbaptvoid
279294113Sbaptmdoc_argv(struct roff_man *mdoc, int line, int tok,
280275432Sbapt	struct mdoc_arg **reta, int *pos, char *buf)
281241675Suqs{
282275432Sbapt	struct mdoc_argv	  tmpv;
283275432Sbapt	struct mdoc_argv	**retv;
284275432Sbapt	const enum mdocargt	 *argtable;
285275432Sbapt	char			 *argname;
286275432Sbapt	int			  ipos, retc;
287275432Sbapt	char			  savechar;
288241675Suqs
289275432Sbapt	*reta = NULL;
290241675Suqs
291275432Sbapt	/* Which flags does this macro support? */
292241675Suqs
293275432Sbapt	argtable = mdocargs[tok].argvs;
294275432Sbapt	if (argtable == NULL)
295275432Sbapt		return;
296241675Suqs
297275432Sbapt	/* Loop over the flags on the input line. */
298241675Suqs
299275432Sbapt	ipos = *pos;
300275432Sbapt	while (buf[ipos] == '-') {
301241675Suqs
302275432Sbapt		/* Seek to the first unescaped space. */
303241675Suqs
304275432Sbapt		for (argname = buf + ++ipos; buf[ipos] != '\0'; ipos++)
305275432Sbapt			if (buf[ipos] == ' ' && buf[ipos - 1] != '\\')
306275432Sbapt				break;
307241675Suqs
308275432Sbapt		/*
309275432Sbapt		 * We want to nil-terminate the word to look it up.
310275432Sbapt		 * But we may not have a flag, in which case we need
311275432Sbapt		 * to restore the line as-is.  So keep around the
312275432Sbapt		 * stray byte, which we'll reset upon exiting.
313275432Sbapt		 */
314241675Suqs
315275432Sbapt		if ((savechar = buf[ipos]) != '\0')
316275432Sbapt			buf[ipos++] = '\0';
317241675Suqs
318275432Sbapt		/*
319275432Sbapt		 * Now look up the word as a flag.  Use temporary
320275432Sbapt		 * storage that we'll copy into the node's flags.
321275432Sbapt		 */
322241675Suqs
323275432Sbapt		while ((tmpv.arg = *argtable++) != MDOC_ARG_MAX)
324275432Sbapt			if ( ! strcmp(argname, mdoc_argnames[tmpv.arg]))
325275432Sbapt				break;
326275432Sbapt
327275432Sbapt		/* If it isn't a flag, restore the saved byte. */
328275432Sbapt
329275432Sbapt		if (tmpv.arg == MDOC_ARG_MAX) {
330275432Sbapt			if (savechar != '\0')
331275432Sbapt				buf[ipos - 1] = savechar;
332241675Suqs			break;
333275432Sbapt		}
334241675Suqs
335275432Sbapt		/* Read to the next word (the first argument). */
336241675Suqs
337275432Sbapt		while (buf[ipos] == ' ')
338275432Sbapt			ipos++;
339241675Suqs
340275432Sbapt		/* Parse the arguments of the flag. */
341241675Suqs
342275432Sbapt		tmpv.line  = line;
343279527Sbapt		tmpv.pos   = *pos;
344275432Sbapt		tmpv.sz    = 0;
345275432Sbapt		tmpv.value = NULL;
346241675Suqs
347275432Sbapt		switch (argvflags[tmpv.arg]) {
348275432Sbapt		case ARGV_SINGLE:
349275432Sbapt			argv_single(mdoc, line, &tmpv, &ipos, buf);
350275432Sbapt			break;
351275432Sbapt		case ARGV_MULTI:
352275432Sbapt			argv_multi(mdoc, line, &tmpv, &ipos, buf);
353275432Sbapt			break;
354275432Sbapt		case ARGV_NONE:
355275432Sbapt			break;
356275432Sbapt		}
357241675Suqs
358275432Sbapt		/* Append to the return values. */
359241675Suqs
360275432Sbapt		if (*reta == NULL)
361275432Sbapt			*reta = mandoc_calloc(1, sizeof(**reta));
362241675Suqs
363275432Sbapt		retc = ++(*reta)->argc;
364275432Sbapt		retv = &(*reta)->argv;
365275432Sbapt		*retv = mandoc_reallocarray(*retv, retc, sizeof(**retv));
366275432Sbapt		memcpy(*retv + retc - 1, &tmpv, sizeof(**retv));
367275432Sbapt
368275432Sbapt		/* Prepare for parsing the next flag. */
369275432Sbapt
370275432Sbapt		*pos = ipos;
371275432Sbapt		argtable = mdocargs[tok].argvs;
372275432Sbapt	}
373241675Suqs}
374241675Suqs
375241675Suqsvoid
376241675Suqsmdoc_argv_free(struct mdoc_arg *p)
377241675Suqs{
378241675Suqs	int		 i;
379241675Suqs
380241675Suqs	if (NULL == p)
381241675Suqs		return;
382241675Suqs
383241675Suqs	if (p->refcnt) {
384241675Suqs		--(p->refcnt);
385241675Suqs		if (p->refcnt)
386241675Suqs			return;
387241675Suqs	}
388241675Suqs	assert(p->argc);
389241675Suqs
390241675Suqs	for (i = (int)p->argc - 1; i >= 0; i--)
391241675Suqs		argn_free(p, i);
392241675Suqs
393241675Suqs	free(p->argv);
394241675Suqs	free(p);
395241675Suqs}
396241675Suqs
397241675Suqsstatic void
398241675Suqsargn_free(struct mdoc_arg *p, int iarg)
399241675Suqs{
400241675Suqs	struct mdoc_argv *arg;
401241675Suqs	int		  j;
402241675Suqs
403241675Suqs	arg = &p->argv[iarg];
404241675Suqs
405241675Suqs	if (arg->sz && arg->value) {
406274880Sbapt		for (j = (int)arg->sz - 1; j >= 0; j--)
407241675Suqs			free(arg->value[j]);
408241675Suqs		free(arg->value);
409241675Suqs	}
410241675Suqs
411241675Suqs	for (--p->argc; iarg < (int)p->argc; iarg++)
412241675Suqs		p->argv[iarg] = p->argv[iarg+1];
413241675Suqs}
414241675Suqs
415241675Suqsenum margserr
416294113Sbaptmdoc_args(struct roff_man *mdoc, int line, int *pos,
417294113Sbapt	char *buf, int tok, char **v)
418241675Suqs{
419294113Sbapt	struct roff_node *n;
420275432Sbapt	char		 *v_local;
421241675Suqs	enum argsflag	  fl;
422241675Suqs
423275432Sbapt	if (v == NULL)
424275432Sbapt		v = &v_local;
425294113Sbapt	fl = tok == TOKEN_NONE ? ARGSFL_NONE : mdocargs[tok].flags;
426275432Sbapt	if (tok != MDOC_It)
427294113Sbapt		return args(mdoc, line, pos, buf, fl, v);
428241675Suqs
429241675Suqs	/*
430241675Suqs	 * We know that we're in an `It', so it's reasonable to expect
431241675Suqs	 * us to be sitting in a `Bl'.  Someday this may not be the case
432241675Suqs	 * (if we allow random `It's sitting out there), so provide a
433241675Suqs	 * safe fall-back into the default behaviour.
434241675Suqs	 */
435241675Suqs
436261344Suqs	for (n = mdoc->last; n; n = n->parent)
437241675Suqs		if (MDOC_Bl == n->tok)
438241675Suqs			if (LIST_column == n->norm->Bl.type) {
439241675Suqs				fl = ARGSFL_TABSEP;
440241675Suqs				break;
441241675Suqs			}
442241675Suqs
443294113Sbapt	return args(mdoc, line, pos, buf, fl, v);
444241675Suqs}
445241675Suqs
446241675Suqsstatic enum margserr
447294113Sbaptargs(struct roff_man *mdoc, int line, int *pos,
448241675Suqs		char *buf, enum argsflag fl, char **v)
449241675Suqs{
450294113Sbapt	char		*p;
451261344Suqs	int		 pairs;
452241675Suqs
453294113Sbapt	if (buf[*pos] == '\0') {
454294113Sbapt		if (mdoc->flags & MDOC_PHRASELIT &&
455294113Sbapt		    ! (mdoc->flags & MDOC_PHRASE)) {
456274880Sbapt			mandoc_msg(MANDOCERR_ARG_QUOTE,
457274880Sbapt			    mdoc->parse, line, *pos, NULL);
458294113Sbapt			mdoc->flags &= ~MDOC_PHRASELIT;
459294113Sbapt		}
460294113Sbapt		return ARGS_EOLN;
461241675Suqs	}
462241675Suqs
463294113Sbapt	*v = buf + *pos;
464241675Suqs
465294113Sbapt	if (fl == ARGSFL_DELIM && args_checkpunct(buf, *pos))
466294113Sbapt		return ARGS_PUNCT;
467241675Suqs
468241675Suqs	/*
469294113Sbapt	 * Tabs in `It' lines in `Bl -column' can't be escaped.
470294113Sbapt	 * Phrases are reparsed for `Ta' and other macros later.
471241675Suqs	 */
472241675Suqs
473294113Sbapt	if (fl == ARGSFL_TABSEP) {
474294113Sbapt		if ((p = strchr(*v, '\t')) != NULL) {
475241675Suqs
476294113Sbapt			/*
477294113Sbapt			 * Words right before and right after
478294113Sbapt			 * tab characters are not parsed,
479294113Sbapt			 * unless there is a blank in between.
480294113Sbapt			 */
481241675Suqs
482294113Sbapt			if (p[-1] != ' ')
483294113Sbapt				mdoc->flags |= MDOC_PHRASEQL;
484294113Sbapt			if (p[1] != ' ')
485294113Sbapt				mdoc->flags |= MDOC_PHRASEQN;
486241675Suqs
487294113Sbapt			/*
488294113Sbapt			 * One or more blanks after a tab cause
489294113Sbapt			 * one leading blank in the next column.
490294113Sbapt			 * So skip all but one of them.
491294113Sbapt			 */
492241675Suqs
493294113Sbapt			*pos += (int)(p - *v) + 1;
494294113Sbapt			while (buf[*pos] == ' ' && buf[*pos + 1] == ' ')
495294113Sbapt				(*pos)++;
496241675Suqs
497294113Sbapt			/*
498294113Sbapt			 * A tab at the end of an input line
499294113Sbapt			 * switches to the next column.
500294113Sbapt			 */
501241675Suqs
502294113Sbapt			if (buf[*pos] == '\0' || buf[*pos + 1] == '\0')
503294113Sbapt				mdoc->flags |= MDOC_PHRASEQN;
504294113Sbapt		} else {
505294113Sbapt			p = strchr(*v, '\0');
506294113Sbapt			if (p[-1] == ' ')
507294113Sbapt				mandoc_msg(MANDOCERR_SPACE_EOL,
508294113Sbapt				    mdoc->parse, line, *pos, NULL);
509294113Sbapt			*pos += (int)(p - *v);
510241675Suqs		}
511241675Suqs
512294113Sbapt		/* Skip any trailing blank characters. */
513294113Sbapt		while (p > *v && p[-1] == ' ' &&
514294113Sbapt		    (p - 1 == *v || p[-2] != '\\'))
515294113Sbapt			p--;
516294113Sbapt		*p = '\0';
517241675Suqs
518294113Sbapt		return ARGS_PHRASE;
519261344Suqs	}
520241675Suqs
521261344Suqs	/*
522241675Suqs	 * Process a quoted literal.  A quote begins with a double-quote
523241675Suqs	 * and ends with a double-quote NOT preceded by a double-quote.
524261344Suqs	 * NUL-terminate the literal in place.
525261344Suqs	 * Collapse pairs of quotes inside quoted literals.
526241675Suqs	 * Whitespace is NOT involved in literal termination.
527241675Suqs	 */
528241675Suqs
529294113Sbapt	if (mdoc->flags & MDOC_PHRASELIT || buf[*pos] == '\"') {
530294113Sbapt		if ( ! (mdoc->flags & MDOC_PHRASELIT))
531241675Suqs			*v = &buf[++(*pos)];
532241675Suqs
533294113Sbapt		if (mdoc->flags & MDOC_PHRASE)
534261344Suqs			mdoc->flags |= MDOC_PHRASELIT;
535241675Suqs
536261344Suqs		pairs = 0;
537241675Suqs		for ( ; buf[*pos]; (*pos)++) {
538261344Suqs			/* Move following text left after quoted quotes. */
539261344Suqs			if (pairs)
540261344Suqs				buf[*pos - pairs] = buf[*pos];
541241675Suqs			if ('\"' != buf[*pos])
542241675Suqs				continue;
543261344Suqs			/* Unquoted quotes end quoted args. */
544241675Suqs			if ('\"' != buf[*pos + 1])
545241675Suqs				break;
546261344Suqs			/* Quoted quotes collapse. */
547261344Suqs			pairs++;
548241675Suqs			(*pos)++;
549241675Suqs		}
550261344Suqs		if (pairs)
551261344Suqs			buf[*pos - pairs] = '\0';
552241675Suqs
553294113Sbapt		if (buf[*pos] == '\0') {
554294113Sbapt			if ( ! (mdoc->flags & MDOC_PHRASE))
555294113Sbapt				mandoc_msg(MANDOCERR_ARG_QUOTE,
556294113Sbapt				    mdoc->parse, line, *pos, NULL);
557294113Sbapt			return ARGS_QWORD;
558241675Suqs		}
559241675Suqs
560261344Suqs		mdoc->flags &= ~MDOC_PHRASELIT;
561241675Suqs		buf[(*pos)++] = '\0';
562241675Suqs
563241675Suqs		if ('\0' == buf[*pos])
564294113Sbapt			return ARGS_QWORD;
565241675Suqs
566241675Suqs		while (' ' == buf[*pos])
567241675Suqs			(*pos)++;
568241675Suqs
569241675Suqs		if ('\0' == buf[*pos])
570274880Sbapt			mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
571274880Sbapt			    line, *pos, NULL);
572241675Suqs
573294113Sbapt		return ARGS_QWORD;
574241675Suqs	}
575241675Suqs
576241675Suqs	p = &buf[*pos];
577261344Suqs	*v = mandoc_getarg(mdoc->parse, &p, line, pos);
578241675Suqs
579294113Sbapt	/*
580294113Sbapt	 * After parsing the last word in this phrase,
581294113Sbapt	 * tell lookup() whether or not to interpret it.
582294113Sbapt	 */
583294113Sbapt
584294113Sbapt	if (*p == '\0' && mdoc->flags & MDOC_PHRASEQL) {
585294113Sbapt		mdoc->flags &= ~MDOC_PHRASEQL;
586294113Sbapt		mdoc->flags |= MDOC_PHRASEQF;
587294113Sbapt	}
588294113Sbapt	return ARGS_WORD;
589241675Suqs}
590241675Suqs
591274880Sbapt/*
592241675Suqs * Check if the string consists only of space-separated closing
593241675Suqs * delimiters.  This is a bit of a dance: the first must be a close
594241675Suqs * delimiter, but it may be followed by middle delimiters.  Arbitrary
595241675Suqs * whitespace may separate these tokens.
596241675Suqs */
597241675Suqsstatic int
598241675Suqsargs_checkpunct(const char *buf, int i)
599241675Suqs{
600241675Suqs	int		 j;
601241675Suqs	char		 dbuf[DELIMSZ];
602241675Suqs	enum mdelim	 d;
603241675Suqs
604241675Suqs	/* First token must be a close-delimiter. */
605241675Suqs
606241675Suqs	for (j = 0; buf[i] && ' ' != buf[i] && j < DELIMSZ; j++, i++)
607241675Suqs		dbuf[j] = buf[i];
608241675Suqs
609241675Suqs	if (DELIMSZ == j)
610294113Sbapt		return 0;
611241675Suqs
612241675Suqs	dbuf[j] = '\0';
613241675Suqs	if (DELIM_CLOSE != mdoc_isdelim(dbuf))
614294113Sbapt		return 0;
615241675Suqs
616241675Suqs	while (' ' == buf[i])
617241675Suqs		i++;
618241675Suqs
619241675Suqs	/* Remaining must NOT be open/none. */
620274880Sbapt
621241675Suqs	while (buf[i]) {
622241675Suqs		j = 0;
623241675Suqs		while (buf[i] && ' ' != buf[i] && j < DELIMSZ)
624241675Suqs			dbuf[j++] = buf[i++];
625241675Suqs
626241675Suqs		if (DELIMSZ == j)
627294113Sbapt			return 0;
628241675Suqs
629241675Suqs		dbuf[j] = '\0';
630241675Suqs		d = mdoc_isdelim(dbuf);
631241675Suqs		if (DELIM_NONE == d || DELIM_OPEN == d)
632294113Sbapt			return 0;
633241675Suqs
634241675Suqs		while (' ' == buf[i])
635241675Suqs			i++;
636241675Suqs	}
637241675Suqs
638294113Sbapt	return '\0' == buf[i];
639241675Suqs}
640241675Suqs
641275432Sbaptstatic void
642294113Sbaptargv_multi(struct roff_man *mdoc, int line,
643241675Suqs		struct mdoc_argv *v, int *pos, char *buf)
644241675Suqs{
645241675Suqs	enum margserr	 ac;
646241675Suqs	char		*p;
647241675Suqs
648241675Suqs	for (v->sz = 0; ; v->sz++) {
649275432Sbapt		if (buf[*pos] == '-')
650241675Suqs			break;
651261344Suqs		ac = args(mdoc, line, pos, buf, ARGSFL_NONE, &p);
652275432Sbapt		if (ac == ARGS_EOLN)
653241675Suqs			break;
654241675Suqs
655275432Sbapt		if (v->sz % MULTI_STEP == 0)
656274880Sbapt			v->value = mandoc_reallocarray(v->value,
657274880Sbapt			    v->sz + MULTI_STEP, sizeof(char *));
658241675Suqs
659241675Suqs		v->value[(int)v->sz] = mandoc_strdup(p);
660241675Suqs	}
661241675Suqs}
662241675Suqs
663275432Sbaptstatic void
664294113Sbaptargv_single(struct roff_man *mdoc, int line,
665241675Suqs		struct mdoc_argv *v, int *pos, char *buf)
666241675Suqs{
667241675Suqs	enum margserr	 ac;
668241675Suqs	char		*p;
669241675Suqs
670261344Suqs	ac = args(mdoc, line, pos, buf, ARGSFL_NONE, &p);
671275432Sbapt	if (ac == ARGS_EOLN)
672275432Sbapt		return;
673241675Suqs
674241675Suqs	v->sz = 1;
675241675Suqs	v->value = mandoc_malloc(sizeof(char *));
676241675Suqs	v->value[0] = mandoc_strdup(p);
677241675Suqs}
678