libuutil: move under bmake
[unleashed.git] / usr / src / cmd / filesync / ignore.c
blob0a08037306a4904f72f5b94c4ce8fbad8759a224
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved
25 * module:
26 * ignore.c
28 * purpose:
29 * routines to manage the ignore lists and test names against them,
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
38 * static
39 * ign_hash ... maintain a hash table of ignored names
40 * cheap_check. build up a table of safe suffixes
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.
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
55 #ident "%W% %E% SMI"
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <libgen.h>
62 #include "filesync.h"
63 #include "messages.h"
66 * routines:
68 static struct list *ign_hash(const char *, int);
69 static void cheap_check(const char *);
72 * globals
74 struct list {
75 char *l_value; /* the actual string */
76 struct list *l_next; /* pointer to next element */
79 static struct list *expr_list; /* list of regular expressions */
80 static struct list *file_list[ HASH_SIZE ]; /* hash table of literal names */
82 static char cheap_last[256]; /* cheap test: last char */
83 static char cheap_penu[256]; /* cheap test: penultimate char */
86 * routine:
87 * ignore_check
89 * purpose:
90 * determine whether or not a particular name matches an ignore pattern.
92 * parameters:
93 * file name
95 * returns:
96 * true/false
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.
107 bool_t
108 ignore_check(const char *name)
109 { struct list *lp;
110 const char *s;
113 * start with the cheap test
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);
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);
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;
132 if (opt_debug & DBG_IGNORE)
133 fprintf(stderr, "IGNO: regex %s : %s\n",
134 lp->l_value, name);
135 return (TRUE);
138 return (FALSE);
142 * routine:
143 * ignore_file
145 * purpose:
146 * to add a specific file to an ignore list
148 * parameters:
149 * command to run
151 void
152 ignore_file(const char *name)
154 cheap_check(name);
156 (void) ign_hash(name, 1);
158 if (opt_debug & DBG_IGNORE)
159 fprintf(stderr, "IGNO: add file %s\n", name);
163 * routine:
164 * ignore_expr
166 * purpose:
167 * to add a regular expression to an ignore list
169 * parameters:
170 * command to run
172 void
173 ignore_expr(const char *expr)
174 { struct list *lp;
176 cheap_check(expr);
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;
186 if (opt_debug & DBG_IGNORE)
187 fprintf(stderr, "IGNO: add expr %s\n", expr);
191 * routine:
192 * ignore_pgm
194 * purpose:
195 * to run a program and gather up the ignore list it produces
197 * parameters:
198 * command to run
200 void
201 ignore_pgm(const char *cmd)
202 { char *s;
203 FILE *fp;
204 char inbuf[ MAX_LINE ];
206 if (opt_debug & DBG_IGNORE)
207 fprintf(stderr, "IGNO: add pgm %s\n", cmd);
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;
217 * read each line, strip off the newline and add it to the list
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;
224 /* skip any leading white space */
225 for (s = inbuf; *s == ' ' || *s == '\t'; s++);
227 /* add this file to the list */
228 if (*s) {
229 cheap_check(s);
230 (void) ign_hash(s, 1);
232 if (opt_debug & DBG_IGNORE)
233 fprintf(stderr, "IGNO: ... %s\n", s);
237 pclose(fp);
241 * routine:
242 * ign_hash
244 * purpose:
245 * to find an entry in the hash list
247 * parameters:
248 * name
249 * allocate flag
251 * returns:
252 * pointer to new list entry or 0
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;
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 ];
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);
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;
283 return (lp);
287 * routine:
288 * cheap_check
290 * purpose:
291 * to update the cheap-check arrays for an ignore expression
293 * parameters:
294 * name/expression
296 static void
297 cheap_check(const char *name)
298 { const char *s;
299 unsigned char c;
300 int i;
302 for (s = name; *s; s++);
303 s--;
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;
312 return;
313 } else
314 cheap_last[c] = 1;
316 if (s <= name)
317 return;
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;
329 * routine:
330 * ignore_reset
332 * purpose:
333 * to free up all the ignore entries so we can start anew
335 void
336 ignore_reset(void)
337 { int i;
338 struct list *np = 0; /* for LINT */
339 struct list *lp;
341 /* clear the cheap check arrays */
342 for (i = 0; i < 255; i++) {
343 cheap_last[i] = 0;
344 cheap_penu[i] = 0;
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);
354 file_list[i] = 0;
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);
363 expr_list = 0;