From 40e5a5811c6ffce9b0974e93cdd927cbcf60c157 Mon Sep 17 00:00:00 2001 From: Joe Hunkeler Date: Tue, 11 Aug 2015 16:51:37 -0400 Subject: Repatch (from linux) of OSX IRAF --- unix/boot/wtar/wtar.c | 717 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 717 insertions(+) create mode 100644 unix/boot/wtar/wtar.c (limited to 'unix/boot/wtar/wtar.c') diff --git a/unix/boot/wtar/wtar.c b/unix/boot/wtar/wtar.c new file mode 100644 index 00000000..2b9c03a1 --- /dev/null +++ b/unix/boot/wtar/wtar.c @@ -0,0 +1,717 @@ +/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc. + */ + +#include +#include +#include +#include +#include + +#define NOKNET +#define import_spp +#define import_finfo +#define import_knames +#include + +#include "../bootProto.h" + + +/* + * 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 + +#define LF_LINK 1 +#define LF_SYMLINK 2 +#define LF_DIR 5 + +/* 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 int ZZSTRT (void); +extern int ZZSTOP (void); +extern int ZFINFO (PKCHAR *fname, XLONG *finfo_struct, XINT *status); + +extern int tape_open (char *fname, int mode); +extern int tape_close (int fd); +extern int tape_write (int fd, char *buf, int nbytes); + + +static void putfiles (char *dir, int out, char *path); +static void tarfileout (char *fname, int out, int ftype, char *path); +static int putheader (register struct fheader *fh, int out); +static int cchksum (register char *p, register int nbytes); +static void printheader (FILE *fp, register struct fheader *fh, int verbose); +static void copyfile (char *fname, struct fheader *fh, int ftype, int out); +static int putblock (int out, char *buf); +static void endtar (int out); +static int u_fmode (int iraf_fmode, int ftype); +static char *dname (char *dir); + + + + +/* 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. + */ +int main (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': + printfnames++; + 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) { + fflush (stdout); + fprintf (stderr, + "cannot open `%s'\n", argv[argno]); + ZZSTOP(); + exit (OSOK+1); + } + } + break; + + default: + fflush (stdout); + 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); + + return (0); +} + + +/* PUTFILES -- Put the named directory tree to the output tarfile. We chdir + * to each subdirectory to minimize path searches and speed up execution. + */ +static void +putfiles ( + char *dir, /* directory name */ + int out, /* output file */ + char *path /* pathname of curr. directory */ +) +{ + char newpath[SZ_PATHNAME+1]; + char oldpath[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) { + fflush (stdout); + fprintf (stderr, "cannot open subdirectory `%s%s'\n", path, dir); + fflush (stderr); + return; + } + + os_fpathname (".", oldpath, SZ_PATHNAME); + sprintf (newpath, "%s%s", dname(path), dir); + strcpy (newpath, dname(newpath)); + + if (debug) + printf ("change directory to %s\n", newpath); + if (os_chdir (dir) == ERR) { + os_dirclose (dp); + fflush (stdout); + 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 (os_symlink (fname, 0, 0)) + tarfileout (fname, out, LF_SYMLINK, newpath); + else 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 (oldpath) == ERR) { + fflush (stdout); + 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. + */ +static void +tarfileout ( + 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) { + fflush (stdout); + fprintf (stderr, "omit binary file `%s'\n", fname); + fflush (stderr); + } + return; + } + + /* Get info on file to make file header. + */ + ZFINFO ((PKCHAR *)vfn2osfn(fname,0), (XLONG *) &fi, (XINT *) &status); + if (status == XERR) { + fflush (stdout); + fprintf (stderr, "Warning: can't get info on file `%s'\n", fname); + fflush (stderr); + return; + } + + /* Format and output the file header. + */ + memset (&fh, 0, sizeof(fh)); + strcpy (fh.name, path); + strcat (fh.name, fname); + strcpy (fh.linkname, ""); + fh.linkflag = 0; + + if (ftype == DIRECTORY_FILE) { + strcpy (fh.name, dname(fh.name)); + fh.size = 0; + fh.isdir = 1; + fh.linkflag = LF_DIR; + } else { + fh.size = fi.fi_size; + fh.isdir = 0; + } + + os_getowner (fname, &fh.uid, &fh.gid); + fh.mode = u_fmode (fi.fi_perm, fi.fi_type); + fh.mtime = os_utime (fi.fi_mtime); + + if (ftype == LF_SYMLINK) { + struct stat fi; + lstat (fname, &fi); + + /* Set attributes of symbolic link, not file pointed to. */ + fh.uid = fi.st_uid; + fh.gid = fi.st_gid; + fh.mode = fi.st_mode; + fh.mtime = fi.st_mtime; + fh.size = 0; + + fh.linkflag = LF_SYMLINK; + os_symlink (fname, fh.linkname, NAMSIZ); + } + + if (putheader (&fh, out) == EOF) { + fflush (stdout); + 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 && !fh.linkflag) + copyfile (fname, &fh, ftype, out); + + if (printfnames) { + printheader (stdout, &fh, verbose); + fflush (stdout); + } +} + + +/* PUTHEADER -- Encode and write the file header to the output tarfile. + */ +static int +putheader ( + register struct fheader *fh, /* (input) file header */ + int out /* output file descriptor */ +) +{ + register char *ip; + register int n; + union hblock hb; + char chksum[10]; + + + /* 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); + + switch (fh->linkflag) { + case LF_SYMLINK: + hb.dbuf.linkflag = '2'; + break; + case LF_DIR: + hb.dbuf.linkflag = '5'; + break; + default: + hb.dbuf.linkflag = '0'; + break; + } + 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 (chksum, "%6o", cchksum (hb.dummy, TBLOCK)); + for (n=0, ip=chksum; n < 8; n++) + hb.dbuf.chksum[n] = *ip++; + + 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. + */ +static int +cchksum ( + 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 . + */ +static void +printheader ( + 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 %8ld %-12.12s %-4.4s %s", + fh->linkflag, + fh->uid, + fh->gid, + fh->size, + tp + 4, tp + 20, + fh->name); + + if (fh->linkflag && *fh->linkname) + 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. + */ +static void +copyfile ( + 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) { + fflush (stdout); + 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) { + fflush (stdout); + 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 *) ((long)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). + */ +static int +putblock (int out, char *buf) +{ + int nbytes = 0; + + if (buf) { + os_amovb (buf, nextblock, TBLOCK); + nextblock += TBLOCK; + if (++nblocks == NBLOCK) + nbytes = SZ_TAPEBUFFER; + } else if (nblocks > 0) + nbytes = SZ_TAPEBUFFER; + + if (nbytes > 0) { + if (tape_write (out, tapeblock, nbytes) < nbytes) { + fflush (stdout); + 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. + */ +static void +endtar (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. + */ +static int +u_fmode (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); +} + + +/* DNAME -- Normalize a directory pathname. For unix, this means convert + * an // sequences into a single /, and make sure the directory pathname ends + * in a single /. + */ +static char * +dname (char *dir) +{ + register char *ip, *op; + static char path[SZ_PATHNAME+1]; + + for (ip=dir, op=path; *ip; *op++ = *ip++) + while (*ip == '/' && *(ip+1) == '/') + ip++; + + if (op > path && *(op-1) != '/') + *op++ = '/'; + *op = EOS; + + return (path); +} -- cgit