utils.c (5722B)
1 #include <stdio.h> 2 #include <strings.h> 3 #include <errno.h> 4 #include <stdarg.h> 5 6 #include "utils.h" 7 8 void 9 panic(const char* fmt, ...) 10 { 11 va_list args; 12 int saved_errno; 13 saved_errno = errno; 14 va_start(args, fmt); 15 fprintf(stderr, "Error: "); 16 vfprintf(stderr, fmt, args); 17 fprintf(stderr, "\n"); 18 va_end(args); 19 if (fmt[0] && fmt[strlen(fmt) - 1] == ':') fprintf(stderr, " %s", strerror(saved_errno)); 20 fputc('\n', stderr); 21 exit(1); 22 } 23 24 void 25 util_fill(int* dst, size_t n, int value) 26 { 27 for (size_t i = 0; i < n; ++i) 28 dst[i] = value; 29 } 30 31 #if defined(_WIN32) 32 33 // to be tested on windows, and then have a shower 34 35 const char* 36 get_home_dir(void) 37 { 38 const char* p = getenv("USERPROFILE"); 39 if (p && *p) return p; 40 const char* drv = getenv("HOMEDRIVE"); 41 const char* pth = getenv("HOMEPATH"); 42 if (drv && pth) { 43 size_t n = strlen(drv) + strlen(pth) + 1; 44 char* buf = (char*)ecalloc(n, sizeof(char)); 45 snprintf(buf, n, "%s%s", drv, pth); 46 return buf; /* caller frees if not an env ptr */ 47 } 48 return NULL; 49 } 50 #else 51 52 // macos/linux 53 54 #include <pwd.h> 55 #include <unistd.h> 56 57 const char* 58 get_home_dir(void) 59 { 60 const char* p = getenv("HOME"); 61 if (p && *p) return p; 62 return NULL; 63 } 64 65 #endif 66 67 const char* 68 expand_tilde(const char* path) 69 { 70 if (!path) return NULL; 71 const char* home = get_home_dir(); 72 if (!home) home = ""; /* degrade to empty if unknown */ 73 74 /* Worst case: every char is '~' → expand to home each time */ 75 size_t hlen = strlen(home); 76 size_t out_cap = 1; /* for '\0' */ 77 for (const char* s = path; *s; ++s) 78 out_cap += (*s == '~') ? hlen : 1; 79 80 char* out = (char*)ecalloc(out_cap, sizeof(char)); 81 82 char* w = out; 83 for (const char* s = path; *s; ++s) { 84 if (*s == '~') { 85 memcpy(w, home, hlen); 86 w += hlen; 87 } else { 88 *w++ = *s; 89 } 90 } 91 *w = '\0'; 92 93 #if defined(_WIN32) 94 /* If get_home_dir() allocated (HOMEDRIVE+HOMEPATH path), free it. */ 95 const char* up = getenv("USERPROFILE"); 96 if (!(up && *up)) { free((void*)home); } 97 #endif 98 return out; 99 } 100 101 char* 102 read_file(const char* file_path) 103 { 104 FILE* fp = fopen(file_path, "rb"); 105 if (fp == NULL) { return NULL; } 106 107 if (fseek(fp, 0, SEEK_END) != 0) { 108 fclose(fp); 109 printf("Failed to find the end of the file\n"); 110 return NULL; 111 } 112 113 long file_size = ftell(fp); 114 115 if (file_size < 0) { 116 fclose(fp); 117 printf("Failed to determine the file size\n"); 118 return NULL; 119 } 120 121 rewind(fp); 122 123 // check for overflow before casting 124 if ((unsigned long)file_size >= SIZE_MAX) { 125 fclose(fp); 126 printf("File too large to fit in memory\n"); 127 return NULL; 128 } 129 130 char* contents = (char*)ecalloc(1, (size_t)file_size + 1); 131 132 size_t bytes_read = fread(contents, 1, (size_t)file_size, fp); 133 if (bytes_read != (size_t)file_size) { 134 free(contents); 135 fclose(fp); 136 printf("Failed to read the file in its entirety\n"); 137 return NULL; 138 } 139 140 contents[file_size] = '\0'; 141 142 fclose(fp); 143 return contents; 144 } 145 146 char* 147 host_from_url(const char* url) 148 { 149 const char *p = url, *start, *end; 150 151 // skip scheme 152 const char* scheme = strstr(p, "://"); 153 if (scheme) 154 p = scheme + 3; 155 else if (p[0] == '/' && p[1] == '/') 156 p += 2; 157 158 // IPv6 literal in brackets 159 if (*p == '[') { 160 start = p + 1; 161 end = strchr(start, ']'); 162 if (!end) return NULL; 163 return strndup(start, (size_t)(end - start)); 164 } 165 166 start = p; 167 168 // stop at / ? # : (drop path, query, fragment, and port) 169 end = strpbrk(start, "/?:#:"); 170 if (!end) end = start + strlen(start); 171 172 // optional: strip leading "www." 173 if (end - start >= 4 && strncasecmp(start, "www.", 4) == 0) start += 4; 174 175 return strndup(start, (size_t)(end - start)); 176 } 177 178 void 179 remove_all_chars(char* str, char c) 180 { 181 char *pr = str, *pw = str; 182 while (*pr) { 183 *pw = *pr++; 184 pw += (*pw != c); 185 } 186 *pw = '\0'; 187 } 188 189 void 190 remove_all_tags(char* str) 191 { 192 char *pr = str, *pw = str; 193 int in_tag = 0; 194 195 while (*pr) { 196 if (*pr == '<') { 197 in_tag = 1; 198 pr++; 199 continue; 200 } else if (*pr == '>') { 201 in_tag = 0; 202 pr++; 203 continue; 204 } 205 if (!in_tag) { *pw++ = *pr; } 206 pr++; 207 } 208 *pw = '\0'; 209 } 210 211 // 212 // open_url 213 // 214 215 #define _POSIX_C_SOURCE 200809L 216 217 #ifdef _WIN32 218 #include <windows.h> 219 int 220 open_url(const char* url) 221 { 222 HINSTANCE r = ShellExecuteA(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL); 223 return ((uintptr_t)r > 32) ? 0 : -1; // ShellExecute error if <= 32 224 } 225 #else 226 static void 227 shell_quote(const char* in, char* out, size_t outsz) 228 { 229 // wrap in single quotes and escape embedded single quotes: ' -> '\'' 230 size_t n = 0; 231 if (n + 1 < outsz) out[n++] = '\''; 232 for (; *in && n + 4 < outsz; ++in) { 233 if (*in == '\'') { 234 memcpy(out + n, "'\\''", 4); 235 n += 4; 236 } else 237 out[n++] = *in; 238 } 239 if (n + 2 <= outsz) { 240 out[n++] = '\''; 241 out[n] = '\0'; 242 } else if (outsz) 243 out[outsz - 1] = '\0'; 244 } 245 246 int 247 open_url(const char* url) 248 { 249 char q[4096]; 250 char cmd[4600]; 251 shell_quote(url, q, sizeof q); 252 #ifdef __APPLE__ 253 snprintf(cmd, sizeof cmd, "open %s >/dev/null 2>&1 &", q); 254 #else 255 snprintf(cmd, sizeof cmd, "xdg-open %s >/dev/null 2>&1 &", q); 256 #endif 257 return system(cmd); // 0 on success from the shell 258 } 259 #endif 260 261 // 262 // nonascii_replace 263 // 264 265 void 266 nonascii_replace(char* s, char to) 267 { 268 unsigned char *r = (unsigned char*)s, *w = (unsigned char*)s; 269 while (*r) { 270 if (*r < 0x80) { // ASCII byte 271 *w++ = *r++; 272 } else { // non-ASCII: skip UTF-8 seq, write one space 273 size_t need = 1; 274 if ((*r & 0xE0) == 0xC0) 275 need = 2; 276 else if ((*r & 0xF0) == 0xE0) 277 need = 3; 278 else if ((*r & 0xF8) == 0xF0) 279 need = 4; 280 281 size_t i = 1; 282 if (need > 1) { 283 for (; i < need && r[i]; ++i) 284 if ((r[i] & 0xC0) != 0x80) break; 285 } 286 r += (i == need) ? need : 1; // tolerate invalid UTF-8 287 288 if (w == (unsigned char*)s || w[-1] != ' ') *w++ = to; 289 } 290 } 291 *w = '\0'; 292 } 293 294 void* 295 ecalloc(size_t c, size_t s) 296 { 297 void* p; 298 if (!(p = calloc(c, s))) { panic("calloc:"); } 299 return p; 300 }