util.c (5609B)
1 /* 2 * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <pwd.h> 18 #include <time.h> 19 #include <ctype.h> 20 #include <errno.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <stdarg.h> 25 #include <unistd.h> 26 #include <sys/stat.h> 27 28 #include "ltk.h" 29 #include "util.h" 30 #include "array.h" 31 #include "memory.h" 32 #include "txtbuf.h" 33 34 /* FIXME: Should these functions really fail on memory error? */ 35 36 char * 37 ltk_read_file(const char *filename, size_t *len_ret, char **errstr_ret) { 38 long len; 39 char *file_contents; 40 FILE *file; 41 42 /* FIXME: https://wiki.sei.cmu.edu/confluence/display/c/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file */ 43 file = fopen(filename, "r"); 44 if (!file) goto error; 45 if (fseek(file, 0, SEEK_END)) goto errorclose; 46 len = ftell(file); 47 if (len < 0) goto errorclose; 48 if (fseek(file, 0, SEEK_SET)) goto errorclose; 49 file_contents = ltk_malloc((size_t)len + 1); 50 clearerr(file); 51 fread(file_contents, 1, (size_t)len, file); 52 if (ferror(file)) goto errorclose; 53 file_contents[len] = '\0'; 54 if (fclose(file)) goto error; 55 *len_ret = (size_t)len; 56 return file_contents; 57 error: 58 if (errstr_ret) 59 *errstr_ret = strerror(errno); 60 return NULL; 61 errorclose: 62 if (errstr_ret) 63 *errstr_ret = strerror(errno); 64 fclose(file); 65 return NULL; 66 } 67 68 /* FIXME: not sure if errno actually is set usefully after all these functions */ 69 int 70 ltk_write_file(const char *path, const char *data, size_t len, char **errstr_ret) { 71 FILE *file = fopen(path, "w"); 72 if (!file) goto error; 73 clearerr(file); 74 if (fwrite(data, 1, len, file) < len) goto errorclose; 75 if (fclose(file)) goto error; 76 return 0; 77 error: 78 if (errstr_ret) 79 *errstr_ret = strerror(errno); 80 return 1; 81 errorclose: 82 if (errstr_ret) 83 *errstr_ret = strerror(errno); 84 fclose(file); 85 return 1; 86 } 87 88 /* If `needed` is larger than `*alloc_size`, resize `*str` to 89 `max(needed, *alloc_size * 2)`. Aborts program on error. */ 90 void 91 ltk_grow_string(char **str, int *alloc_size, int needed) { 92 if (needed <= *alloc_size) return; 93 int new_size = needed > (*alloc_size * 2) ? needed : (*alloc_size * 2); 94 char *new = ltk_realloc(*str, new_size); 95 *str = new; 96 *alloc_size = new_size; 97 } 98 99 /* Get the directory to store ltk files in and optionally create it if it 100 doesn't exist yet and `create` is set. 101 This first checks the environment variable `env` and, if that doesn't 102 exist, the home directory with "/" and `default` appended. 103 Returns NULL on error. */ 104 char * 105 ltk_setup_directory(const char *envname, const char *defaultname, int create) { 106 char *dir, *dir_orig; 107 struct passwd *pw; 108 uid_t uid; 109 110 dir_orig = getenv(envname); 111 if (dir_orig) { 112 dir = ltk_strdup(dir_orig); 113 } else { 114 uid = getuid(); 115 pw = getpwuid(uid); 116 if (!pw) 117 return NULL; 118 size_t len = strlen(pw->pw_dir); 119 size_t dlen = strlen(defaultname); 120 dir = ltk_malloc(len + dlen + 2); 121 strcpy(dir, pw->pw_dir); 122 dir[len] = '/'; 123 strcpy(dir + len + 1, defaultname); 124 } 125 126 if (create && mkdir(dir, 0700) < 0) { 127 if (errno != EEXIST) 128 return NULL; 129 } 130 131 return dir; 132 } 133 134 /* Concatenate the two given strings and return the result. 135 This allocates new memory for the result string, unlike 136 the actual strcat. Aborts program on error */ 137 char * 138 ltk_strcat_useful(const char *str1, const char *str2) { 139 int len1, len2; 140 char *ret; 141 142 len1 = strlen(str1); 143 len2 = strlen(str2); 144 ret = ltk_malloc(len1 + len2 + 1); 145 strcpy(ret, str1); 146 strcpy(ret + len1, str2); 147 148 return ret; 149 } 150 151 static void 152 ltk_log_msg(const char *mode, const char *format, va_list args) { 153 char logtime[25]; /* FIXME: This should always be big enough, right? */ 154 time_t clock; 155 struct tm *timeptr; 156 157 time(&clock); 158 timeptr = localtime(&clock); 159 strftime(logtime, 25, "%Y-%m-%d %H:%M:%S", timeptr); 160 161 fprintf(stderr, "%s ltk %s: ", logtime, mode); 162 vfprintf(stderr, format, args); 163 } 164 165 LTK_GEN_LOG_FUNCS(ltk, ltk_log_msg, ltk_deinit) 166 167 int 168 str_array_equal(const char *terminated, const char *array, size_t len) { 169 if (!strncmp(terminated, array, len)) { 170 /* this is kind of inefficient, but there's no way to know 171 otherwise if strncmp just stopped comparing after a '\0' */ 172 return strlen(terminated) == len; 173 } 174 return 0; 175 } 176 177 size_t 178 prev_utf8(char *text, size_t index) { 179 if (index == 0) 180 return 0; 181 size_t i = index - 1; 182 /* find valid utf8 char - this probably needs to be improved */ 183 while (i > 0 && ((text[i] & 0xC0) == 0x80)) 184 i--; 185 return i; 186 } 187 188 size_t 189 next_utf8(char *text, size_t len, size_t index) { 190 if (index >= len) 191 return len; 192 size_t i = index + 1; 193 while (i < len && ((text[i] & 0xC0) == 0x80)) 194 i++; 195 return i; 196 } 197 198 void 199 ltk_assert_impl(const char *file, int line, const char *func, const char *failedexpr) 200 { 201 (void)fprintf(stderr, 202 "assertion \"%s\" failed: file \"%s\", line %d, function \"%s\"\n", 203 failedexpr, file, line, func); 204 abort(); 205 /* NOTREACHED */ 206 }