aboutsummaryrefslogtreecommitdiff
path: root/src/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils.c')
-rw-r--r--src/utils.c417
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);
+}