f68e55f5e0dcb12133c4e445481fd955ef363d5b
[dragonfly.git] / contrib / mdocml / mdoc_argv.c
1 /*      $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 }