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