Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

v12i069: Public-domain TAR, Part02/03

4 views
Skip to first unread message

Rich Salz

unread,
Nov 29, 1987, 7:00:19 PM11/29/87
to
Submitted-by: John Gilmore <hoptoad!g...@UUNET.UU.NET>
Posting-number: Volume 12, Issue 69
Archive-name: pdtar/part02


: To unbundle, sh this file
echo tar.c
cat >tar.c <<'@@@ Fin de tar.c'
/*
* A public domain tar(1) program.
*
* Written by John Gilmore, ihnp4!hoptoad!gnu, starting 25 Aug 85.
*
* @(#)tar.c 1.34 11/6/87 Public Domain - gnu
*/

#include <stdio.h>
#include <sys/types.h> /* Needed for typedefs in tar.h */

extern char *malloc();
extern char *getenv();
extern char *strncpy();
extern char *optarg; /* Pointer to argument */
extern int optind; /* Global argv index from getopt */

/*
* The following causes "tar.h" to produce definitions of all the
* global variables, rather than just "extern" declarations of them.
*/
#define TAR_EXTERN /**/
#include "tar.h"

/*
* We should use a conversion routine that does reasonable error
* checking -- atoi doesn't. For now, punt. FIXME.
*/
#define intconv atoi
extern int getoldopt();
extern void read_and();
extern void list_archive();
extern void extract_archive();
extern void diff_archive();
extern void create_archive();

static FILE *namef; /* File to read names from */
static char **n_argv; /* Argv used by name routines */
static int n_argc; /* Argc used by name routines */
/* They also use "optind" from getopt(). */

void describe();


/*
* Main routine for tar.
*/
main(argc, argv)
int argc;
char **argv;
{

/* Uncomment this message in particularly buggy versions...
fprintf(stderr,
"tar: You are running an experimental PD tar, maybe use /bin/tar.\n");
*/

tar = "tar"; /* Set program name */

options(argc, argv);

name_init(argc, argv);

if (f_create) {
if (f_extract || f_list || f_diff) goto dupflags;
create_archive();
} else if (f_extract) {
if (f_list || f_diff) goto dupflags;
extr_init();
read_and(extract_archive);
} else if (f_list) {
if (f_diff) goto dupflags;
read_and(list_archive);
} else if (f_diff) {
diff_init();
read_and(diff_archive);
} else {
dupflags:
fprintf (stderr,
"tar: you must specify exactly one of the c, t, x, or d options\n");
describe();
exit(EX_ARGSBAD);
}
exit(0);
/* NOTREACHED */
}


/*
* Parse the options for tar.
*/
int
options(argc, argv)
int argc;
char **argv;
{
register int c; /* Option letter */

/* Set default option values */
blocking = DEFBLOCKING; /* From Makefile */
ar_file = getenv("TAPE"); /* From environment, or */
if (ar_file == 0)
ar_file = DEF_AR_FILE; /* From Makefile */

/* Parse options */
while ((c = getoldopt(argc, argv, "b:BcdDf:hiklmopRstT:vxzZ")
) != EOF) {
switch (c) {

case 'b':
blocking = intconv(optarg);
break;

case 'B':
f_reblock++; /* For reading 4.2BSD pipes */
break;

case 'c':
f_create++;
break;

case 'd':
f_diff++; /* Find difference tape/disk */
break;

case 'D':
f_dironly++; /* Dump dir, not contents */
break;

case 'f':
ar_file = optarg;
break;

case 'h':
f_follow_links++; /* follow symbolic links */
break;

case 'i':
f_ignorez++; /* Ignore zero records (eofs) */
/*
* This can't be the default, because Unix tar
* writes two records of zeros, then pads out the
* block with garbage.
*/
break;

case 'k': /* Don't overwrite files */
#ifdef NO_OPEN3
fprintf(stderr,
"tar: can't do -k option on this system\n");
exit(EX_ARGSBAD);
#else
f_keep++;
#endif
break;

case 'l':
f_local_filesys++;
break;

case 'm':
f_modified++;
break;

case 'o': /* Generate old archive */
f_oldarch++;
break;

case 'p':
f_use_protection++;
break;

case 'R':
f_sayblock++; /* Print block #s for debug */
break; /* of bad tar archives */

case 's':
f_sorted_names++; /* Names to extr are sorted */
break;

case 't':
f_list++;
f_verbose++; /* "t" output == "cv" or "xv" */
break;

case 'T':
name_file = optarg;
f_namefile++;
break;

case 'v':
f_verbose++;
break;

case 'x':
f_extract++;
break;

case 'z': /* Easy to type */
case 'Z': /* Like the filename extension .Z */
f_compress++;
break;

case '?':
describe();
exit(EX_ARGSBAD);

}
}

blocksize = blocking * RECORDSIZE;
}


/*
* Print as much help as the user's gonna get.
*
* We have to sprinkle in the KLUDGE lines because too many compilers
* cannot handle character strings longer than about 512 bytes. Yuk!
* In particular, MSDOS MSC 4.0 (and 5.0) and PDP-11 V7 Unix have this
* problem.
*/
void
describe()
{

fputs("\
tar: valid options:\n\
-b N blocking factor N (block size = Nx512 bytes)\n\
-B reblock as we read (for reading 4.2BSD pipes)\n\
-c create an archive\n\
-d find differences between archive and file system\n\
-D don't dump the contents of directories, just the directory\n\
", stderr); /* KLUDGE */ fputs("\
-f F read/write archive from file or device F (or hostname:/ForD)\n\
-h don't dump symbolic links; dump the files they point to\n\
-i ignore blocks of zeros in the archive, which normally mean EOF\n\
-k keep existing files, don't overwrite them from the archive\n\
-l stay in the local file system (like dump(8)) when creating an archive\n\
", stderr); /* KLUDGE */ fputs("\
-m don't extract file modified time\n\
-o write an old V7 format archive, rather than ANSI [draft 6] format\n\
-p do extract all protection information\n\
-R dump record number within archive with each message\n\
-s list of names to extract is sorted to match the archive\n\
-t list a table of contents of an archive\n\
", stderr); /* KLUDGE */ fputs("\
-T F get names to extract or create from file F\n\
-v verbosely list what files we process\n\
-x extract files from an archive\n\
-z or Z run the archive through compress(1)\n\
", stderr);
}


/*
* Set up to gather file names for tar.
*
* They can either come from stdin or from argv.
*/
name_init(argc, argv)
int argc;
char **argv;
{

if (f_namefile) {
if (optind < argc) {
fprintf(stderr, "tar: too many args with -T option\n");
exit(EX_ARGSBAD);
}
if (!strcmp(name_file, "-")) {
namef = stdin;
} else {
namef = fopen(name_file, "r");
if (namef == NULL) {
fprintf(stderr, "tar: ");
perror(name_file);
exit(EX_BADFILE);
}
}
} else {
/* Get file names from argv, after options. */
n_argc = argc;
n_argv = argv;
}
}

