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