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 - downloadIndex: ┃ B T ┃
Length: 130180 (0x1fc84) Types: TextFile Names: »B«
└─⟦85b835f43⟧ Bits:30000549 8mm tape, Rational 1000, Xlib rev 6.00 └─ ⟦0c20f784e⟧ »DATA« └─⟦1abbe589f⟧ └─⟦059497ac5⟧ └─⟦this⟧
with Text_Io; with Unchecked_Deallocation; with Xlbt_Arithmetic; use Xlbt_Arithmetic; with Xlbt_Basic; use Xlbt_Basic; with Xlbt_Exceptions; use Xlbt_Exceptions; with Xlbt_Font; use Xlbt_Font; with Xlbt_Hint; use Xlbt_Hint; with Xlbt_Proc_Var; use Xlbt_Proc_Var; with Xlbt_Rm; use Xlbt_Rm; with Xlbt_Rm2; use Xlbt_Rm2; with Xlbt_String7; use Xlbt_String7; with Xlbt_String; use Xlbt_String; with Xlbt_String16; use Xlbt_String16; with Xlbt_Univ_Ptr; use Xlbt_Univ_Ptr; with Xlbt_Window; use Xlbt_Window; with Xlbp_Display; use Xlbp_Display; with Xlbp_Error; use Xlbp_Error; with Xlbp_Proc_Var; use Xlbp_Proc_Var; with Xlbp_Rm_Name; use Xlbp_Rm_Name; with Xlbp_U_Char_Converters; use Xlbp_U_Char_Converters; with Xlbit_Library3; use Xlbit_Library3; with Xlbit_Library4; use Xlbit_Library4; with Xlbip_Internal; use Xlbip_Internal; with Xlbmt_Network_Types; use Xlbmt_Network_Types; with Xlbmt_Parameters; use Xlbmt_Parameters; with Xlbmp_Environment; use Xlbmp_Environment; with Xlbmp_Error_Log; use Xlbmp_Error_Log; pragma Elaborate (Xlbit_Library3); pragma Elaborate (Xlbit_Library4); package body Xlbp_Rm is ------------------------------------------------------------------------------ -- X Library Resource Manager -- -- Xlbp_Rm - Manager of application/window/object resources ------------------------------------------------------------------------------ -- Copyright 1989 - 1991 by Rational, Santa Clara, California. -- Copyright 1987 - 1989 by Digital Equipment Corporation, Maynard, Mass. -- Copyright 1987 - 1989 by Massachusetts Institute of Technology, -- Cambridge, Massachusetts. -- -- 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 Digital, MIT, or Rational -- not be used in advertising or publicity pertaining to distribution of -- the software without specific, written prior permission. -- -- Digital, MIT, and Rational disclaim all warranties with regard to this -- software, including all implied warranties of merchantability and fitness, -- in no event shall Digital, MIT, or 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. ------------------------------------------------------------------------------ -- -- These Xrm routines allow very fast lookup of resources in the resource -- database. Several usage patterns are exploited: -- -- (1) Widgets get a lot of resources at one time. Rather than look up each -- from scratch, we can precompute the prioritized list of database levels once, -- then search for each resource starting at the beginning of the list. -- -- (2) Many database levels don't contain any leaf resource nodes. There is no -- point in looking for resources on a level that doesn't contain any. This -- information is kept on a per-level basis...if even just a single hash bucket -- contains a value, that level is searched. I considered having 4 arrays -- coming off each database level, split by tight/loose and intermediate/leaf, -- but this looked like it would burn a lot of memory for negligible performance -- gains. -- -- (3) Sometimes the widget instance tree is structured such that you get the -- same class name repeated on the fully qualified widget name. This can result -- in the same database level occurring multiple times on the search list. The -- code below only checks to see if you get two identical search lists in a row, -- rather than look back through all database levels, but in practice this -- removes all duplicates I've ever observed. -- -- Joel McCormack ------------------------------------------------------------------------------ -- ***************************************************************************** -- * Date - /Name/ Comment -- * -- * 23-OCT-90 - /DRK/ Fixed Free_X_Rm_Hash_Bucket to not dereference -- * - null pointers and to release more storage. -- * 24-OCT-90 - /DRK/ Removed some hard-coded list lengths of 100. -- * - Added exception handlers for bad resource names and -- * - representations in Put_Line_Resources. -- * 30-OCT-90 - /DRK/ Fixed Put_Entry to correctly update the -- * - X_Lib.Resource_Quarks bit vector. -- * 2-NOV-90 - /DRK/ Fixed X_Rm_Put_File_Database to handle null DBs, -- * - and to close its file handle. -- * - /DRK/ Fixed Print_Binding_Quark_List to stop looping -- * - when it reached the end of the list. -- * - /DRK/ Rewrote Strip_C_Style_Quoting to make it work. -- * 6-NOV-90 - /GEB/ Convert to the new X_Lib multitasking protection for -- * - library state. -- * 7-FEB-91 - /GEB/ Remove all Xt conversion hooks. Xt will be doing that -- * - directly; these facilities are not nearly good enough. -- ****************************************************************************** Xrm_Error_Display_Defaults : constant X_String := "Xrm; Unexpected exception while processing server" & Lf & "defaults from %1. Server defaults not loaded." & Lf & "Exception: %6"; Xrm_Error_Bad_Character : constant X_String := "Xrm; Bad character constant {%1}; value reduced." & Lf & "Line: {%2}"; Xrm_Error_Bad_Quoting : constant X_String := "Xrm; Bad character quoting {%1}; ignored." & Lf & "Line: {%2}"; Xrm_Error_Bad_Line : constant X_String := "Xrm; Resource line has bad format; missing :/!" & Lf & "after name. Line: {%1}"; Xrm_Error_Read_Conversion : constant X_String := "Xrm; conversion error on {%1}" & Lf & "Raised exception: %6"; Xrm_Error_Bad_Resource_Name : constant X_String := "Xrm; Bad resource name: {%1}" & Lf & "Line: {%2}"; Xrm_Error_Bad_Resource_Rep : constant X_String := "Xrm; Bad resource representation: {%1}" & Lf & "Line: {%2}"; Xrm_Error_Bad_Kind : constant X_String := "Xrm; Resource line has bad format; missing ':'" & Lf & "after name!rep!kind. Line: {%1}"; Xrm_Error_Read_Db : constant X_String := "Xrm; Error reading database file %1"; Xrm_Error_Cmd_Bad_Kind : constant X_String := "Xrm; Error parsing command line argument %1 (%2)." & Lf & "Option 'kind' not recognized."; Xrm_Error_Cmd_Bad_Default : constant X_String := "Xrm; Error parsing command line argument %1 (%2)." & Lf & "Default value bad/inappropriate/unreadable."; Xrm_Error_Cmd_Bad_Line : constant X_String := "Xrm; Error parsing command line argument %1 (%2)." & Lf & "Argument: {%3}"; type X_Rm_Search_List_Pointer is access X_Rm_Search_List; --/ if Enable_Deallocation then pragma Enable_Deallocation (X_Rm_Search_List_Pointer); --/ end if; procedure Free_X_Rm_Search_List_Pointer is new Unchecked_Deallocation (X_Rm_Search_List, X_Rm_Search_List_Pointer); procedure Heap_Free_X_Rm_Hash_Table is new Unchecked_Deallocation (X_Rm_Hash_Bucket_Array, X_Rm_Hash_Table); procedure Heap_Free_X_Rm_Hash_Bucket is new Unchecked_Deallocation (X_Rm_Hash_Bucket_Rec, X_Rm_Hash_Bucket); procedure Heap_Free_X_Rm_Database is new Unchecked_Deallocation (X_Rm_Database_Rec, X_Rm_Database); --\f procedure Had_Errors (Status : in out X_Rm_Status) is begin if Status < Rm_Had_Errors then Status := Rm_Had_Errors; end if; end Had_Errors; procedure Failed (Status : in out X_Rm_Status) is begin if Status < Rm_Failed then Status := Rm_Failed; end if; end Failed; --\f procedure Free_X_Rm_Entry_Rec is new Unchecked_Deallocation (X_Rm_Entry_Rec, X_Rm_Entry); --\f procedure Private_Free_X_Rm_Value (Destination : in out X_Rm_Value) is ------------------------------------------------------------------------------ -- Destination - Specifies the X_Rm_Value to free -- -- Called to free up any heap storage occupied by an X_Rm_Value. ------------------------------------------------------------------------------ begin case Destination.Kind is ----These guys do not have any type of heap storage associated with them. when Is_None | Is_Boolean | Is_Float | Is_U_Char | Is_U_Short | Is_S_Char | Is_S_Short | Is_S_Long | Is_X_Atom | Is_X_Color | Is_X_Colormap | Is_X_Cursor | Is_X_Font | Is_X_Initial_Window_State | Is_X_Pixel | Is_X_Pixmap | Is_X_Time | Is_X_Window => null; ----These guys have storage but we don't free them because they belong to some -- other guy that would get freed some other way and thus cause them to get -- freed. when Is_X_Screen | Is_X_Visual => null; ----These guys may have storage that needs to be freed. when Is_X_Display => X_Close_Display (Destination.V_X_Display); when Is_X_Rm_File_Type => begin Text_Io.Close (Destination.V_X_Rm_File_Type.all); exception when others => null; end; Free_X_Rm_File_Type (Destination.V_X_Rm_File_Type); when Is_X_Font_Struct => Free_X_Font_Struct (Destination.V_X_Font_Struct); when Is_X_String7_Pointer => if Destination.V_X_String7_Pointer /= null then Free_X_String7_Pointer (Destination.V_X_String7_Pointer); end if; when Is_X_String_Pointer => if Destination.V_X_String_Pointer /= null then Free_X_String_Pointer (Destination.V_X_String_Pointer); end if; when Is_X_String16_Pointer => if Destination.V_X_String16_Pointer /= null then Free_X_String16_Pointer (Destination.V_X_String16_Pointer); end if; when Is_U_Char_List => if Destination.V_U_Char_List /= null then Free_U_Char_List (Destination.V_U_Char_List); end if; when Is_X_Universal_Pointer => if Destination.V_X_Universal_Pointer.Free /= None_X_Procedure_Variable then Proc_Var_X_Univ_Free.Call (Proc_Var_X_Univ_Free.To_Pv (Destination.V_X_Universal_Pointer.Free), Destination.V_X_Universal_Pointer.Pointer); end if; end case; end Private_Free_X_Rm_Value; --\f procedure Free_X_Rm_Entry (Val : in out X_Rm_Entry) is ------------------------------------------------------------------------------ -- Val - Specifies the X_Rm_Entry that is to be deleted/freed -- -- Called to free up the heap storage occupied by an X_Rm_Entry. The -- X_Rm_Entry is deallocated as well as the storage occupied by the X_Rm_Value -- contained within the X_Rm_Entry. ------------------------------------------------------------------------------ begin ----Free the storage contained in the data .Value and then free the Entry -- itself. Private_Free_X_Rm_Value (Val.Value); Free_X_Rm_Entry_Rec (Val); end Free_X_Rm_Entry; --\f procedure Reset_X_Rm_Entry (Destination : X_Rm_Entry) is ------------------------------------------------------------------------------ -- Destination - Specifies the entry to zap. -- -- Zap all storage inside of an X_Rm_Entry. We leave it empty with no -- data. ------------------------------------------------------------------------------ begin Private_Free_X_Rm_Value (Destination.Value); Destination.all := (False, None_X_Rm_Representation, None_X_Rm_Value); end Reset_X_Rm_Entry; --\f function X_New_Rm_Database return X_Rm_Database is ------------------------------------------------------------------------------ -- Called to obtain a newly allocated, completely empty database. Free the -- database after use with Free_X_Rm_Database. ------------------------------------------------------------------------------ begin return new X_Rm_Database_Rec; end X_New_Rm_Database; --\f function Hash_Index (Name : X_Rm_Name) return S_Natural is begin return S_Natural (Name.Id) rem X_Rm_Hash_Bucket_Array'Length; end Hash_Index; --\f procedure Get_Entry (Tight : X_Rm_Hash_Table; Loose : X_Rm_Hash_Table; Names : X_Rm_Name_Array; Classes : X_Rm_Class_Array; Db_Entry : out X_Rm_Entry; Ret_Val : out Boolean) is ------------------------------------------------------------------------------ -- Tight - Specifies the "tight" binding at this point in the lookup -- Loose - Specifies the "loose" binding at this point in the lookup -- Names - Specifies the names we are looking for -- Classes - Specifies the classes we are looking for -- Db_Entry- Receives the value of our result (which may be None_X_Rm_Entry) -- Ret_Val - Receives TRUE if we have a result and FALSE if not ------------------------------------------------------------------------------ Names_I : S_Natural := Names'First; Classes_I : S_Natural := Classes'First; Bucket : X_Rm_Hash_Bucket; Ntight : X_Rm_Hash_Table; Nloose : X_Rm_Hash_Table; Db2 : X_Rm_Entry; function Get_Entry_Lookup (Table : X_Rm_Hash_Table; Name : X_Rm_Name) return Boolean is ------------------------------------------------------------------------------ -- Table - Specifies the hash table to look within -- Name - Specifies the name we are looking for -- -- Called to see if a particular name is located within a particular table. -- If it is then we return all the way back to the original caller with the -- database entry that we just found. ------------------------------------------------------------------------------ Flag : Boolean; begin ----Grab a hash bucket and loop over all buckets in the chain. Bucket := Table (Hash_Index (Name)); while Bucket /= null loop ----See if we just found the name. If so then we are very happy. if Bucket.Name = Name then ----If this is the "terminal" or "last" part of the name sequence that we -- are seeking, and if there is any data here, then return that data. if Names (Names_I + 1) = None_X_Rm_Name then ----Must be leaf node with data, else doesn't match -- Has both data and name. Db2 := Bucket.Value; Db_Entry := Db2; return Db2 /= None_X_Rm_Entry; ----If this isn't the final part of the name sequence then recurse upon -- ourselves and finish the lookup. Exit all the way back. else Ntight := Bucket.Tables (X_Rm_Bind_Tightly); Nloose := Bucket.Tables (X_Rm_Bind_Loosely); if Ntight /= null or else Nloose /= null then Get_Entry (Ntight, Nloose, Names (Names_I + 1 .. Names'Last), Classes (Classes_I + 1 .. Classes'Last), Db_Entry, Flag); if Flag then return True; end if; end if; return False; end if; end if; ----Get the next bucket in the chain. Bucket := Bucket.Next; end loop; return False; end Get_Entry_Lookup; begin ----Sanity check upon ourselves and our caller. --/ if DEBUG then if not ((Tight /= null or Loose /= null) and Names (Names'First) /= None_X_Rm_Name) then raise X_Library_Confusion; end if; --/ end if; ----Check the very first name & class in both tight and loose tables if Tight /= null and then Get_Entry_Lookup (Tight, Names (Names_I)) then Ret_Val := True; return; end if; if Loose /= null and then Get_Entry_Lookup (Loose, Names (Names_I)) then Ret_Val := True; return; end if; if Tight /= null and then Get_Entry_Lookup (Tight, X_Rm_Name (Classes (Classes_I))) then Ret_Val := True; return; end if; if Loose /= null and then Get_Entry_Lookup (Loose, X_Rm_Name (Classes (Classes_I))) then Ret_Val := True; return; end if; ----Now check any remaining names and class, but just in the loose table. if Loose /= null then while Names (Names_I + 1) /= None_X_Rm_Name loop Names_I := Names_I + 1; Classes_I := Classes_I + 1; if Get_Entry_Lookup (Loose, Names (Names_I)) then Ret_Val := True; return; end if; if Get_Entry_Lookup (Loose, X_Rm_Name (Classes (Classes_I))) then Ret_Val := True; return; end if; end loop; end if; ----Didn't find any of the names or classes in either hash table. Db_Entry := None_X_Rm_Entry; Ret_Val := False; end Get_Entry; --\f procedure X_Rm_Get_Resource (Database : X_Rm_Database; Names : X_Rm_Name_Array; Classes : X_Rm_Class_Array; Representation : out X_Rm_Representation; Value : out X_Rm_Value) is -------------------------------------------------------------------------- -- Database - Specifies the database to use for the lookup. -- Names - Specifies the fully qualified name of the value; e.g. a.b.c -- Classes - Specifies the fully qualified class of the value; e.g. A.B.C -- Representation- Receives the representation of the value -- Value - Receives the entry from the database or None_X_Rm_Value -- -- If successful (Value /= None_X_Rm_Value) then Value has received an -- X_Rm_Value from within the database. Any heap based data returned is shared -- with the database (and possibly other databases) so do not attempt free the -- data contained within the value. -- -- Will raise Constraint_Error if either Names or Classes contains -- illegal characters. -------------------------------------------------------------------------- Rdbi : X_Rm_Hash_Bucket; Tight : X_Rm_Hash_Table; Loose : X_Rm_Hash_Table; Db_Entry : X_Rm_Entry; Ret_Val : Boolean; begin if Database /= None_X_Rm_Database and then Database.Contents /= None_X_Rm_Hash_Bucket then Rdbi := Database.Contents; Tight := Rdbi.Tables (X_Rm_Bind_Tightly); Loose := Rdbi.Tables (X_Rm_Bind_Loosely); if (Tight /= null or else Loose /= null) and then Names (Names'First) /= None_X_Rm_Name then Get_Entry (Tight, Loose, Names, Classes, Db_Entry, Ret_Val); if Ret_Val then Representation := Db_Entry.Representation; Value := Db_Entry.Value; else Representation := None_X_Rm_Representation; Value := None_X_Rm_Value; end if; return; end if; end if; Representation := None_X_Rm_Representation; Value := None_X_Rm_Value; end X_Rm_Get_Resource; --\f function Components (Name : X_String) return S_Natural is -------------------------------------------------------------------------- -- Name - Specifies a resource name being parsed. -- -- Return the number of components in the Resource_Name. If a complete -- Resource_Spec is supplied the correct answer is still returned. -------------------------------------------------------------------------- Count : S_Natural := 0; begin -- Count the separators for Pos in Name'Range loop if Name (Pos) = '.' or else Name (Pos) = '*' then Count := Count + 1; elsif Name (Pos) = ':' then exit; end if; end loop; if Count <= 0 then -- Even the empty string has one component. return 1; elsif Name (Name'First) = '.' or else Name (Name'First) = '*' then -- Initial separator didn't separate any components. return Count; else -- All separators were real. return Count + 1; end if; end Components; --\f procedure X_Rm_Get_Resource (Database : X_Rm_Database; Names : X_String; Classes : X_String; Representation : out X_Rm_Representation; Value : out X_Rm_Value) is -------------------------------------------------------------------------- -- Database - Specifies the database to use for the lookup. -- Names - Specifies the fully qualified name of the value. -- Classes - Specifies the fully qualified class of the value. -- Representation- Receives the representation of the value -- Value - Receives the entry from the database or None_X_Rm_Value -- -- If successful (Value /= None_X_Rm_Value) then Value has received an -- X_Rm_Value from within the database. Any heap based data returned is shared -- with the database (and possibly other databases) so do not attempt free the -- data contained within the entry. -------------------------------------------------------------------------- Names_Array : X_Rm_Name_Array (1 .. Components (Names) + 1); Classes_Array : X_Rm_Class_Array (1 .. Components (Classes) + 1); begin X_Rm_String_To_Name_List (Names, Names_Array); X_Rm_String_To_Class_List (Classes, Classes_Array); X_Rm_Get_Resource (Database, Names_Array, Classes_Array, Representation, Value); end X_Rm_Get_Resource; --\f procedure Put_Entry (Bucket : X_Rm_Hash_Bucket; Bindings : X_Rm_Binding_Array; Names : X_Rm_Name_Array; Representation : X_Rm_Representation; Value : X_Rm_Value; Db_Owner : Boolean) is ------------------------------------------------------------------------------ -- Bucket - Specifies the database to modify -- Bindings - Specifies the tight/loose qualifiers -- Names - Specifies the name sequence of the new entry -- Representation - Specifies the user's representation (logical) for the value -- Value - Specifies the (physical) value -- Db_Owner - Specifies TRUE if the database must deallocate heap storage -- -- Called to put a value (or replace and old value) within a database. ------------------------------------------------------------------------------ Ibucket : S_Natural; Pbucket : X_Rm_Hash_Bucket := Bucket; Binding : X_Rm_Binding; Name : X_Rm_Name; Table : X_Rm_Hash_Table; Parent : X_Rm_Hash_Bucket; Quark_Id : S_Natural; -- U_Long; Quark_Bits : X_Rm_Quark_Bit_List; begin ----Search over the entire name sequence. Exit the loop when we reach the -- end of the sequence. for I in S_Natural range 0 .. Names'Length - 1 loop exit when Names (Names'First + I) = None_X_Rm_Name; Name := Names (Names'First + I); Binding := Bindings (Bindings'First + I); ----Remember the parent for marking later. Parent := Pbucket; ----Allocate new hash table if needed to make this binding in this bucket -- non-null. Table := Pbucket.Tables (Binding); if Table = null then Table := new X_Rm_Hash_Bucket_Array; Pbucket.Tables (Binding) := Table; end if; ----Find bucket containing the name; if possible. Ibucket := Hash_Index (Name); Pbucket := Table (Ibucket); while Pbucket /= null and then Pbucket.Name /= Name loop Pbucket := Pbucket.Next; end loop; ----Create new bucket if we didn't find the name.. if Pbucket = null then Pbucket := new X_Rm_Hash_Bucket_Rec; Pbucket.Next := Table (Ibucket); Table (Ibucket) := Pbucket; Pbucket.Name := Name; end if; end loop; ----Mark parent database as having a child with a value, and -- update X_Lib.Resource_Quarks. if Parent /= None_X_Rm_Hash_Bucket then Parent.Has_Values (Binding) := True; Quark_Id := S_Natural (Name.Id); X_Lib.Seize_Resource_Quarks (Quark_Bits); if Quark_Bits = None_X_Rm_Quark_Bit_List then Quark_Bits := new X_Rm_Quark_Bit_Array' (Quark_Id .. Quark_Id => False); elsif Quark_Id not in Quark_Bits'Range then declare Nb : X_Rm_Quark_Bit_List; begin if Quark_Id < Quark_Bits'First then Nb := new X_Rm_Quark_Bit_Array' (Quark_Id .. Quark_Bits'Last => False); else Nb := new X_Rm_Quark_Bit_Array' (Quark_Bits'First .. Quark_Id => False); end if; Nb (Quark_Bits'Range) := Quark_Bits.all; Nb (Quark_Id) := True; Free_X_Rm_Quark_Bit_List (Quark_Bits); Quark_Bits := Nb; end; end if; Quark_Bits (Quark_Id) := True; X_Lib.Release_Resource_Quarks (Quark_Bits); end if; ----The hash bucket is modified to contain the value that was passed in. -- Special case: Kind = Is_None => just clobber the old value. if Value.Kind = Is_None then ----New value is "none". Clobber old value. if Pbucket.Value = None_X_Rm_Entry then return; else Free_X_Rm_Entry (Pbucket.Value); end if; else ----New value is not "none". Clobber old value and assign new. if Pbucket.Value = None_X_Rm_Entry then Pbucket.Value := new X_Rm_Entry_Rec; else Reset_X_Rm_Entry (Pbucket.Value); end if; Pbucket.Value.all := (Representation => Representation, Value => Value, Owner => Db_Owner); end if; end Put_Entry; --\f procedure X_Rm_Put_Resource (Database : in out X_Rm_Database; Bindings : X_Rm_Binding_Array; Quarks : X_Rm_Quark_Array; Representation : X_Rm_Representation; Value : X_Rm_Value; Db_Owner : Boolean := True) is Names : X_Rm_Name_Array (Quarks'Range); begin if Database = None_X_Rm_Database then Database := new X_Rm_Database_Rec; Database.Contents := new X_Rm_Hash_Bucket_Rec; elsif Database.Contents = None_X_Rm_Hash_Bucket then Database.Contents := new X_Rm_Hash_Bucket_Rec; end if; for I in Quarks'Range loop Names (I) := X_Rm_Name (Quarks (I)); end loop; Put_Entry (Bucket => Database.Contents, Bindings => Bindings, Names => Names, Representation => Representation, Value => Value, Db_Owner => Db_Owner); end X_Rm_Put_Resource; --\f procedure X_Rm_Put_Resource (Database : in out X_Rm_Database; Bindings : X_Rm_Binding_Array; Classes : X_Rm_Class_Array; Representation : X_Rm_Representation; Value : X_Rm_Value; Db_Owner : Boolean := True) is Names : X_Rm_Name_Array (Classes'Range); begin if Database = None_X_Rm_Database then Database := new X_Rm_Database_Rec; Database.Contents := new X_Rm_Hash_Bucket_Rec; elsif Database.Contents = None_X_Rm_Hash_Bucket then Database.Contents := new X_Rm_Hash_Bucket_Rec; end if; for I in Classes'Range loop Names (I) := X_Rm_Name (Classes (I)); end loop; Put_Entry (Bucket => Database.Contents, Bindings => Bindings, Names => Names, Representation => Representation, Value => Value, Db_Owner => Db_Owner); end X_Rm_Put_Resource; --\f procedure X_Rm_Put_Resource (Database : in out X_Rm_Database; Bindings : X_Rm_Binding_Array; Names : X_Rm_Name_Array; Representation : X_Rm_Representation; Value : X_Rm_Value; Db_Owner : Boolean := True) is -------------------------------------------------------------------------- -- Database - Specifies the database to modify; if NULL then a new -- database record will be created. -- Bindings - Specifies a list of binding types. -- Names - Specifies the partial name or class list of the resource. -- Representation - Specifies the type of the resource. -- Value - Specifies the value of the resource. -- Db_Owner - Specifies TRUE if the database is to be responsible for -- deallocating any heap storage utilized by Value once the -- last database entry for this Value is overwritten or -- released. -- -- Place an X_Rm_Value into the database. -- -- To "remove" a value from the database simply pass an X_Rm_Value'(Kind => -- Is_None) in as Value. The database storage space will not grow if the -- indicated value was not there to begin with and it will shrink (by a tiny -- amount) if the value is eliminated by an Is_None assignment. -- -- If Db_Owner is TRUE (the default) then any heap storage represented by -- the Value becomes the "property" of the database and it must not be -- subsequently modified or freed. If several entries in one database (or -- individual entries in multiple databases) are all to be given entries -- which will all have the same heap-using Value then either a) none of the -- database entries should be made with Db_Owner => TRUE (meaning that the -- application is responsible for releasing the heap storage), or b) only -- one database entry should have Db_Owner => TRUE and that database must -- be deallocated last of any databases sharing this value. -- -- Note that if Value is an Is_Universal then Db_Owner has no affect if -- the Free routine within the Value is X_Univ_Free.None. A database cannot -- free a Universal pointer when no Free routine has been provided. -------------------------------------------------------------------------- begin if Database = None_X_Rm_Database then Database := new X_Rm_Database_Rec; Database.Contents := new X_Rm_Hash_Bucket_Rec; elsif Database.Contents = None_X_Rm_Hash_Bucket then Database.Contents := new X_Rm_Hash_Bucket_Rec; end if; Put_Entry (Bucket => Database.Contents, Bindings => Bindings, Names => Names, Representation => Representation, Value => Value, Db_Owner => Db_Owner); end X_Rm_Put_Resource; --\f procedure X_Rm_Put_String_Resource (Database : in out X_Rm_Database; Bindings : X_Rm_Binding_Array; Names : X_Rm_Name_Array; Value : X_String) is -------------------------------------------------------------------------- -- Just like X_Rm_Put_Resource above except it is only for installing string -- values. ------------------------------------------------------------------------------ begin if Database = None_X_Rm_Database then Database := new X_Rm_Database_Rec; Database.Contents := new X_Rm_Hash_Bucket_Rec; elsif Database.Contents = None_X_Rm_Hash_Bucket then Database.Contents := new X_Rm_Hash_Bucket_Rec; end if; Put_Entry (Bucket => Database.Contents, Bindings => Bindings, Names => Names, Representation => X_Lib_X_Rm_R_String, Value => (Kind => Is_X_String_Pointer, V_X_String_Pointer => new X_String'(Value)), Db_Owner => True); end X_Rm_Put_String_Resource; --\f procedure X_Rm_Put_Resource (Database : in out X_Rm_Database; Specifier : X_String; Representation : X_String; Value : X_Rm_Value; Db_Owner : Boolean := True) is ------------------------------------------------------------------------------ -- Database - Specifies the database to modify; if NULL then a new -- database record will be created and returned. -- Specifier - Specifies the (may be partial) specification of the -- resource. -- Representation - Specifies the type of the resource. -- Value - Specifies the value of the resource. -- Db_Owner - Specifies TRUE if the database is to be responsible for -- deallocating any heap storage utilized by Value once the -- last database entry for this Value is overwritten or -- released. -- -- A convenience routine that calls X_Rm_String_To_Representation for the -- Representation string, calls X_Rm_String_To_Binding_Name_List for the -- Specifier, and calls X_Rm_Put_Resource above to actually install the -- resource. ------------------------------------------------------------------------------ Bindings : X_Rm_Binding_Array (1 .. Components (Specifier) + 1); Names : X_Rm_Name_Array (1 .. Bindings'Length); begin if Database = None_X_Rm_Database then Database := new X_Rm_Database_Rec; Database.Contents := new X_Rm_Hash_Bucket_Rec; elsif Database.Contents = None_X_Rm_Hash_Bucket then Database.Contents := new X_Rm_Hash_Bucket_Rec; end if; X_Rm_String_To_Binding_Name_List (Specifier, Bindings, Names); Put_Entry (Bucket => Database.Contents, Bindings => Bindings, Names => Names, Representation => X_Rm_String_To_Representation (Str => Representation), Value => Value, Db_Owner => Db_Owner); end X_Rm_Put_Resource; --\f procedure X_Rm_Put_String_Resource (Database : in out X_Rm_Database; Specifier : X_String; Value : X_String) is ------------------------------------------------------------------------------ -- Just like X_Rm_Put_Resource above except it is only for installing string -- values. ------------------------------------------------------------------------------ Bindings : X_Rm_Binding_Array (1 .. Components (Specifier) + 1); Names : X_Rm_Name_Array (1 .. Bindings'Length); begin if Database = None_X_Rm_Database then Database := new X_Rm_Database_Rec; Database.Contents := new X_Rm_Hash_Bucket_Rec; elsif Database.Contents = None_X_Rm_Hash_Bucket then Database.Contents := new X_Rm_Hash_Bucket_Rec; end if; X_Rm_String_To_Binding_Name_List (Specifier, Bindings, Names); Put_Entry (Bucket => Database.Contents, Bindings => Bindings, Names => Names, Representation => X_Lib_X_Rm_R_String, Value => (Kind => Is_X_String_Pointer, V_X_String_Pointer => new X_String'(Value)), Db_Owner => True); end X_Rm_Put_String_Resource; --\f function Get_Tables (Tight : X_Rm_Hash_Table; Loose : X_Rm_Hash_Table; Names : X_Rm_Name_Array; Classes : X_Rm_Class_Array) return X_Rm_Search_List is ------------------------------------------------------------------------------ -- Tight - Specifies the "tight" tables at this point in the lookup -- Loose - Specifies the "loose" tables at this point in the lookup -- Names - Specifies the name sequence we are looking for -- Classes - Specifies the class sequence we are looking for -- -- Called when we are doing a lookup that will take us to the database -- "neighborhood" where we want to do a large number of related lookups. -- We call this to lookup the context for a toolkit widget and then we -- use the tables to do the lookups of the widget's individual resources. ------------------------------------------------------------------------------ Tables : X_Rm_Search_List (1 .. 100); -- Result; unless we overflow Tables_Ptr : X_Rm_Search_List_Pointer; -- Result; if we overflow Tables_Ptr2 : X_Rm_Search_List_Pointer;-- Temp during overflow Tables_I : S_Natural := 0; -- Current count of results procedure Save_Table (Table : X_Rm_Hash_Table) is ------------------------------------------------------------------------------ -- Table - Specifies a table to return to the caller; saved as a result -- -- Called to put a Table into our set of return tables. Since, in the -- absolute worst case, there are (Names'Length)! possible return values, -- and since we have no way of usefully predicting the actual number of -- return values prior to finding them, we will use a mixed strategy. -- There will usually be a "small" number of return tables. I.e. < 100. -- However, there could be very very many. So, the first 100 go into the -- local area and if we happen to overflow that then we will use heap -- storage to store our intermediate results. ------------------------------------------------------------------------------ begin if Tables_I > 1 then if Tables_I <= Tables'Last then if Tables (Tables_I) = X_Rm_Search_List_Entry (Table) then return; end if; else if Tables_Ptr (Tables_I) = X_Rm_Search_List_Entry (Table) then return; end if; end if; end if; Tables_I := Tables_I + 1; if Tables_I <= Tables'Last then -- Fits local table Tables (Tables_I) := X_Rm_Search_List_Entry (Table); return; elsif Tables_Ptr = null then -- Create a heap table Tables_Ptr := new X_Rm_Search_List (1 .. Tables'Last * 2); Tables_Ptr (Tables'Range) := Tables; elsif Tables_I <= Tables_Ptr'Last then -- Fits in heap table null; else -- Recreate heap table Tables_Ptr2 := new X_Rm_Search_List (1 .. Tables_Ptr'Last * 2); Tables_Ptr2 (Tables_Ptr'Range) := Tables_Ptr.all; Free_X_Rm_Search_List_Pointer (Tables_Ptr); Tables_Ptr := Tables_Ptr2; end if; Tables_Ptr (Tables_I) := X_Rm_Search_List_Entry (Table); end Save_Table; procedure Get_Tables (Tight : X_Rm_Hash_Table; Loose : X_Rm_Hash_Table; Names : X_Rm_Name_Array; Classes : X_Rm_Class_Array) is ------------------------------------------------------------------------------ -- Same as the function. -- -- This is the workhorse. This is the guy who recursively walks the database -- calling Save_Table for each table that we want to return. ------------------------------------------------------------------------------ Bucket : X_Rm_Hash_Bucket; -- Current entry Ntight : X_Rm_Hash_Table; -- Current tight table Nloose : X_Rm_Hash_Table; -- Current loose table Names_I : S_Natural := Names'First; Classes_I : S_Natural := Classes'First; procedure Get_Tables_Lookup (Table : X_Rm_Hash_Table; Name : X_Rm_Name) is ------------------------------------------------------------------------------ -- Table - Specifies the hash table to look within -- Name - Specifies the name we are looking for -- -- Called to see if a particular name is located within a particular table. -- If it is then we return all the way back to the original caller with the -- database table that we just found. ------------------------------------------------------------------------------ begin ----Grab a hash bucket and loop over all buckets in the chain. Bucket := Table (Hash_Index (Name)); while Bucket /= null loop ----See if we just found the name. If so then we are very happy. if Bucket.Name = Name then ----If this entry has further tables then we keep it. If not then just return. Ntight := Bucket.Tables (X_Rm_Bind_Tightly); Nloose := Bucket.Tables (X_Rm_Bind_Loosely); if Ntight = null and then Nloose = null then return; end if; ----If we aren't at the end of the name list then recurse. if Names (Names_I + 1) /= None_X_Rm_Name then Get_Tables (Ntight, Nloose, Names (Names_I + 1 .. Names'Last), Classes (Classes_I + 1 .. Classes'Last)); end if; ----If there is a Tight table then return that if there is room for it. if Ntight /= null and then Bucket.Has_Values (X_Rm_Bind_Tightly) then Save_Table (Ntight); end if; ----If there is a loose table then return that if there is room for it. if Nloose /= null and then Bucket.Has_Values (X_Rm_Bind_Loosely) then Save_Table (Nloose); end if; return; end if; ----Loop for the next bucket. Bucket := Bucket.Next; end loop; end Get_Tables_Lookup; begin -- body of procedure Get_Tables ----Sanity check upon ourselves and our caller. --/ if DEBUG then if not ((Tight /= null or Loose /= null) and Names (Names'First) /= None_X_Rm_Name) then raise X_Library_Confusion; end if; --/ end if; ----Check the very first name & class in both tight and loose tables if Tight /= null then Get_Tables_Lookup (Tight, Names (Names_I)); end if; if Loose /= null then Get_Tables_Lookup (Loose, Names (Names_I)); end if; if Tight /= null then Get_Tables_Lookup (Tight, X_Rm_Name (Classes (Classes_I))); end if; if Loose /= null then Get_Tables_Lookup (Loose, X_Rm_Name (Classes (Classes_I))); end if; ----Now check any remaining names and class, but just in the loose table. if Loose /= null then while Names (Names_I + 1) /= None_X_Rm_Name loop Names_I := Names_I + 1; Classes_I := Classes_I + 1; Get_Tables_Lookup (Loose, Names (Names_I)); Get_Tables_Lookup (Loose, X_Rm_Name (Classes (Classes_I))); end loop; end if; end Get_Tables; begin -- body of function Get_Tables ----Return whatever it is that we find. Get_Tables (Tight, Loose, Names, Classes); if Tables_I <= Tables'Last then return Tables (1 .. Tables_I); end if; declare Tbls : X_Rm_Search_List (1 .. Tables_I) := Tables_Ptr (1 .. Tables_I); begin Free_X_Rm_Search_List_Pointer (Tables_Ptr); return Tbls; end; exception when others => Free_X_Rm_Search_List_Pointer (Tables_Ptr); raise; end Get_Tables; --\f function X_Rm_Get_Search_List (Database : X_Rm_Database; Names : X_Rm_Name_Array; Classes : X_Rm_Class_Array) return X_Rm_Search_List is -------------------------------------------------------------------------- -- Database - Specifies the database to search. -- Names - Specifies a list of resource names. -- Classes - Specifies a list of class names. -- -- Takes a list of names and classes and returns a list of database levels -- and wildcards where a match might occur. -------------------------------------------------------------------------- Ntight : X_Rm_Hash_Table; Nloose : X_Rm_Hash_Table; function Tbls return X_Rm_Search_List is ------------------------------------------------------------------------------ -- If there is a non-empty Names list then get the tables for that list. -- Otherwise return an empty list. ------------------------------------------------------------------------------ begin if Names (Names'First) /= None_X_Rm_Name then return Get_Tables (Ntight, Nloose, Names, Classes); else return (1 .. 0 => null); end if; end Tbls; begin ---If there isn't any database then there isn't any list. if Database = None_X_Rm_Database or else Database.Contents = None_X_Rm_Hash_Bucket then return (1 .. 0 => null); end if; ----See if we can find any tables that match this sequence of names. -- We want the tables that match the Names list concatenated with the -- Ntight and then the Nloose tables on the end of the list. Ntight := Database.Contents.Tables (X_Rm_Bind_Tightly); Nloose := Database.Contents.Tables (X_Rm_Bind_Loosely); if Ntight /= null and then Database.Contents.Has_Values (X_Rm_Bind_Tightly) then if Nloose /= null and then Database.Contents.Has_Values (X_Rm_Bind_Loosely) then return Tbls & X_Rm_Search_List_Entry (Ntight) & X_Rm_Search_List_Entry (Nloose); else return Tbls & X_Rm_Search_List_Entry (Ntight); end if; elsif Nloose /= null and then Database.Contents.Has_Values (X_Rm_Bind_Loosely) then return Tbls & X_Rm_Search_List_Entry (Nloose); else return Tbls; end if; end X_Rm_Get_Search_List; --\f procedure X_Rm_Get_Search_Resource (List : X_Rm_Search_List; Name : X_Rm_Name; Class : X_Rm_Class; Representation : out X_Rm_Representation; Value : out X_Rm_Value) is -------------------------------------------------------------------------- -- List - Specifies the search list to use. -- Name - Specifies a name to look up via the list. -- Class - Specifies a class name to look up via the list. -- Representation- Receives the representation of the value -- Value - Receives the entry from the database or None_X_Rm_Value -- -- If successful (Value /= None_X_Rm_Value) then Value has received an -- X_Rm_Value from within the database. Any heap based data returned is shared -- with the database (and possibly other databases) so do not attempt free the -- data contained within the entry. -------------------------------------------------------------------------- Bucket : X_Rm_Hash_Bucket; Db2 : X_Rm_Entry; Name_Hash : S_Natural; Class_Hash : S_Natural; Name_Search : Boolean; Class_Search : Boolean; Quark_Bits : X_Rm_Quark_Bit_List; Db_Entry : X_Rm_Entry; begin -- My numbers show an average of 0.3 searches per call. Compare this to -- the maximal bound of 2 searches per call. Further, the searchList -- tends to be short--often 1 or 2 tables, generally not more than 4. -- So this routine optimizes for the common cases. Code is minimized -- if there is nothing to do, and only three registers are used to -- avoid saving/restoring. X_Lib.Seize_Resource_Quarks (Quark_Bits); if Quark_Bits /= None_X_Rm_Quark_Bit_List and then S_Natural (Name.Id) in Quark_Bits'Range then Name_Search := Quark_Bits (S_Natural (Name.Id)); Name_Hash := Hash_Index (Name); else Name_Search := False; end if; if Quark_Bits /= None_X_Rm_Quark_Bit_List and then S_Natural (Class.Id) in Quark_Bits'Range then Class_Search := Quark_Bits (S_Natural (Class.Id)); Class_Hash := Hash_Index (X_Rm_Name (Class)); else Class_Search := False; end if; X_Lib.Release_Resource_Quarks (Quark_Bits); if not Name_Search and then not Class_Search then ----Most common case. Representation := None_X_Rm_Representation; Value := None_X_Rm_Value; return; end if; ----Check each table in the search list. for I in List'Range loop if List (I) /= null then ----Look for the Name in this table. if Name_Search then Bucket := List (I) (Name_Hash); while Bucket /= null loop if Bucket.Name = Name then Db2 := Bucket.Value; if Db2 /= None_X_Rm_Entry and then Db2.Value.Kind /= Is_None then Representation := Db2.Representation; Value := Db2.Value; return; end if; exit; end if; Bucket := Bucket.Next; end loop; end if; ----Look for the Class in this table. if Class_Search then Bucket := List (I) (Class_Hash); while Bucket /= null loop if Bucket.Name = X_Rm_Name (Class) then Db2 := Bucket.Value; if Db2 /= None_X_Rm_Entry and then Db2.Value.Kind /= Is_None then Representation := Db2.Representation; Value := Db2.Value; return; end if; exit; end if; Bucket := Bucket.Next; end loop; end if; end if; end loop; ----We didn't find anything. Representation := None_X_Rm_Representation; Value := None_X_Rm_Value; end X_Rm_Get_Search_Resource; --\f procedure Skip_Whitespace (Str : in out X_String; Pos : in out S_Natural) is ------------------------------------------------------------------------------ -- Scan the string starting at character Pos until we find a non-whitespace -- character. Pos is set to point to the first non-white character. ------------------------------------------------------------------------------ begin for I in Pos .. Str'Last loop if Str (I) /= ' ' and then Str (I) /= Ht then Pos := I; return; end if; end loop; Pos := Str'Last + 1; end Skip_Whitespace; --\f procedure Read_A_Name (Str : in out X_String; Str_End : S_Natural; Pos : in out S_Natural; Name_Begin : out S_Natural; Name_End : out S_Natural; End_Char : in out X_Character; Status : in out X_Rm_Status) is ------------------------------------------------------------------------------ -- Scan the string from Pos..Str_End looking for ':', '!', or whitespace -- followed by one of those. Running off the end of the string is the same -- as hitting a ':'. Name_Begin..Name_End is the name found. Pos is the -- character after the ':' or '!'. End_Char is the ':' or '!' found. -- Name_Begin = 0 if we got a "format" error and this line should be -- abandoned. ------------------------------------------------------------------------------ begin ----Skip whitespace and mark the beginning of the name. Skip_Whitespace (Str, Pos); Name_Begin := Pos; Name_End := Pos - 1; End_Char := ':'; ----Loop over the remaining string. while Pos <= Str_End loop ----If we hit the :/! then we are done. Return our values. if Str (Pos) = ':' or else Str (Pos) = '!' then End_Char := Str (Pos); Name_End := Pos - 1; Pos := Pos + 1; exit; end if; ----If we hit whitespace then we have the end of the name. Skip the space -- and make sure that we get end-of-string, ':', or '!'. if Str (Pos) = ' ' or else Str (Pos) = Ht then Name_End := Pos - 1; Skip_Whitespace (Str, Pos); if Pos <= Str_End then End_Char := Str (Pos); if End_Char /= ':' and then End_Char /= '!' then Failed (Status); X_Report_Error ("XrmError", "BadLine", Xrm_Error_Bad_Line, Str (Str'First .. Str_End)); Name_Begin := 0; Name_End := 0; return; end if; end if; Pos := Pos + 1; exit; end if; ----Loop on the rest of the string. Pos := Pos + 1; end loop; end Read_A_Name; --\f function Hex (H : X_String) return U_Char_Array is ------------------------------------------------------------------------------ -- H - Specifies the string containing a series of 2-digit hex numbers -- -- Called to turn a "hex" string into a U_Char_Array value. ------------------------------------------------------------------------------ Hi : S_Natural := H'First; Y : U_Char; B : U_Char_Array (1 .. H'Length / 2); Ch : X_String (1 .. 2); begin ----Loop over all of the expected raw data values. if H'Length rem 2 = 1 then raise Constraint_Error; -- Only *pairs* of hex digits allowed. end if; for I in B'Range loop ----Get the first number (2 digits) and convert it. Ch := H (Hi .. Hi + 1); Hi := Hi + 1; Y := 0; if Ch (1) /= '0' then if Ch (1) in '0' .. '9' then Y := (X_Character'Pos (Ch (1)) - X_Character'Pos ('0')) * 16; elsif Ch (1) in 'A' .. 'F' then Y := (X_Character'Pos (Ch (1)) - X_Character'Pos ('A') + 10) * 16; elsif Ch (1) in 'a' .. 'f' then Y := (X_Character'Pos (Ch (1)) - X_Character'Pos ('a') + 10) * 16; end if; end if; if Ch (2) in '0' .. '9' then Y := Y + (X_Character'Pos (Ch (2)) - X_Character'Pos ('0')) * 16; elsif Ch (2) in 'A' .. 'F' then Y := Y + (X_Character'Pos (Ch (2)) - X_Character'Pos ('A') + 10) * 16; elsif Ch (2) in 'a' .. 'f' then Y := Y + (X_Character'Pos (Ch (2)) - X_Character'Pos ('a') + 10) * 16; end if; B (I) := Y; end loop; ----Return our result. return B; end Hex; --\f procedure Get_Db_Value (Buf : in out X_String; Full_Buf : X_String; Value : in out X_Rm_Value; Status : in out X_Rm_Status) is ------------------------------------------------------------------------------ -- Buf - Specifies the string value to be placed into the database -- Full_Buf - Specifies the full input line for error messages -- Value - Receives the converted value -- -- Called when we have read an entry from an Ascii database file. We convert -- the string that we read into a database value. ------------------------------------------------------------------------------ begin case Value.Kind is when Is_None | Is_X_Rm_File_Type | Is_X_Universal_Pointer | Is_X_Atom | Is_X_Color | Is_X_Colormap | Is_X_Cursor | Is_X_Display | Is_X_Font | Is_X_Font_Struct | Is_X_Pixel | Is_X_Pixmap | Is_X_Screen | Is_X_Visual | Is_X_Window => Value := (Kind => Is_None); when Is_Boolean => Value := (Kind => Is_Boolean, V_Boolean => Boolean'Value (To_String (Buf))); when Is_Float => declare Last : Positive; begin Value := (Kind => Is_Float, V_Float => 0.0); Float_Io.Get (To_String (Buf), Value.V_Float, Last); end; when Is_U_Char => Value := (Kind => Is_U_Char, V_U_Char => U_Char'Value (To_String (Buf))); when Is_U_Short => Value := (Kind => Is_U_Short, V_U_Short => U_Short'Value (To_String (Buf))); when Is_S_Char => Value := (Kind => Is_S_Char, V_S_Char => S_Char'Value (To_String (Buf))); when Is_S_Short => Value := (Kind => Is_S_Short, V_S_Short => S_Short'Value (To_String (Buf))); when Is_S_Long => Value := (Kind => Is_S_Long, V_S_Long => S_Long'Value (To_String (Buf))); when Is_X_Initial_Window_State => Value := (Kind => Is_X_Initial_Window_State, V_X_Initial_Window_State => X_Initial_Window_State'Value (To_String (Buf))); when Is_X_Time => Value := (Kind => Is_X_Time, V_X_Time => X_Time'Value (To_String (Buf))); when Is_X_String7_Pointer => Value := (Kind => Is_X_String7_Pointer, V_X_String7_Pointer => new String'(To_String (Buf))); when Is_X_String_Pointer => declare Uca : constant U_Char_Array := Hex (Buf); begin Value := (Kind => Is_X_String_Pointer, V_X_String_Pointer => new X_String (1 .. Uca'Length)); From_Uca (Value.V_X_String_Pointer.all, Uca); end; when Is_X_String16_Pointer => declare Uca : constant U_Char_Array := Hex (Buf); begin Value := (Kind => Is_X_String16_Pointer, V_X_String16_Pointer => new X_String16 (1 .. Uca'Length / 2)); From_Uca (Value.V_X_String16_Pointer.all, Uca); end; when Is_U_Char_List => Value := (Kind => Is_U_Char_List, V_U_Char_List => new U_Char_Array'(Hex (Buf))); end case; exception when others => Failed (Status); X_Report_Exception ("XrmError", "ReadConversion", Xrm_Error_Read_Conversion, Full_Buf); Value := (Kind => Is_None); end Get_Db_Value; --\f procedure Strip_C_Style_Quoting (Buf : in out X_String; Buf_Last : in out S_Natural; Status : in out X_Rm_Status) is ------------------------------------------------------------------------------ -- Buf - Specifies the string to process. -- Buf_Last - Specifies and returns the last in-use position within Buf. -- -- Called to strip the '\' quoting from a string (normal C conventions). -- - "\n" becomes a single Ascii.Lf in the final value. -- - "\n" followed by an Ascii.Lf in the Line string becomes just Ascii.Lf -- - "\000" where "000" is any three digit octal number becomes the -- Ascii character with that Character'Pos. -- - "\\" becomes just a single "\". -- -- Buf_Last is updated to indicate the end of the still-existent Buf when -- we are done. Buf is modified in-place. ------------------------------------------------------------------------------ Ch_0 : constant S_Natural := X_Character'Pos ('0'); Ch_Max : constant S_Natural := X_Character'Pos (X_Character'Last); In_Pos : S_Long := Buf'First; Out_Pos : S_Long := Buf'First; Char : S_Natural; begin -- Each iteration of this loop produces one "output" character, -- but may consume several "input" characters. while In_Pos <= Buf_Last loop if Buf (In_Pos) /= '\' then -- Everything except "\" is copied unchanged. Buf (Out_Pos) := Buf (In_Pos); elsif (In_Pos = Buf_Last) or else (Buf (In_Pos + 1) = '\') then -- "\\" becomes "\", as does a trailing "\". In_Pos := In_Pos + 1; Buf (Out_Pos) := '\'; elsif Buf (In_Pos + 1) = 'n' then -- "\n" becomes Lf. In_Pos := In_Pos + 1; Buf (Out_Pos) := Lf; -- "\n" & Lf becomes a single Lf. if (In_Pos < Buf_Last) and then (Buf (In_Pos + 1) = Lf) then In_Pos := In_Pos + 1; end if; elsif (In_Pos + 3 <= Buf_Last and then Buf (In_Pos + 1) in '0' .. '7' and then Buf (In_Pos + 2) in '0' .. '7' and then Buf (In_Pos + 3) in '0' .. '7') then -- "\ddd" becomes a single character. Char := (X_Character'Pos (Buf (In_Pos + 1)) - Ch_0) * 64 + (X_Character'Pos (Buf (In_Pos + 2)) - Ch_0) * 8 + (X_Character'Pos (Buf (In_Pos + 3)) - Ch_0); -- Complain if this isn't a legal character. if Char > Ch_Max then Char := Char mod (Ch_Max + 1); Had_Errors (Status); X_Report_Warning ("XrmWarning", "BadCharacter", Xrm_Error_Bad_Character, Buf (In_Pos .. In_Pos + 3), Buf (Buf'First .. Out_Pos - 1) & Buf (In_Pos .. Buf_Last)); end if; -- Save the conversion result. In_Pos := In_Pos + 3; Buf (Out_Pos) := X_Character'Val (Char); else -- All other quoting is an error. Had_Errors (Status); X_Report_Warning ("XrmWarning", "BadQuoting", Xrm_Error_Bad_Quoting, Buf (In_Pos .. In_Pos + 1), Buf (Buf'First .. Out_Pos - 1) & Buf (In_Pos .. Buf_Last)); In_Pos := In_Pos + 1; Buf (Out_Pos) := Buf (In_Pos); end if; -- Examine the next character. Out_Pos := Out_Pos + 1; In_Pos := In_Pos + 1; end loop; -- Set the new buffer length. Buf_Last := Out_Pos - 1; end Strip_C_Style_Quoting; --\f procedure Put_Line_Resources (Pdb : in out X_Rm_Database; Buf_In : X_String; Status : in out X_Rm_Status) is ------------------------------------------------------------------------------ -- Pdb - Specifies the database to modify -- Buf - Specifies the resource line we are to process -- -- Called to put one resource line (as from a database file) into an -- existing database. ------------------------------------------------------------------------------ Buf : X_String (1 .. Buf_In'Length) := Buf_In; Bufi : S_Natural; S : S_Natural; Ch : X_Character; Name_Str : S_Natural; Name_End : S_Natural; Bindings : X_Rm_Binding_Array (1 .. Components (Buf_In) + 1); Names : X_Rm_Name_Array (1 .. Bindings'Length); Value : X_Rm_Value; Representation : X_Rm_Representation; Kind : X_Rm_Value_Kind; begin ----Scan the string and remove any '\' quotings. \ on the end of the string -- is ignored. \n is translated to Ascii.Lf. Any other character after a -- \ is simply copied minus the \ unless it looks like an octal number. if Buf'Length = 0 or else Buf (Buf'First) = '!' then return; -- Ignore comment lines. end if; Bufi := Buf'Last; Strip_C_Style_Quoting (Buf, Bufi, Status); ----Scan to the end of the resource name/class specification. We skip -- leading whitespace. We scan for :/!/whitespace as the termination of -- the name. If the name ends in whitespace then we skip that and we -- expect the first non-whitespace to be a :/!. S := Buf'First; Read_A_Name (Buf, Bufi, S, Name_Str, Name_End, Ch, Status); if Name_Str = 0 then return; end if; begin X_Rm_String_To_Binding_Name_List (Buf (Name_Str .. Name_End), Bindings, Names); exception when Constraint_Error => Had_Errors (Status); X_Report_Error ("XrmWarning", "BadName", Xrm_Error_Bad_Resource_Name, Buf (Name_Str .. Name_End), Buf_In); return; end; ----We either have Ch='!' indicating that a representation clause comes next -- or we have Ch=':' indicating that a string value comes next. If we see -- a ':' then we assume that the format is String and the Kind is String. -- If we see a ! then we have a representation. next. Get that name and it is our -- Representation. After the representation. name may be another ! followed by a Kind. -- Assume Is_Bytes if Kind is not there. if Ch = ':' then Representation := X_Lib_X_Rm_R_String; Kind := Is_X_String_Pointer; else Read_A_Name (Buf, Bufi, S, Name_Str, Name_End, Ch, Status); if Name_Str = 0 then return; end if; begin Representation := X_Rm_String_To_Representation (Buf (Name_Str .. Name_End)); exception when Constraint_Error => Had_Errors (Status); X_Report_Error ("XrmWarning", "BadRep", Xrm_Error_Bad_Resource_Rep, Buf (Name_Str .. Name_End), Buf_In); return; end; if Name_Str = 0 then return; end if; if Ch = ':' then Kind := Is_U_Char_List; else Read_A_Name (Buf, Bufi, S, Name_Str, Name_End, Ch, Status); begin Kind := X_Rm_Value_Kind'Value (To_String (Buf (Name_Str .. Name_End))); exception when others => Had_Errors (Status); X_Report_Error ("XrmWarning", "BadKind", Xrm_Error_Bad_Kind, Buf (Buf'First .. Bufi)); return; end; end if; end if; ----If Kind is Is_String then skip whitespace and take the rest of the line -- as the value. if Kind = Is_X_String_Pointer then Skip_Whitespace (Buf, S); Value := (Kind => Is_X_String_Pointer, V_X_String_Pointer => new X_String'(Buf (S .. Bufi))); X_Rm_Put_Resource (Pdb, Bindings, Names, Representation, Value, Db_Owner => True); elsif Kind = Is_X_String7_Pointer then Skip_Whitespace (Buf, S); Value := (Kind => Is_X_String7_Pointer, V_X_String7_Pointer => new String'(To_String (Buf (S .. Bufi)))); X_Rm_Put_Resource (Pdb, Bindings, Names, Representation, Value, Db_Owner => True); ----If Kind is not Is_String then the rest of the line is a bunch of bytes -- encoded 2 hex characters at a time. Dispatch based upon Kind and do -- the right thing. else Skip_Whitespace (Buf, S); Get_Db_Value (Buf (S .. Buf'Last), Buf, Value, Status); X_Rm_Put_Resource (Pdb, Bindings, Names, Representation, Value, Db_Owner => True); end if; end Put_Line_Resources; --\f function File_Get_Line (File : Text_Io.File_Type) return X_String is ------------------------------------------------------------------------------ -- Called to read the next "line" in from a file. The next "line" is number of -- Ada's IO lines up to the first one that does not end in '\'. ------------------------------------------------------------------------------ Buf : String (1 .. 1000); Bufi : Integer := 0; Eol : Boolean := False; begin ----Read in one Ada line. Text_Io.Get_Line (File, Buf, Bufi); ----If we got the whole Ada line and if this Ada line ends in \ then remove -- that \ and pretend that EOL was not seen. Eol := Text_Io."=" (Text_Io.Col (File), 1); if Eol then -- An odd number of trailing '\'s means this line is continued. for I in reverse 1 .. Bufi loop exit when Buf (Bufi) /= '\'; Eol := not Eol; end loop; -- Remove the '\' that caused the continuation. if not Eol then Bufi := Bufi - 1; end if; end if; ----If we don't have a whole line (either we didn't read the entire Ada line -- or else we need to read another Ada line) then recurse and return the -- concatenated fruit of our labors. if not Eol then begin return To_X_String (Buf (1 .. Bufi)) & File_Get_Line (File); exception when Text_Io.End_Error => null; end; end if; ----We're happy, return our "line". return To_X_String (Buf (1 .. Bufi)); end File_Get_Line; --\f procedure X_Rm_Add_Resource (Database : in out X_Rm_Database; Line : X_String; Status : out X_Rm_Status) is ------------------------------------------------------------------------------ -- Database - Specifies the database to update; if NULL then a new database -- record will be created. -- Line - Specifies the "name:value", "name!rep:value", or -- "name!rep!kind:value" string. The name contains any -- full or partial resource specification. The representation is any -- Resource Manager representation. The kind is any X_Rm_Value_Kind -- And, the value is the string/hex-string representation of the -- resource. -- Status - Receives Rm_Successful, Rm_Had_Errors, or Rm_Failed. -- -- Called to add one entry to a database. -- -- The name/representation/kind/string are separated by optional whitespace and -- non-optional '!'/':' characters. The representation and kind are optional. Kind -- cannot be specified if representation is not specified. -- -- The name, representation, and kind are any initial portions of the string that are not -- whitespace; leading and trailing whitespace surrounding the name, representation, or, -- kind is dropped. -- -- The value begins with the first non-whitespace character after the colon -- and continues up to the end of the string or until an embedded Ascii.Lf -- is found. -- -- If the representation is "String" (the default if no representation is given) then the value -- is a simple string of characters using many of the normal C conventions. -- - "\n" becomes a single Ascii.Lf in the final value -- - "\n" followed by an Ascii.Lf in the Line string becomes just Ascii.Lf -- - "\000" where "000" is any octal number of 1..3 digits becomes the -- Ascii character wit that Character'Pos -- - "\\" becomes just a single "\" ------------------------------------------------------------------------------ Str : X_String renames Line; Stri : S_Natural := Str'First; Lstatus : X_Rm_Status; function String_Get_Line return X_String is ------------------------------------------------------------------------------ -- Called to read the next "line" in from Str. The next "line" is all -- unread characters up to the end of the string or up to the first Ascii.Lf -- that is not preceded by a '\'. Any '\' & Ascii.Lf combinations are -- squeezed out of the result. ------------------------------------------------------------------------------ I : S_Natural; B : S_Natural := Stri; begin ----If the string is empty then raise End_Error to signal end-of-string. if Stri > Str'Last then raise Text_Io.End_Error; end if; ----Loop over the contents of the string. I := Stri; while I <= Str'Last loop ----If we hit a Lf then we've found the end of the line. if Str (I) = Lf then Stri := I + 1; return Str (B .. I - 1); end if; ----If we find a '\' then: a) if at the end of the string then drop it and -- return the rest of what we've scanned; b) if followed by Ascii.Lf then -- drop it and the Lf and return what we've scanned with a recursive scan. -- Otherwise skip the \ and whatever character comes after it. if Str (I) = '\' then if I = Str'Last then Stri := Str'Last + 1; return Str (B .. I - 1); elsif Str (I + 1) = Lf then Stri := I + 2; return Str (B .. I - 1) & String_Get_Line; end if; I := I + 1; end if; ----Loop for the next character. I := I + 1; end loop; ----Ran off the end of the string; return the entire contents. Stri := Str'Last + 1; return Str (B .. Str'Last); end String_Get_Line; begin Lstatus := Rm_Successful; begin loop Put_Line_Resources (Database, String_Get_Line, Lstatus); end loop; exception when Text_Io.End_Error => null; end; Status := Lstatus; end X_Rm_Add_Resource; --\f procedure X_Rm_Get_String_Database (Data : X_String; Database : out X_Rm_Database; Status : out X_Rm_Status) is ------------------------------------------------------------------------------ -- Just like X_Rm_Get_File_database except that it reads a string instead -- of a file. ------------------------------------------------------------------------------ Ldb : X_Rm_Database := None_X_Rm_Database; begin X_Rm_Add_Resource (Ldb, Data, Status); Database := Ldb; end X_Rm_Get_String_Database; --\f procedure X_Rm_Get_File_Database (Filename : String; Database : out X_Rm_Database; Status : out X_Rm_Status) is ------------------------------------------------------------------------------ -- Filename - Specifies the name of the file to read. -- Database - Receives the database read; which may be empty; i.e. no entries -- Status - Receives Rm_Successful, Rm_Had_Errors, or Rm_Failed. -- -- Opens the specified file and attempts to read it and create a new database. -- The file must contain lines acceptable to X_Rm_Put_Line_Resource. -- -- Note: Files created by X Library implementations other than this one -- may or may not be usable and files written with this X Library -- implementation may or may not work properly with other implementation -- unless the resource values consist only of strings. -- -- Currently (Sep-1989) this implementation provides a superset of the -- functionality of the existing C X Library implementation. This means that -- this routine can read resource files created by other implementations and -- other implementations can read files created here as long as only string -- values are involved. Compatibility is a goal and as much compatibility as -- possible will be maintained in the future. ------------------------------------------------------------------------------ File : Text_Io.File_Type; Ldb : X_Rm_Database := None_X_Rm_Database; Lstatus : X_Rm_Status; begin ----No errors as yet. Lstatus := Rm_Successful; ----If there is no filename or if there is no file of that name then simply -- return the "empty" database. if Filename'Length = 0 then Database := Ldb; return; end if; begin Text_Io.Open (File, Text_Io.In_File, Filename); exception when Text_Io.Name_Error => Database := Ldb; Failed (Lstatus); Status := Lstatus; X_Report_Error ("XrmError", "FileNotFound", "Xrm; File not found: %1", To_X_String (Filename)); return; when others => Database := Ldb; Failed (Lstatus); Status := Lstatus; X_Report_Error ("XrmError", "FileCannotRead", "Xrm; Cannot open file for reading: %1", To_X_String (Filename)); return; end; ----Loop getting lines from the file until we run out. begin loop Put_Line_Resources (Ldb, File_Get_Line (File), Lstatus); end loop; exception when Text_Io.End_Error => null; -- Simple End-Of-File on the input file. when Storage_Error => Failed (Lstatus); Status := Lstatus; X_Report_Error ("XlibError", "NoMemory", "No free memory available."); when others => Failed (Lstatus); Status := Lstatus; X_Report_Error ("XrmError", "FileReadException", "Xrm; Unexpected exception while reading file: %1", To_X_String (Filename)); end; ----Close the file and return the Database. Text_Io.Close (File); Database := Ldb; Status := Lstatus; end X_Rm_Get_File_Database; --\f procedure Print_Binding_Quark_List (Bindings : X_Rm_Binding_Array; Names : X_Rm_Name_Array; File : Text_Io.File_Type) is ------------------------------------------------------------------------------ -- Bindings - Specifies the bindings between names; '.' and '*' -- Names - Specifies a name list -- File - Specifies the output file -- -- Called when dumping a database to Ascii. We print the name sequence -- that was used to reach a particular value within the database; complete with -- '.' and '*'s. ------------------------------------------------------------------------------ First_Name_Seen : Boolean := False; begin for I in S_Natural range 0 .. Names'Length - 1 loop exit when Names (Names'First + I) = None_X_Rm_Name; if Bindings (Bindings'First + I) = X_Rm_Bind_Loosely then Text_Io.Put (File, '*'); elsif First_Name_Seen then Text_Io.Put (File, '.'); end if; First_Name_Seen := True; Text_Io.Put (File, To_String (X_Rm_Name_To_String (Names (Names'First + I)))); end loop; end Print_Binding_Quark_List; --\f procedure Dump_Bytes (File : Text_Io.File_Type; Uca : U_Char_Array) is Uca_I : S_Natural := Uca'First; Uca_J : S_Natural; R : S_Natural; begin ----Loop doing 50 byte chunks at a time. loop Uca_J := Uca_I + 49; if Uca_J > Uca'Last then Uca_J := Uca'Last; end if; ----Put out the 50 characters. for I in Uca_I .. Uca_J loop Text_Io.Put (File, ' '); R := S_Natural (Uca (I)) / 16; if R <= 9 then Text_Io.Put (File, Character'Val (Character'Pos ('0') + R)); else Text_Io.Put (File, Character'Val (Character'Pos ('A') - 10 + R)); end if; R := S_Natural (Uca (I)) rem 16; if R <= 9 then Text_Io.Put (File, Character'Val (Character'Pos ('0') + R)); else Text_Io.Put (File, Character'Val (Character'Pos ('A') - 10 + R)); end if; end loop; ----Now continue or terminate the line if we are done. if Uca_J < Uca'Last then Text_Io.Put_Line (File, "\"); else exit; end if; Uca_I := Uca_J + 1; end loop; Text_Io.New_Line (File); end Dump_Bytes; --\f procedure Dump_Entry (Bindings : X_Rm_Binding_Array; Names : X_Rm_Name_Array; Representation : X_Rm_Representation; Value : X_Rm_Value; File : Text_Io.File_Type) is ------------------------------------------------------------------------------ -- Bindings - Specifies the tight/loose bindings of the database entry -- Names - Specifies the name components of the database entry -- Representation - Specifies the user's representation type -- Value - Specifies the actual database entry and the physical type -- File - Specifies the output file to use -- -- Called to dump a single database entry to an Ascii output file. ------------------------------------------------------------------------------ function Printable (Astr : X_String7_Pointer) return Boolean is ------------------------------------------------------------------------------ -- Astr - Specifies the string to examine -- -- Returns TRUE if a string contains nothing but "printable" characters. -- Characters are printable if they aren't Ascii.Del or Ascii control -- characters. Ascii.Ht, Ascii.Lf are "ok". ------------------------------------------------------------------------------ begin for I in Astr'Range loop if Astr (I) /= Ascii.Lf and then Astr (I) /= Ascii.Ht and then (Astr (I) < ' ' or else Astr (I) >= Ascii.Del) then return False; end if; end loop; return True; end Printable; function Printable (Astr : X_String_Pointer) return Boolean is ------------------------------------------------------------------------------ -- Astr - Specifies the string to examine -- -- Returns TRUE if a string contains nothing but "printable" characters. -- Characters are printable if they aren't Ascii.Del or Ascii control -- characters. Ascii.Ht, Ascii.Lf are "ok". ------------------------------------------------------------------------------ begin for I in Astr'Range loop if Astr (I) /= Xlbt_String.Lf and then Astr (I) /= Xlbt_String.Ht and then (Astr (I) < Xlbt_String.' ' or else Astr (I) >= Xlbt_String.Del) then return False; end if; end loop; return True; end Printable; begin ----Start the output with the name of the database entry. Put in the right -- type of '.' and '*' divisions in the name. Print_Binding_Quark_List (Bindings, Names, File); ----String values are the easiest. They just dump straight to the file. -- This is also the only compatible-across-all-X-Library-implementations -- type of entry. if Representation = X_Lib_X_Rm_R_String and then Value.Kind = Is_X_String7_Pointer and then Printable (Value.V_X_String7_Pointer) then ----We have a string value and it contains only printable characters. -- Put it out in up-to 200 character increments with '\' continuation lines. declare Ptr : X_String7_Pointer := Value.V_X_String7_Pointer; Char_I : Natural := Ptr'First; Char_J : Natural; begin Text_Io.Put (File, ':' & Ascii.Ht); ----Loop doing 200 character chunks at a time. loop Char_J := Char_I + 199; if Char_J > Ptr'Last then Char_J := Ptr'Last; end if; ----Put out the 200 characters. Watch for line-feed and '\' and quote them. for I in Char_I .. Char_J loop if Ptr (I) = Ascii.Lf then Text_Io.Put (File, "\n"); elsif Ptr (I) = '\' then Text_Io.Put (File, "\\"); else Text_Io.Put (File, Ptr (I)); end if; end loop; ----Now continue or terminate the line if we are done. if Char_J < Ptr'Last then Text_Io.Put_Line (File, "\"); else exit; end if; Char_I := Char_J + 1; end loop; Text_Io.New_Line (File); end; return; end if; ----String values are the next easiest. They just dump straight to the file. -- This is also the only compatible-across-all-X-Library-implementations -- type of entry. if Representation = X_Lib_X_Rm_R_String and then Value.Kind = Is_X_String_Pointer and then Printable (Value.V_X_String_Pointer) then ----We have a string value and it contains only printable characters. -- Put it out in up-to 200 character increments with '\' continuation lines. declare Ptr : X_String_Pointer := Value.V_X_String_Pointer; Char_I : S_Natural := Ptr'First; Char_J : S_Natural; begin Text_Io.Put (File, ':' & Ascii.Ht); ----Loop doing 200 character chunks at a time. loop Char_J := Char_I + 199; if Char_J > Ptr'Last then Char_J := Ptr'Last; end if; ----Put out the 200 characters. Watch for line-feed and '\' and quote them. for I in Char_I .. Char_J loop if Ptr (I) = Xlbt_String.Lf then Text_Io.Put (File, "\n"); elsif Ptr (I) = Xlbt_String.'\' then Text_Io.Put (File, "\\"); else Text_Io.Put (File, Character'Val (X_Character'Pos (Ptr (I)))); end if; end loop; ----Now continue or terminate the line if we are done. if Char_J < Ptr'Last then Text_Io.Put_Line (File, "\"); else exit; end if; Char_I := Char_J + 1; end loop; Text_Io.New_Line (File); end; return; end if; ----We have a non-string (or non-printable) entry. Put it out in the extended -- name!Representation: value ----Put the value out using the appropriate mechanism. Based upon the Kind of -- the physical data. case Value.Kind is when Is_None | Is_X_Rm_File_Type | Is_X_Universal_Pointer | Is_X_Atom | Is_X_Color | Is_X_Colormap | Is_X_Cursor | Is_X_Display | Is_X_Font | Is_X_Font_Struct | Is_X_Pixel | Is_X_Pixmap | Is_X_Screen | Is_X_Visual | Is_X_Window => Text_Io.Put_Line (File, ':' & Ascii.Ht); when Is_Boolean => Text_Io.Put (File, ':' & Ascii.Ht); Text_Io.Put_Line (File, Boolean'Image (Value.V_Boolean)); when Is_Float => Text_Io.Put (File, ':' & Ascii.Ht); Float_Io.Put (File, Value.V_Float); Text_Io.New_Line (File); when Is_U_Char => Text_Io.Put (File, ':' & Ascii.Ht); Text_Io.Put_Line (File, U_Char'Image (Value.V_U_Char)); when Is_U_Short => Text_Io.Put (File, ':' & Ascii.Ht); Text_Io.Put_Line (File, U_Short'Image (Value.V_U_Short)); when Is_S_Char => Text_Io.Put (File, ':' & Ascii.Ht); Text_Io.Put_Line (File, S_Char'Image (Value.V_S_Char)); when Is_S_Short => Text_Io.Put (File, ':' & Ascii.Ht); Text_Io.Put_Line (File, S_Short'Image (Value.V_S_Short)); when Is_S_Long => Text_Io.Put (File, ':' & Ascii.Ht); Text_Io.Put_Line (File, S_Long'Image (Value.V_S_Long)); when Is_X_Initial_Window_State => Text_Io.Put (File, ':' & Ascii.Ht); Text_Io.Put_Line (File, X_Initial_Window_State'Image (Value.V_X_Initial_Window_State)); when Is_X_Time => Text_Io.Put (File, ':' & Ascii.Ht); Text_Io.Put_Line (File, X_Time'Image (Value.V_X_Time)); when Is_X_String7_Pointer => Text_Io.Put (File, '!'); Text_Io.Put (File, To_String (X_Rm_Representation_To_String (Representation))); Text_Io.Put (File, ':' & Ascii.Ht); declare Uca : U_Char_Array (1 .. Value.V_X_String7_Pointer'Length); begin To_Uca (Uca, Value.V_X_String7_Pointer.all); Dump_Bytes (File, Uca); end; when Is_X_String_Pointer => Text_Io.Put (File, '!'); Text_Io.Put (File, To_String (X_Rm_Representation_To_String (Representation))); Text_Io.Put (File, ':' & Ascii.Ht); declare Uca : U_Char_Array (1 .. Value.V_X_String_Pointer'Length); begin To_Uca (Uca, Value.V_X_String_Pointer.all); Dump_Bytes (File, Uca); end; when Is_X_String16_Pointer => Text_Io.Put (File, '!'); Text_Io.Put (File, To_String (X_Rm_Representation_To_String (Representation))); Text_Io.Put (File, ':' & Ascii.Ht); declare Uca : U_Char_Array (1 .. Value.V_X_String16_Pointer'Length); begin To_Uca (Uca, Value.V_X_String16_Pointer.all); Dump_Bytes (File, Uca); end; when Is_U_Char_List => Text_Io.Put (File, '!'); Text_Io.Put (File, To_String (X_Rm_Representation_To_String (Representation))); Text_Io.Put (File, ':' & Ascii.Ht); Dump_Bytes (File, Value.V_U_Char_List.all); end case; end Dump_Entry; --\f procedure Enum (Database : X_Rm_Hash_Bucket; Bindings : in out X_Rm_Binding_Array; Names : in out X_Rm_Name_Array; Count : S_Natural; File : Text_Io.File_Type) is ------------------------------------------------------------------------------ -- Bindings - Specifies a temp area to use for binding info -- Names - Specifies a temp area to use for names -- Count - Specifies where our caller was in the temp areas -- File - Specifies the output file to use -- -- Called to recursively traverse a database and to dump all entries in the -- database into the Ascii output file. ------------------------------------------------------------------------------ procedure Enum_Table (Binding : X_Rm_Binding) is ------------------------------------------------------------------------------ -- Binding - Specifies the type of binding to handle -- -- Called by Enum to handle the Tight or Loose binding for the current -- database entry. This is the guy that actually recurses via Enum calls. ------------------------------------------------------------------------------ Bucket : X_Rm_Hash_Bucket; Table : X_Rm_Hash_Table; begin ----Loop over all table entries. Then loop over the buckets in each entry. -- Recurse on each one we find. Table := Database.Tables (Binding); if Table /= null then Bindings (Count) := Binding; Names (Count + 1) := None_X_Rm_Name; for I in Table'Range loop Bucket := Table (I); while Bucket /= null loop Names (Count) := Bucket.Name; Enum (Bucket, Bindings, Names, Count + 1, File); Bucket := Bucket.Next; end loop; end loop; end if; end Enum_Table; begin ----Do nothing if this entry was null. if Database = null then return; end if; ----Handle longer bindings first; Tight and then Loose ones. Enum_Table (X_Rm_Bind_Tightly); Enum_Table (X_Rm_Bind_Loosely); ----Now handle the current binding. It has the shorter name of the three. Names (Count) := None_X_Rm_Name; if Database.Value /= null then Dump_Entry (Bindings, Names, Database.Value.Representation, Database.Value.Value, File); end if; end Enum; --\f procedure X_Rm_Put_File_Database (Filename : String; Database : X_Rm_Database; Status : out X_Rm_Status) is ------------------------------------------------------------------------------ -- Filename - Specifies the name of the file to write. -- Database - Specifies the database to write. -- Status - Receives Rm_Successful, Rm_Had_Errors, or Rm_Failed. -- -- Opens the specified file and writes the contents of the database in a -- form acceptable to X_Rm_Put_Line_Resource. -- -- Note: Files created by this X Library implementation may or may not be -- readable by other X Library implementations unless the resource values -- consist only of strings. -- -- Currently (Sep-1989) this implementation provides a superset of the -- functionality of the existing C X Library implementation. This means that -- this routine can read resource files created by other implementations and -- other implementations can read files created here as long as only string -- values are involved. Compatibility is a goal and as much compatibility as -- possible will be maintained in the future. ------------------------------------------------------------------------------ Bindings : X_Rm_Binding_Array (1 .. 100); Quarks : X_Rm_Name_Array (1 .. 100); File : Text_Io.File_Type; Lstatus : X_Rm_Status; begin Lstatus := Rm_Successful; -- Obtain a writable file handle begin Text_Io.Create (File, Text_Io.Out_File, Filename); exception when others => begin Text_Io.Open (File, Text_Io.Out_File, Filename); exception when others => Failed (Lstatus); Status := Lstatus; X_Report_Error ("XrmError", "FileCannotWrite", "Xrm; Cannot open file for writing: %1", To_X_String (Filename)); return; end; end; -- Do the work. if Database /= None_X_Rm_Database then Enum (Database.Contents, Bindings, Quarks, Bindings'First, File); end if; -- Close the handle. Text_Io.Close (File); Status := Lstatus; exception when others => begin Text_Io.Close (File); exception when others => null; end; Failed (Lstatus); Status := Lstatus; X_Report_Error ("XrmError", "FileWriteException", "Unexpected exception while writing file: %1", To_X_String (Filename)); end X_Rm_Put_File_Database; --\f procedure Merge (Neww : in out X_Rm_Hash_Bucket; Old : X_Rm_Hash_Bucket; Status : out X_Rm_Status) is -- Neww - Specifies the "new" data to be added to the "old" database -- Old - Specifies the "old" database to be updated -- -- Called to merge Neww data into an Old database. The Neww database is -- irrevocably lost (it all goes into the Old database). New_Table : X_Rm_Hash_Table; Old_Table : X_Rm_Hash_Table; Old_Bucket : X_Rm_Hash_Bucket; New_Bucket : X_Rm_Hash_Bucket; Next_New_Bucket : X_Rm_Hash_Bucket; Old_Search_Bucket : X_Rm_Hash_Bucket; begin ----Merge data in new into old, and destroy new in the process. --/ if DEBUG then if not (Neww /= null and then Old /= null) then raise X_Library_Confusion; end if; --/ end if; ----Merge neww value into old value. if Neww.Value /= null then if Old.Value /= null then Free_X_Rm_Entry (Old.Value); end if; Old.Value := Neww.Value; end if; ----Merge new hash tables into old hash tables. for Binding in X_Rm_Binding loop Neww.Has_Values (Binding) := Neww.Has_Values (Binding) or Old.Has_Values (Binding); Old_Table := Old.Tables (Binding); New_Table := Neww.Tables (Binding); if Old_Table = null then Old.Tables (Binding) := New_Table; elsif New_Table /= null then ----Copy each bucket over individually. for I in New_Table'Range loop Old_Bucket := Old_Table (I); New_Bucket := New_Table (I); ----Find each item in newBucket list in the oldBucket list. while New_Bucket /= null loop Next_New_Bucket := New_Bucket.Next; Old_Search_Bucket := Old_Bucket; ----Locate the Name in the list. while Old_Search_Bucket /= null and then Old_Search_Bucket.Name /= New_Bucket.Name loop Old_Search_Bucket := Old_Search_Bucket.Next; end loop; ---Name not found; just stick newBucket at head of old bucket list if Old_Search_Bucket = null then New_Bucket.Next := Old_Table (I); Old_Table (I) := New_Bucket; ----Name found; merge two buckets with the same Name. else Merge (New_Bucket, Old_Search_Bucket, Status); end if; New_Bucket := Next_New_Bucket; end loop; end loop; ----Deallocate this table. Free_X_Rm_Hash_Table (New_Table); end if; end loop; ----Deallocate this bucket. Free_X_Rm_Hash_Bucket (Neww); end Merge; --\f procedure X_Rm_Merge_Databases (Source : in out X_Rm_Database; Destination : X_Rm_Database; Status : out X_Rm_Status) is ------------------------------------------------------------------------------ -- Source - Specifies the source database. -- Destination - Specifies the target database. -- -- Merges the contents of one database into another. The original Source -- database is destroyed by this operation and the Destination database is modified -- by it. Values in the original Destination database that are replaced by new -- values of the same name and/or class will cause the old values to be -- updated. -- -- Note: This update will cause the old values to be freed if they are -- heap-based values and if the values were placed into the database with -- Db_Owner => TRUE (always the case if X_Rm_Get_@ or X_Rm_Add_@ routines -- were used to create the database). So beware of dangling pointers. -- For example, if Destination has a Foo value that is an X_Font_Struct pointer, -- and if Source replaces Foo with a new X_Font_Struct pointer, then the -- old X_Font_Struct will be freed. If the application has previously -- queried the database and has obtained the old value then continued usage -- of the old value by the application will eventually cause some sort of -- mysterious failure. ------------------------------------------------------------------------------ Newww : X_Rm_Hash_Bucket := Source.Contents; Destinationo : X_Rm_Hash_Bucket := Destination.Contents; begin Status := Rm_Successful; Source.Contents := null; Free_X_Rm_Database (Source); if Destinationo = null then Destination.Contents := Newww; elsif Newww /= null then Merge (Newww, Destinationo, Status); end if; end X_Rm_Merge_Databases; --\f procedure Init_Defaults (Display : X_Display) is ------------------------------------------------------------------------------ -- Display - Specifies the display to initialize -- -- Called when we want to initialize the default database of a display. -- We do the equivalent of reading in the Unix ~/.Xdefaults file. ------------------------------------------------------------------------------ Userdb : X_Rm_Database := None_X_Rm_Database; Xdb : X_Rm_Database := None_X_Rm_Database; Status : X_Rm_Status; Home : constant X_String := X_Env_Get_Home_Dir; Xdefaults_File : constant X_String := X_Env_Concat_Dir_File (Home, To_X_String (X_Option_File)); Xev : X_String_Pointer; begin ----First, get the defaults from the server; if there are none then load -- defaults from the local machine's equivalent of ~/.Xdefaults. Next, -- if there is an XENVIRONMENT variable (or local equivalent) then load that -- file. if Display.X_Defaults /= null then begin X_Rm_Get_String_Database (Display.X_Defaults.all, Xdb, Status); exception when others => X_Report_Exception ("XrmError", "DisplayDefaults", Xrm_Error_Display_Defaults, Display.Display_Name.all); X_Rm_Get_File_Database (To_String (Xdefaults_File), Xdb, Status); end; else X_Rm_Get_File_Database (To_String (Xdefaults_File), Xdb, Status); end if; ----If there was an error with the .Xdefaults file then report that here. if Status /= Rm_Successful then X_Report_Error ("XrmError", "ReadDb", Xrm_Error_Read_Db, Xdefaults_File); end if; ----If there is an XENVIRONMENT environment variable then read that file. -- If not then read the local equivalent to the ~/.Xdefaults-<host> file. Xev := X_Env_Get_Environment_Variable (To_X_String (X_Var_Xenvironment)); if Xev = None_X_String_Pointer or else Xev'Length = 0 then X_Rm_Get_File_Database (To_String (Home) & "." & X_Environment_File & To_String (X_Env_Get_Host_Name), Userdb, Status); if Status /= Rm_Successful then X_Report_Error ("XrmError", "ReadDb", Xrm_Error_Read_Db, Home & "." & To_X_String (X_Environment_File) & X_Env_Get_Host_Name); end if; else X_Rm_Get_File_Database (To_String (Xev.all), Userdb, Status); if Status /= Rm_Successful then X_Report_Error ("XrmError", "ReadDb", Xrm_Error_Read_Db, Xev.all); end if; end if; Free_X_String_Pointer (Xev); ----Now merge the two databases together and use the result. If there is -- no result then simply create a new and totally empty database record. if Userdb /= None_X_Rm_Database then X_Rm_Merge_Databases (Userdb, Xdb, Status); end if; if Xdb /= None_X_Rm_Database then Display.Database := Univ_X_Rm_Database.To_X_Universal_Pointer (Xdb); else Display.Database := Univ_X_Rm_Database.To_X_Universal_Pointer (new X_Rm_Database_Rec); end if; end Init_Defaults; --\f procedure X_Get_Default (Display : X_Display; Program : X_String; Option : X_String; Representation : out X_Rm_Representation; Value : out X_Rm_Value) is ------------------------------------------------------------------------------ -- Display - Specifies the display to use. -- Program - Specifies the program name for the X_Library defaults. -- The name must be passed in with the program arguments -- (usually as ArgV[ArgV'First]). -- Option - Specifies the option name. -- Representation - Receives the representation of the value; -- None_X_Rm_Representation if the option is not in the -- database -- Value - Receives the value of the option if Type /= None -- -- Returns the representation and the value for the specified option. ------------------------------------------------------------------------------ Db_Entry : X_Rm_Entry; Namelist : X_Rm_Name_Array (1 .. 5); Classlist : X_Rm_Class_Array (1 .. 5); Simple_Name : constant X_String := X_Env_Strip_Program_Name (Program); begin ----See if database has ever been initialized. Lookups can be done -- without locks. Lock_Display (Display); begin if Display.Database = None_X_Universal_Pointer then Init_Defaults (Display); end if; exception when others => X_Report_Exception ("XrmError", "DisplayDefaults", Xrm_Error_Display_Defaults, Display.Display_Name.all); end; Unlock_Display (Display); ----See if there is a database to work with. if Display.Database = None_X_Universal_Pointer then Representation := None_X_Rm_Representation; Value := None_X_Rm_Value; return; end if; ----Get the name/class lists to use for the lookup. X_Rm_String_To_Name_List (Simple_Name & '.' & Option, Namelist); X_Rm_String_To_Class_List ("Program.Name", Classlist); ----Do the lookup and return any result. X_Rm_Get_Resource (Univ_X_Rm_Database.From_X_Universal_Pointer (Display.Database), Namelist, Classlist, Representation, Value); end X_Get_Default; --\f procedure X_Rm_Parse_Command (Database : in out X_Rm_Database; Options : X_Rm_Option_Desc_Array; Prefix : X_String; Arg_C : in out S_Natural; Arg_V : in out X_String_Pointer_Array; Status : out X_Rm_Status) is ------------------------------------------------------------------------------ -- Database - Specifies the database to use for the parsing. -- Table - Specifies a table of command line argument data. -- Prepend_Name - Specifies the application name -- Arg_C - Specifies the number of argument strings. -- Arg_V - Specifies the argument strings and returns those not -- used. -- -- Parse command line and store argument values into resource database. -- Allows any unambiguous abbreviation for an option name, but requires -- that the table be ordered with any options that are prefixes of -- other options appearing before the longer option name. ------------------------------------------------------------------------------ type Sort_State is (Dont_Care, Check, Not_Sorted, Sorted); Found_Option : S_Natural; Arg_Save : S_Natural; Arg_Vi : S_Natural := Arg_V'First; Myargc : S_Natural; I : S_Natural; Bindings : X_Rm_Binding_Array (1 .. 100); Names : X_Rm_Name_Array (1 .. 100); Start_Bindings : S_Natural; Start_Names : S_Natural; Arg_P : S_Natural; Matches : S_Natural; Table_Is_Sorted : Sort_State; Arg_Str : X_String_Pointer; Opt_Str : X_String_Pointer; Lstatus : X_Rm_Status; Tstatus : X_Rm_Status; procedure Put_Command_Resource (Value_Str : X_String) is begin X_Rm_String_To_Binding_Name_List (Options (I).Specifier.all, Bindings (Start_Bindings .. Bindings'Last), Names (Start_Names .. Names'First)); X_Rm_Put_String_Resource (Database, Bindings, Names, Value_Str); end Put_Command_Resource; begin Lstatus := Rm_Successful; Myargc := Arg_C; Arg_Vi := Arg_Vi + 1; Arg_Save := Arg_Vi; ----Parse prefix into bindings and name list. X_Rm_String_To_Binding_Name_List (Prefix, Bindings, Names); Start_Bindings := Bindings'First; Start_Names := Names'First; while Names (Start_Names) /= None_X_Rm_Name loop Start_Bindings := Start_Bindings + 1; Start_Names := Start_Names + 1; end loop; if Myargc > 2 then Table_Is_Sorted := Check; else Table_Is_Sorted := Dont_Care; end if; Myargc := Myargc - 1; while Myargc > 0 loop Found_Option := 0; Matches := 0; I := 0; while I < Options'Length loop Arg_Str := Arg_V (Arg_Vi); Opt_Str := Options (Options'First + I).Option; declare Arg_String : constant X_String := Arg_Str.all; Opt_String : constant X_String := Opt_Str.all; begin -- Checking the sort order first insures we don't have to -- redo the check if the arg hits on the last entry in the -- table. Useful because usually '=' is the last entry and -- users frequently specify geometry early in the command. if Table_Is_Sorted = Check and then I > 0 and then Opt_String < Options (Options'First + I - 1). Option.all then Table_Is_Sorted := Not_Sorted; end if; if Arg_String = Opt_String then -- Exact match found Matches := 1; Found_Option := I; exit; elsif Arg_String'Length > Opt_String'Length then -- give preference to stick_arg and is_arg if Arg_String (Arg_String'First .. Arg_String'First - 1 + Opt_String'Length) = Opt_String then if Options (I).Arg_Kind = X_Rm_Option_Sticky_Arg or else Options (I).Arg_Kind = X_Rm_Option_Is_Arg then Arg_P := Arg_String'First + Opt_String'Length; Matches := 1; Found_Option := I; exit; end if; end if; elsif Arg_String = Opt_String (Opt_String'First .. Opt_String'First - 1 + Arg_String'Length) then -- may be an abbreviation for this option Matches := Matches + 1; Found_Option := I; elsif Table_Is_Sorted = Sorted and then Opt_String > Arg_String then exit; end if; end; I := I + 1; end loop; if Table_Is_Sorted = Check and then I >= Options'Length - 1 then Table_Is_Sorted := Sorted; end if; if Matches = 1 then I := Found_Option; case Options (I).Arg_Kind is when X_Rm_Option_No_Arg => Arg_C := Arg_C - 1; Put_Command_Resource (Options (I).Value.all); when X_Rm_Option_Is_Arg => Arg_C := Arg_C - 1; Put_Command_Resource (Arg_V (Arg_Vi).all); when X_Rm_Option_Sticky_Arg => Arg_C := Arg_C - 1; declare Av : constant X_String := Arg_V (Arg_Vi).all; begin Put_Command_Resource (Av (Arg_P .. Av'Last)); end; when X_Rm_Option_Sep_Arg => if Myargc >= 2 then Arg_C := Arg_C - 2; Arg_Vi := Arg_Vi + 1; Myargc := Myargc - 1; Put_Command_Resource (Arg_V (Arg_Vi).all); else Arg_V (Arg_Save) := Arg_V (Arg_Vi); Arg_Save := Arg_Save + 1; end if; when X_Rm_Option_Res_Arg => if Myargc >= 2 then Arg_Vi := Arg_Vi + 1; Myargc := Myargc - 1; Arg_C := Arg_C - 2; X_Rm_Put_Line_Resource (Database, Arg_V (Arg_Vi).all, Tstatus); if Tstatus > Lstatus then Lstatus := Tstatus; end if; if Tstatus /= Rm_Successful then Had_Errors (Lstatus); X_Report_Error ("XrmError", "CmdBadLine", Xrm_Error_Cmd_Bad_Line, Options (I).Option.all, Options (I).Specifier.all, Arg_V (Arg_Vi).all); end if; else Arg_V (Arg_Save) := Arg_V (Arg_Vi); Arg_Save := Arg_Save + 1; end if; when X_Rm_Option_Skip_Arg => if Myargc > 1 then Myargc := Myargc - 1; Arg_V (Arg_Save) := Arg_V (Arg_Vi); Arg_Save := Arg_Save - 1; Arg_Vi := Arg_Vi + 1; end if; Arg_V (Arg_Save) := Arg_V (Arg_Vi); Arg_Save := Arg_Save + 1; when X_Rm_Option_Skip_Line => while Myargc > 0 loop Arg_V (Arg_Save) := Arg_V (Arg_Vi); Arg_Save := Arg_Save + 1; Arg_Vi := Arg_Vi + 1; Myargc := Myargc - 1; end loop; when X_Rm_Option_Skip_N_Args => declare J : S_Natural; begin J := 1 + S_Long'Value (To_String (Options (I).Value.all)); if J > Myargc then J := Myargc; end if; Arg_V (Arg_Save) := Arg_V (Arg_Vi); Arg_Save := Arg_Save + 1; while J > 1 loop J := J - 1; Arg_V (Arg_Save) := Arg_V (Arg_Vi); Arg_Save := Arg_Save + 1; Arg_Vi := Arg_Vi + 1; Myargc := Myargc - 1; end loop; exception when Constraint_Error => Had_Errors (Lstatus); X_Report_Error ("XrmError", "CmdBadDefault", Xrm_Error_Cmd_Bad_Default, Options (I).Option.all, Options (I).Specifier.all); end; when others => Failed (Lstatus); X_Report_Error ("XrmError", "CmdBadKind", Xrm_Error_Cmd_Bad_Kind, Options (I).Option.all, Options (I).Specifier.all); end case; else Arg_V (Arg_Save) := Arg_V (Arg_Vi); --compress arg_list Arg_Save := Arg_Save + 1; end if; Myargc := Myargc - 1; Arg_Vi := Arg_Vi + 1; end loop; if Arg_Save <= Arg_V'Last then Arg_V (Arg_Save) := None_X_String_Pointer; -- put NULL terminator on compressed arg_v end if; Status := Lstatus; end X_Rm_Parse_Command; --\f procedure Free_X_Rm_Hash_Bucket (Hb : in out X_Rm_Hash_Bucket) is begin if Hb.Value /= null then Free_X_Rm_Entry (Hb.Value); end if; for I in Hb.Tables'Range loop if Hb.Tables (I) /= null then for J in Hb.Tables (I)'Range loop if Hb.Tables (I) (J) /= null then Free_X_Rm_Hash_Bucket (Hb.Tables (I) (J)); end if; end loop; Heap_Free_X_Rm_Hash_Table (Hb.Tables (I)); end if; end loop; if Hb.Next /= null then Free_X_Rm_Hash_Bucket (Hb.Next); end if; Heap_Free_X_Rm_Hash_Bucket (Hb); end Free_X_Rm_Hash_Bucket; --\f procedure Free_X_Rm_Database (Database : in out X_Rm_Database) is begin if Database /= null then if Database.Contents /= null then Free_X_Rm_Hash_Bucket (Database.Contents); end if; Heap_Free_X_Rm_Database (Database); end if; end Free_X_Rm_Database; --\f end Xlbp_Rm;