man_validate.c revision 1.1.1.17 1 1.1.1.16 christos /* $OpenBSD$ */
2 1.1 joerg /*
3 1.1.1.12 joerg * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps (at) bsd.lv>
4 1.1.1.17 christos * Copyright (c) 2010, 2012-2016 Ingo Schwarze <schwarze (at) openbsd.org>
5 1.1 joerg *
6 1.1 joerg * Permission to use, copy, modify, and distribute this software for any
7 1.1 joerg * purpose with or without fee is hereby granted, provided that the above
8 1.1 joerg * copyright notice and this permission notice appear in all copies.
9 1.1 joerg *
10 1.1.1.17 christos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 1.1 joerg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 1.1.1.17 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13 1.1 joerg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 1.1 joerg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 1.1 joerg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 1.1 joerg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 1.1 joerg */
18 1.1.1.4 joerg #include "config.h"
19 1.1.1.4 joerg
20 1.1 joerg #include <sys/types.h>
21 1.1 joerg
22 1.1 joerg #include <assert.h>
23 1.1 joerg #include <ctype.h>
24 1.1 joerg #include <errno.h>
25 1.1 joerg #include <limits.h>
26 1.1 joerg #include <stdarg.h>
27 1.1 joerg #include <stdlib.h>
28 1.1.1.10 joerg #include <string.h>
29 1.1.1.11 joerg #include <time.h>
30 1.1 joerg
31 1.1.1.16 christos #include "mandoc_aux.h"
32 1.1.1.17 christos #include "mandoc.h"
33 1.1.1.17 christos #include "roff.h"
34 1.1.1.17 christos #include "man.h"
35 1.1 joerg #include "libmandoc.h"
36 1.1.1.17 christos #include "roff_int.h"
37 1.1.1.17 christos #include "libman.h"
38 1.1 joerg
39 1.1.1.17 christos #define CHKARGS struct roff_man *man, struct roff_node *n
40 1.1 joerg
41 1.1.1.16 christos typedef void (*v_check)(CHKARGS);
42 1.1 joerg
43 1.1.1.16 christos static void check_par(CHKARGS);
44 1.1.1.16 christos static void check_part(CHKARGS);
45 1.1.1.16 christos static void check_root(CHKARGS);
46 1.1.1.13 joerg static void check_text(CHKARGS);
47 1.1 joerg
48 1.1.1.16 christos static void post_AT(CHKARGS);
49 1.1.1.16 christos static void post_IP(CHKARGS);
50 1.1.1.16 christos static void post_vs(CHKARGS);
51 1.1.1.16 christos static void post_ft(CHKARGS);
52 1.1.1.16 christos static void post_OP(CHKARGS);
53 1.1.1.16 christos static void post_TH(CHKARGS);
54 1.1.1.16 christos static void post_UC(CHKARGS);
55 1.1.1.16 christos static void post_UR(CHKARGS);
56 1.1.1.16 christos
57 1.1.1.16 christos static v_check man_valids[MAN_MAX] = {
58 1.1.1.16 christos post_vs, /* br */
59 1.1.1.16 christos post_TH, /* TH */
60 1.1.1.16 christos NULL, /* SH */
61 1.1.1.16 christos NULL, /* SS */
62 1.1.1.16 christos NULL, /* TP */
63 1.1.1.16 christos check_par, /* LP */
64 1.1.1.16 christos check_par, /* PP */
65 1.1.1.16 christos check_par, /* P */
66 1.1.1.16 christos post_IP, /* IP */
67 1.1.1.16 christos NULL, /* HP */
68 1.1.1.16 christos NULL, /* SM */
69 1.1.1.16 christos NULL, /* SB */
70 1.1.1.16 christos NULL, /* BI */
71 1.1.1.16 christos NULL, /* IB */
72 1.1.1.16 christos NULL, /* BR */
73 1.1.1.16 christos NULL, /* RB */
74 1.1.1.16 christos NULL, /* R */
75 1.1.1.16 christos NULL, /* B */
76 1.1.1.16 christos NULL, /* I */
77 1.1.1.16 christos NULL, /* IR */
78 1.1.1.16 christos NULL, /* RI */
79 1.1.1.16 christos post_vs, /* sp */
80 1.1.1.17 christos NULL, /* nf */
81 1.1.1.17 christos NULL, /* fi */
82 1.1.1.16 christos NULL, /* RE */
83 1.1.1.16 christos check_part, /* RS */
84 1.1.1.16 christos NULL, /* DT */
85 1.1.1.16 christos post_UC, /* UC */
86 1.1.1.16 christos NULL, /* PD */
87 1.1.1.16 christos post_AT, /* AT */
88 1.1.1.16 christos NULL, /* in */
89 1.1.1.16 christos post_ft, /* ft */
90 1.1.1.16 christos post_OP, /* OP */
91 1.1.1.17 christos NULL, /* EX */
92 1.1.1.17 christos NULL, /* EE */
93 1.1.1.16 christos post_UR, /* UR */
94 1.1.1.16 christos NULL, /* UE */
95 1.1.1.16 christos NULL, /* ll */
96 1.1 joerg };
97 1.1 joerg
98 1.1 joerg
99 1.1.1.16 christos void
100 1.1.1.17 christos man_node_validate(struct roff_man *man)
101 1.1 joerg {
102 1.1.1.17 christos struct roff_node *n;
103 1.1 joerg v_check *cp;
104 1.1 joerg
105 1.1.1.16 christos n = man->last;
106 1.1.1.17 christos man->last = man->last->child;
107 1.1.1.17 christos while (man->last != NULL) {
108 1.1.1.17 christos man_node_validate(man);
109 1.1.1.17 christos if (man->last == n)
110 1.1.1.17 christos man->last = man->last->child;
111 1.1.1.17 christos else
112 1.1.1.17 christos man->last = man->last->next;
113 1.1.1.17 christos }
114 1.1.1.16 christos
115 1.1.1.17 christos man->last = n;
116 1.1.1.17 christos man->next = ROFF_NEXT_SIBLING;
117 1.1.1.11 joerg switch (n->type) {
118 1.1.1.17 christos case ROFFT_TEXT:
119 1.1.1.16 christos check_text(man, n);
120 1.1.1.11 joerg break;
121 1.1.1.17 christos case ROFFT_ROOT:
122 1.1.1.16 christos check_root(man, n);
123 1.1.1.16 christos break;
124 1.1.1.17 christos case ROFFT_EQN:
125 1.1.1.17 christos case ROFFT_TBL:
126 1.1.1.16 christos break;
127 1.1 joerg default:
128 1.1.1.16 christos cp = man_valids + n->tok;
129 1.1.1.16 christos if (*cp)
130 1.1.1.16 christos (*cp)(man, n);
131 1.1.1.17 christos if (man->last == n)
132 1.1.1.17 christos man_state(man, n);
133 1.1 joerg break;
134 1.1 joerg }
135 1.1 joerg }
136 1.1 joerg
137 1.1.1.16 christos static void
138 1.1.1.16 christos check_root(CHKARGS)
139 1.1 joerg {
140 1.1 joerg
141 1.1.1.16 christos assert((man->flags & (MAN_BLINE | MAN_ELINE)) == 0);
142 1.1 joerg
143 1.1.1.16 christos if (NULL == man->first->child)
144 1.1.1.16 christos mandoc_msg(MANDOCERR_DOC_EMPTY, man->parse,
145 1.1.1.16 christos n->line, n->pos, NULL);
146 1.1.1.16 christos else
147 1.1.1.16 christos man->meta.hasbody = 1;
148 1.1 joerg
149 1.1.1.16 christos if (NULL == man->meta.title) {
150 1.1.1.16 christos mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
151 1.1.1.16 christos n->line, n->pos, NULL);
152 1.1.1.11 joerg
153 1.1.1.6 joerg /*
154 1.1.1.6 joerg * If a title hasn't been set, do so now (by
155 1.1.1.6 joerg * implication, date and section also aren't set).
156 1.1.1.6 joerg */
157 1.1.1.11 joerg
158 1.1.1.16 christos man->meta.title = mandoc_strdup("");
159 1.1.1.16 christos man->meta.msec = mandoc_strdup("");
160 1.1.1.16 christos man->meta.date = man->quick ? mandoc_strdup("") :
161 1.1.1.16 christos mandoc_normdate(man->parse, NULL, n->line, n->pos);
162 1.1.1.6 joerg }
163 1.1 joerg }
164 1.1 joerg
165 1.1.1.13 joerg static void
166 1.1.1.13 joerg check_text(CHKARGS)
167 1.1 joerg {
168 1.1.1.13 joerg char *cp, *p;
169 1.1 joerg
170 1.1.1.15 joerg if (MAN_LITERAL & man->flags)
171 1.1.1.14 joerg return;
172 1.1.1.14 joerg
173 1.1.1.14 joerg cp = n->string;
174 1.1.1.14 joerg for (p = cp; NULL != (p = strchr(p, '\t')); p++)
175 1.1.1.16 christos mandoc_msg(MANDOCERR_FI_TAB, man->parse,
176 1.1.1.16 christos n->line, n->pos + (p - cp), NULL);
177 1.1 joerg }
178 1.1 joerg
179 1.1.1.16 christos static void
180 1.1.1.16 christos post_OP(CHKARGS)
181 1.1.1.16 christos {
182 1.1 joerg
183 1.1.1.17 christos if (n->child == NULL)
184 1.1.1.16 christos mandoc_msg(MANDOCERR_OP_EMPTY, man->parse,
185 1.1.1.16 christos n->line, n->pos, "OP");
186 1.1.1.17 christos else if (n->child->next != NULL && n->child->next->next != NULL) {
187 1.1.1.16 christos n = n->child->next->next;
188 1.1.1.16 christos mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
189 1.1.1.16 christos n->line, n->pos, "OP ... %s", n->string);
190 1.1.1.16 christos }
191 1.1.1.16 christos }
192 1.1 joerg
193 1.1.1.16 christos static void
194 1.1.1.16 christos post_UR(CHKARGS)
195 1.1.1.15 joerg {
196 1.1.1.15 joerg
197 1.1.1.17 christos if (n->type == ROFFT_HEAD && n->child == NULL)
198 1.1.1.16 christos mandoc_vmsg(MANDOCERR_UR_NOHEAD, man->parse,
199 1.1.1.16 christos n->line, n->pos, "UR");
200 1.1.1.16 christos check_part(man, n);
201 1.1.1.15 joerg }
202 1.1.1.15 joerg
203 1.1.1.16 christos static void
204 1.1.1.13 joerg post_ft(CHKARGS)
205 1.1.1.11 joerg {
206 1.1.1.11 joerg char *cp;
207 1.1.1.11 joerg int ok;
208 1.1.1.11 joerg
209 1.1.1.17 christos if (n->child == NULL)
210 1.1.1.16 christos return;
211 1.1.1.11 joerg
212 1.1.1.11 joerg ok = 0;
213 1.1.1.11 joerg cp = n->child->string;
214 1.1.1.11 joerg switch (*cp) {
215 1.1.1.16 christos case '1':
216 1.1.1.16 christos case '2':
217 1.1.1.16 christos case '3':
218 1.1.1.16 christos case '4':
219 1.1.1.16 christos case 'I':
220 1.1.1.16 christos case 'P':
221 1.1.1.16 christos case 'R':
222 1.1.1.11 joerg if ('\0' == cp[1])
223 1.1.1.11 joerg ok = 1;
224 1.1.1.11 joerg break;
225 1.1.1.16 christos case 'B':
226 1.1.1.11 joerg if ('\0' == cp[1] || ('I' == cp[1] && '\0' == cp[2]))
227 1.1.1.11 joerg ok = 1;
228 1.1.1.11 joerg break;
229 1.1.1.16 christos case 'C':
230 1.1.1.11 joerg if ('W' == cp[1] && '\0' == cp[2])
231 1.1.1.11 joerg ok = 1;
232 1.1.1.11 joerg break;
233 1.1.1.11 joerg default:
234 1.1.1.11 joerg break;
235 1.1.1.11 joerg }
236 1.1.1.11 joerg
237 1.1.1.11 joerg if (0 == ok) {
238 1.1.1.16 christos mandoc_vmsg(MANDOCERR_FT_BAD, man->parse,
239 1.1.1.16 christos n->line, n->pos, "ft %s", cp);
240 1.1.1.11 joerg *cp = '\0';
241 1.1.1.11 joerg }
242 1.1.1.13 joerg }
243 1.1 joerg
244 1.1.1.16 christos static void
245 1.1 joerg check_part(CHKARGS)
246 1.1 joerg {
247 1.1 joerg
248 1.1.1.17 christos if (n->type == ROFFT_BODY && n->child == NULL)
249 1.1.1.16 christos mandoc_msg(MANDOCERR_BLK_EMPTY, man->parse,
250 1.1.1.16 christos n->line, n->pos, man_macronames[n->tok]);
251 1.1 joerg }
252 1.1 joerg
253 1.1.1.16 christos static void
254 1.1 joerg check_par(CHKARGS)
255 1.1 joerg {
256 1.1 joerg
257 1.1.1.12 joerg switch (n->type) {
258 1.1.1.17 christos case ROFFT_BLOCK:
259 1.1.1.17 christos if (n->body->child == NULL)
260 1.1.1.17 christos roff_node_delete(man, n);
261 1.1.1.12 joerg break;
262 1.1.1.17 christos case ROFFT_BODY:
263 1.1.1.17 christos if (n->child == NULL)
264 1.1.1.16 christos mandoc_vmsg(MANDOCERR_PAR_SKIP,
265 1.1.1.16 christos man->parse, n->line, n->pos,
266 1.1.1.16 christos "%s empty", man_macronames[n->tok]);
267 1.1.1.12 joerg break;
268 1.1.1.17 christos case ROFFT_HEAD:
269 1.1.1.17 christos if (n->child != NULL)
270 1.1.1.16 christos mandoc_vmsg(MANDOCERR_ARG_SKIP,
271 1.1.1.16 christos man->parse, n->line, n->pos,
272 1.1.1.16 christos "%s %s%s", man_macronames[n->tok],
273 1.1.1.16 christos n->child->string,
274 1.1.1.17 christos n->child->next != NULL ? " ..." : "");
275 1.1.1.12 joerg break;
276 1.1.1.12 joerg default:
277 1.1.1.12 joerg break;
278 1.1.1.12 joerg }
279 1.1 joerg }
280 1.1 joerg
281 1.1.1.16 christos static void
282 1.1.1.15 joerg post_IP(CHKARGS)
283 1.1.1.15 joerg {
284 1.1.1.15 joerg
285 1.1.1.15 joerg switch (n->type) {
286 1.1.1.17 christos case ROFFT_BLOCK:
287 1.1.1.17 christos if (n->head->child == NULL && n->body->child == NULL)
288 1.1.1.17 christos roff_node_delete(man, n);
289 1.1.1.15 joerg break;
290 1.1.1.17 christos case ROFFT_BODY:
291 1.1.1.17 christos if (n->parent->head->child == NULL && n->child == NULL)
292 1.1.1.16 christos mandoc_vmsg(MANDOCERR_PAR_SKIP,
293 1.1.1.16 christos man->parse, n->line, n->pos,
294 1.1.1.16 christos "%s empty", man_macronames[n->tok]);
295 1.1.1.15 joerg break;
296 1.1.1.15 joerg default:
297 1.1.1.15 joerg break;
298 1.1.1.15 joerg }
299 1.1.1.15 joerg }
300 1.1 joerg
301 1.1.1.16 christos static void
302 1.1.1.11 joerg post_TH(CHKARGS)
303 1.1.1.11 joerg {
304 1.1.1.17 christos struct roff_node *nb;
305 1.1.1.12 joerg const char *p;
306 1.1.1.11 joerg
307 1.1.1.15 joerg free(man->meta.title);
308 1.1.1.15 joerg free(man->meta.vol);
309 1.1.1.17 christos free(man->meta.os);
310 1.1.1.15 joerg free(man->meta.msec);
311 1.1.1.15 joerg free(man->meta.date);
312 1.1.1.15 joerg
313 1.1.1.15 joerg man->meta.title = man->meta.vol = man->meta.date =
314 1.1.1.17 christos man->meta.msec = man->meta.os = NULL;
315 1.1.1.16 christos
316 1.1.1.16 christos nb = n;
317 1.1.1.11 joerg
318 1.1.1.17 christos /* ->TITLE<- MSEC DATE OS VOL */
319 1.1.1.11 joerg
320 1.1.1.11 joerg n = n->child;
321 1.1.1.12 joerg if (n && n->string) {
322 1.1.1.12 joerg for (p = n->string; '\0' != *p; p++) {
323 1.1.1.12 joerg /* Only warn about this once... */
324 1.1.1.16 christos if (isalpha((unsigned char)*p) &&
325 1.1.1.16 christos ! isupper((unsigned char)*p)) {
326 1.1.1.16 christos mandoc_vmsg(MANDOCERR_TITLE_CASE,
327 1.1.1.16 christos man->parse, n->line,
328 1.1.1.16 christos n->pos + (p - n->string),
329 1.1.1.16 christos "TH %s", n->string);
330 1.1.1.12 joerg break;
331 1.1.1.12 joerg }
332 1.1.1.12 joerg }
333 1.1.1.15 joerg man->meta.title = mandoc_strdup(n->string);
334 1.1.1.16 christos } else {
335 1.1.1.15 joerg man->meta.title = mandoc_strdup("");
336 1.1.1.16 christos mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
337 1.1.1.16 christos nb->line, nb->pos, "TH");
338 1.1.1.16 christos }
339 1.1.1.11 joerg
340 1.1.1.17 christos /* TITLE ->MSEC<- DATE OS VOL */
341 1.1.1.11 joerg
342 1.1.1.12 joerg if (n)
343 1.1.1.12 joerg n = n->next;
344 1.1.1.12 joerg if (n && n->string)
345 1.1.1.15 joerg man->meta.msec = mandoc_strdup(n->string);
346 1.1.1.16 christos else {
347 1.1.1.15 joerg man->meta.msec = mandoc_strdup("");
348 1.1.1.16 christos mandoc_vmsg(MANDOCERR_MSEC_MISSING, man->parse,
349 1.1.1.16 christos nb->line, nb->pos, "TH %s", man->meta.title);
350 1.1.1.16 christos }
351 1.1.1.11 joerg
352 1.1.1.17 christos /* TITLE MSEC ->DATE<- OS VOL */
353 1.1.1.11 joerg
354 1.1.1.12 joerg if (n)
355 1.1.1.12 joerg n = n->next;
356 1.1.1.14 joerg if (n && n->string && '\0' != n->string[0]) {
357 1.1.1.16 christos man->meta.date = man->quick ?
358 1.1.1.16 christos mandoc_strdup(n->string) :
359 1.1.1.16 christos mandoc_normdate(man->parse, n->string,
360 1.1.1.16 christos n->line, n->pos);
361 1.1.1.16 christos } else {
362 1.1.1.15 joerg man->meta.date = mandoc_strdup("");
363 1.1.1.16 christos mandoc_msg(MANDOCERR_DATE_MISSING, man->parse,
364 1.1.1.16 christos n ? n->line : nb->line,
365 1.1.1.16 christos n ? n->pos : nb->pos, "TH");
366 1.1.1.16 christos }
367 1.1.1.11 joerg
368 1.1.1.17 christos /* TITLE MSEC DATE ->OS<- VOL */
369 1.1.1.11 joerg
370 1.1.1.11 joerg if (n && (n = n->next))
371 1.1.1.17 christos man->meta.os = mandoc_strdup(n->string);
372 1.1.1.16 christos else if (man->defos != NULL)
373 1.1.1.17 christos man->meta.os = mandoc_strdup(man->defos);
374 1.1.1.11 joerg
375 1.1.1.17 christos /* TITLE MSEC DATE OS ->VOL<- */
376 1.1.1.14 joerg /* If missing, use the default VOL name for MSEC. */
377 1.1.1.11 joerg
378 1.1.1.11 joerg if (n && (n = n->next))
379 1.1.1.15 joerg man->meta.vol = mandoc_strdup(n->string);
380 1.1.1.15 joerg else if ('\0' != man->meta.msec[0] &&
381 1.1.1.15 joerg (NULL != (p = mandoc_a2msec(man->meta.msec))))
382 1.1.1.15 joerg man->meta.vol = mandoc_strdup(p);
383 1.1.1.11 joerg
384 1.1.1.16 christos if (n != NULL && (n = n->next) != NULL)
385 1.1.1.16 christos mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse,
386 1.1.1.16 christos n->line, n->pos, "TH ... %s", n->string);
387 1.1.1.16 christos
388 1.1.1.11 joerg /*
389 1.1.1.11 joerg * Remove the `TH' node after we've processed it for our
390 1.1.1.11 joerg * meta-data.
391 1.1.1.11 joerg */
392 1.1.1.17 christos roff_node_delete(man, man->last);
393 1.1.1.11 joerg }
394 1.1.1.11 joerg
395 1.1.1.16 christos static void
396 1.1.1.11 joerg post_UC(CHKARGS)
397 1.1.1.11 joerg {
398 1.1.1.11 joerg static const char * const bsd_versions[] = {
399 1.1.1.11 joerg "3rd Berkeley Distribution",
400 1.1.1.11 joerg "4th Berkeley Distribution",
401 1.1.1.11 joerg "4.2 Berkeley Distribution",
402 1.1.1.11 joerg "4.3 Berkeley Distribution",
403 1.1.1.11 joerg "4.4 Berkeley Distribution",
404 1.1.1.11 joerg };
405 1.1.1.11 joerg
406 1.1.1.11 joerg const char *p, *s;
407 1.1.1.11 joerg
408 1.1.1.11 joerg n = n->child;
409 1.1.1.11 joerg
410 1.1.1.17 christos if (n == NULL || n->type != ROFFT_TEXT)
411 1.1.1.11 joerg p = bsd_versions[0];
412 1.1.1.11 joerg else {
413 1.1.1.11 joerg s = n->string;
414 1.1.1.11 joerg if (0 == strcmp(s, "3"))
415 1.1.1.11 joerg p = bsd_versions[0];
416 1.1.1.11 joerg else if (0 == strcmp(s, "4"))
417 1.1.1.11 joerg p = bsd_versions[1];
418 1.1.1.11 joerg else if (0 == strcmp(s, "5"))
419 1.1.1.11 joerg p = bsd_versions[2];
420 1.1.1.11 joerg else if (0 == strcmp(s, "6"))
421 1.1.1.11 joerg p = bsd_versions[3];
422 1.1.1.11 joerg else if (0 == strcmp(s, "7"))
423 1.1.1.11 joerg p = bsd_versions[4];
424 1.1.1.11 joerg else
425 1.1.1.11 joerg p = bsd_versions[0];
426 1.1.1.11 joerg }
427 1.1.1.11 joerg
428 1.1.1.17 christos free(man->meta.os);
429 1.1.1.17 christos man->meta.os = mandoc_strdup(p);
430 1.1.1.11 joerg }
431 1.1.1.11 joerg
432 1.1.1.16 christos static void
433 1.1.1.11 joerg post_AT(CHKARGS)
434 1.1.1.11 joerg {
435 1.1.1.11 joerg static const char * const unix_versions[] = {
436 1.1.1.11 joerg "7th Edition",
437 1.1.1.11 joerg "System III",
438 1.1.1.11 joerg "System V",
439 1.1.1.11 joerg "System V Release 2",
440 1.1.1.11 joerg };
441 1.1.1.11 joerg
442 1.1.1.17 christos struct roff_node *nn;
443 1.1.1.11 joerg const char *p, *s;
444 1.1.1.11 joerg
445 1.1.1.11 joerg n = n->child;
446 1.1.1.11 joerg
447 1.1.1.17 christos if (n == NULL || n->type != ROFFT_TEXT)
448 1.1.1.11 joerg p = unix_versions[0];
449 1.1.1.11 joerg else {
450 1.1.1.11 joerg s = n->string;
451 1.1.1.11 joerg if (0 == strcmp(s, "3"))
452 1.1.1.11 joerg p = unix_versions[0];
453 1.1.1.11 joerg else if (0 == strcmp(s, "4"))
454 1.1.1.11 joerg p = unix_versions[1];
455 1.1.1.11 joerg else if (0 == strcmp(s, "5")) {
456 1.1.1.11 joerg nn = n->next;
457 1.1.1.17 christos if (nn != NULL &&
458 1.1.1.17 christos nn->type == ROFFT_TEXT &&
459 1.1.1.17 christos nn->string[0] != '\0')
460 1.1.1.11 joerg p = unix_versions[3];
461 1.1.1.11 joerg else
462 1.1.1.11 joerg p = unix_versions[2];
463 1.1.1.11 joerg } else
464 1.1.1.11 joerg p = unix_versions[0];
465 1.1.1.11 joerg }
466 1.1.1.11 joerg
467 1.1.1.17 christos free(man->meta.os);
468 1.1.1.17 christos man->meta.os = mandoc_strdup(p);
469 1.1.1.11 joerg }
470 1.1.1.13 joerg
471 1.1.1.16 christos static void
472 1.1.1.13 joerg post_vs(CHKARGS)
473 1.1.1.13 joerg {
474 1.1.1.13 joerg
475 1.1.1.15 joerg if (NULL != n->prev)
476 1.1.1.16 christos return;
477 1.1.1.15 joerg
478 1.1.1.15 joerg switch (n->parent->tok) {
479 1.1.1.16 christos case MAN_SH:
480 1.1.1.16 christos case MAN_SS:
481 1.1.1.16 christos mandoc_vmsg(MANDOCERR_PAR_SKIP, man->parse, n->line, n->pos,
482 1.1.1.16 christos "%s after %s", man_macronames[n->tok],
483 1.1.1.16 christos man_macronames[n->parent->tok]);
484 1.1.1.15 joerg /* FALLTHROUGH */
485 1.1.1.17 christos case TOKEN_NONE:
486 1.1.1.16 christos /*
487 1.1.1.15 joerg * Don't warn about this because it occurs in pod2man
488 1.1.1.15 joerg * and would cause considerable (unfixable) warnage.
489 1.1.1.15 joerg */
490 1.1.1.17 christos roff_node_delete(man, n);
491 1.1.1.15 joerg break;
492 1.1.1.15 joerg default:
493 1.1.1.15 joerg break;
494 1.1.1.15 joerg }
495 1.1.1.13 joerg }
496