|
|
DataMuseum.dkPresents historical artifacts from the history of: Rational R1000/400 Tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Rational R1000/400 Tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: B T
Length: 57427 (0xe053)
Types: TextFile
Names: »B«
└─⟦85b835f43⟧ Bits:30000549 8mm tape, Rational 1000, Xlib rev 6.00
└─⟦0c20f784e⟧ »DATA«
└─⟦1abbe589f⟧
└─⟦591c5b094⟧
└─⟦this⟧
with Archive;
with Byte_Defs;
with Common;
with Compilation;
with Debug_Tools;
with Directory_Tools;
with Editor;
with File_Transfer;
with Ftp_Defs;
with Ftp_Profile;
with Text_Io;
with Time_Utilities;
with Library;
with Log;
with Profile;
with String_Map_Generic;
with String_Utilities;
with System_Utilities;
with Table_Sort_Generic;
with Transport;
with Transport_Defs;
with Transport_Name;
with Unchecked_Deallocation;
procedure Ship_To_Unix
(Password : String; -- User's password
Force_Update : Boolean := False; -- Update regardless
Effort_Only : Boolean := False; -- Just checking
Unix_Machine : String := "<SHIP_TO_MACHINE>";
Username : String := "<SHIP_TO_USERNAME>") is
------------------------------------------------------------------------------
-- Copyright 1990 - 1991 by Rational, Santa Clara, California.
--
-- All Rights Reserved.
--
-- Permission to use, copy, modify, and distribute this software and its
-- documentation for any purpose and without fee is hereby granted,
-- provided that the above copyright notice(s) appear in all copies and that
-- both that copyright notice(s) and this permission notice appear in
-- supporting documentation, and that the names of Rational not be used in
-- advertising or publicity pertaining to distribution of the software without
-- specific, written prior permission.
--
-- Rational disclaims all warranties with regard to this software, including
-- all implied warranties of merchantability and fitness, in no event shall
-- Rational be liable for any special, indirect or consequential damages or
-- any damages whatsoever resulting from loss of use, data or profits, whether
-- in an action of contract, negligence or other tortious action, arising out
-- of or in connection with the use or performance of this software.
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- Imported Renames
------------------------------------------------------------------------------
package Dt renames Directory_Tools;
function "=" (A, B : Dt.Object.Class_Enumeration) return Boolean
renames Dt.Object."=";
function "=" (A, B : Ftp_Defs.Status_Code) return Boolean
renames Ftp_Defs."=";
function "=" (A, B : Transport_Defs.Status_Code) return Boolean
renames Transport_Defs."=";
------------------------------------------------------------------------------
-- Exceptions
------------------------------------------------------------------------------
I_Am_Confused : exception;
----I give up. Some assumption of the code has changed and the code wasn't
-- changed to reflect it.
Quit : exception;
----Raised when we want to give up on something.
------------------------------------------------------------------------------
-- Types
------------------------------------------------------------------------------
type String_Pointer is access String;
pragma Enable_Deallocation (String_Pointer);
procedure Free_String_Pointer is
new Unchecked_Deallocation (String,
String_Pointer);
type Natural_Array is array (Natural range <>) of Natural;
type Database_Entry_Rec is
record
----These are used only during processing.
-- They are not saved in the database.
Full_R1000_Name : String_Pointer := null;
Update : Boolean := False;
Bad_Name : Boolean := False;
Updated : Boolean := False;
----These are saved in the database.
R1000_Name : String_Pointer; -- Object minus 'V(nn)
Unix_Path : String_Pointer; -- Path on unix
Unix_Name : String_Pointer; -- Name of file on Unix
Version : String_Pointer; -- 'V(nn) or 'V(library)
Directory : Boolean; -- True for libraries/worlds
Ignore : Boolean := False; -- True for R1000-only files
New_One : Boolean := False; -- True for new objects
end record;
type Database_Entry is access Database_Entry_Rec;
pragma Enable_Deallocation (Database_Entry);
procedure Free_Database_Entry is
new Unchecked_Deallocation (Database_Entry_Rec,
Database_Entry);
type Database_Entry_Array is array (Natural range <>) of Database_Entry;
function Check_Object (Obj : Dt.Object.Handle;
Root : String) return Database_Entry;
------------------------------------------------------------------------------
-- Global Data
------------------------------------------------------------------------------
package Obj_Db is
new String_Map_Generic (Range_Type => Database_Entry,
Size => 401,
Ignore_Case => True);
Database : Obj_Db.Map;
Connection : File_Transfer.Connect_Id;
Host_Machine_Name : String_Pointer;
Host_User_Name : String_Pointer;
Files_Processed : Integer := 0;
Files_Transferred : Integer := 0;
Files_Deferred : Integer := 0;
Temp_File_Used : Boolean := False;
Temp_File_Name : constant String :=
"!Machine.Temporary.Ship_To_Unix_" &
Time_Utilities.Image (Time_Utilities.Get_Time,
Date_Style => Time_Utilities.Ada,
Contents => Time_Utilities.Date_Only) & "_" &
Time_Utilities.Image (Time_Utilities.Get_Time,
Time_Style => Time_Utilities.Ada,
Contents => Time_Utilities.Time_Only);
--\f
function X_Env_Get_Environment_Variable
(Var : String) return String_Pointer is
------------------------------------------------------------------------------
-- Var - Specifies the name of the variable
--
-- Returns None_X_String_Pointer if the variable does not exist or if
-- this environment/operating-system/run-time does not support any such
-- concept as environment variables.
--
-- Don't forget to free the string result with Free_X_String_Pointer;
------------------------------------------------------------------------------
--/ if R1000 then
Env : Text_Io.File_Type;
Env_Name : constant String :=
System_Utilities.Home_Library (User => System_Utilities.User_Name)
& "."
& System_Utilities.Session_Name & "_Environment."
& Var;
Env_Ptr : String_Pointer;
function Read_Env return String is
----Read one line from Env; regardless of the length of the
-- line.
Line : String (1 .. 1024);
Length : Natural;
begin
Text_Io.Get_Line (Env, Line, Length);
if Length = Line'Length and then
Text_Io.">" (Text_Io.Col (Env), 1) then
return Line & Read_Env;
else
return Line (1 .. Length);
end if;
end Read_Env;
begin
----Open the Environment Variable file. Read the first line. Close the file.
-- Return that line as the value.
begin
Text_Io.Open (Env, Text_Io.In_File, Env_Name);
Env_Ptr := new String'(Read_Env);
Text_Io.Close (Env);
return Env_Ptr;
exception
when others =>
begin -- Just in case.
Free_String_Pointer (Env_Ptr);
Text_Io.Close (Env);
exception
when others =>
null;
end;
return null;
end;
--/ else
--//
--// begin
--//
--// begin
--// return new X_String'(To_X_String (Ui_Environment.Getenv (Var)));
--// exception
--// when Constraint_Error =>
--// ----Getenv raises this when the variable does not exist.
--// return None_X_String_Pointer;
--// end;
--//
--/ end if;
end X_Env_Get_Environment_Variable;
--\f
function Hs (Str : String) return String_Pointer is
------------------------------------------------------------------------------
-- Str - Specifies the string value
--
-- Called to allocate a string on the heap.
------------------------------------------------------------------------------
Tmp : String (1 .. Str'Length) := Str;
begin
return new String'(Tmp);
end Hs;
--\f
procedure Read_Database (Name : String) is
------------------------------------------------------------------------------
-- Name - Speicifies the name of the database to read
--
-- Called to read in an Ascii database file.
------------------------------------------------------------------------------
Line : String (1 .. 1024);
Len : Natural;
File : Text_Io.File_Type;
Ent : Database_Entry;
Error : Boolean := False;
begin
----Open the file.
begin
Text_Io.Open (File, Text_Io.In_File, Name);
exception
when others =>
Log.Put_Line ("Error opening " & Name & "; exception " &
Debug_Tools.Get_Exception_Name (True, True),
Profile.Exception_Msg,
Profile.Verbose);
raise Quit;
end;
----Read the file.
while not Text_Io.End_Of_File (File) loop
Ent := new Database_Entry_Rec;
Text_Io.Get_Line (File, Line, Len);
if Len = 0 or else Line (1) = '=' then
raise I_Am_Confused;
end if;
Ent.R1000_Name := Hs (Line (1 .. Len));
Text_Io.Get_Line (File, Line, Len);
if Len > 0 and then Line (1) = '=' then
raise I_Am_Confused;
end if;
Ent.Unix_Path := Hs (Line (1 .. Len));
Text_Io.Get_Line (File, Line, Len);
if Len = 0 or else Line (1) = '=' then
raise I_Am_Confused;
end if;
Ent.Unix_Name := Hs (Line (1 .. Len));
Text_Io.Get_Line (File, Line, Len);
if Len > 0 and then Line (1) = '=' then
raise I_Am_Confused;
end if;
Ent.Version := Hs (Line (1 .. Len));
Text_Io.Get_Line (File, Line, Len);
if Len = 0 or else Line (1) = '=' then
raise I_Am_Confused;
end if;
if String_Utilities.Equal ("directory:",
Line (1 .. 10),
Ignore_Case => True) then
Ent.Directory := Boolean'Value (Line (11 .. Len));
else
raise I_Am_Confused;
end if;
Text_Io.Get_Line (File, Line, Len);
if Len = 0 or else Line (1) = '=' then
raise I_Am_Confused;
end if;
if String_Utilities.Equal ("ignore:",
Line (1 .. 7),
Ignore_Case => True) then
Ent.Ignore := Boolean'Value (Line (8 .. Len));
else
raise I_Am_Confused;
end if;
Text_Io.Get_Line (File, Line, Len);
if Len = 0 or else Line (1) = '=' then
raise I_Am_Confused;
end if;
if String_Utilities.Equal ("new:",
Line (1 .. 4),
Ignore_Case => True) then
Ent.New_One := Boolean'Value (Line (5 .. Len));
else
raise I_Am_Confused;
end if;
Text_Io.Get_Line (File, Line, Len);
if Len = 0 or else Line (1) /= '=' then
raise I_Am_Confused;
end if;
if not Ent.Ignore and then
Ent.Unix_Name'Length > 14 and then
(not Ent.Directory or else
String_Utilities.Locate ("/", Ent.Unix_Name.all) = 0) then
Ent.Bad_Name := True;
Error := True;
Log.Put_Line ("Unix name too long," &
Natural'Image (Ent.Unix_Name'Length) &
" characters: " & Ent.Unix_Name.all,
Profile.Error_Msg,
Profile.Verbose);
end if;
Obj_Db.Define (Database, Ent.R1000_Name.all, Ent,
Trap_Multiples => True);
end loop;
----Close the file.
Text_Io.Close (File);
if Error then
raise Quit;
end if;
exception
when Quit =>
raise;
when I_Am_Confused =>
Log.Put_Line ("Database " & Name & " has bad format on line " &
Integer'Image (Integer (Text_Io.Line (File))),
Profile.Error_Msg,
Profile.Verbose);
Text_Io.Close (File);
raise I_Am_Confused;
when others =>
Log.Put_Line ("Database " & Name & " had I/O error: " &
Debug_Tools.Get_Exception_Name (True, True),
Profile.Exception_Msg,
Profile.Verbose);
Log.Put_Line ("Error reading/on line " &
Integer'Image (Integer (Text_Io.Line (File))),
Profile.Error_Msg,
Profile.Verbose);
begin
Text_Io.Close (File);
exception
when others =>
null;
end;
raise I_Am_Confused;
end Read_Database;
--\f
procedure Write_Database (Name : String) is
------------------------------------------------------------------------------
-- Name - Speicifies the name of the database to write
--
-- Called to write out an Ascii database file.
------------------------------------------------------------------------------
File : Text_Io.File_Type;
Ent : Database_Entry;
Iter : Obj_Db.Iterator;
Ary : Database_Entry_Array (1 .. Natural
(Obj_Db.Cardinality (Database)));
function Lss (Left, Right : Database_Entry) return Boolean is
begin
return Left.R1000_Name.all < Right.R1000_Name.all;
end Lss;
procedure Sort is new Table_Sort_Generic (Database_Entry,
Natural,
Database_Entry_Array,
Lss);
begin
----Open the file.
begin
Text_Io.Open (File, Text_Io.Out_File, Name);
exception
when others =>
begin
Text_Io.Create (File, Text_Io.Out_File, Name);
exception
when others =>
Log.Put_Line ("Error opening " & Name & "; exception " &
Debug_Tools.Get_Exception_Name
(True, True),
Profile.Exception_Msg,
Profile.Verbose);
raise Quit;
end;
end;
----Write the file.
Obj_Db.Init (Iter, Database);
for I in Ary'Range loop
Ent := Obj_Db.Eval (Database, Obj_Db.Value (Iter));
Ary (I) := Ent;
if not Ent.Ignore and then
Ent.Unix_Name'Length > 14 and then
(not Ent.Directory or else
String_Utilities.Locate ("/", Ent.Unix_Name.all) = 0) then
Ent.Bad_Name := True;
Ent.Update := False;
Ent.Version := new String (1 .. 0);
Log.Put_Line ("Unix name too long," &
Natural'Image (Ent.Unix_Name'Length) &
" characters: " & Ent.Unix_Name.all,
Profile.Error_Msg,
Profile.Verbose);
end if;
Obj_Db.Next (Iter);
end loop;
Sort (Ary);
for I in Ary'Range loop
Ent := Ary (I);
Text_Io.Put_Line (File, Ent.R1000_Name.all);
Text_Io.Put_Line (File, Ent.Unix_Path.all);
Text_Io.Put_Line (File, Ent.Unix_Name.all);
if Ent.Update or else
(Ent.New_One and then Effort_Only) then
Text_Io.New_Line (File);
else
Text_Io.Put_Line (File, Ent.Version.all);
end if;
Text_Io.Put_Line (File, "Directory: " &
Boolean'Image (Ent.Directory));
Text_Io.Put_Line (File, "Ignore: " & Boolean'Image (Ent.Ignore));
Text_Io.Put_Line (File, "New: " & Boolean'Image (Ent.New_One));
if Ent.New_One then
Log.Put_Line ("New: " & Ent.R1000_Name.all,
Profile.Note_Msg,
Profile.Verbose);
end if;
Text_Io.Put_Line (File, "====");
end loop;
----Close the file.
Text_Io.Close (File);
exception
when others =>
Log.Put_Line ("Database " & Name & " had I/O error: " &
Debug_Tools.Get_Exception_Name (True, True),
Profile.Exception_Msg,
Profile.Verbose);
begin
Text_Io.Close (File);
exception
when others =>
null;
end;
raise Quit;
end Write_Database;
--\f
function Remove_Root (Full : String;
Root : String) return String is
------------------------------------------------------------------------------
-- Full - Specifies the Full_Name of an object
-- Root - Specifies a string that is supposed to match the initial
-- portion of Full
--
-- Removes the Root prefix from the Full name. The initial part of Full must
-- match or we abort.
------------------------------------------------------------------------------
begin
if Full (Full'First .. Full'First + Root'Length - 1) /= Root then
Log.Put_Line ("R1000_Root is " & Root,
Profile.Error_Msg,
Profile.Verbose);
Log.Put_Line ("Object is not contained in R1000_Root: " & Full,
Profile.Error_Msg,
Profile.Verbose);
raise Quit;
end if;
----Drop the '.' that follows the Root.
return Full (Full'First + Root'Length + 1 .. Full'Last);
end Remove_Root;
--\f
function Segment_Name (Name : String) return Natural_Array is
------------------------------------------------------------------------------
-- Name - Specifies the name to segment.
--
-- Returns an array of indexes. Each index indicates the place within the
-- string of a '.' or a ''' or Name'Last+1.
------------------------------------------------------------------------------
Result : Natural_Array (1 .. 2 + Name'Length / 2);
Resulti : Natural := 1;
begin
Result (Resulti) := Name'First - 1;
for I in Name'Range loop
if Name (I) = '.' or else
Name (I) = ''' then
Resulti := Resulti + 1;
Result (Resulti) := I;
end if;
end loop;
Resulti := Resulti + 1;
Result (Resulti) := Name'Last + 1;
return Result (1 .. Resulti);
end Segment_Name;
--\f
function Check_Database (Obj : Dt.Object.Handle;
Root : String) return Database_Entry is
------------------------------------------------------------------------------
-- Obj - Specifies the R1000 object to be transferred to Unix
-- Root - Specifies the root for objects, this portion of the Obj's name
-- must match and it will be removed before we process it
--
-- We take an object handle, we look it up in the database, and we return a
-- pointer to it if we want it transferred and we return null if we do not
-- want it transferred. Makes a new entry in the database if necessary.
------------------------------------------------------------------------------
Uni : constant String :=
Remove_Root (Dt.Naming.Unique_Full_Name (Obj),
Root);
Unique : String (1 .. Uni'Length) := Uni;
Offsets : constant Natural_Array := Segment_Name (Unique);
Offset_Tick : Natural := Offsets'Last;
Class : Dt.Object.Class_Enumeration := Dt.Object.Class (Obj);
Entrec : Database_Entry_Rec;
Ent : Database_Entry;
Subobj : Dt.Object.Handle;
Success : Boolean;
Remote_Name : String (1 .. 1024);
Remote_Namei : Natural := 0;
Len : Natural;
Cap : Boolean;
begin
----Get the name of the object. If it is the same name as the Root then
-- we ignore this object.
if Dt.Naming.Full_Name (Obj) = Root then
return null;
end if;
----For all of the name segments that correspond to directories, make sure that
-- those directories have been processed.
for I in Offsets'First + 1 .. Offsets'Last - 1 loop
if Unique (Offsets (I)) /= '.' then
Offset_Tick := I;
exit;
end if;
Subobj := Dt.Naming.Resolution
(Root & '.' &
Unique (Unique'First .. Offsets (I) - 1));
Ent := Check_Object (Subobj, Root);
if Dt.Object.Class (Subobj) = Dt.Object.Library_Class then
if Ent.Unix_Path'Length > 0 then
Remote_Namei :=
Ent.Unix_Path'Length + Ent.Unix_Name'Length + 1;
Remote_Name (1 .. Remote_Namei) :=
Ent.Unix_Path.all & Ent.Unix_Name.all & '/';
else
Remote_Namei := Ent.Unix_Name'Length + 1;
Remote_Name (1 .. Remote_Namei) := Ent.Unix_Name.all & '/';
end if;
end if;
end loop;
----Locate the 'V version string in the Unique name.
for I in Offsets'First + 1 .. Offsets'Last - 1 loop
if Unique (Offsets (I)) = ''' and then
Unique (Offsets (I) + 1) = 'V' then
Entrec.Version := Hs (Unique
(Offsets (I) .. Offsets (I + 1) - 1));
Entrec.R1000_Name :=
Hs (Unique (Unique'First .. Offsets (I) - 1));
exit;
end if;
end loop;
if Entrec.Version = null then
if Class /= Dt.Object.Library_Class then
Log.Put_Line ("No 'V in Unique name???",
Profile.Error_Msg,
Profile.Verbose);
raise Quit;
else
Entrec.R1000_Name := Hs (Unique);
Entrec.Version := Hs ("'V(library)");
end if;
end if;
----Now look up the R1000_Name in the database. If the name was there already
-- then update the file if that is necessary.
Obj_Db.Find (Database, Entrec.R1000_Name.all, Ent, Success);
if Success then
if Ent.Version.all /= Entrec.Version.all then
Ent.Update := not Ent.Updated and not Ent.Bad_Name;
Free_String_Pointer (Entrec.R1000_Name);
Free_String_Pointer (Ent.Version);
Ent.Version := Entrec.Version;
Ent.Full_R1000_Name := Hs (Unique);
else
Ent.Update := Force_Update and not Ent.Updated;
Free_String_Pointer (Entrec.Version);
Free_String_Pointer (Entrec.R1000_Name);
Ent.Full_R1000_Name := Hs (Unique);
end if;
if Ent.Ignore then
Ent.Update := False;
end if;
if not Ent.Updated and not Ent.Update then
Files_Deferred := Files_Deferred + 1;
end if;
Files_Processed := Files_Processed + 1;
return Ent;
end if;
----Create a Unix name for this object.
Cap := not Lower_Case_Names;
for I in Offsets (Offset_Tick - 1) + 1 .. Offsets (Offset_Tick) - 1 loop
if Unique (I) = '_' then
Cap := not Lower_Case_Names;
elsif Cap then
if Unique (I) in 'a' .. 'z' then
Unique (I) := Character'Val (Character'Pos (Unique (I)) -
Integer (Character'Pos ('a') -
Character'Pos ('A')));
end if;
Cap := False;
else
if Unique (I) in 'A' .. 'Z' then
Unique (I) := Character'Val (Character'Pos (Unique (I)) -
Integer (Character'Pos ('A') -
Character'Pos ('a')));
end if;
end if;
end loop;
Entrec.Unix_Path := Hs (Remote_Name (1 .. Remote_Namei));
Remote_Namei := Offsets (Offset_Tick) - Offsets (Offset_Tick - 1) - 1;
Remote_Name (1 .. Remote_Namei) :=
Unique (Offsets (Offset_Tick - 1) + 1 .. Offsets (Offset_Tick) - 1);
----We have a new object. We need to create a Unix name for this object
-- and make a new database entry.
Entrec.Directory := False;
case Class is
when Dt.Object.Library_Class =>
Entrec.Unix_Name := Hs (String_Utilities.Lower_Case
(Remote_Name (1 .. Remote_Namei)));
Entrec.Directory := True;
when Dt.Object.Ada_Class =>
case Dt.Ada_Object.Unit_Kind'(Dt.Ada_Object.Kind (Obj)) is
when Dt.Ada_Object.Function_Spec |
Dt.Ada_Object.Function_Instantiation =>
Entrec.Unix_Name :=
Hs (Remote_Name (1 .. Remote_Namei) & ".FNS");
when Dt.Ada_Object.Function_Body =>
Entrec.Unix_Name :=
Hs (Remote_Name (1 .. Remote_Namei) & ".FNB");
when Dt.Ada_Object.Procedure_Spec |
Dt.Ada_Object.Procedure_Instantiation =>
Entrec.Unix_Name :=
Hs (Remote_Name (1 .. Remote_Namei) & ".PRS");
when Dt.Ada_Object.Procedure_Body =>
Entrec.Unix_Name :=
Hs (Remote_Name (1 .. Remote_Namei) & ".PRB");
when Dt.Ada_Object.Package_Spec |
Dt.Ada_Object.Package_Instantiation =>
Entrec.Unix_Name :=
Hs (Remote_Name (1 .. Remote_Namei) & ".PKS");
when Dt.Ada_Object.Package_Body =>
Entrec.Unix_Name :=
Hs (Remote_Name (1 .. Remote_Namei) & ".PKB");
when Dt.Ada_Object.Generic_Function =>
Entrec.Unix_Name :=
Hs (Remote_Name (1 .. Remote_Namei) & ".FNG");
when Dt.Ada_Object.Generic_Procedure =>
Entrec.Unix_Name :=
Hs (Remote_Name (1 .. Remote_Namei) & ".PRG");
when Dt.Ada_Object.Generic_Package =>
Entrec.Unix_Name :=
Hs (Remote_Name (1 .. Remote_Namei) & ".PKG");
when others =>
Log.Put_Line ("R1000 Ada class " &
Dt.Ada_Object.Unit_Kind'Image
(Dt.Ada_Object.Unit_Kind'
(Dt.Ada_Object.Kind (Obj))) &
" is not supported.",
Profile.Warning_Msg,
Profile.Verbose);
Log.Put_Line ("Not transferred: " &
Dt.Naming.Unique_Full_Name (Obj),
Profile.Warning_Msg,
Profile.Verbose);
raise Quit;
end case;
when Dt.Object.File_Class =>
Entrec.Unix_Name := Hs (Remote_Name (1 .. Remote_Namei));
when others =>
Log.Put_Line ("R1000 object class " &
Dt.Object.Image (Class) & " is not supported.",
Profile.Warning_Msg,
Profile.Verbose);
Log.Put_Line ("Not transferred: " &
Dt.Naming.Unique_Full_Name (Obj),
Profile.Warning_Msg,
Profile.Verbose);
raise Quit;
end case;
----Create the new database entry.
Ent := new Database_Entry_Rec'(Entrec);
Ent.Update := False; -- Do *not* send new entries.
Ent.New_One := True; -- Let the user check the DB entry 1st.
Ent.Full_R1000_Name := Hs (Unique);
if Effort_Only or not Ent.Update then
Ent.Version := new String (1 .. 0);
end if;
if Ent.Unix_Name'Length > 14 then
Ent.Bad_Name := True;
Ent.Update := False;
Log.Put_Line ("Unix name too long," &
Natural'Image (Ent.Unix_Name'Length) &
" characters: " & Ent.Unix_Name.all,
Profile.Error_Msg,
Profile.Verbose);
end if;
Obj_Db.Define (Database, Entrec.R1000_Name.all, Ent);
Files_Processed := Files_Processed + 1;
Files_Deferred := Files_Deferred + 1;
return Ent;
end Check_Database;
--\f
function Natural_Image (Val : Natural) return String is
------------------------------------------------------------------------------
-- Val - Specifies the value
-- Turn a natural into a string with no leading blanks.
------------------------------------------------------------------------------
Str : constant String := Natural'Image (Val);
begin
return Str (Str'First + 1 .. Str'Last);
end Natural_Image;
--\f
function Duration_Image (Time : Duration) return String is
------------------------------------------------------------------------------
-- Time - Specifies the duration
-- Ada doesn't give us a nice way to turn a Fixed quantity into a string so
-- we must hack one up.
------------------------------------------------------------------------------
Val : Integer := Integer (Time * Duration (1000.0));
Valsub : Natural := (abs Val) rem 1000;
begin
if Valsub < 10 then
return Natural_Image (Val / 1000) & ".00" & Natural_Image (Valsub);
elsif Valsub < 100 then
return Natural_Image (Val / 1000) & ".0" & Natural_Image (Valsub);
else
return Natural_Image (Val / 1000) & "." & Natural_Image (Valsub);
end if;
end Duration_Image;
--\f
function Get_Host_Id (Name : String) return Transport_Defs.Host_Id is
------------------------------------------------------------------------------
-- Name - Specifies the name of some remote host
-- Returns the Host_Id of this host or else returns an empty Host_Id if the
-- name isn't known.
------------------------------------------------------------------------------
begin
return Transport_Name.Host_To_Host_Id (Name);
exception
when Transport_Name.Undefined =>
return (1 .. 0 => 0);
end Get_Host_Id;
--\f
procedure Response (Connection : File_Transfer.Connect_Id) is
------------------------------------------------------------------------------
-- Conection - Specifies the connection to check
-- Called to read any and all Response text that has been received from the
-- Unix_Machine host in response to any FTP command. The response text is
-- send to the Log.
------------------------------------------------------------------------------
Str : String (1 .. 1000);
Len : Natural;
begin
while not File_Transfer.End_Of_Response (Connection) loop
File_Transfer.Read_Response (Connection, Str, Len);
Log.Put_Line ("Ftp: " & Str (1 .. Len),
Profile.Note_Msg,
Profile.Verbose);
end loop;
end Response;
--\f
procedure Status_Ok (Status : Ftp_Defs.Status_Code) is
------------------------------------------------------------------------------
-- Status - Specifies a status to check
-- Complains and raises Quit if the Status != Successful
------------------------------------------------------------------------------
begin
if Status /= Ftp_Defs.Successful then
Log.Put_Line ("Error in Ship_To_Unix: " &
Ftp_Defs.Status_Code'Image (Status),
Profile.Error_Msg,
Profile.Verbose);
raise Quit;
end if;
end Status_Ok;
--\f
procedure Status_Ok (Status : Transport_Defs.Status_Code) is
------------------------------------------------------------------------------
-- Status - Specifies a status to check
-- Complains and raises Quit if the Status != Ok.
------------------------------------------------------------------------------
begin
if Status /= Transport_Defs.Ok then
Log.Put_Line ("Error in Ship_To_Unix: " &
Transport_Defs.Image (Status),
Profile.Error_Msg,
Profile.Verbose);
raise Quit;
end if;
end Status_Ok;
--\f
procedure Disconnect (Connection : in out File_Transfer.Connect_Id) is
------------------------------------------------------------------------------
-- Connection - Specifies the connection to destroy
-- Called (at any time, in any state) when we must get rid of a connection.
-- We try to do it nicely but we are prepared to ignore any errors and to
-- simply drop the connection at the worst.
------------------------------------------------------------------------------
Ftp_Status : Ftp_Defs.Status_Code;
begin
----If the Connection isn't "open" (really means allocated) then we have nothing
-- to do.
if Effort_Only or else
not File_Transfer.Is_Open (Connection) then
return;
end if;
----If there is an actual connection then we must try to Quit out of it.
if File_Transfer.Is_Connected (Connection) then
begin
begin
File_Transfer.Send_Quit (Connection);
Response (Connection);
File_Transfer.Command_Status (Connection, Ftp_Status);
Status_Ok (Ftp_Status);
----If the Quit fails then report that but also continue.
exception
when others =>
Log.Put_Line
("Unexpected exception during File_Transfer.Send_Quit:" &
Debug_Tools.Get_Exception_Name (True, True),
Profile.Error_Msg,
Profile.Verbose);
end;
----Try to disconnect from the remote server.
File_Transfer.Disconnect (Connection);
exception
when others =>
Log.Put_Line
("Unexpected exception during File_Transfer.Disconnect:" &
Debug_Tools.Get_Exception_Name (True, True),
Profile.Error_Msg,
Profile.Verbose);
end;
end if;
----Finally we close (deallocate) the socket.
File_Transfer.Close (Connection);
end Disconnect;
--\f
procedure Mkdir (Connection : File_Transfer.Connect_Id;
Local : String;
Remote : String) is
------------------------------------------------------------------------------
-- Conection - Specifies the connection to use
-- Local - Specifies the name of an existing local object
-- Remote - Specifies the name of a remote object to be created/written
-- Called to perform (and check) the creation of a remote directory.
------------------------------------------------------------------------------
Ftp_Status : Ftp_Defs.Status_Code;
begin
----Inform the log of the impending creation.
Log.Put_Line ("Create directory: " & Remote,
Profile.Note_Msg,
Profile.Verbose);
----Send the create directory command.
if not Effort_Only then
Files_Transferred := Files_Transferred + 1;
File_Transfer.Send_Verbatim (Connection, "XMKD " & Remote);
Response (Connection);
File_Transfer.Command_Status (Connection, Ftp_Status);
Status_Ok (Ftp_Status);
end if;
end Mkdir;
--\f
procedure Store (Connection : File_Transfer.Connect_Id;
Local : String;
Remote : String) is
------------------------------------------------------------------------------
-- Conection - Specifies the connection to use
-- Local - Specifies the name of an existing local object
-- Remote - Specifies the name of a remote object to be created/written
-- Called to perform (and check) the transfer of one file from the R1000 to
-- the Unix_Machine host.
------------------------------------------------------------------------------
Ftp_Status : Ftp_Defs.Status_Code;
Bytes : Natural;
Dura : Duration;
begin
----Copy the Ascii data into a temporary file; Ada can be such a pain; take
-- care to preserve the look-and-feel of our data.
declare
use Text_Io;
Input : File_Type;
Output : File_Type;
Lno : Positive_Count := 1;
Pno : Positive_Count := 1;
Line : String (1 .. 256);
Length : Natural;
Eof : Boolean := False;
Eop : Boolean := False;
Eol : Boolean := False;
begin
----Open the input and output files.
if Effort_Only then
return;
end if;
Files_Transferred := Files_Transferred + 1;
Open (Input, In_File, Local);
begin
Temp_File_Used := True;
Open (Output, Out_File, Temp_File_Name);
exception
when others =>
Create (Output, Out_File, Temp_File_Name);
end;
----Read lines and put them out to the temporary file.
loop
----Try to fit a line into the space available.
if Eof then
if Length > 0 then
New_Line (Output); -- End file with a Lf.
end if;
exit;
end if;
Get_Line (Input, Line, Length);
Eof := Text_Io.End_Of_File (Input);
Eop := Eof or else Pno /= Page (Input);
Eol := Eop or else Length < Line'Length or else
Lno /= Text_Io.Line (Input);
----If we only got part of a line then put out that part. If we got a Ff then
-- append that and add a newline.
if Eol then
Put_Line (Output, Line (1 .. Length));
if Eop and not Eof then
New_Page (Output);
New_Line (Output);
Pno := Page (Input);
end if;
Lno := Text_Io.Line (Input);
else
Put (Output, Line (1 .. Length));
end if;
end loop;
----Close the files.
Close (Input);
Close (Output);
end;
----Inform the log of the impending transfer.
for I in reverse 1 .. 5 loop
Log.Put_Line ("Transfer: " & Local & " => " & Remote,
Profile.Note_Msg,
Profile.Verbose);
----Set up a new socket connection for the actual transfer.
File_Transfer.Send_Data_Port (Connection,
Transport_Defs.Null_Host_Id,
Transport_Defs.Null_Socket_Id);
Response (Connection);
File_Transfer.Command_Status (Connection, Ftp_Status);
Status_Ok (Ftp_Status);
delay 1.0; -- Try a delay; we're getting stupid errors that
-- are probably timing related.
----Begin the transfer and wait for it to end.
File_Transfer.Start_Store (Connection,
Local_Filename => Temp_File_Name,
Remote_Filename => Remote,
Append => False);
Response (Connection);
File_Transfer.Command_Status (Connection, Ftp_Status);
if Ftp_Status /= Ftp_Defs.Transfer_Complete then
Status_Ok (Ftp_Status);
end if;
----Inform the log of a successful transfer.
Bytes := File_Transfer.Last_Transfer_Length (Connection);
Dura := File_Transfer.Last_Transfer_Time (Connection);
Log.Put_Line
("Done, bytes: " &
Natural_Image (Bytes) & ", seconds: " &
Duration_Image (Dura),
Profile.Note_Msg,
Profile.Verbose);
if Bytes /= 0 then
exit;
elsif I = 1 then
Log.Put_Line
("Arghhh!!! 0 Bytes transferred in 0.0 seconds again!!!" &
" We Quit.", Profile.Error_Msg,
Profile.Verbose);
raise Quit;
end if;
Log.Put_Line
("Arghhh!!! 0 Bytes transferred in 0.0 seconds again!!!" &
" Trying one more time.",
Profile.Error_Msg,
Profile.Verbose);
delay 2.0;
end loop;
end Store;
--\f
procedure Tcp_Port_Numbers_Bug_Workaround is
----HP has a silly bug that we have to get-around.
-- Assign port numbers until port numbers reach at least 1024.
-- The R1000 assigns successive numbers starting at around 511.
-- HP and apparently some others cannot accept any number less
-- than 1024. Silly bug.
C : Transport.Connection_Id;
Status : Transport_Defs.Status_Code;
Done : Boolean := False;
begin
while not Done loop
Transport.Open (C, Status, "TCP/IP");
exit when Transport_Defs."/=" (Status, Transport_Defs.Ok);
declare
Port : constant Transport_Defs.Socket_Id :=
Transport.Local_Socket (C);
begin
Done := Byte_Defs.">=" (Port (Port'First), 4);
end;
Transport.Close (C);
end loop;
end Tcp_Port_Numbers_Bug_Workaround;
--\f
function Check_Object (Obj : Dt.Object.Handle;
Root : String) return Database_Entry is
------------------------------------------------------------------------------
-- Obj - Specifies the object to check
-- Root - Specifies the local root path
--
-- Does a Check_Database followed by an update of the object if that is
-- desirable.
------------------------------------------------------------------------------
Dbentry : Database_Entry;
begin
Dbentry := Check_Database (Obj, Root);
if Dbentry.Update and not Dbentry.Updated then
if Dbentry.Directory then
Mkdir (Connection,
Root & "." & Dbentry.Full_R1000_Name.all,
Dbentry.Unix_Path.all & Dbentry.Unix_Name.all);
else
Store (Connection,
Root & "." & Dbentry.Full_R1000_Name.all,
Dbentry.Unix_Path.all & Dbentry.Unix_Name.all);
end if;
if not Effort_Only then
Dbentry.Update := False;
Dbentry.Updated := True;
end if;
Log.Put_Line ("-----------------------------------------------",
Profile.Note_Msg,
Profile.Verbose);
end if;
return Dbentry;
exception
when Quit =>
----Mark his as "not done".
Dbentry.Version := new String (1 .. 0);
raise;
end Check_Object;
--\f
begin
if Unix_Machine /= "<SHIP_TO_MACHINE>" then
Host_Machine_Name := new String'(Unix_Machine);
else
declare
Env : String_Pointer :=
X_Env_Get_Environment_Variable ("SHIP_TO_MACHINE");
begin
if Env = null then
Log.Put_Line
("Unix_Machinge => <SHIP_TO_MACHINE> and environment variable SHIP_TO_MACHINE is not set. Use the Setenv command to set it.",
Profile.Error_Msg);
raise Library.Error;
else
Host_Machine_Name := new String'(Env.all);
end if;
end;
end if;
if Username /= "<SHIP_TO_USERNAME>" then
Host_User_Name := new String'(Username);
else
declare
Env : String_Pointer :=
X_Env_Get_Environment_Variable ("SHIP_TO_USERNAME");
begin
if Env = null then
Log.Put_Line
("Username => <SHIP_TO_USERNAME> and environment variable SHIP_TO_USERNAME is not set. Use the Setenv command to set it.",
Profile.Error_Msg);
raise Library.Error;
else
Host_User_Name := new String'(Env.all);
end if;
end;
end if;
declare
Td_Status : Transport_Defs.Status_Code;
Ftp_Status : Ftp_Defs.Status_Code;
Host_Id : constant Transport_Defs.Host_Id
:= Get_Host_Id (Host_Machine_Name.all);
Ascii_Mode : Boolean;
Local_Root : Dt.Object.Handle;
Local_Root_Name : String_Pointer;
Local_Files : Dt.Object.Iterator;
Dbentry : Database_Entry;
begin
----Say hello to the user; let him know we are alive.
Log.Put_Line ("[Ship_To_Unix("
& R1000_Files & ','
& R1000_Root & ','
& Unix_Root & ','
& Host_Machine_Name.all & ','
& Host_User_Name.all & ','
& Database_Name & ","
& "Force_Update => " & Boolean'Image (Force_Update) & ","
& "Effort_Only => " & Boolean'Image (Effort_Only) & ")]",
Profile.Auxiliary_Msg, Profile.Verbose);
----Make sure that the Host_Machine_Name host is known to the R1000.
if Host_Id'Length = 0 then
Log.Put_Line
("Host name [" & Host_Machine_Name.all &
"] is not in the Transport_Name_Map (Host name undefined).",
Profile.Error_Msg,
Profile.Verbose);
return;
end if;
----Resolve the various file names that we were given.
Local_Root := Dt.Naming.Resolution (R1000_Root);
if Dt.Object.Is_Bad (Local_Root) then
Log.Put_Line ("R1000_Root argument is bad. {" &
Dt.Object.Message (Local_Root) & '}',
Profile.Error_Msg,
Profile.Verbose);
return;
end if;
Local_Root_Name := Hs (Dt.Naming.Full_Name (Local_Root));
Local_Files := Dt.Naming.Resolution (R1000_Files);
if Dt.Object.Is_Bad (Local_Files) then
Log.Put_Line ("R1000_Files argument is bad. {" &
Dt.Object.Message (Local_Files) & '}',
Profile.Error_Msg,
Profile.Verbose);
return;
end if;
----Initialiaze our object map.
Obj_Db.Initialize (Database);
Read_Database (Database_Name);
----Try to get a socket for our use.
if not Effort_Only then
Log.Put_Line ("Get network socket.",
Profile.Note_Msg, Profile.Verbose);
Tcp_Port_Numbers_Bug_Workaround;
File_Transfer.Open (Connection, Td_Status);
Status_Ok (Td_Status);
----Try to call up the Host_Machine_Name.all and see if it will talk to us at
-- all.
Log.Put_Line ("Connect to " & Host_Machine_Name.all,
Profile.Note_Msg, Profile.Verbose);
File_Transfer.Connect (Connection, Host_Id);
Response (Connection);
File_Transfer.Command_Status (Connection, Ftp_Status);
Status_Ok (Ftp_Status);
----Try to log in as the Username/Password that was given to us.
Log.Put_Line ("Send username.", Profile.Note_Msg, Profile.Verbose);
File_Transfer.Send_Username (Connection, Host_User_Name.all);
Response (Connection);
File_Transfer.Command_Status (Connection, Ftp_Status);
if Ftp_Status = Ftp_Defs.Not_Logged_In then
Log.Put_Line ("Username " & Host_User_Name.all &
" invalid on workstation " &
Host_Machine_Name.all, Profile.Error_Msg,
Profile.Verbose);
raise Quit;
elsif Ftp_Status = Ftp_Defs.Need_Password then
Log.Put_Line ("Send password.", Profile.Note_Msg,
Profile.Verbose);
File_Transfer.Send_Password (Connection, Password);
Response (Connection);
File_Transfer.Command_Status (Connection, Ftp_Status);
end if;
if Ftp_Status = Ftp_Defs.Not_Logged_In then
Log.Put_Line ("Password invalid for username " &
Host_User_Name.all & " on workstation " &
Host_Machine_Name.all, Profile.Error_Msg,
Profile.Verbose);
raise Quit;
end if;
Status_Ok (Ftp_Status);
-- ----Set the account for the logged-in job Host_Machine_Name.
--
-- if Account /= "" and then
-- Account /= " " then
-- Log.Put_Line ("Send account.", Profile.Note_Msg, Profile.Verbose);
-- File_Transfer.Send_Account (Connection, Account);
-- Response (Connection);
-- File_Transfer.Command_Status (Connection, Ftp_Status);
-- if Ftp_Status /= Ftp_Defs.Command_Not_Implemented then
-- Status_Ok (Ftp_Status);
-- end if;
-- end if;
--
----Connect to the Unix_Root directory.
Log.Put_Line ("Connect to " & Unix_Root,
Profile.Note_Msg, Profile.Verbose);
File_Transfer.Send_Cwd (Connection, Unix_Root);
Response (Connection);
File_Transfer.Command_Status (Connection, Ftp_Status);
Status_Ok (Ftp_Status);
----We will be working in Stream mode, sending Ascii data, and working in
-- units of Files. These should work on all FTP implementations.
Log.Put_Line
("Set transfer modes (Stream,Image/Telnet/Ascii,File).",
Profile.Note_Msg, Profile.Verbose);
File_Transfer.Set_Mode (Connection, Ftp_Defs.Stream);
Response (Connection);
File_Transfer.Command_Status (Connection, Ftp_Status);
Status_Ok (Ftp_Status);
Ascii_Mode := True;
File_Transfer.Set_Type (Connection, Ftp_Defs.Image);
Response (Connection);
File_Transfer.Command_Status (Connection, Ftp_Status);
if Ftp_Status /= Ftp_Defs.Successful then
File_Transfer.Set_Type (Connection, Ftp_Defs.Ascii_Telnet);
Response (Connection);
File_Transfer.Command_Status (Connection, Ftp_Status);
if Ftp_Status /= Ftp_Defs.Successful then
File_Transfer.Set_Type (Connection, Ftp_Defs.Ascii);
Response (Connection);
File_Transfer.Command_Status (Connection, Ftp_Status);
end if;
end if;
Status_Ok (Ftp_Status);
File_Transfer.Set_Structure (Connection, Ftp_Defs.File);
Response (Connection);
File_Transfer.Command_Status (Connection, Ftp_Status);
Status_Ok (Ftp_Status);
end if;
----Send all of the various files.
-- if not Ascii_Mode then
-- Log.Put_Line ("Set transmission modes (Stream,Ascii,File).",
-- Profile.Note_Msg, Profile.Verbose);
-- Ascii_Mode := True;
-- File_Transfer.Set_Type (Connection, Ftp_Defs.Ascii);
-- Response (Connection);
-- File_Transfer.Command_Status (Connection, Ftp_Status);
-- Status_Ok (Ftp_Status);
-- end if;
while not Dt.Object.Done (Local_Files) loop
Dbentry := Check_Object
(Dt.Object.Value (Local_Files), Local_Root_Name.all);
Dt.Object.Next (Local_Files);
end loop;
----We are all done; say "bye", disconnect, and free up the socket.
Log.Put_Line ("Disconnect.", Profile.Note_Msg, Profile.Verbose);
Disconnect (Connection);
----Now write out the database.
Log.Put_Line ("Write database.", Profile.Note_Msg, Profile.Verbose);
Write_Database (Database_Name);
if Temp_File_Used then
Library.Delete (Temp_File_Name, Response => "<ERROR>");
end if;
----We are done.
Log.Put_Line ("Processed: " &
Integer'Image (Files_Processed) & " files.",
Profile.Note_Msg);
Log.Put_Line ("Deferred: " &
Integer'Image (Files_Deferred) & " files.",
Profile.Note_Msg);
Log.Put_Line ("Transferred:" &
Integer'Image (Files_Transferred) & " files.",
Profile.Note_Msg);
Log.Put_Line ("[end of Ship_To_Unix]",
Profile.Auxiliary_Msg,
Profile.Verbose);
exception
----When we Quit we simply disconnect; error messages have already gone out.
when Quit =>
if Force_Update then
Log.Put_Line ("Updating internal database.",
Profile.Note_Msg, Profile.Verbose);
while not Dt.Object.Done (Local_Files) loop
Dbentry := Check_Database (Dt.Object.Value (Local_Files),
Local_Root_Name.all);
Dbentry.Version := new String (1 .. 0);
Dt.Object.Next (Local_Files);
end loop;
end if;
Log.Put_Line ("Write database.", Profile.Note_Msg, Profile.Verbose);
Write_Database (Database_Name);
Disconnect (Connection);
raise Library.Error;
----Anything else is simply unexpected.
when others =>
Log.Put_Line ("Unexpected exception during Ship_To_Unix script:" &
Debug_Tools.Get_Exception_Name (True, True),
Profile.Error_Msg,
Profile.Verbose);
Disconnect (Connection);
end;
end Ship_To_Unix;