]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_validate.c
Move mdoc_str2sec() into mdoc_validate.c as a2sec(), as that's the only
[mandoc.git] / mdoc_validate.c
1 /* $Id: mdoc_validate.c,v 1.160 2011/03/17 09:22:39 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #ifndef OSNAME
23 #include <sys/utsname.h>
24 #endif
25
26 #include <sys/types.h>
27
28 #include <assert.h>
29 #include <ctype.h>
30 #include <limits.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35
36 #include "mandoc.h"
37 #include "libmdoc.h"
38 #include "libmandoc.h"
39
40 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
41
42 #define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n
43 #define POST_ARGS struct mdoc *mdoc
44
45 #define NUMSIZ 32
46 #define DATESIZE 32
47
48 enum check_ineq {
49 CHECK_LT,
50 CHECK_GT,
51 CHECK_EQ
52 };
53
54 enum check_lvl {
55 CHECK_WARN,
56 CHECK_ERROR,
57 };
58
59 typedef int (*v_pre)(PRE_ARGS);
60 typedef int (*v_post)(POST_ARGS);
61
62 struct valids {
63 v_pre *pre;
64 v_post *post;
65 };
66
67 static int check_count(struct mdoc *, enum mdoc_type,
68 enum check_lvl, enum check_ineq, int);
69 static int check_parent(PRE_ARGS, enum mdoct, enum mdoc_type);
70 static void check_text(struct mdoc *, int, int, char *);
71 static void check_argv(struct mdoc *,
72 struct mdoc_node *, struct mdoc_argv *);
73 static void check_args(struct mdoc *, struct mdoc_node *);
74
75 static int concat(struct mdoc *, char *,
76 const struct mdoc_node *, size_t);
77 static enum mdoc_sec a2sec(const char *);
78
79 static int ebool(POST_ARGS);
80 static int berr_ge1(POST_ARGS);
81 static int bwarn_ge1(POST_ARGS);
82 static int ewarn_eq0(POST_ARGS);
83 static int ewarn_eq1(POST_ARGS);
84 static int ewarn_ge1(POST_ARGS);
85 static int ewarn_le1(POST_ARGS);
86 static int hwarn_eq0(POST_ARGS);
87 static int hwarn_eq1(POST_ARGS);
88 static int hwarn_ge1(POST_ARGS);
89 static int hwarn_le1(POST_ARGS);
90
91 static int post_an(POST_ARGS);
92 static int post_at(POST_ARGS);
93 static int post_bf(POST_ARGS);
94 static int post_bl(POST_ARGS);
95 static int post_bl_block(POST_ARGS);
96 static int post_bl_block_width(POST_ARGS);
97 static int post_bl_block_tag(POST_ARGS);
98 static int post_bl_head(POST_ARGS);
99 static int post_bx(POST_ARGS);
100 static int post_dd(POST_ARGS);
101 static int post_dt(POST_ARGS);
102 static int post_defaults(POST_ARGS);
103 static int post_literal(POST_ARGS);
104 static int post_eoln(POST_ARGS);
105 static int post_it(POST_ARGS);
106 static int post_lb(POST_ARGS);
107 static int post_nm(POST_ARGS);
108 static int post_ns(POST_ARGS);
109 static int post_os(POST_ARGS);
110 static int post_ignpar(POST_ARGS);
111 static int post_prol(POST_ARGS);
112 static int post_root(POST_ARGS);
113 static int post_rs(POST_ARGS);
114 static int post_sh(POST_ARGS);
115 static int post_sh_body(POST_ARGS);
116 static int post_sh_head(POST_ARGS);
117 static int post_st(POST_ARGS);
118 static int post_std(POST_ARGS);
119 static int post_vt(POST_ARGS);
120 static int pre_an(PRE_ARGS);
121 static int pre_bd(PRE_ARGS);
122 static int pre_bl(PRE_ARGS);
123 static int pre_dd(PRE_ARGS);
124 static int pre_display(PRE_ARGS);
125 static int pre_dt(PRE_ARGS);
126 static int pre_it(PRE_ARGS);
127 static int pre_literal(PRE_ARGS);
128 static int pre_os(PRE_ARGS);
129 static int pre_par(PRE_ARGS);
130 static int pre_sh(PRE_ARGS);
131 static int pre_ss(PRE_ARGS);
132 static int pre_std(PRE_ARGS);
133
134 static v_post posts_an[] = { post_an, NULL };
135 static v_post posts_at[] = { post_at, post_defaults, NULL };
136 static v_post posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL };
137 static v_post posts_bf[] = { hwarn_le1, post_bf, NULL };
138 static v_post posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL };
139 static v_post posts_bl[] = { bwarn_ge1, post_bl, NULL };
140 static v_post posts_bx[] = { post_bx, NULL };
141 static v_post posts_bool[] = { ebool, NULL };
142 static v_post posts_eoln[] = { post_eoln, NULL };
143 static v_post posts_defaults[] = { post_defaults, NULL };
144 static v_post posts_dd[] = { post_dd, post_prol, NULL };
145 static v_post posts_dl[] = { post_literal, bwarn_ge1, NULL };
146 static v_post posts_dt[] = { post_dt, post_prol, NULL };
147 static v_post posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
148 static v_post posts_it[] = { post_it, NULL };
149 static v_post posts_lb[] = { post_lb, NULL };
150 static v_post posts_nd[] = { berr_ge1, NULL };
151 static v_post posts_nm[] = { post_nm, NULL };
152 static v_post posts_notext[] = { ewarn_eq0, NULL };
153 static v_post posts_ns[] = { post_ns, NULL };
154 static v_post posts_os[] = { post_os, post_prol, NULL };
155 static v_post posts_rs[] = { post_rs, NULL };
156 static v_post posts_sh[] = { post_ignpar, hwarn_ge1, bwarn_ge1, post_sh, NULL };
157 static v_post posts_sp[] = { ewarn_le1, NULL };
158 static v_post posts_ss[] = { post_ignpar, hwarn_ge1, bwarn_ge1, NULL };
159 static v_post posts_st[] = { post_st, NULL };
160 static v_post posts_std[] = { post_std, NULL };
161 static v_post posts_text[] = { ewarn_ge1, NULL };
162 static v_post posts_text1[] = { ewarn_eq1, NULL };
163 static v_post posts_vt[] = { post_vt, NULL };
164 static v_post posts_wline[] = { bwarn_ge1, NULL };
165 static v_pre pres_an[] = { pre_an, NULL };
166 static v_pre pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL };
167 static v_pre pres_bl[] = { pre_bl, pre_par, NULL };
168 static v_pre pres_d1[] = { pre_display, NULL };
169 static v_pre pres_dl[] = { pre_literal, pre_display, NULL };
170 static v_pre pres_dd[] = { pre_dd, NULL };
171 static v_pre pres_dt[] = { pre_dt, NULL };
172 static v_pre pres_er[] = { NULL, NULL };
173 static v_pre pres_fd[] = { NULL, NULL };
174 static v_pre pres_it[] = { pre_it, pre_par, NULL };
175 static v_pre pres_os[] = { pre_os, NULL };
176 static v_pre pres_pp[] = { pre_par, NULL };
177 static v_pre pres_sh[] = { pre_sh, NULL };
178 static v_pre pres_ss[] = { pre_ss, NULL };
179 static v_pre pres_std[] = { pre_std, NULL };
180
181 const struct valids mdoc_valids[MDOC_MAX] = {
182 { NULL, NULL }, /* Ap */
183 { pres_dd, posts_dd }, /* Dd */
184 { pres_dt, posts_dt }, /* Dt */
185 { pres_os, posts_os }, /* Os */
186 { pres_sh, posts_sh }, /* Sh */
187 { pres_ss, posts_ss }, /* Ss */
188 { pres_pp, posts_notext }, /* Pp */
189 { pres_d1, posts_wline }, /* D1 */
190 { pres_dl, posts_dl }, /* Dl */
191 { pres_bd, posts_bd }, /* Bd */
192 { NULL, NULL }, /* Ed */
193 { pres_bl, posts_bl }, /* Bl */
194 { NULL, NULL }, /* El */
195 { pres_it, posts_it }, /* It */
196 { NULL, NULL }, /* Ad */
197 { pres_an, posts_an }, /* An */
198 { NULL, posts_defaults }, /* Ar */
199 { NULL, NULL }, /* Cd */
200 { NULL, NULL }, /* Cm */
201 { NULL, NULL }, /* Dv */
202 { pres_er, NULL }, /* Er */
203 { NULL, NULL }, /* Ev */
204 { pres_std, posts_std }, /* Ex */
205 { NULL, NULL }, /* Fa */
206 { pres_fd, posts_text }, /* Fd */
207 { NULL, NULL }, /* Fl */
208 { NULL, NULL }, /* Fn */
209 { NULL, NULL }, /* Ft */
210 { NULL, NULL }, /* Ic */
211 { NULL, posts_text1 }, /* In */
212 { NULL, posts_defaults }, /* Li */
213 { NULL, posts_nd }, /* Nd */
214 { NULL, posts_nm }, /* Nm */
215 { NULL, NULL }, /* Op */
216 { NULL, NULL }, /* Ot */
217 { NULL, posts_defaults }, /* Pa */
218 { pres_std, posts_std }, /* Rv */
219 { NULL, posts_st }, /* St */
220 { NULL, NULL }, /* Va */
221 { NULL, posts_vt }, /* Vt */
222 { NULL, posts_text }, /* Xr */
223 { NULL, posts_text }, /* %A */
224 { NULL, posts_text }, /* %B */ /* FIXME: can be used outside Rs/Re. */
225 { NULL, posts_text }, /* %D */
226 { NULL, posts_text }, /* %I */
227 { NULL, posts_text }, /* %J */
228 { NULL, posts_text }, /* %N */
229 { NULL, posts_text }, /* %O */
230 { NULL, posts_text }, /* %P */
231 { NULL, posts_text }, /* %R */
232 { NULL, posts_text }, /* %T */ /* FIXME: can be used outside Rs/Re. */
233 { NULL, posts_text }, /* %V */
234 { NULL, NULL }, /* Ac */
235 { NULL, NULL }, /* Ao */
236 { NULL, NULL }, /* Aq */
237 { NULL, posts_at }, /* At */
238 { NULL, NULL }, /* Bc */
239 { NULL, posts_bf }, /* Bf */
240 { NULL, NULL }, /* Bo */
241 { NULL, NULL }, /* Bq */
242 { NULL, NULL }, /* Bsx */
243 { NULL, posts_bx }, /* Bx */
244 { NULL, posts_bool }, /* Db */
245 { NULL, NULL }, /* Dc */
246 { NULL, NULL }, /* Do */
247 { NULL, NULL }, /* Dq */
248 { NULL, NULL }, /* Ec */
249 { NULL, NULL }, /* Ef */
250 { NULL, NULL }, /* Em */
251 { NULL, NULL }, /* Eo */
252 { NULL, NULL }, /* Fx */
253 { NULL, NULL }, /* Ms */
254 { NULL, posts_notext }, /* No */
255 { NULL, posts_ns }, /* Ns */
256 { NULL, NULL }, /* Nx */
257 { NULL, NULL }, /* Ox */
258 { NULL, NULL }, /* Pc */
259 { NULL, posts_text1 }, /* Pf */
260 { NULL, NULL }, /* Po */
261 { NULL, NULL }, /* Pq */
262 { NULL, NULL }, /* Qc */
263 { NULL, NULL }, /* Ql */
264 { NULL, NULL }, /* Qo */
265 { NULL, NULL }, /* Qq */
266 { NULL, NULL }, /* Re */
267 { NULL, posts_rs }, /* Rs */
268 { NULL, NULL }, /* Sc */
269 { NULL, NULL }, /* So */
270 { NULL, NULL }, /* Sq */
271 { NULL, posts_bool }, /* Sm */
272 { NULL, NULL }, /* Sx */
273 { NULL, NULL }, /* Sy */
274 { NULL, NULL }, /* Tn */
275 { NULL, NULL }, /* Ux */
276 { NULL, NULL }, /* Xc */
277 { NULL, NULL }, /* Xo */
278 { NULL, posts_fo }, /* Fo */
279 { NULL, NULL }, /* Fc */
280 { NULL, NULL }, /* Oo */
281 { NULL, NULL }, /* Oc */
282 { NULL, posts_bk }, /* Bk */
283 { NULL, NULL }, /* Ek */
284 { NULL, posts_eoln }, /* Bt */
285 { NULL, NULL }, /* Hf */
286 { NULL, NULL }, /* Fr */
287 { NULL, posts_eoln }, /* Ud */
288 { NULL, posts_lb }, /* Lb */
289 { NULL, posts_notext }, /* Lp */
290 { NULL, NULL }, /* Lk */
291 { NULL, posts_defaults }, /* Mt */
292 { NULL, NULL }, /* Brq */
293 { NULL, NULL }, /* Bro */
294 { NULL, NULL }, /* Brc */
295 { NULL, posts_text }, /* %C */
296 { NULL, NULL }, /* Es */
297 { NULL, NULL }, /* En */
298 { NULL, NULL }, /* Dx */
299 { NULL, posts_text }, /* %Q */
300 { NULL, posts_notext }, /* br */
301 { pres_pp, posts_sp }, /* sp */
302 { NULL, posts_text1 }, /* %U */
303 { NULL, NULL }, /* Ta */
304 };
305
306 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
307
308 static const enum mdoct rsord[RSORD_MAX] = {
309 MDOC__A,
310 MDOC__T,
311 MDOC__B,
312 MDOC__I,
313 MDOC__J,
314 MDOC__R,
315 MDOC__N,
316 MDOC__V,
317 MDOC__P,
318 MDOC__Q,
319 MDOC__D,
320 MDOC__O,
321 MDOC__C,
322 MDOC__U
323 };
324
325 static const char * const secnames[SEC__MAX] = {
326 NULL,
327 "NAME",
328 "LIBRARY",
329 "SYNOPSIS",
330 "DESCRIPTION",
331 "IMPLEMENTATION NOTES",
332 "RETURN VALUES",
333 "ENVIRONMENT",
334 "FILES",
335 "EXIT STATUS",
336 "EXAMPLES",
337 "DIAGNOSTICS",
338 "COMPATIBILITY",
339 "ERRORS",
340 "SEE ALSO",
341 "STANDARDS",
342 "HISTORY",
343 "AUTHORS",
344 "CAVEATS",
345 "BUGS",
346 "SECURITY CONSIDERATIONS",
347 NULL
348 };
349
350 int
351 mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
352 {
353 v_pre *p;
354 int line, pos;
355 char *tp;
356
357 switch (n->type) {
358 case (MDOC_TEXT):
359 tp = n->string;
360 line = n->line;
361 pos = n->pos;
362 check_text(mdoc, line, pos, tp);
363 /* FALLTHROUGH */
364 case (MDOC_TBL):
365 /* FALLTHROUGH */
366 case (MDOC_EQN):
367 /* FALLTHROUGH */
368 case (MDOC_ROOT):
369 return(1);
370 default:
371 break;
372 }
373
374 check_args(mdoc, n);
375
376 if (NULL == mdoc_valids[n->tok].pre)
377 return(1);
378 for (p = mdoc_valids[n->tok].pre; *p; p++)
379 if ( ! (*p)(mdoc, n))
380 return(0);
381 return(1);
382 }
383
384
385 int
386 mdoc_valid_post(struct mdoc *mdoc)
387 {
388 v_post *p;
389
390 if (MDOC_VALID & mdoc->last->flags)
391 return(1);
392 mdoc->last->flags |= MDOC_VALID;
393
394 switch (mdoc->last->type) {
395 case (MDOC_TEXT):
396 /* FALLTHROUGH */
397 case (MDOC_EQN):
398 /* FALLTHROUGH */
399 case (MDOC_TBL):
400 return(1);
401 case (MDOC_ROOT):
402 return(post_root(mdoc));
403 default:
404 break;
405 }
406
407 if (NULL == mdoc_valids[mdoc->last->tok].post)
408 return(1);
409 for (p = mdoc_valids[mdoc->last->tok].post; *p; p++)
410 if ( ! (*p)(mdoc))
411 return(0);
412
413 return(1);
414 }
415
416 static int
417 check_count(struct mdoc *m, enum mdoc_type type,
418 enum check_lvl lvl, enum check_ineq ineq, int val)
419 {
420 const char *p;
421 enum mandocerr t;
422
423 if (m->last->type != type)
424 return(1);
425
426 switch (ineq) {
427 case (CHECK_LT):
428 p = "less than ";
429 if (m->last->nchild < val)
430 return(1);
431 break;
432 case (CHECK_GT):
433 p = "more than ";
434 if (m->last->nchild > val)
435 return(1);
436 break;
437 case (CHECK_EQ):
438 p = "";
439 if (val == m->last->nchild)
440 return(1);
441 break;
442 default:
443 abort();
444 /* NOTREACHED */
445 }
446
447 t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
448
449 return(mdoc_vmsg(m, t, m->last->line, m->last->pos,
450 "want %s%d children (have %d)",
451 p, val, m->last->nchild));
452 }
453
454 static int
455 berr_ge1(POST_ARGS)
456 {
457
458 return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
459 }
460
461 static int
462 bwarn_ge1(POST_ARGS)
463 {
464 return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
465 }
466
467 static int
468 ewarn_eq0(POST_ARGS)
469 {
470 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
471 }
472
473 static int
474 ewarn_eq1(POST_ARGS)
475 {
476 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
477 }
478
479 static int
480 ewarn_ge1(POST_ARGS)
481 {
482 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
483 }
484
485 static int
486 ewarn_le1(POST_ARGS)
487 {
488 return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
489 }
490
491 static int
492 hwarn_eq0(POST_ARGS)
493 {
494 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
495 }
496
497 static int
498 hwarn_eq1(POST_ARGS)
499 {
500 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
501 }
502
503 static int
504 hwarn_ge1(POST_ARGS)
505 {
506 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
507 }
508
509 static int
510 hwarn_le1(POST_ARGS)
511 {
512 return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_LT, 2));
513 }
514
515 static void
516 check_args(struct mdoc *m, struct mdoc_node *n)
517 {
518 int i;
519
520 if (NULL == n->args)
521 return;
522
523 assert(n->args->argc);
524 for (i = 0; i < (int)n->args->argc; i++)
525 check_argv(m, n, &n->args->argv[i]);
526 }
527
528 static void
529 check_argv(struct mdoc *m, struct mdoc_node *n, struct mdoc_argv *v)
530 {
531 int i;
532
533 for (i = 0; i < (int)v->sz; i++)
534 check_text(m, v->line, v->pos, v->value[i]);
535
536 /* FIXME: move to post_std(). */
537
538 if (MDOC_Std == v->arg)
539 if ( ! (v->sz || m->meta.name))
540 mdoc_nmsg(m, n, MANDOCERR_NONAME);
541 }
542
543 static void
544 check_text(struct mdoc *m, int ln, int pos, char *p)
545 {
546 int c;
547 size_t sz;
548
549 for ( ; *p; p++, pos++) {
550 sz = strcspn(p, "\t\\");
551 p += (int)sz;
552
553 if ('\0' == *p)
554 break;
555
556 pos += (int)sz;
557
558 if ('\t' == *p) {
559 if ( ! (MDOC_LITERAL & m->flags))
560 mdoc_pmsg(m, ln, pos, MANDOCERR_BADTAB);
561 continue;
562 }
563
564 if (0 == (c = mandoc_special(p))) {
565 mdoc_pmsg(m, ln, pos, MANDOCERR_BADESCAPE);
566 continue;
567 }
568
569 p += c - 1;
570 pos += c - 1;
571 }
572 }
573
574 static int
575 check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t)
576 {
577
578 assert(n->parent);
579 if ((MDOC_ROOT == t || tok == n->parent->tok) &&
580 (t == n->parent->type))
581 return(1);
582
583 mdoc_vmsg(mdoc, MANDOCERR_SYNTCHILD,
584 n->line, n->pos, "want parent %s",
585 MDOC_ROOT == t ? "<root>" :
586 mdoc_macronames[tok]);
587 return(0);
588 }
589
590
591 static int
592 pre_display(PRE_ARGS)
593 {
594 struct mdoc_node *node;
595
596 if (MDOC_BLOCK != n->type)
597 return(1);
598
599 for (node = mdoc->last->parent; node; node = node->parent)
600 if (MDOC_BLOCK == node->type)
601 if (MDOC_Bd == node->tok)
602 break;
603
604 if (node)
605 mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP);
606
607 return(1);
608 }
609
610
611 static int
612 pre_bl(PRE_ARGS)
613 {
614 int i, comp, dup;
615 const char *offs, *width;
616 enum mdoc_list lt;
617 struct mdoc_node *np;
618
619 if (MDOC_BLOCK != n->type) {
620 if (ENDBODY_NOT != n->end) {
621 assert(n->pending);
622 np = n->pending->parent;
623 } else
624 np = n->parent;
625
626 assert(np);
627 assert(MDOC_BLOCK == np->type);
628 assert(MDOC_Bl == np->tok);
629 return(1);
630 }
631
632 /*
633 * First figure out which kind of list to use: bind ourselves to
634 * the first mentioned list type and warn about any remaining
635 * ones. If we find no list type, we default to LIST_item.
636 */
637
638 /* LINTED */
639 for (i = 0; n->args && i < (int)n->args->argc; i++) {
640 lt = LIST__NONE;
641 dup = comp = 0;
642 width = offs = NULL;
643 switch (n->args->argv[i].arg) {
644 /* Set list types. */
645 case (MDOC_Bullet):
646 lt = LIST_bullet;
647 break;
648 case (MDOC_Dash):
649 lt = LIST_dash;
650 break;
651 case (MDOC_Enum):
652 lt = LIST_enum;
653 break;
654 case (MDOC_Hyphen):
655 lt = LIST_hyphen;
656 break;
657 case (MDOC_Item):
658 lt = LIST_item;
659 break;
660 case (MDOC_Tag):
661 lt = LIST_tag;
662 break;
663 case (MDOC_Diag):
664 lt = LIST_diag;
665 break;
666 case (MDOC_Hang):
667 lt = LIST_hang;
668 break;
669 case (MDOC_Ohang):
670 lt = LIST_ohang;
671 break;
672 case (MDOC_Inset):
673 lt = LIST_inset;
674 break;
675 case (MDOC_Column):
676 lt = LIST_column;
677 break;
678 /* Set list arguments. */
679 case (MDOC_Compact):
680 dup = n->norm->Bl.comp;
681 comp = 1;
682 break;
683 case (MDOC_Width):
684 dup = (NULL != n->norm->Bl.width);
685 width = n->args->argv[i].value[0];
686 break;
687 case (MDOC_Offset):
688 /* NB: this can be empty! */
689 if (n->args->argv[i].sz) {
690 offs = n->args->argv[i].value[0];
691 dup = (NULL != n->norm->Bl.offs);
692 break;
693 }
694 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
695 break;
696 default:
697 continue;
698 }
699
700 /* Check: duplicate auxiliary arguments. */
701
702 if (dup)
703 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
704
705 if (comp && ! dup)
706 n->norm->Bl.comp = comp;
707 if (offs && ! dup)
708 n->norm->Bl.offs = offs;
709 if (width && ! dup)
710 n->norm->Bl.width = width;
711
712 /* Check: multiple list types. */
713
714 if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE)
715 mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP);
716
717 /* Assign list type. */
718
719 if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) {
720 n->norm->Bl.type = lt;
721 /* Set column information, too. */
722 if (LIST_column == lt) {
723 n->norm->Bl.ncols =
724 n->args->argv[i].sz;
725 n->norm->Bl.cols = (const char **)
726 n->args->argv[i].value;
727 }
728 }
729
730 /* The list type should come first. */
731
732 if (n->norm->Bl.type == LIST__NONE)
733 if (n->norm->Bl.width ||
734 n->norm->Bl.offs ||
735 n->norm->Bl.comp)
736 mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST);
737
738 continue;
739 }
740
741 /* Allow lists to default to LIST_item. */
742
743 if (LIST__NONE == n->norm->Bl.type) {
744 mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE);
745 n->norm->Bl.type = LIST_item;
746 }
747
748 /*
749 * Validate the width field. Some list types don't need width
750 * types and should be warned about them. Others should have it
751 * and must also be warned.
752 */
753
754 switch (n->norm->Bl.type) {
755 case (LIST_tag):
756 if (n->norm->Bl.width)
757 break;
758 mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
759 break;
760 case (LIST_column):
761 /* FALLTHROUGH */
762 case (LIST_diag):
763 /* FALLTHROUGH */
764 case (LIST_ohang):
765 /* FALLTHROUGH */
766 case (LIST_inset):
767 /* FALLTHROUGH */
768 case (LIST_item):
769 if (n->norm->Bl.width)
770 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
771 break;
772 default:
773 break;
774 }
775
776 return(1);
777 }
778
779
780 static int
781 pre_bd(PRE_ARGS)
782 {
783 int i, dup, comp;
784 enum mdoc_disp dt;
785 const char *offs;
786 struct mdoc_node *np;
787
788 if (MDOC_BLOCK != n->type) {
789 if (ENDBODY_NOT != n->end) {
790 assert(n->pending);
791 np = n->pending->parent;
792 } else
793 np = n->parent;
794
795 assert(np);
796 assert(MDOC_BLOCK == np->type);
797 assert(MDOC_Bd == np->tok);
798 return(1);
799 }
800
801 /* LINTED */
802 for (i = 0; n->args && i < (int)n->args->argc; i++) {
803 dt = DISP__NONE;
804 dup = comp = 0;
805 offs = NULL;
806
807 switch (n->args->argv[i].arg) {
808 case (MDOC_Centred):
809 dt = DISP_centred;
810 break;
811 case (MDOC_Ragged):
812 dt = DISP_ragged;
813 break;
814 case (MDOC_Unfilled):
815 dt = DISP_unfilled;
816 break;
817 case (MDOC_Filled):
818 dt = DISP_filled;
819 break;
820 case (MDOC_Literal):
821 dt = DISP_literal;
822 break;
823 case (MDOC_File):
824 mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP);
825 return(0);
826 case (MDOC_Offset):
827 /* NB: this can be empty! */
828 if (n->args->argv[i].sz) {
829 offs = n->args->argv[i].value[0];
830 dup = (NULL != n->norm->Bd.offs);
831 break;
832 }
833 mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
834 break;
835 case (MDOC_Compact):
836 comp = 1;
837 dup = n->norm->Bd.comp;
838 break;
839 default:
840 abort();
841 /* NOTREACHED */
842 }
843
844 /* Check whether we have duplicates. */
845
846 if (dup)
847 mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP);
848
849 /* Make our auxiliary assignments. */
850
851 if (offs && ! dup)
852 n->norm->Bd.offs = offs;
853 if (comp && ! dup)
854 n->norm->Bd.comp = comp;
855
856 /* Check whether a type has already been assigned. */
857
858 if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE)
859 mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP);
860
861 /* Make our type assignment. */
862
863 if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE)
864 n->norm->Bd.type = dt;
865 }
866
867 if (DISP__NONE == n->norm->Bd.type) {
868 mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE);
869 n->norm->Bd.type = DISP_ragged;
870 }
871
872 return(1);
873 }
874
875
876 static int
877 pre_ss(PRE_ARGS)
878 {
879
880 if (MDOC_BLOCK != n->type)
881 return(1);
882 return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY));
883 }
884
885
886 static int
887 pre_sh(PRE_ARGS)
888 {
889
890 if (MDOC_BLOCK != n->type)
891 return(1);
892
893 mdoc->regs->regs[(int)REG_nS].set = 0;
894 return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
895 }
896
897
898 static int
899 pre_it(PRE_ARGS)
900 {
901
902 if (MDOC_BLOCK != n->type)
903 return(1);
904
905 return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY));
906 }
907
908
909 static int
910 pre_an(PRE_ARGS)
911 {
912 int i;
913
914 if (NULL == n->args)
915 return(1);
916
917 for (i = 1; i < (int)n->args->argc; i++)
918 mdoc_pmsg(mdoc, n->args->argv[i].line,
919 n->args->argv[i].pos, MANDOCERR_IGNARGV);
920
921 if (MDOC_Split == n->args->argv[0].arg)
922 n->norm->An.auth = AUTH_split;
923 else if (MDOC_Nosplit == n->args->argv[0].arg)
924 n->norm->An.auth = AUTH_nosplit;
925 else
926 abort();
927
928 return(1);
929 }
930
931 static int
932 pre_std(PRE_ARGS)
933 {
934
935 if (n->args && 1 == n->args->argc)
936 if (MDOC_Std == n->args->argv[0].arg)
937 return(1);
938
939 mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV);
940 return(1);
941 }
942
943 static int
944 pre_dt(PRE_ARGS)
945 {
946
947 if (NULL == mdoc->meta.date || mdoc->meta.os)
948 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
949
950 if (mdoc->meta.title)
951 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
952
953 return(1);
954 }
955
956 static int
957 pre_os(PRE_ARGS)
958 {
959
960 if (NULL == mdoc->meta.title || NULL == mdoc->meta.date)
961 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
962
963 if (mdoc->meta.os)
964 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
965
966 return(1);
967 }
968
969 static int
970 pre_dd(PRE_ARGS)
971 {
972
973 if (mdoc->meta.title || mdoc->meta.os)
974 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO);
975
976 if (mdoc->meta.date)
977 mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP);
978
979 return(1);
980 }
981
982
983 static int
984 post_bf(POST_ARGS)
985 {
986 struct mdoc_node *np;
987 enum mdocargt arg;
988
989 /*
990 * Unlike other data pointers, these are "housed" by the HEAD
991 * element, which contains the goods.
992 */
993
994 if (MDOC_HEAD != mdoc->last->type) {
995 if (ENDBODY_NOT != mdoc->last->end) {
996 assert(mdoc->last->pending);
997 np = mdoc->last->pending->parent->head;
998 } else if (MDOC_BLOCK != mdoc->last->type) {
999 np = mdoc->last->parent->head;
1000 } else
1001 np = mdoc->last->head;
1002
1003 assert(np);
1004 assert(MDOC_HEAD == np->type);
1005 assert(MDOC_Bf == np->tok);
1006 return(1);
1007 }
1008
1009 np = mdoc->last;
1010 assert(MDOC_BLOCK == np->parent->type);
1011 assert(MDOC_Bf == np->parent->tok);
1012
1013 /*
1014 * Cannot have both argument and parameter.
1015 * If neither is specified, let it through with a warning.
1016 */
1017
1018 if (np->parent->args && np->child) {
1019 mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT);
1020 return(0);
1021 } else if (NULL == np->parent->args && NULL == np->child) {
1022 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1023 return(1);
1024 }
1025
1026 /* Extract argument into data. */
1027
1028 if (np->parent->args) {
1029 arg = np->parent->args->argv[0].arg;
1030 if (MDOC_Emphasis == arg)
1031 np->norm->Bf.font = FONT_Em;
1032 else if (MDOC_Literal == arg)
1033 np->norm->Bf.font = FONT_Li;
1034 else if (MDOC_Symbolic == arg)
1035 np->norm->Bf.font = FONT_Sy;
1036 else
1037 abort();
1038 return(1);
1039 }
1040
1041 /* Extract parameter into data. */
1042
1043 if (0 == strcmp(np->child->string, "Em"))
1044 np->norm->Bf.font = FONT_Em;
1045 else if (0 == strcmp(np->child->string, "Li"))
1046 np->norm->Bf.font = FONT_Li;
1047 else if (0 == strcmp(np->child->string, "Sy"))
1048 np->norm->Bf.font = FONT_Sy;
1049 else
1050 mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE);
1051
1052 return(1);
1053 }
1054
1055 static int
1056 post_lb(POST_ARGS)
1057 {
1058 const char *p;
1059 char *buf;
1060 size_t sz;
1061
1062 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1063
1064 assert(mdoc->last->child);
1065 assert(MDOC_TEXT == mdoc->last->child->type);
1066
1067 p = mdoc_a2lib(mdoc->last->child->string);
1068
1069 /* If lookup ok, replace with table value. */
1070
1071 if (p) {
1072 free(mdoc->last->child->string);
1073 mdoc->last->child->string = mandoc_strdup(p);
1074 return(1);
1075 }
1076
1077 /* If not, use "library ``xxxx''. */
1078
1079 sz = strlen(mdoc->last->child->string) +
1080 2 + strlen("\\(lqlibrary\\(rq");
1081 buf = mandoc_malloc(sz);
1082 snprintf(buf, sz, "library \\(lq%s\\(rq",
1083 mdoc->last->child->string);
1084 free(mdoc->last->child->string);
1085 mdoc->last->child->string = buf;
1086 return(1);
1087 }
1088
1089 static int
1090 post_eoln(POST_ARGS)
1091 {
1092
1093 if (mdoc->last->child)
1094 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1095 return(1);
1096 }
1097
1098
1099 static int
1100 post_vt(POST_ARGS)
1101 {
1102 const struct mdoc_node *n;
1103
1104 /*
1105 * The Vt macro comes in both ELEM and BLOCK form, both of which
1106 * have different syntaxes (yet more context-sensitive
1107 * behaviour). ELEM types must have a child, which is already
1108 * guaranteed by the in_line parsing routine; BLOCK types,
1109 * specifically the BODY, should only have TEXT children.
1110 */
1111
1112 if (MDOC_BODY != mdoc->last->type)
1113 return(1);
1114
1115 for (n = mdoc->last->child; n; n = n->next)
1116 if (MDOC_TEXT != n->type)
1117 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1118
1119 return(1);
1120 }
1121
1122
1123 static int
1124 post_nm(POST_ARGS)
1125 {
1126 char buf[BUFSIZ];
1127
1128 /* If no child specified, make sure we have the meta name. */
1129
1130 if (NULL == mdoc->last->child && NULL == mdoc->meta.name) {
1131 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
1132 return(1);
1133 } else if (mdoc->meta.name)
1134 return(1);
1135
1136 /* If no meta name, set it from the child. */
1137
1138 if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ))
1139 return(0);
1140
1141 mdoc->meta.name = mandoc_strdup(buf);
1142
1143 return(1);
1144 }
1145
1146 static int
1147 post_literal(POST_ARGS)
1148 {
1149
1150 /*
1151 * The `Dl' (note "el" not "one") and `Bd' macros unset the
1152 * MDOC_LITERAL flag as they leave. Note that `Bd' only sets
1153 * this in literal mode, but it doesn't hurt to just switch it
1154 * off in general since displays can't be nested.
1155 */
1156
1157 if (MDOC_BODY == mdoc->last->type)
1158 mdoc->flags &= ~MDOC_LITERAL;
1159
1160 return(1);
1161 }
1162
1163 static int
1164 post_defaults(POST_ARGS)
1165 {
1166 struct mdoc_node *nn;
1167
1168 /*
1169 * The `Ar' defaults to "file ..." if no value is provided as an
1170 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1171 * gets an empty string.
1172 */
1173
1174 if (mdoc->last->child)
1175 return(1);
1176
1177 nn = mdoc->last;
1178 mdoc->next = MDOC_NEXT_CHILD;
1179
1180 switch (nn->tok) {
1181 case (MDOC_Ar):
1182 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
1183 return(0);
1184 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
1185 return(0);
1186 break;
1187 case (MDOC_At):
1188 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T"))
1189 return(0);
1190 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX"))
1191 return(0);
1192 break;
1193 case (MDOC_Li):
1194 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
1195 return(0);
1196 break;
1197 case (MDOC_Pa):
1198 /* FALLTHROUGH */
1199 case (MDOC_Mt):
1200 if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
1201 return(0);
1202 break;
1203 default:
1204 abort();
1205 /* NOTREACHED */
1206 }
1207
1208 mdoc->last = nn;
1209 return(1);
1210 }
1211
1212 static int
1213 post_at(POST_ARGS)
1214 {
1215 const char *p, *q;
1216 char *buf;
1217 size_t sz;
1218
1219 /*
1220 * If we have a child, look it up in the standard keys. If a
1221 * key exist, use that instead of the child; if it doesn't,
1222 * prefix "AT&T UNIX " to the existing data.
1223 */
1224
1225 if (NULL == mdoc->last->child)
1226 return(1);
1227
1228 assert(MDOC_TEXT == mdoc->last->child->type);
1229 p = mdoc_a2att(mdoc->last->child->string);
1230
1231 if (p) {
1232 free(mdoc->last->child->string);
1233 mdoc->last->child->string = mandoc_strdup(p);
1234 } else {
1235 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT);
1236 p = "AT&T UNIX ";
1237 q = mdoc->last->child->string;
1238 sz = strlen(p) + strlen(q) + 1;
1239 buf = mandoc_malloc(sz);
1240 strlcpy(buf, p, sz);
1241 strlcat(buf, q, sz);
1242 free(mdoc->last->child->string);
1243 mdoc->last->child->string = buf;
1244 }
1245
1246 return(1);
1247 }
1248
1249 static int
1250 post_an(POST_ARGS)
1251 {
1252 struct mdoc_node *np;
1253
1254 np = mdoc->last;
1255 if (AUTH__NONE == np->norm->An.auth) {
1256 if (0 == np->child)
1257 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
1258 } else if (np->child)
1259 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
1260
1261 return(1);
1262 }
1263
1264
1265 static int
1266 post_it(POST_ARGS)
1267 {
1268 int i, cols, rc;
1269 enum mdoc_list lt;
1270 struct mdoc_node *n, *c;
1271 enum mandocerr er;
1272
1273 if (MDOC_BLOCK != mdoc->last->type)
1274 return(1);
1275
1276 n = mdoc->last->parent->parent;
1277 lt = n->norm->Bl.type;
1278
1279 if (LIST__NONE == lt) {
1280 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE);
1281 return(1);
1282 }
1283
1284 switch (lt) {
1285 case (LIST_tag):
1286 if (mdoc->last->head->child)
1287 break;
1288 /* FIXME: give this a dummy value. */
1289 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1290 break;
1291 case (LIST_hang):
1292 /* FALLTHROUGH */
1293 case (LIST_ohang):
1294 /* FALLTHROUGH */
1295 case (LIST_inset):
1296 /* FALLTHROUGH */
1297 case (LIST_diag):
1298 if (NULL == mdoc->last->head->child)
1299 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS);
1300 break;
1301 case (LIST_bullet):
1302 /* FALLTHROUGH */
1303 case (LIST_dash):
1304 /* FALLTHROUGH */
1305 case (LIST_enum):
1306 /* FALLTHROUGH */
1307 case (LIST_hyphen):
1308 if (NULL == mdoc->last->body->child)
1309 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1310 /* FALLTHROUGH */
1311 case (LIST_item):
1312 if (mdoc->last->head->child)
1313 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST);
1314 break;
1315 case (LIST_column):
1316 cols = (int)n->norm->Bl.ncols;
1317
1318 assert(NULL == mdoc->last->head->child);
1319
1320 if (NULL == mdoc->last->body->child)
1321 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY);
1322
1323 for (i = 0, c = mdoc->last->child; c; c = c->next)
1324 if (MDOC_BODY == c->type)
1325 i++;
1326
1327 if (i < cols)
1328 er = MANDOCERR_ARGCOUNT;
1329 else if (i == cols || i == cols + 1)
1330 break;
1331 else
1332 er = MANDOCERR_SYNTARGCOUNT;
1333
1334 rc = mdoc_vmsg(mdoc, er,
1335 mdoc->last->line, mdoc->last->pos,
1336 "columns == %d (have %d)", cols, i);
1337 return(rc);
1338 default:
1339 break;
1340 }
1341
1342 return(1);
1343 }
1344
1345 static int
1346 post_bl_block(POST_ARGS)
1347 {
1348 struct mdoc_node *n;
1349
1350 /*
1351 * These are fairly complicated, so we've broken them into two
1352 * functions. post_bl_block_tag() is called when a -tag is
1353 * specified, but no -width (it must be guessed). The second
1354 * when a -width is specified (macro indicators must be
1355 * rewritten into real lengths).
1356 */
1357
1358 n = mdoc->last;
1359
1360 if (LIST_tag == n->norm->Bl.type &&
1361 NULL == n->norm->Bl.width) {
1362 if ( ! post_bl_block_tag(mdoc))
1363 return(0);
1364 } else if (NULL != n->norm->Bl.width) {
1365 if ( ! post_bl_block_width(mdoc))
1366 return(0);
1367 } else
1368 return(1);
1369
1370 assert(n->norm->Bl.width);
1371 return(1);
1372 }
1373
1374 static int
1375 post_bl_block_width(POST_ARGS)
1376 {
1377 size_t width;
1378 int i;
1379 enum mdoct tok;
1380 struct mdoc_node *n;
1381 char buf[NUMSIZ];
1382
1383 n = mdoc->last;
1384
1385 /*
1386 * Calculate the real width of a list from the -width string,
1387 * which may contain a macro (with a known default width), a
1388 * literal string, or a scaling width.
1389 *
1390 * If the value to -width is a macro, then we re-write it to be
1391 * the macro's width as set in share/tmac/mdoc/doc-common.
1392 */
1393
1394 if (0 == strcmp(n->norm->Bl.width, "Ds"))
1395 width = 6;
1396 else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
1397 return(1);
1398 else if (0 == (width = mdoc_macro2len(tok))) {
1399 mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH);
1400 return(1);
1401 }
1402
1403 /* The value already exists: free and reallocate it. */
1404
1405 assert(n->args);
1406
1407 for (i = 0; i < (int)n->args->argc; i++)
1408 if (MDOC_Width == n->args->argv[i].arg)
1409 break;
1410
1411 assert(i < (int)n->args->argc);
1412
1413 snprintf(buf, NUMSIZ, "%zun", width);
1414 free(n->args->argv[i].value[0]);
1415 n->args->argv[i].value[0] = mandoc_strdup(buf);
1416
1417 /* Set our width! */
1418 n->norm->Bl.width = n->args->argv[i].value[0];
1419 return(1);
1420 }
1421
1422 static int
1423 post_bl_block_tag(POST_ARGS)
1424 {
1425 struct mdoc_node *n, *nn;
1426 size_t sz, ssz;
1427 int i;
1428 char buf[NUMSIZ];
1429
1430 /*
1431 * Calculate the -width for a `Bl -tag' list if it hasn't been
1432 * provided. Uses the first head macro. NOTE AGAIN: this is
1433 * ONLY if the -width argument has NOT been provided. See
1434 * post_bl_block_width() for converting the -width string.
1435 */
1436
1437 sz = 10;
1438 n = mdoc->last;
1439
1440 for (nn = n->body->child; nn; nn = nn->next) {
1441 if (MDOC_It != nn->tok)
1442 continue;
1443
1444 assert(MDOC_BLOCK == nn->type);
1445 nn = nn->head->child;
1446
1447 if (nn == NULL)
1448 break;
1449
1450 if (MDOC_TEXT == nn->type) {
1451 sz = strlen(nn->string) + 1;
1452 break;
1453 }
1454
1455 if (0 != (ssz = mdoc_macro2len(nn->tok)))
1456 sz = ssz;
1457
1458 break;
1459 }
1460
1461 /* Defaults to ten ens. */
1462
1463 snprintf(buf, NUMSIZ, "%zun", sz);
1464
1465 /*
1466 * We have to dynamically add this to the macro's argument list.
1467 * We're guaranteed that a MDOC_Width doesn't already exist.
1468 */
1469
1470 assert(n->args);
1471 i = (int)(n->args->argc)++;
1472
1473 n->args->argv = mandoc_realloc(n->args->argv,
1474 n->args->argc * sizeof(struct mdoc_argv));
1475
1476 n->args->argv[i].arg = MDOC_Width;
1477 n->args->argv[i].line = n->line;
1478 n->args->argv[i].pos = n->pos;
1479 n->args->argv[i].sz = 1;
1480 n->args->argv[i].value = mandoc_malloc(sizeof(char *));
1481 n->args->argv[i].value[0] = mandoc_strdup(buf);
1482
1483 /* Set our width! */
1484 n->norm->Bl.width = n->args->argv[i].value[0];
1485 return(1);
1486 }
1487
1488
1489 static int
1490 post_bl_head(POST_ARGS)
1491 {
1492 struct mdoc_node *np, *nn, *nnp;
1493 int i, j;
1494
1495 if (LIST_column != mdoc->last->norm->Bl.type)
1496 /* FIXME: this should be ERROR class... */
1497 return(hwarn_eq0(mdoc));
1498
1499 /*
1500 * Convert old-style lists, where the column width specifiers
1501 * trail as macro parameters, to the new-style ("normal-form")
1502 * lists where they're argument values following -column.
1503 */
1504
1505 /* First, disallow both types and allow normal-form. */
1506
1507 /*
1508 * TODO: technically, we can accept both and just merge the two
1509 * lists, but I'll leave that for another day.
1510 */
1511
1512 if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) {
1513 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS);
1514 return(0);
1515 } else if (NULL == mdoc->last->child)
1516 return(1);
1517
1518 np = mdoc->last->parent;
1519 assert(np->args);
1520
1521 for (j = 0; j < (int)np->args->argc; j++)
1522 if (MDOC_Column == np->args->argv[j].arg)
1523 break;
1524
1525 assert(j < (int)np->args->argc);
1526 assert(0 == np->args->argv[j].sz);
1527
1528 /*
1529 * Accomodate for new-style groff column syntax. Shuffle the
1530 * child nodes, all of which must be TEXT, as arguments for the
1531 * column field. Then, delete the head children.
1532 */
1533
1534 np->args->argv[j].sz = (size_t)mdoc->last->nchild;
1535 np->args->argv[j].value = mandoc_malloc
1536 ((size_t)mdoc->last->nchild * sizeof(char *));
1537
1538 mdoc->last->norm->Bl.ncols = np->args->argv[j].sz;
1539 mdoc->last->norm->Bl.cols = (const char **)np->args->argv[j].value;
1540
1541 for (i = 0, nn = mdoc->last->child; nn; i++) {
1542 np->args->argv[j].value[i] = nn->string;
1543 nn->string = NULL;
1544 nnp = nn;
1545 nn = nn->next;
1546 mdoc_node_delete(NULL, nnp);
1547 }
1548
1549 mdoc->last->nchild = 0;
1550 mdoc->last->child = NULL;
1551
1552 return(1);
1553 }
1554
1555 static int
1556 post_bl(POST_ARGS)
1557 {
1558 struct mdoc_node *n;
1559
1560 if (MDOC_HEAD == mdoc->last->type)
1561 return(post_bl_head(mdoc));
1562 if (MDOC_BLOCK == mdoc->last->type)
1563 return(post_bl_block(mdoc));
1564 if (MDOC_BODY != mdoc->last->type)
1565 return(1);
1566
1567 for (n = mdoc->last->child; n; n = n->next) {
1568 switch (n->tok) {
1569 case (MDOC_Lp):
1570 /* FALLTHROUGH */
1571 case (MDOC_Pp):
1572 mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
1573 /* FALLTHROUGH */
1574 case (MDOC_It):
1575 /* FALLTHROUGH */
1576 case (MDOC_Sm):
1577 continue;
1578 default:
1579 break;
1580 }
1581
1582 mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD);
1583 return(0);
1584 }
1585
1586 return(1);
1587 }
1588
1589 static int
1590 ebool(struct mdoc *mdoc)
1591 {
1592
1593 if (NULL == mdoc->last->child) {
1594 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1595 mdoc_node_delete(mdoc, mdoc->last);
1596 return(1);
1597 }
1598 check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
1599
1600 assert(MDOC_TEXT == mdoc->last->child->type);
1601
1602 if (0 == strcmp(mdoc->last->child->string, "on"))
1603 return(1);
1604 if (0 == strcmp(mdoc->last->child->string, "off"))
1605 return(1);
1606
1607 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
1608 return(1);
1609 }
1610
1611 static int
1612 post_root(POST_ARGS)
1613 {
1614 int erc;
1615 struct mdoc_node *n;
1616
1617 erc = 0;
1618
1619 /* Check that we have a finished prologue. */
1620
1621 if ( ! (MDOC_PBODY & mdoc->flags)) {
1622 erc++;
1623 mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG);
1624 }
1625
1626 n = mdoc->first;
1627 assert(n);
1628
1629 /* Check that we begin with a proper `Sh'. */
1630
1631 if (NULL == n->child) {
1632 erc++;
1633 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1634 } else if (MDOC_BLOCK != n->child->type ||
1635 MDOC_Sh != n->child->tok) {
1636 erc++;
1637 /* Can this be lifted? See rxdebug.1 for example. */
1638 mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY);
1639 }
1640
1641 return(erc ? 0 : 1);
1642 }
1643
1644 static int
1645 post_st(POST_ARGS)
1646 {
1647 struct mdoc_node *ch;
1648 const char *p;
1649
1650 if (NULL == (ch = mdoc->last->child)) {
1651 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY);
1652 mdoc_node_delete(mdoc, mdoc->last);
1653 return(1);
1654 }
1655
1656 assert(MDOC_TEXT == ch->type);
1657
1658 if (NULL == (p = mdoc_a2st(ch->string))) {
1659 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD);
1660 mdoc_node_delete(mdoc, mdoc->last);
1661 } else {
1662 free(ch->string);
1663 ch->string = mandoc_strdup(p);
1664 }
1665
1666 return(1);
1667 }
1668
1669 static int
1670 post_rs(POST_ARGS)
1671 {
1672 struct mdoc_node *nn, *next, *prev;
1673 int i, j;
1674
1675 switch (mdoc->last->type) {
1676 case (MDOC_HEAD):
1677 check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
1678 return(1);
1679 case (MDOC_BODY):
1680 if (mdoc->last->child)
1681 break;
1682 check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
1683 return(1);
1684 default:
1685 return(1);
1686 }
1687
1688 /*
1689 * Make sure only certain types of nodes are allowed within the
1690 * the `Rs' body. Delete offending nodes and raise a warning.
1691 * Do this before re-ordering for the sake of clarity.
1692 */
1693
1694 next = NULL;
1695 for (nn = mdoc->last->child; nn; nn = next) {
1696 for (i = 0; i < RSORD_MAX; i++)
1697 if (nn->tok == rsord[i])
1698 break;
1699
1700 if (i < RSORD_MAX) {
1701 if (MDOC__J == rsord[i] || MDOC__B == rsord[i])
1702 mdoc->last->norm->Rs.quote_T++;
1703 next = nn->next;
1704 continue;
1705 }
1706
1707 next = nn->next;
1708 mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD);
1709 mdoc_node_delete(mdoc, nn);
1710 }
1711
1712 /*
1713 * The full `Rs' block needs special handling to order the
1714 * sub-elements according to `rsord'. Pick through each element
1715 * and correctly order it. This is a insertion sort.
1716 */
1717
1718 next = NULL;
1719 for (nn = mdoc->last->child->next; nn; nn = next) {
1720 /* Determine order of `nn'. */
1721 for (i = 0; i < RSORD_MAX; i++)
1722 if (rsord[i] == nn->tok)
1723 break;
1724
1725 /*
1726 * Remove `nn' from the chain. This somewhat
1727 * repeats mdoc_node_unlink(), but since we're
1728 * just re-ordering, there's no need for the
1729 * full unlink process.
1730 */
1731
1732 if (NULL != (next = nn->next))
1733 next->prev = nn->prev;
1734
1735 if (NULL != (prev = nn->prev))
1736 prev->next = nn->next;
1737
1738 nn->prev = nn->next = NULL;
1739
1740 /*
1741 * Scan back until we reach a node that's
1742 * ordered before `nn'.
1743 */
1744
1745 for ( ; prev ; prev = prev->prev) {
1746 /* Determine order of `prev'. */
1747 for (j = 0; j < RSORD_MAX; j++)
1748 if (rsord[j] == prev->tok)
1749 break;
1750
1751 if (j <= i)
1752 break;
1753 }
1754
1755 /*
1756 * Set `nn' back into its correct place in front
1757 * of the `prev' node.
1758 */
1759
1760 nn->prev = prev;
1761
1762 if (prev) {
1763 if (prev->next)
1764 prev->next->prev = nn;
1765 nn->next = prev->next;
1766 prev->next = nn;
1767 } else {
1768 mdoc->last->child->prev = nn;
1769 nn->next = mdoc->last->child;
1770 mdoc->last->child = nn;
1771 }
1772 }
1773
1774 return(1);
1775 }
1776
1777 static int
1778 post_ns(POST_ARGS)
1779 {
1780
1781 if (MDOC_LINE & mdoc->last->flags)
1782 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNNS);
1783 return(1);
1784 }
1785
1786 static int
1787 post_sh(POST_ARGS)
1788 {
1789
1790 if (MDOC_HEAD == mdoc->last->type)
1791 return(post_sh_head(mdoc));
1792 if (MDOC_BODY == mdoc->last->type)
1793 return(post_sh_body(mdoc));
1794
1795 return(1);
1796 }
1797
1798 static int
1799 post_sh_body(POST_ARGS)
1800 {
1801 struct mdoc_node *n;
1802
1803 if (SEC_NAME != mdoc->lastsec)
1804 return(1);
1805
1806 /*
1807 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
1808 * macros (can have multiple `Nm' and one `Nd'). Note that the
1809 * children of the BODY declaration can also be "text".
1810 */
1811
1812 if (NULL == (n = mdoc->last->child)) {
1813 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1814 return(1);
1815 }
1816
1817 for ( ; n && n->next; n = n->next) {
1818 if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
1819 continue;
1820 if (MDOC_TEXT == n->type)
1821 continue;
1822 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1823 }
1824
1825 assert(n);
1826 if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
1827 return(1);
1828
1829 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC);
1830 return(1);
1831 }
1832
1833 static int
1834 post_sh_head(POST_ARGS)
1835 {
1836 char buf[BUFSIZ];
1837 enum mdoc_sec sec;
1838
1839 /*
1840 * Process a new section. Sections are either "named" or
1841 * "custom". Custom sections are user-defined, while named ones
1842 * follow a conventional order and may only appear in certain
1843 * manual sections.
1844 */
1845
1846 if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ))
1847 return(0);
1848
1849 sec = a2sec(buf);
1850
1851 /* The NAME should be first. */
1852
1853 if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
1854 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST);
1855
1856 /* The SYNOPSIS gets special attention in other areas. */
1857
1858 if (SEC_SYNOPSIS == sec)
1859 mdoc->flags |= MDOC_SYNOPSIS;
1860 else
1861 mdoc->flags &= ~MDOC_SYNOPSIS;
1862
1863 /* Mark our last section. */
1864
1865 mdoc->lastsec = sec;
1866
1867 /* We don't care about custom sections after this. */
1868
1869 if (SEC_CUSTOM == sec)
1870 return(1);
1871
1872 /*
1873 * Check whether our non-custom section is being repeated or is
1874 * out of order.
1875 */
1876
1877 if (sec == mdoc->lastnamed)
1878 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP);
1879
1880 if (sec < mdoc->lastnamed)
1881 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO);
1882
1883 /* Mark the last named section. */
1884
1885 mdoc->lastnamed = sec;
1886
1887 /* Check particular section/manual conventions. */
1888
1889 assert(mdoc->meta.msec);
1890
1891 switch (sec) {
1892 case (SEC_RETURN_VALUES):
1893 /* FALLTHROUGH */
1894 case (SEC_ERRORS):
1895 /* FALLTHROUGH */
1896 case (SEC_LIBRARY):
1897 if (*mdoc->meta.msec == '2')
1898 break;
1899 if (*mdoc->meta.msec == '3')
1900 break;
1901 if (*mdoc->meta.msec == '9')
1902 break;
1903 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC);
1904 break;
1905 default:
1906 break;
1907 }
1908
1909 return(1);
1910 }
1911
1912 static int
1913 post_ignpar(POST_ARGS)
1914 {
1915 struct mdoc_node *np;
1916
1917 if (MDOC_BODY != mdoc->last->type)
1918 return(1);
1919
1920 if (NULL != (np = mdoc->last->child))
1921 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1922 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1923 mdoc_node_delete(mdoc, np);
1924 }
1925
1926 if (NULL != (np = mdoc->last->last))
1927 if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
1928 mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR);
1929 mdoc_node_delete(mdoc, np);
1930 }
1931
1932 return(1);
1933 }
1934
1935 static int
1936 pre_par(PRE_ARGS)
1937 {
1938
1939 if (NULL == mdoc->last)
1940 return(1);
1941 if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
1942 return(1);
1943
1944 /*
1945 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
1946 * block: `Lp', `Pp', or non-compact `Bd' or `Bl'.
1947 */
1948
1949 if (MDOC_Pp != mdoc->last->tok && MDOC_Lp != mdoc->last->tok)
1950 return(1);
1951 if (MDOC_Bl == n->tok && n->norm->Bl.comp)
1952 return(1);
1953 if (MDOC_Bd == n->tok && n->norm->Bd.comp)
1954 return(1);
1955 if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
1956 return(1);
1957
1958 mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
1959 mdoc_node_delete(mdoc, mdoc->last);
1960 return(1);
1961 }
1962
1963 static int
1964 pre_literal(PRE_ARGS)
1965 {
1966
1967 if (MDOC_BODY != n->type)
1968 return(1);
1969
1970 /*
1971 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
1972 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
1973 */
1974
1975 switch (n->tok) {
1976 case (MDOC_Dl):
1977 mdoc->flags |= MDOC_LITERAL;
1978 break;
1979 case (MDOC_Bd):
1980 if (DISP_literal == n->norm->Bd.type)
1981 mdoc->flags |= MDOC_LITERAL;
1982 if (DISP_unfilled == n->norm->Bd.type)
1983 mdoc->flags |= MDOC_LITERAL;
1984 break;
1985 default:
1986 abort();
1987 /* NOTREACHED */
1988 }
1989
1990 return(1);
1991 }
1992
1993 static int
1994 post_dd(POST_ARGS)
1995 {
1996 char buf[DATESIZE];
1997 struct mdoc_node *n;
1998
1999 if (mdoc->meta.date)
2000 free(mdoc->meta.date);
2001
2002 n = mdoc->last;
2003 if (NULL == n->child || '\0' == n->child->string[0]) {
2004 mdoc->meta.date = mandoc_normdate(NULL,
2005 mdoc->msg, mdoc->data, n->line, n->pos);
2006 return(1);
2007 }
2008
2009 if ( ! concat(mdoc, buf, n->child, DATESIZE))
2010 return(0);
2011
2012 mdoc->meta.date = mandoc_normdate(buf,
2013 mdoc->msg, mdoc->data, n->line, n->pos);
2014
2015 return(1);
2016 }
2017
2018 static int
2019 post_dt(POST_ARGS)
2020 {
2021 struct mdoc_node *nn, *n;
2022 const char *cp;
2023 char *p;
2024
2025 n = mdoc->last;
2026
2027 if (mdoc->meta.title)
2028 free(mdoc->meta.title);
2029 if (mdoc->meta.vol)
2030 free(mdoc->meta.vol);
2031 if (mdoc->meta.arch)
2032 free(mdoc->meta.arch);
2033
2034 mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL;
2035
2036 /* First make all characters uppercase. */
2037
2038 if (NULL != (nn = n->child))
2039 for (p = nn->string; *p; p++) {
2040 if (toupper((u_char)*p) == *p)
2041 continue;
2042
2043 /*
2044 * FIXME: don't be lazy: have this make all
2045 * characters be uppercase and just warn once.
2046 */
2047 mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE);
2048 break;
2049 }
2050
2051 /* Handles: `.Dt'
2052 * --> title = unknown, volume = local, msec = 0, arch = NULL
2053 */
2054
2055 if (NULL == (nn = n->child)) {
2056 /* XXX: make these macro values. */
2057 /* FIXME: warn about missing values. */
2058 mdoc->meta.title = mandoc_strdup("UNKNOWN");
2059 mdoc->meta.vol = mandoc_strdup("LOCAL");
2060 mdoc->meta.msec = mandoc_strdup("1");
2061 return(1);
2062 }
2063
2064 /* Handles: `.Dt TITLE'
2065 * --> title = TITLE, volume = local, msec = 0, arch = NULL
2066 */
2067
2068 mdoc->meta.title = mandoc_strdup
2069 ('\0' == nn->string[0] ? "UNKNOWN" : nn->string);
2070
2071 if (NULL == (nn = nn->next)) {
2072 /* FIXME: warn about missing msec. */
2073 /* XXX: make this a macro value. */
2074 mdoc->meta.vol = mandoc_strdup("LOCAL");
2075 mdoc->meta.msec = mandoc_strdup("1");
2076 return(1);
2077 }
2078
2079 /* Handles: `.Dt TITLE SEC'
2080 * --> title = TITLE, volume = SEC is msec ?
2081 * format(msec) : SEC,
2082 * msec = SEC is msec ? atoi(msec) : 0,
2083 * arch = NULL
2084 */
2085
2086 cp = mdoc_a2msec(nn->string);
2087 if (cp) {
2088 mdoc->meta.vol = mandoc_strdup(cp);
2089 mdoc->meta.msec = mandoc_strdup(nn->string);
2090 } else {
2091 mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC);
2092 mdoc->meta.vol = mandoc_strdup(nn->string);
2093 mdoc->meta.msec = mandoc_strdup(nn->string);
2094 }
2095
2096 if (NULL == (nn = nn->next))
2097 return(1);
2098
2099 /* Handles: `.Dt TITLE SEC VOL'
2100 * --> title = TITLE, volume = VOL is vol ?
2101 * format(VOL) :
2102 * VOL is arch ? format(arch) :
2103 * VOL
2104 */
2105
2106 cp = mdoc_a2vol(nn->string);
2107 if (cp) {
2108 free(mdoc->meta.vol);
2109 mdoc->meta.vol = mandoc_strdup(cp);
2110 } else {
2111 /* FIXME: warn about bad arch. */
2112 cp = mdoc_a2arch(nn->string);
2113 if (NULL == cp) {
2114 free(mdoc->meta.vol);
2115 mdoc->meta.vol = mandoc_strdup(nn->string);
2116 } else
2117 mdoc->meta.arch = mandoc_strdup(cp);
2118 }
2119
2120 /* Ignore any subsequent parameters... */
2121 /* FIXME: warn about subsequent parameters. */
2122
2123 return(1);
2124 }
2125
2126 static int
2127 post_prol(POST_ARGS)
2128 {
2129 /*
2130 * Remove prologue macros from the document after they're
2131 * processed. The final document uses mdoc_meta for these
2132 * values and discards the originals.
2133 */
2134
2135 mdoc_node_delete(mdoc, mdoc->last);
2136 if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os)
2137 mdoc->flags |= MDOC_PBODY;
2138
2139 return(1);
2140 }
2141
2142 static int
2143 post_bx(POST_ARGS)
2144 {
2145 struct mdoc_node *n;
2146
2147 /*
2148 * Make `Bx's second argument always start with an uppercase
2149 * letter. Groff checks if it's an "accepted" term, but we just
2150 * uppercase blindly.
2151 */
2152
2153 n = mdoc->last->child;
2154 if (n && NULL != (n = n->next))
2155 *n->string = (char)toupper
2156 ((unsigned char)*n->string);
2157
2158 return(1);
2159 }
2160
2161 static int
2162 post_os(POST_ARGS)
2163 {
2164 struct mdoc_node *n;
2165 char buf[BUFSIZ];
2166 #ifndef OSNAME
2167 struct utsname utsname;
2168 #endif
2169
2170 n = mdoc->last;
2171
2172 /*
2173 * Set the operating system by way of the `Os' macro. Note that
2174 * if an argument isn't provided and -DOSNAME="\"foo\"" is
2175 * provided during compilation, this value will be used instead
2176 * of filling in "sysname release" from uname().
2177 */
2178
2179 if (mdoc->meta.os)
2180 free(mdoc->meta.os);
2181
2182 if ( ! concat(mdoc, buf, n->child, BUFSIZ))
2183 return(0);
2184
2185 /* XXX: yes, these can all be dynamically-adjusted buffers, but
2186 * it's really not worth the extra hackery.
2187 */
2188
2189 if ('\0' == buf[0]) {
2190 #ifdef OSNAME
2191 if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
2192 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2193 return(0);
2194 }
2195 #else /*!OSNAME */
2196 if (uname(&utsname)) {
2197 mdoc_nmsg(mdoc, n, MANDOCERR_UNAME);
2198 mdoc->meta.os = mandoc_strdup("UNKNOWN");
2199 return(post_prol(mdoc));
2200 }
2201
2202 if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) {
2203 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2204 return(0);
2205 }
2206 if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) {
2207 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2208 return(0);
2209 }
2210 if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) {
2211 mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
2212 return(0);
2213 }
2214 #endif /*!OSNAME*/
2215 }
2216
2217 mdoc->meta.os = mandoc_strdup(buf);
2218 return(1);
2219 }
2220
2221 static int
2222 post_std(POST_ARGS)
2223 {
2224 struct mdoc_node *nn, *n;
2225
2226 n = mdoc->last;
2227
2228 /*
2229 * Macros accepting `-std' as an argument have the name of the
2230 * current document (`Nm') filled in as the argument if it's not
2231 * provided.
2232 */
2233
2234 if (n->child)
2235 return(1);
2236
2237 if (NULL == mdoc->meta.name)
2238 return(1);
2239
2240 nn = n;
2241 mdoc->next = MDOC_NEXT_CHILD;
2242
2243 if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
2244 return(0);
2245
2246 mdoc->last = nn;
2247 return(1);
2248 }
2249
2250 static int
2251 concat(struct mdoc *m, char *p, const struct mdoc_node *n, size_t sz)
2252 {
2253
2254 p[0] = '\0';
2255
2256 /*
2257 * Concatenate sibling nodes together. All siblings must be of
2258 * type MDOC_TEXT or an assertion is raised. Concatenation is
2259 * separated by a single whitespace. Returns 0 on fatal (string
2260 * overrun) error.
2261 */
2262
2263 for ( ; n; n = n->next) {
2264 assert(MDOC_TEXT == n->type);
2265
2266 if (strlcat(p, n->string, sz) >= sz) {
2267 mdoc_nmsg(m, n, MANDOCERR_MEM);
2268 return(0);
2269 }
2270
2271 if (NULL == n->next)
2272 continue;
2273
2274 if (strlcat(p, " ", sz) >= sz) {
2275 mdoc_nmsg(m, n, MANDOCERR_MEM);
2276 return(0);
2277 }
2278 }
2279
2280 return(1);
2281 }
2282
2283 static enum mdoc_sec
2284 a2sec(const char *p)
2285 {
2286 int i;
2287
2288 for (i = 0; i < (int)SEC__MAX; i++)
2289 if (secnames[i] && 0 == strcmp(p, secnames[i]))
2290 return((enum mdoc_sec)i);
2291
2292 return(SEC_CUSTOM);
2293 }
2294