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