]> git.cameronkatri.com Git - mandoc.git/blob - mdoc.c
mention requests and macros in more messages
[mandoc.git] / mdoc.c
1 /* $Id: mdoc.c,v 1.222 2014/08/01 17:27:44 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <sys/types.h>
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31
32 #include "mdoc.h"
33 #include "mandoc.h"
34 #include "mandoc_aux.h"
35 #include "libmdoc.h"
36 #include "libmandoc.h"
37
38 const char *const __mdoc_macronames[MDOC_MAX + 1] = {
39 "Ap", "Dd", "Dt", "Os",
40 "Sh", "Ss", "Pp", "D1",
41 "Dl", "Bd", "Ed", "Bl",
42 "El", "It", "Ad", "An",
43 "Ar", "Cd", "Cm", "Dv",
44 "Er", "Ev", "Ex", "Fa",
45 "Fd", "Fl", "Fn", "Ft",
46 "Ic", "In", "Li", "Nd",
47 "Nm", "Op", "Ot", "Pa",
48 "Rv", "St", "Va", "Vt",
49 "Xr", "%A", "%B", "%D",
50 "%I", "%J", "%N", "%O",
51 "%P", "%R", "%T", "%V",
52 "Ac", "Ao", "Aq", "At",
53 "Bc", "Bf", "Bo", "Bq",
54 "Bsx", "Bx", "Db", "Dc",
55 "Do", "Dq", "Ec", "Ef",
56 "Em", "Eo", "Fx", "Ms",
57 "No", "Ns", "Nx", "Ox",
58 "Pc", "Pf", "Po", "Pq",
59 "Qc", "Ql", "Qo", "Qq",
60 "Re", "Rs", "Sc", "So",
61 "Sq", "Sm", "Sx", "Sy",
62 "Tn", "Ux", "Xc", "Xo",
63 "Fo", "Fc", "Oo", "Oc",
64 "Bk", "Ek", "Bt", "Hf",
65 "Fr", "Ud", "Lb", "Lp",
66 "Lk", "Mt", "Brq", "Bro",
67 "Brc", "%C", "Es", "En",
68 "Dx", "%Q", "br", "sp",
69 "%U", "Ta", "ll", "text",
70 };
71
72 const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
73 "split", "nosplit", "ragged",
74 "unfilled", "literal", "file",
75 "offset", "bullet", "dash",
76 "hyphen", "item", "enum",
77 "tag", "diag", "hang",
78 "ohang", "inset", "column",
79 "width", "compact", "std",
80 "filled", "words", "emphasis",
81 "symbolic", "nested", "centered"
82 };
83
84 const char * const *mdoc_macronames = __mdoc_macronames;
85 const char * const *mdoc_argnames = __mdoc_argnames;
86
87 static void mdoc_node_free(struct mdoc_node *);
88 static void mdoc_node_unlink(struct mdoc *,
89 struct mdoc_node *);
90 static void mdoc_free1(struct mdoc *);
91 static void mdoc_alloc1(struct mdoc *);
92 static struct mdoc_node *node_alloc(struct mdoc *, int, int,
93 enum mdoct, enum mdoc_type);
94 static int node_append(struct mdoc *,
95 struct mdoc_node *);
96 #if 0
97 static int mdoc_preptext(struct mdoc *, int, char *, int);
98 #endif
99 static int mdoc_ptext(struct mdoc *, int, char *, int);
100 static int mdoc_pmacro(struct mdoc *, int, char *, int);
101
102
103 const struct mdoc_node *
104 mdoc_node(const struct mdoc *mdoc)
105 {
106
107 return(mdoc->first);
108 }
109
110 const struct mdoc_meta *
111 mdoc_meta(const struct mdoc *mdoc)
112 {
113
114 return(&mdoc->meta);
115 }
116
117 /*
118 * Frees volatile resources (parse tree, meta-data, fields).
119 */
120 static void
121 mdoc_free1(struct mdoc *mdoc)
122 {
123
124 if (mdoc->first)
125 mdoc_node_delete(mdoc, mdoc->first);
126 if (mdoc->meta.title)
127 free(mdoc->meta.title);
128 if (mdoc->meta.os)
129 free(mdoc->meta.os);
130 if (mdoc->meta.name)
131 free(mdoc->meta.name);
132 if (mdoc->meta.arch)
133 free(mdoc->meta.arch);
134 if (mdoc->meta.vol)
135 free(mdoc->meta.vol);
136 if (mdoc->meta.msec)
137 free(mdoc->meta.msec);
138 if (mdoc->meta.date)
139 free(mdoc->meta.date);
140 }
141
142 /*
143 * Allocate all volatile resources (parse tree, meta-data, fields).
144 */
145 static void
146 mdoc_alloc1(struct mdoc *mdoc)
147 {
148
149 memset(&mdoc->meta, 0, sizeof(struct mdoc_meta));
150 mdoc->flags = 0;
151 mdoc->lastnamed = mdoc->lastsec = SEC_NONE;
152 mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node));
153 mdoc->first = mdoc->last;
154 mdoc->last->type = MDOC_ROOT;
155 mdoc->last->tok = MDOC_MAX;
156 mdoc->next = MDOC_NEXT_CHILD;
157 }
158
159 /*
160 * Free up volatile resources (see mdoc_free1()) then re-initialises the
161 * data with mdoc_alloc1(). After invocation, parse data has been reset
162 * and the parser is ready for re-invocation on a new tree; however,
163 * cross-parse non-volatile data is kept intact.
164 */
165 void
166 mdoc_reset(struct mdoc *mdoc)
167 {
168
169 mdoc_free1(mdoc);
170 mdoc_alloc1(mdoc);
171 }
172
173 /*
174 * Completely free up all volatile and non-volatile parse resources.
175 * After invocation, the pointer is no longer usable.
176 */
177 void
178 mdoc_free(struct mdoc *mdoc)
179 {
180
181 mdoc_free1(mdoc);
182 free(mdoc);
183 }
184
185 /*
186 * Allocate volatile and non-volatile parse resources.
187 */
188 struct mdoc *
189 mdoc_alloc(struct roff *roff, struct mparse *parse,
190 const char *defos, int quick)
191 {
192 struct mdoc *p;
193
194 p = mandoc_calloc(1, sizeof(struct mdoc));
195
196 p->parse = parse;
197 p->defos = defos;
198 p->quick = quick;
199 p->roff = roff;
200
201 mdoc_hash_init();
202 mdoc_alloc1(p);
203 return(p);
204 }
205
206 int
207 mdoc_endparse(struct mdoc *mdoc)
208 {
209
210 return(mdoc_macroend(mdoc));
211 }
212
213 int
214 mdoc_addeqn(struct mdoc *mdoc, const struct eqn *ep)
215 {
216 struct mdoc_node *n;
217
218 n = node_alloc(mdoc, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN);
219 n->eqn = ep;
220
221 if ( ! node_append(mdoc, n))
222 return(0);
223
224 mdoc->next = MDOC_NEXT_SIBLING;
225 return(1);
226 }
227
228 int
229 mdoc_addspan(struct mdoc *mdoc, const struct tbl_span *sp)
230 {
231 struct mdoc_node *n;
232
233 n = node_alloc(mdoc, sp->line, 0, MDOC_MAX, MDOC_TBL);
234 n->span = sp;
235
236 if ( ! node_append(mdoc, n))
237 return(0);
238
239 mdoc->next = MDOC_NEXT_SIBLING;
240 return(1);
241 }
242
243 /*
244 * Main parse routine. Parses a single line -- really just hands off to
245 * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()).
246 */
247 int
248 mdoc_parseln(struct mdoc *mdoc, int ln, char *buf, int offs)
249 {
250
251 mdoc->flags |= MDOC_NEWLINE;
252
253 /*
254 * Let the roff nS register switch SYNOPSIS mode early,
255 * such that the parser knows at all times
256 * whether this mode is on or off.
257 * Note that this mode is also switched by the Sh macro.
258 */
259 if (roff_getreg(mdoc->roff, "nS"))
260 mdoc->flags |= MDOC_SYNOPSIS;
261 else
262 mdoc->flags &= ~MDOC_SYNOPSIS;
263
264 return(roff_getcontrol(mdoc->roff, buf, &offs) ?
265 mdoc_pmacro(mdoc, ln, buf, offs) :
266 mdoc_ptext(mdoc, ln, buf, offs));
267 }
268
269 int
270 mdoc_macro(MACRO_PROT_ARGS)
271 {
272 assert(tok < MDOC_MAX);
273
274 /* If we're in the body, deny prologue calls. */
275
276 if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
277 MDOC_PBODY & mdoc->flags) {
278 mandoc_vmsg(MANDOCERR_PROLOG_ONLY, mdoc->parse,
279 line, ppos, "%s", mdoc_macronames[tok]);
280 return(1);
281 }
282
283 /* If we're in the prologue, deny "body" macros. */
284
285 if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
286 ! (MDOC_PBODY & mdoc->flags)) {
287 mandoc_vmsg(MANDOCERR_PROLOG_BAD, mdoc->parse,
288 line, ppos, "%s", mdoc_macronames[tok]);
289 if (NULL == mdoc->meta.msec)
290 mdoc->meta.msec = mandoc_strdup("1");
291 if (NULL == mdoc->meta.title)
292 mdoc->meta.title = mandoc_strdup("UNKNOWN");
293 if (NULL == mdoc->meta.vol)
294 mdoc->meta.vol = mandoc_strdup("LOCAL");
295 if (NULL == mdoc->meta.os)
296 mdoc->meta.os = mandoc_strdup("LOCAL");
297 if (NULL == mdoc->meta.date)
298 mdoc->meta.date = mandoc_normdate
299 (mdoc->parse, NULL, line, ppos);
300 mdoc->flags |= MDOC_PBODY;
301 }
302
303 return((*mdoc_macros[tok].fp)(mdoc, tok, line, ppos, pos, buf));
304 }
305
306
307 static int
308 node_append(struct mdoc *mdoc, struct mdoc_node *p)
309 {
310
311 assert(mdoc->last);
312 assert(mdoc->first);
313 assert(MDOC_ROOT != p->type);
314
315 switch (mdoc->next) {
316 case MDOC_NEXT_SIBLING:
317 mdoc->last->next = p;
318 p->prev = mdoc->last;
319 p->parent = mdoc->last->parent;
320 break;
321 case MDOC_NEXT_CHILD:
322 mdoc->last->child = p;
323 p->parent = mdoc->last;
324 break;
325 default:
326 abort();
327 /* NOTREACHED */
328 }
329
330 p->parent->nchild++;
331
332 /*
333 * Copy over the normalised-data pointer of our parent. Not
334 * everybody has one, but copying a null pointer is fine.
335 */
336
337 switch (p->type) {
338 case MDOC_BODY:
339 if (ENDBODY_NOT != p->end)
340 break;
341 /* FALLTHROUGH */
342 case MDOC_TAIL:
343 /* FALLTHROUGH */
344 case MDOC_HEAD:
345 p->norm = p->parent->norm;
346 break;
347 default:
348 break;
349 }
350
351 if ( ! mdoc_valid_pre(mdoc, p))
352 return(0);
353
354 switch (p->type) {
355 case MDOC_HEAD:
356 assert(MDOC_BLOCK == p->parent->type);
357 p->parent->head = p;
358 break;
359 case MDOC_TAIL:
360 assert(MDOC_BLOCK == p->parent->type);
361 p->parent->tail = p;
362 break;
363 case MDOC_BODY:
364 if (p->end)
365 break;
366 assert(MDOC_BLOCK == p->parent->type);
367 p->parent->body = p;
368 break;
369 default:
370 break;
371 }
372
373 mdoc->last = p;
374
375 switch (p->type) {
376 case MDOC_TBL:
377 /* FALLTHROUGH */
378 case MDOC_TEXT:
379 if ( ! mdoc_valid_post(mdoc))
380 return(0);
381 break;
382 default:
383 break;
384 }
385
386 return(1);
387 }
388
389 static struct mdoc_node *
390 node_alloc(struct mdoc *mdoc, int line, int pos,
391 enum mdoct tok, enum mdoc_type type)
392 {
393 struct mdoc_node *p;
394
395 p = mandoc_calloc(1, sizeof(struct mdoc_node));
396 p->sec = mdoc->lastsec;
397 p->line = line;
398 p->pos = pos;
399 p->lastline = line;
400 p->tok = tok;
401 p->type = type;
402
403 /* Flag analysis. */
404
405 if (MDOC_SYNOPSIS & mdoc->flags)
406 p->flags |= MDOC_SYNPRETTY;
407 else
408 p->flags &= ~MDOC_SYNPRETTY;
409 if (MDOC_NEWLINE & mdoc->flags)
410 p->flags |= MDOC_LINE;
411 mdoc->flags &= ~MDOC_NEWLINE;
412
413 return(p);
414 }
415
416 int
417 mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
418 {
419 struct mdoc_node *p;
420
421 p = node_alloc(mdoc, line, pos, tok, MDOC_TAIL);
422 if ( ! node_append(mdoc, p))
423 return(0);
424 mdoc->next = MDOC_NEXT_CHILD;
425 return(1);
426 }
427
428 int
429 mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
430 {
431 struct mdoc_node *p;
432
433 assert(mdoc->first);
434 assert(mdoc->last);
435
436 p = node_alloc(mdoc, line, pos, tok, MDOC_HEAD);
437 if ( ! node_append(mdoc, p))
438 return(0);
439 mdoc->next = MDOC_NEXT_CHILD;
440 return(1);
441 }
442
443 int
444 mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
445 {
446 struct mdoc_node *p;
447
448 p = node_alloc(mdoc, line, pos, tok, MDOC_BODY);
449 if ( ! node_append(mdoc, p))
450 return(0);
451 mdoc->next = MDOC_NEXT_CHILD;
452 return(1);
453 }
454
455 int
456 mdoc_endbody_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok,
457 struct mdoc_node *body, enum mdoc_endbody end)
458 {
459 struct mdoc_node *p;
460
461 p = node_alloc(mdoc, line, pos, tok, MDOC_BODY);
462 p->pending = body;
463 p->norm = body->norm;
464 p->end = end;
465 if ( ! node_append(mdoc, p))
466 return(0);
467 mdoc->next = MDOC_NEXT_SIBLING;
468 return(1);
469 }
470
471 int
472 mdoc_block_alloc(struct mdoc *mdoc, int line, int pos,
473 enum mdoct tok, struct mdoc_arg *args)
474 {
475 struct mdoc_node *p;
476
477 p = node_alloc(mdoc, line, pos, tok, MDOC_BLOCK);
478 p->args = args;
479 if (p->args)
480 (args->refcnt)++;
481
482 switch (tok) {
483 case MDOC_Bd:
484 /* FALLTHROUGH */
485 case MDOC_Bf:
486 /* FALLTHROUGH */
487 case MDOC_Bl:
488 /* FALLTHROUGH */
489 case MDOC_En:
490 /* FALLTHROUGH */
491 case MDOC_Rs:
492 p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
493 break;
494 default:
495 break;
496 }
497
498 if ( ! node_append(mdoc, p))
499 return(0);
500 mdoc->next = MDOC_NEXT_CHILD;
501 return(1);
502 }
503
504 int
505 mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos,
506 enum mdoct tok, struct mdoc_arg *args)
507 {
508 struct mdoc_node *p;
509
510 p = node_alloc(mdoc, line, pos, tok, MDOC_ELEM);
511 p->args = args;
512 if (p->args)
513 (args->refcnt)++;
514
515 switch (tok) {
516 case MDOC_An:
517 p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
518 break;
519 default:
520 break;
521 }
522
523 if ( ! node_append(mdoc, p))
524 return(0);
525 mdoc->next = MDOC_NEXT_CHILD;
526 return(1);
527 }
528
529 int
530 mdoc_word_alloc(struct mdoc *mdoc, int line, int pos, const char *p)
531 {
532 struct mdoc_node *n;
533
534 n = node_alloc(mdoc, line, pos, MDOC_MAX, MDOC_TEXT);
535 n->string = roff_strdup(mdoc->roff, p);
536
537 if ( ! node_append(mdoc, n))
538 return(0);
539
540 mdoc->next = MDOC_NEXT_SIBLING;
541 return(1);
542 }
543
544 void
545 mdoc_word_append(struct mdoc *mdoc, const char *p)
546 {
547 struct mdoc_node *n;
548 char *addstr, *newstr;
549
550 n = mdoc->last;
551 addstr = roff_strdup(mdoc->roff, p);
552 mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
553 free(addstr);
554 free(n->string);
555 n->string = newstr;
556 mdoc->next = MDOC_NEXT_SIBLING;
557 }
558
559 static void
560 mdoc_node_free(struct mdoc_node *p)
561 {
562
563 if (MDOC_BLOCK == p->type || MDOC_ELEM == p->type)
564 free(p->norm);
565 if (p->string)
566 free(p->string);
567 if (p->args)
568 mdoc_argv_free(p->args);
569 free(p);
570 }
571
572 static void
573 mdoc_node_unlink(struct mdoc *mdoc, struct mdoc_node *n)
574 {
575
576 /* Adjust siblings. */
577
578 if (n->prev)
579 n->prev->next = n->next;
580 if (n->next)
581 n->next->prev = n->prev;
582
583 /* Adjust parent. */
584
585 if (n->parent) {
586 n->parent->nchild--;
587 if (n->parent->child == n)
588 n->parent->child = n->prev ? n->prev : n->next;
589 if (n->parent->last == n)
590 n->parent->last = n->prev ? n->prev : NULL;
591 }
592
593 /* Adjust parse point, if applicable. */
594
595 if (mdoc && mdoc->last == n) {
596 if (n->prev) {
597 mdoc->last = n->prev;
598 mdoc->next = MDOC_NEXT_SIBLING;
599 } else {
600 mdoc->last = n->parent;
601 mdoc->next = MDOC_NEXT_CHILD;
602 }
603 }
604
605 if (mdoc && mdoc->first == n)
606 mdoc->first = NULL;
607 }
608
609 void
610 mdoc_node_delete(struct mdoc *mdoc, struct mdoc_node *p)
611 {
612
613 while (p->child) {
614 assert(p->nchild);
615 mdoc_node_delete(mdoc, p->child);
616 }
617 assert(0 == p->nchild);
618
619 mdoc_node_unlink(mdoc, p);
620 mdoc_node_free(p);
621 }
622
623 int
624 mdoc_node_relink(struct mdoc *mdoc, struct mdoc_node *p)
625 {
626
627 mdoc_node_unlink(mdoc, p);
628 return(node_append(mdoc, p));
629 }
630
631 #if 0
632 /*
633 * Pre-treat a text line.
634 * Text lines can consist of equations, which must be handled apart from
635 * the regular text.
636 * Thus, use this function to step through a line checking if it has any
637 * equations embedded in it.
638 * This must handle multiple equations AND equations that do not end at
639 * the end-of-line, i.e., will re-enter in the next roff parse.
640 */
641 static int
642 mdoc_preptext(struct mdoc *mdoc, int line, char *buf, int offs)
643 {
644 char *start, *end;
645 char delim;
646
647 while ('\0' != buf[offs]) {
648 /* Mark starting position if eqn is set. */
649 start = NULL;
650 if ('\0' != (delim = roff_eqndelim(mdoc->roff)))
651 if (NULL != (start = strchr(buf + offs, delim)))
652 *start++ = '\0';
653
654 /* Parse text as normal. */
655 if ( ! mdoc_ptext(mdoc, line, buf, offs))
656 return(0);
657
658 /* Continue only if an equation exists. */
659 if (NULL == start)
660 break;
661
662 /* Read past the end of the equation. */
663 offs += start - (buf + offs);
664 assert(start == &buf[offs]);
665 if (NULL != (end = strchr(buf + offs, delim))) {
666 *end++ = '\0';
667 while (' ' == *end)
668 end++;
669 }
670
671 /* Parse the equation itself. */
672 roff_openeqn(mdoc->roff, NULL, line, offs, buf);
673
674 /* Process a finished equation? */
675 if (roff_closeeqn(mdoc->roff))
676 if ( ! mdoc_addeqn(mdoc, roff_eqn(mdoc->roff)))
677 return(0);
678 offs += (end - (buf + offs));
679 }
680
681 return(1);
682 }
683 #endif
684
685 /*
686 * Parse free-form text, that is, a line that does not begin with the
687 * control character.
688 */
689 static int
690 mdoc_ptext(struct mdoc *mdoc, int line, char *buf, int offs)
691 {
692 char *c, *ws, *end;
693 struct mdoc_node *n;
694
695 assert(mdoc->last);
696 n = mdoc->last;
697
698 /*
699 * Divert directly to list processing if we're encountering a
700 * columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry
701 * (a MDOC_BODY means it's already open, in which case we should
702 * process within its context in the normal way).
703 */
704
705 if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
706 LIST_column == n->norm->Bl.type) {
707 /* `Bl' is open without any children. */
708 mdoc->flags |= MDOC_FREECOL;
709 return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf));
710 }
711
712 if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
713 NULL != n->parent &&
714 MDOC_Bl == n->parent->tok &&
715 LIST_column == n->parent->norm->Bl.type) {
716 /* `Bl' has block-level `It' children. */
717 mdoc->flags |= MDOC_FREECOL;
718 return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf));
719 }
720
721 /*
722 * Search for the beginning of unescaped trailing whitespace (ws)
723 * and for the first character not to be output (end).
724 */
725
726 /* FIXME: replace with strcspn(). */
727 ws = NULL;
728 for (c = end = buf + offs; *c; c++) {
729 switch (*c) {
730 case ' ':
731 if (NULL == ws)
732 ws = c;
733 continue;
734 case '\t':
735 /*
736 * Always warn about trailing tabs,
737 * even outside literal context,
738 * where they should be put on the next line.
739 */
740 if (NULL == ws)
741 ws = c;
742 /*
743 * Strip trailing tabs in literal context only;
744 * outside, they affect the next line.
745 */
746 if (MDOC_LITERAL & mdoc->flags)
747 continue;
748 break;
749 case '\\':
750 /* Skip the escaped character, too, if any. */
751 if (c[1])
752 c++;
753 /* FALLTHROUGH */
754 default:
755 ws = NULL;
756 break;
757 }
758 end = c + 1;
759 }
760 *end = '\0';
761
762 if (ws)
763 mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
764 line, (int)(ws-buf), NULL);
765
766 if ('\0' == buf[offs] && ! (MDOC_LITERAL & mdoc->flags)) {
767 mandoc_msg(MANDOCERR_FI_BLANK, mdoc->parse,
768 line, (int)(c - buf), NULL);
769
770 /*
771 * Insert a `sp' in the case of a blank line. Technically,
772 * blank lines aren't allowed, but enough manuals assume this
773 * behaviour that we want to work around it.
774 */
775 if ( ! mdoc_elem_alloc(mdoc, line, offs, MDOC_sp, NULL))
776 return(0);
777
778 mdoc->next = MDOC_NEXT_SIBLING;
779
780 return(mdoc_valid_post(mdoc));
781 }
782
783 if ( ! mdoc_word_alloc(mdoc, line, offs, buf+offs))
784 return(0);
785
786 if (MDOC_LITERAL & mdoc->flags)
787 return(1);
788
789 /*
790 * End-of-sentence check. If the last character is an unescaped
791 * EOS character, then flag the node as being the end of a
792 * sentence. The front-end will know how to interpret this.
793 */
794
795 assert(buf < end);
796
797 if (mandoc_eos(buf+offs, (size_t)(end-buf-offs)))
798 mdoc->last->flags |= MDOC_EOS;
799
800 return(1);
801 }
802
803 /*
804 * Parse a macro line, that is, a line beginning with the control
805 * character.
806 */
807 static int
808 mdoc_pmacro(struct mdoc *mdoc, int ln, char *buf, int offs)
809 {
810 enum mdoct tok;
811 int i, sv;
812 char mac[5];
813 struct mdoc_node *n;
814
815 /* Empty post-control lines are ignored. */
816
817 if ('"' == buf[offs]) {
818 mandoc_msg(MANDOCERR_COMMENT_BAD, mdoc->parse,
819 ln, offs, NULL);
820 return(1);
821 } else if ('\0' == buf[offs])
822 return(1);
823
824 sv = offs;
825
826 /*
827 * Copy the first word into a nil-terminated buffer.
828 * Stop copying when a tab, space, or eoln is encountered.
829 */
830
831 i = 0;
832 while (i < 4 && '\0' != buf[offs] && ' ' != buf[offs] &&
833 '\t' != buf[offs])
834 mac[i++] = buf[offs++];
835
836 mac[i] = '\0';
837
838 tok = (i > 1 && i < 4) ? mdoc_hash_find(mac) : MDOC_MAX;
839
840 if (MDOC_MAX == tok) {
841 mandoc_msg(MANDOCERR_MACRO, mdoc->parse,
842 ln, sv, buf + sv - 1);
843 return(1);
844 }
845
846 /* Disregard the first trailing tab, if applicable. */
847
848 if ('\t' == buf[offs])
849 offs++;
850
851 /* Jump to the next non-whitespace word. */
852
853 while (buf[offs] && ' ' == buf[offs])
854 offs++;
855
856 /*
857 * Trailing whitespace. Note that tabs are allowed to be passed
858 * into the parser as "text", so we only warn about spaces here.
859 */
860
861 if ('\0' == buf[offs] && ' ' == buf[offs - 1])
862 mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse,
863 ln, offs - 1, NULL);
864
865 /*
866 * If an initial macro or a list invocation, divert directly
867 * into macro processing.
868 */
869
870 if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok)
871 return(mdoc_macro(mdoc, tok, ln, sv, &offs, buf));
872
873 n = mdoc->last;
874 assert(mdoc->last);
875
876 /*
877 * If the first macro of a `Bl -column', open an `It' block
878 * context around the parsed macro.
879 */
880
881 if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
882 LIST_column == n->norm->Bl.type) {
883 mdoc->flags |= MDOC_FREECOL;
884 return(mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf));
885 }
886
887 /*
888 * If we're following a block-level `It' within a `Bl -column'
889 * context (perhaps opened in the above block or in ptext()),
890 * then open an `It' block context around the parsed macro.
891 */
892
893 if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
894 NULL != n->parent &&
895 MDOC_Bl == n->parent->tok &&
896 LIST_column == n->parent->norm->Bl.type) {
897 mdoc->flags |= MDOC_FREECOL;
898 return(mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf));
899 }
900
901 /* Normal processing of a macro. */
902
903 if ( ! mdoc_macro(mdoc, tok, ln, sv, &offs, buf))
904 return(0);
905
906 /* In quick mode (for mandocdb), abort after the NAME section. */
907
908 if (mdoc->quick && MDOC_Sh == tok &&
909 SEC_NAME != mdoc->last->sec)
910 return(2);
911
912 return(1);
913 }
914
915 enum mdelim
916 mdoc_isdelim(const char *p)
917 {
918
919 if ('\0' == p[0])
920 return(DELIM_NONE);
921
922 if ('\0' == p[1])
923 switch (p[0]) {
924 case '(':
925 /* FALLTHROUGH */
926 case '[':
927 return(DELIM_OPEN);
928 case '|':
929 return(DELIM_MIDDLE);
930 case '.':
931 /* FALLTHROUGH */
932 case ',':
933 /* FALLTHROUGH */
934 case ';':
935 /* FALLTHROUGH */
936 case ':':
937 /* FALLTHROUGH */
938 case '?':
939 /* FALLTHROUGH */
940 case '!':
941 /* FALLTHROUGH */
942 case ')':
943 /* FALLTHROUGH */
944 case ']':
945 return(DELIM_CLOSE);
946 default:
947 return(DELIM_NONE);
948 }
949
950 if ('\\' != p[0])
951 return(DELIM_NONE);
952
953 if (0 == strcmp(p + 1, "."))
954 return(DELIM_CLOSE);
955 if (0 == strcmp(p + 1, "fR|\\fP"))
956 return(DELIM_MIDDLE);
957
958 return(DELIM_NONE);
959 }
960
961 void
962 mdoc_deroff(char **dest, const struct mdoc_node *n)
963 {
964 char *cp;
965 size_t sz;
966
967 if (MDOC_TEXT != n->type) {
968 for (n = n->child; n; n = n->next)
969 mdoc_deroff(dest, n);
970 return;
971 }
972
973 /* Skip leading whitespace. */
974
975 for (cp = n->string; '\0' != *cp; cp++)
976 if (0 == isspace((unsigned char)*cp))
977 break;
978
979 /* Skip trailing whitespace. */
980
981 for (sz = strlen(cp); sz; sz--)
982 if (0 == isspace((unsigned char)cp[sz-1]))
983 break;
984
985 /* Skip empty strings. */
986
987 if (0 == sz)
988 return;
989
990 if (NULL == *dest) {
991 *dest = mandoc_strndup(cp, sz);
992 return;
993 }
994
995 mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
996 free(*dest);
997 *dest = cp;
998 }