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