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