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