Krolyxon
Commit c666d30

Branches

Refs →

nsxivutil.c

346 lines · 7.0 KB · C

1/* sxiv: util.c
2 * Copyright (c) 2012 Bert Muennich <be.muennich at googlemail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#define _POSIX_C_SOURCE 200112L
20
21#include <stdlib.h>
22#include <string.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <unistd.h>
26#include <errno.h>
27
28#include "options.h"
29#include "util.h"
30
31enum {
32 BUF_SIZE = 1024,
33 DNAME_CNT = 512,
34 FNAME_LEN = 1024
35};
36
37void cleanup(void);
38
39void* s_malloc(size_t size) {
40 void *ptr;
41
42 ptr = malloc(size);
43 if (ptr == NULL)
44 die("could not allocate memory");
45 return ptr;
46}
47
48void* s_realloc(void *ptr, size_t size) {
49 ptr = realloc(ptr, size);
50 if (ptr == NULL)
51 die("could not allocate memory");
52 return ptr;
53}
54
55char* s_strdup(char *s) {
56 char *d = NULL;
57
58 if (s != NULL) {
59 d = malloc(strlen(s) + 1);
60 if (d == NULL)
61 die("could not allocate memory");
62 strcpy(d, s);
63 }
64 return d;
65}
66
67void warn(const char* fmt, ...) {
68 va_list args;
69
70 if (fmt == NULL || options->quiet)
71 return;
72
73 va_start(args, fmt);
74 fprintf(stderr, "sxiv: warning: ");
75 vfprintf(stderr, fmt, args);
76 fprintf(stderr, "\n");
77 va_end(args);
78}
79
80void die(const char* fmt, ...) {
81 va_list args;
82
83 if (fmt == NULL)
84 return;
85
86 va_start(args, fmt);
87 fprintf(stderr, "sxiv: error: ");
88 vfprintf(stderr, fmt, args);
89 fprintf(stderr, "\n");
90 va_end(args);
91
92 cleanup();
93 exit(EXIT_FAILURE);
94}
95
96ssize_t get_line(char **buf, size_t *n, FILE *stream) {
97 size_t len;
98 char *s;
99
100 if (stream == NULL || feof(stream) || ferror(stream))
101 return -1;
102
103 if (*buf == NULL || *n == 0) {
104 *n = BUF_SIZE;
105 *buf = (char*) s_malloc(*n);
106 }
107 s = *buf;
108
109 while (true) {
110 if (fgets(s, *n - (s - *buf), stream) == NULL)
111 return -1;
112 len = strlen(s);
113 if (feof(stream))
114 break;
115 if (len > 0 && s[len-1] == '\n')
116 break;
117 if (len + 1 == *n - (s - *buf)) {
118 *buf = (char*) s_realloc(*buf, 2 * *n);
119 s = *buf + *n - 1;
120 *n *= 2;
121 } else {
122 s += len;
123 }
124 }
125 return s - *buf + len;
126}
127
128void size_readable(float *size, const char **unit) {
129 const char *units[] = { "", "K", "M", "G" };
130 int i;
131
132 for (i = 0; i < ARRLEN(units) && *size > 1024.0; i++)
133 *size /= 1024.0;
134 *unit = units[MIN(i, ARRLEN(units) - 1)];
135}
136
137char* absolute_path(const char *filename) {
138 size_t len;
139 const char *basename;
140 char *dir, *dirname = NULL, *path = NULL, *s;
141 char *cwd = NULL, *twd = NULL;
142
143 if (filename == NULL || *filename == '\0' || *filename == '/')
144 return NULL;
145
146 len = FNAME_LEN;
147 cwd = (char*) s_malloc(len);
148 while ((s = getcwd(cwd, len)) == NULL && errno == ERANGE) {
149 len *= 2;
150 cwd = (char*) s_realloc(cwd, len);
151 }
152 if (s == NULL)
153 goto error;
154
155 s = strrchr(filename, '/');
156 if (s != NULL) {
157 len = s - filename;
158 dirname = (char*) s_malloc(len + 1);
159 strncpy(dirname, filename, len);
160 dirname[len] = '\0';
161 basename = s + 1;
162
163 if (chdir(cwd) < 0)
164 /* we're not able to come back afterwards */
165 goto error;
166 if (chdir(dirname) < 0)
167 goto error;
168
169 len = FNAME_LEN;
170 twd = (char*) s_malloc(len);
171 while ((s = getcwd(twd, len)) == NULL && errno == ERANGE) {
172 len *= 2;
173 twd = (char*) s_realloc(twd, len);
174 }
175 if (chdir(cwd) < 0)
176 die("could not revert to prior working directory");
177 if (s == NULL)
178 goto error;
179 dir = twd;
180 } else {
181 /* only a single filename given */
182 basename = filename;
183 dir = cwd;
184 }
185
186 len = strlen(dir) + strlen(basename) + 2;
187 path = (char*) s_malloc(len);
188 snprintf(path, len, "%s/%s", dir, basename);
189
190 goto end;
191
192error:
193 if (path != NULL) {
194 free(path);
195 path = NULL;
196 }
197
198end:
199 if (dirname != NULL)
200 free(dirname);
201 if (cwd != NULL)
202 free(cwd);
203 if (twd != NULL)
204 free(twd);
205
206 return path;
207}
208
209int r_opendir(r_dir_t *rdir, const char *dirname) {
210 if (rdir == NULL || dirname == NULL || *dirname == '\0')
211 return -1;
212
213 if ((rdir->dir = opendir(dirname)) == NULL) {
214 rdir->name = NULL;
215 rdir->stack = NULL;
216 return -1;
217 }
218
219 rdir->stcap = DNAME_CNT;
220 rdir->stack = (char**) s_malloc(rdir->stcap * sizeof(char*));
221 rdir->stlen = 0;
222
223 rdir->name = (char*) dirname;
224 rdir->d = 0;
225
226 return 0;
227}
228
229int r_closedir(r_dir_t *rdir) {
230 int ret = 0;
231
232 if (rdir == NULL)
233 return -1;
234
235 if (rdir->stack != NULL) {
236 while (rdir->stlen > 0)
237 free(rdir->stack[--rdir->stlen]);
238 free(rdir->stack);
239 rdir->stack = NULL;
240 }
241
242 if (rdir->dir != NULL) {
243 if ((ret = closedir(rdir->dir)) == 0)
244 rdir->dir = NULL;
245 }
246
247 if (rdir->d != 0 && rdir->name != NULL) {
248 free(rdir->name);
249 rdir->name = NULL;
250 }
251
252 return ret;
253}
254
255char* r_readdir(r_dir_t *rdir) {
256 size_t len;
257 char *filename;
258 struct dirent *dentry;
259 struct stat fstats;
260
261 if (rdir == NULL || rdir->dir == NULL || rdir->name == NULL)
262 return NULL;
263
264 while (true) {
265 if (rdir->dir != NULL && (dentry = readdir(rdir->dir)) != NULL) {
266 if (STREQ(dentry->d_name, ".") || STREQ(dentry->d_name, ".."))
267 continue;
268
269 len = strlen(rdir->name) + strlen(dentry->d_name) + 2;
270 filename = (char*) s_malloc(len);
271 snprintf(filename, len, "%s%s%s", rdir->name,
272 rdir->name[strlen(rdir->name)-1] == '/' ? "" : "/",
273 dentry->d_name);
274
275 if (stat(filename, &fstats) < 0)
276 continue;
277 if (S_ISDIR(fstats.st_mode)) {
278 /* put subdirectory on the stack */
279 if (rdir->stlen == rdir->stcap) {
280 rdir->stcap *= 2;
281 rdir->stack = (char**) s_realloc(rdir->stack,
282 rdir->stcap * sizeof(char*));
283 }
284 rdir->stack[rdir->stlen++] = filename;
285 continue;
286 }
287 return filename;
288 }
289
290 if (rdir->stlen > 0) {
291 /* open next subdirectory */
292 closedir(rdir->dir);
293 if (rdir->d != 0)
294 free(rdir->name);
295 rdir->name = rdir->stack[--rdir->stlen];
296 rdir->d = 1;
297 if ((rdir->dir = opendir(rdir->name)) == NULL)
298 warn("could not open directory: %s", rdir->name);
299 continue;
300 }
301 /* no more entries */
302 break;
303 }
304 return NULL;
305}
306
307int r_mkdir(const char *path) {
308 char *dir, *d;
309 struct stat stats;
310 int err = 0;
311
312 if (path == NULL || *path == '\0')
313 return -1;
314
315 if (stat(path, &stats) == 0) {
316 if (S_ISDIR(stats.st_mode)) {
317 return 0;
318 } else {
319 warn("not a directory: %s", path);
320 return -1;
321 }
322 }
323
324 d = dir = (char*) s_malloc(strlen(path) + 1);
325 strcpy(dir, path);
326
327 while (d != NULL && err == 0) {
328 d = strchr(d + 1, '/');
329 if (d != NULL)
330 *d = '\0';
331 if (access(dir, F_OK) < 0 && errno == ENOENT) {
332 if (mkdir(dir, 0755) < 0) {
333 warn("could not create directory: %s", dir);
334 err = -1;
335 }
336 } else if (stat(dir, &stats) < 0 || !S_ISDIR(stats.st_mode)) {
337 warn("not a directory: %s", dir);
338 err = -1;
339 }
340 if (d != NULL)
341 *d = '/';
342 }
343 free(dir);
344
345 return err;
346}