]> git.cameronkatri.com Git - mandoc.git/blob - mdoc.c
Wrangle mdoc_args() and mdoc_zargs() to use enum return type.
[mandoc.git] / mdoc.c
1 /* $Id: mdoc.c,v 1.123 2010/04/08 07:53:01 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 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 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 <stdarg.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30
31 #include "libmdoc.h"
32 #include "libmandoc.h"
33
34 const char *const __mdoc_merrnames[MERRMAX] = {
35 "trailing whitespace", /* ETAILWS */
36 "unexpected quoted parameter", /* EQUOTPARM */
37 "unterminated quoted parameter", /* EQUOTTERM */
38 "argument parameter suggested", /* EARGVAL */
39 "macro disallowed in prologue", /* EBODYPROL */
40 "macro disallowed in body", /* EPROLBODY */
41 "text disallowed in prologue", /* ETEXTPROL */
42 "blank line disallowed", /* ENOBLANK */
43 "text parameter too long", /* ETOOLONG */
44 "invalid escape sequence", /* EESCAPE */
45 "invalid character", /* EPRINT */
46 "document has no body", /* ENODAT */
47 "document has no prologue", /* ENOPROLOGUE */
48 "expected line arguments", /* ELINE */
49 "invalid AT&T argument", /* EATT */
50 "default name not yet set", /* ENAME */
51 "missing list type", /* ELISTTYPE */
52 "missing display type", /* EDISPTYPE */
53 "too many display types", /* EMULTIDISP */
54 "too many list types", /* EMULTILIST */
55 "NAME section must be first", /* ESECNAME */
56 "badly-formed NAME section", /* ENAMESECINC */
57 "argument repeated", /* EARGREP */
58 "expected boolean parameter", /* EBOOL */
59 "inconsistent column syntax", /* ECOLMIS */
60 "nested display invalid", /* ENESTDISP */
61 "width argument missing", /* EMISSWIDTH */
62 "invalid section for this manual section", /* EWRONGMSEC */
63 "section out of conventional order", /* ESECOOO */
64 "section repeated", /* ESECREP */
65 "invalid standard argument", /* EBADSTAND */
66 "multi-line arguments discouraged", /* ENOMULTILINE */
67 "multi-line arguments suggested", /* EMULTILINE */
68 "line arguments discouraged", /* ENOLINE */
69 "prologue macro out of conventional order", /* EPROLOOO */
70 "prologue macro repeated", /* EPROLREP */
71 "invalid manual section", /* EBADMSEC */
72 "invalid section", /* EBADSEC */
73 "invalid font mode", /* EFONT */
74 "invalid date syntax", /* EBADDATE */
75 "invalid number format", /* ENUMFMT */
76 "superfluous width argument", /* ENOWIDTH */
77 "system: utsname error", /* EUTSNAME */
78 "obsolete macro", /* EOBS */
79 "end-of-line scope violation", /* EIMPBRK */
80 "empty macro ignored", /* EIGNE */
81 "unclosed explicit scope", /* EOPEN */
82 "unterminated quoted phrase", /* EQUOTPHR */
83 "closure macro without prior context", /* ENOCTX */
84 "no description found for library", /* ELIB */
85 "bad child for parent context", /* EBADCHILD */
86 "list arguments preceding type", /* ENOTYPE */
87 "deprecated comment style", /* EBADCOMMENT */
88 };
89
90 const char *const __mdoc_macronames[MDOC_MAX] = {
91 "Ap", "Dd", "Dt", "Os",
92 "Sh", "Ss", "Pp", "D1",
93 "Dl", "Bd", "Ed", "Bl",
94 "El", "It", "Ad", "An",
95 "Ar", "Cd", "Cm", "Dv",
96 "Er", "Ev", "Ex", "Fa",
97 "Fd", "Fl", "Fn", "Ft",
98 "Ic", "In", "Li", "Nd",
99 "Nm", "Op", "Ot", "Pa",
100 "Rv", "St", "Va", "Vt",
101 /* LINTED */
102 "Xr", "%A", "%B", "%D",
103 /* LINTED */
104 "%I", "%J", "%N", "%O",
105 /* LINTED */
106 "%P", "%R", "%T", "%V",
107 "Ac", "Ao", "Aq", "At",
108 "Bc", "Bf", "Bo", "Bq",
109 "Bsx", "Bx", "Db", "Dc",
110 "Do", "Dq", "Ec", "Ef",
111 "Em", "Eo", "Fx", "Ms",
112 "No", "Ns", "Nx", "Ox",
113 "Pc", "Pf", "Po", "Pq",
114 "Qc", "Ql", "Qo", "Qq",
115 "Re", "Rs", "Sc", "So",
116 "Sq", "Sm", "Sx", "Sy",
117 "Tn", "Ux", "Xc", "Xo",
118 "Fo", "Fc", "Oo", "Oc",
119 "Bk", "Ek", "Bt", "Hf",
120 "Fr", "Ud", "Lb", "Lp",
121 "Lk", "Mt", "Brq", "Bro",
122 /* LINTED */
123 "Brc", "%C", "Es", "En",
124 /* LINTED */
125 "Dx", "%Q", "br", "sp",
126 /* LINTED */
127 "%U"
128 };
129
130 const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
131 "split", "nosplit", "ragged",
132 "unfilled", "literal", "file",
133 "offset", "bullet", "dash",
134 "hyphen", "item", "enum",
135 "tag", "diag", "hang",
136 "ohang", "inset", "column",
137 "width", "compact", "std",
138 "filled", "words", "emphasis",
139 "symbolic", "nested", "centered"
140 };
141
142 const char * const *mdoc_macronames = __mdoc_macronames;
143 const char * const *mdoc_argnames = __mdoc_argnames;
144
145 static void mdoc_node_free(struct mdoc_node *);
146 static void mdoc_node_unlink(struct mdoc *,
147 struct mdoc_node *);
148 static void mdoc_free1(struct mdoc *);
149 static void mdoc_alloc1(struct mdoc *);
150 static struct mdoc_node *node_alloc(struct mdoc *, int, int,
151 enum mdoct, enum mdoc_type);
152 static int node_append(struct mdoc *,
153 struct mdoc_node *);
154 static int mdoc_ptext(struct mdoc *, int, char *);
155 static int mdoc_pmacro(struct mdoc *, int, char *);
156 static int macrowarn(struct mdoc *, int, const char *);
157 static int pstring(struct mdoc *, int, int,
158 const char *, size_t);
159
160 const struct mdoc_node *
161 mdoc_node(const struct mdoc *m)
162 {
163
164 return(MDOC_HALT & m->flags ? NULL : m->first);
165 }
166
167
168 const struct mdoc_meta *
169 mdoc_meta(const struct mdoc *m)
170 {
171
172 return(MDOC_HALT & m->flags ? NULL : &m->meta);
173 }
174
175
176 /*
177 * Frees volatile resources (parse tree, meta-data, fields).
178 */
179 static void
180 mdoc_free1(struct mdoc *mdoc)
181 {
182
183 if (mdoc->first)
184 mdoc_node_delete(mdoc, mdoc->first);
185 if (mdoc->meta.title)
186 free(mdoc->meta.title);
187 if (mdoc->meta.os)
188 free(mdoc->meta.os);
189 if (mdoc->meta.name)
190 free(mdoc->meta.name);
191 if (mdoc->meta.arch)
192 free(mdoc->meta.arch);
193 if (mdoc->meta.vol)
194 free(mdoc->meta.vol);
195 }
196
197
198 /*
199 * Allocate all volatile resources (parse tree, meta-data, fields).
200 */
201 static void
202 mdoc_alloc1(struct mdoc *mdoc)
203 {
204
205 memset(&mdoc->meta, 0, sizeof(struct mdoc_meta));
206 mdoc->flags = 0;
207 mdoc->lastnamed = mdoc->lastsec = SEC_NONE;
208 mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node));
209 mdoc->first = mdoc->last;
210 mdoc->last->type = MDOC_ROOT;
211 mdoc->next = MDOC_NEXT_CHILD;
212 }
213
214
215 /*
216 * Free up volatile resources (see mdoc_free1()) then re-initialises the
217 * data with mdoc_alloc1(). After invocation, parse data has been reset
218 * and the parser is ready for re-invocation on a new tree; however,
219 * cross-parse non-volatile data is kept intact.
220 */
221 void
222 mdoc_reset(struct mdoc *mdoc)
223 {
224
225 mdoc_free1(mdoc);
226 mdoc_alloc1(mdoc);
227 }
228
229
230 /*
231 * Completely free up all volatile and non-volatile parse resources.
232 * After invocation, the pointer is no longer usable.
233 */
234 void
235 mdoc_free(struct mdoc *mdoc)
236 {
237
238 mdoc_free1(mdoc);
239 free(mdoc);
240 }
241
242
243 /*
244 * Allocate volatile and non-volatile parse resources.
245 */
246 struct mdoc *
247 mdoc_alloc(void *data, int pflags, const struct mdoc_cb *cb)
248 {
249 struct mdoc *p;
250
251 p = mandoc_calloc(1, sizeof(struct mdoc));
252
253 if (cb)
254 memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
255
256 p->data = data;
257 p->pflags = pflags;
258
259 mdoc_hash_init();
260 mdoc_alloc1(p);
261 return(p);
262 }
263
264
265 /*
266 * Climb back up the parse tree, validating open scopes. Mostly calls
267 * through to macro_end() in macro.c.
268 */
269 int
270 mdoc_endparse(struct mdoc *m)
271 {
272
273 if (MDOC_HALT & m->flags)
274 return(0);
275 else if (mdoc_macroend(m))
276 return(1);
277 m->flags |= MDOC_HALT;
278 return(0);
279 }
280
281
282 /*
283 * Main parse routine. Parses a single line -- really just hands off to
284 * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()).
285 */
286 int
287 mdoc_parseln(struct mdoc *m, int ln, char *buf)
288 {
289
290 if (MDOC_HALT & m->flags)
291 return(0);
292
293 return('.' == *buf ? mdoc_pmacro(m, ln, buf) :
294 mdoc_ptext(m, ln, buf));
295 }
296
297
298 int
299 mdoc_verr(struct mdoc *mdoc, int ln, int pos,
300 const char *fmt, ...)
301 {
302 char buf[256];
303 va_list ap;
304
305 if (NULL == mdoc->cb.mdoc_err)
306 return(0);
307
308 va_start(ap, fmt);
309 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
310 va_end(ap);
311
312 return((*mdoc->cb.mdoc_err)(mdoc->data, ln, pos, buf));
313 }
314
315
316 int
317 mdoc_vwarn(struct mdoc *mdoc, int ln, int pos, const char *fmt, ...)
318 {
319 char buf[256];
320 va_list ap;
321
322 if (NULL == mdoc->cb.mdoc_warn)
323 return(0);
324
325 va_start(ap, fmt);
326 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
327 va_end(ap);
328
329 return((*mdoc->cb.mdoc_warn)(mdoc->data, ln, pos, buf));
330 }
331
332
333 int
334 mdoc_err(struct mdoc *m, int line, int pos, int iserr, enum merr type)
335 {
336 const char *p;
337
338 p = __mdoc_merrnames[(int)type];
339 assert(p);
340
341 if (iserr)
342 return(mdoc_verr(m, line, pos, p));
343
344 return(mdoc_vwarn(m, line, pos, p));
345 }
346
347
348 int
349 mdoc_macro(struct mdoc *m, enum mdoct tok,
350 int ln, int pp, int *pos, char *buf)
351 {
352 assert(tok < MDOC_MAX);
353
354 /* If we're in the body, deny prologue calls. */
355
356 if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
357 MDOC_PBODY & m->flags)
358 return(mdoc_perr(m, ln, pp, EPROLBODY));
359
360 /* If we're in the prologue, deny "body" macros. */
361
362 if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
363 ! (MDOC_PBODY & m->flags)) {
364 if ( ! mdoc_pwarn(m, ln, pp, EBODYPROL))
365 return(0);
366 if (NULL == m->meta.title)
367 m->meta.title = mandoc_strdup("unknown");
368 if (NULL == m->meta.vol)
369 m->meta.vol = mandoc_strdup("local");
370 if (NULL == m->meta.os)
371 m->meta.os = mandoc_strdup("local");
372 if (0 == m->meta.date)
373 m->meta.date = time(NULL);
374 m->flags |= MDOC_PBODY;
375 }
376
377 return((*mdoc_macros[tok].fp)(m, tok, ln, pp, pos, buf));
378 }
379
380
381 static int
382 node_append(struct mdoc *mdoc, struct mdoc_node *p)
383 {
384
385 assert(mdoc->last);
386 assert(mdoc->first);
387 assert(MDOC_ROOT != p->type);
388
389 switch (mdoc->next) {
390 case (MDOC_NEXT_SIBLING):
391 mdoc->last->next = p;
392 p->prev = mdoc->last;
393 p->parent = mdoc->last->parent;
394 break;
395 case (MDOC_NEXT_CHILD):
396 mdoc->last->child = p;
397 p->parent = mdoc->last;
398 break;
399 default:
400 abort();
401 /* NOTREACHED */
402 }
403
404 p->parent->nchild++;
405
406 if ( ! mdoc_valid_pre(mdoc, p))
407 return(0);
408 if ( ! mdoc_action_pre(mdoc, p))
409 return(0);
410
411 switch (p->type) {
412 case (MDOC_HEAD):
413 assert(MDOC_BLOCK == p->parent->type);
414 p->parent->head = p;
415 break;
416 case (MDOC_TAIL):
417 assert(MDOC_BLOCK == p->parent->type);
418 p->parent->tail = p;
419 break;
420 case (MDOC_BODY):
421 assert(MDOC_BLOCK == p->parent->type);
422 p->parent->body = p;
423 break;
424 default:
425 break;
426 }
427
428 mdoc->last = p;
429
430 switch (p->type) {
431 case (MDOC_TEXT):
432 if ( ! mdoc_valid_post(mdoc))
433 return(0);
434 if ( ! mdoc_action_post(mdoc))
435 return(0);
436 break;
437 default:
438 break;
439 }
440
441 return(1);
442 }
443
444
445 static struct mdoc_node *
446 node_alloc(struct mdoc *m, int line, int pos,
447 enum mdoct tok, enum mdoc_type type)
448 {
449 struct mdoc_node *p;
450
451 p = mandoc_calloc(1, sizeof(struct mdoc_node));
452 p->sec = m->lastsec;
453 p->line = line;
454 p->pos = pos;
455 p->tok = tok;
456 p->type = type;
457
458 return(p);
459 }
460
461
462 int
463 mdoc_tail_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
464 {
465 struct mdoc_node *p;
466
467 p = node_alloc(m, line, pos, tok, MDOC_TAIL);
468 if ( ! node_append(m, p))
469 return(0);
470 m->next = MDOC_NEXT_CHILD;
471 return(1);
472 }
473
474
475 int
476 mdoc_head_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
477 {
478 struct mdoc_node *p;
479
480 assert(m->first);
481 assert(m->last);
482
483 p = node_alloc(m, line, pos, tok, MDOC_HEAD);
484 if ( ! node_append(m, p))
485 return(0);
486 m->next = MDOC_NEXT_CHILD;
487 return(1);
488 }
489
490
491 int
492 mdoc_body_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
493 {
494 struct mdoc_node *p;
495
496 p = node_alloc(m, line, pos, tok, MDOC_BODY);
497 if ( ! node_append(m, p))
498 return(0);
499 m->next = MDOC_NEXT_CHILD;
500 return(1);
501 }
502
503
504 int
505 mdoc_block_alloc(struct mdoc *m, int line, int pos,
506 enum mdoct tok, struct mdoc_arg *args)
507 {
508 struct mdoc_node *p;
509
510 p = node_alloc(m, line, pos, tok, MDOC_BLOCK);
511 p->args = args;
512 if (p->args)
513 (args->refcnt)++;
514 if ( ! node_append(m, p))
515 return(0);
516 m->next = MDOC_NEXT_CHILD;
517 return(1);
518 }
519
520
521 int
522 mdoc_elem_alloc(struct mdoc *m, int line, int pos,
523 enum mdoct tok, struct mdoc_arg *args)
524 {
525 struct mdoc_node *p;
526
527 p = node_alloc(m, line, pos, tok, MDOC_ELEM);
528 p->args = args;
529 if (p->args)
530 (args->refcnt)++;
531 if ( ! node_append(m, p))
532 return(0);
533 m->next = MDOC_NEXT_CHILD;
534 return(1);
535 }
536
537
538 static int
539 pstring(struct mdoc *m, int line, int pos, const char *p, size_t len)
540 {
541 struct mdoc_node *n;
542 size_t sv;
543
544 n = node_alloc(m, line, pos, -1, MDOC_TEXT);
545 n->string = mandoc_malloc(len + 1);
546 sv = strlcpy(n->string, p, len + 1);
547
548 /* Prohibit truncation. */
549 assert(sv < len + 1);
550
551 if ( ! node_append(m, n))
552 return(0);
553 m->next = MDOC_NEXT_SIBLING;
554 return(1);
555 }
556
557
558 int
559 mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p)
560 {
561
562 return(pstring(m, line, pos, p, strlen(p)));
563 }
564
565
566 void
567 mdoc_node_free(struct mdoc_node *p)
568 {
569
570 if (p->string)
571 free(p->string);
572 if (p->args)
573 mdoc_argv_free(p->args);
574 free(p);
575 }
576
577
578 static void
579 mdoc_node_unlink(struct mdoc *m, struct mdoc_node *n)
580 {
581
582 /* Adjust siblings. */
583
584 if (n->prev)
585 n->prev->next = n->next;
586 if (n->next)
587 n->next->prev = n->prev;
588
589 /* Adjust parent. */
590
591 if (n->parent) {
592 n->parent->nchild--;
593 if (n->parent->child == n)
594 n->parent->child = n->prev ? n->prev : n->next;
595 }
596
597 /* Adjust parse point, if applicable. */
598
599 if (m && m->last == n) {
600 if (n->prev) {
601 m->last = n->prev;
602 m->next = MDOC_NEXT_SIBLING;
603 } else {
604 m->last = n->parent;
605 m->next = MDOC_NEXT_CHILD;
606 }
607 }
608
609 if (m && m->first == n)
610 m->first = NULL;
611 }
612
613
614 void
615 mdoc_node_delete(struct mdoc *m, struct mdoc_node *p)
616 {
617
618 while (p->child) {
619 assert(p->nchild);
620 mdoc_node_delete(m, p->child);
621 }
622 assert(0 == p->nchild);
623
624 mdoc_node_unlink(m, p);
625 mdoc_node_free(p);
626 }
627
628
629 /*
630 * Parse free-form text, that is, a line that does not begin with the
631 * control character.
632 */
633 static int
634 mdoc_ptext(struct mdoc *m, int line, char *buf)
635 {
636 int i, j;
637 char sv;
638
639 /* Ignore bogus comments. */
640
641 if ('\\' == buf[0] && '.' == buf[1] && '\"' == buf[2])
642 return(mdoc_pwarn(m, line, 0, EBADCOMMENT));
643
644 if (SEC_NONE == m->lastnamed)
645 return(mdoc_perr(m, line, 0, ETEXTPROL));
646
647 /*
648 * If in literal mode, then pass the buffer directly to the
649 * back-end, as it should be preserved as a single term.
650 */
651
652 if (MDOC_LITERAL & m->flags)
653 return(mdoc_word_alloc(m, line, 0, buf));
654
655 /* Disallow blank/white-space lines in non-literal mode. */
656
657 for (i = 0; ' ' == buf[i]; i++)
658 /* Skip leading whitespace. */ ;
659
660 if ('\0' == buf[i]) {
661 if ( ! mdoc_pwarn(m, line, 0, ENOBLANK))
662 return(0);
663 /*
664 * Assume that a `Pp' should be inserted in the case of
665 * a blank line. Technically, blank lines aren't
666 * allowed, but enough manuals assume this behaviour
667 * that we want to work around it.
668 */
669 if ( ! mdoc_elem_alloc(m, line, 0, MDOC_Pp, NULL))
670 return(0);
671 }
672
673 /*
674 * Break apart a free-form line into tokens. Spaces are
675 * stripped out of the input.
676 */
677
678 for (j = i; buf[i]; i++) {
679 if (' ' != buf[i])
680 continue;
681
682 /* Escaped whitespace. */
683 if (i && ' ' == buf[i] && '\\' == buf[i - 1])
684 continue;
685
686 sv = buf[i];
687 buf[i++] = '\0';
688
689 if ( ! pstring(m, line, j, &buf[j], (size_t)(i - j)))
690 return(0);
691
692 /* Trailing whitespace? Check at overwritten byte. */
693
694 if (' ' == sv && '\0' == buf[i])
695 if ( ! mdoc_pwarn(m, line, i - 1, ETAILWS))
696 return(0);
697
698 for ( ; ' ' == buf[i]; i++)
699 /* Skip trailing whitespace. */ ;
700
701 j = i;
702
703 /* Trailing whitespace? */
704
705 if (' ' == buf[i - 1] && '\0' == buf[i])
706 if ( ! mdoc_pwarn(m, line, i - 1, ETAILWS))
707 return(0);
708
709 if ('\0' == buf[i])
710 break;
711 }
712
713 if (j != i && ! pstring(m, line, j, &buf[j], (size_t)(i - j)))
714 return(0);
715
716 m->next = MDOC_NEXT_SIBLING;
717 return(1);
718 }
719
720
721
722 static int
723 macrowarn(struct mdoc *m, int ln, const char *buf)
724 {
725 if ( ! (MDOC_IGN_MACRO & m->pflags))
726 return(mdoc_verr(m, ln, 0,
727 "unknown macro: %s%s",
728 buf, strlen(buf) > 3 ? "..." : ""));
729 return(mdoc_vwarn(m, ln, 0, "unknown macro: %s%s",
730 buf, strlen(buf) > 3 ? "..." : ""));
731 }
732
733
734 /*
735 * Parse a macro line, that is, a line beginning with the control
736 * character.
737 */
738 int
739 mdoc_pmacro(struct mdoc *m, int ln, char *buf)
740 {
741 int i, j, c;
742 char mac[5];
743
744 /* Empty lines are ignored. */
745
746 if ('\0' == buf[1])
747 return(1);
748
749 i = 1;
750
751 /* Accept whitespace after the initial control char. */
752
753 if (' ' == buf[i]) {
754 i++;
755 while (buf[i] && ' ' == buf[i])
756 i++;
757 if ('\0' == buf[i])
758 return(1);
759 }
760
761 /* Copy the first word into a nil-terminated buffer. */
762
763 for (j = 0; j < 4; j++, i++) {
764 if ('\0' == (mac[j] = buf[i]))
765 break;
766 else if (' ' == buf[i])
767 break;
768
769 /* Check for invalid characters. */
770
771 if (isgraph((u_char)buf[i]))
772 continue;
773 return(mdoc_perr(m, ln, i, EPRINT));
774 }
775
776 mac[j] = 0;
777
778 if (j == 4 || j < 2) {
779 if ( ! macrowarn(m, ln, mac))
780 goto err;
781 return(1);
782 }
783
784 if (MDOC_MAX == (c = mdoc_hash_find(mac))) {
785 if ( ! macrowarn(m, ln, mac))
786 goto err;
787 return(1);
788 }
789
790 /* The macro is sane. Jump to the next word. */
791
792 while (buf[i] && ' ' == buf[i])
793 i++;
794
795 /* Trailing whitespace? */
796
797 if ('\0' == buf[i] && ' ' == buf[i - 1])
798 if ( ! mdoc_pwarn(m, ln, i - 1, ETAILWS))
799 goto err;
800
801 /*
802 * Begin recursive parse sequence. Since we're at the start of
803 * the line, we don't need to do callable/parseable checks.
804 */
805 if ( ! mdoc_macro(m, c, ln, 1, &i, buf))
806 goto err;
807
808 return(1);
809
810 err: /* Error out. */
811
812 m->flags |= MDOC_HALT;
813 return(0);
814 }
815
816