summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backup.c172
-rw-r--r--src/config.c24
-rw-r--r--src/ignore.c66
-rwxr-xr-xsrc/lib.h24
-rwxr-xr-xsrc/main.c123
5 files changed, 409 insertions, 0 deletions
diff --git a/src/backup.c b/src/backup.c
new file mode 100644
index 0000000..d90d1d1
--- /dev/null
+++ b/src/backup.c
@@ -0,0 +1,172 @@
+#include "lib.h"
+#include <dirent.h>
+#include <regex.h>
+
+int check_path_type(const char* path) {
+ struct stat path_stat;
+ if (stat(path, &path_stat) != 0) {
+ return path_type_unknown;
+ }
+ if (S_ISDIR(path_stat.st_mode)) {
+ return path_type_directory;
+ }
+ return path_type_file;
+}
+
+int follows_ignore_rules(char* path, char* ignore_line) {
+ char lastbitofthepath[PATH_MAX];
+ int k = 0;
+ for (int i = 0; path[i] != 0; i++) {
+ if (path[i] == '/') {
+ lastbitofthepath[k] = 0;
+ k = 0;
+ continue;
+ }
+ lastbitofthepath[k++] = path[i];
+ }
+ if (k != 0 && lastbitofthepath[k] != 0) {
+ lastbitofthepath[k] = 0;
+ }
+
+ regex_t re;
+ int ret;
+
+ if (regcomp(&re, ignore_line, REG_EXTENDED) != 0) {
+ return 0;
+ }
+
+ ret = regexec(&re, lastbitofthepath, (size_t) 0, NULL, 0);
+ regfree(&re);
+
+ if (ret == 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int should_ignore(char* path, char* ignore_instructions, int VERBOSE) {
+ int i = 0, k = 0;
+ char line[PATH_MAX];
+ while (ignore_instructions[i] != 0) {
+ if (ignore_instructions[i] != '\n') {
+ line[k++] = ignore_instructions[i];
+ i++;
+ continue;
+ }
+ line[k] = 0;
+ k = 0;
+ if (!follows_ignore_rules(path, line)) {
+ i++;
+ continue;
+ }
+ if (VERBOSE) {
+ printf("Ignoring %s due to ignore rule %s\n", path, line);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+void copy_to_backup(char* source_path, int VERBOSE) {
+ char destination_path[PATH_MAX];
+ int index_of_first_slash = -1;
+ for (int i = 0; i < PATH_MAX; i++) {
+ if (source_path[i] != '/') continue;
+ index_of_first_slash = i;
+ break;
+ }
+
+ if (index_of_first_slash == -1) {
+ // I DON'T EVEN KNOW HOW TO HANDLE THIS
+ fprintf(stderr, "Weird path issue encountered, failed to copy to source\nPath encountered: %s", source_path);
+ return;
+ }
+
+ int i = 0;
+ for (; i < index_of_first_slash; i++) {
+ destination_path[i] = source_path[i];
+ }
+ destination_path[i++] = '.';
+ destination_path[i++] = 'b';
+ destination_path[i] = 'k';
+ while (++i < PATH_MAX) {
+ destination_path[i] = source_path[i - 3];
+ }
+
+ if (VERBOSE) {
+ printf("%s -> %s\n", source_path, destination_path);
+ }
+
+ verify_parent_dirs_exist(destination_path, VERBOSE);
+
+ char command[2 * PATH_MAX + 10];
+ snprintf(command, 2 * PATH_MAX + 10, "cp \"%s\" \"%s\"", source_path, destination_path);
+ system(command);
+ if (VERBOSE) {
+ printf("Executing: %s\n", command);
+ }
+}
+
+void push(char* directories, int cursor, const char* to_add) {
+ int i = 0;
+ for (; to_add[i] != 0; i++) {
+ directories[cursor * PATH_MAX + i] = to_add[i];
+ }
+ while (i < PATH_MAX) {
+ directories[cursor * PATH_MAX + i++] = 0;
+ }
+}
+
+void backup(char* target, char* ignore_instructions, int VERBOSE) {
+ int file_type = check_path_type(target);
+ if (file_type != path_type_directory) {
+ fprintf(stderr, "Input not a directory\n");
+ return;
+ }
+
+ int cursor = 0;
+ char* directories = malloc(PATH_MAX * PATH_MAX * sizeof(*directories));
+ push(directories, cursor, target);
+ while (cursor != -1) {
+ char* path = &directories[(cursor--) * PATH_MAX];
+ DIR* dir_ptr = opendir(path);
+ if (dir_ptr == NULL) {
+ fprintf(stderr, "Couldn't open the directory: %s\n", path);
+ continue;
+ }
+
+ struct dirent* entry_ptr;
+ char full_path[PATH_MAX];
+ int i = 0;
+ for (; path[i] != 0; i++) {
+ full_path[i] = path[i];
+ }
+ full_path[i++] = '/';
+ while ((entry_ptr = readdir(dir_ptr)) != NULL) {
+ if (
+ strcmp(entry_ptr->d_name, ".") == 0 ||
+ strcmp(entry_ptr->d_name, "..") == 0
+ ) continue;
+ int j = 0;
+ for (; entry_ptr->d_name[j] != 0; j++) {
+ full_path[i + j] = entry_ptr->d_name[j];
+ }
+ full_path[i + j] = 0;
+
+ if (VERBOSE) {
+ printf("Looking at: %s\n", full_path);
+ }
+ if (should_ignore(full_path, ignore_instructions, VERBOSE)) {
+ continue;
+ }
+
+ if (entry_ptr->d_type == DT_REG) {
+ copy_to_backup(full_path, VERBOSE);
+ continue;
+ }
+ push(directories, ++cursor, full_path);
+ }
+ closedir(dir_ptr);
+ }
+}
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 0000000..ab0eb05
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,24 @@
+#include "lib.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+char config[PATH_MAX] = {0};
+char* get_default_config() {
+ if (config[0] != 0) return config;
+
+ const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
+ const char* home = getenv("HOME");
+
+ if (home == NULL) {
+ fprintf(stderr, "HOME environment variable not set, failed to determine the config path");
+ return NULL;
+ }
+
+ if (xdg_config_home != NULL && xdg_config_home[0] != 0) {
+ snprintf(config, PATH_MAX, "%s/backbencher/.backupignore", xdg_config_home);
+ } else {
+ snprintf(config, PATH_MAX, "%s/.config/backbencher/.backupignore", home);
+ }
+
+ return config;
+}
diff --git a/src/ignore.c b/src/ignore.c
new file mode 100644
index 0000000..fbfb8ed
--- /dev/null
+++ b/src/ignore.c
@@ -0,0 +1,66 @@
+#include "lib.h"
+
+int _;
+
+void mkdir_if_not_exist(char* path, int VERBOSE) {
+ if (mkdir(path, S_IRWXU) < 0) return;
+ if (VERBOSE) {
+ printf("(Created to create directory %s)", path);
+ }
+}
+
+void verify_parent_dirs_exist(char* file_path, int VERBOSE) {
+ for (int i = 0; file_path[i] != 0; i++) {
+ if (file_path[i] == '/') file_path[i] = 0;
+ if (file_path[i] != 0) continue;
+ mkdir_if_not_exist(file_path, VERBOSE);
+ file_path[i] = '/';
+ }
+}
+
+char* get_ignore_file(char* ignore_file, int VERBOSE) {
+ verify_parent_dirs_exist(ignore_file, VERBOSE);
+ FILE* file = fopen(ignore_file, "r");
+ char* default_config;
+
+ if (file != 0) {
+ float current_pointer = ftell(file);
+ fseek(file, 0, SEEK_END);
+ long int file_size = ftell(file);
+ fseek(file, current_pointer, SEEK_SET);
+
+ char* contents = malloc(file_size * sizeof(*contents));
+ _ = fread(contents, 1, file_size, file);
+ fclose(file);
+
+ return contents;
+ }
+
+ if (strcmp(ignore_file, (default_config = get_default_config())) != 0) {
+ return NULL;
+ }
+
+ // mainly used for first time running
+ if (default_config == NULL) {
+ return NULL;
+ }
+
+ file = fopen(default_config, "w");
+ if (file == 0) {
+ return NULL;
+ }
+
+ int content_length = strlen(DEFAULT_CONFIG_CONTENTS);
+ _ = fwrite(DEFAULT_CONFIG_CONTENTS, 1, content_length, file);
+ fclose(file);
+ if (VERBOSE) {
+ printf("Creating %s\n", default_config);
+ }
+
+ char* contents = malloc(content_length * sizeof(char));
+ for (int i = 0; i < content_length; i++) {
+ contents[i] = DEFAULT_CONFIG_CONTENTS[i];
+ }
+ return contents;
+}
+
diff --git a/src/lib.h b/src/lib.h
new file mode 100755
index 0000000..999e7d7
--- /dev/null
+++ b/src/lib.h
@@ -0,0 +1,24 @@
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define NULL_STR (char*)0
+#define DEFAULT_CONFIG_CONTENTS "^node_modules$\n^target$\n^build$\n^dist$\n^\\.next$\n^\\.output$\n^\\.vercel$\n^\\.wrangler$\n^\\.venv$\n^\\.astro$\n^\\.devenv$\n^__pycache__$"
+
+typedef enum {
+ path_type_unknown,
+ path_type_directory,
+ path_type_file
+} path_type;
+
+char* get_default_config();
+char* shift(int* argc, char*** argv);
+char* get_ignore_file(char* ignore_file, int VERBOSE);
+void mkdir_if_not_exist(char* path, int VERBOSE);
+void verify_parent_dirs_exist(char* file_path, int VERBOSE);
+
+void backup(char* target, char* ignore_instructions, int VERBOSE);
diff --git a/src/main.c b/src/main.c
new file mode 100755
index 0000000..7e6e1ee
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,123 @@
+#include "lib.h"
+
+#define VERSION "1.0"
+
+void help() {
+ printf("Backup your files\n");
+ printf("Usage: Backbencher [OPTIONS] [input]\n");
+ printf("Arguments:\n");
+ printf("\t[input]... The path of the folder to backup\n");
+ printf("Options:\n");
+ printf("\t-i --ignore-file <filename> Specify which files to exclude\n\t\t(default = ~/.config/backbencher/.backupignore)\n");
+ printf("\t-h --help \tPrint help\n");
+ printf("\t-v --verbose \tVerbose logging \n");
+ printf("\t-V --version \tPrint version\n");
+}
+
+void assign(char* to, char* from) {
+ int i = 0;
+ for (; from[i] != 0; i++) {
+ to[i] = from[i];
+ }
+ to[i] = 0;
+}
+
+int source_main(int argc, char** argv) {
+ shift(&argc, &argv);
+
+ char* ignore_file = get_default_config();
+ char* target = NULL_STR;
+ int VERBOSE = 0;
+
+ while (1) {
+ char* segment = shift(&argc, &argv);
+ if (segment == NULL_STR) {
+ break;
+ }
+ else if (segment[0] == '-' && segment[1] == '-') {
+ if (strcmp(&segment[2], "ignore-file") == 0) {
+ assign(&ignore_file[0], shift(&argc, &argv));
+ if (ignore_file == NULL_STR) {
+ fprintf(stderr, "Missing <filename> for --ignore-file\n");
+ return 1;
+ }
+ }
+ else if (strcmp(&segment[2], "help") == 0) {
+ help();
+ return 0;
+ }
+ else if (strcmp(&segment[2], "verbose") == 0) {
+ VERBOSE = 1;
+ }
+ else if (strcmp(&segment[2], "version") == 0) {
+ printf(VERSION);
+ printf("\n");
+ return 0;
+ }
+ } else if (segment[0] == '-') {
+ if (segment[1] == 'i') {
+ assign(&ignore_file[0], shift(&argc, &argv));
+ if (ignore_file == NULL_STR) {
+ fprintf(stderr, "Missing <filename> for --ignore-file\n");
+ return 1;
+ }
+ }
+ else if (segment[1] == 'h') {
+ help();
+ return 0;
+ }
+ else if (segment[1] == 'v') {
+ VERBOSE = 1;
+ }
+ else if (segment[1] == 'V') {
+ printf(VERSION);
+ printf("\n");
+ return 0;
+ }
+ } else {
+ target = segment;
+ }
+ }
+
+ if (target == NULL_STR) {
+ fprintf(stderr, "Missing [input] - The folder to backup\n");
+ return 1;
+ }
+
+ if (VERBOSE) {
+ printf("Target folder: %s\n", target);
+ printf("Using ignore file: %s\n", ignore_file);
+ printf("Using verbosity: %d\n", VERBOSE);
+ }
+
+ char* ignore_instructions = get_ignore_file(ignore_file, VERBOSE);
+ if (ignore_instructions == NULL) {
+ fprintf(stderr, "I don't have permissions to read the ignore file\n");
+ return 1;
+ }
+ if (VERBOSE) {
+ printf("Ignoring rules:\n%s\n", ignore_instructions);
+ }
+
+ backup(target, ignore_instructions, VERBOSE);
+
+ free(ignore_instructions);
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ int result = source_main(argc, argv);
+ if (result != 0) {
+ printf("\n");
+ help();
+ }
+ return result;
+}
+
+char* shift(int* argc, char*** argv) {
+ if (argc == 0) return NULL_STR;
+ --(*argc);
+ char* output = **argv;
+ ++(*argv);
+ return output;
+}