]> git.cameronkatri.com Git - mandoc.git/blob - mdoc.c
38f97ed16c9ba7df8fe1a69dd7a25c93d88ae49a
[mandoc.git] / mdoc.c
1 /* $Id: mdoc.c,v 1.130 2010/05/13 06:22:11 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
158
159 const struct mdoc_node *
160 mdoc_node(const struct mdoc *m)
161 {
162
163 return(MDOC_HALT & m->flags ? NULL : m->first);
164 }
165
166
167 const struct mdoc_meta *
168 mdoc_meta(const struct mdoc *m)
169 {
170
171 return(MDOC_HALT & m->flags ? NULL : &m->meta);
172 }
173
174
175 /*
176 * Frees volatile resources (parse tree, meta-data, fields).
177 */
178 static void
179 mdoc_free1(struct mdoc *mdoc)
180 {
181
182 if (mdoc->first)
183 mdoc_node_delete(mdoc, mdoc->first);
184 if (mdoc->meta.title)
185 free(mdoc->meta.title);
186 if (mdoc->meta.os)
187 free(mdoc->meta.os);
188 if (mdoc->meta.name)
189 free(mdoc->meta.name);
190 if (mdoc->meta.arch)
191 free(mdoc->meta.arch);
192 if (mdoc->meta.vol)
193 free(mdoc->meta.vol);
194 }
195
196
197 /*
198 * Allocate all volatile resources (parse tree, meta-data, fields).
199 */
200 static void
201 mdoc_alloc1(struct mdoc *mdoc)
202 {
203
204 memset(&mdoc->meta, 0, sizeof(struct mdoc_meta));
205 mdoc->flags = 0;
206 mdoc->lastnamed = mdoc->lastsec = SEC_NONE;
207 mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node));
208 mdoc->first = mdoc->last;
209 mdoc->last->type = MDOC_ROOT;
210 mdoc->next = MDOC_NEXT_CHILD;
211 }
212
213
214 /*
215 * Free up volatile resources (see mdoc_free1()) then re-initialises the
216 * data with mdoc_alloc1(). After invocation, parse data has been reset
217 * and the parser is ready for re-invocation on a new tree; however,
218 * cross-parse non-volatile data is kept intact.
219 */
220 void
221 mdoc_reset(struct mdoc *mdoc)
222 {
223
224 mdoc_free1(mdoc);
225 mdoc_alloc1(mdoc);
226 }
227
228
229 /*
230 * Completely free up all volatile and non-volatile parse resources.
231 * After invocation, the pointer is no longer usable.
232 */
233 void
234 mdoc_free(struct mdoc *mdoc)
235 {
236
237 mdoc_free1(mdoc);
238 free(mdoc);
239 }
240
241
242 /*
243 * Allocate volatile and non-volatile parse resources.
244 */
245 struct mdoc *
246 mdoc_alloc(void *data, int pflags, const struct mdoc_cb *cb)
247 {
248 struct mdoc *p;
249
250 p = mandoc_calloc(1, sizeof(struct mdoc));
251
252 if (cb)
253 memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
254
255 p->data = data;
256 p->pflags = pflags;
257
258 mdoc_hash_init();
259 mdoc_alloc1(p);
260 return(p);
261 }
262
263
264 /*
265 * Climb back up the parse tree, validating open scopes. Mostly calls
266 * through to macro_end() in macro.c.
267 */
268 int
269 mdoc_endparse(struct mdoc *m)
270 {
271
272 if (MDOC_HALT & m->flags)
273 return(0);
274 else if (mdoc_macroend(m))
275 return(1);
276 m->flags |= MDOC_HALT;
277 return(0);
278 }
279
280
281 /*
282 * Main parse routine. Parses a single line -- really just hands off to
283 * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()).
284 */
285 int
286 mdoc_parseln(struct mdoc *m, int ln, char *buf)
287 {
288
289 if (MDOC_HALT & m->flags)
290 return(0);
291
292 m->flags |= MDOC_NEWLINE;
293 return('.' == *buf ?
294 mdoc_pmacro(m, ln, buf) :
295 mdoc_ptext(m, ln, buf));
296 }
297
298
299 int
300 mdoc_verr(struct mdoc *mdoc, int ln, int pos,
301 const char *fmt, ...)
302 {
303 char buf[256];
304 va_list ap;
305
306 if (NULL == mdoc->cb.mdoc_err)
307 return(0);
308
309 va_start(ap, fmt);
310 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
311 va_end(ap);
312
313 return((*mdoc->cb.mdoc_err)(mdoc->data, ln, pos, buf));
314 }
315
316
317 int
318 mdoc_vwarn(struct mdoc *mdoc, int ln, int pos, const char *fmt, ...)
319 {
320 char buf[256];
321 va_list ap;
322
323 if (NULL == mdoc->cb.mdoc_warn)
324 return(0);
325
326 va_start(ap, fmt);
327 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
328 va_end(ap);
329
330 return((*mdoc->cb.mdoc_warn)(mdoc->data, ln, pos, buf));
331 }
332
333
334 int
335 mdoc_err(struct mdoc *m, int line, int pos, int iserr, enum merr type)
336 {
337 const char *p;
338
339 p = __mdoc_merrnames[(int)type];
340 assert(p);
341
342 if (iserr)
343 return(mdoc_verr(m, line, pos, p));
344
345 return(mdoc_vwarn(m, line, pos, p));
346 }
347
348
349 int
350 mdoc_macro(struct mdoc *m, enum mdoct tok,
351 int ln, int pp, int *pos, char *buf)
352 {
353 assert(tok < MDOC_MAX);
354
355 /* If we're in the body, deny prologue calls. */
356
357 if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
358 MDOC_PBODY & m->flags)
359 return(mdoc_perr(m, ln, pp, EPROLBODY));
360
361 /* If we're in the prologue, deny "body" macros. */
362
363 if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
364 ! (MDOC_PBODY & m->flags)) {
365 if ( ! mdoc_pwarn(m, ln, pp, EBODYPROL))
366 return(0);
367 if (NULL == m->meta.title)
368 m->meta.title = mandoc_strdup("unknown");
369 if (NULL == m->meta.vol)
370 m->meta.vol = mandoc_strdup("local");
371 if (NULL == m->meta.os)
372 m->meta.os = mandoc_strdup("local");
373 if (0 == m->meta.date)
374 m->meta.date = time(NULL);
375 m->flags |= MDOC_PBODY;
376 }
377
378 return((*mdoc_macros[tok].fp)(m, tok, ln, pp, pos, buf));
379 }
380
381
382 static int
383 node_append(struct mdoc *mdoc, struct mdoc_node *p)
384 {
385
386 assert(mdoc->last);
387 assert(mdoc->first);
388 assert(MDOC_ROOT != p->type);
389
390 switch (mdoc->next) {
391 case (MDOC_NEXT_SIBLING):
392 mdoc->last->next = p;
393 p->prev = mdoc->last;
394 p->parent = mdoc->last->parent;
395 break;
396 case (MDOC_NEXT_CHILD):
397 mdoc->last->child = p;
398 p->parent = mdoc->last;
399 break;
400 default:
401 abort();
402 /* NOTREACHED */
403 }
404
405 p->parent->nchild++;
406
407 if ( ! mdoc_valid_pre(mdoc, p))
408 return(0);
409 if ( ! mdoc_action_pre(mdoc, p))
410 return(0);
411
412 switch (p->type) {
413 case (MDOC_HEAD):
414 assert(MDOC_BLOCK == p->parent->type);
415 p->parent->head = p;
416 break;
417 case (MDOC_TAIL):
418 assert(MDOC_BLOCK == p->parent->type);
419 p->parent->tail = p;
420 break;
421 case (MDOC_BODY):
422 assert(MDOC_BLOCK == p->parent->type);
423 p->parent->body = p;
424 break;
425 default:
426 break;
427 }
428
429 mdoc->last = p;
430
431 switch (p->type) {
432 case (MDOC_TEXT):
433 if ( ! mdoc_valid_post(mdoc))
434 return(0);
435 if ( ! mdoc_action_post(mdoc))
436 return(0);
437 break;
438 default:
439 break;
440 }
441
442 return(1);
443 }
444
445
446 static struct mdoc_node *
447 node_alloc(struct mdoc *m, int line, int pos,
448 enum mdoct tok, enum mdoc_type type)
449 {
450 struct mdoc_node *p;
451
452 p = mandoc_calloc(1, sizeof(struct mdoc_node));
453 p->sec = m->lastsec;
454 p->line = line;
455 p->pos = pos;
456 p->tok = tok;
457 p->type = type;
458 if (MDOC_NEWLINE & m->flags)
459 p->flags |= MDOC_LINE;
460 m->flags &= ~MDOC_NEWLINE;
461 return(p);
462 }
463
464
465 int
466 mdoc_tail_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
467 {
468 struct mdoc_node *p;
469
470 p = node_alloc(m, line, pos, tok, MDOC_TAIL);
471 if ( ! node_append(m, p))
472 return(0);
473 m->next = MDOC_NEXT_CHILD;
474 return(1);
475 }
476
477
478 int
479 mdoc_head_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
480 {
481 struct mdoc_node *p;
482
483 assert(m->first);
484 assert(m->last);
485
486 p = node_alloc(m, line, pos, tok, MDOC_HEAD);
487 if ( ! node_append(m, p))
488 return(0);
489 m->next = MDOC_NEXT_CHILD;
490 return(1);
491 }
492
493
494 int
495 mdoc_body_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
496 {
497 struct mdoc_node *p;
498
499 p = node_alloc(m, line, pos, tok, MDOC_BODY);
500 if ( ! node_append(m, p))
501 return(0);
502 m->next = MDOC_NEXT_CHILD;
503 return(1);
504 }
505
506
507 int
508 mdoc_block_alloc(struct mdoc *m, int line, int pos,
509 enum mdoct tok, struct mdoc_arg *args)
510 {
511 struct mdoc_node *p;
512
513 p = node_alloc(m, line, pos, tok, MDOC_BLOCK);
514 p->args = args;
515 if (p->args)
516 (args->refcnt)++;
517 if ( ! node_append(m, p))
518 return(0);
519 m->next = MDOC_NEXT_CHILD;
520 return(1);
521 }
522
523
524 int
525 mdoc_elem_alloc(struct mdoc *m, int line, int pos,
526 enum mdoct tok, struct mdoc_arg *args)
527 {
528 struct mdoc_node *p;
529
530 p = node_alloc(m, line, pos, tok, MDOC_ELEM);
531 p->args = args;
532 if (p->args)
533 (args->refcnt)++;
534 if ( ! node_append(m, p))
535 return(0);
536 m->next = MDOC_NEXT_CHILD;
537 return(1);
538 }
539
540
541 int
542 mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p)
543 {
544 struct mdoc_node *n;
545 size_t sv, len;
546
547 len = strlen(p);
548
549 n = node_alloc(m, line, pos, MDOC_MAX, MDOC_TEXT);
550 n->string = mandoc_malloc(len + 1);
551 sv = strlcpy(n->string, p, len + 1);
552
553 /* Prohibit truncation. */
554 assert(sv < len + 1);
555
556 if ( ! node_append(m, n))
557 return(0);
558
559 m->next = MDOC_NEXT_SIBLING;
560 return(1);
561 }
562
563
564 void
565 mdoc_node_free(struct mdoc_node *p)
566 {
567
568 if (p->string)
569 free(p->string);
570 if (p->args)
571 mdoc_argv_free(p->args);
572 free(p);
573 }
574
575
576 static void
577 mdoc_node_unlink(struct mdoc *m, struct mdoc_node *n)
578 {
579
580 /* Adjust siblings. */
581
582 if (n->prev)
583 n->prev->next = n->next;
584 if (n->next)
585 n->next->prev = n->prev;
586
587 /* Adjust parent. */
588
589 if (n->parent) {
590 n->parent->nchild--;
591 if (n->parent->child == n)
592 n->parent->child = n->prev ? n->prev : n->next;
593 }
594
595 /* Adjust parse point, if applicable. */
596
597 if (m && m->last == n) {
598 if (n->prev) {
599 m->last = n->prev;
600 m->next = MDOC_NEXT_SIBLING;
601 } else {
602 m->last = n->parent;
603 m->next = MDOC_NEXT_CHILD;
604 }
605 }
606
607 if (m && m->first == n)
608 m->first = NULL;
609 }
610
611
612 void
613 mdoc_node_delete(struct mdoc *m, struct mdoc_node *p)
614 {
615
616 while (p->child) {
617 assert(p->nchild);
618 mdoc_node_delete(m, p->child);
619 }
620 assert(0 == p->nchild);
621
622 mdoc_node_unlink(m, p);
623 mdoc_node_free(p);
624 }
625
626
627 /*
628 * Parse free-form text, that is, a line that does not begin with the
629 * control character.
630 */
631 static int
632 mdoc_ptext(struct mdoc *m, int line, char *buf)
633 {
634 int i;
635
636 /* Ignore bogus comments. */
637
638 if ('\\' == buf[0] && '.' == buf[1] && '\"' == buf[2])
639 return(mdoc_pwarn(m, line, 0, EBADCOMMENT));
640
641 /* No text before an initial macro. */
642
643 if (SEC_NONE == m->lastnamed)
644 return(mdoc_perr(m, line, 0, ETEXTPROL));
645
646 /* Literal just gets pulled in as-is. */
647
648 if (MDOC_LITERAL & m->flags)
649 return(mdoc_word_alloc(m, line, 0, buf));
650
651 /* Check for a blank line, which may also consist of spaces. */
652
653 for (i = 0; ' ' == buf[i]; i++)
654 /* Skip to first non-space. */ ;
655
656 if ('\0' == buf[i]) {
657 if ( ! mdoc_pwarn(m, line, 0, ENOBLANK))
658 return(0);
659
660 /*
661 * Insert a `Pp' in the case of a blank line. Technically,
662 * blank lines aren't allowed, but enough manuals assume this
663 * behaviour that we want to work around it.
664 */
665 if ( ! mdoc_elem_alloc(m, line, 0, MDOC_Pp, NULL))
666 return(0);
667
668 m->next = MDOC_NEXT_SIBLING;
669 return(1);
670 }
671
672 /*
673 * Warn if the last un-escaped character is whitespace. Then
674 * strip away the remaining spaces (tabs stay!).
675 */
676
677 i = (int)strlen(buf);
678 assert(i);
679
680 if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
681 if (i > 1 && '\\' != buf[i - 2])
682 if ( ! mdoc_pwarn(m, line, i - 1, ETAILWS))
683 return(0);
684
685 for (--i; i && ' ' == buf[i]; i--)
686 /* Spin back to non-space. */ ;
687
688 /* Jump ahead of escaped whitespace. */
689 i += '\\' == buf[i] ? 2 : 1;
690
691 buf[i] = '\0';
692 }
693
694 /* Allocate the whole word. */
695
696 if ( ! mdoc_word_alloc(m, line, 0, buf))
697 return(0);
698
699 /*
700 * End-of-sentence check. If the last character is an unescaped
701 * EOS character, then flag the node as being the end of a
702 * sentence. The front-end will know how to interpret this.
703 */
704
705 assert(i);
706
707 if (mandoc_eos(buf, (size_t)i))
708 m->last->flags |= MDOC_EOS;
709
710 return(1);
711 }
712
713
714 static int
715 macrowarn(struct mdoc *m, int ln, const char *buf)
716 {
717 if ( ! (MDOC_IGN_MACRO & m->pflags))
718 return(mdoc_verr(m, ln, 0, "unknown macro: %s%s",
719 buf, strlen(buf) > 3 ? "..." : ""));
720 return(mdoc_vwarn(m, ln, 0, "unknown macro: %s%s",
721 buf, strlen(buf) > 3 ? "..." : ""));
722 }
723
724
725 /*
726 * Parse a macro line, that is, a line beginning with the control
727 * character.
728 */
729 int
730 mdoc_pmacro(struct mdoc *m, int ln, char *buf)
731 {
732 enum mdoct tok;
733 int i, j, sv;
734 char mac[5];
735
736 /* Empty lines are ignored. */
737
738 if ('\0' == buf[1])
739 return(1);
740
741 i = 1;
742
743 /* Accept whitespace after the initial control char. */
744
745 if (' ' == buf[i]) {
746 i++;
747 while (buf[i] && ' ' == buf[i])
748 i++;
749 if ('\0' == buf[i])
750 return(1);
751 }
752
753 sv = i;
754
755 /* Copy the first word into a nil-terminated buffer. */
756
757 for (j = 0; j < 4; j++, i++) {
758 if ('\0' == (mac[j] = buf[i]))
759 break;
760 else if (' ' == buf[i])
761 break;
762
763 /* Check for invalid characters. */
764
765 if (isgraph((u_char)buf[i]))
766 continue;
767 return(mdoc_perr(m, ln, i, EPRINT));
768 }
769
770 mac[j] = 0;
771
772 if (j == 4 || j < 2) {
773 if ( ! macrowarn(m, ln, mac))
774 goto err;
775 return(1);
776 }
777
778 if (MDOC_MAX == (tok = mdoc_hash_find(mac))) {
779 if ( ! macrowarn(m, ln, mac))
780 goto err;
781 return(1);
782 }
783
784 /* The macro is sane. Jump to the next word. */
785
786 while (buf[i] && ' ' == buf[i])
787 i++;
788
789 /*
790 * Trailing whitespace. Note that tabs are allowed to be passed
791 * into the parser as "text", so we only warn about spaces here.
792 */
793
794 if ('\0' == buf[i] && ' ' == buf[i - 1])
795 if ( ! mdoc_pwarn(m, ln, i - 1, ETAILWS))
796 goto err;
797
798 /*
799 * Begin recursive parse sequence. Since we're at the start of
800 * the line, we don't need to do callable/parseable checks.
801 */
802 if ( ! mdoc_macro(m, tok, ln, sv, &i, buf))
803 goto err;
804
805 return(1);
806
807 err: /* Error out. */
808
809 m->flags |= MDOC_HALT;
810 return(0);
811 }
812
813