about summary refs log tree commit diff stats
path: root/rf.c
diff options
context:
space:
mode:
Diffstat (limited to 'rf.c')
-rw-r--r--rf.c261
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;
+}