1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved 24 * 25 * module: 26 * ignore.c 27 * 28 * purpose: 29 * routines to manage the ignore lists and test names against them, 30 * 31 * contents: 32 * ignore_check ... is a particular file covered by an ignore rule 33 * ignore_file .... add a specific file name to be ignored 34 * ignore_expr .... add a regular expression for files to be ignored 35 * ignore_pgm ..... add a rule to run a program to generate a list 36 * ignore_reset ... flush the internal optimization data structures 37 * 38 * static 39 * ign_hash ... maintain a hash table of ignored names 40 * cheap_check. build up a table of safe suffixes 41 * 42 * notes: 43 * a much simpler implementation could have been provided, but 44 * this test (every file tested against every rule) has the 45 * potential to be EXTREMELY expensive. This module implements 46 * an engine that attempts to optimize the process of determining 47 * that a file has not been ignored. 48 * 49 * the usage scenario is 50 * per base 51 * call ignore_{file,expr,pgm} for each ignore rule 52 * call ignore_check for every file under the base 53 * call ignore_reset when you are done 54 */ 55 #ident "%W% %E% SMI" 56 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <libgen.h> 61 62 #include "filesync.h" 63 #include "messages.h" 64 65 /* 66 * routines: 67 */ 68 static struct list *ign_hash(const char *, int); 69 static void cheap_check(const char *); 70 71 /* 72 * globals 73 */ 74 struct list { 75 char *l_value; /* the actual string */ 76 struct list *l_next; /* pointer to next element */ 77 }; 78 79 static struct list *expr_list; /* list of regular expressions */ 80 static struct list *file_list[ HASH_SIZE ]; /* hash table of literal names */ 81 82 static char cheap_last[256]; /* cheap test: last char */ 83 static char cheap_penu[256]; /* cheap test: penultimate char */ 84 85 /* 86 * routine: 87 * ignore_check 88 * 89 * purpose: 90 * determine whether or not a particular name matches an ignore pattern. 91 * 92 * parameters: 93 * file name 94 * 95 * returns: 96 * true/false 97 * 98 * note: 99 * becuse this routine is called on every single file in 100 * every single sub-directory, it is critical that we make 101 * it fail quickly for most files. The purpose of the cheap_last 102 * and cheap_penu arrays is to quickly determine there is no chance 103 * that a name will match any expression. Most expressions have 104 * wildcards near the front and constant suffixes, so our cheap 105 * test is to look at the last two bytes. 106 */ 107 bool_t 108 ignore_check(const char *name) 109 { struct list *lp; 110 const char *s; 111 112 /* 113 * start with the cheap test 114 */ 115 for (s = name; *s; s++); 116 if (cheap_last[ (unsigned char) s[-1] ] == 0 || 117 cheap_penu[ (unsigned char) s[-2] ] == 0) 118 return (FALSE); 119 120 /* check the literal names in the hash table */ 121 if (ign_hash(name, 0)) { 122 if (opt_debug & DBG_IGNORE) 123 fprintf(stderr, "IGNO: match %s\n", name); 124 return (TRUE); 125 } 126 127 /* check all the regular expressions */ 128 for (lp = expr_list; lp; lp = lp->l_next) { 129 if (gmatch(name, lp->l_value) == 0) 130 continue; 131 132 if (opt_debug & DBG_IGNORE) 133 fprintf(stderr, "IGNO: regex %s : %s\n", 134 lp->l_value, name); 135 return (TRUE); 136 } 137 138 return (FALSE); 139 } 140 141 /* 142 * routine: 143 * ignore_file 144 * 145 * purpose: 146 * to add a specific file to an ignore list 147 * 148 * parameters: 149 * command to run 150 */ 151 void 152 ignore_file(const char *name) 153 { 154 cheap_check(name); 155 156 (void) ign_hash(name, 1); 157 158 if (opt_debug & DBG_IGNORE) 159 fprintf(stderr, "IGNO: add file %s\n", name); 160 } 161 162 /* 163 * routine: 164 * ignore_expr 165 * 166 * purpose: 167 * to add a regular expression to an ignore list 168 * 169 * parameters: 170 * command to run 171 */ 172 void 173 ignore_expr(const char *expr) 174 { struct list *lp; 175 176 cheap_check(expr); 177 178 /* allocate a new node and stick it on the front of the list */ 179 lp = malloc(sizeof (*lp)); 180 if (lp == 0) 181 nomem("ignore list"); 182 lp->l_value = strdup(expr); 183 lp->l_next = expr_list; 184 expr_list = lp; 185 186 if (opt_debug & DBG_IGNORE) 187 fprintf(stderr, "IGNO: add expr %s\n", expr); 188 } 189 190 /* 191 * routine: 192 * ignore_pgm 193 * 194 * purpose: 195 * to run a program and gather up the ignore list it produces 196 * 197 * parameters: 198 * command to run 199 */ 200 void 201 ignore_pgm(const char *cmd) 202 { char *s; 203 FILE *fp; 204 char inbuf[ MAX_LINE ]; 205 206 if (opt_debug & DBG_IGNORE) 207 fprintf(stderr, "IGNO: add pgm %s\n", cmd); 208 209 /* run the command and collect its ouput */ 210 fp = popen(cmd, "r"); 211 if (fp == NULL) { 212 fprintf(stderr, gettext(ERR_badrun), cmd); 213 return; 214 } 215 216 /* 217 * read each line, strip off the newline and add it to the list 218 */ 219 while (fgets(inbuf, sizeof (inbuf), fp) != 0) { 220 /* strip off any trailing newline */ 221 for (s = inbuf; *s && *s != '\n'; s++); 222 *s = 0; 223 224 /* skip any leading white space */ 225 for (s = inbuf; *s == ' ' || *s == '\t'; s++); 226 227 /* add this file to the list */ 228 if (*s) { 229 cheap_check(s); 230 (void) ign_hash(s, 1); 231 232 if (opt_debug & DBG_IGNORE) 233 fprintf(stderr, "IGNO: ... %s\n", s); 234 } 235 } 236 237 pclose(fp); 238 } 239 240 /* 241 * routine: 242 * ign_hash 243 * 244 * purpose: 245 * to find an entry in the hash list 246 * 247 * parameters: 248 * name 249 * allocate flag 250 * 251 * returns: 252 * pointer to new list entry or 0 253 */ 254 static struct list * 255 ign_hash(const char *name, int alloc) 256 { const unsigned char *s; 257 int i; 258 struct list *lp; 259 struct list **pp; 260 261 /* perform the hash and find the chain */ 262 for (s = (const unsigned char *) name, i = 0; *s; s++) 263 i += *s; 264 pp = &file_list[i % HASH_SIZE ]; 265 266 /* search for the specified entry */ 267 for (lp = *pp; lp; lp = *pp) { 268 if (strcmp(name, lp->l_value) == 0) 269 return (lp); 270 pp = &(lp->l_next); 271 } 272 273 /* if caller said alloc, buy a new node and chain it in */ 274 if (alloc) { 275 lp = malloc(sizeof (*lp)); 276 if (lp == 0) 277 nomem("ignore list"); 278 lp->l_value = strdup(name); 279 lp->l_next = 0; 280 *pp = lp; 281 } 282 283 return (lp); 284 } 285 286 /* 287 * routine: 288 * cheap_check 289 * 290 * purpose: 291 * to update the cheap-check arrays for an ignore expression 292 * 293 * parameters: 294 * name/expression 295 */ 296 static void 297 cheap_check(const char *name) 298 { const char *s; 299 unsigned char c; 300 int i; 301 302 for (s = name; *s; s++); 303 s--; 304 305 /* if expr ends in a wild card, we are undone */ 306 c = *s; 307 if (c == '*' || c == '?' || c == ']' || c == '}') { 308 for (i = 0; i < 256; i++) { 309 cheap_last[i] = 1; 310 cheap_penu[i] = 1; 311 } 312 return; 313 } else 314 cheap_last[c] = 1; 315 316 if (s <= name) 317 return; 318 319 /* check the next to last character too */ 320 c = s[-1]; 321 if (c == '*' || c == '?' || c == ']' || c == '}') { 322 for (i = 0; i < 256; i++) 323 cheap_penu[i] = 1; 324 } else 325 cheap_penu[c] = 1; 326 } 327 328 /* 329 * routine: 330 * ignore_reset 331 * 332 * purpose: 333 * to free up all the ignore entries so we can start anew 334 */ 335 void 336 ignore_reset(void) 337 { int i; 338 struct list *np = 0; /* for LINT */ 339 struct list *lp; 340 341 /* clear the cheap check arrays */ 342 for (i = 0; i < 255; i++) { 343 cheap_last[i] = 0; 344 cheap_penu[i] = 0; 345 } 346 347 /* free all of the literal hash chains */ 348 for (i = 0; i < HASH_SIZE; i++) { 349 for (lp = file_list[i]; lp; lp = np) { 350 np = lp->l_next; 351 free(lp->l_value); 352 free(lp); 353 } 354 file_list[i] = 0; 355 } 356 357 /* free all of the expressions on the chain */ 358 for (lp = expr_list; lp; lp = np) { 359 np = lp->l_next; 360 free(lp->l_value); 361 free(lp); 362 } 363 expr_list = 0; 364 }