#include <u.h>
#include <libc.h>
#include "dat.h"
#include "fns.h"
static char*
append(char **p, char *s)
{
int n;
char *o;
if(s == nil)
return nil;
n = strlen(s)+1;
memmove(o = *p, s, n);
*p += n;
return o;
}
static Dir*
xdirdup(Dir *d, int n)
{
char *p;
Dir *o;
int i;
p = nil;
for(i=0; i<n; i++){
p += strlen(d[i].name)+1;
if(d[i].uid) p += strlen(d[i].uid)+1;
if(d[i].gid) p += strlen(d[i].gid)+1;
if(d[i].muid) p += strlen(d[i].muid)+1;
}
o = malloc(n*sizeof(*d) + (uintptr)p);
memmove(o, d, n*sizeof(*d));
p = (char*)&o[n];
for(i=0; i<n; i++){
o[i].name = append(&p, d[i].name);
o[i].uid = append(&p, d[i].uid);
o[i].gid = append(&p, d[i].gid);
o[i].muid = append(&p, d[i].muid);
}
return o;
}
static int xdirread0(char **path, int (*namecmp)(char *, char *), Dir **d);
int
xdirread(char **path, int (*namecmp)(char *, char *), Dir **d)
{
Dir *t;
int n;
if((n = xdirread0(path, namecmp, &t)) > 0)
*d = xdirdup(t, n);
else
*d = nil;
return n;
}
static Dir*
xdirstat0(char **path, int (*namecmp)(char *, char *), char *err)
{
char *base, *name;
Dir *d, *t;
int n, i;
if(d = dirstat(*path)){
if(d->name[0] == 0)
d->name = "/";
return d;
}
if(!splitpath(*path, &base, &name))
return nil;
if((n = xdirread0(&base, namecmp, &t)) < 0)
goto out;
for(i=0; i<n; i++){
if(namecmp(t[i].name, name))
continue;
free(*path); *path = conspath(base, t[i].name);
d = xdirdup(&t[i], 1);
goto out;
}
werrstr("%s", err);
out:
free(base);
free(name);
return d;
}
Dir*
xdirstat(char **path, int (*namecmp)(char *, char *))
{
return xdirstat0(path, namecmp, "name not found");
}
typedef struct XDir XDir;
struct XDir
{
Qid qid;
char *path;
int ndir;
Dir *dir;
XDir *next;
};
static void
freexdir(XDir *x)
{
free(x->path);
free(x->dir);
free(x);
}
static int
qidcmp(Qid *q1, Qid *q2)
{
return (q1->type != q2->type) || (q1->path != q2->path) || (q1->vers != q2->vers);
}
static XDir *xdirlist;
static int xdircount;
static int
xdirread0(char **path, int (*namecmp)(char *, char *), Dir **d)
{
XDir *x, *p;
int fd, n;
Dir *t;
t = nil;
for(p = nil, x = xdirlist; x; p=x, x=x->next){
if(namecmp(x->path, *path))
continue;
if(x == xdirlist)
xdirlist = x->next;
else
p->next = x->next;
while(t = dirstat(x->path)){
if(qidcmp(&t->qid, &x->qid))
break;
free(t);
x->next = xdirlist;
xdirlist = x;
if(strcmp(x->path, *path)){
free(*path);
*path = strdup(x->path);
}
if(d) *d = x->dir;
return x->ndir;
}
xdircount--;
freexdir(x);
break;
}
if((fd = open(*path, OREAD)) < 0){
free(t);
if(t = xdirstat0(path, namecmp, "directory entry not found"))
fd = open(*path, OREAD);
} else if(t == nil)
t = dirfstat(fd);
n = -1;
if(fd < 0 || t == nil)
goto out;
if((t->qid.type & QTDIR) == 0){
werrstr("not a directory");
goto out;
}
if((n = dirreadall(fd, d)) < 0)
goto out;
if(xdircount >= 8){
xdircount--;
for(p = xdirlist, x = xdirlist->next; x->next; p = x, x = x->next)
;
p->next = nil;
freexdir(x);
}
x = mallocz(sizeof(*x), 1);
x->qid = t->qid;
x->path = strdup(*path);
x->ndir = n;
x->dir = *d;
x->next = xdirlist;
xdirlist = x;
xdircount++;
out:
if(fd >= 0)
close(fd);
free(t);
return n;
}
void
xdirflush(char *path, int (*namecmp)(char *, char *))
{
XDir **pp, **xx, *x;
char *d, *s;
int n;
n = strlen(path);
if(s = strrchr(path, '/'))
n = s - path;
d = smprint("%.*s", utfnlen(path, n), path);
s = malloc(++n);
for(pp = &xdirlist; x = *pp; pp = xx){
xx = &x->next;
snprint(s, n, "%s", x->path);
if(namecmp(d, s) == 0){
*pp = *xx; xx = pp;
xdircount--;
freexdir(x);
}
}
free(s);
free(d);
}