Backed out changeset e05f5705283d (wrong name)
[unleashed.git] / usr / src / cmd / filesync / rules.c
blob7789ae5c4782f951eb812719b3d6e3d5f1ad46a2
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 * rules.c
28 * purpose:
29 * to read and write the rules file and manage rules lists
31 * contents:
32 * reading rules file
33 * read_rules
34 * (static) read_command
35 * writing rules file
36 * write_rules
37 * (static) rw_header, rw_base
38 * adding rules
39 * add_ignore, add_include
40 * (static) add_rule
41 * adding/checking restrictions
42 * add_restr, check_restr
44 #pragma ident "%Z%%M% %I% %E% SMI"
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <time.h>
50 #include <ctype.h>
52 #include "filesync.h"
53 #include "database.h"
54 #include "messages.h"
55 #include "debug.h"
58 * routines:
60 static errmask_t rw_base(FILE *file, struct base *bp);
61 static errmask_t rw_header(FILE *file);
62 static errmask_t add_rule(struct base *, int, const char *);
63 static char *read_cmd(char *);
66 * globals
68 static int rules_added;
69 static int restr_added;
72 * locals
74 #define RULE_MAJOR 1 /* rules file format major rev */
75 #define RULE_MINOR 1 /* rules file format minor rev */
76 #define RULE_TAG "PACKINGRULES" /* magic string for rules files */
79 * routine:
80 * read_rules
82 * purpose:
83 * to read in the rules file
85 * parameters:
86 * name of rules file
88 * returns:
89 * error mask
91 * notes:
92 * later when I implement a proper (comment preserving) update
93 * function I'm going to wish I had figured out how to build the
94 * input functions for this function in a way that would make
95 * the more usable for that too.
97 errmask_t
98 read_rules(char *name)
99 { FILE *file;
100 errmask_t errs = 0;
101 int flags;
102 int major, minor;
103 char *s, *s1, *s2;
104 struct base *bp;
105 char *errstr = "???";
107 file = fopen(name, "r");
108 if (file == NULL) {
109 fprintf(stderr, gettext(ERR_open), gettext(TXT_rules),
110 name);
111 return (ERR_FILES);
114 lex_linenum = 0;
116 if (opt_debug & DBG_FILES)
117 fprintf(stderr, "FILE: READ RULES %s\n", name);
119 bp = &omnibase; /* default base before any others */
121 while (!feof(file)) {
122 /* find the first token on the line */
123 s = lex(file);
125 /* skip blank lines and comments */
126 if (s == 0 || *s == 0 || *s == '#' || *s == '*')
127 continue;
129 /* see if the first token is a known keyword */
130 if (strcmp(s, "BASE") == 0) {
132 /* get the source & destination tokens */
133 errstr = gettext(TXT_srcdst);
134 s1 = lex(0);
135 if (s1 == 0)
136 goto bad;
137 s1 = strdup(s1);
139 s2 = lex(0);
140 if (s2 == 0)
141 goto bad;
142 s2 = strdup(s2);
144 /* creat the new base pair */
145 bp = add_base(s1, s2);
146 bp->b_flags |= F_LISTED;
148 free(s1);
149 free(s2);
150 continue;
153 if (strcmp(s, "LIST") == 0) {
155 /* make sure we are associated with a real base */
156 if (bp == &omnibase) {
157 errstr = gettext(TXT_nobase);
158 goto bad;
161 /* skip to the next token */
162 s = lex(0);
163 errstr = gettext(TXT_noargs);
164 if (s == 0)
165 goto bad;
167 /* see if it is a program or a name */
168 if (*s == '!') {
169 errs |= add_rule(bp, R_PROGRAM,
170 read_cmd(&s[1]));
171 } else {
172 do {
173 flags = wildcards(s) ? R_WILD : 0;
174 errs |= add_rule(bp, flags, s);
175 s = lex(0);
176 } while (s != 0);
178 continue;
181 if (strcmp(s, "IGNORE") == 0) {
183 /* skip to the next token */
184 s = lex(0);
185 errstr = gettext(TXT_noargs);
186 if (s == 0)
187 goto bad;
189 flags = R_IGNORE;
191 /* see if it is a program or a name */
192 if (*s == '!') {
193 errs |= add_rule(bp, R_PROGRAM|flags,
194 read_cmd(&s[1]));
195 } else {
196 do {
197 if (wildcards(s))
198 flags |= R_WILD;
199 errs |= add_rule(bp, flags, s);
200 s = lex(0);
201 } while (s != 0);
203 continue;
206 if (strcmp(s, "VERSION") == 0 || strcmp(s, RULE_TAG) == 0) {
207 s = lex(0);
208 errstr = gettext(TXT_noargs);
209 if (s == 0)
210 goto bad;
212 major = strtol(s, &s1, 10);
213 errstr = gettext(TXT_badver);
214 if (*s1 != '.')
215 goto bad;
216 minor = strtol(&s1[1], 0, 10);
218 if (major != RULE_MAJOR || minor > RULE_MINOR) {
219 fprintf(stderr, gettext(ERR_badver),
220 major, minor, gettext(TXT_rules), name);
221 errs |= ERR_FILES;
223 continue;
226 bad: /* log the error and continue processing to find others */
227 fprintf(stderr, gettext(ERR_badinput),
228 lex_linenum, errstr, name);
229 errs |= ERR_FILES;
233 (void) fclose(file);
234 return (errs);
238 * routine:
239 * read_cmd
241 * purpose:
242 * to lex a runnable command (! lines) into a buffer
244 * parameters:
245 * first token
247 * returns:
248 * pointer to a command line in a static buffer
249 * (it is assumed the caller will copy it promptly)
251 * notes:
252 * this is necessary because lex has already choped off
253 * the first token for us
255 static char *read_cmd(char * s)
257 static char cmdbuf[ MAX_LINE ];
259 cmdbuf[0] = 0;
261 do {
262 if (*s) {
263 strcat(cmdbuf, s);
264 strcat(cmdbuf, " ");
266 } while ((s = lex(0)) != 0);
268 return (cmdbuf);
272 * routine:
273 * write_rules
275 * purpose:
276 * to rewrite the rules file, appending the new rules
278 * parameters:
279 * name of output file
281 * returns:
282 * error mask
285 errmask_t
286 write_rules(char *name)
287 { FILE *newfile;
288 errmask_t errs = 0;
289 struct base *bp;
290 char tmpname[ MAX_PATH ];
292 /* if no-touch is specified, we don't update files */
293 if (opt_notouch || rules_added == 0)
294 return (0);
296 /* create a temporary output file */
297 sprintf(tmpname, "%s-TMP", name);
299 /* create our output file */
300 newfile = fopen(tmpname, "w+");
301 if (newfile == NULL) {
302 fprintf(stderr, gettext(ERR_creat), gettext(TXT_rules),
303 name);
304 return (ERR_FILES);
307 if (opt_debug & DBG_FILES)
308 fprintf(stderr, "FILE: UPDATE RULES %s\n", name);
310 errs |= rw_header(newfile);
311 errs |= rw_base(newfile, &omnibase);
312 for (bp = bases; bp; bp = bp->b_next)
313 errs |= rw_base(newfile, bp);
315 if (ferror(newfile)) {
316 fprintf(stderr, gettext(ERR_write), gettext(TXT_rules),
317 tmpname);
318 errs |= ERR_FILES;
321 if (fclose(newfile)) {
322 fprintf(stderr, gettext(ERR_fclose), gettext(TXT_rules),
323 tmpname);
324 errs |= ERR_FILES;
327 /* now switch the new file for the old one */
328 if (errs == 0)
329 if (rename(tmpname, name) != 0) {
330 fprintf(stderr, gettext(ERR_rename),
331 gettext(TXT_rules), tmpname, name);
332 errs |= ERR_FILES;
335 return (errs);
339 * routine:
340 * rw_header
342 * purpose:
343 * to write out a rules header
345 * parameters:
346 * FILE* for the output file
348 * returns:
349 * error mask
351 * notes:
353 static errmask_t rw_header(FILE *file)
355 time_t now;
356 struct tm *local;
358 /* figure out what time it is */
359 (void) time(&now);
360 local = localtime(&now);
362 fprintf(file, "%s %d.%d\n", RULE_TAG, RULE_MAJOR, RULE_MINOR);
363 fprintf(file, "#\n");
364 fprintf(file, "# filesync rules, last written by %s, %s",
365 cuserid((char *) 0), asctime(local));
366 fprintf(file, "#\n");
368 return (0);
372 * routine:
373 * rw_base
375 * purpose:
376 * to write out the summary for one base-pair
378 * parameters:
379 * FILE * for the output file
381 * returns:
382 * error mask
384 * notes:
386 static errmask_t rw_base(FILE *file, struct base *bp)
387 { struct rule *rp;
389 fprintf(file, "\n");
391 /* global rules don't appear within a base */
392 if (bp->b_ident)
393 fprintf(file, "BASE %s %s\n", noblanks(bp->b_src_spec),
394 noblanks(bp->b_dst_spec));
396 for (rp = bp->b_includes; rp; rp = rp->r_next)
397 if (rp->r_flags & R_PROGRAM)
398 fprintf(file, "LIST !%s\n", rp->r_file);
399 else
400 fprintf(file, "LIST %s\n", noblanks(rp->r_file));
402 for (rp = bp->b_excludes; rp; rp = rp->r_next)
403 if (rp->r_flags & R_PROGRAM)
404 fprintf(file, "IGNORE !%s\n", rp->r_file);
405 else
406 fprintf(file, "IGNORE %s\n", noblanks(rp->r_file));
408 return (0);
412 * routine:
413 * add_rule
415 * purpose:
416 * to add a new rule
418 * parameters:
419 * pointer to list base
420 * rule flags
421 * associated name/arguments
423 * returns:
424 * error flags
426 * notes:
427 * we always copy the argument string because most of them
428 * were read from a file and are just in a transient buffer
430 static errmask_t add_rule(struct base *bp, int flags, const char *args)
431 { struct rule *rp;
432 struct rule **list;
434 rp = malloc(sizeof (struct rule));
435 if (rp == 0)
436 nomem("rule struture");
438 /* initialize the new base */
439 memset((void *) rp, 0, sizeof (struct rule));
440 rp->r_flags = flags;
441 rp->r_file = strdup(args);
443 /* figure out which list to put it on */
444 if (flags&R_IGNORE)
445 list = &bp->b_excludes;
446 else if (flags&R_RESTRICT)
447 list = &bp->b_restrictions;
448 else
449 list = &bp->b_includes;
451 while (*list)
452 list = &((*list)->r_next);
453 *list = rp;
455 if (flags & R_NEW)
456 rules_added++;
458 if (opt_debug & DBG_RULE) {
459 fprintf(stderr, "RULE: base=%d, ", bp->b_ident);
460 fprintf(stderr, "flags=%s, ",
461 showflags(rflags, rp->r_flags));
462 fprintf(stderr, "arg=%s\n", rp->r_file);
465 return (0);
469 * routine:
470 * add_ignore, add_include
472 * purpose:
473 * wrappers for add_rule that permit outsiders (like main.c)
474 * not to know what is inside of a base, file, or list entry
476 * parameters:
477 * base under which rules should be added
478 * argument associated with rule
480 * returns:
481 * error flags
483 * notes:
484 * basically these routines figure out what the right
485 * flags are for a rule, and what list to put it on,
486 * and then call a common handler.
488 errmask_t
489 add_ignore(struct base *bp, char *name)
490 { int flags = R_IGNORE | R_NEW;
492 if (bp == 0)
493 bp = &omnibase;
495 if (wildcards(name))
496 flags |= R_WILD;
498 return (add_rule(bp, flags, name));
501 errmask_t
502 add_include(struct base *bp, char *name)
503 { int flags = R_NEW;
505 if (bp == 0)
506 bp = &omnibase;
508 if (wildcards(name))
509 flags |= R_WILD;
511 bp->b_flags |= F_LISTED;
513 return (add_rule(bp, flags, name));
517 * routine:
518 * add_restr
520 * purpose:
521 * to add a restriction to a base
523 * parameters:
524 * address of base
525 * restriction string
527 * returns:
528 * error mask
530 * notes:
531 * a restriction is specified on the command line and
532 * tells us to limit our analysis/reconcilation to
533 * specified files and/or directories. We deal with
534 * these by adding a restriction rule to any base that
535 * looks like it might fit the restriction. We need to
536 * treat this as a rule because the restriction string
537 * may extend beyond the base directory and part-way into
538 * its tree ... meaning that individual file names under
539 * the base will have to be checked against the restriction.
541 errmask_t
542 add_restr(char *restr)
543 { const char *s;
544 errmask_t errs = 0;
545 struct base *bp;
547 for (bp = bases; bp; bp = bp->b_next) {
549 * see if this restriction could apply to this base.
550 * It could match either the source or destination
551 * directory name for this base. If it matches neither
552 * then the restriction does not apply to this base.
554 s = prefix(restr, bp->b_src_name);
555 if (s == 0)
556 s = prefix(restr, bp->b_dst_name);
557 if (s == 0)
558 continue;
561 * if there is more restriction string after the
562 * base, we will need to note the remainder of the
563 * string so that we can match individual files
564 * against it.
566 if (*s == '/')
567 s++;
569 errs |= add_rule(bp, R_RESTRICT, s);
570 restr_added++;
573 return (errs);
577 * routine:
578 * check_restr
580 * purpose:
581 * to see if an argument falls within restrictions
583 * parameters:
584 * pointer to relevant base
585 * file name
587 * returns:
588 * TRUE name is within restrictions
589 * FALSE name is outside of restrictions
590 * MAYBE name is on the path to a restriction
592 * notes:
593 * if no restrictions have been specified, we evaluate
594 * everything. If any restrictions have been specified,
595 * we process only files that match one of the restrictions.
597 * add_restr has ensured that if the restriction includes
598 * a portion that must be matched by individual files under
599 * the base, that the restriction rule will contain that
600 * portion of the restriction which must be matched against
601 * individual file names.
603 bool_t
604 check_restr(struct base *bp, const char *name)
605 { struct rule *rp;
607 /* if there are no restrictions, everything is OK */
608 if (restr_added == 0)
609 return (TRUE);
611 /* now we have to run through the list */
612 for (rp = bp->b_restrictions; rp; rp = rp->r_next) {
613 /* see if current path is under the restriction */
614 if (prefix(name, rp->r_file))
615 return (TRUE);
617 /* see if current path is on the way to restr */
618 if (prefix(rp->r_file, name))
620 * this is kinky, but walker really needs
621 * to know the difference between a directory
622 * that we are unreservedly scanning, and one
623 * that we are scanning only to find something
624 * beneath it.
626 return (MAYBE);
630 * there are restrictions in effect and this file doesn't seem
631 * to meet any of them
633 if (opt_debug & DBG_RULE)
634 fprintf(stderr, "RULE: FAIL RESTRICTION base=%d, file=%s\n",
635 bp->b_ident, name);
637 return (FALSE);