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