home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-09-11 | 53.0 KB | 2,309 lines |
- Subject: v12i069: Public-domain TAR, Part02/03
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rs@uunet.UU.NET
-
- Submitted-by: John Gilmore <hoptoad!gnu@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
-