man_validate.c revision 1.1.1.16.2.1 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.16.2.1 pgoyette * 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.16.2.1 pgoyette * 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.16.2.1 pgoyette * 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.16.2.1 pgoyette #include "mandoc.h"
33 1.1.1.16.2.1 pgoyette #include "roff.h"
34 1.1.1.16.2.1 pgoyette #include "man.h"
35 1.1 joerg #include "libmandoc.h"
36 1.1.1.16.2.1 pgoyette #include "roff_int.h"
37 1.1.1.16.2.1 pgoyette #include "libman.h"
38 1.1 joerg
39 1.1.1.16.2.1 pgoyette #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.16.2.1 pgoyette NULL, /* nf */
81 1.1.1.16.2.1 pgoyette 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.16.2.1 pgoyette NULL, /* EX */
92 1.1.1.16.2.1 pgoyette 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.16.2.1 pgoyette man_node_validate(struct roff_man *man)
101 1.1 joerg {
102 1.1.1.16.2.1 pgoyette 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.16.2.1 pgoyette man->last = man->last->child;
107 1.1.1.16.2.1 pgoyette while (man->last != NULL) {
108 1.1.1.16.2.1 pgoyette man_node_validate(man);
109 1.1.1.16.2.1 pgoyette if (man->last == n)
110 1.1.1.16.2.1 pgoyette man->last = man->last->child;
111 1.1.1.16.2.1 pgoyette else
112 1.1.1.16.2.1 pgoyette man->last = man->last->next;
113 1.1.1.16.2.1 pgoyette }
114 1.1.1.16 christos
115 1.1.1.16.2.1 pgoyette man->last = n;
116 1.1.1.16.2.1 pgoyette man->next = ROFF_NEXT_SIBLING;
117 1.1.1.11 joerg switch (n->type) {
118 1.1.1.16.2.1 pgoyette case ROFFT_TEXT:
119 1.1.1.16 christos check_text(man, n);
120 1.1.1.11 joerg break;
121 1.1.1.16.2.1 pgoyette case ROFFT_ROOT:
122 1.1.1.16 christos check_root(man, n);
123 1.1.1.16 christos break;
124 1.1.1.16.2.1 pgoyette case ROFFT_EQN:
125 1.1.1.16.2.1 pgoyette 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.16.2.1 pgoyette if (man->last == n)
132 1.1.1.16.2.1 pgoyette 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.16.2.1 pgoyette 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.16.2.1 pgoyette 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.16.2.1 pgoyette 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.16.2.1 pgoyette 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.16.2.1 pgoyette 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.16.2.1 pgoyette case ROFFT_BLOCK:
259 1.1.1.16.2.1 pgoyette if (n->body->child == NULL)
260 1.1.1.16.2.1 pgoyette roff_node_delete(man, n);
261 1.1.1.12 joerg break;
262 1.1.1.16.2.1 pgoyette case ROFFT_BODY:
263 1.1.1.16.2.1 pgoyette 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.16.2.1 pgoyette case ROFFT_HEAD:
269 1.1.1.16.2.1 pgoyette 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.16.2.1 pgoyette 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.16.2.1 pgoyette case ROFFT_BLOCK:
287 1.1.1.16.2.1 pgoyette if (n->head->child == NULL && n->body->child == NULL)
288 1.1.1.16.2.1 pgoyette roff_node_delete(man, n);
289 1.1.1.15 joerg break;
290 1.1.1.16.2.1 pgoyette case ROFFT_BODY:
291 1.1.1.16.2.1 pgoyette 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.16.2.1 pgoyette 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.16.2.1 pgoyette 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.16.2.1 pgoyette 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.16.2.1 pgoyette /* ->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.16.2.1 pgoyette /* 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.16.2.1 pgoyette /* 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.16.2.1 pgoyette /* 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.16.2.1 pgoyette man->meta.os = mandoc_strdup(n->string);
372 1.1.1.16 christos else if (man->defos != NULL)
373 1.1.1.16.2.1 pgoyette man->meta.os = mandoc_strdup(man->defos);
374 1.1.1.11 joerg
375 1.1.1.16.2.1 pgoyette /* 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.16.2.1 pgoyette 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.16.2.1 pgoyette 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.16.2.1 pgoyette free(man->meta.os);
429 1.1.1.16.2.1 pgoyette 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.16.2.1 pgoyette 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.16.2.1 pgoyette 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.16.2.1 pgoyette if (nn != NULL &&
458 1.1.1.16.2.1 pgoyette nn->type == ROFFT_TEXT &&
459 1.1.1.16.2.1 pgoyette 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.16.2.1 pgoyette free(man->meta.os);
468 1.1.1.16.2.1 pgoyette 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.16.2.1 pgoyette 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.16.2.1 pgoyette 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