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