Commit 5690f02e by Shawn O. Pearce

Rewrite git_foreach_dirent into gitfo_dirent

Our fileops API is currently private.  We aren't planning on supplying
a cross-platform file API to applications that link to us.  If we did,
we'd probably whole-sale publish fileops, not just the dirent code.

By moving it to be private we can also change the call signature to
permit the buffer to be passed down through the call chain.  This is
very helpful when we are doing a recursive scan as we can reuse just
one buffer in all stack frames, reducing the impact the recursion has
on the stack frames in the data cache.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
parent 9eb79764
......@@ -187,31 +187,25 @@ int gitfo_close_cached(gitfo_cache *ioc)
return gitfo_close(fd);
}
/**
* Walk a directory and run 'fn' for each encountered entry
* (except '.' and '..').
*/
int git_foreach_dirent(const char *wd, int (*fn)(void *, const char *), void *arg)
int gitfo_dirent(
char *path,
size_t path_sz,
int (*fn)(void *, char *),
void *arg)
{
char path[GIT_PATH_MAX];
size_t wd_len;
size_t wd_len = strlen(path);
DIR *dir;
struct dirent *de;
if (!wd)
if (!wd_len || path_sz < wd_len + 2)
return GIT_ERROR;
wd_len = strlen(wd);
if (!wd_len || sizeof(path) < wd_len + 2)
return GIT_ERROR;
strcpy(path, wd);
while (path[wd_len - 1] == '/')
wd_len--;
path[wd_len++] = '/';
path[wd_len] = '\0';
dir = opendir(wd);
dir = opendir(path);
if (!dir)
return git_os_error();
......@@ -228,7 +222,7 @@ int git_foreach_dirent(const char *wd, int (*fn)(void *, const char *), void *ar
}
de_len = strlen(de->d_name);
if (sizeof(path) < wd_len + de_len + 1) {
if (path_sz < wd_len + de_len + 1) {
closedir(dir);
return GIT_ERROR;
}
......
......@@ -9,6 +9,7 @@
/** Force 64 bit off_t size on POSIX. */
#define _FILE_OFFSET_BITS 64
#include "common.h"
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
......@@ -18,8 +19,6 @@
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include "errors.h"
#include "git/fileops.h"
#define GITFO_BUF_INIT {NULL, 0}
......@@ -43,6 +42,23 @@ extern off_t gitfo_size(git_file fd);
extern int gitfo_read_file(gitfo_buf *obj, const char *path);
extern void gitfo_free_buf(gitfo_buf *obj);
/**
* Walk each directory entry, except '.' and '..', calling fn(state).
*
* @param pathbuf buffer the function reads the initial directory
* path from, and updates with each successive entry's name.
* @param pathmax maximum allocation of pathbuf.
* @param fn function to invoke with each entry. The first arg is
* the input state and the second arg is pathbuf. The function
* may modify the pathbuf, but only by appending new text.
* @param state to pass to fn as the first arg.
*/
extern int gitfo_dirent(
char *pathbuf,
size_t pathmax,
int (*fn)(void *, char *),
void *state);
extern gitfo_cache *gitfo_enable_caching(git_file fd, size_t cache_size);
extern int gitfo_write_cached(gitfo_cache *ioc, void *buf, size_t len);
extern int gitfo_flush_cached(gitfo_cache *ioc);
......
#ifndef INCLUDE_git_fileops_h__
#define INCLUDE_git_fileops_h__
#include "common.h"
/**
* @file git/fileops.h
* @brief Git platform agnostic filesystem operations
* @defgroup git_fileops Git filesystem operations
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* For each directory entry (except "." and ".."), run the function
* "fn", passing it "arg" as its first argument and the path to
* the entry as the second argument.
* @param dir The directory to walk
* @param fn The callback function to run for each entry in *dir.
* "fn" may return >0 to signal "I'm done. Stop parsing and
* return successfully" or <0 to signal an error. All non-zero
* return codes cause directory traversal to stop.
* @param arg The first argument that will be passed to 'fn'
* @return GIT_SUCCESS if all entries were successfully traversed,
* otherwise the result of fn.
*/
GIT_EXTERN(int) git_foreach_dirent(const char *dir,
int (*fn)(void *, const char *), void *arg);
/** @} */
GIT_END_DECL
#endif /* INCLUDE_git_fileops_h__ */
#include "test_lib.h"
#include "fileops.h"
static char path_buffer[GIT_PATH_MAX];
static int state_loc;
static const char* names[] = {
"./a",
"./asdf",
"./pack-foo.pack",
NULL
};
static int one_entry(void *state, char *path)
{
const char **c;
must_be_true(state == &state_loc);
must_be_true(path == path_buffer);
for (c = names; *c; c++) {
if (!strcmp(*c, path)) {
*c = "";
return 0;
}
}
test_die("unexpected path \"%s\"", path);
}
BEGIN_TEST(setup)
const char **c;
for (c = names; *c; c++) {
git_file fd = gitfo_creat(*c, 0600);
must_be_true(fd >= 0);
gitfo_close(fd);
}
END_TEST
BEGIN_TEST(direent_walk)
const char **c;
strcpy(path_buffer, ".");
must_pass(gitfo_dirent(path_buffer,
sizeof(path_buffer),
one_entry,
&state_loc));
for (c = names; *c; c++)
must_pass(strcmp("", *c));
END_TEST
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment