aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBert <ber.t@gmx.com>2011-04-11 08:52:07 +0200
committerBert <ber.t@gmx.com>2011-04-11 08:52:07 +0200
commitbac610ddc486fba1f6d40cd5a9a95fbb2bc6c8e5 (patch)
treec8f678d0a0926c30cd211807b1aa2f3c612304fc
parentb2f1b997ed60a40ffb9c0964ed0716c375b30072 (diff)
parentdd9e5dabb155fb9bb06d5a8e5fd6717df1751762 (diff)
Merge branch 'tcache'
Conflicts: main.c
-rw-r--r--Makefile2
-rw-r--r--README.md1
-rw-r--r--main.c110
-rw-r--r--options.c8
-rw-r--r--options.h1
-rw-r--r--sxiv.125
-rw-r--r--thumbs.c210
-rw-r--r--thumbs.h3
-rw-r--r--util.c222
-rw-r--r--util.h23
10 files changed, 506 insertions, 99 deletions
diff --git a/Makefile b/Makefile
index d814b2a..274ae1e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
all: sxiv
-VERSION=git-20110407
+VERSION=git-20110408
CC?=gcc
PREFIX?=/usr/local
diff --git a/README.md b/README.md
index 0d50674..424caf2 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,7 @@ sxiv supports the following command-line options:
-a Display all given files, do not filter out unsupported files
(shorter startup time for long file list or slow file types)
+ -C Remove all orphaned cache files from thumbnail cache and exit
-d Scale all images to 100%, but fit large images into window
-F Use size-hints to make the window fixed/floating
-f Start in fullscreen mode
diff --git a/main.c b/main.c
index 45f3666..0a66aab 100644
--- a/main.c
+++ b/main.c
@@ -19,7 +19,6 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <dirent.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/time.h>
@@ -48,7 +47,6 @@ typedef enum {
void update_title();
int check_append(const char*);
-void read_dir_rec(const char*);
void run();
appmode_t mode;
@@ -56,7 +54,6 @@ img_t img;
tns_t tns;
win_t win;
-#define DNAME_CNT 512
#define FNAME_CNT 1024
const char **filenames;
int filecnt, fileidx;
@@ -95,13 +92,24 @@ int load_image(int new) {
return ret;
}
+int fncmp(const void *a, const void *b) {
+ return strcoll(*((char* const*) a), *((char* const*) b));
+}
+
int main(int argc, char **argv) {
- int i;
+ int i, start;
const char *filename;
struct stat fstats;
+ r_dir_t dir;
parse_options(argc, argv);
+ if (options->clean_cache) {
+ tns_init(&tns, 0);
+ tns_clear_cache(&tns);
+ exit(0);
+ }
+
if (!options->filecnt) {
print_usage();
exit(1);
@@ -123,13 +131,26 @@ int main(int argc, char **argv) {
} else {
for (i = 0; i < options->filecnt; ++i) {
filename = options->filenames[i];
- if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
- if (options->recursive)
- read_dir_rec(filename);
- else
- warn("ignoring directory: %s", filename);
- } else {
+
+ if (stat(filename, &fstats) || !S_ISDIR(fstats.st_mode)) {
check_append(filename);
+ } else {
+ if (!options->recursive) {
+ warn("ignoring directory: %s", filename);
+ continue;
+ }
+ if (r_opendir(&dir, filename)) {
+ warn("could not open directory: %s", filename);
+ continue;
+ }
+ start = fileidx;
+ while ((filename = r_readdir(&dir))) {
+ if (!check_append(filename))
+ free((void*) filename);
+ }
+ r_closedir(&dir);
+ if (fileidx - start > 1)
+ qsort(filenames + start, fileidx - start, sizeof(char*), fncmp);
}
}
}
@@ -215,75 +236,6 @@ int check_append(const char *filename) {
}
}
-int fncmp(const void *a, const void *b) {
- return strcoll(*((char* const*) a), *((char* const*) b));
-}
-
-void read_dir_rec(const char *dirname) {
- char *filename;
- const char **dirnames;
- int dircnt, diridx;
- int fcnt, fstart;
- unsigned char first;
- size_t len;
- DIR *dir;
- struct dirent *dentry;
- struct stat fstats;
-
- if (!dirname)
- return;
-
- dircnt = DNAME_CNT;
- diridx = first = 1;
- dirnames = (const char**) s_malloc(dircnt * sizeof(const char*));
- dirnames[0] = dirname;
-
- fcnt = 0;
- fstart = fileidx;
-
- while (diridx > 0) {
- dirname = dirnames[--diridx];
- if (!(dir = opendir(dirname))) {
- warn("could not open directory: %s", dirname);
- } else {
- while ((dentry = readdir(dir))) {
- if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
- continue;
-
- len = strlen(dirname) + strlen(dentry->d_name) + 2;
- filename = (char*) s_malloc(len * sizeof(char));
- snprintf(filename, len, "%s%s%s", dirname,
- dirname[strlen(dirname)-1] == '/' ? "" : "/", dentry->d_name);
-
- if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
- if (diridx == dircnt) {
- dircnt *= 2;
- dirnames = (const char**) s_realloc(dirnames,
- dircnt * sizeof(const char*));
- }
- dirnames[diridx++] = filename;
- } else {
- if (check_append(filename))
- ++fcnt;
- else
- free(filename);
- }
- }
- closedir(dir);
- }
-
- if (!first)
- free((void*) dirname);
- else
- first = 0;
- }
-
- if (fcnt > 1)
- qsort(filenames + fstart, fcnt, sizeof(char*), fncmp);
-
- free(dirnames);
-}
-
#if EXT_COMMANDS
int run_command(const char *cline, Bool reload) {
int fncnt, fnlen;
diff --git a/options.c b/options.c
index f5066f4..8105485 100644
--- a/options.c
+++ b/options.c
@@ -31,7 +31,7 @@ options_t _options;
const options_t *options = (const options_t*) &_options;
void print_usage() {
- printf("usage: sxiv [-adFfhpqrstvZ] [-g GEOMETRY] [-z ZOOM] FILES...\n");
+ printf("usage: sxiv [-aCdFfhpqrstvZ] [-g GEOMETRY] [-z ZOOM] FILES...\n");
}
void print_version() {
@@ -53,9 +53,10 @@ void parse_options(int argc, char **argv) {
_options.all = 0;
_options.quiet = 0;
+ _options.clean_cache = 0;
_options.recursive = 0;
- while ((opt = getopt(argc, argv, "adFfg:hpqrstvZz:")) != -1) {
+ while ((opt = getopt(argc, argv, "aCdFfg:hpqrstvZz:")) != -1) {
switch (opt) {
case '?':
print_usage();
@@ -63,6 +64,9 @@ void parse_options(int argc, char **argv) {
case 'a':
_options.all = 1;
break;
+ case 'C':
+ _options.clean_cache = 1;
+ break;
case 'd':
_options.scalemode = SCALE_DOWN;
break;
diff --git a/options.h b/options.h
index c32b50a..b7b8ba5 100644
--- a/options.h
+++ b/options.h
@@ -37,6 +37,7 @@ typedef struct {
unsigned char all;
unsigned char quiet;
+ unsigned char clean_cache;
unsigned char recursive;
} options_t;
diff --git a/sxiv.1 b/sxiv.1
index 35e7bfb..e7f19e2 100644
--- a/sxiv.1
+++ b/sxiv.1
@@ -3,7 +3,7 @@
sxiv \- Simple (or small or suckless) X Image Viewer
.SH SYNOPSIS
.B sxiv
-.RB [ \-adFfhpqrstvZ ]
+.RB [ \-aCdFfhpqrstvZ ]
.RB [ \-g
.IR GEOMETRY ]
.RB [ \-z
@@ -24,6 +24,9 @@ sxiv has two modes of operation: image and thumbnail mode. The default is image
mode, in which only the current image is shown. In thumbnail mode a grid of
small previews is displayed, making it easy to choose an image to open.
.P
+sxiv can also cache its thumbnails. Please see the section THUMBNAIL CACHING
+for information on how to enable this feature.
+.P
Please note, that the fullscreen mode requires an EWMH/NetWM compliant window
manager.
.SH OPTIONS
@@ -33,6 +36,9 @@ Display all given files, do not filter out unsupported files. This might result
in a much shorter startup time, when the file list is very long or contains
large files of slow loadable types, e.g. gif and progressive jpg.
.TP
+.B \-C
+Remove all orphaned cache files from the thumbnail cache directory and exit.
+.TP
.B \-d
Scale all images to 100%, but fit large images into window.
.TP
@@ -191,6 +197,23 @@ Pan image left.
.TP
.B Shift+ScrollDown
Pan image right.
+.SH THUMBNAIL CACHING
+To enable thumbnail caching, please make sure to create the directory
+.I ~/.sxiv/
+with write permissions. sxiv will then store all thumbnails inside this
+directory, but it will not create this directory by itself. It rather uses the
+existance of this directory as an affirmation, that the user wants thumbnails
+to be cached.
+.P
+Use the command line option
+.I \-C
+to keep the cache directory clean by removing all orphaned cache files.
+Additionally, run the following command afterwards inside the cache directory
+to remove empty subdirectories:
+.P
+.RS
+find -type d -empty -delete
+.RE
.SH AUTHORS
.EX
Bert Muennich <ber.t at gmx.com>
diff --git a/thumbs.c b/thumbs.c
index da2adee..74ed708 100644
--- a/thumbs.c
+++ b/thumbs.c
@@ -18,49 +18,214 @@
#include <stdlib.h>
#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include "config.h"
#include "thumbs.h"
#include "util.h"
extern Imlib_Image *im_invalid;
+
const int thumb_dim = THUMB_SIZE + 10;
+char *cache_dir = NULL;
+
+int tns_cache_enabled() {
+ struct stat stats;
+
+ return cache_dir && !stat(cache_dir, &stats) && S_ISDIR(stats.st_mode) &&
+ !access(cache_dir, W_OK);
+}
+
+char* tns_cache_filename(const char *filename) {
+ size_t len;
+ char *cfile = NULL;
+ const char *abspath;
+
+ if (!cache_dir || !filename)
+ return NULL;
+
+ if (*filename != '/') {
+ if (!(abspath = absolute_path(filename)))
+ return NULL;
+ } else {
+ abspath = filename;
+ }
+
+ if (strncmp(abspath, cache_dir, strlen(cache_dir))) {
+ len = strlen(cache_dir) + strlen(abspath) + 6;
+ cfile = (char*) s_malloc(len);
+ snprintf(cfile, len, "%s/%s.png", cache_dir, abspath + 1);
+ }
+
+ if (abspath != filename)
+ free((void*) abspath);
+
+ return cfile;
+}
+
+Imlib_Image* tns_cache_load(const char *filename) {
+ char *cfile;
+ struct stat cstats, fstats;
+ Imlib_Image *im = NULL;
+
+ if (!filename)
+ return NULL;
+
+ if (stat(filename, &fstats))
+ return NULL;
+
+ if ((cfile = tns_cache_filename(filename))) {
+ if (!stat(cfile, &cstats) &&
+ cstats.st_mtim.tv_sec == fstats.st_mtim.tv_sec &&
+ cstats.st_mtim.tv_nsec == fstats.st_mtim.tv_nsec)
+ {
+ im = imlib_load_image(cfile);
+ }
+ free(cfile);
+ }
+
+ return im;
+}
+
+void tns_cache_write(thumb_t *t, Bool force) {
+ char *cfile, *dirend;
+ struct stat cstats, fstats;
+ struct timeval times[2];
+ Imlib_Load_Error err = 0;
+
+ if (!t || !t->im || !t->filename)
+ return;
+
+ if (stat(t->filename, &fstats))
+ return;
+
+ if ((cfile = tns_cache_filename(t->filename))) {
+ if (force || stat(cfile, &cstats) ||
+ cstats.st_mtim.tv_sec != fstats.st_mtim.tv_sec ||
+ cstats.st_mtim.tv_nsec != fstats.st_mtim.tv_nsec)
+ {
+ if ((dirend = strrchr(cfile, '/'))) {
+ *dirend = '\0';
+ err = r_mkdir(cfile);
+ *dirend = '/';
+ }
+
+ if (!err) {
+ imlib_context_set_image(t->im);
+ imlib_image_set_format("png");
+ imlib_save_image_with_error_return(cfile, &err);
+ }
+
+ if (err) {
+ warn("could not cache thumbnail: %s", t->filename);
+ } else {
+ TIMESPEC_TO_TIMEVAL(&times[0], &fstats.st_atim);
+ TIMESPEC_TO_TIMEVAL(&times[1], &fstats.st_mtim);
+ utimes(cfile, times);
+ }
+ }
+ free(cfile);
+ }
+}
+
+void tns_clear_cache(tns_t *tns) {
+ int dirlen, delete;
+ char *cfile, *filename, *tpos;
+ r_dir_t dir;
+
+ if (!cache_dir)
+ return;
+
+ if (r_opendir(&dir, cache_dir)) {
+ warn("could not open thumbnail cache directory: %s", cache_dir);
+ return;
+ }
+
+ dirlen = strlen(cache_dir);
+
+ while ((cfile = r_readdir(&dir))) {
+ filename = cfile + dirlen;
+ delete = 0;
+
+ if ((tpos = strrchr(filename, '.'))) {
+ *tpos = '\0';
+ delete = access(filename, F_OK);
+ *tpos = '.';
+ }
+
+ if (delete && unlink(cfile))
+ warn("could not delete cache file: %s", cfile);
+
+ free(cfile);
+ }
+
+ r_closedir(&dir);
+}
+
void tns_init(tns_t *tns, int cnt) {
+ int len;
+ char *homedir;
+
if (!tns)
return;
+ if (cnt) {
+ tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t));
+ memset(tns->thumbs, 0, cnt * sizeof(thumb_t));
+ } else {
+ tns->thumbs = NULL;
+ }
+
tns->cnt = tns->first = tns->sel = 0;
- tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t));
- memset(tns->thumbs, 0, cnt * sizeof(thumb_t));
tns->cap = cnt;
tns->dirty = 0;
+
+ if ((homedir = getenv("HOME"))) {
+ if (cache_dir)
+ free(cache_dir);
+ len = strlen(homedir) + 10;
+ cache_dir = (char*) s_malloc(len * sizeof(char));
+ snprintf(cache_dir, len, "%s/.sxiv", homedir);
+ } else {
+ warn("could not locate thumbnail cache directory");
+ }
}
void tns_free(tns_t *tns, win_t *win) {
int i;
- if (!tns || !tns->thumbs)
+ if (!tns)
return;
- for (i = 0; i < tns->cnt; ++i) {
- if (tns->thumbs[i].im) {
- imlib_context_set_image(tns->thumbs[i].im);
- imlib_free_image();
+ if (tns->thumbs) {
+ for (i = 0; i < tns->cnt; ++i) {
+ if (tns->thumbs[i].im) {
+ imlib_context_set_image(tns->thumbs[i].im);
+ imlib_free_image();
+ }
}
+ free(tns->thumbs);
+ tns->thumbs = NULL;
}
- free(tns->thumbs);
- tns->thumbs = NULL;
+ if (cache_dir) {
+ free(cache_dir);
+ cache_dir = NULL;
+ }
}
void tns_load(tns_t *tns, win_t *win, int n, const char *filename) {
int w, h;
+ int use_cache, cached = 0;
float z, zw, zh;
thumb_t *t;
Imlib_Image *im;
- if (!tns || !win || !filename)
+ if (!tns || !tns->thumbs || !win || !filename)
return;
if (n >= tns->cap)
@@ -75,7 +240,12 @@ void tns_load(tns_t *tns, win_t *win, int n, const char *filename) {
imlib_free_image();
}
- if ((im = imlib_load_image(filename)))
+ if ((use_cache = tns_cache_enabled())) {
+ if ((im = tns_cache_load(filename)))
+ cached = 1;
+ }
+
+ if (cached || (im = imlib_load_image(filename)))
imlib_context_set_image(im);
else
imlib_context_set_image(im_invalid);
@@ -84,10 +254,12 @@ void tns_load(tns_t *tns, win_t *win, int n, const char *filename) {
h = imlib_image_get_height();
if (im) {
+ t->filename = filename;
zw = (float) THUMB_SIZE / (float) w;
zh = (float) THUMB_SIZE / (float) h;
z = MIN(zw, zh);
} else {
+ t->filename = NULL;
z = 1.0;
}
@@ -99,6 +271,8 @@ void tns_load(tns_t *tns, win_t *win, int n, const char *filename) {
die("could not allocate memory");
if (im)
imlib_free_image_and_decache();
+ if (use_cache && !cached)
+ tns_cache_write(t, False);
tns->dirty = 1;
}
@@ -134,7 +308,10 @@ void tns_render(tns_t *tns, win_t *win) {
int i, cnt, r, x, y;
thumb_t *t;
- if (!tns || !tns->dirty || !win)
+ if (!tns || !tns->thumbs || !win)
+ return;
+
+ if (!tns->dirty)
return;
win_clear(win);
@@ -182,7 +359,7 @@ void tns_highlight(tns_t *tns, win_t *win, int n, Bool hl) {
thumb_t *t;
unsigned long col;
- if (!tns || !win)
+ if (!tns || !tns->thumbs || !win)
return;
if (n >= 0 && n < tns->cnt) {
@@ -205,7 +382,7 @@ void tns_highlight(tns_t *tns, win_t *win, int n, Bool hl) {
int tns_move_selection(tns_t *tns, win_t *win, tnsdir_t dir) {
int old;
- if (!tns || !win)
+ if (!tns || !tns->thumbs || !win)
return 0;
old = tns->sel;
@@ -264,7 +441,10 @@ int tns_translate(tns_t *tns, int x, int y) {
int n;
thumb_t *t;
- if (!tns || x < tns->x || y < tns->y)
+ if (!tns || !tns->thumbs)
+ return -1;
+
+ if (x < tns->x || y < tns->y)
return -1;
n = tns->first + (y - tns->y) / thumb_dim * tns->cols +
diff --git a/thumbs.h b/thumbs.h
index 4b428d7..4a8af09 100644
--- a/thumbs.h
+++ b/thumbs.h
@@ -32,6 +32,7 @@ typedef enum {
typedef struct {
Imlib_Image *im;
+ const char *filename;
int x;
int y;
int w;
@@ -51,6 +52,8 @@ typedef struct {
unsigned char dirty;
} tns_t;
+void tns_clear_cache(tns_t*);
+
void tns_init(tns_t*, int);
void tns_free(tns_t*, win_t*);
diff --git a/util.c b/util.c
index 5c5737b..e16de94 100644
--- a/util.c
+++ b/util.c
@@ -18,11 +18,16 @@
#include <stdlib.h>
#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
#include "options.h"
#include "util.h"
-#define FNAME_LEN 512
+#define DNAME_CNT 512
+#define FNAME_LEN 1024
void cleanup();
@@ -78,6 +83,221 @@ void size_readable(float *size, const char **unit) {
*unit = units[MIN(i, LEN(units) - 1)];
}
+char* absolute_path(const char *filename) {
+ size_t len;
+ char *path = NULL;
+ const char *basename;
+ char *dirname = NULL;
+ char *cwd = NULL;
+ char *twd = NULL;
+ char *dir;
+ char *s;
+
+ if (!filename || *filename == '\0' || *filename == '/')
+ return NULL;
+
+ len = FNAME_LEN;
+ cwd = (char*) s_malloc(len);
+ while (!(s = getcwd(cwd, len)) && errno == ERANGE) {
+ len *= 2;
+ cwd = (char*) s_realloc(cwd, len);
+ }
+ if (!s)
+ goto error;
+
+ s = strrchr(filename, '/');
+ if (s) {
+ len = s - filename;
+ dirname = (char*) s_malloc(len + 1);
+ strncpy(dirname, filename, len);
+ dirname[len] = '\0';
+ basename = s + 1;
+
+ if (chdir(cwd))
+ /* we're not able to come back afterwards */
+ goto error;
+ if (chdir(dirname))
+ goto error;
+
+ len = FNAME_LEN;
+ twd = (char*) s_malloc(len);
+ while (!(s = getcwd(twd, len)) && errno == ERANGE) {
+ len *= 2;
+ twd = (char*) s_realloc(twd, len);
+ }
+ if (chdir(cwd))
+ die("could not revert to prior working directory");
+ if (!s)
+ goto error;
+ dir = twd;
+ } else {
+ /* only a single filename given */
+ basename = filename;
+ dir = cwd;
+ }
+
+ len = strlen(dir) + strlen(basename) + 2;
+ path = (char*) s_malloc(len);
+ snprintf(path, len, "%s/%s", dir, basename);
+
+goto end;
+
+error:
+ if (path) {
+ free(path);
+ path = NULL;
+ }
+
+end:
+ if (dirname)
+ free(dirname);
+ if (cwd)
+ free(cwd);
+ if (twd)
+ free(twd);
+
+ return path;
+}
+
+int r_opendir(r_dir_t *rdir, const char *dirname) {
+ if (!rdir || !dirname || !*dirname)
+ return -1;
+
+ if (!(rdir->dir = opendir(dirname))) {
+ rdir->name = NULL;
+ rdir->stack = NULL;
+ return -1;
+ }
+
+ rdir->stcap = DNAME_CNT;
+ rdir->stack = (char**) s_malloc(rdir->stcap * sizeof(char*));
+ rdir->stlen = 0;
+
+ rdir->name = (char*) dirname;
+ rdir->d = 0;
+
+ return 0;
+}
+
+int r_closedir(r_dir_t *rdir) {
+ int ret = 0;
+
+ if (!rdir)
+ return -1;
+
+ if (rdir->stack) {
+ while (rdir->stlen > 0)
+ free(rdir->stack[--rdir->stlen]);
+ free(rdir->stack);
+ rdir->stack = NULL;
+ }
+
+ if (rdir->dir) {
+ if (!(ret = closedir(rdir->dir)))
+ rdir->dir = NULL;
+ }
+
+ if (rdir->d && rdir->name) {
+ free(rdir->name);
+ rdir->name = NULL;
+ }
+
+ return ret;
+}
+
+char* r_readdir(r_dir_t *rdir) {
+ size_t len;
+ char *filename;
+ struct dirent *dentry;
+ struct stat fstats;
+
+ if (!rdir || !rdir->dir || !rdir->name)
+ return NULL;
+
+ while (1) {
+ if (rdir->dir && (dentry = readdir(rdir->dir))) {
+ if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
+ continue;
+
+ len = strlen(rdir->name) + strlen(dentry->d_name) + 2;
+ filename = (char*) s_malloc(len);
+ snprintf(filename, len, "%s%s%s", rdir->name,
+ rdir->name[strlen(rdir->name)-1] == '/' ? "" : "/",
+ dentry->d_name);
+
+ if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
+ /* put subdirectory on the stack */
+ if (rdir->stlen == rdir->stcap) {
+ rdir->stcap *= 2;
+ rdir->stack = (char**) s_realloc(rdir->stack,
+ rdir->stcap * sizeof(char*));
+ }
+ rdir->stack[rdir->stlen++] = filename;
+ continue;
+ }
+ return filename;
+ }
+
+ if (rdir->stlen > 0) {
+ /* open next subdirectory */
+ closedir(rdir->dir);
+ if (rdir->d)
+ free(rdir->name);
+ rdir->name = rdir->stack[--rdir->stlen];
+ rdir->d = 1;
+ if (!(rdir->dir = opendir(rdir->name)))
+ warn("could not open directory: %s", rdir->name);
+ continue;
+ }
+
+ /* no more entries */
+ break;
+ }
+
+ return NULL;
+}
+
+int r_mkdir(const char *path) {
+ char *dir, *d;
+ struct stat stats;
+ int err = 0;
+
+ if (!path || !*path)
+ return -1;
+
+ if (!stat(path, &stats)) {
+ if (S_ISDIR(stats.st_mode)) {
+ return 0;
+ } else {
+ warn("not a directory: %s", path);
+ return -1;
+ }
+ }
+
+ d = dir = (char*) s_malloc(strlen(path) + 1);
+ strcpy(dir, path);
+
+ while (d != NULL && !err) {
+ d = strchr(d + 1, '/');
+ if (d != NULL)
+ *d = '\0';
+ if (access(dir, F_OK) && errno == ENOENT) {
+ if (mkdir(dir, 0755)) {
+ warn("could not create directory: %s", dir);
+ err = -1;
+ }
+ } else if (stat(dir, &stats) || !S_ISDIR(stats.st_mode)) {
+ warn("not a directory: %s", dir);
+ err = -1;
+ }
+ if (d != NULL)
+ *d = '/';
+ }
+ free(dir);
+
+ return err;
+}
+
char* readline(FILE *stream) {
size_t len;
char *buf, *s, *end;
diff --git a/util.h b/util.h
index f1db6b8..8e8a20d 100644
--- a/util.h
+++ b/util.h
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <stdarg.h>
+#include <dirent.h>
#define ABS(a) ((a) < 0 ? (-(a)) : (a))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
@@ -30,6 +31,21 @@
#define TV_TO_DOUBLE(x) ((double) ((x).tv_sec) + 0.000001 * \
(double) ((x).tv_usec))
+#define TIMESPEC_TO_TIMEVAL(tv, ts) { \
+ (tv)->tv_sec = (ts)->tv_sec; \
+ (tv)->tv_usec = (ts)->tv_nsec / 1000; \
+}
+
+typedef struct {
+ DIR *dir;
+ char *name;
+ int d;
+
+ char **stack;
+ int stcap;
+ int stlen;
+} r_dir_t;
+
void* s_malloc(size_t);
void* s_realloc(void*, size_t);
@@ -38,6 +54,13 @@ void die(const char*, ...);
void size_readable(float*, const char**);
+char* absolute_path(const char*);
+
+int r_opendir(r_dir_t*, const char*);
+int r_closedir(r_dir_t*);
+char* r_readdir(r_dir_t*);
+int r_mkdir(const char *);
+
char* readline(FILE*);
#endif /* UTIL_H */