1BSD/s6/cptree.c
#
#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);
}