/*
* Get the next name from argv or the name file.
*
* Result is in static storage and can't be relied upon across two calls.
*/
char *
name_next()
{
static char buffer[NAMSIZ+2]; /* Holding pattern */
register char *p;
register char *q;

if (namef == NULL) {
/* Names come from argv, after options */
if (optind < n_argc)
return n_argv[optind++];
return (char *)NULL;
}
for (;;) {
p = fgets(buffer, NAMSIZ+1 /*nl*/, namef);
if (p == NULL) return p; /* End of file */
q = p+strlen(p)-1; /* Find the newline */
if (q <= p) continue; /* Ignore empty lines */
*q-- = '\0'; /* Zap the newline */
while (q > p && *q == '/') *q-- = '\0'; /* Zap trailing /s */
return p;
}
/* NOTREACHED */
}


/*
* Close the name file, if any.
*/
name_close()
{

if (namef != NULL && namef != stdin) fclose(namef);
}


/*
* Gather names in a list for scanning.
* Could hash them later if we really care.
*
* If the names are already sorted to match the archive, we just
* read them one by one. name_gather reads the first one, and it
* is called by name_match as appropriate to read the next ones.
* At EOF, the last name read is just left in the buffer.
* This option lets users of small machines extract an arbitrary
* number of files by doing "tar t" and editing down the list of files.
*/
name_gather()
{
register char *p;
static struct name namebuf[1]; /* One-name buffer */

if (f_sorted_names) {
p = name_next();
if (p) {
namebuf[0].length = strlen(p);
if (namebuf[0].length >= sizeof namebuf[0].name) {
fprintf(stderr, "Argument name too long: %s\n",
p);
namebuf[0].length = (sizeof namebuf[0].name) - 1;
}
strncpy(namebuf[0].name, p, namebuf[0].length);
namebuf[0].name[ namebuf[0].length ] = 0;
namebuf[0].next = (struct name *)NULL;
namebuf[0].found = 0;
namelist = namebuf;
namelast = namelist;
}
return;
}

/* Non sorted names -- read them all in */
while (NULL != (p = name_next())) {
addname(p);
}
}

/*
* Add a name to the namelist.
*/
addname(name)
char *name; /* pointer to name */
{
register int i; /* Length of string */
register struct name *p; /* Current struct pointer */

i = strlen(name);
/*NOSTRICT*/
p = (struct name *)
malloc((unsigned)(i + sizeof(struct name) - NAMSIZ));
if (!p) {
fprintf(stderr,"tar: cannot allocate mem for namelist entry\n");
exit(EX_SYSTEM);
}
p->next = (struct name *)NULL;
p->length = i;
strncpy(p->name, name, i);
p->name[i] = '\0'; /* Null term */
p->found = 0;
p->regexp = 0; /* Assume not a regular expression */
p->firstch = 1; /* Assume first char is literal */
if (index(name, '*') || index(name, '[') || index(name, '?')) {
p->regexp = 1; /* No, it's a regexp */
if (name[0] == '*' || name[0] == '[' || name[0] == '?')
p->firstch = 0; /* Not even 1st char literal */
}

if (namelast) namelast->next = p;
namelast = p;
if (!namelist) namelist = p;
}


/*
* Match a name from an archive, p, with a name from the namelist.
*/
name_match(p)
register char *p;
{
register struct name *nlp;
register int len;

again:
if (0 == (nlp = namelist)) /* Empty namelist is easy */
return 1;
len = strlen(p);
for (; nlp != 0; nlp = nlp->next) {
/* If first chars don't match, quick skip */
if (nlp->firstch && nlp->name[0] != p[0])
continue;

/* Regular expressions */
if (nlp->regexp) {
if (wildmat(p, nlp->name)) {
nlp->found = 1; /* Remember it matched */
return 1; /* We got a match */
}
continue;
}

/* Plain Old Strings */
if (nlp->length <= len /* Archive len >= specified */
&& (p[nlp->length] == '\0' || p[nlp->length] == '/')
/* Full match on file/dirname */
&& strncmp(p, nlp->name, nlp->length) == 0) /* Name compare */
{
nlp->found = 1; /* Remember it matched */
return 1; /* We got a match */
}
}

/*
* Filename from archive not found in namelist.
* If we have the whole namelist here, just return 0.
* Otherwise, read the next name in and compare it.
* If this was the last name, namelist->found will remain on.
* If not, we loop to compare the newly read name.
*/
if (f_sorted_names && namelist->found) {
name_gather(); /* Read one more */
if (!namelist->found) goto again;
}
return 0;
}


/*
* Print the names of things in the namelist that were not matched.
*/
names_notfound()
{
register struct name *nlp;
register char *p;

for (nlp = namelist; nlp != 0; nlp = nlp->next) {
if (!nlp->found) {
fprintf(stderr, "tar: %s not found in archive\n",
nlp->name);
}
/*
* We could free() the list, but the process is about
* to die anyway, so save some CPU time. Amigas and
* other similarly broken software will need to waste
* the time, though.
*/
#ifndef unix
if (!f_sorted_names)
free(nlp);
#endif
}
namelist = (struct name *)NULL;
namelast = (struct name *)NULL;

if (f_sorted_names) {
while (0 != (p = name_next()))
fprintf(stderr, "tar: %s not found in archive\n", p);
}
}
@@@ Fin de tar.c
echo create.c
cat >create.c <<'@@@ Fin de create.c'
/*
* Create a tar archive.
*
* Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
*
* @(#)create.c 1.36 11/6/87 Public Domain - gnu
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>

#ifndef V7
#include <fcntl.h>
#endif

#ifndef MSDOS
#include <pwd.h>
#include <grp.h>
#endif

#ifdef BSD42
#include <sys/dir.h>
#else
#ifdef MSDOS
#include <sys/dir.h>
#else
/*
* FIXME: On other systems there is no standard place for the header file
* for the portable directory access routines. Change the #include line
* below to bring it in from wherever it is.
*/
#include "ndir.h"
#endif
#endif

#ifdef USG
#include <sys/sysmacros.h> /* major() and minor() defined here */
#endif

/*
* V7 doesn't have a #define for this.
*/
#ifndef O_RDONLY
#define O_RDONLY 0
#endif

/*
* Most people don't have a #define for this.
*/
#ifndef O_BINARY
#define O_BINARY 0
#endif

#include "tar.h"
#include "port.h"

extern union record *head; /* Points to current tape header */
extern struct stat hstat; /* Stat struct corresponding */
extern int head_standard; /* Tape header is in ANSI format */

/*
* If there are no symbolic links, there is no lstat(). Use stat().
*/
#ifndef S_IFLNK
#define lstat stat
#endif

extern char *malloc();
extern char *strcpy();
extern char *strncpy();
extern void bzero();
extern void bcopy();
extern int errno;

extern void print_header();

union record *start_header();
void finish_header();
void finduname();
void findgname();
char *name_next();
void to_oct();
void dump_file();

static nolinks; /* Gets set if we run out of RAM */

void
create_archive()
{
register char *p;

open_archive(0); /* Open for writing */

while (p = name_next()) {
dump_file(p, -1);
}

write_eot();
close_archive();
name_close();
}

