]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_action.c
f4cff48694c5b9f5921a3b79f82465167bae13d4
[mandoc.git] / mdoc_action.c
1 /* $Id: mdoc_action.c,v 1.86 2010/11/30 10:32:05 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
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 above
7 * copyright notice and this permission notice appear in all copies.
8 *
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.
16 */
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #ifndef OSNAME
22 #include <sys/utsname.h>
23 #endif
24
25 #include <assert.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30
31 #include "mandoc.h"
32 #include "libmdoc.h"
33 #include "libmandoc.h"
34
35 /*
36 * FIXME: this file is deprecated. All future "actions" should be
37 * pushed into mdoc_validate.c.
38 */
39
40 #define POST_ARGS struct mdoc *m, struct mdoc_node *n
41 #define PRE_ARGS struct mdoc *m, struct mdoc_node *n
42
43 #define NUMSIZ 32
44 #define DATESIZ 32
45
46 struct actions {
47 int (*pre)(PRE_ARGS);
48 int (*post)(POST_ARGS);
49 };
50
51 static int concat(struct mdoc *, char *,
52 const struct mdoc_node *, size_t);
53
54 static int post_bl(POST_ARGS);
55 static int post_bl_tagwidth(POST_ARGS);
56 static int post_bl_width(POST_ARGS);
57 static int post_dd(POST_ARGS);
58 static int post_dt(POST_ARGS);
59 static int post_os(POST_ARGS);
60 static int post_prol(POST_ARGS);
61 static int post_std(POST_ARGS);
62
63 static const struct actions mdoc_actions[MDOC_MAX] = {
64 { NULL, NULL }, /* Ap */
65 { NULL, post_dd }, /* Dd */
66 { NULL, post_dt }, /* Dt */
67 { NULL, post_os }, /* Os */
68 { NULL, NULL }, /* Sh */
69 { NULL, NULL }, /* Ss */
70 { NULL, NULL }, /* Pp */
71 { NULL, NULL }, /* D1 */
72 { NULL, NULL }, /* Dl */
73 { NULL, NULL }, /* Bd */
74 { NULL, NULL }, /* Ed */
75 { NULL, post_bl }, /* Bl */
76 { NULL, NULL }, /* El */
77 { NULL, NULL }, /* It */
78 { NULL, NULL }, /* Ad */
79 { NULL, NULL }, /* An */
80 { NULL, NULL }, /* Ar */
81 { NULL, NULL }, /* Cd */
82 { NULL, NULL }, /* Cm */
83 { NULL, NULL }, /* Dv */
84 { NULL, NULL }, /* Er */
85 { NULL, NULL }, /* Ev */
86 { NULL, post_std }, /* Ex */
87 { NULL, NULL }, /* Fa */
88 { NULL, NULL }, /* Fd */
89 { NULL, NULL }, /* Fl */
90 { NULL, NULL }, /* Fn */
91 { NULL, NULL }, /* Ft */
92 { NULL, NULL }, /* Ic */
93 { NULL, NULL }, /* In */
94 { NULL, NULL }, /* Li */
95 { NULL, NULL }, /* Nd */
96 { NULL, NULL }, /* Nm */
97 { NULL, NULL }, /* Op */
98 { NULL, NULL }, /* Ot */
99 { NULL, NULL }, /* Pa */
100 { NULL, post_std }, /* Rv */
101 { NULL, NULL }, /* St */
102 { NULL, NULL }, /* Va */
103 { NULL, NULL }, /* Vt */
104 { NULL, NULL }, /* Xr */
105 { NULL, NULL }, /* %A */
106 { NULL, NULL }, /* %B */
107 { NULL, NULL }, /* %D */
108 { NULL, NULL }, /* %I */
109 { NULL, NULL }, /* %J */
110 { NULL, NULL }, /* %N */
111 { NULL, NULL }, /* %O */
112 { NULL, NULL }, /* %P */
113 { NULL, NULL }, /* %R */
114 { NULL, NULL }, /* %T */
115 { NULL, NULL }, /* %V */
116 { NULL, NULL }, /* Ac */
117 { NULL, NULL }, /* Ao */
118 { NULL, NULL }, /* Aq */
119 { NULL, NULL }, /* At */
120 { NULL, NULL }, /* Bc */
121 { NULL, NULL }, /* Bf */
122 { NULL, NULL }, /* Bo */
123 { NULL, NULL }, /* Bq */
124 { NULL, NULL }, /* Bsx */
125 { NULL, NULL }, /* Bx */
126 { NULL, NULL }, /* Db */
127 { NULL, NULL }, /* Dc */
128 { NULL, NULL }, /* Do */
129 { NULL, NULL }, /* Dq */
130 { NULL, NULL }, /* Ec */
131 { NULL, NULL }, /* Ef */
132 { NULL, NULL }, /* Em */
133 { NULL, NULL }, /* Eo */
134 { NULL, NULL }, /* Fx */
135 { NULL, NULL }, /* Ms */
136 { NULL, NULL }, /* No */
137 { NULL, NULL }, /* Ns */
138 { NULL, NULL }, /* Nx */
139 { NULL, NULL }, /* Ox */
140 { NULL, NULL }, /* Pc */
141 { NULL, NULL }, /* Pf */
142 { NULL, NULL }, /* Po */
143 { NULL, NULL }, /* Pq */
144 { NULL, NULL }, /* Qc */
145 { NULL, NULL }, /* Ql */
146 { NULL, NULL }, /* Qo */
147 { NULL, NULL }, /* Qq */
148 { NULL, NULL }, /* Re */
149 { NULL, NULL }, /* Rs */
150 { NULL, NULL }, /* Sc */
151 { NULL, NULL }, /* So */
152 { NULL, NULL }, /* Sq */
153 { NULL, NULL }, /* Sm */
154 { NULL, NULL }, /* Sx */
155 { NULL, NULL }, /* Sy */
156 { NULL, NULL }, /* Tn */
157 { NULL, NULL }, /* Ux */
158 { NULL, NULL }, /* Xc */
159 { NULL, NULL }, /* Xo */
160 { NULL, NULL }, /* Fo */
161 { NULL, NULL }, /* Fc */
162 { NULL, NULL }, /* Oo */
163 { NULL, NULL }, /* Oc */
164 { NULL, NULL }, /* Bk */
165 { NULL, NULL }, /* Ek */
166 { NULL, NULL }, /* Bt */
167 { NULL, NULL }, /* Hf */
168 { NULL, NULL }, /* Fr */
169 { NULL, NULL }, /* Ud */
170 { NULL, NULL }, /* Lb */
171 { NULL, NULL }, /* Lp */
172 { NULL, NULL }, /* Lk */
173 { NULL, NULL }, /* Mt */
174 { NULL, NULL }, /* Brq */
175 { NULL, NULL }, /* Bro */
176 { NULL, NULL }, /* Brc */
177 { NULL, NULL }, /* %C */
178 { NULL, NULL }, /* Es */
179 { NULL, NULL }, /* En */
180 { NULL, NULL }, /* Dx */
181 { NULL, NULL }, /* %Q */
182 { NULL, NULL }, /* br */
183 { NULL, NULL }, /* sp */
184 { NULL, NULL }, /* %U */
185 { NULL, NULL }, /* Ta */
186 };
187
188
189 int
190 mdoc_action_pre(struct mdoc *m, struct mdoc_node *n)
191 {
192
193 switch (n->type) {
194 case (MDOC_ROOT):
195 /* FALLTHROUGH */
196 case (MDOC_TEXT):
197 return(1);
198 default:
199 break;
200 }
201
202 if (NULL == mdoc_actions[n->tok].pre)
203 return(1);
204 return((*mdoc_actions[n->tok].pre)(m, n));
205 }
206
207
208 int
209 mdoc_action_post(struct mdoc *m)
210 {
211
212 if (MDOC_ACTED & m->last->flags)
213 return(1);
214 m->last->flags |= MDOC_ACTED;
215
216 switch (m->last->type) {
217 case (MDOC_TEXT):
218 /* FALLTHROUGH */
219 case (MDOC_ROOT):
220 return(1);
221 default:
222 break;
223 }
224
225 if (NULL == mdoc_actions[m->last->tok].post)
226 return(1);
227 return((*mdoc_actions[m->last->tok].post)(m, m->last));
228 }
229
230
231 /*
232 * Concatenate sibling nodes together. All siblings must be of type
233 * MDOC_TEXT or an assertion is raised. Concatenation is separated by a
234 * single whitespace.
235 */
236 static int
237 concat(struct mdoc *m, char *p, const struct mdoc_node *n, size_t sz)
238 {
239
240 assert(sz);
241 p[0] = '\0';
242 for ( ; n; n = n->next) {
243 assert(MDOC_TEXT == n->type);
244 /*
245 * XXX: yes, these can technically be resized, but it's
246 * highly unlikely that we're going to get here, so let
247 * it slip for now.
248 */
249 if (strlcat(p, n->string, sz) >= sz) {
250 mdoc_nmsg(m, n, MANDOCERR_MEM);
251 return(0);
252 }
253 if (NULL == n->next)
254 continue;
255 if (strlcat(p, " ", sz) >= sz) {
256 mdoc_nmsg(m, n, MANDOCERR_MEM);
257 return(0);
258 }
259 }
260
261 return(1);
262 }
263
264
265 /*
266 * Macros accepting `-std' as an argument have the name of the current
267 * document (`Nm') filled in as the argument if it's not provided.
268 */
269 static int
270 post_std(POST_ARGS)
271 {
272 struct mdoc_node *nn;
273
274 if (n->child)
275 return(1);
276 if (NULL == m->meta.name)
277 return(1);
278
279 nn = n;
280 m->next = MDOC_NEXT_CHILD;
281
282 if ( ! mdoc_word_alloc(m, n->line, n->pos, m->meta.name))
283 return(0);
284 m->last = nn;
285 return(1);
286 }
287
288 /*
289 * Parse out the contents of `Dt'. See in-line documentation for how we
290 * handle the various fields of this macro.
291 */
292 static int
293 post_dt(POST_ARGS)
294 {
295 struct mdoc_node *nn;
296 const char *cp;
297
298 if (m->meta.title)
299 free(m->meta.title);
300 if (m->meta.vol)
301 free(m->meta.vol);
302 if (m->meta.arch)
303 free(m->meta.arch);
304
305 m->meta.title = m->meta.vol = m->meta.arch = NULL;
306 /* Handles: `.Dt'
307 * --> title = unknown, volume = local, msec = 0, arch = NULL
308 */
309
310 if (NULL == (nn = n->child)) {
311 /* XXX: make these macro values. */
312 /* FIXME: warn about missing values. */
313 m->meta.title = mandoc_strdup("UNKNOWN");
314 m->meta.vol = mandoc_strdup("LOCAL");
315 m->meta.msec = mandoc_strdup("1");
316 return(post_prol(m, n));
317 }
318
319 /* Handles: `.Dt TITLE'
320 * --> title = TITLE, volume = local, msec = 0, arch = NULL
321 */
322
323 m->meta.title = mandoc_strdup
324 ('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
325
326 if (NULL == (nn = nn->next)) {
327 /* FIXME: warn about missing msec. */
328 /* XXX: make this a macro value. */
329 m->meta.vol = mandoc_strdup("LOCAL");
330 m->meta.msec = mandoc_strdup("1");
331 return(post_prol(m, n));
332 }
333
334 /* Handles: `.Dt TITLE SEC'
335 * --> title = TITLE, volume = SEC is msec ?
336 * format(msec) : SEC,
337 * msec = SEC is msec ? atoi(msec) : 0,
338 * arch = NULL
339 */
340
341 cp = mdoc_a2msec(nn->string);
342 if (cp) {
343 m->meta.vol = mandoc_strdup(cp);
344 m->meta.msec = mandoc_strdup(nn->string);
345 } else if (mdoc_nmsg(m, n, MANDOCERR_BADMSEC)) {
346 m->meta.vol = mandoc_strdup(nn->string);
347 m->meta.msec = mandoc_strdup(nn->string);
348 } else
349 return(0);
350
351 if (NULL == (nn = nn->next))
352 return(post_prol(m, n));
353
354 /* Handles: `.Dt TITLE SEC VOL'
355 * --> title = TITLE, volume = VOL is vol ?
356 * format(VOL) :
357 * VOL is arch ? format(arch) :
358 * VOL
359 */
360
361 cp = mdoc_a2vol(nn->string);
362 if (cp) {
363 free(m->meta.vol);
364 m->meta.vol = mandoc_strdup(cp);
365 } else {
366 /* FIXME: warn about bad arch. */
367 cp = mdoc_a2arch(nn->string);
368 if (NULL == cp) {
369 free(m->meta.vol);
370 m->meta.vol = mandoc_strdup(nn->string);
371 } else
372 m->meta.arch = mandoc_strdup(cp);
373 }
374
375 /* Ignore any subsequent parameters... */
376 /* FIXME: warn about subsequent parameters. */
377
378 return(post_prol(m, n));
379 }
380
381
382 /*
383 * Set the operating system by way of the `Os' macro. Note that if an
384 * argument isn't provided and -DOSNAME="\"foo\"" is provided during
385 * compilation, this value will be used instead of filling in "sysname
386 * release" from uname().
387 */
388 static int
389 post_os(POST_ARGS)
390 {
391 char buf[BUFSIZ];
392 #ifndef OSNAME
393 struct utsname utsname;
394 #endif
395
396 if (m->meta.os)
397 free(m->meta.os);
398
399 if ( ! concat(m, buf, n->child, BUFSIZ))
400 return(0);
401
402 /* XXX: yes, these can all be dynamically-adjusted buffers, but
403 * it's really not worth the extra hackery.
404 */
405
406 if ('\0' == buf[0]) {
407 #ifdef OSNAME
408 if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
409 mdoc_nmsg(m, n, MANDOCERR_MEM);
410 return(0);
411 }
412 #else /*!OSNAME */
413 if (-1 == uname(&utsname))
414 return(mdoc_nmsg(m, n, MANDOCERR_UTSNAME));
415
416 if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
417 mdoc_nmsg(m, n, MANDOCERR_MEM);
418 return(0);
419 }
420 if (strlcat(buf, " ", 64) >= BUFSIZ) {
421 mdoc_nmsg(m, n, MANDOCERR_MEM);
422 return(0);
423 }
424 if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
425 mdoc_nmsg(m, n, MANDOCERR_MEM);
426 return(0);
427 }
428 #endif /*!OSNAME*/
429 }
430
431 m->meta.os = mandoc_strdup(buf);
432 return(post_prol(m, n));
433 }
434
435
436 /*
437 * Calculate the -width for a `Bl -tag' list if it hasn't been provided.
438 * Uses the first head macro. NOTE AGAIN: this is ONLY if the -width
439 * argument has NOT been provided. See post_bl_width() for converting
440 * the -width string.
441 */
442 static int
443 post_bl_tagwidth(POST_ARGS)
444 {
445 struct mdoc_node *nn;
446 size_t sz, ssz;
447 int i;
448 char buf[NUMSIZ];
449
450 sz = 10;
451
452 for (nn = n->body->child; nn; nn = nn->next) {
453 if (MDOC_It != nn->tok)
454 continue;
455
456 assert(MDOC_BLOCK == nn->type);
457 nn = nn->head->child;
458
459 if (nn == NULL) {
460 /* No -width for .Bl and first .It is emtpy */
461 if ( ! mdoc_nmsg(m, n, MANDOCERR_NOWIDTHARG))
462 return(0);
463 break;
464 }
465
466 if (MDOC_TEXT == nn->type) {
467 sz = strlen(nn->string) + 1;
468 break;
469 }
470
471 if (0 != (ssz = mdoc_macro2len(nn->tok)))
472 sz = ssz;
473 else if ( ! mdoc_nmsg(m, n, MANDOCERR_NOWIDTHARG))
474 return(0);
475
476 break;
477 }
478
479 /* Defaults to ten ens. */
480
481 snprintf(buf, NUMSIZ, "%zun", 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 assert(n->args);
489 i = (int)(n->args->argc)++;
490
491 n->args->argv = mandoc_realloc(n->args->argv,
492 n->args->argc * sizeof(struct mdoc_argv));
493
494 n->args->argv[i].arg = MDOC_Width;
495 n->args->argv[i].line = n->line;
496 n->args->argv[i].pos = n->pos;
497 n->args->argv[i].sz = 1;
498 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
499 n->args->argv[i].value[0] = mandoc_strdup(buf);
500
501 /* Set our width! */
502 n->data.Bl->width = n->args->argv[i].value[0];
503 return(1);
504 }
505
506
507 /*
508 * Calculate the real width of a list from the -width string, which may
509 * contain a macro (with a known default width), a literal string, or a
510 * scaling width.
511 */
512 static int
513 post_bl_width(POST_ARGS)
514 {
515 size_t width;
516 int i;
517 enum mdoct tok;
518 char buf[NUMSIZ];
519
520 /*
521 * If the value to -width is a macro, then we re-write it to be
522 * the macro's width as set in share/tmac/mdoc/doc-common.
523 */
524
525 if (0 == strcmp(n->data.Bl->width, "Ds"))
526 width = 6;
527 else if (MDOC_MAX == (tok = mdoc_hash_find(n->data.Bl->width)))
528 return(1);
529 else if (0 == (width = mdoc_macro2len(tok)))
530 return(mdoc_nmsg(m, n, MANDOCERR_BADWIDTH));
531
532 /* The value already exists: free and reallocate it. */
533
534 assert(n->args);
535
536 for (i = 0; i < (int)n->args->argc; i++)
537 if (MDOC_Width == n->args->argv[i].arg)
538 break;
539
540 assert(i < (int)n->args->argc);
541
542 snprintf(buf, NUMSIZ, "%zun", width);
543 free(n->args->argv[i].value[0]);
544 n->args->argv[i].value[0] = mandoc_strdup(buf);
545
546 /* Set our width! */
547 n->data.Bl->width = n->args->argv[i].value[0];
548 return(1);
549 }
550
551
552 static int
553 post_bl(POST_ARGS)
554 {
555
556 if (MDOC_BLOCK != n->type)
557 return(1);
558
559 /*
560 * These are fairly complicated, so we've broken them into two
561 * functions. post_bl_tagwidth() is called when a -tag is
562 * specified, but no -width (it must be guessed). The second
563 * when a -width is specified (macro indicators must be
564 * rewritten into real lengths).
565 */
566
567 if (LIST_tag == n->data.Bl->type && NULL == n->data.Bl->width) {
568 if ( ! post_bl_tagwidth(m, n))
569 return(0);
570 } else if (NULL != n->data.Bl->width) {
571 if ( ! post_bl_width(m, n))
572 return(0);
573 } else
574 return(1);
575
576 assert(n->data.Bl->width);
577 return(1);
578 }
579
580 /*
581 * Parse the date field in `Dd'.
582 */
583 static int
584 post_dd(POST_ARGS)
585 {
586 char buf[DATESIZ];
587
588 if (NULL == n->child) {
589 m->meta.date = time(NULL);
590 return(post_prol(m, n));
591 }
592
593 if ( ! concat(m, buf, n->child, DATESIZ))
594 return(0);
595
596 m->meta.date = mandoc_a2time
597 (MTIME_MDOCDATE | MTIME_CANONICAL, buf);
598
599 if (0 == m->meta.date) {
600 if ( ! mdoc_nmsg(m, n, MANDOCERR_BADDATE))
601 return(0);
602 m->meta.date = time(NULL);
603 }
604
605 return(post_prol(m, n));
606 }
607
608
609 /*
610 * Remove prologue macros from the document after they're processed.
611 * The final document uses mdoc_meta for these values and discards the
612 * originals.
613 */
614 static int
615 post_prol(POST_ARGS)
616 {
617
618 mdoc_node_delete(m, n);
619 if (m->meta.title && m->meta.date && m->meta.os)
620 m->flags |= MDOC_PBODY;
621 return(1);
622 }