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