/*
* Dump a single file. If it's a directory, recurse.
* Result is 1 for success, 0 for failure.
* Sets global "hstat" to stat() output for this file.
*/
void
dump_file(p, curdev)
char *p; /* File name to dump */
int curdev; /* Device our parent dir was on */
{
union record *header;
char type;

/*
* Use stat if following (rather than dumping) 4.2BSD's
* symbolic links. Otherwise, use lstat (which, on non-4.2
* systems, is #define'd to stat anyway.
*/
if (0 != f_follow_links? stat(p, &hstat): lstat(p, &hstat))
{
badperror:
perror(p);
badfile:
errors++;
return;
}

/*
* See if we are crossing from one file system to another,
* and avoid doing so if the user only wants to dump one file system.
*/
if (f_local_filesys && curdev >= 0 && curdev != hstat.st_dev) {
annorec(stderr, tar);
fprintf(stderr,
"%s: is on a different filesystem; not dumped\n",
p);
return;
}

/*
* Check for multiple links.
*
* We maintain a list of all such files that we've written so
* far. Any time we see another, we check the list and
* avoid dumping the data again if we've done it once already.
*/
if (hstat.st_nlink > 1) switch (hstat.st_mode & S_IFMT) {
register struct link *lp;

case S_IFREG: /* Regular file */
#ifdef S_IFCTG
case S_IFCTG: /* Contigous file */
#endif
#ifdef S_IFCHR
case S_IFCHR: /* Character special file */
#endif

#ifdef S_IFBLK
case S_IFBLK: /* Block special file */
#endif

#ifdef S_IFIFO
case S_IFIFO: /* Fifo special file */
#endif

/* First quick and dirty. Hashing, etc later FIXME */
for (lp = linklist; lp; lp = lp->next) {
if (lp->ino == hstat.st_ino &&
lp->dev == hstat.st_dev) {
/* We found a link. */
hstat.st_size = 0;
header = start_header(p, &hstat);
if (header == NULL) goto badfile;
strcpy(header->header.linkname,
lp->name);
header->header.linkflag = LF_LINK;
finish_header(header);
/* FIXME: Maybe remove from list after all links found? */
return; /* We dumped it */
}
}

/* Not found. Add it to the list of possible links. */
lp = (struct link *) malloc( (unsigned)
(strlen(p) + sizeof(struct link) - NAMSIZ));
if (!lp) {
if (!nolinks) {
fprintf(stderr,
"tar: no memory for links, they will be dumped as separate files\n");
nolinks++;
}
}
lp->ino = hstat.st_ino;
lp->dev = hstat.st_dev;
strcpy(lp->name, p);
lp->next = linklist;
linklist = lp;
}

/*
* This is not a link to a previously dumped file, so dump it.
*/
switch (hstat.st_mode & S_IFMT) {

case S_IFREG: /* Regular file */
#ifdef S_IFCTG
case S_IFCTG: /* Contigous file */
#endif
{
int f; /* File descriptor */
int bufsize, count;
register long sizeleft;
register union record *start;

sizeleft = hstat.st_size;
/* Don't bother opening empty, world readable files. */
if (sizeleft > 0 || 0444 != (0444 & hstat.st_mode)) {
f = open(p, O_RDONLY|O_BINARY);
if (f < 0) goto badperror;
} else {
f = -1;
}
header = start_header(p, &hstat);
if (header == NULL) goto badfile;
#ifdef S_IFCTG
/* Mark contiguous files, if we support them */
if (f_standard && (hstat.st_mode & S_IFMT) == S_IFCTG) {
header->header.linkflag = LF_CONTIG;
}
#endif
finish_header(header);
while (sizeleft > 0) {
start = findrec();
bufsize = endofrecs()->charptr - start->charptr;
if (sizeleft < bufsize) {
/* Last read -- zero out area beyond */
bufsize = (int)sizeleft;
count = bufsize % RECORDSIZE;
if (count)
bzero(start->charptr + sizeleft,
RECORDSIZE - count);
}
count = read(f, start->charptr, bufsize);
if (count < 0) {
annorec(stderr, tar);
fprintf(stderr,
"read error at byte %ld, reading %d bytes, in file ",
hstat.st_size - sizeleft,
bufsize);
perror(p); /* FIXME */
goto padit;
}
sizeleft -= count;
/* This is nonportable (the type of userec's arg). */
userec(start+(count-1)/RECORDSIZE);
if (count == bufsize) continue;
annorec(stderr, tar);
fprintf(stderr,
"%s: file shrunk by %d bytes, padding with zeros.\n",
p, sizeleft);
goto padit; /* Short read */
}
if (f >= 0)
(void)close(f);

break;

/*
* File shrunk or gave error, pad out tape to match
* the size we specified in the header.
*/
padit:
abort();
}

#ifdef S_IFLNK
case S_IFLNK: /* Symbolic link */
{
int size;

hstat.st_size = 0; /* Force 0 size on symlink */
header = start_header(p, &hstat);
if (header == NULL) goto badfile;
size = readlink(p, header->header.linkname, NAMSIZ);
if (size < 0) goto badperror;
if (size == NAMSIZ) {
annorec(stderr, tar);
fprintf(stderr,
"%s: symbolic link too long\n", p);
break;
}
header->header.linkname[size] = '\0';
header->header.linkflag = LF_SYMLINK;
finish_header(header); /* Nothing more to do to it */
}
break;
#endif

case S_IFDIR: /* Directory */
{
register DIR *dirp;
register struct direct *d;
char namebuf[NAMSIZ+2];
register int len;
int our_device = hstat.st_dev;

/* Build new prototype name */
strncpy(namebuf, p, sizeof (namebuf));
len = strlen(namebuf);
while (len >= 1 && '/' == namebuf[len-1])
len--; /* Delete trailing slashes */
namebuf[len++] = '/'; /* Now add exactly one back */
namebuf[len] = '\0'; /* Make sure null-terminated */

/*
* Output directory header record with permissions
* FIXME, do this AFTER files, to avoid R/O dir problems?
* If old archive format, don't write record at all.
*/
if (!f_oldarch) {
hstat.st_size = 0; /* Force 0 size on dir */
/*
* If people could really read standard archives,
* this should be: (FIXME)
header = start_header(f_standard? p: namebuf, &hstat);
* but since they'd interpret LF_DIR records as
* regular files, we'd better put the / on the name.
*/
header = start_header(namebuf, &hstat);
if (header == NULL)
goto badfile; /* eg name too long */
if (f_standard) {
header->header.linkflag = LF_DIR;
}
finish_header(header); /* Done with directory header */
}

/* Hack to remove "./" from the front of all the file names */
if (len == 2 && namebuf[0] == '.') {
len = 0;
}

/* Now output all the files in the directory */
if (f_dironly)
break; /* Unless the user says no */
errno = 0;
dirp = opendir(p);
if (!dirp) {
if (errno) {
perror (p);
} else {
annorec(stderr, tar);
fprintf(stderr, "%s: error opening directory",
p);
}
break;
}

/* Should speed this up by cd-ing into the dir, FIXME */
while (NULL != (d=readdir(dirp))) {
/* Skip . and .. */
if (d->d_name[0] == '.') {
if (d->d_name[1] == '\0') continue;
if (d->d_name[1] == '.') {
if (d->d_name[2] == '\0') continue;
}
}
if (d->d_namlen + len >= NAMSIZ) {
annorec(stderr, tar);
fprintf(stderr, "%s%s: name too long\n",
namebuf, d->d_name);
continue;
}
strcpy(namebuf+len, d->d_name);
dump_file(namebuf, our_device);
}

closedir(dirp);
}
break;

#ifdef S_IFCHR
case S_IFCHR: /* Character special file */
type = LF_CHR;
goto easy;
#endif

#ifdef S_IFBLK
case S_IFBLK: /* Block special file */
type = LF_BLK;
goto easy;
#endif

#ifdef S_IFIFO
case S_IFIFO: /* Fifo special file */
type = LF_FIFO;
#endif

easy:
if (!f_standard) goto unknown;

hstat.st_size = 0; /* Force 0 size */
header = start_header(p, &hstat);
if (header == NULL) goto badfile; /* eg name too long */

header->header.linkflag = type;
if (type != LF_FIFO) {
to_oct((long) major(hstat.st_rdev), 8,
header->header.devmajor);
to_oct((long) minor(hstat.st_rdev), 8,
header->header.devminor);
}

finish_header(header);
break;

default:
unknown:
annorec(stderr, tar);
fprintf(stderr,
"%s: Unknown file type; file ignored.\n", p);
break;
}
}


