Krolyxon
Commit 045abbd

Branches

Refs →

nsxivutil.c

287 lines · 5.9 KB · C

1/* Copyright 2011-2020 Bert Muennich
2 * Copyright 2021-2022 nsxiv contributors
3 *
4 * This file is a part of nsxiv.
5 *
6 * nsxiv is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2 of the License,
9 * or (at your option) any later version.
10 *
11 * nsxiv is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with nsxiv. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "nsxiv.h"
21
22#include <assert.h>
23#include <errno.h>
24#include <stdarg.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <sys/stat.h>
29#include <unistd.h>
30
31const char *progname = "nsxiv";
32
33void* emalloc(size_t size)
34{
35 void *ptr;
36
37 ptr = malloc(size);
38 if (ptr == NULL)
39 error(EXIT_FAILURE, errno, NULL);
40 return ptr;
41}
42
43void* ecalloc(size_t nmemb, size_t size)
44{
45 void *ptr;
46
47 ptr = calloc(nmemb, size);
48 if (ptr == NULL)
49 error(EXIT_FAILURE, errno, NULL);
50 return ptr;
51}
52
53void* erealloc(void *ptr, size_t size)
54{
55 ptr = realloc(ptr, size);
56 if (ptr == NULL)
57 error(EXIT_FAILURE, errno, NULL);
58 return ptr;
59}
60
61char* estrdup(const char *s)
62{
63 size_t n = strlen(s) + 1;
64 return memcpy(emalloc(n), s, n);
65}
66
67void error(int eval, int err, const char* fmt, ...)
68{
69 va_list ap;
70
71 if (eval == 0 && options->quiet)
72 return;
73
74 fflush(stdout);
75 fprintf(stderr, "%s: ", progname);
76 va_start(ap, fmt);
77 if (fmt != NULL)
78 vfprintf(stderr, fmt, ap);
79 va_end(ap);
80 if (err != 0)
81 fprintf(stderr, "%s%s", fmt != NULL ? ": " : "", strerror(err));
82 fputc('\n', stderr);
83
84 if (eval != 0)
85 exit(eval);
86}
87
88int r_opendir(r_dir_t *rdir, const char *dirname, bool recursive)
89{
90 if (*dirname == '\0')
91 return -1;
92
93 if ((rdir->dir = opendir(dirname)) == NULL) {
94 rdir->name = NULL;
95 rdir->stack = NULL;
96 return -1;
97 }
98
99 rdir->stcap = 512;
100 rdir->stack = emalloc(rdir->stcap * sizeof(*rdir->stack));
101 rdir->stlen = 0;
102
103 rdir->name = (char*) dirname;
104 rdir->d = 0;
105 rdir->recursive = recursive;
106
107 return 0;
108}
109
110int r_closedir(r_dir_t *rdir)
111{
112 int ret = 0;
113
114 if (rdir->stack != NULL) {
115 while (rdir->stlen > 0)
116 free(rdir->stack[--rdir->stlen]);
117 free(rdir->stack);
118 rdir->stack = NULL;
119 }
120
121 if (rdir->dir != NULL) {
122 if ((ret = closedir(rdir->dir)) == 0)
123 rdir->dir = NULL;
124 }
125
126 if (rdir->d != 0) {
127 free(rdir->name);
128 rdir->name = NULL;
129 }
130
131 return ret;
132}
133
134char* r_readdir(r_dir_t *rdir, bool skip_dotfiles)
135{
136 size_t len;
137 char *filename;
138 struct dirent *dentry;
139 struct stat fstats;
140
141 while (true) {
142 if (rdir->dir != NULL && (dentry = readdir(rdir->dir)) != NULL) {
143 if (dentry->d_name[0] == '.') {
144 if (skip_dotfiles)
145 continue;
146 if (dentry->d_name[1] == '\0')
147 continue;
148 if (dentry->d_name[1] == '.' && dentry->d_name[2] == '\0')
149 continue;
150 }
151
152 len = strlen(rdir->name) + strlen(dentry->d_name) + 2;
153 filename = emalloc(len);
154 snprintf(filename, len, "%s%s%s", rdir->name,
155 rdir->name[strlen(rdir->name)-1] == '/' ? "" : "/",
156 dentry->d_name);
157
158 if (stat(filename, &fstats) < 0) {
159 free(filename);
160 continue;
161 }
162 if (S_ISDIR(fstats.st_mode)) {
163 /* put subdirectory on the stack */
164 if (rdir->stlen == rdir->stcap) {
165 rdir->stcap *= 2;
166 rdir->stack = erealloc(rdir->stack,
167 rdir->stcap * sizeof(*rdir->stack));
168 }
169 rdir->stack[rdir->stlen++] = filename;
170 continue;
171 }
172 return filename;
173 }
174
175 if (rdir->recursive && rdir->stlen > 0) {
176 /* open next subdirectory */
177 assert(rdir->dir != NULL);
178 closedir(rdir->dir);
179 if (rdir->d != 0)
180 free(rdir->name);
181 rdir->name = rdir->stack[--rdir->stlen];
182 rdir->d = 1;
183 if ((rdir->dir = opendir(rdir->name)) == NULL)
184 error(0, errno, "%s", rdir->name);
185 continue;
186 }
187 /* no more entries */
188 break;
189 }
190 return NULL;
191}
192
193int r_mkdir(char *path)
194{
195 int rc = 0;
196 char c, *s = path;
197 struct stat st;
198
199 while (*s != '\0' && rc == 0) {
200 if (*s == '/') {
201 s++;
202 continue;
203 }
204 for (; *s != '\0' && *s != '/'; s++);
205 c = *s;
206 *s = '\0';
207 if (mkdir(path, 0755) == -1) {
208 if (errno != EEXIST || stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
209 error(0, errno, "%s", path);
210 rc = -1;
211 }
212 }
213 *s = c;
214 }
215 return rc;
216}
217
218void construct_argv(char **argv, unsigned int len, ...)
219{
220 unsigned int i;
221 va_list args;
222
223 va_start(args, len);
224 for (i = 0; i < len; ++i)
225 argv[i] = va_arg(args, char *);
226 va_end(args);
227 if (argv[len-1] != NULL)
228 error(EXIT_FAILURE, 0, "argv not NULL terminated");
229}
230
231spawn_t spawn(const char *cmd, char *const argv[], unsigned int flags)
232{
233 pid_t pid;
234 spawn_t status = { -1, -1, -1 };
235 int pfd_read[2] = { -1, -1 }, pfd_write[2] = { -1, -1 };
236 const bool r = flags & X_READ;
237 const bool w = flags & X_WRITE;
238
239 if (cmd == NULL || argv == NULL || flags == 0)
240 return status;
241
242 if (r && pipe(pfd_read) < 0) {
243 error(0, errno, "pipe: %s", cmd);
244 return status;
245 }
246
247 if (w && pipe(pfd_write) < 0) {
248 error(0, errno, "pipe: %s", cmd);
249 if (r) {
250 close(pfd_read[0]);
251 close(pfd_read[1]);
252 }
253 return status;
254 }
255
256 if ((pid = fork()) == 0) { /* in child */
257 if ((r && dup2(pfd_read[1], 1) < 0) || (w && dup2(pfd_write[0], 0) < 0))
258 error(EXIT_FAILURE, errno, "dup2: %s", cmd);
259
260 if (r) {
261 close(pfd_read[0]);
262 close(pfd_read[1]);
263 }
264 if (w) {
265 close(pfd_write[0]);
266 close(pfd_write[1]);
267 }
268 execv(cmd, argv);
269 error(EXIT_FAILURE, errno, "exec: %s", cmd);
270 } else if (pid < 0) { /* fork failed */
271 error(0, errno, "fork: %s", cmd);
272 if (r)
273 close(pfd_read[0]);
274 if (w)
275 close(pfd_write[1]);
276 } else { /* in parent */
277 status.pid = pid;
278 status.readfd = pfd_read[0];
279 status.writefd = pfd_write[1];
280 }
281
282 if (r)
283 close(pfd_read[1]);
284 if (w)
285 close(pfd_write[0]);
286 return status;
287}