gmni

a gemini line mode client
git clone https://git.clttr.info/gmni.git
Log (Feed) | Files | Refs (Tags) | README | LICENSE

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 }