diff options
Diffstat (limited to 'rf.c')
-rw-r--r-- | rf.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/rf.c b/rf.c new file mode 100644 index 0000000..b1f33d7 --- /dev/null +++ b/rf.c @@ -0,0 +1,261 @@ +#include <dirent.h> +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "config.h" + +#define NAME "rf" +#define VERSION "0.0.1" + +struct switches { + int basename; +}; + +int version(char *error) { + fprintf(stderr, "%s version %s\n", NAME, VERSION); + return error == NULL ? 0 : 1; +} + +int usage(char *error) { + if (error != NULL) { + fprintf(stderr, "Error: %s\n\n", error); + } + + fprintf(stderr, "Usage: %s [OPTIONS]\n", NAME); + fprintf(stderr, "\nOptions:\n"); + fprintf(stderr, " --basename, -b only show basename in results\n"); + fprintf(stderr, " --help, -h show help\n"); + fprintf(stderr, " --version show version\n"); + fprintf(stderr, "\n"); + + return version(error); +} + +int is_child(char *dirname) { + if (strcmp("..", dirname) == 0 || strcmp(".", dirname) == 0) { + return 0; + } + + return 1; +} + +int not_in_array(char **arr, char *dirname, size_t size) { + for (int i = 0; i < size; i++) { + if (strstr(dirname, arr[i])) { + return 0; + } + } + + return 1; +} + +int at_side(int beginning, char *filename, char *str) { + int matched = 1; + + if (beginning) { + for (int c = 0; c < strlen(str); c++) { + if (str[c] != filename[c]) { + matched = 0; + break; + } + } + } else { + for (int c = strlen(str), d = strlen(filename); c >= 0; c--, d--) { + if (str[c] != filename[d]) { + matched = 0; + break; + } + } + } + + return matched; +} + +int excluded_extension(char *filename) { + for (int i = 0; i < ignored_extensions_size; i++) { + int res = at_side(0, filename, ignored_extensions[i]); + + if (res) { + return res; + } + } + + return 0; +} + +void print_result(char *path, struct switches *switches, struct dirent *entry) { + if (switches->basename) { + printf("%s\n", entry->d_name); + } else { + char full_path[PATH_MAX]; + full_path[0] = '\0'; + strcat(full_path, path); + strcat(full_path, "/"); + strcat(full_path, entry->d_name); + printf("%s\n", full_path); + } +} + +char *strslice(char *source, int start, int end) { + int diff = start + end; + int len = strlen(source) - diff; + char *output = malloc(sizeof(char *) * len); + memset(output, '\0', len); + + for (int i = 0; i < len; i++) { + output[i] = source[i + start]; + } + + return output; +} + +void recurse_find(char **patterns, int *pattern_count, char *dirname, + struct switches *switches) { + char path[PATH_MAX] = {'\0'}; + strcat(path, dirname); + + DIR *dir = opendir(path); + + if (dir != NULL && not_in_array(ignored_dirs, dirname, ignored_dirs_size)) { + struct dirent *entry; + + while ((entry = readdir(dir)) != NULL) { + int matched = 0; + char *parsed = NULL; + + switch (entry->d_type) { + case DT_DIR: + if (is_child(entry->d_name) && + not_in_array(ignored_dirs, entry->d_name, ignored_dirs_size)) { + char child_path[PATH_MAX] = {'\0'}; + strcat(child_path, path); + strcat(child_path, "/"); + strcat(child_path, entry->d_name); + recurse_find(patterns, pattern_count, child_path, switches); + } + + break; + case DT_REG: + if (excluded_extension(entry->d_name)) { + break; + } + + for (int p = 0; p < *pattern_count; p++) { + char *pattern = patterns[p]; + char first = pattern[0]; + char last = pattern[strlen(pattern) - 1]; + + if (last == '$' && first == '^') { + // show everything + if (strlen(pattern) == 2) { + matched = 1; + } else { + // match whole string + parsed = strslice(pattern, 1, 1); + + if (strcmp(entry->d_name, parsed) == 0) { + matched = 1; + } + } + } else if (last == '$') { + // match at end + parsed = strslice(pattern, 0, 1); + + if (at_side(0, entry->d_name, parsed)) { + matched = 1; + } + } else if (first == '^') { + // match at beginning + parsed = strslice(pattern, 1, 0); + + if (at_side(1, entry->d_name, parsed)) { + matched = 1; + } + } else { + // substring match + if (strstr(entry->d_name, pattern) != NULL) { + matched = 1; + } + } + } + + break; + default: + break; + } + + if (matched) { + print_result(path, switches, entry); + } + + free(parsed); + } + + closedir(dir); + } +} + +int main(int argc, char **argv) { + static struct option options[] = {{"basename", no_argument, 0, 0}, + {"version", no_argument, 0, 0}, + {"help", no_argument, 0, 0}, + {0, 0, 0, 0}}; + + int basename = 0; + + int index = 0; + int res; + + while ((res = getopt_long(argc, argv, "hvb", options, &index)) > -1) { + switch (res) { + case 0: + if (strcmp("version", options[index].name) == 0) { + return version(NULL); + } else if (strcmp("help", options[index].name) == 0) { + return usage(NULL); + } else if (strcmp("basename", options[index].name) == 0) { + basename = 1; + } + + break; + + case 'v': + return version(NULL); + + case 'h': + return usage(NULL); + + case 'b': + basename = 1; + break; + } + } + + if (optind < argc) { + while (optind < argc) { + optind++; + } + + int pattern_count = optind - 1; + + char **patterns = malloc(sizeof(char *) * optind); + memset(patterns, '\0', optind); + + for (int i = 0; i < optind; i++) { + patterns[i] = argv[i + 1]; + } + + struct switches switches = {.basename = basename}; + + recurse_find(patterns, &pattern_count, ".", &switches); + free(patterns); + } + + return 0; +} |