/*
* Make a header block for the file name whose stat info is st .
* Return header pointer for success, NULL if the name is too long.
*/
union record *
start_header(name, st)
char *name;
register struct stat *st;
{
register union record *header;

header = (union record *) findrec();
bzero(header->charptr, sizeof(*header)); /* XXX speed up */

/*
* Check the file name and put it in the record.
*/
while ('/' == *name) {
static int warned_once = 0;

name++; /* Force relative path */
if (!warned_once++) {
annorec(stderr, tar);
fprintf(stderr,
"Removing leading / from absolute path names in the archive.\n");
}
}
strcpy(header->header.name, name);
if (header->header.name[NAMSIZ-1]) {
annorec(stderr, tar);
fprintf(stderr, "%s: name too long\n", name);
return NULL;
}

to_oct((long) (st->st_mode & ~S_IFMT),
8, header->header.mode);
to_oct((long) st->st_uid, 8, header->header.uid);
to_oct((long) st->st_gid, 8, header->header.gid);
to_oct((long) st->st_size, 1+12, header->header.size);
to_oct((long) st->st_mtime, 1+12, header->header.mtime);
/* header->header.linkflag is left as null */

#ifndef NONAMES
/* Fill in new Unix Standard fields if desired. */
if (f_standard) {
header->header.linkflag = LF_NORMAL; /* New default */
strcpy(header->header.magic, TMAGIC); /* Mark as Unix Std */
finduname(header->header.uname, st->st_uid);
findgname(header->header.gname, st->st_gid);
}
#endif
return header;
}

/*
* Finish off a filled-in header block and write it out.
* We also print the file name and/or full info if verbose is on.
*/
void
finish_header(header)
register union record *header;
{
register int i, sum;
register char *p;

bcopy(CHKBLANKS, header->header.chksum, sizeof(header->header.chksum));

sum = 0;
p = header->charptr;
for (i = sizeof(*header); --i >= 0; ) {
/*
* We can't use unsigned char here because of old compilers,
* e.g. V7.
*/
sum += 0xFF & *p++;
}

/*
* Fill in the checksum field. It's formatted differently
* from the other fields: it has [6] digits, a null, then a
* space -- rather than digits, a space, then a null.
* We use to_oct then write the null in over to_oct's space.
* The final space is already there, from checksumming, and
* to_oct doesn't modify it.
*
* This is a fast way to do:
* (void) sprintf(header->header.chksum, "%6o", sum);
*/
to_oct((long) sum, 8, header->header.chksum);
header->header.chksum[6] = '\0'; /* Zap the space */

userec(header);

if (f_verbose) {
/* These globals are parameters to print_header, sigh */
head = header;
/* hstat is already set up */
head_standard = f_standard;
print_header(stderr);
}

return;
}


/*
* Quick and dirty octal conversion.
* Converts long "value" into a "digs"-digit field at "where",
* including a trailing space and room for a null. "digs"==3 means
* 1 digit, a space, and room for a null.
*
* We assume the trailing null is already there and don't fill it in.
* This fact is used by start_header and finish_header, so don't change it!
*
* This should be equivalent to:
* (void) sprintf(where, "%*lo ", digs-2, value);
* except that sprintf fills in the trailing null and we don't.
*/
void
to_oct(value, digs, where)
register long value;
register int digs;
register char *where;
{

--digs; /* Trailing null slot is left alone */
where[--digs] = ' '; /* Put in the space, though */

/* Produce the digits -- at least one */
do {
where[--digs] = '0' + (char)(value & 7); /* one octal digit */
value >>= 3;
} while (digs > 0 && value != 0);

/* Leading spaces, if necessary */
while (digs > 0)
where[--digs] = ' ';

}


/*
* Write the EOT record(s).
* We actually zero at least one record, through the end of the block.
* Old tar writes garbage after two zeroed records -- and PDtar used to.
*/
write_eot()
{
union record *p;
int bufsize;

p = findrec();
bufsize = endofrecs()->charptr - p->charptr;
bzero(p->charptr, bufsize);
userec(p);
}
@@@ Fin de create.c
echo extract.c
cat >extract.c <<'@@@ Fin de extract.c'
/*
* Extract files from a tar archive.
*
* Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
*
* @(#) extract.c 1.32 87/11/11 Public Domain - gnu
*/

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifdef BSD42
#include <sys/file.h>
#endif

#ifdef USG
#include <fcntl.h>
#endif

#ifdef MSDOS
#include <fcntl.h>
#endif /* MSDOS */

/*
* Some people don't have a #define for these.
*/
#ifndef O_BINARY
#define O_BINARY 0
#endif
#ifndef O_NDELAY
#define O_NDELAY 0
#endif

#ifdef NO_OPEN3
/* We need the #define's even though we don't use them. */
#include "open3.h"
#endif

#ifdef EMUL_OPEN3
/* Simulated 3-argument open for systems that don't have it */
#include "open3.h"
#endif

extern int errno; /* From libc.a */
extern time_t time(); /* From libc.a */
extern char *index(); /* From libc.a or port.c */

#include "tar.h"
#include "port.h"

extern union record *head; /* Points to current tape header */
extern struct stat hstat; /* Stat struct corresponding */
extern int head_standard; /* Tape header is in ANSI format */

extern void print_header();
extern void skip_file();
extern void pr_mkdir();

int make_dirs(); /* Makes required directories */

static time_t now = 0; /* Current time */
static we_are_root = 0; /* True if our effective uid == 0 */
static int notumask = ~0; /* Masks out bits user doesn't want */

/*
* Set up to extract files.
*/
extr_init()
{
int ourmask;

now = time((time_t *)0);
if (geteuid() == 0)
we_are_root = 1;

/*
* We need to know our umask. But if f_use_protection is set,
* leave our kernel umask at 0, and our "notumask" at ~0.
*/
ourmask = umask(0); /* Read it */
if (!f_use_protection) {
(void) umask (ourmask); /* Set it back how it was */
notumask = ~ourmask; /* Make umask override permissions */
}
}


