xref: /dragonfly/contrib/mdocml/mdoc_argv.c (revision a563ca70)
1 /*	$Id: mdoc_argv.c,v 1.77 2011/05/12 23:44:01 kristaps Exp $ */
2 /*
3  * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20 
21 #include <sys/types.h>
22 
23 #include <assert.h>
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 
29 #include "mdoc.h"
30 #include "mandoc.h"
31 #include "libmdoc.h"
32 #include "libmandoc.h"
33 
34 #define	MULTI_STEP	 5 /* pre-allocate argument values */
35 #define	DELIMSZ	  	 6 /* max possible size of a delimiter */
36 
37 enum	argsflag {
38 	ARGSFL_NONE = 0,
39 	ARGSFL_DELIM, /* handle delimiters of [[::delim::][ ]+]+ */
40 	ARGSFL_TABSEP /* handle tab/`Ta' separated phrases */
41 };
42 
43 enum	argvflag {
44 	ARGV_NONE, /* no args to flag (e.g., -split) */
45 	ARGV_SINGLE, /* one arg to flag (e.g., -file xxx)  */
46 	ARGV_MULTI, /* multiple args (e.g., -column xxx yyy) */
47 	ARGV_OPT_SINGLE /* optional arg (e.g., -offset [xxx]) */
48 };
49 
50 static	enum mdocargt	 argv_a2arg(enum mdoct, const char *);
51 static	enum margserr	 args(struct mdoc *, int, int *,
52 				char *, enum argsflag, char **);
53 static	int		 args_checkpunct(const char *, int);
54 static	int		 argv(struct mdoc *, int,
55 				struct mdoc_argv *, int *, char *);
56 static	int		 argv_single(struct mdoc *, int,
57 				struct mdoc_argv *, int *, char *);
58 static	int		 argv_opt_single(struct mdoc *, int,
59 				struct mdoc_argv *, int *, char *);
60 static	int		 argv_multi(struct mdoc *, int,
61 				struct mdoc_argv *, int *, char *);
62 static	void		 argn_free(struct mdoc_arg *, int);
63 
64 static	const enum argvflag argvflags[MDOC_ARG_MAX] = {
65 	ARGV_NONE,	/* MDOC_Split */
66 	ARGV_NONE,	/* MDOC_Nosplit */
67 	ARGV_NONE,	/* MDOC_Ragged */
68 	ARGV_NONE,	/* MDOC_Unfilled */
69 	ARGV_NONE,	/* MDOC_Literal */
70 	ARGV_SINGLE,	/* MDOC_File */
71 	ARGV_OPT_SINGLE, /* MDOC_Offset */
72 	ARGV_NONE,	/* MDOC_Bullet */
73 	ARGV_NONE,	/* MDOC_Dash */
74 	ARGV_NONE,	/* MDOC_Hyphen */
75 	ARGV_NONE,	/* MDOC_Item */
76 	ARGV_NONE,	/* MDOC_Enum */
77 	ARGV_NONE,	/* MDOC_Tag */
78 	ARGV_NONE,	/* MDOC_Diag */
79 	ARGV_NONE,	/* MDOC_Hang */
80 	ARGV_NONE,	/* MDOC_Ohang */
81 	ARGV_NONE,	/* MDOC_Inset */
82 	ARGV_MULTI,	/* MDOC_Column */
83 	ARGV_SINGLE,	/* MDOC_Width */
84 	ARGV_NONE,	/* MDOC_Compact */
85 	ARGV_NONE,	/* MDOC_Std */
86 	ARGV_NONE,	/* MDOC_Filled */
87 	ARGV_NONE,	/* MDOC_Words */
88 	ARGV_NONE,	/* MDOC_Emphasis */
89 	ARGV_NONE,	/* MDOC_Symbolic */
90 	ARGV_NONE	/* MDOC_Symbolic */
91 };
92 
93 static	const enum argsflag argflags[MDOC_MAX] = {
94 	ARGSFL_NONE, /* Ap */
95 	ARGSFL_NONE, /* Dd */
96 	ARGSFL_NONE, /* Dt */
97 	ARGSFL_NONE, /* Os */
98 	ARGSFL_NONE, /* Sh */
99 	ARGSFL_NONE, /* Ss */
100 	ARGSFL_NONE, /* Pp */
101 	ARGSFL_DELIM, /* D1 */
102 	ARGSFL_DELIM, /* Dl */
103 	ARGSFL_NONE, /* Bd */
104 	ARGSFL_NONE, /* Ed */
105 	ARGSFL_NONE, /* Bl */
106 	ARGSFL_NONE, /* El */
107 	ARGSFL_NONE, /* It */
108 	ARGSFL_DELIM, /* Ad */
109 	ARGSFL_DELIM, /* An */
110 	ARGSFL_DELIM, /* Ar */
111 	ARGSFL_NONE, /* Cd */
112 	ARGSFL_DELIM, /* Cm */
113 	ARGSFL_DELIM, /* Dv */
114 	ARGSFL_DELIM, /* Er */
115 	ARGSFL_DELIM, /* Ev */
116 	ARGSFL_NONE, /* Ex */
117 	ARGSFL_DELIM, /* Fa */
118 	ARGSFL_NONE, /* Fd */
119 	ARGSFL_DELIM, /* Fl */
120 	ARGSFL_DELIM, /* Fn */
121 	ARGSFL_DELIM, /* Ft */
122 	ARGSFL_DELIM, /* Ic */
123 	ARGSFL_NONE, /* In */
124 	ARGSFL_DELIM, /* Li */
125 	ARGSFL_NONE, /* Nd */
126 	ARGSFL_DELIM, /* Nm */
127 	ARGSFL_DELIM, /* Op */
128 	ARGSFL_NONE, /* Ot */
129 	ARGSFL_DELIM, /* Pa */
130 	ARGSFL_NONE, /* Rv */
131 	ARGSFL_DELIM, /* St */
132 	ARGSFL_DELIM, /* Va */
133 	ARGSFL_DELIM, /* Vt */
134 	ARGSFL_DELIM, /* Xr */
135 	ARGSFL_NONE, /* %A */
136 	ARGSFL_NONE, /* %B */
137 	ARGSFL_NONE, /* %D */
138 	ARGSFL_NONE, /* %I */
139 	ARGSFL_NONE, /* %J */
140 	ARGSFL_NONE, /* %N */
141 	ARGSFL_NONE, /* %O */
142 	ARGSFL_NONE, /* %P */
143 	ARGSFL_NONE, /* %R */
144 	ARGSFL_NONE, /* %T */
145 	ARGSFL_NONE, /* %V */
146 	ARGSFL_DELIM, /* Ac */
147 	ARGSFL_NONE, /* Ao */
148 	ARGSFL_DELIM, /* Aq */
149 	ARGSFL_DELIM, /* At */
150 	ARGSFL_DELIM, /* Bc */
151 	ARGSFL_NONE, /* Bf */
152 	ARGSFL_NONE, /* Bo */
153 	ARGSFL_DELIM, /* Bq */
154 	ARGSFL_DELIM, /* Bsx */
155 	ARGSFL_DELIM, /* Bx */
156 	ARGSFL_NONE, /* Db */
157 	ARGSFL_DELIM, /* Dc */
158 	ARGSFL_NONE, /* Do */
159 	ARGSFL_DELIM, /* Dq */
160 	ARGSFL_DELIM, /* Ec */
161 	ARGSFL_NONE, /* Ef */
162 	ARGSFL_DELIM, /* Em */
163 	ARGSFL_NONE, /* Eo */
164 	ARGSFL_DELIM, /* Fx */
165 	ARGSFL_DELIM, /* Ms */
166 	ARGSFL_DELIM, /* No */
167 	ARGSFL_DELIM, /* Ns */
168 	ARGSFL_DELIM, /* Nx */
169 	ARGSFL_DELIM, /* Ox */
170 	ARGSFL_DELIM, /* Pc */
171 	ARGSFL_DELIM, /* Pf */
172 	ARGSFL_NONE, /* Po */
173 	ARGSFL_DELIM, /* Pq */
174 	ARGSFL_DELIM, /* Qc */
175 	ARGSFL_DELIM, /* Ql */
176 	ARGSFL_NONE, /* Qo */
177 	ARGSFL_DELIM, /* Qq */
178 	ARGSFL_NONE, /* Re */
179 	ARGSFL_NONE, /* Rs */
180 	ARGSFL_DELIM, /* Sc */
181 	ARGSFL_NONE, /* So */
182 	ARGSFL_DELIM, /* Sq */
183 	ARGSFL_NONE, /* Sm */
184 	ARGSFL_DELIM, /* Sx */
185 	ARGSFL_DELIM, /* Sy */
186 	ARGSFL_DELIM, /* Tn */
187 	ARGSFL_DELIM, /* Ux */
188 	ARGSFL_DELIM, /* Xc */
189 	ARGSFL_NONE, /* Xo */
190 	ARGSFL_NONE, /* Fo */
191 	ARGSFL_NONE, /* Fc */
192 	ARGSFL_NONE, /* Oo */
193 	ARGSFL_DELIM, /* Oc */
194 	ARGSFL_NONE, /* Bk */
195 	ARGSFL_NONE, /* Ek */
196 	ARGSFL_NONE, /* Bt */
197 	ARGSFL_NONE, /* Hf */
198 	ARGSFL_NONE, /* Fr */
199 	ARGSFL_NONE, /* Ud */
200 	ARGSFL_NONE, /* Lb */
201 	ARGSFL_NONE, /* Lp */
202 	ARGSFL_DELIM, /* Lk */
203 	ARGSFL_DELIM, /* Mt */
204 	ARGSFL_DELIM, /* Brq */
205 	ARGSFL_NONE, /* Bro */
206 	ARGSFL_DELIM, /* Brc */
207 	ARGSFL_NONE, /* %C */
208 	ARGSFL_NONE, /* Es */
209 	ARGSFL_NONE, /* En */
210 	ARGSFL_NONE, /* Dx */
211 	ARGSFL_NONE, /* %Q */
212 	ARGSFL_NONE, /* br */
213 	ARGSFL_NONE, /* sp */
214 	ARGSFL_NONE, /* %U */
215 	ARGSFL_NONE, /* Ta */
216 };
217 
218 static	const enum mdocargt args_Ex[] = {
219 	MDOC_Std,
220 	MDOC_ARG_MAX
221 };
222 
223 static	const enum mdocargt args_An[] = {
224 	MDOC_Split,
225 	MDOC_Nosplit,
226 	MDOC_ARG_MAX
227 };
228 
229 static	const enum mdocargt args_Bd[] = {
230 	MDOC_Ragged,
231 	MDOC_Unfilled,
232 	MDOC_Filled,
233 	MDOC_Literal,
234 	MDOC_File,
235 	MDOC_Offset,
236 	MDOC_Compact,
237 	MDOC_Centred,
238 	MDOC_ARG_MAX
239 };
240 
241 static	const enum mdocargt args_Bf[] = {
242 	MDOC_Emphasis,
243 	MDOC_Literal,
244 	MDOC_Symbolic,
245 	MDOC_ARG_MAX
246 };
247 
248 static	const enum mdocargt args_Bk[] = {
249 	MDOC_Words,
250 	MDOC_ARG_MAX
251 };
252 
253 static	const enum mdocargt args_Bl[] = {
254 	MDOC_Bullet,
255 	MDOC_Dash,
256 	MDOC_Hyphen,
257 	MDOC_Item,
258 	MDOC_Enum,
259 	MDOC_Tag,
260 	MDOC_Diag,
261 	MDOC_Hang,
262 	MDOC_Ohang,
263 	MDOC_Inset,
264 	MDOC_Column,
265 	MDOC_Width,
266 	MDOC_Offset,
267 	MDOC_Compact,
268 	MDOC_Nested,
269 	MDOC_ARG_MAX
270 };
271 
272 /*
273  * Parse an argument from line text.  This comes in the form of -key
274  * [value0...], which may either have a single mandatory value, at least
275  * one mandatory value, an optional single value, or no value.
276  */
277 enum margverr
278 mdoc_argv(struct mdoc *m, int line, enum mdoct tok,
279 		struct mdoc_arg **v, int *pos, char *buf)
280 {
281 	char		 *p, sv;
282 	struct mdoc_argv tmp;
283 	struct mdoc_arg	 *arg;
284 
285 	if ('\0' == buf[*pos])
286 		return(ARGV_EOLN);
287 
288 	assert(' ' != buf[*pos]);
289 
290 	/* Parse through to the first unescaped space. */
291 
292 	p = &buf[++(*pos)];
293 
294 	assert(*pos > 0);
295 
296 	/* LINTED */
297 	while (buf[*pos]) {
298 		if (' ' == buf[*pos])
299 			if ('\\' != buf[*pos - 1])
300 				break;
301 		(*pos)++;
302 	}
303 
304 	/* XXX - save zeroed byte, if not an argument. */
305 
306 	sv = '\0';
307 	if (buf[*pos]) {
308 		sv = buf[*pos];
309 		buf[(*pos)++] = '\0';
310 	}
311 
312 	memset(&tmp, 0, sizeof(struct mdoc_argv));
313 	tmp.line = line;
314 	tmp.pos = *pos;
315 
316 	/* See if our token accepts the argument. */
317 
318 	if (MDOC_ARG_MAX == (tmp.arg = argv_a2arg(tok, p))) {
319 		/* XXX - restore saved zeroed byte. */
320 		if (sv)
321 			buf[*pos - 1] = sv;
322 		return(ARGV_WORD);
323 	}
324 
325 	while (buf[*pos] && ' ' == buf[*pos])
326 		(*pos)++;
327 
328 	if ( ! argv(m, line, &tmp, pos, buf))
329 		return(ARGV_ERROR);
330 
331 	if (NULL == (arg = *v))
332 		arg = *v = mandoc_calloc(1, sizeof(struct mdoc_arg));
333 
334 	arg->argc++;
335 	arg->argv = mandoc_realloc
336 		(arg->argv, arg->argc * sizeof(struct mdoc_argv));
337 
338 	memcpy(&arg->argv[(int)arg->argc - 1],
339 			&tmp, sizeof(struct mdoc_argv));
340 
341 	return(ARGV_ARG);
342 }
343 
344 void
345 mdoc_argv_free(struct mdoc_arg *p)
346 {
347 	int		 i;
348 
349 	if (NULL == p)
350 		return;
351 
352 	if (p->refcnt) {
353 		--(p->refcnt);
354 		if (p->refcnt)
355 			return;
356 	}
357 	assert(p->argc);
358 
359 	for (i = (int)p->argc - 1; i >= 0; i--)
360 		argn_free(p, i);
361 
362 	free(p->argv);
363 	free(p);
364 }
365 
366 static void
367 argn_free(struct mdoc_arg *p, int iarg)
368 {
369 	struct mdoc_argv *arg;
370 	int		  j;
371 
372 	arg = &p->argv[iarg];
373 
374 	if (arg->sz && arg->value) {
375 		for (j = (int)arg->sz - 1; j >= 0; j--)
376 			free(arg->value[j]);
377 		free(arg->value);
378 	}
379 
380 	for (--p->argc; iarg < (int)p->argc; iarg++)
381 		p->argv[iarg] = p->argv[iarg+1];
382 }
383 
384 enum margserr
385 mdoc_zargs(struct mdoc *m, int line, int *pos, char *buf, char **v)
386 {
387 
388 	return(args(m, line, pos, buf, ARGSFL_NONE, v));
389 }
390 
391 enum margserr
392 mdoc_args(struct mdoc *m, int line, int *pos,
393 		char *buf, enum mdoct tok, char **v)
394 {
395 	enum argsflag	  fl;
396 	struct mdoc_node *n;
397 
398 	fl = argflags[tok];
399 
400 	if (MDOC_It != tok)
401 		return(args(m, line, pos, buf, fl, v));
402 
403 	/*
404 	 * We know that we're in an `It', so it's reasonable to expect
405 	 * us to be sitting in a `Bl'.  Someday this may not be the case
406 	 * (if we allow random `It's sitting out there), so provide a
407 	 * safe fall-back into the default behaviour.
408 	 */
409 
410 	for (n = m->last; n; n = n->parent)
411 		if (MDOC_Bl == n->tok)
412 			if (LIST_column == n->norm->Bl.type) {
413 				fl = ARGSFL_TABSEP;
414 				break;
415 			}
416 
417 	return(args(m, line, pos, buf, fl, v));
418 }
419 
420 static enum margserr
421 args(struct mdoc *m, int line, int *pos,
422 		char *buf, enum argsflag fl, char **v)
423 {
424 	char		*p, *pp;
425 	enum margserr	 rc;
426 
427 	assert(' ' != buf[*pos]);
428 
429 	if ('\0' == buf[*pos]) {
430 		if (MDOC_PPHRASE & m->flags)
431 			return(ARGS_EOLN);
432 		/*
433 		 * If we're not in a partial phrase and the flag for
434 		 * being a phrase literal is still set, the punctuation
435 		 * is unterminated.
436 		 */
437 		if (MDOC_PHRASELIT & m->flags)
438 			mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE);
439 
440 		m->flags &= ~MDOC_PHRASELIT;
441 		return(ARGS_EOLN);
442 	}
443 
444 	*v = &buf[*pos];
445 
446 	if (ARGSFL_DELIM == fl)
447 		if (args_checkpunct(buf, *pos))
448 			return(ARGS_PUNCT);
449 
450 	/*
451 	 * First handle TABSEP items, restricted to `Bl -column'.  This
452 	 * ignores conventional token parsing and instead uses tabs or
453 	 * `Ta' macros to separate phrases.  Phrases are parsed again
454 	 * for arguments at a later phase.
455 	 */
456 
457 	if (ARGSFL_TABSEP == fl) {
458 		/* Scan ahead to tab (can't be escaped). */
459 		p = strchr(*v, '\t');
460 		pp = NULL;
461 
462 		/* Scan ahead to unescaped `Ta'. */
463 		if ( ! (MDOC_PHRASELIT & m->flags))
464 			for (pp = *v; ; pp++) {
465 				if (NULL == (pp = strstr(pp, "Ta")))
466 					break;
467 				if (pp > *v && ' ' != *(pp - 1))
468 					continue;
469 				if (' ' == *(pp + 2) || '\0' == *(pp + 2))
470 					break;
471 			}
472 
473 		/* By default, assume a phrase. */
474 		rc = ARGS_PHRASE;
475 
476 		/*
477 		 * Adjust new-buffer position to be beyond delimiter
478 		 * mark (e.g., Ta -> end + 2).
479 		 */
480 		if (p && pp) {
481 			*pos += pp < p ? 2 : 1;
482 			rc = pp < p ? ARGS_PHRASE : ARGS_PPHRASE;
483 			p = pp < p ? pp : p;
484 		} else if (p && ! pp) {
485 			rc = ARGS_PPHRASE;
486 			*pos += 1;
487 		} else if (pp && ! p) {
488 			p = pp;
489 			*pos += 2;
490 		} else {
491 			rc = ARGS_PEND;
492 			p = strchr(*v, 0);
493 		}
494 
495 		/* Whitespace check for eoln case... */
496 		if ('\0' == *p && ' ' == *(p - 1))
497 			mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE);
498 
499 		*pos += (int)(p - *v);
500 
501 		/* Strip delimiter's preceding whitespace. */
502 		pp = p - 1;
503 		while (pp > *v && ' ' == *pp) {
504 			if (pp > *v && '\\' == *(pp - 1))
505 				break;
506 			pp--;
507 		}
508 		*(pp + 1) = 0;
509 
510 		/* Strip delimiter's proceeding whitespace. */
511 		for (pp = &buf[*pos]; ' ' == *pp; pp++, (*pos)++)
512 			/* Skip ahead. */ ;
513 
514 		return(rc);
515 	}
516 
517 	/*
518 	 * Process a quoted literal.  A quote begins with a double-quote
519 	 * and ends with a double-quote NOT preceded by a double-quote.
520 	 * Whitespace is NOT involved in literal termination.
521 	 */
522 
523 	if (MDOC_PHRASELIT & m->flags || '\"' == buf[*pos]) {
524 		if ( ! (MDOC_PHRASELIT & m->flags))
525 			*v = &buf[++(*pos)];
526 
527 		if (MDOC_PPHRASE & m->flags)
528 			m->flags |= MDOC_PHRASELIT;
529 
530 		for ( ; buf[*pos]; (*pos)++) {
531 			if ('\"' != buf[*pos])
532 				continue;
533 			if ('\"' != buf[*pos + 1])
534 				break;
535 			(*pos)++;
536 		}
537 
538 		if ('\0' == buf[*pos]) {
539 			if (MDOC_PPHRASE & m->flags)
540 				return(ARGS_QWORD);
541 			mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE);
542 			return(ARGS_QWORD);
543 		}
544 
545 		m->flags &= ~MDOC_PHRASELIT;
546 		buf[(*pos)++] = '\0';
547 
548 		if ('\0' == buf[*pos])
549 			return(ARGS_QWORD);
550 
551 		while (' ' == buf[*pos])
552 			(*pos)++;
553 
554 		if ('\0' == buf[*pos])
555 			mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE);
556 
557 		return(ARGS_QWORD);
558 	}
559 
560 	p = &buf[*pos];
561 	*v = mandoc_getarg(m->parse, &p, line, pos);
562 
563 	return(ARGS_WORD);
564 }
565 
566 /*
567  * Check if the string consists only of space-separated closing
568  * delimiters.  This is a bit of a dance: the first must be a close
569  * delimiter, but it may be followed by middle delimiters.  Arbitrary
570  * whitespace may separate these tokens.
571  */
572 static int
573 args_checkpunct(const char *buf, int i)
574 {
575 	int		 j;
576 	char		 dbuf[DELIMSZ];
577 	enum mdelim	 d;
578 
579 	/* First token must be a close-delimiter. */
580 
581 	for (j = 0; buf[i] && ' ' != buf[i] && j < DELIMSZ; j++, i++)
582 		dbuf[j] = buf[i];
583 
584 	if (DELIMSZ == j)
585 		return(0);
586 
587 	dbuf[j] = '\0';
588 	if (DELIM_CLOSE != mdoc_isdelim(dbuf))
589 		return(0);
590 
591 	while (' ' == buf[i])
592 		i++;
593 
594 	/* Remaining must NOT be open/none. */
595 
596 	while (buf[i]) {
597 		j = 0;
598 		while (buf[i] && ' ' != buf[i] && j < DELIMSZ)
599 			dbuf[j++] = buf[i++];
600 
601 		if (DELIMSZ == j)
602 			return(0);
603 
604 		dbuf[j] = '\0';
605 		d = mdoc_isdelim(dbuf);
606 		if (DELIM_NONE == d || DELIM_OPEN == d)
607 			return(0);
608 
609 		while (' ' == buf[i])
610 			i++;
611 	}
612 
613 	return('\0' == buf[i]);
614 }
615 
616 /*
617  * Match up an argument string (e.g., `-foo bar' having "foo") with the
618  * correrct identifier.  It must apply to the given macro.  If none was
619  * found (including bad matches), return MDOC_ARG_MAX.
620  */
621 static enum mdocargt
622 argv_a2arg(enum mdoct tok, const char *p)
623 {
624 	const enum mdocargt *argsp;
625 
626 	argsp = NULL;
627 
628 	switch (tok) {
629 	case (MDOC_An):
630 		argsp = args_An;
631 		break;
632 	case (MDOC_Bd):
633 		argsp = args_Bd;
634 		break;
635 	case (MDOC_Bf):
636 		argsp = args_Bf;
637 		break;
638 	case (MDOC_Bk):
639 		argsp = args_Bk;
640 		break;
641 	case (MDOC_Bl):
642 		argsp = args_Bl;
643 		break;
644 	case (MDOC_Rv):
645 		/* FALLTHROUGH */
646 	case (MDOC_Ex):
647 		argsp = args_Ex;
648 		break;
649 	default:
650 		return(MDOC_ARG_MAX);
651 	}
652 
653 	assert(argsp);
654 
655 	for ( ; MDOC_ARG_MAX != *argsp ; argsp++)
656 		if (0 == strcmp(p, mdoc_argnames[*argsp]))
657 			return(*argsp);
658 
659 	return(MDOC_ARG_MAX);
660 }
661 
662 static int
663 argv_multi(struct mdoc *m, int line,
664 		struct mdoc_argv *v, int *pos, char *buf)
665 {
666 	enum margserr	 ac;
667 	char		*p;
668 
669 	for (v->sz = 0; ; v->sz++) {
670 		if ('-' == buf[*pos])
671 			break;
672 		ac = args(m, line, pos, buf, ARGSFL_NONE, &p);
673 		if (ARGS_ERROR == ac)
674 			return(0);
675 		else if (ARGS_EOLN == ac)
676 			break;
677 
678 		if (0 == v->sz % MULTI_STEP)
679 			v->value = mandoc_realloc(v->value,
680 				(v->sz + MULTI_STEP) * sizeof(char *));
681 
682 		v->value[(int)v->sz] = mandoc_strdup(p);
683 	}
684 
685 	return(1);
686 }
687 
688 static int
689 argv_opt_single(struct mdoc *m, int line,
690 		struct mdoc_argv *v, int *pos, char *buf)
691 {
692 	enum margserr	 ac;
693 	char		*p;
694 
695 	if ('-' == buf[*pos])
696 		return(1);
697 
698 	ac = args(m, line, pos, buf, ARGSFL_NONE, &p);
699 	if (ARGS_ERROR == ac)
700 		return(0);
701 	if (ARGS_EOLN == ac)
702 		return(1);
703 
704 	v->sz = 1;
705 	v->value = mandoc_malloc(sizeof(char *));
706 	v->value[0] = mandoc_strdup(p);
707 
708 	return(1);
709 }
710 
711 /*
712  * Parse a single, mandatory value from the stream.
713  */
714 static int
715 argv_single(struct mdoc *m, int line,
716 		struct mdoc_argv *v, int *pos, char *buf)
717 {
718 	int		 ppos;
719 	enum margserr	 ac;
720 	char		*p;
721 
722 	ppos = *pos;
723 
724 	ac = args(m, line, pos, buf, ARGSFL_NONE, &p);
725 	if (ARGS_EOLN == ac) {
726 		mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTARGVCOUNT);
727 		return(0);
728 	} else if (ARGS_ERROR == ac)
729 		return(0);
730 
731 	v->sz = 1;
732 	v->value = mandoc_malloc(sizeof(char *));
733 	v->value[0] = mandoc_strdup(p);
734 
735 	return(1);
736 }
737 
738 /*
739  * Determine rules for parsing arguments.  Arguments can either accept
740  * no parameters, an optional single parameter, one parameter, or
741  * multiple parameters.
742  */
743 static int
744 argv(struct mdoc *mdoc, int line,
745 		struct mdoc_argv *v, int *pos, char *buf)
746 {
747 
748 	v->sz = 0;
749 	v->value = NULL;
750 
751 	switch (argvflags[v->arg]) {
752 	case (ARGV_SINGLE):
753 		return(argv_single(mdoc, line, v, pos, buf));
754 	case (ARGV_MULTI):
755 		return(argv_multi(mdoc, line, v, pos, buf));
756 	case (ARGV_OPT_SINGLE):
757 		return(argv_opt_single(mdoc, line, v, pos, buf));
758 	case (ARGV_NONE):
759 		break;
760 	default:
761 		abort();
762 		/* NOTREACHED */
763 	}
764 
765 	return(1);
766 }
767