--------------------------------------------------------------------------------
--                                                                            --
--  FCOPY -- A program to copy files between Hewlett-Packard FMP, SIMH        --
--           magnetic tape, and SIMH paper tape image formats.                --
--                                                                            --
--------------------------------------------------------------------------------
--                                                                            --
--  Copyright  2004-2025 Palomar Corporation.  All rights reserved.          --
--                                                                            --
--------------------------------------------------------------------------------
--
-- Change Log:
--
--   2025-03-10 [4.51] -- FMP_IO.Parse_Name no longer raises Size_Error if the
--                        supplied filename has a record size specified for a
--                        non-type-2 file.  We now test for this result and
--                        print a warning that the record size parameter is
--                        ignored (the prior behavior was fatal, so filenames
--                        constructed by FST, etc., that contain record size
--                        parameters for all file types could not be copied).
--
--   2020-04-06 [4.50] -- Changed File_Type to Device_File to reflect updated
--                        Device_IO library.  Added per-subfile types and mixed
--                        output in FMP files.
--
--   2020-03-11 [4.40] -- Changed Copy_Records to trim trailing DC3s from
--                        source files unless --raw-data is specified.
--
--   2020-03-10 [4.40] -- Changed text verification to report the subfile number
--                        when the control character limit is exceeded.
--
--   2019-11-05 [4.32] -- Fixed burst when a subfile is empty.  Was trying to
--                        write an EOF when the file had not been opened.
--
--   2012-10-10 [4.31] -- Corrected help text for the "--file-number" option
--                        with one parameter (copies from that file through the
--                        end of the image, not only that file number).
--
--   2012-08-29 [4.30] -- Modified for Device_IO logical view option.
--
--   2012-08-10 [4.20] -- Modified for new command line options (file-number,
--                        merge, and pad).
--
--   2012-08-07 [4.10] -- Revised to use new Device_IO EOF handling and
--                        Is_Mutable calling sequence.
--
--   2010-06-11 [4.00] -- Extensive changes to device I/O routines and
--                        processing flow to allow multiple input files,
--                        concatenation, output overwriting or appending,
--                        subfile splitting, etc.
--
--   2008-02-09 [3.00] -- Rewrote program in Ada as FCOPY.  Added verify and raw
--                        options.
--
--   2008-02-07 [2.08] -- A pair of DC3s to begin a binary record is treated as
--                        an EOF.
--
--   2008-02-04 [2.07] -- Lines of NULs are stripped in text mode.  Also, fixed
--                        bug where type 2 file without record size parameter
--                        reported error and then went off into an infinite
--                        loop.
--
--   2008-02-01 [2.06] -- Fixed "attempt to read past end of file" when trailing
--                        length word is missing (off by one error).  Also, now
--                        reports file name with error.  Also, now reports as
--                        "access beyond EOF" (was "illegal record length").
--                        Fixed problem with type 2 files accessing too many
--                        records (was not parsing record size).
--
--   2008-01-27 [2.05] -- Type 4 writes text with the parity bit trimmed off.
--
--   2007-09-22 [2.04] -- Fixed bug in calculating FMGR record length with bit
--                        15 set.
--
--   2007-07-16 [2.03] -- Fixed typo in help text.
--
--   2006-06-26 [2.02] -- Fixed loop when file name has .fmgr extension but no
--                        NAMR parameter string (e.g., "x.ftn.fmgr").
--
--   2004-06-25 [1.00] -- Original program FMGR, written in Pascal.
--
--------------------------------------------------------------------------------
--
-- Invocation:
--
--   fcopy [-m | -p | -f] [OPTION]... INPUT-FILE...
--
--
-- Options:
--
--   General options:
--
--     -h or --help
--        Print the help text and exit.
--
--     -V or --version
--        Print the version number and exit.
--
--   Media options:
--
--     -m or --mag-tape
--        Write SIMH-compatible magnetic tape files.
--
--     -p or --paper-tape
--        Write SIMH-compatible paper tape files.
--
--     -f or --fmp-file
--        Write HP File Management Package files.
--
--   Input options:
--
--     -t or --file-type=<FTYPE>[,<FTYPE>...]
--        Set the type(s) of the input (sub)file(s) to <FTYPE> (1-32767).  If
--        the option is omitted, the file type is inferred from the input file
--        extension.  If a single value is specified, that type applies to all
--        subfiles of all input files.  Otherwise, each value applies in the
--        order of the subfiles encountered.  If the number of subfiles exceeds
--        the number of file types given, then the last given type applies to
--        the remaining subfiles.
--
--     -r or --record-size=<RSIZE>
--        Set the record size of type 2 input files to <RSIZE> words (1-32767).
--
--     -n or --file-number=<X>[,<Y>]
--        Copy subfiles <X> through the end of the image or subfiles <X>
--        through <Y> from the input files.  To copy a single subfile, specify
--        <Y> = <X>.
--
--   Output options:
--
--     -o or --output=<FILE>
--        Concatenate all of the input files to <FILE>.
--
--     -a or --append
--        Append instead of replacing output files.
--
--     -g or --merge
--        Merge subfiles within the input files into single files on output.
--
--     -b or --burst
--        Write subfiles within the input files to separate, sequentially named
--        output files.
--
--     -l or --leader
--        Write paper-tape leader and trailer.
--
--   Record-processing options:
--
--     -s or --swap-bytes
--        Exchange the bytes of each word in binary data records.
--
--     -w or --raw-data
--        Do not mask text data to 7 bits, trim trailing DC3s, or omit text
--        records containing only NULs.
--
--     -v or --verify
--        Verify the integrity of text and binary data records.  Relocatable and
--        absolute binary checksums are verified, and text files are verified
--        not to contain more than 10% binary data.
--
--     -d or --pad
--        Pad odd-length records to an even length.
--
--
-- Option Restrictions:
--
--   Either the --mag-tape, --paper-tape, or --fmp-file option must be
--   specified.
--
--   If --output is not specified, separate output files are created for each
--   input file.
--
--   --record-size may be specified only if one of the file types given is type
--     2.
--
--   --file-number may be specified only if one input file is specified.
--
--   --leader may be specified only with --paper-tape.
--
--   --burst may not be specified with --merge.
--
--
-- Option Notes:
--
--   --swap-bytes is ignored unless the input files contain binary data.
--
--   --raw-data is ignored unless the input files contain text data.
--
--------------------------------------------------------------------------------
--
-- This program copies and converts files between SIMH magnetic tape, SIMH paper
-- tape, and FMP file image formats.  Concatenating, merging and splitting on
-- subfile (i.e., embedded logical end-of-file) boundaries are supported, as
-- well as one-for-one conversions.  Per-word byte swapping and text character
-- parity-bit masking is provided as an aid to interchange between SIMH and HP
-- files.
--
-- Bidirectional interchange of all content types between magnetic tape and FMP
-- image formats is supported.  Interchange with paper tape format is
-- bidirectional for source (plain text), relocatable binary, absolute binary,
-- and byte-stream data.  As the paper tape image format has no inherent record
-- markers, copying general variable-length data (e.g., FMP type 3) to paper
-- tape loses record divisions and is rendered as a byte stream.
--
-- The desired output file media conversions are specified by the --mag-tape,
-- --paper-tape, or --fmp-file options.  The medium of each input file is
-- inferred by the file extension.  Magnetic tape files are assumed for ".tape"
-- and ".tap" extensions.  FMP files are assumed for ".fmp" and ".fmgr"
-- extensions.  All other extensions imply paper tape files.
--
-- The type of each input file is either implied by the file extension or
-- specified explicitly by the --file-type option and, if the file type is 2, by
-- the --record-size option.  A sequence of explicit file types may be
-- specified, allowing each of the input files to be of different types.
--
-- For magnetic tape images, type 3 (generic variable record length data) is
-- assumed.  For FMP images, the FMP NAMR substring, if present in the file
-- name, is used.  For paper tape, the following extensions are recognized for
-- input (I) and generated for output (O):
--
--                   File   Rec
--   I/O  Extension  Type   Size   Description
--   ===  =========  =====  =====  ==================
--   I/O    .disc      1     128   SIMH Disc Image
--   I      .tar       2    5120   TAR Binary
--   I      .fst       2    5120   FST Binary
--   I/O    .data      3      -    Generic Binary
--   I/O    .src       4      -    Source
--   I      .txt       4      -    Source
--   I/O    .rbin      5      -    Relocatable Binary
--   I/O    .abin      7      -    Absolute Binary
--
-- If an input file type cannot be determined by the extension, and the type
-- has not been specified explicitly, an error occurs.
--
-- In the absence of the --output and --burst options, each input file is
-- converted to the specified medium and written to an output file with the same
-- name and a type-specific extension.  If --output is specified, the converted
-- input files are concatenated into a single output file, overwriting any prior
-- output file; if --append is added, the concatenated files are appended to
-- the specified file.
--
-- For concatenation to an FMP file, the output file type must be specified,
-- either by the FMP NAMR substring or by the --file-type option; otherwise, an
-- error occurs.  Concatenating dissimilar input files should be made only to a
-- type 3 FMP file, although any variable-record-length type will be accepted.
-- Mixing fixed- and variable-record-length data in a single FMP file is not
-- allowed.  Concatenation of input files with dissimilar types is unrestricted
-- with magnetic tape and paper tape output images.
--
-- If an input file contains subfiles, a subset of the subfiles may be copied
-- with the --file-number option.  If a single file number is given, the
-- subfiles beginning with that number through the end of the input file are
-- copied.  If two numbers are given, the subfiles from the first number through
-- the last number are copied.  In the absence of the --file-number option, all
-- subfiles are copied.
--
-- If the subfiles of a single input file are dissimilar, the --file-type
-- option should be used to identify the type of data, as it cannot be inferred
-- by the input file extension.  Each file type in the comma-separated list of
-- file types applies to an input subfile in the order specified.  So, for
-- example, a list of three file types could apply to three input files
-- containing one subfile each, or to two input files containing two subfiles
-- and one subfile, respectively, or to a single input file containing three
-- subfiles.  If the number of subfiles across all of the specified input files
-- exceeds the number of file types given, the last (or only) file type is
-- applied to all remaining subfiles.
--
-- Specifying the --burst option will split an input file containing subfiles
-- into multiple output files.  The file names are derived either from the input
-- name or from the name specified with --output, plus an appended sequence
-- number.  If --burst is not specified, then --merge may be specified to
-- discard embedded subfile marks and inhibit subfile marks between concatenated
-- files.  If neither --burst nor --merge is specified, then embedded subfile
-- marks are retained, and subfile marks are added to the output file between
-- concatenated input files.
--
-- Note that the FMP specification does not define subfile marks for file types
-- 1 and 2; for these types, FCOPY writes a record filled repeatedly with the
-- string "<EOF>" as a subfile mark.
--
-- In summary, the actions for the various combinations of the --output,
-- --merge, and --burst options are:
--
--   O  M  B   Action
--   =  =  =   =================================================================
--   0  0  0   Copy all subfiles in each input file to a corresponding output
--             file name derived from the input name, retaining any embedded
--             subfile marks.
--
--   0  0  1   Copy each subfile in each input file to a corresponding output
--             file name derived from the input name and the subfile number.
--
--   0  1  0   Copy all subfiles in each input file to a corresponding output
--             file name derived from the input name, removing any embedded
--             subfile marks.
--
--   0  1  1   Not allowed.
--
--   1  0  0   Copy all subfiles in each input file to a single specified output
--             file name, retaining any embedded subfile marks.
--
--   1  0  1   Copy each subfile in each input file to an output file name
--             derived from the specified name and the subfile number.
--
--   1  1  0   Copy all subfiles in each input file to a single specified output
--             file name, retaining any embedded subfile marks.
--
--   1  1  1   Not allowed.
--
-- For paper-tape output files, leader and trailer (a block of NULs) may be
-- requested with the --leader option.  This option does not affect the output
-- of NULs as subfile separators within a paper tape image file.  When --leader
-- is not specified for source files, the result is a plain-text file suitable
-- for editing or viewing on a host system.
--
-- Use of the --leader option produces an image file that is an exact copy of a
-- physical paper tape as punched by an RTE system.  This option is normally
-- not needed with SIMH versions 3.2-1 and higher if the AUTOTRAILER option is
-- set for the PTR device.  For earlier versions, trailer must be present in
-- the paper tape image file for the end of the tape to be sensed properly.
--
-- For binary files of any format, specifying --swap-bytes will exchange the
-- bytes of each 16-bit word.  This is useful when converting between systems
-- that expect little-endian vs. big-endian formats.  Notably, this means that a
-- converted FMP type 1 file containing an HP disc image may be used directly
-- as a SIMH disc volume on Intel host platforms.
--
-- For text files, all characters are normally masked to the lower seven bits
-- to remove ASCII parity, trailing DC3s are trimmed, and any records that
-- consist solely of NULs are dropped.  Specifying --raw-data will prevent this
-- conversion.
--
-- If the --verify option is specified, checksums will be calculated and
-- verified for relocatable and absolute binary files, and text files will be
-- examined for an excess of binary data.  If the checksum does not match, or if
-- the binary data threshold value (typically 5%) is exceeded, the file is
-- declared bad.  Bad output files are renamed to append ".bad" to prevent
-- inadvertent use.
--
-- While all media image formats support variable-length records containing an
-- odd number of bytes, some client programs may require records having an
-- integral number of 16-bit words.  For example, RTE and other operating
-- systems predating the C.83 (2340) software revision do not support FMP
-- records containing an odd number of bytes.  To facilitate conversions for
-- these systems, the --pad option will pad odd-byte-length records with a zero
-- byte (for binary) or a blank character (for text).  Failure to use --pad when
-- copying host text files to FMP or magnetic tape images typically will cause
-- errors when the files are accessed by the RTE editors, compilers, and
-- assemblers.
--
--------------------------------------------------------------------------------
--
-- Examples:
--
--    1. fcopy --paper-tape 92069-18176.ty4.fmp
--
--       Copies ASCII source records to "92069-18176.src".  Each source record
--       ends with a CR and LF.
--
--    2. fcopy --paper-tape --leader 92069-18176.ty4.fmp
--
--       Copies ASCII source records to "92069-18176.src".  Each source record
--       ends with a CR and LF.  The file begins and ends with 100 NULs.
--
--    3. fcopy --mag-tape --verify --output=mt.tape --append 24999-16050.ty5.fmp
--
--       Appends relocatable binary records to the existing file "mt.tape".  The
--       checksum of each record is verified as it is copied.  An end-of-file
--       marker is appended after the last relocatable record.
--
--    4. fcopy --paper-tape 24016-80001.ty3.fmp
--
--       Copies binary records to "24016-80001.data."  Binary records are
--       concatenated to form a byte stream; there are no record separators.
--
--    5. fcopy --paper-tape --file-type=4 24016-80001.ty3.fmp
--
--       Copies source records to "24016-80001.src."  Each source record ends
--       with a CR and LF.
--
--    6. fcopy --fmp-file --swap-bytes --output=7925.data boot-disk.ty1.fmp
--
--       Copies 128-word binary data records to "7925.data" with no record
--       separators.  The bytes forming each data word are swapped, transforming
--       the big-endian FMP file into the little-endian FMP file.  The resulting
--       file may be ATTACHed as a SIMH-format disc file.
--
--    7. fcopy --mag-tape --output=full.tape --merge pt-1.src pt-2.src pt-3.src
--
--       Copies "pt-1.src", "pt-2.src", and "pt-3.src" to "full.tape" without
--       any intervening subfile markers, resulting in a single file on the
--       tape.  An end-of-file marker is appended after the last file.
--
--    8. fcopy --mag-tape printer.rbin plotter.rbin
--
--       Copies "printer.rbin" to "printer.tape" and "plotter.rbin" to
--       "plotter.tape".  An end-of-file marker is appended to each file.
--
--    9. fcopy --fmp-file --merge --output=gen.ty3.fmp gen-files.tape
--
--       Copies all subfiles in "gen-files.tape" to the file "gen.ty3.fmp"
--       without any intervening subfile markers.
--
--   10. fcopy --fmp-file --output=gen.ty3.fmp gen-files.tape
--
--       Copies all subfiles in "gen-files.tape" to the file "gen.ty3.fmp"
--       with each pair of subfiles separated by a subfile marker.
--
--   11. fcopy --fmp-file --burst --output=gen.ty3.fmp gen-files.tape
--
--       Copies each subfile in "gen-files.tape" to a separate output file.
--       The first subfile is copied to "gen_001.ty3.fmp", the second to
--       "gen_002.ty3.fmp", etc.
--
--   12. fcopy --fmp-file --burst gen-files.tape
--
--       Copies each subfile in "gen-files.tape" to a separate output file.
--       The first subfile is copied to "gen-files_001.ty3.fmp", the second to
--       "gen-files_002.ty3.fmp", etc.
--
--   13. fcopy --fmp-file --burst --output=generator.ty3.fmp gen-files.tape
--
--       Copies each subfile in "gen-files.tape" to a separate output file.
--       The first subfile is copied to "generator_001.ty3.fmp", the second to
--       "generator_002.ty3.fmp", etc.
--
--   14. fcopy --fmp-file --burst --append gen-files.tape
--
--       Appends each subfile in "gen-files.tape" to a separate output file.
--       The first subfile is appended to "gen-files_001.ty3.fmp", the second
--       to "gen-files_002.ty3.fmp", etc.
--
--   15. fcopy --paper-tape --file-type=5 --file-number=3,5 assy.tape
--
--       Copies the third through fifth relocatable binary subfiles from
--       "assy.tape" to "assy.rbin".  Subfile markers are inserted between the
--       files in the output image.  If the input file has fewer than five
--       subfiles, the missing subfiles are omitted.  If the input file has
--       fewer than three subfiles, the output file is empty.
--
--   16. fcopy --fmp-file --file-number=3 assy.tape
--
--       Copies all subfiles starting at the third from "assy.tape" to
--       "assy.ty3.fmp".  If the input file has fewer than three subfiles, the
--       output file is empty.
--
--   17. fcopy --mag-tape --verify --file-type=5,4,7 pt.bin
--
--       The first three input subfiles contained in the input file are copied
--       to "pt.tape" and verified as relocatable binary, text, and absolute
--       binary records, respectively.  If any additional subfiles exist in
--       "pt.bin", they are copied and verified as absolute binary records.  An
--       end-of-file marker is appended after the last absolute record.
--
--   18. fcopy --fmp-file --file-type=4,5 binary1.rbin binary2.rbin
--
--       The two input subfiles contained in "binary1.rbin" are copied to
--       "binary1.ty3.fmp" as text and relocatable binary data, respectively.
--       All subfiles in "binary2.rbin" are copied to "binary2.ty5.fmp".  End-
--       of-file markers are appended to each file.  (The first FMP file would
--       normally have been named with "ty5", based on the ".rbin" extension of
--       its corresponding input file.  However, the --file-type option
--       overrides this implicit assumption, and because the file contained
--       mixed types, it is named with "ty3" to indicate generic binary data.)
--
--------------------------------------------------------------------------------
