util.c (2848B)
1 #include <assert.h> 2 #include <bearssl.h> 3 #include <ctype.h> 4 #include <errno.h> 5 #include <gmni/gmni.h> 6 #include <libgen.h> 7 #include <limits.h> 8 #include <stdbool.h> 9 #include <stdint.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <sys/stat.h> 14 #include <unistd.h> 15 #include "util.h" 16 17 char * 18 trim_ws(char *in) 19 { 20 while (isspace(*in)) ++in; 21 return in; 22 } 23 24 void 25 posix_dirname(char *path, char *dname) 26 { 27 char p[PATH_MAX+1]; 28 char *t; 29 30 assert(strlen(path) <= PATH_MAX); 31 32 strcpy(p, path); 33 t = dirname(p); 34 memmove(dname, t, strlen(t) + 1); 35 } 36 37 /** Make directory and all of its parents */ 38 int 39 mkdirs(char *path, mode_t mode) 40 { 41 char dname[PATH_MAX + 1]; 42 posix_dirname(path, dname); 43 if (strcmp(dname, "/") == 0) { 44 return 0; 45 } 46 if (mkdirs(dname, mode) != 0) { 47 return -1; 48 } 49 if (mkdir(path, mode) != 0 && errno != EEXIST) { 50 return -1; 51 } 52 errno = 0; 53 return 0; 54 } 55 56 char * 57 getpath(const struct pathspec *paths, size_t npaths) { 58 for (size_t i = 0; i < npaths; i++) { 59 const char *var = ""; 60 if (paths[i].var) { 61 var = getenv(paths[i].var); 62 } 63 if (var) { 64 char *out = calloc(1, 65 strlen(var) + strlen(paths[i].path) + 1); 66 strcat(strcat(out, var), paths[i].path); 67 return out; 68 } 69 } 70 return NULL; 71 } 72 73 static char * 74 get_final_path(const char *path, char *url) 75 { 76 assert(path); 77 assert(url); 78 79 size_t len = strlen(path); 80 size_t hlen = *path == '~' ? strlen(getenv("HOME")) : 0; 81 size_t flen = path[len - 1] == '/' ? strlen(basename(url)) : 0; 82 83 if (len + hlen + flen >= PATH_MAX) { // PATH_MAX includes NUL 84 fprintf(stderr, 85 "Path %s exceeds limit of %d bytes%s\n", path, PATH_MAX, 86 hlen || flen ? " after necessary substitutions are made" : ""); 87 return NULL; 88 } 89 90 char *buf = malloc(PATH_MAX); 91 switch (path[0]) { 92 case '\0': 93 strcpy(buf, "./"); 94 break; 95 case '~':; 96 int n = snprintf(buf, PATH_MAX, "%s/%s", getenv("HOME"), &path[1]); 97 assert(n < PATH_MAX); 98 break; 99 default: 100 strcpy(buf, path); 101 break; 102 } 103 if (flen > 0) strcat(buf, basename(url)); 104 105 return buf; 106 } 107 108 int 109 print_resp_file(bool end_with_nl, struct gemini_response resp, FILE *out) 110 { 111 assert(out); 112 size_t i = 0; 113 while (resp.body[i] != '\0') { 114 if (fputc(resp.body[i], out) == EOF) { 115 fprintf(stderr, "Error: write: %s\n", strerror(errno)); 116 return 0; 117 } 118 i++; 119 } 120 if (end_with_nl && i > 0 && resp.body[i - 1] != '\n') { 121 fputc('\n', out); 122 } 123 return 1; 124 } 125 126 int 127 print_resp(bool end_with_nl, struct gemini_response resp, const char *pth, char *url) 128 { 129 assert(pth); 130 assert(url); 131 132 int ret = 0; 133 FILE *out = NULL; 134 135 char *path = get_final_path(pth, url); 136 if (!path) goto cleanup; 137 out = fopen(path, "w"); 138 if (!out) { 139 fprintf(stderr, "Could not open %s for writing: %s\n", 140 path, strerror(errno)); 141 goto cleanup; 142 } 143 ret = print_resp_file(end_with_nl, resp, out); 144 cleanup: 145 if (out) fclose(out); 146 free(path); 147 return ret; 148 }