Krolyxon
Commit 5034792

Branches

Refs →

nsxivutil.c

355 lines · 7.2 KB · C

1/* sxiv: util.c
2 * Copyright (c) 2011 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
137void time_readable(float *time, const char **unit) {
138 const char *units[] = { "s", "m", "h" };
139 int i;
140
141 for (i = 0; i < ARRLEN(units) && *time >= 60.0; i++)
142 *time /= 60.0;
143 *unit = units[MIN(i, ARRLEN(units) - 1)];
144}
145
146char* absolute_path(const char *filename) {
147 size_t len;
148 const char *basename;
149 char *dir, *dirname = NULL, *path = NULL, *s;
150 char *cwd = NULL, *twd = NULL;
151
152 if (filename == NULL || *filename == '\0' || *filename == '/')
153 return NULL;
154
155 len = FNAME_LEN;
156 cwd = (char*) s_malloc(len);
157 while ((s = getcwd(cwd, len)) == NULL && errno == ERANGE) {
158 len *= 2;
159 cwd = (char*) s_realloc(cwd, len);
160 }
161 if (s == NULL)
162 goto error;
163
164 s = strrchr(filename, '/');
165 if (s != NULL) {
166 len = s - filename;
167 dirname = (char*) s_malloc(len + 1);
168 strncpy(dirname, filename, len);
169 dirname[len] = '\0';
170 basename = s + 1;
171
172 if (chdir(cwd) < 0)
173 /* we're not able to come back afterwards */
174 goto error;
175 if (chdir(dirname) < 0)
176 goto error;
177
178 len = FNAME_LEN;
179 twd = (char*) s_malloc(len);
180 while ((s = getcwd(twd, len)) == NULL && errno == ERANGE) {
181 len *= 2;
182 twd = (char*) s_realloc(twd, len);
183 }
184 if (chdir(cwd) < 0)
185 die("could not revert to prior working directory");
186 if (s == NULL)
187 goto error;
188 dir = twd;
189 } else {
190 /* only a single filename given */
191 basename = filename;
192 dir = cwd;
193 }
194
195 len = strlen(dir) + strlen(basename) + 2;
196 path = (char*) s_malloc(len);
197 snprintf(path, len, "%s/%s", dir, basename);
198
199 goto end;
200
201error:
202 if (path != NULL) {
203 free(path);
204 path = NULL;
205 }
206
207end:
208 if (dirname != NULL)
209 free(dirname);
210 if (cwd != NULL)
211 free(cwd);
212 if (twd != NULL)
213 free(twd);
214
215 return path;
216}
217
218int r_opendir(r_dir_t *rdir, const char *dirname) {
219 if (rdir == NULL || dirname == NULL || *dirname == '\0')
220 return -1;
221
222 if ((rdir->dir = opendir(dirname)) == NULL) {
223 rdir->name = NULL;
224 rdir->stack = NULL;
225 return -1;
226 }
227
228 rdir->stcap = DNAME_CNT;
229 rdir->stack = (char**) s_malloc(rdir->stcap * sizeof(char*));
230 rdir->stlen = 0;
231
232 rdir->name = (char*) dirname;
233 rdir->d = 0;
234
235 return 0;
236}
237
238int r_closedir(r_dir_t *rdir) {
239 int ret = 0;
240
241 if (rdir == NULL)
242 return -1;
243
244 if (rdir->stack != NULL) {
245 while (rdir->stlen > 0)
246 free(rdir->stack[--rdir->stlen]);
247 free(rdir->stack);
248 rdir->stack = NULL;
249 }
250
251 if (rdir->dir != NULL) {
252 if ((ret = closedir(rdir->dir)) == 0)
253 rdir->dir = NULL;
254 }
255
256 if (rdir->d != 0 && rdir->name != NULL) {
257 free(rdir->name);
258 rdir->name = NULL;
259 }
260
261 return ret;
262}
263
264char* r_readdir(r_dir_t *rdir) {
265 size_t len;
266 char *filename;
267 struct dirent *dentry;
268 struct stat fstats;
269
270 if (rdir == NULL || rdir->dir == NULL || rdir->name == NULL)
271 return NULL;
272
273 while (true) {
274 if (rdir->dir != NULL && (dentry = readdir(rdir->dir)) != NULL) {
275 if (STREQ(dentry->d_name, ".") || STREQ(dentry->d_name, ".."))
276 continue;
277
278 len = strlen(rdir->name) + strlen(dentry->d_name) + 2;
279 filename = (char*) s_malloc(len);
280 snprintf(filename, len, "%s%s%s", rdir->name,
281 rdir->name[strlen(rdir->name)-1] == '/' ? "" : "/",
282 dentry->d_name);
283
284 if (stat(filename, &fstats) < 0)
285 continue;
286 if (S_ISDIR(fstats.st_mode)) {
287 /* put subdirectory on the stack */
288 if (rdir->stlen == rdir->stcap) {
289 rdir->stcap *= 2;
290 rdir->stack = (char**) s_realloc(rdir->stack,
291 rdir->stcap * sizeof(char*));
292 }
293 rdir->stack[rdir->stlen++] = filename;
294 continue;
295 }
296 return filename;
297 }
298
299 if (rdir->stlen > 0) {
300 /* open next subdirectory */
301 closedir(rdir->dir);
302 if (rdir->d != 0)
303 free(rdir->name);
304 rdir->name = rdir->stack[--rdir->stlen];
305 rdir->d = 1;
306 if ((rdir->dir = opendir(rdir->name)) == NULL)
307 warn("could not open directory: %s", rdir->name);
308 continue;
309 }
310 /* no more entries */
311 break;
312 }
313 return NULL;
314}
315
316int r_mkdir(const char *path) {
317 char *dir, *d;
318 struct stat stats;
319 int err = 0;
320
321 if (path == NULL || *path == '\0')
322 return -1;
323
324 if (stat(path, &stats) == 0) {
325 if (S_ISDIR(stats.st_mode)) {
326 return 0;
327 } else {
328 warn("not a directory: %s", path);
329 return -1;
330 }
331 }
332
333 d = dir = (char*) s_malloc(strlen(path) + 1);
334 strcpy(dir, path);
335
336 while (d != NULL && err == 0) {
337 d = strchr(d + 1, '/');
338 if (d != NULL)
339 *d = '\0';
340 if (access(dir, F_OK) < 0 && errno == ENOENT) {
341 if (mkdir(dir, 0755) < 0) {
342 warn("could not create directory: %s", dir);
343 err = -1;
344 }
345 } else if (stat(dir, &stats) < 0 || !S_ISDIR(stats.st_mode)) {
346 warn("not a directory: %s", dir);
347 err = -1;
348 }
349 if (d != NULL)
350 *d = '/';
351 }
352 free(dir);
353
354 return err;
355}