cgmnlm

colorful gemini line mode browser
git clone https://git.clttr.info/cgmnlm.git
Log (Feed) | Files | Refs (Tags) | README | LICENSE

util.c (2750B)


      1 #include <assert.h>
      2 #include <bearssl.h>
      3 #include <errno.h>
      4 #include <gmni/gmni.h>
      5 #include <libgen.h>
      6 #include <limits.h>
      7 #include <stdint.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <sys/stat.h>
     12 #include <unistd.h>
     13 #include "util.h"
     14 
     15 void
     16 posix_dirname(char *path, char *dname)
     17 {
     18 	char p[PATH_MAX+1];
     19 	char *t;
     20 
     21 	assert(strlen(path) <= PATH_MAX);
     22 
     23 	strcpy(p, path);
     24 	t = dirname(p);
     25 	memmove(dname, t, strlen(t) + 1);
     26 }
     27 
     28 /** Make directory and all of its parents */
     29 int
     30 mkdirs(char *path, mode_t mode)
     31 {
     32 	char dname[PATH_MAX + 1];
     33 	posix_dirname(path, dname);
     34 	if (strcmp(dname, "/") == 0) {
     35 		return 0;
     36 	}
     37 	if (mkdirs(dname, mode) != 0) {
     38 		return -1;
     39 	}
     40 	if (mkdir(path, mode) != 0 && errno != EEXIST) {
     41 		return -1;
     42 	}
     43 	errno = 0;
     44 	return 0;
     45 }
     46 
     47 char *
     48 getpath(const struct pathspec *paths, size_t npaths) {
     49 	for (size_t i = 0; i < npaths; i++) {
     50 		const char *var = "";
     51 		if (paths[i].var) {
     52 			var = getenv(paths[i].var);
     53 		}
     54 		if (var) {
     55 			char *out = calloc(1,
     56 				strlen(var) + strlen(paths[i].path) + 1);
     57 			strcat(strcat(out, var), paths[i].path);
     58 			return out;
     59 		}
     60 	}
     61 	return NULL;
     62 }
     63 
     64 int
     65 download_resp(FILE *out, struct gemini_response resp, const char *path,
     66 		char *url)
     67 {
     68 	char path_buf[PATH_MAX];
     69 	int n = 0;
     70 	assert(path);
     71 	switch (path[0]) {
     72 	case '\0':
     73 		strcpy(path_buf, "./");
     74 		break;
     75 	case '~':
     76 		n = snprintf(path_buf, PATH_MAX, "%s/%s", getenv("HOME"), &path[1]);
     77 		if (n > PATH_MAX) {
     78 			fprintf(stderr,
     79 				"Path %s exceeds limit of %d bytes and has been truncated\n",
     80 				path_buf, PATH_MAX);
     81 			return 1;
     82 		}
     83 		break;
     84 	default:
     85 		if (strlen(path) > PATH_MAX) {
     86 			fprintf(stderr, "Path %s exceeds limit of %d bytes\n",
     87 				path, PATH_MAX);
     88 			return 1;
     89 		}
     90 		strcpy(path_buf, path);
     91 	}
     92 	char path_res[PATH_MAX];
     93 	if (path_buf[strlen(path_buf)-1] == '/') {
     94 		n = snprintf(path_res, PATH_MAX, "%s%s", path_buf, basename(url));
     95 		if (n > PATH_MAX) {
     96 			fprintf(stderr, 
     97 				"Path %s exceeds limit of %d bytes and has been truncated\n",
     98 				path_res, PATH_MAX);
     99 			return 1;
    100 		}
    101 	} else {
    102 		strcpy(path_res, path_buf);
    103 	}
    104 	FILE *f = fopen(path_res, "w");
    105 	if (f == NULL) {
    106 		fprintf(stderr, "Could not open %s for writing: %s\n",
    107 			path_res, strerror(errno));
    108 		return 1;
    109 	}
    110 	fprintf(out, "Downloading %s to %s\n", url, path_res);
    111 	char buf[BUFSIZ];
    112 	for (int n = 1; n > 0;) {
    113 		if (resp.sc) {
    114 			n = br_sslio_read(&resp.body, buf, BUFSIZ);
    115 		} else {
    116 			n = read(resp.fd, buf, BUFSIZ);
    117 		}
    118 		if (n < 0) {
    119 			break;
    120 		}
    121 		ssize_t w = 0;
    122 		while (w < (ssize_t)n) {
    123 			ssize_t x = fwrite(&buf[w], 1, n - w, f);
    124 			if (ferror(f)) {
    125 				fprintf(stderr, "Error: write: %s\n",
    126 					strerror(errno));
    127 				return 1;
    128 			}
    129 			w += x;
    130 		}
    131 	}
    132 	fprintf(out, "Finished download\n");
    133 	fclose(f);
    134 	return 0;
    135 }