/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved * * module: * ignore.c * * purpose: * routines to manage the ignore lists and test names against them, * * contents: * ignore_check ... is a particular file covered by an ignore rule * ignore_file .... add a specific file name to be ignored * ignore_expr .... add a regular expression for files to be ignored * ignore_pgm ..... add a rule to run a program to generate a list * ignore_reset ... flush the internal optimization data structures * * static * ign_hash ... maintain a hash table of ignored names * cheap_check. build up a table of safe suffixes * * notes: * a much simpler implementation could have been provided, but * this test (every file tested against every rule) has the * potential to be EXTREMELY expensive. This module implements * an engine that attempts to optimize the process of determining * that a file has not been ignored. * * the usage scenario is * per base * call ignore_{file,expr,pgm} for each ignore rule * call ignore_check for every file under the base * call ignore_reset when you are done */ #ident "%W% %E% SMI" #include #include #include #include #include "filesync.h" #include "messages.h" /* * routines: */ static struct list *ign_hash(const char *, int); static void cheap_check(const char *); /* * globals */ struct list { char *l_value; /* the actual string */ struct list *l_next; /* pointer to next element */ }; static struct list *expr_list; /* list of regular expressions */ static struct list *file_list[ HASH_SIZE ]; /* hash table of literal names */ static char cheap_last[256]; /* cheap test: last char */ static char cheap_penu[256]; /* cheap test: penultimate char */ /* * routine: * ignore_check * * purpose: * determine whether or not a particular name matches an ignore pattern. * * parameters: * file name * * returns: * true/false * * note: * becuse this routine is called on every single file in * every single sub-directory, it is critical that we make * it fail quickly for most files. The purpose of the cheap_last * and cheap_penu arrays is to quickly determine there is no chance * that a name will match any expression. Most expressions have * wildcards near the front and constant suffixes, so our cheap * test is to look at the last two bytes. */ bool_t ignore_check(const char *name) { struct list *lp; const char *s; /* * start with the cheap test */ for (s = name; *s; s++); if (cheap_last[ (unsigned char) s[-1] ] == 0 || cheap_penu[ (unsigned char) s[-2] ] == 0) return (FALSE); /* check the literal names in the hash table */ if (ign_hash(name, 0)) { if (opt_debug & DBG_IGNORE) fprintf(stderr, "IGNO: match %s\n", name); return (TRUE); } /* check all the regular expressions */ for (lp = expr_list; lp; lp = lp->l_next) { if (gmatch(name, lp->l_value) == 0) continue; if (opt_debug & DBG_IGNORE) fprintf(stderr, "IGNO: regex %s : %s\n", lp->l_value, name); return (TRUE); } return (FALSE); } /* * routine: * ignore_file * * purpose: * to add a specific file to an ignore list * * parameters: * command to run */ void ignore_file(const char *name) { cheap_check(name); (void) ign_hash(name, 1); if (opt_debug & DBG_IGNORE) fprintf(stderr, "IGNO: add file %s\n", name); } /* * routine: * ignore_expr * * purpose: * to add a regular expression to an ignore list * * parameters: * command to run */ void ignore_expr(const char *expr) { struct list *lp; cheap_check(expr); /* allocate a new node and stick it on the front of the list */ lp = malloc(sizeof (*lp)); if (lp == 0) nomem("ignore list"); lp->l_value = strdup(expr); lp->l_next = expr_list; expr_list = lp; if (opt_debug & DBG_IGNORE) fprintf(stderr, "IGNO: add expr %s\n", expr); } /* * routine: * ignore_pgm * * purpose: * to run a program and gather up the ignore list it produces * * parameters: * command to run */ void ignore_pgm(const char *cmd) { char *s; FILE *fp; char inbuf[ MAX_LINE ]; if (opt_debug & DBG_IGNORE) fprintf(stderr, "IGNO: add pgm %s\n", cmd); /* run the command and collect its ouput */ fp = popen(cmd, "r"); if (fp == NULL) { fprintf(stderr, gettext(ERR_badrun), cmd); return; } /* * read each line, strip off the newline and add it to the list */ while (fgets(inbuf, sizeof (inbuf), fp) != 0) { /* strip off any trailing newline */ for (s = inbuf; *s && *s != '\n'; s++); *s = 0; /* skip any leading white space */ for (s = inbuf; *s == ' ' || *s == '\t'; s++); /* add this file to the list */ if (*s) { cheap_check(s); (void) ign_hash(s, 1); if (opt_debug & DBG_IGNORE) fprintf(stderr, "IGNO: ... %s\n", s); } } pclose(fp); } /* * routine: * ign_hash * * purpose: * to find an entry in the hash list * * parameters: * name * allocate flag * * returns: * pointer to new list entry or 0 */ static struct list * ign_hash(const char *name, int alloc) { const unsigned char *s; int i; struct list *lp; struct list **pp; /* perform the hash and find the chain */ for (s = (const unsigned char *) name, i = 0; *s; s++) i += *s; pp = &file_list[i % HASH_SIZE ]; /* search for the specified entry */ for (lp = *pp; lp; lp = *pp) { if (strcmp(name, lp->l_value) == 0) return (lp); pp = &(lp->l_next); } /* if caller said alloc, buy a new node and chain it in */ if (alloc) { lp = malloc(sizeof (*lp)); if (lp == 0) nomem("ignore list"); lp->l_value = strdup(name); lp->l_next = 0; *pp = lp; } return (lp); } /* * routine: * cheap_check * * purpose: * to update the cheap-check arrays for an ignore expression * * parameters: * name/expression */ static void cheap_check(const char *name) { const char *s; unsigned char c; int i; for (s = name; *s; s++); s--; /* if expr ends in a wild card, we are undone */ c = *s; if (c == '*' || c == '?' || c == ']' || c == '}') { for (i = 0; i < 256; i++) { cheap_last[i] = 1; cheap_penu[i] = 1; } return; } else cheap_last[c] = 1; if (s <= name) return; /* check the next to last character too */ c = s[-1]; if (c == '*' || c == '?' || c == ']' || c == '}') { for (i = 0; i < 256; i++) cheap_penu[i] = 1; } else cheap_penu[c] = 1; } /* * routine: * ignore_reset * * purpose: * to free up all the ignore entries so we can start anew */ void ignore_reset(void) { int i; struct list *np = 0; /* for LINT */ struct list *lp; /* clear the cheap check arrays */ for (i = 0; i < 255; i++) { cheap_last[i] = 0; cheap_penu[i] = 0; } /* free all of the literal hash chains */ for (i = 0; i < HASH_SIZE; i++) { for (lp = file_list[i]; lp; lp = np) { np = lp->l_next; free(lp->l_value); free(lp); } file_list[i] = 0; } /* free all of the expressions on the chain */ for (lp = expr_list; lp; lp = np) { np = lp->l_next; free(lp->l_value); free(lp); } expr_list = 0; }