xref: /openbsd-src/usr.bin/mandoc/mdoc_argv.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /* $Id: mdoc_argv.c,v 1.1 2009/04/06 20:30:40 kristaps Exp $ */
2 /*
3  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
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
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 #include <sys/types.h>
20 
21 #include <assert.h>
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 
27 #include "libmdoc.h"
28 
29 /*
30  * Routines to parse arguments of macros.  Arguments follow the syntax
31  * of `-arg [val [valN...]]'.  Arguments come in all types:  quoted
32  * arguments, multiple arguments per value, no-value arguments, etc.
33  *
34  * There's no limit to the number or arguments that may be allocated.
35  */
36 
37 #define	ARGS_QUOTED	(1 << 0)
38 #define	ARGS_DELIM	(1 << 1)
39 #define	ARGS_TABSEP	(1 << 2)
40 #define	ARGS_ARGVLIKE	(1 << 3)
41 
42 #define	ARGV_NONE	(1 << 0)
43 #define	ARGV_SINGLE	(1 << 1)
44 #define	ARGV_MULTI	(1 << 2)
45 #define	ARGV_OPT_SINGLE	(1 << 3)
46 
47 #define	MULTI_STEP	 5
48 
49 enum 	mwarn {
50 	WQUOTPARM,
51 	WARGVPARM,
52 	WCOLEMPTY,
53 	WTAILWS
54 };
55 
56 enum	merr {
57 	EQUOTTERM,
58 	EMALLOC,
59 	EARGVAL
60 };
61 
62 static	int		 argv_a2arg(int, const char *);
63 static	int		 args(struct mdoc *, int, int *,
64 				char *, int, char **);
65 static	int		 argv(struct mdoc *, int,
66 				struct mdoc_argv *, int *, char *);
67 static	int		 argv_single(struct mdoc *, int,
68 				struct mdoc_argv *, int *, char *);
69 static	int		 argv_opt_single(struct mdoc *, int,
70 				struct mdoc_argv *, int *, char *);
71 static	int		 argv_multi(struct mdoc *, int,
72 				struct mdoc_argv *, int *, char *);
73 static	int		 pwarn(struct mdoc *, int, int, enum mwarn);
74 static	int		 perr(struct mdoc *, int, int, enum merr);
75 
76 #define verr(m, t) perr((m), (m)->last->line, (m)->last->pos, (t))
77 
78 /* Per-argument flags. */
79 
80 static	int mdoc_argvflags[MDOC_ARG_MAX] = {
81 	ARGV_NONE,	/* MDOC_Split */
82 	ARGV_NONE,	/* MDOC_Nosplit */
83 	ARGV_NONE,	/* MDOC_Ragged */
84 	ARGV_NONE,	/* MDOC_Unfilled */
85 	ARGV_NONE,	/* MDOC_Literal */
86 	ARGV_NONE,	/* MDOC_File */
87 	ARGV_SINGLE,	/* MDOC_Offset */
88 	ARGV_NONE,	/* MDOC_Bullet */
89 	ARGV_NONE,	/* MDOC_Dash */
90 	ARGV_NONE,	/* MDOC_Hyphen */
91 	ARGV_NONE,	/* MDOC_Item */
92 	ARGV_NONE,	/* MDOC_Enum */
93 	ARGV_NONE,	/* MDOC_Tag */
94 	ARGV_NONE,	/* MDOC_Diag */
95 	ARGV_NONE,	/* MDOC_Hang */
96 	ARGV_NONE,	/* MDOC_Ohang */
97 	ARGV_NONE,	/* MDOC_Inset */
98 	ARGV_MULTI,	/* MDOC_Column */
99 	ARGV_SINGLE,	/* MDOC_Width */
100 	ARGV_NONE,	/* MDOC_Compact */
101 	ARGV_OPT_SINGLE, /* MDOC_Std */
102 	ARGV_NONE,	/* MDOC_Filled */
103 	ARGV_NONE,	/* MDOC_Words */
104 	ARGV_NONE,	/* MDOC_Emphasis */
105 	ARGV_NONE,	/* MDOC_Symbolic */
106 	ARGV_NONE	/* MDOC_Symbolic */
107 };
108 
109 static	int mdoc_argflags[MDOC_MAX] = {
110 	0, /* \" */
111 	0, /* Dd */
112 	0, /* Dt */
113 	0, /* Os */
114 	ARGS_QUOTED, /* Sh */
115 	ARGS_QUOTED, /* Ss */
116 	ARGS_DELIM, /* Pp */
117 	ARGS_DELIM, /* D1 */
118 	ARGS_DELIM | ARGS_QUOTED, /* Dl */
119 	0, /* Bd */
120 	0, /* Ed */
121 	0, /* Bl */
122 	0, /* El */
123 	0, /* It */
124 	ARGS_DELIM, /* Ad */
125 	ARGS_DELIM, /* An */
126 	ARGS_DELIM | ARGS_QUOTED, /* Ar */
127 	ARGS_QUOTED, /* Cd */
128 	ARGS_DELIM, /* Cm */
129 	ARGS_DELIM, /* Dv */
130 	ARGS_DELIM, /* Er */
131 	ARGS_DELIM, /* Ev */
132 	0, /* Ex */
133 	ARGS_DELIM | ARGS_QUOTED, /* Fa */
134 	0, /* Fd */
135 	ARGS_DELIM, /* Fl */
136 	ARGS_DELIM | ARGS_QUOTED, /* Fn */
137 	ARGS_DELIM | ARGS_QUOTED, /* Ft */
138 	ARGS_DELIM, /* Ic */
139 	0, /* In */
140 	ARGS_DELIM | ARGS_QUOTED, /* Li */
141 	ARGS_QUOTED, /* Nd */
142 	ARGS_DELIM, /* Nm */
143 	ARGS_DELIM, /* Op */
144 	0, /* Ot */
145 	ARGS_DELIM, /* Pa */
146 	0, /* Rv */
147 	ARGS_DELIM | ARGS_ARGVLIKE, /* St */
148 	ARGS_DELIM, /* Va */
149 	ARGS_DELIM, /* Vt */
150 	ARGS_DELIM, /* Xr */
151 	ARGS_QUOTED, /* %A */
152 	ARGS_QUOTED, /* %B */
153 	ARGS_QUOTED, /* %D */
154 	ARGS_QUOTED, /* %I */
155 	ARGS_QUOTED, /* %J */
156 	ARGS_QUOTED, /* %N */
157 	ARGS_QUOTED, /* %O */
158 	ARGS_QUOTED, /* %P */
159 	ARGS_QUOTED, /* %R */
160 	ARGS_QUOTED, /* %T */
161 	ARGS_QUOTED, /* %V */
162 	ARGS_DELIM, /* Ac */
163 	0, /* Ao */
164 	ARGS_DELIM, /* Aq */
165 	ARGS_DELIM, /* At */
166 	ARGS_DELIM, /* Bc */
167 	0, /* Bf */
168 	0, /* Bo */
169 	ARGS_DELIM, /* Bq */
170 	ARGS_DELIM, /* Bsx */
171 	ARGS_DELIM, /* Bx */
172 	0, /* Db */
173 	ARGS_DELIM, /* Dc */
174 	0, /* Do */
175 	ARGS_DELIM, /* Dq */
176 	ARGS_DELIM, /* Ec */
177 	0, /* Ef */
178 	ARGS_DELIM, /* Em */
179 	0, /* Eo */
180 	ARGS_DELIM, /* Fx */
181 	ARGS_DELIM, /* Ms */
182 	ARGS_DELIM, /* No */
183 	ARGS_DELIM, /* Ns */
184 	ARGS_DELIM, /* Nx */
185 	ARGS_DELIM, /* Ox */
186 	ARGS_DELIM, /* Pc */
187 	ARGS_DELIM, /* Pf */
188 	0, /* Po */
189 	ARGS_DELIM, /* Pq */
190 	ARGS_DELIM, /* Qc */
191 	ARGS_DELIM, /* Ql */
192 	0, /* Qo */
193 	ARGS_DELIM, /* Qq */
194 	0, /* Re */
195 	0, /* Rs */
196 	ARGS_DELIM, /* Sc */
197 	0, /* So */
198 	ARGS_DELIM, /* Sq */
199 	0, /* Sm */
200 	ARGS_DELIM, /* Sx */
201 	ARGS_DELIM | ARGS_QUOTED, /* Sy */
202 	ARGS_DELIM, /* Tn */
203 	ARGS_DELIM, /* Ux */
204 	ARGS_DELIM, /* Xc */
205 	0, /* Xo */
206 	ARGS_QUOTED, /* Fo */
207 	0, /* Fc */
208 	0, /* Oo */
209 	ARGS_DELIM, /* Oc */
210 	0, /* Bk */
211 	0, /* Ek */
212 	0, /* Bt */
213 	0, /* Hf */
214 	0, /* Fr */
215 	0, /* Ud */
216 	0, /* Lb */
217 	0, /* Ap */
218 	ARGS_DELIM, /* Lp */
219 	ARGS_DELIM | ARGS_QUOTED, /* Lk */
220 	ARGS_DELIM | ARGS_QUOTED, /* Mt */
221 	ARGS_DELIM, /* Brq */
222 	0, /* Bro */
223 	ARGS_DELIM, /* Brc */
224 	ARGS_QUOTED, /* %C */
225 	0, /* Es */
226 	0, /* En */
227 	0, /* Dx */
228 	ARGS_QUOTED, /* %Q */
229 };
230 
231 
232 /*
233  * Parse an argument from line text.  This comes in the form of -key
234  * [value0...], which may either have a single mandatory value, at least
235  * one mandatory value, an optional single value, or no value.
236  */
237 int
238 mdoc_argv(struct mdoc *mdoc, int line, int tok,
239 		struct mdoc_arg **v, int *pos, char *buf)
240 {
241 	int		  i;
242 	char		 *p, sv;
243 	struct mdoc_argv tmp;
244 	struct mdoc_arg	 *arg;
245 
246 	if (0 == buf[*pos])
247 		return(ARGV_EOLN);
248 
249 	assert(' ' != buf[*pos]);
250 
251 	if ('-' != buf[*pos] || ARGS_ARGVLIKE & mdoc_argflags[tok])
252 		return(ARGV_WORD);
253 
254 	/* Parse through to the first unescaped space. */
255 
256 	i = *pos;
257 	p = &buf[++(*pos)];
258 
259 	assert(*pos > 0);
260 
261 	/* LINTED */
262 	while (buf[*pos]) {
263 		if (' ' == buf[*pos])
264 			if ('\\' != buf[*pos - 1])
265 				break;
266 		(*pos)++;
267 	}
268 
269 	/* XXX - save zeroed byte, if not an argument. */
270 
271 	sv = 0;
272 	if (buf[*pos]) {
273 		sv = buf[*pos];
274 		buf[(*pos)++] = 0;
275 	}
276 
277 	(void)memset(&tmp, 0, sizeof(struct mdoc_argv));
278 	tmp.line = line;
279 	tmp.pos = *pos;
280 
281 	/* See if our token accepts the argument. */
282 
283 	if (MDOC_ARG_MAX == (tmp.arg = argv_a2arg(tok, p))) {
284 		/* XXX - restore saved zeroed byte. */
285 		if (sv)
286 			buf[*pos - 1] = sv;
287 		if ( ! pwarn(mdoc, line, i, WARGVPARM))
288 			return(ARGV_ERROR);
289 		return(ARGV_WORD);
290 	}
291 
292 	while (buf[*pos] && ' ' == buf[*pos])
293 		(*pos)++;
294 
295 	if ( ! argv(mdoc, line, &tmp, pos, buf))
296 		return(ARGV_ERROR);
297 
298 	if (NULL == (arg = *v)) {
299 		*v = calloc(1, sizeof(struct mdoc_arg));
300 		if (NULL == *v) {
301 			(void)verr(mdoc, EMALLOC);
302 			return(ARGV_ERROR);
303 		}
304 		arg = *v;
305 	}
306 
307 	arg->argc++;
308 	arg->argv = realloc(arg->argv, arg->argc *
309 			sizeof(struct mdoc_argv));
310 
311 	if (NULL == arg->argv) {
312 		(void)verr(mdoc, EMALLOC);
313 		return(ARGV_ERROR);
314 	}
315 
316 	(void)memcpy(&arg->argv[(int)arg->argc - 1],
317 			&tmp, sizeof(struct mdoc_argv));
318 
319 	return(ARGV_ARG);
320 }
321 
322 
323 void
324 mdoc_argv_free(struct mdoc_arg *p)
325 {
326 	int		 i, j;
327 
328 	if (NULL == p)
329 		return;
330 
331 	if (p->refcnt) {
332 		--(p->refcnt);
333 		if (p->refcnt)
334 			return;
335 	}
336 	assert(p->argc);
337 
338 	/* LINTED */
339 	for (i = 0; i < (int)p->argc; i++) {
340 		if (0 == p->argv[i].sz)
341 			continue;
342 		/* LINTED */
343 		for (j = 0; j < (int)p->argv[i].sz; j++)
344 			free(p->argv[i].value[j]);
345 
346 		free(p->argv[i].value);
347 	}
348 
349 	free(p->argv);
350 	free(p);
351 }
352 
353 
354 
355 static int
356 perr(struct mdoc *mdoc, int line, int pos, enum merr code)
357 {
358 	char		*p;
359 
360 	p = NULL;
361 	switch (code) {
362 	case (EMALLOC):
363 		p = "memory exhausted";
364 		break;
365 	case (EQUOTTERM):
366 		p = "unterminated quoted parameter";
367 		break;
368 	case (EARGVAL):
369 		p = "argument requires a value";
370 		break;
371 	}
372 	assert(p);
373 	return(mdoc_perr(mdoc, line, pos, p));
374 }
375 
376 
377 static int
378 pwarn(struct mdoc *mdoc, int line, int pos, enum mwarn code)
379 {
380 	char		*p;
381 	int		 c;
382 
383 	p = NULL;
384 	c = WARN_SYNTAX;
385 	switch (code) {
386 	case (WQUOTPARM):
387 		p = "unexpected quoted parameter";
388 		break;
389 	case (WARGVPARM):
390 		p = "argument-like parameter";
391 		break;
392 	case (WCOLEMPTY):
393 		p = "last list column is empty";
394 		c = WARN_COMPAT;
395 		break;
396 	case (WTAILWS):
397 		p = "trailing whitespace";
398 		c = WARN_COMPAT;
399 		break;
400 	}
401 	assert(p);
402 	return(mdoc_pwarn(mdoc, line, pos, c, p));
403 }
404 
405 
406 int
407 mdoc_args(struct mdoc *mdoc, int line,
408 		int *pos, char *buf, int tok, char **v)
409 {
410 	int		  fl, c, i;
411 	struct mdoc_node *n;
412 
413 	fl = (0 == tok) ? 0 : mdoc_argflags[tok];
414 
415 	/*
416 	 * Override per-macro argument flags with context-specific ones.
417 	 * As of now, this is only valid for `It' depending on its list
418 	 * context.
419 	 */
420 
421 	switch (tok) {
422 	case (MDOC_It):
423 		for (n = mdoc->last; n; n = n->parent)
424 			if (MDOC_BLOCK == n->type && MDOC_Bl == n->tok)
425 				break;
426 
427 		assert(n);
428 		c = (int)(n->args ? n->args->argc : 0);
429 		assert(c > 0);
430 
431 		/*
432 		 * Using `Bl -column' adds ARGS_TABSEP to the arguments
433 		 * and invalidates ARGS_DELIM.  Using `Bl -diag' allows
434 		 * for quoted arguments.
435 		 */
436 
437 		/* LINTED */
438 		for (i = 0; i < c; i++) {
439 			switch (n->args->argv[i].arg) {
440 			case (MDOC_Column):
441 				fl |= ARGS_TABSEP;
442 				fl &= ~ARGS_DELIM;
443 				i = c;
444 				break;
445 			case (MDOC_Diag):
446 				fl |= ARGS_QUOTED;
447 				i = c;
448 				break;
449 			default:
450 				break;
451 			}
452 		}
453 		break;
454 	default:
455 		break;
456 	}
457 
458 	return(args(mdoc, line, pos, buf, fl, v));
459 }
460 
461 
462 static int
463 args(struct mdoc *mdoc, int line,
464 		int *pos, char *buf, int fl, char **v)
465 {
466 	int		  i;
467 	char		 *p, *pp;
468 
469 	assert(*pos > 0);
470 
471 	if (0 == buf[*pos])
472 		return(ARGS_EOLN);
473 
474 	if ('\"' == buf[*pos] && ! (fl & ARGS_QUOTED))
475 		if ( ! pwarn(mdoc, line, *pos, WQUOTPARM))
476 			return(ARGS_ERROR);
477 
478 	if ( ! (fl & ARGS_ARGVLIKE) && '-' == buf[*pos])
479 		if ( ! pwarn(mdoc, line, *pos, WARGVPARM))
480 			return(ARGS_ERROR);
481 
482 	/*
483 	 * If the first character is a delimiter and we're to look for
484 	 * delimited strings, then pass down the buffer seeing if it
485 	 * follows the pattern of [[::delim::][ ]+]+.
486 	 */
487 
488 	if ((fl & ARGS_DELIM) && mdoc_iscdelim(buf[*pos])) {
489 		for (i = *pos; buf[i]; ) {
490 			if ( ! mdoc_iscdelim(buf[i]))
491 				break;
492 			i++;
493 			/* There must be at least one space... */
494 			if (0 == buf[i] || ' ' != buf[i])
495 				break;
496 			i++;
497 			while (buf[i] && ' ' == buf[i])
498 				i++;
499 		}
500 		if (0 == buf[i]) {
501 			*v = &buf[*pos];
502 			return(ARGS_PUNCT);
503 		}
504 	}
505 
506 	/* First parse non-quoted strings. */
507 
508 	if ('\"' != buf[*pos] || ! (ARGS_QUOTED & fl)) {
509 		*v = &buf[*pos];
510 
511 		/*
512 		 * Thar be dragons here!  If we're tab-separated, search
513 		 * ahead for either a tab or the `Ta' macro.
514 		 * If a `Ta' is detected, it must be space-buffered before and
515 		 * after.  If either of these hold true, then prune out the
516 		 * extra spaces and call it an argument.
517 		 */
518 
519 		if (ARGS_TABSEP & fl) {
520 			/* Scan ahead to unescaped tab. */
521 
522 			p = strchr(*v, '\t');
523 
524 			/* Scan ahead to unescaped `Ta'. */
525 
526 			for (pp = *v; ; pp++) {
527 				if (NULL == (pp = strstr(pp, "Ta")))
528 					break;
529 				if (pp > *v && ' ' != *(pp - 1))
530 					continue;
531 				if (' ' == *(pp + 2) || 0 == *(pp + 2))
532 					break;
533 			}
534 
535 			/* Choose delimiter tab/Ta. */
536 
537 			if (p && pp)
538 				p = (p < pp ? p : pp);
539 			else if ( ! p && pp)
540 				p = pp;
541 
542 			/* Strip delimiter's preceding whitespace. */
543 
544 			if (p && p > *v) {
545 				pp = p - 1;
546 				while (pp > *v && ' ' == *pp)
547 					pp--;
548 				if (pp == *v && ' ' == *pp)
549 					*pp = 0;
550 				else if (' ' == *pp)
551 					*(pp + 1) = 0;
552 			}
553 
554 			/* ...in- and proceding whitespace. */
555 
556 			if (p && ('\t' != *p)) {
557 				*p++ = 0;
558 				*p++ = 0;
559 			} else if (p)
560 				*p++ = 0;
561 
562 			if (p) {
563 				while (' ' == *p)
564 					p++;
565 				if (0 != *p)
566 					*(p - 1) = 0;
567 				*pos += (int)(p - *v);
568 			}
569 
570 			if (p && 0 == *p)
571 				if ( ! pwarn(mdoc, line, *pos, WCOLEMPTY))
572 					return(0);
573 			if (p && 0 == *p && p > *v && ' ' == *(p - 1))
574 				if ( ! pwarn(mdoc, line, *pos, WTAILWS))
575 					return(0);
576 
577 			if (p)
578 				return(ARGS_PHRASE);
579 
580 			/* Configure the eoln case, too. */
581 
582 			p = strchr(*v, 0);
583 			assert(p);
584 
585 			if (p > *v && ' ' == *(p - 1))
586 				if ( ! pwarn(mdoc, line, *pos, WTAILWS))
587 					return(0);
588 			*pos += (int)(p - *v);
589 
590 			return(ARGS_PHRASE);
591 		}
592 
593 		/* Do non-tabsep look-ahead here. */
594 
595 		if ( ! (ARGS_TABSEP & fl))
596 			while (buf[*pos]) {
597 				if (' ' == buf[*pos])
598 					if ('\\' != buf[*pos - 1])
599 						break;
600 				(*pos)++;
601 			}
602 
603 		if (0 == buf[*pos])
604 			return(ARGS_WORD);
605 
606 		buf[(*pos)++] = 0;
607 
608 		if (0 == buf[*pos])
609 			return(ARGS_WORD);
610 
611 		if ( ! (ARGS_TABSEP & fl))
612 			while (buf[*pos] && ' ' == buf[*pos])
613 				(*pos)++;
614 
615 		if (buf[*pos])
616 			return(ARGS_WORD);
617 
618 		if ( ! pwarn(mdoc, line, *pos, WTAILWS))
619 			return(ARGS_ERROR);
620 
621 		return(ARGS_WORD);
622 	}
623 
624 	/*
625 	 * If we're a quoted string (and quoted strings are allowed),
626 	 * then parse ahead to the next quote.  If none's found, it's an
627 	 * error.  After, parse to the next word.
628 	 */
629 
630 	*v = &buf[++(*pos)];
631 
632 	while (buf[*pos] && '\"' != buf[*pos])
633 		(*pos)++;
634 
635 	if (0 == buf[*pos]) {
636 		(void)perr(mdoc, line, *pos, EQUOTTERM);
637 		return(ARGS_ERROR);
638 	}
639 
640 	buf[(*pos)++] = 0;
641 	if (0 == buf[*pos])
642 		return(ARGS_QWORD);
643 
644 	while (buf[*pos] && ' ' == buf[*pos])
645 		(*pos)++;
646 
647 	if (buf[*pos])
648 		return(ARGS_QWORD);
649 
650 	if ( ! pwarn(mdoc, line, *pos, WTAILWS))
651 		return(ARGS_ERROR);
652 
653 	return(ARGS_QWORD);
654 }
655 
656 
657 static int
658 argv_a2arg(int tok, const char *argv)
659 {
660 
661 	/*
662 	 * Parse an argument identifier from its text.  XXX - this
663 	 * should really be table-driven to clarify the code.
664 	 *
665 	 * If you add an argument to the list, make sure that you
666 	 * register it here with its one or more macros!
667 	 */
668 
669 	switch (tok) {
670 	case (MDOC_An):
671 		if (0 == strcmp(argv, "split"))
672 			return(MDOC_Split);
673 		else if (0 == strcmp(argv, "nosplit"))
674 			return(MDOC_Nosplit);
675 		break;
676 
677 	case (MDOC_Bd):
678 		if (0 == strcmp(argv, "ragged"))
679 			return(MDOC_Ragged);
680 		else if (0 == strcmp(argv, "unfilled"))
681 			return(MDOC_Unfilled);
682 		else if (0 == strcmp(argv, "filled"))
683 			return(MDOC_Filled);
684 		else if (0 == strcmp(argv, "literal"))
685 			return(MDOC_Literal);
686 		else if (0 == strcmp(argv, "file"))
687 			return(MDOC_File);
688 		else if (0 == strcmp(argv, "offset"))
689 			return(MDOC_Offset);
690 		else if (0 == strcmp(argv, "compact"))
691 			return(MDOC_Compact);
692 		break;
693 
694 	case (MDOC_Bf):
695 		if (0 == strcmp(argv, "emphasis"))
696 			return(MDOC_Emphasis);
697 		else if (0 == strcmp(argv, "literal"))
698 			return(MDOC_Literal);
699 		else if (0 == strcmp(argv, "symbolic"))
700 			return(MDOC_Symbolic);
701 		break;
702 
703 	case (MDOC_Bk):
704 		if (0 == strcmp(argv, "words"))
705 			return(MDOC_Words);
706 		break;
707 
708 	case (MDOC_Bl):
709 		if (0 == strcmp(argv, "bullet"))
710 			return(MDOC_Bullet);
711 		else if (0 == strcmp(argv, "dash"))
712 			return(MDOC_Dash);
713 		else if (0 == strcmp(argv, "hyphen"))
714 			return(MDOC_Hyphen);
715 		else if (0 == strcmp(argv, "item"))
716 			return(MDOC_Item);
717 		else if (0 == strcmp(argv, "enum"))
718 			return(MDOC_Enum);
719 		else if (0 == strcmp(argv, "tag"))
720 			return(MDOC_Tag);
721 		else if (0 == strcmp(argv, "diag"))
722 			return(MDOC_Diag);
723 		else if (0 == strcmp(argv, "hang"))
724 			return(MDOC_Hang);
725 		else if (0 == strcmp(argv, "ohang"))
726 			return(MDOC_Ohang);
727 		else if (0 == strcmp(argv, "inset"))
728 			return(MDOC_Inset);
729 		else if (0 == strcmp(argv, "column"))
730 			return(MDOC_Column);
731 		else if (0 == strcmp(argv, "width"))
732 			return(MDOC_Width);
733 		else if (0 == strcmp(argv, "offset"))
734 			return(MDOC_Offset);
735 		else if (0 == strcmp(argv, "compact"))
736 			return(MDOC_Compact);
737 		else if (0 == strcmp(argv, "nested"))
738 			return(MDOC_Nested);
739 		break;
740 
741 	case (MDOC_Rv):
742 		/* FALLTHROUGH */
743 	case (MDOC_Ex):
744 		if (0 == strcmp(argv, "std"))
745 			return(MDOC_Std);
746 		break;
747 	default:
748 		break;
749 	}
750 
751 	return(MDOC_ARG_MAX);
752 }
753 
754 
755 static int
756 argv_multi(struct mdoc *mdoc, int line,
757 		struct mdoc_argv *v, int *pos, char *buf)
758 {
759 	int		 c, ppos;
760 	char		*p;
761 
762 	ppos = *pos;
763 
764 	for (v->sz = 0; ; v->sz++) {
765 		if ('-' == buf[*pos])
766 			break;
767 		c = args(mdoc, line, pos, buf, ARGS_QUOTED, &p);
768 		if (ARGS_ERROR == c)
769 			return(0);
770 		else if (ARGS_EOLN == c)
771 			break;
772 
773 		if (0 == v->sz % MULTI_STEP) {
774 			v->value = realloc(v->value,
775 				(v->sz + MULTI_STEP) * sizeof(char *));
776 			if (NULL == v->value) {
777 				(void)verr(mdoc, EMALLOC);
778 				return(ARGV_ERROR);
779 			}
780 		}
781 		if (NULL == (v->value[(int)v->sz] = strdup(p)))
782 			return(verr(mdoc, EMALLOC));
783 	}
784 
785 	if (v->sz)
786 		return(1);
787 
788 	return(perr(mdoc, line, ppos, EARGVAL));
789 }
790 
791 
792 static int
793 argv_opt_single(struct mdoc *mdoc, int line,
794 		struct mdoc_argv *v, int *pos, char *buf)
795 {
796 	int		 c;
797 	char		*p;
798 
799 	if ('-' == buf[*pos])
800 		return(1);
801 
802 	c = args(mdoc, line, pos, buf, ARGS_QUOTED, &p);
803 	if (ARGS_ERROR == c)
804 		return(0);
805 	if (ARGS_EOLN == c)
806 		return(1);
807 
808 	v->sz = 1;
809 	if (NULL == (v->value = calloc(1, sizeof(char *))))
810 		return(verr(mdoc, EMALLOC));
811 	if (NULL == (v->value[0] = strdup(p)))
812 		return(verr(mdoc, EMALLOC));
813 
814 	return(1);
815 }
816 
817 
818 /*
819  * Parse a single, mandatory value from the stream.
820  */
821 static int
822 argv_single(struct mdoc *mdoc, int line,
823 		struct mdoc_argv *v, int *pos, char *buf)
824 {
825 	int		 c, ppos;
826 	char		*p;
827 
828 	ppos = *pos;
829 
830 	c = args(mdoc, line, pos, buf, ARGS_QUOTED, &p);
831 	if (ARGS_ERROR == c)
832 		return(0);
833 	if (ARGS_EOLN == c)
834 		return(perr(mdoc, line, ppos, EARGVAL));
835 
836 	v->sz = 1;
837 	if (NULL == (v->value = calloc(1, sizeof(char *))))
838 		return(verr(mdoc, EMALLOC));
839 	if (NULL == (v->value[0] = strdup(p)))
840 		return(verr(mdoc, EMALLOC));
841 
842 	return(1);
843 }
844 
845 
846 /*
847  * Determine rules for parsing arguments.  Arguments can either accept
848  * no parameters, an optional single parameter, one parameter, or
849  * multiple parameters.
850  */
851 static int
852 argv(struct mdoc *mdoc, int line,
853 		struct mdoc_argv *v, int *pos, char *buf)
854 {
855 
856 	v->sz = 0;
857 	v->value = NULL;
858 
859 	switch (mdoc_argvflags[v->arg]) {
860 	case (ARGV_SINGLE):
861 		return(argv_single(mdoc, line, v, pos, buf));
862 	case (ARGV_MULTI):
863 		return(argv_multi(mdoc, line, v, pos, buf));
864 	case (ARGV_OPT_SINGLE):
865 		return(argv_opt_single(mdoc, line, v, pos, buf));
866 	default:
867 		/* ARGV_NONE */
868 		break;
869 	}
870 
871 	return(1);
872 }
873