|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 20097 (0x4e81)
Types: TextFile
Notes: UNIX file
Names: »tar.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »cmd/tar.c«
/*
* Tape archive
* tar [0-7bcflmrtuvwx]+ [blocks] [archive] pathname*
*/
#include <stdio.h>
#include <types.h>
#include <errno.h>
#include <canon.h>
#include <stat.h>
#include <dir.h>
#define S_PERM 07777 /* should be in stat.h */
#define MAXBLK 20
#define roundup(n, r) (((n)+(r)-1)/(r))
typedef struct dirhd_t {
dev_t t_dev;
ino_t t_ino;
unsigned short t_nlink,
t_mode,
t_uid,
t_gid;
size_t t_size;
time_t t_mtime;
char *t_name;
struct dirhd_t *t_cont[];
} dirhd_t;
typedef union tarhd_t {
struct {
char th_name[100],
th_mode[8],
th_uid[8],
th_gid[8],
th_size[12],
th_mtime[12],
th_check[8],
th_islink,
th_link[100],
th_pad[255];
};
char th_data[BUFSIZ];
} tarhd_t;
typedef unsigned short flag_t; /* fastest type for machine */
flag_t linkmsg = 0, /* message if not all links found */
modtime = 1, /* restore modtimes */
verbose = 0;
unixbug = 0; /* avoid bug in U**X tar */
FILE *whether = (FILE *)NULL, /* ask about each file */
*tarfile;
char tapedev[10] = '\0';
char *archive = &tapedev[0];
unsigned short blocking = 1; /* blocking factor */
time_t oldtime[2], /* for utime */
recently, /* set to 6 months ago for tv key */
time();
dirhd_t *newdirhd(),
*update(),
*research();
tarhd_t *readhdr(),
*readblk(),
*writeblk();
char *havelink();
long getoctl();
extern char *malloc();
extern char *ctime();
main(argc, argv)
int argc;
char *argv[];
{
char *key,
unit = '\0',
function = 0,
deffunc,
prefix[101] = '\0';
unsigned short arg = 2;
dirhd_t *args;
if (argc < 2) {
fprintf(stderr,
"Usage: %s [crtux][0-7bflmvw] [blocks] [archive] pathname*\n",
argv[0]);
exit(-1);
}
for (key = argv[1]; *key != '\0'; key++) switch (*key) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
unit = *key;
deffunc = 't';
continue;
case 'b':
if (argc <= arg)
fatal("missing blocking factor");
else if ((blocking = atoi(argv[arg++])) <= 0
|| blocking > MAXBLK)
fatal("illegal blocking factor");
deffunc = 'r';
continue;
case 'f':
if (argc <= arg)
fatal("missing archive name");
else
archive = argv[arg++];
deffunc = 't';
continue;
case 'l':
linkmsg = 1;
deffunc = 'r';
continue;
case 'm':
modtime = 0;
deffunc = 'x';
continue;
case 'v':
verbose = 1;
deffunc = 't';
continue;
case 'U':
unixbug = 1;
deffunc = 't';
continue;
case 'w':
whether = fopen("/dev/tty", "r+w");
deffunc = 'x';
continue;
case 'c':
case 'r':
case 't':
case 'u':
case 'x':
if (function)
fatal("keys c, r, t, u, x are mutually exclusive");
else
function = *key;
continue;
default:
fatal("illegal key `%c'", *key);
}
if (!function)
function = deffunc;
oldtime[0] = time((time_t *)0);
if (verbose && function=='t')
recently = oldtime[0] - 6*((time_t)30*24*60*60);
/* construct descriptors for args given */
args = (dirhd_t *) malloc(sizeof (dirhd_t)
+ (argc-arg)*sizeof (dirhd_t *));
if (args == NULL)
fatal("out of memory");
args->t_mode = S_IFDIR;
args->t_nlink = 0;
for (; arg < argc; arg++) {
dirhd_t *argp;
if ((argp = newdirhd(argv[arg], strlen(argv[arg]))) == NULL)
fprintf(stderr, "Tar: %s: out of memory\n", argv[arg]);
else
args->t_cont[args->t_nlink++] = argp;
}
/* open archive file */
if (*archive == '\0')
sprintf(archive, "/dev/%smt%c", blocking==1 ? "" : "r", unit);
if (function=='t' || function=='x')
if (strcmp(archive, "-")==0)
tarfile = stdin;
else
tarfile = fopen(archive, "r");
else if (function=='c')
if (strcmp(archive, "-")==0)
tarfile = stdout;
else
tarfile = fopen(archive, "w");
else
tarfile = fopen(archive, "r+w");
if (tarfile==NULL) {
exit(perror("Tar: %s", archive));
}
setbuf(tarfile, NULL);
/* perform required function */
switch (function) {
case 't':
table(args);
break;
case 'x':
extract(args);
break;
case 'r':
while (readblk() != NULL)
;
goto doappend;
case 'u':
if ((args = update(prefix, args, readhdr())) == NULL)
break;
case 'c':
doappend:
append(prefix, args);
flushtar();
}
exit(errno);
}
fatal(args)
char *args;
{
fprintf(stderr, "Tar: %r\n", &args);
exit(-1);
}
dirhd_t *
newdirhd(name, len)
register char *name;
unsigned short len;
{
register dirhd_t *dp;
if ((dp = (dirhd_t *) malloc(sizeof (dirhd_t))) != NULL
&& (dp->t_name = malloc(len+1)) == NULL) {
free(dp);
dp = NULL;
}
if (dp != NULL) {
dp->t_nlink = 0;
strncpy(dp->t_name, name, len);
dp->t_name[len] = '\0';
}
return (dp);
}
int
perror(args)
char *args;
{
register int err;
if ((err=errno) >= sys_nerr)
fprintf(stderr, "%r: Bad error number\n", &args);
else if (err)
fprintf(stderr, "%r: %s\n", &args, sys_errlist[err]);
return (err);
}
table(args)
register dirhd_t *args;
{
register tarhd_t *header;
for (; (header = readhdr()) != NULL; skipfile(header)) {
if (argcont(args, header->th_name, 0) < 0)
continue;
if (verbose) {
register unsigned short mode = getoct(header->th_mode);
unsigned short uid = getoct(header->th_uid),
gid = getoct(header->th_gid);
size_t size = getoctl(header->th_size);
time_t mtime = getoctl(header->th_mtime);
char *timestr = ctime(&mtime);
if (header->th_name[strnlen(header->th_name, 100)-1]
== '/')
putchar('d');
else
putchar('-');
printf("%c%c%c",
mode&S_IREAD ? 'r' : '-',
mode&S_IWRITE ? 'w' : '-',
mode&S_ISUID ? 's' :
mode&S_IEXEC ? 'x' : '-');
printf("%c%c%c",
mode&S_IREAD>>3 ? 'r' : '-',
mode&S_IWRITE>>3 ? 'w' : '-',
mode&S_ISGID ? 's' :
mode&S_IEXEC>>3 ? 'x' : '-');
printf("%c%c%c",
mode&S_IREAD>>6 ? 'r' : '-',
mode&S_IWRITE>>6 ? 'w' : '-',
mode&S_ISVTX ? 't' :
mode&S_IEXEC>>6 ? 'x' : '-');
printf("%3d %3d %6ld %.11s%.5s ",
gid,
uid,
size,
timestr,
mtime > recently ? timestr+11 : timestr+19);
}
printf("%.100s", header->th_name);
if (header->th_islink)
if (verbose)
printf("\n%33s link to %s\n", "",
header->th_link);
else
printf(" link to %s\n", header->th_link);
else
putchar('\n');
}
}
extract(args)
register dirhd_t *args;
{
register tarhd_t *header;
short skipping = 0;
while ((header = readhdr()) != NULL) {
register char name[101];
unsigned short namelen = strnlen(header->th_name, 100),
mode,
uid,
gid;
if (!skipping || contains(name, header->th_name) >= 0) {
strncpy(name, header->th_name, namelen);
name[namelen--] = '\0';
if ((skipping = argcont(args, name, 0)) >= 0)
skipping = disallow('x', name);
}
switch (skipping) {
case -1:
skipping = 0;
case 1:
skipfile(header);
continue;
}
if (verbose)
printf("x %s\n", name);
mode = getoct(header->th_mode);
uid = getoct(header->th_uid);
gid = getoct(header->th_gid);
oldtime[1] = getoctl(header->th_mtime);
if (name[namelen] == '/') {
name[namelen] = '\0';
makepath(name);
chmod(name, mode);
name[namelen] = '/';
} else if (header->th_islink == '\0') {
int fd = recreate(name, mode);
size_t size = getoctl(header->th_size);
for (; size > 0; size -= sizeof (tarhd_t)) {
header = readblk();
if (fd >= 0 && write(fd, header->th_data,
size > sizeof (tarhd_t)
? sizeof (tarhd_t)
: (int)size) <= 0)
perror("Tar: %s", name);
}
if (fd >= 0)
close(fd);
} else {
struct stat statbuf;
flag_t xlink = 1;
if (stat(header->th_link, &statbuf) < 0)
close(recreate(header->th_link, mode));
else if (havelink(statbuf.st_dev, statbuf.st_ino, 0)
!= NULL)
xlink = 0;
if (xlink)
fprintf(stderr, "Tar: Must extract %s\n",
header->th_link);
unlink(name);
mkparent(name);
if (link(header->th_link, name) < 0)
fprintf(stderr, "Tar: Can't link %s to %s\n",
name, header->th_link);
}
if (modtime)
utime(name, oldtime);
chown(name, uid, gid);
}
}
dirhd_t *
update(name, args, header)
char *name;
register dirhd_t *args;
tarhd_t *header;
{
unsigned short namelen = strlen(name);
flag_t donedir = 0;
if (header == NULL)
return (args);
if (args->t_nlink == 0 && (args = research(name, args)) == NULL)
return (NULL);
do switch (args->t_mode&S_IFMT) {
register short arg;
register dirhd_t *argp;
case S_IFREG:
if (args->t_mtime <= getoctl(header->th_mtime))
args->t_nlink = 0;
skipfile(header);
continue;
case S_IFDIR:
if (namelen != 0 && !donedir) {
name[namelen] = '/';
donedir++;
}
if ((arg = argcont(args, header->th_name+namelen+donedir, -1))
>= 0) {
strcpy(name+namelen+donedir,
(argp=args->t_cont[arg])->t_name);
if ((args->t_cont[arg] = update(name, argp, header))
== NULL)
args->t_cont[arg]
= args->t_cont[--args->t_nlink];
} else
skipfile(header);
name[namelen+donedir] = '\0';
} while ((header=readhdr()) != NULL
&& contains(name, header->th_name));
if (header != NULL)
ungetblk();
if (args->t_nlink == 0) {
if (namelen != 0)
free(args->t_name);
free((char *) args);
return (NULL);
} else
return (args);
}
append(name, args)
char *name;
register dirhd_t *args;
{
unsigned short namelen;
register unsigned short arg;
register dirhd_t *argp;
if ((namelen = strlen(name)) != 0 && disallow('a', name))
return;
if (args->t_nlink == 0 && (args = research(name, args)) == NULL)
return;
switch (args->t_mode&S_IFMT) {
case S_IFDIR:
if (namelen != 0) {
name[namelen++] = '/'; name[namelen] = '\0';
writehdr(name, args, "");
}
for (arg = 0; arg < args->t_nlink; arg++) {
strcpy(name+namelen, (argp=args->t_cont[arg])->t_name);
append(name, argp);
}
break;
case S_IFREG:
if (args->t_nlink != 1) {
char *link;
if ((link=havelink(args->t_dev,
args->t_ino, 1))!=NULL) {
writehdr(name, args, link);
break;
} else {
filelink(args->t_dev,
args->t_ino,
args->t_nlink,
name);
}
}
writehdr(name, args, "");
writefil(name, args->t_size);
}
if (namelen != 0)
free(args->t_name);
free((char *) args);
}
dirhd_t *
research(name, args)
char *name;
register dirhd_t *args;
{
unsigned short namelen;
register short nfile;
int fd;
struct stat statbuf;
if ((namelen = strlen(name)) == 0)
name = ".";
if (stat(name, &statbuf) < 0) {
perror("Tar: %s", name);
return (args);
}
args->t_dev = statbuf.st_dev;
args->t_ino = statbuf.st_ino;
args->t_nlink = statbuf.st_nlink;
args->t_uid = statbuf.st_uid;
args->t_gid = statbuf.st_gid;
args->t_mtime = statbuf.st_mtime;
switch ((args->t_mode = statbuf.st_mode)&S_IFMT) {
case S_IFREG:
args->t_size = statbuf.st_size;
return (args);
default:
args->t_size = 0;
return (args);
case S_IFDIR:
args->t_size = 0;
}
if ((nfile = (short) (statbuf.st_size/sizeof (struct direct))) > 2) {
args = (dirhd_t *) realloc((char *)args,
sizeof (dirhd_t) + (nfile-2)*sizeof (dirhd_t *));
if (args == NULL) {
fprintf(stderr, "Tar: %s: out of memory\n", name);
return (NULL);
}
}
args->t_nlink = 0;
if ((fd = open(name, 0)) < 0) {
perror("Tar: %s", name);
return (args);
}
for (; nfile > 0; --nfile) {
struct direct dir_ent;
unsigned short arglen;
dirhd_t *argp;
switch (read(fd, (char *)&dir_ent, sizeof (struct direct))) {
case -1:
perror("Tar: %s", name);
case 0:
break;
default:
if (dir_ent.d_ino == 0
|| strcmp(dir_ent.d_name, ".") == 0
|| strcmp(dir_ent.d_name, "..") == 0)
continue;
else if ((arglen=strnlen(dir_ent.d_name, DIRSIZ))
+ namelen > 99)
fprintf(stderr,
"Tar: %s/%.*s: name too long\n",
name, DIRSIZ, dir_ent.d_name);
else if ((argp = newdirhd(dir_ent.d_name, arglen))
== NULL)
fprintf(stderr,
"Tar: %s/%.*s: out of memory\n",
name, DIRSIZ, dir_ent.d_name);
else
args->t_cont[args->t_nlink++] = argp;
continue;
}
break;
}
close(fd);
return (args);
}
int
argcont(args, name, ret)
register dirhd_t *args;
char *name;
int ret;
{
register unsigned short arg;
if (args->t_nlink == 0)
return (ret);
else for (arg = 0; arg < args->t_nlink; arg++)
if (contains(args->t_cont[arg]->t_name, name))
return (arg);
return (-1);
}
int
strnlen(str, maxlen)
register char *str;
register unsigned short maxlen;
{
register unsigned short len = 0;
while (maxlen && *str++ != '\0') {
--maxlen; ++len;
}
return (len);
}
/*
* Create file system paths
*/
int
makepath(pathname)
char *pathname;
{
struct stat statbuf;
register int err;
if (stat(pathname, &statbuf) == 0)
if ((statbuf.st_mode&S_IFMT) == S_IFDIR)
return (0);
else
errno = ENOTDIR;
else if (errno == ENOENT)
return ((err=mkparent(pathname)) != 0 ? err : mkdir(pathname));
return (perror("Tar: %s", pathname));
}
int
mkparent(pathname)
register char *pathname;
{
register char *pathend = &pathname[strlen(pathname)];
while (pathend > pathname && *--pathend != '/')
;
if (pathend > pathname) {
*pathend = '\0';
errno = makepath(pathname);
*pathend = '/';
} else
errno = 0;
return (errno);
}
mkdir(pathname)
char *pathname;
{
int status;
switch(fork()) {
case -1:
break;
case 0:
close(2);
execl("/bin/mkdir", "mkdir", pathname, NULL);
exit(errno);
default:
wait(&status);
errno = status>>8;
}
return (perror("Tar: %s", pathname));
}
int
recreate(pathname, mode)
char *pathname;
unsigned short mode;
{
int fd;
if ((fd = create(pathname, mode)) < 0
&& (errno != ENOENT
|| mkparent(pathname) == 0 && (fd = create(pathname, mode)) < 0))
perror("Tar: %s", pathname);
return (fd);
}
int
create(pathname, mode)
char *pathname;
unsigned short mode;
{
int fd;
unlink(pathname);
if ((fd = creat(pathname, mode)) >= 0) {
struct stat statbuf;
fstat(fd, &statbuf);
filelink(statbuf.st_dev, statbuf.st_ino,
statbuf.st_nlink, pathname);
}
return (fd);
}
/*
* Ask about current action
*/
int
disallow(function, pathname)
char function,
*pathname;
{
while (whether) {
register int c1,
c;
fprintf(whether, "%c %s? ", function, pathname);
for (c1=c=getc(whether); c!=EOF && c!='\n'; c=getc(whether))
;
switch (c1) {
case EOF:
case 'x':
if (function == 'a')
flushtar();
exit(errno);
case '\n':
case 'n':
case 'N':
return (1);
case 'y':
case 'Y':
return (0);
}
}
return (0);
}
/*
* High level I/O routines
*/
tarhd_t *
readhdr()
{
register tarhd_t *header;
while ((header = readblk()) != NULL) {
if (header->th_name[0] == '\0')
if (unixbug) {
ungetblk();
return (NULL);
} else
continue;
else {
int check = getoct(header->th_check);
strncpy(header->th_check, " ",
sizeof(header->th_check));
if (checksum(header->th_data, sizeof(tarhd_t)) != check)
fprintf(stderr, "Tar: %.100s: bad checksum\n",
header->th_name);
else
break;
}
}
return (header);
}
skipfile(header)
tarhd_t *header;
{
register size_t size;
if (header->th_islink)
return;
for (size = getoctl(header->th_size);
size > 0 && readblk() != NULL;
size -= sizeof (tarhd_t))
;
}
writehdr(name, args, link)
char *name;
register dirhd_t *args;
char *link;
{
register tarhd_t *header = writeblk();
if (verbose)
fprintf(stderr, "a %s", name);
strncpy(header->th_name, name, sizeof(header->th_name));
putoct(header->th_mode, args->t_mode&S_PERM);
putoct(header->th_uid, args->t_uid);
putoct(header->th_gid, args->t_gid);
putoctl(header->th_size, args->t_size);
putoctl(header->th_mtime, args->t_mtime);
strncpy(header->th_check, " ", sizeof(header->th_check));
if (*link == '\0') {
header->th_islink = 0;
if (verbose)
if (args->t_size == 0)
putc('\n', stderr);
else
fprintf(stderr, " %ld block%s\n",
roundup(args->t_size, BUFSIZ),
args->t_size <= BUFSIZ ? "" : "s");
} else {
header->th_islink = '1';
if (verbose)
fprintf(stderr, " link to %s\n", link);
}
strncpy(header->th_link, link, sizeof(header->th_link));
strncpy(header->th_pad, "", sizeof(header->th_pad));
putoct(header->th_check, checksum(header->th_data, sizeof(tarhd_t)));
}
writefil(name, size)
char *name;
register size_t size;
{
int fd;
register tarhd_t *header;
if ((fd = open(name, 0)) < 0) {
perror("Tar: %s", name);
}
for (; size > 0; size -= sizeof (tarhd_t)) {
header = writeblk();
if (fd >= 0)
read(fd, header->th_data, sizeof (tarhd_t));
else
strncpy(header->th_data, "", sizeof (tarhd_t));
}
if (fd >= 0)
close(fd);
}
/*
* Tape I/O, with record blocking if specified
*/
tarhd_t buffer[MAXBLK],
*current = &buffer[0];
tarhd_t *
readblk()
{
static unsigned short blocks = 0;
if (feof(tarfile))
return (NULL);
if (current == &buffer[blocks]) {
current = &buffer[0];
blocks = fread((char *) buffer,
sizeof (tarhd_t), MAXBLK, tarfile);
if (ferror(tarfile)) {
exit(perror("Tar: %s", archive));
} else if (feof(tarfile)) {
return (NULL);
}
}
return (current++);
}
ungetblk()
{
--current;
}
tarhd_t *
writeblk()
{
if (current == &buffer[blocking]) {
current = &buffer[0];
fwrite((char *)buffer, sizeof (tarhd_t), blocking, tarfile);
if (ferror(tarfile)) {
exit(perror("Tar: %s", archive));
}
}
return (current++);
}
flushtar()
{
register tarhd_t *header;
while ((header=writeblk()) != &buffer[0])
strncpy(header->th_data, "", sizeof (tarhd_t));
if (linkmsg)
misslink();
}
/*
* Keep track of files by ino in hash table
*/
typedef struct link_t {
dev_t t_dev;
ino_t t_ino;
unsigned short t_nlink;
struct link_t *t_next;
char t_link[];
} link_t;
#define NHASH 64
link_t *linklist[NHASH];
filelink(dev, ino, nlink, link)
dev_t dev;
ino_t ino;
unsigned short nlink;
char *link;
{
unsigned short id = ino%NHASH;
register link_t *lp;
lp = (link_t *) malloc(sizeof (link_t) + strlen(link) + 1);
if (lp == NULL)
fprintf(stderr, "Tar: %s: note: link info lost\n", link);
else {
lp->t_next = linklist[id];
linklist[id] = lp;
lp->t_dev = dev;
lp->t_ino = ino;
lp->t_nlink = nlink;
strcpy(lp->t_link, link);
}
}
char *
havelink(dev, ino, flag)
dev_t dev;
ino_t ino;
flag_t flag;
{
register link_t *lp;
for (lp = linklist[ino%NHASH]; lp != NULL; lp = lp->t_next) {
if (lp->t_ino == ino && lp->t_dev == dev) {
if (flag)
--lp->t_nlink;
return (lp->t_link);
}
}
return (NULL);
}
misslink()
{
unsigned short ino;
for (ino = 0; ino < NHASH; ino++) {
register link_t *lp;
for (lp = linklist[ino]; lp != NULL; lp=lp->t_next) {
register short nlink;
if (nlink = lp->t_nlink - 1)
fprintf(stderr,
"Tar: missed %d link%s to %s\n",
nlink, nlink==1 ? "" : "s",
lp->t_link);
}
}
}
/*
* Read unsigned octal numbers
*/
int
getoct(cp)
register char *cp;
{
register int val = 0;
register char c;
while (*cp == ' ')
cp++;
while ((c = *cp++ - '0') >= 0 && c <= 7)
val = val<<3 | c;
return (val);
}
long
getoctl(cp)
register char *cp;
{
register long val = 0;
register char c;
while (*cp==' ')
cp++;
while ((c = *cp++ - '0') >= 0 && c <= 7)
val = val<<3 | c;
return (val);
}
/*
* Write unsigned octal numbers
* in fixed format
*/
putoct(str, val)
char *str;
unsigned short val;
{
register char *cp;
*(cp = &str[7]) = '\0';
*--cp = ' ';
*--cp = (val&07) + '0';
while (cp != str)
*--cp = (val>>=3) ? (val&07) + '0' : ' ';
}
putoctl(str, val)
char *str;
register unsigned long val;
{
register char *cp;
*(cp = &str[11]) = ' ';
*--cp = (val&07) + '0';
while (cp != str)
*--cp = (val>>=3) ? (val&07) + '0' : ' ';
}
/*
* Compute checksum of string
*/
int
checksum(str, len)
register char *str;
register int len;
{
register int check = 0;
if (len) do
check += *str++;
while (--len);
return (check);
}
/*
* Return -1 if dname is a directory prefix of fname
* 0 if no match
* 1 if complete match
*/
int
contains(dname, fname)
register char *dname,
*fname;
{
if (*dname=='\0')
return (-1);
while (*dname!='\0')
if (*dname++ != *fname++)
return (0);
if (*fname=='\0')
return (1);
else if (*fname=='/' || *--fname=='/')
return (-1);
else
return (0);
}