|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T b
Length: 10609 (0x2971)
Types: TextFile
Names: »buffer.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
└─⟦this⟧ »EUUGD11/euug-87hel/sec1/pdtar/buffer.c«
/*
* Buffer management for public domain tar.
*
* Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
*
* @(#) buffer.c 1.14 10/28/86 Public Domain - gnu
*/
#include <stdio.h>
#include <errno.h>
#include <sys/types.h> /* For non-Berkeley systems */
#include <sys/file.h>
#include <signal.h>
#include "tar.h"
#include "port.h"
#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() */
extern char *valloc();
/*
* V7 doesn't have a #define for this.
*/
#ifndef O_RDONLY
#define O_RDONLY 0
#endif
#define MAGIC_STAT 105 /* Magic status returned by child, if
it can't exec compress. We hope compress
never returns this status! */
/*
* 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;
static union record record_save_area;
static int saved_recno;
/*
* PID of child compress program, if f_compress.
*/
static int compress_pid;
/*
* Record number of the start of this block of records
*/
static int baserec;
/*
* Error recovery stuff
*/
static int r_error_count;
/*
* Return the location of the next available input or output record.
*/
union record *
findrec()
{
if (ar_record == ar_last) {
flush_archive();
if (ar_record == ar_last)
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;
}
/*
* Open an archive file. The argument specifies whether we are
* reading or writing.
*/
open_archive(read)
int read;
{
if (ar_file[0] == '-' && ar_file[1] == '\0') {
if (read) archive = STDIN;
else archive = STDOUT;
} else if (read) {
archive = open(ar_file, O_RDONLY);
} else {
archive = creat(ar_file, 0666);
}
if (archive < 0) {
perror(ar_file);
exit(EX_BADARCH);
}
/*NOSTRICT*/
ar_block = (union record *) valloc((unsigned)blocksize);
if (!ar_block) {
fprintf(stderr,
"tar: could not allocate memory for blocking factor %d\n",
blocking);
exit(EX_ARGSBAD);
}
ar_record = ar_block;
ar_last = ar_block + blocking;
/*
* Handle compressed archives.
*
* FIXME, currently supported for reading only.
* FIXME, writing involves forking again for a small process
* that will reblock the output of compress to the user's specs.
*/
if (f_compress) {
int pipes[2];
int err;
if (!read) {
fprintf(stderr,
"tar: cannot write compressed archives yet.\n");
exit(EX_ARGSBAD);
}
/* Create a pipe to get compress's output to us */
err = pipe(pipes);
if (err < 0) {
perror ("tar: cannot create pipe to compress");
exit(EX_SYSTEM);
}
/* Fork compress process */
compress_pid = fork();
if (compress_pid < 0) {
perror("tar: cannot fork compress");
exit(EX_SYSTEM);
}
/*
* Child process.
*
* Move input to stdin, write side of pipe to stdout,
* then exec compress.
*/
if (compress_pid == 0) {
(void) close (pipes[PREAD]); /* We won't use it */
if (archive != STDIN) {
(void) close(STDIN);
err = dup(archive);
if (err != 0) {
perror(
"tar: cannot dup input to stdin");
exit(EX_SYSTEM);
}
(void) close(archive);
}
if (pipes[PWRITE] != STDOUT) {
(void) close (STDOUT);
err = dup (pipes[PWRITE]);
if (err != STDOUT) {
perror(
"tar: cannot dup pipe output");
exit(MAGIC_STAT);
}
(void) close (pipes[PWRITE]);
}
execlp("compress", "compress", "-d", (char *)0);
perror("tar: cannot exec compress");
exit(MAGIC_STAT);
}
/*
* Parent process. Clean up.
* FIXME, note that this may leave standard input closed,
* if the compressed archive was on standard input.
*/
(void) close (archive); /* Close compressed archive */
(void) close (pipes[PWRITE]); /* Close write side of pipe */
archive = pipes[PREAD]; /* Read side is our archive */
#ifdef BSD42
f_reblock++; /* Pipe will give random # of bytes */
#endif BSD42
}
ar_reading = read;
if (read) {
ar_last = ar_block; /* Set up for 1st block = # 0 */
flush_archive();
}
}
/*
* 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.
*/
saverec(pointer)
union record **pointer;
{
save_rec = pointer;
saved_recno = baserec + ar_record - ar_block;
}
/*
* Perform a write to flush the buffer.
*/
fl_write()
{
int err;
err = write(archive, ar_block->charptr, blocksize);
if (err == blocksize) return;
/* FIXME, multi volume support on write goes here */
if (err < 0)
perror(ar_file);
else
fprintf(stderr, "tar: %s: write failed, short %d bytes\n",
ar_file, blocksize - err);
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 */
annorec(stderr, tar);
fprintf(stderr, "Read error on ");
perror(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) {
annorec(stderr, tar);
fprintf(stderr, "Too many errors, quitting.\n");
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;
}
error_loop:
err = read(archive, ar_block->charptr, blocksize);
if (err == blocksize) return;
if (err < 0) {
readerror();
goto error_loop; /* Try again */
}
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) {
annorec(stderr, tar);
fprintf(stderr, "Blocksize = %d records\n",
err / RECORDSIZE);
}
ar_last = ar_block + ((unsigned)(blocksize - left))/RECORDSIZE;
return;
}
if (f_reblock) {
/*
* User warned us about this. Fix up.
*/
if (left > 0) {
error_loop_2:
err = read(archive, more, left);
if (err < 0) {
readerror();
goto error_loop_2; /* Try again */
}
if (err == 0) {
annorec(stderr, tar);
fprintf(stderr,
"%s: eof not on block boundary, strange...\n",
ar_file);
exit(EX_BADARCH);
}
left -= err;
more += err;
goto again;
}
} else {
annorec(stderr, tar);
fprintf(stderr, "%s: read %d bytes, strange...\n",
ar_file, err);
exit(EX_BADARCH);
}
}
/*
* Flush the current buffer to/from the archive.
*/
flush_archive()
{
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)
fl_write();
else
fl_read();
}
/*
* Close the archive file.
*/
close_archive()
{
int child;
int status;
if (!ar_reading) flush_archive();
(void) close(archive);
if (f_compress) {
/*
* Loop waiting for the right child to die, or for
* no more kids.
*/
while (((child = wait(&status)) != compress_pid) && child != -1)
;
if (child != -1) {
switch (TERM_SIGNAL(status)) {
case 0: /* Terminated by itself */
if (TERM_VALUE(status) == MAGIC_STAT) {
exit(EX_SYSTEM);/* Child had trouble */
}
if (TERM_VALUE(status))
fprintf(stderr,
"tar: compress child returned status %d\n",
TERM_VALUE(status));
case SIGPIPE:
break; /* This is OK. */
default:
fprintf(stderr,
"tar: compress child died with signal %d%s\n",
TERM_SIGNAL(status),
TERM_COREDUMP(status)? " (core dumped)": "");
}
}
}
}
/*
* 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;
if (f_sayblock) {
if (prefix) {
fputs(prefix, stream);
putc(' ', stream);
}
sprintf(buffer, "rec %d: ",
savedp? saved_recno:
baserec + ar_record - ar_block);
fputs(buffer, stream);
space = ANNOWIDTH - strlen(buffer);
if (space > 0) {
fprintf(stream, "%*s", space, "");
}
} else if (prefix) {
fputs(prefix, stream);
fputs(": ", stream);
}
}