1BSD/s6/cptree.c

Compare this file to the similar file:
Show the results in this format:

#
#define isdir(a) ((a.flags & 060000) == 040000)
#define isquot(a) ((a.flags & 060000) == 020000 && a.addr[0] == -1)
/*
 * cptree [ - ] [ -y ] [ -q ] source dest
 *
 * Author : Bill Joy (UCB) October 14, 1976
 */
char progname[] "cptree";
char usagestr[] "usage: %s [ - ] [ -y ] [ -q ] source dest\n";

struct dbuff {
	int	ino;
	char	name[14];
};
struct stbuff {
	int	dev;
	int	inumber;
	int	flags;
	char	nlinks;
	char	uid;
	char	gid;
	char	size0;
	int	size1;
	int	addr[8];
	int	actime[2];
	int	modtime[2];
};
struct linkf {
	int	*link;
	int	inumber;
	char	links;
	char	sofar;
	int	*next;
};
struct linkg {
	char	*sname;
	int	inumber;
};
struct {
	int	*link;
	char	*lname;
};
struct linkf *link0;
struct linkg *linkl;

char *memptr;
char *high;
char *sbrk(), *brk();

char *concat();
int status;
int noread;
int specfil;
int newmode;
int suser;

int terse;
int aok;
int verbose;
int noquot;

char **xargv;

main(argc,argv)
char *argv[];
{
	register char *argp;
	int *num;

	memptr = high = sbrk(0);
	suser = getuid() == 0;
	argv++;
	argc--;
	while (argc && *(argp = *argv) == '-')
	{
		if (!*++argp)
			terse++;
		else
		{
			do
			{
				switch(*argp++)
				{
				case 'y':
					aok++;
					break;
				case 'v':
					verbose++;
					break;
				case 'q':
					noquot++;
					break;
				default:
					goto usage;
				}
			} while(*argp);
		}
		argc--;
		argv++;
	}
	if (argc != 2)
usage:
	{
		printf(usagestr, progname);
		exit(1);
	}
	else
	{
		xargv = argv;
		ok(argv[0], argv[1]);
		cptree(argv[0], argv[1]);
	}
	exit(0);
}

owner(a)
struct stbuff *a;
{
	return(((a->gid & 0377) << 8) | (a->uid & 0377));
}

cptree(spth, dpth)
char *spth, *dpth;
{
	register struct linkg *lp;
	char nspth[120], ndpth[120];
	int curq, maxq;
	char qstr[8];
	struct dbuff sdbuff;
	struct stbuff source, dest;
	int sunit, i;

	if (stat(spth, &source) < 0)
		panic("\"%s\": cannot stat", spth);
	else if (stat(dpth, &dest) < 0)
		panic("\"%s\": cannot stat", dpth);
	else if (!isdir(source))
		panic("\"%s\": not a directory", spth);
	else if (!isdir(dest))
		panic("\"%s\": not a directory", dpth);
	else if ((sunit = open(spth, 0)) < 0)
		panic("\"%s\": cannot open directory", spth);
	else
	{
top:
		while ((i = read(sunit, &sdbuff, 16) == 16))
		{
			concat(nspth, spth, sdbuff.name);
			concat(ndpth, dpth, sdbuff.name);
			if (sdbuff.ino == 0)
				continue;
			else if (stat(nspth, &source) < 0)
				panic("\"%s\": cannot stat", nspth);
			else if (source.flags & 060000)
				if (isdir(source))
				{
					if (equal(sdbuff.name, "."))
						continue;
					else if (equal(sdbuff.name, ".."))
						continue;
					else if (source.inumber == 1)
						panic("source tree across devices detected at \"%s\" on pass 2", nspth);
					sys("mkdir", ndpth, 0);
					newmode = source.flags & 07777;
					if (chmod(ndpth, newmode))
						panic("can't chmod of \"%s\" to %o", ndpth, 0);
					if (suser)
						chown(ndpth, owner(&source));
					cptree(nspth, ndpth);
				}
				else if (isquot(source))
					if (noquot)
						continue;
					else if (suser)
					{
						curq = source.addr[1];
						maxq = source.addr[2];
						itoa(maxq, qstr);
						sys("quot", dpth, "0", qstr, 0);
						chown(ndpth, owner(&source));
					}
					else
						printf("\"%s/.q\" no can do\n", spth);
				else
					panic("special file \"%s\" detected on pass 2 only", nspth);
			else
			{
				if (source.nlinks > 1)
					for (lp = link0; lp < linkl; lp++)
						if (lp->inumber == source.inumber)
							if (lp->sname)
								if (link(lp->sname, ndpth) < 0)
									panic("can't link \"%s\" to \"%s\"", lp->sname, ndpth);
								else
									goto top;
							else
							{
								lp->sname = save(ndpth);
								break;
							}
				sys("cp", nspth, ndpth, 0);
				if (suser)
					chown(ndpth, owner(&source));
			}
		}
		if (i == -1)
			panic("error reading \"%s\":", spth);
		else if (i && i != 16)
			panic("bad directory structure: \"%s\"", spth);
	}
	close(sunit);
}


