Krolyxon
Commit 79d780a

Branches

Refs →

nsxivutil.c

344 lines · 6.7 KB · C

1/* sxiv: util.c
2 * Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de>
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();
38
39void* s_malloc(size_t size) {
40 void *ptr;
41
42 if (!(ptr = malloc(size)))
43 die("could not allocate memory");
44 return ptr;
45}
46
47void* s_realloc(void *ptr, size_t size) {
48 if (!(ptr = realloc(ptr, size)))
49 die("could not allocate memory");
50 return ptr;
51}
52
53char* s_strdup(char *s) {
54 char *d = NULL;
55
56 if (s) {
57 if (!(d = malloc(strlen(s) + 1)))
58 die("could not allocate memory");
59 strcpy(d, s);
60 }
61 return d;
62}
63
64void warn(const char* fmt, ...) {
65 va_list args;
66
67 if (!fmt || options->quiet)
68 return;
69
70 va_start(args, fmt);
71 fprintf(stderr, "sxiv: warning: ");
72 vfprintf(stderr, fmt, args);
73 fprintf(stderr, "\n");
74 va_end(args);
75}
76
77void die(const char* fmt, ...) {
78 va_list args;
79
80 if (!fmt)
81 return;
82
83 va_start(args, fmt);
84 fprintf(stderr, "sxiv: error: ");
85 vfprintf(stderr, fmt, args);
86 fprintf(stderr, "\n");
87 va_end(args);
88
89 cleanup();
90 exit(1);
91}
92
93ssize_t get_line(char **buf, size_t *n, FILE *stream) {
94 size_t len;
95 char *s;
96
97 if (!stream || feof(stream) || ferror(stream))
98 return -1;
99
100 if (!*buf || !*n) {
101 *n = BUF_SIZE;
102 *buf = (char*) s_malloc(*n);
103 }
104 s = *buf;
105
106 while (1) {
107 if (!fgets(s, *n - (s - *buf), stream))
108 return -1;
109 len = strlen(s);
110 if (feof(stream))
111 break;
112 if (len > 0 && s[len-1] == '\n')
113 break;
114 if (len + 1 == *n - (s - *buf)) {
115 *buf = (char*) s_realloc(*buf, 2 * *n);
116 s = *buf + *n - 1;
117 *n *= 2;
118 } else {
119 s += len;
120 }
121 }
122
123 return s - *buf + len;
124}
125
126void size_readable(float *size, const char **unit) {
127 const char *units[] = { "", "K", "M", "G" };
128 int i;
129
130 for (i = 0; i < ARRLEN(units) && *size > 1024; i++)
131 *size /= 1024;
132 *unit = units[MIN(i, ARRLEN(units) - 1)];
133}
134
135char* absolute_path(const char *filename) {
136 size_t len;
137 const char *basename;
138 char *dir, *dirname = NULL, *path = NULL, *s;
139 char *cwd = NULL, *twd = NULL;
140
141 if (!filename || *filename == '\0' || *filename == '/')
142 return NULL;
143
144 len = FNAME_LEN;
145 cwd = (char*) s_malloc(len);
146 while (!(s = getcwd(cwd, len)) && errno == ERANGE) {
147 len *= 2;
148 cwd = (char*) s_realloc(cwd, len);
149 }
150 if (!s)
151 goto error;
152
153 s = strrchr(filename, '/');
154 if (s) {
155 len = s - filename;
156 dirname = (char*) s_malloc(len + 1);
157 strncpy(dirname, filename, len);
158 dirname[len] = '\0';
159 basename = s + 1;
160
161 if (chdir(cwd))
162 /* we're not able to come back afterwards */
163 goto error;
164 if (chdir(dirname))
165 goto error;
166
167 len = FNAME_LEN;
168 twd = (char*) s_malloc(len);
169 while (!(s = getcwd(twd, len)) && errno == ERANGE) {
170 len *= 2;
171 twd = (char*) s_realloc(twd, len);
172 }
173 if (chdir(cwd))
174 die("could not revert to prior working directory");
175 if (!s)
176 goto error;
177 dir = twd;
178 } else {
179 /* only a single filename given */
180 basename = filename;
181 dir = cwd;
182 }
183
184 len = strlen(dir) + strlen(basename) + 2;
185 path = (char*) s_malloc(len);
186 snprintf(path, len, "%s/%s", dir, basename);
187
188 goto end;
189
190error:
191 if (path) {
192 free(path);
193 path = NULL;
194 }
195
196end:
197 if (dirname)
198 free(dirname);
199 if (cwd)
200 free(cwd);
201 if (twd)
202 free(twd);
203
204 return path;
205}
206
207int r_opendir(r_dir_t *rdir, const char *dirname) {
208 if (!rdir || !dirname || !*dirname)
209 return -1;
210
211 if (!(rdir->dir = opendir(dirname))) {
212 rdir->name = NULL;
213 rdir->stack = NULL;
214 return -1;
215 }
216
217 rdir->stcap = DNAME_CNT;
218 rdir->stack = (char**) s_malloc(rdir->stcap * sizeof(char*));
219 rdir->stlen = 0;
220
221 rdir->name = (char*) dirname;
222 rdir->d = 0;
223
224 return 0;
225}
226
227int r_closedir(r_dir_t *rdir) {
228 int ret = 0;
229
230 if (!rdir)
231 return -1;
232
233 if (rdir->stack) {
234 while (rdir->stlen > 0)
235 free(rdir->stack[--rdir->stlen]);
236 free(rdir->stack);
237 rdir->stack = NULL;
238 }
239
240 if (rdir->dir) {
241 if (!(ret = closedir(rdir->dir)))
242 rdir->dir = NULL;
243 }
244
245 if (rdir->d && rdir->name) {
246 free(rdir->name);
247 rdir->name = NULL;
248 }
249
250 return ret;
251}
252
253char* r_readdir(r_dir_t *rdir) {
254 size_t len;
255 char *filename;
256 struct dirent *dentry;
257 struct stat fstats;
258
259 if (!rdir || !rdir->dir || !rdir->name)
260 return NULL;
261
262 while (1) {
263 if (rdir->dir && (dentry = readdir(rdir->dir))) {
264 if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
265 continue;
266
267 len = strlen(rdir->name) + strlen(dentry->d_name) + 2;
268 filename = (char*) s_malloc(len);
269 snprintf(filename, len, "%s%s%s", rdir->name,
270 rdir->name[strlen(rdir->name)-1] == '/' ? "" : "/",
271 dentry->d_name);
272
273 if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
274 /* put subdirectory on the stack */
275 if (rdir->stlen == rdir->stcap) {
276 rdir->stcap *= 2;
277 rdir->stack = (char**) s_realloc(rdir->stack,
278 rdir->stcap * sizeof(char*));
279 }
280 rdir->stack[rdir->stlen++] = filename;
281 continue;
282 }
283 return filename;
284 }
285
286 if (rdir->stlen > 0) {
287 /* open next subdirectory */
288 closedir(rdir->dir);
289 if (rdir->d)
290 free(rdir->name);
291 rdir->name = rdir->stack[--rdir->stlen];
292 rdir->d = 1;
293 if (!(rdir->dir = opendir(rdir->name)))
294 warn("could not open directory: %s", rdir->name);
295 continue;
296 }
297
298 /* no more entries */
299 break;
300 }
301
302 return NULL;
303}
304
305int r_mkdir(const char *path) {
306 char *dir, *d;
307 struct stat stats;
308 int err = 0;
309
310 if (!path || !*path)
311 return -1;
312
313 if (!stat(path, &stats)) {
314 if (S_ISDIR(stats.st_mode)) {
315 return 0;
316 } else {
317 warn("not a directory: %s", path);
318 return -1;
319 }
320 }
321
322 d = dir = (char*) s_malloc(strlen(path) + 1);
323 strcpy(dir, path);
324
325 while (d != NULL && !err) {
326 d = strchr(d + 1, '/');
327 if (d != NULL)
328 *d = '\0';
329 if (access(dir, F_OK) && errno == ENOENT) {
330 if (mkdir(dir, 0755)) {
331 warn("could not create directory: %s", dir);
332 err = -1;
333 }
334 } else if (stat(dir, &stats) || !S_ISDIR(stats.st_mode)) {
335 warn("not a directory: %s", dir);
336 err = -1;
337 }
338 if (d != NULL)
339 *d = '/';
340 }
341 free(dir);
342
343 return err;
344}