1 /* $Id: man_validate.c,v 1.11 2009/10/27 21:40:07 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include <sys/types.h> 18 19 #include <assert.h> 20 #include <ctype.h> 21 #include <errno.h> 22 #include <limits.h> 23 #include <stdarg.h> 24 #include <stdlib.h> 25 26 #include "libman.h" 27 #include "libmandoc.h" 28 29 #define CHKARGS struct man *m, const struct man_node *n 30 31 typedef int (*v_check)(CHKARGS); 32 33 struct man_valid { 34 v_check *pres; 35 v_check *posts; 36 }; 37 38 static int check_bline(CHKARGS); 39 static int check_eq0(CHKARGS); 40 static int check_le1(CHKARGS); 41 static int check_ge2(CHKARGS); 42 static int check_le5(CHKARGS); 43 static int check_par(CHKARGS); 44 static int check_part(CHKARGS); 45 static int check_root(CHKARGS); 46 static int check_sec(CHKARGS); 47 static int check_text(CHKARGS); 48 49 static v_check posts_eq0[] = { check_eq0, NULL }; 50 static v_check posts_ge2_le5[] = { check_ge2, check_le5, NULL }; 51 static v_check posts_par[] = { check_par, NULL }; 52 static v_check posts_part[] = { check_part, NULL }; 53 static v_check posts_sec[] = { check_sec, NULL }; 54 static v_check posts_sp[] = { check_le1, NULL }; 55 static v_check pres_bline[] = { check_bline, NULL }; 56 57 static const struct man_valid man_valids[MAN_MAX] = { 58 { pres_bline, posts_eq0 }, /* br */ 59 { pres_bline, posts_ge2_le5 }, /* TH */ 60 { pres_bline, posts_sec }, /* SH */ 61 { pres_bline, posts_sec }, /* SS */ 62 { pres_bline, posts_par }, /* TP */ 63 { pres_bline, posts_par }, /* LP */ 64 { pres_bline, posts_par }, /* PP */ 65 { pres_bline, posts_par }, /* P */ 66 { pres_bline, posts_par }, /* IP */ 67 { pres_bline, posts_par }, /* HP */ 68 { NULL, NULL }, /* SM */ 69 { NULL, NULL }, /* SB */ 70 { NULL, NULL }, /* BI */ 71 { NULL, NULL }, /* IB */ 72 { NULL, NULL }, /* BR */ 73 { NULL, NULL }, /* RB */ 74 { NULL, NULL }, /* R */ 75 { NULL, NULL }, /* B */ 76 { NULL, NULL }, /* I */ 77 { NULL, NULL }, /* IR */ 78 { NULL, NULL }, /* RI */ 79 { pres_bline, posts_eq0 }, /* na */ 80 { NULL, NULL }, /* i */ 81 { pres_bline, posts_sp }, /* sp */ 82 { pres_bline, posts_eq0 }, /* nf */ 83 { pres_bline, posts_eq0 }, /* fi */ 84 { NULL, NULL }, /* r */ 85 { NULL, NULL }, /* RE */ 86 { NULL, posts_part }, /* RS */ 87 { NULL, NULL }, /* DT */ 88 { NULL, NULL }, /* UC */ 89 { NULL, NULL }, /* PD */ 90 }; 91 92 93 int 94 man_valid_pre(struct man *m, const struct man_node *n) 95 { 96 v_check *cp; 97 98 if (MAN_TEXT == n->type) 99 return(1); 100 if (MAN_ROOT == n->type) 101 return(1); 102 103 if (NULL == (cp = man_valids[n->tok].pres)) 104 return(1); 105 for ( ; *cp; cp++) 106 if ( ! (*cp)(m, n)) 107 return(0); 108 return(1); 109 } 110 111 112 int 113 man_valid_post(struct man *m) 114 { 115 v_check *cp; 116 117 if (MAN_VALID & m->last->flags) 118 return(1); 119 m->last->flags |= MAN_VALID; 120 121 switch (m->last->type) { 122 case (MAN_TEXT): 123 return(check_text(m, m->last)); 124 case (MAN_ROOT): 125 return(check_root(m, m->last)); 126 default: 127 break; 128 } 129 130 if (NULL == (cp = man_valids[m->last->tok].posts)) 131 return(1); 132 for ( ; *cp; cp++) 133 if ( ! (*cp)(m, m->last)) 134 return(0); 135 136 return(1); 137 } 138 139 140 static int 141 check_root(CHKARGS) 142 { 143 144 if (MAN_BLINE & m->flags) 145 return(man_nwarn(m, n, WEXITSCOPE)); 146 if (MAN_ELINE & m->flags) 147 return(man_nwarn(m, n, WEXITSCOPE)); 148 149 m->flags &= ~MAN_BLINE; 150 m->flags &= ~MAN_ELINE; 151 152 if (NULL == m->first->child) 153 return(man_nerr(m, n, WNODATA)); 154 if (NULL == m->meta.title) 155 return(man_nerr(m, n, WNOTITLE)); 156 157 return(1); 158 } 159 160 161 static int 162 check_text(CHKARGS) 163 { 164 const char *p; 165 int pos, c; 166 167 assert(n->string); 168 169 for (p = n->string, pos = n->pos + 1; *p; p++, pos++) { 170 if ('\\' == *p) { 171 c = mandoc_special(p); 172 if (c) { 173 p += c - 1; 174 pos += c - 1; 175 continue; 176 } 177 if ( ! (MAN_IGN_ESCAPE & m->pflags)) 178 return(man_perr(m, n->line, pos, WESCAPE)); 179 if ( ! man_pwarn(m, n->line, pos, WESCAPE)) 180 return(0); 181 continue; 182 } 183 184 if ('\t' == *p || isprint((u_char)*p)) 185 continue; 186 187 if (MAN_IGN_CHARS & m->pflags) 188 return(man_pwarn(m, n->line, pos, WNPRINT)); 189 return(man_perr(m, n->line, pos, WNPRINT)); 190 } 191 192 return(1); 193 } 194 195 196 #define INEQ_DEFINE(x, ineq, name) \ 197 static int \ 198 check_##name(CHKARGS) \ 199 { \ 200 if (n->nchild ineq (x)) \ 201 return(1); \ 202 return(man_verr(m, n->line, n->pos, \ 203 "expected line arguments %s %d, have %d", \ 204 #ineq, (x), n->nchild)); \ 205 } 206 207 INEQ_DEFINE(0, ==, eq0) 208 INEQ_DEFINE(1, <=, le1) 209 INEQ_DEFINE(2, >=, ge2) 210 INEQ_DEFINE(5, <=, le5) 211 212 213 static int 214 check_sec(CHKARGS) 215 { 216 217 if (MAN_BODY == n->type && 0 == n->nchild) 218 return(man_nwarn(m, n, WBODYARGS)); 219 if (MAN_HEAD == n->type && 0 == n->nchild) 220 return(man_nerr(m, n, WHEADARGS)); 221 return(1); 222 } 223 224 225 static int 226 check_part(CHKARGS) 227 { 228 229 if (MAN_BODY == n->type && 0 == n->nchild) 230 return(man_nwarn(m, n, WBODYARGS)); 231 return(1); 232 } 233 234 235 static int 236 check_par(CHKARGS) 237 { 238 239 if (MAN_BODY == n->type) 240 switch (n->tok) { 241 case (MAN_IP): 242 /* FALLTHROUGH */ 243 case (MAN_HP): 244 /* FALLTHROUGH */ 245 case (MAN_TP): 246 /* Body-less lists are ok. */ 247 break; 248 default: 249 if (n->nchild) 250 break; 251 return(man_nwarn(m, n, WBODYARGS)); 252 } 253 if (MAN_HEAD == n->type) 254 switch (n->tok) { 255 case (MAN_PP): 256 /* FALLTHROUGH */ 257 case (MAN_P): 258 /* FALLTHROUGH */ 259 case (MAN_LP): 260 if (0 == n->nchild) 261 break; 262 return(man_nwarn(m, n, WNHEADARGS)); 263 default: 264 if (n->nchild) 265 break; 266 return(man_nwarn(m, n, WHEADARGS)); 267 } 268 269 return(1); 270 } 271 272 273 static int 274 check_bline(CHKARGS) 275 { 276 277 assert( ! (MAN_ELINE & m->flags)); 278 if (MAN_BLINE & m->flags) 279 return(man_nerr(m, n, WLNSCOPE)); 280 return(1); 281 } 282 283