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