diff options
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); +} |