diff options
| -rw-r--r-- | .backupignore | 68 | ||||
| -rw-r--r-- | .clangd | 3 | ||||
| -rwxr-xr-x | .envrc | 12 | ||||
| -rw-r--r--[-rwxr-xr-x] | .gitignore | 11 | ||||
| -rwxr-xr-x | CMakeLists.txt | 14 | ||||
| -rwxr-xr-x | LICENSE | 21 | ||||
| -rwxr-xr-x | README.md | 23 | ||||
| -rwxr-xr-x | backbencher | bin | 0 -> 18840 bytes | |||
| -rw-r--r-- | build.c | 126 | ||||
| -rwxr-xr-x | devenv.lock | 103 | ||||
| -rwxr-xr-x | devenv.nix | 8 | ||||
| -rwxr-xr-x | devenv.yaml | 15 | ||||
| -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 |
20 files changed, 417 insertions, 601 deletions
diff --git a/.backupignore b/.backupignore new file mode 100644 index 0000000..15aa790 --- /dev/null +++ b/.backupignore @@ -0,0 +1,68 @@ +node_modules/ +__pycache__/ +target/ +.next/ +dist/ +build/ +*.iso + +.authinfo +backp/ +.bash_history +.bash_logout +.bun +.cache/ +.cargo/ +.config/ +# Desktop +# dev +.dmrc +.docker +# Documents +# Downloads +.face +.face.icon +# .gitconfig +# .gitconfig-local +# .gitconfig-org +.gnupg +go +.ICEauthority +.lesshst +.local +# Mail +# .mbsyncrc +# Music +.node_repl_history +.npm +.nv +.nvidia-settings-rc +.nvm +.omp +opt +# orgs +# .password-store +# Pictures +.pki +prep +.profile +Public +.pyenv +.python_history +.rustup +.sentry-native +# .ssh +.steam +!.steam/debian-installation/steamapps/common/assettocorsa/content/cars +!.steam/debian-installation/steamapps/common/assettocorsa/content/tracks +.steampath +.steampid +.sudo_as_admin_successful +Templates +.var +# Videos +.wget-hsts +.Xauthority +.xsession +.xsession-errors +.xsession-errors.old @@ -0,0 +1,3 @@ +CompileFlags: + Add: + - -lgit2 @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -export DIRENV_WARN_TIMEOUT=20s - -eval "$(devenv direnvrc)" - -# `use devenv` supports the same options as the `devenv shell` command. -# -# To silence all output, use `--quiet`. -# -# Example usage: use devenv --quiet --impure --option services.postgres.enable:bool true -use devenv diff --git a/.gitignore b/.gitignore index 60bb522..378eac2 100755..100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1 @@ -# Devenv -.devenv* -devenv.local.nix -devenv.local.yaml - -# direnv -.direnv - -# pre-commit -.pre-commit-config.yaml - build diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100755 index 0125655..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.10) -project(backbencher) - -file(GLOB SOURCES "src/**/*.c") -file(GLOB SRC_FILES "src/*.c") -set(SOURCES ${SOURCES} ${SRC_FILES}) - -if(SOURCES) - add_executable(backbencher ${SOURCES}) -else() - message(FATAL_ERROR "No .c files found in source") -endif() - -target_compile_options(backbencher PRIVATE -Ofast) diff --git a/LICENSE b/LICENSE deleted file mode 100755 index 1b40a2e..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 Aargh Rai - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100755 index 35e8b23..0000000 --- a/README.md +++ /dev/null @@ -1,23 +0,0 @@ -## Backbencher - -Backup your files -Currently only UNIX compliant - -### Usage -```bash -backbencher [OPTIONS] [input] -``` - -### Arguments -- `[input]...` - The path of the folder to backup - -### Options -```bash --i, --ignore-file <filename> Specify which files to exclude - (default: ~/.config/backbencher/.backupignore) - --h, --help Print help --v, --verbose Verbose logging --V, --version Print version -``` diff --git a/backbencher b/backbencher Binary files differnew file mode 100755 index 0000000..308bea5 --- /dev/null +++ b/backbencher @@ -0,0 +1,126 @@ +#include <sys/wait.h> +#include <sys/stat.h> +#include <unistd.h> +#include <linux/limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <time.h> +#include <string.h> + +#define RELEASE_MODE + +#ifdef RELEASE_MODE +#define CC "gcc", "-O3" +#else +#define CC "gcc", "-fsanitize=address", "-g" +#endif + +#define EXE_NAME "backbencher" + +int status; + +#define RUN_CMD(...) do { \ + if (fork() == 0) { \ + char* args[] = __VA_ARGS__; \ + print_cmd(args); \ + execvp(args[0], args); \ + return 0; \ + } else { \ + wait(&status); \ + } \ +} while (0); + +void print_cmd(char** args) { + int i = 0; + while (args[i] != NULL) { + printf("%s ", args[i++]); + } + printf("\n"); +} + +// https://github.com/tsoding/nob.h/blob/0a08926d8094fc4ae678155c5d73ae21d1f96f3f/nob.h#L2413 +int needs_rebuild(char* bin_file, char* source_file) { + struct stat statbuf = {0}; + if (stat(bin_file, &statbuf) < 0) { + if (errno == ENOENT) return 1; + return -1; + } + time_t output_path_time = statbuf.st_mtime; + + if (stat(source_file, &statbuf) < 0) { + fprintf(stderr, "could not stat %s: %s", source_file, strerror(errno)); + return -1; + } + + time_t input_path_time = statbuf.st_mtime; + return input_path_time > output_path_time; +} + +void getlibgit2(char *output, int output_max_size) { + int link[2]; + pid_t pid; + + pipe(link); + pid = fork(); + + if (pid == 0) { + dup2(link[1], STDOUT_FILENO); + close(link[0]); + close(link[1]); + execl("/usr/bin/pkg-config", "--cflags", "--libs", "libgit2", (char*)0); + exit(0); + } + + close(link[1]); + int nbytes = read(link[0], output, output_max_size); + wait(NULL); + + int i = -1; + while (++i < nbytes) { + if (output[i] >= 32) continue; + nbytes = i - 1; + break; + } + output[nbytes] = 0; + return; +} + +int main(int argc, char** argv) { + if (needs_rebuild(argv[0], __FILE__)) { + RUN_CMD({CC, __FILE__, "-o", argv[0], NULL}); + if (status != 0) { + // cc error + return 0; + } + print_cmd(argv); + execvp(argv[0], argv); + } + + if (argc >= 2 && strcmp(argv[1], "install") == 0) { + printf("install -m 755 " EXE_NAME " /usr/local/bin/" EXE_NAME); + char dest[PATH_MAX]; + snprintf(dest, sizeof(dest), "/usr/local/bin/%s", EXE_NAME); + char *install_args[] = {"install", "-m", "755", EXE_NAME, dest, NULL}; + execvp("install", install_args); + perror("execvp"); + return 0; + } + if (argc >= 2 && strcmp(argv[1], "release") == 0) { + char libgit[4096]; + getlibgit2(libgit, 4096); + RUN_CMD({CC, "src/main.c", libgit, "-o", EXE_NAME, "-O3", NULL}); + return 0; + } + + char libgit[4096]; + getlibgit2(libgit, 4096); + RUN_CMD({CC, "src/main.c", libgit, "-o", EXE_NAME, NULL}); + + if (status == 0 && argc >= 2) { + argv[1] = malloc(1024); + strncpy(argv[1], "./" EXE_NAME, 1024); + print_cmd(argv + 1); + execvp(argv[1], argv + 1); + } +} diff --git a/devenv.lock b/devenv.lock deleted file mode 100755 index 1dcb611..0000000 --- a/devenv.lock +++ /dev/null @@ -1,103 +0,0 @@ -{ - "nodes": { - "devenv": { - "locked": { - "dir": "src/modules", - "lastModified": 1765463314, - "owner": "cachix", - "repo": "devenv", - "rev": "ec805a5ad3e3ffb6d47d05c078a60db2e636fac4", - "type": "github" - }, - "original": { - "dir": "src/modules", - "owner": "cachix", - "repo": "devenv", - "type": "github" - } - }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1765121682, - "owner": "edolstra", - "repo": "flake-compat", - "rev": "65f23138d8d09a92e30f1e5c87611b23ef451bf3", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "git-hooks": { - "inputs": { - "flake-compat": "flake-compat", - "gitignore": "gitignore", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1765464257, - "owner": "cachix", - "repo": "git-hooks.nix", - "rev": "09e45f2598e1a8499c3594fe11ec2943f34fe509", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "git-hooks.nix", - "type": "github" - } - }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "git-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1762808025, - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1764580874, - "owner": "cachix", - "repo": "devenv-nixpkgs", - "rev": "dcf61356c3ab25f1362b4a4428a6d871e84f1d1d", - "type": "github" - }, - "original": { - "owner": "cachix", - "ref": "rolling", - "repo": "devenv-nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "devenv": "devenv", - "git-hooks": "git-hooks", - "nixpkgs": "nixpkgs", - "pre-commit-hooks": [ - "git-hooks" - ] - } - } - }, - "root": "root", - "version": 7 -} diff --git a/devenv.nix b/devenv.nix deleted file mode 100755 index b8babeb..0000000 --- a/devenv.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ pkgs, lib, config, inputs, ... }: - -{ - packages = with pkgs; [ git gcc gnumake cmake ]; - enterShell = '' - git --version # Use packages - ''; -} diff --git a/devenv.yaml b/devenv.yaml deleted file mode 100755 index 116a2ad..0000000 --- a/devenv.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json -inputs: - nixpkgs: - url: github:cachix/devenv-nixpkgs/rolling - -# If you're using non-OSS software, you can set allowUnfree to true. -# allowUnfree: true - -# If you're willing to use a package that's vulnerable -# permittedInsecurePackages: -# - "openssl-1.1.1w" - -# If you have more than one devenv you can merge them -#imports: -# - ./backend 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; +} |
