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