/*
* Extract a file from the archive.
*/
void
extract_archive()
{
register char *data;
int fd, check, namelen, written, openflag;
long size;
time_t acc_upd_times[2];
register int skipcrud;

saverec(&head); /* Make sure it sticks around */
userec(head); /* And go past it in the archive */
decode_header(head, &hstat, &head_standard, 1); /* Snarf fields */

/* Print the record from 'head' and 'hstat' */
if (f_verbose)
print_header(stdout);

/*
* Check for fully specified pathnames and other atrocities.
*
* Note, we can't just make a pointer to the new file name,
* since saverec() might move the header and adjust "head".
* We have to start from "head" every time we want to touch
* the header record.
*/
skipcrud = 0;
while ('/' == head->header.name[skipcrud]) {
static int warned_once = 0;

skipcrud++; /* Force relative path */
if (!warned_once++) {
annorec(stderr, tar);
fprintf(stderr,
"Removing leading / from absolute path names in the archive.\n");
}
}

switch (head->header.linkflag) {

default:
annofile(stderr, tar);
fprintf(stderr,
"Unknown file type '%c' for %s, extracted as normal file\n",
head->header.linkflag, skipcrud+head->header.name);
/* FALL THRU */

case LF_OLDNORMAL:
case LF_NORMAL:
case LF_CONTIG:
/*
* Appears to be a file.
* See if it's really a directory.
*/
namelen = strlen(skipcrud+head->header.name)-1;
if (head->header.name[skipcrud+namelen] == '/')
goto really_dir;

/* FIXME, deal with protection issues */
again_file:
openflag = f_keep?
O_BINARY|O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_EXCL:
O_BINARY|O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_TRUNC;
#ifdef O_CTG
/*
* Contiguous files (on the Masscomp) have to specify
* the size in the open call that creates them.
*/
if (head->header.lnkflag == LF_CONTIG)
fd = open(skipcrud+head->header.name, openflag | O_CTG,
hstat.st_mode, hstat.st_size);
else
#endif
{
#ifdef NO_OPEN3
/*
* On raw V7 we won't let them specify -k (f_keep), but
* we just bull ahead and create the files.
*/
fd = creat(skipcrud+head->header.name,
hstat.st_mode);
#else
/*
* With 3-arg open(), we can do this up right.
*/
fd = open(skipcrud+head->header.name, openflag,
hstat.st_mode);
#endif
}

if (fd < 0) {
if (make_dirs(skipcrud+head->header.name))
goto again_file;
annofile(stderr, tar);
fprintf(stderr, "Could not make file ");
perror(skipcrud+head->header.name);
skip_file((long)hstat.st_size);
goto quit;
}

for (size = hstat.st_size;
size > 0;
size -= written) {
/*
* Locate data, determine max length
* writeable, write it, record that
* we have used the data, then check
* if the write worked.
*/
data = findrec()->charptr;
if (data == NULL) { /* Check it... */
annorec(stderr, tar);
fprintf(stderr, "Unexpected EOF on archive file\n");
break;
}
written = endofrecs()->charptr - data;
if (written > size) written = size;
errno = 0;
check = write(fd, data, written);
/*
* The following is in violation of strict
* typing, since the arg to userec
* should be a struct rec *. FIXME.
*/
userec(data + written - 1);
if (check == written) continue;
/*
* Error in writing to file.
* Print it, skip to next file in archive.
*/
annofile(stderr, tar);
fprintf(stderr,
"Tried to write %d bytes to file, could only write %d:\n",
written, check);
perror(skipcrud+head->header.name);
skip_file((long)(size - written));
break; /* Still do the close, mod time, chmod, etc */
}

check = close(fd);
if (check < 0) {
annofile(stderr, tar);
fprintf(stderr, "Error while closing ");
perror(skipcrud+head->header.name);
}

set_filestat:
/*
* Set the modified time of the file.
*
* Note that we set the accessed time to "now", which
* is really "the time we started extracting files".
*/
if (!f_modified) {
acc_upd_times[0] = now; /* Accessed now */
acc_upd_times[1] = hstat.st_mtime; /* Mod'd */
if (utime(skipcrud+head->header.name,
acc_upd_times) < 0) {
annofile(stderr, tar);
perror(skipcrud+head->header.name);
}
}

/*
* If we are root, set the owner and group of the extracted
* file. This does what is wanted both on real Unix and on
* System V. If we are running as a user, we extract as that
* user; if running as root, we extract as the original owner.
*/
if (we_are_root) {
if (chown(skipcrud+head->header.name, hstat.st_uid,
hstat.st_gid) < 0) {
annofile(stderr, tar);
perror(skipcrud+head->header.name);
}
}

/*
* If '-k' is not set, open() or creat() could have saved
* the permission bits from a previously created file,
* ignoring the ones we specified.
* Even if -k is set, if the file has abnormal
* mode bits, we must chmod since writing or chown() has
* probably reset them.
*
* If -k is set, we know *we* created this file, so the mode
* bits were set by our open(). If the file is "normal", we
* skip the chmod. This works because we did umask(0) if -p
* is set, so umask will have left the specified mode alone.
*/
if ((!f_keep)
|| (hstat.st_mode & (S_ISUID|S_ISGID|S_ISVTX))) {
if (chmod(skipcrud+head->header.name,
notumask & (int)hstat.st_mode) < 0) {
annofile(stderr, tar);
perror(skipcrud+head->header.name);
}
}

quit:
break;

case LF_LINK:
again_link:
check = link (head->header.linkname,
skipcrud+head->header.name);
if (check == 0)
break;
if (make_dirs(skipcrud+head->header.name))
goto again_link;
annofile(stderr, tar);
fprintf(stderr, "Could not link %s to ",
skipcrud+head->header.name);
perror(head->header.linkname);
break;

#ifdef S_IFLNK
case LF_SYMLINK:
again_symlink:
check = symlink(head->header.linkname,
skipcrud+head->header.name);
/* FIXME, don't worry uid, gid, etc... */
if (check == 0)
break;
if (make_dirs(skipcrud+head->header.name))
goto again_symlink;
annofile(stderr, tar);
fprintf(stderr, "Could not create symlink ");
perror(head->header.linkname);
break;
#endif

#ifdef S_IFCHR
case LF_CHR:
hstat.st_mode |= S_IFCHR;
goto make_node;
#endif

#ifdef S_IFBLK
case LF_BLK:
hstat.st_mode |= S_IFBLK;
goto make_node;
#endif

#ifdef S_IFIFO
/* If local system doesn't support FIFOs, use default case */
case LF_FIFO:
hstat.st_mode |= S_IFIFO;
hstat.st_rdev = 0; /* FIXME, do we need this? */
goto make_node;
#endif

make_node:
check = mknod(skipcrud+head->header.name,
(int) hstat.st_mode, (int) hstat.st_rdev);
if (check != 0) {
if (make_dirs(skipcrud+head->header.name))
goto make_node;
annofile(stderr, tar);
fprintf(stderr, "Could not make ");
perror(skipcrud+head->header.name);
break;
};
goto set_filestat;

case LF_DIR:
namelen = strlen(skipcrud+head->header.name)-1;
really_dir:
/* Check for trailing /, and zap as many as we find. */
while (namelen && head->header.name[skipcrud+namelen] == '/')
head->header.name[skipcrud+namelen--] = '\0';

again_dir:
check = mkdir(skipcrud+head->header.name,
0300 | (int)hstat.st_mode);
if (check != 0) {
if (make_dirs(skipcrud+head->header.name))
goto again_dir;
/* If we're trying to create '.', let it be. */
if (head->header.name[skipcrud+namelen] == '.' &&
(namelen==0 ||
head->header.name[skipcrud+namelen-1]=='/'))
goto check_perms;
annofile(stderr, tar);
fprintf(stderr, "Could not make directory ");
perror(skipcrud+head->header.name);
break;
}

check_perms:
if (0300 != (0300 & (int) hstat.st_mode)) {
hstat.st_mode |= 0300;
annofile(stderr, tar);
fprintf(stderr,
"Added write & execute permission to directory %s\n",
skipcrud+head->header.name);
}

goto set_filestat;
/* FIXME, Remember timestamps for after files created? */
/* FIXME, change mode after files created (if was R/O dir) */

}

/* We don't need to save it any longer. */
saverec((union record **) 0); /* Unsave it */
}

