Article 2683 of comp.sources.unix:
Path: nntpd.lkg.dec.com!crl.dec.com!crl.dec.com!caen!usenet.cis.ufl.edu!usenet.eel.ufl.edu!news.mathworks.com!uunet!in2.uu.net!news1.digital.com!vixie!vixie!not-for-mail
From: nmm1@cus.cam.ac.uk (Nick Maclaren)
Newsgroups: comp.sources.unix
Subject: v29i027: mvstape - suite of utilities for handling IBM MVS tapes, Part08/09
Date: 4 Aug 1995 15:04:22 -0700
Organization: Vixie Enterprises
Lines: 1715
Sender: vixie@vix.com
Approved: vixie@gw.home.vix.com
Message-ID: <3vu5h6$gkq@gw.home.vix.com>
References: <1.807573809.16947@gw.home.vix.com>
NNTP-Posting-Host: gw.home.vix.com

Submitted-By: nmm1@cus.cam.ac.uk (Nick Maclaren)
Posting-Number: Volume 29, Issue 27
Archive-Name: mvstape/part08

#! /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 archive 8 (of 9)."
# Contents:  mvstape.c readtape.1
# Wrapped by nmm1@ursa.cus.cam.ac.uk on Fri Aug  4 15:43:59 1995
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'mvstape.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mvstape.c'\"
else
echo shar: Extracting \"'mvstape.c'\" \(26261 characters\)
sed "s/^X//" >'mvstape.c' <<'END_OF_FILE'
X/* (C) Copyright N.M. Maclaren 1993, 1994, 1995
X   (C) Copyright the University of Cambridge 1993, 1994, 1995
X   ALL RIGHTS RESERVED
X
Xmvstape.c  -  the initialisation module, including argument decoding and
Xtermination functions. */
X
X
X
X/* WARNING:  the whole program assumes that CHAR_BIT == 8, sizeof(short) >= 2
Xand sizeof(int) >= 4.  There is no point in putting the effort into dealing
Xwith this, given the expected lifetime of this program and the foulness of the
Xjob. */
X
X
X
X#include <ctype.h>
X
X#include "version.h"
X#include "mvstape.h"
X#include "ioerrs.h"
X
X
X
X/* The real global variables, to avoid them being passed through multiple
Xlevels of function call.  There is no need to worry about efficiency, except
Xfor the main I/O buffer and TLS dictionary. */
X
Xconst char *command_name = default_name;
X
Xint command_mode = 0, return_code = EXIT_SUCCESS;
X
Xint tape_fileno = -1, tape_file_count = -1, block_number = 0,
X    tape_type = 0, tape_marks = 0, tape_mark_count = 0,
X    IO_error_limit = 0, disk_IO_error_limit = 0,
X    IO_error_count = 0, disk_IO_errors = 0;
XCODE_PAGE *default_code_page = NULL;
Xconst char *type_names[] = {"<unset>", "TLS", "SL", "NL"};
Xchar tape_name[TAPEnamesize] = "";
Xchar tape_owner[TAPEownersize] = "";
Xchar tape_date[date_charsize] = "";
X
Xint TLS_version = 0, TLS_dict_fileno = -1, TLS_dict_length = 0,
X    TLS_dict_updateable = 0, stop_on_EOF = 0;
Xvoid *TLS_dictionary = NULL;
Xint (*read_actual_block)(void) = NULL,
X    (*read_basic_block)(void) = NULL;
Xchar *actual_block_buffer = NULL;
Xchar *block_buffer = NULL;
X
XFILE *command_file = NULL, *listing_file = NULL, *IO_error_file = NULL;
Xint tar_fileno = -1;
Xint debug = 0, verification = 0, allow_errors = 0, print_lines = 0,
X    renaming_option = -1, no_name_check = 0;
X
Xchar *command_args = NULL;
Xint command_flags = 0, max_seqno_matched = 0;
X
Xint temp_tar_fileno = -1;
Xchar temp_tar_filename[L_tmpnam] = "";
Xint temp_VBS_fileno = -1;
Xchar temp_VBS_filename[L_tmpnam] = "";
X
X
X
X/* Constants used only in this module. */
X
X#define max_tape_marks     1000     /* For checking only (could be INT_MAX) */
X#define max_IO_errors      10000    /* For checking only (could be INT_MAX) */
X#define max_print_lines    100      /* For limiting lunacy (could be INT_MAX) */
X
X
X
XSTATIC void syntax (int argc, char *argv[]) {
X
X/* Diagnose an argument error, in the typical user-hostile UNIX manner. */
X
X    int i;
X
X    if (command_mode == mode_reading)
X        fprintf(stderr,"\
XSyntax: %s [ -t tape file ] [-d disk copy ] [ -l tape type ] \n\
X                 [ -e commands | -f command file ] [ -r renaming ] \n\
X                 [ -T tar file ] [ -c code page ] [ -v | -V ] [ -D | -E ] \n\
X                 [ -F I/O error file ] [ -X I/O errors ] [ -Z ] \n\
X                 [ -W write errors ] [ -z ] \n",
X            command_name);
X    else if (command_mode == mode_scanning)
X        fprintf(stderr,"\
XSyntax: %s [ -t tape file ] [ -d disk copy ] [ -l tape type ] \n\
X                 [ -m tape marks ] [ -c code page ] [ -v ] \n\
X                 [ -p print lines ] [ -D | -E ] [ -F I/O error file ] \n\
X                 [ -X I/O errors ] [ -Z ] \n",
X            command_name);
X    else
X        fprintf(stderr,"\
XSyntax:  %s [ -t tape file ] [ -d disk copy ] [ -l tape type ] \n\
X                 [ -m tape marks ] [ -e commands | -f command file ] \n\
X                 [ -r renaming ] [ -T tar file ] [ -c code page ] \n\
X                 [ -v | -w | -V ] [ -p print lines ] [ -D | -E ] \n\
X                 [ -F I/O error file ] [ -X I/O errors ] [ -Z ] \n\
X                 [ -W write errors ] [ -z ] \n",
X            command_name);
X    if (debug > 0) {
X        for (i = 0; i < argc; ++i) fprintf(stderr,"'%s' ",argv[i]);
X        fputc('\n',stderr);
X    }
X    stop(EXIT_FAILURE);
X}
X
X
X
XSTATIC char *arg_value (char *argv[], int *p_i, const char *text) {
X
X/* Get the value if an argument flag matches, separating the value if it is
Xattached and using the next one if not.   Note that the checking for the end
Xof the arguments is done on return and that it must preserve writability (for
Xcertain kludges in command.c, to do with the -e argument). */
X
X    int len = strlen(text);
X
X    if (strncmp(argv[*p_i],text,len) != 0)
X        return NULL;
X    else if (argv[*p_i][len] != '\0')
X        return &argv[*p_i][len];
X    else if (argv[++*p_i] == NULL)
X        return "";
X    else
X        return argv[*p_i];
X}
X
X
X
XSTATIC int check_arg (const char *one, const char *two) {
X
X/* Check the equality of two strings, ignoring case. */
X
X    int i, j;
X
X    do {
X        if ((i = toupper(*one++)) != (j = toupper(*two++))) return 0;
X    } while (i != '\0' && j != '\0');
X    return 1;
X}
X
X
X
XSTATIC int get_int (const char *string) {
X
X/* Convert a string to an integer, checking that the string is nothing but
Xdigits.  It returns -1 for failure. */
X
X    int k, n;
X
X    if (! isdigit(string[0]) || sscanf(string,"%d%n",&n,&k) != 1) n = -1;
X    return n;
X}
X
X
X
XSTATIC void signal_received (const char *message) {
X
X/* This is called when the program catches a fatal signal. */
X
X    fatal("%s signal received",ERR_private,message);
X}
X
X
X
Xint main (int argc, char *argv[]) {
X
X/* Decode the options, open and close the main files and otherwise initialise
Xand terminate. */
X
X    int real_type, i, k;
X    char *arg;
X    const char *tls_dict_name = NULL;
X
X    setvbuf(stdout,NULL,_IOLBF,BUFSIZ);
X    setvbuf(stdout,NULL,_IOLBF,BUFSIZ);
X    check_compiled_code();
X
X/* Munge over the arguments, in a rather horrible way. */
X
X    if (argv[0] == NULL || ! isgraph(argv[0][0]))
X        command_name = default_name;
X    else if ((command_name = strrchr(argv[0],'/')) != NULL)
X        ++command_name;
X    else
X         command_name = argv[0];
X    if (check_arg(command_name,reading_name))
X        command_mode = mode_reading;
X    else if (check_arg(command_name,scanning_name))
X        command_mode = mode_scanning;
X    initialize_code_pages();
X    if (argc <= 1) syntax(argc,argv);
X
X/* I have never understood why getargs() is any use, because it isn't any
Xclearer and is often bug-ridden. */
X
X    errno = 0;
X    for (i = 1; i < argc; ++i) {
X        if ((arg = arg_value(argv,&i,"-c")) != NULL) {
X            if (default_code_page != NULL) syntax(argc,argv);
X            if (i >= argc ||
X                    (default_code_page = select_code_page(arg)) == NULL) {
X                fprintf(stderr,"%s code pages: ",command_name);
X                k = 0;
X                while (code_pages[k].name != NULL)
X                    fprintf(stderr," %s",code_pages[k++].name);
X                putc('\n',stderr);
X                syntax(argc,argv);
X            }
X        } else if ((arg = arg_value(argv,&i,"-d")) != NULL) {
X            if (i >= argc || tls_dict_name != NULL) syntax(argc,argv);
X            tls_dict_name = arg;
X        } else if (command_mode != mode_scanning &&
X                    (arg = arg_value(argv,&i,"-e")) != NULL) {
X            if (i >= argc || command_args != NULL) syntax(argc,argv);
X            command_args = arg;
X        } else if (command_mode != mode_scanning &&
X                    (arg = arg_value(argv,&i,"-f")) != NULL) {
X            if (i >= argc || command_file != NULL) syntax(argc,argv);
X            if (strcmp(arg,"-") == 0)
X                command_file = stdin;
X            else if ((command_file = fopen(arg,"r")) == NULL)
X                fatal("unable to open command file",ERR_system);
X        } else if ((arg = arg_value(argv,&i,"-l")) != NULL) {
X            if (i >= argc || tape_type != 0) syntax(argc,argv);
X            if (check_arg(arg,"TLS"))
X                tape_type = type_TLS;
X            else if (check_arg(arg,"SL"))
X                tape_type = type_SL;
X            else if (check_arg(arg,"NL"))
X                tape_type = type_NL;
X            else
X                syntax(argc,argv);
X        } else if (command_mode != mode_reading &&
X                (arg = arg_value(argv,&i,"-m")) != NULL) {
X            if (i >= argc || tape_marks != 0 ||
X                    (tape_marks = get_int(arg)) <= 0 ||
X                    tape_marks > max_tape_marks)
X                syntax(argc,argv);
X        } else if (command_mode != mode_reading &&
X                    (arg = arg_value(argv,&i,"-p")) != NULL) {
X            if (i >= argc || print_lines != 0 ||
X                    (print_lines = get_int(arg)) <= 0 ||
X                    print_lines > max_print_lines)
X                syntax(argc,argv);
X        } else if (command_mode != mode_scanning &&
X                    (arg = arg_value(argv,&i,"-r")) != NULL) {
X            if (i >= argc || renaming_option >= 0 ||
X                    (renaming_option = get_int(arg)) < 0 || renaming_option > 3)
X                syntax(argc,argv);
X        } else if ((arg = arg_value(argv,&i,"-t")) != NULL) {
X            if (i >= argc || tape_fileno != -1) syntax(argc,argv);
X            open_tape(arg);
X        } else if (strcmp(argv[i],"-v") == 0) {
X            if (i >= argc || verification != 0) syntax(argc,argv);
X            verification = verify_matched;    /* Until the mode is known */
X        } else if (command_mode != mode_reading && strcmp(argv[i],"-w") == 0) {
X            if (i >= argc || verification != 0) syntax(argc,argv);
X            verification = verify_all|verify_details;
X        } else if (command_mode != mode_scanning && strcmp(argv[i],"-z") == 0) {
X            if (i >= argc || no_name_check != 0) syntax(argc,argv);
X            no_name_check = 1;
X            SET_NO_NAME_CHECK();
X        } else if (strcmp(argv[i],"-D") == 0) {
X            if (i >= argc || debug != 0) syntax(argc,argv);
X            debug = 1;
X        } else if (strcmp(argv[i],"-E") == 0) {
X            if (i >= argc || debug != 0) syntax(argc,argv);
X            debug = 2;
X        } else if ((arg = arg_value(argv,&i,"-F")) != NULL) {
X            if (i >= argc || IO_error_file != 0) syntax(argc,argv);
X            if ((IO_error_file = fopen(arg,"r")) == NULL)
X                fatal("unable to open IO_error file",ERR_system);
X        } else if (command_mode != mode_scanning &&
X                (arg = arg_value(argv,&i,"-T")) != NULL) {
X            if (i >= argc || tar_fileno >= 0) syntax(argc,argv);
X            if (strcmp(arg,"-") == 0) {
X                if (STDOUT_FILENO >= 0)
X                    tar_fileno = STDOUT_FILENO;
X                else
X                    fatal("tar output cannot be sent to stdout on this system",
X                        ERR_private);
X            } else if ((tar_fileno = Open(arg,O_WRITE,O_WRMODE)) < 0)
X                fatal("unable to open output tar file",ERR_system);
X        } else if (command_mode != mode_scanning && strcmp(argv[i],"-V") == 0) {
X            if (i >= argc || verification != 0) syntax(argc,argv);
X            verification = verify_all;
X        } else if ((arg = arg_value(argv,&i,"-X")) != NULL) {
X            if (i >= argc || IO_error_limit != 0 ||
X                    (IO_error_limit = get_int(arg)) <= 0 ||
X                    IO_error_limit > max_IO_errors)
X                syntax(argc,argv);
X        } else if ((arg = arg_value(argv,&i,"-W")) != NULL) {
X            if (i >= argc || disk_IO_error_limit != 0 ||
X                    (disk_IO_error_limit = get_int(arg)) <= 0 ||
X                    disk_IO_error_limit > max_IO_errors)
X                syntax(argc,argv);
X        } else if (strcmp(argv[i],"-Z") == 0) {
X            if (i >= argc || allow_errors != 0) syntax(argc,argv);
X            allow_errors = 1;
X        } else
X            syntax(argc,argv);
X    }
X
X/* Deal with the dual-mode command, check seriously incompatible options, set
Xdefaults and set up the listing file to be stdout unless it is used for the tar
Xoutput. */
X
X    if (command_mode == 0) {
X        command_mode =
X            (command_args != NULL || command_file != NULL ? mode_reading :
X                    mode_scanning);
X        comment("the command is in %s mode",
X            (command_mode == mode_reading ? "reading" : "scanning"));
X    }
X    if (command_mode == mode_scanning) {
X        if (renaming_option >= 0)
X            warn("-r will be ignored because the tape is being scanned",
X                ERR_private);
X        renaming_option = 0;
X        if (no_name_check)
X            warn("-z will be ignored because the tape is being scanned",
X                ERR_private);
X        no_name_check = 0;
X        if (tar_fileno >= 0)
X            warn("-T will be ignored because the tape is being scanned",
X                ERR_private);
X        tar_fileno = -1;
X        if (verification == verify_all)
X            warn("-V will be treated as -v because the tape is being scanned",
X                ERR_private);
X        if (verification == verify_matched ||
X                (verification == 0 && print_lines > 0))
X            verification = verify_all;
X    } else {
X        if (verification&verify_details)
X            warn("-w will be treated as -v because the tape is being read",
X                ERR_private);
X        verification = verify_matched;
X        if (print_lines > 0)
X            warn("-p will be ignored because the tape is being read",
X                ERR_private);
X        print_lines = 0;
X        if (tape_marks > 0)
X            warn("-m will be ignored because the tape is being read",
X                ERR_private);
X        tape_marks = 0;
X    }
X    if (default_code_page == NULL) default_code_page = select_code_page(NULL);
X    if (renaming_option < 0) renaming_option = 0;
X
X/* Open the TLS dictionary or 'diskized' file if it has been specified. */
X
X    if (tls_dict_name != NULL && strcmp(tls_dict_name,"-") == 0) {
X        if (STDIN_FILENO >= 0)
X            TLS_dict_fileno = STDIN_FILENO;
X        else
X            fatal("diskized tapes cannot be read from stdin on this system",
X                ERR_private);
X        TLS_dict_updateable = 0;
X        tls_dict_name = NULL;
X    } else if (tls_dict_name != NULL &&
X            (tape_fileno < 0 || Access(tls_dict_name,RD_OK) == 0)) {
X        TLS_dict_updateable = (Access(tls_dict_name,WR_OK) == 0);
X        k = (TLS_dict_updateable ? O_UPDATE : O_READ);
X        if ((TLS_dict_fileno = Open(tls_dict_name,k,O_RDMODE)) < 0)
X            fatal("unable to read TLS dictionary or 'diskized' tape",
X                ERR_system);
X        tls_dict_name = NULL;
X    }
X
X/* Check on various incompatibility problems. */
X
X    if (tape_fileno < 0 && TLS_dict_fileno < 0)
X        fatal("either a tape device or diskized tape must be specified",
X            ERR_private);
X    if (tape_fileno >= 0 && IO_error_file != NULL)
X        fatal("an I/O error file cannot be specified for a real tape",
X            ERR_private);
X    if (command_file == stdin && TLS_dict_fileno == STDIN_FILENO)
X        fatal("it is impossible to set both -d and -f to stdin",ERR_private);
X    if (IO_error_limit == 0)
X        IO_error_limit = (command_mode == mode_scanning ? 100 : 10);
X    if (disk_IO_error_limit == 0 && command_mode == mode_reading)
X        disk_IO_error_limit = 10;
X    listing_file = (tar_fileno == STDOUT_FILENO ? stderr : stdout);
X
X/* Display the relevant global variables if debugging. */
X
X    if (debug) {
X        comment("Global variables after argument decoding:");
X        comment("command_mode = %s",
X            (command_mode == mode_reading ? "reading" :
X                (command_mode == mode_scanning ? "scanning" : "<unset>")));
X        comment("tape_fileno = %d,    tape_type = %s,    tape_marks = %d",
X            tape_fileno,type_names[tape_type],tape_marks);
X        comment("tar_fileno = %d,    TLS_dict_fileno = %d (%s)",
X            tar_fileno,TLS_dict_fileno,
X            (tls_dict_name == NULL ? "<not kept>" : tls_dict_name));
X        if (command_args != NULL)
X            comment("command_args = '%s'",command_args);
X        else
X            comment("command_args = NULL");
X        comment("command_file = %p,    listing_file = %p",
X            command_file,listing_file);
X        comment("stdin = %p,    stdout = %p,    stderr = %p",
X            stdin,stdout,stderr);
X        comment("verification = %s%s,    print_lines = %d",
X            (verification&verify_matched ? "matched" :
X                (verification&verify_all ? "all" : "none")),
X            (verification&verify_details ? "+details" : ""),
X            print_lines);
X        comment("code_page = %s,    renaming_option = %d",
X            default_code_page->name,renaming_option);
X        comment("IO_error_limit = %d,    disk_IO_error_limit = %d",
X            IO_error_limit,disk_IO_error_limit);
X        comment("allow_errors = %d,    no_name_check = %d",
X            allow_errors,no_name_check);
X        comment("");
X    }
X
X/* Read and parse the commands, close the command file, and check on a few
Xoddities. */
X
X    if (command_mode == mode_reading)
X        read_commands();
X    else
X        max_seqno_matched = max_tape_marks;
X    if (command_file != NULL && command_file != stdin) {
X        errno = 0;
X        k = fclose(command_file);
X        command_file = NULL;
X        if (k != 0) fatal("unable to close the command file",ERR_system);
X    }
X    if (command_mode == mode_reading &&
X            ! (command_flags&(match_by_name|match_by_number)))
X        warn("no matching commands were specified",ERR_private);
X
X/* Display the relevant global variables if debugging. */
X
X    if (debug) {
X        comment("Global variables after reading commands");
X        i = ((command_flags&match_by_name) && (command_flags&match_by_number) ?
X                0x1 : 0x0);
X        i |= ((command_flags&format_specified) &&
X                    (command_flags&(match_by_name|match_by_number)) ?
X                0x2 : 0x0);
X        comment("command_flags = %s%s%s%s%s,    max_seqno_matched = %d",
X            (command_flags&match_by_name ? "by_name" : ""),
X            (command_flags ? (i&0x1 ? "+" : "") : "none"),
X            (command_flags&match_by_number ? "by_number" : ""),
X            (i&0x2 ? "+" : ""),
X            (command_flags&format_specified ? "formats" : ""),
X            max_seqno_matched);
X        comment("");
X    }
X
X/* Get an I/O buffer, large enough for a full TLS dictionary.  It is also
Ximportant to allow 4 bytes over the maximum normal blocksize to simplify the
Xcode in unpick.c, but that is no problem.  Then set up the signal handler. */
X
X    errno = 0;
X    if ((actual_block_buffer = malloc(constant_64K)) == NULL)
X        fatal("unable to get space for main I/O buffer",ERR_system);
X    block_buffer = actual_block_buffer;
X    basic_tape_signal(signal_received);
X
X/* Set up the input function variables, read the volume labels and TLS
Xdictionary, and rewind the tape and close the TLS dictionary if both were
Xspecified.  Note that the structure of this code is critical to the working of
Xfunction read_disk_block() in misc.c when reading from stdin. */
X
X    block_number = 0;
X    read_basic_block = read_simple_block;
X    if (TLS_dict_fileno >= 0) {
X        read_actual_block = read_disk_block;
X        real_type = read_volume_labels(1);
X        if (tape_fileno >= 0) {
X            rewind_tape();
X            if (TLS_dict_fileno != STDIN_FILENO) {
X                errno = 0;
X                k = Close(TLS_dict_fileno);
X                TLS_dict_fileno = -1;
X                if (k != 0)
X                    fatal("unable to close disk copy of TLS dictionary",
X                        ERR_system);
X            } else
X                TLS_dict_fileno = -1;
X            read_actual_block = read_tape_block;
X
X/* If there is no tape, rewind the 'diskized' file; if there is no disk file,
Xrewind the tape. */
X
X        } else {
X            errno = 0;
X            if (TLS_dict_fileno != STDIN_FILENO &&
X                    Lseek(TLS_dict_fileno,0l,SEEK_SET) < 0)
X                fatal("unable to reposition 'diskized' tape file",ERR_system);
X            reset_disk_file(0);
X        }
X    } else {
X        read_actual_block = read_tape_block;
X        real_type = read_volume_labels(0);
X        rewind_tape();
X    }
X
X/* Check the type of tape and issue some diagnostics that need it. */
X
X    if (tape_type == 0)
X        tape_type = real_type;
X    else if (tape_type < real_type) {
X        warn("the tape type was specified as %s but is actually %s",
X            ERR_private,type_names[tape_type],type_names[real_type]);
X        tape_type = real_type;
X    } else if (tape_type > real_type)
X        comment("%s processing will be used, though the tape is %s",
X            type_names[tape_type],type_names[real_type]);
X    if (tape_type != type_TLS && tape_fileno >= 0 &&
X            (TLS_dict_fileno >= 0 || tls_dict_name != NULL))
X        fatal("both -t and -d were specified for non-TLS processing",
X            ERR_private);
X
X/* Check that the label-specific processing is appropriate. */
X
X    if (tape_fileno >= 0 && tape_type == type_NL) {
X        if (tape_marks <= 0)
X            tape_marks = (command_mode == mode_reading ? max_tape_marks : 1);
X    } else {
X        if (tape_marks > 0)
X            warn("the -m argument will be ignored",ERR_private);
X        tape_marks = max_tape_marks;
X        if (command_mode == mode_scanning && tape_type == type_NL)
X            stop_on_EOF = 1;
X    }
X    if (tape_type == type_NL && (command_flags&match_by_name)) {
X        fatal("cannot match by name on unlabelled tapes",ERR_private);
X        if (! (command_flags&match_by_number)) stop(EXIT_FAILURE);
X    }
X    if (tape_type == type_NL && command_mode == mode_scanning && verification)
X        verification |= verify_details;
X    if (tape_type != type_NL && (command_flags&format_specified))
X        warn("the format command is ignored for labelled tapes",ERR_private);
X
X/* Display the relevant global variables if debugging. */
X
X    if (debug) {
X        comment("Global variables after reading the tape labels:");
X        comment("return_code = %d,    verification = %s%s",return_code,
X            (verification&verify_matched ? "matched" :
X                (verification&verify_all ? "all" : "none")),
X            (verification&verify_details ? "+details" : ""));
X        comment("tape_name = %s,    tape_owner = '%s',    tape_date = %s",
X            tape_name,tape_owner,tape_date);
X        comment("tape_type = %s,    tape_marks = %d",
X            type_names[tape_type],tape_marks);
X        comment("TLS_version = %d",TLS_version);
X        comment("read_actual_block = %p,    read_basic_block = %p",
X            read_actual_block,read_basic_block);
X        comment("read_disk_block = %p,    read_tape_block = %p",
X            read_disk_block,read_tape_block);
X        comment("read_simple_block = %p,    read_TLS_block = %p",
X            read_simple_block,read_TLS_block);
X        comment("actual_block_buffer = %p,    block_buffer = %p",
X            actual_block_buffer,block_buffer);
X        comment("");
X        if (tape_type != type_NL && debug >= 2) {
X            display_buffer(stderr,vol1_label,TAPE_LABEL_SIZE,TAPE_LABEL_SIZE);
X            display_buffer(stderr,hdr1_label,TAPE_LABEL_SIZE,TAPE_LABEL_SIZE);
X        }
X        if (TLS_dictionary != NULL) display_TLS_dictionary();
X    }
X
X/* Write the TLS dictionary, if appropriate, and print out the volume
Xinformation. */
X
X    if (tape_type == type_TLS && tls_dict_name != NULL)
X        write_TLS_dictionary(tls_dict_name);
X    if (verification != 0 || command_mode == mode_scanning) print_volume();
X
X/* If there are no commands and the whole tape is not being verified, we are
Xdone.  Otherwise, go and either read or scan the tape, and stop when done.  It
Xwould be slightly cleaner to close the files here, but it is done in function
Xstop() just a few lines down!  Note that there is a kludge when scanning disk
Xfiles that causes a direct call to stop() from function read_diskized_data() in
Xmisc.c. */
X
X    if (command_mode == mode_reading) {
X        if (command_flags == 0) stop(EXIT_SUCCESS);
X        errno = 0;
X        if (tmpnam(temp_tar_filename) == NULL ||
X                (temp_tar_fileno = Open(temp_tar_filename,
X                        O_SCRATCH,O_WRMODE)) < 0)
X            fatal("unable to open the tar work file",ERR_system);
X        process_commands();
X    } else {
X        if (verification == 0 && print_lines <= 0) stop(EXIT_SUCCESS);
X        process_scanning();
X    }
X    stop(EXIT_SUCCESS);
X    return EXIT_SUCCESS;    /* Silencing various compilers */
X}
X
X
X
Xvoid stop (int code) {
X
X/* This closes the tape, issues final diagnostics, closes any remaining open
Xfiles, and exits. It does not seem worth the bother of freeing store etc.,
Xbecause this should happen automatically in exit(). */
X
X    static int closed = 0, dead = 0;
X
X    if (code != EXIT_SUCCESS) comment("further processing has been abandoned");
X    if (tape_fileno >= 0 && ! closed) {
X        closed = 1;
X        rewind_tape();
X        close_tape();
X    }
X    if (dead) abort();    /* Our knickers are completely twisted! */
X    dead = 1;
X    if (IO_error_count > 0)
X        comment("there were %d I/O errors detected on tape",IO_error_count);
X    if (disk_IO_errors > 0)
X        comment("there were %d failures detected when writing to disk",
X            disk_IO_errors);
X    if ((code != EXIT_SUCCESS || debug) && tape_file_count >= 0)
X        comment("%d files were scanned on the tape",tape_file_count);
X
X/* Close the possibly open files. */
X
X    if (tar_fileno >= 0 && tar_fileno != STDOUT_FILENO && Close(tar_fileno))
X        warn("unable to close tar file",ERR_system);
X    if (TLS_dict_fileno >= 0 && TLS_dict_fileno != STDIN_FILENO &&
X            Close(TLS_dict_fileno))
X        warn("unable to close TLS disk dictionary",ERR_system);
X    if (command_file != NULL && command_file != stdin && fclose(command_file))
X        warn("unable to close command file",ERR_system);
X    if (IO_error_file != NULL && fclose(IO_error_file))
X        warn("unable to close I/O error file",ERR_system);
X
X/* Close and delete the work files, if any. */
X
X    if (temp_VBS_fileno >= 0 && Close(temp_VBS_fileno))
X        warn("unable to close VBS work file",ERR_system);
X    if (temp_VBS_filename[0] != '\0' && Unlink(temp_VBS_filename))
X        warn("unable to delete VBS work file",ERR_system);
X    if (temp_tar_fileno >= 0 && Close(temp_tar_fileno))
X        warn("unable to close tar work file",ERR_system);
X    if (temp_tar_filename[0] != '\0' && Unlink(temp_tar_filename))
X        warn("unable to delete tar work file",ERR_system);
X
X/* And this is where we normally stop! */
X
X    exit(code == EXIT_SUCCESS ? return_code : code);
X}
END_OF_FILE
if test 26261 -ne `wc -c <'mvstape.c'`; then
    echo shar: \"'mvstape.c'\" unpacked with wrong size!
