]> git.cameronkatri.com Git - mandoc.git/blob - action.c
Removed "regression" for OpenBSD (broken).
[mandoc.git] / action.c
1 /* $Id: action.c,v 1.41 2009/03/12 16:30:50 kristaps Exp $ */
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 <sys/utsname.h>
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "private.h"
28
29 /*
30 * Actions are executed on macros after they've been post-validated: in
31 * other words, a macro will not be "acted upon" until all of its
32 * children have been filled in (post-fix order).
33 */
34
35 enum merr {
36 ENOWIDTH
37 };
38
39 enum mwarn {
40 WBADSEC,
41 WNOWIDTH,
42 WBADDATE
43 };
44
45 struct actions {
46 int (*post)(struct mdoc *);
47 };
48
49 static int nwarn(struct mdoc *,
50 const struct mdoc_node *, enum mwarn);
51 static int nerr(struct mdoc *,
52 const struct mdoc_node *, enum merr);
53 static int post_ar(struct mdoc *);
54 static int post_bl(struct mdoc *);
55 static int post_bl_width(struct mdoc *);
56 static int post_bl_tagwidth(struct mdoc *);
57 static int post_dd(struct mdoc *);
58 static int post_dt(struct mdoc *);
59 static int post_nm(struct mdoc *);
60 static int post_os(struct mdoc *);
61 static int post_sh(struct mdoc *);
62 static int post_std(struct mdoc *);
63 static int post_prologue(struct mdoc *);
64
65 const struct actions mdoc_actions[MDOC_MAX] = {
66 { NULL }, /* \" */
67 { post_dd }, /* Dd */
68 { post_dt }, /* Dt */
69 { post_os }, /* Os */
70 { post_sh }, /* Sh */
71 { NULL }, /* Ss */
72 { NULL }, /* Pp */
73 { NULL }, /* D1 */
74 { NULL }, /* Dl */
75 { NULL }, /* Bd */
76 { NULL }, /* Ed */
77 { post_bl }, /* Bl */
78 { NULL }, /* El */
79 { NULL }, /* It */
80 { NULL }, /* Ad */
81 { NULL }, /* An */
82 { post_ar }, /* Ar */
83 { NULL }, /* Cd */
84 { NULL }, /* Cm */
85 { NULL }, /* Dv */
86 { NULL }, /* Er */
87 { NULL }, /* Ev */
88 { post_std }, /* Ex */
89 { NULL }, /* Fa */
90 { NULL }, /* Fd */
91 { NULL }, /* Fl */
92 { NULL }, /* Fn */
93 { NULL }, /* Ft */
94 { NULL }, /* Ic */
95 { NULL }, /* In */
96 { NULL }, /* Li */
97 { NULL }, /* Nd */
98 { post_nm }, /* Nm */
99 { NULL }, /* Op */
100 { NULL }, /* Ot */
101 { NULL }, /* Pa */
102 { post_std }, /* Rv */
103 { NULL }, /* St */
104 { NULL }, /* Va */
105 { NULL }, /* Vt */
106 { NULL }, /* Xr */
107 { NULL }, /* %A */
108 { NULL }, /* %B */
109 { NULL }, /* %D */
110 { NULL }, /* %I */
111 { NULL }, /* %J */
112 { NULL }, /* %N */
113 { NULL }, /* %O */
114 { NULL }, /* %P */
115 { NULL }, /* %R */
116 { NULL }, /* %T */
117 { NULL }, /* %V */
118 { NULL }, /* Ac */
119 { NULL }, /* Ao */
120 { NULL }, /* Aq */
121 { NULL }, /* At */
122 { NULL }, /* Bc */
123 { NULL }, /* Bf */
124 { NULL }, /* Bo */
125 { NULL }, /* Bq */
126 { NULL }, /* Bsx */
127 { NULL }, /* Bx */
128 { NULL }, /* Db */
129 { NULL }, /* Dc */
130 { NULL }, /* Do */
131 { NULL }, /* Dq */
132 { NULL }, /* Ec */
133 { NULL }, /* Ef */
134 { NULL }, /* Em */
135 { NULL }, /* Eo */
136 { NULL }, /* Fx */
137 { NULL }, /* Ms */
138 { NULL }, /* No */
139 { NULL }, /* Ns */
140 { NULL }, /* Nx */
141 { NULL }, /* Ox */
142 { NULL }, /* Pc */
143 { NULL }, /* Pf */
144 { NULL }, /* Po */
145 { NULL }, /* Pq */
146 { NULL }, /* Qc */
147 { NULL }, /* Ql */
148 { NULL }, /* Qo */
149 { NULL }, /* Qq */
150 { NULL }, /* Re */
151 { NULL }, /* Rs */
152 { NULL }, /* Sc */
153 { NULL }, /* So */
154 { NULL }, /* Sq */
155 { NULL }, /* Sm */
156 { NULL }, /* Sx */
157 { NULL }, /* Sy */
158 { NULL }, /* Tn */
159 { NULL }, /* Ux */
160 { NULL }, /* Xc */
161 { NULL }, /* Xo */
162 { NULL }, /* Fo */
163 { NULL }, /* Fc */
164 { NULL }, /* Oo */
165 { NULL }, /* Oc */
166 { NULL }, /* Bk */
167 { NULL }, /* Ek */
168 { NULL }, /* Bt */
169 { NULL }, /* Hf */
170 { NULL }, /* Fr */
171 { NULL }, /* Ud */
172 { NULL }, /* Lb */
173 { NULL }, /* Ap */
174 { NULL }, /* Lp */
175 { NULL }, /* Lk */
176 { NULL }, /* Mt */
177 { NULL }, /* Brq */
178 { NULL }, /* Bro */
179 { NULL }, /* Brc */
180 { NULL }, /* %C */
181 };
182
183
184 #define merr(m, t) nerr((m), (m)->last, (t))
185 static int
186 nerr(struct mdoc *m, const struct mdoc_node *n, enum merr type)
187 {
188 char *p;
189
190 p = NULL;
191
192 switch (type) {
193 case (ENOWIDTH):
194 p = "missing width argument";
195 break;
196 }
197
198 assert(p);
199 return(mdoc_nerr(m, n, p));
200 }
201
202
203 #define mwarn(m, t) nwarn((m), (m)->last, (t))
204 static int
205 nwarn(struct mdoc *m, const struct mdoc_node *n, enum mwarn type)
206 {
207 char *p;
208 int c;
209
210 p = NULL;
211 c = WARN_SYNTAX;
212
213 switch (type) {
214 case (WBADSEC):
215 p = "inappropriate document section in manual section";
216 c = WARN_COMPAT;
217 break;
218 case (WNOWIDTH):
219 p = "cannot determine default width";
220 break;
221 case (WBADDATE):
222 p = "malformed date syntax";
223 break;
224 }
225
226 assert(p);
227 return(mdoc_nwarn(m, n, c, p));
228 }
229
230
231 static int
232 post_std(struct mdoc *mdoc)
233 {
234
235 /*
236 * If '-std' is invoked without an argument, fill it in with our
237 * name (if it's been set).
238 */
239
240 if (NULL == mdoc->last->args)
241 return(1);
242 if (mdoc->last->args->argv[0].sz)
243 return(1);
244
245 assert(mdoc->meta.name);
246
247 mdoc_msg(mdoc, "writing %s argument: %s",
248 mdoc_argnames[MDOC_Std],
249 mdoc->meta.name);
250
251 mdoc->last->args->argv[0].value = xcalloc(1, sizeof(char *));
252 mdoc->last->args->argv[0].sz = 1;
253 mdoc->last->args->argv[0].value[0] = xstrdup(mdoc->meta.name);
254 return(1);
255 }
256
257
258 static int
259 post_nm(struct mdoc *mdoc)
260 {
261 char buf[64];
262
263 if (mdoc->meta.name)
264 return(1);
265
266 (void)xstrlcpys(buf, mdoc->last->child, sizeof(buf));
267 mdoc->meta.name = xstrdup(buf);
268 mdoc_msg(mdoc, "name: %s", mdoc->meta.name);
269
270 return(1);
271 }
272
273
274 static int
275 post_sh(struct mdoc *mdoc)
276 {
277 enum mdoc_sec sec;
278 char buf[64];
279
280 /*
281 * We keep track of the current section /and/ the "named"
282 * section, which is one of the conventional ones, in order to
283 * check ordering.
284 */
285
286 if (MDOC_HEAD != mdoc->last->type)
287 return(1);
288
289 (void)xstrlcpys(buf, mdoc->last->child, sizeof(buf));
290 if (SEC_CUSTOM != (sec = mdoc_atosec(buf)))
291 mdoc->lastnamed = sec;
292
293 mdoc->lastsec = sec;
294
295 switch (mdoc->lastsec) {
296 case (SEC_RETURN_VALUES):
297 /* FALLTHROUGH */
298 case (SEC_ERRORS):
299 switch (mdoc->meta.msec) {
300 case (2):
301 /* FALLTHROUGH */
302 case (3):
303 /* FALLTHROUGH */
304 case (9):
305 break;
306 default:
307 return(mwarn(mdoc, WBADSEC));
308 }
309 break;
310 default:
311 break;
312 }
313 return(1);
314 }
315
316
317 static int
318 post_dt(struct mdoc *mdoc)
319 {
320 struct mdoc_node *n;
321 const char *cp;
322 char *ep;
323 long lval;
324
325 if (mdoc->meta.title)
326 free(mdoc->meta.title);
327 if (mdoc->meta.vol)
328 free(mdoc->meta.vol);
329 if (mdoc->meta.arch)
330 free(mdoc->meta.arch);
331
332 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
333 mdoc->meta.msec = 0;
334
335 /* Handles: `.Dt'
336 * --> title = unknown, volume = local, msec = 0, arch = NULL
337 */
338
339 if (NULL == (n = mdoc->last->child)) {
340 mdoc->meta.title = xstrdup("unknown");
341 mdoc->meta.vol = xstrdup("local");
342 mdoc_msg(mdoc, "title: %s", mdoc->meta.title);
343 mdoc_msg(mdoc, "volume: %s", mdoc->meta.vol);
344 mdoc_msg(mdoc, "arch: <unset>");
345 mdoc_msg(mdoc, "msec: <unset>");
346 return(post_prologue(mdoc));
347 }
348
349 /* Handles: `.Dt TITLE'
350 * --> title = TITLE, volume = local, msec = 0, arch = NULL
351 */
352
353 mdoc->meta.title = xstrdup(n->string);
354 mdoc_msg(mdoc, "title: %s", mdoc->meta.title);
355
356 if (NULL == (n = n->next)) {
357 mdoc->meta.vol = xstrdup("local");
358 mdoc_msg(mdoc, "volume: %s", mdoc->meta.vol);
359 mdoc_msg(mdoc, "arch: <unset>");
360 mdoc_msg(mdoc, "msec: %d", mdoc->meta.msec);
361 return(post_prologue(mdoc));
362 }
363
364 /* Handles: `.Dt TITLE SEC'
365 * --> title = TITLE, volume = SEC is msec ?
366 * format(msec) : SEC,
367 * msec = SEC is msec ? atoi(msec) : 0,
368 * arch = NULL
369 */
370
371 if ((cp = mdoc_a2msec(n->string))) {
372 mdoc->meta.vol = xstrdup(cp);
373 errno = 0;
374 lval = strtol(n->string, &ep, 10);
375 if (n->string[0] != '\0' && *ep == '\0')
376 mdoc->meta.msec = (int)lval;
377 } else
378 mdoc->meta.vol = xstrdup(n->string);
379
380 if (NULL == (n = n->next)) {
381 mdoc_msg(mdoc, "volume: %s", mdoc->meta.vol);
382 mdoc_msg(mdoc, "arch: <unset>");
383 mdoc_msg(mdoc, "msec: %d", mdoc->meta.msec);
384 return(post_prologue(mdoc));
385 }
386
387 /* Handles: `.Dt TITLE SEC VOL'
388 * --> title = TITLE, volume = VOL is vol ?
389 * format(VOL) :
390 * VOL is arch ? format(arch) :
391 * VOL
392 */
393
394 if ((cp = mdoc_a2vol(n->string))) {
395 free(mdoc->meta.vol);
396 mdoc->meta.vol = xstrdup(cp);
397 n = n->next;
398 } else {
399 cp = mdoc_a2arch(n->string);
400 if (NULL == cp) {
401 free(mdoc->meta.vol);
402 mdoc->meta.vol = xstrdup(n->string);
403 } else
404 mdoc->meta.arch = xstrdup(cp);
405 }
406
407 mdoc_msg(mdoc, "volume: %s", mdoc->meta.vol);
408 mdoc_msg(mdoc, "arch: %s", mdoc->meta.arch ?
409 mdoc->meta.arch : "<unset>");
410 mdoc_msg(mdoc, "msec: %d", mdoc->meta.msec);
411
412 /* Ignore any subsequent parameters... */
413
414 return(post_prologue(mdoc));
415 }
416
417
418 static int
419 post_os(struct mdoc *mdoc)
420 {
421 char buf[64];
422 struct utsname utsname;
423
424 if (mdoc->meta.os)
425 free(mdoc->meta.os);
426
427 (void)xstrlcpys(buf, mdoc->last->child, sizeof(buf));
428
429 if (0 == buf[0]) {
430 if (-1 == uname(&utsname))
431 return(mdoc_err(mdoc, "utsname"));
432 (void)xstrlcpy(buf, utsname.sysname, sizeof(buf));
433 (void)xstrlcat(buf, " ", sizeof(buf));
434 (void)xstrlcat(buf, utsname.release, sizeof(buf));
435 }
436
437 mdoc->meta.os = xstrdup(buf);
438 mdoc_msg(mdoc, "system: %s", mdoc->meta.os);
439
440 mdoc->lastnamed = mdoc->lastsec = SEC_BODY;
441
442 return(post_prologue(mdoc));
443 }
444
445
446 static int
447 post_bl_tagwidth(struct mdoc *mdoc)
448 {
449 struct mdoc_node *n;
450 int sz;
451 char buf[32];
452
453 /*
454 * If -tag has been specified and -width has not been, then try
455 * to intuit our width from the first body element.
456 */
457
458 if (NULL == (n = mdoc->last->body->child))
459 return(1);
460
461 /*
462 * Use the text width, if a text node, or the default macro
463 * width if a macro.
464 */
465
466 if ((n = n->head->child)) {
467 if (MDOC_TEXT != n->type) {
468 if (0 == (sz = (int)mdoc_macro2len(n->tok)))
469 sz = -1;
470 } else
471 sz = (int)strlen(n->string) + 1;
472 } else
473 sz = -1;
474
475 if (-1 == sz) {
476 if ( ! mwarn(mdoc, WNOWIDTH))
477 return(0);
478 sz = 10;
479 }
480
481 (void)snprintf(buf, sizeof(buf), "%dn", sz);
482
483 /*
484 * We have to dynamically add this to the macro's argument list.
485 * We're guaranteed that a MDOC_Width doesn't already exist.
486 */
487
488 if (NULL == mdoc->last->args) {
489 mdoc->last->args = xcalloc
490 (1, sizeof(struct mdoc_arg));
491 mdoc->last->args->refcnt = 1;
492 }
493
494 n = mdoc->last;
495 sz = (int)n->args->argc;
496
497 (n->args->argc)++;
498
499 n->args->argv = xrealloc(n->args->argv,
500 n->args->argc * sizeof(struct mdoc_arg));
501
502 n->args->argv[sz - 1].arg = MDOC_Width;
503 n->args->argv[sz - 1].line = mdoc->last->line;
504 n->args->argv[sz - 1].pos = mdoc->last->pos;
505 n->args->argv[sz - 1].sz = 1;
506 n->args->argv[sz - 1].value = xcalloc(1, sizeof(char *));
507 n->args->argv[sz - 1].value[0] = xstrdup(buf);
508
509 mdoc_msg(mdoc, "adding %s argument: %s",
510 mdoc_argnames[MDOC_Width], buf);
511
512 return(1);
513 }
514
515
516 static int
517 post_bl_width(struct mdoc *m)
518 {
519 size_t width;
520 int i, tok;
521 char buf[32];
522 char *p;
523
524 if (NULL == m->last->args)
525 return(merr(m, ENOWIDTH));
526
527 for (i = 0; i < (int)m->last->args->argc; i++)
528 if (MDOC_Width == m->last->args->argv[i].arg)
529 break;
530
531 if (i == (int)m->last->args->argc)
532 return(merr(m, ENOWIDTH));
533
534 p = m->last->args->argv[i].value[0];
535
536 /*
537 * If the value to -width is a macro, then we re-write it to be
538 * the macro's width as set in share/tmac/mdoc/doc-common.
539 */
540
541 if (xstrcmp(p, "Ds"))
542 width = 8;
543 else if (MDOC_MAX == (tok = mdoc_tokhash_find(m->htab, p)))
544 return(1);
545 else if (0 == (width = mdoc_macro2len(tok)))
546 return(mwarn(m, WNOWIDTH));
547
548 mdoc_msg(m, "re-writing %s argument: %s -> %zun",
549 mdoc_argnames[MDOC_Width], p, width);
550
551 /* The value already exists: free and reallocate it. */
552
553 (void)snprintf(buf, sizeof(buf), "%zun", width);
554
555 free(m->last->args->argv[i].value[0]);
556 m->last->args->argv[i].value[0] = xstrdup(buf);
557
558 return(1);
559 }
560
561
562 static int
563 post_bl(struct mdoc *mdoc)
564 {
565 int i, r, len;
566
567 if (MDOC_BLOCK != mdoc->last->type)
568 return(1);
569
570 /*
571 * These are fairly complicated, so we've broken them into two
572 * functions. post_bl_tagwidth() is called when a -tag is
573 * specified, but no -width (it must be guessed). The second
574 * when a -width is specified (macro indicators must be
575 * rewritten into real lengths).
576 */
577
578 len = (int)(mdoc->last->args ? mdoc->last->args->argc : 0);
579
580 for (r = i = 0; i < len; i++) {
581 if (MDOC_Tag == mdoc->last->args->argv[i].arg)
582 r |= 1 << 0;
583 if (MDOC_Width == mdoc->last->args->argv[i].arg)
584 r |= 1 << 1;
585 }
586
587 if (r & (1 << 0) && ! (r & (1 << 1))) {
588 if ( ! post_bl_tagwidth(mdoc))
589 return(0);
590 } else if (r & (1 << 1))
591 if ( ! post_bl_width(mdoc))
592 return(0);
593
594 return(1);
595 }
596
597
598 static int
599 post_ar(struct mdoc *mdoc)
600 {
601 struct mdoc_node *n;
602
603 if (mdoc->last->child)
604 return(1);
605
606 n = mdoc->last;
607
608 mdoc->next = MDOC_NEXT_CHILD;
609 if ( ! mdoc_word_alloc(mdoc, mdoc->last->line,
610 mdoc->last->pos, "file"))
611 return(0);
612 mdoc->next = MDOC_NEXT_SIBLING;
613 if ( ! mdoc_word_alloc(mdoc, mdoc->last->line,
614 mdoc->last->pos, "..."))
615 return(0);
616
617 mdoc->last = n;
618 mdoc->next = MDOC_NEXT_SIBLING;
619 return(1);
620 }
621
622
623 static int
624 post_dd(struct mdoc *mdoc)
625 {
626 char buf[64];
627
628 (void)xstrlcpys(buf, mdoc->last->child, sizeof(buf));
629
630 if (0 == (mdoc->meta.date = mdoc_atotime(buf))) {
631 if ( ! mwarn(mdoc, WBADDATE))
632 return(0);
633 mdoc->meta.date = time(NULL);
634 }
635
636 mdoc_msg(mdoc, "date: %u", mdoc->meta.date);
637 return(post_prologue(mdoc));
638 }
639
640
641 static int
642 post_prologue(struct mdoc *mdoc)
643 {
644 struct mdoc_node *n;
645
646 /*
647 * The end document shouldn't have the prologue macros as part
648 * of the syntax tree (they encompass only meta-data).
649 */
650
651 if (mdoc->last->parent->child == mdoc->last)
652 mdoc->last->parent->child = mdoc->last->prev;
653 if (mdoc->last->prev)
654 mdoc->last->prev->next = NULL;
655
656 n = mdoc->last;
657 assert(NULL == mdoc->last->next);
658
659 if (mdoc->last->prev) {
660 mdoc->last = mdoc->last->prev;
661 mdoc->next = MDOC_NEXT_SIBLING;
662 } else {
663 mdoc->last = mdoc->last->parent;
664 mdoc->next = MDOC_NEXT_CHILD;
665 }
666
667 mdoc_node_freelist(n);
668 return(1);
669 }
670
671
672 int
673 mdoc_action_post(struct mdoc *mdoc)
674 {
675
676 if (MDOC_ACTED & mdoc->last->flags)
677 return(1);
678 mdoc->last->flags |= MDOC_ACTED;
679
680 if (MDOC_TEXT == mdoc->last->type)
681 return(1);
682 if (MDOC_ROOT == mdoc->last->type)
683 return(1);
684 if (NULL == mdoc_actions[mdoc->last->tok].post)
685 return(1);
686 return((*mdoc_actions[mdoc->last->tok].post)(mdoc));
687 }