diff options
| author | Joseph Hunkeler <jhunkeler@gmail.com> | 2023-10-26 19:53:29 -0400 | 
|---|---|---|
| committer | Joseph Hunkeler <jhunkeler@gmail.com> | 2023-10-26 19:53:29 -0400 | 
| commit | 17178535cc9df5e834dfd43e3b2b919e02e5798d (patch) | |
| tree | 5e55e8b2c2453ccf6271b190cf45e90d2c25179d /src/utils.c | |
| download | stasis-17178535cc9df5e834dfd43e3b2b919e02e5798d.tar.gz | |
Initial commit
Diffstat (limited to 'src/utils.c')
| -rw-r--r-- | src/utils.c | 417 | 
1 files changed, 417 insertions, 0 deletions
| diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..c0bb28f --- /dev/null +++ b/src/utils.c @@ -0,0 +1,417 @@ +#include <stdarg.h> +#include "ohmycal.h" + +char *dirstack[1024]; +const size_t dirstack_max = sizeof(dirstack) / sizeof(dirstack[0]); +size_t dirstack_len = 0; +int pushd(const char *path) { +    if (dirstack_len + 1 > dirstack_max) { +        return -1; +    } +    dirstack[dirstack_len] = realpath(".", NULL); +    dirstack_len++; +    return chdir(path); +} + +int popd() { +    int result = -1; +    if (dirstack_len - 1 < 0) { +        return result; +    } +    dirstack_len--; +    result = chdir(dirstack[dirstack_len]); +    free(dirstack[dirstack_len]); +    dirstack[dirstack_len] = NULL; +    return result; +} + +int rmtree(char *_path) { +    int status = 0; +    char path[PATH_MAX] = {0}; +    strncpy(path, _path, sizeof(path)); +    DIR *dir; +    struct dirent *d_entity; + +    dir = opendir(path); +    if (!dir) { +        return 1; +    } + +    while ((d_entity = readdir(dir)) != NULL) { +        char abspath[PATH_MAX] = {0}; +        strcat(abspath, path); +        strcat(abspath, DIR_SEP); +        strcat(abspath, d_entity->d_name); + +        if (!strcmp(d_entity->d_name, ".") || !strcmp(d_entity->d_name, "..") || !strcmp(abspath, path)) { +            continue; +        } + +        // Test for sufficient privilege +        if (access(abspath, F_OK) < 0 && errno == EACCES) { +            continue; +        } + +        // Push directories on to the stack first +        if (d_entity->d_type == DT_DIR) { +            rmtree(abspath); +        } else { +            remove(abspath); +        } +    } +    closedir(dir); + +    if (access(path, F_OK) == 0) { +        remove(path); +    } +    return status; +} + +/** + * Expand "~" to the user's home directory + * + * Example: + * ~~~{.c} + * char *home = expandpath("~");             // == /home/username + * char *config = expandpath("~/.config");   // == /home/username/.config + * char *nope = expandpath("/tmp/test");     // == /tmp/test + * char *nada = expandpath("/~/broken");     // == /~/broken + * + * free(home); + * free(config); + * free(nope); + * free(nada); + * ~~~ + * + * @param _path (Must start with a `~`) + * @return success=expanded path or original path, failure=NULL + */ +char *expandpath(const char *_path) { +    if (_path == NULL) { +        return NULL; +    } +    const char *homes[] = { +            "HOME", +            "USERPROFILE", +    }; +    char home[PATH_MAX]; +    char tmp[PATH_MAX]; +    char *ptmp = tmp; +    char result[PATH_MAX]; +    char *sep = NULL; + +    memset(home, '\0', sizeof(home)); +    memset(ptmp, '\0', sizeof(tmp)); +    memset(result, '\0', sizeof(result)); + +    strncpy(ptmp, _path, PATH_MAX - 1); + +    // Check whether there's a reason to continue processing the string +    if (*ptmp != '~') { +        return strdup(ptmp); +    } + +    // Remove tilde from the string and shift its contents to the left +    strchrdel(ptmp, "~"); + +    // Figure out where the user's home directory resides +    for (size_t i = 0; i < sizeof(homes) / sizeof(*homes); i++) { +        char *tmphome; +        if ((tmphome = getenv(homes[i])) != NULL) { +            strncpy(home, tmphome, PATH_MAX - 1); +            break; +        } +    } + +    // A broken runtime environment means we can't do anything else here +    if (isempty(home)) { +        return NULL; +    } + +    // Scan the path for a directory separator +    if ((sep = strpbrk(ptmp, "/\\")) != NULL) { +        // Jump past it +        ptmp = sep + 1; +    } + +    // Construct the new path +    strncat(result, home, PATH_MAX - 1); +    if (sep) { +        strncat(result, DIR_SEP, PATH_MAX - 1); +        strncat(result, ptmp, PATH_MAX - 1); +    } + +    return strdup(result); +} + +/** + * Strip directory from file name + * Note: Caller is responsible for freeing memory + * + * @param _path + * @return success=file name, failure=NULL + */ +char *path_basename(char *path) { +    char *result = NULL; +    char *last = NULL; + +    if ((last = strrchr(path, '/')) == NULL) { +        return result; +    } +    // Perform a lookahead ensuring the string is valid beyond the last separator +    if (last++ != NULL) { +        result = last; +    } + +    return result; +} + +char **file_readlines(const char *filename, size_t start, size_t limit, ReaderFn *readerFn) { +    FILE *fp = NULL; +    char **result = NULL; +    char *buffer = NULL; +    size_t lines = 0; +    int use_stdin = 0; + +    if (strcmp(filename, "-") == 0) { +        use_stdin = 1; +    } + +    if (use_stdin) { +        fp = stdin; +    } else { +        fp = fopen(filename, "r"); +    } + +    if (fp == NULL) { +        perror(filename); +        fprintf(SYSERROR); +        return NULL; +    } + +    // Allocate buffer +    if ((buffer = calloc(BUFSIZ, sizeof(char))) == NULL) { +        perror("line buffer"); +        fprintf(SYSERROR); +        if (!use_stdin) { +            fclose(fp); +        } +        return NULL; +    } + +    // count number the of lines in the file +    while ((fgets(buffer, BUFSIZ - 1, fp)) != NULL) { +        lines++; +    } + +    if (!lines) { +        free(buffer); +        if (!use_stdin) { +            fclose(fp); +        } +        return NULL; +    } + +    rewind(fp); + +    // Handle invalid start offset +    if (start > lines) { +        start = 0; +    } + +    // Adjust line count when start offset is non-zero +    if (start != 0 && start < lines) { +        lines -= start; +    } + + +    // Handle minimum and maximum limits +    if (limit == 0 || limit > lines) { +        limit = lines; +    } + +    // Populate results array +    result = calloc(limit + 1, sizeof(char *)); +    for (size_t i = start; i < limit; i++) { +        if (i < start) { +            continue; +        } + +        if (fgets(buffer, BUFSIZ - 1, fp) == NULL) { +            break; +        } + +        if (readerFn != NULL) { +            int status = readerFn(i - start, &buffer); +            // A status greater than zero indicates we should ignore this line entirely and "continue" +            // A status less than zero indicates we should "break" +            // A zero status proceeds normally +            if (status > 0) { +                i--; +                continue; +            } else if (status < 0) { +                break; +            } +        } +        result[i] = strdup(buffer); +        memset(buffer, '\0', BUFSIZ); +    } + +    free(buffer); +    if (!use_stdin) { +        fclose(fp); +    } +    return result; +} + +char *find_program(const char *name) { +    static char result[PATH_MAX] = {0}; +    char *_env_path = getenv(PATH_ENV_VAR); +    if (!_env_path) { +        errno = EINVAL; +        return NULL; +    } +    char *path = strdup(_env_path); +    char *path_orig = path; +    char *path_elem = NULL; + +    if (!path) { +        errno = ENOMEM; +        return NULL; +    } + +    result[0] = '\0'; +    while ((path_elem = strsep(&path, PATH_SEP))) { +        char abspath[PATH_MAX] = {0}; +        strcat(abspath, path_elem); +        strcat(abspath, DIR_SEP); +        strcat(abspath, name); +        if (access(abspath, F_OK) < 0) { +            continue; +        } +        strncpy(result, abspath, sizeof(result)); +        break; +    } +    path = path_orig; +    free(path); +    return strlen(result) ? result : NULL; +} + +int touch(const char *filename) { +    if (access(filename, F_OK) == 0) { +        return 0; +    } + +    FILE *fp = fopen(filename, "w"); +    if (!fp) { +        perror(filename); +        return 1; +    } +    fprintf(stderr, ""); +    fclose(fp); +    return 0; +} + +int git_clone(struct Process *proc, char *url, char *destdir, char *gitref) { +    int result = -1; +    char *chdir_to = NULL; +    char *program = find_program("git"); +    if (!program) { +        return result; +    } + +    static char command[PATH_MAX]; +    sprintf(command, "%s clone --recursive %s", program, url); +    if (destdir && access(destdir, F_OK) < 0) { +        sprintf(command + strlen(command), " %s", destdir); +        result = shell2(proc, command); +    } + +    if (destdir) { +        chdir_to = destdir; +    } else { +        chdir_to = path_basename(url); +    } + +    pushd(chdir_to); +    { +        memset(command, 0, sizeof(command)); +        sprintf(command, "%s fetch --all", program); +        result += shell2(proc, command); + +        if (gitref != NULL) { +            memset(command, 0, sizeof(command)); +            sprintf(command, "%s checkout %s", program, gitref); +            result += shell2(proc, command); +        } +        popd(); +    } +    return result; +} + + +char *git_describe(const char *path) { +    pushd(path); +    static char version[NAME_MAX]; +    FILE *pp; +    pp = popen("git describe --always --tags", "r"); +    memset(version, 0, sizeof(version)); +    fgets(version, sizeof(version) - 1, pp); +    strip(version); +    pclose(pp); +    popd(); +    return version; +} + +#define OMC_COLOR_RED "\e[1;91m" +#define OMC_COLOR_GREEN "\e[1;92m" +#define OMC_COLOR_YELLOW "\e[1;93m" +#define OMC_COLOR_BLUE "\e[1;94m" +#define OMC_COLOR_WHITE "\e[1;97m" +#define OMC_COLOR_RESET "\e[0;37m\e[0m" + +int msg(unsigned type, char *fmt, ...) { +    FILE *stream = NULL; +    char header[255]; +    char status[255]; + +    if (type & OMC_MSG_NOP) { +        // quiet mode +        return 0; +    } + +    memset(header, 0, sizeof(header)); +    memset(status, 0, sizeof(status)); + +    va_list args; +    va_start(args, fmt); + +    stream = stdout; +    if (type & OMC_MSG_ERROR) { +        // for error output +        stream = stderr; +        fprintf(stream, "%s", OMC_COLOR_RED); +        strcpy(status, " ERROR: "); +    } else if (type & OMC_MSG_WARN) { +        stream = stderr; +        fprintf(stream, "%s", OMC_COLOR_YELLOW); +        strcpy(status, " WARNING: "); +    } else { +        fprintf(stream, "%s", OMC_COLOR_GREEN); +        strcpy(status, " "); +    } + +    if (type & OMC_MSG_L1) { +        sprintf(header, "==>%s" OMC_COLOR_RESET OMC_COLOR_WHITE, status); +    } else if (type & OMC_MSG_L2) { +        sprintf(header, " ->%s" OMC_COLOR_RESET, status); +    } else if (type & OMC_MSG_L3) { +        sprintf(header, OMC_COLOR_BLUE "  ->%s" OMC_COLOR_RESET, status); +    } + +    fprintf(stream, "%s", header); +    vfprintf(stream, fmt, args); +    printf("%s", OMC_COLOR_RESET); +    printf("%s", OMC_COLOR_RESET); +    va_end(args); +} | 
