Annotation of src/usr.bin/mandoc/mdoc_action.c, Revision 1.47
1.47 ! schwarze 1: /* $Id: mdoc_action.c,v 1.46 2010/10/24 18:15:43 schwarze Exp $ */
1.1 kristaps 2: /*
1.43 schwarze 3: * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
1.1 kristaps 4: *
5: * Permission to use, copy, modify, and distribute this software for any
1.2 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.2 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.24 schwarze 17: #ifndef OSNAME
1.1 kristaps 18: #include <sys/utsname.h>
1.24 schwarze 19: #endif
1.1 kristaps 20:
21: #include <assert.h>
22: #include <stdio.h>
23: #include <stdlib.h>
24: #include <string.h>
1.24 schwarze 25: #include <time.h>
1.1 kristaps 26:
1.36 schwarze 27: #include "mandoc.h"
1.1 kristaps 28: #include "libmdoc.h"
1.25 schwarze 29: #include "libmandoc.h"
1.1 kristaps 30:
1.45 schwarze 31: #include "out.h"
32: #include "term.h"
33: #include "tbl.h"
34:
1.43 schwarze 35: /*
36: * FIXME: this file is deprecated. All future "actions" should be
37: * pushed into mdoc_validate.c.
38: */
39:
1.20 schwarze 40: #define POST_ARGS struct mdoc *m, struct mdoc_node *n
1.37 schwarze 41: #define PRE_ARGS struct mdoc *m, struct mdoc_node *n
1.1 kristaps 42:
1.25 schwarze 43: #define NUMSIZ 32
1.45 schwarze 44: #define DATESIZE 32
1.25 schwarze 45:
1.1 kristaps 46: struct actions {
47: int (*pre)(PRE_ARGS);
48: int (*post)(POST_ARGS);
49: };
50:
1.25 schwarze 51: static int concat(struct mdoc *, char *,
52: const struct mdoc_node *, size_t);
1.31 schwarze 53: static inline int order_rs(enum mdoct);
1.23 schwarze 54:
1.1 kristaps 55: static int post_ar(POST_ARGS);
1.15 schwarze 56: static int post_at(POST_ARGS);
1.1 kristaps 57: static int post_bl(POST_ARGS);
1.6 schwarze 58: static int post_bl_head(POST_ARGS);
1.15 schwarze 59: static int post_bl_tagwidth(POST_ARGS);
1.1 kristaps 60: static int post_bl_width(POST_ARGS);
61: static int post_dd(POST_ARGS);
62: static int post_display(POST_ARGS);
63: static int post_dt(POST_ARGS);
1.15 schwarze 64: static int post_lb(POST_ARGS);
1.39 schwarze 65: static int post_li(POST_ARGS);
1.1 kristaps 66: static int post_nm(POST_ARGS);
67: static int post_os(POST_ARGS);
1.25 schwarze 68: static int post_pa(POST_ARGS);
1.1 kristaps 69: static int post_prol(POST_ARGS);
1.23 schwarze 70: static int post_rs(POST_ARGS);
1.1 kristaps 71: static int post_sh(POST_ARGS);
1.15 schwarze 72: static int post_st(POST_ARGS);
1.1 kristaps 73: static int post_std(POST_ARGS);
74:
75: static int pre_bd(PRE_ARGS);
76: static int pre_dl(PRE_ARGS);
1.45 schwarze 77: static int pre_ts(PRE_ARGS);
1.1 kristaps 78:
1.21 schwarze 79: static const struct actions mdoc_actions[MDOC_MAX] = {
1.4 schwarze 80: { NULL, NULL }, /* Ap */
1.1 kristaps 81: { NULL, post_dd }, /* Dd */
82: { NULL, post_dt }, /* Dt */
83: { NULL, post_os }, /* Os */
84: { NULL, post_sh }, /* Sh */
85: { NULL, NULL }, /* Ss */
86: { NULL, NULL }, /* Pp */
87: { NULL, NULL }, /* D1 */
88: { pre_dl, post_display }, /* Dl */
89: { pre_bd, post_display }, /* Bd */
90: { NULL, NULL }, /* Ed */
1.41 schwarze 91: { NULL, post_bl }, /* Bl */
1.1 kristaps 92: { NULL, NULL }, /* El */
93: { NULL, NULL }, /* It */
94: { NULL, NULL }, /* Ad */
95: { NULL, NULL }, /* An */
96: { NULL, post_ar }, /* Ar */
1.20 schwarze 97: { NULL, NULL }, /* Cd */
1.1 kristaps 98: { NULL, NULL }, /* Cm */
99: { NULL, NULL }, /* Dv */
100: { NULL, NULL }, /* Er */
101: { NULL, NULL }, /* Ev */
102: { NULL, post_std }, /* Ex */
103: { NULL, NULL }, /* Fa */
104: { NULL, NULL }, /* Fd */
105: { NULL, NULL }, /* Fl */
106: { NULL, NULL }, /* Fn */
107: { NULL, NULL }, /* Ft */
108: { NULL, NULL }, /* Ic */
109: { NULL, NULL }, /* In */
1.39 schwarze 110: { NULL, post_li }, /* Li */
1.1 kristaps 111: { NULL, NULL }, /* Nd */
112: { NULL, post_nm }, /* Nm */
113: { NULL, NULL }, /* Op */
114: { NULL, NULL }, /* Ot */
1.25 schwarze 115: { NULL, post_pa }, /* Pa */
1.1 kristaps 116: { NULL, post_std }, /* Rv */
1.15 schwarze 117: { NULL, post_st }, /* St */
1.1 kristaps 118: { NULL, NULL }, /* Va */
119: { NULL, NULL }, /* Vt */
120: { NULL, NULL }, /* Xr */
121: { NULL, NULL }, /* %A */
122: { NULL, NULL }, /* %B */
123: { NULL, NULL }, /* %D */
124: { NULL, NULL }, /* %I */
125: { NULL, NULL }, /* %J */
126: { NULL, NULL }, /* %N */
127: { NULL, NULL }, /* %O */
128: { NULL, NULL }, /* %P */
129: { NULL, NULL }, /* %R */
130: { NULL, NULL }, /* %T */
131: { NULL, NULL }, /* %V */
132: { NULL, NULL }, /* Ac */
133: { NULL, NULL }, /* Ao */
134: { NULL, NULL }, /* Aq */
1.15 schwarze 135: { NULL, post_at }, /* At */
1.1 kristaps 136: { NULL, NULL }, /* Bc */
137: { NULL, NULL }, /* Bf */
138: { NULL, NULL }, /* Bo */
139: { NULL, NULL }, /* Bq */
140: { NULL, NULL }, /* Bsx */
141: { NULL, NULL }, /* Bx */
142: { NULL, NULL }, /* Db */
143: { NULL, NULL }, /* Dc */
144: { NULL, NULL }, /* Do */
145: { NULL, NULL }, /* Dq */
146: { NULL, NULL }, /* Ec */
147: { NULL, NULL }, /* Ef */
148: { NULL, NULL }, /* Em */
149: { NULL, NULL }, /* Eo */
150: { NULL, NULL }, /* Fx */
151: { NULL, NULL }, /* Ms */
152: { NULL, NULL }, /* No */
153: { NULL, NULL }, /* Ns */
154: { NULL, NULL }, /* Nx */
155: { NULL, NULL }, /* Ox */
156: { NULL, NULL }, /* Pc */
157: { NULL, NULL }, /* Pf */
158: { NULL, NULL }, /* Po */
159: { NULL, NULL }, /* Pq */
160: { NULL, NULL }, /* Qc */
161: { NULL, NULL }, /* Ql */
162: { NULL, NULL }, /* Qo */
163: { NULL, NULL }, /* Qq */
164: { NULL, NULL }, /* Re */
1.23 schwarze 165: { NULL, post_rs }, /* Rs */
1.1 kristaps 166: { NULL, NULL }, /* Sc */
167: { NULL, NULL }, /* So */
168: { NULL, NULL }, /* Sq */
169: { NULL, NULL }, /* Sm */
170: { NULL, NULL }, /* Sx */
171: { NULL, NULL }, /* Sy */
172: { NULL, NULL }, /* Tn */
173: { NULL, NULL }, /* Ux */
174: { NULL, NULL }, /* Xc */
175: { NULL, NULL }, /* Xo */
176: { NULL, NULL }, /* Fo */
177: { NULL, NULL }, /* Fc */
178: { NULL, NULL }, /* Oo */
179: { NULL, NULL }, /* Oc */
180: { NULL, NULL }, /* Bk */
181: { NULL, NULL }, /* Ek */
182: { NULL, NULL }, /* Bt */
183: { NULL, NULL }, /* Hf */
184: { NULL, NULL }, /* Fr */
185: { NULL, NULL }, /* Ud */
1.15 schwarze 186: { NULL, post_lb }, /* Lb */
1.1 kristaps 187: { NULL, NULL }, /* Lp */
1.24 schwarze 188: { NULL, NULL }, /* Lk */
1.1 kristaps 189: { NULL, NULL }, /* Mt */
190: { NULL, NULL }, /* Brq */
191: { NULL, NULL }, /* Bro */
192: { NULL, NULL }, /* Brc */
193: { NULL, NULL }, /* %C */
194: { NULL, NULL }, /* Es */
195: { NULL, NULL }, /* En */
196: { NULL, NULL }, /* Dx */
197: { NULL, NULL }, /* %Q */
1.16 schwarze 198: { NULL, NULL }, /* br */
199: { NULL, NULL }, /* sp */
1.24 schwarze 200: { NULL, NULL }, /* %U */
1.40 schwarze 201: { NULL, NULL }, /* Ta */
1.45 schwarze 202: { pre_ts, NULL }, /* TS */
203: { NULL, NULL }, /* TE */
1.1 kristaps 204: };
205:
1.24 schwarze 206: #define RSORD_MAX 14
1.23 schwarze 207:
1.31 schwarze 208: static const enum mdoct rsord[RSORD_MAX] = {
1.23 schwarze 209: MDOC__A,
210: MDOC__T,
211: MDOC__B,
212: MDOC__I,
213: MDOC__J,
214: MDOC__R,
215: MDOC__N,
216: MDOC__V,
217: MDOC__P,
218: MDOC__Q,
219: MDOC__D,
220: MDOC__O,
1.24 schwarze 221: MDOC__C,
222: MDOC__U
1.23 schwarze 223: };
1.15 schwarze 224:
1.1 kristaps 225:
226: int
1.37 schwarze 227: mdoc_action_pre(struct mdoc *m, struct mdoc_node *n)
1.1 kristaps 228: {
229:
230: switch (n->type) {
231: case (MDOC_ROOT):
232: /* FALLTHROUGH */
233: case (MDOC_TEXT):
234: return(1);
235: default:
236: break;
237: }
238:
239: if (NULL == mdoc_actions[n->tok].pre)
240: return(1);
241: return((*mdoc_actions[n->tok].pre)(m, n));
242: }
243:
244:
245: int
246: mdoc_action_post(struct mdoc *m)
247: {
248:
249: if (MDOC_ACTED & m->last->flags)
250: return(1);
251: m->last->flags |= MDOC_ACTED;
252:
253: switch (m->last->type) {
254: case (MDOC_TEXT):
255: /* FALLTHROUGH */
256: case (MDOC_ROOT):
257: return(1);
258: default:
259: break;
260: }
261:
262: if (NULL == mdoc_actions[m->last->tok].post)
263: return(1);
1.20 schwarze 264: return((*mdoc_actions[m->last->tok].post)(m, m->last));
1.1 kristaps 265: }
266:
267:
1.25 schwarze 268: /*
269: * Concatenate sibling nodes together. All siblings must be of type
270: * MDOC_TEXT or an assertion is raised. Concatenation is separated by a
271: * single whitespace.
272: */
1.1 kristaps 273: static int
1.25 schwarze 274: concat(struct mdoc *m, char *p, const struct mdoc_node *n, size_t sz)
1.1 kristaps 275: {
276:
1.25 schwarze 277: assert(sz);
278: p[0] = '\0';
1.1 kristaps 279: for ( ; n; n = n->next) {
280: assert(MDOC_TEXT == n->type);
1.36 schwarze 281: /*
282: * XXX: yes, these can technically be resized, but it's
283: * highly unlikely that we're going to get here, so let
284: * it slip for now.
285: */
286: if (strlcat(p, n->string, sz) >= sz) {
287: mdoc_nmsg(m, n, MANDOCERR_MEM);
288: return(0);
289: }
1.1 kristaps 290: if (NULL == n->next)
291: continue;
1.36 schwarze 292: if (strlcat(p, " ", sz) >= sz) {
293: mdoc_nmsg(m, n, MANDOCERR_MEM);
294: return(0);
295: }
1.1 kristaps 296: }
297:
298: return(1);
299: }
300:
301:
1.25 schwarze 302: /*
303: * Macros accepting `-std' as an argument have the name of the current
304: * document (`Nm') filled in as the argument if it's not provided.
305: */
1.1 kristaps 306: static int
307: post_std(POST_ARGS)
308: {
1.36 schwarze 309: struct mdoc_node *nn;
1.1 kristaps 310:
1.20 schwarze 311: if (n->child)
1.1 kristaps 312: return(1);
1.36 schwarze 313: if (NULL == m->meta.name)
314: return(1);
1.20 schwarze 315:
316: nn = n;
317: m->next = MDOC_NEXT_CHILD;
1.36 schwarze 318:
1.20 schwarze 319: if ( ! mdoc_word_alloc(m, n->line, n->pos, m->meta.name))
320: return(0);
321: m->last = nn;
1.1 kristaps 322: return(1);
323: }
324:
325:
1.25 schwarze 326: /*
327: * The `Nm' macro's first use sets the name of the document. See also
328: * post_std(), etc.
329: */
1.1 kristaps 330: static int
331: post_nm(POST_ARGS)
332: {
1.25 schwarze 333: char buf[BUFSIZ];
1.1 kristaps 334:
335: if (m->meta.name)
336: return(1);
1.25 schwarze 337: if ( ! concat(m, buf, n->child, BUFSIZ))
1.1 kristaps 338: return(0);
1.25 schwarze 339: m->meta.name = mandoc_strdup(buf);
1.1 kristaps 340: return(1);
341: }
342:
343:
1.25 schwarze 344: /*
345: * Look up the value of `Lb' for matching predefined strings. If it has
346: * one, then substitute the current value for the formatted value. Note
347: * that the lookup may fail (we can provide arbitrary strings).
348: */
349: /* ARGSUSED */
1.1 kristaps 350: static int
1.15 schwarze 351: post_lb(POST_ARGS)
352: {
353: const char *p;
354: char *buf;
355: size_t sz;
356:
1.20 schwarze 357: assert(MDOC_TEXT == n->child->type);
358: p = mdoc_a2lib(n->child->string);
1.25 schwarze 359:
360: if (p) {
1.20 schwarze 361: free(n->child->string);
1.25 schwarze 362: n->child->string = mandoc_strdup(p);
1.15 schwarze 363: return(1);
364: }
365:
1.25 schwarze 366: sz = strlen(n->child->string) +
367: 2 + strlen("\\(lqlibrary\\(rq");
368: buf = mandoc_malloc(sz);
369: snprintf(buf, sz, "library \\(lq%s\\(rq", n->child->string);
1.20 schwarze 370: free(n->child->string);
1.25 schwarze 371: n->child->string = buf;
1.15 schwarze 372: return(1);
373: }
374:
375:
1.25 schwarze 376: /*
377: * Substitute the value of `St' for the corresponding formatted string.
378: * We're guaranteed that this exists (it's been verified during the
379: * validation phase).
380: */
381: /* ARGSUSED */
1.15 schwarze 382: static int
383: post_st(POST_ARGS)
384: {
385: const char *p;
386:
1.20 schwarze 387: assert(MDOC_TEXT == n->child->type);
388: p = mdoc_a2st(n->child->string);
1.34 schwarze 389: if (p != NULL) {
390: free(n->child->string);
391: n->child->string = mandoc_strdup(p);
392: }
1.15 schwarze 393: return(1);
394: }
395:
396:
1.25 schwarze 397: /*
398: * Look up the standard string in a table. We know that it exists from
399: * the validation phase, so assert on failure. If a standard key wasn't
400: * supplied, supply the default ``AT&T UNIX''.
401: */
1.15 schwarze 402: static int
403: post_at(POST_ARGS)
404: {
1.34 schwarze 405: struct mdoc_node *nn;
406: const char *p, *q;
407: char *buf;
408: size_t sz;
1.15 schwarze 409:
1.20 schwarze 410: if (n->child) {
411: assert(MDOC_TEXT == n->child->type);
412: p = mdoc_a2att(n->child->string);
1.34 schwarze 413: if (p) {
414: free(n->child->string);
415: n->child->string = mandoc_strdup(p);
416: } else {
417: p = "AT&T UNIX ";
418: q = n->child->string;
419: sz = strlen(p) + strlen(q) + 1;
420: buf = mandoc_malloc(sz);
421: strlcpy(buf, p, sz);
422: strlcat(buf, q, sz);
423: free(n->child->string);
424: n->child->string = buf;
425: }
1.15 schwarze 426: return(1);
427: }
428:
1.20 schwarze 429: nn = n;
1.15 schwarze 430: m->next = MDOC_NEXT_CHILD;
1.20 schwarze 431: if ( ! mdoc_word_alloc(m, nn->line, nn->pos, "AT&T UNIX"))
1.15 schwarze 432: return(0);
1.20 schwarze 433: m->last = nn;
1.15 schwarze 434: return(1);
435: }
436:
437:
1.25 schwarze 438: /*
439: * Mark the current section. The ``named'' section (lastnamed) is set
440: * whenever the current section isn't a custom section--we use this to
441: * keep track of section ordering. Also check that the section is
442: * allowed within the document's manual section.
443: */
1.15 schwarze 444: static int
1.1 kristaps 445: post_sh(POST_ARGS)
446: {
447: enum mdoc_sec sec;
1.25 schwarze 448: char buf[BUFSIZ];
1.1 kristaps 449:
1.20 schwarze 450: if (MDOC_HEAD != n->type)
1.1 kristaps 451: return(1);
452:
1.25 schwarze 453: if ( ! concat(m, buf, n->child, BUFSIZ))
1.1 kristaps 454: return(0);
1.32 schwarze 455: sec = mdoc_str2sec(buf);
1.30 schwarze 456: /*
457: * The first section should always make us move into a non-new
458: * state.
459: */
1.29 schwarze 460: if (SEC_NONE == m->lastnamed || SEC_CUSTOM != sec)
1.1 kristaps 461: m->lastnamed = sec;
1.42 schwarze 462:
463: /*
464: * Switch the parser's SYNOPSIS mode, to be copied
465: * into individual nodes when creating them.
466: * Note that this mode can also be set and unset
467: * using the roff nS register.
468: */
469: if (SEC_SYNOPSIS == sec)
470: m->flags |= MDOC_SYNOPSIS;
471: else
472: m->flags &= ~MDOC_SYNOPSIS;
1.1 kristaps 473:
1.25 schwarze 474: /* Some sections only live in certain manual sections. */
475:
1.1 kristaps 476: switch ((m->lastsec = sec)) {
477: case (SEC_RETURN_VALUES):
478: /* FALLTHROUGH */
479: case (SEC_ERRORS):
1.35 schwarze 480: assert(m->meta.msec);
481: if (*m->meta.msec == '2')
1.1 kristaps 482: break;
1.35 schwarze 483: if (*m->meta.msec == '3')
484: break;
485: if (*m->meta.msec == '9')
486: break;
1.36 schwarze 487: return(mdoc_nmsg(m, n, MANDOCERR_SECMSEC));
1.1 kristaps 488: default:
489: break;
490: }
491: return(1);
492: }
493:
494:
1.25 schwarze 495: /*
496: * Parse out the contents of `Dt'. See in-line documentation for how we
497: * handle the various fields of this macro.
498: */
1.1 kristaps 499: static int
500: post_dt(POST_ARGS)
501: {
1.20 schwarze 502: struct mdoc_node *nn;
1.1 kristaps 503: const char *cp;
504:
505: if (m->meta.title)
506: free(m->meta.title);
507: if (m->meta.vol)
508: free(m->meta.vol);
509: if (m->meta.arch)
510: free(m->meta.arch);
511:
512: m->meta.title = m->meta.vol = m->meta.arch = NULL;
513: /* Handles: `.Dt'
514: * --> title = unknown, volume = local, msec = 0, arch = NULL
515: */
516:
1.20 schwarze 517: if (NULL == (nn = n->child)) {
1.25 schwarze 518: /* XXX: make these macro values. */
1.35 schwarze 519: /* FIXME: warn about missing values. */
1.40 schwarze 520: m->meta.title = mandoc_strdup("UNKNOWN");
521: m->meta.vol = mandoc_strdup("LOCAL");
1.35 schwarze 522: m->meta.msec = mandoc_strdup("1");
1.20 schwarze 523: return(post_prol(m, n));
1.1 kristaps 524: }
525:
526: /* Handles: `.Dt TITLE'
527: * --> title = TITLE, volume = local, msec = 0, arch = NULL
528: */
529:
1.40 schwarze 530: m->meta.title = mandoc_strdup
531: ('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
1.1 kristaps 532:
1.20 schwarze 533: if (NULL == (nn = nn->next)) {
1.35 schwarze 534: /* FIXME: warn about missing msec. */
1.25 schwarze 535: /* XXX: make this a macro value. */
1.40 schwarze 536: m->meta.vol = mandoc_strdup("LOCAL");
1.35 schwarze 537: m->meta.msec = mandoc_strdup("1");
1.20 schwarze 538: return(post_prol(m, n));
1.1 kristaps 539: }
540:
541: /* Handles: `.Dt TITLE SEC'
542: * --> title = TITLE, volume = SEC is msec ?
543: * format(msec) : SEC,
544: * msec = SEC is msec ? atoi(msec) : 0,
545: * arch = NULL
546: */
547:
1.20 schwarze 548: cp = mdoc_a2msec(nn->string);
1.1 kristaps 549: if (cp) {
1.25 schwarze 550: m->meta.vol = mandoc_strdup(cp);
1.35 schwarze 551: m->meta.msec = mandoc_strdup(nn->string);
1.36 schwarze 552: } else if (mdoc_nmsg(m, n, MANDOCERR_BADMSEC)) {
1.25 schwarze 553: m->meta.vol = mandoc_strdup(nn->string);
1.35 schwarze 554: m->meta.msec = mandoc_strdup(nn->string);
555: } else
556: return(0);
1.1 kristaps 557:
1.20 schwarze 558: if (NULL == (nn = nn->next))
559: return(post_prol(m, n));
1.1 kristaps 560:
561: /* Handles: `.Dt TITLE SEC VOL'
562: * --> title = TITLE, volume = VOL is vol ?
563: * format(VOL) :
564: * VOL is arch ? format(arch) :
565: * VOL
566: */
567:
1.20 schwarze 568: cp = mdoc_a2vol(nn->string);
1.1 kristaps 569: if (cp) {
570: free(m->meta.vol);
1.25 schwarze 571: m->meta.vol = mandoc_strdup(cp);
1.1 kristaps 572: } else {
1.35 schwarze 573: /* FIXME: warn about bad arch. */
1.20 schwarze 574: cp = mdoc_a2arch(nn->string);
1.1 kristaps 575: if (NULL == cp) {
576: free(m->meta.vol);
1.25 schwarze 577: m->meta.vol = mandoc_strdup(nn->string);
578: } else
579: m->meta.arch = mandoc_strdup(cp);
1.1 kristaps 580: }
581:
582: /* Ignore any subsequent parameters... */
1.25 schwarze 583: /* FIXME: warn about subsequent parameters. */
1.1 kristaps 584:
1.20 schwarze 585: return(post_prol(m, n));
1.1 kristaps 586: }
587:
588:
1.25 schwarze 589: /*
590: * Set the operating system by way of the `Os' macro. Note that if an
591: * argument isn't provided and -DOSNAME="\"foo\"" is provided during
592: * compilation, this value will be used instead of filling in "sysname
593: * release" from uname().
594: */
1.1 kristaps 595: static int
596: post_os(POST_ARGS)
597: {
1.25 schwarze 598: char buf[BUFSIZ];
599: #ifndef OSNAME
1.1 kristaps 600: struct utsname utsname;
1.24 schwarze 601: #endif
602:
1.46 schwarze 603: free(m->meta.os);
1.1 kristaps 604:
1.25 schwarze 605: if ( ! concat(m, buf, n->child, BUFSIZ))
1.1 kristaps 606: return(0);
607:
1.36 schwarze 608: /* XXX: yes, these can all be dynamically-adjusted buffers, but
609: * it's really not worth the extra hackery.
610: */
611:
1.25 schwarze 612: if ('\0' == buf[0]) {
613: #ifdef OSNAME
1.36 schwarze 614: if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
615: mdoc_nmsg(m, n, MANDOCERR_MEM);
616: return(0);
617: }
1.25 schwarze 618: #else /*!OSNAME */
1.46 schwarze 619: if (uname(&utsname)) {
620: mdoc_nmsg(m, n, MANDOCERR_UNAME);
621: m->meta.os = mandoc_strdup("UNKNOWN");
622: return(post_prol(m, n));
623: }
1.36 schwarze 624:
625: if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
626: mdoc_nmsg(m, n, MANDOCERR_MEM);
627: return(0);
628: }
1.46 schwarze 629: if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
1.36 schwarze 630: mdoc_nmsg(m, n, MANDOCERR_MEM);
631: return(0);
632: }
633: if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
634: mdoc_nmsg(m, n, MANDOCERR_MEM);
635: return(0);
636: }
1.25 schwarze 637: #endif /*!OSNAME*/
1.1 kristaps 638: }
639:
1.25 schwarze 640: m->meta.os = mandoc_strdup(buf);
1.20 schwarze 641: return(post_prol(m, n));
1.1 kristaps 642: }
643:
644:
645: /*
646: * Calculate the -width for a `Bl -tag' list if it hasn't been provided.
1.25 schwarze 647: * Uses the first head macro. NOTE AGAIN: this is ONLY if the -width
648: * argument has NOT been provided. See post_bl_width() for converting
649: * the -width string.
1.1 kristaps 650: */
651: static int
1.20 schwarze 652: post_bl_tagwidth(POST_ARGS)
1.1 kristaps 653: {
1.25 schwarze 654: struct mdoc_node *nn;
1.41 schwarze 655: size_t sz, ssz;
1.25 schwarze 656: int i;
657: char buf[NUMSIZ];
1.1 kristaps 658:
1.41 schwarze 659: sz = 10;
1.38 schwarze 660:
661: for (nn = n->body->child; nn; nn = nn->next) {
1.41 schwarze 662: if (MDOC_It != nn->tok)
663: continue;
1.25 schwarze 664:
1.20 schwarze 665: assert(MDOC_BLOCK == nn->type);
666: nn = nn->head->child;
1.41 schwarze 667:
1.47 ! schwarze 668: if (nn == NULL)
1.44 schwarze 669: break;
670:
1.41 schwarze 671: if (MDOC_TEXT == nn->type) {
1.25 schwarze 672: sz = strlen(nn->string) + 1;
1.41 schwarze 673: break;
674: }
675:
676: if (0 != (ssz = mdoc_macro2len(nn->tok)))
677: sz = ssz;
678:
679: break;
1.1 kristaps 680: }
681:
1.41 schwarze 682: /* Defaults to ten ens. */
683:
1.25 schwarze 684: snprintf(buf, NUMSIZ, "%zun", sz);
1.1 kristaps 685:
686: /*
687: * We have to dynamically add this to the macro's argument list.
688: * We're guaranteed that a MDOC_Width doesn't already exist.
689: */
690:
1.41 schwarze 691: assert(n->args);
692: i = (int)(n->args->argc)++;
693:
694: n->args->argv = mandoc_realloc(n->args->argv,
695: n->args->argc * sizeof(struct mdoc_argv));
1.1 kristaps 696:
1.41 schwarze 697: n->args->argv[i].arg = MDOC_Width;
698: n->args->argv[i].line = n->line;
699: n->args->argv[i].pos = n->pos;
700: n->args->argv[i].sz = 1;
701: n->args->argv[i].value = mandoc_malloc(sizeof(char *));
702: n->args->argv[i].value[0] = mandoc_strdup(buf);
1.1 kristaps 703:
1.41 schwarze 704: /* Set our width! */
1.43 schwarze 705: n->data.Bl->width = n->args->argv[i].value[0];
1.1 kristaps 706: return(1);
707: }
708:
709:
1.25 schwarze 710: /*
711: * Calculate the real width of a list from the -width string, which may
712: * contain a macro (with a known default width), a literal string, or a
713: * scaling width.
714: */
1.1 kristaps 715: static int
1.20 schwarze 716: post_bl_width(POST_ARGS)
1.1 kristaps 717: {
718: size_t width;
1.28 schwarze 719: int i;
720: enum mdoct tok;
1.25 schwarze 721: char buf[NUMSIZ];
1.1 kristaps 722:
723: /*
724: * If the value to -width is a macro, then we re-write it to be
725: * the macro's width as set in share/tmac/mdoc/doc-common.
726: */
727:
1.43 schwarze 728: if (0 == strcmp(n->data.Bl->width, "Ds"))
1.9 schwarze 729: width = 6;
1.43 schwarze 730: else if (MDOC_MAX == (tok = mdoc_hash_find(n->data.Bl->width)))
1.1 kristaps 731: return(1);
732: else if (0 == (width = mdoc_macro2len(tok)))
1.36 schwarze 733: return(mdoc_nmsg(m, n, MANDOCERR_BADWIDTH));
1.1 kristaps 734:
735: /* The value already exists: free and reallocate it. */
736:
1.41 schwarze 737: assert(n->args);
738:
739: for (i = 0; i < (int)n->args->argc; i++)
740: if (MDOC_Width == n->args->argv[i].arg)
741: break;
742:
743: assert(i < (int)n->args->argc);
744:
1.25 schwarze 745: snprintf(buf, NUMSIZ, "%zun", width);
1.20 schwarze 746: free(n->args->argv[i].value[0]);
1.25 schwarze 747: n->args->argv[i].value[0] = mandoc_strdup(buf);
1.41 schwarze 748:
749: /* Set our width! */
1.43 schwarze 750: n->data.Bl->width = n->args->argv[i].value[0];
1.1 kristaps 751: return(1);
752: }
753:
754:
1.25 schwarze 755: /*
756: * Do processing for -column lists, which can have two distinct styles
757: * of invocation. Merge this two styles into a consistent form.
758: */
1.20 schwarze 759: /* ARGSUSED */
1.1 kristaps 760: static int
1.6 schwarze 761: post_bl_head(POST_ARGS)
762: {
763: int i, c;
1.20 schwarze 764: struct mdoc_node *np, *nn, *nnp;
1.6 schwarze 765:
1.43 schwarze 766: if (LIST_column != n->data.Bl->type)
1.41 schwarze 767: return(1);
768: else if (NULL == n->child)
1.6 schwarze 769: return(1);
770:
1.20 schwarze 771: np = n->parent;
772: assert(np->args);
1.6 schwarze 773:
1.20 schwarze 774: for (c = 0; c < (int)np->args->argc; c++)
775: if (MDOC_Column == np->args->argv[c].arg)
1.6 schwarze 776: break;
777:
1.41 schwarze 778: assert(c < (int)np->args->argc);
1.20 schwarze 779: assert(0 == np->args->argv[c].sz);
1.6 schwarze 780:
781: /*
782: * Accomodate for new-style groff column syntax. Shuffle the
783: * child nodes, all of which must be TEXT, as arguments for the
784: * column field. Then, delete the head children.
785: */
786:
1.20 schwarze 787: np->args->argv[c].sz = (size_t)n->nchild;
1.25 schwarze 788: np->args->argv[c].value = mandoc_malloc
1.20 schwarze 789: ((size_t)n->nchild * sizeof(char *));
1.6 schwarze 790:
1.43 schwarze 791: n->data.Bl->ncols = np->args->argv[c].sz;
792: n->data.Bl->cols = (const char **)np->args->argv[c].value;
793:
1.20 schwarze 794: for (i = 0, nn = n->child; nn; i++) {
795: np->args->argv[c].value[i] = nn->string;
1.6 schwarze 796: nn->string = NULL;
797: nnp = nn;
798: nn = nn->next;
1.30 schwarze 799: mdoc_node_delete(NULL, nnp);
1.6 schwarze 800: }
801:
1.20 schwarze 802: n->nchild = 0;
803: n->child = NULL;
1.6 schwarze 804: return(1);
805: }
806:
807:
808: static int
1.1 kristaps 809: post_bl(POST_ARGS)
810: {
811:
1.20 schwarze 812: if (MDOC_HEAD == n->type)
813: return(post_bl_head(m, n));
814: if (MDOC_BLOCK != n->type)
1.1 kristaps 815: return(1);
816:
817: /*
818: * These are fairly complicated, so we've broken them into two
819: * functions. post_bl_tagwidth() is called when a -tag is
820: * specified, but no -width (it must be guessed). The second
821: * when a -width is specified (macro indicators must be
822: * rewritten into real lengths).
823: */
824:
1.43 schwarze 825: if (LIST_tag == n->data.Bl->type && NULL == n->data.Bl->width) {
1.20 schwarze 826: if ( ! post_bl_tagwidth(m, n))
1.1 kristaps 827: return(0);
1.43 schwarze 828: } else if (NULL != n->data.Bl->width) {
1.20 schwarze 829: if ( ! post_bl_width(m, n))
1.1 kristaps 830: return(0);
1.41 schwarze 831: } else
832: return(1);
833:
1.43 schwarze 834: assert(n->data.Bl->width);
1.3 schwarze 835: return(1);
836: }
837:
838:
1.25 schwarze 839: /*
840: * The `Pa' macro defaults to a tilde if no value is provided as an
841: * argument.
842: */
1.3 schwarze 843: static int
1.25 schwarze 844: post_pa(POST_ARGS)
1.3 schwarze 845: {
1.20 schwarze 846: struct mdoc_node *np;
1.3 schwarze 847:
1.20 schwarze 848: if (n->child)
1.3 schwarze 849: return(1);
850:
1.20 schwarze 851: np = n;
1.3 schwarze 852: m->next = MDOC_NEXT_CHILD;
1.20 schwarze 853: if ( ! mdoc_word_alloc(m, n->line, n->pos, "~"))
1.39 schwarze 854: return(0);
855: m->last = np;
856: return(1);
857: }
858:
859:
860: /*
861: * Empty `Li' macros get an empty string to make front-ends add an extra
862: * space.
863: */
864: static int
865: post_li(POST_ARGS)
866: {
867: struct mdoc_node *np;
868:
869: if (n->child)
870: return(1);
871:
872: np = n;
873: m->next = MDOC_NEXT_CHILD;
874: if ( ! mdoc_word_alloc(m, n->line, n->pos, ""))
1.3 schwarze 875: return(0);
1.20 schwarze 876: m->last = np;
1.1 kristaps 877: return(1);
878: }
879:
880:
1.25 schwarze 881: /*
882: * The `Ar' macro defaults to two strings "file ..." if no value is
883: * provided as an argument.
884: */
1.1 kristaps 885: static int
886: post_ar(POST_ARGS)
887: {
1.20 schwarze 888: struct mdoc_node *np;
1.1 kristaps 889:
1.20 schwarze 890: if (n->child)
1.1 kristaps 891: return(1);
892:
1.20 schwarze 893: np = n;
1.1 kristaps 894: m->next = MDOC_NEXT_CHILD;
1.25 schwarze 895: /* XXX: make into macro values. */
1.20 schwarze 896: if ( ! mdoc_word_alloc(m, n->line, n->pos, "file"))
1.1 kristaps 897: return(0);
1.20 schwarze 898: if ( ! mdoc_word_alloc(m, n->line, n->pos, "..."))
1.1 kristaps 899: return(0);
1.20 schwarze 900: m->last = np;
1.1 kristaps 901: return(1);
902: }
903:
904:
1.25 schwarze 905: /*
1.26 schwarze 906: * Parse the date field in `Dd'.
1.25 schwarze 907: */
1.1 kristaps 908: static int
909: post_dd(POST_ARGS)
910: {
1.45 schwarze 911: char buf[DATESIZE];
1.44 schwarze 912:
913: if (NULL == n->child) {
914: m->meta.date = time(NULL);
915: return(post_prol(m, n));
916: }
1.1 kristaps 917:
1.45 schwarze 918: if ( ! concat(m, buf, n->child, DATESIZE))
1.1 kristaps 919: return(0);
920:
1.26 schwarze 921: m->meta.date = mandoc_a2time
922: (MTIME_MDOCDATE | MTIME_CANONICAL, buf);
923:
1.25 schwarze 924: if (0 == m->meta.date) {
1.36 schwarze 925: if ( ! mdoc_nmsg(m, n, MANDOCERR_BADDATE))
1.1 kristaps 926: return(0);
927: m->meta.date = time(NULL);
928: }
929:
1.20 schwarze 930: return(post_prol(m, n));
1.1 kristaps 931: }
932:
933:
1.25 schwarze 934: /*
935: * Remove prologue macros from the document after they're processed.
936: * The final document uses mdoc_meta for these values and discards the
937: * originals.
938: */
1.1 kristaps 939: static int
940: post_prol(POST_ARGS)
941: {
1.18 schwarze 942:
1.30 schwarze 943: mdoc_node_delete(m, n);
1.18 schwarze 944: if (m->meta.title && m->meta.date && m->meta.os)
945: m->flags |= MDOC_PBODY;
1.1 kristaps 946: return(1);
947: }
948:
949:
1.25 schwarze 950: /*
951: * Trigger a literal context.
952: */
1.1 kristaps 953: static int
954: pre_dl(PRE_ARGS)
955: {
956:
1.7 schwarze 957: if (MDOC_BODY == n->type)
958: m->flags |= MDOC_LITERAL;
1.1 kristaps 959: return(1);
960: }
961:
962:
1.22 schwarze 963: static int
1.1 kristaps 964: pre_bd(PRE_ARGS)
965: {
966:
967: if (MDOC_BODY != n->type)
968: return(1);
969:
1.43 schwarze 970: assert(n->data.Bd);
971: if (DISP_literal == n->data.Bd->type)
1.41 schwarze 972: m->flags |= MDOC_LITERAL;
1.43 schwarze 973: if (DISP_unfilled == n->data.Bd->type)
1.41 schwarze 974: m->flags |= MDOC_LITERAL;
1.45 schwarze 975:
976: return(1);
977: }
978:
979:
980: /* ARGSUSED */
981: static int
982: pre_ts(PRE_ARGS)
983: {
984:
985: if (MDOC_BLOCK == n->type)
986: n->data.TS = tbl_alloc();
1.1 kristaps 987:
988: return(1);
989: }
990:
991:
992: static int
993: post_display(POST_ARGS)
994: {
995:
1.20 schwarze 996: if (MDOC_BODY == n->type)
1.1 kristaps 997: m->flags &= ~MDOC_LITERAL;
998: return(1);
999: }
1000:
1001:
1.23 schwarze 1002: static inline int
1.31 schwarze 1003: order_rs(enum mdoct t)
1.23 schwarze 1004: {
1005: int i;
1006:
1.31 schwarze 1007: for (i = 0; i < (int)RSORD_MAX; i++)
1.23 schwarze 1008: if (rsord[i] == t)
1009: return(i);
1010:
1011: abort();
1012: /* NOTREACHED */
1013: }
1014:
1015:
1016: /* ARGSUSED */
1017: static int
1018: post_rs(POST_ARGS)
1019: {
1020: struct mdoc_node *nn, *next, *prev;
1021: int o;
1022:
1023: if (MDOC_BLOCK != n->type)
1024: return(1);
1025:
1026: assert(n->body->child);
1027: for (next = NULL, nn = n->body->child->next; nn; nn = next) {
1028: o = order_rs(nn->tok);
1029:
1030: /* Remove `nn' from the chain. */
1031: next = nn->next;
1032: if (next)
1033: next->prev = nn->prev;
1034:
1035: prev = nn->prev;
1036: if (prev)
1037: prev->next = nn->next;
1038:
1039: nn->prev = nn->next = NULL;
1040:
1041: /*
1042: * Scan back until we reach a node that's ordered before
1043: * us, then set ourselves as being the next.
1044: */
1045: for ( ; prev; prev = prev->prev)
1046: if (order_rs(prev->tok) <= o)
1047: break;
1048:
1049: nn->prev = prev;
1050: if (prev) {
1051: if (prev->next)
1052: prev->next->prev = nn;
1053: nn->next = prev->next;
1054: prev->next = nn;
1055: continue;
1056: }
1057:
1058: n->body->child->prev = nn;
1059: nn->next = n->body->child;
1060: n->body->child = nn;
1061: }
1062: return(1);
1063: }