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