#include #define NOKNET #define import_spp #define import_finfo #define import_knames #include /* * WTAR -- Write a UNIX tar format file (on disk, tape, or to stdout) * * Switches: * f write to named file, otherwise write to stdout * t print name of each file written * v verbose; print full description of each file * d print debug messages * o omit binary files (e.g. when foreign host has * incompatible binary file format) */ #define TBLOCK 512 #define NBLOCK 20 #define NAMSIZ 100 #define MAXERR 20 #define MAXTRYS 100 #define SZ_TAPEBUFFER (TBLOCK * NBLOCK) #define RWXR_XR_X 0755 /* File header structure. One of these precedes each file on the tape. * Each file occupies an integral number of TBLOCK size logical blocks * on the tape. The number of logical blocks per physical block is variable, * with at most NBLOCK logical blocks per physical tape block. Two zero * blocks mark the end of the tar file. */ union hblock { char dummy[TBLOCK]; struct header { char name[NAMSIZ]; /* NULL delimited */ char mode[8]; /* octal, ascii */ char uid[8]; char gid[8]; char size[12]; char mtime[12]; char chksum[8]; char linkflag; char linkname[NAMSIZ]; } dbuf; }; /* Decoded file header. */ struct fheader { char name[NAMSIZ]; int mode; int uid; int gid; int isdir; long size; long mtime; long chksum; int linkflag; char linkname[NAMSIZ]; }; /* Map TAR file mode bits into characters for printed output. */ struct _modebits { int code; char ch; } modebits[] = { 040000, 'd', 0400, 'r', 0200, 'w', 0100, 'x', 040, 'r', 020, 'w', 010, 'x', 04, 'r', 02, 'w', 01, 'x', 0, 0 }; int debug=NO; /* Print debugging messages */ int omitbinary; /* omit binary files */ int printfnames; /* Print file names */ int verbose; /* Print everything */ struct fheader *curfil; int nerrs; char *first_file; char tapeblock[SZ_TAPEBUFFER]; char *nextblock = NULL; int nblocks; int in; int out = EOF; extern char *vfn2osfn(); long os_utime(); /* MAIN -- "wtar [-tvdo] [-f tarfile] [files]". If no files are listed the * current directory tree is used as input. If no output file is specified * output is to the standard output. */ main (argc, argv) int argc; char *argv[]; { static char *def_flist[2] = { ".", NULL }; char *argp, **flist; int argno, ftype, i; ZZSTRT(); flist = def_flist; omitbinary = NO; printfnames = debug; verbose = debug; if (debug) { printf ("wtar called with %d arguments:", argc); for (argno=1; (argp = argv[argno]) != NULL; argno++) printf (" %s", argp); printf ("\n"); } /* Process the argument list. */ for (argno=1; (argp = argv[argno]) != NULL; argno++) { if (*argp != '-') { flist = &argv[argno]; break; } else { for (argp++; *argp; argp++) { switch (*argp) { case 'd': debug++; printfnames++; verbose++; break; case 't': printfnames++; break; case 'v': verbose++; break; case 'o': omitbinary++; break; case 'f': if (argv[argno+1]) { argno++; if (debug) printf ("open output file `%s'\n", argv[argno]); out = tape_open (argv[argno], 1); if (out == ERR) { fprintf (stderr, "cannot open `%s'\n", argv[argno]); ZZSTOP(); exit (OSOK+1); } } break; default: fprintf (stderr, "Warning: unknown switch -%c\n", *argp); fflush (stderr); } } } } /* Write to the standard output if no output file specified. * The filename "stdin" is reserved. */ if (out == ERR) { if (debug) printf ("output defaults to stdout\n"); out = tape_open ("stdout", 1); } nextblock = tapeblock; nblocks = 0; /* Put each directory and file listed on the command line to * the tarfile. */ for (i=0; (argp = flist[i]) != NULL; i++) if ((ftype = os_filetype (argp)) == DIRECTORY_FILE) putfiles (argp, out, ""); else tarfileout (argp, out, ftype, ""); /* Close the tarfile. */ endtar (out); tape_close (out); ZZSTOP(); exit (OSOK); } /* PUTFILES -- Put the named directory tree to the output tarfile. We chdir * to each subdirectory to minimize path searches and speed up execution. */ putfiles (dir, out, path) char *dir; /* directory name */ int out; /* output file */ char *path; /* pathname of curr. directory */ { char newpath[SZ_PATHNAME+1]; char fname[SZ_PATHNAME+1]; int ftype, dp; if (debug) printf ("putfiles (%s, %d, %s)\n", dir, out, path); /* Put the directory file itself to the output as a file. */ tarfileout (dir, out, DIRECTORY_FILE, path); if ((dp = os_diropen (dir)) == ERR) { fprintf (stderr, "cannot open subdirectory `%s%s'\n", path, dir); fflush (stderr); return; } sprintf (newpath, "%s%s/", path, dir); if (debug) printf ("change directory to %s\n", newpath); if (os_chdir (dir) == ERR) { os_dirclose (dp); fprintf (stderr, "cannot change directory to `%s'\n", newpath); fflush (stderr); return; } /* Put each file in the directory to the output file. Recursively * read any directories encountered. */ while (os_gfdir (dp, fname, SZ_PATHNAME) > 0) if ((ftype = os_filetype (fname)) == DIRECTORY_FILE) putfiles (fname, out, newpath); else tarfileout (fname, out, ftype, newpath); if (debug) printf ("return from subdirectory %s\n", newpath); if (os_chdir ("..") == ERR) { fprintf (stderr, "cannot return from subdirectory `%s'\n", newpath); fflush (stderr); } os_dirclose (dp); } /* TARFILEOUT -- Write the named file to the output in tar format. */ tarfileout (fname, out, ftype, path) char *fname; /* file to be output */ int out; /* output stream */ int ftype; /* file type */ char *path; /* current path */ { struct _finfo fi; struct fheader fh; int status; if (debug) printf ("put file `%s', type %d\n", fname, ftype); if (ftype == BINARY_FILE && omitbinary) { if (printfnames) { fprintf (stderr, "omit binary file `%s'\n", fname); fflush (stderr); } return; } /* Get info on file to make file header. */ ZFINFO ((PKCHAR *)vfn2osfn(fname,0), &fi, &status); if (status == XERR) { fprintf (stderr, "Warning: can't get info on file `%s'\n", fname); fflush (stderr); return; } /* Format and output the file header. */ strcpy (fh.name, path); strcat (fh.name, fname); if (ftype == DIRECTORY_FILE) { strcat (fh.name, "/"); fh.size = 0; fh.isdir = 1; } else { fh.size = fi.fi_size; fh.isdir = 0; } os_getowner (fname, &fh.uid, &fh.gid); fh.linkflag = 0; /* no links allowed */ strcpy (fh.linkname, ""); fh.mode = u_fmode (fi.fi_perm, fi.fi_type); fh.mtime = os_utime (fi.fi_mtime); if (putheader (&fh, out) == EOF) { fprintf (stderr, "Warning: could not write file header for `%s'\n", fname); fflush (stderr); return; } /* Copy the file data. */ if (fh.size > 0 && !fh.isdir) copyfile (fname, &fh, ftype, out); if (printfnames) { printheader (stdout, &fh, verbose); fflush (stdout); } } /* PUTHEADER -- Encode and write the file header to the output tarfile. */ putheader (fh, out) register struct fheader *fh; /* (input) file header */ int out; /* output file descriptor */ { register char *ip, *op; register int n; union hblock hb; /* Clear the header block. */ for (n=0; n < TBLOCK; n++) hb.dummy[n] = '\0'; /* Encode the file header. */ strcpy (hb.dbuf.name, fh->name); sprintf (hb.dbuf.mode, "%6o ", fh->mode); sprintf (hb.dbuf.uid, "%6o ", fh->uid); sprintf (hb.dbuf.gid, "%6o ", fh->gid); sprintf (hb.dbuf.size, "%11lo ", fh->size); sprintf (hb.dbuf.mtime, "%11lo ", fh->mtime); n = fh->linkflag; if (n >= '0' && n <= '9') hb.dbuf.linkflag = n - '0'; else hb.dbuf.linkflag = 0; strcpy (hb.dbuf.linkname, fh->linkname); /* Encode the checksum value for the file header and then * write the field. Calculate the checksum with the checksum * field blanked out. Compute the actual checksum as the sum of * all bytes in the header block. A sum of zero indicates the * end of the tar file. */ for (n=0; n < 8; n++) hb.dbuf.chksum[n] = ' '; sprintf (hb.dbuf.chksum, "%6.8o", cchksum (hb.dummy, TBLOCK)); if (debug) { printf ("File header:\n"); printf (" name = %s\n", hb.dbuf.name); printf (" mode = %s\n", hb.dbuf.mode); printf (" uid = %s\n", hb.dbuf.uid); printf (" gid = %s\n", hb.dbuf.gid); printf (" size = %-12.12s\n", hb.dbuf.size); printf (" mtime = %-12.12s\n", hb.dbuf.mtime); printf (" chksum = %s\n", hb.dbuf.chksum); printf (" linkflag = %c\n", hb.dbuf.linkflag); printf (" linkname = %s\n", hb.dbuf.linkname); fflush (stdout); } /* Write the header to the tarfile. */ return (putblock (out, hb.dummy)); } /* CCHKSUM -- Compute the checksum of a byte array. */ cchksum (p, nbytes) register char *p; register int nbytes; { register int sum; for (sum=0; --nbytes >= 0; ) sum += *p++; return (sum); } /* PRINTHEADER -- Print the file header in either short or long (verbose) * format, e.g.: * drwxr-xr-x 9 tody 1024 Nov 3 17:53 . */ printheader (fp, fh, verbose) FILE *fp; /* output file */ register struct fheader *fh; /* file header struct */ int verbose; /* long format output */ { register struct _modebits *mp; char *tp, *ctime(); if (!verbose) { fprintf (fp, "%s\n", fh->name); return; } for (mp=modebits; mp->code; mp++) fprintf (fp, "%c", mp->code & fh->mode ? mp->ch : '-'); tp = ctime (&fh->mtime); fprintf (fp, "%3d %4d %2d %8d %-12.12s %-4.4s %s", fh->linkflag, fh->uid, fh->gid, fh->size, tp + 4, tp + 20, fh->name); if (fh->linkflag) fprintf (fp, " -> %s\n", fh->linkname); else fprintf (fp, "\n"); } /* COPYFILE -- Copy bytes from the input file to the output file. Each file * consists of a integral number of TBLOCK size blocks on the output file. */ copyfile (fname, fh, ftype, out) char *fname; /* file being read from */ struct fheader *fh; /* file header structure */ int ftype; /* file type, text or binary */ int out; /* output file */ { register char *bp; register int i; int nbytes, nleft, blocks, fd, count, total, ch; char buf[TBLOCK*2]; bp = buf; total = nbytes = 0; blocks = (fh->size + TBLOCK - 1 ) / TBLOCK; if ((fd = os_open (fname, 0, ftype)) == ERR) { fprintf (stderr, "Warning: cannot open file `%s'\n", fname); fflush (stderr); goto pad_; } while (blocks > 0) { if ((count = os_read (fd, bp, TBLOCK)) == ERR || count > TBLOCK) { fprintf (stderr, "Warning: file read error on `%s'\n", fname); fflush (stderr); if (nerrs++ > MAXERR) { fprintf (stderr, "Too many errors\n"); exit (OSOK+1); } } else { /* Buffer input to TBLOCK blocks. */ if (count == 0) /* EOF */ break; else if ((nbytes += count) < TBLOCK) bp += count; else { putblock (out, buf); blocks--; /* Copy overflow back to beginning... */ if (nbytes > TBLOCK) { nleft = nbytes - TBLOCK; os_amovb (&buf[TBLOCK], buf, nbytes - TBLOCK); } else nleft = 0; bp = (char *) ((int)buf + nleft); total += nbytes; nbytes = nleft; } } } os_close (fd); /* Fill current block and subsequent full blocks until the number of * bytes specified in the file header have been output. All files * occupy an integral number of 512 byte blocks on tape. For text * files, pad with spaces, otherwise pad with nulls. Also, for text * files, add newlines to avoid excessively long lines. */ pad_: ch = (ftype == TEXT_FILE) ? ' ' : '\0'; while (blocks > 0) { for (i=nbytes; i < TBLOCK; i++) if (ftype == TEXT_FILE && i % 64 == 0) buf[i] = '\n'; else buf[i] = ch; if (ftype == TEXT_FILE) buf[TBLOCK-1] = '\n'; putblock (out, buf); blocks--; nbytes = 0; } } /* PUTBLOCK -- Write a block to tape (buffered). */ putblock (out, buf) int out; char *buf; { int nbytes = 0; if (buf) { os_amovb (buf, nextblock, TBLOCK); nextblock += TBLOCK; if (++nblocks == NBLOCK) nbytes = SZ_TAPEBUFFER; } else nbytes = nblocks * TBLOCK; if (nbytes > 0) { if (tape_write (out, tapeblock, nbytes) < nbytes) { fprintf (stderr, "Warning: write error on tarfile\n"); fflush (stderr); } nextblock = tapeblock; nblocks = 0; } return (TBLOCK); } /* ENDTAR -- Write the end of the tar file, i.e., two zero blocks. */ endtar (out) int out; { register int i; union hblock hb; if (debug) printf ("write end of tar file\n"); for (i=0; i < TBLOCK; i++) hb.dummy[i] = '\0'; putblock (out, hb.dummy); /* write 2 null blocks */ putblock (out, hb.dummy); putblock (out, 0); /* flush tape buffer */ } /* U_FMODE -- Convert the IRAF file mode bits to the corresponding UNIX bits * for the tar file header. */ u_fmode (iraf_fmode, ftype) int iraf_fmode; int ftype; { register int in = iraf_fmode; register int m = 0; int exec; exec = (ftype == FI_DIRECTORY || ftype == FI_EXECUTABLE); if (in & 001) m |= 0400; /* Owner READ */ if (in & 002) m |= 0200; /* WRITE */ if (exec) m |= 0100; /* EXECUTE */ if (in & 004) m |= 040; /* Group READ */ if (in & 010) m |= 020; /* WRITE */ if (exec) m |= 010; /* EXECUTE */ if (in & 020) m |= 004; /* World READ */ if (in & 040) m |= 002; /* WRITE */ if (exec) m |= 001; /* EXECUTE */ return (m); }