diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Cli/Cli.c | 34 | ||||
| -rw-r--r-- | src/Cli/Cli.h | 3 | ||||
| -rw-r--r-- | src/FileProcessor/FileProcessor.c | 45 | ||||
| -rw-r--r-- | src/FileProcessor/FileProcessor.h | 4 | ||||
| -rw-r--r-- | src/Main.c | 19 | ||||
| -rw-r--r-- | src/Pattern/Pattern.c | 148 | ||||
| -rw-r--r-- | src/Pattern/Pattern.h | 27 |
7 files changed, 269 insertions, 11 deletions
diff --git a/src/Cli/Cli.c b/src/Cli/Cli.c index 3648493..c7569da 100644 --- a/src/Cli/Cli.c +++ b/src/Cli/Cli.c @@ -16,6 +16,8 @@ void cli_print_usage(const char *program_name) { " -d, --directory DIRECTORY Process a directory recursively\n" " -o, --output OUTPUT Set the output path\n" " -f, --overwrite Overwrite an existing output directory\n" + " --include PATTERN Keep entries matching PATTERN\n" + " --exclude PATTERN Drop entries matching PATTERN\n" " -l, --log-level LEVEL error, warning, info, debug, or silent\n" " -v, --version Show the program version\n" " -h, --help Show this help\n", @@ -28,12 +30,29 @@ static const char *strip_optional_equals(const char *value) { return value != NULL && value[0] == '=' ? value + 1 : value; } +static CliParseResult append_pattern(CliOptions *options, PatternKind kind, + const char *value, const char *name) { + const char *pattern = strip_optional_equals(value); + + if (pattern_list_append(&options->patterns, kind, pattern) != 0) { + logger_error("invalid %s pattern '%s'", name, pattern); + return CLI_PARSE_ERROR; + } + return CLI_PARSE_OK; +} + +void cli_free_options(CliOptions *options) { + pattern_list_clear(&options->patterns); +} + CliParseResult cli_parse(int argc, char **argv, CliOptions *options) { static const struct option long_options[] = { {"directory", required_argument, NULL, 'd'}, {"output", required_argument, NULL, 'o'}, {"overwrite", no_argument, NULL, 'f'}, {"force", no_argument, NULL, 'f'}, + {"include", required_argument, NULL, 1}, + {"exclude", required_argument, NULL, 2}, {"log-level", required_argument, NULL, 'l'}, {"version", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, @@ -46,6 +65,9 @@ CliParseResult cli_parse(int argc, char **argv, CliOptions *options) { options->output_path = NULL; options->overwrite_output = 0; options->log_level = LOG_LEVEL_INFO; + options->patterns.items = NULL; + options->patterns.length = 0; + options->patterns.capacity = 0; while ((option = getopt_long(argc, argv, "d:o:fl:vh", long_options, NULL)) != -1) { @@ -59,6 +81,18 @@ CliParseResult cli_parse(int argc, char **argv, CliOptions *options) { case 'f': options->overwrite_output = 1; break; + case 1: + if (append_pattern(options, PATTERN_INCLUDE, optarg, "include") != + CLI_PARSE_OK) { + return CLI_PARSE_ERROR; + } + break; + case 2: + if (append_pattern(options, PATTERN_EXCLUDE, optarg, "exclude") != + CLI_PARSE_OK) { + return CLI_PARSE_ERROR; + } + break; case 'l': if (logger_parse_level(optarg, &options->log_level) != 0) { logger_error("unknown log level '%s'", optarg); diff --git a/src/Cli/Cli.h b/src/Cli/Cli.h index 9695fd4..e574c88 100644 --- a/src/Cli/Cli.h +++ b/src/Cli/Cli.h @@ -2,6 +2,7 @@ #define YAPSSG_CLI_H #include "Logger/Logger.h" +#include "Pattern/Pattern.h" typedef enum { INPUT_TYPE_NONE, @@ -22,9 +23,11 @@ typedef struct { const char *output_path; int overwrite_output; LogLevel log_level; + PatternList patterns; } CliOptions; CliParseResult cli_parse(int argc, char **argv, CliOptions *options); +void cli_free_options(CliOptions *options); void cli_print_usage(const char *program_name); #endif diff --git a/src/FileProcessor/FileProcessor.c b/src/FileProcessor/FileProcessor.c index d856fb4..84efece 100644 --- a/src/FileProcessor/FileProcessor.c +++ b/src/FileProcessor/FileProcessor.c @@ -6,6 +6,7 @@ #include "Logger/Logger.h" #include "LuaEngine/LuaEngine.h" #include "Path/Path.h" +#include "Pattern/Pattern.h" #include <dirent.h> #include <errno.h> @@ -206,28 +207,49 @@ static int copy_symlink(const char *input_path, const char *output_path, return 0; } -static int copy_tree(const char *input_path, const char *output_path) { +static int copy_tree(const char *input_path, const char *output_path, + const char *relative_path, const PatternList *patterns, + PatternKind subtree_kind) { struct stat status; DIR *directory; + PatternKind contents_kind; int result = 0; if (lstat(input_path, &status) != 0) { logger_error("cannot inspect %s: %s", input_path, strerror(errno)); return -1; } - if (S_ISLNK(status.st_mode)) { - return copy_symlink(input_path, output_path, &status); - } - if (S_ISREG(status.st_mode)) { + + if (S_ISLNK(status.st_mode) || S_ISREG(status.st_mode)) { + if (pattern_list_resolve(patterns, relative_path, 0, subtree_kind) == + PATTERN_EXCLUDE) { + logger_debug("excluding %s", input_path); + return 0; + } + if (S_ISLNK(status.st_mode)) { + return copy_symlink(input_path, output_path, &status); + } return has_html_extension(input_path) ? process_file(input_path, output_path) : copy_regular_file(input_path, output_path, status.st_mode); } + if (!S_ISDIR(status.st_mode)) { logger_error("unsupported file type: %s", input_path); return -1; } + if (relative_path[0] == '\0') { + contents_kind = pattern_list_default_kind(patterns); + } else { + contents_kind = + pattern_list_resolve(patterns, relative_path, 1, subtree_kind); + if (contents_kind == PATTERN_EXCLUDE) { + logger_debug("excluding directory %s", input_path); + return 0; + } + } + if (mkdir(output_path, status.st_mode & 0777) != 0) { logger_error("cannot create directory %s: %s", output_path, strerror(errno)); @@ -244,6 +266,7 @@ static int copy_tree(const char *input_path, const char *output_path) { struct dirent *entry; char *input_child; char *output_child; + char *relative_child; errno = 0; entry = readdir(directory); @@ -261,14 +284,17 @@ static int copy_tree(const char *input_path, const char *output_path) { input_child = path_join(input_path, entry->d_name); output_child = path_join(output_path, entry->d_name); - if (input_child == NULL || output_child == NULL) { + relative_child = path_join(relative_path, entry->d_name); + if (input_child == NULL || output_child == NULL || relative_child == NULL) { logger_error("unable to allocate child path"); result = -1; } else { - result = copy_tree(input_child, output_child); + result = copy_tree(input_child, output_child, relative_child, patterns, + contents_kind); } free(input_child); free(output_child); + free(relative_child); } if (closedir(directory) != 0 && result == 0) { @@ -346,7 +372,7 @@ static int remove_tree(const char *path) { } int process_directory(const char *input_path, const char *output_path, - int overwrite_output) { + int overwrite_output, const PatternList *patterns) { struct stat status; struct stat output_status; char *resolved_input; @@ -387,7 +413,8 @@ int process_directory(const char *input_path, const char *output_path, result = -1; } else { logger_info("processing directory %s -> %s", input_path, output_path); - result = copy_tree(input_path, output_path); + result = copy_tree(input_path, output_path, "", patterns, + pattern_list_default_kind(patterns)); } free(resolved_input); diff --git a/src/FileProcessor/FileProcessor.h b/src/FileProcessor/FileProcessor.h index 90027d7..55a00bb 100644 --- a/src/FileProcessor/FileProcessor.h +++ b/src/FileProcessor/FileProcessor.h @@ -1,8 +1,10 @@ #ifndef YAPSSG_FILE_PROCESSOR_H #define YAPSSG_FILE_PROCESSOR_H +#include "Pattern/Pattern.h" + int process_file(const char *input_path, const char *output_path); int process_directory(const char *input_path, const char *output_path, - int overwrite_output); + int overwrite_output, const PatternList *patterns); #endif @@ -6,6 +6,13 @@ #include <libxml/parser.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> + +static const char *base_name(const char *path) { + const char *slash = strrchr(path, '/'); + + return slash == NULL ? path : slash + 1; +} static int run(int argc, char **argv) { CliOptions options; @@ -16,6 +23,7 @@ static int run(int argc, char **argv) { parse_result = cli_parse(argc, argv, &options); if (parse_result != CLI_PARSE_OK) { + cli_free_options(&options); return parse_result == CLI_PARSE_ERROR ? EXIT_FAILURE : EXIT_SUCCESS; } @@ -26,6 +34,7 @@ static int run(int argc, char **argv) { automatic_output = path_make_default_output(options.input_path); if (automatic_output == NULL) { logger_error("unable to allocate output path"); + cli_free_options(&options); return EXIT_FAILURE; } output_path = automatic_output; @@ -33,12 +42,20 @@ static int run(int argc, char **argv) { if (options.input_type == INPUT_TYPE_DIRECTORY) { result = process_directory(options.input_path, output_path, - options.overwrite_output); + options.overwrite_output, &options.patterns); + } else if (pattern_list_resolve( + &options.patterns, base_name(options.input_path), 0, + pattern_list_default_kind(&options.patterns)) == + PATTERN_EXCLUDE) { + logger_error("input file is excluded by the active patterns: %s", + options.input_path); + result = -1; } else { result = process_file(options.input_path, output_path); } free(automatic_output); + cli_free_options(&options); return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/Pattern/Pattern.c b/src/Pattern/Pattern.c new file mode 100644 index 0000000..9147e5e --- /dev/null +++ b/src/Pattern/Pattern.c @@ -0,0 +1,148 @@ +#define _POSIX_C_SOURCE 200809L + +#include "Pattern/Pattern.h" + +#include <fnmatch.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +static char *duplicate_string(const char *value, size_t length) { + char *copy = malloc(length + 1); + + if (copy != NULL) { + memcpy(copy, value, length); + copy[length] = '\0'; + } + return copy; +} + +static int grow_pattern_list(PatternList *list) { + size_t larger_capacity; + Pattern *larger_items; + + if (list->capacity > SIZE_MAX / (2 * sizeof(*list->items))) { + return -1; + } + + larger_capacity = list->capacity == 0 ? 4 : list->capacity * 2; + larger_items = realloc(list->items, larger_capacity * sizeof(*list->items)); + if (larger_items == NULL) { + return -1; + } + + list->items = larger_items; + list->capacity = larger_capacity; + return 0; +} + +int pattern_list_append(PatternList *list, PatternKind kind, + const char *pattern) { + size_t length; + int directory_only; + Pattern *entry; + + if (pattern == NULL) { + return -1; + } + + length = strlen(pattern); + directory_only = length > 0 && pattern[length - 1] == '/'; + if (directory_only) { + --length; + } + if (length == 0) { + return -1; + } + + if (list->length == list->capacity && grow_pattern_list(list) != 0) { + return -1; + } + + entry = &list->items[list->length]; + entry->kind = kind; + entry->directory_only = directory_only; + entry->pattern = duplicate_string(pattern, length); + if (entry->pattern == NULL) { + return -1; + } + + ++list->length; + return 0; +} + +void pattern_list_clear(PatternList *list) { + size_t index; + + for (index = 0; index < list->length; ++index) { + free(list->items[index].pattern); + } + free(list->items); + list->items = NULL; + list->length = 0; + list->capacity = 0; +} + +static int has_include(const PatternList *list) { + size_t index; + + for (index = 0; index < list->length; ++index) { + if (list->items[index].kind == PATTERN_INCLUDE) { + return 1; + } + } + return 0; +} + +PatternKind pattern_list_default_kind(const PatternList *list) { + return has_include(list) ? PATTERN_EXCLUDE : PATTERN_INCLUDE; +} + +static const char *base_name(const char *path) { + const char *slash = strrchr(path, '/'); + + return slash == NULL ? path : slash + 1; +} + +static int matches_anchored(const char *pattern, const char *path) { + const char *cursor = path; + + if (fnmatch(pattern, path, FNM_PATHNAME) == 0) { + return 1; + } + while (*cursor != '\0') { + cursor = strchr(cursor, '/'); + if (cursor == NULL) { + break; + } + ++cursor; + if (fnmatch(pattern, cursor, FNM_PATHNAME) == 0) { + return 1; + } + } + return 0; +} + +static int matches_pattern(const Pattern *pattern, const char *path) { + if (strchr(pattern->pattern, '/') == NULL) { + return fnmatch(pattern->pattern, base_name(path), 0) == 0; + } + return matches_anchored(pattern->pattern, path); +} + +PatternKind pattern_list_resolve(const PatternList *list, const char *path, + int is_directory, PatternKind fallback) { + size_t index; + + for (index = 0; index < list->length; ++index) { + const Pattern *pattern = &list->items[index]; + + if (pattern->directory_only && !is_directory) { + continue; + } + if (matches_pattern(pattern, path)) { + return pattern->kind; + } + } + return fallback; +}
\ No newline at end of file diff --git a/src/Pattern/Pattern.h b/src/Pattern/Pattern.h new file mode 100644 index 0000000..52b817f --- /dev/null +++ b/src/Pattern/Pattern.h @@ -0,0 +1,27 @@ +#ifndef YAPSSG_PATTERN_H +#define YAPSSG_PATTERN_H + +#include <stddef.h> + +typedef enum { PATTERN_INCLUDE, PATTERN_EXCLUDE } PatternKind; + +typedef struct { + PatternKind kind; + char *pattern; + int directory_only; +} Pattern; + +typedef struct { + Pattern *items; + size_t length; + size_t capacity; +} PatternList; + +int pattern_list_append(PatternList *list, PatternKind kind, + const char *pattern); +void pattern_list_clear(PatternList *list); +PatternKind pattern_list_default_kind(const PatternList *list); +PatternKind pattern_list_resolve(const PatternList *list, const char *path, + int is_directory, PatternKind fallback); + +#endif
\ No newline at end of file |
