/******************************************************************************* Copyright(C) Jonas 'Sortie' Termansen 2013. This file is part of Sortix. Sortix is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Sortix is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Sortix. If not, see . rules.cpp Determines whether a given path is included in the filesystem. *******************************************************************************/ #include #include #include #include #include #include #include #include #include "rules.h" static void error_fp(FILE* fp, int status, int errnum, const char* format, ...) { fprintf(fp, "%s: ", program_invocation_name); va_list list; va_start(list, format); vfprintf(fp, format, list); va_end(list); if ( errnum ) fprintf(fp, ": %s", strerror(errnum)); fprintf(fp, "\n"); if ( status ) exit(status); } static const char* SkipCharacters(const char* str, char c) { while ( *str == c) str++; return str; } // /usr/bin/foobar match /usr = true // /usr/bin/foobar match usr = false // ///usr////bin//foobar match //usr// = true // ///usr////bin//foobar match //usr//./evince = false // TODO: Should this support . and .. too? static bool PathMatchesPattern(const char* path, const char* pattern) { bool last_was_slash = false; while ( true ) { if ( !*pattern ) return !*path || last_was_slash; if ( (last_was_slash = *pattern == '/') ) { if ( *path == '/' ) { path = SkipCharacters(path, '/'); pattern = SkipCharacters(pattern, '/'); continue; } return false; } if ( *pattern++ != *path++ ) return false; } } InclusionRule::InclusionRule(const char* pattern, InclusionRuleType rule) { this->pattern = strdup(pattern); this->rule = rule; } InclusionRule::~InclusionRule() { free(pattern); } bool InclusionRule::MatchesPath(const char* path) const { return PathMatchesPattern(path, pattern); } InclusionRules::InclusionRules() { rules = NULL; num_rules = num_rules_allocated = 0; default_inclusion = true; } InclusionRules::~InclusionRules() { for ( size_t i = 0; i < num_rules; i++ ) delete rules[i]; delete[] rules; } bool InclusionRules::IncludesPath(const char* path) const { bool determined = false; bool included = false; for ( size_t i = 0; i < num_rules; i++ ) { InclusionRule* rule = rules[i]; if ( !rule->MatchesPath(path) ) continue; switch ( rules[i]->rule ) { case RULE_INCLUDE: included = true; determined = true; break; case RULE_EXCLUDE: included = false; determined = true; break; } } if ( !determined ) included = default_inclusion; return included; } bool InclusionRules::ChangeRulesAmount(size_t new_length) { size_t new_num_rules = new_length < num_rules ? new_length : num_rules; for ( size_t i = new_num_rules; i < num_rules; i++ ) delete rules[i]; num_rules = new_num_rules; InclusionRule** new_rules = new InclusionRule*[new_length]; for ( size_t i = 0; i < new_length && i < num_rules; i++ ) new_rules[i] = rules[i]; delete[] rules; rules = new_rules; num_rules_allocated = new_length; return true; } bool InclusionRules::AddRule(InclusionRule* rule) { if ( num_rules == num_rules_allocated ) { size_t new_length = num_rules_allocated ? 2 * num_rules_allocated : 32; if ( !ChangeRulesAmount(new_length) ) return false; } rules[num_rules++] = rule; return true; } static const char* SkipWhitespace(const char* line) { while ( *line && isspace(*line) ) line++; return line; } static char* SkipWhitespace(char* line) { return (char*) SkipWhitespace((const char*) line); } static bool IsLineComment(const char* line) { return !*line || *line == '#'; } static const char* IsLineCommand(const char* line, const char* command) { while ( *line && isspace(*line) ) line++; size_t cmdlen = strlen(command); if ( strncmp(line, command, cmdlen) != 0 ) return NULL; if ( line[cmdlen] && !isspace(line[cmdlen]) ) return NULL; while ( line[cmdlen] && isspace(line[cmdlen]) ) cmdlen++; return line + cmdlen; } bool InclusionRules::AddRulesFromFile(FILE* fp, FILE* err, const char* fpname) { size_t rules_at_start = num_rules; size_t line_size; size_t line_num = 0; char* mem = NULL; ssize_t line_len; while ( 0 <= (line_len = getline(&mem, &line_size, fp)) ) { char* line = mem; line_num++; if ( line_len && line[line_len-1] == '\n' ) line[line_len-1] = '\0'; line = SkipWhitespace(line); if ( IsLineComment(line) ) continue; const char* parameter; if ( (parameter = IsLineCommand(line, "default")) ) { bool value; if ( !strcmp(parameter, "true") ) value = true; else if ( !strcmp(parameter, "true") ) value = false; else { error_fp(err, 0, 0, "%s:%zu: not a boolean '%s'", fpname, line_num, parameter); goto error_out; } if ( !default_inclusion_determined ) default_inclusion = value, default_inclusion_determined = true; else default_inclusion = default_inclusion || value; } else if ( (parameter = IsLineCommand(line, "exclude")) || (parameter = IsLineCommand(line, "include")) ) { if ( !*parameter ) { error_fp(err, 0, 0, "%s:%zu: no parameter given", fpname, line_num); goto error_out; } const char* pattern = parameter; InclusionRuleType type = line[0] == 'e' ? RULE_EXCLUDE : RULE_INCLUDE; InclusionRule* rule = new InclusionRule(pattern, type); if ( !AddRule(rule) ) goto error_out_errno; } else { error_fp(err, 0, 0, "%s:%zu: line not understood: '%s'", fpname, line_num, line); goto error_out; } } free(mem); if ( ferror(fp) ) { error_out_errno: error_fp(err, 0, errno, "%s", fpname); error_out: ChangeRulesAmount(rules_at_start); return false; } return true; }