/*
* After a file/link/symlink/dir creation has failed, see if
* it's because some required directory was not present, and if
* so, create all required dirs.
*/
int
make_dirs(pathname)
char *pathname;
{
char *p; /* Points into path */
int madeone = 0; /* Did we do anything yet? */
int save_errno = errno; /* Remember caller's errno */
int check;

if (errno != ENOENT)
return 0; /* Not our problem */

for (p = index(pathname, '/'); p != NULL; p = index(p+1, '/')) {
/* Avoid mkdir of empty string, if leading or double '/' */
if (p == pathname || p[-1] == '/')
continue;
/* Avoid mkdir where last part of path is '.' */
if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/'))
continue;
*p = 0; /* Truncate the path there */
check = mkdir (pathname, 0777); /* Try to create it as a dir */
if (check == 0) {
/* Fix ownership */
if (we_are_root) {
if (chown(pathname, hstat.st_uid,
hstat.st_gid) < 0) {
annofile(stderr, tar);
perror(pathname);
}
}
pr_mkdir(pathname, p-pathname, notumask&0777, stdout);
madeone++; /* Remember if we made one */
*p = '/';
continue;
}
*p = '/';
if (errno == EEXIST) /* Directory already exists */
continue;
/*
* Some other error in the mkdir. We return to the caller.
*/
break;
}

errno = save_errno; /* Restore caller's errno */
return madeone; /* Tell them to retry if we made one */
}
@@@ Fin de extract.c
echo buffer.c
cat >buffer.c <<'@@@ Fin de buffer.c'
/*
* Buffer management for public domain tar.
*
* Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
*
* @(#) buffer.c 1.28 11/6/87 Public Domain - gnu
*/

#include <stdio.h>
#include <errno.h>
#include <sys/types.h> /* For non-Berkeley systems */
#include <sys/stat.h>
#include <signal.h>

#ifdef MSDOS
# include <fcntl.h>
#else
# ifdef XENIX
# include <sys/inode.h>
# endif
# include <sys/file.h>
#endif

#include "tar.h"
#include "port.h"

#define STDIN 0 /* Standard input file descriptor */
#define STDOUT 1 /* Standard output file descriptor */

#define PREAD 0 /* Read file descriptor from pipe() */
#define PWRITE 1 /* Write file descriptor from pipe() */

extern char *valloc();
extern char *index(), *strcat();

/*
* V7 doesn't have a #define for this.
*/
#ifndef O_RDONLY
#define O_RDONLY 0
#endif

#define MAGIC_STAT 105 /* Magic status returned by child, if
it can't exec. We hope compress/sh
never return this status! */
/*
* The record pointed to by save_rec should not be overlaid
* when reading in a new tape block. Copy it to record_save_area first, and
* change the pointer in *save_rec to point to record_save_area.
* Saved_recno records the record number at the time of the save.
* This is used by annofile() to print the record number of a file's
* header record.
*/
static union record **save_rec;
static union record record_save_area;
static long saved_recno;

/*
* PID of child program, if f_compress or remote archive access.
*/
static int childpid = 0;

/*
* Record number of the start of this block of records
*/
static long baserec;

/*
* Error recovery stuff
*/
static int r_error_count;

/*
* Have we hit EOF yet?
*/
static int eof;


/*
* Return the location of the next available input or output record.
* Return NULL for EOF. Once we have returned NULL, we just keep returning
* it, to avoid accidentally going on to the next file on the "tape".
*/
union record *
findrec()
{
if (ar_record == ar_last) {
if (eof)
return (union record *)NULL; /* EOF */
flush_archive();
if (ar_record == ar_last) {
eof++;
return (union record *)NULL; /* EOF */
}
}
return ar_record;
}


/*
* Indicate that we have used all records up thru the argument.
* (should the arg have an off-by-1? XXX FIXME)
*/
void
userec(rec)
union record *rec;
{
while(rec >= ar_record)
ar_record++;
/*
* Do NOT flush the archive here. If we do, the same
* argument to userec() could mean the next record (if the
* input block is exactly one record long), which is not what
* is intended.
*/
if (ar_record > ar_last)
abort();
}


/*
* Return a pointer to the end of the current records buffer.
* All the space between findrec() and endofrecs() is available
* for filling with data, or taking data from.
*/
union record *
endofrecs()
{
return ar_last;
}


/*
* Duplicate a file descriptor into a certain slot.
* Equivalent to BSD "dup2" with error reporting.
*/
void
dupto(from, to, msg)
int from, to;
char *msg;
{
int err;

if (from != to) {
(void) close(to);
err = dup(from);
if (err != to) {
fprintf(stderr, "tar: cannot dup ");
perror(msg);
exit(EX_SYSTEM);
}
(void) close(from);
}
}


