man_validate.c revision 1.1.1.11 1 1.1.1.11 joerg /* $Vendor-Id: man_validate.c,v 1.57 2011/01/01 12:59:17 kristaps Exp $ */
2 1.1 joerg /*
3 1.1.1.11 joerg * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps (at) bsd.lv>
4 1.1 joerg *
5 1.1 joerg * Permission to use, copy, modify, and distribute this software for any
6 1.1 joerg * purpose with or without fee is hereby granted, provided that the above
7 1.1 joerg * copyright notice and this permission notice appear in all copies.
8 1.1 joerg *
9 1.1 joerg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 1.1 joerg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 1.1 joerg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 1.1 joerg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 1.1 joerg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 1.1 joerg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 1.1 joerg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 1.1 joerg */
17 1.1.1.4 joerg #ifdef HAVE_CONFIG_H
18 1.1.1.4 joerg #include "config.h"
19 1.1.1.4 joerg #endif
20 1.1.1.4 joerg
21 1.1 joerg #include <sys/types.h>
22 1.1 joerg
23 1.1 joerg #include <assert.h>
24 1.1 joerg #include <ctype.h>
25 1.1 joerg #include <errno.h>
26 1.1 joerg #include <limits.h>
27 1.1 joerg #include <stdarg.h>
28 1.1 joerg #include <stdlib.h>
29 1.1.1.10 joerg #include <string.h>
30 1.1.1.11 joerg #include <time.h>
31 1.1 joerg
32 1.1.1.7 joerg #include "mandoc.h"
33 1.1 joerg #include "libman.h"
34 1.1 joerg #include "libmandoc.h"
35 1.1 joerg
36 1.1.1.8 joerg #define CHKARGS struct man *m, struct man_node *n
37 1.1 joerg
38 1.1 joerg typedef int (*v_check)(CHKARGS);
39 1.1 joerg
40 1.1 joerg struct man_valid {
41 1.1 joerg v_check *pres;
42 1.1 joerg v_check *posts;
43 1.1 joerg };
44 1.1 joerg
45 1.1 joerg static int check_bline(CHKARGS);
46 1.1 joerg static int check_eq0(CHKARGS);
47 1.1.1.11 joerg static int check_ft(CHKARGS);
48 1.1 joerg static int check_le1(CHKARGS);
49 1.1 joerg static int check_ge2(CHKARGS);
50 1.1 joerg static int check_le5(CHKARGS);
51 1.1 joerg static int check_par(CHKARGS);
52 1.1 joerg static int check_part(CHKARGS);
53 1.1 joerg static int check_root(CHKARGS);
54 1.1 joerg static int check_sec(CHKARGS);
55 1.1 joerg static int check_text(CHKARGS);
56 1.1.1.5 joerg static int check_title(CHKARGS);
57 1.1 joerg
58 1.1.1.11 joerg static int post_AT(CHKARGS);
59 1.1.1.11 joerg static int post_fi(CHKARGS);
60 1.1.1.11 joerg static int post_nf(CHKARGS);
61 1.1.1.11 joerg static int post_TH(CHKARGS);
62 1.1.1.11 joerg static int post_UC(CHKARGS);
63 1.1.1.11 joerg
64 1.1.1.11 joerg static v_check posts_at[] = { post_AT, NULL };
65 1.1 joerg static v_check posts_eq0[] = { check_eq0, NULL };
66 1.1.1.11 joerg static v_check posts_fi[] = { check_eq0, post_fi, NULL };
67 1.1.1.11 joerg static v_check posts_le1[] = { check_le1, NULL };
68 1.1.1.11 joerg static v_check posts_ft[] = { check_ft, NULL };
69 1.1.1.11 joerg static v_check posts_nf[] = { check_eq0, post_nf, NULL };
70 1.1 joerg static v_check posts_par[] = { check_par, NULL };
71 1.1 joerg static v_check posts_part[] = { check_part, NULL };
72 1.1 joerg static v_check posts_sec[] = { check_sec, NULL };
73 1.1.1.11 joerg static v_check posts_th[] = { check_ge2, check_le5, check_title, post_TH, NULL };
74 1.1.1.11 joerg static v_check posts_uc[] = { post_UC, NULL };
75 1.1 joerg static v_check pres_bline[] = { check_bline, NULL };
76 1.1 joerg
77 1.1.1.11 joerg
78 1.1 joerg static const struct man_valid man_valids[MAN_MAX] = {
79 1.1.1.5 joerg { NULL, posts_eq0 }, /* br */
80 1.1.1.5 joerg { pres_bline, posts_th }, /* TH */
81 1.1 joerg { pres_bline, posts_sec }, /* SH */
82 1.1 joerg { pres_bline, posts_sec }, /* SS */
83 1.1 joerg { pres_bline, posts_par }, /* TP */
84 1.1 joerg { pres_bline, posts_par }, /* LP */
85 1.1 joerg { pres_bline, posts_par }, /* PP */
86 1.1 joerg { pres_bline, posts_par }, /* P */
87 1.1 joerg { pres_bline, posts_par }, /* IP */
88 1.1 joerg { pres_bline, posts_par }, /* HP */
89 1.1 joerg { NULL, NULL }, /* SM */
90 1.1 joerg { NULL, NULL }, /* SB */
91 1.1 joerg { NULL, NULL }, /* BI */
92 1.1 joerg { NULL, NULL }, /* IB */
93 1.1 joerg { NULL, NULL }, /* BR */
94 1.1 joerg { NULL, NULL }, /* RB */
95 1.1 joerg { NULL, NULL }, /* R */
96 1.1 joerg { NULL, NULL }, /* B */
97 1.1 joerg { NULL, NULL }, /* I */
98 1.1 joerg { NULL, NULL }, /* IR */
99 1.1 joerg { NULL, NULL }, /* RI */
100 1.1.1.10 joerg { NULL, posts_eq0 }, /* na */ /* FIXME: should warn only. */
101 1.1.1.10 joerg { NULL, posts_le1 }, /* sp */ /* FIXME: should warn only. */
102 1.1.1.11 joerg { pres_bline, posts_nf }, /* nf */
103 1.1.1.11 joerg { pres_bline, posts_fi }, /* fi */
104 1.1 joerg { NULL, NULL }, /* RE */
105 1.1 joerg { NULL, posts_part }, /* RS */
106 1.1 joerg { NULL, NULL }, /* DT */
107 1.1.1.11 joerg { NULL, posts_uc }, /* UC */
108 1.1.1.2 joerg { NULL, NULL }, /* PD */
109 1.1.1.11 joerg { NULL, posts_at }, /* AT */
110 1.1.1.10 joerg { NULL, NULL }, /* in */
111 1.1.1.11 joerg { NULL, posts_ft }, /* ft */
112 1.1 joerg };
113 1.1 joerg
114 1.1 joerg
115 1.1 joerg int
116 1.1.1.8 joerg man_valid_pre(struct man *m, struct man_node *n)
117 1.1 joerg {
118 1.1 joerg v_check *cp;
119 1.1 joerg
120 1.1.1.11 joerg switch (n->type) {
121 1.1.1.11 joerg case (MAN_TEXT):
122 1.1.1.11 joerg /* FALLTHROUGH */
123 1.1.1.11 joerg case (MAN_ROOT):
124 1.1.1.11 joerg /* FALLTHROUGH */
125 1.1.1.11 joerg case (MAN_TBL):
126 1.1 joerg return(1);
127 1.1.1.11 joerg default:
128 1.1.1.11 joerg break;
129 1.1.1.11 joerg }
130 1.1 joerg
131 1.1 joerg if (NULL == (cp = man_valids[n->tok].pres))
132 1.1 joerg return(1);
133 1.1 joerg for ( ; *cp; cp++)
134 1.1 joerg if ( ! (*cp)(m, n))
135 1.1 joerg return(0);
136 1.1 joerg return(1);
137 1.1 joerg }
138 1.1 joerg
139 1.1 joerg
140 1.1 joerg int
141 1.1 joerg man_valid_post(struct man *m)
142 1.1 joerg {
143 1.1 joerg v_check *cp;
144 1.1 joerg
145 1.1 joerg if (MAN_VALID & m->last->flags)
146 1.1 joerg return(1);
147 1.1 joerg m->last->flags |= MAN_VALID;
148 1.1 joerg
149 1.1 joerg switch (m->last->type) {
150 1.1 joerg case (MAN_TEXT):
151 1.1 joerg return(check_text(m, m->last));
152 1.1 joerg case (MAN_ROOT):
153 1.1 joerg return(check_root(m, m->last));
154 1.1.1.11 joerg case (MAN_TBL):
155 1.1.1.11 joerg return(1);
156 1.1 joerg default:
157 1.1 joerg break;
158 1.1 joerg }
159 1.1 joerg
160 1.1 joerg if (NULL == (cp = man_valids[m->last->tok].posts))
161 1.1 joerg return(1);
162 1.1 joerg for ( ; *cp; cp++)
163 1.1 joerg if ( ! (*cp)(m, m->last))
164 1.1 joerg return(0);
165 1.1 joerg
166 1.1 joerg return(1);
167 1.1 joerg }
168 1.1 joerg
169 1.1 joerg
170 1.1 joerg static int
171 1.1 joerg check_root(CHKARGS)
172 1.1 joerg {
173 1.1 joerg
174 1.1 joerg if (MAN_BLINE & m->flags)
175 1.1.1.11 joerg man_nmsg(m, n, MANDOCERR_SCOPEEXIT);
176 1.1.1.11 joerg else if (MAN_ELINE & m->flags)
177 1.1.1.11 joerg man_nmsg(m, n, MANDOCERR_SCOPEEXIT);
178 1.1 joerg
179 1.1 joerg m->flags &= ~MAN_BLINE;
180 1.1 joerg m->flags &= ~MAN_ELINE;
181 1.1 joerg
182 1.1.1.7 joerg if (NULL == m->first->child) {
183 1.1.1.7 joerg man_nmsg(m, n, MANDOCERR_NODOCBODY);
184 1.1.1.7 joerg return(0);
185 1.1.1.7 joerg } else if (NULL == m->meta.title) {
186 1.1.1.11 joerg man_nmsg(m, n, MANDOCERR_NOTITLE);
187 1.1.1.11 joerg
188 1.1.1.6 joerg /*
189 1.1.1.6 joerg * If a title hasn't been set, do so now (by
190 1.1.1.6 joerg * implication, date and section also aren't set).
191 1.1.1.6 joerg */
192 1.1.1.11 joerg
193 1.1.1.6 joerg m->meta.title = mandoc_strdup("unknown");
194 1.1.1.6 joerg m->meta.date = time(NULL);
195 1.1.1.7 joerg m->meta.msec = mandoc_strdup("1");
196 1.1.1.6 joerg }
197 1.1 joerg
198 1.1 joerg return(1);
199 1.1 joerg }
200 1.1 joerg
201 1.1 joerg
202 1.1 joerg static int
203 1.1.1.5 joerg check_title(CHKARGS)
204 1.1.1.5 joerg {
205 1.1.1.5 joerg const char *p;
206 1.1.1.5 joerg
207 1.1.1.5 joerg assert(n->child);
208 1.1.1.7 joerg /* FIXME: is this sufficient? */
209 1.1.1.7 joerg if ('\0' == *n->child->string) {
210 1.1.1.7 joerg man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
211 1.1.1.7 joerg return(0);
212 1.1.1.7 joerg }
213 1.1.1.5 joerg
214 1.1.1.5 joerg for (p = n->child->string; '\0' != *p; p++)
215 1.1.1.11 joerg /* Only warn about this once... */
216 1.1.1.11 joerg if (isalpha((u_char)*p) && ! isupper((u_char)*p)) {
217 1.1.1.11 joerg man_nmsg(m, n, MANDOCERR_UPPERCASE);
218 1.1.1.11 joerg break;
219 1.1.1.11 joerg }
220 1.1.1.5 joerg
221 1.1.1.5 joerg return(1);
222 1.1.1.5 joerg }
223 1.1.1.5 joerg
224 1.1.1.5 joerg
225 1.1.1.5 joerg static int
226 1.1 joerg check_text(CHKARGS)
227 1.1 joerg {
228 1.1.1.8 joerg char *p;
229 1.1 joerg int pos, c;
230 1.1.1.10 joerg size_t sz;
231 1.1 joerg
232 1.1 joerg for (p = n->string, pos = n->pos + 1; *p; p++, pos++) {
233 1.1.1.10 joerg sz = strcspn(p, "\t\\");
234 1.1.1.10 joerg p += (int)sz;
235 1.1.1.10 joerg
236 1.1.1.10 joerg if ('\0' == *p)
237 1.1.1.10 joerg break;
238 1.1.1.10 joerg
239 1.1.1.10 joerg pos += (int)sz;
240 1.1.1.7 joerg
241 1.1.1.10 joerg if ('\t' == *p) {
242 1.1.1.10 joerg if (MAN_LITERAL & m->flags)
243 1.1.1.10 joerg continue;
244 1.1.1.10 joerg if (man_pmsg(m, n->line, pos, MANDOCERR_BADTAB))
245 1.1.1.10 joerg continue;
246 1.1.1.10 joerg return(0);
247 1.1 joerg }
248 1.1 joerg
249 1.1.1.10 joerg /* Check the special character. */
250 1.1.1.9 joerg
251 1.1.1.10 joerg c = mandoc_special(p);
252 1.1.1.10 joerg if (c) {
253 1.1.1.10 joerg p += c - 1;
254 1.1.1.10 joerg pos += c - 1;
255 1.1.1.11 joerg } else
256 1.1.1.11 joerg man_pmsg(m, n->line, pos, MANDOCERR_BADESCAPE);
257 1.1 joerg }
258 1.1 joerg
259 1.1 joerg return(1);
260 1.1 joerg }
261 1.1 joerg
262 1.1 joerg
263 1.1 joerg #define INEQ_DEFINE(x, ineq, name) \
264 1.1 joerg static int \
265 1.1 joerg check_##name(CHKARGS) \
266 1.1 joerg { \
267 1.1 joerg if (n->nchild ineq (x)) \
268 1.1 joerg return(1); \
269 1.1.1.7 joerg man_vmsg(m, MANDOCERR_SYNTARGCOUNT, n->line, n->pos, \
270 1.1.1.7 joerg "line arguments %s %d (have %d)", \
271 1.1.1.7 joerg #ineq, (x), n->nchild); \
272 1.1.1.7 joerg return(0); \
273 1.1 joerg }
274 1.1 joerg
275 1.1 joerg INEQ_DEFINE(0, ==, eq0)
276 1.1 joerg INEQ_DEFINE(1, <=, le1)
277 1.1 joerg INEQ_DEFINE(2, >=, ge2)
278 1.1 joerg INEQ_DEFINE(5, <=, le5)
279 1.1 joerg
280 1.1.1.11 joerg static int
281 1.1.1.11 joerg check_ft(CHKARGS)
282 1.1.1.11 joerg {
283 1.1.1.11 joerg char *cp;
284 1.1.1.11 joerg int ok;
285 1.1.1.11 joerg
286 1.1.1.11 joerg if (0 == n->nchild)
287 1.1.1.11 joerg return(1);
288 1.1.1.11 joerg
289 1.1.1.11 joerg ok = 0;
290 1.1.1.11 joerg cp = n->child->string;
291 1.1.1.11 joerg switch (*cp) {
292 1.1.1.11 joerg case ('1'):
293 1.1.1.11 joerg /* FALLTHROUGH */
294 1.1.1.11 joerg case ('2'):
295 1.1.1.11 joerg /* FALLTHROUGH */
296 1.1.1.11 joerg case ('3'):
297 1.1.1.11 joerg /* FALLTHROUGH */
298 1.1.1.11 joerg case ('4'):
299 1.1.1.11 joerg /* FALLTHROUGH */
300 1.1.1.11 joerg case ('I'):
301 1.1.1.11 joerg /* FALLTHROUGH */
302 1.1.1.11 joerg case ('P'):
303 1.1.1.11 joerg /* FALLTHROUGH */
304 1.1.1.11 joerg case ('R'):
305 1.1.1.11 joerg if ('\0' == cp[1])
306 1.1.1.11 joerg ok = 1;
307 1.1.1.11 joerg break;
308 1.1.1.11 joerg case ('B'):
309 1.1.1.11 joerg if ('\0' == cp[1] || ('I' == cp[1] && '\0' == cp[2]))
310 1.1.1.11 joerg ok = 1;
311 1.1.1.11 joerg break;
312 1.1.1.11 joerg case ('C'):
313 1.1.1.11 joerg if ('W' == cp[1] && '\0' == cp[2])
314 1.1.1.11 joerg ok = 1;
315 1.1.1.11 joerg break;
316 1.1.1.11 joerg default:
317 1.1.1.11 joerg break;
318 1.1.1.11 joerg }
319 1.1.1.11 joerg
320 1.1.1.11 joerg if (0 == ok) {
321 1.1.1.11 joerg man_vmsg(m, MANDOCERR_BADFONT,
322 1.1.1.11 joerg n->line, n->pos, "%s", cp);
323 1.1.1.11 joerg *cp = '\0';
324 1.1.1.11 joerg }
325 1.1.1.11 joerg
326 1.1.1.11 joerg if (1 < n->nchild)
327 1.1.1.11 joerg man_vmsg(m, MANDOCERR_ARGCOUNT, n->line, n->pos,
328 1.1.1.11 joerg "want one child (have %d)", n->nchild);
329 1.1.1.11 joerg
330 1.1.1.11 joerg return(1);
331 1.1.1.11 joerg }
332 1.1 joerg
333 1.1 joerg static int
334 1.1 joerg check_sec(CHKARGS)
335 1.1 joerg {
336 1.1 joerg
337 1.1.1.7 joerg if (MAN_HEAD == n->type && 0 == n->nchild) {
338 1.1.1.7 joerg man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
339 1.1.1.7 joerg return(0);
340 1.1.1.7 joerg } else if (MAN_BODY == n->type && 0 == n->nchild)
341 1.1.1.11 joerg man_nmsg(m, n, MANDOCERR_NOBODY);
342 1.1.1.7 joerg
343 1.1 joerg return(1);
344 1.1 joerg }
345 1.1 joerg
346 1.1 joerg
347 1.1 joerg static int
348 1.1 joerg check_part(CHKARGS)
349 1.1 joerg {
350 1.1 joerg
351 1.1 joerg if (MAN_BODY == n->type && 0 == n->nchild)
352 1.1.1.11 joerg man_nmsg(m, n, MANDOCERR_NOBODY);
353 1.1.1.11 joerg
354 1.1 joerg return(1);
355 1.1 joerg }
356 1.1 joerg
357 1.1 joerg
358 1.1 joerg static int
359 1.1 joerg check_par(CHKARGS)
360 1.1 joerg {
361 1.1 joerg
362 1.1 joerg if (MAN_BODY == n->type)
363 1.1 joerg switch (n->tok) {
364 1.1 joerg case (MAN_IP):
365 1.1 joerg /* FALLTHROUGH */
366 1.1 joerg case (MAN_HP):
367 1.1 joerg /* FALLTHROUGH */
368 1.1 joerg case (MAN_TP):
369 1.1 joerg /* Body-less lists are ok. */
370 1.1 joerg break;
371 1.1 joerg default:
372 1.1.1.11 joerg if (0 == n->nchild)
373 1.1.1.11 joerg man_nmsg(m, n, MANDOCERR_NOBODY);
374 1.1.1.11 joerg break;
375 1.1 joerg }
376 1.1 joerg if (MAN_HEAD == n->type)
377 1.1 joerg switch (n->tok) {
378 1.1 joerg case (MAN_PP):
379 1.1 joerg /* FALLTHROUGH */
380 1.1 joerg case (MAN_P):
381 1.1 joerg /* FALLTHROUGH */
382 1.1 joerg case (MAN_LP):
383 1.1 joerg if (n->nchild)
384 1.1.1.11 joerg man_nmsg(m, n, MANDOCERR_ARGSLOST);
385 1.1.1.11 joerg break;
386 1.1.1.11 joerg default:
387 1.1.1.11 joerg break;
388 1.1 joerg }
389 1.1 joerg
390 1.1 joerg return(1);
391 1.1 joerg }
392 1.1 joerg
393 1.1 joerg
394 1.1 joerg static int
395 1.1 joerg check_bline(CHKARGS)
396 1.1 joerg {
397 1.1 joerg
398 1.1 joerg assert( ! (MAN_ELINE & m->flags));
399 1.1.1.7 joerg if (MAN_BLINE & m->flags) {
400 1.1.1.7 joerg man_nmsg(m, n, MANDOCERR_SYNTLINESCOPE);
401 1.1.1.7 joerg return(0);
402 1.1.1.7 joerg }
403 1.1.1.5 joerg
404 1.1 joerg return(1);
405 1.1 joerg }
406 1.1 joerg
407 1.1.1.11 joerg static int
408 1.1.1.11 joerg post_TH(CHKARGS)
409 1.1.1.11 joerg {
410 1.1.1.11 joerg
411 1.1.1.11 joerg if (m->meta.title)
412 1.1.1.11 joerg free(m->meta.title);
413 1.1.1.11 joerg if (m->meta.vol)
414 1.1.1.11 joerg free(m->meta.vol);
415 1.1.1.11 joerg if (m->meta.source)
416 1.1.1.11 joerg free(m->meta.source);
417 1.1.1.11 joerg if (m->meta.msec)
418 1.1.1.11 joerg free(m->meta.msec);
419 1.1.1.11 joerg if (m->meta.rawdate)
420 1.1.1.11 joerg free(m->meta.rawdate);
421 1.1.1.11 joerg
422 1.1.1.11 joerg m->meta.title = m->meta.vol = m->meta.rawdate =
423 1.1.1.11 joerg m->meta.msec = m->meta.source = NULL;
424 1.1.1.11 joerg m->meta.date = 0;
425 1.1.1.11 joerg
426 1.1.1.11 joerg /* ->TITLE<- MSEC DATE SOURCE VOL */
427 1.1.1.11 joerg
428 1.1.1.11 joerg n = n->child;
429 1.1.1.11 joerg assert(n);
430 1.1.1.11 joerg m->meta.title = mandoc_strdup(n->string);
431 1.1.1.11 joerg
432 1.1.1.11 joerg /* TITLE ->MSEC<- DATE SOURCE VOL */
433 1.1.1.11 joerg
434 1.1.1.11 joerg n = n->next;
435 1.1.1.11 joerg assert(n);
436 1.1.1.11 joerg m->meta.msec = mandoc_strdup(n->string);
437 1.1.1.11 joerg
438 1.1.1.11 joerg /* TITLE MSEC ->DATE<- SOURCE VOL */
439 1.1.1.11 joerg
440 1.1.1.11 joerg /*
441 1.1.1.11 joerg * Try to parse the date. If this works, stash the epoch (this
442 1.1.1.11 joerg * is optimal because we can reformat it in the canonical form).
443 1.1.1.11 joerg * If it doesn't parse, isn't specified at all, or is an empty
444 1.1.1.11 joerg * string, then use the current date.
445 1.1.1.11 joerg */
446 1.1.1.11 joerg
447 1.1.1.11 joerg n = n->next;
448 1.1.1.11 joerg if (n && n->string && *n->string) {
449 1.1.1.11 joerg m->meta.date = mandoc_a2time
450 1.1.1.11 joerg (MTIME_ISO_8601, n->string);
451 1.1.1.11 joerg if (0 == m->meta.date) {
452 1.1.1.11 joerg man_nmsg(m, n, MANDOCERR_BADDATE);
453 1.1.1.11 joerg m->meta.rawdate = mandoc_strdup(n->string);
454 1.1.1.11 joerg }
455 1.1.1.11 joerg } else
456 1.1.1.11 joerg m->meta.date = time(NULL);
457 1.1.1.11 joerg
458 1.1.1.11 joerg /* TITLE MSEC DATE ->SOURCE<- VOL */
459 1.1.1.11 joerg
460 1.1.1.11 joerg if (n && (n = n->next))
461 1.1.1.11 joerg m->meta.source = mandoc_strdup(n->string);
462 1.1.1.11 joerg
463 1.1.1.11 joerg /* TITLE MSEC DATE SOURCE ->VOL<- */
464 1.1.1.11 joerg
465 1.1.1.11 joerg if (n && (n = n->next))
466 1.1.1.11 joerg m->meta.vol = mandoc_strdup(n->string);
467 1.1.1.11 joerg
468 1.1.1.11 joerg /*
469 1.1.1.11 joerg * Remove the `TH' node after we've processed it for our
470 1.1.1.11 joerg * meta-data.
471 1.1.1.11 joerg */
472 1.1.1.11 joerg man_node_delete(m, m->last);
473 1.1.1.11 joerg return(1);
474 1.1.1.11 joerg }
475 1.1.1.11 joerg
476 1.1.1.11 joerg static int
477 1.1.1.11 joerg post_nf(CHKARGS)
478 1.1.1.11 joerg {
479 1.1.1.11 joerg
480 1.1.1.11 joerg if (MAN_LITERAL & m->flags)
481 1.1.1.11 joerg man_nmsg(m, n, MANDOCERR_SCOPEREP);
482 1.1.1.11 joerg
483 1.1.1.11 joerg m->flags |= MAN_LITERAL;
484 1.1.1.11 joerg return(1);
485 1.1.1.11 joerg }
486 1.1.1.11 joerg
487 1.1.1.11 joerg static int
488 1.1.1.11 joerg post_fi(CHKARGS)
489 1.1.1.11 joerg {
490 1.1.1.11 joerg
491 1.1.1.11 joerg if ( ! (MAN_LITERAL & m->flags))
492 1.1.1.11 joerg man_nmsg(m, n, MANDOCERR_NOSCOPE);
493 1.1.1.11 joerg
494 1.1.1.11 joerg m->flags &= ~MAN_LITERAL;
495 1.1.1.11 joerg return(1);
496 1.1.1.11 joerg }
497 1.1.1.11 joerg
498 1.1.1.11 joerg static int
499 1.1.1.11 joerg post_UC(CHKARGS)
500 1.1.1.11 joerg {
501 1.1.1.11 joerg static const char * const bsd_versions[] = {
502 1.1.1.11 joerg "3rd Berkeley Distribution",
503 1.1.1.11 joerg "4th Berkeley Distribution",
504 1.1.1.11 joerg "4.2 Berkeley Distribution",
505 1.1.1.11 joerg "4.3 Berkeley Distribution",
506 1.1.1.11 joerg "4.4 Berkeley Distribution",
507 1.1.1.11 joerg };
508 1.1.1.11 joerg
509 1.1.1.11 joerg const char *p, *s;
510 1.1.1.11 joerg
511 1.1.1.11 joerg n = n->child;
512 1.1.1.11 joerg n = m->last->child;
513 1.1.1.11 joerg
514 1.1.1.11 joerg if (NULL == n || MAN_TEXT != n->type)
515 1.1.1.11 joerg p = bsd_versions[0];
516 1.1.1.11 joerg else {
517 1.1.1.11 joerg s = n->string;
518 1.1.1.11 joerg if (0 == strcmp(s, "3"))
519 1.1.1.11 joerg p = bsd_versions[0];
520 1.1.1.11 joerg else if (0 == strcmp(s, "4"))
521 1.1.1.11 joerg p = bsd_versions[1];
522 1.1.1.11 joerg else if (0 == strcmp(s, "5"))
523 1.1.1.11 joerg p = bsd_versions[2];
524 1.1.1.11 joerg else if (0 == strcmp(s, "6"))
525 1.1.1.11 joerg p = bsd_versions[3];
526 1.1.1.11 joerg else if (0 == strcmp(s, "7"))
527 1.1.1.11 joerg p = bsd_versions[4];
528 1.1.1.11 joerg else
529 1.1.1.11 joerg p = bsd_versions[0];
530 1.1.1.11 joerg }
531 1.1.1.11 joerg
532 1.1.1.11 joerg if (m->meta.source)
533 1.1.1.11 joerg free(m->meta.source);
534 1.1.1.11 joerg
535 1.1.1.11 joerg m->meta.source = mandoc_strdup(p);
536 1.1.1.11 joerg return(1);
537 1.1.1.11 joerg }
538 1.1.1.11 joerg
539 1.1.1.11 joerg static int
540 1.1.1.11 joerg post_AT(CHKARGS)
541 1.1.1.11 joerg {
542 1.1.1.11 joerg static const char * const unix_versions[] = {
543 1.1.1.11 joerg "7th Edition",
544 1.1.1.11 joerg "System III",
545 1.1.1.11 joerg "System V",
546 1.1.1.11 joerg "System V Release 2",
547 1.1.1.11 joerg };
548 1.1.1.11 joerg
549 1.1.1.11 joerg const char *p, *s;
550 1.1.1.11 joerg struct man_node *nn;
551 1.1.1.11 joerg
552 1.1.1.11 joerg n = n->child;
553 1.1.1.11 joerg
554 1.1.1.11 joerg if (NULL == n || MAN_TEXT != n->type)
555 1.1.1.11 joerg p = unix_versions[0];
556 1.1.1.11 joerg else {
557 1.1.1.11 joerg s = n->string;
558 1.1.1.11 joerg if (0 == strcmp(s, "3"))
559 1.1.1.11 joerg p = unix_versions[0];
560 1.1.1.11 joerg else if (0 == strcmp(s, "4"))
561 1.1.1.11 joerg p = unix_versions[1];
562 1.1.1.11 joerg else if (0 == strcmp(s, "5")) {
563 1.1.1.11 joerg nn = n->next;
564 1.1.1.11 joerg if (nn && MAN_TEXT == nn->type && nn->string[0])
565 1.1.1.11 joerg p = unix_versions[3];
566 1.1.1.11 joerg else
567 1.1.1.11 joerg p = unix_versions[2];
568 1.1.1.11 joerg } else
569 1.1.1.11 joerg p = unix_versions[0];
570 1.1.1.11 joerg }
571 1.1.1.11 joerg
572 1.1.1.11 joerg if (m->meta.source)
573 1.1.1.11 joerg free(m->meta.source);
574 1.1.1.11 joerg
575 1.1.1.11 joerg m->meta.source = mandoc_strdup(p);
576 1.1.1.11 joerg return(1);
577 1.1.1.11 joerg }
578