1 /* $Id: mdoc_validate.c,v 1.352 2017/08/02 13:29:04 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
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.
18 */
19 #include "config.h"
20
21 #include <sys/types.h>
22 #ifndef OSNAME
23 #include <sys/utsname.h>
24 #endif
36 #include "mandoc_xr.h"
37 #include "roff.h"
38 #include "mdoc.h"
39 #include "libmandoc.h"
40 #include "roff_int.h"
41 #include "libmdoc.h"
42
43 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
44
45 #define POST_ARGS struct roff_man *mdoc
46
47 enum check_ineq {
48 CHECK_LT,
49 CHECK_GT,
50 CHECK_EQ
51 };
52
53 typedef void (*v_post)(POST_ARGS);
54
55 static int build_list(struct roff_man *, int);
56 static void check_text(struct roff_man *, int, int, char *);
57 static void check_argv(struct roff_man *,
58 struct roff_node *, struct mdoc_argv *);
59 static void check_args(struct roff_man *, struct roff_node *);
60 static void check_toptext(struct roff_man *, int, int, const char *);
61 static int child_an(const struct roff_node *);
62 static size_t macro2len(enum roff_tok);
63 static void rewrite_macro2len(struct roff_man *, char **);
64 static int similar(const char *, const char *);
65
66 static void post_an(POST_ARGS);
67 static void post_an_norm(POST_ARGS);
68 static void post_at(POST_ARGS);
69 static void post_bd(POST_ARGS);
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);
75 static void post_bl_norm(POST_ARGS);
76 static void post_bx(POST_ARGS);
77 static void post_defaults(POST_ARGS);
78 static void post_display(POST_ARGS);
79 static void post_dd(POST_ARGS);
152 post_delim_nb, /* Op */
153 post_obsolete, /* Ot */
154 post_defaults, /* Pa */
155 post_rv, /* Rv */
156 post_st, /* St */
157 post_delim_nb, /* Va */
158 post_delim_nb, /* Vt */
159 post_xr, /* Xr */
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 */
172 post_delim_nb, /* Ao */
173 post_delim_nb, /* Aq */
174 post_at, /* At */
175 NULL, /* Bc */
176 post_bf, /* Bf */
177 post_delim_nb, /* Bo */
178 NULL, /* Bq */
179 post_xx, /* Bsx */
180 post_bx, /* Bx */
181 post_obsolete, /* Db */
182 NULL, /* Dc */
183 NULL, /* Do */
184 NULL, /* Dq */
185 NULL, /* Ec */
186 NULL, /* Ef */
187 post_delim_nb, /* Em */
188 NULL, /* Eo */
189 post_xx, /* Fx */
190 post_delim_nb, /* Ms */
191 NULL, /* No */
192 post_ns, /* Ns */
193 post_xx, /* Nx */
194 post_xx, /* Ox */
195 NULL, /* Pc */
196 NULL, /* Pf */
197 post_delim_nb, /* Po */
198 post_delim_nb, /* Pq */
199 NULL, /* Qc */
200 post_delim_nb, /* Ql */
201 post_delim_nb, /* Qo */
202 post_delim_nb, /* Qq */
203 NULL, /* Re */
204 post_rs, /* Rs */
205 NULL, /* Sc */
206 post_delim_nb, /* So */
207 post_delim_nb, /* Sq */
208 post_sm, /* Sm */
209 post_sx, /* Sx */
210 post_delim_nb, /* Sy */
211 post_useless, /* Tn */
212 post_xx, /* Ux */
213 NULL, /* Xc */
214 NULL, /* Xo */
215 post_fo, /* Fo */
216 NULL, /* Fc */
217 post_delim_nb, /* Oo */
218 NULL, /* Oc */
219 post_bk, /* Bk */
220 NULL, /* Ek */
221 post_eoln, /* Bt */
222 post_obsolete, /* Hf */
223 post_obsolete, /* Fr */
224 post_eoln, /* Ud */
225 post_lb, /* Lb */
226 post_par, /* Lp */
227 post_delim_nb, /* Lk */
228 post_defaults, /* Mt */
229 post_delim_nb, /* Brq */
230 post_delim_nb, /* Bro */
231 NULL, /* Brc */
232 NULL, /* %C */
233 post_es, /* Es */
234 post_en, /* En */
235 post_xx, /* Dx */
236 NULL, /* %Q */
237 NULL, /* %U */
238 NULL, /* Ta */
239 };
240 static const v_post *const mdoc_valids = __mdoc_valids - MDOC_Dd;
241
242 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
243
244 static const enum roff_tok rsord[RSORD_MAX] = {
245 MDOC__A,
246 MDOC__T,
247 MDOC__B,
248 MDOC__I,
249 MDOC__J,
250 MDOC__R,
271 "FILES",
272 "EXIT STATUS",
273 "EXAMPLES",
274 "DIAGNOSTICS",
275 "COMPATIBILITY",
276 "ERRORS",
277 "SEE ALSO",
278 "STANDARDS",
279 "HISTORY",
280 "AUTHORS",
281 "CAVEATS",
282 "BUGS",
283 "SECURITY CONSIDERATIONS",
284 NULL
285 };
286
287
288 void
289 mdoc_node_validate(struct roff_man *mdoc)
290 {
291 struct roff_node *n;
292 const v_post *p;
293
294 n = mdoc->last;
295 mdoc->last = mdoc->last->child;
296 while (mdoc->last != NULL) {
297 mdoc_node_validate(mdoc);
298 if (mdoc->last == n)
299 mdoc->last = mdoc->last->child;
300 else
301 mdoc->last = mdoc->last->next;
302 }
303
304 mdoc->last = n;
305 mdoc->next = ROFF_NEXT_SIBLING;
306 switch (n->type) {
307 case ROFFT_TEXT:
308 if (n->sec != SEC_SYNOPSIS ||
309 (n->parent->tok != MDOC_Cd && n->parent->tok != MDOC_Fd))
310 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)))
315 check_toptext(mdoc, n->line, n->pos, n->string);
316 break;
317 case ROFFT_EQN:
318 case ROFFT_TBL:
319 break;
320 case ROFFT_ROOT:
321 post_root(mdoc);
322 break;
323 default:
324 check_args(mdoc, mdoc->last);
325
326 /*
327 * Closing delimiters are not special at the
328 * beginning of a block, opening delimiters
329 * are not special at the end.
330 */
331
332 if (n->child != NULL)
333 n->child->flags &= ~NODE_DELIMC;
334 if (n->last != NULL)
335 n->last->flags &= ~NODE_DELIMO;
336
378 int i;
379
380 for (i = 0; i < (int)v->sz; i++)
381 check_text(mdoc, v->line, v->pos, v->value[i]);
382 }
383
384 static void
385 check_text(struct roff_man *mdoc, int ln, int pos, char *p)
386 {
387 char *cp;
388
389 if (MDOC_LITERAL & mdoc->flags)
390 return;
391
392 for (cp = p; NULL != (p = strchr(p, '\t')); p++)
393 mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
394 ln, pos + (int)(p - cp), NULL);
395 }
396
397 static void
398 check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
399 {
400 const char *cp, *cpr;
401
402 if (*p == '\0')
403 return;
404
405 if ((cp = strstr(p, "OpenBSD")) != NULL)
406 mandoc_msg(MANDOCERR_BX, mdoc->parse,
407 ln, pos + (cp - p), "Ox");
408 if ((cp = strstr(p, "NetBSD")) != NULL)
409 mandoc_msg(MANDOCERR_BX, mdoc->parse,
410 ln, pos + (cp - p), "Nx");
411 if ((cp = strstr(p, "FreeBSD")) != NULL)
412 mandoc_msg(MANDOCERR_BX, mdoc->parse,
413 ln, pos + (cp - p), "Fx");
414 if ((cp = strstr(p, "DragonFly")) != NULL)
415 mandoc_msg(MANDOCERR_BX, mdoc->parse,
416 ln, pos + (cp - p), "Dx");
417
513 return;
514 break;
515 case ']':
516 for (cp = lc; cp >= nch->string; cp--)
517 if (*cp == '[')
518 return;
519 break;
520 case '|':
521 if (lc == nch->string + 1 && lc[-1] == '|')
522 return;
523 default:
524 break;
525 }
526
527 /* Exactly two non-alphanumeric bytes. */
528 if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
529 return;
530
531 /* At least three alphabetic words with a sentence ending. */
532 if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
533 tok == MDOC_Li || tok == MDOC_Po || tok == MDOC_Pq ||
534 tok == MDOC_Sy)) {
535 nw = 0;
536 for (cp = lc - 1; cp >= nch->string; cp--) {
537 if (*cp == ' ') {
538 nw++;
539 if (cp > nch->string && cp[-1] == ',')
540 cp--;
541 } else if (isalpha((unsigned int)*cp)) {
542 if (nw > 1)
543 return;
544 } else
545 break;
546 }
547 }
548
549 mandoc_vmsg(MANDOCERR_DELIM_NB, mdoc->parse,
550 nch->line, nch->pos + (lc - nch->string),
551 "%s%s %s", roff_name[tok],
552 nch == mdoc->last->child ? "" : " ...", nch->string);
553 }
554
930
931 post_delim_nb(mdoc);
932
933 n = mdoc->last;
934 assert(n->child->type == ROFFT_TEXT);
935 mdoc->next = ROFF_NEXT_CHILD;
936
937 if ((p = mdoc_a2lib(n->child->string)) != NULL) {
938 n->child->flags |= NODE_NOPRT;
939 roff_word_alloc(mdoc, n->line, n->pos, p);
940 mdoc->last->flags = NODE_NOSRC;
941 mdoc->last = n;
942 return;
943 }
944
945 mandoc_vmsg(MANDOCERR_LB_BAD, mdoc->parse, n->child->line,
946 n->child->pos, "Lb %s", n->child->string);
947
948 roff_word_alloc(mdoc, n->line, n->pos, "library");
949 mdoc->last->flags = NODE_NOSRC;
950 roff_word_alloc(mdoc, n->line, n->pos, "\\(Lq");
951 mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
952 mdoc->last = mdoc->last->next;
953 roff_word_alloc(mdoc, n->line, n->pos, "\\(Rq");
954 mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
955 mdoc->last = n;
956 }
957
958 static void
959 post_rv(POST_ARGS)
960 {
961 struct roff_node *n;
962 int ic;
963
964 post_std(mdoc);
965
966 n = mdoc->last;
967 mdoc->next = ROFF_NEXT_CHILD;
968 if (n->child != NULL) {
969 roff_word_alloc(mdoc, n->line, n->pos, "The");
970 mdoc->last->flags |= NODE_NOSRC;
971 ic = build_list(mdoc, MDOC_Fn);
972 roff_word_alloc(mdoc, n->line, n->pos,
973 ic > 1 ? "functions return" : "function returns");
1897
1898 if (mdoc->meta.vol == NULL)
1899 mdoc->meta.vol = mandoc_strdup("LOCAL");
1900
1901 if (mdoc->meta.os == NULL) {
1902 mandoc_msg(MANDOCERR_OS_MISSING,
1903 mdoc->parse, 0, 0, NULL);
1904 mdoc->meta.os = mandoc_strdup("");
1905 } else if (mdoc->meta.os_e &&
1906 (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
1907 mandoc_msg(MANDOCERR_RCS_MISSING, mdoc->parse, 0, 0,
1908 mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1909 "(OpenBSD)" : "(NetBSD)");
1910
1911 if (mdoc->meta.arch != NULL &&
1912 (arch = arches[mdoc->meta.os_e]) != NULL) {
1913 while (*arch != NULL && strcmp(*arch, mdoc->meta.arch))
1914 arch++;
1915 if (*arch == NULL) {
1916 n = mdoc->first->child;
1917 while (n->tok != MDOC_Dt)
1918 n = n->next;
1919 n = n->child->next->next;
1920 mandoc_vmsg(MANDOCERR_ARCH_BAD,
1921 mdoc->parse, n->line, n->pos,
1922 "Dt ... %s %s", mdoc->meta.arch,
1923 mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1924 "(OpenBSD)" : "(NetBSD)");
1925 }
1926 }
1927
1928 /* Check that we begin with a proper `Sh'. */
1929
1930 n = mdoc->first->child;
1931 while (n != NULL && n->tok >= MDOC_Dd &&
1932 mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
1933 n = n->next;
1934
1935 if (n == NULL)
1936 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1937 else if (n->tok != MDOC_Sh)
1938 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
1939 n->line, n->pos, roff_name[n->tok]);
1940 }
1941
1942 static void
1943 post_rs(POST_ARGS)
1944 {
1945 struct roff_node *np, *nch, *next, *prev;
1946 int i, j;
1947
1948 np = mdoc->last;
1949
1950 if (np->type != ROFFT_BODY)
1951 return;
1952
|
1 /* $Id: mdoc_validate.c,v 1.360 2018/08/01 16:00:58 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
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.
18 */
19 #include "config.h"
20
21 #include <sys/types.h>
22 #ifndef OSNAME
23 #include <sys/utsname.h>
24 #endif
36 #include "mandoc_xr.h"
37 #include "roff.h"
38 #include "mdoc.h"
39 #include "libmandoc.h"
40 #include "roff_int.h"
41 #include "libmdoc.h"
42
43 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
44
45 #define POST_ARGS struct roff_man *mdoc
46
47 enum check_ineq {
48 CHECK_LT,
49 CHECK_GT,
50 CHECK_EQ
51 };
52
53 typedef void (*v_post)(POST_ARGS);
54
55 static int build_list(struct roff_man *, int);
56 static void check_argv(struct roff_man *,
57 struct roff_node *, struct mdoc_argv *);
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 *);
61 static void check_toptext(struct roff_man *, int, int, const char *);
62 static int child_an(const struct roff_node *);
63 static size_t macro2len(enum roff_tok);
64 static void rewrite_macro2len(struct roff_man *, char **);
65 static int similar(const char *, const char *);
66
67 static void post_an(POST_ARGS);
68 static void post_an_norm(POST_ARGS);
69 static void post_at(POST_ARGS);
70 static void post_bd(POST_ARGS);
71 static void post_bf(POST_ARGS);
72 static void post_bk(POST_ARGS);
73 static void post_bl(POST_ARGS);
74 static void post_bl_block(POST_ARGS);
75 static void post_bl_head(POST_ARGS);
76 static void post_bl_norm(POST_ARGS);
77 static void post_bx(POST_ARGS);
78 static void post_defaults(POST_ARGS);
79 static void post_display(POST_ARGS);
80 static void post_dd(POST_ARGS);
153 post_delim_nb, /* Op */
154 post_obsolete, /* Ot */
155 post_defaults, /* Pa */
156 post_rv, /* Rv */
157 post_st, /* St */
158 post_delim_nb, /* Va */
159 post_delim_nb, /* Vt */
160 post_xr, /* Xr */
161 NULL, /* %A */
162 post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */
163 NULL, /* %D */
164 NULL, /* %I */
165 NULL, /* %J */
166 post_hyph, /* %N */
167 post_hyph, /* %O */
168 NULL, /* %P */
169 post_hyph, /* %R */
170 post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */
171 NULL, /* %V */
172 NULL, /* Ac */
173 NULL, /* Ao */
174 post_delim_nb, /* Aq */
175 post_at, /* At */
176 NULL, /* Bc */
177 post_bf, /* Bf */
178 NULL, /* Bo */
179 NULL, /* Bq */
180 post_xx, /* Bsx */
181 post_bx, /* Bx */
182 post_obsolete, /* Db */
183 NULL, /* Dc */
184 NULL, /* Do */
185 NULL, /* Dq */
186 NULL, /* Ec */
187 NULL, /* Ef */
188 post_delim_nb, /* Em */
189 NULL, /* Eo */
190 post_xx, /* Fx */
191 post_delim_nb, /* Ms */
192 NULL, /* No */
193 post_ns, /* Ns */
194 post_xx, /* Nx */
195 post_xx, /* Ox */
196 NULL, /* Pc */
197 NULL, /* Pf */
198 NULL, /* Po */
199 post_delim_nb, /* Pq */
200 NULL, /* Qc */
201 post_delim_nb, /* Ql */
202 NULL, /* Qo */
203 post_delim_nb, /* Qq */
204 NULL, /* Re */
205 post_rs, /* Rs */
206 NULL, /* Sc */
207 NULL, /* So */
208 post_delim_nb, /* Sq */
209 post_sm, /* Sm */
210 post_sx, /* Sx */
211 post_delim_nb, /* Sy */
212 post_useless, /* Tn */
213 post_xx, /* Ux */
214 NULL, /* Xc */
215 NULL, /* Xo */
216 post_fo, /* Fo */
217 NULL, /* Fc */
218 NULL, /* Oo */
219 NULL, /* Oc */
220 post_bk, /* Bk */
221 NULL, /* Ek */
222 post_eoln, /* Bt */
223 post_obsolete, /* Hf */
224 post_obsolete, /* Fr */
225 post_eoln, /* Ud */
226 post_lb, /* Lb */
227 post_par, /* Lp */
228 post_delim_nb, /* Lk */
229 post_defaults, /* Mt */
230 post_delim_nb, /* Brq */
231 NULL, /* Bro */
232 NULL, /* Brc */
233 NULL, /* %C */
234 post_es, /* Es */
235 post_en, /* En */
236 post_xx, /* Dx */
237 NULL, /* %Q */
238 NULL, /* %U */
239 NULL, /* Ta */
240 };
241 static const v_post *const mdoc_valids = __mdoc_valids - MDOC_Dd;
242
243 #define RSORD_MAX 14 /* Number of `Rs' blocks. */
244
245 static const enum roff_tok rsord[RSORD_MAX] = {
246 MDOC__A,
247 MDOC__T,
248 MDOC__B,
249 MDOC__I,
250 MDOC__J,
251 MDOC__R,
272 "FILES",
273 "EXIT STATUS",
274 "EXAMPLES",
275 "DIAGNOSTICS",
276 "COMPATIBILITY",
277 "ERRORS",
278 "SEE ALSO",
279 "STANDARDS",
280 "HISTORY",
281 "AUTHORS",
282 "CAVEATS",
283 "BUGS",
284 "SECURITY CONSIDERATIONS",
285 NULL
286 };
287
288
289 void
290 mdoc_node_validate(struct roff_man *mdoc)
291 {
292 struct roff_node *n, *np;
293 const v_post *p;
294
295 n = mdoc->last;
296 mdoc->last = mdoc->last->child;
297 while (mdoc->last != NULL) {
298 mdoc_node_validate(mdoc);
299 if (mdoc->last == n)
300 mdoc->last = mdoc->last->child;
301 else
302 mdoc->last = mdoc->last->next;
303 }
304
305 mdoc->last = n;
306 mdoc->next = ROFF_NEXT_SIBLING;
307 switch (n->type) {
308 case ROFFT_TEXT:
309 np = n->parent;
310 if (n->sec != SEC_SYNOPSIS ||
311 (np->tok != MDOC_Cd && np->tok != MDOC_Fd))
312 check_text(mdoc, n->line, n->pos, n->string);
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)))
321 check_toptext(mdoc, n->line, n->pos, n->string);
322 break;
323 case ROFFT_COMMENT:
324 case ROFFT_EQN:
325 case ROFFT_TBL:
326 break;
327 case ROFFT_ROOT:
328 post_root(mdoc);
329 break;
330 default:
331 check_args(mdoc, mdoc->last);
332
333 /*
334 * Closing delimiters are not special at the
335 * beginning of a block, opening delimiters
336 * are not special at the end.
337 */
338
339 if (n->child != NULL)
340 n->child->flags &= ~NODE_DELIMC;
341 if (n->last != NULL)
342 n->last->flags &= ~NODE_DELIMO;
343
385 int i;
386
387 for (i = 0; i < (int)v->sz; i++)
388 check_text(mdoc, v->line, v->pos, v->value[i]);
389 }
390
391 static void
392 check_text(struct roff_man *mdoc, int ln, int pos, char *p)
393 {
394 char *cp;
395
396 if (MDOC_LITERAL & mdoc->flags)
397 return;
398
399 for (cp = p; NULL != (p = strchr(p, '\t')); p++)
400 mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
401 ln, pos + (int)(p - cp), NULL);
402 }
403
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
456 check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p)
457 {
458 const char *cp, *cpr;
459
460 if (*p == '\0')
461 return;
462
463 if ((cp = strstr(p, "OpenBSD")) != NULL)
464 mandoc_msg(MANDOCERR_BX, mdoc->parse,
465 ln, pos + (cp - p), "Ox");
466 if ((cp = strstr(p, "NetBSD")) != NULL)
467 mandoc_msg(MANDOCERR_BX, mdoc->parse,
468 ln, pos + (cp - p), "Nx");
469 if ((cp = strstr(p, "FreeBSD")) != NULL)
470 mandoc_msg(MANDOCERR_BX, mdoc->parse,
471 ln, pos + (cp - p), "Fx");
472 if ((cp = strstr(p, "DragonFly")) != NULL)
473 mandoc_msg(MANDOCERR_BX, mdoc->parse,
474 ln, pos + (cp - p), "Dx");
475
571 return;
572 break;
573 case ']':
574 for (cp = lc; cp >= nch->string; cp--)
575 if (*cp == '[')
576 return;
577 break;
578 case '|':
579 if (lc == nch->string + 1 && lc[-1] == '|')
580 return;
581 default:
582 break;
583 }
584
585 /* Exactly two non-alphanumeric bytes. */
586 if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1]))
587 return;
588
589 /* At least three alphabetic words with a sentence ending. */
590 if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em ||
591 tok == MDOC_Li || tok == MDOC_Pq || tok == MDOC_Sy)) {
592 nw = 0;
593 for (cp = lc - 1; cp >= nch->string; cp--) {
594 if (*cp == ' ') {
595 nw++;
596 if (cp > nch->string && cp[-1] == ',')
597 cp--;
598 } else if (isalpha((unsigned int)*cp)) {
599 if (nw > 1)
600 return;
601 } else
602 break;
603 }
604 }
605
606 mandoc_vmsg(MANDOCERR_DELIM_NB, mdoc->parse,
607 nch->line, nch->pos + (lc - nch->string),
608 "%s%s %s", roff_name[tok],
609 nch == mdoc->last->child ? "" : " ...", nch->string);
610 }
611
987
988 post_delim_nb(mdoc);
989
990 n = mdoc->last;
991 assert(n->child->type == ROFFT_TEXT);
992 mdoc->next = ROFF_NEXT_CHILD;
993
994 if ((p = mdoc_a2lib(n->child->string)) != NULL) {
995 n->child->flags |= NODE_NOPRT;
996 roff_word_alloc(mdoc, n->line, n->pos, p);
997 mdoc->last->flags = NODE_NOSRC;
998 mdoc->last = n;
999 return;
1000 }
1001
1002 mandoc_vmsg(MANDOCERR_LB_BAD, mdoc->parse, n->child->line,
1003 n->child->pos, "Lb %s", n->child->string);
1004
1005 roff_word_alloc(mdoc, n->line, n->pos, "library");
1006 mdoc->last->flags = NODE_NOSRC;
1007 roff_word_alloc(mdoc, n->line, n->pos, "\\(lq");
1008 mdoc->last->flags = NODE_DELIMO | NODE_NOSRC;
1009 mdoc->last = mdoc->last->next;
1010 roff_word_alloc(mdoc, n->line, n->pos, "\\(rq");
1011 mdoc->last->flags = NODE_DELIMC | NODE_NOSRC;
1012 mdoc->last = n;
1013 }
1014
1015 static void
1016 post_rv(POST_ARGS)
1017 {
1018 struct roff_node *n;
1019 int ic;
1020
1021 post_std(mdoc);
1022
1023 n = mdoc->last;
1024 mdoc->next = ROFF_NEXT_CHILD;
1025 if (n->child != NULL) {
1026 roff_word_alloc(mdoc, n->line, n->pos, "The");
1027 mdoc->last->flags |= NODE_NOSRC;
1028 ic = build_list(mdoc, MDOC_Fn);
1029 roff_word_alloc(mdoc, n->line, n->pos,
1030 ic > 1 ? "functions return" : "function returns");
1954
1955 if (mdoc->meta.vol == NULL)
1956 mdoc->meta.vol = mandoc_strdup("LOCAL");
1957
1958 if (mdoc->meta.os == NULL) {
1959 mandoc_msg(MANDOCERR_OS_MISSING,
1960 mdoc->parse, 0, 0, NULL);
1961 mdoc->meta.os = mandoc_strdup("");
1962 } else if (mdoc->meta.os_e &&
1963 (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0)
1964 mandoc_msg(MANDOCERR_RCS_MISSING, mdoc->parse, 0, 0,
1965 mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1966 "(OpenBSD)" : "(NetBSD)");
1967
1968 if (mdoc->meta.arch != NULL &&
1969 (arch = arches[mdoc->meta.os_e]) != NULL) {
1970 while (*arch != NULL && strcmp(*arch, mdoc->meta.arch))
1971 arch++;
1972 if (*arch == NULL) {
1973 n = mdoc->first->child;
1974 while (n->tok != MDOC_Dt ||
1975 n->child == NULL ||
1976 n->child->next == NULL ||
1977 n->child->next->next == NULL)
1978 n = n->next;
1979 n = n->child->next->next;
1980 mandoc_vmsg(MANDOCERR_ARCH_BAD,
1981 mdoc->parse, n->line, n->pos,
1982 "Dt ... %s %s", mdoc->meta.arch,
1983 mdoc->meta.os_e == MANDOC_OS_OPENBSD ?
1984 "(OpenBSD)" : "(NetBSD)");
1985 }
1986 }
1987
1988 /* Check that we begin with a proper `Sh'. */
1989
1990 n = mdoc->first->child;
1991 while (n != NULL &&
1992 (n->type == ROFFT_COMMENT ||
1993 (n->tok >= MDOC_Dd &&
1994 mdoc_macros[n->tok].flags & MDOC_PROLOGUE)))
1995 n = n->next;
1996
1997 if (n == NULL)
1998 mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
1999 else if (n->tok != MDOC_Sh)
2000 mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
2001 n->line, n->pos, roff_name[n->tok]);
2002 }
2003
2004 static void
2005 post_rs(POST_ARGS)
2006 {
2007 struct roff_node *np, *nch, *next, *prev;
2008 int i, j;
2009
2010 np = mdoc->last;
2011
2012 if (np->type != ROFFT_BODY)
2013 return;
2014
|