194 lines · 3.3 KB · C
RawDownload
#include <u.h>#define NOPLAN9DEFINES#include <libc.h>#include <sys/stat.h>#include <dirent.h> extern int _p9dir(struct stat*, struct stat*, char*, Dir*, char**, char*); #if defined(__linux__)static intmygetdents(int fd, struct dirent *buf, int n){	off_t off;	int nn; 	/* This doesn't match the man page, but it works in Debian with a 2.2 kernel */	off = p9seek(fd, 0, 1);	nn = getdirentries(fd, (void*)buf, n, &off);	return nn;}#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)static intmygetdents(int fd, struct dirent *buf, int n){	long off;	return getdirentries(fd, (void*)buf, n, &off);}#elif defined(__sun__) || defined(__NetBSD__)static intmygetdents(int fd, struct dirent *buf, int n){	return getdents(fd, (void*)buf, n);}#elif defined(__AIX__)static intmygetdents(int fd, struct dirent *buf, int n){	return getdirent(fd, (void*)buf, n);}#endif static intcountde(char *p, int n){	char *e;	int m;	struct dirent *de; 	e = p+n;	m = 0;	while(p < e){		de = (struct dirent*)p;		if(de->d_reclen <= 4+2+2+1 || p+de->d_reclen > e)			break;		if(de->d_name[0]=='.' && de->d_name[1]==0)			de->d_name[0] = 0;		else if(de->d_name[0]=='.' && de->d_name[1]=='.' && de->d_name[2]==0)			de->d_name[0] = 0;		m++;		p += de->d_reclen;	}	return m;} static intdirpackage(int fd, char *buf, int n, Dir **dp){	int oldwd;	char *p, *str, *estr;	int i, nstr, m;	struct dirent *de;	struct stat st, lst;	Dir *d; 	n = countde(buf, n);	if(n <= 0)		return n; 	if((oldwd = open(".", O_RDONLY)) < 0)		return -1;	if(fchdir(fd) < 0)		return -1;			p = buf;	nstr = 0; 	for(i=0; i<n; i++){		de = (struct dirent*)p;		memset(&lst, 0, sizeof lst);		if(de->d_name[0] == 0)			/* nothing */ {}		else if(lstat(de->d_name, &lst) < 0)			de->d_name[0] = 0;		else{			st = lst;			if(S_ISLNK(lst.st_mode))				stat(de->d_name, &st);			nstr += _p9dir(&lst, &st, de->d_name, nil, nil, nil);		}		p += de->d_reclen;	} 	d = malloc(sizeof(Dir)*n+nstr);	if(d == nil){		fchdir(oldwd);		close(oldwd);		return -1;	}	str = (char*)&d[n];	estr = str+nstr; 	p = buf;	m = 0;	for(i=0; i<n; i++){		de = (struct dirent*)p;		if(de->d_name[0] != 0 && lstat(de->d_name, &lst) >= 0){			st = lst;			if((lst.st_mode&S_IFMT) == S_IFLNK)				stat(de->d_name, &st);			_p9dir(&lst, &st, de->d_name, &d[m++], &str, estr);		}		p += de->d_reclen;	} 	fchdir(oldwd);	close(oldwd);	*dp = d;	return m;} longdirread(int fd, Dir **dp){	char *buf;	struct stat st;	int n; 	*dp = 0; 	if(fstat(fd, &st) < 0)		return -1; 	if(st.st_blksize < 8192)		st.st_blksize = 8192; 	buf = malloc(st.st_blksize);	if(buf == nil)		return -1; 	n = mygetdents(fd, (void*)buf, st.st_blksize);	if(n < 0){		free(buf);		return -1;	}	n = dirpackage(fd, buf, n, dp);	free(buf);	return n;}  longdirreadall(int fd, Dir **d){	uchar *buf, *nbuf;	long n, ts;	struct stat st; 	if(fstat(fd, &st) < 0)		return -1; 	if(st.st_blksize < 8192)		st.st_blksize = 8192; 	buf = nil;	ts = 0;	for(;;){		nbuf = realloc(buf, ts+st.st_blksize);		if(nbuf == nil){			free(buf);			return -1;		}		buf = nbuf;		n = mygetdents(fd, (void*)(buf+ts), st.st_blksize);		if(n <= 0)			break;		ts += n;	}	if(ts >= 0)		ts = dirpackage(fd, (char*)buf, ts, d);	free(buf);	if(ts == 0 && n < 0)		return -1;	return ts;}