1521dda2e1b1845de465be9ebb61d29935edee1d
[dragonfly.git] / usr.bin / mandoc / mdoc_action.c
1 /*      $Id: mdoc_action.c,v 1.24 2009/10/27 21:40:07 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #ifndef OSNAME
18 #include <sys/utsname.h>
19 #endif
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27
28 #include "libmdoc.h"
29
30 #define POST_ARGS struct mdoc *m, struct mdoc_node *n
31 #define PRE_ARGS  struct mdoc *m, const struct mdoc_node *n
32
33 struct  actions {
34         int     (*pre)(PRE_ARGS);
35         int     (*post)(POST_ARGS);
36 };
37
38 static  int       concat(struct mdoc *,
39                         const struct mdoc_node *,
40                         char *, size_t);
41 static  inline int order_rs(int);
42
43 static  int       post_ar(POST_ARGS);
44 static  int       post_at(POST_ARGS);
45 static  int       post_bl(POST_ARGS);
46 static  int       post_bl_head(POST_ARGS);
47 static  int       post_bl_tagwidth(POST_ARGS);
48 static  int       post_bl_width(POST_ARGS);
49 static  int       post_dd(POST_ARGS);
50 static  int       post_display(POST_ARGS);
51 static  int       post_dt(POST_ARGS);
52 static  int       post_lb(POST_ARGS);
53 static  int       post_nm(POST_ARGS);
54 static  int       post_os(POST_ARGS);
55 static  int       post_prol(POST_ARGS);
56 static  int       post_rs(POST_ARGS);
57 static  int       post_sh(POST_ARGS);
58 static  int       post_st(POST_ARGS);
59 static  int       post_std(POST_ARGS);
60 static  int       post_tilde(POST_ARGS);
61
62 static  int       pre_bd(PRE_ARGS);
63 static  int       pre_bl(PRE_ARGS);
64 static  int       pre_dl(PRE_ARGS);
65 static  int       pre_offset(PRE_ARGS);
66
67 static  const struct actions mdoc_actions[MDOC_MAX] = {
68         { NULL, NULL }, /* Ap */
69         { NULL, post_dd }, /* Dd */
70         { NULL, post_dt }, /* Dt */
71         { NULL, post_os }, /* Os */
72         { NULL, post_sh }, /* Sh */
73         { NULL, NULL }, /* Ss */
74         { NULL, NULL }, /* Pp */
75         { NULL, NULL }, /* D1 */
76         { pre_dl, post_display }, /* Dl */
77         { pre_bd, post_display }, /* Bd */
78         { NULL, NULL }, /* Ed */
79         { pre_bl, post_bl }, /* Bl */
80         { NULL, NULL }, /* El */
81         { NULL, NULL }, /* It */
82         { NULL, NULL }, /* Ad */
83         { NULL, NULL }, /* An */
84         { NULL, post_ar }, /* Ar */
85         { NULL, NULL }, /* Cd */
86         { NULL, NULL }, /* Cm */
87         { NULL, NULL }, /* Dv */
88         { NULL, NULL }, /* Er */
89         { NULL, NULL }, /* Ev */
90         { NULL, post_std }, /* Ex */
91         { NULL, NULL }, /* Fa */
92         { NULL, NULL }, /* Fd */
93         { NULL, NULL }, /* Fl */
94         { NULL, NULL }, /* Fn */
95         { NULL, NULL }, /* Ft */
96         { NULL, NULL }, /* Ic */
97         { NULL, NULL }, /* In */
98         { NULL, NULL }, /* Li */
99         { NULL, NULL }, /* Nd */
100         { NULL, post_nm }, /* Nm */
101         { NULL, NULL }, /* Op */
102         { NULL, NULL }, /* Ot */
103         { NULL, post_tilde }, /* Pa */
104         { NULL, post_std }, /* Rv */
105         { NULL, post_st }, /* St */
106         { NULL, NULL }, /* Va */
107         { NULL, NULL }, /* Vt */
108         { NULL, NULL }, /* Xr */
109         { NULL, NULL }, /* %A */
110         { NULL, NULL }, /* %B */
111         { NULL, NULL }, /* %D */
112         { NULL, NULL }, /* %I */
113         { NULL, NULL }, /* %J */
114         { NULL, NULL }, /* %N */
115         { NULL, NULL }, /* %O */
116         { NULL, NULL }, /* %P */
117         { NULL, NULL }, /* %R */
118         { NULL, NULL }, /* %T */
119         { NULL, NULL }, /* %V */
120         { NULL, NULL }, /* Ac */
121         { NULL, NULL }, /* Ao */
122         { NULL, NULL }, /* Aq */
123         { NULL, post_at }, /* At */
124         { NULL, NULL }, /* Bc */
125         { NULL, NULL }, /* Bf */
126         { NULL, NULL }, /* Bo */
127         { NULL, NULL }, /* Bq */
128         { NULL, NULL }, /* Bsx */
129         { NULL, NULL }, /* Bx */
130         { NULL, NULL }, /* Db */
131         { NULL, NULL }, /* Dc */
132         { NULL, NULL }, /* Do */
133         { NULL, NULL }, /* Dq */
134         { NULL, NULL }, /* Ec */
135         { NULL, NULL }, /* Ef */
136         { NULL, NULL }, /* Em */
137         { NULL, NULL }, /* Eo */
138         { NULL, NULL }, /* Fx */
139         { NULL, NULL }, /* Ms */
140         { NULL, NULL }, /* No */
141         { NULL, NULL }, /* Ns */
142         { NULL, NULL }, /* Nx */
143         { NULL, NULL }, /* Ox */
144         { NULL, NULL }, /* Pc */
145         { NULL, NULL }, /* Pf */
146         { NULL, NULL }, /* Po */
147         { NULL, NULL }, /* Pq */
148         { NULL, NULL }, /* Qc */
149         { NULL, NULL }, /* Ql */
150         { NULL, NULL }, /* Qo */
151         { NULL, NULL }, /* Qq */
152         { NULL, NULL }, /* Re */
153         { NULL, post_rs }, /* Rs */
154         { NULL, NULL }, /* Sc */
155         { NULL, NULL }, /* So */
156         { NULL, NULL }, /* Sq */
157         { NULL, NULL }, /* Sm */
158         { NULL, NULL }, /* Sx */
159         { NULL, NULL }, /* Sy */
160         { NULL, NULL }, /* Tn */
161         { NULL, NULL }, /* Ux */
162         { NULL, NULL }, /* Xc */
163         { NULL, NULL }, /* Xo */
164         { NULL, NULL }, /* Fo */
165         { NULL, NULL }, /* Fc */
166         { NULL, NULL }, /* Oo */
167         { NULL, NULL }, /* Oc */
168         { NULL, NULL }, /* Bk */
169         { NULL, NULL }, /* Ek */
170         { NULL, NULL }, /* Bt */
171         { NULL, NULL }, /* Hf */
172         { NULL, NULL }, /* Fr */
173         { NULL, NULL }, /* Ud */
174         { NULL, post_lb }, /* Lb */
175         { NULL, NULL }, /* Lp */
176         { NULL, NULL }, /* Lk */
177         { NULL, NULL }, /* Mt */
178         { NULL, NULL }, /* Brq */
179         { NULL, NULL }, /* Bro */
180         { NULL, NULL }, /* Brc */
181         { NULL, NULL }, /* %C */
182         { NULL, NULL }, /* Es */
183         { NULL, NULL }, /* En */
184         { NULL, NULL }, /* Dx */
185         { NULL, NULL }, /* %Q */
186         { NULL, NULL }, /* br */
187         { NULL, NULL }, /* sp */
188         { NULL, NULL }, /* %U */
189 };
190
191 #define RSORD_MAX 14
192
193 static  const int rsord[RSORD_MAX] = {
194         MDOC__A,
195         MDOC__T,
196         MDOC__B,
197         MDOC__I,
198         MDOC__J,
199         MDOC__R,
200         MDOC__N,
201         MDOC__V,
202         MDOC__P,
203         MDOC__Q,
204         MDOC__D,
205         MDOC__O,
206         MDOC__C,
207         MDOC__U
208 };
209
210
211 int
212 mdoc_action_pre(struct mdoc *m, const struct mdoc_node *n)
213 {
214
215         switch (n->type) {
216         case (MDOC_ROOT):
217                 /* FALLTHROUGH */
218         case (MDOC_TEXT):
219                 return(1);
220         default:
221                 break;
222         }
223
224         if (NULL == mdoc_actions[n->tok].pre)
225                 return(1);
226         return((*mdoc_actions[n->tok].pre)(m, n));
227 }
228
229
230 int
231 mdoc_action_post(struct mdoc *m)
232 {
233
234         if (MDOC_ACTED & m->last->flags)
235                 return(1);
236         m->last->flags |= MDOC_ACTED;
237
238         switch (m->last->type) {
239         case (MDOC_TEXT):
240                 /* FALLTHROUGH */
241         case (MDOC_ROOT):
242                 return(1);
243         default:
244                 break;
245         }
246
247         if (NULL == mdoc_actions[m->last->tok].post)
248                 return(1);
249         return((*mdoc_actions[m->last->tok].post)(m, m->last));
250 }
251
252
253 static int
254 concat(struct mdoc *m, const struct mdoc_node *n,
255                 char *buf, size_t sz)
256 {
257
258         for ( ; n; n = n->next) {
259                 assert(MDOC_TEXT == n->type);
260                 if (strlcat(buf, n->string, sz) >= sz)
261                         return(mdoc_nerr(m, n, ETOOLONG));
262                 if (NULL == n->next)
263                         continue;
264                 if (strlcat(buf, " ", sz) >= sz)
265                         return(mdoc_nerr(m, n, ETOOLONG));
266         }
267
268         return(1);
269 }
270
271
272 static int
273 post_std(POST_ARGS)
274 {
275         struct mdoc_node        *nn;
276
277         if (n->child)
278                 return(1);
279
280         nn = n;
281         m->next = MDOC_NEXT_CHILD;
282         assert(m->meta.name);
283         if ( ! mdoc_word_alloc(m, n->line, n->pos, m->meta.name))
284                 return(0);
285         m->last = nn;
286
287         return(1);
288 }
289
290
291 static int
292 post_nm(POST_ARGS)
293 {
294         char             buf[64];
295
296         if (m->meta.name)
297                 return(1);
298
299         buf[0] = 0;
300         if ( ! concat(m, n->child, buf, sizeof(buf)))
301                 return(0);
302         if (NULL == (m->meta.name = strdup(buf)))
303                 return(mdoc_nerr(m, n, EMALLOC));
304
305         return(1);
306 }
307
308
309 static int
310 post_lb(POST_ARGS)
311 {
312         const char      *p;
313         char            *buf;
314         size_t           sz;
315
316         assert(MDOC_TEXT == n->child->type);
317         p = mdoc_a2lib(n->child->string);
318         if (NULL == p) {
319                 sz = strlen(n->child->string) +
320                         2 + strlen("\\(lqlibrary\\(rq");
321                 buf = malloc(sz);
322                 if (NULL == buf)
323                         return(mdoc_nerr(m, n, EMALLOC));
324                 (void)snprintf(buf, sz, "library \\(lq%s\\(rq",
325                                 n->child->string);
326                 free(n->child->string);
327                 n->child->string = buf;
328                 return(1);
329         }
330
331         free(n->child->string);
332         n->child->string = strdup(p);
333         if (NULL == n->child->string)
334                 return(mdoc_nerr(m, n, EMALLOC));
335
336         return(1);
337 }
338
339
340 static int
341 post_st(POST_ARGS)
342 {
343         const char      *p;
344
345         assert(MDOC_TEXT == n->child->type);
346         p = mdoc_a2st(n->child->string);
347         assert(p);
348         free(n->child->string);
349         n->child->string = strdup(p);
350         if (NULL == n->child->string)
351                 return(mdoc_nerr(m, n, EMALLOC));
352
353         return(1);
354 }
355
356
357 static int
358 post_at(POST_ARGS)
359 {
360         struct mdoc_node        *nn;
361         const char              *p;
362
363         if (n->child) {
364                 assert(MDOC_TEXT == n->child->type);
365                 p = mdoc_a2att(n->child->string);
366                 assert(p);
367                 free(n->child->string);
368                 n->child->string = strdup(p);
369                 if (NULL == n->child->string)
370                         return(mdoc_nerr(m, n, EMALLOC));
371                 return(1);
372         }
373
374         nn = n;
375         m->next = MDOC_NEXT_CHILD;
376
377         if ( ! mdoc_word_alloc(m, nn->line, nn->pos, "AT&T UNIX"))
378                 return(0);
379         m->last = nn;
380
381         return(1);
382 }
383
384
385 static int
386 post_sh(POST_ARGS)
387 {
388         enum mdoc_sec    sec;
389         char             buf[64];
390
391         /*
392          * We keep track of the current section /and/ the "named"
393          * section, which is one of the conventional ones, in order to
394          * check ordering.
395          */
396
397         if (MDOC_HEAD != n->type)
398                 return(1);
399
400         buf[0] = 0;
401         if ( ! concat(m, n->child, buf, sizeof(buf)))
402                 return(0);
403         if (SEC_CUSTOM != (sec = mdoc_atosec(buf)))
404                 m->lastnamed = sec;
405
406         switch ((m->lastsec = sec)) {
407         case (SEC_RETURN_VALUES):
408                 /* FALLTHROUGH */
409         case (SEC_ERRORS):
410                 switch (m->meta.msec) {
411                 case (2):
412                         /* FALLTHROUGH */
413                 case (3):
414                         /* FALLTHROUGH */
415                 case (9):
416                         break;
417                 default:
418                         return(mdoc_nwarn(m, n, EBADSEC));
419                 }
420                 break;
421         default:
422                 break;
423         }
424         return(1);
425 }
426
427
428 static int
429 post_dt(POST_ARGS)
430 {
431         struct mdoc_node *nn;
432         const char       *cp;
433         char             *ep;
434         long              lval;
435
436         if (m->meta.title)
437                 free(m->meta.title);
438         if (m->meta.vol)
439                 free(m->meta.vol);
440         if (m->meta.arch)
441                 free(m->meta.arch);
442
443         m->meta.title = m->meta.vol = m->meta.arch = NULL;
444         m->meta.msec = 0;
445
446         /* Handles: `.Dt'
447          *   --> title = unknown, volume = local, msec = 0, arch = NULL
448          */
449
450         if (NULL == (nn = n->child)) {
451                 if (NULL == (m->meta.title = strdup("unknown")))
452                         return(mdoc_nerr(m, n, EMALLOC));
453                 if (NULL == (m->meta.vol = strdup("local")))
454                         return(mdoc_nerr(m, n, EMALLOC));
455                 return(post_prol(m, n));
456         }
457
458         /* Handles: `.Dt TITLE'
459          *   --> title = TITLE, volume = local, msec = 0, arch = NULL
460          */
461
462         if (NULL == (m->meta.title = strdup(nn->string)))
463                 return(mdoc_nerr(m, n, EMALLOC));
464
465         if (NULL == (nn = nn->next)) {
466                 if (NULL == (m->meta.vol = strdup("local")))
467                         return(mdoc_nerr(m, n, EMALLOC));
468                 return(post_prol(m, n));
469         }
470
471         /* Handles: `.Dt TITLE SEC'
472          *   --> title = TITLE, volume = SEC is msec ?
473          *           format(msec) : SEC,
474          *       msec = SEC is msec ? atoi(msec) : 0,
475          *       arch = NULL
476          */
477
478         cp = mdoc_a2msec(nn->string);
479         if (cp) {
480                 if (NULL == (m->meta.vol = strdup(cp)))
481                         return(mdoc_nerr(m, n, EMALLOC));
482                 errno = 0;
483                 lval = strtol(nn->string, &ep, 10);
484                 if (nn->string[0] != '\0' && *ep == '\0')
485                         m->meta.msec = (int)lval;
486         } else if (NULL == (m->meta.vol = strdup(nn->string)))
487                 return(mdoc_nerr(m, n, EMALLOC));
488
489         if (NULL == (nn = nn->next))
490                 return(post_prol(m, n));
491
492         /* Handles: `.Dt TITLE SEC VOL'
493          *   --> title = TITLE, volume = VOL is vol ?
494          *       format(VOL) :
495          *           VOL is arch ? format(arch) :
496          *               VOL
497          */
498
499         cp = mdoc_a2vol(nn->string);
500         if (cp) {
501                 free(m->meta.vol);
502                 if (NULL == (m->meta.vol = strdup(cp)))
503                         return(mdoc_nerr(m, n, EMALLOC));
504         } else {
505                 cp = mdoc_a2arch(nn->string);
506                 if (NULL == cp) {
507                         free(m->meta.vol);
508                         if (NULL == (m->meta.vol = strdup(nn->string)))
509                                 return(mdoc_nerr(m, n, EMALLOC));
510                 } else if (NULL == (m->meta.arch = strdup(cp)))
511                         return(mdoc_nerr(m, n, EMALLOC));
512         }
513
514         /* Ignore any subsequent parameters... */
515
516         return(post_prol(m, n));
517 }
518
519
520 static int
521 post_os(POST_ARGS)
522 {
523         char              buf[64];
524 #ifndef OSNAME
525         struct utsname    utsname;
526 #endif
527
528         /*
529          * Setting OSNAME to be the name of the target operating system,
530          * e.g., "OpenBSD 4.4", will result in the compile-time constant
531          * by supplied instead of the value in uname().
532          */
533
534         if (m->meta.os)
535                 free(m->meta.os);
536
537         buf[0] = 0;
538         if ( ! concat(m, n->child, buf, sizeof(buf)))
539                 return(0);
540
541         if (0 == buf[0]) {
542 #ifdef  OSNAME
543                 if (strlcat(buf, OSNAME, 64) >= 64)
544                         return(mdoc_nerr(m, n, EUTSNAME));
545 #else
546                 if (-1 == uname(&utsname))
547                         return(mdoc_nerr(m, n, EUTSNAME));
548                 if (strlcat(buf, utsname.sysname, 64) >= 64)
549                         return(mdoc_nerr(m, n, ETOOLONG));
550                 if (strlcat(buf, " ", 64) >= 64)
551                         return(mdoc_nerr(m, n, ETOOLONG));
552                 if (strlcat(buf, utsname.release, 64) >= 64)
553                         return(mdoc_nerr(m, n, ETOOLONG));
554 #endif
555         }
556
557         if (NULL == (m->meta.os = strdup(buf)))
558                 return(mdoc_nerr(m, n, EMALLOC));
559
560         return(post_prol(m, n));
561 }
562
563
564 /*
565  * Calculate the -width for a `Bl -tag' list if it hasn't been provided.
566  * Uses the first head macro.
567  */
568 static int
569 post_bl_tagwidth(POST_ARGS)
570 {
571         struct mdoc_node  *nn;
572         int                sz;
573         char               buf[32];
574
575         /*
576          * Use the text width, if a text node, or the default macro
577          * width if a macro.
578          */
579
580         nn = n->body->child;
581         if (nn) {
582                 assert(MDOC_BLOCK == nn->type);
583                 assert(MDOC_It == nn->tok);
584                 nn = nn->head->child;
585         }
586
587         sz = 10; /* Default size. */
588
589         if (nn) {
590                 if (MDOC_TEXT != nn->type) {
591                         if (0 == (sz = (int)mdoc_macro2len(nn->tok)))
592                                 if ( ! mdoc_nwarn(m, n, ENOWIDTH))
593                                         return(0);
594                 } else
595                         sz = (int)strlen(nn->string) + 1;
596         }
597
598         if (-1 == snprintf(buf, sizeof(buf), "%dn", sz))
599                 return(mdoc_nerr(m, n, ENUMFMT));
600
601         /*
602          * We have to dynamically add this to the macro's argument list.
603          * We're guaranteed that a MDOC_Width doesn't already exist.
604          */
605
606         nn = n;
607         assert(nn->args);
608         sz = (int)(nn->args->argc)++;
609
610         nn->args->argv = realloc(nn->args->argv,
611                         nn->args->argc * sizeof(struct mdoc_argv));
612
613         if (NULL == nn->args->argv)
614                 return(mdoc_nerr(m, n, EMALLOC));
615
616         nn->args->argv[sz].arg = MDOC_Width;
617         nn->args->argv[sz].line = n->line;
618         nn->args->argv[sz].pos = n->pos;
619         nn->args->argv[sz].sz = 1;
620         nn->args->argv[sz].value = calloc(1, sizeof(char *));
621
622         if (NULL == nn->args->argv[sz].value)
623                 return(mdoc_nerr(m, n, EMALLOC));
624         if (NULL == (nn->args->argv[sz].value[0] = strdup(buf)))
625                 return(mdoc_nerr(m, n, EMALLOC));
626
627         return(1);
628 }
629
630
631 static int
632 post_bl_width(POST_ARGS)
633 {
634         size_t            width;
635         int               i, tok;
636         char              buf[32];
637         char             *p;
638
639         if (NULL == n->args)
640                 return(1);
641
642         for (i = 0; i < (int)n->args->argc; i++)
643                 if (MDOC_Width == n->args->argv[i].arg)
644                         break;
645
646         if (i == (int)n->args->argc)
647                 return(1);
648         p = n->args->argv[i].value[0];
649
650         /*
651          * If the value to -width is a macro, then we re-write it to be
652          * the macro's width as set in share/tmac/mdoc/doc-common.
653          */
654
655         if (0 == strcmp(p, "Ds"))
656                 width = 6;
657         else if (MDOC_MAX == (tok = mdoc_hash_find(p)))
658                 return(1);
659         else if (0 == (width = mdoc_macro2len(tok)))
660                 return(mdoc_nwarn(m, n, ENOWIDTH));
661
662         /* The value already exists: free and reallocate it. */
663
664         if (-1 == snprintf(buf, sizeof(buf), "%zun", width))
665                 return(mdoc_nerr(m, n, ENUMFMT));
666
667         free(n->args->argv[i].value[0]);
668         n->args->argv[i].value[0] = strdup(buf);
669         if (NULL == n->args->argv[i].value[0])
670                 return(mdoc_nerr(m, n, EMALLOC));
671
672         return(1);
673 }
674
675
676 /* ARGSUSED */
677 static int
678 post_bl_head(POST_ARGS)
679 {
680         int                      i, c;
681         struct mdoc_node        *np, *nn, *nnp;
682
683         if (NULL == n->child)
684                 return(1);
685
686         np = n->parent;
687         assert(np->args);
688
689         for (c = 0; c < (int)np->args->argc; c++)
690                 if (MDOC_Column == np->args->argv[c].arg)
691                         break;
692
693         /* Only process -column. */
694
695         if (c == (int)np->args->argc)
696                 return(1);
697
698         assert(0 == np->args->argv[c].sz);
699
700         /*
701          * Accomodate for new-style groff column syntax.  Shuffle the
702          * child nodes, all of which must be TEXT, as arguments for the
703          * column field.  Then, delete the head children.
704          */
705
706         np->args->argv[c].sz = (size_t)n->nchild;
707         np->args->argv[c].value = malloc
708                 ((size_t)n->nchild * sizeof(char *));
709
710         for (i = 0, nn = n->child; nn; i++) {
711                 np->args->argv[c].value[i] = nn->string;
712                 nn->string = NULL;
713                 nnp = nn;
714                 nn = nn->next;
715                 mdoc_node_free(nnp);
716         }
717
718         n->nchild = 0;
719         n->child = NULL;
720
721         return(1);
722 }
723
724
725 static int
726 post_bl(POST_ARGS)
727 {
728         int               i, r, len;
729
730         if (MDOC_HEAD == n->type)
731                 return(post_bl_head(m, n));
732         if (MDOC_BLOCK != n->type)
733                 return(1);
734
735         /*
736          * These are fairly complicated, so we've broken them into two
737          * functions.  post_bl_tagwidth() is called when a -tag is
738          * specified, but no -width (it must be guessed).  The second
739          * when a -width is specified (macro indicators must be
740          * rewritten into real lengths).
741          */
742
743         len = (int)(n->args ? n->args->argc : 0);
744
745         for (r = i = 0; i < len; i++) {
746                 if (MDOC_Tag == n->args->argv[i].arg)
747                         r |= 1 << 0;
748                 if (MDOC_Width == n->args->argv[i].arg)
749                         r |= 1 << 1;
750         }
751
752         if (r & (1 << 0) && ! (r & (1 << 1))) {
753                 if ( ! post_bl_tagwidth(m, n))
754                         return(0);
755         } else if (r & (1 << 1))
756                 if ( ! post_bl_width(m, n))
757                         return(0);
758
759         return(1);
760 }
761
762
763 static int
764 post_tilde(POST_ARGS)
765 {
766         struct mdoc_node *np;
767
768         if (n->child)
769                 return(1);
770
771         np = n;
772         m->next = MDOC_NEXT_CHILD;
773
774         if ( ! mdoc_word_alloc(m, n->line, n->pos, "~"))
775                 return(0);
776         m->last = np;
777
778         return(1);
779 }
780
781
782 static int
783 post_ar(POST_ARGS)
784 {
785         struct mdoc_node *np;
786
787         if (n->child)
788                 return(1);
789
790         np = n;
791         m->next = MDOC_NEXT_CHILD;
792         if ( ! mdoc_word_alloc(m, n->line, n->pos, "file"))
793                 return(0);
794         if ( ! mdoc_word_alloc(m, n->line, n->pos, "..."))
795                 return(0);
796         m->last = np;
797
798         return(1);
799 }
800
801
802 static int
803 post_dd(POST_ARGS)
804 {
805         char              buf[64];
806
807         buf[0] = 0;
808         if ( ! concat(m, n->child, buf, sizeof(buf)))
809                 return(0);
810
811         if (0 == (m->meta.date = mdoc_atotime(buf))) {
812                 if ( ! mdoc_nwarn(m, n, EBADDATE))
813                         return(0);
814                 m->meta.date = time(NULL);
815         }
816
817         return(post_prol(m, n));
818 }
819
820
821 static int
822 post_prol(POST_ARGS)
823 {
824         struct mdoc_node *np;
825
826         /* Remove prologue macros from AST.  */
827
828         if (n->parent->child == n)
829                 n->parent->child = n->prev;
830         if (n->prev)
831                 n->prev->next = NULL;
832
833         np = n;
834         assert(NULL == n->next);
835
836         if (n->prev) {
837                 m->last = n->prev;
838                 m->next = MDOC_NEXT_SIBLING;
839         } else {
840                 m->last = n->parent;
841                 m->next = MDOC_NEXT_CHILD;
842         }
843
844         mdoc_node_freelist(np);
845
846         if (m->meta.title && m->meta.date && m->meta.os)
847                 m->flags |= MDOC_PBODY;
848
849         return(1);
850 }
851
852
853 static int
854 pre_dl(PRE_ARGS)
855 {
856
857         if (MDOC_BODY == n->type)
858                 m->flags |= MDOC_LITERAL;
859         return(1);
860 }
861
862
863 static int
864 pre_offset(PRE_ARGS)
865 {
866         int              i;
867
868         /*
869          * Make sure that an empty offset produces an 8n length space as
870          * stipulated by mdoc.samples.
871          */
872
873         assert(n->args);
874         for (i = 0; i < (int)n->args->argc; i++) {
875                 if (MDOC_Offset != n->args->argv[i].arg)
876                         continue;
877                 if (n->args->argv[i].sz)
878                         break;
879                 assert(1 == n->args->refcnt);
880                 /* If no value set, length of <string>. */
881                 n->args->argv[i].value =
882                 calloc(1, sizeof(char *));
883                 if (NULL == n->args->argv[i].value)
884                         return(mdoc_nerr(m, n, EMALLOC));
885                 n->args->argv[i].sz++;
886                 n->args->argv[i].value[0] = strdup("8n");
887                 if (NULL == n->args->argv[i].value[0])
888                         return(mdoc_nerr(m, n, EMALLOC));
889                 break;
890         }
891
892         return(1);
893 }
894
895
896 static int
897 pre_bl(PRE_ARGS)
898 {
899
900         return(MDOC_BLOCK == n->type ? pre_offset(m, n) : 1);
901 }
902
903
904 static int
905 pre_bd(PRE_ARGS)
906 {
907         int              i;
908
909         if (MDOC_BLOCK == n->type)
910                 return(pre_offset(m, n));
911         if (MDOC_BODY != n->type)
912                 return(1);
913
914         /* Enter literal context if `Bd -literal' or `-unfilled'. */
915
916         for (n = n->parent, i = 0; i < (int)n->args->argc; i++)
917                 if (MDOC_Literal == n->args->argv[i].arg)
918                         m->flags |= MDOC_LITERAL;
919                 else if (MDOC_Unfilled == n->args->argv[i].arg)
920                         m->flags |= MDOC_LITERAL;
921
922         return(1);
923 }
924
925
926 static int
927 post_display(POST_ARGS)
928 {
929
930         if (MDOC_BODY == n->type)
931                 m->flags &= ~MDOC_LITERAL;
932         return(1);
933 }
934
935
936 static inline int
937 order_rs(int t)
938 {
939         int             i;
940
941         for (i = 0; i < RSORD_MAX; i++)
942                 if (rsord[i] == t)
943                         return(i);
944
945         abort();
946         /* NOTREACHED */
947 }
948
949
950 /* ARGSUSED */
951 static int
952 post_rs(POST_ARGS)
953 {
954         struct mdoc_node        *nn, *next, *prev;
955         int                      o;
956
957         if (MDOC_BLOCK != n->type)
958                 return(1);
959
960         assert(n->body->child);
961         for (next = NULL, nn = n->body->child->next; nn; nn = next) {
962                 o = order_rs(nn->tok);
963
964                 /* Remove `nn' from the chain. */
965                 next = nn->next;
966                 if (next)
967                         next->prev = nn->prev;
968
969                 prev = nn->prev;
970                 if (prev)
971                         prev->next = nn->next;
972
973                 nn->prev = nn->next = NULL;
974
975                 /*
976                  * Scan back until we reach a node that's ordered before
977                  * us, then set ourselves as being the next.
978                  */
979                 for ( ; prev; prev = prev->prev)
980                         if (order_rs(prev->tok) <= o)
981                                 break;
982
983                 nn->prev = prev;
984                 if (prev) {
985                         if (prev->next)
986                                 prev->next->prev = nn;
987                         nn->next = prev->next;
988                         prev->next = nn;
989                         continue;
990                 }
991
992                 n->body->child->prev = nn;
993                 nn->next = n->body->child;
994                 n->body->child = nn;
995         }
996         return(1);
997 }