#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tree.h"
struct tree_entry {
struct tree_entry *next;
char *name;
size_t dirname_length;
int fd;
int flags;
};
#define isDir 1
#define isDirLink 2
#define needsTraversal 4
struct tree {
struct tree_entry *stack;
DIR *d;
int initialDirFd;
int flags;
char *buff;
char *basename;
size_t buff_length;
size_t path_length;
size_t dirname_length;
int depth;
int openCount;
int maxOpenCount;
struct stat lst;
struct stat st;
};
#define needsReturn 8
#define hasStat 16
#define hasLstat 32
#define HAVE_DIRENT_D_NAMLEN 1
#ifdef HAVE_DIRENT_D_NAMLEN
#define D_NAMELEN(dp) (dp)->d_namlen
#else
#define D_NAMELEN(dp) (strlen((dp)->d_name))
#endif
#if 0
static void
dumpStack(struct tree *t)
{
struct tree_entry *te;
printf("\tbuff: %s\n", t->buff);
printf("\tpwd: "); fflush(stdout); system("pwd");
printf("\tstack:\n");
for (te = t->stack; te != NULL; te = te->next) {
printf("\t\tte->name: %s %s\n", te->name, te->flags & needsTraversal ? "" : "*");
}
}
#endif
static void
tree_add(struct tree *t, const char *path)
{
struct tree_entry *te;
te = malloc(sizeof(*te));
memset(te, 0, sizeof(*te));
te->next = t->stack;
t->stack = te;
te->fd = -1;
te->name = strdup(path);
te->flags = needsTraversal;
te->dirname_length = t->dirname_length;
}
static void
tree_append(struct tree *t, const char *name, size_t name_length)
{
if (t->buff != NULL)
t->buff[t->dirname_length] = '\0';
while (name_length + 1 + t->dirname_length >= t->buff_length) {
t->buff_length *= 2;
if (t->buff_length < 1024)
t->buff_length = 1024;
t->buff = realloc(t->buff, t->buff_length);
}
t->basename = t->buff + t->dirname_length;
t->path_length = t->dirname_length + name_length;
if (t->dirname_length > 0) {
*t->basename++ = '/';
t->path_length ++;
}
strcpy(t->basename, name);
}
struct tree *
tree_open(const char *path)
{
struct tree *t;
t = malloc(sizeof(*t));
memset(t, 0, sizeof(*t));
tree_append(t, path, strlen(path));
t->initialDirFd = open(".", O_RDONLY);
t->flags = needsReturn;
return (t);
}
static void
tree_ascend(struct tree *t)
{
struct tree_entry *te;
te = t->stack;
t->depth--;
if (te->flags & isDirLink) {
fchdir(te->fd);
close(te->fd);
t->openCount--;
} else {
chdir("..");
}
}
static void
tree_pop(struct tree *t)
{
struct tree_entry *te;
te = t->stack;
t->stack = te->next;
t->dirname_length = te->dirname_length;
free(te->name);
free(te);
}
int
tree_next(struct tree *t)
{
struct dirent *de = NULL;
if (t->flags & needsReturn) {
t->flags &= ~needsReturn;
return (1);
}
while (t->stack != NULL) {
while (t->d != NULL) {
de = readdir(t->d);
if (de == NULL) {
closedir(t->d);
t->d = NULL;
} else if (de->d_name[0] == '.'
&& de->d_name[1] == '\0') {
} else if (de->d_name[0] == '.'
&& de->d_name[1] == '.'
&& de->d_name[2] == '\0') {
} else {
tree_append(t, de->d_name, D_NAMELEN(de));
t->flags &= ~hasLstat;
t->flags &= ~hasStat;
return (1);
}
}
if (t->stack->flags & needsTraversal) {
tree_append(t, t->stack->name, strlen(t->stack->name));
t->stack->flags &= ~needsTraversal;
if (t->stack->flags & isDirLink) {
t->stack->fd = open(".", O_RDONLY);
t->openCount++;
if (t->openCount > t->maxOpenCount)
t->maxOpenCount = t->openCount;
}
if (chdir(t->stack->name) == 0) {
t->depth++;
t->dirname_length = t->path_length;
t->d = opendir(".");
} else
tree_pop(t);
continue;
}
tree_ascend(t);
tree_pop(t);
}
return (0);
}
void
tree_descend(struct tree *t)
{
const struct stat *s = tree_current_lstat(t);
if (S_ISDIR(s->st_mode)) {
tree_add(t, t->basename);
t->stack->flags |= isDir;
}
if (S_ISLNK(s->st_mode) && S_ISDIR(tree_current_stat(t)->st_mode)) {
tree_add(t, t->basename);
t->stack->flags |= isDirLink;
}
}
const struct stat *
tree_current_stat(struct tree *t)
{
if (!(t->flags & hasStat)) {
stat(t->basename, &t->st);
t->flags |= hasStat;
}
return (&t->st);
}
const struct stat *
tree_current_lstat(struct tree *t)
{
if (!(t->flags & hasLstat)) {
lstat(t->basename, &t->lst);
t->flags |= hasLstat;
}
return (&t->lst);
}
const char *
tree_current_access_path(struct tree *t)
{
return (t->basename);
}
const char *
tree_current_path(struct tree *t)
{
return (t->buff);
}
size_t
tree_current_pathlen(struct tree *t)
{
return (t->path_length);
}
int
tree_current_depth(struct tree *t)
{
return (t->depth);
}
void
tree_close(struct tree *t)
{
while (t->stack != NULL)
tree_pop(t);
if (t->buff)
free(t->buff);
if (t->initialDirFd >= 0) {
fchdir(t->initialDirFd);
close(t->initialDirFd);
t->initialDirFd = -1;
}
free(t);
}
#if 0
#include <stdio.h>
int main(int argc, char **argv)
{
size_t max_path_len = 0;
int max_depth = 0;
system("pwd");
while (*++argv) {
struct tree *t = tree_open(*argv);
while (tree_next(t)) {
size_t path_len = tree_current_pathlen(t);
int depth = tree_current_depth(t);
if (path_len > max_path_len)
max_path_len = path_len;
if (depth > max_depth)
max_depth = depth;
printf("%s\n", tree_current_path(t));
if (S_ISDIR(tree_current_lstat(t)->st_mode))
tree_descend(t);
}
tree_close(t);
printf("Max path length: %d\n", max_path_len);
printf("Max depth: %d\n", max_depth);
printf("Final open count: %d\n", t->openCount);
printf("Max open count: %d\n", t->maxOpenCount);
fflush(stdout);
system("pwd");
}
return (0);
}
#endif