/*
* Fork a child to deal with remote files or compression.
* If rem_host is zero, we are called only for compression.
*/
void
child_open(rem_host, rem_file)
char *rem_host, *rem_file;
{

#ifdef MSDOS
fprintf(stderr,
"MSDOS %s cannot deal with compressed or remote archives\n", tar);
exit(EX_ARGSBAD);
#else

int pipes[2];
int err;
struct stat arstat;
char cmdbuf[1000]; /* For big file and host names */

/* Create a pipe to talk to the child over */
err = pipe(pipes);
if (err < 0) {
perror ("tar: cannot create pipe to child");
exit(EX_SYSTEM);
}

/* Fork child process */
childpid = fork();
if (childpid < 0) {
perror("tar: cannot fork");
exit(EX_SYSTEM);
}

/*
* Parent process. Clean up.
*
* We always close the archive file (stdin, stdout, or opened file)
* since the child will end up reading or writing that for us.
* Note that this may leave standard input closed.
* We close the child's end of the pipe since they will handle
* that too; and we set <archive> to the other end of the pipe.
*
* If reading, we set f_reblock since reading pipes or network
* sockets produces odd length data.
*/
if (childpid > 0) {
(void) close (archive);
if (ar_reading) {
(void) close (pipes[PWRITE]);
archive = pipes[PREAD];
f_reblock++;
} else {
(void) close (pipes[PREAD]);
archive = pipes[PWRITE];
}
return;
}

/*
* Child process.
*/
if (ar_reading) {
/*
* Reading from the child...
*
* Close the read-side of the pipe, which our parent will use.
* Move the write-side of pipe to stdout,
* If local, move archive input to child's stdin,
* then run the child.
*/
(void) close (pipes[PREAD]);
dupto(pipes[PWRITE], STDOUT, "to stdout");
if (rem_host) {
(void) close (STDIN); /* rsh abuses stdin */
if (STDIN != open("/dev/null"))
perror("Can't open /dev/null");
sprintf(cmdbuf,
"rsh '%s' dd '<%s' bs=%db",
rem_host, rem_file, blocking);
if (f_compress)
strcat(cmdbuf, "| compress -d");
#ifdef DEBUG
fprintf(stderr, "Exec-ing: %s\n", cmdbuf);
#endif
execlp("sh", "sh", "-c", cmdbuf, (char *)0);
perror("tar: cannot exec sh");
} else {
/*
* If we are reading a disk file, compress is OK;
* otherwise, we have to reblock the input in case it's
* coming from a tape drive. This is an optimization.
*/
dupto(archive, STDIN, "to stdin");
err = fstat(STDIN, &arstat);
if (err != 0) {
perror("tar: can't fstat archive");
exit(EX_SYSTEM);
}
if ((arstat.st_mode & S_IFMT) == S_IFREG) {
execlp("compress", "compress", "-d", (char *)0);
perror("tar: cannot exec compress");
} else {
/* Non-regular file needs dd before compress */
sprintf(cmdbuf,
"dd bs=%db | compress -d",
blocking);
#ifdef DEBUG
fprintf(stderr, "Exec-ing: %s\n", cmdbuf);
#endif
execlp("sh", "sh", "-c", cmdbuf, (char *)0);
perror("tar: cannot exec sh");
}
}
exit(MAGIC_STAT);
} else {
/*
* Writing archive to the child.
* It would like to run either:
* compress
* compress | dd obs=20b
* rsh 'host' dd obs=20b '>foo'
* or compress | rsh 'host' dd obs=20b '>foo'
*
* We need the dd to reblock the output to the
* user's specs, if writing to a device or over
* the net. However, it produces a stupid
* message about how many blocks it processed.
* Because the shell on the remote end could be just
* about any shell, we can't depend on it to do
* redirect stderr properly for us -- the csh
* doesn't use the same syntax as the Bourne shell.
* On the other hand, if we just ignore stderr on
* this end, we won't see errors from rsh, or from
* the inability of "dd" to write its output file.
* The combination of the local sh, the rsh, the
* remote csh, and maybe a remote sh conspires to mess
* up any possible quoting method, so grumble! we
* punt and just accept the fucking "xxx blocks"
* messages. The real fix would be a "dd" that
* would shut up.
*
* Close the write-side of the pipe, which our parent will use.
* Move the read-side of the pipe to stdin,
* If local, move archive output to the child's stdout.
* then run the child.
*/
(void) close (pipes[PWRITE]);
dupto(pipes[PREAD], STDIN, "to stdin");
if (!rem_host)
dupto(archive, STDOUT, "to stdout");

cmdbuf[0] = '\0';
if (f_compress) {
if (!rem_host) {
err = fstat(STDOUT, &arstat);
if (err != 0) {
perror("tar: can't fstat archive");
exit(EX_SYSTEM);
}
if ((arstat.st_mode & S_IFMT) == S_IFREG) {
execlp("compress", "compress", (char *)0);
perror("tar: cannot exec compress");
}
}
strcat(cmdbuf, "compress | ");
}
if (rem_host) {
sprintf(cmdbuf+strlen(cmdbuf),
"rsh '%s' dd obs=%db '>%s'",
rem_host, blocking, rem_file);
} else {
sprintf(cmdbuf+strlen(cmdbuf),
"dd obs=%db",
blocking);
}
#ifdef DEBUG
fprintf(stderr, "Exec-ing: %s\n", cmdbuf);
#endif
execlp("sh", "sh", "-c", cmdbuf, (char *)0);
perror("tar: cannot exec sh");
exit(MAGIC_STAT);
}
#endif /* MSDOS */
}


/*
* Open an archive file. The argument specifies whether we are
* reading or writing.
*/
open_archive(read)
int read;
{
char *colon, *slash;
char *rem_host = 0, *rem_file;

colon = index(ar_file, ':');
if (colon) {
slash = index(ar_file, '/');
if (slash && slash > colon) {
/*
* Remote file specified. Parse out separately,
* and don't try to open it on the local system.
*/
rem_file = colon + 1;
rem_host = ar_file;
*colon = '\0';
goto gotit;
}
}

if (ar_file[0] == '-' && ar_file[1] == '\0') {
f_reblock++; /* Could be a pipe, be safe */
if (read) archive = STDIN;
else archive = STDOUT;
} else if (read) {
archive = open(ar_file, O_RDONLY);
} else {
archive = creat(ar_file, 0666);
}

if (archive < 0) {
perror(ar_file);
exit(EX_BADARCH);
}

#ifdef MSDOS
setmode(archive, O_BINARY);
#endif

gotit:
if (blocksize == 0) {
fprintf(stderr, "tar: invalid value for blocksize\n");
exit(EX_ARGSBAD);
}

/*NOSTRICT*/
ar_block = (union record *) valloc((unsigned)blocksize);
if (!ar_block) {
fprintf(stderr,
"tar: could not allocate memory for blocking factor %d\n",
blocking);
exit(EX_ARGSBAD);
}

ar_record = ar_block;
ar_last = ar_block + blocking;
ar_reading = read;

if (f_compress || rem_host)
child_open(rem_host, rem_file);

if (read) {
ar_last = ar_block; /* Set up for 1st block = # 0 */
(void) findrec(); /* Read it in, check for EOF */
}
}


/*
* Remember a union record * as pointing to something that we
* need to keep when reading onward in the file. Only one such
* thing can be remembered at once, and it only works when reading
* an archive.
*
* We calculate "offset" then add it because some compilers end up
* adding (baserec+ar_record), doing a 9-bit shift of baserec, then
* subtracting ar_block from that, shifting it back, losing the top 9 bits.
*/
saverec(pointer)
union record **pointer;
{
long offset;

save_rec = pointer;
offset = ar_record - ar_block;
saved_recno = baserec + offset;
}

/*
* Perform a write to flush the buffer.
*/
fl_write()
{
int err;

err = write(archive, ar_block->charptr, blocksize);
if (err == blocksize) return;
/* FIXME, multi volume support on write goes here */
if (err < 0)
perror(ar_file);
else
fprintf(stderr, "tar: %s: write failed, short %d bytes\n",
ar_file, blocksize - err);
exit(EX_BADARCH);
}


