mirror of
https://github.com/brouhaha/tapeutils.git
synced 2026-01-11 23:53:18 +00:00
Add utility to list the contents of a TAPEX archive tape.
This commit is contained in:
parent
a209669c38
commit
c1162b17d9
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,3 +7,4 @@ taperead
|
||||
tapewrite
|
||||
read20
|
||||
t10backup
|
||||
tapex
|
||||
|
||||
6
Makefile
6
Makefile
@ -34,10 +34,10 @@ PACKAGE = tapeutils
|
||||
VERSION = 0.6
|
||||
DSTNAME = $(PACKAGE)-$(VERSION)
|
||||
|
||||
PROGRAMS = tapecopy tapedump taperead tapewrite t10backup read20
|
||||
PROGRAMS = tapecopy tapedump taperead tapewrite t10backup read20 tapex
|
||||
|
||||
HEADERS = tapeio.h t10backup.h dumper.h
|
||||
SOURCES = tapeio.c tapecopy.c tapedump.c taperead.c tapewrite.c t10backup.c read20.c
|
||||
SOURCES = tapeio.c tapecopy.c tapedump.c taperead.c tapewrite.c t10backup.c read20.c tapex.c
|
||||
MISC = COPYING
|
||||
|
||||
DISTFILES = $(MISC) Makefile $(HEADERS) $(SOURCES)
|
||||
@ -67,6 +67,8 @@ t10backup: t10backup.o tapeio.o
|
||||
|
||||
read20: read20.o tapeio.o
|
||||
|
||||
tapex: tapex.o tapeio.o
|
||||
|
||||
|
||||
include $(SOURCES:.c=.d)
|
||||
|
||||
|
||||
607
tapex.c
Normal file
607
tapex.c
Normal file
@ -0,0 +1,607 @@
|
||||
/*
|
||||
Dump contents of TAPEX tapes, or tape images. Based on Eric Smith's
|
||||
modified t10backup.c.
|
||||
|
||||
Copyright 1999 Eric Smith
|
||||
Copyright 2017 Lars Brinkhoff
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as published
|
||||
by the Free Software Foundation. Note that permission is not granted
|
||||
to redistribute this program under the terms of any other version of the
|
||||
General Public License.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tapeio.h"
|
||||
|
||||
#define bool long
|
||||
#define false 0
|
||||
#define true 1
|
||||
|
||||
#define MAX_FILE_SPEC 1024
|
||||
|
||||
#define RAWSIZE (5*(512))
|
||||
|
||||
#define endof(s) (strchr(s, (char) 0))
|
||||
|
||||
char *progname;
|
||||
|
||||
|
||||
tape_handle_t tape; /* Source "tape". */
|
||||
|
||||
|
||||
bool eightbit = false; /* Status of -8 (eight-bit) flag. */
|
||||
bool buildtree = false; /* Status of -d (build trees) flag. */
|
||||
bool interchange = false; /* Status of -i (interchange) flag. */
|
||||
long verbose = 0; /* Status of -v (verbose) flag. */
|
||||
|
||||
|
||||
char* argfiles [MAX_FILE_SPEC]; /* File spec's to extract. */
|
||||
long argcount; /* Number of them. */
|
||||
|
||||
|
||||
unsigned char rawdata[RAWSIZE]; /* Raw data for a tape block. */
|
||||
|
||||
long headlh[512], headrh[512]; /* Header block from tape. */
|
||||
long datalh[512], datarh[512]; /* Data block from tape. */
|
||||
|
||||
long prevSEQ; /* SEQ number of previous block. */
|
||||
long currentfilenumber;
|
||||
|
||||
long defercount; /* Count of defered output bytes. */
|
||||
|
||||
bool extracting;
|
||||
FILE* destination;
|
||||
|
||||
/* Tape information: */
|
||||
|
||||
char systemname[100];
|
||||
char savesetname[100];
|
||||
|
||||
/* File information: */
|
||||
|
||||
long a_bsiz; /* For now. */
|
||||
long a_alls;
|
||||
long a_mode;
|
||||
long a_leng;
|
||||
|
||||
char filedev[100]; /* Device: */
|
||||
char filedir[100]; /* [ufd] */
|
||||
char filename[1000]; /* file name. */
|
||||
char fileext[100]; /* extension. */
|
||||
int file_bytesize;
|
||||
int file_blocks;
|
||||
long long file_bytes;
|
||||
|
||||
char filespec[7][100]; /* [0]: device:ufd. */
|
||||
/* [1-5]: sfd's, stored directly here. */
|
||||
/* [6]: file.ext */
|
||||
|
||||
char cname[100]; /* Canonical name. */
|
||||
|
||||
|
||||
void print_usage (FILE *f)
|
||||
{
|
||||
fprintf (f, "Usage: %s [options] [filespecs...]\n", progname);
|
||||
fprintf (f, "Options:\n"
|
||||
" -t list directory\n"
|
||||
" -x extract files\n"
|
||||
" -f <file> read input from file\n"
|
||||
" /dev/* tape drive\n"
|
||||
" host:file rmt server\n"
|
||||
" file tape image file\n"
|
||||
" - tape image from stdin\n"
|
||||
" -s <range> use save sets in range\n"
|
||||
" e.g. '-s 2' for only save set 2\n"
|
||||
" '-s 2,' for save set 2 through EOT\n"
|
||||
" '-s 3,5' for save sets 3 through 5\n"
|
||||
" -v verbose\n"
|
||||
" -vv very verbose\n"
|
||||
" -8 eight bit mode\n"
|
||||
" -i interchange mode\n"
|
||||
" -d build directory tree\n");
|
||||
}
|
||||
|
||||
|
||||
void fatal (int retval, char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (fmt)
|
||||
{
|
||||
fprintf (stderr, "%s: ", progname);
|
||||
va_start (ap, fmt);
|
||||
vfprintf (stderr, fmt, ap);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
if (retval == 1)
|
||||
print_usage (stderr);
|
||||
|
||||
exit (retval);
|
||||
}
|
||||
|
||||
|
||||
void warning (char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fprintf (stderr, "%s: ", progname);
|
||||
va_start (ap, fmt);
|
||||
vfprintf (stderr, fmt, ap);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
|
||||
/* unpackheader unpacks the header block from the raw stream. */
|
||||
|
||||
void unpackheader (void)
|
||||
{
|
||||
unsigned char* rawptr;
|
||||
long i, left, right;
|
||||
unsigned char c;
|
||||
|
||||
rawptr = & rawdata [0];
|
||||
|
||||
for (i = 0; i < 512; i++)
|
||||
{
|
||||
left = * (rawptr++) << 10;
|
||||
left |= * (rawptr++) << 2;
|
||||
left |= (c = * (rawptr++)) >> 6;
|
||||
right = (c & 077) << 12;
|
||||
right |= * (rawptr++) << 4;
|
||||
right |= * (rawptr++) & 017;
|
||||
headlh [i] = left;
|
||||
headrh [i] = right;
|
||||
}
|
||||
}
|
||||
|
||||
void unpack_filename(void)
|
||||
{
|
||||
long long x;
|
||||
char *s = filename;
|
||||
int i, j;
|
||||
|
||||
for (i = 2; i < 512; i++) {
|
||||
x = headlh[i];
|
||||
x <<= 18;
|
||||
x += headrh[i];
|
||||
if (x == 0)
|
||||
break;
|
||||
for (j = 0; j < 5; j++) {
|
||||
*s++ = ((x >> 29) & 0177);
|
||||
x <<= 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* unpackdata unpacks the data block from the raw stream. */
|
||||
|
||||
void unpackdata (void)
|
||||
{
|
||||
unsigned char* rawptr;
|
||||
long i, left, right;
|
||||
unsigned char c;
|
||||
|
||||
rawptr = & rawdata [32*5];
|
||||
|
||||
for (i = 0; i < 512; i++)
|
||||
{
|
||||
left = * (rawptr++) << 10;
|
||||
left |= * (rawptr++) << 2;
|
||||
left |= (c = * (rawptr++)) >> 6;
|
||||
right = (c & 077) << 12;
|
||||
right |= * (rawptr++) << 4;
|
||||
right |= * (rawptr++) & 017;
|
||||
datalh [i] = left;
|
||||
datarh [i] = right;
|
||||
}
|
||||
}
|
||||
|
||||
void unpackinfo (void)
|
||||
{
|
||||
file_bytesize = (headlh[0] >> 6) & 077;
|
||||
file_blocks = headrh[0];
|
||||
file_bytes = headlh[1];
|
||||
file_bytes <<= 18;
|
||||
file_bytes += headrh[1];
|
||||
unpack_filename();
|
||||
}
|
||||
|
||||
/* pars_5chars reads five ASCII chars from a machine word. */
|
||||
|
||||
void pars_5chars (long index, char *store)
|
||||
{
|
||||
long l, r;
|
||||
|
||||
l = datalh [index];
|
||||
r = datarh [index];
|
||||
|
||||
store [0] = (0177 & (l >> 11));
|
||||
store [1] = (0177 & (l >> 4));
|
||||
store [2] = (0177 & ((l << 3) | ((r >> 15) & 017)));
|
||||
store [3] = (0177 & (r >> 8));
|
||||
store [4] = (0177 & (r >> 1));
|
||||
}
|
||||
|
||||
|
||||
/* pars_asciz stores asciz text from data */
|
||||
|
||||
void pars_asciz (long index, char *store)
|
||||
{
|
||||
long words;
|
||||
|
||||
words = datarh [index++];
|
||||
while ((words--) > 0)
|
||||
{
|
||||
pars_5chars (index++, store);
|
||||
store += 5;
|
||||
}
|
||||
* store = (char) 0;
|
||||
}
|
||||
|
||||
|
||||
void zerotapeinfo (void)
|
||||
{
|
||||
}
|
||||
|
||||
void zerofileinfo (void)
|
||||
{
|
||||
file_bytesize = 0;
|
||||
file_blocks = 0;
|
||||
file_bytes = 0;
|
||||
filename[0] = 0;
|
||||
}
|
||||
|
||||
|
||||
void printtapeinfo (void)
|
||||
{
|
||||
if (!verbose)
|
||||
return;
|
||||
|
||||
fprintf (stderr, "%s", filename);
|
||||
if (verbose > 1)
|
||||
fprintf (stderr, " %d blocks; %lld %d-bit bytes\n",
|
||||
file_blocks, file_bytes, file_bytesize);
|
||||
else
|
||||
fputc ('\n', stderr);
|
||||
|
||||
if (verbose > 2 && (headlh[0] & 0770077) != 0)
|
||||
fprintf (stderr, "MYSTERY HEADER: %06lo%06lo %06lo%06lo\n",
|
||||
headlh[0], headrh[0], headlh[1], headrh[1]);
|
||||
}
|
||||
|
||||
|
||||
void downcase (char *s)
|
||||
{
|
||||
while (*s != (char) 0) {
|
||||
if (isupper(*s)) *s = tolower(*s);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void buildfilenames (void)
|
||||
{
|
||||
long i;
|
||||
|
||||
if (* filedev != (char) 0)
|
||||
sprintf (filespec [0], "%s:%s", filedev, filedir);
|
||||
else
|
||||
sprintf (filespec [0], "%s", filedir);
|
||||
|
||||
sprintf (filespec [6], "%s.%s", filename, fileext);
|
||||
|
||||
for (i = 0; i < 7; i++)
|
||||
downcase (filespec [i]);
|
||||
|
||||
sprintf (cname, "%s", filespec [0]);
|
||||
for (i = 1; i < 6; i++)
|
||||
{
|
||||
if (* filespec [i] != (char) 0)
|
||||
sprintf (endof (cname), ".%s", filespec [i]);
|
||||
}
|
||||
if (* cname != (char) 0)
|
||||
sprintf (endof (cname), "..%s", filespec [6]);
|
||||
else
|
||||
sprintf (cname, "%s", filespec [6]);
|
||||
}
|
||||
|
||||
|
||||
void printfileinfo (void)
|
||||
{
|
||||
buildfilenames ();
|
||||
printf ("%3ld %s", currentfilenumber, cname);
|
||||
if (verbose)
|
||||
{
|
||||
printf (" (%ld) alloc:%ld, mode:%lo, len:%ld", a_bsiz, a_alls, a_mode, a_leng);
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
|
||||
/* readblock reads one logical block from the input stream. */
|
||||
/* The header is unpacked into head{l,r}; the data is not. */
|
||||
|
||||
int readblock (void)
|
||||
{
|
||||
long i;
|
||||
i = getrec (tape, rawdata, RAWSIZE);
|
||||
if (i == 0)
|
||||
return (0);
|
||||
if (i != RAWSIZE)
|
||||
{
|
||||
fprintf (stderr, "record length %ld, expected %d\n", i, RAWSIZE);
|
||||
while (i++ < RAWSIZE) rawdata [i] = (char) 0;
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
||||
/* Disk file output routines: */
|
||||
|
||||
void WriteBlock (void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/* OpenOutput opens the output file, according to -d and -i flags. */
|
||||
|
||||
bool OpenOutput (void)
|
||||
{
|
||||
struct stat statbuf;
|
||||
char oname [100];
|
||||
long i;
|
||||
|
||||
defercount = 0;
|
||||
|
||||
if (interchange)
|
||||
destination = fopen (filespec [6], "w");
|
||||
else if (! buildtree)
|
||||
destination = fopen(cname, "w");
|
||||
else
|
||||
{
|
||||
for (i = 0, oname [0] = (char) 0; i < 6; i++)
|
||||
{
|
||||
if (* filespec [i] == (char) 0)
|
||||
break;
|
||||
sprintf (endof (oname), "%s", filespec [i]);
|
||||
if (stat (oname, & statbuf) != 0)
|
||||
{
|
||||
if (mkdir (oname, 0777) != 0)
|
||||
{
|
||||
warning ("cannot create %s/\n", oname);
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
sprintf (endof (oname), "/");
|
||||
}
|
||||
sprintf (endof (oname), "%s", filespec [6]);
|
||||
destination = fopen (oname, "w");
|
||||
}
|
||||
|
||||
return (destination != NULL);
|
||||
}
|
||||
|
||||
void CloseOutput (void)
|
||||
{
|
||||
/* Close output file after us. */
|
||||
}
|
||||
|
||||
/* Argmatch checks if the current file matches the given argument: */
|
||||
|
||||
bool argmatch (char *arg)
|
||||
{
|
||||
long target;
|
||||
char* f;
|
||||
char* p;
|
||||
char* s;
|
||||
|
||||
if (*arg == '#')
|
||||
{
|
||||
(void) sscanf (arg, "#%ld", & target);
|
||||
return (target == currentfilenumber);
|
||||
}
|
||||
|
||||
if (*arg == '*')
|
||||
return (1);
|
||||
|
||||
for (f = cname; *f != (char) 0; f++)
|
||||
{
|
||||
for (p = f, s = arg; (*s != (char) 0) && (*p == *s); p++, s++)
|
||||
;
|
||||
if (*s == (char) 0)
|
||||
return (true);
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
/* doextract performs the job of "tapex -x ..." */
|
||||
|
||||
int doextract (void)
|
||||
{
|
||||
int got_blocks = 0;
|
||||
long i;
|
||||
|
||||
currentfilenumber = 0;
|
||||
extracting = false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (! readblock ())
|
||||
return (got_blocks);
|
||||
|
||||
got_blocks = 1;
|
||||
|
||||
currentfilenumber++;
|
||||
zerofileinfo ();
|
||||
buildfilenames ();
|
||||
for (i = 0; i < argcount; i++)
|
||||
{
|
||||
if (argmatch (argfiles [i]))
|
||||
{
|
||||
if (*argfiles[i] == '#')
|
||||
{
|
||||
/* Maybe do a pure shift here? */
|
||||
argfiles [i] = argfiles [--argcount];
|
||||
}
|
||||
extracting = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (extracting)
|
||||
{
|
||||
if (OpenOutput ())
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
printf ("Extracting %s", cname);
|
||||
fflush (stdout);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
warning ("can't open %s for output\n", cname);
|
||||
extracting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (extracting)
|
||||
{
|
||||
WriteBlock ();
|
||||
}
|
||||
}
|
||||
|
||||
/* dodirectory performs the job of "backup -t ..." */
|
||||
|
||||
int dodirectory (void)
|
||||
{
|
||||
int end_of_tape = 0;
|
||||
int n;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
n = readblock ();
|
||||
if (n == 0) {
|
||||
if (end_of_tape)
|
||||
return 0;
|
||||
end_of_tape = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
end_of_tape = 0;
|
||||
unpackheader ();
|
||||
unpackinfo ();
|
||||
zerotapeinfo ();
|
||||
printtapeinfo ();
|
||||
|
||||
while (file_blocks-- > 0)
|
||||
{
|
||||
readblock ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* command decoder and dispatcher */
|
||||
|
||||
void checkarg (char *arg)
|
||||
{
|
||||
long i;
|
||||
char c;
|
||||
|
||||
if (*arg == '#')
|
||||
{
|
||||
if (sscanf (arg, "#%ld%c", & i, & c) != 1)
|
||||
fatal (1, "bad argument: '%s'\n", arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
long i;
|
||||
char action = '\0';
|
||||
char* inputname = NULL;
|
||||
char *arg;
|
||||
|
||||
progname = argv [0];
|
||||
argcount = 0;
|
||||
|
||||
while (--argc > 0)
|
||||
{
|
||||
++argv;
|
||||
arg = argv [0];
|
||||
if (arg [0] == '-')
|
||||
{
|
||||
while (*++arg)
|
||||
switch (*arg)
|
||||
{
|
||||
case '8':
|
||||
eightbit = true; break;
|
||||
case 'd':
|
||||
buildtree = true; break;
|
||||
case 'f':
|
||||
if (--argc < 0)
|
||||
fatal (1, "input file name missing\n");
|
||||
inputname = (++argv) [0];
|
||||
break;
|
||||
case 't':
|
||||
verbose++;
|
||||
/* Fall through. */
|
||||
case 'x':
|
||||
action = *arg; break;
|
||||
case 'v':
|
||||
verbose++; break;
|
||||
default:
|
||||
fatal (1, "bad option %c\n", arg [1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (argcount >= MAX_FILE_SPEC)
|
||||
fatal (1, "too many file specs\n");
|
||||
argfiles [argcount++] = argv [0];
|
||||
}
|
||||
}
|
||||
|
||||
if (! action)
|
||||
fatal (1, "either -t or -x must be specified\n");
|
||||
|
||||
for (i = 0; i < argcount; i++)
|
||||
checkarg (argfiles[i]);
|
||||
|
||||
if (inputname == NULL)
|
||||
fatal (1, "no input file given\n");
|
||||
|
||||
tape = opentape (inputname, 0, 0);
|
||||
if (! tape)
|
||||
fatal (1, "can't open %s for input\n", inputname);
|
||||
|
||||
switch (action) {
|
||||
case 't': dodirectory (); break;
|
||||
case 'x': doextract (); break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user