#include <dirent.h>
#include <fnmatch.h>
#include <string.h>
#include "bart.h"
static int count_slashes(const char *);
static struct rule *gen_rulestruct(void);
static struct tree_modifier *gen_tree_modifier(void);
static struct dir_component *gen_dir_component(void);
static void init_rule(uint_t, struct rule *);
static void add_modifier(struct rule *, char *);
static struct rule *add_subtree_rule(char *, char *, int, int *);
static struct rule *add_single_rule(char *);
static void dirs_cleanup(struct dir_component *);
static void add_dir(struct dir_component **, char *);
static char *lex(FILE *);
static int match_subtree(const char *, char *);
static struct rule *get_last_entry(boolean_t);
static int lex_linenum;
static struct rule *first_rule = NULL, *current_rule = NULL;
int
exclude_fname(const char *fname, char fname_type, struct rule *rule_ptr)
{
char *pattern, *ptr, *fname_ptr, saved_char;
char fname_cp[PATH_MAX], pattern_cp[PATH_MAX];
int num_pattern_slash, i, num_fname_slash, slashes_to_adv;
struct tree_modifier *mod_ptr;
if (rule_ptr->modifiers == NULL)
if (rule_ptr->attr_list == 0)
return (EXCLUDE_PRUNE);
else
return (NO_EXCLUDE);
for (mod_ptr = rule_ptr->modifiers; mod_ptr != NULL;
mod_ptr = mod_ptr->next) {
pattern = mod_ptr->mod_str;
if (mod_ptr->is_dir == B_FALSE) {
if (fname_type == 'D') {
if (mod_ptr->include == B_TRUE)
return (EXCLUDE_SKIP);
else
continue;
}
num_pattern_slash = count_slashes(pattern);
num_fname_slash = count_slashes(fname);
if (num_pattern_slash > num_fname_slash) {
if (mod_ptr->include == B_TRUE)
return (EXCLUDE_SKIP);
}
(void) strlcpy(fname_cp, fname, sizeof (fname_cp));
ptr = fname_cp;
slashes_to_adv = num_fname_slash - num_pattern_slash;
for (i = 0; i < slashes_to_adv; i++) {
ptr = strchr(ptr, '/');
ptr++;
}
if ((pattern[0] == '.') && (pattern[1] == '.') &&
(pattern[2] == '/')) {
pattern = strchr(pattern, '/');
ptr = strchr(ptr, '/');
}
if (fnmatch(pattern, ptr, FNM_PATHNAME) == 0) {
if (mod_ptr->include == B_FALSE)
return (EXCLUDE_SKIP);
} else if (mod_ptr->include == B_TRUE) {
return (EXCLUDE_SKIP);
}
} else {
(void) strlcpy(fname_cp,
(fname+strlen(rule_ptr->subtree)),
sizeof (fname_cp));
(void) strlcpy(pattern_cp, pattern,
sizeof (pattern_cp));
if (fname_type != 'D') {
ptr = strrchr(fname_cp, '/');
if (ptr != NULL)
*ptr = '\0';
if (strlen(fname_cp) == 0)
if (mod_ptr->include == B_TRUE)
return (EXCLUDE_SKIP);
}
num_pattern_slash = count_slashes(pattern_cp);
num_fname_slash = count_slashes(fname_cp);
if ((num_pattern_slash > num_fname_slash) &&
(fname_type != 'D')) {
if (mod_ptr->include == B_TRUE)
return (EXCLUDE_SKIP);
}
if (fname_cp[0] == '/') {
(void) strlcpy(fname_cp,
strchr(fname_cp, '/') + 1,
sizeof (fname_cp));
num_fname_slash--;
}
while (num_pattern_slash <= num_fname_slash) {
fname_ptr = fname_cp;
for (i = 0; i < num_pattern_slash; i++) {
ptr = strchr(fname_ptr, '/');
fname_ptr = ptr + 1;
}
saved_char = *(++ptr);
*ptr = '\0';
if (fnmatch(pattern_cp, fname_cp,
FNM_PATHNAME) == 0) {
if (mod_ptr->include == B_TRUE) {
break;
} else if (fname_type == 'D')
return (EXCLUDE_PRUNE);
else
return (EXCLUDE_SKIP);
} else if (mod_ptr->include == B_TRUE) {
if (fname_type == 'D')
return (EXCLUDE_PRUNE);
else
return (EXCLUDE_SKIP);
}
*ptr = saved_char;
(void) strlcpy(fname_cp,
strchr(fname_cp, '/') + 1,
sizeof (fname_cp));
num_fname_slash--;
}
if (num_pattern_slash == (num_fname_slash + 1)) {
ptr = strrchr(pattern_cp, '/');
*ptr = '\0';
if (fnmatch(pattern_cp, fname_cp,
FNM_PATHNAME) == 0) {
if (mod_ptr->include == B_FALSE) {
if (fname_type == 'D')
return (EXCLUDE_PRUNE);
else
return (EXCLUDE_SKIP);
}
} else if (mod_ptr->include == B_TRUE)
return (EXCLUDE_SKIP);
}
}
}
return (NO_EXCLUDE);
}
static int
count_slashes(const char *in_path)
{
int num_fname_slash = 0;
const char *p;
for (p = in_path; *p != '\0'; p++)
if (*p == '/')
num_fname_slash++;
return (num_fname_slash);
}
static struct rule *
gen_rulestruct(void)
{
struct rule *new_rule;
new_rule = (struct rule *)safe_calloc(sizeof (struct rule));
return (new_rule);
}
static struct tree_modifier *
gen_tree_modifier(void)
{
struct tree_modifier *new_modifier;
new_modifier = (struct tree_modifier *)safe_calloc
(sizeof (struct tree_modifier));
return (new_modifier);
}
static struct dir_component *
gen_dir_component(void)
{
struct dir_component *new_dir;
new_dir = (struct dir_component *)safe_calloc
(sizeof (struct dir_component));
return (new_dir);
}
static struct rule *
setup_default_rule(char *reloc_root, uint_t flags)
{
struct rule *new_rule;
new_rule = add_single_rule(reloc_root[0] == '\0' ? "/" : reloc_root);
init_rule(flags, new_rule);
add_modifier(new_rule, "*");
return (new_rule);
}
static void
init_rule(uint_t flags, struct rule *new_rule)
{
if (new_rule == NULL)
return;
new_rule->attr_list = flags;
}
int
read_rules(FILE *file, char *reloc_root, uint_t in_flags, int create)
{
char *s;
struct rule *block_begin = NULL, *new_rule, *rp;
struct attr_keyword *akp;
int check_flag, ignore_flag, syntax_err, ret_code;
int global_block;
ret_code = EXIT;
lex_linenum = 0;
check_flag = 0;
ignore_flag = 0;
syntax_err = 0;
global_block = 1;
if (file == NULL) {
(void) setup_default_rule(reloc_root, in_flags);
return (ret_code);
} else if (!create) {
block_begin = setup_default_rule("/", in_flags);
}
while (!feof(file)) {
s = lex(file);
if (s == NULL || *s == 0 || *s == '#')
continue;
if (s[0] == '/') {
global_block = 0;
new_rule = add_subtree_rule(s, reloc_root, create,
&ret_code);
s = lex(0);
while ((s != NULL) && (*s != 0) && (*s != '#')) {
add_modifier(new_rule, s);
s = lex(0);
}
if (block_begin == NULL ||
(ignore_flag != 0) || (check_flag != 0)) {
block_begin = new_rule;
check_flag = 0;
ignore_flag = 0;
}
init_rule(in_flags, new_rule);
} else if (IGNORE_KEYWORD(s) || CHECK_KEYWORD(s)) {
int check_kw;
if (IGNORE_KEYWORD(s)) {
ignore_flag++;
check_kw = 0;
} else {
check_flag++;
check_kw = 1;
}
s = lex(0);
while ((s != NULL) && (*s != 0) && (*s != '#')) {
akp = attr_keylookup(s);
if (akp == NULL) {
(void) fprintf(stderr, SYNTAX_ERR, s);
syntax_err++;
exit(2);
}
if (global_block) {
if (check_kw)
in_flags |= akp->ak_flags;
else
in_flags &= ~(akp->ak_flags);
} else {
for (rp = block_begin; rp != NULL;
rp = rp->next) {
if (check_kw)
rp->attr_list |=
akp->ak_flags;
else
rp->attr_list &=
~(akp->ak_flags);
}
}
s = lex(0);
}
} else {
(void) fprintf(stderr, SYNTAX_ERR, s);
s = lex(0);
while (s != NULL && *s != 0) {
(void) fprintf(stderr, " %s", s);
s = lex(0);
}
(void) fprintf(stderr, "\n");
syntax_err++;
}
}
(void) fclose(file);
if (syntax_err) {
(void) fprintf(stderr, SYNTAX_ABORT);
exit(2);
}
return (ret_code);
}
static void
add_modifier(struct rule *rule, char *modifier_str)
{
int include, is_dir;
char *pattern;
struct tree_modifier *new_mod_ptr, *curr_mod_ptr;
struct rule *this_rule;
include = B_TRUE;
pattern = modifier_str;
if (pattern[0] == '!') {
include = B_FALSE;
pattern++;
}
is_dir = (pattern[0] != '\0' && pattern[strlen(pattern) - 1] == '/');
for (this_rule = rule; this_rule != NULL; this_rule = this_rule->next) {
new_mod_ptr = gen_tree_modifier();
new_mod_ptr->include = include;
new_mod_ptr->is_dir = is_dir;
new_mod_ptr->mod_str = safe_strdup(pattern);
if (is_dir && !include) {
new_mod_ptr->next = this_rule->modifiers;
this_rule->modifiers = new_mod_ptr;
} else if (this_rule->modifiers == NULL)
this_rule->modifiers = new_mod_ptr;
else {
curr_mod_ptr = this_rule->modifiers;
while (curr_mod_ptr->next != NULL)
curr_mod_ptr = curr_mod_ptr->next;
curr_mod_ptr->next = new_mod_ptr;
}
}
}
static struct rule *
add_subtree_rule(char *rule, char *reloc_root, int create, int *err_code)
{
char full_path[PATH_MAX], pattern[PATH_MAX];
char new_dirname[PATH_MAX];
char *beg_pattern, *end_pattern, *curr_dirname;
struct dir_component *current_level = NULL, *next_level = NULL;
struct dir_component *tmp_ptr;
DIR *dir_ptr;
struct dirent *dir_entry;
struct rule *begin_rule = NULL;
int ret;
struct stat64 statb;
(void) snprintf(full_path, sizeof (full_path),
(rule[0] == '/') ? "%s%s" : "%s/%s", reloc_root, rule);
if (create == 0)
return (add_single_rule(full_path));
add_dir(¤t_level, NULL);
if (strcmp(full_path, "/") == 0)
(void) strcpy(current_level->dirname, "/");
beg_pattern = full_path;
while (beg_pattern != NULL) {
while (*beg_pattern == '/')
beg_pattern++;
if (*beg_pattern == '\0')
break;
end_pattern = strchr(beg_pattern, '/');
if (end_pattern != NULL)
(void) strlcpy(pattern, beg_pattern,
end_pattern - beg_pattern + 1);
else
(void) strlcpy(pattern, beg_pattern, sizeof (pattern));
beg_pattern = end_pattern;
while (current_level != NULL) {
curr_dirname = current_level->dirname;
if (strlen(curr_dirname) == 0)
(void) strcpy(curr_dirname, "/");
dir_ptr = opendir(curr_dirname);
dir_entry = NULL;
if (dir_ptr == NULL) {
perror(curr_dirname);
*err_code = WARNING_EXIT;
} else
dir_entry = readdir(dir_ptr);
while (dir_entry != NULL) {
if ((strcmp(dir_entry->d_name, ".") == 0) ||
(strcmp(dir_entry->d_name, "..") == 0)) {
dir_entry = readdir(dir_ptr);
continue;
}
if (fnmatch(pattern, dir_entry->d_name,
FNM_PATHNAME) == 0) {
if (curr_dirname[strlen(curr_dirname)-1]
!= '/')
(void) snprintf(new_dirname,
sizeof (new_dirname),
"%s/%s", curr_dirname,
dir_entry->d_name);
else
(void) snprintf(new_dirname,
sizeof (new_dirname),
"%s%s", curr_dirname,
dir_entry->d_name);
add_dir(&next_level, new_dirname);
}
dir_entry = readdir(dir_ptr);
}
if (dir_ptr != NULL)
(void) closedir(dir_ptr);
tmp_ptr = current_level;
current_level = current_level->next;
free(tmp_ptr);
}
current_level = next_level;
next_level = NULL;
}
tmp_ptr = current_level;
if (current_level == NULL) {
(void) fprintf(stderr, INVALID_SUBTREE, full_path);
*err_code = WARNING_EXIT;
}
while (current_level != NULL) {
ret = lstat64(current_level->dirname, &statb);
if (ret < 0) {
(void) fprintf(stderr, INVALID_SUBTREE,
current_level->dirname);
current_level = current_level->next;
*err_code = WARNING_EXIT;
continue;
}
if (begin_rule == NULL) {
begin_rule =
add_single_rule(current_level->dirname);
} else
(void) add_single_rule(current_level->dirname);
current_level = current_level->next;
}
dirs_cleanup(tmp_ptr);
return (begin_rule);
}
static struct rule *
add_single_rule(char *path)
{
if (first_rule == NULL) {
first_rule = gen_rulestruct();
current_rule = first_rule;
} else {
current_rule->next = gen_rulestruct();
current_rule->next->prev = current_rule;
current_rule = current_rule->next;
}
(void) strlcpy(current_rule->subtree, path,
sizeof (current_rule->subtree));
return (current_rule);
}
static char *
lex(FILE *file)
{
char c, delim;
char *p;
char *s;
static char *savep;
static char namebuf[ BUF_SIZE ];
static char inbuf[ BUF_SIZE ];
if (file) {
p = inbuf + sizeof (inbuf);
s = inbuf;
while (savep = fgets(s, p - s, file)) {
lex_linenum++;
while (*s && s[1])
s++;
if (*s == '\n')
s--;
if (s < inbuf || *s != '\\')
break;
continue;
}
if (savep == NULL)
return (0);
s = inbuf;
} else {
if (savep == NULL)
return (0);
s = savep;
}
savep = NULL;
while (isspace(*s))
s++;
if (*s == 0)
return (0);
c = *s;
if (c == '\'' || c == '"') {
delim = c;
s++;
} else
delim = 0;
for (p = namebuf; (c = *s) != 0; s++) {
if (c == '\\') {
s++;
*p++ = *s;
continue;
}
if (c == delim) {
s++;
break;
}
if (delim == 0 && isspace(c))
break;
*p++ = *s;
}
savep = *s ? s : 0;
*p = 0;
return (namebuf);
}
static void
dirs_cleanup(struct dir_component *dir)
{
struct dir_component *next;
while (dir != NULL) {
next = dir->next;
free(dir);
dir = next;
}
}
static void
add_dir(struct dir_component **dir, char *dirname)
{
struct dir_component *new, *next_dir;
new = gen_dir_component();
if (dirname != NULL)
(void) strlcpy(new->dirname, dirname, sizeof (new->dirname));
if (*dir == NULL)
*dir = new;
else {
next_dir = *dir;
while (next_dir->next != NULL)
next_dir = next_dir->next;
next_dir->next = new;
}
}
static struct rule *
get_last_entry(boolean_t reset)
{
static struct rule *curr_root = NULL;
if (reset) {
curr_root = first_rule;
while (curr_root != NULL)
if (curr_root->next == NULL)
break;
else
curr_root = curr_root->next;
} else
curr_root = (curr_root->prev);
return (curr_root);
}
struct rule *
get_first_subtree()
{
return (first_rule);
}
struct rule *
get_next_subtree(struct rule *entry)
{
return (entry->next);
}
char *
safe_strdup(char *s)
{
char *ret;
size_t len;
len = strlen(s) + 1;
ret = safe_calloc(len);
(void) strlcpy(ret, s, len);
return (ret);
}
struct rule *
check_rules(const char *fname, char type)
{
struct rule *root;
root = get_last_entry(B_TRUE);
while (root != NULL) {
if (match_subtree(fname, root->subtree)) {
if (exclude_fname(fname, type, root) == NO_EXCLUDE)
break;
}
root = get_last_entry(B_FALSE);
}
return (root);
}
static int
match_subtree(const char *fname, char *rule)
{
int match, num_rule_slash;
char *ptr, fname_cp[PATH_MAX];
if ((num_rule_slash = count_slashes(rule)) > count_slashes(fname))
return (0);
(void) strlcpy(fname_cp, fname, sizeof (fname_cp));
for (ptr = fname_cp; num_rule_slash > 0; num_rule_slash--, ptr++)
ptr = strchr(ptr, '/');
if (*(rule + strlen(rule) - 1) != '/') {
while (*ptr != '\0') {
if (*ptr == '/')
break;
ptr++;
}
}
*ptr = '\0';
match = fnmatch(rule, fname_cp, FNM_PATHNAME);
if (match != 0)
return (0);
else
return (1);
}
void
process_glob_ignores(char *ignore_list, uint_t *flags)
{
char *cp;
struct attr_keyword *akp;
if (ignore_list == NULL)
usage();
cp = strtok(ignore_list, ",");
while (cp != NULL) {
akp = attr_keylookup(cp);
if (akp == NULL)
(void) fprintf(stderr, "ERROR: Invalid keyword %s\n",
cp);
else
*flags &= ~akp->ak_flags;
cp = strtok(NULL, ",");
}
}