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