simh_tapes_0.1.1_a126825a/simhtapetool/src/simhtapetool.adb

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
--  Copyright ©2022 Stephen Merrony
--
--  This program is free software: you can redistribute it and/or modify
--  it under the terms of the GNU Affero General Public License as published
--  by the Free Software Foundation, either version 3 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU Affero General Public License for more details.
--
--  You should have received a copy of the GNU Affero General Public License
--  along with this program.  If not, see <https://www.gnu.org/licenses/>.

with Ada.Directories;
with Ada.Exceptions;
with Ada.Streams.Stream_IO;
with Ada.Strings.Unbounded;   use Ada.Strings.Unbounded;
with Ada.Text_IO;             use Ada.Text_IO;

with Interfaces;              use Interfaces;

with Parse_Args;              use Parse_Args;

with Simh_Tapes;              use Simh_Tapes;

procedure Simhtapetool is
   AP : Argument_Parser;
begin
   AP.Add_Option (Make_Boolean_Option (False), "help",       'h', Usage => "Display this help text");
   AP.Add_Option (Make_String_Option (""),     "create",     'c', Usage => "Create a new SimH Magnetic Tape Image file");
   AP.Add_Option (Make_String_Option (""),     "definition", 'd', Usage => "CSV Definition for a new image");
   AP.Add_Option (Make_String_Option (""),     "extract",    'e', Usage => "Extract each file from the tape image");
   AP.Add_Option (Make_String_Option (""),     "scan",       's', Usage => "Scan SimH Magnetic Tape Image file for correctness, listing files");
   --  AP.Add_Option (Make_Boolean_Option (False), "verbose",    'v', Usage => "Be more verbose");

   AP.Parse_Command_Line;
   if not AP.Parse_Success or else AP.Boolean_Value ("help") then
      AP.Usage;
   end if;

   declare
      Extract_Filename     : constant String := AP.String_Value ("extract");
      Scan_Filename        : constant String := AP.String_Value ("scan");
      Create_Filename      : constant String := AP.String_Value ("create");
      Definition_Filename  : constant String := AP.String_Value ("definition");
   begin
      if Scan_Filename /= "" then
         --  SCAN --
         Put_Line ("Scanning SimH magnetic tape image: " & Scan_Filename);
         Put_Line (Scan_Image (Scan_Filename));
      elsif Extract_Filename /= "" then
         --  EXTRACT  --
         Put_Line ("Extracting files from tape image: " & Extract_Filename);
         Dump_All_Files (Extract_Filename);
         Put_Line ("Extraction complete");
      elsif Create_Filename /= "" then
         --  CREATE  --
         if Definition_Filename = "" then
            Put_Line ("ERROR: You must specify a CSV file defining the new image with '--definition' or '-d'");
         else
            declare
               CSV_File          : File_Type;         --  Contains the description of the tape image
               CSV_Line          : Unbounded_String;
               Tape_Image_File   : Ada.Streams.Stream_IO.File_Type;  --  The new tape image we are creating
               Tape_Image_Stream : Ada.Streams.Stream_IO.Stream_Access;
               Comma_Pos         : Natural;
               Src_Filename      : Unbounded_String;  --  Each source file to be written to the tape image
               Src_Block_Size    : Positive;

               Invalid_Definition : exception;
            begin
               --  Open the CSV description for the new image
               Open (CSV_File, In_File, Definition_Filename);
               if Ada.Directories.Exists (Create_Filename) then
                  raise File_Already_Exists with "File " & Create_Filename & " already exists";
               end if;
               --  Create/Open the new tape image file
               Ada.Streams.Stream_IO.Create (File => Tape_Image_File, Name => Create_Filename);
               Tape_Image_Stream := Ada.Streams.Stream_IO.Stream (Tape_Image_File);

               while not End_Of_File (CSV_File) loop
                  CSV_Line := To_Unbounded_String (Get_Line (CSV_File));
                  Comma_Pos := Index (CSV_Line, ",");
                  if Comma_Pos = 0 then
                     raise Invalid_Definition with "No comma in CSV definition line";
                  end if;
                  Src_Filename := Unbounded_Slice (CSV_Line, 1, Comma_Pos - 1);
                  Src_Block_Size := Integer'Value (Slice (CSV_Line, Comma_Pos + 1, Length (CSV_Line)));
                  Put_Line ("INFO: Processing file: " & To_String (Src_Filename) &
                            " with block size:" & Src_Block_Size'Image);
                  declare
                     Src_File   : Ada.Streams.Stream_IO.File_Type;
                     Src_Stream : Ada.Streams.Stream_IO.Stream_Access;
                     Bytes_Read : Natural := 0;
                     Block      : Mt_Rec (1 .. Src_Block_Size) := (others => 0);
                  begin
                     Ada.Streams.Stream_IO.Open (Src_File, Ada.Streams.Stream_IO.In_File, To_String (Src_Filename));
                     Src_Stream := Ada.Streams.Stream_IO.Stream (Src_File);
                     while not Ada.Streams.Stream_IO.End_Of_File (Src_File) loop
                        for B in 1 .. Src_Block_Size loop
                           if Ada.Streams.Stream_IO.End_Of_File (Src_File) then
                              Block (B) := 0;
                           else
                              Unsigned_8'Read (Src_Stream, Block (B));
                              Bytes_Read := Bytes_Read + 1;
                           end if;
                        end loop;
                        if Bytes_Read > 0 then
                           Write_Meta_Data   (Tape_Image_Stream, Unsigned_32 (Bytes_Read)); --  Block header
                           Write_Record_Data (Tape_Image_Stream, Block (1 .. Bytes_Read));  --  Block of data
                           Write_Meta_Data   (Tape_Image_Stream, Unsigned_32 (Bytes_Read)); --  Block trailer
                           Bytes_Read := 0;
                        end if;
                     end loop;
                     Ada.Streams.Stream_IO.Close (Src_File);
                     Write_Meta_Data (Tape_Image_Stream, Mtr_Tmk);
                  exception
                     when Name_Error =>
                        Put_Line ("ERROR: Cannot open file to write to tape image: " & To_String (Src_Filename));
                        raise;
                  end;
               end loop;
               Close (CSV_File);
               Write_Meta_Data (Tape_Image_Stream, Mtr_EOM);
               Ada.Streams.Stream_IO.Close (Tape_Image_File);
               Put_Line ("INFO: Magnetic tape image created");
            exception
               when Name_Error =>
                  Put_Line ("ERROR: Cannot open/use CSV definition file");
            end;
         end if;
      else
         AP.Usage;
      end if;

   end;

   exception
      when E : others =>
         Put_Line ("ERROR: " & Ada.Exceptions.Exception_Information (E));

end Simhtapetool;