save(dpth)
char *dpth;
{
	register char *cp, *dp;
	char *rp;
	register size;

	size = 1;
	cp = dpth;
	while (*cp++)
		size++;
	rp = cp = alloc(size);
	dp = dpth;
	while (*cp++ = *dp++)
		continue;
	return(rp);
}

ok(spth, dpth)
char *spth, *dpth;
{
	struct stbuff source, dest;
	register struct linkf *lp;
	register struct linkg *bp, *cp;
	int head;

	if (stat(spth, &source) < 0)
		panic("\"%s\": cannot stat", spth);
	else if (stat(dpth, &dest) < 0)
		panic("\"%s\": cannot stat", dpth);
	else if (!isdir(source))
		panic("\"%s\": not a directory", spth);
	else if (!isdir(dest))
		panic("\"%s\": not a directory", dpth);
	else
	{
		getlinks(spth, source.dev == dest.dev ? dest.inumber : 0);
		head = 0;
		bp = link0;
		for (lp = link0; lp;)
			if (lp->links != lp->sofar)
			{
				if (!terse)
				{
					if (!head)
					{
						if (noread)
							putchar('\n');
						head++;
						printf("Inode  Links  Missing\n");
					}
					printf("%5d\t%3d\t%3d\n", lp->inumber, lp->links, lp->links - lp->sofar);
					for (cp = lp->link; cp; cp = cp->link)
						printf("\t%s\n", cp->lname);
				}
				goto saveit;
			}
			else if (lp->sofar != 1)
			{
saveit:
				cp = lp->next;
				bp->inumber = lp->inumber;
				bp->sname = 0;
				bp++;
				lp = cp;
			}
			else
				lp = lp->next;
		high = linkl = bp;
		if (noread)
			panic("abandoned because of insufficient access");
		else if (specfil)
			panic("abandoned because of special files");
		else if (head)
			cani();
	}
}

cani()
{
	register char c, ch;

	if (!aok && ttyn(0) != 'x')
	{
		printf("\nok ? ");
		ch = c = getchar();
		while (ch != '\n' && ch)
			ch = getchar();
		if (c != 'y')
			exit(1);
	}
}

getlinks(spth, dinode)
char *spth;
{
	char nspth[120];
	struct dbuff sdbuff;
	struct stbuff source;
	int sunit, i;

	if (stat(spth, &source) < 0)
		panic("\"%s\": cannot stat", spth);
	else if (source.inumber == dinode)
		panic("\"%s\" is a subdirectory of \"%s\" at \"%s\"", xargv[0], xargv[1], spth);
	else if (!isdir(source))
		panic("\"%s\": not a directory", spth);
	else if ((sunit = open(spth, 0)) < 0)
		panic("\"%s\": cannot open directory", spth);
	else
	{
		while ((i = read(sunit, &sdbuff, 16) == 16))
		{
			if (sdbuff.ino == 0)
				continue;
			else if (equal(sdbuff.name, "."))
				continue;
			else if (equal(sdbuff.name, ".."))
				continue;
			concat(nspth, spth, sdbuff.name);
			if (stat(nspth, &source) < 0)
				panic("\"%s\": cannot stat", nspth);
			else if (source.flags & 060000)
				if (isdir(source))
				{
					if (source.inumber == 1)
						panic("source tree extends across devices at \"%s\"", nspth);
					else
						getlinks(nspth, dinode);
				}
				else if (isquot(source))
					continue;
				else
				{
					printf("special file: \"%s\"\n", nspth);
					specfil++;
				}
			else if (access(nspth, 4) < 0)
			{
				printf("no read access: \"%s\"\n", nspth);
				noread++;
			}
			else if (source.nlinks > 1)
				enter(&source, nspth);
		}
		if (i == -1)
			panic("error reading \"%s\":", spth);
		else if (i && i != sizeof sdbuff)
			panic("bad directory structure: \"%s\"");
	}
	close(sunit);
}

