rsalz@bbn.com (Rich Salz) (04/12/90)
Submitted-by: Matt Landau <mlandau@diamond.bbn.com> Posting-number: Volume 21, Issue 102 Archive-name: exebyte_toc Exetoc is a program that allows you to write a table of contents file onto the front of a 2GB helical scan tape, read it back later, and update it without disturbing the other contents of the tape. It does this by reserving a fixed amount of space at the start of the tape (about 10 megabytes in the current implementation) that can be used to store information about the contents of the tape. About 2 megabytes of this space is available for keeping the table of contents. The rest acts as a buffer space between the end of the table of contents and the first data file on the tape. #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: Makefile exetoc.c smtops.c exitcodes.h smtio.h smtops.h # exetoc.man # Wrapped by mlandau@dilithium on Wed Apr 11 14:41:09 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f Makefile -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"Makefile\" else echo shar: Extracting \"Makefile\" \(765 characters\) sed "s/^X//" >Makefile <<'END_OF_Makefile' XSRCS = exetoc.c smtops.c XOBJS = exetoc.o smtops.o XHDRS = exitcodes.h smtio.h smtops.h XDOCS = exetoc.man X X# Uncomment this if you have librmt.a installed someplace XRMT = -DREMOTE=1 -I/usr/local/include XLIBS = /usr/local/lib/librmt.a X X# Uncomment this if you DON'T have librmt.a installed someplace X#RMT = X#LIBS = X XCFLAGS = $(RMT) -O X XBINDIR = /usr/local/bin XMANDIR = /usr/local/man/man1 XMANEXT = 1 X Xexetoc: $(OBJS) X cc $(CFLAGS) -o exetoc $(OBJS) $(LIBS) X Xinstall: exetoc X install -s exetoc $(BINDIR) X cp exetoc.man $(MANDIR)/exetoc.$(MANEXT) X Xclean: X /bin/rm -f exetoc a.out $(OBJS) core *.BAK *.CKP X Xshar: X shar Makefile $(SRCS) $(HDRS) $(DOCS) >exetoc.shar X Xsaber: X # load $(SRCS) X X Xexetoc.o: exitcodes.h smtio.h smtops.h Xsmtops.o: exitcodes.h smtio.h smtops.h END_OF_Makefile if test 765 -ne `wc -c <Makefile`; then echo shar: \"Makefile\" unpacked with wrong size! fi # end of overwriting check fi if test -f exetoc.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"exetoc.c\" else echo shar: Extracting \"exetoc.c\" \(8029 characters\) sed "s/^X//" >exetoc.c <<'END_OF_exetoc.c' X/* X * exetoc.c -- A program to read, write, and rewrite tables of contents X * on tapes in an exebyte tape drive. X * X * USAGE: exetoc [-t|f tape] [-g file] [-p file] [-i] [-v] [-q] X * X * -t specifies the tape drive, default is /dev/rsmt0 X * -f is a synonym for -t, a la mt. X * -g gets the table of contents from the tape and X * sticks it into "file", which may be "-" X * for standard output. X * -p puts the table of contents contained in "file" X * onto the front of the tape. You can use X * "-" to take the table of contents from X * standard input. X * -i initializes the tape by creating a blank table X * of contents. X * -v verifies that this tape has been initialized. X * -q causes the program to work quietly. X * X * You MUST provide exactly one of the -i, -g, -p, or -v flags. X */ X X#if !lint && !SABER Xstatic char RcsId[] = "$Header: exetoc.c,v 1.3 89/10/27 16:14:34 mlandau Exp $"; X#endif X X#include <stdio.h> X#include <fcntl.h> X#include <sys/types.h> X#include <sys/stat.h> X X#include "exitcodes.h" X#include "smtops.h" X X#define FORWARD /* nothing */ X X#define KBytes(n) (n * 1024) X#define MBytes(n) (1024 * KBytes(n)) X#define MIN(a, b) ((a) < (b) ? (a) : (b)) X#define streq(s1, s2) (!strcmp(s1, s2)) X X#define IOBUF_SIZE KBytes(62) /* Exebyte likes 62KB chunks */ X#define TOC_SIZE MBytes(10) /* TOC occupied 10 MB on the tape */ X#define TOC_USEABLE MBytes(2) /* About 2 MB of it can be used */ X X#define TOC_MARKER "[ExeTOC Table of Contents]" X X#define OP_NONE 0 X#define OP_VERIFY 1 X#define OP_INIT 2 X#define OP_FETCH 3 X#define OP_STORE 4 X X/* Getopt stuff */ Xextern char *optarg; Xextern int optind; Xextern int opterr; X X/* Linked in later */ Xextern char *getenv(); Xextern char *rindex(); X X/* Shut Saber up */ XFORWARD void usage(); XFORWARD void set_operation(); XFORWARD void mark_tape(); XFORWARD void initialize_tape(); XFORWARD int check_tape(); XFORWARD void toc_to_file(); XFORWARD void toc_from_file(); XFORWARD void rewind_named_device(); X X X/* Only need one big buffer to hold the table of contents */ Xstatic char Buffer[IOBUF_SIZE]; Xstatic int Quiet = 0; X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X int option; X int operation = OP_NONE; X char *tapename = "/dev/rsmt0"; X int hastoc; X int tapefd; X int tocfd; X X opterr = 0; X if ((tapename = getenv("EXEBYTE")) == NULL) X tapename = "/dev/rsmt0"; X while ((option = getopt(argc, argv, "t:f:g:p:ivq")) != EOF) X { X switch (option) X { X case 't': X case 'f': X tapename = optarg; X break; X X case 'g': X set_operation(&operation, OP_FETCH); X if (streq(optarg, "-")) X tocfd = fileno(stdout); X else X tocfd = check_open(optarg, O_WRONLY|O_CREAT|O_TRUNC, 0666); X break; X X case 'p': X set_operation(&operation, OP_STORE); X if (streq(optarg, "-")) X tocfd = fileno(stdin); X else X tocfd = check_open(optarg, O_RDONLY, 0666); X break; X X case 'i': X set_operation(&operation, OP_INIT); X break; X X case 'v': X set_operation(&operation, OP_VERIFY); X break; X X case 'q': X Quiet = 1; X break; X X default: X usage(argv[0]); X exit(EXIT_USAGE); X /* NOTREACHED */ X break; X } X } X X switch (operation) X { X case OP_NONE: X fputs("You must specify one of -g, -p, -i, or -v\n", stderr); X exit(EXIT_USAGE); X X case OP_INIT: X tapefd = smt_open(tapename, O_WRONLY); X initialize_tape(tapefd); X smt_close(tapefd); X rewind_named_device(tapename); X exit(EXIT_OK); X X case OP_VERIFY: X tapefd = smt_open(tapename, O_RDONLY); X hastoc = check_tape(tapefd); X smt_close(tapefd); X rewind_named_device(tapename); X if (!Quiet) X printf("Tape in %s %s a labeled ExeTOC tape.\n", X tapename, hastoc ? "is" : "is not"); X exit(hastoc ? EXIT_OK : EXIT_NOTOC); X X case OP_FETCH: X tapefd = smt_open(tapename, O_RDWR); X if (!check_tape(tapefd)) X { X fprintf(stderr, "Tape in %s is not a labeled ExeTOC tape.\n", X tapename); X exit(EXIT_NOTOC); X } X toc_to_file(tapefd, tocfd); X smt_close(tapefd); X rewind_named_device(tapename); X if (tocfd != fileno(stdout)) X close(tocfd); X exit(EXIT_OK); X X case OP_STORE: X tapefd = smt_open(tapename, O_RDWR); X if (!check_tape(tapefd)) X { X fprintf(stderr, "Tape in %s is not a labeled ExeTOC tape.\n", X tapename); X exit(EXIT_NOTOC); X } X mark_tape(tapefd); X toc_from_file(tapefd, tocfd); X smt_close_without_eof(tapefd); X rewind_named_device(tapename); X if (tocfd != fileno(stdin)) X close(tocfd); X exit(EXIT_OK); X X default: X fprintf(stderr, "Unknown tape operation code (%d)\n", operation); X exit(EXIT_USAGE); X } X} X Xvoid usage(progname) Xchar *progname; X{ X static char *summary = X "usage: %s [-t tape] [-g file] [-p file] [-i] [-v] [-q]\n"; X static char *syntax[] = { X "", X "\t-t specifies the tape device. Default is $EXEBYTE, or /dev/rsmt0.", X "\t-g gets the table of contents from the tape into the named file.", X "\t-p puts the table of contants in the named file onto the tape.", X "\t-i initializes a new tape so it can include a table of contents.", X "\t-v verifies that a tape has previously been initialized.", X "\t-q causes the program to work more quietly than usual.", X "", X "(Note: the tape is always rewound after any of these operations.)", X NULL X }; X char *p; X register int i; X X if ((p = rindex(progname, '/')) != NULL) X progname = p+1; X X fprintf(stderr, summary, progname); X for (i = 0; syntax[i] != NULL; ++i) X fprintf(stderr, "%s\n", syntax[i]); X} X X X Xvoid rewind_named_device(name) Xchar *name; X{ X int tapefd = smt_open(name, O_RDONLY); X X smt_rewind(tapefd); X smt_close(tapefd); X} X X Xvoid set_operation(op, opcode) Xint *op; Xint opcode; X{ X if (*op != OP_NONE) X { X fputs("Only one of -g, -p, -i, and -q may be supplied.\n", stderr); X exit(EXIT_USAGE); X } X *op = opcode; X} X X Xint check_open(name, mode, perm) Xchar *name; Xint mode; Xint perm; X{ X int fd; X X if ((fd = open(name, mode, perm)) < 0) X { X perror(name); X exit(EXIT_IO); X } X return (fd); X} X X Xvoid mark_tape(tapefd) Xint tapefd; X{ X bzero(Buffer, sizeof(Buffer)); X strcpy(Buffer, TOC_MARKER); X smt_rewind(tapefd); X if (smt_write(tapefd, Buffer, sizeof(Buffer)) < sizeof(Buffer)) X { X perror("tape label"); X exit(EXIT_IO); X } X} X X Xvoid initialize_tape(tapefd) Xint tapefd; X{ X int nbufs = (TOC_SIZE / IOBUF_SIZE); X X mark_tape(tapefd); X bzero(Buffer, sizeof(Buffer)); X while (--nbufs > 0) X smt_write(tapefd, Buffer, sizeof(Buffer)); X} X X Xint check_tape(tapefd) Xint tapefd; X{ X smt_rewind(tapefd); X return (smt_read(tapefd, Buffer, sizeof(Buffer)) == sizeof(Buffer) X && X streq(Buffer, TOC_MARKER)); X} X X Xvoid toc_to_file(tapefd, tocfd) Xint tapefd; Xint tocfd; X{ X int n; X register int i; X register char *bp; X X bzero(Buffer, sizeof(Buffer)); X while ((n = smt_read(tapefd, Buffer, sizeof(Buffer))) > 0) X { X if (n < sizeof(Buffer)) X { X perror("tape read"); X exit(EXIT_IO); X } X for (bp = Buffer, i = 0; i < sizeof(Buffer) && *bp != 0; bp++, i++) X continue; X if (write(tocfd, Buffer, i) != i) X { X perror("file write"); X exit(EXIT_IO); X } X if (i < sizeof(Buffer)) X break; X } X} X X Xvoid toc_from_file(tapefd, tocfd) Xint tapefd; Xint tocfd; X{ X struct stat s; X int n; X X if (tocfd != fileno(stdin)) X { X if (fstat(tocfd, &s) < 0) X { X perror("fstat"); X exit(EXIT_IO); X } X if (s.st_size > TOC_USEABLE) X { X fputs("Table of Contents file is too large.\n", stderr); X exit(EXIT_TOOBIG); X } X } X X bzero(Buffer, sizeof(Buffer)); X while ((n = read(tocfd, Buffer, sizeof(Buffer))) > 0) X { X if (n < sizeof(Buffer)) X bzero(Buffer + n, sizeof(Buffer) - n); X if (smt_write(tapefd, Buffer, sizeof(Buffer)) < sizeof(Buffer)) X { X perror("tape write"); X exit(EXIT_IO); X } X } X if (n < 0) X { X perror("file read"); X exit(EXIT_IO); X } X} END_OF_exetoc.c if test 8029 -ne `wc -c <exetoc.c`; then echo shar: \"exetoc.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f smtops.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"smtops.c\" else echo shar: Extracting \"smtops.c\" \(3587 characters\) sed "s/^X//" >smtops.c <<'END_OF_smtops.c' X#if !lint && !SABER Xstatic char RcsId[] = "$Header: smtops.c,v 1.2 89/10/27 16:14:58 mlandau Exp $"; X#endif X X#include <stdio.h> X#include <fcntl.h> X#include <sys/types.h> X#include <sys/ioctl.h> X#include <sys/mtio.h> X X#if REMOTE X#include <rmt.h> X#endif X X#include "exitcodes.h" X#include "smtops.h" X X X/**************************************************************************** X * BASIC MAGTAPE OPERATIONS FOR EXEBYTE DRIVES X ***************************************************************************/ X X X/* X * smt_open(): Open a tape device. X * X * Actually, it's just a wrapper around open, but data abstraction is X * good, right? X */ X X Xint smt_open(device, mode) Xchar *device; Xint mode; X{ X int fd; X X if ((fd = open(device, mode)) < 0) X { X perror(device); X exit(EXIT_IO); X } X return (fd); X} X X X/* X * smt_close(): Close a tape device -- this is just a wrapper around close. X */ X Xvoid smt_close(tapefd) Xint tapefd; X{ X if (close(tapefd) < 0) X { X perror("tape close"); X exit(EXIT_IO); X } X} X X X/* X * smt_close_without_eof(): Rewind and close a tape device. X * X * This routine provides a rewind-and-close operation, which is X * necessary to prevent the tape device driver from adding an EOF X * mark to the tape if the last operation before closing the device X * was a write. Since we are continutally rewriting the same file, X * we need to inform the driver that we don't want a new EOF mark X * every time we do so. X */ X Xvoid smt_close_without_eof(tapefd) Xint tapefd; X{ X smt_rewind(tapefd); X smt_close(tapefd); X} X X X/* X * smt_read(): Read from the tape drive X * smt_write(): Write to the tape drive X * X * X * This are wrappers around read and write. They're mostly here X * so that we can use the rmt library if we want to. X */ X Xint smt_read(tapefd, buffer, count) Xint tapefd; Xchar *buffer; Xint count; X{ X return (read(tapefd, buffer, count)); X} X X Xint smt_write(tapefd, buffer, count) Xint tapefd; Xchar *buffer; Xint count; X{ X return (write(tapefd, buffer, count)); X} X X X/* X * smt_status(): Return the status of the tape drive. X * X * This code is cribbed from the mts command. The smt_stat structure X * looks like this: X * X * struct smt_stat X * { X * char smt_type[8]; -- cartridge type X * u_long smt_remain; -- KBytes left on tape X * u_long smt_size; -- Total size of tape (KBytes) X * u_long smt_ecc; -- ECC numbers X * long smt_wp:1; -- write protected? X * long smt_bot:1; -- at beginning of tape? X * } X */ X Xstruct smt_stat *smt_status(tapefd) Xint tapefd; X{ X static struct smt_stat status; X X if (ioctl(tapefd, SMTIOGETSTAT, &status) < 0) X { X perror("tape status"); X exit(EXIT_IO); X } X return (&status); X} X X X X/* X * smt_rewind(): Rewind a tape and verify that it worked. X */ X Xvoid smt_rewind(tapefd) Xint tapefd; X{ X static struct mtop rewind_op = { MTREW, 1 }; X X if (ioctl(tapefd, MTIOCTOP, &rewind_op) < 0) X { X perror("tape rewind"); X exit(EXIT_IO); X } X#if !REMOTE X /* rmtlib.a does not like the custom ioctls used in the smt_status X routine, so don't use it internally */ X if (!smt_status(tapefd)->smt_bot) X { X fputs("Could not rewind tape for some unknown reason."); X exit(EXIT_IO); X } X#endif X} X X X/* X * smt_eof(): Write an EOF mark on the tape. X * X * We will probably never need this, but it doesn't hurt to include it. X */ X Xvoid smt_eof(tapefd) Xint tapefd; X{ X static struct mtop eof_op = { MTWEOF, 1 }; X X if (ioctl(tapefd, MTIOCTOP, &eof_op) < 0) X { X perror("write eof"); X exit(EXIT_IO); X } X} X X END_OF_smtops.c if test 3587 -ne `wc -c <smtops.c`; then echo shar: \"smtops.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f exitcodes.h -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"exitcodes.h\" else echo shar: Extracting \"exitcodes.h\" \(153 characters\) sed "s/^X//" >exitcodes.h <<'END_OF_exitcodes.h' X#ifndef EXITCODES_H X#define EXITCODES_H 1 X X#define EXIT_OK 0 X#define EXIT_USAGE 1 X#define EXIT_IO 2 X#define EXIT_NOTOC 3 X#define EXIT_TOOBIG 4 X X#endif END_OF_exitcodes.h if test 153 -ne `wc -c <exitcodes.h`; then echo shar: \"exitcodes.h\" unpacked with wrong size! fi # end of overwriting check fi if test -f smtio.h -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"smtio.h\" else echo shar: Extracting \"smtio.h\" \(445 characters\) sed "s/^X//" >smtio.h <<'END_OF_smtio.h' X#include <sys/ioctl.h> X X/* structures and defines for ioctls */ Xstruct smt_stat { X char smt_type[8]; /* Cartridge type */ X unsigned long smt_remain; /* # of kilobytes remaining on tape */ X unsigned long smt_size; /* size (in kb) of tape */ X unsigned long smt_ecc; /* ECC count */ X long smt_wp:1; /* Write protected */ X long smt_bot:1; /* At beginning of tape */ X}; X X#define SMTIOGETSTAT _IOR(m,0,struct smt_stat) /* get the drive status */ END_OF_smtio.h if test 445 -ne `wc -c <smtio.h`; then echo shar: \"smtio.h\" unpacked with wrong size! fi # end of overwriting check fi if test -f smtops.h -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"smtops.h\" else echo shar: Extracting \"smtops.h\" \(308 characters\) sed "s/^X//" >smtops.h <<'END_OF_smtops.h' X#ifndef SMTOPS_H X#define SMTOPS_H 1 X X#include "smtio.h" Xextern struct smt_stat *smt_status(); X Xextern int smt_open(); Xextern void smt_close(); Xextern void smt_close_without_eof(); Xextern void smt_rewind(); Xextern void smt_eof(); Xextern int smt_read(); Xextern int smt_write(); X X#endif END_OF_smtops.h if test 308 -ne `wc -c <smtops.h`; then echo shar: \"smtops.h\" unpacked with wrong size! fi # end of overwriting check fi if test -f exetoc.man -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"exetoc.man\" else echo shar: Extracting \"exetoc.man\" \(3224 characters\) sed "s/^X//" >exetoc.man <<'END_OF_exetoc.man' X.TH EXETOC 1L "27 Oct 1989" X.SH NAME Xexetoc \- Manage a table of contents on an Exebyte 2GB tape cartridge. X.SH SYNOPSIS X.B exetoc X.BI [-t tape ] X.BI [-g file ] X.BI [-p file ] X.B [-i] X.B [-v] X.B [-q] X.SH DESCRIPTION X.B Exetoc is a program that allows you to write a table of contents Xfile onto the front of a 2GB helical scan tape, read it back later, Xand update it without disturbing the other contents of the tape. X.PP XIt does this by reserving a fixed amount of space at the start of Xthe tape (about 10 megabytes in the current implementation) that can be Xused to store information about the contents of the tape. About 2 Xmegabytes of this space is available for keeping the table of Xcontents. The rest acts as a buffer space between the end of the table Xof contents and the first data file on the tape. X.SH OPTIONS X.B Exetoc understands the following options: X.TP X.BI -t tapedrive XSets the tape drive name to something other than the default, which Xis derived by looking for the environment variable EXETOC, or using X"/dev/rsmt0" if the environment variable is not set. Exetoc is normally Xlinked with the remote tape library, allowing you to use Xnames of the form \fIhost:device\fP for direct access to remote tape Xdrives. X.TP X.BI -g file XGets the table of contents from the tape and places a copy of Xit into the named file. If \fIfile\fP is given as -, the table of Xcontents is copied to standard output. X.TP X.BI -p file XPuts a table of contents onto the tape. The contents of the table Xare taken from the named file, or from standard input if \fIfile\fP Xis given as -. This flag may be used to create and rewrite tables Xof contents. X.TP X.B -i XInitializes a tape by creating a blank table of contents at the Xfront of the tape. You must initialize a tape with the -i flag before Xyou can write a table of contents onto it for the first time. X.TP X.B -v XVerifies that the tape in the tape drive has previously been initialized Xwith exetoc -i. X.TP X.B -q XCauses the program to work more quietly than it otherwise would. X.LP XAny other option causes exetoc to deliver a lengthy message explaining Xthe legal flags. X.SH "ENVIRIONMENT VARIABLES" X.TP XEXEBYTE XSpecifies the default tape drive to use. Exetoc is normally linked Xwith the remote tape library, allowing you to use names of the form X\fIhost:device\fP for direct access to remote tape drives. X.SH "EXIT STATUS" XExetoc exits with one of the following well-defined status codes: X.TP X0 Xindicates successful completion of an operation. X.TP X1 Xindicates an error in usage (illegal command line flag, for instance). X.TP X2 Xindicates an I/O error of some kind. A diagnostic message is printed Xon standard error in this case, explaining what the error was. X.TP X3 Xindicates that a read, write, or verify operation was attempted with a Xtape that has never been initialized to hold a table of contents. X.TP X4 Xindicates that the table of contents file you are trying to write onto Xthe tape is too large to fit. X.SH BUGS XThe tape is rewound after \fIany\fP exetoc operation, even if the no-rewind Xdevice was specified on the command line. X.SH "SEE ALSO" X.nf Xdd(1), bdd(1), mt(1) X.SH AUTHOR X.nf XMatt Landau, BBN Systems and Technologies Corp. (mlandau@bbn.com) END_OF_exetoc.man if test 3224 -ne `wc -c <exetoc.man`; then echo shar: \"exetoc.man\" unpacked with wrong size! fi # end of overwriting check fi echo shar: End of shell archive. exit 0 exit 0 # Just in case... -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.