Annotation of src/usr.bin/mandoc/mdoc_validate.c, Revision 1.278
1.278 ! schwarze 1: /* $OpenBSD: mdoc_validate.c,v 1.277 2018/08/17 20:31:52 schwarze Exp $ */
1.1 kristaps 2: /*
1.101 schwarze 3: * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
1.270 schwarze 4: * Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org>
1.117 schwarze 5: * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
1.1 kristaps 6: *
7: * Permission to use, copy, modify, and distribute this software for any
1.2 schwarze 8: * purpose with or without fee is hereby granted, provided that the above
9: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 10: *
1.200 schwarze 11: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1.2 schwarze 12: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1.200 schwarze 13: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1.2 schwarze 14: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1 kristaps 18: */
1.162 schwarze 19: #include <sys/types.h>
1.114 schwarze 20: #ifndef OSNAME
1.76 schwarze 21: #include <sys/utsname.h>
22: #endif
23:
1.1 kristaps 24: #include <assert.h>
25: #include <ctype.h>
1.31 schwarze 26: #include <limits.h>
1.57 schwarze 27: #include <stdio.h>
1.1 kristaps 28: #include <stdlib.h>
29: #include <string.h>
1.76 schwarze 30: #include <time.h>
1.1 kristaps 31:
1.200 schwarze 32: #include "mandoc_aux.h"
33: #include "mandoc.h"
1.261 schwarze 34: #include "mandoc_xr.h"
1.200 schwarze 35: #include "roff.h"
1.92 schwarze 36: #include "mdoc.h"
1.200 schwarze 37: #include "libmandoc.h"
1.203 schwarze 38: #include "roff_int.h"
1.1 kristaps 39: #include "libmdoc.h"
40:
41: /* FIXME: .Bl -diag can't have non-text children in HEAD. */
42:
1.202 schwarze 43: #define POST_ARGS struct roff_man *mdoc
1.1 kristaps 44:
1.72 schwarze 45: enum check_ineq {
46: CHECK_LT,
47: CHECK_GT,
48: CHECK_EQ
49: };
50:
1.175 schwarze 51: typedef void (*v_post)(POST_ARGS);
1.1 kristaps 52:
1.233 schwarze 53: static int build_list(struct roff_man *, int);
1.202 schwarze 54: static void check_argv(struct roff_man *,
1.201 schwarze 55: struct roff_node *, struct mdoc_argv *);
1.202 schwarze 56: static void check_args(struct roff_man *, struct roff_node *);
1.270 schwarze 57: static void check_text(struct roff_man *, int, int, char *);
58: static void check_text_em(struct roff_man *, int, int, char *);
1.251 schwarze 59: static void check_toptext(struct roff_man *, int, int, const char *);
1.201 schwarze 60: static int child_an(const struct roff_node *);
1.236 schwarze 61: static size_t macro2len(enum roff_tok);
1.238 schwarze 62: static void rewrite_macro2len(struct roff_man *, char **);
1.258 schwarze 63: static int similar(const char *, const char *);
1.28 schwarze 64:
1.278 ! schwarze 65: static void post_abort(POST_ARGS);
1.175 schwarze 66: static void post_an(POST_ARGS);
1.215 schwarze 67: static void post_an_norm(POST_ARGS);
1.175 schwarze 68: static void post_at(POST_ARGS);
1.215 schwarze 69: static void post_bd(POST_ARGS);
1.175 schwarze 70: static void post_bf(POST_ARGS);
71: static void post_bk(POST_ARGS);
72: static void post_bl(POST_ARGS);
73: static void post_bl_block(POST_ARGS);
74: static void post_bl_head(POST_ARGS);
1.215 schwarze 75: static void post_bl_norm(POST_ARGS);
1.175 schwarze 76: static void post_bx(POST_ARGS);
77: static void post_defaults(POST_ARGS);
1.215 schwarze 78: static void post_display(POST_ARGS);
1.175 schwarze 79: static void post_dd(POST_ARGS);
1.248 schwarze 80: static void post_delim(POST_ARGS);
1.263 schwarze 81: static void post_delim_nb(POST_ARGS);
1.175 schwarze 82: static void post_dt(POST_ARGS);
83: static void post_en(POST_ARGS);
84: static void post_es(POST_ARGS);
85: static void post_eoln(POST_ARGS);
86: static void post_ex(POST_ARGS);
87: static void post_fa(POST_ARGS);
88: static void post_fn(POST_ARGS);
89: static void post_fname(POST_ARGS);
90: static void post_fo(POST_ARGS);
91: static void post_hyph(POST_ARGS);
92: static void post_ignpar(POST_ARGS);
93: static void post_it(POST_ARGS);
94: static void post_lb(POST_ARGS);
95: static void post_nd(POST_ARGS);
96: static void post_nm(POST_ARGS);
97: static void post_ns(POST_ARGS);
1.215 schwarze 98: static void post_obsolete(POST_ARGS);
1.175 schwarze 99: static void post_os(POST_ARGS);
100: static void post_par(POST_ARGS);
1.215 schwarze 101: static void post_prevpar(POST_ARGS);
1.175 schwarze 102: static void post_root(POST_ARGS);
103: static void post_rs(POST_ARGS);
1.233 schwarze 104: static void post_rv(POST_ARGS);
1.175 schwarze 105: static void post_sh(POST_ARGS);
106: static void post_sh_head(POST_ARGS);
107: static void post_sh_name(POST_ARGS);
108: static void post_sh_see_also(POST_ARGS);
109: static void post_sh_authors(POST_ARGS);
110: static void post_sm(POST_ARGS);
111: static void post_st(POST_ARGS);
1.215 schwarze 112: static void post_std(POST_ARGS);
1.263 schwarze 113: static void post_sx(POST_ARGS);
1.244 schwarze 114: static void post_useless(POST_ARGS);
1.226 schwarze 115: static void post_xr(POST_ARGS);
1.231 schwarze 116: static void post_xx(POST_ARGS);
1.175 schwarze 117:
1.276 schwarze 118: static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = {
1.215 schwarze 119: post_dd, /* Dd */
120: post_dt, /* Dt */
121: post_os, /* Os */
122: post_sh, /* Sh */
123: post_ignpar, /* Ss */
124: post_par, /* Pp */
125: post_display, /* D1 */
126: post_display, /* Dl */
127: post_display, /* Bd */
128: NULL, /* Ed */
129: post_bl, /* Bl */
130: NULL, /* El */
131: post_it, /* It */
1.263 schwarze 132: post_delim_nb, /* Ad */
1.215 schwarze 133: post_an, /* An */
1.236 schwarze 134: NULL, /* Ap */
1.215 schwarze 135: post_defaults, /* Ar */
136: NULL, /* Cd */
1.263 schwarze 137: post_delim_nb, /* Cm */
138: post_delim_nb, /* Dv */
139: post_delim_nb, /* Er */
140: post_delim_nb, /* Ev */
1.215 schwarze 141: post_ex, /* Ex */
142: post_fa, /* Fa */
143: NULL, /* Fd */
1.263 schwarze 144: post_delim_nb, /* Fl */
1.215 schwarze 145: post_fn, /* Fn */
1.263 schwarze 146: post_delim_nb, /* Ft */
147: post_delim_nb, /* Ic */
148: post_delim_nb, /* In */
1.215 schwarze 149: post_defaults, /* Li */
150: post_nd, /* Nd */
151: post_nm, /* Nm */
1.263 schwarze 152: post_delim_nb, /* Op */
1.278 ! schwarze 153: post_abort, /* Ot */
1.215 schwarze 154: post_defaults, /* Pa */
1.233 schwarze 155: post_rv, /* Rv */
1.215 schwarze 156: post_st, /* St */
1.263 schwarze 157: post_delim_nb, /* Va */
158: post_delim_nb, /* Vt */
1.226 schwarze 159: post_xr, /* Xr */
1.215 schwarze 160: NULL, /* %A */
161: post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */
162: NULL, /* %D */
163: NULL, /* %I */
164: NULL, /* %J */
165: post_hyph, /* %N */
166: post_hyph, /* %O */
167: NULL, /* %P */
168: post_hyph, /* %R */
169: post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */
170: NULL, /* %V */
171: NULL, /* Ac */
1.269 schwarze 172: NULL, /* Ao */
1.263 schwarze 173: post_delim_nb, /* Aq */
1.215 schwarze 174: post_at, /* At */
175: NULL, /* Bc */
176: post_bf, /* Bf */
1.269 schwarze 177: NULL, /* Bo */
1.215 schwarze 178: NULL, /* Bq */
1.231 schwarze 179: post_xx, /* Bsx */
1.215 schwarze 180: post_bx, /* Bx */
181: post_obsolete, /* Db */
182: NULL, /* Dc */
183: NULL, /* Do */
184: NULL, /* Dq */
185: NULL, /* Ec */
186: NULL, /* Ef */
1.263 schwarze 187: post_delim_nb, /* Em */
1.215 schwarze 188: NULL, /* Eo */
1.231 schwarze 189: post_xx, /* Fx */
1.263 schwarze 190: post_delim_nb, /* Ms */
1.250 schwarze 191: NULL, /* No */
1.215 schwarze 192: post_ns, /* Ns */
1.231 schwarze 193: post_xx, /* Nx */
194: post_xx, /* Ox */
1.215 schwarze 195: NULL, /* Pc */
196: NULL, /* Pf */
1.269 schwarze 197: NULL, /* Po */
1.263 schwarze 198: post_delim_nb, /* Pq */
1.215 schwarze 199: NULL, /* Qc */
1.263 schwarze 200: post_delim_nb, /* Ql */
1.269 schwarze 201: NULL, /* Qo */
1.263 schwarze 202: post_delim_nb, /* Qq */
1.215 schwarze 203: NULL, /* Re */
204: post_rs, /* Rs */
205: NULL, /* Sc */
1.269 schwarze 206: NULL, /* So */
1.263 schwarze 207: post_delim_nb, /* Sq */
1.215 schwarze 208: post_sm, /* Sm */
1.263 schwarze 209: post_sx, /* Sx */
210: post_delim_nb, /* Sy */
1.244 schwarze 211: post_useless, /* Tn */
1.231 schwarze 212: post_xx, /* Ux */
1.215 schwarze 213: NULL, /* Xc */
214: NULL, /* Xo */
215: post_fo, /* Fo */
216: NULL, /* Fc */
1.269 schwarze 217: NULL, /* Oo */
1.215 schwarze 218: NULL, /* Oc */
219: post_bk, /* Bk */
220: NULL, /* Ek */
221: post_eoln, /* Bt */
1.248 schwarze 222: post_obsolete, /* Hf */
1.215 schwarze 223: post_obsolete, /* Fr */
224: post_eoln, /* Ud */
225: post_lb, /* Lb */
1.278 ! schwarze 226: post_abort, /* Lp */
1.263 schwarze 227: post_delim_nb, /* Lk */
1.215 schwarze 228: post_defaults, /* Mt */
1.263 schwarze 229: post_delim_nb, /* Brq */
1.269 schwarze 230: NULL, /* Bro */
1.215 schwarze 231: NULL, /* Brc */
232: NULL, /* %C */
233: post_es, /* Es */
234: post_en, /* En */
1.231 schwarze 235: post_xx, /* Dx */
1.215 schwarze 236: NULL, /* %Q */
237: NULL, /* %U */
238: NULL, /* Ta */
1.1 kristaps 239: };
240:
1.76 schwarze 241: #define RSORD_MAX 14 /* Number of `Rs' blocks. */
242:
1.236 schwarze 243: static const enum roff_tok rsord[RSORD_MAX] = {
1.76 schwarze 244: MDOC__A,
245: MDOC__T,
246: MDOC__B,
247: MDOC__I,
248: MDOC__J,
249: MDOC__R,
250: MDOC__N,
251: MDOC__V,
1.104 schwarze 252: MDOC__U,
1.76 schwarze 253: MDOC__P,
254: MDOC__Q,
1.110 schwarze 255: MDOC__C,
1.76 schwarze 256: MDOC__D,
1.110 schwarze 257: MDOC__O
1.76 schwarze 258: };
259:
1.91 schwarze 260: static const char * const secnames[SEC__MAX] = {
261: NULL,
262: "NAME",
263: "LIBRARY",
264: "SYNOPSIS",
265: "DESCRIPTION",
1.126 dlg 266: "CONTEXT",
1.91 schwarze 267: "IMPLEMENTATION NOTES",
268: "RETURN VALUES",
269: "ENVIRONMENT",
270: "FILES",
271: "EXIT STATUS",
272: "EXAMPLES",
273: "DIAGNOSTICS",
274: "COMPATIBILITY",
275: "ERRORS",
276: "SEE ALSO",
277: "STANDARDS",
278: "HISTORY",
279: "AUTHORS",
280: "CAVEATS",
281: "BUGS",
282: "SECURITY CONSIDERATIONS",
283: NULL
284: };
1.1 kristaps 285:
1.128 schwarze 286:
1.278 ! schwarze 287: /* Validate the subtree rooted at mdoc->last. */
1.175 schwarze 288: void
1.214 schwarze 289: mdoc_node_validate(struct roff_man *mdoc)
1.1 kristaps 290: {
1.270 schwarze 291: struct roff_node *n, *np;
1.236 schwarze 292: const v_post *p;
1.1 kristaps 293:
1.278 ! schwarze 294: /*
! 295: * Translate obsolete macros to modern macros first
! 296: * such that later code does not need to look
! 297: * for the obsolete versions.
! 298: */
! 299:
1.160 schwarze 300: n = mdoc->last;
1.278 ! schwarze 301: switch (n->tok) {
! 302: case MDOC_Lp:
! 303: n->tok = MDOC_Pp;
! 304: break;
! 305: case MDOC_Ot:
! 306: post_obsolete(mdoc);
! 307: n->tok = MDOC_Ft;
! 308: break;
! 309: default:
! 310: break;
! 311: }
! 312:
! 313: /*
! 314: * Iterate over all children, recursing into each one
! 315: * in turn, depth-first.
! 316: */
! 317:
1.214 schwarze 318: mdoc->last = mdoc->last->child;
319: while (mdoc->last != NULL) {
320: mdoc_node_validate(mdoc);
321: if (mdoc->last == n)
322: mdoc->last = mdoc->last->child;
323: else
324: mdoc->last = mdoc->last->next;
325: }
1.1 kristaps 326:
1.278 ! schwarze 327: /* Finally validate the macro itself. */
! 328:
1.214 schwarze 329: mdoc->last = n;
330: mdoc->next = ROFF_NEXT_SIBLING;
1.160 schwarze 331: switch (n->type) {
1.200 schwarze 332: case ROFFT_TEXT:
1.270 schwarze 333: np = n->parent;
1.228 schwarze 334: if (n->sec != SEC_SYNOPSIS ||
1.270 schwarze 335: (np->tok != MDOC_Cd && np->tok != MDOC_Fd))
1.215 schwarze 336: check_text(mdoc, n->line, n->pos, n->string);
1.270 schwarze 337: if (np->tok != MDOC_Ql && np->tok != MDOC_Dl &&
338: (np->tok != MDOC_Bd ||
339: (mdoc->flags & MDOC_LITERAL) == 0) &&
340: (np->tok != MDOC_It || np->type != ROFFT_HEAD ||
341: np->parent->parent->norm->Bl.type != LIST_diag))
342: check_text_em(mdoc, n->line, n->pos, n->string);
343: if (np->tok == MDOC_It || (np->type == ROFFT_BODY &&
344: (np->tok == MDOC_Sh || np->tok == MDOC_Ss)))
1.251 schwarze 345: check_toptext(mdoc, n->line, n->pos, n->string);
1.215 schwarze 346: break;
1.273 schwarze 347: case ROFFT_COMMENT:
1.200 schwarze 348: case ROFFT_EQN:
349: case ROFFT_TBL:
1.175 schwarze 350: break;
1.200 schwarze 351: case ROFFT_ROOT:
1.175 schwarze 352: post_root(mdoc);
353: break;
1.84 schwarze 354: default:
1.215 schwarze 355: check_args(mdoc, mdoc->last);
1.171 schwarze 356:
357: /*
358: * Closing delimiters are not special at the
359: * beginning of a block, opening delimiters
360: * are not special at the end.
361: */
362:
363: if (n->child != NULL)
1.230 schwarze 364: n->child->flags &= ~NODE_DELIMC;
1.171 schwarze 365: if (n->last != NULL)
1.230 schwarze 366: n->last->flags &= ~NODE_DELIMO;
1.171 schwarze 367:
368: /* Call the macro's postprocessor. */
369:
1.239 schwarze 370: if (n->tok < ROFF_MAX) {
371: switch(n->tok) {
372: case ROFF_br:
1.242 schwarze 373: case ROFF_sp:
1.239 schwarze 374: post_par(mdoc);
375: break;
376: default:
1.240 schwarze 377: roff_validate(mdoc);
378: break;
1.239 schwarze 379: }
380: break;
381: }
382:
383: assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
1.276 schwarze 384: p = mdoc_valids + (n->tok - MDOC_Dd);
1.175 schwarze 385: if (*p)
386: (*p)(mdoc);
1.214 schwarze 387: if (mdoc->last == n)
388: mdoc_state(mdoc, n);
1.175 schwarze 389: break;
1.84 schwarze 390: }
1.1 kristaps 391: }
392:
1.175 schwarze 393: static void
1.202 schwarze 394: check_args(struct roff_man *mdoc, struct roff_node *n)
1.1 kristaps 395: {
396: int i;
397:
398: if (NULL == n->args)
1.76 schwarze 399: return;
1.1 kristaps 400:
401: assert(n->args->argc);
402: for (i = 0; i < (int)n->args->argc; i++)
1.109 schwarze 403: check_argv(mdoc, n, &n->args->argv[i]);
1.1 kristaps 404: }
405:
1.76 schwarze 406: static void
1.202 schwarze 407: check_argv(struct roff_man *mdoc, struct roff_node *n, struct mdoc_argv *v)
1.1 kristaps 408: {
409: int i;
410:
411: for (i = 0; i < (int)v->sz; i++)
1.109 schwarze 412: check_text(mdoc, v->line, v->pos, v->value[i]);
1.1 kristaps 413: }
414:
1.76 schwarze 415: static void
1.202 schwarze 416: check_text(struct roff_man *mdoc, int ln, int pos, char *p)
1.1 kristaps 417: {
1.95 schwarze 418: char *cp;
1.65 schwarze 419:
1.109 schwarze 420: if (MDOC_LITERAL & mdoc->flags)
1.96 schwarze 421: return;
422:
423: for (cp = p; NULL != (p = strchr(p, '\t')); p++)
1.147 schwarze 424: mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
425: ln, pos + (int)(p - cp), NULL);
1.270 schwarze 426: }
427:
428: static void
429: check_text_em(struct roff_man *mdoc, int ln, int pos, char *p)
430: {
431: const struct roff_node *np, *nn;
432: char *cp;
433:
434: np = mdoc->last->prev;
435: nn = mdoc->last->next;
436:
437: /* Look for em-dashes wrongly encoded as "--". */
438:
439: for (cp = p; *cp != '\0'; cp++) {
1.271 schwarze 440: if (cp[0] != '-' || cp[1] != '-')
1.270 schwarze 441: continue;
1.271 schwarze 442: cp++;
1.270 schwarze 443:
444: /* Skip input sequences of more than two '-'. */
445:
446: if (cp[1] == '-') {
447: while (cp[1] == '-')
448: cp++;
449: continue;
450: }
451:
452: /* Skip "--" directly attached to something else. */
453:
454: if ((cp - p > 1 && cp[-2] != ' ') ||
455: (cp[1] != '\0' && cp[1] != ' '))
456: continue;
457:
458: /* Require a letter right before or right afterwards. */
459:
460: if ((cp - p > 2 ?
461: isalpha((unsigned char)cp[-3]) :
462: np != NULL &&
463: np->type == ROFFT_TEXT &&
1.275 schwarze 464: *np->string != '\0' &&
1.270 schwarze 465: isalpha((unsigned char)np->string[
466: strlen(np->string) - 1])) ||
1.274 schwarze 467: (cp[1] != '\0' && cp[2] != '\0' ?
1.270 schwarze 468: isalpha((unsigned char)cp[2]) :
469: nn != NULL &&
470: nn->type == ROFFT_TEXT &&
471: isalpha((unsigned char)*nn->string))) {
472: mandoc_msg(MANDOCERR_DASHDASH, mdoc->parse,
473: ln, pos + (int)(cp - p) - 1, NULL);
474: break;
475: }
476: }
1.1 kristaps 477: }
478:
1.175 schwarze 479: static void
1.251 schwarze 480: check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
1.245 schwarze 481: {
1.251 schwarze 482: const char *cp, *cpr;
483:
484: if (*p == '\0')
485: return;
1.245 schwarze 486:
487: if ((cp = strstr(p, "OpenBSD")) != NULL)
488: mandoc_msg(MANDOCERR_BX, mdoc->parse,
489: ln, pos + (cp - p), "Ox");
490: if ((cp = strstr(p, "NetBSD")) != NULL)
491: mandoc_msg(MANDOCERR_BX, mdoc->parse,
492: ln, pos + (cp - p), "Nx");
493: if ((cp = strstr(p, "FreeBSD")) != NULL)
494: mandoc_msg(MANDOCERR_BX, mdoc->parse,
495: ln, pos + (cp - p), "Fx");
496: if ((cp = strstr(p, "DragonFly")) != NULL)
497: mandoc_msg(MANDOCERR_BX, mdoc->parse,
498: ln, pos + (cp - p), "Dx");
1.251 schwarze 499:
500: cp = p;
501: while ((cp = strstr(cp + 1, "()")) != NULL) {
502: for (cpr = cp - 1; cpr >= p; cpr--)
503: if (*cpr != '_' && !isalnum((unsigned char)*cpr))
504: break;
505: if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) {
506: cpr++;
507: mandoc_vmsg(MANDOCERR_FUNC, mdoc->parse,
508: ln, pos + (cpr - p),
509: "%.*s()", (int)(cp - cpr), cpr);
510: }
511: }
1.245 schwarze 512: }
513:
514: static void
1.278 ! schwarze 515: post_abort(POST_ARGS)
! 516: {
! 517: abort();
! 518: }
! 519:
! 520: static void
1.248 schwarze 521: post_delim(POST_ARGS)
522: {
523: const struct roff_node *nch;
1.263 schwarze 524: const char *lc;
525: enum mdelim delim;
526: enum roff_tok tok;
527:
528: tok = mdoc->last->tok;
529: nch = mdoc->last->last;
530: if (nch == NULL || nch->type != ROFFT_TEXT)
531: return;
532: lc = strchr(nch->string, '\0') - 1;
533: if (lc < nch->string)
534: return;
535: delim = mdoc_isdelim(lc);
536: if (delim == DELIM_NONE || delim == DELIM_OPEN)
537: return;
538: if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh ||
539: tok == MDOC_Ss || tok == MDOC_Fo))
540: return;
541:
542: mandoc_vmsg(MANDOCERR_DELIM, mdoc->parse,
543: nch->line, nch->pos + (lc - nch->string),
544: "%s%s %s", roff_name[tok],
545: nch == mdoc->last->child ? "" : " ...", nch->string);
546: }
547:
548: static void
549: post_delim_nb(POST_ARGS)
550: {
551: const struct roff_node *nch;
1.249 schwarze 552: const char *lc, *cp;
553: int nw;
1.248 schwarze 554: enum mdelim delim;
1.249 schwarze 555: enum roff_tok tok;
1.248 schwarze 556:
1.249 schwarze 557: /*
558: * Find candidates: at least two bytes,
559: * the last one a closing or middle delimiter.
560: */
561:
562: tok = mdoc->last->tok;
1.248 schwarze 563: nch = mdoc->last->last;
564: if (nch == NULL || nch->type != ROFFT_TEXT)
565: return;
566: lc = strchr(nch->string, '\0') - 1;
567: if (lc <= nch->string)
568: return;
569: delim = mdoc_isdelim(lc);
570: if (delim == DELIM_NONE || delim == DELIM_OPEN)
571: return;
1.249 schwarze 572:
573: /*
574: * Reduce false positives by allowing various cases.
575: */
576:
577: /* Escaped delimiters. */
578: if (lc > nch->string + 1 && lc[-2] == '\\' &&
579: (lc[-1] == '&' || lc[-1] == 'e'))
580: return;
581:
582: /* Specific byte sequences. */
583: switch (*lc) {
584: case ')':
585: for (cp = lc; cp >= nch->string; cp--)
586: if (*cp == '(')
587: return;
588: break;
589: case '.':
590: if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.')
591: return;
592: if (lc[-1] == '.')
593: return;
594: break;
595: case ';':
596: if (tok == MDOC_Vt)
597: return;
598: break;
599: case '?':
600: if (lc[-1] == '?')
601: return;
602: break;
603: case ']':
604: for (cp = lc; cp >= nch->string; cp--)
605: if (*cp == '[')
606: return;
607: break;
608: case '|':
609: if (lc == nch->string + 1 && lc[-1] == '|')
610: return;
611: default:
612: break;
613: }
614:
615: /* Exactly two non-alphanumeric bytes. */
616: if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
617: return;
618:
619: /* At least three alphabetic words with a sentence ending. */
620: if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
1.269 schwarze 621: tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) {
1.249 schwarze 622: nw = 0;
623: for (cp = lc - 1; cp >= nch->string; cp--) {
624: if (*cp == ' ') {
625: nw++;
626: if (cp > nch->string && cp[-1] == ',')
627: cp--;
628: } else if (isalpha((unsigned int)*cp)) {
629: if (nw > 1)
630: return;
631: } else
632: break;
633: }
634: }
635:
1.263 schwarze 636: mandoc_vmsg(MANDOCERR_DELIM_NB, mdoc->parse,
1.248 schwarze 637: nch->line, nch->pos + (lc - nch->string),
1.249 schwarze 638: "%s%s %s", roff_name[tok],
1.248 schwarze 639: nch == mdoc->last->child ? "" : " ...", nch->string);
640: }
641:
642: static void
1.215 schwarze 643: post_bl_norm(POST_ARGS)
1.1 kristaps 644: {
1.215 schwarze 645: struct roff_node *n;
1.156 schwarze 646: struct mdoc_argv *argv, *wa;
1.145 schwarze 647: int i;
1.156 schwarze 648: enum mdocargt mdoclt;
1.65 schwarze 649: enum mdoc_list lt;
1.1 kristaps 650:
1.215 schwarze 651: n = mdoc->last->parent;
652: n->norm->Bl.type = LIST__NONE;
1.1 kristaps 653:
1.128 schwarze 654: /*
1.60 schwarze 655: * First figure out which kind of list to use: bind ourselves to
656: * the first mentioned list type and warn about any remaining
657: * ones. If we find no list type, we default to LIST_item.
658: */
1.1 kristaps 659:
1.162 schwarze 660: wa = (n->args == NULL) ? NULL : n->args->argv;
1.156 schwarze 661: mdoclt = MDOC_ARG_MAX;
1.60 schwarze 662: for (i = 0; n->args && i < (int)n->args->argc; i++) {
1.145 schwarze 663: argv = n->args->argv + i;
1.60 schwarze 664: lt = LIST__NONE;
1.145 schwarze 665: switch (argv->arg) {
1.60 schwarze 666: /* Set list types. */
1.128 schwarze 667: case MDOC_Bullet:
1.60 schwarze 668: lt = LIST_bullet;
669: break;
1.128 schwarze 670: case MDOC_Dash:
1.60 schwarze 671: lt = LIST_dash;
672: break;
1.128 schwarze 673: case MDOC_Enum:
1.60 schwarze 674: lt = LIST_enum;
675: break;
1.128 schwarze 676: case MDOC_Hyphen:
1.60 schwarze 677: lt = LIST_hyphen;
678: break;
1.128 schwarze 679: case MDOC_Item:
1.60 schwarze 680: lt = LIST_item;
681: break;
1.128 schwarze 682: case MDOC_Tag:
1.60 schwarze 683: lt = LIST_tag;
684: break;
1.128 schwarze 685: case MDOC_Diag:
1.60 schwarze 686: lt = LIST_diag;
687: break;
1.128 schwarze 688: case MDOC_Hang:
1.60 schwarze 689: lt = LIST_hang;
690: break;
1.128 schwarze 691: case MDOC_Ohang:
1.60 schwarze 692: lt = LIST_ohang;
693: break;
1.128 schwarze 694: case MDOC_Inset:
1.60 schwarze 695: lt = LIST_inset;
696: break;
1.128 schwarze 697: case MDOC_Column:
1.60 schwarze 698: lt = LIST_column;
699: break;
700: /* Set list arguments. */
1.128 schwarze 701: case MDOC_Compact:
1.145 schwarze 702: if (n->norm->Bl.comp)
703: mandoc_msg(MANDOCERR_ARG_REP,
704: mdoc->parse, argv->line,
705: argv->pos, "Bl -compact");
706: n->norm->Bl.comp = 1;
1.60 schwarze 707: break;
1.128 schwarze 708: case MDOC_Width:
1.156 schwarze 709: wa = argv;
1.145 schwarze 710: if (0 == argv->sz) {
711: mandoc_msg(MANDOCERR_ARG_EMPTY,
712: mdoc->parse, argv->line,
713: argv->pos, "Bl -width");
714: n->norm->Bl.width = "0n";
1.101 schwarze 715: break;
716: }
1.145 schwarze 717: if (NULL != n->norm->Bl.width)
718: mandoc_vmsg(MANDOCERR_ARG_REP,
719: mdoc->parse, argv->line,
720: argv->pos, "Bl -width %s",
721: argv->value[0]);
1.238 schwarze 722: rewrite_macro2len(mdoc, argv->value);
1.145 schwarze 723: n->norm->Bl.width = argv->value[0];
1.5 schwarze 724: break;
1.128 schwarze 725: case MDOC_Offset:
1.145 schwarze 726: if (0 == argv->sz) {
727: mandoc_msg(MANDOCERR_ARG_EMPTY,
728: mdoc->parse, argv->line,
729: argv->pos, "Bl -offset");
1.61 schwarze 730: break;
731: }
1.145 schwarze 732: if (NULL != n->norm->Bl.offs)
733: mandoc_vmsg(MANDOCERR_ARG_REP,
734: mdoc->parse, argv->line,
735: argv->pos, "Bl -offset %s",
736: argv->value[0]);
1.238 schwarze 737: rewrite_macro2len(mdoc, argv->value);
1.145 schwarze 738: n->norm->Bl.offs = argv->value[0];
1.1 kristaps 739: break;
1.66 schwarze 740: default:
741: continue;
1.1 kristaps 742: }
1.146 schwarze 743: if (LIST__NONE == lt)
744: continue;
1.156 schwarze 745: mdoclt = argv->arg;
1.1 kristaps 746:
1.60 schwarze 747: /* Check: multiple list types. */
748:
1.146 schwarze 749: if (LIST__NONE != n->norm->Bl.type) {
1.157 schwarze 750: mandoc_vmsg(MANDOCERR_BL_REP,
1.146 schwarze 751: mdoc->parse, n->line, n->pos,
1.157 schwarze 752: "Bl -%s", mdoc_argnames[argv->arg]);
1.146 schwarze 753: continue;
1.65 schwarze 754: }
1.60 schwarze 755:
756: /* The list type should come first. */
757:
1.146 schwarze 758: if (n->norm->Bl.width ||
759: n->norm->Bl.offs ||
760: n->norm->Bl.comp)
1.157 schwarze 761: mandoc_vmsg(MANDOCERR_BL_LATETYPE,
762: mdoc->parse, n->line, n->pos, "Bl -%s",
1.146 schwarze 763: mdoc_argnames[n->args->argv[0].arg]);
764:
765: n->norm->Bl.type = lt;
766: if (LIST_column == lt) {
767: n->norm->Bl.ncols = argv->sz;
768: n->norm->Bl.cols = (void *)argv->value;
769: }
1.60 schwarze 770: }
771:
772: /* Allow lists to default to LIST_item. */
773:
1.82 schwarze 774: if (LIST__NONE == n->norm->Bl.type) {
1.157 schwarze 775: mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
776: n->line, n->pos, "Bl");
1.82 schwarze 777: n->norm->Bl.type = LIST_item;
1.219 schwarze 778: mdoclt = MDOC_Item;
1.55 schwarze 779: }
1.1 kristaps 780:
1.128 schwarze 781: /*
1.5 schwarze 782: * Validate the width field. Some list types don't need width
783: * types and should be warned about them. Others should have it
1.103 schwarze 784: * and must also be warned. Yet others have a default and need
785: * no warning.
1.5 schwarze 786: */
787:
1.82 schwarze 788: switch (n->norm->Bl.type) {
1.128 schwarze 789: case LIST_tag:
1.264 schwarze 790: if (n->norm->Bl.width == NULL)
1.157 schwarze 791: mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
792: n->line, n->pos, "Bl -tag");
1.76 schwarze 793: break;
1.128 schwarze 794: case LIST_column:
795: case LIST_diag:
796: case LIST_ohang:
797: case LIST_inset:
798: case LIST_item:
1.264 schwarze 799: if (n->norm->Bl.width != NULL)
1.156 schwarze 800: mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
801: wa->line, wa->pos, "Bl -%s",
802: mdoc_argnames[mdoclt]);
1.264 schwarze 803: n->norm->Bl.width = NULL;
1.103 schwarze 804: break;
1.128 schwarze 805: case LIST_bullet:
806: case LIST_dash:
807: case LIST_hyphen:
1.264 schwarze 808: if (n->norm->Bl.width == NULL)
1.103 schwarze 809: n->norm->Bl.width = "2n";
810: break;
1.128 schwarze 811: case LIST_enum:
1.264 schwarze 812: if (n->norm->Bl.width == NULL)
1.103 schwarze 813: n->norm->Bl.width = "3n";
1.75 schwarze 814: break;
1.5 schwarze 815: default:
816: break;
817: }
1.1 kristaps 818: }
819:
1.175 schwarze 820: static void
1.215 schwarze 821: post_bd(POST_ARGS)
1.1 kristaps 822: {
1.215 schwarze 823: struct roff_node *n;
1.145 schwarze 824: struct mdoc_argv *argv;
825: int i;
1.128 schwarze 826: enum mdoc_disp dt;
1.1 kristaps 827:
1.215 schwarze 828: n = mdoc->last;
1.61 schwarze 829: for (i = 0; n->args && i < (int)n->args->argc; i++) {
1.145 schwarze 830: argv = n->args->argv + i;
1.61 schwarze 831: dt = DISP__NONE;
832:
1.145 schwarze 833: switch (argv->arg) {
1.128 schwarze 834: case MDOC_Centred:
1.149 schwarze 835: dt = DISP_centered;
1.61 schwarze 836: break;
1.128 schwarze 837: case MDOC_Ragged:
1.61 schwarze 838: dt = DISP_ragged;
839: break;
1.128 schwarze 840: case MDOC_Unfilled:
1.61 schwarze 841: dt = DISP_unfilled;
842: break;
1.128 schwarze 843: case MDOC_Filled:
1.61 schwarze 844: dt = DISP_filled;
845: break;
1.128 schwarze 846: case MDOC_Literal:
1.61 schwarze 847: dt = DISP_literal;
848: break;
1.128 schwarze 849: case MDOC_File:
1.157 schwarze 850: mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
851: n->line, n->pos, NULL);
1.174 schwarze 852: break;
1.128 schwarze 853: case MDOC_Offset:
1.145 schwarze 854: if (0 == argv->sz) {
855: mandoc_msg(MANDOCERR_ARG_EMPTY,
856: mdoc->parse, argv->line,
857: argv->pos, "Bd -offset");
1.1 kristaps 858: break;
1.61 schwarze 859: }
1.145 schwarze 860: if (NULL != n->norm->Bd.offs)
861: mandoc_vmsg(MANDOCERR_ARG_REP,
862: mdoc->parse, argv->line,
863: argv->pos, "Bd -offset %s",
864: argv->value[0]);
1.238 schwarze 865: rewrite_macro2len(mdoc, argv->value);
1.145 schwarze 866: n->norm->Bd.offs = argv->value[0];
1.55 schwarze 867: break;
1.128 schwarze 868: case MDOC_Compact:
1.145 schwarze 869: if (n->norm->Bd.comp)
870: mandoc_msg(MANDOCERR_ARG_REP,
871: mdoc->parse, argv->line,
872: argv->pos, "Bd -compact");
873: n->norm->Bd.comp = 1;
1.61 schwarze 874: break;
1.1 kristaps 875: default:
1.61 schwarze 876: abort();
1.1 kristaps 877: }
1.146 schwarze 878: if (DISP__NONE == dt)
879: continue;
1.61 schwarze 880:
1.146 schwarze 881: if (DISP__NONE == n->norm->Bd.type)
1.82 schwarze 882: n->norm->Bd.type = dt;
1.146 schwarze 883: else
1.157 schwarze 884: mandoc_vmsg(MANDOCERR_BD_REP,
1.146 schwarze 885: mdoc->parse, n->line, n->pos,
1.157 schwarze 886: "Bd -%s", mdoc_argnames[argv->arg]);
1.61 schwarze 887: }
888:
1.82 schwarze 889: if (DISP__NONE == n->norm->Bd.type) {
1.157 schwarze 890: mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
891: n->line, n->pos, "Bd");
1.82 schwarze 892: n->norm->Bd.type = DISP_ragged;
1.61 schwarze 893: }
1.1 kristaps 894: }
895:
1.233 schwarze 896: /*
897: * Stand-alone line macros.
898: */
899:
1.175 schwarze 900: static void
1.215 schwarze 901: post_an_norm(POST_ARGS)
1.1 kristaps 902: {
1.215 schwarze 903: struct roff_node *n;
1.156 schwarze 904: struct mdoc_argv *argv;
905: size_t i;
1.1 kristaps 906:
1.215 schwarze 907: n = mdoc->last;
1.156 schwarze 908: if (n->args == NULL)
1.175 schwarze 909: return;
1.128 schwarze 910:
1.156 schwarze 911: for (i = 1; i < n->args->argc; i++) {
912: argv = n->args->argv + i;
913: mandoc_vmsg(MANDOCERR_AN_REP,
914: mdoc->parse, argv->line, argv->pos,
915: "An -%s", mdoc_argnames[argv->arg]);
916: }
1.72 schwarze 917:
1.156 schwarze 918: argv = n->args->argv;
919: if (argv->arg == MDOC_Split)
1.82 schwarze 920: n->norm->An.auth = AUTH_split;
1.156 schwarze 921: else if (argv->arg == MDOC_Nosplit)
1.82 schwarze 922: n->norm->An.auth = AUTH_nosplit;
1.65 schwarze 923: else
924: abort();
1.1 kristaps 925: }
926:
1.175 schwarze 927: static void
1.233 schwarze 928: post_eoln(POST_ARGS)
929: {
930: struct roff_node *n;
931:
1.244 schwarze 932: post_useless(mdoc);
1.233 schwarze 933: n = mdoc->last;
934: if (n->child != NULL)
1.236 schwarze 935: mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, n->line,
936: n->pos, "%s %s", roff_name[n->tok], n->child->string);
1.233 schwarze 937:
938: while (n->child != NULL)
939: roff_node_delete(mdoc, n->child);
940:
941: roff_word_alloc(mdoc, n->line, n->pos, n->tok == MDOC_Bt ?
942: "is currently in beta test." : "currently under development.");
943: mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
944: mdoc->last = n;
945: }
946:
947: static int
948: build_list(struct roff_man *mdoc, int tok)
949: {
950: struct roff_node *n;
951: int ic;
952:
953: n = mdoc->last->next;
954: for (ic = 1;; ic++) {
955: roff_elem_alloc(mdoc, n->line, n->pos, tok);
956: mdoc->last->flags |= NODE_NOSRC;
957: mdoc_node_relink(mdoc, n);
958: n = mdoc->last = mdoc->last->parent;
959: mdoc->next = ROFF_NEXT_SIBLING;
960: if (n->next == NULL)
961: return ic;
962: if (ic > 1 || n->next->next != NULL) {
963: roff_word_alloc(mdoc, n->line, n->pos, ",");
964: mdoc->last->flags |= NODE_DELIMC | NODE_NOSRC;
965: }
966: n = mdoc->last->next;
967: if (n->next == NULL) {
968: roff_word_alloc(mdoc, n->line, n->pos, "and");
969: mdoc->last->flags |= NODE_NOSRC;
970: }
971: }
972: }
973:
974: static void
975: post_ex(POST_ARGS)
976: {
977: struct roff_node *n;
978: int ic;
979:
980: post_std(mdoc);
981:
982: n = mdoc->last;
983: mdoc->next = ROFF_NEXT_CHILD;
984: roff_word_alloc(mdoc, n->line, n->pos, "The");
985: mdoc->last->flags |= NODE_NOSRC;
986:
987: if (mdoc->last->next != NULL)
988: ic = build_list(mdoc, MDOC_Nm);
989: else if (mdoc->meta.name != NULL) {
990: roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Nm);
991: mdoc->last->flags |= NODE_NOSRC;
992: roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
993: mdoc->last->flags |= NODE_NOSRC;
994: mdoc->last = mdoc->last->parent;
995: mdoc->next = ROFF_NEXT_SIBLING;
996: ic = 1;
997: } else {
998: mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
999: n->line, n->pos, "Ex");
1000: ic = 0;
1001: }
1002:
1003: roff_word_alloc(mdoc, n->line, n->pos,
1004: ic > 1 ? "utilities exit\\~0" : "utility exits\\~0");
1005: mdoc->last->flags |= NODE_NOSRC;
1006: roff_word_alloc(mdoc, n->line, n->pos,
1007: "on success, and\\~>0 if an error occurs.");
1008: mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
1009: mdoc->last = n;
1010: }
1011:
1012: static void
1013: post_lb(POST_ARGS)
1014: {
1015: struct roff_node *n;
1016:
1.263 schwarze 1017: post_delim_nb(mdoc);
1.248 schwarze 1018:
1.233 schwarze 1019: n = mdoc->last;
1020: assert(n->child->type == ROFFT_TEXT);
1021: mdoc->next = ROFF_NEXT_CHILD;
1022: roff_word_alloc(mdoc, n->line, n->pos, "library");
1023: mdoc->last->flags = NODE_NOSRC;
1.272 schwarze 1024: roff_word_alloc(mdoc, n->line, n->pos, "\\(lq");
1.233 schwarze 1025: mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
1026: mdoc->last = mdoc->last->next;
1.272 schwarze 1027: roff_word_alloc(mdoc, n->line, n->pos, "\\(rq");
1.233 schwarze 1028: mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
1029: mdoc->last = n;
1030: }
1031:
1032: static void
1033: post_rv(POST_ARGS)
1034: {
1035: struct roff_node *n;
1036: int ic;
1037:
1038: post_std(mdoc);
1039:
1040: n = mdoc->last;
1041: mdoc->next = ROFF_NEXT_CHILD;
1042: if (n->child != NULL) {
1043: roff_word_alloc(mdoc, n->line, n->pos, "The");
1044: mdoc->last->flags |= NODE_NOSRC;
1045: ic = build_list(mdoc, MDOC_Fn);
1046: roff_word_alloc(mdoc, n->line, n->pos,
1047: ic > 1 ? "functions return" : "function returns");
1048: mdoc->last->flags |= NODE_NOSRC;
1049: roff_word_alloc(mdoc, n->line, n->pos,
1050: "the value\\~0 if successful;");
1051: } else
1052: roff_word_alloc(mdoc, n->line, n->pos, "Upon successful "
1053: "completion, the value\\~0 is returned;");
1054: mdoc->last->flags |= NODE_NOSRC;
1055:
1056: roff_word_alloc(mdoc, n->line, n->pos, "otherwise "
1057: "the value\\~\\-1 is returned and the global variable");
1058: mdoc->last->flags |= NODE_NOSRC;
1059: roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Va);
1060: mdoc->last->flags |= NODE_NOSRC;
1061: roff_word_alloc(mdoc, n->line, n->pos, "errno");
1062: mdoc->last->flags |= NODE_NOSRC;
1063: mdoc->last = mdoc->last->parent;
1064: mdoc->next = ROFF_NEXT_SIBLING;
1065: roff_word_alloc(mdoc, n->line, n->pos,
1066: "is set to indicate the error.");
1067: mdoc->last->flags |= NODE_EOS | NODE_NOSRC;
1068: mdoc->last = n;
1069: }
1070:
1071: static void
1.215 schwarze 1072: post_std(POST_ARGS)
1.60 schwarze 1073: {
1.215 schwarze 1074: struct roff_node *n;
1.60 schwarze 1075:
1.263 schwarze 1076: post_delim(mdoc);
1077:
1.215 schwarze 1078: n = mdoc->last;
1079: if (n->args && n->args->argc == 1)
1080: if (n->args->argv[0].arg == MDOC_Std)
1.175 schwarze 1081: return;
1.60 schwarze 1082:
1.143 schwarze 1083: mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
1.236 schwarze 1084: n->line, n->pos, roff_name[n->tok]);
1.60 schwarze 1085: }
1086:
1.175 schwarze 1087: static void
1.233 schwarze 1088: post_st(POST_ARGS)
1089: {
1090: struct roff_node *n, *nch;
1091: const char *p;
1092:
1093: n = mdoc->last;
1094: nch = n->child;
1095: assert(nch->type == ROFFT_TEXT);
1096:
1097: if ((p = mdoc_a2st(nch->string)) == NULL) {
1098: mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
1099: nch->line, nch->pos, "St %s", nch->string);
1100: roff_node_delete(mdoc, n);
1101: return;
1102: }
1103:
1104: nch->flags |= NODE_NOPRT;
1105: mdoc->next = ROFF_NEXT_CHILD;
1106: roff_word_alloc(mdoc, nch->line, nch->pos, p);
1107: mdoc->last->flags |= NODE_NOSRC;
1108: mdoc->last= n;
1109: }
1110:
1111: static void
1.215 schwarze 1112: post_obsolete(POST_ARGS)
1.136 schwarze 1113: {
1.215 schwarze 1114: struct roff_node *n;
1.136 schwarze 1115:
1.215 schwarze 1116: n = mdoc->last;
1.200 schwarze 1117: if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK)
1.136 schwarze 1118: mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
1.236 schwarze 1119: n->line, n->pos, roff_name[n->tok]);
1.244 schwarze 1120: }
1121:
1122: static void
1123: post_useless(POST_ARGS)
1124: {
1125: struct roff_node *n;
1126:
1127: n = mdoc->last;
1128: mandoc_msg(MANDOCERR_MACRO_USELESS, mdoc->parse,
1129: n->line, n->pos, roff_name[n->tok]);
1.136 schwarze 1130: }
1131:
1.233 schwarze 1132: /*
1133: * Block macros.
1134: */
1135:
1.175 schwarze 1136: static void
1.1 kristaps 1137: post_bf(POST_ARGS)
1138: {
1.201 schwarze 1139: struct roff_node *np, *nch;
1.65 schwarze 1140:
1141: /*
1142: * Unlike other data pointers, these are "housed" by the HEAD
1143: * element, which contains the goods.
1144: */
1.1 kristaps 1145:
1.194 schwarze 1146: np = mdoc->last;
1.200 schwarze 1147: if (np->type != ROFFT_HEAD)
1.175 schwarze 1148: return;
1.65 schwarze 1149:
1.200 schwarze 1150: assert(np->parent->type == ROFFT_BLOCK);
1.213 schwarze 1151: assert(np->parent->tok == MDOC_Bf);
1.1 kristaps 1152:
1.144 schwarze 1153: /* Check the number of arguments. */
1.1 kristaps 1154:
1.144 schwarze 1155: nch = np->child;
1.213 schwarze 1156: if (np->parent->args == NULL) {
1157: if (nch == NULL) {
1.157 schwarze 1158: mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
1159: np->line, np->pos, "Bf");
1.175 schwarze 1160: return;
1.144 schwarze 1161: }
1162: nch = nch->next;
1.76 schwarze 1163: }
1.213 schwarze 1164: if (nch != NULL)
1.144 schwarze 1165: mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1166: nch->line, nch->pos, "Bf ... %s", nch->string);
1.65 schwarze 1167:
1168: /* Extract argument into data. */
1.128 schwarze 1169:
1.213 schwarze 1170: if (np->parent->args != NULL) {
1171: switch (np->parent->args->argv[0].arg) {
1172: case MDOC_Emphasis:
1.82 schwarze 1173: np->norm->Bf.font = FONT_Em;
1.213 schwarze 1174: break;
1175: case MDOC_Literal:
1.82 schwarze 1176: np->norm->Bf.font = FONT_Li;
1.213 schwarze 1177: break;
1178: case MDOC_Symbolic:
1.82 schwarze 1179: np->norm->Bf.font = FONT_Sy;
1.213 schwarze 1180: break;
1181: default:
1.65 schwarze 1182: abort();
1.213 schwarze 1183: }
1.175 schwarze 1184: return;
1.55 schwarze 1185: }
1.1 kristaps 1186:
1.65 schwarze 1187: /* Extract parameter into data. */
1.1 kristaps 1188:
1.213 schwarze 1189: if ( ! strcmp(np->child->string, "Em"))
1.82 schwarze 1190: np->norm->Bf.font = FONT_Em;
1.213 schwarze 1191: else if ( ! strcmp(np->child->string, "Li"))
1.82 schwarze 1192: np->norm->Bf.font = FONT_Li;
1.213 schwarze 1193: else if ( ! strcmp(np->child->string, "Sy"))
1.82 schwarze 1194: np->norm->Bf.font = FONT_Sy;
1.128 schwarze 1195: else
1.144 schwarze 1196: mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
1197: np->child->line, np->child->pos,
1198: "Bf %s", np->child->string);
1.27 schwarze 1199: }
1200:
1.175 schwarze 1201: static void
1.167 schwarze 1202: post_fname(POST_ARGS)
1203: {
1.201 schwarze 1204: const struct roff_node *n;
1.169 schwarze 1205: const char *cp;
1206: size_t pos;
1.167 schwarze 1207:
1208: n = mdoc->last->child;
1209: pos = strcspn(n->string, "()");
1.169 schwarze 1210: cp = n->string + pos;
1211: if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
1.167 schwarze 1212: mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
1213: n->line, n->pos + pos, n->string);
1214: }
1215:
1.175 schwarze 1216: static void
1.167 schwarze 1217: post_fn(POST_ARGS)
1218: {
1219:
1220: post_fname(mdoc);
1221: post_fa(mdoc);
1222: }
1223:
1.175 schwarze 1224: static void
1.160 schwarze 1225: post_fo(POST_ARGS)
1226: {
1.201 schwarze 1227: const struct roff_node *n;
1.190 schwarze 1228:
1229: n = mdoc->last;
1.160 schwarze 1230:
1.200 schwarze 1231: if (n->type != ROFFT_HEAD)
1.190 schwarze 1232: return;
1233:
1234: if (n->child == NULL) {
1235: mandoc_msg(MANDOCERR_FO_NOHEAD, mdoc->parse,
1236: n->line, n->pos, "Fo");
1237: return;
1238: }
1239: if (n->child != n->last) {
1240: mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1241: n->child->next->line, n->child->next->pos,
1242: "Fo ... %s", n->child->next->string);
1243: while (n->child != n->last)
1.203 schwarze 1244: roff_node_delete(mdoc, n->last);
1.263 schwarze 1245: } else
1246: post_delim(mdoc);
1.190 schwarze 1247:
1248: post_fname(mdoc);
1.166 schwarze 1249: }
1250:
1.175 schwarze 1251: static void
1.166 schwarze 1252: post_fa(POST_ARGS)
1253: {
1.201 schwarze 1254: const struct roff_node *n;
1.166 schwarze 1255: const char *cp;
1256:
1257: for (n = mdoc->last->child; n != NULL; n = n->next) {
1258: for (cp = n->string; *cp != '\0'; cp++) {
1259: /* Ignore callbacks and alterations. */
1260: if (*cp == '(' || *cp == '{')
1261: break;
1262: if (*cp != ',')
1263: continue;
1264: mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
1265: n->line, n->pos + (cp - n->string),
1266: n->string);
1267: break;
1268: }
1269: }
1.263 schwarze 1270: post_delim_nb(mdoc);
1.1 kristaps 1271: }
1272:
1.175 schwarze 1273: static void
1.1 kristaps 1274: post_nm(POST_ARGS)
1275: {
1.201 schwarze 1276: struct roff_node *n;
1.182 schwarze 1277:
1278: n = mdoc->last;
1279:
1.267 schwarze 1280: if (n->sec == SEC_NAME && n->child != NULL &&
1281: n->child->type == ROFFT_TEXT && mdoc->meta.msec != NULL)
1.262 schwarze 1282: mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1);
1283:
1.278 ! schwarze 1284: if (n->last != NULL && n->last->tok == MDOC_Pp)
1.182 schwarze 1285: mdoc_node_relink(mdoc, n->last);
1.1 kristaps 1286:
1.227 schwarze 1287: if (mdoc->meta.name == NULL)
1288: deroff(&mdoc->meta.name, n);
1.76 schwarze 1289:
1.227 schwarze 1290: if (mdoc->meta.name == NULL ||
1291: (mdoc->lastsec == SEC_NAME && n->child == NULL))
1.157 schwarze 1292: mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
1.182 schwarze 1293: n->line, n->pos, "Nm");
1.234 schwarze 1294:
1.263 schwarze 1295: switch (n->type) {
1296: case ROFFT_ELEM:
1297: post_delim_nb(mdoc);
1298: break;
1299: case ROFFT_HEAD:
1.248 schwarze 1300: post_delim(mdoc);
1.263 schwarze 1301: break;
1302: default:
1303: return;
1304: }
1.248 schwarze 1305:
1.263 schwarze 1306: if ((n->child != NULL && n->child->type == ROFFT_TEXT) ||
1.234 schwarze 1307: mdoc->meta.name == NULL)
1308: return;
1309:
1310: mdoc->next = ROFF_NEXT_CHILD;
1311: roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
1312: mdoc->last->flags |= NODE_NOSRC;
1313: mdoc->last = n;
1.76 schwarze 1314: }
1315:
1.175 schwarze 1316: static void
1.160 schwarze 1317: post_nd(POST_ARGS)
1318: {
1.201 schwarze 1319: struct roff_node *n;
1.183 schwarze 1320:
1321: n = mdoc->last;
1322:
1.200 schwarze 1323: if (n->type != ROFFT_BODY)
1.183 schwarze 1324: return;
1.235 schwarze 1325:
1326: if (n->sec != SEC_NAME)
1327: mandoc_msg(MANDOCERR_ND_LATE, mdoc->parse,
1328: n->line, n->pos, "Nd");
1.183 schwarze 1329:
1330: if (n->child == NULL)
1331: mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse,
1332: n->line, n->pos, "Nd");
1.263 schwarze 1333: else
1334: post_delim(mdoc);
1.160 schwarze 1335:
1.175 schwarze 1336: post_hyph(mdoc);
1.160 schwarze 1337: }
1338:
1.175 schwarze 1339: static void
1.215 schwarze 1340: post_display(POST_ARGS)
1.76 schwarze 1341: {
1.215 schwarze 1342: struct roff_node *n, *np;
1.128 schwarze 1343:
1.189 schwarze 1344: n = mdoc->last;
1.215 schwarze 1345: switch (n->type) {
1346: case ROFFT_BODY:
1.222 schwarze 1347: if (n->end != ENDBODY_NOT) {
1.223 schwarze 1348: if (n->tok == MDOC_Bd &&
1349: n->body->parent->args == NULL)
1.222 schwarze 1350: roff_node_delete(mdoc, n);
1351: } else if (n->child == NULL)
1.215 schwarze 1352: mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1.236 schwarze 1353: n->line, n->pos, roff_name[n->tok]);
1.215 schwarze 1354: else if (n->tok == MDOC_D1)
1355: post_hyph(mdoc);
1356: break;
1357: case ROFFT_BLOCK:
1358: if (n->tok == MDOC_Bd) {
1.216 schwarze 1359: if (n->args == NULL) {
1360: mandoc_msg(MANDOCERR_BD_NOARG,
1361: mdoc->parse, n->line, n->pos, "Bd");
1362: mdoc->next = ROFF_NEXT_SIBLING;
1363: while (n->body->child != NULL)
1364: mdoc_node_relink(mdoc,
1365: n->body->child);
1366: roff_node_delete(mdoc, n);
1367: break;
1368: }
1.215 schwarze 1369: post_bd(mdoc);
1370: post_prevpar(mdoc);
1371: }
1372: for (np = n->parent; np != NULL; np = np->parent) {
1373: if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) {
1374: mandoc_vmsg(MANDOCERR_BD_NEST,
1375: mdoc->parse, n->line, n->pos,
1.236 schwarze 1376: "%s in Bd", roff_name[n->tok]);
1.215 schwarze 1377: break;
1378: }
1379: }
1380: break;
1381: default:
1382: break;
1383: }
1.1 kristaps 1384: }
1385:
1.175 schwarze 1386: static void
1.76 schwarze 1387: post_defaults(POST_ARGS)
1388: {
1.201 schwarze 1389: struct roff_node *nn;
1.76 schwarze 1390:
1.248 schwarze 1391: if (mdoc->last->child != NULL) {
1.263 schwarze 1392: post_delim_nb(mdoc);
1.248 schwarze 1393: return;
1394: }
1395:
1.76 schwarze 1396: /*
1397: * The `Ar' defaults to "file ..." if no value is provided as an
1398: * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
1399: * gets an empty string.
1400: */
1401:
1402: nn = mdoc->last;
1403: switch (nn->tok) {
1.128 schwarze 1404: case MDOC_Ar:
1.214 schwarze 1405: mdoc->next = ROFF_NEXT_CHILD;
1.205 schwarze 1406: roff_word_alloc(mdoc, nn->line, nn->pos, "file");
1.229 schwarze 1407: mdoc->last->flags |= NODE_NOSRC;
1.205 schwarze 1408: roff_word_alloc(mdoc, nn->line, nn->pos, "...");
1.229 schwarze 1409: mdoc->last->flags |= NODE_NOSRC;
1.76 schwarze 1410: break;
1.128 schwarze 1411: case MDOC_Pa:
1412: case MDOC_Mt:
1.214 schwarze 1413: mdoc->next = ROFF_NEXT_CHILD;
1.205 schwarze 1414: roff_word_alloc(mdoc, nn->line, nn->pos, "~");
1.229 schwarze 1415: mdoc->last->flags |= NODE_NOSRC;
1.76 schwarze 1416: break;
1417: default:
1418: abort();
1.128 schwarze 1419: }
1.76 schwarze 1420: mdoc->last = nn;
1421: }
1.1 kristaps 1422:
1.175 schwarze 1423: static void
1.1 kristaps 1424: post_at(POST_ARGS)
1425: {
1.232 schwarze 1426: struct roff_node *n, *nch;
1427: const char *att;
1.1 kristaps 1428:
1.160 schwarze 1429: n = mdoc->last;
1.232 schwarze 1430: nch = n->child;
1.160 schwarze 1431:
1.76 schwarze 1432: /*
1433: * If we have a child, look it up in the standard keys. If a
1434: * key exist, use that instead of the child; if it doesn't,
1435: * prefix "AT&T UNIX " to the existing data.
1436: */
1.128 schwarze 1437:
1.232 schwarze 1438: att = NULL;
1439: if (nch != NULL && ((att = mdoc_a2att(nch->string)) == NULL))
1.157 schwarze 1440: mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
1.232 schwarze 1441: nch->line, nch->pos, "At %s", nch->string);
1442:
1443: mdoc->next = ROFF_NEXT_CHILD;
1444: if (att != NULL) {
1445: roff_word_alloc(mdoc, nch->line, nch->pos, att);
1446: nch->flags |= NODE_NOPRT;
1.131 schwarze 1447: } else
1.232 schwarze 1448: roff_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
1449: mdoc->last->flags |= NODE_NOSRC;
1450: mdoc->last = n;
1.1 kristaps 1451: }
1452:
1.175 schwarze 1453: static void
1.1 kristaps 1454: post_an(POST_ARGS)
1455: {
1.201 schwarze 1456: struct roff_node *np, *nch;
1.1 kristaps 1457:
1.215 schwarze 1458: post_an_norm(mdoc);
1459:
1.65 schwarze 1460: np = mdoc->last;
1.187 schwarze 1461: nch = np->child;
1462: if (np->norm->An.auth == AUTH__NONE) {
1463: if (nch == NULL)
1464: mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1465: np->line, np->pos, "An");
1.248 schwarze 1466: else
1.263 schwarze 1467: post_delim_nb(mdoc);
1.187 schwarze 1468: } else if (nch != NULL)
1.185 schwarze 1469: mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1470: nch->line, nch->pos, "An ... %s", nch->string);
1.136 schwarze 1471: }
1472:
1.175 schwarze 1473: static void
1.136 schwarze 1474: post_en(POST_ARGS)
1475: {
1476:
1.215 schwarze 1477: post_obsolete(mdoc);
1.200 schwarze 1478: if (mdoc->last->type == ROFFT_BLOCK)
1.136 schwarze 1479: mdoc->last->norm->Es = mdoc->last_es;
1480: }
1481:
1.175 schwarze 1482: static void
1.136 schwarze 1483: post_es(POST_ARGS)
1484: {
1485:
1.215 schwarze 1486: post_obsolete(mdoc);
1.136 schwarze 1487: mdoc->last_es = mdoc->last;
1.231 schwarze 1488: }
1489:
1490: static void
1491: post_xx(POST_ARGS)
1492: {
1493: struct roff_node *n;
1494: const char *os;
1.265 schwarze 1495: char *v;
1.231 schwarze 1496:
1.263 schwarze 1497: post_delim_nb(mdoc);
1.248 schwarze 1498:
1.231 schwarze 1499: n = mdoc->last;
1500: switch (n->tok) {
1501: case MDOC_Bsx:
1502: os = "BSD/OS";
1503: break;
1504: case MDOC_Dx:
1505: os = "DragonFly";
1506: break;
1507: case MDOC_Fx:
1508: os = "FreeBSD";
1509: break;
1510: case MDOC_Nx:
1511: os = "NetBSD";
1.265 schwarze 1512: if (n->child == NULL)
1513: break;
1514: v = n->child->string;
1515: if ((v[0] != '0' && v[0] != '1') || v[1] != '.' ||
1516: v[2] < '0' || v[2] > '9' ||
1517: v[3] < 'a' || v[3] > 'z' || v[4] != '\0')
1518: break;
1519: n->child->flags |= NODE_NOPRT;
1520: mdoc->next = ROFF_NEXT_CHILD;
1521: roff_word_alloc(mdoc, n->child->line, n->child->pos, v);
1522: v = mdoc->last->string;
1523: v[3] = toupper((unsigned char)v[3]);
1524: mdoc->last->flags |= NODE_NOSRC;
1525: mdoc->last = n;
1.231 schwarze 1526: break;
1527: case MDOC_Ox:
1528: os = "OpenBSD";
1529: break;
1530: case MDOC_Ux:
1531: os = "UNIX";
1532: break;
1533: default:
1534: abort();
1535: }
1536: mdoc->next = ROFF_NEXT_CHILD;
1537: roff_word_alloc(mdoc, n->line, n->pos, os);
1538: mdoc->last->flags |= NODE_NOSRC;
1539: mdoc->last = n;
1.1 kristaps 1540: }
1541:
1.175 schwarze 1542: static void
1.1 kristaps 1543: post_it(POST_ARGS)
1544: {
1.201 schwarze 1545: struct roff_node *nbl, *nit, *nch;
1.91 schwarze 1546: int i, cols;
1.60 schwarze 1547: enum mdoc_list lt;
1.1 kristaps 1548:
1.215 schwarze 1549: post_prevpar(mdoc);
1550:
1.142 schwarze 1551: nit = mdoc->last;
1.200 schwarze 1552: if (nit->type != ROFFT_BLOCK)
1.175 schwarze 1553: return;
1.1 kristaps 1554:
1.142 schwarze 1555: nbl = nit->parent->parent;
1556: lt = nbl->norm->Bl.type;
1.60 schwarze 1557:
1558: switch (lt) {
1.128 schwarze 1559: case LIST_tag:
1560: case LIST_hang:
1561: case LIST_ohang:
1562: case LIST_inset:
1563: case LIST_diag:
1.180 schwarze 1564: if (nit->head->child == NULL)
1.157 schwarze 1565: mandoc_vmsg(MANDOCERR_IT_NOHEAD,
1.142 schwarze 1566: mdoc->parse, nit->line, nit->pos,
1.157 schwarze 1567: "Bl -%s It",
1.142 schwarze 1568: mdoc_argnames[nbl->args->argv[0].arg]);
1.1 kristaps 1569: break;
1.128 schwarze 1570: case LIST_bullet:
1571: case LIST_dash:
1572: case LIST_enum:
1573: case LIST_hyphen:
1.180 schwarze 1574: if (nit->body == NULL || nit->body->child == NULL)
1.157 schwarze 1575: mandoc_vmsg(MANDOCERR_IT_NOBODY,
1.143 schwarze 1576: mdoc->parse, nit->line, nit->pos,
1.157 schwarze 1577: "Bl -%s It",
1.143 schwarze 1578: mdoc_argnames[nbl->args->argv[0].arg]);
1.1 kristaps 1579: /* FALLTHROUGH */
1.128 schwarze 1580: case LIST_item:
1.220 schwarze 1581: if ((nch = nit->head->child) != NULL)
1.236 schwarze 1582: mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse,
1583: nit->line, nit->pos, "It %s",
1584: nch->string == NULL ? roff_name[nch->tok] :
1585: nch->string);
1.1 kristaps 1586: break;
1.128 schwarze 1587: case LIST_column:
1.142 schwarze 1588: cols = (int)nbl->norm->Bl.ncols;
1.60 schwarze 1589:
1.180 schwarze 1590: assert(nit->head->child == NULL);
1.60 schwarze 1591:
1.260 schwarze 1592: if (nit->head->next->child == NULL &&
1593: nit->head->next->next == NULL) {
1594: mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
1595: nit->line, nit->pos, "It");
1596: roff_node_delete(mdoc, nit);
1597: break;
1598: }
1599:
1.213 schwarze 1600: i = 0;
1.260 schwarze 1601: for (nch = nit->child; nch != NULL; nch = nch->next) {
1602: if (nch->type != ROFFT_BODY)
1603: continue;
1604: if (i++ && nch->flags & NODE_LINE)
1605: mandoc_msg(MANDOCERR_TA_LINE, mdoc->parse,
1606: nch->line, nch->pos, "Ta");
1607: }
1.154 schwarze 1608: if (i < cols || i > cols + 1)
1.192 schwarze 1609: mandoc_vmsg(MANDOCERR_BL_COL,
1.154 schwarze 1610: mdoc->parse, nit->line, nit->pos,
1.192 schwarze 1611: "%d columns, %d cells", cols, i);
1.260 schwarze 1612: else if (nit->head->next->child != NULL &&
1613: nit->head->next->child->line > nit->line)
1614: mandoc_msg(MANDOCERR_IT_NOARG, mdoc->parse,
1615: nit->line, nit->pos, "Bl -column It");
1.154 schwarze 1616: break;
1.1 kristaps 1617: default:
1.143 schwarze 1618: abort();
1.1 kristaps 1619: }
1620: }
1621:
1.175 schwarze 1622: static void
1.128 schwarze 1623: post_bl_block(POST_ARGS)
1.11 schwarze 1624: {
1.201 schwarze 1625: struct roff_node *n, *ni, *nc;
1.11 schwarze 1626:
1.215 schwarze 1627: post_prevpar(mdoc);
1628:
1.76 schwarze 1629: n = mdoc->last;
1.213 schwarze 1630: for (ni = n->body->child; ni != NULL; ni = ni->next) {
1631: if (ni->body == NULL)
1.107 schwarze 1632: continue;
1633: nc = ni->body->last;
1.213 schwarze 1634: while (nc != NULL) {
1.107 schwarze 1635: switch (nc->tok) {
1.128 schwarze 1636: case MDOC_Pp:
1.239 schwarze 1637: case ROFF_br:
1.107 schwarze 1638: break;
1639: default:
1640: nc = NULL;
1641: continue;
1642: }
1.213 schwarze 1643: if (ni->next == NULL) {
1.137 schwarze 1644: mandoc_msg(MANDOCERR_PAR_MOVE,
1645: mdoc->parse, nc->line, nc->pos,
1.236 schwarze 1646: roff_name[nc->tok]);
1.175 schwarze 1647: mdoc_node_relink(mdoc, nc);
1.213 schwarze 1648: } else if (n->norm->Bl.comp == 0 &&
1649: n->norm->Bl.type != LIST_column) {
1.137 schwarze 1650: mandoc_vmsg(MANDOCERR_PAR_SKIP,
1651: mdoc->parse, nc->line, nc->pos,
1.236 schwarze 1652: "%s before It", roff_name[nc->tok]);
1.203 schwarze 1653: roff_node_delete(mdoc, nc);
1.107 schwarze 1654: } else
1655: break;
1656: nc = ni->body->last;
1657: }
1658: }
1.11 schwarze 1659: }
1660:
1.170 schwarze 1661: /*
1662: * If the argument of -offset or -width is a macro,
1663: * replace it with the associated default width.
1664: */
1.238 schwarze 1665: static void
1666: rewrite_macro2len(struct roff_man *mdoc, char **arg)
1.1 kristaps 1667: {
1.76 schwarze 1668: size_t width;
1.236 schwarze 1669: enum roff_tok tok;
1.1 kristaps 1670:
1.170 schwarze 1671: if (*arg == NULL)
1672: return;
1673: else if ( ! strcmp(*arg, "Ds"))
1.76 schwarze 1674: width = 6;
1.238 schwarze 1675: else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE)
1.170 schwarze 1676: return;
1.146 schwarze 1677: else
1678: width = macro2len(tok);
1.1 kristaps 1679:
1.170 schwarze 1680: free(*arg);
1681: mandoc_asprintf(arg, "%zun", width);
1.1 kristaps 1682: }
1683:
1.175 schwarze 1684: static void
1.128 schwarze 1685: post_bl_head(POST_ARGS)
1.1 kristaps 1686: {
1.201 schwarze 1687: struct roff_node *nbl, *nh, *nch, *nnext;
1.155 schwarze 1688: struct mdoc_argv *argv;
1.76 schwarze 1689: int i, j;
1.1 kristaps 1690:
1.215 schwarze 1691: post_bl_norm(mdoc);
1692:
1.186 schwarze 1693: nh = mdoc->last;
1694: if (nh->norm->Bl.type != LIST_column) {
1695: if ((nch = nh->child) == NULL)
1696: return;
1697: mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
1698: nch->line, nch->pos, "Bl ... %s", nch->string);
1699: while (nch != NULL) {
1.203 schwarze 1700: roff_node_delete(mdoc, nch);
1.186 schwarze 1701: nch = nh->child;
1702: }
1.175 schwarze 1703: return;
1704: }
1.36 schwarze 1705:
1.76 schwarze 1706: /*
1.155 schwarze 1707: * Append old-style lists, where the column width specifiers
1.76 schwarze 1708: * trail as macro parameters, to the new-style ("normal-form")
1709: * lists where they're argument values following -column.
1710: */
1.36 schwarze 1711:
1.186 schwarze 1712: if (nh->child == NULL)
1.175 schwarze 1713: return;
1.76 schwarze 1714:
1.186 schwarze 1715: nbl = nh->parent;
1716: for (j = 0; j < (int)nbl->args->argc; j++)
1717: if (nbl->args->argv[j].arg == MDOC_Column)
1.76 schwarze 1718: break;
1719:
1.186 schwarze 1720: assert(j < (int)nbl->args->argc);
1.76 schwarze 1721:
1722: /*
1.93 schwarze 1723: * Accommodate for new-style groff column syntax. Shuffle the
1.76 schwarze 1724: * child nodes, all of which must be TEXT, as arguments for the
1725: * column field. Then, delete the head children.
1726: */
1727:
1.186 schwarze 1728: argv = nbl->args->argv + j;
1.155 schwarze 1729: i = argv->sz;
1.217 schwarze 1730: for (nch = nh->child; nch != NULL; nch = nch->next)
1731: argv->sz++;
1.155 schwarze 1732: argv->value = mandoc_reallocarray(argv->value,
1733: argv->sz, sizeof(char *));
1.76 schwarze 1734:
1.186 schwarze 1735: nh->norm->Bl.ncols = argv->sz;
1736: nh->norm->Bl.cols = (void *)argv->value;
1.76 schwarze 1737:
1.186 schwarze 1738: for (nch = nh->child; nch != NULL; nch = nnext) {
1739: argv->value[i++] = nch->string;
1740: nch->string = NULL;
1741: nnext = nch->next;
1.203 schwarze 1742: roff_node_delete(NULL, nch);
1.76 schwarze 1743: }
1.186 schwarze 1744: nh->child = NULL;
1.76 schwarze 1745: }
1746:
1.175 schwarze 1747: static void
1.76 schwarze 1748: post_bl(POST_ARGS)
1749: {
1.201 schwarze 1750: struct roff_node *nparent, *nprev; /* of the Bl block */
1751: struct roff_node *nblock, *nbody; /* of the Bl */
1752: struct roff_node *nchild, *nnext; /* of the Bl body */
1.247 schwarze 1753: const char *prev_Er;
1754: int order;
1.113 schwarze 1755:
1756: nbody = mdoc->last;
1757: switch (nbody->type) {
1.200 schwarze 1758: case ROFFT_BLOCK:
1.175 schwarze 1759: post_bl_block(mdoc);
1760: return;
1.200 schwarze 1761: case ROFFT_HEAD:
1.175 schwarze 1762: post_bl_head(mdoc);
1763: return;
1.200 schwarze 1764: case ROFFT_BODY:
1.113 schwarze 1765: break;
1766: default:
1.175 schwarze 1767: return;
1.113 schwarze 1768: }
1.214 schwarze 1769: if (nbody->end != ENDBODY_NOT)
1770: return;
1.36 schwarze 1771:
1.113 schwarze 1772: nchild = nbody->child;
1.189 schwarze 1773: if (nchild == NULL) {
1.191 schwarze 1774: mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse,
1.189 schwarze 1775: nbody->line, nbody->pos, "Bl");
1776: return;
1777: }
1778: while (nchild != NULL) {
1.224 schwarze 1779: nnext = nchild->next;
1.181 schwarze 1780: if (nchild->tok == MDOC_It ||
1781: (nchild->tok == MDOC_Sm &&
1.224 schwarze 1782: nnext != NULL && nnext->tok == MDOC_It)) {
1783: nchild = nnext;
1784: continue;
1785: }
1786:
1787: /*
1788: * In .Bl -column, the first rows may be implicit,
1789: * that is, they may not start with .It macros.
1790: * Such rows may be followed by nodes generated on the
1791: * roff level, for example .TS, which cannot be moved
1792: * out of the list. In that case, wrap such roff nodes
1793: * into an implicit row.
1794: */
1795:
1796: if (nchild->prev != NULL) {
1797: mdoc->last = nchild;
1798: mdoc->next = ROFF_NEXT_SIBLING;
1799: roff_block_alloc(mdoc, nchild->line,
1800: nchild->pos, MDOC_It);
1801: roff_head_alloc(mdoc, nchild->line,
1802: nchild->pos, MDOC_It);
1803: mdoc->next = ROFF_NEXT_SIBLING;
1804: roff_body_alloc(mdoc, nchild->line,
1805: nchild->pos, MDOC_It);
1806: while (nchild->tok != MDOC_It) {
1807: mdoc_node_relink(mdoc, nchild);
1808: if ((nchild = nnext) == NULL)
1809: break;
1810: nnext = nchild->next;
1811: mdoc->next = ROFF_NEXT_SIBLING;
1812: }
1813: mdoc->last = nbody;
1.76 schwarze 1814: continue;
1.79 schwarze 1815: }
1816:
1.139 schwarze 1817: mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
1.236 schwarze 1818: nchild->line, nchild->pos, roff_name[nchild->tok]);
1.113 schwarze 1819:
1820: /*
1821: * Move the node out of the Bl block.
1822: * First, collect all required node pointers.
1823: */
1824:
1825: nblock = nbody->parent;
1826: nprev = nblock->prev;
1827: nparent = nblock->parent;
1828:
1829: /*
1830: * Unlink this child.
1831: */
1832:
1.217 schwarze 1833: nbody->child = nnext;
1834: if (nnext == NULL)
1.113 schwarze 1835: nbody->last = NULL;
1.217 schwarze 1836: else
1.113 schwarze 1837: nnext->prev = NULL;
1838:
1839: /*
1840: * Relink this child.
1841: */
1842:
1843: nchild->parent = nparent;
1844: nchild->prev = nprev;
1845: nchild->next = nblock;
1846:
1847: nblock->prev = nchild;
1.213 schwarze 1848: if (nprev == NULL)
1.113 schwarze 1849: nparent->child = nchild;
1850: else
1851: nprev->next = nchild;
1852:
1853: nchild = nnext;
1.76 schwarze 1854: }
1.247 schwarze 1855:
1.255 schwarze 1856: if (mdoc->meta.os_e != MANDOC_OS_NETBSD)
1.247 schwarze 1857: return;
1858:
1859: prev_Er = NULL;
1860: for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) {
1861: if (nchild->tok != MDOC_It)
1862: continue;
1863: if ((nnext = nchild->head->child) == NULL)
1864: continue;
1865: if (nnext->type == ROFFT_BLOCK)
1866: nnext = nnext->body->child;
1867: if (nnext == NULL || nnext->tok != MDOC_Er)
1868: continue;
1869: nnext = nnext->child;
1870: if (prev_Er != NULL) {
1871: order = strcmp(prev_Er, nnext->string);
1872: if (order > 0)
1873: mandoc_vmsg(MANDOCERR_ER_ORDER,
1874: mdoc->parse, nnext->line, nnext->pos,
1.255 schwarze 1875: "Er %s %s (NetBSD)",
1876: prev_Er, nnext->string);
1.247 schwarze 1877: else if (order == 0)
1878: mandoc_vmsg(MANDOCERR_ER_REP,
1879: mdoc->parse, nnext->line, nnext->pos,
1.255 schwarze 1880: "Er %s (NetBSD)", prev_Er);
1.247 schwarze 1881: }
1882: prev_Er = nnext->string;
1883: }
1.76 schwarze 1884: }
1885:
1.175 schwarze 1886: static void
1.160 schwarze 1887: post_bk(POST_ARGS)
1888: {
1.201 schwarze 1889: struct roff_node *n;
1.160 schwarze 1890:
1.186 schwarze 1891: n = mdoc->last;
1892:
1.200 schwarze 1893: if (n->type == ROFFT_BLOCK && n->body->child == NULL) {
1.191 schwarze 1894: mandoc_msg(MANDOCERR_BLK_EMPTY,
1.186 schwarze 1895: mdoc->parse, n->line, n->pos, "Bk");
1.203 schwarze 1896: roff_node_delete(mdoc, n);
1.186 schwarze 1897: }
1.160 schwarze 1898: }
1899:
1.175 schwarze 1900: static void
1.213 schwarze 1901: post_sm(POST_ARGS)
1.76 schwarze 1902: {
1.201 schwarze 1903: struct roff_node *nch;
1.146 schwarze 1904:
1905: nch = mdoc->last->child;
1.76 schwarze 1906:
1.173 schwarze 1907: if (nch == NULL) {
1908: mdoc->flags ^= MDOC_SMOFF;
1.175 schwarze 1909: return;
1.83 schwarze 1910: }
1.76 schwarze 1911:
1.200 schwarze 1912: assert(nch->type == ROFFT_TEXT);
1.76 schwarze 1913:
1.173 schwarze 1914: if ( ! strcmp(nch->string, "on")) {
1915: mdoc->flags &= ~MDOC_SMOFF;
1.175 schwarze 1916: return;
1.115 schwarze 1917: }
1.173 schwarze 1918: if ( ! strcmp(nch->string, "off")) {
1919: mdoc->flags |= MDOC_SMOFF;
1.175 schwarze 1920: return;
1.115 schwarze 1921: }
1.76 schwarze 1922:
1.146 schwarze 1923: mandoc_vmsg(MANDOCERR_SM_BAD,
1924: mdoc->parse, nch->line, nch->pos,
1.236 schwarze 1925: "%s %s", roff_name[mdoc->last->tok], nch->string);
1.175 schwarze 1926: mdoc_node_relink(mdoc, nch);
1927: return;
1.76 schwarze 1928: }
1929:
1.175 schwarze 1930: static void
1.76 schwarze 1931: post_root(POST_ARGS)
1932: {
1.257 schwarze 1933: const char *openbsd_arch[] = {
1934: "alpha", "amd64", "arm64", "armv7", "hppa", "i386",
1935: "landisk", "loongson", "luna88k", "macppc", "mips64",
1936: "octeon", "sgi", "socppc", "sparc64", NULL
1937: };
1938: const char *netbsd_arch[] = {
1939: "acorn26", "acorn32", "algor", "alpha", "amiga",
1940: "arc", "atari",
1941: "bebox", "cats", "cesfic", "cobalt", "dreamcast",
1942: "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5",
1943: "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa",
1944: "i386", "ibmnws", "luna68k",
1945: "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc",
1946: "netwinder", "news68k", "newsmips", "next68k",
1947: "pc532", "playstation2", "pmax", "pmppc", "prep",
1948: "sandpoint", "sbmips", "sgimips", "shark",
1949: "sparc", "sparc64", "sun2", "sun3",
1950: "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL
1951: };
1952: const char **arches[] = { NULL, netbsd_arch, openbsd_arch };
1953:
1.201 schwarze 1954: struct roff_node *n;
1.257 schwarze 1955: const char **arch;
1.76 schwarze 1956:
1.152 schwarze 1957: /* Add missing prologue data. */
1.76 schwarze 1958:
1.161 schwarze 1959: if (mdoc->meta.date == NULL)
1.252 schwarze 1960: mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
1961: mandoc_normdate(mdoc, NULL, 0, 0);
1.161 schwarze 1962:
1963: if (mdoc->meta.title == NULL) {
1964: mandoc_msg(MANDOCERR_DT_NOTITLE,
1965: mdoc->parse, 0, 0, "EOF");
1966: mdoc->meta.title = mandoc_strdup("UNTITLED");
1967: }
1968:
1969: if (mdoc->meta.vol == NULL)
1970: mdoc->meta.vol = mandoc_strdup("LOCAL");
1971:
1972: if (mdoc->meta.os == NULL) {
1973: mandoc_msg(MANDOCERR_OS_MISSING,
1974: mdoc->parse, 0, 0, NULL);
1975: mdoc->meta.os = mandoc_strdup("");
1.254 schwarze 1976: } else if (mdoc->meta.os_e &&
1977: (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
1.255 schwarze 1978: mandoc_msg(MANDOCERR_RCS_MISSING, mdoc->parse, 0, 0,
1979: mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1980: "(OpenBSD)" : "(NetBSD)");
1.257 schwarze 1981:
1982: if (mdoc->meta.arch != NULL &&
1983: (arch = arches[mdoc->meta.os_e]) != NULL) {
1984: while (*arch != NULL && strcmp(*arch, mdoc->meta.arch))
1985: arch++;
1986: if (*arch == NULL) {
1987: n = mdoc->first->child;
1.268 schwarze 1988: while (n->tok != MDOC_Dt ||
1989: n->child == NULL ||
1990: n->child->next == NULL ||
1991: n->child->next->next == NULL)
1.257 schwarze 1992: n = n->next;
1993: n = n->child->next->next;
1994: mandoc_vmsg(MANDOCERR_ARCH_BAD,
1995: mdoc->parse, n->line, n->pos,
1996: "Dt ... %s %s", mdoc->meta.arch,
1997: mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1998: "(OpenBSD)" : "(NetBSD)");
1999: }
2000: }
1.76 schwarze 2001:
2002: /* Check that we begin with a proper `Sh'. */
2003:
1.163 schwarze 2004: n = mdoc->first->child;
1.273 schwarze 2005: while (n != NULL &&
2006: (n->type == ROFFT_COMMENT ||
2007: (n->tok >= MDOC_Dd &&
1.277 schwarze 2008: mdoc_macro(n->tok)->flags & MDOC_PROLOGUE)))
1.163 schwarze 2009: n = n->next;
2010:
2011: if (n == NULL)
2012: mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
2013: else if (n->tok != MDOC_Sh)
1.135 schwarze 2014: mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1.236 schwarze 2015: n->line, n->pos, roff_name[n->tok]);
1.76 schwarze 2016: }
2017:
1.175 schwarze 2018: static void
1.76 schwarze 2019: post_rs(POST_ARGS)
2020: {
1.201 schwarze 2021: struct roff_node *np, *nch, *next, *prev;
1.76 schwarze 2022: int i, j;
2023:
1.184 schwarze 2024: np = mdoc->last;
2025:
1.200 schwarze 2026: if (np->type != ROFFT_BODY)
1.175 schwarze 2027: return;
1.184 schwarze 2028:
2029: if (np->child == NULL) {
2030: mandoc_msg(MANDOCERR_RS_EMPTY, mdoc->parse,
2031: np->line, np->pos, "Rs");
1.175 schwarze 2032: return;
1.83 schwarze 2033: }
1.76 schwarze 2034:
2035: /*
2036: * The full `Rs' block needs special handling to order the
2037: * sub-elements according to `rsord'. Pick through each element
1.148 schwarze 2038: * and correctly order it. This is an insertion sort.
1.76 schwarze 2039: */
2040:
2041: next = NULL;
1.184 schwarze 2042: for (nch = np->child->next; nch != NULL; nch = next) {
2043: /* Determine order number of this child. */
1.76 schwarze 2044: for (i = 0; i < RSORD_MAX; i++)
1.184 schwarze 2045: if (rsord[i] == nch->tok)
1.76 schwarze 2046: break;
2047:
1.148 schwarze 2048: if (i == RSORD_MAX) {
1.236 schwarze 2049: mandoc_msg(MANDOCERR_RS_BAD, mdoc->parse,
2050: nch->line, nch->pos, roff_name[nch->tok]);
1.148 schwarze 2051: i = -1;
1.184 schwarze 2052: } else if (nch->tok == MDOC__J || nch->tok == MDOC__B)
2053: np->norm->Rs.quote_T++;
1.148 schwarze 2054:
1.128 schwarze 2055: /*
1.184 schwarze 2056: * Remove this child from the chain. This somewhat
1.203 schwarze 2057: * repeats roff_node_unlink(), but since we're
1.76 schwarze 2058: * just re-ordering, there's no need for the
2059: * full unlink process.
2060: */
1.128 schwarze 2061:
1.184 schwarze 2062: if ((next = nch->next) != NULL)
2063: next->prev = nch->prev;
1.76 schwarze 2064:
1.184 schwarze 2065: if ((prev = nch->prev) != NULL)
2066: prev->next = nch->next;
1.76 schwarze 2067:
1.184 schwarze 2068: nch->prev = nch->next = NULL;
1.76 schwarze 2069:
1.128 schwarze 2070: /*
1.76 schwarze 2071: * Scan back until we reach a node that's
1.184 schwarze 2072: * to be ordered before this child.
1.76 schwarze 2073: */
2074:
2075: for ( ; prev ; prev = prev->prev) {
2076: /* Determine order of `prev'. */
2077: for (j = 0; j < RSORD_MAX; j++)
2078: if (rsord[j] == prev->tok)
2079: break;
1.148 schwarze 2080: if (j == RSORD_MAX)
2081: j = -1;
1.76 schwarze 2082:
2083: if (j <= i)
2084: break;
2085: }
2086:
2087: /*
1.184 schwarze 2088: * Set this child back into its correct place
2089: * in front of the `prev' node.
1.76 schwarze 2090: */
2091:
1.184 schwarze 2092: nch->prev = prev;
1.76 schwarze 2093:
1.184 schwarze 2094: if (prev == NULL) {
2095: np->child->prev = nch;
2096: nch->next = np->child;
2097: np->child = nch;
2098: } else {
1.76 schwarze 2099: if (prev->next)
1.184 schwarze 2100: prev->next->prev = nch;
2101: nch->next = prev->next;
2102: prev->next = nch;
1.36 schwarze 2103: }
1.76 schwarze 2104: }
1.114 schwarze 2105: }
2106:
2107: /*
2108: * For some arguments of some macros,
2109: * convert all breakable hyphens into ASCII_HYPH.
2110: */
1.175 schwarze 2111: static void
1.114 schwarze 2112: post_hyph(POST_ARGS)
2113: {
1.201 schwarze 2114: struct roff_node *nch;
1.114 schwarze 2115: char *cp;
2116:
1.189 schwarze 2117: for (nch = mdoc->last->child; nch != NULL; nch = nch->next) {
1.200 schwarze 2118: if (nch->type != ROFFT_TEXT)
1.114 schwarze 2119: continue;
2120: cp = nch->string;
1.189 schwarze 2121: if (*cp == '\0')
1.114 schwarze 2122: continue;
1.189 schwarze 2123: while (*(++cp) != '\0')
2124: if (*cp == '-' &&
1.114 schwarze 2125: isalpha((unsigned char)cp[-1]) &&
2126: isalpha((unsigned char)cp[1]))
2127: *cp = ASCII_HYPH;
2128: }
1.160 schwarze 2129: }
2130:
1.175 schwarze 2131: static void
1.88 schwarze 2132: post_ns(POST_ARGS)
2133: {
1.259 schwarze 2134: struct roff_node *n;
1.88 schwarze 2135:
1.259 schwarze 2136: n = mdoc->last;
2137: if (n->flags & NODE_LINE ||
2138: (n->next != NULL && n->next->flags & NODE_DELIMC))
1.158 schwarze 2139: mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
1.259 schwarze 2140: n->line, n->pos, NULL);
1.1 kristaps 2141: }
2142:
1.175 schwarze 2143: static void
1.263 schwarze 2144: post_sx(POST_ARGS)
2145: {
2146: post_delim(mdoc);
2147: post_hyph(mdoc);
2148: }
2149:
2150: static void
1.1 kristaps 2151: post_sh(POST_ARGS)
2152: {
2153:
1.160 schwarze 2154: post_ignpar(mdoc);
2155:
1.164 schwarze 2156: switch (mdoc->last->type) {
1.200 schwarze 2157: case ROFFT_HEAD:
1.175 schwarze 2158: post_sh_head(mdoc);
2159: break;
1.200 schwarze 2160: case ROFFT_BODY:
1.164 schwarze 2161: switch (mdoc->lastsec) {
2162: case SEC_NAME:
1.175 schwarze 2163: post_sh_name(mdoc);
2164: break;
1.165 schwarze 2165: case SEC_SEE_ALSO:
1.175 schwarze 2166: post_sh_see_also(mdoc);
2167: break;
1.164 schwarze 2168: case SEC_AUTHORS:
1.175 schwarze 2169: post_sh_authors(mdoc);
2170: break;
1.164 schwarze 2171: default:
2172: break;
2173: }
2174: break;
2175: default:
2176: break;
2177: }
1.1 kristaps 2178: }
2179:
1.175 schwarze 2180: static void
1.164 schwarze 2181: post_sh_name(POST_ARGS)
1.1 kristaps 2182: {
1.201 schwarze 2183: struct roff_node *n;
1.198 schwarze 2184: int hasnm, hasnd;
1.1 kristaps 2185:
1.198 schwarze 2186: hasnm = hasnd = 0;
1.1 kristaps 2187:
1.198 schwarze 2188: for (n = mdoc->last->child; n != NULL; n = n->next) {
2189: switch (n->tok) {
2190: case MDOC_Nm:
1.227 schwarze 2191: if (hasnm && n->child != NULL)
2192: mandoc_vmsg(MANDOCERR_NAMESEC_PUNCT,
2193: mdoc->parse, n->line, n->pos,
2194: "Nm %s", n->child->string);
1.198 schwarze 2195: hasnm = 1;
1.227 schwarze 2196: continue;
1.198 schwarze 2197: case MDOC_Nd:
2198: hasnd = 1;
2199: if (n->next != NULL)
2200: mandoc_msg(MANDOCERR_NAMESEC_ND,
2201: mdoc->parse, n->line, n->pos, NULL);
2202: break;
1.204 schwarze 2203: case TOKEN_NONE:
1.227 schwarze 2204: if (n->type == ROFFT_TEXT &&
2205: n->string[0] == ',' && n->string[1] == '\0' &&
2206: n->next != NULL && n->next->tok == MDOC_Nm) {
2207: n = n->next;
2208: continue;
2209: }
1.199 schwarze 2210: /* FALLTHROUGH */
1.198 schwarze 2211: default:
2212: mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
1.236 schwarze 2213: n->line, n->pos, roff_name[n->tok]);
1.227 schwarze 2214: continue;
1.198 schwarze 2215: }
1.227 schwarze 2216: break;
1.76 schwarze 2217: }
1.1 kristaps 2218:
1.198 schwarze 2219: if ( ! hasnm)
2220: mandoc_msg(MANDOCERR_NAMESEC_NONM, mdoc->parse,
2221: mdoc->last->line, mdoc->last->pos, NULL);
2222: if ( ! hasnd)
2223: mandoc_msg(MANDOCERR_NAMESEC_NOND, mdoc->parse,
2224: mdoc->last->line, mdoc->last->pos, NULL);
1.165 schwarze 2225: }
2226:
1.175 schwarze 2227: static void
1.165 schwarze 2228: post_sh_see_also(POST_ARGS)
2229: {
1.201 schwarze 2230: const struct roff_node *n;
1.193 schwarze 2231: const char *name, *sec;
1.165 schwarze 2232: const char *lastname, *lastsec, *lastpunct;
2233: int cmp;
2234:
2235: n = mdoc->last->child;
2236: lastname = lastsec = lastpunct = NULL;
2237: while (n != NULL) {
1.217 schwarze 2238: if (n->tok != MDOC_Xr ||
2239: n->child == NULL ||
2240: n->child->next == NULL)
1.165 schwarze 2241: break;
2242:
2243: /* Process one .Xr node. */
2244:
2245: name = n->child->string;
2246: sec = n->child->next->string;
2247: if (lastsec != NULL) {
2248: if (lastpunct[0] != ',' || lastpunct[1] != '\0')
2249: mandoc_vmsg(MANDOCERR_XR_PUNCT,
2250: mdoc->parse, n->line, n->pos,
2251: "%s before %s(%s)", lastpunct,
2252: name, sec);
2253: cmp = strcmp(lastsec, sec);
2254: if (cmp > 0)
2255: mandoc_vmsg(MANDOCERR_XR_ORDER,
2256: mdoc->parse, n->line, n->pos,
2257: "%s(%s) after %s(%s)", name,
2258: sec, lastname, lastsec);
2259: else if (cmp == 0 &&
2260: strcasecmp(lastname, name) > 0)
2261: mandoc_vmsg(MANDOCERR_XR_ORDER,
2262: mdoc->parse, n->line, n->pos,
2263: "%s after %s", name, lastname);
2264: }
2265: lastname = name;
2266: lastsec = sec;
2267:
2268: /* Process the following node. */
2269:
2270: n = n->next;
2271: if (n == NULL)
2272: break;
2273: if (n->tok == MDOC_Xr) {
2274: lastpunct = "none";
2275: continue;
2276: }
1.200 schwarze 2277: if (n->type != ROFFT_TEXT)
1.165 schwarze 2278: break;
2279: for (name = n->string; *name != '\0'; name++)
2280: if (isalpha((const unsigned char)*name))
1.175 schwarze 2281: return;
1.165 schwarze 2282: lastpunct = n->string;
1.243 schwarze 2283: if (n->next == NULL || n->next->tok == MDOC_Rs)
1.165 schwarze 2284: mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
2285: n->line, n->pos, "%s after %s(%s)",
2286: lastpunct, lastname, lastsec);
2287: n = n->next;
2288: }
1.164 schwarze 2289: }
2290:
2291: static int
1.201 schwarze 2292: child_an(const struct roff_node *n)
1.164 schwarze 2293: {
2294:
2295: for (n = n->child; n != NULL; n = n->next)
1.217 schwarze 2296: if ((n->tok == MDOC_An && n->child != NULL) || child_an(n))
1.210 schwarze 2297: return 1;
2298: return 0;
1.164 schwarze 2299: }
2300:
1.175 schwarze 2301: static void
1.164 schwarze 2302: post_sh_authors(POST_ARGS)
2303: {
2304:
2305: if ( ! child_an(mdoc->last))
2306: mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
2307: mdoc->last->line, mdoc->last->pos, NULL);
1.1 kristaps 2308: }
2309:
1.258 schwarze 2310: /*
2311: * Return an upper bound for the string distance (allowing
2312: * transpositions). Not a full Levenshtein implementation
2313: * because Levenshtein is quadratic in the string length
2314: * and this function is called for every standard name,
2315: * so the check for each custom name would be cubic.
2316: * The following crude heuristics is linear, resulting
2317: * in quadratic behaviour for checking one custom name,
2318: * which does not cause measurable slowdown.
2319: */
2320: static int
2321: similar(const char *s1, const char *s2)
2322: {
2323: const int maxdist = 3;
2324: int dist = 0;
2325:
2326: while (s1[0] != '\0' && s2[0] != '\0') {
2327: if (s1[0] == s2[0]) {
2328: s1++;
2329: s2++;
2330: continue;
2331: }
2332: if (++dist > maxdist)
2333: return INT_MAX;
2334: if (s1[1] == s2[1]) { /* replacement */
2335: s1++;
2336: s2++;
2337: } else if (s1[0] == s2[1] && s1[1] == s2[0]) {
2338: s1 += 2; /* transposition */
2339: s2 += 2;
2340: } else if (s1[0] == s2[1]) /* insertion */
2341: s2++;
2342: else if (s1[1] == s2[0]) /* deletion */
2343: s1++;
2344: else
2345: return INT_MAX;
2346: }
2347: dist += strlen(s1) + strlen(s2);
2348: return dist > maxdist ? INT_MAX : dist;
2349: }
2350:
1.175 schwarze 2351: static void
1.1 kristaps 2352: post_sh_head(POST_ARGS)
2353: {
1.221 schwarze 2354: struct roff_node *nch;
2355: const char *goodsec;
1.258 schwarze 2356: const char *const *testsec;
2357: int dist, mindist;
1.221 schwarze 2358: enum roff_sec sec;
1.1 kristaps 2359:
2360: /*
2361: * Process a new section. Sections are either "named" or
1.76 schwarze 2362: * "custom". Custom sections are user-defined, while named ones
2363: * follow a conventional order and may only appear in certain
2364: * manual sections.
1.1 kristaps 2365: */
2366:
1.214 schwarze 2367: sec = mdoc->last->sec;
1.76 schwarze 2368:
2369: /* The NAME should be first. */
1.55 schwarze 2370:
1.218 schwarze 2371: if (sec != SEC_NAME && mdoc->lastnamed == SEC_NONE)
1.157 schwarze 2372: mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
1.218 schwarze 2373: mdoc->last->line, mdoc->last->pos, "Sh %s",
1.221 schwarze 2374: sec != SEC_CUSTOM ? secnames[sec] :
2375: (nch = mdoc->last->child) == NULL ? "" :
2376: nch->type == ROFFT_TEXT ? nch->string :
1.236 schwarze 2377: roff_name[nch->tok]);
1.1 kristaps 2378:
1.76 schwarze 2379: /* The SYNOPSIS gets special attention in other areas. */
1.9 schwarze 2380:
1.214 schwarze 2381: if (sec == SEC_SYNOPSIS) {
1.116 schwarze 2382: roff_setreg(mdoc->roff, "nS", 1, '=');
1.76 schwarze 2383: mdoc->flags |= MDOC_SYNOPSIS;
1.112 schwarze 2384: } else {
1.116 schwarze 2385: roff_setreg(mdoc->roff, "nS", 0, '=');
1.76 schwarze 2386: mdoc->flags &= ~MDOC_SYNOPSIS;
1.112 schwarze 2387: }
1.1 kristaps 2388:
1.76 schwarze 2389: /* Mark our last section. */
1.1 kristaps 2390:
1.76 schwarze 2391: mdoc->lastsec = sec;
1.100 schwarze 2392:
1.76 schwarze 2393: /* We don't care about custom sections after this. */
1.51 schwarze 2394:
1.258 schwarze 2395: if (sec == SEC_CUSTOM) {
2396: if ((nch = mdoc->last->child) == NULL ||
2397: nch->type != ROFFT_TEXT || nch->next != NULL)
2398: return;
2399: goodsec = NULL;
2400: mindist = INT_MAX;
2401: for (testsec = secnames + 1; *testsec != NULL; testsec++) {
2402: dist = similar(nch->string, *testsec);
2403: if (dist < mindist) {
2404: goodsec = *testsec;
2405: mindist = dist;
2406: }
2407: }
2408: if (goodsec != NULL)
2409: mandoc_vmsg(MANDOCERR_SEC_TYPO, mdoc->parse,
2410: nch->line, nch->pos, "Sh %s instead of %s",
2411: nch->string, goodsec);
1.175 schwarze 2412: return;
1.258 schwarze 2413: }
1.51 schwarze 2414:
1.76 schwarze 2415: /*
2416: * Check whether our non-custom section is being repeated or is
2417: * out of order.
2418: */
2419:
1.1 kristaps 2420: if (sec == mdoc->lastnamed)
1.157 schwarze 2421: mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
2422: mdoc->last->line, mdoc->last->pos,
1.214 schwarze 2423: "Sh %s", secnames[sec]);
1.51 schwarze 2424:
1.1 kristaps 2425: if (sec < mdoc->lastnamed)
1.157 schwarze 2426: mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
2427: mdoc->last->line, mdoc->last->pos,
1.214 schwarze 2428: "Sh %s", secnames[sec]);
1.76 schwarze 2429:
2430: /* Mark the last named section. */
2431:
2432: mdoc->lastnamed = sec;
2433:
2434: /* Check particular section/manual conventions. */
1.1 kristaps 2435:
1.214 schwarze 2436: if (mdoc->meta.msec == NULL)
1.175 schwarze 2437: return;
1.1 kristaps 2438:
1.135 schwarze 2439: goodsec = NULL;
1.1 kristaps 2440: switch (sec) {
1.128 schwarze 2441: case SEC_ERRORS:
1.121 schwarze 2442: if (*mdoc->meta.msec == '4')
2443: break;
1.135 schwarze 2444: goodsec = "2, 3, 4, 9";
1.121 schwarze 2445: /* FALLTHROUGH */
1.128 schwarze 2446: case SEC_RETURN_VALUES:
2447: case SEC_LIBRARY:
1.54 schwarze 2448: if (*mdoc->meta.msec == '2')
2449: break;
2450: if (*mdoc->meta.msec == '3')
2451: break;
1.135 schwarze 2452: if (NULL == goodsec)
2453: goodsec = "2, 3, 9";
1.126 dlg 2454: /* FALLTHROUGH */
1.128 schwarze 2455: case SEC_CONTEXT:
1.54 schwarze 2456: if (*mdoc->meta.msec == '9')
1.1 kristaps 2457: break;
1.135 schwarze 2458: if (NULL == goodsec)
2459: goodsec = "9";
2460: mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
2461: mdoc->last->line, mdoc->last->pos,
1.214 schwarze 2462: "Sh %s for %s only", secnames[sec], goodsec);
1.76 schwarze 2463: break;
1.1 kristaps 2464: default:
2465: break;
2466: }
1.70 schwarze 2467: }
2468:
1.175 schwarze 2469: static void
1.226 schwarze 2470: post_xr(POST_ARGS)
2471: {
2472: struct roff_node *n, *nch;
2473:
2474: n = mdoc->last;
2475: nch = n->child;
2476: if (nch->next == NULL) {
2477: mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse,
2478: n->line, n->pos, "Xr %s", nch->string);
1.261 schwarze 2479: } else {
1.248 schwarze 2480: assert(nch->next == n->last);
1.262 schwarze 2481: if(mandoc_xr_add(nch->next->string, nch->string,
2482: nch->line, nch->pos))
2483: mandoc_vmsg(MANDOCERR_XR_SELF, mdoc->parse,
2484: nch->line, nch->pos, "Xr %s %s",
2485: nch->string, nch->next->string);
1.261 schwarze 2486: }
1.263 schwarze 2487: post_delim_nb(mdoc);
1.226 schwarze 2488: }
2489:
2490: static void
1.79 schwarze 2491: post_ignpar(POST_ARGS)
2492: {
1.201 schwarze 2493: struct roff_node *np;
1.79 schwarze 2494:
1.189 schwarze 2495: switch (mdoc->last->type) {
1.237 schwarze 2496: case ROFFT_BLOCK:
2497: post_prevpar(mdoc);
2498: return;
1.200 schwarze 2499: case ROFFT_HEAD:
1.263 schwarze 2500: post_delim(mdoc);
1.189 schwarze 2501: post_hyph(mdoc);
2502: return;
1.200 schwarze 2503: case ROFFT_BODY:
1.189 schwarze 2504: break;
2505: default:
1.175 schwarze 2506: return;
1.189 schwarze 2507: }
1.79 schwarze 2508:
1.213 schwarze 2509: if ((np = mdoc->last->child) != NULL)
1.278 ! schwarze 2510: if (np->tok == MDOC_Pp) {
1.137 schwarze 2511: mandoc_vmsg(MANDOCERR_PAR_SKIP,
2512: mdoc->parse, np->line, np->pos,
1.236 schwarze 2513: "%s after %s", roff_name[np->tok],
2514: roff_name[mdoc->last->tok]);
1.203 schwarze 2515: roff_node_delete(mdoc, np);
1.79 schwarze 2516: }
2517:
1.213 schwarze 2518: if ((np = mdoc->last->last) != NULL)
1.278 ! schwarze 2519: if (np->tok == MDOC_Pp) {
1.137 schwarze 2520: mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
2521: np->line, np->pos, "%s at the end of %s",
1.236 schwarze 2522: roff_name[np->tok],
2523: roff_name[mdoc->last->tok]);
1.203 schwarze 2524: roff_node_delete(mdoc, np);
1.79 schwarze 2525: }
1.76 schwarze 2526: }
1.70 schwarze 2527:
1.175 schwarze 2528: static void
1.215 schwarze 2529: post_prevpar(POST_ARGS)
1.70 schwarze 2530: {
1.215 schwarze 2531: struct roff_node *n;
1.70 schwarze 2532:
1.215 schwarze 2533: n = mdoc->last;
2534: if (NULL == n->prev)
1.175 schwarze 2535: return;
1.200 schwarze 2536: if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK)
1.175 schwarze 2537: return;
1.70 schwarze 2538:
1.128 schwarze 2539: /*
1.278 ! schwarze 2540: * Don't allow `Pp' prior to a paragraph-type
! 2541: * block: `Pp' or non-compact `Bd' or `Bl'.
1.76 schwarze 2542: */
1.70 schwarze 2543:
1.278 ! schwarze 2544: if (n->prev->tok != MDOC_Pp && n->prev->tok != ROFF_br)
1.175 schwarze 2545: return;
1.215 schwarze 2546: if (n->tok == MDOC_Bl && n->norm->Bl.comp)
1.175 schwarze 2547: return;
1.215 schwarze 2548: if (n->tok == MDOC_Bd && n->norm->Bd.comp)
1.175 schwarze 2549: return;
1.215 schwarze 2550: if (n->tok == MDOC_It && n->parent->norm->Bl.comp)
1.175 schwarze 2551: return;
1.106 schwarze 2552:
1.137 schwarze 2553: mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
1.236 schwarze 2554: n->prev->line, n->prev->pos, "%s before %s",
2555: roff_name[n->prev->tok], roff_name[n->tok]);
1.215 schwarze 2556: roff_node_delete(mdoc, n->prev);
1.106 schwarze 2557: }
2558:
1.175 schwarze 2559: static void
1.106 schwarze 2560: post_par(POST_ARGS)
2561: {
1.201 schwarze 2562: struct roff_node *np;
1.106 schwarze 2563:
1.185 schwarze 2564: np = mdoc->last;
1.242 schwarze 2565: if (np->tok != ROFF_br && np->tok != ROFF_sp)
1.215 schwarze 2566: post_prevpar(mdoc);
1.160 schwarze 2567:
1.242 schwarze 2568: if (np->tok == ROFF_sp) {
1.217 schwarze 2569: if (np->child != NULL && np->child->next != NULL)
1.185 schwarze 2570: mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2571: np->child->next->line, np->child->next->pos,
2572: "sp ... %s", np->child->next->string);
2573: } else if (np->child != NULL)
2574: mandoc_vmsg(MANDOCERR_ARG_SKIP,
2575: mdoc->parse, np->line, np->pos, "%s %s",
1.236 schwarze 2576: roff_name[np->tok], np->child->string);
1.106 schwarze 2577:
1.213 schwarze 2578: if ((np = mdoc->last->prev) == NULL) {
1.137 schwarze 2579: np = mdoc->last->parent;
1.213 schwarze 2580: if (np->tok != MDOC_Sh && np->tok != MDOC_Ss)
1.175 schwarze 2581: return;
1.278 ! schwarze 2582: } else if (np->tok != MDOC_Pp &&
1.239 schwarze 2583: (mdoc->last->tok != ROFF_br ||
1.242 schwarze 2584: (np->tok != ROFF_sp && np->tok != ROFF_br)))
1.175 schwarze 2585: return;
1.70 schwarze 2586:
1.137 schwarze 2587: mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
1.236 schwarze 2588: mdoc->last->line, mdoc->last->pos, "%s after %s",
2589: roff_name[mdoc->last->tok], roff_name[np->tok]);
1.203 schwarze 2590: roff_node_delete(mdoc, mdoc->last);
1.1 kristaps 2591: }
1.76 schwarze 2592:
1.175 schwarze 2593: static void
1.76 schwarze 2594: post_dd(POST_ARGS)
2595: {
1.201 schwarze 2596: struct roff_node *n;
1.124 schwarze 2597: char *datestr;
1.76 schwarze 2598:
1.214 schwarze 2599: n = mdoc->last;
1.229 schwarze 2600: n->flags |= NODE_NOPRT;
2601:
1.214 schwarze 2602: if (mdoc->meta.date != NULL) {
2603: mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2604: n->line, n->pos, "Dd");
1.89 schwarze 2605: free(mdoc->meta.date);
1.214 schwarze 2606: } else if (mdoc->flags & MDOC_PBODY)
2607: mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2608: n->line, n->pos, "Dd");
2609: else if (mdoc->meta.title != NULL)
2610: mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2611: n->line, n->pos, "Dd after Dt");
2612: else if (mdoc->meta.os != NULL)
2613: mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2614: n->line, n->pos, "Dd after Os");
1.89 schwarze 2615:
1.213 schwarze 2616: if (n->child == NULL || n->child->string[0] == '\0') {
1.118 schwarze 2617: mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
1.252 schwarze 2618: mandoc_normdate(mdoc, NULL, n->line, n->pos);
1.229 schwarze 2619: return;
1.76 schwarze 2620: }
2621:
1.124 schwarze 2622: datestr = NULL;
1.207 schwarze 2623: deroff(&datestr, n);
1.124 schwarze 2624: if (mdoc->quick)
2625: mdoc->meta.date = datestr;
2626: else {
1.252 schwarze 2627: mdoc->meta.date = mandoc_normdate(mdoc,
1.124 schwarze 2628: datestr, n->line, n->pos);
2629: free(datestr);
1.95 schwarze 2630: }
1.76 schwarze 2631: }
2632:
1.175 schwarze 2633: static void
1.76 schwarze 2634: post_dt(POST_ARGS)
2635: {
1.201 schwarze 2636: struct roff_node *nn, *n;
1.76 schwarze 2637: const char *cp;
2638: char *p;
2639:
2640: n = mdoc->last;
1.229 schwarze 2641: n->flags |= NODE_NOPRT;
2642:
1.214 schwarze 2643: if (mdoc->flags & MDOC_PBODY) {
2644: mandoc_msg(MANDOCERR_DT_LATE, mdoc->parse,
2645: n->line, n->pos, "Dt");
1.229 schwarze 2646: return;
1.214 schwarze 2647: }
2648:
2649: if (mdoc->meta.title != NULL)
2650: mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2651: n->line, n->pos, "Dt");
2652: else if (mdoc->meta.os != NULL)
2653: mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
2654: n->line, n->pos, "Dt after Os");
1.76 schwarze 2655:
1.161 schwarze 2656: free(mdoc->meta.title);
2657: free(mdoc->meta.msec);
2658: free(mdoc->meta.vol);
2659: free(mdoc->meta.arch);
2660:
2661: mdoc->meta.title = NULL;
2662: mdoc->meta.msec = NULL;
2663: mdoc->meta.vol = NULL;
2664: mdoc->meta.arch = NULL;
1.76 schwarze 2665:
1.196 schwarze 2666: /* Mandatory first argument: title. */
1.76 schwarze 2667:
1.196 schwarze 2668: nn = n->child;
2669: if (nn == NULL || *nn->string == '\0') {
1.161 schwarze 2670: mandoc_msg(MANDOCERR_DT_NOTITLE,
2671: mdoc->parse, n->line, n->pos, "Dt");
2672: mdoc->meta.title = mandoc_strdup("UNTITLED");
1.196 schwarze 2673: } else {
2674: mdoc->meta.title = mandoc_strdup(nn->string);
2675:
2676: /* Check that all characters are uppercase. */
2677:
2678: for (p = nn->string; *p != '\0'; p++)
2679: if (islower((unsigned char)*p)) {
2680: mandoc_vmsg(MANDOCERR_TITLE_CASE,
2681: mdoc->parse, nn->line,
2682: nn->pos + (p - nn->string),
2683: "Dt %s", nn->string);
2684: break;
2685: }
1.76 schwarze 2686: }
2687:
1.226 schwarze 2688: /* Mandatory second argument: section. */
1.76 schwarze 2689:
1.196 schwarze 2690: if (nn != NULL)
2691: nn = nn->next;
1.76 schwarze 2692:
1.196 schwarze 2693: if (nn == NULL) {
1.161 schwarze 2694: mandoc_vmsg(MANDOCERR_MSEC_MISSING,
2695: mdoc->parse, n->line, n->pos,
2696: "Dt %s", mdoc->meta.title);
1.76 schwarze 2697: mdoc->meta.vol = mandoc_strdup("LOCAL");
1.229 schwarze 2698: return; /* msec and arch remain NULL. */
1.76 schwarze 2699: }
2700:
1.196 schwarze 2701: mdoc->meta.msec = mandoc_strdup(nn->string);
2702:
2703: /* Infer volume title from section number. */
1.76 schwarze 2704:
1.99 schwarze 2705: cp = mandoc_a2msec(nn->string);
1.196 schwarze 2706: if (cp == NULL) {
1.157 schwarze 2707: mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
2708: nn->line, nn->pos, "Dt ... %s", nn->string);
1.76 schwarze 2709: mdoc->meta.vol = mandoc_strdup(nn->string);
1.196 schwarze 2710: } else
2711: mdoc->meta.vol = mandoc_strdup(cp);
2712:
2713: /* Optional third argument: architecture. */
2714:
2715: if ((nn = nn->next) == NULL)
1.229 schwarze 2716: return;
1.196 schwarze 2717:
2718: for (p = nn->string; *p != '\0'; p++)
2719: *p = tolower((unsigned char)*p);
2720: mdoc->meta.arch = mandoc_strdup(nn->string);
1.76 schwarze 2721:
1.196 schwarze 2722: /* Ignore fourth and later arguments. */
1.76 schwarze 2723:
1.196 schwarze 2724: if ((nn = nn->next) != NULL)
2725: mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
2726: nn->line, nn->pos, "Dt ... %s", nn->string);
1.87 schwarze 2727: }
2728:
1.175 schwarze 2729: static void
1.87 schwarze 2730: post_bx(POST_ARGS)
2731: {
1.232 schwarze 2732: struct roff_node *n, *nch;
1.245 schwarze 2733: const char *macro;
1.248 schwarze 2734:
1.263 schwarze 2735: post_delim_nb(mdoc);
1.232 schwarze 2736:
2737: n = mdoc->last;
2738: nch = n->child;
2739:
2740: if (nch != NULL) {
1.245 schwarze 2741: macro = !strcmp(nch->string, "Open") ? "Ox" :
2742: !strcmp(nch->string, "Net") ? "Nx" :
2743: !strcmp(nch->string, "Free") ? "Fx" :
2744: !strcmp(nch->string, "DragonFly") ? "Dx" : NULL;
2745: if (macro != NULL)
2746: mandoc_msg(MANDOCERR_BX, mdoc->parse,
2747: n->line, n->pos, macro);
1.232 schwarze 2748: mdoc->last = nch;
2749: nch = nch->next;
2750: mdoc->next = ROFF_NEXT_SIBLING;
2751: roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2752: mdoc->last->flags |= NODE_NOSRC;
2753: mdoc->next = ROFF_NEXT_SIBLING;
2754: } else
2755: mdoc->next = ROFF_NEXT_CHILD;
2756: roff_word_alloc(mdoc, n->line, n->pos, "BSD");
2757: mdoc->last->flags |= NODE_NOSRC;
2758:
2759: if (nch == NULL) {
2760: mdoc->last = n;
2761: return;
2762: }
2763:
2764: roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2765: mdoc->last->flags |= NODE_NOSRC;
2766: mdoc->next = ROFF_NEXT_SIBLING;
2767: roff_word_alloc(mdoc, n->line, n->pos, "-");
2768: mdoc->last->flags |= NODE_NOSRC;
2769: roff_elem_alloc(mdoc, n->line, n->pos, MDOC_Ns);
2770: mdoc->last->flags |= NODE_NOSRC;
2771: mdoc->last = n;
1.87 schwarze 2772:
1.128 schwarze 2773: /*
1.87 schwarze 2774: * Make `Bx's second argument always start with an uppercase
2775: * letter. Groff checks if it's an "accepted" term, but we just
2776: * uppercase blindly.
2777: */
2778:
1.232 schwarze 2779: *nch->string = (char)toupper((unsigned char)*nch->string);
1.76 schwarze 2780: }
2781:
1.175 schwarze 2782: static void
1.76 schwarze 2783: post_os(POST_ARGS)
2784: {
2785: #ifndef OSNAME
2786: struct utsname utsname;
1.119 schwarze 2787: static char *defbuf;
1.76 schwarze 2788: #endif
1.201 schwarze 2789: struct roff_node *n;
1.76 schwarze 2790:
2791: n = mdoc->last;
1.229 schwarze 2792: n->flags |= NODE_NOPRT;
2793:
1.214 schwarze 2794: if (mdoc->meta.os != NULL)
2795: mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
2796: n->line, n->pos, "Os");
2797: else if (mdoc->flags & MDOC_PBODY)
2798: mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
2799: n->line, n->pos, "Os");
1.263 schwarze 2800:
2801: post_delim(mdoc);
1.76 schwarze 2802:
2803: /*
1.102 schwarze 2804: * Set the operating system by way of the `Os' macro.
2805: * The order of precedence is:
2806: * 1. the argument of the `Os' macro, unless empty
2807: * 2. the -Ios=foo command line argument, if provided
2808: * 3. -DOSNAME="\"foo\"", if provided during compilation
2809: * 4. "sysname release" from uname(3)
1.128 schwarze 2810: */
1.76 schwarze 2811:
1.102 schwarze 2812: free(mdoc->meta.os);
1.124 schwarze 2813: mdoc->meta.os = NULL;
1.207 schwarze 2814: deroff(&mdoc->meta.os, n);
1.124 schwarze 2815: if (mdoc->meta.os)
1.247 schwarze 2816: goto out;
1.119 schwarze 2817:
1.255 schwarze 2818: if (mdoc->os_s != NULL) {
2819: mdoc->meta.os = mandoc_strdup(mdoc->os_s);
1.247 schwarze 2820: goto out;
1.119 schwarze 2821: }
2822:
1.76 schwarze 2823: #ifdef OSNAME
1.119 schwarze 2824: mdoc->meta.os = mandoc_strdup(OSNAME);
1.76 schwarze 2825: #else /*!OSNAME */
1.213 schwarze 2826: if (defbuf == NULL) {
2827: if (uname(&utsname) == -1) {
1.151 schwarze 2828: mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
2829: n->line, n->pos, "Os");
1.128 schwarze 2830: defbuf = mandoc_strdup("UNKNOWN");
2831: } else
1.123 schwarze 2832: mandoc_asprintf(&defbuf, "%s %s",
2833: utsname.sysname, utsname.release);
1.119 schwarze 2834: }
2835: mdoc->meta.os = mandoc_strdup(defbuf);
1.76 schwarze 2836: #endif /*!OSNAME*/
1.247 schwarze 2837:
1.255 schwarze 2838: out:
2839: if (mdoc->meta.os_e == MANDOC_OS_OTHER) {
2840: if (strstr(mdoc->meta.os, "OpenBSD") != NULL)
2841: mdoc->meta.os_e = MANDOC_OS_OPENBSD;
2842: else if (strstr(mdoc->meta.os, "NetBSD") != NULL)
2843: mdoc->meta.os_e = MANDOC_OS_NETBSD;
2844: }
1.252 schwarze 2845:
2846: /*
2847: * This is the earliest point where we can check
2848: * Mdocdate conventions because we don't know
2849: * the operating system earlier.
2850: */
1.256 schwarze 2851:
2852: if (n->child != NULL)
2853: mandoc_vmsg(MANDOCERR_OS_ARG, mdoc->parse,
2854: n->child->line, n->child->pos,
2855: "Os %s (%s)", n->child->string,
2856: mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
2857: "OpenBSD" : "NetBSD");
1.252 schwarze 2858:
2859: while (n->tok != MDOC_Dd)
2860: if ((n = n->prev) == NULL)
2861: return;
2862: if ((n = n->child) == NULL)
2863: return;
1.253 schwarze 2864: if (strncmp(n->string, "$" "Mdocdate", 9)) {
1.255 schwarze 2865: if (mdoc->meta.os_e == MANDOC_OS_OPENBSD)
1.252 schwarze 2866: mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING,
2867: mdoc->parse, n->line, n->pos,
1.255 schwarze 2868: "Dd %s (OpenBSD)", n->string);
1.252 schwarze 2869: } else {
1.255 schwarze 2870: if (mdoc->meta.os_e == MANDOC_OS_NETBSD)
1.252 schwarze 2871: mandoc_vmsg(MANDOCERR_MDOCDATE,
2872: mdoc->parse, n->line, n->pos,
1.255 schwarze 2873: "Dd %s (NetBSD)", n->string);
1.252 schwarze 2874: }
1.76 schwarze 2875: }
2876:
1.214 schwarze 2877: enum roff_sec
2878: mdoc_a2sec(const char *p)
1.91 schwarze 2879: {
2880: int i;
2881:
1.128 schwarze 2882: for (i = 0; i < (int)SEC__MAX; i++)
1.91 schwarze 2883: if (secnames[i] && 0 == strcmp(p, secnames[i]))
1.210 schwarze 2884: return (enum roff_sec)i;
1.91 schwarze 2885:
1.210 schwarze 2886: return SEC_CUSTOM;
1.91 schwarze 2887: }
2888:
2889: static size_t
1.236 schwarze 2890: macro2len(enum roff_tok macro)
1.91 schwarze 2891: {
2892:
2893: switch (macro) {
1.128 schwarze 2894: case MDOC_Ad:
1.210 schwarze 2895: return 12;
1.128 schwarze 2896: case MDOC_Ao:
1.210 schwarze 2897: return 12;
1.128 schwarze 2898: case MDOC_An:
1.210 schwarze 2899: return 12;
1.128 schwarze 2900: case MDOC_Aq:
1.210 schwarze 2901: return 12;
1.128 schwarze 2902: case MDOC_Ar:
1.210 schwarze 2903: return 12;
1.128 schwarze 2904: case MDOC_Bo:
1.210 schwarze 2905: return 12;
1.128 schwarze 2906: case MDOC_Bq:
1.210 schwarze 2907: return 12;
1.128 schwarze 2908: case MDOC_Cd:
1.210 schwarze 2909: return 12;
1.128 schwarze 2910: case MDOC_Cm:
1.210 schwarze 2911: return 10;
1.128 schwarze 2912: case MDOC_Do:
1.210 schwarze 2913: return 10;
1.128 schwarze 2914: case MDOC_Dq:
1.210 schwarze 2915: return 12;
1.128 schwarze 2916: case MDOC_Dv:
1.210 schwarze 2917: return 12;
1.128 schwarze 2918: case MDOC_Eo:
1.210 schwarze 2919: return 12;
1.128 schwarze 2920: case MDOC_Em:
1.210 schwarze 2921: return 10;
1.128 schwarze 2922: case MDOC_Er:
1.210 schwarze 2923: return 17;
1.128 schwarze 2924: case MDOC_Ev:
1.210 schwarze 2925: return 15;
1.128 schwarze 2926: case MDOC_Fa:
1.210 schwarze 2927: return 12;
1.128 schwarze 2928: case MDOC_Fl:
1.210 schwarze 2929: return 10;
1.128 schwarze 2930: case MDOC_Fo:
1.210 schwarze 2931: return 16;
1.128 schwarze 2932: case MDOC_Fn:
1.210 schwarze 2933: return 16;
1.128 schwarze 2934: case MDOC_Ic:
1.210 schwarze 2935: return 10;
1.128 schwarze 2936: case MDOC_Li:
1.210 schwarze 2937: return 16;
1.128 schwarze 2938: case MDOC_Ms:
1.210 schwarze 2939: return 6;
1.128 schwarze 2940: case MDOC_Nm:
1.210 schwarze 2941: return 10;
1.128 schwarze 2942: case MDOC_No:
1.210 schwarze 2943: return 12;
1.128 schwarze 2944: case MDOC_Oo:
1.210 schwarze 2945: return 10;
1.128 schwarze 2946: case MDOC_Op:
1.210 schwarze 2947: return 14;
1.128 schwarze 2948: case MDOC_Pa:
1.210 schwarze 2949: return 32;
1.128 schwarze 2950: case MDOC_Pf:
1.210 schwarze 2951: return 12;
1.128 schwarze 2952: case MDOC_Po:
1.210 schwarze 2953: return 12;
1.128 schwarze 2954: case MDOC_Pq:
1.210 schwarze 2955: return 12;
1.128 schwarze 2956: case MDOC_Ql:
1.210 schwarze 2957: return 16;
1.128 schwarze 2958: case MDOC_Qo:
1.210 schwarze 2959: return 12;
1.128 schwarze 2960: case MDOC_So:
1.210 schwarze 2961: return 12;
1.128 schwarze 2962: case MDOC_Sq:
1.210 schwarze 2963: return 12;
1.128 schwarze 2964: case MDOC_Sy:
1.210 schwarze 2965: return 6;
1.128 schwarze 2966: case MDOC_Sx:
1.210 schwarze 2967: return 16;
1.128 schwarze 2968: case MDOC_Tn:
1.210 schwarze 2969: return 10;
1.128 schwarze 2970: case MDOC_Va:
1.210 schwarze 2971: return 12;
1.128 schwarze 2972: case MDOC_Vt:
1.210 schwarze 2973: return 12;
1.128 schwarze 2974: case MDOC_Xr:
1.210 schwarze 2975: return 10;
1.91 schwarze 2976: default:
2977: break;
2978: };
1.210 schwarze 2979: return 0;
1.91 schwarze 2980: }