enter(source, path)
struct stbuff *source;
char *path;
{
	register struct linkf *lp;
	register struct linkg *ip;
	register struct stbuff *src;

	src = source;
	for (lp = link0; lp; lp = lp->next)
		if (lp->inumber == src->inumber)
		{
			lp->sofar++;
			goto found;
		}
	lp = alloc(sizeof *lp);
	if (!link0)
		link0 = lp;
	else
		linkl->next = lp;
	linkl = lp;
	lp->inumber = src->inumber;
	lp->links = src->nlinks;
	lp->sofar = 1;
	lp->link = 0;
	lp->next = 0;
found:
	ip = alloc(sizeof *ip);
	ip->link = lp->link;
	ip->lname = save(path);
	lp->link = ip;
}


alloc(need)
{
	register have, cnt, *wp;

	need = (need+1) &~ 1;
	if ((have = high - memptr) < need)
	{
		if (sbrk(need-have) == -1)
			panic("ran out of memory");
		high = sbrk(0);
	}
	wp = memptr;
	cnt = need >> 1;
	do {
		*wp++ = 0;
	} while (--cnt);
	wp = memptr;
	memptr =+ need;
	return(wp);
}

char *concat(a, b, c)
char *a, *b, *c;
{
	register char *ra, *rb;
	register cnt;

	cnt = 14;
	rb = c;
	while (*rb++)
		if (!--cnt)
			break;
	cnt = 14 - cnt;
	rb = b;
	while (*rb++)
		cnt++;
	if (cnt >= 100)
		panic("path name too long: \"%s/%s\"", b, c);
	ra = a;
	rb = b;
	while (*ra++ = *rb++)
		continue;
	*(ra-1) = '/';
	rb = c;
	cnt = 14;
	while (*ra++ = *rb++)
		if (!--cnt)
		{
			*ra++ = 0;
			break;
		}
	return(a);
}

panic(a1, a2, a3, a4)
{
	if (a1)
	{
		printf("%s: ", progname);
		printf(a1, a2, a3, a4);
		putchar('\n');
	}
	exit(1);
}

equal(a, b)
char *a, *b;
{
	register char *ra, *rb;

	ra = a;
	rb = b;
	while (*ra == *rb++)
		if (*ra)
			ra++;
		else
			return(!*--rb);
	return(0);
}

char *itoastr;

itoa(aint, str)
char *str;
{
	itoastr = str;
	if (aint)
		itoa2(aint);
	else
		*itoastr++ = '0';
	*itoastr++ = 0;
}

itoa2(aint)
{
	if (aint)
	{
		itoa2(ldiv(0, aint, 10));
		*itoastr++ = '0' + lrem(0, aint, 10);
	}
}

sys(name)
char *name;
{
	int i, k;
	register char *argp, *rp;
	char routine[25];

	if ((i = fork()) < 0)
		panic("try again.");
	else if (i)
	{
		while ((k = wait(&status)) != i && k != -1)
			continue;
		if (k == -1)
		{
			printf("no children to wait for !!!\n");
			status = 0;
		}
		else if (status)
			panic("\"%s\" failed", name);
	}
	else
	{
		argp = "/usr/bin/";
		rp = routine;
		while(*rp++ = *argp++)
			continue;
		rp--;
		argp = name;
		while (*rp++ = *argp++)
			continue;
		execv(routine+4, &name);
		execv(routine, &name);
		panic("can't find \"%s\"", name);
	}
}

putchar(c)
{
	write(2, &c, 1);
}