@@ -1,6 +1,6 @@ | |||
all: sxiv | |||
VERSION=git-20110407 | |||
VERSION=git-20110408 | |||
CC?=gcc | |||
PREFIX?=/usr/local | |||
@@ -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 | |||
@@ -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; | |||
@@ -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; | |||
@@ -37,6 +37,7 @@ typedef struct { | |||
unsigned char all; | |||
unsigned char quiet; | |||
unsigned char clean_cache; | |||
unsigned char recursive; | |||
} options_t; | |||
@@ -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> | |||
@@ -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(×[0], &fstats.st_atim); | |||
TIMESPEC_TO_TIMEVAL(×[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 + | |||
@@ -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*); | |||
@@ -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; | |||
@@ -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 */ |