Annotation of mandoc/action.c, Revision 1.30
1.30 ! kristaps 1: /* $Id: action.c,v 1.29 2009/03/05 12:08:53 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the
7: * above copyright notice and this permission notice appear in all
8: * copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11: * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12: * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13: * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17: * PERFORMANCE OF THIS SOFTWARE.
18: */
19: #include <assert.h>
1.20 kristaps 20: #include <stdio.h>
1.1 kristaps 21: #include <stdlib.h>
1.20 kristaps 22: #include <string.h>
1.5 kristaps 23: #include <time.h>
1.1 kristaps 24:
25: #include "private.h"
26:
1.14 kristaps 27: /*
28: * Actions are executed on macros after they've been post-validated: in
29: * other words, a macro will not be "acted upon" until all of its
30: * children have been filled in (post-fix order).
31: */
1.1 kristaps 32:
33: struct actions {
1.10 kristaps 34: int (*post)(struct mdoc *);
1.1 kristaps 35: };
36:
1.11 kristaps 37: /* Per-macro action routines. */
1.1 kristaps 38:
1.30 ! kristaps 39: static int post_ar(struct mdoc *);
1.20 kristaps 40: static int post_bl(struct mdoc *);
1.23 kristaps 41: static int post_bl_width(struct mdoc *);
42: static int post_bl_tagwidth(struct mdoc *);
1.29 kristaps 43: static int post_dd(struct mdoc *);
1.13 kristaps 44: static int post_dt(struct mdoc *);
45: static int post_nm(struct mdoc *);
1.29 kristaps 46: static int post_os(struct mdoc *);
47: static int post_sh(struct mdoc *);
1.30 ! kristaps 48: static int post_ex(struct mdoc *);
1.13 kristaps 49:
50: static int post_prologue(struct mdoc *);
1.3 kristaps 51:
1.11 kristaps 52: /* Array of macro action routines. */
53:
1.1 kristaps 54: const struct actions mdoc_actions[MDOC_MAX] = {
1.10 kristaps 55: { NULL }, /* \" */
56: { post_dd }, /* Dd */
57: { post_dt }, /* Dt */
58: { post_os }, /* Os */
59: { post_sh }, /* Sh */
60: { NULL }, /* Ss */
61: { NULL }, /* Pp */
62: { NULL }, /* D1 */
63: { NULL }, /* Dl */
64: { NULL }, /* Bd */
65: { NULL }, /* Ed */
1.20 kristaps 66: { post_bl }, /* Bl */
1.10 kristaps 67: { NULL }, /* El */
68: { NULL }, /* It */
69: { NULL }, /* Ad */
70: { NULL }, /* An */
1.30 ! kristaps 71: { post_ar }, /* Ar */
1.10 kristaps 72: { NULL }, /* Cd */
73: { NULL }, /* Cm */
74: { NULL }, /* Dv */
75: { NULL }, /* Er */
76: { NULL }, /* Ev */
1.29 kristaps 77: { post_ex }, /* Ex */
1.10 kristaps 78: { NULL }, /* Fa */
79: { NULL }, /* Fd */
80: { NULL }, /* Fl */
81: { NULL }, /* Fn */
82: { NULL }, /* Ft */
83: { NULL }, /* Ic */
84: { NULL }, /* In */
85: { NULL }, /* Li */
86: { NULL }, /* Nd */
87: { post_nm }, /* Nm */
88: { NULL }, /* Op */
89: { NULL }, /* Ot */
90: { NULL }, /* Pa */
91: { NULL }, /* Rv */
92: { NULL }, /* St */
93: { NULL }, /* Va */
94: { NULL }, /* Vt */
95: { NULL }, /* Xr */
96: { NULL }, /* %A */
97: { NULL }, /* %B */
98: { NULL }, /* %D */
99: { NULL }, /* %I */
100: { NULL }, /* %J */
101: { NULL }, /* %N */
102: { NULL }, /* %O */
103: { NULL }, /* %P */
104: { NULL }, /* %R */
105: { NULL }, /* %T */
106: { NULL }, /* %V */
107: { NULL }, /* Ac */
108: { NULL }, /* Ao */
109: { NULL }, /* Aq */
110: { NULL }, /* At */
111: { NULL }, /* Bc */
112: { NULL }, /* Bf */
113: { NULL }, /* Bo */
114: { NULL }, /* Bq */
115: { NULL }, /* Bsx */
116: { NULL }, /* Bx */
117: { NULL }, /* Db */
118: { NULL }, /* Dc */
119: { NULL }, /* Do */
120: { NULL }, /* Dq */
121: { NULL }, /* Ec */
122: { NULL }, /* Ef */
123: { NULL }, /* Em */
124: { NULL }, /* Eo */
125: { NULL }, /* Fx */
126: { NULL }, /* Ms */
127: { NULL }, /* No */
128: { NULL }, /* Ns */
129: { NULL }, /* Nx */
130: { NULL }, /* Ox */
131: { NULL }, /* Pc */
132: { NULL }, /* Pf */
133: { NULL }, /* Po */
134: { NULL }, /* Pq */
135: { NULL }, /* Qc */
136: { NULL }, /* Ql */
137: { NULL }, /* Qo */
138: { NULL }, /* Qq */
139: { NULL }, /* Re */
140: { NULL }, /* Rs */
141: { NULL }, /* Sc */
142: { NULL }, /* So */
143: { NULL }, /* Sq */
144: { NULL }, /* Sm */
145: { NULL }, /* Sx */
146: { NULL }, /* Sy */
147: { NULL }, /* Tn */
148: { NULL }, /* Ux */
149: { NULL }, /* Xc */
150: { NULL }, /* Xo */
151: { NULL }, /* Fo */
152: { NULL }, /* Fc */
153: { NULL }, /* Oo */
154: { NULL }, /* Oc */
155: { NULL }, /* Bk */
156: { NULL }, /* Ek */
157: { NULL }, /* Bt */
158: { NULL }, /* Hf */
159: { NULL }, /* Fr */
160: { NULL }, /* Ud */
1.1 kristaps 161: };
162:
163:
1.3 kristaps 164: static int
1.29 kristaps 165: post_ex(struct mdoc *mdoc)
166: {
167:
168: /*
169: * If `.Ex -std' is invoked without an argument, fill it in with
170: * our name (if it's been set).
171: */
172:
173: if (0 == mdoc->last->data.elem.argc)
174: return(1);
1.30 ! kristaps 175: if (mdoc->last->data.elem.argv[0].sz)
1.29 kristaps 176: return(1);
177:
1.30 ! kristaps 178: assert(mdoc->meta.name);
1.29 kristaps 179:
180: mdoc_msg(mdoc, "writing %s argument: %s",
181: mdoc_argnames[MDOC_Std], mdoc->meta.name);
182:
183: mdoc->last->data.elem.argv[0].sz = 1;
184: mdoc->last->data.elem.argv[0].value = xcalloc(1, sizeof(char *));
185: mdoc->last->data.elem.argv[0].value[0] = xstrdup(mdoc->meta.name);
186: return(1);
187: }
188:
189:
190: static int
1.10 kristaps 191: post_nm(struct mdoc *mdoc)
192: {
193: char buf[64];
194:
195: assert(MDOC_ELEM == mdoc->last->type);
196: assert(MDOC_Nm == mdoc->last->tok);
197:
1.23 kristaps 198: /*
199: * The `Nm' macro sets the document's name when used the first
200: * time with an argument. Subsequent calls without a value will
201: * result in the name value being used.
202: */
203:
1.10 kristaps 204: if (mdoc->meta.name)
205: return(1);
206:
1.11 kristaps 207: if (xstrlcats(buf, mdoc->last->child, 64)) {
208: mdoc->meta.name = xstrdup(buf);
209: return(1);
210: }
1.10 kristaps 211:
1.11 kristaps 212: return(mdoc_err(mdoc, "macro parameters too long"));
1.10 kristaps 213: }
214:
215:
216: static int
1.4 kristaps 217: post_sh(struct mdoc *mdoc)
1.1 kristaps 218: {
1.10 kristaps 219: enum mdoc_sec sec;
220: char buf[64];
1.3 kristaps 221:
1.23 kristaps 222: /*
223: * We keep track of the current section /and/ the "named"
224: * section, which is one of the conventional ones, in order to
225: * check ordering.
226: */
227:
1.3 kristaps 228: if (MDOC_HEAD != mdoc->last->type)
229: return(1);
1.11 kristaps 230: if (xstrlcats(buf, mdoc->last->child, 64)) {
231: if (SEC_CUSTOM != (sec = mdoc_atosec(buf)))
1.16 kristaps 232: mdoc->lastnamed = sec;
1.19 kristaps 233: mdoc->lastsec = sec;
1.23 kristaps 234: } else
235: return(mdoc_err(mdoc, "parameters too long"));
236:
237: switch (mdoc->lastsec) {
238: case (SEC_RETURN_VALUES):
239: /* FALLTHROUGH */
240: case (SEC_ERRORS):
241: switch (mdoc->meta.msec) {
242: case (MSEC_2):
243: /* FALLTHROUGH */
244: case (MSEC_3):
245: /* FALLTHROUGH */
246: case (MSEC_9):
247: break;
248: default:
249: return(mdoc_warn(mdoc, WARN_COMPAT,
250: "inappropriate section for "
251: "manual section"));
252: }
253: break;
254: default:
255: break;
1.11 kristaps 256: }
1.23 kristaps 257: return(1);
1.1 kristaps 258: }
259:
1.3 kristaps 260:
1.4 kristaps 261: static int
262: post_dt(struct mdoc *mdoc)
263: {
1.5 kristaps 264: int i;
265: char *p;
266: struct mdoc_node *n;
267:
1.23 kristaps 268: /*
269: * Prologue title must be parsed into document meta-data.
270: */
271:
1.5 kristaps 272: assert(MDOC_ELEM == mdoc->last->type);
1.7 kristaps 273: assert(MDOC_Dt == mdoc->last->tok);
1.5 kristaps 274:
1.10 kristaps 275: assert(NULL == mdoc->meta.title);
1.4 kristaps 276:
1.18 kristaps 277: /* LINTED */
1.5 kristaps 278: for (i = 0, n = mdoc->last->child; n; n = n->next, i++) {
279: assert(MDOC_TEXT == n->type);
280: p = n->data.text.string;
1.4 kristaps 281:
1.5 kristaps 282: switch (i) {
283: case (0):
1.10 kristaps 284: mdoc->meta.title = xstrdup(p);
285: break;
1.5 kristaps 286: case (1):
287: mdoc->meta.msec = mdoc_atomsec(p);
288: if (MSEC_DEFAULT != mdoc->meta.msec)
289: break;
1.27 kristaps 290: return(mdoc_nerr(mdoc, n,
291: "invalid parameter syntax"));
1.5 kristaps 292: case (2):
293: mdoc->meta.vol = mdoc_atovol(p);
294: if (VOL_DEFAULT != mdoc->meta.vol)
295: break;
296: mdoc->meta.arch = mdoc_atoarch(p);
297: if (ARCH_DEFAULT != mdoc->meta.arch)
298: break;
1.27 kristaps 299: return(mdoc_nerr(mdoc, n,
300: "invalid parameter syntax"));
1.5 kristaps 301: default:
1.27 kristaps 302: return(mdoc_nerr(mdoc, n,
303: "too many parameters"));
1.5 kristaps 304: }
1.4 kristaps 305: }
306:
1.10 kristaps 307: if (NULL == mdoc->meta.title)
1.17 kristaps 308: mdoc->meta.title = xstrdup("UNTITLED");
1.13 kristaps 309:
1.15 kristaps 310: mdoc_msg(mdoc, "title: %s", mdoc->meta.title);
311:
1.13 kristaps 312: return(post_prologue(mdoc));
1.4 kristaps 313: }
314:
315:
316: static int
317: post_os(struct mdoc *mdoc)
318: {
1.10 kristaps 319: char buf[64];
1.5 kristaps 320:
1.23 kristaps 321: /*
322: * Prologue operating system must be parsed into document
323: * meta-data.
324: */
325:
1.5 kristaps 326: assert(MDOC_ELEM == mdoc->last->type);
1.7 kristaps 327: assert(MDOC_Os == mdoc->last->tok);
1.10 kristaps 328: assert(NULL == mdoc->meta.os);
1.5 kristaps 329:
1.10 kristaps 330: if ( ! xstrlcats(buf, mdoc->last->child, 64))
331: return(mdoc_err(mdoc, "macro parameters too long"));
1.6 kristaps 332:
1.22 kristaps 333: mdoc->meta.os = xstrdup(buf[0] ? buf : "LOCAL");
1.16 kristaps 334: mdoc->lastnamed = SEC_BODY;
1.13 kristaps 335:
336: return(post_prologue(mdoc));
1.4 kristaps 337: }
338:
339:
1.20 kristaps 340: static int
1.23 kristaps 341: post_bl_tagwidth(struct mdoc *mdoc)
1.20 kristaps 342: {
1.23 kristaps 343: struct mdoc_node *n;
344: struct mdoc_block *b;
345: int sz;
1.20 kristaps 346: char buf[32];
347:
1.23 kristaps 348: /*
349: * If -tag has been specified and -width has not been, then try
350: * to intuit our width from the first body element.
351: */
352:
353: b = &mdoc->last->data.block;
354:
355: if (NULL == (n = b->body->child))
356: return(1);
357: assert(MDOC_It == n->tok);
358:
359: /*
360: * Use the text width, if a text node, or the default macro
361: * width if a macro.
362: */
363:
364: if ((n = n->data.block.head->child)) {
365: if (MDOC_TEXT != n->type) {
366: if (0 == (sz = mdoc_macro2len(n->tok)))
367: sz = -1;
368: } else
369: sz = (int)strlen(n->data.text.string) + 1;
370: } else
371: sz = -1;
372:
373: if (-1 == sz) {
374: if ( ! mdoc_warn(mdoc, WARN_SYNTAX,
375: "cannot determine default %s",
376: mdoc_argnames[MDOC_Width]))
377: return(0);
378: sz = 10;
379: }
380:
381: (void)snprintf(buf, sizeof(buf), "%dn", sz);
382:
383: /*
384: * We have to dynamically add this to the macro's argument list.
385: * We're guaranteed that a MDOC_Width doesn't already exist.
386: */
387:
388: (b->argc)++;
389: b->argv = xrealloc(b->argv, b->argc * sizeof(struct mdoc_arg));
390:
391: b->argv[b->argc - 1].arg = MDOC_Width;
392: b->argv[b->argc - 1].line = mdoc->last->line;
393: b->argv[b->argc - 1].pos = mdoc->last->pos;
394: b->argv[b->argc - 1].sz = 1;
395: b->argv[b->argc - 1].value = xcalloc(1, sizeof(char *));
396: b->argv[b->argc - 1].value[0] = xstrdup(buf);
397:
398: mdoc_msg(mdoc, "adding %s argument: %dn",
399: mdoc_argnames[MDOC_Width], sz);
400:
401: return(1);
402: }
1.20 kristaps 403:
404:
1.23 kristaps 405: static int
406: post_bl_width(struct mdoc *mdoc)
407: {
408: size_t width;
409: int i, tok;
410: char buf[32];
411: char **p;
412:
413: for (i = 0; i < (int)mdoc->last->data.block.argc; i++)
414: if (MDOC_Width == mdoc->last->data.block.argv[i].arg)
1.20 kristaps 415: break;
416:
1.23 kristaps 417: assert(i < (int)mdoc->last->data.block.argc);
418: assert(1 == mdoc->last->data.block.argv[i].sz);
419: p = &mdoc->last->data.block.argv[i].value[0];
420:
421: /*
422: * If the value to -width is a macro, then we re-write it to be
423: * the macro's width as set in share/tmac/mdoc/doc-common.
424: */
1.20 kristaps 425:
1.28 kristaps 426: if (xstrcmp(*p, "Ds"))
1.26 kristaps 427: width = 8;
1.28 kristaps 428: else if (MDOC_MAX == (tok = mdoc_find(mdoc, *p)))
1.20 kristaps 429: return(1);
1.24 kristaps 430: else if (0 == (width = mdoc_macro2len(tok)))
1.20 kristaps 431: return(mdoc_warn(mdoc, WARN_SYNTAX,
1.25 kristaps 432: "%s macro has no length",
1.20 kristaps 433: mdoc_argnames[MDOC_Width]));
434:
435: mdoc_msg(mdoc, "re-writing %s argument: %s -> %zun",
1.23 kristaps 436: mdoc_argnames[MDOC_Width], *p, width);
437:
438: /* The value already exists: free and reallocate it. */
1.20 kristaps 439:
440: (void)snprintf(buf, sizeof(buf), "%zun", width);
441:
1.23 kristaps 442: free(*p);
443: *p = strdup(buf);
444:
445: return(1);
446: }
447:
448:
449: static int
450: post_bl(struct mdoc *mdoc)
451: {
452: int i, r;
453:
454: if (MDOC_BLOCK != mdoc->last->type)
455: return(1);
456:
457: /*
458: * These are fairly complicated, so we've broken them into two
459: * functions. post_bl_tagwidth() is called when a -tag is
460: * specified, but no -width (it must be guessed). The second
461: * when a -width is specified (macro indicators must be
462: * rewritten into real lengths).
463: */
464:
465: for (r = i = 0; i < (int)mdoc->last->data.block.argc; i++) {
466: if (MDOC_Tag == mdoc->last->data.block.argv[i].arg)
467: r |= 1 << 0;
468: if (MDOC_Width == mdoc->last->data.block.argv[i].arg)
469: r |= 1 << 1;
470: }
471:
472: if (r & (1 << 0) && ! (r & (1 << 1))) {
473: if ( ! post_bl_tagwidth(mdoc))
474: return(0);
475: } else if (r & (1 << 1))
476: if ( ! post_bl_width(mdoc))
477: return(0);
1.20 kristaps 478:
479: return(1);
480: }
481:
482:
1.4 kristaps 483: static int
1.30 ! kristaps 484: post_ar(struct mdoc *mdoc)
! 485: {
! 486: struct mdoc_node *n;
! 487:
! 488: if (mdoc->last->child)
! 489: return(1);
! 490:
! 491: n = mdoc->last;
! 492:
! 493: mdoc->next = MDOC_NEXT_CHILD;
! 494: mdoc_word_alloc(mdoc, mdoc->last->line,
! 495: mdoc->last->pos, "file");
! 496: mdoc->next = MDOC_NEXT_SIBLING;
! 497: mdoc_word_alloc(mdoc, mdoc->last->line,
! 498: mdoc->last->pos, "...");
! 499:
! 500: mdoc->last = n;
! 501: mdoc->next = MDOC_NEXT_SIBLING;
! 502: return(1);
! 503: }
! 504:
! 505:
! 506: static int
1.4 kristaps 507: post_dd(struct mdoc *mdoc)
508: {
1.15 kristaps 509: char buf[64];
1.4 kristaps 510:
1.23 kristaps 511: /*
512: * Prologue date must be parsed into document meta-data. We
513: * accept multiple kinds of dates, described mostly in
514: * mdoc_atotime().
515: */
516:
1.5 kristaps 517: assert(MDOC_ELEM == mdoc->last->type);
1.7 kristaps 518: assert(MDOC_Dd == mdoc->last->tok);
1.5 kristaps 519:
520: assert(0 == mdoc->meta.date);
1.4 kristaps 521:
1.15 kristaps 522: if ( ! xstrlcats(buf, mdoc->last->child, 64))
523: return(mdoc_err(mdoc, "macro parameters too long"));
524: if (0 == (mdoc->meta.date = mdoc_atotime(buf)))
525: return(mdoc_err(mdoc, "invalid parameter syntax"));
1.5 kristaps 526:
1.15 kristaps 527: mdoc_msg(mdoc, "date: %u", mdoc->meta.date);
1.5 kristaps 528:
1.15 kristaps 529: return(post_prologue(mdoc));
1.4 kristaps 530: }
531:
532:
1.13 kristaps 533: static int
534: post_prologue(struct mdoc *mdoc)
535: {
536: struct mdoc_node *n;
537:
1.23 kristaps 538: /*
539: * The end document shouldn't have the prologue macros as part
540: * of the syntax tree (they encompass only meta-data).
541: */
542:
1.13 kristaps 543: if (mdoc->last->parent->child == mdoc->last)
544: mdoc->last->parent->child = mdoc->last->prev;
545: if (mdoc->last->prev)
546: mdoc->last->prev->next = NULL;
547:
548: n = mdoc->last;
549: assert(NULL == mdoc->last->next);
550:
551: if (mdoc->last->prev) {
552: mdoc->last = mdoc->last->prev;
553: mdoc->next = MDOC_NEXT_SIBLING;
554: } else {
555: mdoc->last = mdoc->last->parent;
556: mdoc->next = MDOC_NEXT_CHILD;
557: }
558:
559: mdoc_node_freelist(n);
560: return(1);
561: }
562:
563:
1.4 kristaps 564: int
565: mdoc_action_post(struct mdoc *mdoc)
1.3 kristaps 566: {
567:
1.12 kristaps 568: if (MDOC_ACTED & mdoc->last->flags)
569: return(1);
570: mdoc->last->flags |= MDOC_ACTED;
571:
1.7 kristaps 572: if (MDOC_TEXT == mdoc->last->type)
573: return(1);
574: if (MDOC_ROOT == mdoc->last->type)
1.3 kristaps 575: return(1);
1.7 kristaps 576: if (NULL == mdoc_actions[mdoc->last->tok].post)
1.3 kristaps 577: return(1);
1.7 kristaps 578: return((*mdoc_actions[mdoc->last->tok].post)(mdoc));
1.3 kristaps 579: }
CVSweb