home *** CD-ROM | disk | FTP | other *** search
- /*
- * Diff files from a tar archive.
- *
- * Written 30 April 1987 by John Gilmore, ihnp4!hoptoad!gnu.
- *
- * @(#) diffarch.c 1.10 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
-
- /* Some systems don't have these #define's -- we fake it here. */
- #ifndef O_RDONLY
- #define O_RDONLY 0
- #endif
- #ifndef O_NDELAY
- #define O_NDELAY 0
- #endif
-
- extern int errno; /* From libc.a */
- extern char *valloc(); /* From libc.a */
-
- #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();
-
- char *filedata; /* Pointer to area for reading
- file contents into */
-
- /*
- * Initialize for a diff operation
- */
- diff_init()
- {
-
- /*NOSTRICT*/
- filedata = (char *) valloc((unsigned)blocksize);
- if (!filedata) {
- fprintf(stderr,
- "tar: could not allocate memory for diff buffer of %d bytes\n",
- blocking);
- exit(EX_ARGSBAD);
- }
- }
-
- /*
- * Diff a file against the archive.
- */
- void
- diff_archive()
- {
- register char *data;
- int fd, check, namelen, written;
- int err, firsttime;
- long size;
- struct stat filestat;
- char linkbuf[NAMSIZ+3];
-
- errno = EPIPE; /* FIXME, remove perrors */
-
- 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);
-
- switch (head->header.linkflag) {
-
- default:
- annofile(stderr, tar);
- fprintf(stderr,
- "Unknown file type '%c' for %s, diffed as normal file\n",
- head->header.linkflag, 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(head->header.name)-1;
- if (head->header.name[namelen] == '/')
- goto really_dir;
-
- fd = open(head->header.name, O_NDELAY|O_RDONLY);
-
- if (fd < 0) {
- if (errno == ENOENT) {
- /* Expected error -- to stdout */
- annofile(stdout, (char *)NULL);
- fprintf(stdout, "%s: does not exist\n",
- head->header.name);
- } else {
- annofile(stderr, (char *)NULL);
- perror(head->header.name);
- }
- skip_file((long)hstat.st_size);
- goto quit;
- }
- #ifdef AMIGA
- err = stat(head->header.name, &filestat);
- #else
- err = fstat(fd, &filestat);
- #endif
- if (err < 0) {
- annofile(stdout, (char *)NULL);
- fprintf(stdout, "Cannot fstat file ");
- perror(head->header.name);
- skip_file((long)hstat.st_size);
- goto qclose;
- }
-
- if ((filestat.st_mode & S_IFMT) != S_IFREG) {
- annofile(stdout, (char *)NULL);
- fprintf(stdout, "%s: not a regular file\n",
- head->header.name);
- skip_file((long)hstat.st_size);
- goto qclose;
- }
-
- filestat.st_mode &= ~S_IFMT;
- if (filestat.st_mode != hstat.st_mode)
- sigh("mode");
- if (filestat.st_uid != hstat.st_uid)
- sigh("uid");
- if (filestat.st_gid != hstat.st_gid)
- sigh("gid");
- if (filestat.st_size != hstat.st_size) {
- sigh("size");
- skip_file((long)hstat.st_size);
- goto qclose;
- }
- if (filestat.st_mtime != hstat.st_mtime)
- sigh("mod time");
-
- firsttime = 0;
-
- 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 = read (fd, filedata, 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) {
- /* The read worked, now compare the data */
- if (bcmp(data, filedata, check) == 0)
- continue; /* It compares */
- if (firsttime++) {
- annofile(stdout, (char *)NULL);
- fprintf(stdout, "%s: data differs\n",
- head->header.name);
- }
- }
-
- /*
- * Error in reading from file.
- * Print it, skip to next file in archive.
- */
- annofile(stderr, tar);
- fprintf(stderr,
- "Tried to read %d bytes from file, could only read %d:\n",
- written, check);
- perror(head->header.name);
- skip_file((long)(size - written));
- break; /* Still do the close, mod time, chmod, etc */
- }
-
- qclose:
- check = close(fd);
- if (check < 0) {
- annofile(stderr, tar);
- fprintf(stderr, "Error while closing ");
- perror(head->header.name);
- }
-
- quit:
- break;
-
- case LF_LINK:
- check = 1; /* FIXME deal with this */
- /* check = link (head->header.linkname,
- head->header.name); */
- /* FIXME, don't worry uid, gid, etc... */
- if (check == 0)
- break;
- annofile(stderr, tar);
- fprintf(stderr, "Could not link %s to ",
- head->header.name);
- perror(head->header.linkname);
- break;
-
- #ifdef S_IFLNK
- case LF_SYMLINK:
- check = readlink(head->header.name, linkbuf,
- (sizeof linkbuf)-1);
-
- if (check < 0) {
- if (errno == ENOENT) {
- annofile(stdout, (char *)NULL);
- fprintf(stdout,
- "%s: no such file or directory\n",
- head->header.name);
- } else {
- annofile(stderr, tar);
- fprintf(stderr, "Could not read link");
- perror(head->header.name);
- }
- break;
- }
-
- linkbuf[check] = '\0'; /* Null-terminate it */
- if (strncmp(head->header.linkname, linkbuf, check) != 0) {
- annofile(stdout, (char *)NULL);
- fprintf(stdout, "%s: symlink differs\n",
- head->header.linkname);
- }
- break;
- #endif
-
- case LF_CHR:
- hstat.st_mode |= S_IFCHR;
- goto make_node;
-
- #ifdef S_IFBLK
- /* If local system doesn't support block devices, use default case */
- 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:
- /* FIXME, deal with umask */
-
- check = 1; /* FIXME, implement this */
- /* check = mknod(head->header.name, (int) hstat.st_mode,
- (int) hstat.st_rdev); */
- if (check != 0) {
- annofile(stderr, tar);
- fprintf(stderr, "Could not make ");
- perror(head->header.name);
- break;
- };
- break;
-
- case LF_DIR:
- /* Check for trailing / */
- namelen = strlen(head->header.name)-1;
- really_dir:
- while (namelen && head->header.name[namelen] == '/')
- head->header.name[namelen--] = '\0'; /* Zap / */
-
- check = 1; /* FIXME, implement this */
- /* check = mkdir(head->header.name, 0300 | (int)hstat.st_mode); */
- #ifndef AMIGA
- if (check != 0) {
- annofile(stderr, tar);
- fprintf(stderr, "Could not make directory ");
- perror(head->header.name);
- break;
- }
- #endif
- break;
-
- }
-
- /* We don't need to save it any longer. */
- saverec((union record **) 0); /* Unsave it */
- }
-
- /*
- * Sigh about something that differs.
- */
- sigh(what)
- char *what;
- {
-
- annofile(stdout, (char *)NULL);
- fprintf(stdout, "%s: %s differs\n",
- head->header.name, what);
- }
-