home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************************
- * $Id: buffer.c,v 1.7 1993/01/14 21:50:21 ak Exp $
- *****************************************************************************
- * $Log: buffer.c,v $
- * Revision 1.7 1993/01/14 21:50:21 ak
- * EMX 0.8f fixes and enhancements by Kai Uwe Rommel.
- *
- * Revision 1.6 1992/09/29 09:55:27 ak
- * K.U.R., once again :-)
- * - removed a dup() in buffer.c
- * - EMX opendir with hidden/system test in gnu.c (...dotdot)
- *
- * Revision 1.5 1992/09/26 08:32:16 ak
- * *** empty log message ***
- *
- * Revision 1.4 1992/09/22 21:40:17 ak
- * Yet another fix from K.U.R.:
- * Ich habe noch einen Bug im multi-volume handling gefunden. Die Abfrage
- * auf ENOSPC und EIO war nicht symmetrisch fuer Lesen und Schreiben.
- * Multi-Volume-Archive auf floppies konnte man zwar schreiben, aber
- * nicht lesen.
- *
- * Revision 1.3 1992/09/12 15:57:14 ak
- * - Usenet patches for GNU TAR 1.10
- * - Bugfixes and patches of Kai Uwe Rommel:
- * filename conversion for FAT
- * EMX 0.8e
- * -0..1 alias for a: b:
- * -2..7 alias for +++TAPE$x
- *
- * Revision 1.2 1992/09/02 20:07:46 ak
- * Version AK200
- * - Tape access
- * - Quick file access
- * - OS/2 extended attributes
- * - Some OS/2 fixes
- * - Some fixes of Kai Uwe Rommel
- *
- * Revision 1.1.1.1 1992/09/02 19:16:58 ak
- * Original GNU Tar 1.10 with some filenames shortened for FAT compliance.
- *
- * Revision 1.1 1992/09/02 19:16:55 ak
- * Initial revision
- *
- *****************************************************************************/
-
- static char *rcsid = "$Id: buffer.c,v 1.7 1993/01/14 21:50:21 ak Exp $";
-
- /*
- * Modified by Andreas Kaiser July 92.
- * See CHANGES.AK for info.
- */
-
- /* Buffer management for tar.
- Copyright (C) 1988 Free Software Foundation
-
- This file is part of GNU Tar.
-
- GNU Tar is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- GNU Tar is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNU Tar; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /*
- * Buffer management for tar.
- *
- * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
- *
- * @(#) buffer.c 1.28 11/6/87 - gnu
- */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <errno.h>
- #include <sys/types.h> /* For non-Berkeley systems */
- #include <sys/stat.h>
- #include <signal.h>
-
- #ifndef MSDOS
- #include <sys/ioctl.h>
- #if !defined(USG) || defined(HAVE_MTIO)
- #include <sys/mtio.h>
- #endif
- #endif
-
- #ifdef MSDOS
- # include <fcntl.h>
- # include <process.h>
- #else
- # ifdef XENIX
- # include <sys/inode.h>
- # endif
- # include <sys/file.h>
- #endif
-
- extern int errno;
-
- #ifdef OS2
- # define INCL_DOSPROCESS
- # include <os2.h>
- # include <process.h>
- # include <string.h>
- static int stdio;
- #endif
-
- #include "tar.h"
- #include "port.h"
- #include "rmt.h"
- #include "regex.h"
-
- /* Either stdout or stderr: The thing we write messages (standard msgs, not
- errors) to. Stdout unless we're writing a pipe, in which case stderr */
- FILE *msg_file = stdout;
-
- #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() */
-
- #ifdef __STDC__
- extern void *malloc();
- extern void *valloc();
- #else
- extern char *malloc();
- extern char *valloc();
- #endif
- extern time_t time();
-
- extern char *index(), *strcat();
- extern char *strcpy();
-
- /*
- * V7 doesn't have a #define for this.
- */
- #ifndef O_RDONLY
- #define O_RDONLY 0
- #endif
- #ifndef O_RDWR
- #define O_RDWR 2
- #endif
- #ifndef O_CREAT
- #define O_CREAT 0
- #endif
- #ifndef O_BINARY
- #define O_BINARY 0
- #endif
-
- #define MAGIC_STAT 105 /* Magic status returned by child, if
- it can't exec. We hope compress/sh
- never return this status! */
-
- void writeerror();
- void readerror();
-
- void ck_pipe();
- void ck_close();
-
- extern void finish_header();
- extern void to_oct();
-
- #ifndef MSDOS
- /* Obnoxious test to see if dimwit is trying to dump the archive */
- dev_t ar_dev;
- ino_t ar_ino;
- #endif
-
- /*
- * 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;
- union record record_save_area;
- static long saved_recno;
-
- /*
- * PID of child program, if f_compress or remote archive access.
- */
- static int childpid = 0;
- #ifdef OS2
- static char * childname;
- #endif
-
- /*
- * Record number of the start of this block of records
- */
- long baserec;
-
- /*
- * Error recovery stuff
- */
- static int r_error_count;
-
- /*
- * Have we hit EOF yet?
- */
- static int eof;
-
- /* JF we're reading, but we just read the last record and its time to update */
- extern time_to_start_writing;
- int file_to_switch_to= -1; /* If remote update, close archive, and use
- this descriptor to write to */
-
- int volno = 1; /* JF which volume of a multi-volume tape
- we're on */
-
- char *save_name = 0; /* Name of the file we are currently writing */
- long save_totsize; /* total size of file we are writing. Only
- valid if save_name is non_zero */
- long save_sizeleft; /* Where we are in the file we are writing.
- Only valid if save_name is non-zero */
-
- int write_archive_to_stdout;
-
- /* Used by fl_read and fl_write to store the real info about saved names */
- static char real_s_name[NAMSIZ];
- static long real_s_totsize;
- static long real_s_sizeleft;
-
- /* Reset the EOF flag (if set), and re-set ar_record, etc */
-
- void
- reset_eof()
- {
- if(eof) {
- eof=0;
- ar_record=ar_block;
- ar_last=ar_block+blocking;
- ar_reading=0;
- }
- }
-
- /*
- * 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) {
- err=close(to);
- if(err<0 && errno!=EBADF) {
- msg_perror("Cannot close descriptor %d",to);
- exit(EX_SYSTEM);
- }
- err = dup(from);
- if (err != to) {
- msg_perror("cannot dup %s",msg);
- exit(EX_SYSTEM);
- }
- ck_close(from);
- }
- }
-
- /* return non-zero if p is the name of a directory */
- isfile(p)
- char *p;
- {
- struct stat stbuf;
-
- if(stat(p,&stbuf)<0)
- return 1;
- if((stbuf.st_mode&S_IFMT)==S_IFREG)
- return 1;
- return 0;
- }
-
- #ifdef MSDOS
- # ifdef OS2
-
- int buffer_size;
- int rmt_fd = -1;
-
- void
- child_open()
- {
- int save[2];
- int pipe1[2], pipe2[2];
- int fd, r, no, a, b, mode;
- char *compress = (f_zip ? "gzip" : "compress");
-
- if (ar_reading) {
- a = 0;
- b = 1;
- mode = O_RDONLY|O_BINARY;
- } else {
- a = 1;
- b = 0;
- mode = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
- }
-
- stdio = (strcmp(ar_file, "-") == 0 || ar_file[0] == 0);
-
- ck_pipe(pipe1);
-
- noinherit(save[b] = dup(b));
-
- if (ar_reading && is_device)
- f_reblock = 1;
-
- if (stdio)
- save[a] = -1;
- else {
- noinherit(save[a] = dup(a));
- close(a);
-
- if ((fd = rmtopen(ar_file, mode, 0666)) < 0) {
- msg_perror("can't open archive %s",ar_file);
- exit(EX_BADARCH);
- }
-
- if (_isrmt(fd)) {
- rmt_fd = fd;
- fd -= __REM_BIAS;
- }
-
- if (fd != a) {
- msg_perror("can't dup archive %s",ar_file);
- exit(EX_BADARCH);
- }
- }
-
- dupto(pipe1[b], b, "pipe");
- inherit(a);
- inherit(b);
- noinherit(pipe1[a]);
-
- if (is_device || f_buffered) {
- if (ar_reading)
- r = spawnlp(P_NOWAIT, "buffer", "buffer", "-i90", "-o0", NULL);
- else {
- char bs[10];
- sprintf(bs, "-b%d", blocksize);
- r = spawnlp(P_NOWAIT, "buffer", "buffer", "-i0", "-o90", bs, NULL);
- }
- if (r < 0) {
- msg_perror("can't spawn buffer");
- exit(EX_SYSTEM);
- }
- childpid = r;
- childname = "buffer";
-
- ck_pipe(pipe2);
- dupto(pipe1[a], a, "pipe");
- dupto(pipe2[b], b, "pipe");
- noinherit(pipe2[a]);
- inherit(a);
- inherit(b);
- r = spawnlp(P_NOWAIT, compress, compress,
- ar_reading ? "-d" : NULL, NULL);
- if (r < 0) {
- msg_perror("can't spawn %s", compress);
- exit(EX_SYSTEM);
- }
- archive = pipe2[a];
- } else {
- r = spawnlp(P_NOWAIT, compress, compress,
- ar_reading ? "-d" : NULL, NULL);
- if (r < 0) {
- msg_perror("can't spawn %s", compress);
- exit(EX_SYSTEM);
- }
- archive = pipe1[a];
- childpid = r;
- childname = compress;
- }
-
- if (stdio) {
- if (ar_reading)
- freopen("nul", "r", stdin);
- else
- freopen("nul", "w", stdout);
- }
-
- if (save[0] >= 0)
- dupto(save[0], 0, "saved to stdin");
- if (save[1] >= 0)
- dupto(save[1], 1, "saved to stdout");
- inherit(0);
- inherit(1);
- }
-
- static void
- child_status(int childpid, char *who, int force)
- {
- RESULTCODES st;
- PID pid;
-
- if (DosWaitChild(0, 0, &st, &pid, childpid) == 0) {
- switch (st.codeTerminate) {
- case TC_EXIT:
- if (st.codeResult > force)
- msg("child '%s' return code %d", who, st.codeResult);
- break;
- case TC_HARDERROR:
- msg("child '%s' aborted with hard error", who);
- break;
- case TC_TRAP:
- msg("child '%s' aborted by trap, who");
- break;
- case TC_KILLPROCESS:
- msg("child '%s' killed", who);
- break;
- default:
- msg("child '%s' terminated with unknown status %x.%x",
- who, st.codeTerminate, st.codeResult);
- }
- }
- }
-
- void
- child_close(force)
- {
- if (childpid)
- child_status(childpid, childname, force);
- #ifdef DIRECT
- if (rmt_fd >= 0)
- rmtclose(rmt_fd);
- #endif
- childpid = 0;
- }
-
- #else /* MSDOS but not OS2 */
-
- void
- child_open()
- {
- fprintf(stderr,"MSDOS %s can't use compressed or remote archives\n",tar);
- exit(EX_ARGSBAD);
- }
-
- void
- child_close()
- {
- }
-
- # endif /* MSDOS */
- #else /* unix */
-
- void
- child_open()
- {
- int pipe[2];
- int err = 0;
-
- int kidpipe[2];
- int kidchildpid;
-
- ck_pipe(pipe);
-
- childpid=fork();
- if(childpid<0) {
- msg_perror("cannot fork");
- exit(EX_SYSTEM);
- }
- if(childpid>0) {
- /* We're the parent. Clean up and be happy */
- /* This, at least, is easy */
-
- if(ar_reading) {
- f_reblock++;
- archive=pipe[PREAD];
- ck_close(pipe[PWRITE]);
- } else {
- archive = pipe[PWRITE];
- ck_close(pipe[PREAD]);
- }
- return;
- }
-
- /* We're the kid */
- if(ar_reading) {
- dupto(pipe[PWRITE],STDOUT,"(child) pipe to stdout");
- ck_close(pipe[PREAD]);
- } else {
- dupto(pipe[PREAD],STDIN,"(child) pipe to stdin");
- ck_close(pipe[PWRITE]);
- }
-
- /* We need a child tar only if
- 1: we're reading/writing stdin/out (to force reblocking)
- 2: the file is to be accessed by rmt (compress doesn't know how)
- 3: the file is not a plain file */
- #ifdef NO_REMOTE
- if(!(ar_file[0]=='-' && ar_file[1]=='\0') && isfile(ar_file))
- #else
- if(!(ar_file[0]=='-' && ar_file[1]=='\0') && !_remdev(ar_file) && isfile(ar_file))
- #endif
- {
- /* We don't need a child tar. Open the archive */
- if(ar_reading) {
- archive=open(ar_file, O_RDONLY|O_BINARY, 0666);
- if(archive<0) {
- msg_perror("can't open archive %s",ar_file);
- exit(EX_BADARCH);
- }
- dupto(archive,STDIN,"archive to stdin");
- /* close(archive); */
- } else {
- archive=creat(ar_file,0666);
- if(archive<0) {
- msg_perror("can't open archive %s",ar_file);
- exit(EX_BADARCH);
- }
- dupto(archive,STDOUT,"archive to stdout");
- /* close(archive); */
- }
- } else {
- /* We need a child tar */
- ck_pipe(kidpipe);
-
- kidchildpid=fork();
- if(kidchildpid<0) {
- msg_perror("child can't fork");
- exit(EX_SYSTEM);
- }
-
- if(kidchildpid>0) {
- /* About to exec compress: set up the files */
- if(ar_reading) {
- dupto(kidpipe[PREAD],STDIN,"((child)) pipe to stdin");
- ck_close(kidpipe[PWRITE]);
- /* dup2(pipe[PWRITE],STDOUT); */
- } else {
- /* dup2(pipe[PREAD],STDIN); */
- dupto(kidpipe[PWRITE],STDOUT,"((child)) pipe to stdout");
- ck_close(kidpipe[PREAD]);
- }
- /* ck_close(pipe[PREAD]); */
- /* ck_close(pipe[PWRITE]); */
- /* ck_close(kidpipe[PREAD]);
- ck_close(kidpipe[PWRITE]); */
- } else {
- /* Grandchild. Do the right thing, namely sit here and
- read/write the archive, and feed stuff back to compress */
- tar="tar (child)";
- if(ar_reading) {
- dupto(kidpipe[PWRITE],STDOUT,"[child] pipe to stdout");
- ck_close(kidpipe[PREAD]);
- } else {
- dupto(kidpipe[PREAD],STDIN,"[child] pipe to stdin");
- ck_close(kidpipe[PWRITE]);
- }
-
- if (ar_file[0] == '-' && ar_file[1] == '\0') {
- if (ar_reading)
- archive = STDIN;
- else
- archive = STDOUT;
- } else /* This can't happen if (ar_reading==2)
- archive = rmtopen(ar_file, O_RDWR|O_CREAT|O_BINARY, 0666);
- else */if(ar_reading)
- archive = rmtopen(ar_file, O_RDONLY|O_BINARY, 0666);
- else
- archive = rmtcreat(ar_file, 0666);
-
- if (archive < 0) {
- msg_perror("can't open archive %s",ar_file);
- exit(EX_BADARCH);
- }
-
- if(ar_reading) {
- for(;;) {
- char *ptr;
- int max,count;
-
- r_error_count = 0;
- error_loop:
- err=rmtread(archive, ar_block->charptr,(int)(blocksize));
- if(err<0) {
- readerror();
- goto error_loop;
- }
- if(err==0)
- break;
- ptr = ar_block->charptr;
- max = err;
- while(max) {
- count = (max<RECORDSIZE) ? max : RECORDSIZE;
- err=write(STDOUT,ptr,count);
- if(err!=count) {
- if(err<0) {
- msg_perror("can't write to compress");
- exit(EX_SYSTEM);
- } else
- msg("write to compress short %d bytes",count-err);
- count = (err<0) ? 0 : err;
- }
- ptr+=count;
- max-=count;
- }
- }
- } else {
- for(;;) {
- int n;
- char *ptr;
-
- n=blocksize;
- ptr = ar_block->charptr;
- while(n) {
- err=read(STDIN,ptr,(n<RECORDSIZE) ? n : RECORDSIZE);
- if(err<=0)
- break;
- n-=err;
- ptr+=err;
- }
- /* EOF */
- if(err==0) {
- if(f_compress<2)
- blocksize-=n;
- else
- bzero(ar_block->charptr+blocksize-n,n);
- err=rmtwrite(archive,ar_block->charptr,blocksize);
- if(err!=(blocksize))
- writeerror(err);
- if(f_compress<2)
- blocksize+=n;
- break;
- }
- if(n) {
- msg_perror("can't read from compress");
- exit(EX_SYSTEM);
- }
- err=rmtwrite(archive, ar_block->charptr, (int)blocksize);
- if(err!=blocksize)
- writeerror(err);
- }
- }
-
- /* close_archive(); */
- exit(0);
- }
- }
- /* So we should exec compress (-d) */
- if(ar_reading) {
- execlp("gzip", "gzip", "-d", (char *)0);
- /* Try uncompress if gunzip fails */
- execlp("compress", "compress", "-d", (char *)0);
- } else if (f_zip)
- execlp("gzip", "gzip", "-8", (char *)0);
- else
- execlp("compress", "compress", (char *)0);
- msg_perror("can't exec compress");
- _exit(EX_SYSTEM);
- }
-
- void
- child_close()
- {
- int child, status;
-
- 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))
- msg("child returned status %d",
- TERM_VALUE(status));
- case SIGPIPE:
- break; /* This is OK. */
-
- default:
- msg("child died with signal %d%s",
- TERM_SIGNAL(status),
- TERM_COREDUMP(status)? " (core dumped)": "");
- }
- }
- }
- }
- #endif
-
- /*
- * Open an archive file. The argument specifies whether we are
- * reading or writing.
- */
- /* JF if the arg is 2, open for reading and writing. */
- open_archive(reading)
- int reading;
- {
- extern int map_read;
-
- #ifdef HOST
- if (f_host && reading)
- host_recv(ar_file);
- #endif
-
- msg_file = f_exstdout ? stderr : stdout;
-
- if (blocksize == 0) {
- msg("invalid value for blocksize");
- exit(EX_ARGSBAD);
- }
-
- if(ar_file==0) {
- msg("No archive name given, what should I do?");
- exit(EX_BADARCH);
- }
-
- /*NOSTRICT*/
- if(f_multivol) {
- ar_block = (union record *) valloc((unsigned)(blocksize+(2*RECORDSIZE)));
- if(ar_block)
- ar_block += 2;
- } else
- ar_block = (union record *) valloc((unsigned)blocksize);
- if (!ar_block) {
- msg("could not allocate memory for blocking factor %d",
- blocking);
- exit(EX_ARGSBAD);
- }
-
- ar_record = ar_block;
- ar_last = ar_block + blocking;
- ar_reading = reading;
-
- if (f_compress) {
- if(reading==2 || f_verify) {
- msg("cannot update or verify compressed archives");
- exit(EX_ARGSBAD);
- }
- child_open();
- if(!reading && ar_file[0]=='-' && ar_file[1]=='\0')
- msg_file = stderr;
- /* child_open(rem_host, rem_file); */
- } else if (ar_file[0] == '-' && ar_file[1] == '\0') {
- f_reblock++; /* Could be a pipe, be safe */
- if(f_verify) {
- msg("can't verify stdin/stdout archive");
- exit(EX_ARGSBAD);
- }
- if(reading==2) {
- archive=STDIN;
- msg_file=stderr;
- write_archive_to_stdout++;
- } else if (reading)
- archive = STDIN;
- else {
- archive = STDOUT;
- msg_file = stderr;
- }
- } else if (reading==2 || f_verify) {
- archive = rmtopen(ar_file, O_RDWR|O_CREAT|O_BINARY, 0666);
- } else if(reading) {
- archive = rmtopen(ar_file, O_RDONLY|O_BINARY, 0666);
- } else {
- archive = rmtcreat(ar_file, 0666);
- }
- #ifndef MSDOS
- if(!_isrmt(archive)) {
- struct stat tmp_stat;
-
- fstat(archive,&tmp_stat);
- if((tmp_stat.st_mode&S_IFMT)==S_IFREG) {
- ar_dev=tmp_stat.st_dev;
- ar_ino=tmp_stat.st_ino;
- }
- }
- #endif
-
- if (archive < 0) {
- msg_perror("can't open %s",ar_file);
- exit(EX_BADARCH);
- }
- #ifdef MSDOS
- if (!_isrmt(archive))
- setmode(archive, O_BINARY);
- #endif
-
- if (reading) {
- ar_last = ar_block; /* Set up for 1st block = # 0 */
- if (f_map_file && map_read)
- return;
- (void) findrec(); /* Read it in, check for EOF */
-
- if(f_volhdr) {
- union record *head;
- #ifndef REGEX
- char *ptr;
-
- if(f_multivol) {
- ptr=malloc(strlen(f_volhdr)+20);
- sprintf(ptr,"%s Volume %d",f_volhdr,1);
- } else
- ptr=f_volhdr;
- #endif
- head=findrec();
- if(!head) {
- msg("Archive not labelled to match %s",f_volhdr);
- exit(EX_BADVOL);
- }
- #ifdef REGEX
- if (re_match (label_pattern, head->header.name,
- strlen (head->header.name), 0, 0) < 0) {
- msg ("Volume mismatch! %s!=%s", f_volhdr,
- head->header.name);
- exit (EX_BADVOL);
- }
- #else
- if(strcmp(ptr,head->header.name)) {
- msg("Volume mismatch! %s!=%s",ptr,head->header.name);
- exit(EX_BADVOL);
- }
- if(ptr!=f_volhdr)
- free(ptr);
- #endif
- }
- } else if(f_volhdr) {
- bzero((void *)ar_block,RECORDSIZE);
- if(f_multivol)
- sprintf(ar_block->header.name,"%s Volume 1",f_volhdr);
- else
- strcpy(ar_block->header.name,f_volhdr);
- ar_block->header.linkflag = LF_VOLHDR;
- to_oct(time(0), 1+12, ar_block->header.mtime);
- finish_header(ar_block);
- /* ar_record++; */
- }
- }
-
-
- /*
- * 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.
- */
-
- /*send_buffer_to_file();
- if(new_volume) {
- deal_with_new_volume_stuff();
- send_buffer_to_file();
- }
- */
-
- fl_write()
- {
- int err;
- int copy_back;
- static long bytes_written = 0;
-
- if(tape_length && bytes_written >= tape_length * 1024) {
- errno = ENOSPC;
- err = 0;
- } else
- err = rmtwrite(archive, ar_block->charptr,(int) blocksize);
- if(err!=blocksize && !f_multivol)
- writeerror(err);
- else if (f_totals)
- tot_written += blocksize;
-
- if(err>0)
- bytes_written+=err;
- if (err == blocksize) {
- if(f_multivol) {
- if(!save_name) {
- real_s_name[0]='\0';
- real_s_totsize=0;
- real_s_sizeleft = 0;
- return;
- }
- #ifdef MSDOS
- if(save_name[1]==':')
- save_name+=2;
- #endif
- while(*save_name=='/')
- save_name++;
-
- /* strcpy(real_s_name,save_name); */
- strncpy(real_s_name,save_name,NAMSIZ-1);
- real_s_name[NAMSIZ]='\0';
- real_s_totsize = save_totsize;
- real_s_sizeleft = save_sizeleft;
- }
- return;
- }
-
- /* We're multivol Panic if we didn't get the right kind of response */
- /* ENXIO is for the UNIX PC */
- #ifdef ENXIO
- if(err>0 || (err<0 && errno!=ENOSPC && errno!=EIO && errno!=ENXIO))
- #else
- if(err>0 || (err<0 && errno!=ENOSPC && errno!=EIO))
- #endif
- writeerror(err);
-
- if(new_volume(0)<0)
- return;
- bytes_written=0;
- if(f_volhdr && real_s_name[0]) {
- copy_back=2;
- ar_block-=2;
- } else if(f_volhdr || real_s_name[0]) {
- copy_back = 1;
- ar_block--;
- } else
- copy_back = 0;
- if(f_volhdr) {
- bzero((void *)ar_block,RECORDSIZE);
- sprintf(ar_block->header.name,"%s Volume %d",f_volhdr,volno);
- to_oct(time(0), 1+12, ar_block->header.mtime);
- ar_block->header.linkflag = LF_VOLHDR;
- finish_header(ar_block);
- }
- if(real_s_name[0]) {
- int tmp;
-
- if(f_volhdr)
- ar_block++;
- bzero((void *)ar_block,RECORDSIZE);
- strcpy(ar_block->header.name,real_s_name);
- ar_block->header.linkflag = LF_MULTIVOL;
- to_oct((long)real_s_sizeleft,1+12,
- ar_block->header.size);
- to_oct((long)real_s_totsize-real_s_sizeleft,
- 1+12,ar_block->header.offset);
- tmp=f_verbose;
- f_verbose=0;
- finish_header(ar_block);
- f_verbose=tmp;
- if(f_volhdr)
- ar_block--;
- }
-
- err = rmtwrite(archive, ar_block->charptr,(int) blocksize);
- if(err!=blocksize)
- writeerror(err);
- else if (f_totals)
- tot_written += blocksize;
-
-
- bytes_written = blocksize;
- if(copy_back) {
- ar_block+=copy_back;
- bcopy((void *)(ar_block+blocking-copy_back),
- (void *)ar_record,
- copy_back*RECORDSIZE);
- ar_record+=copy_back;
-
- if(real_s_sizeleft>=copy_back*RECORDSIZE)
- real_s_sizeleft-=copy_back*RECORDSIZE;
- else if((real_s_sizeleft+RECORDSIZE-1)/RECORDSIZE<=copy_back)
- real_s_name[0] = '\0';
- else {
- #ifdef MSDOS
- if(save_name[1]==':')
- save_name+=2;
- #endif
- while(*save_name=='/')
- save_name++;
-
- strcpy(real_s_name,save_name);
- real_s_sizeleft = save_sizeleft;
- real_s_totsize=save_totsize;
- }
- copy_back = 0;
- }
- }
-
- /* Handle write errors on the archive. Write errors are always fatal */
- /* Hitting the end of a volume does not cause a write error unless the write
- * was the first block of the volume */
-
- void
- writeerror(err)
- int err;
- {
- if (err < 0) {
- msg_perror("can't write to %s",ar_file);
- exit(EX_BADARCH);
- } else {
- msg("only wrote %u of %u bytes to %s",err,blocksize,ar_file);
- 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 */
-
- msg_perror("read error on %s",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) {
- msg("Too many errors, quitting.");
- 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;
- }
- if(write_archive_to_stdout && baserec!=0) {
- err=rmtwrite(1, ar_block->charptr, blocksize);
- if(err!=blocksize)
- writeerror(err);
- }
- if(f_multivol) {
- if(save_name) {
- if(save_name!=real_s_name) {
- #ifdef MSDOS
- if(save_name[1]==':')
- save_name+=2;
- #endif
- while(*save_name=='/')
- save_name++;
-
- strcpy(real_s_name,save_name);
- save_name=real_s_name;
- }
- real_s_totsize = save_totsize;
- real_s_sizeleft = save_sizeleft;
-
- } else {
- real_s_name[0]='\0';
- real_s_totsize=0;
- real_s_sizeleft = 0;
- }
- }
-
- error_loop:
- err = rmtread(archive, ar_block->charptr, (int)blocksize);
- if (err == blocksize)
- return;
-
- if((err == 0 || (err<0 && (errno==ENOSPC || errno == EIO))) &&
- f_multivol) {
- union record *head;
-
- try_volume:
- if(new_volume((cmd_mode==CMD_APPEND || cmd_mode==CMD_CAT || cmd_mode==CMD_UPDATE) ? 2 : 1)<0)
- return;
- vol_error:
- err = rmtread(archive, ar_block->charptr,(int) blocksize);
- if(err < 0) {
- readerror();
- goto vol_error;
- }
- if(err!=blocksize)
- goto short_read;
-
- head=ar_block;
-
- if(head->header.linkflag==LF_VOLHDR) {
- if(f_volhdr) {
- #ifndef REGEX
- char *ptr;
-
- ptr=(char *)malloc(strlen(f_volhdr)+20);
- sprintf(ptr,"%s Volume %d",f_volhdr,volno);
- #endif
- #ifdef REGEX
- if (re_match (label_pattern, head->header.name,
- strlen (head->header.name),
- 0, 0) < 0) {
- msg("Volume mismatch! %s!=%s",f_volhdr,
- head->header.name);
- --volno;
- goto try_volume;
- }
- #else
- if(strcmp(ptr,head->header.name)) {
- msg("Volume mismatch! %s!=%s",ptr,head->header.name);
- --volno;
- free(ptr);
- goto try_volume;
- }
- free(ptr);
- #endif
- }
- if(f_verbose)
- fprintf(msg_file,"Reading %s",head->header.name);
- head++;
- } else if(f_volhdr) {
- msg("Warning: No volume header!");
- }
-
- if(real_s_name[0]) {
- long from_oct();
-
- if(head->header.linkflag!=LF_MULTIVOL || strcmp(head->header.name,real_s_name)) {
- msg("%s is not continued on this volume!",real_s_name);
- --volno;
- goto try_volume;
- }
- if(real_s_totsize!=from_oct(1+12,head->header.size)+from_oct(1+12,head->header.offset)) {
- msg("%s is the wrong size (%ld!=%ld+%ld)",
- head->header.name,save_totsize,
- from_oct(1+12,head->header.size),
- from_oct(1+12,head->header.offset));
- --volno;
- goto try_volume;
- }
- if(real_s_totsize-real_s_sizeleft!=from_oct(1+12,head->header.offset)) {
- msg("This volume is out of sequence");
- --volno;
- goto try_volume;
- }
- head++;
- }
- ar_record=head;
- return;
- } else if (err < 0) {
- readerror();
- goto error_loop; /* Try again */
- }
-
- short_read:
- 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) {
- /* msg("Blocksize = %d record%s",
- err / RECORDSIZE, (err > RECORDSIZE)? "s": "");*/
- msg("Blocksize = %d records", err / RECORDSIZE);
- }
- ar_last = ar_block + ((unsigned)(blocksize - left))/RECORDSIZE;
- return;
- }
- if (f_reblock) {
- /*
- * User warned us about this. Fix up.
- */
- if (left > 0) {
- error2loop:
- err = rmtread(archive, more, (int)left);
- if (err < 0) {
- readerror();
- goto error2loop; /* Try again */
- }
- if (err == 0) {
- msg("archive %s EOF not on block boundary",ar_file);
- exit(EX_BADARCH);
- }
- left -= err;
- more += err;
- goto again;
- }
- } else {
- msg("only read %d bytes from archive %s",err,ar_file);
- exit(EX_BADARCH);
- }
- }
-
-
- /*
- * Flush the current buffer to/from the archive.
- */
- flush_archive()
- {
- int c;
-
- 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) {
- if(time_to_start_writing) {
- time_to_start_writing=0;
- ar_reading=0;
-
- if(file_to_switch_to>=0) {
- if((c=rmtclose(archive))<0)
- msg_perror("Warning: can't close %s(%d,%d)",ar_file,archive,c);
-
- archive=file_to_switch_to;
- } else
- (void)backspace_output();
- fl_write();
- } else
- fl_read();
- } else {
- fl_write();
- }
- }
-
- /* Backspace the archive descriptor by one blocks worth.
- If its a tape, MTIOCTOP will work. If its something else,
- we try to seek on it. If we can't seek, we lose! */
- backspace_output()
- {
- long cur;
- /* int er; */
- extern char *output_start;
-
- #ifdef MTIOCTOP
- struct mtop t;
-
- t.mt_op = MTBSR;
- t.mt_count = 1;
- if((rmtioctl(archive,MTIOCTOP,&t))>=0)
- return 1;
- if(errno==EIO && (rmtioctl(archive,MTIOCTOP,&t))>=0)
- return 1;
- #endif
-
- cur=rmtlseek(archive,0L,1);
- cur-=blocksize;
- /* Seek back to the beginning of this block and
- start writing there. */
-
- if(rmtlseek(archive,cur,0)!=cur) {
- /* Lseek failed. Try a different method */
- msg("Couldn't backspace archive file. It may be unreadable without -i.");
- /* Replace the first part of the block with nulls */
- if(ar_block->charptr!=output_start)
- bzero(ar_block->charptr,output_start-ar_block->charptr);
- return 2;
- }
- return 3;
- }
-
-
- /*
- * Close the archive file.
- */
- close_archive()
- {
- int child;
- int status;
- int c;
-
- if (time_to_start_writing || !ar_reading)
- flush_archive();
- if(cmd_mode==CMD_DELETE) {
- long pos;
-
- pos = rmtlseek(archive,0L,1);
- #ifndef MSDOS
- /* FIXME does ftruncate really take an INT?! */
- (void) ftruncate(archive,(int)pos);
- #else
- (void)rmtwrite(archive,"",0);
- #endif
- }
- if(f_verify)
- verify_volume();
-
- if((c=rmtclose(archive))<0)
- msg_perror("Warning: can't close %s(%d,%d)",ar_file,archive,c);
-
- if (childpid)
- child_close();
-
- #ifdef HOST
- !!!! if (f_host) {
- if (!ar_reading)
- host_send(ar_file);
- host_end();
- }
- #endif
- }
-
-
- #ifdef DONTDEF
- /*
- * 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;
- int save_e;
-
- save_e=errno;
- /* 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;
- (void) 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);
- }
- errno=save_e;
- }
- #endif
-
- /* We've hit the end of the old volume. Close it and open the next one */
- /* Values for type: 0: writing 1: reading 2: updating */
- new_volume(type)
- int type;
- {
- int c;
- char inbuf[80];
- char *p;
- static FILE *read_file = 0;
- extern int now_verifying;
- extern char TTY_NAME[];
- #ifndef ANSI
- char *getenv();
- #endif
-
- if(!read_file && !f_run_script_at_end)
- read_file = (archive==0) ? fopen(TTY_NAME, "r") : stdin;
-
- if(now_verifying)
- return -1;
- if(f_verify)
- verify_volume();
- if((c=rmtclose(archive))<0)
- msg_perror("Warning: can't close %s(%d,%d)",ar_file,archive,c);
-
- if (childpid)
- child_close();
- #ifdef HOST
- if (f_host && type==0)
- host_send(ar_file);
- #endif
- volno++;
- tryagain:
- if (f_run_script_at_end)
- system(info_script);
- else for(;;) {
- fprintf(msg_file,"\007Prepare volume #%d and hit return: ",volno);
- fflush(msg_file);
- #ifdef __EMX__
- while (_read_kbd(0, 0, 0) != -1);
- /* not available with -Zomf -Zsys in 0.8e, but in 0.8f */
- #endif
- if(fgets(inbuf,sizeof(inbuf),read_file)==0) {
- fprintf(msg_file,"EOF? What does that mean?");
- if(cmd_mode!=CMD_EXTRACT && cmd_mode!=CMD_LIST && cmd_mode!=CMD_DIFF)
- msg("Warning: Archive is INCOMPLETE!");
- exit(EX_BADARCH);
- }
- if(inbuf[0]=='\n' || inbuf[0]=='y' || inbuf[0]=='Y')
- break;
-
- switch(inbuf[0]) {
- case '?':
- {
- fprintf(msg_file,"\
- n [name] Give a new filename for the next (and subsequent) volume(s)\n\
- q Abort tar\n\
- ! Spawn a subshell\n\
- ? Print this list\n");
- }
- break;
-
- case 'q': /* Quit */
- fprintf(msg_file,"No new volume; exiting.\n");
- if(cmd_mode!=CMD_EXTRACT && cmd_mode!=CMD_LIST && cmd_mode!=CMD_DIFF)
- msg("Warning: Archive is INCOMPLETE!");
- exit(EX_BADARCH);
-
- case 'n': /* Get new file name */
- {
- char *q,*r;
- static char *old_name;
-
- for(q= &inbuf[1];*q==' ' || *q=='\t';q++)
- ;
- for(r=q;*r;r++)
- if(*r=='\n')
- *r='\0';
- if(old_name)
- free(old_name);
- old_name=p=(char *)malloc((unsigned)(strlen(q)+2));
- if(p==0) {
- msg("Can't allocate memory for name");
- exit(EX_SYSTEM);
- }
- (void) strcpy(p,q);
- ar_file=p;
- }
- break;
-
- case '!':
- #ifdef MSDOS
- spawnl(P_WAIT,getenv("COMSPEC"),"-",0);
- #else
- /* JF this needs work! */
- switch(fork()) {
- case -1:
- msg_perror("can't fork!");
- break;
- case 0:
- p=getenv("SHELL");
- if(p==0) p="/bin/sh";
- execlp(p,"-sh","-i",0);
- msg_perror("can't exec a shell %s",p);
- _exit(55);
- default:
- wait(0);
- break;
- }
- #endif
- break;
- }
- }
-
- #ifdef HOST
- if (f_host && type!=0)
- host_recv(ar_file);
- #endif
- if(f_compress)
- child_open();
- else if(type==2 || f_verify)
- archive=rmtopen(ar_file,O_RDWR|O_CREAT|O_BINARY,0666);
- else if(type==1)
- archive=rmtopen(ar_file,O_RDONLY|O_BINARY,0666);
- else if(type==0)
- archive=rmtcreat(ar_file,0666);
- else
- archive= -1;
-
- if(archive<0) {
- msg_perror("can't open %s",ar_file);
- goto tryagain;
- }
- #ifdef MSDOS
- setmode(archive,O_BINARY);
- #endif
- return 0;
- }
-
- /* this is a useless function that takes a buffer returned by wantbytes
- and does nothing with it. If the function called by wantbytes returns
- an error indicator (non-zero), this function is called for the rest of
- the file.
- */
- int
- no_op(size,data)
- int size;
- char *data;
- {
- return 0;
- }
-
- /* Some other routine wants SIZE bytes in the archive. For each chunk of
- the archive, call FUNC with the size of the chunk, and the address of
- the chunk it can work with.
- */
- int
- wantbytes(size,func)
- long size;
- int (*func)();
- {
- char *data;
- long data_size;
-
- while(size) {
- data = findrec()->charptr;
- if (data == NULL) { /* Check it... */
- msg("Unexpected EOF on archive file");
- return -1;
- }
- data_size = endofrecs()->charptr - data;
- if(data_size>size)
- data_size=size;
- if((*func)(data_size,data))
- func=no_op;
- userec((union record *)(data + data_size - 1));
- size-=data_size;
- }
- return 0;
- }
-