xref: /titanic_54/usr/src/cmd/bart/rules.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*7c478bd9Sstevel@tonic-gate 
28*7c478bd9Sstevel@tonic-gate #include <dirent.h>
29*7c478bd9Sstevel@tonic-gate #include <fnmatch.h>
30*7c478bd9Sstevel@tonic-gate #include "bart.h"
31*7c478bd9Sstevel@tonic-gate 
32*7c478bd9Sstevel@tonic-gate static int count_slashes(const char *);
33*7c478bd9Sstevel@tonic-gate static struct rule *gen_rulestruct(void);
34*7c478bd9Sstevel@tonic-gate static struct tree_modifier *gen_tree_modifier(void);
35*7c478bd9Sstevel@tonic-gate static struct dir_component *gen_dir_component(void);
36*7c478bd9Sstevel@tonic-gate static void init_rule(uint_t, struct rule *);
37*7c478bd9Sstevel@tonic-gate static void add_modifier(struct rule *, char *);
38*7c478bd9Sstevel@tonic-gate static struct rule *add_subtree_rule(char *, char *, int, int *);
39*7c478bd9Sstevel@tonic-gate static struct rule *add_single_rule(char *);
40*7c478bd9Sstevel@tonic-gate static void dirs_cleanup(struct dir_component *);
41*7c478bd9Sstevel@tonic-gate static void add_dir(struct dir_component **, char *);
42*7c478bd9Sstevel@tonic-gate static char *lex(FILE *);
43*7c478bd9Sstevel@tonic-gate static int match_subtree(const char *, char *);
44*7c478bd9Sstevel@tonic-gate static struct rule *get_last_entry(boolean_t);
45*7c478bd9Sstevel@tonic-gate 
46*7c478bd9Sstevel@tonic-gate static int	lex_linenum;	/* line number in current input file	*/
47*7c478bd9Sstevel@tonic-gate static struct rule	*first_rule = NULL, *current_rule = NULL;
48*7c478bd9Sstevel@tonic-gate 
49*7c478bd9Sstevel@tonic-gate /*
50*7c478bd9Sstevel@tonic-gate  * This function is responsible for validating whether or not a given file
51*7c478bd9Sstevel@tonic-gate  * should be cataloged, based upon the modifiers for a subtree.
52*7c478bd9Sstevel@tonic-gate  * For example, a line in the rules file: '/home/nickiso *.c' should only
53*7c478bd9Sstevel@tonic-gate  * catalog the C files (based upon pattern matching) in the subtree
54*7c478bd9Sstevel@tonic-gate  * '/home/nickiso'.
55*7c478bd9Sstevel@tonic-gate  *
56*7c478bd9Sstevel@tonic-gate  * Return non-zero if it should be excluded, 0 if it should be cataloged.
57*7c478bd9Sstevel@tonic-gate  */
58*7c478bd9Sstevel@tonic-gate int
59*7c478bd9Sstevel@tonic-gate exclude_fname(const char *fname, char fname_type, struct rule *rule_ptr)
60*7c478bd9Sstevel@tonic-gate {
61*7c478bd9Sstevel@tonic-gate 	char	*pattern, *ptr, component[PATH_MAX], fname_cp[PATH_MAX],
62*7c478bd9Sstevel@tonic-gate 		pattern_cp[PATH_MAX];
63*7c478bd9Sstevel@tonic-gate 	int	match, num_pattern_slash, num_fname_slash, i, slashes_to_adv,
64*7c478bd9Sstevel@tonic-gate 		ret_val = 0;
65*7c478bd9Sstevel@tonic-gate 	struct  tree_modifier   *mod_ptr;
66*7c478bd9Sstevel@tonic-gate 	boolean_t		dir_flag;
67*7c478bd9Sstevel@tonic-gate 
68*7c478bd9Sstevel@tonic-gate 	/*
69*7c478bd9Sstevel@tonic-gate 	 * For a given entry in the rules struct, the modifiers, e.g., '*.c',
70*7c478bd9Sstevel@tonic-gate 	 * are kept in a linked list.  Get a ptr to the head of the list.
71*7c478bd9Sstevel@tonic-gate 	 */
72*7c478bd9Sstevel@tonic-gate 	mod_ptr = rule_ptr->modifiers;
73*7c478bd9Sstevel@tonic-gate 
74*7c478bd9Sstevel@tonic-gate 	/*
75*7c478bd9Sstevel@tonic-gate 	 * Walk through all the modifiers until its they are exhausted OR
76*7c478bd9Sstevel@tonic-gate 	 * until the file should definitely be excluded.
77*7c478bd9Sstevel@tonic-gate 	 */
78*7c478bd9Sstevel@tonic-gate 	while ((mod_ptr != NULL) && !ret_val) {
79*7c478bd9Sstevel@tonic-gate 		/* First, see if we should be matching files or dirs */
80*7c478bd9Sstevel@tonic-gate 		if (mod_ptr->mod_str[(strlen(mod_ptr->mod_str)-1)] == '/')
81*7c478bd9Sstevel@tonic-gate 			dir_flag = B_TRUE;
82*7c478bd9Sstevel@tonic-gate 		else
83*7c478bd9Sstevel@tonic-gate 			dir_flag = B_FALSE;
84*7c478bd9Sstevel@tonic-gate 
85*7c478bd9Sstevel@tonic-gate 		if (mod_ptr->mod_str[0] == '!') {
86*7c478bd9Sstevel@tonic-gate 			pattern = (mod_ptr->mod_str + 1);
87*7c478bd9Sstevel@tonic-gate 		} else {
88*7c478bd9Sstevel@tonic-gate 			pattern = mod_ptr->mod_str;
89*7c478bd9Sstevel@tonic-gate 		}
90*7c478bd9Sstevel@tonic-gate 
91*7c478bd9Sstevel@tonic-gate 		if (dir_flag == B_FALSE) {
92*7c478bd9Sstevel@tonic-gate 			/*
93*7c478bd9Sstevel@tonic-gate 			 * In the case when a user is trying to filter on
94*7c478bd9Sstevel@tonic-gate 			 * FILES and the entry is a directory, its excluded!
95*7c478bd9Sstevel@tonic-gate 			 */
96*7c478bd9Sstevel@tonic-gate 			if (fname_type == 'D') {
97*7c478bd9Sstevel@tonic-gate 				ret_val = 1;
98*7c478bd9Sstevel@tonic-gate 				break;
99*7c478bd9Sstevel@tonic-gate 			}
100*7c478bd9Sstevel@tonic-gate 
101*7c478bd9Sstevel@tonic-gate 			/*
102*7c478bd9Sstevel@tonic-gate 			 * Match patterns against filenames.
103*7c478bd9Sstevel@tonic-gate 			 * Need to be able to handle multi-level patterns,
104*7c478bd9Sstevel@tonic-gate 			 * eg. "SCCS/<star-wildcard>.c", which means
105*7c478bd9Sstevel@tonic-gate 			 * 'only match C files under SCCS directories.
106*7c478bd9Sstevel@tonic-gate 			 *
107*7c478bd9Sstevel@tonic-gate 			 * Determine the number of levels in the filename and
108*7c478bd9Sstevel@tonic-gate 			 * in the pattern.
109*7c478bd9Sstevel@tonic-gate 			 */
110*7c478bd9Sstevel@tonic-gate 			num_pattern_slash = count_slashes(pattern);
111*7c478bd9Sstevel@tonic-gate 			num_fname_slash = count_slashes(fname);
112*7c478bd9Sstevel@tonic-gate 
113*7c478bd9Sstevel@tonic-gate 			/* Check for trivial exclude condition */
114*7c478bd9Sstevel@tonic-gate 			if (num_pattern_slash > num_fname_slash) {
115*7c478bd9Sstevel@tonic-gate 				ret_val = 1;
116*7c478bd9Sstevel@tonic-gate 				break;
117*7c478bd9Sstevel@tonic-gate 			}
118*7c478bd9Sstevel@tonic-gate 
119*7c478bd9Sstevel@tonic-gate 			/*
120*7c478bd9Sstevel@tonic-gate 			 * Do an apples to apples comparison, based upon the
121*7c478bd9Sstevel@tonic-gate 			 * number of levels:
122*7c478bd9Sstevel@tonic-gate 			 *
123*7c478bd9Sstevel@tonic-gate 			 * Assume fname is /A/B/C/D/E and the pattern is D/E.
124*7c478bd9Sstevel@tonic-gate 			 * In that case, 'ptr' will point to "D/E" and
125*7c478bd9Sstevel@tonic-gate 			 * 'slashes_to_adv' will be '4'.
126*7c478bd9Sstevel@tonic-gate 			 */
127*7c478bd9Sstevel@tonic-gate 			(void) strlcpy(fname_cp, fname, sizeof (fname_cp));
128*7c478bd9Sstevel@tonic-gate 			ptr = fname_cp;
129*7c478bd9Sstevel@tonic-gate 			slashes_to_adv = num_fname_slash - num_pattern_slash;
130*7c478bd9Sstevel@tonic-gate 			for (i = 0; i < slashes_to_adv; i++)  {
131*7c478bd9Sstevel@tonic-gate 				ptr = strchr(ptr, '/');
132*7c478bd9Sstevel@tonic-gate 				ptr++;
133*7c478bd9Sstevel@tonic-gate 			}
134*7c478bd9Sstevel@tonic-gate 			if ((pattern[0] == '.') && (pattern[1] == '.') &&
135*7c478bd9Sstevel@tonic-gate 			    (pattern[2] == '/')) {
136*7c478bd9Sstevel@tonic-gate 				pattern = strchr(pattern, '/');
137*7c478bd9Sstevel@tonic-gate 				ptr = strchr(ptr, '/');
138*7c478bd9Sstevel@tonic-gate 			}
139*7c478bd9Sstevel@tonic-gate 
140*7c478bd9Sstevel@tonic-gate 
141*7c478bd9Sstevel@tonic-gate 			/* OK, now do the fnmatch() and set the return value */
142*7c478bd9Sstevel@tonic-gate 			match = fnmatch(pattern, ptr, FNM_PATHNAME);
143*7c478bd9Sstevel@tonic-gate 
144*7c478bd9Sstevel@tonic-gate 			if (match == 0)
145*7c478bd9Sstevel@tonic-gate 				ret_val = 0;
146*7c478bd9Sstevel@tonic-gate 			else
147*7c478bd9Sstevel@tonic-gate 				ret_val = 1;
148*7c478bd9Sstevel@tonic-gate 
149*7c478bd9Sstevel@tonic-gate 		} else {
150*7c478bd9Sstevel@tonic-gate 			/*
151*7c478bd9Sstevel@tonic-gate 			 * The rule requires directory matching.
152*7c478bd9Sstevel@tonic-gate 			 *
153*7c478bd9Sstevel@tonic-gate 			 * First, make copies, since both the pattern and
154*7c478bd9Sstevel@tonic-gate 			 * filename need to be modified.
155*7c478bd9Sstevel@tonic-gate 			 *
156*7c478bd9Sstevel@tonic-gate 			 * When copying 'fname', ignore the relocatable root
157*7c478bd9Sstevel@tonic-gate 			 * since pattern matching is done for the string AFTER
158*7c478bd9Sstevel@tonic-gate 			 * the relocatable root.  For example, if the
159*7c478bd9Sstevel@tonic-gate 			 * relocatable root is "/dir1/dir2/dir3" and the
160*7c478bd9Sstevel@tonic-gate 			 * pattern is "dir3/", we do NOT want to include every
161*7c478bd9Sstevel@tonic-gate 			 * directory in the relocatable root.  Instead, we
162*7c478bd9Sstevel@tonic-gate 			 * only want to include subtrees that look like:
163*7c478bd9Sstevel@tonic-gate 			 * "/dir1/dir2/dir3/....dir3/....."
164*7c478bd9Sstevel@tonic-gate 			 *
165*7c478bd9Sstevel@tonic-gate 			 * NOTE: the 'pattern_cp' does NOT have a trailing '/':
166*7c478bd9Sstevel@tonic-gate 			 * necessary for fnmatch().
167*7c478bd9Sstevel@tonic-gate 			 */
168*7c478bd9Sstevel@tonic-gate 			(void) strlcpy(fname_cp,
169*7c478bd9Sstevel@tonic-gate 			    (fname+strlen(rule_ptr->subtree)),
170*7c478bd9Sstevel@tonic-gate 			    sizeof (fname_cp));
171*7c478bd9Sstevel@tonic-gate 			(void) strlcpy(pattern_cp, pattern,
172*7c478bd9Sstevel@tonic-gate 			    sizeof (pattern_cp));
173*7c478bd9Sstevel@tonic-gate 
174*7c478bd9Sstevel@tonic-gate 			/*
175*7c478bd9Sstevel@tonic-gate 			 * For non-directory entries, remove the trailing
176*7c478bd9Sstevel@tonic-gate 			 * name, e.g., for a file /A/B/C/D where 'D' is
177*7c478bd9Sstevel@tonic-gate 			 * the actual filename, remove the 'D' since it
178*7c478bd9Sstevel@tonic-gate 			 * should *not* be considered in the directory match.
179*7c478bd9Sstevel@tonic-gate 			 */
180*7c478bd9Sstevel@tonic-gate 			if (fname_type != 'D') {
181*7c478bd9Sstevel@tonic-gate 				ptr = strrchr(fname_cp, '/');
182*7c478bd9Sstevel@tonic-gate 				if (ptr != NULL)
183*7c478bd9Sstevel@tonic-gate 					*ptr = '\0';
184*7c478bd9Sstevel@tonic-gate 
185*7c478bd9Sstevel@tonic-gate 				/* Trivial case: simple filename */
186*7c478bd9Sstevel@tonic-gate 				if (strlen(fname_cp) == 0) {
187*7c478bd9Sstevel@tonic-gate 					ret_val = 1;
188*7c478bd9Sstevel@tonic-gate 					break;
189*7c478bd9Sstevel@tonic-gate 				}
190*7c478bd9Sstevel@tonic-gate 			}
191*7c478bd9Sstevel@tonic-gate 
192*7c478bd9Sstevel@tonic-gate 			/* Count the # of slashes in the pattern and fname */
193*7c478bd9Sstevel@tonic-gate 			num_pattern_slash = count_slashes(pattern_cp);
194*7c478bd9Sstevel@tonic-gate 			num_fname_slash = count_slashes(fname_cp);
195*7c478bd9Sstevel@tonic-gate 
196*7c478bd9Sstevel@tonic-gate 			/*
197*7c478bd9Sstevel@tonic-gate 			 * fname_cp is too short, bail!
198*7c478bd9Sstevel@tonic-gate 			 */
199*7c478bd9Sstevel@tonic-gate 			if (num_pattern_slash > num_fname_slash) {
200*7c478bd9Sstevel@tonic-gate 				ret_val = 1;
201*7c478bd9Sstevel@tonic-gate 				break;
202*7c478bd9Sstevel@tonic-gate 			}
203*7c478bd9Sstevel@tonic-gate 
204*7c478bd9Sstevel@tonic-gate 			/*
205*7c478bd9Sstevel@tonic-gate 			 * OK, walk through the filename and check for the
206*7c478bd9Sstevel@tonic-gate 			 * pattern.
207*7c478bd9Sstevel@tonic-gate 			 * This loop will termate when the match is found OR
208*7c478bd9Sstevel@tonic-gate 			 * there fname is too short to possibly match.
209*7c478bd9Sstevel@tonic-gate 			 */
210*7c478bd9Sstevel@tonic-gate 			while (strlen(fname_cp) > 0) {
211*7c478bd9Sstevel@tonic-gate 				num_fname_slash = count_slashes(fname_cp);
212*7c478bd9Sstevel@tonic-gate 
213*7c478bd9Sstevel@tonic-gate 				/*
214*7c478bd9Sstevel@tonic-gate 				 * fname is too short, bail!
215*7c478bd9Sstevel@tonic-gate 				 */
216*7c478bd9Sstevel@tonic-gate 				if (num_pattern_slash > num_fname_slash) {
217*7c478bd9Sstevel@tonic-gate 					ret_val = 1;
218*7c478bd9Sstevel@tonic-gate 					break;
219*7c478bd9Sstevel@tonic-gate 				}
220*7c478bd9Sstevel@tonic-gate 
221*7c478bd9Sstevel@tonic-gate 				/*
222*7c478bd9Sstevel@tonic-gate 				 * The next stanza selects an appropriate
223*7c478bd9Sstevel@tonic-gate 				 * substring of the filename.
224*7c478bd9Sstevel@tonic-gate 				 * For example, if pattern is 'C/D/E' and
225*7c478bd9Sstevel@tonic-gate 				 * filename is '/A/B/C/D/E', this stanza will
226*7c478bd9Sstevel@tonic-gate 				 * set ptr to 'C/D/E'.
227*7c478bd9Sstevel@tonic-gate 				 */
228*7c478bd9Sstevel@tonic-gate 
229*7c478bd9Sstevel@tonic-gate 				component[0] = '\0';
230*7c478bd9Sstevel@tonic-gate 				if (num_fname_slash > 0) {
231*7c478bd9Sstevel@tonic-gate 					ptr = (fname_cp+1);
232*7c478bd9Sstevel@tonic-gate 
233*7c478bd9Sstevel@tonic-gate 					for (i = 0; i < (num_pattern_slash-1);
234*7c478bd9Sstevel@tonic-gate 					    i++) {
235*7c478bd9Sstevel@tonic-gate 						ptr = strchr(ptr, '/');
236*7c478bd9Sstevel@tonic-gate 						ptr++;
237*7c478bd9Sstevel@tonic-gate 					}
238*7c478bd9Sstevel@tonic-gate 
239*7c478bd9Sstevel@tonic-gate 					if (ptr != NULL)
240*7c478bd9Sstevel@tonic-gate 						(void) strlcpy(component, ptr,
241*7c478bd9Sstevel@tonic-gate 						    sizeof (component));
242*7c478bd9Sstevel@tonic-gate 
243*7c478bd9Sstevel@tonic-gate 				} else
244*7c478bd9Sstevel@tonic-gate 					(void) strlcpy(component, fname_cp,
245*7c478bd9Sstevel@tonic-gate 					    sizeof (component));
246*7c478bd9Sstevel@tonic-gate 
247*7c478bd9Sstevel@tonic-gate 				/*
248*7c478bd9Sstevel@tonic-gate 				 * See if they match.  If they do, set exclude
249*7c478bd9Sstevel@tonic-gate 				 * to appropriate value and exit.
250*7c478bd9Sstevel@tonic-gate 				 */
251*7c478bd9Sstevel@tonic-gate 				match =  fnmatch(pattern_cp, component,
252*7c478bd9Sstevel@tonic-gate 				    FNM_PATHNAME);
253*7c478bd9Sstevel@tonic-gate 
254*7c478bd9Sstevel@tonic-gate 				/*
255*7c478bd9Sstevel@tonic-gate 				 * Special case: match "/" and "*" or "?".
256*7c478bd9Sstevel@tonic-gate 				 * Necessary since explicitly NOT matched by
257*7c478bd9Sstevel@tonic-gate 				 * fnmatch()
258*7c478bd9Sstevel@tonic-gate 				 */
259*7c478bd9Sstevel@tonic-gate 				if ((match == 1) && (strlen(component) == 1) &&
260*7c478bd9Sstevel@tonic-gate 				    (component[0] == '/') &&
261*7c478bd9Sstevel@tonic-gate 				    (strlen(pattern_cp) == 1)) {
262*7c478bd9Sstevel@tonic-gate 					if ((pattern_cp[0] == '?') ||
263*7c478bd9Sstevel@tonic-gate 					    (pattern_cp[0] == '*'))
264*7c478bd9Sstevel@tonic-gate 						match = 0;
265*7c478bd9Sstevel@tonic-gate 				}
266*7c478bd9Sstevel@tonic-gate 
267*7c478bd9Sstevel@tonic-gate 				/*
268*7c478bd9Sstevel@tonic-gate 				 * Test to see if there is a match.
269*7c478bd9Sstevel@tonic-gate 				 *
270*7c478bd9Sstevel@tonic-gate 				 * If it matches we are done for this rule.
271*7c478bd9Sstevel@tonic-gate 				 *
272*7c478bd9Sstevel@tonic-gate 				 * If there is NOT a match, then we need
273*7c478bd9Sstevel@tonic-gate 				 * to iterate down the filename until it
274*7c478bd9Sstevel@tonic-gate 				 * matches OR we determine it cannot match.
275*7c478bd9Sstevel@tonic-gate 				 */
276*7c478bd9Sstevel@tonic-gate 				if (match == 0) {
277*7c478bd9Sstevel@tonic-gate 					ret_val = 0;
278*7c478bd9Sstevel@tonic-gate 					break;
279*7c478bd9Sstevel@tonic-gate 				} else {
280*7c478bd9Sstevel@tonic-gate 					/*
281*7c478bd9Sstevel@tonic-gate 					 * No match.  Remove the last 'segment'
282*7c478bd9Sstevel@tonic-gate 					 * of the filename, e.g., if its
283*7c478bd9Sstevel@tonic-gate 					 * "/A/B/C/D/E", then remove "/E".
284*7c478bd9Sstevel@tonic-gate 					 *  If nothing left to remove, we are
285*7c478bd9Sstevel@tonic-gate 					 * done.
286*7c478bd9Sstevel@tonic-gate 					 */
287*7c478bd9Sstevel@tonic-gate 					ptr = strrchr(fname_cp, '/');
288*7c478bd9Sstevel@tonic-gate 					if (ptr != NULL)
289*7c478bd9Sstevel@tonic-gate 						*ptr = '\0';
290*7c478bd9Sstevel@tonic-gate 					else {
291*7c478bd9Sstevel@tonic-gate 						fname_cp[0] = '\0';
292*7c478bd9Sstevel@tonic-gate 						ret_val = 1;
293*7c478bd9Sstevel@tonic-gate 					}
294*7c478bd9Sstevel@tonic-gate 				}
295*7c478bd9Sstevel@tonic-gate 			}
296*7c478bd9Sstevel@tonic-gate 		}
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate 		/*
299*7c478bd9Sstevel@tonic-gate 		 * Take into account whether or not this rule began with
300*7c478bd9Sstevel@tonic-gate 		 * a '!'
301*7c478bd9Sstevel@tonic-gate 		 */
302*7c478bd9Sstevel@tonic-gate 		if (mod_ptr->include == B_FALSE) {
303*7c478bd9Sstevel@tonic-gate 			if (ret_val == 0)
304*7c478bd9Sstevel@tonic-gate 				ret_val = 1;
305*7c478bd9Sstevel@tonic-gate 			else ret_val = 0;
306*7c478bd9Sstevel@tonic-gate 		}
307*7c478bd9Sstevel@tonic-gate 
308*7c478bd9Sstevel@tonic-gate 		/* Advance to the next modifier */
309*7c478bd9Sstevel@tonic-gate 		mod_ptr = mod_ptr->next;
310*7c478bd9Sstevel@tonic-gate 	}
311*7c478bd9Sstevel@tonic-gate 
312*7c478bd9Sstevel@tonic-gate 	return (ret_val);
313*7c478bd9Sstevel@tonic-gate }
314*7c478bd9Sstevel@tonic-gate 
315*7c478bd9Sstevel@tonic-gate static int
316*7c478bd9Sstevel@tonic-gate count_slashes(const char *in_path)
317*7c478bd9Sstevel@tonic-gate {
318*7c478bd9Sstevel@tonic-gate 	int num_fname_slash = 0;
319*7c478bd9Sstevel@tonic-gate 	const char *p;
320*7c478bd9Sstevel@tonic-gate 	for (p = in_path; *p != '\0'; p++)
321*7c478bd9Sstevel@tonic-gate 		if (*p == '/')
322*7c478bd9Sstevel@tonic-gate 			num_fname_slash++;
323*7c478bd9Sstevel@tonic-gate 	return (num_fname_slash);
324*7c478bd9Sstevel@tonic-gate }
325*7c478bd9Sstevel@tonic-gate 
326*7c478bd9Sstevel@tonic-gate static struct rule *
327*7c478bd9Sstevel@tonic-gate gen_rulestruct(void)
328*7c478bd9Sstevel@tonic-gate {
329*7c478bd9Sstevel@tonic-gate 	struct rule	*new_rule;
330*7c478bd9Sstevel@tonic-gate 
331*7c478bd9Sstevel@tonic-gate 	new_rule = (struct rule *)safe_calloc(sizeof (struct rule));
332*7c478bd9Sstevel@tonic-gate 	new_rule->traversed = B_FALSE;
333*7c478bd9Sstevel@tonic-gate 	return (new_rule);
334*7c478bd9Sstevel@tonic-gate }
335*7c478bd9Sstevel@tonic-gate 
336*7c478bd9Sstevel@tonic-gate static struct tree_modifier *
337*7c478bd9Sstevel@tonic-gate gen_tree_modifier(void)
338*7c478bd9Sstevel@tonic-gate {
339*7c478bd9Sstevel@tonic-gate 	struct tree_modifier	*new_modifier;
340*7c478bd9Sstevel@tonic-gate 
341*7c478bd9Sstevel@tonic-gate 	new_modifier = (struct tree_modifier *)safe_calloc
342*7c478bd9Sstevel@tonic-gate 	    (sizeof (struct tree_modifier));
343*7c478bd9Sstevel@tonic-gate 	return (new_modifier);
344*7c478bd9Sstevel@tonic-gate }
345*7c478bd9Sstevel@tonic-gate 
346*7c478bd9Sstevel@tonic-gate static struct dir_component *
347*7c478bd9Sstevel@tonic-gate gen_dir_component(void)
348*7c478bd9Sstevel@tonic-gate {
349*7c478bd9Sstevel@tonic-gate 	struct dir_component	*new_dir;
350*7c478bd9Sstevel@tonic-gate 
351*7c478bd9Sstevel@tonic-gate 	new_dir = (struct dir_component *)safe_calloc
352*7c478bd9Sstevel@tonic-gate 	    (sizeof (struct dir_component));
353*7c478bd9Sstevel@tonic-gate 	return (new_dir);
354*7c478bd9Sstevel@tonic-gate }
355*7c478bd9Sstevel@tonic-gate 
356*7c478bd9Sstevel@tonic-gate /*
357*7c478bd9Sstevel@tonic-gate  * Set up a default rule when there is no rules file.
358*7c478bd9Sstevel@tonic-gate  */
359*7c478bd9Sstevel@tonic-gate static struct rule *
360*7c478bd9Sstevel@tonic-gate setup_default_rule(char *reloc_root, uint_t flags)
361*7c478bd9Sstevel@tonic-gate {
362*7c478bd9Sstevel@tonic-gate 	struct	rule	*new_rule;
363*7c478bd9Sstevel@tonic-gate 
364*7c478bd9Sstevel@tonic-gate 	new_rule = add_single_rule(reloc_root[0] == '\0' ? "/" : reloc_root);
365*7c478bd9Sstevel@tonic-gate 	init_rule(flags, new_rule);
366*7c478bd9Sstevel@tonic-gate 	add_modifier(new_rule, "*");
367*7c478bd9Sstevel@tonic-gate 
368*7c478bd9Sstevel@tonic-gate 	return (new_rule);
369*7c478bd9Sstevel@tonic-gate }
370*7c478bd9Sstevel@tonic-gate 
371*7c478bd9Sstevel@tonic-gate /*
372*7c478bd9Sstevel@tonic-gate  * Utility function, used to initialize the flag in a new rule structure.
373*7c478bd9Sstevel@tonic-gate  */
374*7c478bd9Sstevel@tonic-gate static void
375*7c478bd9Sstevel@tonic-gate init_rule(uint_t flags, struct rule *new_rule)
376*7c478bd9Sstevel@tonic-gate {
377*7c478bd9Sstevel@tonic-gate 
378*7c478bd9Sstevel@tonic-gate 	if (new_rule == NULL)
379*7c478bd9Sstevel@tonic-gate 		return;
380*7c478bd9Sstevel@tonic-gate 	new_rule->attr_list = flags;
381*7c478bd9Sstevel@tonic-gate }
382*7c478bd9Sstevel@tonic-gate 
383*7c478bd9Sstevel@tonic-gate /*
384*7c478bd9Sstevel@tonic-gate  * Function to read the rulesfile.  Used by both 'bart create' and
385*7c478bd9Sstevel@tonic-gate  * 'bart compare'.
386*7c478bd9Sstevel@tonic-gate  */
387*7c478bd9Sstevel@tonic-gate int
388*7c478bd9Sstevel@tonic-gate read_rules(FILE *file, char *reloc_root, uint_t in_flags, int create)
389*7c478bd9Sstevel@tonic-gate {
390*7c478bd9Sstevel@tonic-gate 	char		*s;
391*7c478bd9Sstevel@tonic-gate 	struct rule	*block_begin = NULL, *new_rule, *rp;
392*7c478bd9Sstevel@tonic-gate 	struct attr_keyword *akp;
393*7c478bd9Sstevel@tonic-gate 	int		check_flag, ignore_flag, syntax_err, ret_code;
394*7c478bd9Sstevel@tonic-gate 
395*7c478bd9Sstevel@tonic-gate 	ret_code = EXIT;
396*7c478bd9Sstevel@tonic-gate 
397*7c478bd9Sstevel@tonic-gate 	lex_linenum = 0;
398*7c478bd9Sstevel@tonic-gate 	check_flag = 0;
399*7c478bd9Sstevel@tonic-gate 	ignore_flag = 0;
400*7c478bd9Sstevel@tonic-gate 	syntax_err = 0;
401*7c478bd9Sstevel@tonic-gate 
402*7c478bd9Sstevel@tonic-gate 	if (file == NULL) {
403*7c478bd9Sstevel@tonic-gate 		(void) setup_default_rule(reloc_root, in_flags);
404*7c478bd9Sstevel@tonic-gate 		return (ret_code);
405*7c478bd9Sstevel@tonic-gate 	} else if (!create) {
406*7c478bd9Sstevel@tonic-gate 		block_begin = setup_default_rule("/", in_flags);
407*7c478bd9Sstevel@tonic-gate 	}
408*7c478bd9Sstevel@tonic-gate 
409*7c478bd9Sstevel@tonic-gate 	while (!feof(file)) {
410*7c478bd9Sstevel@tonic-gate 		/* Read a line from the file */
411*7c478bd9Sstevel@tonic-gate 		s = lex(file);
412*7c478bd9Sstevel@tonic-gate 
413*7c478bd9Sstevel@tonic-gate 		/* skip blank lines and comments */
414*7c478bd9Sstevel@tonic-gate 		if (s == NULL || *s == 0 || *s == '#')
415*7c478bd9Sstevel@tonic-gate 			continue;
416*7c478bd9Sstevel@tonic-gate 
417*7c478bd9Sstevel@tonic-gate 		/*
418*7c478bd9Sstevel@tonic-gate 		 * Beginning of a subtree and possibly a new block.
419*7c478bd9Sstevel@tonic-gate 		 *
420*7c478bd9Sstevel@tonic-gate 		 * If this is a new block, keep track of the beginning of
421*7c478bd9Sstevel@tonic-gate 		 * the block. if there are directives later on, we need to
422*7c478bd9Sstevel@tonic-gate 		 * apply that directive to all members of the block.
423*7c478bd9Sstevel@tonic-gate 		 *
424*7c478bd9Sstevel@tonic-gate 		 * If the first stmt in the file was an 'IGNORE all' or
425*7c478bd9Sstevel@tonic-gate 		 * 'IGNORE contents', we need to keep track of it and
426*7c478bd9Sstevel@tonic-gate 		 * automatically switch off contents checking for new
427*7c478bd9Sstevel@tonic-gate 		 * subtrees.
428*7c478bd9Sstevel@tonic-gate 		 */
429*7c478bd9Sstevel@tonic-gate 		if (s[0] == '/') {
430*7c478bd9Sstevel@tonic-gate 			new_rule = add_subtree_rule(s, reloc_root, create,
431*7c478bd9Sstevel@tonic-gate 			    &ret_code);
432*7c478bd9Sstevel@tonic-gate 
433*7c478bd9Sstevel@tonic-gate 			s = lex(0);
434*7c478bd9Sstevel@tonic-gate 			while ((s != NULL) && (*s != 0) && (*s != '#')) {
435*7c478bd9Sstevel@tonic-gate 				add_modifier(new_rule, s);
436*7c478bd9Sstevel@tonic-gate 				s = lex(0);
437*7c478bd9Sstevel@tonic-gate 			}
438*7c478bd9Sstevel@tonic-gate 
439*7c478bd9Sstevel@tonic-gate 			/* Found a new block, keep track of the beginning */
440*7c478bd9Sstevel@tonic-gate 			if (block_begin == NULL ||
441*7c478bd9Sstevel@tonic-gate 			    (ignore_flag != 0) || (check_flag != 0)) {
442*7c478bd9Sstevel@tonic-gate 				block_begin = new_rule;
443*7c478bd9Sstevel@tonic-gate 				check_flag = 0;
444*7c478bd9Sstevel@tonic-gate 				ignore_flag = 0;
445*7c478bd9Sstevel@tonic-gate 			}
446*7c478bd9Sstevel@tonic-gate 
447*7c478bd9Sstevel@tonic-gate 			/* Apply global settings to this block, if any */
448*7c478bd9Sstevel@tonic-gate 			init_rule(in_flags, new_rule);
449*7c478bd9Sstevel@tonic-gate 		} else if (IGNORE_KEYWORD(s) || CHECK_KEYWORD(s)) {
450*7c478bd9Sstevel@tonic-gate 			int check_kw;
451*7c478bd9Sstevel@tonic-gate 
452*7c478bd9Sstevel@tonic-gate 			if (IGNORE_KEYWORD(s)) {
453*7c478bd9Sstevel@tonic-gate 				ignore_flag++;
454*7c478bd9Sstevel@tonic-gate 				check_kw = 0;
455*7c478bd9Sstevel@tonic-gate 			} else {
456*7c478bd9Sstevel@tonic-gate 				check_flag++;
457*7c478bd9Sstevel@tonic-gate 				check_kw = 1;
458*7c478bd9Sstevel@tonic-gate 			}
459*7c478bd9Sstevel@tonic-gate 
460*7c478bd9Sstevel@tonic-gate 			/* Parse next token */
461*7c478bd9Sstevel@tonic-gate 			s = lex(0);
462*7c478bd9Sstevel@tonic-gate 			while ((s != NULL) && (*s != 0) && (*s != '#')) {
463*7c478bd9Sstevel@tonic-gate 				akp = attr_keylookup(s);
464*7c478bd9Sstevel@tonic-gate 				if (akp == NULL) {
465*7c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, SYNTAX_ERR, s);
466*7c478bd9Sstevel@tonic-gate 					syntax_err++;
467*7c478bd9Sstevel@tonic-gate 					exit(2);
468*7c478bd9Sstevel@tonic-gate 				}
469*7c478bd9Sstevel@tonic-gate 
470*7c478bd9Sstevel@tonic-gate 				/*
471*7c478bd9Sstevel@tonic-gate 				 * For all the flags, check if this is a global
472*7c478bd9Sstevel@tonic-gate 				 * IGNORE/CHECK. If so, set the global flag.
473*7c478bd9Sstevel@tonic-gate 				 *
474*7c478bd9Sstevel@tonic-gate 				 * NOTE: The only time you can have a
475*7c478bd9Sstevel@tonic-gate 				 * global ignore is when its the
476*7c478bd9Sstevel@tonic-gate 				 * stmt before any blocks have been
477*7c478bd9Sstevel@tonic-gate 				 * spec'd.
478*7c478bd9Sstevel@tonic-gate 				 */
479*7c478bd9Sstevel@tonic-gate 				if (block_begin == NULL) {
480*7c478bd9Sstevel@tonic-gate 					if (check_kw)
481*7c478bd9Sstevel@tonic-gate 						in_flags |= akp->ak_flags;
482*7c478bd9Sstevel@tonic-gate 					else
483*7c478bd9Sstevel@tonic-gate 						in_flags &= ~(akp->ak_flags);
484*7c478bd9Sstevel@tonic-gate 				} else {
485*7c478bd9Sstevel@tonic-gate 					for (rp = block_begin; rp != NULL;
486*7c478bd9Sstevel@tonic-gate 					    rp = rp->next) {
487*7c478bd9Sstevel@tonic-gate 						if (check_kw)
488*7c478bd9Sstevel@tonic-gate 							rp->attr_list |=
489*7c478bd9Sstevel@tonic-gate 							    akp->ak_flags;
490*7c478bd9Sstevel@tonic-gate 						else
491*7c478bd9Sstevel@tonic-gate 							rp->attr_list &=
492*7c478bd9Sstevel@tonic-gate 							    ~(akp->ak_flags);
493*7c478bd9Sstevel@tonic-gate 					}
494*7c478bd9Sstevel@tonic-gate 				}
495*7c478bd9Sstevel@tonic-gate 
496*7c478bd9Sstevel@tonic-gate 				/* Parse next token */
497*7c478bd9Sstevel@tonic-gate 				s = lex(0);
498*7c478bd9Sstevel@tonic-gate 			}
499*7c478bd9Sstevel@tonic-gate 		} else {
500*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, SYNTAX_ERR, s);
501*7c478bd9Sstevel@tonic-gate 			s = lex(0);
502*7c478bd9Sstevel@tonic-gate 			while (s != NULL && *s != 0) {
503*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, " %s", s);
504*7c478bd9Sstevel@tonic-gate 				s = lex(0);
505*7c478bd9Sstevel@tonic-gate 			}
506*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "\n");
507*7c478bd9Sstevel@tonic-gate 			syntax_err++;
508*7c478bd9Sstevel@tonic-gate 		}
509*7c478bd9Sstevel@tonic-gate 	}
510*7c478bd9Sstevel@tonic-gate 
511*7c478bd9Sstevel@tonic-gate 	(void) fclose(file);
512*7c478bd9Sstevel@tonic-gate 
513*7c478bd9Sstevel@tonic-gate 	if (syntax_err) {
514*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, SYNTAX_ABORT);
515*7c478bd9Sstevel@tonic-gate 		exit(2);
516*7c478bd9Sstevel@tonic-gate 	}
517*7c478bd9Sstevel@tonic-gate 
518*7c478bd9Sstevel@tonic-gate 	return (ret_code);
519*7c478bd9Sstevel@tonic-gate }
520*7c478bd9Sstevel@tonic-gate 
521*7c478bd9Sstevel@tonic-gate static void
522*7c478bd9Sstevel@tonic-gate add_modifier(struct rule *rule, char *modifier_str)
523*7c478bd9Sstevel@tonic-gate {
524*7c478bd9Sstevel@tonic-gate 	struct tree_modifier	*new_mod_ptr, *curr_mod_ptr;
525*7c478bd9Sstevel@tonic-gate 	struct rule		*this_rule;
526*7c478bd9Sstevel@tonic-gate 
527*7c478bd9Sstevel@tonic-gate 	this_rule = rule;
528*7c478bd9Sstevel@tonic-gate 	while (this_rule != NULL) {
529*7c478bd9Sstevel@tonic-gate 		new_mod_ptr = gen_tree_modifier();
530*7c478bd9Sstevel@tonic-gate 		new_mod_ptr->mod_str = safe_strdup(modifier_str);
531*7c478bd9Sstevel@tonic-gate 		/* Next, see if the pattern is an include or an exclude */
532*7c478bd9Sstevel@tonic-gate 		if (new_mod_ptr->mod_str[0] == '!') {
533*7c478bd9Sstevel@tonic-gate 			new_mod_ptr->mod_str = (new_mod_ptr->mod_str + 1);
534*7c478bd9Sstevel@tonic-gate 			new_mod_ptr->include = B_FALSE;
535*7c478bd9Sstevel@tonic-gate 		} else {
536*7c478bd9Sstevel@tonic-gate 			new_mod_ptr->include = B_TRUE;
537*7c478bd9Sstevel@tonic-gate 		}
538*7c478bd9Sstevel@tonic-gate 
539*7c478bd9Sstevel@tonic-gate 		if (this_rule->modifiers == NULL)
540*7c478bd9Sstevel@tonic-gate 			this_rule->modifiers = new_mod_ptr;
541*7c478bd9Sstevel@tonic-gate 		else {
542*7c478bd9Sstevel@tonic-gate 			curr_mod_ptr = this_rule->modifiers;
543*7c478bd9Sstevel@tonic-gate 			while (curr_mod_ptr->next != NULL)
544*7c478bd9Sstevel@tonic-gate 				curr_mod_ptr = curr_mod_ptr->next;
545*7c478bd9Sstevel@tonic-gate 
546*7c478bd9Sstevel@tonic-gate 			curr_mod_ptr->next = new_mod_ptr;
547*7c478bd9Sstevel@tonic-gate 		}
548*7c478bd9Sstevel@tonic-gate 		this_rule = this_rule->next;
549*7c478bd9Sstevel@tonic-gate 	}
550*7c478bd9Sstevel@tonic-gate }
551*7c478bd9Sstevel@tonic-gate 
552*7c478bd9Sstevel@tonic-gate /*
553*7c478bd9Sstevel@tonic-gate  * This funtion is invoked when reading rulesfiles.  A subtree may have
554*7c478bd9Sstevel@tonic-gate  * wildcards in it, e.g., '/home/n*', which is expected to match all home
555*7c478bd9Sstevel@tonic-gate  * dirs which start with an 'n'.
556*7c478bd9Sstevel@tonic-gate  *
557*7c478bd9Sstevel@tonic-gate  * This function needs to break down the subtree into its components.  For
558*7c478bd9Sstevel@tonic-gate  * each component, see how many directories match.  Take the subtree list just
559*7c478bd9Sstevel@tonic-gate  * generated and run it through again, this time looking at the next component.
560*7c478bd9Sstevel@tonic-gate  * At each iteration, keep a linked list of subtrees that currently match.
561*7c478bd9Sstevel@tonic-gate  * Once the final list is created, invoke add_single_rule() to create the
562*7c478bd9Sstevel@tonic-gate  * rule struct with the correct information.
563*7c478bd9Sstevel@tonic-gate  *
564*7c478bd9Sstevel@tonic-gate  * This function returns a ptr to the first element in the block of subtrees
565*7c478bd9Sstevel@tonic-gate  * which matched the subtree def'n in the rulesfile.
566*7c478bd9Sstevel@tonic-gate  */
567*7c478bd9Sstevel@tonic-gate static struct rule *
568*7c478bd9Sstevel@tonic-gate add_subtree_rule(char *rule, char *reloc_root, int create, int *err_code)
569*7c478bd9Sstevel@tonic-gate {
570*7c478bd9Sstevel@tonic-gate 	char			full_path[PATH_MAX], pattern[PATH_MAX],
571*7c478bd9Sstevel@tonic-gate 				new_dirname[PATH_MAX], *beg_pattern,
572*7c478bd9Sstevel@tonic-gate 				*end_pattern, *curr_dirname;
573*7c478bd9Sstevel@tonic-gate 	struct	dir_component	*current_level = NULL, *next_level = NULL,
574*7c478bd9Sstevel@tonic-gate 				*tmp_ptr;
575*7c478bd9Sstevel@tonic-gate 	DIR			*dir_ptr;
576*7c478bd9Sstevel@tonic-gate 	struct dirent		*dir_entry;
577*7c478bd9Sstevel@tonic-gate 	struct rule		*begin_rule = NULL;
578*7c478bd9Sstevel@tonic-gate 	int			ret;
579*7c478bd9Sstevel@tonic-gate 	struct stat64		statb;
580*7c478bd9Sstevel@tonic-gate 
581*7c478bd9Sstevel@tonic-gate 	(void) snprintf(full_path, sizeof (full_path),
582*7c478bd9Sstevel@tonic-gate 	    (rule[0] == '/') ? "%s%s" : "%s/%s", reloc_root, rule);
583*7c478bd9Sstevel@tonic-gate 
584*7c478bd9Sstevel@tonic-gate 	/*
585*7c478bd9Sstevel@tonic-gate 	 * In the case of 'bart compare', don't validate
586*7c478bd9Sstevel@tonic-gate 	 * the subtrees, since the machine running the
587*7c478bd9Sstevel@tonic-gate 	 * comparison may not be the machine which generated
588*7c478bd9Sstevel@tonic-gate 	 * the manifest.
589*7c478bd9Sstevel@tonic-gate 	 */
590*7c478bd9Sstevel@tonic-gate 	if (create == 0)
591*7c478bd9Sstevel@tonic-gate 		return (add_single_rule(full_path));
592*7c478bd9Sstevel@tonic-gate 
593*7c478bd9Sstevel@tonic-gate 
594*7c478bd9Sstevel@tonic-gate 	/* Insert 'current_level' into the linked list */
595*7c478bd9Sstevel@tonic-gate 	add_dir(&current_level, NULL);
596*7c478bd9Sstevel@tonic-gate 
597*7c478bd9Sstevel@tonic-gate 	/* Special case: occurs when -R is "/" and the subtree is "/" */
598*7c478bd9Sstevel@tonic-gate 	if (strcmp(full_path, "/") == 0)
599*7c478bd9Sstevel@tonic-gate 		(void) strcpy(current_level->dirname, "/");
600*7c478bd9Sstevel@tonic-gate 
601*7c478bd9Sstevel@tonic-gate 	beg_pattern = full_path;
602*7c478bd9Sstevel@tonic-gate 
603*7c478bd9Sstevel@tonic-gate 	while (beg_pattern != NULL) {
604*7c478bd9Sstevel@tonic-gate 		/*
605*7c478bd9Sstevel@tonic-gate 		 * Extract the pathname component starting at 'beg_pattern'.
606*7c478bd9Sstevel@tonic-gate 		 * Take those chars and put them into 'pattern'.
607*7c478bd9Sstevel@tonic-gate 		 */
608*7c478bd9Sstevel@tonic-gate 		while (*beg_pattern == '/')
609*7c478bd9Sstevel@tonic-gate 			beg_pattern++;
610*7c478bd9Sstevel@tonic-gate 		if (*beg_pattern == '\0')	/* end of pathname */
611*7c478bd9Sstevel@tonic-gate 			break;
612*7c478bd9Sstevel@tonic-gate 		end_pattern = strchr(beg_pattern, '/');
613*7c478bd9Sstevel@tonic-gate 		if (end_pattern != NULL)
614*7c478bd9Sstevel@tonic-gate 			(void) strlcpy(pattern, beg_pattern,
615*7c478bd9Sstevel@tonic-gate 			    end_pattern - beg_pattern + 1);
616*7c478bd9Sstevel@tonic-gate 		else
617*7c478bd9Sstevel@tonic-gate 			(void) strlcpy(pattern, beg_pattern, sizeof (pattern));
618*7c478bd9Sstevel@tonic-gate 		beg_pattern = end_pattern;
619*7c478bd9Sstevel@tonic-gate 
620*7c478bd9Sstevel@tonic-gate 		/*
621*7c478bd9Sstevel@tonic-gate 		 * At this point, search for 'pattern' as a *subdirectory* of
622*7c478bd9Sstevel@tonic-gate 		 * the dirs in the linked list.
623*7c478bd9Sstevel@tonic-gate 		 */
624*7c478bd9Sstevel@tonic-gate 		while (current_level != NULL) {
625*7c478bd9Sstevel@tonic-gate 			/* curr_dirname used to make the code more readable */
626*7c478bd9Sstevel@tonic-gate 			curr_dirname = current_level->dirname;
627*7c478bd9Sstevel@tonic-gate 
628*7c478bd9Sstevel@tonic-gate 			/* Initialization case */
629*7c478bd9Sstevel@tonic-gate 			if (strlen(curr_dirname) == 0)
630*7c478bd9Sstevel@tonic-gate 				(void) strcpy(curr_dirname, "/");
631*7c478bd9Sstevel@tonic-gate 
632*7c478bd9Sstevel@tonic-gate 			/* Open up the dir for this element in the list */
633*7c478bd9Sstevel@tonic-gate 			dir_ptr = opendir(curr_dirname);
634*7c478bd9Sstevel@tonic-gate 			dir_entry = NULL;
635*7c478bd9Sstevel@tonic-gate 
636*7c478bd9Sstevel@tonic-gate 			if (dir_ptr == NULL) {
637*7c478bd9Sstevel@tonic-gate 				perror(curr_dirname);
638*7c478bd9Sstevel@tonic-gate 				*err_code = WARNING_EXIT;
639*7c478bd9Sstevel@tonic-gate 			} else
640*7c478bd9Sstevel@tonic-gate 				dir_entry = readdir(dir_ptr);
641*7c478bd9Sstevel@tonic-gate 
642*7c478bd9Sstevel@tonic-gate 			/*
643*7c478bd9Sstevel@tonic-gate 			 * Now iterate through the subdirs of 'curr_dirname'
644*7c478bd9Sstevel@tonic-gate 			 * In the case of a match against 'pattern',
645*7c478bd9Sstevel@tonic-gate 			 * add the path to the next linked list, which
646*7c478bd9Sstevel@tonic-gate 			 * will be matched on the next iteration.
647*7c478bd9Sstevel@tonic-gate 			 */
648*7c478bd9Sstevel@tonic-gate 			while (dir_entry != NULL) {
649*7c478bd9Sstevel@tonic-gate 				/* Skip the dirs "." and ".." */
650*7c478bd9Sstevel@tonic-gate 				if ((strcmp(dir_entry->d_name, ".") == 0) ||
651*7c478bd9Sstevel@tonic-gate 				    (strcmp(dir_entry->d_name, "..") == 0)) {
652*7c478bd9Sstevel@tonic-gate 					dir_entry = readdir(dir_ptr);
653*7c478bd9Sstevel@tonic-gate 					continue;
654*7c478bd9Sstevel@tonic-gate 				}
655*7c478bd9Sstevel@tonic-gate 				if (fnmatch(pattern, dir_entry->d_name,
656*7c478bd9Sstevel@tonic-gate 				    FNM_PATHNAME) == 0) {
657*7c478bd9Sstevel@tonic-gate 					/*
658*7c478bd9Sstevel@tonic-gate 					 * Build 'new_dirname' which will be
659*7c478bd9Sstevel@tonic-gate 					 * examined on the next iteration.
660*7c478bd9Sstevel@tonic-gate 					 */
661*7c478bd9Sstevel@tonic-gate 					if (curr_dirname[strlen(curr_dirname)-1]
662*7c478bd9Sstevel@tonic-gate 									!= '/')
663*7c478bd9Sstevel@tonic-gate 						(void) snprintf(new_dirname,
664*7c478bd9Sstevel@tonic-gate 						    sizeof (new_dirname),
665*7c478bd9Sstevel@tonic-gate 						    "%s/%s", curr_dirname,
666*7c478bd9Sstevel@tonic-gate 						    dir_entry->d_name);
667*7c478bd9Sstevel@tonic-gate 					else
668*7c478bd9Sstevel@tonic-gate 						(void) snprintf(new_dirname,
669*7c478bd9Sstevel@tonic-gate 						    sizeof (new_dirname),
670*7c478bd9Sstevel@tonic-gate 						    "%s%s", curr_dirname,
671*7c478bd9Sstevel@tonic-gate 						    dir_entry->d_name);
672*7c478bd9Sstevel@tonic-gate 
673*7c478bd9Sstevel@tonic-gate 					/* Add to the next lined list */
674*7c478bd9Sstevel@tonic-gate 					add_dir(&next_level, new_dirname);
675*7c478bd9Sstevel@tonic-gate 				}
676*7c478bd9Sstevel@tonic-gate 				dir_entry = readdir(dir_ptr);
677*7c478bd9Sstevel@tonic-gate 			}
678*7c478bd9Sstevel@tonic-gate 
679*7c478bd9Sstevel@tonic-gate 			/* Close directory */
680*7c478bd9Sstevel@tonic-gate 			if (dir_ptr != NULL)
681*7c478bd9Sstevel@tonic-gate 				(void) closedir(dir_ptr);
682*7c478bd9Sstevel@tonic-gate 
683*7c478bd9Sstevel@tonic-gate 			/* Free this entry and move on.... */
684*7c478bd9Sstevel@tonic-gate 			tmp_ptr = current_level;
685*7c478bd9Sstevel@tonic-gate 			current_level = current_level->next;
686*7c478bd9Sstevel@tonic-gate 			free(tmp_ptr);
687*7c478bd9Sstevel@tonic-gate 		}
688*7c478bd9Sstevel@tonic-gate 
689*7c478bd9Sstevel@tonic-gate 		/*
690*7c478bd9Sstevel@tonic-gate 		 * OK, done with this level.  Move to the next level and
691*7c478bd9Sstevel@tonic-gate 		 * advance the ptrs which indicate the component name.
692*7c478bd9Sstevel@tonic-gate 		 */
693*7c478bd9Sstevel@tonic-gate 		current_level = next_level;
694*7c478bd9Sstevel@tonic-gate 		next_level = NULL;
695*7c478bd9Sstevel@tonic-gate 	}
696*7c478bd9Sstevel@tonic-gate 
697*7c478bd9Sstevel@tonic-gate 	tmp_ptr = current_level;
698*7c478bd9Sstevel@tonic-gate 
699*7c478bd9Sstevel@tonic-gate 	/* Error case: the subtree doesn't exist! */
700*7c478bd9Sstevel@tonic-gate 	if (current_level == NULL) {
701*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, INVALID_SUBTREE, full_path);
702*7c478bd9Sstevel@tonic-gate 		*err_code = WARNING_EXIT;
703*7c478bd9Sstevel@tonic-gate 	}
704*7c478bd9Sstevel@tonic-gate 
705*7c478bd9Sstevel@tonic-gate 	/*
706*7c478bd9Sstevel@tonic-gate 	 * Iterate through all the dirnames which match the pattern and
707*7c478bd9Sstevel@tonic-gate 	 * add them to to global list of subtrees which must be examined.
708*7c478bd9Sstevel@tonic-gate 	 */
709*7c478bd9Sstevel@tonic-gate 	while (current_level != NULL) {
710*7c478bd9Sstevel@tonic-gate 		/*
711*7c478bd9Sstevel@tonic-gate 		 * Sanity check for 'bart create', make sure the subtree
712*7c478bd9Sstevel@tonic-gate 		 * points to a valid object.
713*7c478bd9Sstevel@tonic-gate 		 */
714*7c478bd9Sstevel@tonic-gate 		ret = lstat64(current_level->dirname, &statb);
715*7c478bd9Sstevel@tonic-gate 		if (ret < 0) {
716*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, INVALID_SUBTREE,
717*7c478bd9Sstevel@tonic-gate 			    current_level->dirname);
718*7c478bd9Sstevel@tonic-gate 			current_level = current_level->next;
719*7c478bd9Sstevel@tonic-gate 			*err_code = WARNING_EXIT;
720*7c478bd9Sstevel@tonic-gate 			continue;
721*7c478bd9Sstevel@tonic-gate 		}
722*7c478bd9Sstevel@tonic-gate 
723*7c478bd9Sstevel@tonic-gate 		if (begin_rule == NULL) {
724*7c478bd9Sstevel@tonic-gate 			begin_rule =
725*7c478bd9Sstevel@tonic-gate 			    add_single_rule(current_level->dirname);
726*7c478bd9Sstevel@tonic-gate 		} else
727*7c478bd9Sstevel@tonic-gate 			(void) add_single_rule(current_level->dirname);
728*7c478bd9Sstevel@tonic-gate 
729*7c478bd9Sstevel@tonic-gate 		current_level = current_level->next;
730*7c478bd9Sstevel@tonic-gate 	}
731*7c478bd9Sstevel@tonic-gate 
732*7c478bd9Sstevel@tonic-gate 	/*
733*7c478bd9Sstevel@tonic-gate 	 * Free up the memory and return a ptr to the first entry in the
734*7c478bd9Sstevel@tonic-gate 	 * subtree block.  This is necessary for the parser, which may need
735*7c478bd9Sstevel@tonic-gate 	 * to add modifier strings to all the elements in this block.
736*7c478bd9Sstevel@tonic-gate 	 */
737*7c478bd9Sstevel@tonic-gate 	dirs_cleanup(tmp_ptr);
738*7c478bd9Sstevel@tonic-gate 
739*7c478bd9Sstevel@tonic-gate 	return (begin_rule);
740*7c478bd9Sstevel@tonic-gate }
741*7c478bd9Sstevel@tonic-gate 
742*7c478bd9Sstevel@tonic-gate 
743*7c478bd9Sstevel@tonic-gate /*
744*7c478bd9Sstevel@tonic-gate  * Add a single entry to the linked list of rules to be read.  Does not do
745*7c478bd9Sstevel@tonic-gate  * the wildcard expansion of 'add_subtree_rule', so is much simpler.
746*7c478bd9Sstevel@tonic-gate  */
747*7c478bd9Sstevel@tonic-gate static struct rule *
748*7c478bd9Sstevel@tonic-gate add_single_rule(char *path)
749*7c478bd9Sstevel@tonic-gate {
750*7c478bd9Sstevel@tonic-gate 
751*7c478bd9Sstevel@tonic-gate 	/*
752*7c478bd9Sstevel@tonic-gate 	 * If the rules list does NOT exist, then create it.
753*7c478bd9Sstevel@tonic-gate 	 * If the rules list does exist, then traverse the next element.
754*7c478bd9Sstevel@tonic-gate 	 */
755*7c478bd9Sstevel@tonic-gate 	if (first_rule == NULL) {
756*7c478bd9Sstevel@tonic-gate 		first_rule = gen_rulestruct();
757*7c478bd9Sstevel@tonic-gate 		current_rule = first_rule;
758*7c478bd9Sstevel@tonic-gate 	} else {
759*7c478bd9Sstevel@tonic-gate 		current_rule->next = gen_rulestruct();
760*7c478bd9Sstevel@tonic-gate 		current_rule->next->prev = current_rule;
761*7c478bd9Sstevel@tonic-gate 		current_rule = current_rule->next;
762*7c478bd9Sstevel@tonic-gate 	}
763*7c478bd9Sstevel@tonic-gate 
764*7c478bd9Sstevel@tonic-gate 	/* Setup the rule struct, handle relocatable roots, i.e. '-R' option */
765*7c478bd9Sstevel@tonic-gate 	(void) strlcpy(current_rule->subtree, path,
766*7c478bd9Sstevel@tonic-gate 	    sizeof (current_rule->subtree));
767*7c478bd9Sstevel@tonic-gate 
768*7c478bd9Sstevel@tonic-gate 	return (current_rule);
769*7c478bd9Sstevel@tonic-gate }
770*7c478bd9Sstevel@tonic-gate 
771*7c478bd9Sstevel@tonic-gate /*
772*7c478bd9Sstevel@tonic-gate  * Code stolen from filesync utility, used by read_rules() to read in the
773*7c478bd9Sstevel@tonic-gate  * rulesfile.
774*7c478bd9Sstevel@tonic-gate  */
775*7c478bd9Sstevel@tonic-gate static char *
776*7c478bd9Sstevel@tonic-gate lex(FILE *file)
777*7c478bd9Sstevel@tonic-gate {
778*7c478bd9Sstevel@tonic-gate 	char c, delim;
779*7c478bd9Sstevel@tonic-gate 	char *p;
780*7c478bd9Sstevel@tonic-gate 	char *s;
781*7c478bd9Sstevel@tonic-gate 	static char *savep;
782*7c478bd9Sstevel@tonic-gate 	static char namebuf[ BUF_SIZE ];
783*7c478bd9Sstevel@tonic-gate 	static char inbuf[ BUF_SIZE ];
784*7c478bd9Sstevel@tonic-gate 
785*7c478bd9Sstevel@tonic-gate 	if (file) {			/* read a new line		*/
786*7c478bd9Sstevel@tonic-gate 		p = inbuf + sizeof (inbuf);
787*7c478bd9Sstevel@tonic-gate 
788*7c478bd9Sstevel@tonic-gate 		s = inbuf;
789*7c478bd9Sstevel@tonic-gate 		/* read the next input line, with all continuations	*/
790*7c478bd9Sstevel@tonic-gate 		while (savep = fgets(s, p - s, file)) {
791*7c478bd9Sstevel@tonic-gate 			lex_linenum++;
792*7c478bd9Sstevel@tonic-gate 
793*7c478bd9Sstevel@tonic-gate 			/* go find the last character of the input line	*/
794*7c478bd9Sstevel@tonic-gate 			while (*s && s[1])
795*7c478bd9Sstevel@tonic-gate 				s++;
796*7c478bd9Sstevel@tonic-gate 			if (*s == '\n')
797*7c478bd9Sstevel@tonic-gate 				s--;
798*7c478bd9Sstevel@tonic-gate 
799*7c478bd9Sstevel@tonic-gate 			/* see whether or not we need a continuation	*/
800*7c478bd9Sstevel@tonic-gate 			if (s < inbuf || *s != '\\')
801*7c478bd9Sstevel@tonic-gate 				break;
802*7c478bd9Sstevel@tonic-gate 
803*7c478bd9Sstevel@tonic-gate 			continue;
804*7c478bd9Sstevel@tonic-gate 		}
805*7c478bd9Sstevel@tonic-gate 
806*7c478bd9Sstevel@tonic-gate 		if (savep == NULL)
807*7c478bd9Sstevel@tonic-gate 			return (0);
808*7c478bd9Sstevel@tonic-gate 
809*7c478bd9Sstevel@tonic-gate 		s = inbuf;
810*7c478bd9Sstevel@tonic-gate 	} else {			/* continue with old line	*/
811*7c478bd9Sstevel@tonic-gate 		if (savep == NULL)
812*7c478bd9Sstevel@tonic-gate 			return (0);
813*7c478bd9Sstevel@tonic-gate 		s = savep;
814*7c478bd9Sstevel@tonic-gate 	}
815*7c478bd9Sstevel@tonic-gate 	savep = NULL;
816*7c478bd9Sstevel@tonic-gate 
817*7c478bd9Sstevel@tonic-gate 	/* skip over leading white space	*/
818*7c478bd9Sstevel@tonic-gate 	while (isspace(*s))
819*7c478bd9Sstevel@tonic-gate 		s++;
820*7c478bd9Sstevel@tonic-gate 	if (*s == 0)
821*7c478bd9Sstevel@tonic-gate 		return (0);
822*7c478bd9Sstevel@tonic-gate 
823*7c478bd9Sstevel@tonic-gate 	/* see if this is a quoted string	*/
824*7c478bd9Sstevel@tonic-gate 	c = *s;
825*7c478bd9Sstevel@tonic-gate 	if (c == '\'' || c == '"') {
826*7c478bd9Sstevel@tonic-gate 		delim = c;
827*7c478bd9Sstevel@tonic-gate 		s++;
828*7c478bd9Sstevel@tonic-gate 	} else
829*7c478bd9Sstevel@tonic-gate 		delim = 0;
830*7c478bd9Sstevel@tonic-gate 
831*7c478bd9Sstevel@tonic-gate 	/* copy the token into the buffer	*/
832*7c478bd9Sstevel@tonic-gate 	for (p = namebuf; (c = *s) != 0; s++) {
833*7c478bd9Sstevel@tonic-gate 		/* literal escape		*/
834*7c478bd9Sstevel@tonic-gate 		if (c == '\\') {
835*7c478bd9Sstevel@tonic-gate 			s++;
836*7c478bd9Sstevel@tonic-gate 			*p++ = *s;
837*7c478bd9Sstevel@tonic-gate 			continue;
838*7c478bd9Sstevel@tonic-gate 		}
839*7c478bd9Sstevel@tonic-gate 
840*7c478bd9Sstevel@tonic-gate 		/* closing delimiter		*/
841*7c478bd9Sstevel@tonic-gate 		if (c == delim) {
842*7c478bd9Sstevel@tonic-gate 			s++;
843*7c478bd9Sstevel@tonic-gate 			break;
844*7c478bd9Sstevel@tonic-gate 		}
845*7c478bd9Sstevel@tonic-gate 
846*7c478bd9Sstevel@tonic-gate 		/* delimiting white space	*/
847*7c478bd9Sstevel@tonic-gate 		if (delim == 0 && isspace(c))
848*7c478bd9Sstevel@tonic-gate 			break;
849*7c478bd9Sstevel@tonic-gate 
850*7c478bd9Sstevel@tonic-gate 		/* ordinary characters		*/
851*7c478bd9Sstevel@tonic-gate 		*p++ = *s;
852*7c478bd9Sstevel@tonic-gate 	}
853*7c478bd9Sstevel@tonic-gate 
854*7c478bd9Sstevel@tonic-gate 
855*7c478bd9Sstevel@tonic-gate 	/* remember where we left off		*/
856*7c478bd9Sstevel@tonic-gate 	savep = *s ? s : 0;
857*7c478bd9Sstevel@tonic-gate 
858*7c478bd9Sstevel@tonic-gate 	/* null terminate and return the buffer	*/
859*7c478bd9Sstevel@tonic-gate 	*p = 0;
860*7c478bd9Sstevel@tonic-gate 	return (namebuf);
861*7c478bd9Sstevel@tonic-gate }
862*7c478bd9Sstevel@tonic-gate 
863*7c478bd9Sstevel@tonic-gate /*
864*7c478bd9Sstevel@tonic-gate  * Iterate through the dir strcutures and free memory.
865*7c478bd9Sstevel@tonic-gate  */
866*7c478bd9Sstevel@tonic-gate static void
867*7c478bd9Sstevel@tonic-gate dirs_cleanup(struct dir_component *dir)
868*7c478bd9Sstevel@tonic-gate {
869*7c478bd9Sstevel@tonic-gate 	struct	dir_component	*next;
870*7c478bd9Sstevel@tonic-gate 
871*7c478bd9Sstevel@tonic-gate 	while (dir != NULL) {
872*7c478bd9Sstevel@tonic-gate 		next = dir->next;
873*7c478bd9Sstevel@tonic-gate 		free(dir);
874*7c478bd9Sstevel@tonic-gate 		dir = next;
875*7c478bd9Sstevel@tonic-gate 	}
876*7c478bd9Sstevel@tonic-gate }
877*7c478bd9Sstevel@tonic-gate 
878*7c478bd9Sstevel@tonic-gate /*
879*7c478bd9Sstevel@tonic-gate  * Create and initialize a new dir structure.  Used by add_subtree_rule() when
880*7c478bd9Sstevel@tonic-gate  * doing expansion of directory names caused by wildcards.
881*7c478bd9Sstevel@tonic-gate  */
882*7c478bd9Sstevel@tonic-gate static void
883*7c478bd9Sstevel@tonic-gate add_dir(struct dir_component **dir, char *dirname)
884*7c478bd9Sstevel@tonic-gate {
885*7c478bd9Sstevel@tonic-gate 	struct	dir_component	*new, *next_dir;
886*7c478bd9Sstevel@tonic-gate 
887*7c478bd9Sstevel@tonic-gate 	new = gen_dir_component();
888*7c478bd9Sstevel@tonic-gate 	if (dirname != NULL)
889*7c478bd9Sstevel@tonic-gate 		(void) strlcpy(new->dirname, dirname, sizeof (new->dirname));
890*7c478bd9Sstevel@tonic-gate 
891*7c478bd9Sstevel@tonic-gate 	if (*dir == NULL)
892*7c478bd9Sstevel@tonic-gate 		*dir = new;
893*7c478bd9Sstevel@tonic-gate 	else {
894*7c478bd9Sstevel@tonic-gate 		next_dir = *dir;
895*7c478bd9Sstevel@tonic-gate 		while (next_dir->next != NULL)
896*7c478bd9Sstevel@tonic-gate 			next_dir = next_dir->next;
897*7c478bd9Sstevel@tonic-gate 
898*7c478bd9Sstevel@tonic-gate 		next_dir->next = new;
899*7c478bd9Sstevel@tonic-gate 	}
900*7c478bd9Sstevel@tonic-gate }
901*7c478bd9Sstevel@tonic-gate 
902*7c478bd9Sstevel@tonic-gate /*
903*7c478bd9Sstevel@tonic-gate  * Traverse the linked list of rules in a REVERSE order.
904*7c478bd9Sstevel@tonic-gate  */
905*7c478bd9Sstevel@tonic-gate static struct rule *
906*7c478bd9Sstevel@tonic-gate get_last_entry(boolean_t reset)
907*7c478bd9Sstevel@tonic-gate {
908*7c478bd9Sstevel@tonic-gate 	static struct rule	*curr_root = NULL;
909*7c478bd9Sstevel@tonic-gate 
910*7c478bd9Sstevel@tonic-gate 	if (reset) {
911*7c478bd9Sstevel@tonic-gate 
912*7c478bd9Sstevel@tonic-gate 		curr_root = first_rule;
913*7c478bd9Sstevel@tonic-gate 
914*7c478bd9Sstevel@tonic-gate 		/* RESET: set cur_root to the end of the list */
915*7c478bd9Sstevel@tonic-gate 		while (curr_root != NULL)
916*7c478bd9Sstevel@tonic-gate 			if (curr_root->next == NULL)
917*7c478bd9Sstevel@tonic-gate 				break;
918*7c478bd9Sstevel@tonic-gate 			else
919*7c478bd9Sstevel@tonic-gate 				curr_root = curr_root->next;
920*7c478bd9Sstevel@tonic-gate 	} else
921*7c478bd9Sstevel@tonic-gate 		curr_root = (curr_root->prev);
922*7c478bd9Sstevel@tonic-gate 
923*7c478bd9Sstevel@tonic-gate 	return (curr_root);
924*7c478bd9Sstevel@tonic-gate }
925*7c478bd9Sstevel@tonic-gate 
926*7c478bd9Sstevel@tonic-gate /*
927*7c478bd9Sstevel@tonic-gate  * Traverse the first entry, used by 'bart create' to iterate through
928*7c478bd9Sstevel@tonic-gate  * subtrees or individual filenames.
929*7c478bd9Sstevel@tonic-gate  */
930*7c478bd9Sstevel@tonic-gate struct rule *
931*7c478bd9Sstevel@tonic-gate get_first_subtree()
932*7c478bd9Sstevel@tonic-gate {
933*7c478bd9Sstevel@tonic-gate 	return (first_rule);
934*7c478bd9Sstevel@tonic-gate }
935*7c478bd9Sstevel@tonic-gate 
936*7c478bd9Sstevel@tonic-gate /*
937*7c478bd9Sstevel@tonic-gate  * Traverse the next entry, used by 'bart create' to iterate through
938*7c478bd9Sstevel@tonic-gate  * subtrees or individual filenames.
939*7c478bd9Sstevel@tonic-gate  */
940*7c478bd9Sstevel@tonic-gate struct rule *
941*7c478bd9Sstevel@tonic-gate get_next_subtree(struct rule *entry)
942*7c478bd9Sstevel@tonic-gate {
943*7c478bd9Sstevel@tonic-gate 	return (entry->next);
944*7c478bd9Sstevel@tonic-gate }
945*7c478bd9Sstevel@tonic-gate 
946*7c478bd9Sstevel@tonic-gate char *
947*7c478bd9Sstevel@tonic-gate safe_strdup(char *s)
948*7c478bd9Sstevel@tonic-gate {
949*7c478bd9Sstevel@tonic-gate 	char *ret;
950*7c478bd9Sstevel@tonic-gate 	size_t len;
951*7c478bd9Sstevel@tonic-gate 
952*7c478bd9Sstevel@tonic-gate 	len = strlen(s) + 1;
953*7c478bd9Sstevel@tonic-gate 	ret = safe_calloc(len);
954*7c478bd9Sstevel@tonic-gate 	(void) strlcpy(ret, s, len);
955*7c478bd9Sstevel@tonic-gate 	return (ret);
956*7c478bd9Sstevel@tonic-gate }
957*7c478bd9Sstevel@tonic-gate 
958*7c478bd9Sstevel@tonic-gate /*
959*7c478bd9Sstevel@tonic-gate  * Function to match a filename against the subtrees in the link list
960*7c478bd9Sstevel@tonic-gate  * of 'rule' strcutures.  Upon finding a matching rule, see if it should
961*7c478bd9Sstevel@tonic-gate  * be excluded.  Keep going until a match is found OR all rules have been
962*7c478bd9Sstevel@tonic-gate  * exhausted.
963*7c478bd9Sstevel@tonic-gate  * NOTES: Rules are parsed in reverse;
964*7c478bd9Sstevel@tonic-gate  * satisfies the spec that "Last rule wins".  Also, the default rule should
965*7c478bd9Sstevel@tonic-gate  * always match, so this function should NEVER return NULL.
966*7c478bd9Sstevel@tonic-gate  */
967*7c478bd9Sstevel@tonic-gate struct rule *
968*7c478bd9Sstevel@tonic-gate check_rules(const char *fname, char type)
969*7c478bd9Sstevel@tonic-gate {
970*7c478bd9Sstevel@tonic-gate 	struct rule		*root;
971*7c478bd9Sstevel@tonic-gate 
972*7c478bd9Sstevel@tonic-gate 	root = get_last_entry(B_TRUE);
973*7c478bd9Sstevel@tonic-gate 	while (root != NULL) {
974*7c478bd9Sstevel@tonic-gate 		if (match_subtree(fname, root->subtree)) {
975*7c478bd9Sstevel@tonic-gate 			if (exclude_fname(fname, type, root) == 0)
976*7c478bd9Sstevel@tonic-gate 				break;
977*7c478bd9Sstevel@tonic-gate 		}
978*7c478bd9Sstevel@tonic-gate 		root = get_last_entry(B_FALSE);
979*7c478bd9Sstevel@tonic-gate 	}
980*7c478bd9Sstevel@tonic-gate 
981*7c478bd9Sstevel@tonic-gate 	return (root);
982*7c478bd9Sstevel@tonic-gate }
983*7c478bd9Sstevel@tonic-gate 
984*7c478bd9Sstevel@tonic-gate /*
985*7c478bd9Sstevel@tonic-gate  * Function to match a filename against a subtree.  Break both paths into
986*7c478bd9Sstevel@tonic-gate  * their components and do the comparison.  Comparison is OK when all the
987*7c478bd9Sstevel@tonic-gate  * components match until the subtree is exhausted.  For example, a
988*7c478bd9Sstevel@tonic-gate  * subtree of "/home/nickiso" should match against "/home/nickiso/src/foo.c"
989*7c478bd9Sstevel@tonic-gate  */
990*7c478bd9Sstevel@tonic-gate static int
991*7c478bd9Sstevel@tonic-gate match_subtree(const char *fname, char *rule)
992*7c478bd9Sstevel@tonic-gate {
993*7c478bd9Sstevel@tonic-gate 	int	match;
994*7c478bd9Sstevel@tonic-gate 
995*7c478bd9Sstevel@tonic-gate 	/* Trivial case, always match against '/' */
996*7c478bd9Sstevel@tonic-gate 	if (strcmp(rule, "/") == 0)
997*7c478bd9Sstevel@tonic-gate 		return (1);
998*7c478bd9Sstevel@tonic-gate 
999*7c478bd9Sstevel@tonic-gate 	/*
1000*7c478bd9Sstevel@tonic-gate 	 * Walk through the rule and fname to see if they match, or not.
1001*7c478bd9Sstevel@tonic-gate 	 *
1002*7c478bd9Sstevel@tonic-gate 	 * A "component" is one part of a fname, e.g., assuming an fname
1003*7c478bd9Sstevel@tonic-gate 	 * of "/A/B/C/D", it has four components: "A", "B", "C", "D".
1004*7c478bd9Sstevel@tonic-gate 	 */
1005*7c478bd9Sstevel@tonic-gate 
1006*7c478bd9Sstevel@tonic-gate 	/* OK, now see if the match */
1007*7c478bd9Sstevel@tonic-gate 	match = fnmatch(rule, fname, FNM_PATHNAME);
1008*7c478bd9Sstevel@tonic-gate 
1009*7c478bd9Sstevel@tonic-gate 	/* No match, return failure */
1010*7c478bd9Sstevel@tonic-gate 	if (match != 0)
1011*7c478bd9Sstevel@tonic-gate 		return (0);
1012*7c478bd9Sstevel@tonic-gate 	else
1013*7c478bd9Sstevel@tonic-gate 		return (1);
1014*7c478bd9Sstevel@tonic-gate }
1015*7c478bd9Sstevel@tonic-gate 
1016*7c478bd9Sstevel@tonic-gate void
1017*7c478bd9Sstevel@tonic-gate process_glob_ignores(char *ignore_list, uint_t *flags)
1018*7c478bd9Sstevel@tonic-gate {
1019*7c478bd9Sstevel@tonic-gate 	char	*cp;
1020*7c478bd9Sstevel@tonic-gate 	struct attr_keyword *akp;
1021*7c478bd9Sstevel@tonic-gate 
1022*7c478bd9Sstevel@tonic-gate 	if (ignore_list == NULL)
1023*7c478bd9Sstevel@tonic-gate 		usage();
1024*7c478bd9Sstevel@tonic-gate 
1025*7c478bd9Sstevel@tonic-gate 	cp = strtok(ignore_list, ",");
1026*7c478bd9Sstevel@tonic-gate 	while (cp != NULL) {
1027*7c478bd9Sstevel@tonic-gate 		akp = attr_keylookup(cp);
1028*7c478bd9Sstevel@tonic-gate 		if (akp == NULL)
1029*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, "ERROR: Invalid keyword %s\n",
1030*7c478bd9Sstevel@tonic-gate 			    cp);
1031*7c478bd9Sstevel@tonic-gate 		else
1032*7c478bd9Sstevel@tonic-gate 			*flags &= ~akp->ak_flags;
1033*7c478bd9Sstevel@tonic-gate 		cp = strtok(NULL, ",");
1034*7c478bd9Sstevel@tonic-gate 	}
1035*7c478bd9Sstevel@tonic-gate }
1036