diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/backup.c | 173 | ||||
| -rw-r--r-- | src/backup_streamer.c | 54 | ||||
| -rw-r--r-- | src/config.c | 24 | ||||
| -rw-r--r-- | src/ignore.c | 66 | ||||
| -rw-r--r--[-rwxr-xr-x] | src/lib.h | 61 | ||||
| -rw-r--r--[-rwxr-xr-x] | src/main.c | 143 | ||||
| -rw-r--r-- | src/recursive_reader.c | 21 | ||||
| -rw-r--r-- | src/tmp_repo.c | 72 |
8 files changed, 220 insertions, 394 deletions
diff --git a/src/backup.c b/src/backup.c deleted file mode 100644 index 060777c..0000000 --- a/src/backup.c +++ /dev/null @@ -1,173 +0,0 @@ -#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); - } - free(directories); -} diff --git a/src/backup_streamer.c b/src/backup_streamer.c new file mode 100644 index 0000000..3b5929b --- /dev/null +++ b/src/backup_streamer.c @@ -0,0 +1,54 @@ +#include <unistd.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <assert.h> +#include <string.h> + +#include "lib.h" + +struct backup_streamer backup_streamer_init(char *cwd) { + struct backup_streamer output; + output.cwd_length = strnlen(cwd, PATH_MAX); + char *end = stpncpy(output.to_prefix, cwd, PATH_MAX - 3); + *(end++) = '.'; + *(end++) = 'b'; + *(end++) = 'k'; + *(end++) = 0; + return output; +} + +void get_backup_destination(struct backup_streamer *streamer, char* from) { + char *cursor = stpncpy(streamer->cursor, streamer->to_prefix, PATH_MAX); + from += streamer->cwd_length; + int n = (cursor - streamer->cursor) - 2; + strncpy(cursor, from, PATH_MAX - n); +} + +int is_dir(char *path) { + struct stat info; + if (stat(path, &info) != 0) return -1; + return S_ISDIR(info.st_mode); +} + +void backup_file(char *from, char *to) { + ensure_parents_exist(to); + pid_t pid; + if ((pid = fork()) == 0) { + char *argv[] = {"/usr/bin/cp", from, to, NULL}; + execv(argv[0], argv); + } + assert(waitpid(pid, NULL, 0) != -1); +} + +void ensure_parents_exist(char *path) { + int n = strnlen(path, PATH_MAX); + for (int i = 1; i < n; i++) { + if (path[i] != '/') continue; + path[i] = 0; + if (is_dir(path) == -1) { + debugf("mkdir %s\n", path); + assert(mkdir(path, 0755) != -1); + } + path[i] = '/'; + } +} diff --git a/src/config.c b/src/config.c deleted file mode 100644 index ab0eb05..0000000 --- a/src/config.c +++ /dev/null @@ -1,24 +0,0 @@ -#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 deleted file mode 100644 index fbfb8ed..0000000 --- a/src/ignore.c +++ /dev/null @@ -1,66 +0,0 @@ -#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 index 999e7d7..6c098c7 100755..100644 --- a/src/lib.h +++ b/src/lib.h @@ -1,24 +1,47 @@ -#include <unistd.h> -#include <string.h> -#include <stdio.h> -#include <sys/stat.h> -#include <dirent.h> +#ifndef LIB_H_ +#define LIB_H_ + +#include <git2.h> +#include <linux/limits.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__$" +#define debugf(...) if (debug) (void)printf("[debug] " __VA_ARGS__) + +int is_git_metadata_dir(char *path); + +struct tmp_repo { + git_repository* repo; + char path[70]; +}; +struct tmp_repo tmp_repo_init(); +void tmp_repo_deinit(struct tmp_repo repo); + +int check_for_backupignore(); +void copy_over_backupignore(struct tmp_repo repo); + +int is_ignored(struct tmp_repo repo, char *path); + +struct recursive_reader { + FILE *pfile; + char *line; + size_t capacity; +}; +struct recursive_reader recursive_reader_init(); +int recursive_reader_read(struct recursive_reader *reader); +void recursive_reader_deinit(struct recursive_reader reader); + +struct backup_streamer { + int cwd_length; + char to_prefix[PATH_MAX]; + char cursor[PATH_MAX]; +}; +struct backup_streamer backup_streamer_init(char *cwd); +void get_backup_destination(struct backup_streamer *streamer, char* from); -typedef enum { - path_type_unknown, - path_type_directory, - path_type_file -} path_type; +int is_dir(char *path); +void backup_file(char *from, char *to); +void ensure_parents_exist(char *path); -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); +int debug; -void backup(char* target, char* ignore_instructions, int VERBOSE); +#endif // LIB_H_ diff --git a/src/main.c b/src/main.c index 7d33850..1d419fa 100755..100644 --- a/src/main.c +++ b/src/main.c @@ -1,125 +1,44 @@ -#include "lib.h" +#include <unistd.h> +#include <assert.h> -#define VERSION "1.0" +#include "lib.h" -void help() { - printf( -"Backup your files\n" -"Usage: Backbencher [OPTIONS] [input]\n" -"Arguments:\n" -"\t[input]... The path of the folder to backup\n" -"Options:\n" -"\t-i --ignore-file <filename> Specify which files to exclude\n\t\t(default = ~/.config/backbencher/.backupignore)\n" -"\t-h --help \tPrint help\n" -"\t-v --verbose \tVerbose logging\n" -"\t-V --version \tPrint version\n" - ); -} +int main(int argc, char **argv) { + (void)argv; -void assign(char* to, char* from) { - int i = 0; - for (; from[i] != 0; i++) { - to[i] = from[i]; - } - to[i] = 0; -} + debug = argc > 1; + if (check_for_backupignore()) return 1; + debugf(".backupignore found\n"); -int source_main(int argc, char** argv) { - shift(&argc, &argv); + struct tmp_repo repo = tmp_repo_init(); + assert(repo.repo != NULL); + debugf("%s initialized\n", repo.path); - char* ignore_file = get_default_config(); - char* target = NULL_STR; - int VERBOSE = 0; + copy_over_backupignore(repo); + debugf("ignore file loaded\n"); - 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; - } - } + char *cwd = getcwd(NULL, 0); + assert(cwd != NULL); + struct backup_streamer streamer = backup_streamer_init(cwd); + free(cwd); - if (target == NULL_STR) { - fprintf(stderr, "Missing [input] - The folder to backup\n"); - return 1; - } + struct recursive_reader reader = recursive_reader_init(); + while (recursive_reader_read(&reader) != -1) { + if (is_ignored(repo, reader.line)) continue; + if (is_dir(reader.line)) continue; - if (VERBOSE) { - printf("Target folder: %s\n", target); - printf("Using ignore file: %s\n", ignore_file); - printf("Using verbosity: %d\n", VERBOSE); + get_backup_destination(&streamer, reader.line); + debugf("cp %s %s\n", reader.line, streamer.cursor); + backup_file(reader.line, streamer.cursor); } - 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); + recursive_reader_deinit(reader); + tmp_repo_deinit(repo); + git_repository_free(repo.repo); + git_libgit2_shutdown(); 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; -} +#include "tmp_repo.c" +#include "recursive_reader.c" +#include "backup_streamer.c" diff --git a/src/recursive_reader.c b/src/recursive_reader.c new file mode 100644 index 0000000..0a32131 --- /dev/null +++ b/src/recursive_reader.c @@ -0,0 +1,21 @@ +#include <assert.h> + +#include "lib.h" + +struct recursive_reader recursive_reader_init() { + FILE *pipe = popen("find \"$PWD\" -print", "r"); + assert(pipe != NULL); + return (struct recursive_reader) { pipe, NULL, 0 }; +} + +int recursive_reader_read(struct recursive_reader *reader) { + int n = getline(&reader->line, &reader->capacity, reader->pfile); + reader->line[n - 1] = 0; + return n; +} + +void recursive_reader_deinit(struct recursive_reader reader) { + free(reader.line); + int status = pclose(reader.pfile); + assert(status != -1); +} diff --git a/src/tmp_repo.c b/src/tmp_repo.c new file mode 100644 index 0000000..ff4536c --- /dev/null +++ b/src/tmp_repo.c @@ -0,0 +1,72 @@ +#include <unistd.h> +#include <sys/random.h> +#include <sys/wait.h> +#include <assert.h> +#include <string.h> + +#include "lib.h" + +struct tmp_repo tmp_repo_init() { + unsigned char rng_bytes[64]; + assert(getrandom(rng_bytes, sizeof(rng_bytes), 0) == sizeof(rng_bytes)); + + char repo_path[70] = "/tmp/"; + for (int i = 0; i < 64; i++) { + repo_path[5 + i] = (char)('a' + rng_bytes[i] % 26); + } + repo_path[69] = '\0'; + + git_libgit2_init(); + + git_repository *repo = NULL; + assert(git_repository_init(&repo, repo_path, 0) == 0); + struct tmp_repo output; + memcpy(output.path, repo_path, 70); + output.repo = repo; + return output; +} + +void tmp_repo_deinit(struct tmp_repo repo) { + pid_t pid; + if ((pid = fork()) == 0) { + char *argv[] = {"/usr/bin/rm", "-rf", repo.path, NULL}; + execv(argv[0], argv); + } + assert(waitpid(pid, NULL, 0) != -1); +} + +int check_for_backupignore() { + FILE *file = fopen(".backupignore", "r"); + if (file == NULL) { + fprintf(stderr, "Missing .backupignore file in the current directory\n"); + return 1; + } else fclose(file); + return 0; +} + +void copy_over_backupignore(struct tmp_repo repo) { + char cp_destination[81]; + snprintf(cp_destination, 81, "%s/.gitignore", repo.path); + + pid_t pid; + if ((pid = fork()) == 0) { + char *argv[] = {"/usr/bin/cp", ".backupignore", cp_destination, NULL}; + execv(argv[0], argv); + } + assert(waitpid(pid, NULL, 0) != -1); +} + +int is_ignored(struct tmp_repo repo, char *path) { + if (is_git_metadata_dir(path)) return 0; + int ignored; + assert(git_ignore_path_is_ignored(&ignored, repo.repo, path) >= 0); + return ignored; +} + +int is_git_metadata_dir(char *path) { + int n = strnlen(path, PATH_MAX); + for (int i = 0; i < n - 6; i++) { + if (strncmp(path + i, "/.git/", 6) == 0) return 1; + } + return 0; +} |
