Krolyxon
Commit bac610d

Branches

Refs →

nsxivbac610d

Merge branch 'tcache'

Author
Bert
Date
2011-04-11 08:52 +0200
Parents
b2f1b99 dd9e5da
Commit
bac610ddc486fba1f6d40cd5a9a95fbb2bc6c8e5

+506 −99

10 files changed

  1. M Makefile +1 −1
  2. M README.md +1 −0
  3. M main.c +31 −79
  4. M options.c +6 −2
  5. M options.h +1 −0
  6. M sxiv.1 +24 −1
  7. M thumbs.c +195 −15
  8. M thumbs.h +3 −0
  9. M util.c +221 −1
  10. M util.h +23 −0
M Makefile View
@@ -1,6 +1,6 @@
11 all: sxiv
22
3−VERSION=git-20110407
3+VERSION=git-20110408
44
55 CC?=gcc
66 PREFIX?=/usr/local
M README.md View
@@ -36,6 +36,7 @@ sxiv supports the following command-line options:
3636
3737 -a Display all given files, do not filter out unsupported files
3838 (shorter startup time for long file list or slow file types)
39+ -C Remove all orphaned cache files from thumbnail cache and exit
3940 -d Scale all images to 100%, but fit large images into window
4041 -F Use size-hints to make the window fixed/floating
4142 -f Start in fullscreen mode
M main.c View
@@ -19,7 +19,6 @@
1919 #include <stdlib.h>
2020 #include <stdio.h>
2121 #include <string.h>
22−#include <dirent.h>
2322 #include <sys/select.h>
2423 #include <sys/stat.h>
2524 #include <sys/time.h>
@@ -48,7 +47,6 @@ typedef enum {
4847
4948 void update_title();
5049 int check_append(const char*);
51−void read_dir_rec(const char*);
5250 void run();
5351
5452 appmode_t mode;
@@ -56,7 +54,6 @@ img_t img;
5654 tns_t tns;
5755 win_t win;
5856
59−#define DNAME_CNT 512
6057 #define FNAME_CNT 1024
6158 const char **filenames;
6259 int filecnt, fileidx;
@@ -95,13 +92,24 @@ int load_image(int new) {
9592 return ret;
9693 }
9794
95+int fncmp(const void *a, const void *b) {
96+ return strcoll(*((char* const*) a), *((char* const*) b));
97+}
98+
9899 int main(int argc, char **argv) {
99− int i;
100+ int i, start;
100101 const char *filename;
101102 struct stat fstats;
103+ r_dir_t dir;
102104
103105 parse_options(argc, argv);
104106
107+ if (options->clean_cache) {
108+ tns_init(&tns, 0);
109+ tns_clear_cache(&tns);
110+ exit(0);
111+ }
112+
105113 if (!options->filecnt) {
106114 print_usage();
107115 exit(1);
@@ -123,13 +131,26 @@ int main(int argc, char **argv) {
123131 } else {
124132 for (i = 0; i < options->filecnt; ++i) {
125133 filename = options->filenames[i];
126− if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
127− if (options->recursive)
128− read_dir_rec(filename);
129− else
130− warn("ignoring directory: %s", filename);
131− } else {
134+
135+ if (stat(filename, &fstats) || !S_ISDIR(fstats.st_mode)) {
132136 check_append(filename);
137+ } else {
138+ if (!options->recursive) {
139+ warn("ignoring directory: %s", filename);
140+ continue;
141+ }
142+ if (r_opendir(&dir, filename)) {
143+ warn("could not open directory: %s", filename);
144+ continue;
145+ }
146+ start = fileidx;
147+ while ((filename = r_readdir(&dir))) {
148+ if (!check_append(filename))
149+ free((void*) filename);
150+ }
151+ r_closedir(&dir);
152+ if (fileidx - start > 1)
153+ qsort(filenames + start, fileidx - start, sizeof(char*), fncmp);
133154 }
134155 }
135156 }
@@ -215,75 +236,6 @@ int check_append(const char *filename) {
215236 }
216237 }
217238
218−int fncmp(const void *a, const void *b) {
219− return strcoll(*((char* const*) a), *((char* const*) b));
220−}
221−
222−void read_dir_rec(const char *dirname) {
223− char *filename;
224− const char **dirnames;
225− int dircnt, diridx;
226− int fcnt, fstart;
227− unsigned char first;
228− size_t len;
229− DIR *dir;
230− struct dirent *dentry;
231− struct stat fstats;
232−
233− if (!dirname)
234− return;
235−
236− dircnt = DNAME_CNT;
237− diridx = first = 1;
238− dirnames = (const char**) s_malloc(dircnt * sizeof(const char*));
239− dirnames[0] = dirname;
240−
241− fcnt = 0;
242− fstart = fileidx;
243−
244− while (diridx > 0) {
245− dirname = dirnames[--diridx];
246− if (!(dir = opendir(dirname))) {
247− warn("could not open directory: %s", dirname);
248− } else {
249− while ((dentry = readdir(dir))) {
250− if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
251− continue;
252−
253− len = strlen(dirname) + strlen(dentry->d_name) + 2;
254− filename = (char*) s_malloc(len * sizeof(char));
255− snprintf(filename, len, "%s%s%s", dirname,
256− dirname[strlen(dirname)-1] == '/' ? "" : "/", dentry->d_name);
257−
258− if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
259− if (diridx == dircnt) {
260− dircnt *= 2;
261− dirnames = (const char**) s_realloc(dirnames,
262− dircnt * sizeof(const char*));
263− }
264− dirnames[diridx++] = filename;
265− } else {
266− if (check_append(filename))
267− ++fcnt;
268− else
269− free(filename);
270− }
271− }
272− closedir(dir);
273− }
274−
275− if (!first)
276− free((void*) dirname);
277− else
278− first = 0;
279− }
280−
281− if (fcnt > 1)
282− qsort(filenames + fstart, fcnt, sizeof(char*), fncmp);
283−
284− free(dirnames);
285−}
286−
287239 #if EXT_COMMANDS
288240 int run_command(const char *cline, Bool reload) {
289241 int fncnt, fnlen;
M options.c View
@@ -31,7 +31,7 @@ options_t _options;
3131 const options_t *options = (const options_t*) &_options;
3232
3333 void print_usage() {
34− printf("usage: sxiv [-adFfhpqrstvZ] [-g GEOMETRY] [-z ZOOM] FILES...\n");
34+ printf("usage: sxiv [-aCdFfhpqrstvZ] [-g GEOMETRY] [-z ZOOM] FILES...\n");
3535 }
3636
3737 void print_version() {
@@ -53,9 +53,10 @@ void parse_options(int argc, char **argv) {
5353
5454 _options.all = 0;
5555 _options.quiet = 0;
56+ _options.clean_cache = 0;
5657 _options.recursive = 0;
5758
58− while ((opt = getopt(argc, argv, "adFfg:hpqrstvZz:")) != -1) {
59+ while ((opt = getopt(argc, argv, "aCdFfg:hpqrstvZz:")) != -1) {
5960 switch (opt) {
6061 case '?':
6162 print_usage();
@@ -63,6 +64,9 @@ void parse_options(int argc, char **argv) {
6364 case 'a':
6465 _options.all = 1;
6566 break;
67+ case 'C':
68+ _options.clean_cache = 1;
69+ break;
6670 case 'd':
6771 _options.scalemode = SCALE_DOWN;
6872 break;
M options.h View
@@ -37,6 +37,7 @@ typedef struct {
3737
3838 unsigned char all;
3939 unsigned char quiet;
40+ unsigned char clean_cache;
4041 unsigned char recursive;
4142 } options_t;
4243
M sxiv.1 View
@@ -3,7 +3,7 @@
33 sxiv \- Simple (or small or suckless) X Image Viewer
44 .SH SYNOPSIS
55 .B sxiv
6−.RB [ \-adFfhpqrstvZ ]
6+.RB [ \-aCdFfhpqrstvZ ]
77 .RB [ \-g
88 .IR GEOMETRY ]
99 .RB [ \-z
@@ -24,6 +24,9 @@ sxiv has two modes of operation: image and thumbnail mode. The default is image
2424 mode, in which only the current image is shown. In thumbnail mode a grid of
2525 small previews is displayed, making it easy to choose an image to open.
2626 .P
27+sxiv can also cache its thumbnails. Please see the section THUMBNAIL CACHING
28+for information on how to enable this feature.
29+.P
2730 Please note, that the fullscreen mode requires an EWMH/NetWM compliant window
2831 manager.
2932 .SH OPTIONS
@@ -33,6 +36,9 @@ Display all given files, do not filter out unsupported files. This might result
3336 in a much shorter startup time, when the file list is very long or contains
3437 large files of slow loadable types, e.g. gif and progressive jpg.
3538 .TP
39+.B \-C
40+Remove all orphaned cache files from the thumbnail cache directory and exit.
41+.TP
3642 .B \-d
3743 Scale all images to 100%, but fit large images into window.
3844 .TP
@@ -191,6 +197,23 @@ Pan image left.
191197 .TP
192198 .B Shift+ScrollDown
193199 Pan image right.
200+.SH THUMBNAIL CACHING
201+To enable thumbnail caching, please make sure to create the directory
202+.I ~/.sxiv/
203+with write permissions. sxiv will then store all thumbnails inside this
204+directory, but it will not create this directory by itself. It rather uses the
205+existance of this directory as an affirmation, that the user wants thumbnails
206+to be cached.
207+.P
208+Use the command line option
209+.I \-C
210+to keep the cache directory clean by removing all orphaned cache files.
211+Additionally, run the following command afterwards inside the cache directory
212+to remove empty subdirectories:
213+.P
214+.RS
215+find -type d -empty -delete
216+.RE
194217 .SH AUTHORS
195218 .EX
196219 Bert Muennich <ber.t at gmx.com>
M thumbs.c View
@@ -18,49 +18,214 @@
1818
1919 #include <stdlib.h>
2020 #include <string.h>
21+#include <sys/time.h>
22+#include <sys/types.h>
23+#include <sys/stat.h>
24+#include <unistd.h>
2125
2226 #include "config.h"
2327 #include "thumbs.h"
2428 #include "util.h"
2529
2630 extern Imlib_Image *im_invalid;
31+
2732 const int thumb_dim = THUMB_SIZE + 10;
33+char *cache_dir = NULL;
34+
35+int tns_cache_enabled() {
36+ struct stat stats;
37+
38+ return cache_dir && !stat(cache_dir, &stats) && S_ISDIR(stats.st_mode) &&
39+ !access(cache_dir, W_OK);
40+}
41+
42+char* tns_cache_filename(const char *filename) {
43+ size_t len;
44+ char *cfile = NULL;
45+ const char *abspath;
46+
47+ if (!cache_dir || !filename)
48+ return NULL;
49+
50+ if (*filename != '/') {
51+ if (!(abspath = absolute_path(filename)))
52+ return NULL;
53+ } else {
54+ abspath = filename;
55+ }
56+
57+ if (strncmp(abspath, cache_dir, strlen(cache_dir))) {
58+ len = strlen(cache_dir) + strlen(abspath) + 6;
59+ cfile = (char*) s_malloc(len);
60+ snprintf(cfile, len, "%s/%s.png", cache_dir, abspath + 1);
61+ }
62+
63+ if (abspath != filename)
64+ free((void*) abspath);
65+
66+ return cfile;
67+}
68+
69+Imlib_Image* tns_cache_load(const char *filename) {
70+ char *cfile;
71+ struct stat cstats, fstats;
72+ Imlib_Image *im = NULL;
73+
74+ if (!filename)
75+ return NULL;
76+
77+ if (stat(filename, &fstats))
78+ return NULL;
79+
80+ if ((cfile = tns_cache_filename(filename))) {
81+ if (!stat(cfile, &cstats) &&
82+ cstats.st_mtim.tv_sec == fstats.st_mtim.tv_sec &&
83+ cstats.st_mtim.tv_nsec == fstats.st_mtim.tv_nsec)
84+ {
85+ im = imlib_load_image(cfile);
86+ }
87+ free(cfile);
88+ }
89+
90+ return im;
91+}
92+
93+void tns_cache_write(thumb_t *t, Bool force) {
94+ char *cfile, *dirend;
95+ struct stat cstats, fstats;
96+ struct timeval times[2];
97+ Imlib_Load_Error err = 0;
98+
99+ if (!t || !t->im || !t->filename)
100+ return;
101+
102+ if (stat(t->filename, &fstats))
103+ return;
104+
105+ if ((cfile = tns_cache_filename(t->filename))) {
106+ if (force || stat(cfile, &cstats) ||
107+ cstats.st_mtim.tv_sec != fstats.st_mtim.tv_sec ||
108+ cstats.st_mtim.tv_nsec != fstats.st_mtim.tv_nsec)
109+ {
110+ if ((dirend = strrchr(cfile, '/'))) {
111+ *dirend = '\0';
112+ err = r_mkdir(cfile);
113+ *dirend = '/';
114+ }
115+
116+ if (!err) {
117+ imlib_context_set_image(t->im);
118+ imlib_image_set_format("png");
119+ imlib_save_image_with_error_return(cfile, &err);
120+ }
121+
122+ if (err) {
123+ warn("could not cache thumbnail: %s", t->filename);
124+ } else {
125+ TIMESPEC_TO_TIMEVAL(&times[0], &fstats.st_atim);
126+ TIMESPEC_TO_TIMEVAL(&times[1], &fstats.st_mtim);
127+ utimes(cfile, times);
128+ }
129+ }
130+ free(cfile);
131+ }
132+}
133+
134+void tns_clear_cache(tns_t *tns) {
135+ int dirlen, delete;
136+ char *cfile, *filename, *tpos;
137+ r_dir_t dir;
138+
139+ if (!cache_dir)
140+ return;
141+
142+ if (r_opendir(&dir, cache_dir)) {
143+ warn("could not open thumbnail cache directory: %s", cache_dir);
144+ return;
145+ }
146+
147+ dirlen = strlen(cache_dir);
148+
149+ while ((cfile = r_readdir(&dir))) {
150+ filename = cfile + dirlen;
151+ delete = 0;
152+
153+ if ((tpos = strrchr(filename, '.'))) {
154+ *tpos = '\0';
155+ delete = access(filename, F_OK);
156+ *tpos = '.';
157+ }
158+
159+ if (delete && unlink(cfile))
160+ warn("could not delete cache file: %s", cfile);
161+
162+ free(cfile);
163+ }
164+
165+ r_closedir(&dir);
166+}
167+
28168
29169 void tns_init(tns_t *tns, int cnt) {
170+ int len;
171+ char *homedir;
172+
30173 if (!tns)
31174 return;
32175
176+ if (cnt) {
177+ tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t));
178+ memset(tns->thumbs, 0, cnt * sizeof(thumb_t));
179+ } else {
180+ tns->thumbs = NULL;
181+ }
182+
33183 tns->cnt = tns->first = tns->sel = 0;
34− tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t));
35− memset(tns->thumbs, 0, cnt * sizeof(thumb_t));
36184 tns->cap = cnt;
37185 tns->dirty = 0;
186+
187+ if ((homedir = getenv("HOME"))) {
188+ if (cache_dir)
189+ free(cache_dir);
190+ len = strlen(homedir) + 10;
191+ cache_dir = (char*) s_malloc(len * sizeof(char));
192+ snprintf(cache_dir, len, "%s/.sxiv", homedir);
193+ } else {
194+ warn("could not locate thumbnail cache directory");
195+ }
38196 }
39197
40198 void tns_free(tns_t *tns, win_t *win) {
41199 int i;
42200
43− if (!tns || !tns->thumbs)
201+ if (!tns)
44202 return;
45203
46− for (i = 0; i < tns->cnt; ++i) {
47− if (tns->thumbs[i].im) {
48− imlib_context_set_image(tns->thumbs[i].im);
49− imlib_free_image();
204+ if (tns->thumbs) {
205+ for (i = 0; i < tns->cnt; ++i) {
206+ if (tns->thumbs[i].im) {
207+ imlib_context_set_image(tns->thumbs[i].im);
208+ imlib_free_image();
209+ }
50210 }
211+ free(tns->thumbs);
212+ tns->thumbs = NULL;
51213 }
52214
53− free(tns->thumbs);
54− tns->thumbs = NULL;
215+ if (cache_dir) {
216+ free(cache_dir);
217+ cache_dir = NULL;
218+ }
55219 }
56220
57221 void tns_load(tns_t *tns, win_t *win, int n, const char *filename) {
58222 int w, h;
223+ int use_cache, cached = 0;
59224 float z, zw, zh;
60225 thumb_t *t;
61226 Imlib_Image *im;
62227
63− if (!tns || !win || !filename)
228+ if (!tns || !tns->thumbs || !win || !filename)
64229 return;
65230
66231 if (n >= tns->cap)
@@ -75,7 +240,12 @@ void tns_load(tns_t *tns, win_t *win, int n, const char *filename) {
75240 imlib_free_image();
76241 }
77242
78− if ((im = imlib_load_image(filename)))
243+ if ((use_cache = tns_cache_enabled())) {
244+ if ((im = tns_cache_load(filename)))
245+ cached = 1;
246+ }
247+
248+ if (cached || (im = imlib_load_image(filename)))
79249 imlib_context_set_image(im);
80250 else
81251 imlib_context_set_image(im_invalid);
@@ -84,10 +254,12 @@ void tns_load(tns_t *tns, win_t *win, int n, const char *filename) {
84254 h = imlib_image_get_height();
85255
86256 if (im) {
257+ t->filename = filename;
87258 zw = (float) THUMB_SIZE / (float) w;
88259 zh = (float) THUMB_SIZE / (float) h;
89260 z = MIN(zw, zh);
90261 } else {
262+ t->filename = NULL;
91263 z = 1.0;
92264 }
93265
@@ -99,6 +271,8 @@ void tns_load(tns_t *tns, win_t *win, int n, const char *filename) {
99271 die("could not allocate memory");
100272 if (im)
101273 imlib_free_image_and_decache();
274+ if (use_cache && !cached)
275+ tns_cache_write(t, False);
102276
103277 tns->dirty = 1;
104278 }
@@ -134,7 +308,10 @@ void tns_render(tns_t *tns, win_t *win) {
134308 int i, cnt, r, x, y;
135309 thumb_t *t;
136310
137− if (!tns || !tns->dirty || !win)
311+ if (!tns || !tns->thumbs || !win)
312+ return;
313+
314+ if (!tns->dirty)
138315 return;
139316
140317 win_clear(win);
@@ -182,7 +359,7 @@ void tns_highlight(tns_t *tns, win_t *win, int n, Bool hl) {
182359 thumb_t *t;
183360 unsigned long col;
184361
185− if (!tns || !win)
362+ if (!tns || !tns->thumbs || !win)
186363 return;
187364
188365 if (n >= 0 && n < tns->cnt) {
@@ -205,7 +382,7 @@ void tns_highlight(tns_t *tns, win_t *win, int n, Bool hl) {
205382 int tns_move_selection(tns_t *tns, win_t *win, tnsdir_t dir) {
206383 int old;
207384
208− if (!tns || !win)
385+ if (!tns || !tns->thumbs || !win)
209386 return 0;
210387
211388 old = tns->sel;
@@ -264,7 +441,10 @@ int tns_translate(tns_t *tns, int x, int y) {
264441 int n;
265442 thumb_t *t;
266443
267− if (!tns || x < tns->x || y < tns->y)
444+ if (!tns || !tns->thumbs)
445+ return -1;
446+
447+ if (x < tns->x || y < tns->y)
268448 return -1;
269449
270450 n = tns->first + (y - tns->y) / thumb_dim * tns->cols +
M thumbs.h View
@@ -32,6 +32,7 @@ typedef enum {
3232
3333 typedef struct {
3434 Imlib_Image *im;
35+ const char *filename;
3536 int x;
3637 int y;
3738 int w;
@@ -51,6 +52,8 @@ typedef struct {
5152 unsigned char dirty;
5253 } tns_t;
5354
55+void tns_clear_cache(tns_t*);
56+
5457 void tns_init(tns_t*, int);
5558 void tns_free(tns_t*, win_t*);
5659
M util.c View
@@ -18,11 +18,16 @@
1818
1919 #include <stdlib.h>
2020 #include <string.h>
21+#include <sys/types.h>
22+#include <sys/stat.h>
23+#include <unistd.h>
24+#include <errno.h>
2125
2226 #include "options.h"
2327 #include "util.h"
2428
25−#define FNAME_LEN 512
29+#define DNAME_CNT 512
30+#define FNAME_LEN 1024
2631
2732 void cleanup();
2833
@@ -78,6 +83,221 @@ void size_readable(float *size, const char **unit) {
7883 *unit = units[MIN(i, LEN(units) - 1)];
7984 }
8085
86+char* absolute_path(const char *filename) {
87+ size_t len;
88+ char *path = NULL;
89+ const char *basename;
90+ char *dirname = NULL;
91+ char *cwd = NULL;
92+ char *twd = NULL;
93+ char *dir;
94+ char *s;
95+
96+ if (!filename || *filename == '\0' || *filename == '/')
97+ return NULL;
98+
99+ len = FNAME_LEN;
100+ cwd = (char*) s_malloc(len);
101+ while (!(s = getcwd(cwd, len)) && errno == ERANGE) {
102+ len *= 2;
103+ cwd = (char*) s_realloc(cwd, len);
104+ }
105+ if (!s)
106+ goto error;
107+
108+ s = strrchr(filename, '/');
109+ if (s) {
110+ len = s - filename;
111+ dirname = (char*) s_malloc(len + 1);
112+ strncpy(dirname, filename, len);
113+ dirname[len] = '\0';
114+ basename = s + 1;
115+
116+ if (chdir(cwd))
117+ /* we're not able to come back afterwards */
118+ goto error;
119+ if (chdir(dirname))
120+ goto error;
121+
122+ len = FNAME_LEN;
123+ twd = (char*) s_malloc(len);
124+ while (!(s = getcwd(twd, len)) && errno == ERANGE) {
125+ len *= 2;
126+ twd = (char*) s_realloc(twd, len);
127+ }
128+ if (chdir(cwd))
129+ die("could not revert to prior working directory");
130+ if (!s)
131+ goto error;
132+ dir = twd;
133+ } else {
134+ /* only a single filename given */
135+ basename = filename;
136+ dir = cwd;
137+ }
138+
139+ len = strlen(dir) + strlen(basename) + 2;
140+ path = (char*) s_malloc(len);
141+ snprintf(path, len, "%s/%s", dir, basename);
142+
143+goto end;
144+
145+error:
146+ if (path) {
147+ free(path);
148+ path = NULL;
149+ }
150+
151+end:
152+ if (dirname)
153+ free(dirname);
154+ if (cwd)
155+ free(cwd);
156+ if (twd)
157+ free(twd);
158+
159+ return path;
160+}
161+
162+int r_opendir(r_dir_t *rdir, const char *dirname) {
163+ if (!rdir || !dirname || !*dirname)
164+ return -1;
165+
166+ if (!(rdir->dir = opendir(dirname))) {
167+ rdir->name = NULL;
168+ rdir->stack = NULL;
169+ return -1;
170+ }
171+
172+ rdir->stcap = DNAME_CNT;
173+ rdir->stack = (char**) s_malloc(rdir->stcap * sizeof(char*));
174+ rdir->stlen = 0;
175+
176+ rdir->name = (char*) dirname;
177+ rdir->d = 0;
178+
179+ return 0;
180+}
181+
182+int r_closedir(r_dir_t *rdir) {
183+ int ret = 0;
184+
185+ if (!rdir)
186+ return -1;
187+
188+ if (rdir->stack) {
189+ while (rdir->stlen > 0)
190+ free(rdir->stack[--rdir->stlen]);
191+ free(rdir->stack);
192+ rdir->stack = NULL;
193+ }
194+
195+ if (rdir->dir) {
196+ if (!(ret = closedir(rdir->dir)))
197+ rdir->dir = NULL;
198+ }
199+
200+ if (rdir->d && rdir->name) {
201+ free(rdir->name);
202+ rdir->name = NULL;
203+ }
204+
205+ return ret;
206+}
207+
208+char* r_readdir(r_dir_t *rdir) {
209+ size_t len;
210+ char *filename;
211+ struct dirent *dentry;
212+ struct stat fstats;
213+
214+ if (!rdir || !rdir->dir || !rdir->name)
215+ return NULL;
216+
217+ while (1) {
218+ if (rdir->dir && (dentry = readdir(rdir->dir))) {
219+ if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
220+ continue;
221+
222+ len = strlen(rdir->name) + strlen(dentry->d_name) + 2;
223+ filename = (char*) s_malloc(len);
224+ snprintf(filename, len, "%s%s%s", rdir->name,
225+ rdir->name[strlen(rdir->name)-1] == '/' ? "" : "/",
226+ dentry->d_name);
227+
228+ if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
229+ /* put subdirectory on the stack */
230+ if (rdir->stlen == rdir->stcap) {
231+ rdir->stcap *= 2;
232+ rdir->stack = (char**) s_realloc(rdir->stack,
233+ rdir->stcap * sizeof(char*));
234+ }
235+ rdir->stack[rdir->stlen++] = filename;
236+ continue;
237+ }
238+ return filename;
239+ }
240+
241+ if (rdir->stlen > 0) {
242+ /* open next subdirectory */
243+ closedir(rdir->dir);
244+ if (rdir->d)
245+ free(rdir->name);
246+ rdir->name = rdir->stack[--rdir->stlen];
247+ rdir->d = 1;
248+ if (!(rdir->dir = opendir(rdir->name)))
249+ warn("could not open directory: %s", rdir->name);
250+ continue;
251+ }
252+
253+ /* no more entries */
254+ break;
255+ }
256+
257+ return NULL;
258+}
259+
260+int r_mkdir(const char *path) {
261+ char *dir, *d;
262+ struct stat stats;
263+ int err = 0;
264+
265+ if (!path || !*path)
266+ return -1;
267+
268+ if (!stat(path, &stats)) {
269+ if (S_ISDIR(stats.st_mode)) {
270+ return 0;
271+ } else {
272+ warn("not a directory: %s", path);
273+ return -1;
274+ }
275+ }
276+
277+ d = dir = (char*) s_malloc(strlen(path) + 1);
278+ strcpy(dir, path);
279+
280+ while (d != NULL && !err) {
281+ d = strchr(d + 1, '/');
282+ if (d != NULL)
283+ *d = '\0';
284+ if (access(dir, F_OK) && errno == ENOENT) {
285+ if (mkdir(dir, 0755)) {
286+ warn("could not create directory: %s", dir);
287+ err = -1;
288+ }
289+ } else if (stat(dir, &stats) || !S_ISDIR(stats.st_mode)) {
290+ warn("not a directory: %s", dir);
291+ err = -1;
292+ }
293+ if (d != NULL)
294+ *d = '/';
295+ }
296+ free(dir);
297+
298+ return err;
299+}
300+
81301 char* readline(FILE *stream) {
82302 size_t len;
83303 char *buf, *s, *end;
M util.h View
@@ -21,6 +21,7 @@
2121
2222 #include <stdio.h>
2323 #include <stdarg.h>
24+#include <dirent.h>
2425
2526 #define ABS(a) ((a) < 0 ? (-(a)) : (a))
2627 #define MIN(a,b) ((a) < (b) ? (a) : (b))
@@ -30,6 +31,21 @@
3031 #define TV_TO_DOUBLE(x) ((double) ((x).tv_sec) + 0.000001 * \
3132 (double) ((x).tv_usec))
3233
34+#define TIMESPEC_TO_TIMEVAL(tv, ts) { \
35+ (tv)->tv_sec = (ts)->tv_sec; \
36+ (tv)->tv_usec = (ts)->tv_nsec / 1000; \
37+}
38+
39+typedef struct {
40+ DIR *dir;
41+ char *name;
42+ int d;
43+
44+ char **stack;
45+ int stcap;
46+ int stlen;
47+} r_dir_t;
48+
3349 void* s_malloc(size_t);
3450 void* s_realloc(void*, size_t);
3551
@@ -38,6 +54,13 @@ void die(const char*, ...);
3854
3955 void size_readable(float*, const char**);
4056
57+char* absolute_path(const char*);
58+
59+int r_opendir(r_dir_t*, const char*);
60+int r_closedir(r_dir_t*);
61+char* r_readdir(r_dir_t*);
62+int r_mkdir(const char *);
63+
4164 char* readline(FILE*);
4265
4366 #endif /* UTIL_H */