koreth%panarthea.ebay@sun.com (Steven Grimm) (07/20/89)
Submitted-by: 7103_300@uwovax.uwo.ca (Eric R. Smith) Posting-number: Volume 2, Issue 47 Archive-name: gnutar/part01 Here is GNU Tar 1.04 ported to TOS. It has all of the features that are applicable on a single tasking machine; it can read, write, and create files that are completely compatible with the Unix tar utility (even the timestamps are the same). This version is more recent than the one distributed with the GCC; in particular, a minor bug in the interpretation of the TZ environment variable is fixed, and the "stat" function returns more sensible read/write/execute flags (and can now be used on "." and ".."). For those not familiar with "tar", it is an archive utility, widely used on Unix systems. It can be used to store whole filesystems. Unlike zoo and arc, it does not do compression; under Unix the "compress" utility is often applied to tar files, and the same could easily be done on the ST. GNU tar has a number of extra features, including the ability to create multi-volume tar files. The files readme.st and tar.tex in the ARC file contain more information. In accordance with the GNU license, the complete source code (in C) is included, as well as the executable file gnutar.ttp. [The binary will appear in comp.binaries.atari.st. -sg] gnutar was compiled with the GCC v1.34, with the new improved library (in fact, a newer more improved version :-). -- Eric R. Smith email: Dept. of Mathematics 7103_300@uwovax.uwo.ca University of Western Ontario 7103_300@uwovax.bitnet London, Ont. Canada N6A 5B7 (a shared mailbox: put my name on ph: (519) 661-3638 the Subj: line, please!) #!/bin/sh # shar: Shell Archiver (v1.22) # # This is part 1 of a multipart archive # do not concatenate these parts, unpack them in order with /bin/sh # # Run the following text with /bin/sh to create: # BUFFER.C # COPYING # CREATE.C # DIFFARCH.C # EXTRACT.C # GETDATE.Y # GETOLDOP.C # LIST.C # MAKEFILE # MSD_DIR.C # MSD_DIR.H # NAMES.C # OPEN3.H # PORT.C # PORT.H # README # README.ST # RMT.H # RTAPE_LI.C # RTAPE_SE.C # TAR.C # TAR.H # TAR.TEX # UPDATE.C # VERSION.C # WILDMAT.C # if test -r s2_seq_.tmp then echo "Must unpack archives in sequence!" next=`cat s2_seq_.tmp`; echo "Please unpack part $next next" exit 1; fi echo "x - extracting BUFFER.C (Text)" sed 's/^X//' << 'SHAR_EOF' > BUFFER.C && X/* X X Copyright (C) 1988 Free Software Foundation X XGNU tar is distributed in the hope that it will be useful, but WITHOUT ANY XWARRANTY. No author or distributor accepts responsibility to anyone Xfor the consequences of using it or for whether it serves any Xparticular purpose or works at all, unless he says so in writing. XRefer to the GNU tar General Public License for full details. X XEveryone is granted permission to copy, modify and redistribute GNU tar, Xbut only under the conditions described in the GNU tar General Public XLicense. A copy of this license is supposed to have been given to you Xalong with GNU tar so you can know your rights and responsibilities. It Xshould be in a file named COPYING. Among other things, the copyright Xnotice and this notice must be preserved on all copies. X XIn other words, go ahead and share GNU tar, but don't try to stop Xanyone else from sharing it farther. Help stamp out software hoarding! X*/ X X/* X * Buffer management for tar. X * X * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985. X * X * @(#) buffer.c 1.28 11/6/87 - gnu X */ X X#include <stdio.h> X#include <errno.h> X#ifdef atarist X#include <types.h> X#include <stat.h> X#include <string.h> X#else X#include <sys/types.h> /* For non-Berkeley systems */ X#include <sys/stat.h> X#endif X X#include <signal.h> X X#ifndef MSDOS X#include <sys/ioctl.h> X#ifndef USG X#include <sys/mtio.h> X#endif X#endif X X#ifdef MSDOS X# include <fcntl.h> X# include <process.h> X#else X# ifdef XENIX X# include <sys/inode.h> X# endif X# include <sys/file.h> X#endif X X#include "tar.h" X#include "port.h" X#include "rmt.h" X XFILE *msg_file; /* Either stdout or stderr: The thing we write messages X (standard msgs, not errors) to. Stdout unless we're X writing a pipe, in which case stderr */ X X#define STDIN 0 /* Standard input file descriptor */ X#define STDOUT 1 /* Standard output file descriptor */ X X#define PREAD 0 /* Read file descriptor from pipe() */ X#define PWRITE 1 /* Write file descriptor from pipe() */ X X#ifdef __GNU__ Xextern void *malloc(); Xextern void *valloc(); X#else Xextern char *malloc(); Xextern char *valloc(); X#endif X Xextern char *index(), *strcat(); Xextern char *strcpy(); X X/* X * V7 doesn't have a #define for this. X */ X#ifndef O_RDONLY X#define O_RDONLY 0 X#endif X#ifndef O_RDWR X#define O_RDWR 2 X#endif X#ifndef O_CREAT X#define O_CREAT 0 X#endif X#ifndef O_BINARY X#define O_BINARY 0 X#endif X X#define MAGIC_STAT 105 /* Magic status returned by child, if X it can't exec. We hope compress/sh X never return this status! */ X Xvoid writeerror(); Xvoid readerror(); Xextern void finish_header(); X X/* X * The record pointed to by save_rec should not be overlaid X * when reading in a new tape block. Copy it to record_save_area first, and X * change the pointer in *save_rec to point to record_save_area. X * Saved_recno records the record number at the time of the save. X * This is used by annofile() to print the record number of a file's X * header record. X */ Xstatic union record **save_rec; X union record record_save_area; Xstatic long saved_recno; X X/* X * PID of child program, if f_compress or remote archive access. X */ Xstatic int childpid = 0; X X/* X * Record number of the start of this block of records X */ Xlong baserec; X X/* X * Error recovery stuff X */ Xstatic int r_error_count; X X/* X * Have we hit EOF yet? X */ Xstatic int eof; X X/* JF we're reading, but we just read the last record and its time to update */ Xextern time_to_start_writing; Xint file_to_switch_to= -1; /* If remote update, close archive, and use X this descriptor to write to */ X Xstatic int volno = 1; /* JF which volume of a multi-volume tape X we're on */ X Xchar *save_name = 0; /* Name of the file we are currently writing */ Xlong save_totsize; /* total size of file we are writing. Only X valid if save_name is non_zero */ Xlong save_sizeleft; /* Where we are in the file we are writing. X Only valid if save_name is non-zero */ X Xint write_archive_to_stdout; X X/* Used by fl_read and fl_write to store the real info about saved names */ Xstatic char real_s_name[NAMSIZ]; Xstatic long real_s_totsize; Xstatic long real_s_sizeleft; X X/* X * Return the location of the next available input or output record. X * Return NULL for EOF. Once we have returned NULL, we just keep returning X * it, to avoid accidentally going on to the next file on the "tape". X */ Xunion record * Xfindrec() X{ X if (ar_record == ar_last) { X if (eof) X return (union record *)NULL; /* EOF */ X flush_archive(); X if (ar_record == ar_last) { X eof++; X return (union record *)NULL; /* EOF */ X } X } X return ar_record; X} X X X/* X * Indicate that we have used all records up thru the argument. X * (should the arg have an off-by-1? XXX FIXME) X */ Xvoid Xuserec(rec) X union record *rec; X{ X while(rec >= ar_record) X ar_record++; X /* X * Do NOT flush the archive here. If we do, the same X * argument to userec() could mean the next record (if the X * input block is exactly one record long), which is not what X * is intended. X */ X if (ar_record > ar_last) X abort(); X} X X X/* X * Return a pointer to the end of the current records buffer. X * All the space between findrec() and endofrecs() is available X * for filling with data, or taking data from. X */ Xunion record * Xendofrecs() X{ X return ar_last; X} X X X/* X * Duplicate a file descriptor into a certain slot. X * Equivalent to BSD "dup2" with error reporting. X */ Xvoid Xdupto(from, to, msg) X int from, to; X char *msg; X{ X int err; X X if (from != to) { X (void) close(to); X err = dup(from); X if (err != to) { X msg_perror("cannot dup %s",msg); X exit(EX_SYSTEM); X } X (void) close(from); X } X} X X#ifdef MSDOS Xvoid Xchild_open() X{ X#ifdef atarist X fprintf(stderr,"GEMDOS %s can't use compressed or remote archives\n",tar); X#else X fprintf(stderr,"MSDOS %s can't use compressed or remote archives\n",tar); X#endif X exit(EX_ARGSBAD); X} X#else Xvoid Xchild_open() X{ X int pipe[2]; X int err = 0; X X int kidpipe[2]; X int kidchildpid; X X void ck_pipe(); X void ck_close(); X X#define READ 0 X#define WRITE 1 X X ck_pipe(pipe); X X childpid=fork(); X if(childpid<0) { X msg_perror("cannot fork"); X exit(EX_SYSTEM); X } X if(childpid>0) { X /* We're the parent. Clean up and be happy */ X /* This, at least, is easy */ X X if(ar_reading) { X f_reblock++; X archive=pipe[READ]; X ck_close(pipe[WRITE]); X } else { X archive = pipe[WRITE]; X ck_close(pipe[READ]); X } X return; X } X X /* We're the kid */ X if(ar_reading) X dupto(pipe[WRITE],STDOUT,"(child) pipe to stdout"); X else X dupto(pipe[READ],STDIN,"(child) pipe to stdin"); X /* ck_close(pipe[READ]); X ck_close(pipe[WRITE]);*/ X X /* We need a child tar only if X 1: we're reading/writing stdin/out (to force reblocking) X 2: the file is to be accessed by rmt (compress doesn't know how X 3: the file is not a plain file */ X#ifdef NO_REMOTE X if(!(ar_file[0]=='-' && ar_file[1]=='\0') && isfile(ar_file)) X#else X if(!(ar_file[0]=='-' && ar_file[1]=='\0') && !_remdev(ar_file) && isfile(ar_file)) X#endif X { X /* We don't need a child tar. Open the archive */ X if(ar_reading) { X archive=open(ar_file, O_RDONLY|O_BINARY, 0666); X if(archive<0) { X msg_perror("can't open archive %s",ar_file); X exit(EX_BADARCH); X } X dupto(archive,STDIN,"archive to stdin"); X /* close(archive); */ X } else { X archive=creat(ar_file,0666); X if(archive<0) { X msg_perror("can't open archive %s",ar_file); X exit(EX_BADARCH); X } X dupto(archive,STDOUT,"archive to stdout"); X /* close(archive); */ X } X } else { X /* We need a child tar */ X ck_pipe(kidpipe); X X kidchildpid=fork(); X if(kidchildpid<0) { X msg_perror("child can't fork"); X exit(EX_SYSTEM); X } X X if(kidchildpid>0) { X /* About to exec compress: set up the files */ X if(ar_reading) { X dupto(kidpipe[READ],STDIN,"((child)) pipe to stdin"); X ck_close(kidpipe[WRITE]); X /* dup2(pipe[WRITE],STDOUT); */ X } else { X /* dup2(pipe[READ],STDIN); */ X dupto(kidpipe[WRITE],STDOUT,"((child)) pipe to stdout"); X ck_close(kidpipe[READ]); X } X /* ck_close(pipe[READ]); */ X /* ck_close(pipe[WRITE]); */ X /* ck_close(kidpipe[READ]); X ck_close(kidpipe[WRITE]); */ X } else { X /* Grandchild. Do the right thing, namely sit here and X read/write the archive, and feed stuff back to compress */ X tar="tar (child)"; X if(ar_reading) { X dupto(kidpipe[WRITE],STDOUT,"[child] pipe to stdout"); X ck_close(kidpipe[READ]); X } else { X dupto(kidpipe[READ],STDIN,"[child] pipe to stdin"); X ck_close(kidpipe[WRITE]); X } X X if (ar_file[0] == '-' && ar_file[1] == '\0') { X if (ar_reading) X archive = STDIN; X else X archive = STDOUT; X } else /* This can't happen if (ar_reading==2) X archive = rmtopen(ar_file, O_RDWR|O_CREAT|O_BINARY, 0666); X else */if(ar_reading) X archive = rmtopen(ar_file, O_RDONLY|O_BINARY, 0666); X else X archive = rmtcreat(ar_file, 0666); X X if (archive < 0) { X msg_perror("can't open archive %s",ar_file); X exit(EX_BADARCH); X } X X if(ar_reading) { X for(;;) { X char *ptr; X int max,count; X X r_error_count = 0; X error_loop: X err=rmtread(archive, ar_block->charptr,(int)(blocksize)); X if(err<0) { X readerror(); X goto error_loop; X } X if(err==0) X break; X ptr = ar_block->charptr; X max = err; X while(max) { X count = (max<RECORDSIZE) ? max : RECORDSIZE; X err=write(STDOUT,ptr,count); X if(err!=count) { X if(err<0) { X msg_perror("can't write to compress"); X exit(EX_SYSTEM); X } else X msg("write to compress short %d bytes",count-err); X count = (err<0) ? 0 : err; X } X ptr+=count; X max-=count; X } X } X } else { X for(;;) { X int n; X char *ptr; X X n=blocksize; X ptr = ar_block->charptr; X while(n) { X err=read(STDIN,ptr,(n<RECORDSIZE) ? n : RECORDSIZE); X if(err<=0) X break; X n-=err; X ptr+=err; X } X /* EOF */ X if(err==0) { X blocksize-=n; X err=rmtwrite(archive,ar_block->charptr,blocksize); X if(err!=(blocksize)) X writeerror(err); X blocksize+=n; X break; X } X if(n) { X msg_perror("can't read from compress"); X exit(EX_SYSTEM); X } X err=rmtwrite(archive, ar_block->charptr, (int)blocksize); X if(err!=blocksize) X writeerror(err); X } X } X X /* close_archive(); */ X exit(0); X } X } X /* So we should exec compress (-d) */ X if(ar_reading) X execlp("compress", "compress", "-d", (char *)0); X else X execlp("compress", "compress", (char *)0); X msg_perror("can't exec compress"); X _exit(EX_SYSTEM); X} X X X/* return non-zero if p is the name of a directory */ Xisfile(p) Xchar *p; X{ X struct stat stbuf; X X if(stat(p,&stbuf)<0) X return 1; X if((stbuf.st_mode&S_IFMT)==S_IFREG) X return 1; X return 0; X} X X#endif X X#ifdef DONTDEF X/* X * Fork a child to deal with remote files or compression. X * If rem_host is zero, we are called only for compression. X */ Xvoid Xchild_open(rem_host, rem_file) X char *rem_host, *rem_file; X{ X X#ifdef MSDOS X fprintf(stderr, X#ifdef atarist X "MSDOS %s cannot deal with compressed or remote archives\n", tar); X#else X "MSDOS %s cannot deal with compressed or remote archives\n", tar); X#endif X exit(EX_ARGSBAD); X#else X X int pipes[2]; X int err; X struct stat arstat; X char cmdbuf[1000]; /* For big file and host names */ X X int other_pipes[2]; /* JF for remote update and Multivol */ X X /* Create a pipe to talk to the child over */ X err = pipe(pipes); X if (err < 0) { X msg_perror ("cannot create pipe to child"); X exit(EX_SYSTEM); X } X X if(cmd_mode==CMD_CAT || cmd_mode==CMD_UPDATE || cmd_mode==CMD_APPEND) { X err=pipe(other_pipes); X if(err<0) { X msg_perror("cannot create a pipe"); X exit(EX_SYSTEM); X } X } X X /* Fork child process */ X childpid = fork(); X if (childpid < 0) { X msg_perror("cannot fork"); X exit(EX_SYSTEM); X } X X /* X * Parent process. Clean up. X * X * We always close the archive file (stdin, stdout, or opened file) X * since the child will end up reading or writing that for us. X * Note that this may leave standard input closed. X * We close the child's end of the pipe since they will handle X * that too; and we set <archive> to the other end of the pipe. X * X * If reading, we set f_reblock since reading pipes or network X * sockets produces odd length data. X */ X if (childpid > 0) { X if(f_multivol || cmd_mode==CMD_CAT || cmd_mode==CMD_UPDATE || cmd_mode==CMD_APPEND) { X (void)close(other_pipes[PREAD]); X file_to_switch_to=other_pipes[PWRITE]; X } X (void) close (archive); X if (ar_reading) { X (void) close (pipes[PWRITE]); X archive = pipes[PREAD]; X f_reblock++; X } else { X (void) close (pipes[PREAD]); X archive = pipes[PWRITE]; X } X return; X } X X /* X * Child process. X */ X X /* We can't do any of these to a compressed file, so it'd better X be remote */ X if(f_multivol || cmd_mode==CMD_UPDATE || cmd_mode==CMD_APPEND || cmd_mode==CMD_CAT) { X char mode; X X (void) close(pipes[PREAD]); X dupto(pipes[PWRITE],STDOUT,"(child) to stdout"); X (void) close(other_pipes[PWRITE]); X dupto(other_pipes[PREAD],STDIN,"(child) to stdin"); X if(f_multivol) { X if(cmd_mode==CMD_CREATE) X mode='c'; X else if(cmd_mode==CMD_DIFF || cmd_mode==CMD_LIST || cmd_mode==CMD_EXTRACT) X mode='r'; X else mode='c'; X } else X mode='c'; X X (void) sprintf(cmdbuf,"rsh '%s' tar -S%c -b %d -f '%s'",rem_host,mode,blocking,rem_file); X X execlp("sh","sh","-c",cmdbuf,(char *)0); X msg_perror("cannot exec sh"); X exit(MAGIC_STAT); X } X X if (ar_reading) { X /* X * Reading from the child... X * X * Close the read-side of the pipe, which our parent will use. X * Move the write-side of pipe to stdout, X * If local, move archive input to child's stdin, X * then run the child. X */ X (void) close (pipes[PREAD]); X dupto(pipes[PWRITE], STDOUT, "to stdout"); X if (rem_host) { X (void) close (STDIN); /* rsh abuses stdin */ X if (STDIN != open("/dev/null")) X msg_perror("Can't open /dev/null"); X (void) sprintf(cmdbuf, X "rsh '%s' dd '<%s' bs=%db", X rem_host, rem_file, blocking); X if (f_compress) X strcat(cmdbuf, "| compress -d"); X#ifdef DEBUG X fprintf(stderr, "Exec-ing: %s\n", cmdbuf); X#endif X execlp("sh", "sh", "-c", cmdbuf, (char *)0); X msg_perror("cannot exec sh"); X } else { X /* X * If we are reading a disk file, compress is OK; X * otherwise, we have to reblock the input in case it's X * coming from a tape drive. This is an optimization. X */ X dupto(archive, STDIN, "to stdin"); X err = fstat(STDIN, &arstat); X if (err != 0) { X msg_perror("can't fstat archive"); X exit(EX_SYSTEM); X } X if ((arstat.st_mode & S_IFMT) == S_IFREG) { X execlp("compress", "compress", "-d", (char *)0); X msg_perror("cannot exec compress"); X } else { X /* Non-regular file needs dd before compress */ X (void) sprintf(cmdbuf, X "dd bs=%db | compress -d", X blocking); X#ifdef DEBUG X fprintf(stderr, "Exec-ing: %s\n", cmdbuf); X#endif X execlp("sh", "sh", "-c", cmdbuf, (char *)0); X msg_perror("cannot exec sh"); X } X } X exit(MAGIC_STAT); X } else { X /* X * Writing archive to the child. X * It would like to run either: X * compress X * compress | dd obs=20b X * rsh 'host' dd obs=20b '>foo' X * or compress | rsh 'host' dd obs=20b '>foo' X * X * We need the dd to reblock the output to the X * user's specs, if writing to a device or over X * the net. However, it produces a stupid X * message about how many blocks it processed. X * Because the shell on the remote end could be just X * about any shell, we can't depend on it to do X * redirect stderr properly for us -- the csh X * doesn't use the same syntax as the Bourne shell. X * On the other hand, if we just ignore stderr on X * this end, we won't see errors from rsh, or from X * the inability of "dd" to write its output file. X * The combination of the local sh, the rsh, the X * remote csh, and maybe a remote sh conspires to mess X * up any possible quoting method, so grumble! we X * punt and just accept the fucking "xxx blocks" X * messages. The real fix would be a "dd" that X * would shut up. X * X * Close the write-side of the pipe, which our parent will use. X * Move the read-side of the pipe to stdin, X * If local, move archive output to the child's stdout. X * then run the child. X */ X (void) close (pipes[PWRITE]); X dupto(pipes[PREAD], STDIN, "to stdin"); X if (!rem_host) X dupto(archive, STDOUT, "to stdout"); X X cmdbuf[0] = '\0'; X if (f_compress) { X if (!rem_host) { X err = fstat(STDOUT, &arstat); X if (err != 0) { X msg_perror("can't fstat archive"); X exit(EX_SYSTEM); X } X if ((arstat.st_mode & S_IFMT) == S_IFREG) { X execlp("compress", "compress", (char *)0); X msg_perror("cannot exec compress"); X _exit(EX_SYSTEM); X } X } X strcat(cmdbuf, "compress | "); X } X if (rem_host) { X (void) sprintf(cmdbuf+strlen(cmdbuf), X "rsh '%s' dd obs=%db '>%s'", X rem_host, blocking, rem_file); X } else { X (void) sprintf(cmdbuf+strlen(cmdbuf), X "dd obs=%db", X blocking); X } X#ifdef DEBUG X fprintf(stderr, "Exec-ing: %s\n", cmdbuf); X#endif X execlp("sh", "sh", "-c", cmdbuf, (char *)0); X msg_perror("cannot exec sh"); X exit(MAGIC_STAT); X } X#endif /* MSDOS */ X} X X#endif X X/* X * Open an archive file. The argument specifies whether we are X * reading or writing. X */ X/* JF if the arg is 2, open for reading and writing. */ Xopen_archive(reading) X int reading; X{ X msg_file = stdout; X X/* #ifndef MSDOS */ X#ifdef DonTDeF X /* JF Not doing this on MSdos systems looks like a good idea. X Does this stuff about 'slash' mean I can't simply say X remotesys:iggy to store it in 'iggy' on remotesys? Do I X have to say remotesys:./iggy in order for it to work? X Who knows?? */ X colon = index(ar_file, ':'); X if (colon) { X slash = index(ar_file, '/'); X if (slash && slash > colon) { X /* X * Remote file specified. Parse out separately, X * and don't try to open it on the local system. X */ X rem_file = colon + 1; X rem_host = ar_file; X *colon = '\0'; X goto gotit; X } X } X#endif X X if (blocksize == 0) { X msg("invalid value for blocksize"); X exit(EX_ARGSBAD); X } X X /*NOSTRICT*/ X if(f_multivol) { X ar_block = (union record *) valloc((unsigned)(blocksize+(2*RECORDSIZE))); X if(ar_block) X ar_block += 2; X } else X ar_block = (union record *) valloc((unsigned)blocksize); X if (!ar_block) { X msg("could not allocate memory for blocking factor %d", X blocking); X exit(EX_ARGSBAD); X } X X ar_record = ar_block; X ar_last = ar_block + blocking; X ar_reading = reading; X X if (f_compress) { X if(reading==2 || f_verify) { X msg("cannot update or verify compressed archives"); X exit(EX_ARGSBAD); X } X child_open(); X if(!reading && ar_file[0]=='-' && ar_file[1]=='\0') X msg_file = stderr; X /* child_open(rem_host, rem_file); */ X } else if (ar_file[0] == '-' && ar_file[1] == '\0') { X f_reblock++; /* Could be a pipe, be safe */ X if(f_verify) { X msg("can't verify stdin/stdout archive"); X exit(EX_ARGSBAD); X } X if(reading==2) { X archive=STDIN; X msg_file=stderr; X write_archive_to_stdout++; X } else if (reading) X archive = STDIN; X else { X archive = STDOUT; X msg_file = stderr; X } X } else if (reading==2 || f_verify) { X archive = rmtopen(ar_file, O_RDWR|O_CREAT|O_BINARY, 0666); X } else if(reading) { X archive = rmtopen(ar_file, O_RDONLY|O_BINARY, 0666); X } else { X archive = rmtcreat(ar_file, 0666); X } X X if (archive < 0) { X msg_perror("can't open %s",ar_file); X exit(EX_BADARCH); X } X#ifdef MSDOS X setmode(archive, O_BINARY); X#endif X X if (reading) { X ar_last = ar_block; /* Set up for 1st block = # 0 */ X (void) findrec(); /* Read it in, check for EOF */ X X if(f_volhdr) { X union record *head; X char *ptr; X X if(f_multivol) { X ptr=malloc(strlen(f_volhdr)+20); X sprintf(ptr,"%s Volume %d",f_volhdr,1); X } else X ptr=f_volhdr; X head=findrec(); X if(!head) X exit(EX_BADARCH); X if(strcmp(ptr,head->header.name)) { X msg("Volume mismatch! %s!=%s\n",ptr,head->header.name); X exit(EX_BADARCH); X } X if(ptr!=f_volhdr) X free(ptr); X } X } else if(f_volhdr) { X bzero((void *)ar_block,RECORDSIZE); X if(f_multivol) X sprintf(ar_block->header.name,"%s Volume 1",f_volhdr); X else X strcpy(ar_block->header.name,f_volhdr); X ar_block->header.linkflag = LF_VOLHDR; X finish_header(ar_block); X /* ar_record++; */ X } X} X X X/* X * Remember a union record * as pointing to something that we X * need to keep when reading onward in the file. Only one such X * thing can be remembered at once, and it only works when reading X * an archive. X * X * We calculate "offset" then add it because some compilers end up X * adding (baserec+ar_record), doing a 9-bit shift of baserec, then X * subtracting ar_block from that, shifting it back, losing the top 9 bits. X */ Xsaverec(pointer) X union record **pointer; X{ X long offset; X X save_rec = pointer; X offset = ar_record - ar_block; X saved_recno = baserec + offset; X} X X/* X * Perform a write to flush the buffer. X */ X X/*send_buffer_to_file(); X if(new_volume) { X deal_with_new_volume_stuff(); X send_buffer_to_file(); X } X */ X Xfl_write() X{ X int err; X int copy_back; X extern int errno; X#ifdef TEST X static long test_written = 0; X#endif X X#ifdef TEST X if(test_written>=30720) { X errno = ENOSPC; X err = 0; X } else X#endif X err = rmtwrite(archive, ar_block->charptr,(int) blocksize); X if(err!=blocksize && !f_multivol) X writeerror(err); X X#ifdef TEST X if(err>0) X test_written+=err; X#endif X if (err == blocksize) { X if(f_multivol) { X if(!save_name) { X real_s_name[0]='\0'; X real_s_totsize=0; X real_s_sizeleft = 0; X return; X } X#ifdef MSDOS X if(save_name[1]==':') X save_name+=2; X#endif X while(*save_name=='/') X save_name++; X X strcpy(real_s_name,save_name); X real_s_totsize = save_totsize; X real_s_sizeleft = save_sizeleft; X } X return; X } X X#ifndef atarist /* ENOSPC isn't available under GEMDOS; punt */ X /* We're multivol Panic if we didn't get the right kind of response */ X if(err>0 || (errno!=ENOSPC && errno!=EIO)) X writeerror(err); X#endif X X if(new_volume(0)<0) X return; X#ifdef TEST X test_written=0; X#endif X if(f_volhdr && real_s_name[0]) { X copy_back=2; X ar_block-=2; X } else if(f_volhdr || real_s_name[0]) { X copy_back = 1; X ar_block--; X } else X copy_back = 0; X if(f_volhdr) { X bzero((void *)ar_block,RECORDSIZE); X sprintf(ar_block->header.name,"%s Volume %d",f_volhdr,volno); X ar_block->header.linkflag = LF_VOLHDR; X finish_header(ar_block); X } X if(real_s_name[0]) { X extern void to_oct(); X int tmp; X X if(f_volhdr) X ar_block++; X bzero((void *)ar_block,RECORDSIZE); X strcpy(ar_block->header.name,real_s_name); X ar_block->header.linkflag = LF_MULTIVOL; X to_oct((long)real_s_sizeleft,1+12, X ar_block->header.size); X to_oct((long)real_s_totsize-real_s_sizeleft, X 1+12,ar_block->header.offset); X tmp=f_verbose; X f_verbose=0; X finish_header(ar_block); X f_verbose=tmp; X if(f_volhdr) X ar_block--; X } X X err = rmtwrite(archive, ar_block->charptr,(int) blocksize); X if(err!=blocksize) X writeerror(err); X X#ifdef TEST X test_written = blocksize; X#endif X if(copy_back) { X ar_block+=copy_back; X bcopy((void *)(ar_block+blocking-copy_back), X (void *)ar_record, X copy_back*RECORDSIZE); X ar_record+=copy_back; X X if(real_s_sizeleft>=copy_back*RECORDSIZE) X real_s_sizeleft-=copy_back*RECORDSIZE; X else if((real_s_sizeleft+RECORDSIZE-1)/RECORDSIZE<=copy_back) X real_s_name[0] = '\0'; X else { X#ifdef MSDOS X if(save_name[1]==':') X save_name+=2; X#endif X while(*save_name=='/') X save_name++; X X strcpy(real_s_name,save_name); X real_s_sizeleft = save_sizeleft; X real_s_totsize=save_totsize; X } X copy_back = 0; X } X} X X/* Handle write errors on the archive. Write errors are always fatal */ X/* Hitting the end of a volume does not cause a write error unless the write X* was the first block of the volume */ X Xvoid Xwriteerror(err) Xint err; X{ X if (err < 0) { X msg_perror("can't write to %s",ar_file); X exit(EX_BADARCH); X } else { X msg("write to %s short %d bytes",ar_file,blocksize-err); X exit(EX_BADARCH); X } X} X X/* X * Handle read errors on the archive. X * X * If the read should be retried, readerror() returns to the caller. X */ Xvoid Xreaderror() X{ X# define READ_ERROR_MAX 10 X X read_error_flag++; /* Tell callers */ X X msg_perror("read error on %s",ar_file); X X if (baserec == 0) { X /* First block of tape. Probably stupidity error */ X exit(EX_BADARCH); X } X X /* X * Read error in mid archive. We retry up to READ_ERROR_MAX times X * and then give up on reading the archive. We set read_error_flag X * for our callers, so they can cope if they want. X */ X if (r_error_count++ > READ_ERROR_MAX) { X msg("Too many errors, quitting."); X exit(EX_BADARCH); X } X return; X} X X X/* X * Perform a read to flush the buffer. X */ Xfl_read() X{ X int err; /* Result from system call */ X int left; /* Bytes left */ X char *more; /* Pointer to next byte to read */ X X /* X * Clear the count of errors. This only applies to a single X * call to fl_read. We leave read_error_flag alone; it is X * only turned off by higher level software. X */ X r_error_count = 0; /* Clear error count */ X X /* X * If we are about to wipe out a record that X * somebody needs to keep, copy it out to a holding X * area and adjust somebody's pointer to it. X */ X if (save_rec && X *save_rec >= ar_record && X *save_rec < ar_last) { X record_save_area = **save_rec; X *save_rec = &record_save_area; X } X if(write_archive_to_stdout && baserec!=0) { X err=rmtwrite(1, ar_block->charptr, blocksize); X if(err!=blocksize) X writeerror(err); X } X if(f_multivol) { X if(save_name) { X if(save_name!=real_s_name) { X#ifdef MSDOS X if(save_name[1]==':') X save_name+=2; X#endif X while(*save_name=='/') X save_name++; X X strcpy(real_s_name,save_name); X save_name=real_s_name; X } X real_s_totsize = save_totsize; X real_s_sizeleft = save_sizeleft; X X } else { X real_s_name[0]='\0'; X real_s_totsize=0; X real_s_sizeleft = 0; X } X } X Xerror_loop: X err = rmtread(archive, ar_block->charptr, (int)blocksize); X if (err == blocksize) X return; X X if (err < 0) { X readerror(); X goto error_loop; /* Try again */ X } X X if(err == 0 && f_multivol) { X union record *head; X X try_volume: X if(new_volume((cmd_mode==CMD_APPEND || cmd_mode==CMD_CAT || cmd_mode==CMD_UPDATE) ? 2 : 1)<0) X return; X vol_error: X err = rmtread(archive, ar_block->charptr,(int) blocksize); X if(err < 0) { X readerror(); X goto vol_error; X } X if(err!=blocksize) X goto short_read; X X head=ar_block; X X if(head->header.linkflag==LF_VOLHDR) { X if(f_volhdr) { X char *ptr; X X ptr=(char *)malloc(strlen(f_volhdr)+20); X sprintf(ptr,"%s Volume %d",f_volhdr,volno); X if(strcmp(ptr,head->header.name)) { X msg("Volume mismatch! %s!=%s\n",ptr,head->header.name); X --volno; X free(ptr); X goto try_volume; X } X free(ptr); X } X if(f_verbose) X fprintf(msg_file,"Reading %s\n",head->header.name); X head++; X } else if(f_volhdr) { X msg("Warning: No volume header!"); X } X X if(real_s_name[0]) { X long from_oct(); X X if(head->header.linkflag!=LF_MULTIVOL || strcmp(head->header.name,real_s_name)) { X msg("%s is not continued on this volume!",real_s_name); X --volno; X goto try_volume; X } X if(real_s_totsize!=from_oct(1+12,head->header.size)+from_oct(1+12,head->header.offset)) { X msg("%s is the wrong size (%ld!=%ld+%ld)", X head->header.name,save_totsize, X from_oct(1+12,head->header.size), X from_oct(1+12,head->header.offset)); X --volno; X goto try_volume; X } X if(real_s_totsize-real_s_sizeleft!=from_oct(1+12,head->header.offset)) { X msg("This volume is out of sequence"); X --volno; X goto try_volume; X } X head++; X } X ar_record=head; X return; X } X X short_read: X more = ar_block->charptr + err; X left = blocksize - err; X Xagain: X if (0 == (((unsigned)left) % RECORDSIZE)) { X /* FIXME, for size=0, multi vol support */ X /* On the first block, warn about the problem */ X if (!f_reblock && baserec == 0 && f_verbose && err > 0) { X msg("Blocksize = %d record%s", X err / RECORDSIZE, (err > RECORDSIZE)? "s": ""); X } X ar_last = ar_block + ((unsigned)(blocksize - left))/RECORDSIZE; X return; X } X if (f_reblock) { X /* X * User warned us about this. Fix up. X */ X if (left > 0) { Xerror2loop: X err = rmtread(archive, more, (int)left); X if (err < 0) { X readerror(); X goto error2loop; /* Try again */ X } X if (err == 0) { X msg("archive %s EOF not no block boundry",ar_file); X exit(EX_BADARCH); X } X left -= err; X more += err; X goto again; X } X } else { X msg("only read %d byes from archive %s",err,ar_file); X exit(EX_BADARCH); X } X} X X X/* X * Flush the current buffer to/from the archive. X */ Xflush_archive() X{ X baserec += ar_last - ar_block; /* Keep track of block #s */ X ar_record = ar_block; /* Restore pointer to start */ X ar_last = ar_block + blocking; /* Restore pointer to end */ X X if (ar_reading) { X if(time_to_start_writing) { X time_to_start_writing=0; X ar_reading=0; X X if(file_to_switch_to>=0) { X rmtclose(archive); X archive=file_to_switch_to; X } else X (void)backspace_output(); X fl_write(); X } else X fl_read(); X } else { X fl_write(); X } X} X X/* Backspace the archive descriptor by one blocks worth. X If its a tape, MTIOCTOP will work. If its something else, X we try to seek on it. If we can't seek, we lose! */ Xbackspace_output() X{ X long cur; X /* int er; */ X extern char *output_start; X extern int errno; X X#ifdef MTIOCTOP X struct mtop t; X X t.mt_op = MTBSR; X t.mt_count = 1; X if((rmtioctl(archive,MTIOCTOP,&t))>=0) X return 1; X if(errno==EIO && (rmtioctl(archive,MTIOCTOP,&t))>=0) X return 1; X#endif X X cur=rmtlseek(archive,0L,1); X cur-=blocksize; X /* Seek back to the beginning of this block and X start writing there. */ X X if(rmtlseek(archive,cur,0)!=cur) { X /* Lseek failed. Try a different method */ X msg("Couldn't backspace archive file. It may be unreadable without -i."); X /* Replace the first part of the block with nulls */ X if(ar_block->charptr!=output_start) X bzero(ar_block->charptr,output_start-ar_block->charptr); X return 2; X } X return 3; X} X X X/* X * Close the archive file. X */ Xclose_archive() X{ X int child; X int status; X X if (time_to_start_writing || !ar_reading) X flush_archive(); X if(cmd_mode==CMD_DELETE) { X long pos; X X pos = rmtlseek(archive,0L,1); X#ifndef MSDOS X /* FIXME does ftruncate really take an INT?! */ X (void) ftruncate(archive,(int)pos); X#else X (void)rmtwrite(archive,"",0); X#endif X } X if(f_verify) X verify_volume(); X (void) rmtclose(archive); X X#ifndef MSDOS X if (childpid) { X /* X * Loop waiting for the right child to die, or for X * no more kids. X */ X while (((child = wait(&status)) != childpid) && child != -1) X ; X X if (child != -1) { X switch (TERM_SIGNAL(status)) { X case 0: X /* Child voluntarily terminated -- but why? */ X if (TERM_VALUE(status) == MAGIC_STAT) { X exit(EX_SYSTEM);/* Child had trouble */ X } X if (TERM_VALUE(status) == (SIGPIPE + 128)) { X /* X * /bin/sh returns this if its child X * dies with SIGPIPE. 'Sok. X */ X break; X } else if (TERM_VALUE(status)) X msg("child returned status %d", X TERM_VALUE(status)); X case SIGPIPE: X break; /* This is OK. */ X X default: X msg("child died with signal %d%s", X TERM_SIGNAL(status), X TERM_COREDUMP(status)? " (core dumped)": ""); X } X } X } X#endif /* MSDOS */ X} X X X#ifdef DONTDEF X/* X * Message management. X * X * anno writes a message prefix on stream (eg stdout, stderr). X * X * The specified prefix is normally output followed by a colon and a space. X * However, if other command line options are set, more output can come X * out, such as the record # within the archive. X * X * If the specified prefix is NULL, no output is produced unless the X * command line option(s) are set. X * X * If the third argument is 1, the "saved" record # is used; if 0, the X * "current" record # is used. X */ Xvoid Xanno(stream, prefix, savedp) X FILE *stream; X char *prefix; X int savedp; X{ X# define MAXANNO 50 X char buffer[MAXANNO]; /* Holds annorecment */ X# define ANNOWIDTH 13 X int space; X long offset; X int save_e; X X save_e=errno; X /* Make sure previous output gets out in sequence */ X if (stream == stderr) X fflush(stdout); X if (f_sayblock) { X if (prefix) { X fputs(prefix, stream); X putc(' ', stream); X } X offset = ar_record - ar_block; X (void) sprintf(buffer, "rec %d: ", X savedp? saved_recno: X baserec + offset); X fputs(buffer, stream); X space = ANNOWIDTH - strlen(buffer); X if (space > 0) { X fprintf(stream, "%*s", space, ""); X } X } else if (prefix) { X fputs(prefix, stream); X fputs(": ", stream); X } X errno=save_e; X} X#endif X X/* We've hit the end of the old volume. Close it and open the next one */ X/* Values for type: 0: writing 1: reading 2: updating */ Xnew_volume(type) Xint type; X{ X /* int c; */ X char inbuf[80]; X char *p; X extern int now_verifying; X char *getenv(); X X if(now_verifying) X return -1; X if(f_verify) X verify_volume(); X if(rmtclose(archive)<0) { X msg_perror("can't close %s",ar_file); X exit(EX_BADARCH); X } X volno++; X for(;;) { X fprintf(msg_file,"Prepare volume #%d and hit return: ",volno); X if(fgets(inbuf,80,stdin)==0 || inbuf[0]=='\n') X break; X switch(inbuf[0]) { X case '?': X fprintf(msg_file,"\ X n [name] Give a new filename for the next (and subsequent) volume(s)\n\ X q Abort tar\n\ X ! Spawn a subshell\n\ X ? Print this list\n"); X break; X X case 'q': /* Quit */ X fprintf(msg_file,"No new volume; exiting.\n"); X if(cmd_mode!=CMD_EXTRACT && cmd_mode!=CMD_LIST && cmd_mode!=CMD_DIFF) X msg("Warning: Archive is INCOMPLETE!"); X exit(EX_BADARCH); X X case 'n': /* Get new file name */ X { X char *q,*r; X static char *old_name; X X for(q= &inbuf[1];*q==' ' || *q=='\t';q++) X ; X for(r=q;*r;r++) X if(*r=='\n') X *r='\0'; X if(old_name) X free(old_name); X old_name=p=(char *)malloc((unsigned)(strlen(q)+2)); X if(p==0) { X msg("Can't allocate memory for name"); X exit(EX_SYSTEM); X } X (void) strcpy(p,q); X ar_file=p; X break; X } X X case '!': X#ifdef MSDOS X# ifdef atarist X spawnl(P_WAIT,getenv("SHELL"),NULL); X# else X spawnl(P_WAIT,getenv("COMSPEC"),"-",0); X# endif X#else X /* JF this needs work! */ X switch(fork()) { X case -1: X msg_perror("can't fork!"); X break; X case 0: X p=getenv("SHELL"); X if(p==0) p="/bin/sh"; X execlp(p,"-sh","-i",0); X msg_perror("can't exec a shell %s",p); X _exit(55); X default: X wait(0); X break; X } X#endif X break; X } X } X X if(type==2 || f_verify) X archive=rmtopen(ar_file,O_RDWR|O_CREAT,0666); X else if(type==1) X archive=rmtopen(ar_file,O_RDONLY,0666); X else if(type==0) X archive=rmtcreat(ar_file,0666); X else X archive= -1; X X if(archive<0) { X msg_perror("can't open %s",ar_file); X exit(EX_BADARCH); X } X#ifdef MSDOS X setmode(archive,O_BINARY); X#endif X return 0; X} X X/* this is a useless function that takes a buffer returned by wantbytes X and does nothing with it. If the function called by wantbytes returns X an error indicator (non-zero), this function is called for the rest of X the file. X */ Xint Xno_op(size,data) Xint size; Xchar *data; X{ X return 0; X} X X/* Some other routine wants SIZE bytes in the archive. For each chunk of X the archive, call FUNC with the size of the chunk, and the address of X the chunk it can work with. X */ Xint Xwantbytes(size,func) Xlong size; Xint (*func)(); X{ X char *data; X long data_size; X X while(size) { X data = findrec()->charptr; X if (data == NULL) { /* Check it... */ X msg("Unexpected EOF on archive file"); X return -1; X } X data_size = endofrecs()->charptr - data; X if(data_size>size) X data_size=size; X if((*func)(data_size,data)) X func=no_op; X userec((union record *)(data + data_size - 1)); X size-=data_size; X } X return 0; X} SHAR_EOF chmod 0644 BUFFER.C || echo "restore of BUFFER.C fails" echo "x - extracting COPYING (Text)" sed 's/^X//' << 'SHAR_EOF' > COPYING && X X GNU TAR GENERAL PUBLIC LICENSE X (Clarified 11 Feb 1988) X X Copyright (C) 1988 Richard M. Stallman X Everyone is permitted to copy and distribute verbatim copies X of this license, but changing it is not allowed. You can also X use this wording to make the terms for other programs. X X The license agreements of most software companies keep you at the Xmercy of those companies. By contrast, our general public license is Xintended to give everyone the right to share GNU tar. To make sure that Xyou get the rights we want you to have, we need to make restrictions Xthat forbid anyone to deny you these rights or to ask you to surrender Xthe rights. Hence this license agreement. X X Specifically, we want to make sure that you have the right to give Xaway copies of GNU tar, that you receive source code or else can get it Xif you want it, that you can change GNU tar or use pieces of it in new Xfree programs, and that you know you can do these things. X X To make sure that everyone has such rights, we have to forbid you to Xdeprive anyone else of these rights. For example, if you distribute Xcopies of GNU tar, you must give the recipients all the rights that you Xhave. You must make sure that they, too, receive or can get the Xsource code. And you must tell them their rights. X X Also, for our own protection, we must make certain that everyone Xfinds out that there is no warranty for GNU tar. If GNU tar is modified by Xsomeone else and passed on, we want its recipients to know that what Xthey have is not what we distributed, so that any problems introduced Xby others will not reflect on our reputation. X X Therefore we (Richard Stallman and the Free Software Foundation, XInc.) make the following terms which say what you must do to be Xallowed to distribute or change GNU tar. X X X COPYING POLICIES X X 1. You may copy and distribute verbatim copies of GNU tar source code as Xyou receive it, in any medium, provided that you conspicuously and Xappropriately publish on each copy a valid copyright notice "Copyright X(C) 1988 Free Software Foundation, Inc." (or with whatever year is Xappropriate); keep intact the notices on all files that refer to this XLicense Agreement and to the absence of any warranty; and give any Xother recipients of the GNU tar program a copy of this License XAgreement along with the program. You may charge a distribution Xfee for the physical act of transferring a copy. X X 2. You may modify your copy or copies of GNU tar or any portion of it, Xand copy and distribute such modifications under the terms of XParagraph 1 above, provided that you also do the following: X X a) cause the modified files to carry prominent notices stating X that you changed the files and the date of any change; and X X b) cause the whole of any work that you distribute or publish, X that in whole or in part contains or is a derivative of GNU tar or X any part thereof, to be licensed at no charge to all third X parties on terms identical to those contained in this License X Agreement (except that you may choose to grant more extensive X warranty protection to third parties, at your option). X X c) You may charge a distribution fee for the physical act of X transferring a copy, and you may at your option offer warranty X protection in exchange for a fee. X XMere aggregation of another unrelated program with this program (or its Xderivative) on a volume of a storage or distribution medium does not bring Xthe other program under the scope of these terms. X X 3. You may copy and distribute GNU CC (or a portion or derivative of it, Xunder Paragraph 2) in object code or executable form under the terms of XParagraphs 1 and 2 above provided that you also do one of the following: X X a) accompany it with the complete corresponding machine-readable X source code, which must be distributed under the terms of X Paragraphs 1 and 2 above; or, X X b) accompany it with a written offer, valid for at least three X years, to give any third party free (except for a nominal X shipping charge) a complete machine-readable copy of the X corresponding source code, to be distributed under the terms of X Paragraphs 1 and 2 above; or, X X c) accompany it with the information you received as to where the X corresponding source code may be obtained. (This alternative is X allowed only for noncommercial distribution and only if you X received the program in object code or executable form alone.) X XFor an executable file, complete source code means all the source code for Xall modules it contains; but, as a special exception, it need not include Xsource code for modules which are standard libraries that accompany the Xoperating system on which the executable file runs. X X 4. You may not copy, sublicense, distribute or transfer GNU tar Xexcept as expressly provided under this License Agreement. Any attempt Xotherwise to copy, sublicense, distribute or transfer GNU tar is void and Xyour rights to use the program under this License agreement shall be Xautomatically terminated. However, parties who have received computer Xsoftware programs from you with this License Agreement will not have Xtheir licenses terminated so long as such parties remain in full compliance. X X 5. If you wish to incorporate parts of GNU tar into other free Xprograms whose distribution conditions are different, write to the Free XSoftware Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not yet Xworked out a simple rule that can be stated here, but we will often permit Xthis. We will be guided by the two goals of preserving the free status of Xall derivatives of our free software and of promoting the sharing and reuse Xof software. X X XYour comments and suggestions about our licensing policies and our Xsoftware are welcome! Please contact the Free Software Foundation, Inc., X675 Mass Ave, Cambridge, MA 02139, or call (617) 876-3296. X X NO WARRANTY X X BECAUSE GNU tar IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY NO XWARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT XWHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC, XRICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE GNU tar "AS IS" WITHOUT XWARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT XLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR XA PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND XPERFORMANCE OF GNU tar IS WITH YOU. SHOULD GNU tar PROVE DEFECTIVE, YOU XASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. X X IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M. SHAR_EOF echo "End of part 1" echo "File COPYING is continued in part 2" echo "2" > s2_seq_.tmp exit 0