1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright (c) 1995 Sun Microsystems, Inc.  All Rights Reserved
24*0Sstevel@tonic-gate  *
25*0Sstevel@tonic-gate  * module:
26*0Sstevel@tonic-gate  *	rules.c
27*0Sstevel@tonic-gate  *
28*0Sstevel@tonic-gate  * purpose:
29*0Sstevel@tonic-gate  *	to read and write the rules file and manage rules lists
30*0Sstevel@tonic-gate  *
31*0Sstevel@tonic-gate  * contents:
32*0Sstevel@tonic-gate  *	reading rules file
33*0Sstevel@tonic-gate  *		read_rules
34*0Sstevel@tonic-gate  *		(static) read_command
35*0Sstevel@tonic-gate  *	writing rules file
36*0Sstevel@tonic-gate  *		write_rules
37*0Sstevel@tonic-gate  *		(static) rw_header, rw_base
38*0Sstevel@tonic-gate  *	adding rules
39*0Sstevel@tonic-gate  *		add_ignore, add_include
40*0Sstevel@tonic-gate  *		(static) add_rule
41*0Sstevel@tonic-gate  *	adding/checking restrictions
42*0Sstevel@tonic-gate  *		add_restr, check_restr
43*0Sstevel@tonic-gate  */
44*0Sstevel@tonic-gate #ident	"%W%	%E% SMI"
45*0Sstevel@tonic-gate 
46*0Sstevel@tonic-gate #include <stdio.h>
47*0Sstevel@tonic-gate #include <stdlib.h>
48*0Sstevel@tonic-gate #include <string.h>
49*0Sstevel@tonic-gate #include <time.h>
50*0Sstevel@tonic-gate #include <ctype.h>
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate #include "filesync.h"
53*0Sstevel@tonic-gate #include "database.h"
54*0Sstevel@tonic-gate #include "messages.h"
55*0Sstevel@tonic-gate #include "debug.h"
56*0Sstevel@tonic-gate 
57*0Sstevel@tonic-gate /*
58*0Sstevel@tonic-gate  * routines:
59*0Sstevel@tonic-gate  */
60*0Sstevel@tonic-gate static errmask_t rw_base(FILE *file, struct base *bp);
61*0Sstevel@tonic-gate static errmask_t rw_header(FILE *file);
62*0Sstevel@tonic-gate static errmask_t add_rule(struct base *, int, const char *);
63*0Sstevel@tonic-gate static char *read_cmd(char *);
64*0Sstevel@tonic-gate 
65*0Sstevel@tonic-gate /*
66*0Sstevel@tonic-gate  * globals
67*0Sstevel@tonic-gate  */
68*0Sstevel@tonic-gate static int rules_added;
69*0Sstevel@tonic-gate static int restr_added;
70*0Sstevel@tonic-gate 
71*0Sstevel@tonic-gate /*
72*0Sstevel@tonic-gate  * locals
73*0Sstevel@tonic-gate  */
74*0Sstevel@tonic-gate #define	RULE_MAJOR	1		/* rules file format major rev	*/
75*0Sstevel@tonic-gate #define	RULE_MINOR	1		/* rules file format minor rev	*/
76*0Sstevel@tonic-gate #define	RULE_TAG	"PACKINGRULES"	/* magic string for rules files	*/
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate /*
79*0Sstevel@tonic-gate  * routine:
80*0Sstevel@tonic-gate  *	read_rules
81*0Sstevel@tonic-gate  *
82*0Sstevel@tonic-gate  * purpose:
83*0Sstevel@tonic-gate  *	to read in the rules file
84*0Sstevel@tonic-gate  *
85*0Sstevel@tonic-gate  * parameters:
86*0Sstevel@tonic-gate  *	name of rules file
87*0Sstevel@tonic-gate  *
88*0Sstevel@tonic-gate  * returns:
89*0Sstevel@tonic-gate  *	error mask
90*0Sstevel@tonic-gate  *
91*0Sstevel@tonic-gate  * notes:
92*0Sstevel@tonic-gate  *	later when I implement a proper (comment preserving) update
93*0Sstevel@tonic-gate  *	function I'm going to wish I had figured out how to build the
94*0Sstevel@tonic-gate  *	input functions for this function in a way that would make
95*0Sstevel@tonic-gate  *	the more usable for that too.
96*0Sstevel@tonic-gate  */
97*0Sstevel@tonic-gate errmask_t
98*0Sstevel@tonic-gate read_rules(char *name)
99*0Sstevel@tonic-gate {	FILE *file;
100*0Sstevel@tonic-gate 	errmask_t errs = 0;
101*0Sstevel@tonic-gate 	int flags;
102*0Sstevel@tonic-gate 	int major, minor;
103*0Sstevel@tonic-gate 	char *s, *s1, *s2;
104*0Sstevel@tonic-gate 	struct base *bp;
105*0Sstevel@tonic-gate 	char *errstr = "???";
106*0Sstevel@tonic-gate 
107*0Sstevel@tonic-gate 	file = fopen(name, "r");
108*0Sstevel@tonic-gate 	if (file == NULL) {
109*0Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_open), gettext(TXT_rules),
110*0Sstevel@tonic-gate 			name);
111*0Sstevel@tonic-gate 		return (ERR_FILES);
112*0Sstevel@tonic-gate 	}
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate 	lex_linenum = 0;
115*0Sstevel@tonic-gate 
116*0Sstevel@tonic-gate 	if (opt_debug & DBG_FILES)
117*0Sstevel@tonic-gate 		fprintf(stderr, "FILE: READ RULES %s\n", name);
118*0Sstevel@tonic-gate 
119*0Sstevel@tonic-gate 	bp = &omnibase;		/* default base before any others	*/
120*0Sstevel@tonic-gate 
121*0Sstevel@tonic-gate 	while (!feof(file)) {
122*0Sstevel@tonic-gate 		/* find the first token on the line	*/
123*0Sstevel@tonic-gate 		s = lex(file);
124*0Sstevel@tonic-gate 
125*0Sstevel@tonic-gate 		/* skip blank lines and comments	*/
126*0Sstevel@tonic-gate 		if (s == 0 || *s == 0 || *s == '#' || *s == '*')
127*0Sstevel@tonic-gate 			continue;
128*0Sstevel@tonic-gate 
129*0Sstevel@tonic-gate 		/* see if the first token is a known keyword	*/
130*0Sstevel@tonic-gate 		if (strcmp(s, "BASE") == 0) {
131*0Sstevel@tonic-gate 
132*0Sstevel@tonic-gate 			/* get the source & destination tokens	*/
133*0Sstevel@tonic-gate 			errstr = gettext(TXT_srcdst);
134*0Sstevel@tonic-gate 			s1 = lex(0);
135*0Sstevel@tonic-gate 			if (s1 == 0)
136*0Sstevel@tonic-gate 				goto bad;
137*0Sstevel@tonic-gate 			s1 = strdup(s1);
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate 			s2 = lex(0);
140*0Sstevel@tonic-gate 			if (s2 == 0)
141*0Sstevel@tonic-gate 				goto bad;
142*0Sstevel@tonic-gate 			s2 = strdup(s2);
143*0Sstevel@tonic-gate 
144*0Sstevel@tonic-gate 			/* creat the new base pair		*/
145*0Sstevel@tonic-gate 			bp = add_base(s1, s2);
146*0Sstevel@tonic-gate 			bp->b_flags |= F_LISTED;
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate 			free(s1);
149*0Sstevel@tonic-gate 			free(s2);
150*0Sstevel@tonic-gate 			continue;
151*0Sstevel@tonic-gate 		}
152*0Sstevel@tonic-gate 
153*0Sstevel@tonic-gate 		if (strcmp(s, "LIST") == 0) {
154*0Sstevel@tonic-gate 
155*0Sstevel@tonic-gate 			/* make sure we are associated with a real base */
156*0Sstevel@tonic-gate 			if (bp == &omnibase) {
157*0Sstevel@tonic-gate 				errstr = gettext(TXT_nobase);
158*0Sstevel@tonic-gate 				goto bad;
159*0Sstevel@tonic-gate 			}
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate 			/* skip to the next token */
162*0Sstevel@tonic-gate 			s = lex(0);
163*0Sstevel@tonic-gate 			errstr = gettext(TXT_noargs);
164*0Sstevel@tonic-gate 			if (s == 0)
165*0Sstevel@tonic-gate 				goto bad;
166*0Sstevel@tonic-gate 
167*0Sstevel@tonic-gate 			/* see if it is a program or a name */
168*0Sstevel@tonic-gate 			if (*s == '!') {
169*0Sstevel@tonic-gate 				errs |= add_rule(bp, R_PROGRAM,
170*0Sstevel@tonic-gate 						read_cmd(&s[1]));
171*0Sstevel@tonic-gate 			} else {
172*0Sstevel@tonic-gate 				do {
173*0Sstevel@tonic-gate 					flags = wildcards(s) ? R_WILD : 0;
174*0Sstevel@tonic-gate 					errs |= add_rule(bp, flags, s);
175*0Sstevel@tonic-gate 					s = lex(0);
176*0Sstevel@tonic-gate 				} while (s != 0);
177*0Sstevel@tonic-gate 			}
178*0Sstevel@tonic-gate 			continue;
179*0Sstevel@tonic-gate 		}
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate 		if (strcmp(s, "IGNORE") == 0) {
182*0Sstevel@tonic-gate 
183*0Sstevel@tonic-gate 			/* skip to the next token */
184*0Sstevel@tonic-gate 			s = lex(0);
185*0Sstevel@tonic-gate 			errstr = gettext(TXT_noargs);
186*0Sstevel@tonic-gate 			if (s == 0)
187*0Sstevel@tonic-gate 				goto bad;
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate 			flags = R_IGNORE;
190*0Sstevel@tonic-gate 
191*0Sstevel@tonic-gate 			/* see if it is a program or a name */
192*0Sstevel@tonic-gate 			if (*s == '!') {
193*0Sstevel@tonic-gate 				errs |= add_rule(bp, R_PROGRAM|flags,
194*0Sstevel@tonic-gate 						read_cmd(&s[1]));
195*0Sstevel@tonic-gate 			} else {
196*0Sstevel@tonic-gate 				do {
197*0Sstevel@tonic-gate 					if (wildcards(s))
198*0Sstevel@tonic-gate 						flags |= R_WILD;
199*0Sstevel@tonic-gate 					errs |= add_rule(bp, flags, s);
200*0Sstevel@tonic-gate 					s = lex(0);
201*0Sstevel@tonic-gate 				} while (s != 0);
202*0Sstevel@tonic-gate 			}
203*0Sstevel@tonic-gate 			continue;
204*0Sstevel@tonic-gate 		}
205*0Sstevel@tonic-gate 
206*0Sstevel@tonic-gate 		if (strcmp(s, "VERSION") == 0 || strcmp(s, RULE_TAG) == 0) {
207*0Sstevel@tonic-gate 			s = lex(0);
208*0Sstevel@tonic-gate 			errstr = gettext(TXT_noargs);
209*0Sstevel@tonic-gate 			if (s == 0)
210*0Sstevel@tonic-gate 				goto bad;
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate 			major = strtol(s, &s1, 10);
213*0Sstevel@tonic-gate 			errstr = gettext(TXT_badver);
214*0Sstevel@tonic-gate 			if (*s1 != '.')
215*0Sstevel@tonic-gate 				goto bad;
216*0Sstevel@tonic-gate 			minor = strtol(&s1[1], 0, 10);
217*0Sstevel@tonic-gate 
218*0Sstevel@tonic-gate 			if (major != RULE_MAJOR || minor > RULE_MINOR) {
219*0Sstevel@tonic-gate 				fprintf(stderr, gettext(ERR_badver),
220*0Sstevel@tonic-gate 					major, minor, gettext(TXT_rules), name);
221*0Sstevel@tonic-gate 				errs |= ERR_FILES;
222*0Sstevel@tonic-gate 			}
223*0Sstevel@tonic-gate 			continue;
224*0Sstevel@tonic-gate 		}
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate 	bad:	/* log the error and continue processing to find others	*/
227*0Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_badinput),
228*0Sstevel@tonic-gate 			lex_linenum, errstr, name);
229*0Sstevel@tonic-gate 		errs |= ERR_FILES;
230*0Sstevel@tonic-gate 	}
231*0Sstevel@tonic-gate 
232*0Sstevel@tonic-gate 
233*0Sstevel@tonic-gate 	(void) fclose(file);
234*0Sstevel@tonic-gate 	return (errs);
235*0Sstevel@tonic-gate }
236*0Sstevel@tonic-gate 
237*0Sstevel@tonic-gate /*
238*0Sstevel@tonic-gate  * routine:
239*0Sstevel@tonic-gate  *	read_cmd
240*0Sstevel@tonic-gate  *
241*0Sstevel@tonic-gate  * purpose:
242*0Sstevel@tonic-gate  *	to lex a runnable command (! lines) into a buffer
243*0Sstevel@tonic-gate  *
244*0Sstevel@tonic-gate  * parameters:
245*0Sstevel@tonic-gate  *	first token
246*0Sstevel@tonic-gate  *
247*0Sstevel@tonic-gate  * returns:
248*0Sstevel@tonic-gate  *	pointer to a command line in a static buffer
249*0Sstevel@tonic-gate  *	(it is assumed the caller will copy it promptly)
250*0Sstevel@tonic-gate  *
251*0Sstevel@tonic-gate  * notes:
252*0Sstevel@tonic-gate  *	this is necessary because lex has already choped off
253*0Sstevel@tonic-gate  *	the first token for us
254*0Sstevel@tonic-gate  */
255*0Sstevel@tonic-gate static char *read_cmd(char * s)
256*0Sstevel@tonic-gate {
257*0Sstevel@tonic-gate 	static char cmdbuf[ MAX_LINE ];
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate 	cmdbuf[0] = 0;
260*0Sstevel@tonic-gate 
261*0Sstevel@tonic-gate 	do {
262*0Sstevel@tonic-gate 		if (*s) {
263*0Sstevel@tonic-gate 			strcat(cmdbuf, s);
264*0Sstevel@tonic-gate 			strcat(cmdbuf, " ");
265*0Sstevel@tonic-gate 		}
266*0Sstevel@tonic-gate 	} while ((s = lex(0)) != 0);
267*0Sstevel@tonic-gate 
268*0Sstevel@tonic-gate 	return (cmdbuf);
269*0Sstevel@tonic-gate }
270*0Sstevel@tonic-gate 
271*0Sstevel@tonic-gate /*
272*0Sstevel@tonic-gate  * routine:
273*0Sstevel@tonic-gate  *	write_rules
274*0Sstevel@tonic-gate  *
275*0Sstevel@tonic-gate  * purpose:
276*0Sstevel@tonic-gate  *	to rewrite the rules file, appending the new rules
277*0Sstevel@tonic-gate  *
278*0Sstevel@tonic-gate  * parameters:
279*0Sstevel@tonic-gate  *	name of output file
280*0Sstevel@tonic-gate  *
281*0Sstevel@tonic-gate  * returns:
282*0Sstevel@tonic-gate  *	error mask
283*0Sstevel@tonic-gate  *
284*0Sstevel@tonic-gate  */
285*0Sstevel@tonic-gate errmask_t
286*0Sstevel@tonic-gate write_rules(char *name)
287*0Sstevel@tonic-gate {	FILE *newfile;
288*0Sstevel@tonic-gate 	errmask_t errs = 0;
289*0Sstevel@tonic-gate 	struct base *bp;
290*0Sstevel@tonic-gate 	char tmpname[ MAX_PATH ];
291*0Sstevel@tonic-gate 
292*0Sstevel@tonic-gate 	/* if no-touch is specified, we don't update files	*/
293*0Sstevel@tonic-gate 	if (opt_notouch || rules_added == 0)
294*0Sstevel@tonic-gate 		return (0);
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate 	/* create a temporary output file			*/
297*0Sstevel@tonic-gate 	sprintf(tmpname, "%s-TMP", name);
298*0Sstevel@tonic-gate 
299*0Sstevel@tonic-gate 	/* create our output file	*/
300*0Sstevel@tonic-gate 	newfile = fopen(tmpname, "w+");
301*0Sstevel@tonic-gate 	if (newfile == NULL) {
302*0Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_creat), gettext(TXT_rules),
303*0Sstevel@tonic-gate 			name);
304*0Sstevel@tonic-gate 		return (ERR_FILES);
305*0Sstevel@tonic-gate 	}
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 	if (opt_debug & DBG_FILES)
308*0Sstevel@tonic-gate 		fprintf(stderr, "FILE: UPDATE RULES %s\n", name);
309*0Sstevel@tonic-gate 
310*0Sstevel@tonic-gate 	errs |= rw_header(newfile);
311*0Sstevel@tonic-gate 	errs |= rw_base(newfile, &omnibase);
312*0Sstevel@tonic-gate 	for (bp = bases; bp; bp = bp->b_next)
313*0Sstevel@tonic-gate 		errs |= rw_base(newfile, bp);
314*0Sstevel@tonic-gate 
315*0Sstevel@tonic-gate 	if (ferror(newfile)) {
316*0Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_write), gettext(TXT_rules),
317*0Sstevel@tonic-gate 			tmpname);
318*0Sstevel@tonic-gate 		errs |= ERR_FILES;
319*0Sstevel@tonic-gate 	}
320*0Sstevel@tonic-gate 
321*0Sstevel@tonic-gate 	if (fclose(newfile)) {
322*0Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_fclose), gettext(TXT_rules),
323*0Sstevel@tonic-gate 			tmpname);
324*0Sstevel@tonic-gate 		errs |= ERR_FILES;
325*0Sstevel@tonic-gate 	}
326*0Sstevel@tonic-gate 
327*0Sstevel@tonic-gate 	/* now switch the new file for the old one	*/
328*0Sstevel@tonic-gate 	if (errs == 0)
329*0Sstevel@tonic-gate 		if (rename(tmpname, name) != 0) {
330*0Sstevel@tonic-gate 			fprintf(stderr, gettext(ERR_rename),
331*0Sstevel@tonic-gate 				gettext(TXT_rules), tmpname, name);
332*0Sstevel@tonic-gate 			errs |= ERR_FILES;
333*0Sstevel@tonic-gate 		}
334*0Sstevel@tonic-gate 
335*0Sstevel@tonic-gate 	return (errs);
336*0Sstevel@tonic-gate }
337*0Sstevel@tonic-gate 
338*0Sstevel@tonic-gate /*
339*0Sstevel@tonic-gate  * routine:
340*0Sstevel@tonic-gate  *	rw_header
341*0Sstevel@tonic-gate  *
342*0Sstevel@tonic-gate  * purpose:
343*0Sstevel@tonic-gate  *	to write out a rules header
344*0Sstevel@tonic-gate  *
345*0Sstevel@tonic-gate  * parameters:
346*0Sstevel@tonic-gate  *	FILE* for the output file
347*0Sstevel@tonic-gate  *
348*0Sstevel@tonic-gate  * returns:
349*0Sstevel@tonic-gate  *	error mask
350*0Sstevel@tonic-gate  *
351*0Sstevel@tonic-gate  * notes:
352*0Sstevel@tonic-gate  */
353*0Sstevel@tonic-gate static errmask_t rw_header(FILE *file)
354*0Sstevel@tonic-gate {
355*0Sstevel@tonic-gate 	time_t now;
356*0Sstevel@tonic-gate 	struct tm *local;
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate 	/* figure out what time it is	*/
359*0Sstevel@tonic-gate 	(void) time(&now);
360*0Sstevel@tonic-gate 	local = localtime(&now);
361*0Sstevel@tonic-gate 
362*0Sstevel@tonic-gate 	fprintf(file, "%s %d.%d\n", RULE_TAG, RULE_MAJOR, RULE_MINOR);
363*0Sstevel@tonic-gate 	fprintf(file, "#\n");
364*0Sstevel@tonic-gate 	fprintf(file, "# filesync rules, last written by %s, %s",
365*0Sstevel@tonic-gate 		cuserid((char *) 0), asctime(local));
366*0Sstevel@tonic-gate 	fprintf(file, "#\n");
367*0Sstevel@tonic-gate 
368*0Sstevel@tonic-gate 	return (0);
369*0Sstevel@tonic-gate }
370*0Sstevel@tonic-gate 
371*0Sstevel@tonic-gate /*
372*0Sstevel@tonic-gate  * routine:
373*0Sstevel@tonic-gate  *	rw_base
374*0Sstevel@tonic-gate  *
375*0Sstevel@tonic-gate  * purpose:
376*0Sstevel@tonic-gate  *	to write out the summary for one base-pair
377*0Sstevel@tonic-gate  *
378*0Sstevel@tonic-gate  * parameters:
379*0Sstevel@tonic-gate  *	FILE * for the output file
380*0Sstevel@tonic-gate  *
381*0Sstevel@tonic-gate  * returns:
382*0Sstevel@tonic-gate  *	error mask
383*0Sstevel@tonic-gate  *
384*0Sstevel@tonic-gate  * notes:
385*0Sstevel@tonic-gate  */
386*0Sstevel@tonic-gate static errmask_t rw_base(FILE *file, struct base *bp)
387*0Sstevel@tonic-gate {	struct rule *rp;
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate 	fprintf(file, "\n");
390*0Sstevel@tonic-gate 
391*0Sstevel@tonic-gate 	/* global rules don't appear within a base */
392*0Sstevel@tonic-gate 	if (bp->b_ident)
393*0Sstevel@tonic-gate 		fprintf(file, "BASE %s %s\n", noblanks(bp->b_src_spec),
394*0Sstevel@tonic-gate 				noblanks(bp->b_dst_spec));
395*0Sstevel@tonic-gate 
396*0Sstevel@tonic-gate 	for (rp = bp->b_includes; rp; rp = rp->r_next)
397*0Sstevel@tonic-gate 		if (rp->r_flags & R_PROGRAM)
398*0Sstevel@tonic-gate 			fprintf(file, "LIST !%s\n", rp->r_file);
399*0Sstevel@tonic-gate 		else
400*0Sstevel@tonic-gate 			fprintf(file, "LIST %s\n", noblanks(rp->r_file));
401*0Sstevel@tonic-gate 
402*0Sstevel@tonic-gate 	for (rp = bp->b_excludes; rp; rp = rp->r_next)
403*0Sstevel@tonic-gate 		if (rp->r_flags & R_PROGRAM)
404*0Sstevel@tonic-gate 			fprintf(file, "IGNORE !%s\n", rp->r_file);
405*0Sstevel@tonic-gate 		else
406*0Sstevel@tonic-gate 			fprintf(file, "IGNORE %s\n", noblanks(rp->r_file));
407*0Sstevel@tonic-gate 
408*0Sstevel@tonic-gate 	return (0);
409*0Sstevel@tonic-gate }
410*0Sstevel@tonic-gate 
411*0Sstevel@tonic-gate /*
412*0Sstevel@tonic-gate  * routine:
413*0Sstevel@tonic-gate  *	add_rule
414*0Sstevel@tonic-gate  *
415*0Sstevel@tonic-gate  * purpose:
416*0Sstevel@tonic-gate  *	to add a new rule
417*0Sstevel@tonic-gate  *
418*0Sstevel@tonic-gate  * parameters:
419*0Sstevel@tonic-gate  *	pointer to list base
420*0Sstevel@tonic-gate  *	rule flags
421*0Sstevel@tonic-gate  *	associated name/arguments
422*0Sstevel@tonic-gate  *
423*0Sstevel@tonic-gate  * returns:
424*0Sstevel@tonic-gate  *	error flags
425*0Sstevel@tonic-gate  *
426*0Sstevel@tonic-gate  * notes:
427*0Sstevel@tonic-gate  *	we always copy the argument string because most of them
428*0Sstevel@tonic-gate  *	were read from a file and are just in a transient buffer
429*0Sstevel@tonic-gate  */
430*0Sstevel@tonic-gate static errmask_t add_rule(struct base *bp, int flags, const char *args)
431*0Sstevel@tonic-gate {	struct rule *rp;
432*0Sstevel@tonic-gate 	struct rule **list;
433*0Sstevel@tonic-gate 
434*0Sstevel@tonic-gate 	rp = malloc(sizeof (struct rule));
435*0Sstevel@tonic-gate 	if (rp == 0)
436*0Sstevel@tonic-gate 		nomem("rule struture");
437*0Sstevel@tonic-gate 
438*0Sstevel@tonic-gate 	/* initialize the new base			*/
439*0Sstevel@tonic-gate 	memset((void *) rp, 0, sizeof (struct rule));
440*0Sstevel@tonic-gate 	rp->r_flags = flags;
441*0Sstevel@tonic-gate 	rp->r_file = strdup(args);
442*0Sstevel@tonic-gate 
443*0Sstevel@tonic-gate 	/* figure out which list to put it on		*/
444*0Sstevel@tonic-gate 	if (flags&R_IGNORE)
445*0Sstevel@tonic-gate 		list = &bp->b_excludes;
446*0Sstevel@tonic-gate 	else if (flags&R_RESTRICT)
447*0Sstevel@tonic-gate 		list = &bp->b_restrictions;
448*0Sstevel@tonic-gate 	else
449*0Sstevel@tonic-gate 		list = &bp->b_includes;
450*0Sstevel@tonic-gate 
451*0Sstevel@tonic-gate 	while (*list)
452*0Sstevel@tonic-gate 		list = &((*list)->r_next);
453*0Sstevel@tonic-gate 	*list = rp;
454*0Sstevel@tonic-gate 
455*0Sstevel@tonic-gate 	if (flags & R_NEW)
456*0Sstevel@tonic-gate 		rules_added++;
457*0Sstevel@tonic-gate 
458*0Sstevel@tonic-gate 	if (opt_debug & DBG_RULE) {
459*0Sstevel@tonic-gate 		fprintf(stderr, "RULE: base=%d, ", bp->b_ident);
460*0Sstevel@tonic-gate 		fprintf(stderr, "flags=%s, ",
461*0Sstevel@tonic-gate 			showflags(rflags, rp->r_flags));
462*0Sstevel@tonic-gate 		fprintf(stderr, "arg=%s\n", rp->r_file);
463*0Sstevel@tonic-gate 	}
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate 	return (0);
466*0Sstevel@tonic-gate }
467*0Sstevel@tonic-gate 
468*0Sstevel@tonic-gate /*
469*0Sstevel@tonic-gate  * routine:
470*0Sstevel@tonic-gate  *	add_ignore, add_include
471*0Sstevel@tonic-gate  *
472*0Sstevel@tonic-gate  * purpose:
473*0Sstevel@tonic-gate  *	wrappers for add_rule that permit outsiders (like main.c)
474*0Sstevel@tonic-gate  *	not to know what is inside of a base, file, or list entry
475*0Sstevel@tonic-gate  *
476*0Sstevel@tonic-gate  * parameters:
477*0Sstevel@tonic-gate  *	base under which rules should be added
478*0Sstevel@tonic-gate  *	argument associated with rule
479*0Sstevel@tonic-gate  *
480*0Sstevel@tonic-gate  * returns:
481*0Sstevel@tonic-gate  *	error flags
482*0Sstevel@tonic-gate  *
483*0Sstevel@tonic-gate  * notes:
484*0Sstevel@tonic-gate  *	basically these routines figure out what the right
485*0Sstevel@tonic-gate  *	flags are for a rule, and what list to put it on,
486*0Sstevel@tonic-gate  *	and then call a common handler.
487*0Sstevel@tonic-gate  */
488*0Sstevel@tonic-gate errmask_t
489*0Sstevel@tonic-gate add_ignore(struct base *bp, char *name)
490*0Sstevel@tonic-gate {	int flags = R_IGNORE | R_NEW;
491*0Sstevel@tonic-gate 
492*0Sstevel@tonic-gate 	if (bp == 0)
493*0Sstevel@tonic-gate 		bp = &omnibase;
494*0Sstevel@tonic-gate 
495*0Sstevel@tonic-gate 	if (wildcards(name))
496*0Sstevel@tonic-gate 		flags |= R_WILD;
497*0Sstevel@tonic-gate 
498*0Sstevel@tonic-gate 	return (add_rule(bp, flags, name));
499*0Sstevel@tonic-gate }
500*0Sstevel@tonic-gate 
501*0Sstevel@tonic-gate errmask_t
502*0Sstevel@tonic-gate add_include(struct base *bp, char *name)
503*0Sstevel@tonic-gate {	int flags = R_NEW;
504*0Sstevel@tonic-gate 
505*0Sstevel@tonic-gate 	if (bp == 0)
506*0Sstevel@tonic-gate 		bp = &omnibase;
507*0Sstevel@tonic-gate 
508*0Sstevel@tonic-gate 	if (wildcards(name))
509*0Sstevel@tonic-gate 		flags |= R_WILD;
510*0Sstevel@tonic-gate 
511*0Sstevel@tonic-gate 	bp->b_flags |= F_LISTED;
512*0Sstevel@tonic-gate 
513*0Sstevel@tonic-gate 	return (add_rule(bp, flags, name));
514*0Sstevel@tonic-gate }
515*0Sstevel@tonic-gate 
516*0Sstevel@tonic-gate /*
517*0Sstevel@tonic-gate  * routine:
518*0Sstevel@tonic-gate  *	add_restr
519*0Sstevel@tonic-gate  *
520*0Sstevel@tonic-gate  * purpose:
521*0Sstevel@tonic-gate  *	to add a restriction to a base
522*0Sstevel@tonic-gate  *
523*0Sstevel@tonic-gate  * parameters:
524*0Sstevel@tonic-gate  *	address of base
525*0Sstevel@tonic-gate  *	restriction string
526*0Sstevel@tonic-gate  *
527*0Sstevel@tonic-gate  * returns:
528*0Sstevel@tonic-gate  * 	error mask
529*0Sstevel@tonic-gate  *
530*0Sstevel@tonic-gate  * notes:
531*0Sstevel@tonic-gate  *	a restriction is specified on the command line and
532*0Sstevel@tonic-gate  *	tells us to limit our analysis/reconcilation to
533*0Sstevel@tonic-gate  *	specified files and/or directories.  We deal with
534*0Sstevel@tonic-gate  *	these by adding a restriction rule to any base that
535*0Sstevel@tonic-gate  *	looks like it might fit the restriction.  We need to
536*0Sstevel@tonic-gate  *	treat this as a rule because the restriction string
537*0Sstevel@tonic-gate  *	may extend beyond the base directory and part-way into
538*0Sstevel@tonic-gate  *	its tree ... meaning that individual file names under
539*0Sstevel@tonic-gate  *	the base will have to be checked against the restriction.
540*0Sstevel@tonic-gate  */
541*0Sstevel@tonic-gate errmask_t
542*0Sstevel@tonic-gate add_restr(char *restr)
543*0Sstevel@tonic-gate {	const char *s;
544*0Sstevel@tonic-gate 	errmask_t errs = 0;
545*0Sstevel@tonic-gate 	struct base *bp;
546*0Sstevel@tonic-gate 
547*0Sstevel@tonic-gate 	for (bp = bases; bp; bp = bp->b_next) {
548*0Sstevel@tonic-gate 		/*
549*0Sstevel@tonic-gate 		 * see if this restriction could apply to this base.
550*0Sstevel@tonic-gate 		 * It could match either the source or destination
551*0Sstevel@tonic-gate 		 * directory name for this base.  If it matches neither
552*0Sstevel@tonic-gate 		 * then the restriction does not apply to this base.
553*0Sstevel@tonic-gate 		 */
554*0Sstevel@tonic-gate 		s = prefix(restr, bp->b_src_name);
555*0Sstevel@tonic-gate 		if (s == 0)
556*0Sstevel@tonic-gate 			s = prefix(restr, bp->b_dst_name);
557*0Sstevel@tonic-gate 		if (s == 0)
558*0Sstevel@tonic-gate 			continue;
559*0Sstevel@tonic-gate 
560*0Sstevel@tonic-gate 		/*
561*0Sstevel@tonic-gate 		 * if there is more restriction string after the
562*0Sstevel@tonic-gate 		 * base, we will need to note the remainder of the
563*0Sstevel@tonic-gate 		 * string so that we can match individual files
564*0Sstevel@tonic-gate 		 * against it.
565*0Sstevel@tonic-gate 		 */
566*0Sstevel@tonic-gate 		if (*s == '/')
567*0Sstevel@tonic-gate 			s++;
568*0Sstevel@tonic-gate 
569*0Sstevel@tonic-gate 		errs |= add_rule(bp, R_RESTRICT, s);
570*0Sstevel@tonic-gate 		restr_added++;
571*0Sstevel@tonic-gate 	}
572*0Sstevel@tonic-gate 
573*0Sstevel@tonic-gate 	return (errs);
574*0Sstevel@tonic-gate }
575*0Sstevel@tonic-gate 
576*0Sstevel@tonic-gate /*
577*0Sstevel@tonic-gate  * routine:
578*0Sstevel@tonic-gate  *	check_restr
579*0Sstevel@tonic-gate  *
580*0Sstevel@tonic-gate  * purpose:
581*0Sstevel@tonic-gate  *	to see if an argument falls within restrictions
582*0Sstevel@tonic-gate  *
583*0Sstevel@tonic-gate  * parameters:
584*0Sstevel@tonic-gate  *	pointer to relevent base
585*0Sstevel@tonic-gate  *	file name
586*0Sstevel@tonic-gate  *
587*0Sstevel@tonic-gate  * returns:
588*0Sstevel@tonic-gate  *	TRUE	name is within restrictions
589*0Sstevel@tonic-gate  *	FALSE	name is outside of restrictions
590*0Sstevel@tonic-gate  *	MAYBE	name is on the path to a restriction
591*0Sstevel@tonic-gate  *
592*0Sstevel@tonic-gate  * notes:
593*0Sstevel@tonic-gate  *	if no restrictions have been specified, we evaluate
594*0Sstevel@tonic-gate  *	everything.  If any restrictions have been specified,
595*0Sstevel@tonic-gate  *	we process only files that match one of the restrictions.
596*0Sstevel@tonic-gate  *
597*0Sstevel@tonic-gate  *	add_restr has ensured that if the restriction includes
598*0Sstevel@tonic-gate  *	a portion that must be matched by individual files under
599*0Sstevel@tonic-gate  *	the base, that the restriction rule will contain that
600*0Sstevel@tonic-gate  *	portion of the restriction which must be matched against
601*0Sstevel@tonic-gate  *	individual file names.
602*0Sstevel@tonic-gate  */
603*0Sstevel@tonic-gate bool_t
604*0Sstevel@tonic-gate check_restr(struct base *bp, const char *name)
605*0Sstevel@tonic-gate {	struct rule *rp;
606*0Sstevel@tonic-gate 
607*0Sstevel@tonic-gate 	/* if there are no restrictions, everything is OK	*/
608*0Sstevel@tonic-gate 	if (restr_added == 0)
609*0Sstevel@tonic-gate 		return (TRUE);
610*0Sstevel@tonic-gate 
611*0Sstevel@tonic-gate 	/* now we have to run through the list			*/
612*0Sstevel@tonic-gate 	for (rp = bp->b_restrictions; rp; rp = rp->r_next) {
613*0Sstevel@tonic-gate 		/* see if current path is under the restriction	*/
614*0Sstevel@tonic-gate 		if (prefix(name, rp->r_file))
615*0Sstevel@tonic-gate 			return (TRUE);
616*0Sstevel@tonic-gate 
617*0Sstevel@tonic-gate 		/* see if current path is on the way to restr	*/
618*0Sstevel@tonic-gate 		if (prefix(rp->r_file, name))
619*0Sstevel@tonic-gate 			/*
620*0Sstevel@tonic-gate 			 * this is kinky, but walker really needs
621*0Sstevel@tonic-gate 			 * to know the difference between a directory
622*0Sstevel@tonic-gate 			 * that we are unreservedly scanning, and one
623*0Sstevel@tonic-gate 			 * that we are scanning only to find something
624*0Sstevel@tonic-gate 			 * beneath it.
625*0Sstevel@tonic-gate 			 */
626*0Sstevel@tonic-gate 			return (MAYBE);
627*0Sstevel@tonic-gate 	}
628*0Sstevel@tonic-gate 
629*0Sstevel@tonic-gate 	/*
630*0Sstevel@tonic-gate 	 * there are restrictions in effect and this file doesn't seem
631*0Sstevel@tonic-gate 	 * to meet any of them
632*0Sstevel@tonic-gate 	 */
633*0Sstevel@tonic-gate 	if (opt_debug & DBG_RULE)
634*0Sstevel@tonic-gate 		fprintf(stderr, "RULE: FAIL RESTRICTION base=%d, file=%s\n",
635*0Sstevel@tonic-gate 			bp->b_ident, name);
636*0Sstevel@tonic-gate 
637*0Sstevel@tonic-gate 	return (FALSE);
638*0Sstevel@tonic-gate }
639