/*
* Handle read errors on the archive.
*
* If the read should be retried, readerror() returns to the caller.
*/
void
readerror()
{
# define READ_ERROR_MAX 10

read_error_flag++; /* Tell callers */

annorec(stderr, tar);
fprintf(stderr, "Read error on ");
perror(ar_file);

if (baserec == 0) {
/* First block of tape. Probably stupidity error */
exit(EX_BADARCH);
}

/*
* Read error in mid archive. We retry up to READ_ERROR_MAX times
* and then give up on reading the archive. We set read_error_flag
* for our callers, so they can cope if they want.
*/
if (r_error_count++ > READ_ERROR_MAX) {
annorec(stderr, tar);
fprintf(stderr, "Too many errors, quitting.\n");
exit(EX_BADARCH);
}
return;
}


/*
* Perform a read to flush the buffer.
*/
fl_read()
{
int err; /* Result from system call */
int left; /* Bytes left */
char *more; /* Pointer to next byte to read */

/*
* Clear the count of errors. This only applies to a single
* call to fl_read. We leave read_error_flag alone; it is
* only turned off by higher level software.
*/
r_error_count = 0; /* Clear error count */

/*
* If we are about to wipe out a record that
* somebody needs to keep, copy it out to a holding
* area and adjust somebody's pointer to it.
*/
if (save_rec &&
*save_rec >= ar_record &&
*save_rec < ar_last) {
record_save_area = **save_rec;
*save_rec = &record_save_area;
}
error_loop:
err = read(archive, ar_block->charptr, blocksize);
if (err == blocksize) return;
if (err < 0) {
readerror();
goto error_loop; /* Try again */
}

more = ar_block->charptr + err;
left = blocksize - err;

again:
if (0 == (((unsigned)left) % RECORDSIZE)) {
/* FIXME, for size=0, multi vol support */
/* On the first block, warn about the problem */
if (!f_reblock && baserec == 0 && f_verbose && err > 0) {
annorec(stderr, tar);
fprintf(stderr, "Blocksize = %d record%s\n",
err / RECORDSIZE, (err > RECORDSIZE)? "s": "");
}
ar_last = ar_block + ((unsigned)(blocksize - left))/RECORDSIZE;
return;
}
if (f_reblock) {
/*
* User warned us about this. Fix up.
*/
if (left > 0) {
error2loop:
err = read(archive, more, left);
if (err < 0) {
readerror();
goto error2loop; /* Try again */
}
if (err == 0) {
annorec(stderr, tar);
fprintf(stderr,
"%s: eof not on block boundary, strange...\n",
ar_file);
exit(EX_BADARCH);
}
left -= err;
more += err;
goto again;
}
} else {
annorec(stderr, tar);
fprintf(stderr, "%s: read %d bytes, strange...\n",
ar_file, err);
exit(EX_BADARCH);
}
}


/*
* Flush the current buffer to/from the archive.
*/
flush_archive()
{

baserec += ar_last - ar_block; /* Keep track of block #s */
ar_record = ar_block; /* Restore pointer to start */
ar_last = ar_block + blocking; /* Restore pointer to end */

if (!ar_reading)
fl_write();
else
fl_read();
}

/*
* Close the archive file.
*/
close_archive()
{
int child;
int status;

if (!ar_reading) flush_archive();
(void) close(archive);

#ifndef MSDOS
if (childpid) {
/*
* Loop waiting for the right child to die, or for
* no more kids.
*/
while (((child = wait(&status)) != childpid) && child != -1)
;

if (child != -1) {
switch (TERM_SIGNAL(status)) {
case 0:
/* Child voluntarily terminated -- but why? */
if (TERM_VALUE(status) == MAGIC_STAT) {
exit(EX_SYSTEM);/* Child had trouble */
}
if (TERM_VALUE(status) == (SIGPIPE + 128)) {
/*
* /bin/sh returns this if its child
* dies with SIGPIPE. 'Sok.
*/
break;
} else if (TERM_VALUE(status))
fprintf(stderr,
"tar: child returned status %d\n",
TERM_VALUE(status));
case SIGPIPE:
break; /* This is OK. */

default:
fprintf(stderr,
"tar: child died with signal %d%s\n",
TERM_SIGNAL(status),
TERM_COREDUMP(status)? " (core dumped)": "");
}
}
}
#endif /* MSDOS */
}


/*
* Message management.
*
* anno writes a message prefix on stream (eg stdout, stderr).
*
* The specified prefix is normally output followed by a colon and a space.
* However, if other command line options are set, more output can come
* out, such as the record # within the archive.
*
* If the specified prefix is NULL, no output is produced unless the
* command line option(s) are set.
*
* If the third argument is 1, the "saved" record # is used; if 0, the
* "current" record # is used.
*/
void
anno(stream, prefix, savedp)
FILE *stream;
char *prefix;
int savedp;
{
# define MAXANNO 50
char buffer[MAXANNO]; /* Holds annorecment */
# define ANNOWIDTH 13
int space;
long offset;

/* Make sure previous output gets out in sequence */
if (stream == stderr)
fflush(stdout);
if (f_sayblock) {
if (prefix) {
fputs(prefix, stream);
putc(' ', stream);
}
offset = ar_record - ar_block;
sprintf(buffer, "rec %d: ",
savedp? saved_recno:
baserec + offset);
fputs(buffer, stream);
space = ANNOWIDTH - strlen(buffer);
if (space > 0) {
fprintf(stream, "%*s", space, "");
}
} else if (prefix) {
fputs(prefix, stream);
fputs(": ", stream);
}
}
@@@ Fin de buffer.c
echo getoldopt.c
cat >getoldopt.c <<'@@@ Fin de getoldopt.c'
/*
* Plug-compatible replacement for getopt() for parsing tar-like
* arguments. If the first argument begins with "-", it uses getopt;
* otherwise, it uses the old rules used by tar, dump, and ps.
*
* Written 25 August 1985 by John Gilmore (ihnp4!hoptoad!gnu) and placed
* in the Pubic Domain for your edification and enjoyment.
*
* @(#)getoldopt.c 1.4 2/4/86 Public Domain - gnu
*/

#include <stdio.h>


int
getoldopt(argc, argv, optstring)
int argc;
char **argv;
char *optstring;
{
extern char *optarg; /* Points to next arg */
extern int optind; /* Global argv index */
static char *key; /* Points to next keyletter */
static char use_getopt; /* !=0 if argv[1][0] was '-' */
extern char *index();
char c;
char *place;

optarg = NULL;

if (key == NULL) { /* First time */
if (argc < 2) return EOF;
key = argv[1];
if (*key == '-')
use_getopt++;
else
optind = 2;
}

if (use_getopt)
return getopt(argc, argv, optstring);

c = *key++;
if (c == '\0') {
key--;
return EOF;
}
place = index(optstring, c);

if (place == NULL || c == ':') {
fprintf(stderr, "%s: unknown option %c\n", argv[0], c);
return('?');
}

place++;
if (*place == ':') {
if (optind < argc) {
optarg = argv[optind];
optind++;
} else {
fprintf(stderr, "%s: %c argument missing\n",
argv[0], c);
return('?');
}
}

return(c);
}
@@@ Fin de getoldopt.c
exit 0

0 new messages