mandoc 0.0.1

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