fi
# end of 'mvstape.c'
fi
if test -f 'readtape.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'readtape.1'\"
else
echo shar: Extracting \"'readtape.1'\" \(25110 characters\)
sed "s/^X//" >'readtape.1' <<'END_OF_FILE'
X.TH READTAPE 1L
X.SH NAME
Xreadtape \- read a 1/2" magnetic tape or `diskized' tape file
X.SH SYNOPSIS
X.B readtape
X.BI \-t " tape"
X[
X.BI \-d " file"
X]
X[
X.BI \-l " type"
X]
X[
X.BI \-e " commands"
X]
X[
X.BI \-f " file"
X]
X[
X.BI \-r " renaming"
X]
X[
X.BI \-T " file"
X]
X[
X.BI \-c " codepage"
X]
X[
X.B \-v
X|
X.B \-V
X]
X[
X.B \-D
X|
X.B \-E
X]
X[
X.BI \-F " errfile"
X]
X[
X.B \-x
X]
X[
X.BI \-X " errors"
X]
X[
X.BI \-W " errors"
X]
X.SH DESCRIPTION
XThe
X.I readtape
Xcommand is used to read a TLS, other IBM standard labelled or unlabelled tape
Xor `diskized' tape file, which is assumed to have been written on an IBM MVS
Xsystem, but need not have been.  Other tapes can be handled, but their contents
Xmay have to be converted by a separate utility, as will any files with unusual
Xformats.  The command normally determines the type of tape automatically, but
Xthere is an option to force a particular assumption.  [TLS is the Tape Library
XSystem using IBM standard labelled tapes that was developed as part of the
XPhoenix user-friendly front end to MVS by the University of Cambridge Computing
XService.]
X.PP
XIEBCOPY unloaded PDSs on non-TLS tapes will be detected if the record format
X.I VS
Xand the record length is 4 bytes less than the blocksize.  They will then be
Xhandled much like TLS PDSs.
X.PP
XIt will handle sequential files in any normal MVS format (i.e. not
X.IR RECFM=D ),
XPartitioned Datasets (PDSs) in TLS or IEBCOPY unloaded formats and simple direct
Xaccess files in TLS format, but no VSAM files, IEHMOVE unloaded files or worse.
X.PP
XThe command can read the files into the UNIX filing system or create a
X.I tar
Xformat file containing them.  There are facilities for selecting subsets of the
Xfiles and mapping MVS to UNIX names, and some recovery from I/O errors.  It is
Xnot, however, designed for recovering data from badly corrupted tapes.  It can
Xalso read a `diskized' tape produced by the Phoenix command
X.I DISCTLS
Xor the
X.I loadtape
Xcommand.
X.SS Options
X.I readtape
Xrecognises the following arguments:
X.TP
X.B \-c
Xspecifies the default code page to use for EBCDIC to ASCII conversion:
X.I phx
X(the one used in Phoenix, and the default),
X.I phx8
X(the 8-bit Phoenix one),
X.I phxmvt
X(the one used when Phoenix ran under MVT),
X.I ibmmvs
X(`standard' IBM MVS),
X.I ibm037
X(IBM code page 37) or
X.I ibm500
X(IBM code page 500).  The case of the code page name is ignored.
X.TP
X.B \-d
Xspecifies a disk copy of the header labels and TLS dictionary, which will be
Xread if it exists and created if it does not.
X.TP
X.B \-e
Xspecifies a string containing the matching commands to use.
X.TP
X.B \-f
Xspecifies a file of matching commands, to concatenate after the ones in the
X.B \-e
Xargument (if any).
X.TP
X.B \-l
Xspecifies the tape type (with case ignored):
X.I TLS
X(Phoenix Tape Library System),
X.I SL
X(IBM Standard Label) or
X.I NL
X(unlabelled or something else).  This can be used to handle a TLS tape as SL or
XNL and a SL tape as NL.  The default is to determine the type from the tape.
X.TP
X.B \-r
Xspecifies a number between 0 and 3 inclusive, which controls some aspects of
Xthe default renaming algorithm; the default is 0.  See later for details.
X.TP
X.B \-t
Xspecifies the tape device file on which the magnetic tape is mounted; this must
Xbe a non-rewinding device.
X.TP
X.B \-v
Xwrites the MVS dataset name and some label information for each file matched,
Xand the UNIX file name it is copied to.  See
X.I scantape(1L)
Xfor more information.
X.TP
X.B \-x
Xcarries on reading files after I/O errors (default is to skip the file).
X.TP
X.B \-D
Xprints debugging information on stderr (not useful for most users).
X.TP
X.B \-E
Xprints an excessive amount of debugging information (not recommended for most
Xusers, even in desperation).
X.TP
X.B \-F
Xspecifies the name of a file containing block numbers to emulate I/O errors in
X`diskized' files (not useful for most users).
X.TP
X.B \-T
Xspecifies the name of a
X.I tar
Xfile to write instead of creating each file on the UNIX filing system.
X.TP
X.B \-V
Xwrites the MVS dataset name and some label information for each file on the
Xtape, whether copied or not, and the UNIX file name if copied.
X.TP
X.B \-W
Xspecifies how many file allocation or I/O errors occur before the command stops
X(default 10, maximum 10000). This applies only to the disk files that are
Xcopied off tape; all I/O errors on work files are fatal.
X.TP
X.B \-X
Xspecifies how many I/O errors occur before the command stops (default 100,
Xmaximum 10000).
X.PP
XNormally the
X.B \-t
Xargument is compulsory, but there is an exception for `diskized' tape files.  If
X.B \-d
Xis specified and
X.B \-t
Xis not, the disk copy of the header labels etc. is assumed to be a `diskized'
Xtape file and will be read as if it were the tape.  Such a `diskized' tape file
Xmay be created by the Phoenix command
X.I DISCTLS
Xor the
X.I loadtape
Xcommand.
X.PP
XThe
X.BR \-d ,
X.B \-f
Xand
X.B \-T
Xarguments may use
X.B \-
Xinstead of a filename, in which case
X.I stdin
Xor
X.I stdout
Xwill be used (as appropriate), so that the command can be used in a pipe.
XNaturally, it is forbidden for both
X.B \-d
Xand
X.B \-f
Xto refer to
X.I stdin
Xin the same command.  Verification (i.e. output from the
X.B \-v,
Xor
X.B \-V
Xoptions) will be sent to
X.I stdout
Xunless
X.BR \-T " " \-
Xis specified (i.e.
X.I stdout
Xis being used for
X.I tar
Xfile output), when it will be sent to
X.I stderr.
XNote that the
X.B \-t
Xargument must refer to a real tape device, because positioning operations are
Xneeded.
X.PP
XIf the
X.B \-d
Xoption refers to a disk file that was created by the
X.I loadtape
Xcommand with a dummy block in place of the TLS dictionary, an attempt will be
Xmade to replace the block with a valid TLS dictionary recovered from the 
Xtrailer labels.  This is likely only on tape decks that cannot read blocks of
Xbetween 32K and 64K.
X.PP
XThe
X.IR phx ,
X.I phxmvt
Xand
X.I ibmmvs
Xcode pages map EBCDIC to 7-bit ASCII with both EBCDIC
X.I NL
Xand
X.I LF
Xmapped to ASCII
X.IR LF ,
Xand EBCDIC
X.I NUL
Xmapped to ASCII
X.I SUB
X(to avoid confusing UNIX utilities); the first two also map pound sterling to
XISO Latin 1.  The remainder map all 256 EBCDIC characters to ISO Latin 1.
X.PP
XUnlike most UNIX utilities, incompatible or impossible options are an error.  A
Xfew non-fatal warnings are given when the options are incompatible with the
Xtape, or with the commands provided, but most errors are fatal.  The exit code
Xwill be non-zero if some function cannot be performed, whether or not
Xprocessing continued.
X.SH MATCHING COMMANDS
XFile matching commands to
X.I readtape
Xare read from the
X.B \-e
Xargument and the file specified by the
X.B \-f
Xargument and are separated by semicolons or newlines.  They are applied to
Xeach file on the MVS tape in turn until there is a match, so a file will never
Xbe copied more than once (see later about TLS tapes).  If there is more than
Xone file of the same name, each will be copied, which usually means that the
Xlast one will overwrite the rest.  The following commands are available:
X.RS 5.0
X.PP
X.B PREFIX
X[
X.I file name
X]
X.RE
X.PP
XThis sets up a Phoenix (or TSO) prefix to use for MVS file names beginning with
Xa
X.BR . ;
Xthe default (which may be reset by a
X.B PREFIX
Xcommand without a name) is the UNIX login userid.  If the file name ends in
Xa
X.B .
X(period), it will be removed; this means that the command
X.B "PREFIX ."
Xwill set a null prefix, which can be useful.  Note that the
X.B PREFIX
Xcommand replaces the prefix, and does not extend it (unlike the
X.B CD
Xcommand).
X.RS 5.0
X.PP
X.B CODE
X[
X.I code page
X]
X.RE
X.PP
XThis sets up a code page to use for the following commands.  The code page may
Xbe any of the ones that can be specified in the
X.B \-c
Xargument.  The default (which may be reset by a
X.B CODE
Xcommand without a name) is the value set by the arguments.
X.RS 5.0
X.PP
X.B CD
X[
X.I directory
X]
X.RE
X.PP
XThis sets up a UNIX directory to use for UNIX file names not beginning with a
X.BR / ;
Xthe default (which may be reset by a CD command without a name) is the current
Xworking directory on entry to the command.  Note that
X.B .
Xand
X.B ..
Xare handled textually and not by reference to the real filing system; only the
Xmost perverse use will notice the difference.
X.RS 5.0
X.PP
X.BI FORMAT " format"
X[
X.I record length
X]
X.RE
X.PP
XThis sets up a default MVS file format for the following commands; it is
Xignored with a warning if the tape is labelled (including TLS tapes).  The
Xformat may be one of
X.IR F[B][S][A] ,
X.I V[B][S][A]
Xor
X.IR U[A] ,
Xand the default is
X.I U
X65536 (sic).  The record length is optional unless the format is
X.I F[B][S][A]
Xand may be from 1 to 65536;
X.I U[A]
Xdefaults to 65536,
X.I V[B][A]
Xto 65532 (sic) and
X.I V[B]S[A]
Xto unlimited.
X.RS 5.0
X.TP
X.BI COPY " filename"
X[
X.B TEXT
X|
X.B DATA
X|
X.B BINARY
X|
X.B STREAM
X|
X.B LINES
X|
X.B RECORDS
X|
X.B BLOCKS
X]
X[
X.B TRANS
X|
X.B NOTRANS
X]
X[
X.B STRIP
X|
X.B NOSTRIP
X]
X[
X.B TRANSCC
X|
X.B IGNORECC
X|
X.B INCLUDECC
X]
X.TP
X.BI APPEND " filename"
X[
X.B TEXT
X|
X.B DATA
X|
X.B BINARY
X|
X.B STREAM
X|
X.B LINES
X|
X.B RECORDS
X|
X.B BLOCKS
X]
X[
X.B TRANS
X|
X.B NOTRANS
X]
X[
X.B STRIP
X|
X.B NOSTRIP
X]
X[
X.B TRANSCC
X|
X.B IGNORECC
X|
X.B INCLUDECC
X]
X.RE
X.PP
XThis copies the MVS file matched by the name with the mode and options
Xspecified (see later); the default is
X.BR TEXT .
XIf
X.B APPEND
Xis used and the UNIX file exists, the file will be appended to it rather than
Xoverwriting it; a warning is given if
X.B APPEND
Xis used with
X.BR -T .
X.PP
XIf the MVS file name matches the filename specified, ignoring case, the former
Xis converted to a UNIX file name by some empirical rules that convert some
Xcommon MVS conventions to their nearest equivalent UNIX ones; these rules are
Xdescribed later.  There are a few extra constraints on the matching rules, but
Xthey should affect few users; see later for details.
X.B *
Xis treated as a wild character and will match any sequence of characters
X(including none); there may be up to 10
X.BR * s
Xin the name.
X.PP
XThe file name may also be of the form
X.I n
Xor
X.IR n-m ,
Xwhere
X.I m
Xand
X.I n
Xare integers, in which case the file with those particular sequence numbers are
Xcopied; these numbers correspond to those used by TLS, the
X.I fileseq
XPhoenix parameter and the
X.I LABEL
XJCL parameter.  Unlabelled tapes can be copied only by number, obviously.  The
Xnumbers may be followed by a PDS member name in the usual format (possibly
Xincluding wild cards), for copying particular members of PDSs specified by
Xsequence number.
X.PP
X.B TEXT
Xis the usual mode for human-readable text, and is the default; it is a shorthand
Xfor
X.B LINES
Xwith a default of
X.B TRANS,
X.B STRIP
Xand
X.B TRANSCC.
X.B DATA
Xmay be more useful for some data files; it is a shorthand for
X.B LINES
Xwith a default of
X.B TRANS
Xand
X.B INCLUDECC.
X.B BINARY
Xis simple stream binary; it is a shorthand for
X.B STREAM
Xwith a default of
X.B IGNORECC.
XThese should be enough for most users, and are very similar to the two modes of
XInternet
X.I ftp.
XThe following basic modes may be useful to the more expert:
X.PP
X.B STREAM
Xignores all record boundaries and concatenates the record contents end-to-end.
X.B LINES
Xreplaces record boundaries by UNIX newlines.
X.B RECORDS
Xwrites the length as a 4-byte integer in binary before and after the
Xrecord contents (like BSD Fortran unformatted files).
X.B BLOCKS
Xdoes the same with the physical tape blocks.
X.PP
X.B TRANS
Xspecifies translation from EBCDIC; the default (except for
X.B TEXT
Xand
X.B DATA)
Xis
X.B NOTRANS
Xwhich copies the data as binary.
X.B STRIP
Xremoves trailing spaces; the default (except for
X.B TEXT)
Xis
X.B NOSTRIP,
Xwhich does not.
X.B TRANSCC
Xtranslates ANSI control characters into their ASCII equivalents (see later),
X.B IGNORECC
Xignores any ANSI or machine code control character field; the default (except
Xfor
X.B TEXT
Xand
X.B BINARY)
Xis
X.B INCLUDECC
Xwhich treats them as part of the data.  It is an error to specify
Xany of these options with
X.B BLOCKS
Xmode, or to specify
X.B TRANSCC
Xfor other than
X.B LINES
Xmode.
X.RS 5.0
X.TP
X.BI COPY " filename1" AS " filename2"
X[
X.B TEXT
X|
X.B DATA
X|
X.B BINARY
X|
X.B STREAM
X|
X.B LINES
X|
X.B RECORDS
X|
X.B BLOCKS
X]
X[
X.B TRANS
X|
X.B NOTRANS
X]
X[
X.B STRIP
X|
X.B NOSTRIP
X]
X[
X.B TRANSCC
X|
X.B IGNORECC
X|
X.B INCLUDECC
X]
X.TP
X.BI APPEND " filename1" AS " filename2"
X[
X.B TEXT
X|
X.B DATA
X|
X.B BINARY
X|
X.B STREAM
X|
X.B LINES
X|
X.B RECORDS
X|
X.B BLOCKS
X]
X[
X.B TRANS
X|
X.B NOTRANS
X]
X[
X.B STRIP
X|
X.B NOSTRIP
X]
X[
X.B TRANSCC
X|
X.B IGNORECC
X|
X.B INCLUDECC
X]
X.RE
X.PP
XThis matches and copies the file exactly as for the previous form of
X.B COPY.
Xthe only difference is that the second file name is a UNIX file name that gives
Xthe name to be used.  The second file name may include up to as many
X.BR * s
Xas there were in the first, and these are matched in order.  If the first file
Xname is of the form
X.I n-m
X(not
X.IR n ),
Xthere is an initial implicit
X.BR * ,
Xwhich matches the sequence number padded to four digits with leading zeroes
X(i.e. from 0001 up to 9999).   Thus there may be up to one more
X.B *
Xin the second name than in the first, and a command like
X.B "COPY 5-23:ERR*"
Xwill produce names like
X.I "ERRLIST.0017"
X(see later for details).
X.RS 5.0
X.PP
X.BI SKIP " filename"
X.RE
X.PP
XThis skips the MVS file name specified (i.e. does not copy it), and may be used
Xwhen most files of a particular form are to be copied, but a few are not.
X.SS Usage Notes
XThe default handling of text files under Phoenix is to treat ANSI control
Xcharacters as separate from the record contents, and to strip trailing spaces.
XThe default action of most MVS and CMS utilities and languages is the converse.
XThe actions of UNIX utilities are too bizarre and multifarious to be described
Xhere, but the following notes may help with moving files from MVS and CMS to
XUNIX.
X.PP
XMost human-readable text, including programs and most data, will be transferred
Xcorrectly by default (i.e. in
X.B TEXT
Xmode).  If readtape gives warnings about bad ANSI control characters or the
Xresult appears to be missing its first column, then option
X.B INCLUDECC
Xshould be used.  Any file with important machine code control characters should
Xbe transferred with
X.B INCLUDECC,
Xand the control character field (i.e. the first column) edited using
X.I sed,
X.I awk,
X.I perl,
X.I icon,
X.I emacs
Xor any other suitable utility.
X.PP
XMost UNIX Fortran implementations break the Fortran 77 standard and do not
Xrequire trailing spaces for formatted data input, whereas MVS and CMS Fortrans
Xfollow the standard and do.  This also applies to many packages written in
XFortran or following Fortran conventions (e.g. the statistical package SPSS).
XStripping trailing spaces is usually a good idea because it saves so much space
X(though it is not done by many Internet
X.I ftp
Xcommands), but can be disabled with the
X.B NOSTRIP
Xoption for
X.B TEXT
Xmode or by using
X.B DATA
Xmode.
X.PP
XThe code pages provided should satisfy most requirements but, if they do not,
Xit is possible to read the file as binary and then translate it with
X.IR tr .
XThis is the reason that the otherwise strange combination of mode
X.B LINES
Xand
X.B BINARY
Xis supported.
X.PP
XFortran unformatted files can be transferred to most UNIX Fortrans by using
X.B RECORDS
Xmode, and can then be read as Fortran unformatted files on UNIX.  If they
Xcontain solely integers, the default of no translation will work for big-endian
Xmachines (including Sun); if they contain solely characters, the
X.B TRANS
Xoption should be specified.  In all other cases, they will need unpicking
Xafter they have been read into Fortran on UNIX; this is not difficult, but is
Xtricky, and an expert should be consulted.
X.PP
XThe only form of direct access file handled is simple fixed format, unkeyed,
Xwithout capacity records, but this accounts for 95% of all MVS and CMS direct
Xaccess files.  In general, they should be transferred in
X.B STREAM
Xmode with
X.B NOSTRIP,
Xand they can then be read as Fortran, BCPL, PL/I or whatever direct access
Xfiles.  The constraints on interpreting their contents are exactly the same as
Xfor Fortran unformatted files, as described above.
X.PP
XFiles that contain stream binary (such as DVI, Plotstream etc.) are most
Xeasily transferred in
X.B BINARY
Xmode; if this does not work, try the
X.B INCLUDECC
Xoption to see if that improves things.  Semi-stream files of printable
Xcharacters (e.g. TTP Hex. or Postscript) may as well be copied in
X.B TEXT
Xmode, though several other modes will work.  Some UNIX utilities may object to
Xthe newline characters, in which case
X.B STREAM
Xmode with the
X.B TRANS
Xoption should be used (and possibly
X.B INCLUDECC
Xand
X.B NOSTRIP,
Xwhen necessary).
X.PP
X.B BLOCKS
Xmode is solely for the expert to unpick bad or non-standard files, and will
Xinvariably require writing a program to unpick the result; C, C++, Lisp, Perl
Xor Icon are likely to be the best languages for this.  In desperation (e.g. if
Xthe command refuses to transfer the file at all, because of bad labels or
Xinvalid TLS format),
X.I readtape
Xcan be used with the
X.BR -l " " nl
Xoption, and file
X.I 3*N-1
Xread (where
X.I N
Xis the standard label or TLS sequence number).  A user who needs to do this may
Xfind it easier to extend the source of
X.I readtape,
Xto add support for this extra kind of file.
X.SS Restrictions
XFor implementation and sanity reasons, there are a few constraints on the
Xcommands that are acceptable to
X.I readtape.
XUsers writing sensible
X.B COPY
Xcommands should rarely notice these, but they need to be mentioned.  The most
Ximportant are:
X.PP
XOnly the last 17 characters of MVS file names are stored in the tape labels,
Xthough TLS stores the whole 44 characters.  For matching purposes on non-TLS
Xtapes, these 17 characters will be used as if they are the whole MVS name.
XIf this causes the MVS name to start with a
X.B .
X(period), the command
X.B PREFIX .
Xcan be used to set a null prefix for matching it; without this, it can be
Xmatched only by number.
X.PP
XA MVS name will normally match a whole file (sequential or PDS); each member of
Xthe latter will be copied as a separate UNIX file.  There is no check against
Xmatching whole PDSs with a
X.B COPY
Xcommand with an
X.B AS
Xclause and, in such cases, the members will overwrite one another.
X.PP
XPDS members may be matched by a MVS file name that uses normal Phoenix notation
X(i.e. either
X.I A.B(C)
Xor
X.IR A.B:C );
Xwild cards are allowed in both the main name and the member name (e.g.
X.IR FRED.JOE.*:DOC* ).
X.B AS
Xclauses are also allowed.
X.PP
XPDS aliases are matched as if they were main names but are set up as hard links
Xif the file has already been copied under the main name or a previous alias.  If
Xthe
X.B APPEND
Xcommand was used, the link will not remove an existing file or link; otherwise
Xit will do so.  Directories are not removed in this way.
X.PP
XIf the command is operating in TLS mode, only the latest non-deleted file with
Xthe same name will be considered for matching by name.  The directory can be
Xprinted out in full by using the
X.B -V
Xargument or the scantape command, and earlier versions can then be transferred
Xby sequence number.
X.PP
XThere is no further check in the command for identical UNIX file names, nor
Xwhether a UNIX name matches a directory.  When transferring
X.I FRED.JOE
Xand other files starting
X.IR FRED.JOE. ,
Xcommands like the following may be used:
X.RS 5.0
X.PP
X.B COPY FRED.JOE as fred/joe/!!!
X.PP
X.B COPY FRED.JOE.*
X.RE
X.PP
XIf
X.B -T
Xis specified, it must be possible to allocate a temporary file large enough to
Xhold the longest file.  Also, handling
X.I V[B]S[A]
Xrecords needs another one long enough to hold the largest record.
X.PP
XThe command refuses to copy files with invalid DCB characteristics, incorrect
Xformats and so on.  These can still be copied as
X.B BLOCKS,
Xor by treating the tape as unlabelled and specifying format
X.IR U ,
Xbut they must then be unpicked by another program.
X.PP
XCertain unusual combinations of options and file formats cause warnings,
Xbecause they cannot be handled properly or involve the loss of information.
XThese should all be self-explanatory.
X.SS Default File Names
XThe following default name conversion rules are used when a
X.B COPY
Xcommand without
X.B AS
Xis used.  They should enable most MVS tapes to be read by a few simple
X.B COPY
Xcommands.  Note that the feature of absolute path names has not been
Xperpetrated in this command, though they can be created using
X.B AS
Xclauses.
X.RS 5.0
X.PP
XAll letters are changed to lower case.
X.PP
XAny leading
X.B .
X(period) is removed; a prefix set by the
X.B PREFIX
Xcommand is ignored for the purpose of creating the UNIX name.
X.PP
XAll occurrences of
X.B .
X(period) are changed to
X.BR / .
X.PP
XIf the name then ends in a string like
X.I /abc:xyz
Xor
X.IR /abc(xyz) ,
Xand the
X.B \-r
Xoption is 0 or 2, that part is changed to
X.I /xyz.abc
X(note the order).  If the
X.B \-r
Xoption is 1 or 3, that part is changed to
X.IR /abc/xyz .
X.PP
XIf the name then ends in a string like
X.IR /abc/xyz ,
Xand the
X.B \-r
Xoption is 2 or 3, that part is changed to
X.IR /abc.xyz ;
Xnote that this rule is applied after the previous one.
X.PP
XThe name is then interpreted relative to the directory specified by the
X.B CD
Xcommand, if any, or current working directory.
X.PP
XThe above has the effect of turning the MVS filename components and the PDS
Xmember name (if any) into a UNIX directory structure, except possibly the last
X(depending on the value of the
X.B \-r
Xoption).  MVS names like
X.I .FRED.JOE.BERT
Xand
X.I SUE.JILL(SALLY)
X(or
X.IR SUE.JILL:SALLY )
Xwill create the following UNIX names:
X.RS 5.0
X.TP
X.IR fred/joe/bert " and " sue/sally.jill " (-r0)"
X.TP
X.IR fred/joe/bert " and " sue/jill/sally " (-r1)"
X.TP
X.IR fred/joe.bert " and " sue/sally.jill " (-r2)"
X.TP
X.IR fred/joe.bert " and " sue/jill.sally " (-r3)"
X.RE
X.PP
XDefault names for unlabelled tape files are
X.IR tapefile_* ,
Xwhere
X.B *
Xis the sequence number padded to four digits (as described above).
X.RE
X.SS Text File Conversion
XConverting text files from MVS format to UNIX is fairly simple.  There are a
Xfew cases that cannot be handled properly, and the command will issue a warning
Xwhen it meets any of these.  They include:
X.RS 5.0
X.PP
XEBCDIC characters that have no ASCII equivalent; the only one that occurs in
Xnormal text files at Cambridge is EBCDIC pound and this is converted to the
XISO Latin 1 pound.  Note that binary zero
X.RI ( NUL )
Xis treated as one of these, to avoid confusing UNIX utilities.  They are all
Xreplaced by
X.IR SUB .
X.PP
XEBCDIC newline-like control characters
X.RI ( LF ,
X.IR FF ,
X.I VT
Xand
X.I CR)
Xthat occur in the text of a record.  These are translated correctly, with a
Xwarning.
X.PP
XANSI control characters other than
X.IR space ,
X.IR 0 ,
X.IR \- ,
X.I 1
Xand
X.IR + .
XThese are translated to the sequence
X.IR NL-VT ,
Xwith a warning.  A warning is also given for
X.I +
Xas the first ANSI control character of a file.
X.PP
XNote that ANSI control characters
X.IR space ,
X.IR 0 ,
X.IR \- ,
X.I 1
Xand
X.I +
Xare translated to
X.IR NL ,
X.IR NL-NL ,
X.IR NL-NL-NL ,
X.I NL-FF
Xand
X.IR CR ,
Xrespectively, with the first newline omitted if they are the first ANSI control
Xcharacter of the file. This is usually the correct action, but may change the
Xnumber of lines in a file; the
X.B IGNORECC
Xoption should be used if this causes trouble.
X.RE
X.SH BUGS
XThe handling of I/O errors is crude, but this is as much a restriction of UNIX
Xas the program.
X.PP
XBlocks up to 65535 can be handled, provided the device driver and tape deck
Xhandle them correctly, but longer ones are rejected.
X.PP
XThe tape device must be a raw device, on systems that support both raw and
Xblock magnetic tape devices, though this is not checked.
X.PP
XThe combination of the
X.B \-v
Xoption and commands like
X.B "COPY *(MAIN)"
Xwill cause all selected PDSs to be listed (i.e. all PDSs in this case); while
Xthis may seem odd, it is too much hassle to be worth fixing.
X.PP
XUsing the 8-bit code pages
X.RI ( phx8 ,
X.I ibm037
Xor
X.IR ibm500 )
Xwill copy even characters that cause trouble to standard UNIX utilities.  For
Xexample,
X.I awk
Xand
X.I tr
Xdo not handle
X.I NUL
Xcorrectly, at least on some systems.
X.PP
XIt is inevitable that there will be a lot of bugs in a program as complex as
Xthis, even after extensive testing and use.  This is made much worse by the
Xfact that its potential input has not got a precise definition, and there are
Xassuredly many possible ways of (mis)writing tapes that the author has not
Xthought of.  There are three consequences of this:
X.RS 5.0
X.PP
X.I readtape
Xhas a large (very large, by UNIX standards) amount of internal and external
Xconsistency checking, and will almost certainly object to some valid tapes or
Xfiles.  These should be reported as bugs, and will be fixed (or documented as
Xfeatures!) if feasible and when time permits.
X.PP
XAlmost every line of code was checked, but there is clearly no possibility of
Xchecking all relevant combinations within a human lifetime.  Most bugs are
Xlikely to be very context-dependent, and may disappear when rerun with
Xapparently irrelevant changes.
X.PP
XIt is essential to preserve the input tape or `diskized' tape file until it is
Xabsolutely certain that it has been unpicked correctly.  An improved version of
X.I readtape
Xcan only recover data from the original MVS-style input, and not from the
XUNIX-style output.
X.RE
X.SH AUTHOR
X.I readtape
Xwas developed by N.M. Maclaren of the University of Cambridge Computing
XService.
X.SH SEE ALSO
Xloadtape(1L), mvstape(1L), scantape(1L).
END_OF_FILE
if test 25110 -ne `wc -c <'readtape.1'`; then
    echo shar: \"'readtape.1'\" unpacked with wrong size!
fi
# end of 'readtape.1'
fi
echo shar: End of archive 8 \(of 9\).
cp /dev/null ark8isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 9 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0


