1 Introduction
2 Ground Rules
Building a File System
3 File Systems
4 File Content Data Structure
5 Allocation Cluster Manager
6 Exceptions and Emancipation
7 Base Classes, Testing, and More
8 File Meta Data
9 Native File Class
10 Our File System
11 Allocation Table
12 File System Support Code
13 Initializing the File System
14 Contiguous Files
15 Rebuilding the File System
16 Native File System Support Methods
17 Lookups, Wildcards, and Unicode, Oh My
18 Finishing the File System Class
The Init Program
19 Hardware Abstraction and UOS Architecture
20 Init Command Mode
21 Using Our File System
22 Hardware and Device Lists
23 Fun with Stores: Partitions
24 Fun with Stores: RAID
25 Fun with Stores: RAM Disks
26 Init wrap-up
The Executive
27 Overview of The Executive
28 Starting the Kernel
29 The Kernel
30 Making a Store Bootable
31 The MMC
32 The HMC
33 Loading the components
34 Using the File Processor
35 Symbols and the SSC
36 The File Processor and Device Management
37 The File Processor and File System Management
38 Finishing Executive Startup
Users and Security
39 Introduction to Users and Security
40 More Fun With Stores: File Heaps
41 File Heaps, part 2
42 SysUAF
43 TUser
44 SysUAF API
Terminal I/O
45 Shells and UCL
46 UOS API, the Application Side
47 UOS API, the Executive Side
48 I/O Devices
49 Streams
50 Terminal Output Filters
51 The TTerminal Class
52 Handles
53 Putting it All Together
54 Getting Terminal Input
55 QIO
56 Cooking Terminal Input
57 Putting it all together, part 2
58 Quotas and I/O
UCL
59 UCL Basics
60 Symbol Substitution
61 Command execution
62 Command execution, part 2
63 Command Abbreviation
64 ASTs
65 Expressions, Part 1
66 Expressions, Part 2: Support code
67 Expressions, part 3: Parsing
68 SYS_GETJPIW and SYS_TRNLNM
69 Expressions, part 4: Evaluation
UCL Lexical Functions
70 PROCESS_SCAN
71 PROCESS_SCAN, Part 2
72 TProcess updates
73 Unicode revisted
74 Lexical functions: F$CONTEXT
75 Lexical functions: F$PID
76 Lexical Functions: F$CUNITS
77 Lexical Functions: F$CVSI and F$CVUI
78 UOS Date and Time Formatting
79 Lexical Functions: F$CVTIME
80 LIB_CVTIME
81 Date/Time Contexts
82 SYS_GETTIM, LIB_Get_Timestamp, SYS_ASCTIM, and LIB_SYS_ASCTIM
83 Lexical Functions: F$DELTA_TIME
84 Lexical functions: F$DEVICE
85 SYS_DEVICE_SCAN
86 Lexical functions: F$DIRECTORY
87 Lexical functions: F$EDIT and F$ELEMENT
88 Lexical functions: F$ENVIRONMENT
89 SYS_GETUAI
90 Lexical functions: F$EXTRACT and F$IDENTIFIER
91 LIB_FAO and LIB_FAOL
92 LIB_FAO and LIB_FAOL, part 2
93 Lexical functions: F$FAO
94 File Processing Structures
95 Lexical functions: F$FILE_ATTRIBUTES
96 SYS_DISPLAY
97 Lexical functions: F$GETDVI
98 Parse_GetDVI
99 GetDVI
100 GetDVI, part 2
101 GetDVI, part 3
102 Lexical functions: F$GETJPI
103 GETJPI
104 Lexical functions: F$GETSYI
105 GETSYI
106 Lexical functions: F$INTEGER, F$LENGTH, F$LOCATE, and F$MATCH_WILD
107 Lexical function: F$PARSE
108 FILESCAN
109 SYS_PARSE
110 Lexical Functions: F$MODE, F$PRIVILEGE, and F$PROCESS
111 File Lookup Service
112 Lexical Functions: F$SEARCH
113 SYS_SEARCH
114 F$SETPRV and SYS_SETPRV
115 Lexical Functions: F$STRING, F$TIME, and F$TYPE
116 More on symbols
117 Lexical Functions: F$TRNLNM
118 SYS_TRNLNM, Part 2
119 Lexical functions: F$UNIQUE, F$USER, and F$VERIFY
120 Lexical functions: F$MESSAGE
121 TUOS_File_Wrapper
122 OPEN, CLOSE, and READ system services
UCL Commands
123 WRITE
124 Symbol assignment
125 The @ command
126 @ and EXIT
127 CRELNT system service
128 DELLNT system service
129 IF...THEN...ELSE
130 Comments, labels, and GOTO
131 GOSUB and RETURN
132 CALL, SUBROUTINE, and ENDSUBROUTINE
133 ON, SET {NO}ON, and error handling
134 INQUIRE
135 SYS_WRITE Service
136 OPEN
137 CLOSE
138 DELLNM system service
139 READ
140 Command Recall
141 RECALL
142 RUN
143 LIB_RUN
144 The Data Stream Interface
145 Preparing for execution
146 EOJ and LOGOUT
147 SYS_DELPROC and LIB_GET_FOREIGN
CUSPs and utilities
148 The I/O Queue
149 Timers
150 Logging in, part one
151 Logging in, part 2
152 System configuration
153 SET NODE utility
154 UUI
155 SETTERM utility
156 SETTERM utility, part 2
157 SETTERM utility, part 3
158 AUTHORIZE utility
159 AUTHORIZE utility, UI
160 AUTHORIZE utility, Access Restrictions
161 AUTHORIZE utility, Part 4
162 AUTHORIZE utility, Reporting
163 AUTHORIZE utility, Part 6
164 Authentication
165 Hashlib
166 Authenticate, Part 7
167 Logging in, part 3
168 DAY_OF_WEEK, CVT_FROM_INTERNAL_TIME, and SPAWN
169 DAY_OF_WEEK and CVT_FROM_INTERNAL_TIME
170 LIB_SPAWN
171 CREPRC
172 CREPRC, Part 2
173 COPY
174 COPY, part 2
175 COPY, part 3
176 COPY, part 4
177 LIB_Get_Default_File_Protection and LIB_Substitute_Wildcards
178 CREATESTREAM, STREAMNAME, and Set_Contiguous
179 Help Files
180 LBR Services
181 LBR Services, Part 2
182 LIBRARY utility
183 LIBRARY utility, Part 2
184 FS Services
185 FS Services, Part 2
186 Implementing Help
187 HELP
188 HELP, Part 2
189 DMG_Get_Key and LIB_Put_Formatted_Output
190 LIBRARY utility, Part 3
191 Shutting Down UOS
192 SHUTDOWN
193 WAIT
194 SETIMR
195 WAITFR and Scheduling
196 REPLY, OPCOM, and Mailboxes
197 REPLY utility
198 Mailboxes
199 BRKTHRU
200 OPCOM
201 Mailbox Services
202 Mailboxes, Part 2
203 DEFINE
204 CRELNM
205 DISABLE
206 STOP
207 OPCCRASH and SHUTDOWN
208 APPEND
Glossary/Index
Downloads
|
CRELNT system service
Let's briefly review how symbol tables work in UOS. As we've discussed in the distant past, DCL uses local symbol tables and VMS only
supports logical name tables. In UOS, we've combined these into system symbol tables.
So, though we use the VMS system services names (such as CRELNT for Create Logical
Name Table), remember that logical names are equivalent to symbol names in UOS. Logical
name tables are equivalent to symbol name tables in UOS.
We have used the LN_* constants to
reference the process/job/system/cluster symbol table names. That was fine for our uses
up to this point, but with the need to dynamically create and delete tables, we need
a means of refer to specific tables within the process name space. Thus, we have
modified the code to handle actual table names, as does VMS. We translate the LN_* codes to actual
names. For instance, LN_PROCESS is converted to "lnm$process". There is little change
to behavior since lnm$process will almost always refer to the default process symbol
table, no matter what the table's actual name is. However, there are certain cases,
where we will need to look up the table that is literally named "lnm$process". I'll
point those out when appropriate. Allowing tables by names means that we can create
as many different concurrent tables as we need.
To keep track of the burgeoning number of potential tables, the tables are gathered
into parent "tables", which are implemented as lists. but these tables also need
names. For instance, the "lnm$process_directory" table is the collection of all process-private
tables. It is necessary to refer to these parent tables to indicate where a table
to be created/deleted is located, so we will use these parent table names in our code.
CRELNT is the system service that we use to create a table. Here is the documentation:
CRELNT
Creates a process-private or public-sharable symbol table.
Format
SYS_CRELNT( Attr, Resnam, Reslen, Quota, Promsk, Tabnam, Partab, AcMode )
Arguments
Attr
Address of the attribute flags to apply to the newly created table. If the address
is 0 or points to an integer 0 value, no flags are associated with the table. The
valid flags are:
Flag | Description |
LNM_M_CREATE_IF | Create the table only if it does not exist. If not
specified, and a table with the same name and access mode already exists, it is deleted
and a new table is created. |
LNM_M_MAKE_DEFAULT | Applies only to process-private tables. If specified,
the new table is made the new default (lnm$process). |
Resnam
Address where to write the new table name. If 0, the name isn't written.
Reslen
Address of a 64-bit integer to receive the length of the result name.
Quota
Quota for the new table, indicating the limit of the table contents in bytes. 0 indicates
no quota.
Promsk
Protection mask for the new table.
Tabnam
Address of a TSRB structure specifying the new table name. If this is 0 or points
to a TSRB of a null string, the table name is created by UOS with a unique name of
"lnm$x" where "x" is a hexadecimal value.
Partab
Address of a TSRB structure specifying the new table's parent. If this is "lnm$process_directory"
the new table will be private to the current process.
AcMode
Address of the access mode of the new table. If not specified (the address is 0),
the process' current access mode is used.
Description
This system service creates a symbol table. The SYSNAM privilege is required to create
a table at an access mode more privileged than that of the calling process.
Condition Codes
Code | Meaning |
SS_NORMAL | Normal completion |
SS_ACCVIO | Could not access the memory associated with one of the parameters |
SS_BADPARAM | An invalid parameter was passed |
SS_NOLOGTAB | The specified parent table wasn't found |
SS_NOPRIV | User lacked the privilege to perform the operation |
SS_RESULTOVF | The table was created, but the result name location was specified and was too small to contain the full name |
SS_SUPERSEDE | Normal completion. Existing table was superseded by the new table |
function CRELNT( Attr : int64 ; var Resnam : string ; Quota : int64 ;
Promsk : int64 ; Tabnam, Partab : string ;
AcMode : int64 ) : int64 ;
var S_Resname, S_Tabnam, S_Partab : TSRB ;
Reslen : int64 ;
begin
Resnam := ' ' ;
Set_String( Resnam, S_Resname ) ;
Set_String( Tabnam, S_Tabnam ) ;
Set_String( Partab, S_Partab ) ;
Result := SYS_CRELNT( int64( @Attr ), int64( @S_Resname ), int64( @Reslen ),
int64( @Quota ), int64( @Promsk ), int64( @S_Tabnam ), int64( @S_Partab ),
int64( @AcMode ) ) ;
setlength( Resnam, Reslen ) ;
end ;
This new routine is added to PasStarlet. It provides a Pascal interface that wraps
the strings into SRBs, calls the executive, and sets the result string.
function SYS_CRELNT( Attr, Resnam, Reslen, Quota, Promsk, Tabnam, Partab,
AcMode : int64 ) : int64 ;
var SysRequest : TS3I5_Request ;
Resname_SRB : PSRB ;
Tabnam_SRB : PSRB ;
Partab_SRB : PSRB ;
begin
Resname_SRB := PSRB( pointer( Resnam ) ) ;
Tabnam_SRB := PSRB( pointer( Tabnam ) ) ;
Partab_SRB := PSRB( pointer( Partab ) ) ;
SysRequest.Request.Subsystem := UOS_Subsystem_SSC ;
SysRequest.Request.Request := UOS_SSC_CRELNT ;
SysRequest.Request.Length := sizeof( SysRequest ) - sizeof( Sysrequest.Request ) ;
SysRequest.Request.Status := 0 ;
SysRequest.SRB1.Buffer := Resname_SRB.Buffer ;
SysRequest.SRB2.Buffer := Tabnam_SRB.Buffer ;
SysRequest.SRB3.Buffer := Partab_SRB.Buffer ;
SysRequest.SRB1.Length := Resname_SRB.Length ;
SysRequest.SRB2.Length := Tabnam_SRB.Length ;
SysRequest.SRB3.Length := Partab_SRB.Length ;
SysRequest.Integer1 := Attr ;
SysRequest.Integer2 := Reslen ;
SysRequest.Integer3 := Quota ;
SysRequest.Integer4 := Promsk ;
SysRequest.Integer5 := AcMode ;
Call_To_Ring0( integer( @SysRequest ) ) ;
Result := SysRequest.Request.Status ;
end ;
This is the new routine in the SYS unit. It is standard fare for system service wrappers.
UOS_SSC_CRELNT:
begin
UE := Enter_System_Call( Request, SReq, PID, MMC,
sizeof( TS3I5_Request ) - sizeof( TSystem_Request ), Base ) ;
if( UE <> nil ) then
begin
Set_Last_Error( UE ) ;
exit ;
end ;
try
PS3I5 := PS3I5_Request( Base ) ;
PS3I5.Request.Status := CRELNT( Kernel, PID, PS3I5.SRB2, PS3I5.SRB3,
PS3I5.SRB1, PS3I5.Integer2, PS3I5.Integer1,
PS3I5.Integer3, PS3I5.Integer4, PS3I5.Integer5 ) ;
finally
Exit_System_Call( Base, PID, MMC, sizeof( TS3I5_Request ) ) ;
end ;
end ;
This code is added to the SSC's API method to handle the CRELNT call.
function TUOS_SSC.CRELNT( Kernel : TUOS_Kernel ; PID : TPID ;
TableName, ParentName, Return_Name : TSRB ;
Reslen, _Attr, _Quota, _Promsk, _AcMode : int64 ) : int64 ;
var Table_Name, Parent_Name : string ;
Status : integer ;
Attr, Quota, Promsk, AcMode : int64 ;
US : TUOS_String ;
begin
// Get the values and validate...
if( ParentName.Buffer = 0 ) then
begin
Result := SS_BADPARAM ;
exit ;
end ;
US := Get_User_String( Kernel, PID, TableName, Status ) ;
if( Status = UE_Error ) then
begin
Result := Status ;
exit ;
end ;
Table_Name := trim( US.Contents ) ;
This new method starts by obtaining the parent table name and the new table name,
exiting if there was an error.
US := Get_User_String( Kernel, PID, ParentName, Status ) ;
if( Status = UE_Error ) then
begin
Result := Status ;
exit ;
end ;
Parent_Name := US.Contents ;
Attr := Get_User_Integer( Kernel, PID, _Attr, Status ) ;
if( Status = UE_Error ) then // Error reading user value
begin
Result := Status ;
exit ;
end ;
Quota := Get_User_Integer( Kernel, PID, _Quota, Status ) ;
if( Status = UE_Error ) then // Error reading user value
begin
Result := Status ;
exit ;
end ;
Promsk := Get_User_Integer( Kernel, PID, _Promsk, Status ) ;
if( Status = UE_Error ) then // Error reading user value
begin
Result := Status ;
exit ;
end ;
if( _AcMode = 0 ) then
begin
_AcMode := USC.Access_Mode( PID ) ;
end else
begin
AcMode := Get_User_Integer( Kernel, PID, _AcMode, Status ) ;
if( Status = UE_Error ) then // Error reading user value
begin
Result := Status ;
exit ;
end ;
if( ( AcMode > 3 ) or ( AcMode < 0 ) ) then
begin
Result := SS_BADPARAM ;
exit ;
end ;
if( AcMode < USC.Access_Mode( PID ) ) then
begin
AcMode := USC.Access_Mode( PID ) ;
end ;
end ;
if( ( PID <> 0 ) and ( AcMode < PSL_C_SUPER ) ) then
begin
if( ( USC.Get_Process_Info( PID, JPI_CURPRIV ) and SYSNAM ) = 0 ) then
begin
Result := SS_NOPRIV ; // User must have SYSNAM to use KERNEL or EXECUTIVE access mode
exit ;
end ;
end ;
Next we get the rest of the parameters. If the access mode address is not provided (0),
we default to the process' access mode. Otherwise we get the value from the user's
memory, exiting with an error if that fails. If the accessmode is not in the range
of 0 to 3, inclusive, we exit with an error. Finally, we maximize the current
process' access mode with the requested access mode. That is, the outermost value of
the two is what the access mode will be set to. However, if we are setting the access
mode to Kernel or Executive, the process must have the SYSNAM privilege or we exit
with an error.
// Create the table...
Result := Create_Symbol_Table( Kernel, PID, Table_Name, Parent_Name, Return_Name,
Reslen, Attr, Quota, Promsk, AcMode ) ;
if( ( Result <> 0 ) and ( Result <> SS_SUPERSEDE ) ) then
begin
Generate_Exception( Result ) ;
end ;
end ; // TUOS_SSC.CRELNT
Finally we call the Create_Symbol_Table function to create the table,
and if the result is an error, we generate an exception.
function TUOS_SSC.Create_Symbol_Table( Kernel : TUOS_Kernel ; PID : TPID ;
var Table_Name, Parent_Name : string ;
Return_Name : TSRB ; Reslen : int64 ;
Attr, Quota, Promsk, AcMode : int64 ) : int64 ;
var I : integer ;
Table : TUOS_Symbol_Table ;
begin
// Setup...
Result := 0 ;
Table_Name := trim( lowercase( Table_Name ) ) ;
Parent_Name := trim( lowercase( Parent_Name ) ) ;
// Default the name...
if( Table_Name = '' ) then
begin
Table_Name := 'lnm$' + lowercase( CVTB( 10, 16, inttostr( LIB_Get_Timestamp ) ) ) ;
end ;
This method does the actual creation of the table. If the table name is null or not
provided, we default to a name starting with "LNM$" followed by a unique hexadecimal
value. To get a unique value, we use the current timestamp. This doesn't guarantee
that there are no conflicts with existing table names, just that this routine will
never generate two identical table names.
// Create table...
if( Parent_Name = 'lnm$process_directory' ) then
begin
if( PID = 0 ) then
begin
Result := SS_NOLOGTAB ;
exit ;
end ;
Table := USC.Resolve_Table( PChar( Table_Name ), PID, nil, True, 3 ) ;
if( Table <> nil ) then // Table exists
begin
if( ( Attr and LNM_M_CREATE_IF ) <> 0 ) then // Create if not exists
begin
Result := 0 ;
exit ;
end ;
Table.Clear ;
Result := SS_SUPERSEDE ;
end else
begin
Table := USC.Create_Symbol_Table( Kernel, PID, PChar( Table_Name ),
Attr, Quota, Promsk, AcMode ) ; // Create table
end ;
end else
If the name of the parent table is "lnm$process_directory", we first see if the table
already exists. If so, we check for the presence of the LNM_M_CREATE_IF
flag. If set, we exit with no error. If not set, we clear the existing table - essentially
recreating it. If the table doesn't already exist, we pass the request on to the
USC component.
if( Parent_Name = 'lnm$system_directory' ) then
begin
if( ( PID <> 0 )
and
not USC.Validate_Protection( PID, FAB_V_UPD, 0,
PROTECTION_SYSTEM_WRITE or PROTECTION_SYSTEM_DELETE, 0 )
) then
begin
Result := SS_NOPRIV ;
exit ;
end ;
I := Symbols.IndexOf( Table_Name ) ;
if( I <> -1 ) then // Table already exists
begin
if( ( Attr and LNM_M_CREATE_IF ) <> 0 ) then // Create if not exists
begin
Result := 0 ;
exit ;
end ;
Table := TUOS_Symbol_Table( Symbols.Objects[ I ] ) ;
Table.Clear ;
Result := SS_SUPERSEDE ;
end else
begin
Symbols.AddObject( Table_Name, TSymbol_Table.Create( Table_Name ) ) ;
end ;
end else
If the parent table is "lnm$system_directory", we check the protection. For the moment,
we are hardcoding the protection to PROTECTION_SYSTEM_WRITE or PROTECTION_SYSTEM_DELETE .
If the user doesn't have the necessary privileges, we exit with an error. Otherwise,
we check the flags as above, again clearing the table or exiting. If the table doesn't
exist, we construct a new TSymbol_Table instance and add it to lnm$system_directory.
begin
Result := SS_NOLOGTAB ;
exit ;
end ;
// Return table name...
if( Return_Name.Buffer <> 0 ) then
begin
Table_Name := Table.Name ;
if( Return_Name.Length < length( Table_Name ) ) then
begin
setlength( Table_Name, Return_Name.Length ) ;
Result := SS_RESULTOVF ;
end ;
Set_User_String( Kernel, PID, Return_Name, Table_Name ) ;
if( ResLen <> 0 ) then
begin
Write_User_int64( Kernel, PID, ResLen, length( Table_Name ) ) ;
end ;
end ; // if( Return_Name.Buffer <> 0 )
end ; // TUOS_SSC.Create_Symbol_Table
If the parent table is not one we recognize, we exit with the SS_NOLOGTAB error. Otherwise,
we fall through to the code to return the table name. Because the table name may
have been created by this routine (if null was passed for the table name), the calling code
will want to know the name we assigned to it. So, if a return name address was supplied,
we first check to see if the size of the buffer is large enough to hold the name.
If not, we set the return error code to SS_RESULTOVF and trim the length of the table
name to write (the actual table name is not truncated - only the value we write back).
If a result length is specified, we then write the value of the (truncated) string
length to that address.
function TUSC.Create_Symbol_Table( Kernel : TUOS_Kernel ; PID : TPID ;
Table_Name : PChar ; Attr, Quota, Promsk, AcMode : int64 ) : TUOS_Symbol_Table ;
var Process : TProcess ;
T : string ;
begin
Result := nil ;
Process := Get_Process( PID ) ;
if( Process = nil ) then // Process doesn't exist
begin
Generate_Exception( UOSErr_Nonexistent_Process ) ;
exit ;
end ;
T := lowercase( trim( string( Table_Name ) ) ) ;
if( T = '' ) then
begin
exit ;
end ;
if( Process.Symbol_Table_Directory.Indexof( T ) <> -1 ) then
begin
exit ;
end ;
Result := TSymbol_Table.Create( T ) ;
Process.Symbol_Table_Directory.AddObject( T, Result ) ;
if( ( Attr and LNM_M_MAKE_DEFAULT ) <> 0 ) then
begin
Process._Symbols := Result ;
end ;
end ; // TUSC.Create_Symbol_Table
This new method of the USC first gets the specified process - exiting with an error
if the process doesn't exist. We also exit if a null table name is passed or the
parent table doesn't exist. The calling code checks for validity, so here we don't
bother returning those error codes. Otherwise, we create a symbol table and tell the process to add the table list.
If the LNM_M_MAKE_DEFAULT flag is set, we point the default table to
the new table.
function TProcess.Create_Symbol_Table( Name : string ) : int64 ;
var ST : TSymbol_Table ;
begin
Name := uppercase( Name ) ;
if( Symbol_Table_Directory.Indexof( Name ) <> -1 ) then // Already exists
begin
exit ;
end ;
Result := 0 ;
ST := TSymbol_Table.Create( Name ) ;
Symbol_Table_Directory.AddObject( ST.Name, ST ) ;
end ;
This new TProcess method creates the table and adds it to the table directory
list. However, if a table with the same name exists, we exit.
function TUSC.Resolve_Table( LNTable : PChar ; PID : TPID ; N : PChar ;
CS : boolean ; Access : integer ) : TUOS_Symbol_Table ;
var I : integer ;
Process : TProcess ;
Name, Table : string ;
begin
Result := nil ;
Process := Get_Process( PID ) ;
if( Process = nil ) then
begin
exit ;
end ;
Table := LNTable ;
Name := N ;
This method is used to return a table instance given a table name. If no table name
is specified, we return the table in which we find the specified symbol. In that
case, we first check the current lnm$process table, and then the lnm$job table (which
is the default table for the top-level job process). The first thing we do is get
the specified process and exit if it doesn't exist.
if( Table = 'lnm$process' ) then
begin
Result := Process._Symbols ;
exit ;
end ;
if( Table = 'lnm$job' ) then
begin
Result := Process._Job_Symbols ;
exit ;
end ;
if( Table = '' ) then
begin
Result := Process._Symbols ;
if( Result.Exists( N, CS, Access ) ) then
begin
exit ;
end ;
Result := nil ;
if( Process._Job_Symbols <> nil ) then
begin
Result := Process._Job_Symbols ;
if( Result.Exists( N, CS, Access ) ) then
begin
exit ;
end ;
Result := nil ;
end ;
end ; // if( Table = '' )
In the case of lnm$process and lnm$job, we return the appropriate table. If no table
is specified we first check for the passed symbol in the process default table. If
not there, we likewise check the job table. If the symbol is found, we return
the table instance that contains the symbol.
for I := 0 to Process.Symbol_Table_Directory.Count - 1 do
begin
if( Table = Process.Symbol_Table_Directory[ I ] ) then
begin
Result := TUOS_Symbol_Table( Process.Symbol_Table_Directory.Objects[ I ] ) ;
exit ;
end ;
end ;
end ; // TUSC.Resolve_Table
If we get here, either the symbol wasn't found in either the process or job table, or
a table name other than lnm$job or lnm$process was passed. So we loop through the
tables in the list. If the specified table is found, we return the instance. Otherwise
the method ends with a result of nil.
In the next article, we will look at the DELLNT system service.
Copyright © 2021 by Alan Conroy. This article may be copied
in whole or in part as long as this copyright is included.
|