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
|
PROCESS_SCAN
Before we look at the first of the lexical functions, we will look at a system
call that we will be making use of. Normally, we work from the top down - from
an application down to the executive API code. But, for reasons that will become
clear later on, we'll start with the executive and then look at the application
(UCL) code that uses the system call.
Process_Scan
Process_Scan is a system call that works with the GetJPI call. It is used to
iterate through processes. Process_Scan creates a context that GETJPI uses - each
call to GETJPI using that context will get the next matching process ID. Filters
can be added to the context when it is created. Each filter is used to filter
out undesired processes. When Process_Scan is called, it is passed an item list
that indicates one or more filters to use (or no filters, if all process IDs are
to be iterated over). Each filter consists of an item to compare, the value to
compare against, and the type of comparison to do. The following items can be
used for comparisons:
Item | Meaning |
PSCAN_ACCOUNT | Account name |
PSCAN_AUTHPRI | Authorized base priority |
PSCAN_CURPRIV | Privilege name keyword |
PSCAN_HW_MODEL | Hardware model number |
PSCAN_HW_NAME | Hardware name |
PSCAN_JOBPRCCNT | Subprocess count for entire job |
PSCAN_JOBTYPE | Job-type keyword |
PSCAN_KT_COUNT | Kernel thread count |
PSCAN_MASTER_PID | PID of master process |
PSCAN_MODE | Process mode keyword |
PSCAN_MULTITHREAD | Maximum thread count |
PSCAN_NODE_CSID | Node's cluster ID number |
PSCAN_NODENAME | Node name |
PSCAN_OWNER | PID of immediate parent process |
PSCAN_PRCCNT | Subprocess count of process |
PSCAN_PRCNAM | Process name |
PSCAN_PRI | Process priority level number |
PSCAN_PRIB | Base process priority level number |
PSCAN_STATE | Process state keyword |
PSCAN_STS | Process status keyword |
PSCAN_TERMINAL | Terminal name |
PSCAN_USERNAME | User name |
For instance, the PSCAN_ACCOUNT item can be used to compare the account name for
the user associated with a process.
The types of comparisons include the following:
Comparison | Meaning |
PSCAN_M_EQL | Equal to the value |
PSCAN_M_LSS | Less than the value |
PSCAN_M_LEQ | Less than or equal to the value |
PSCAN_M_GTR | Greater than the value |
PSCAN_M_GEQ | Greater than or equal to the value |
PSCAN_M_NEQ | Not equal to the value |
Thus, we could have a filter that would compare the account name of a process'
user with "SYSTEM" and a comparison of PSCAN_M_NEQ to only iterate over processes
whose account name is not "SYSTEM".
The comparisons are combined with optional flags. But trying to combine different
comparisons in the same filter will result in something unexpected - or even an
error. For instance, PSCAN_M_NEQ or PSCAN_M_GEQ not only makes no sense, but would
produce unexpected results or an exception. The following flags can be combined,
although some combinations may not make much sense. VMS reports errors if certain
flags are used with certain comparison items. UOS will allow any of the flags on
any value, although certain combinations are probably not useful.
Flag | Meaning |
PSCAN_M_BIT_ALL | All bits in the comparison value must be set in the item |
PSCAN_M_BIT_ANY | Any bit in the comparison value that is set in the item means a match |
PSCAN_M_CASE_BLIND | Perform string comparisons case-insensitive |
PSCAN_M_OR | Logically or this filter with the next one |
PSCAN_M_PREFIX_MATCH | Match against the portion of the item to the length of the specified comparison value |
PSCAN_M_WILDCARD | Treat wildcard characters as wildcards for matching |
Thus, we could use PSCAN_M_EQL or PSCAN_M_WILDCARD with the PSCAN_USERNAME item
and a comparison value of "A*" to filter out all processes whose username doesn't
start with "A". This particular filter could also be accomplished with a
prefix match, by the way. Note that if we compared with "A*" but didn't include
the wildcard flag, no processes would match, because it would look for a user name
that is literally "A*", but user names cannot contain asterisks.
When a context contains multiple filters, a process must meet every requirement.
However, if the PSCAN_M_OR flag is used, then the filter comparison
is logically ored with the next filter. In fact, any number of filters can be
ored together this way.
Here are the definitions for the above constants:
// Process Scan Filter criteria...
const PSCAN_Terminator = 0 ; // Account name - by reference
const PSCAN_ACCOUNT = 1 ; // Account name - by reference
const PSCAN_AUTHPRI = 2 ; // Authorized base priority
const PSCAN_CURPRIV = 3 ; // Current privileges - by reference
const PSCAN_HW_MODEL = 4 ; // Hardware model number
const PSCAN_HW_NAME = 5 ; // Hardware name - by reference
const PSCAN_JOBPRCCNT = 6 ; // Subprocess count for entire job.
const PSCAN_JOBTYPE = 7 ; // Job-type keyword
const PSCAN_MASTER_PID = 8 ; // PID of master process
const PSCAN_MODE = 9 ; // Process mode keyword
const PSCAN_NODE_CSID = 10 ; // Node's cluster ID number
const PSCAN_NODENAME = 11 ; // Node name - by reference
const PSCAN_OWNER = 12 ; // PID of immediate parent process
const PSCAN_PRCCNT = 13 ; // Subprocess count of process
const PSCAN_PRCNAM = 14 ; // Process name - by reference
const PSCAN_PRI = 15 ; // Process priority level number
const PSCAN_PRIB = 16 ; // Base process priority level number
const PSCAN_STATE = 17 ; // Process state keyword
const PSCAN_STS = 18 ; // Process status keyword
const PSCAN_TERMINAL = 19 ; // Terminal name - by reference
const PSCAN_USERNAME = 20 ; // User name - by reference
const PSCAN_KT_COUNT = 21 ; // Kernel threads
const PSCAN_MULTITHREAD = 22 ; // Maximum Kernel threads
// Process Scan Filter qualifiers...
const PSCAN_M_COMPARISON_MASK = 7 ;
const PSCAN_M_EQL = 0 ; // Equal to the value
const PSCAN_M_LSS = 1 ; // Less than the value
const PSCAN_M_LEQ = 2 ; // Less than or equal to the value
const PSCAN_M_GTR = 3 ; // Greater than the value
const PSCAN_M_GEQ = 4 ; // Greater than or equal to the value
const PSCAN_M_NEQ = 5 ; // Not equal to the value
const PSCAN_M_BIT_ALL = 8 ; // Requires that all bits be set
const PSCAN_M_BIT_ANY = 16 ; // Requests that any bit be set
const PSCAN_M_CASE_BLIND = 32 ; // Match without case sensitivity
const PSCAN_M_WILDCARD = 64 ; // Match a wildcard pattern
const PSCAN_M_OR = 128 ; // Or this filter item with the next one
const PSCAN_M_PREFIX_MATCH = 256 ; // Match prefix
We've encapsulated filters in the TFilter class:
TFilter = class
public // API...
Selection_Criteria : longint ; // See PSCAN_*
Value : string ;
ValueI : int64 ; // Non-string value
Qualifier : longint ; // See PSCAN_M_*
function Get_Filter_Value( PID : TPID ;
Criteria : int64 ) : string ;
end ;
The TFilter class holds the information for a single comparison.
function TFilter.Get_Filter_Value( PID : TPID ; Criteria : int64 ) : string ;
var I, L : integer ;
Process, _Process : TProcess ;
begin
// Setup...
Result := '' ;
Process := Get_Process( PID ) ;
if( Process = nil ) then
begin
exit ;
end ;
This method is used to get the value associated with the criteria and the given
process. The result is a string, but the string may contain an integer value
(in binary form). The first thing we do is get the process object instance from
the passed ID and set the result to a null string. We'll look at Get_Process
later in the article.
// Handle each criteria...
case Criteria of
PSCAN_ACCOUNT: // Account name
begin
Result := Process.User.Owner_Name ;
end ;
PSCAN_AUTHPRI: // Authorized base priority
begin
Set_Integer_Result( Process.User.Get_Auth_Privileges ) ;
end ;
PSCAN_CURPRIV: // Current Privileges
begin
Set_Integer_Result( Process.Current_Privileges ) ;
end ;
PSCAN_HW_MODEL: // Hardware model number
begin
Set_Integer_Result( Process.Hardware_Code ) ;
end ;
PSCAN_HW_NAME: // Hardware name
begin
Result := Process.Hardware_Description ;
end ;
PSCAN_JOBPRCCNT: // Subprocess count for entire job.
begin
_Process := Process.Job ;
Set_Integer_Result( _Process.Child_Count ) ;
_Process.Free_Remote ;
end ;
PSCAN_JOBTYPE: // Job-type
begin
_Process := Process.Job ;
Set_Integer_Result( _Process.Job_Type ) ;
_Process.Free_Remote ;
end ;
PSCAN_KT_COUNT: // Kernel thread count
begin
Set_Integer_Result( Process.KThread_Count ) ;
end ;
PSCAN_MASTER_PID: // PID of master process
begin
_Process := Process.Job ;
Set_Integer_Result( _Process._PID ) ;
_Process.Free_Remote ;
end ;
PSCAN_MODE: // Process mode
begin
_Process := Process.Job ;
I := _Process.Job_Type ;
if( I < JPI_K_BATCH ) then
begin
I := JPI_K_INTERACTIVE ;
end ;
Set_Integer_Result( I ) ;
_Process.Free_Remote ;
end ;
PSCAN_MULTITHREAD:
begin
Set_Integer_Result( Process.MaxK_Threads ) ;
end ;
PSCAN_NODE_CSID: // Node's cluster ID number
begin
Set_Integer_Result( Process.CSID ) ;
end ;
PSCAN_NODENAME: // Node name
begin
Result := Process.NodeName ;
end ;
PSCAN_OWNER: // PID of immediate parent process
begin
Set_Integer_Result( Process._Parent ) ;
end ;
PSCAN_PRCCNT: // Subprocess count of process
begin
Set_Integer_Result( Process.Child_Count ) ;
end ;
PSCAN_PRCNAM: // Process name
begin
Result := Process.Name ;
end ;
PSCAN_PRI: // Process priority level number
begin
Set_Integer_Result( Process.Priority ) ;
end ;
PSCAN_PRIB: // Base process priority level number
begin
Set_Integer_Result( Process.Base_Priority ) ;
end ;
PSCAN_STATE: // Process state
begin
Set_Integer_Result( Process.State ) ;
end ;
PSCAN_STS: // Process status
begin
Set_Integer_Result( Process.Status ) ;
end ;
PSCAN_TERMINAL: // Terminal name
begin
Result := Process.Terminal_Name ;
end ;
PSCAN_USERNAME: // User name
begin
Result := Process.User.Name ;
end ;
end ; // case Criteria
Process.Free_Remote ;
end ; // TFilter.Get_Filter_Value
We switch on the criteria and return the appropriate value.
PSCAN_MODE is a special case in that the JPI_K_INTERACTIVE
constant is not a value that the Process.Job_Type method returns, but the
PSCAN_MODE criteria combines various process modes into that single
constant, so we can't simply take the process mode value and use it as it. It's
just one of those odd VMS things that we go with.
You might notice the calls to Process.Free_Remote. For now, this does nothing, but
we will talk about it in the future. Essentially it has to do with accessing
processes on other computers that are part of a cluster.
Finally, some of the above TProcess methods are new and we'll look at those in the
next article.
Now let's look at the local helper function used by this method:
procedure Set_Integer_Result( Int : int64 ) ;
begin
setlength( Result, sizeof( Int ) ) ;
move( Int, PChar( Result )[ 0 ], length( Result ) ) ;
end ;
Set_Integer_Result is used to pack a 64-bit integer value into an 8-byte
string.
Now let's look at the TContext class:
type TContext = class
public // Constructors and destructors...
constructor Create ;
destructor Destroy ; override ;
public // API...
Filters : TList ; // List of filters
Last_PID : TPID ; // Context
function Next_PID : TPID ;
end ; // TContext
The TContext class is used to maintain a context as a program iterates
through the processes on the system. It contains a list of TFilter objects, and
the last process ID returned by the context.
// API...
constructor TContext.Create ;
begin
inherited Create ;
Filters := TList.Create ;
end ;
destructor TContext.Destroy ;
var I : integer ;
begin
for I := 0 to Filters.Count - 1 do
begin
TFilter( Filters[ I ] ).Free ;
Filters[ I ] := nil ;
end ;
Filters.Free ;
inherited Destroy ;
end ;
The constructor creates the filter list. The destructor frees each filter and
then frees the list.
function TContext.Next_PID : TPID ;
var C : boolean ;
F : integer ;
Filter : TFilter ;
I : integer ;
Match : boolean ;
Process : TProcess ;
L, R : string ;
begin
if( Last_PID = 0 - 1 ) then // Already processed all processes
begin
Result := 0 ;
exit ;
end ;
The Next_PID method returns the next process ID matching the filters.
If the last ID returned is the maximum value (all bits set), the iteration is
complete and we exit with the result of 0. Using the expression "0 - 1" gives us
that maimum value - regardless of whether it is a signed or unsigned integer.
// Find next matching process...
while( True ) do
begin
Result := Last_PID ;
if( Result = 0 ) then // First time
begin
// Find first PID...
for I := 0 to Processes.Count - 1 do
begin
Process := TProcess( Processes[ I ] ) ;
if( Process <> nil ) then
begin
Result := Process._PID ;
break ;
end ;
end ;
end else
begin
// Find next PID
Result := Find_Next_PID( Last_PID ) ;
if( Result = 0 ) then
begin
Last_PID := 0 ; // Done
dec( Last_PID ) ;
exit ;
end ;
end ; // if( Result = 0 )
Last_PID := Result ;
We loop until we've found a matching process, or run out of them. First, we
start with the last returned ID. If this is 0, then this is our first iteration.
We iterate through the Processes list until we find one that isn't
nil.
On the other hand, if we're not on the first iteration, we find the next process
ID.
// Now that we have the next potential process, check against the filters...
Match := False ;
F := -1 ;
while( F < Filters.Count - 1 ) do
begin
inc( F ) ;
Filter := TFilter( Filters[ F ] ) ; // Get next filter
// Get values to compare...
L := Filter.Get_Filter_Value( Result, Filter.Selection_Criteria ) ;
if( Is_By_Reference( Filter.Selection_Criteria ) ) then
begin
R := Filter.Value ;
end else
begin
setlength( R, sizeof( Filter.ValueI ) ) ;
move( Filter.ValueI, PChar( R )[ 0 ], sizeof( Filter.ValueI ) ) ;
end ;
At this point, we have the next potential ID. So we need to apply our
filters to see if we should return this ID or move on to the next one. We set the
Match flag to false and then loop through the filters.
For each time through the loop, we get the filter instance, then get the filter
value. This is the process-specific value to which we compare the comparison value. We
assign this to L because this is the left side of the comparison.
Then we get the comparison value from the filter and assign it to R
(right side value). Because the comparison value could be a string or an integer,
we handle this slightly differently. For strings, we simple get Filter.Value .
For integers, we set the length of R to 8 and copy the 64-bit integer
value into it. Either way, we now have the comparison value in R .
(Is_By_Reference is described later in the article.)
// Compare the values and filter based on comparison mask...
C := Compare_Values( L, R, Filter.Qualifier, Numeric_Criteria( Filter.Selection_Criteria ) ) ;
if( not C ) then // Not a match
begin
if( ( Filter.Qualifier and PSCAN_M_OR ) = 0 ) then // Not oring with next filter
begin
Match := False ;
break ;
end ;
end else
begin
Match := True ;
while( ( Filter.Qualifier and PSCAN_M_OR ) <> 0 ) do // Skip further oring filters
begin
if( F >= Filters.Count - 1 ) then
begin
break ; // Ran out of filters
end ;
inc( F ) ;
Filter := TFilter( Filters[ F ] ) ; // Get next filter
end ;
end ; // if( not C )
end ; // while( F < Filters.Count - 1 )
if( Match ) then
begin
exit ;
end ;
end ; // while( True )
end ; // TContext.Next_PID
Next we compare the values using the filter's qualifier. If there isn't a match, we
check to see if the filter has the OR flag. If not, the match fails so we set Match
to false and exist the inner loop. If there is an OR, we continue on to the next
filter. However, if the compared values do match, we set Match to true
and then check to see if the filter has the OR flag. If so, we skip through the rest
of the filters until a non-OR filter is found (since we found a match, the rest of
the ORed filter checks are unnecessary). The functions, Compare_Values and
Numeric_Criteria are described later in the article.
If we have a match when we're done with the filters, we return the process ID. If not,
we loop back and move to the next process and then repeat the inner loop to apply
the filters.
function Is_By_Reference( Item : integer ) : boolean ;
begin
Result := (
( Item = PSCAN_ACCOUNT )
or
( Item = PSCAN_CURPRIV )
or
( Item = PSCAN_HW_NAME )
or
( Item = PSCAN_NODENAME )
or
( Item = PSCAN_PRCNAM )
or
( Item = PSCAN_TERMINAL )
or
( Item = PSCAN_USERNAME )
) ;
end ; // Is_By_Reference
This function simply returns true if the filter item is passed by reference (as
a string). Note that the privileges are passed by reference even though they
would fit in an 8-byte integer. This is because on 32-bit systems, VMS passed
privileges via a 32-bit integer value, which wasn't enough to pass the entire
privilege mask. UOS follows the VMS approach.
function Numeric_Criteria( Item : integer ) : boolean ;
begin
Result := (
( Item = PSCAN_AUTHPRI )
or
( Item = PSCAN_CURPRIV )
or
( Item = PSCAN_HW_MODEL )
or
( Item = PSCAN_JOBPRCCNT )
or
( Item = PSCAN_JOBTYPE )
or
( Item = PSCAN_MASTER_PID )
or
( Item = PSCAN_MODE )
or
( Item = PSCAN_NODE_CSID )
or
( Item = PSCAN_OWNER )
or
( Item = PSCAN_PRCCNT )
or
( Item = PSCAN_PRI )
or
( Item = PSCAN_PRIB )
or
( Item = PSCAN_STATE )
or
( Item = PSCAN_STS )
or
( Item = PSCAN_KT_COUNT )
or
( Item = PSCAN_MULTITHREAD )
) ;
end ; // Numeric_Criteria
This function returns true if the comparison value is numeric.
function Compare_Values( const L, R : string ; Flags : integer ; Binary : boolean ) : boolean ;
var I : integer ;
LC, RC : cardinal ;
LU, RU : TUnicode_String ;
begin
LU := TUnicode_String.Create ;
RU := TUnicode_String.Create ;
try
if( Binary ) then
begin
LU.Assign_From_String( L, ST_ASCII ) ;
RU.Assign_From_String( R, ST_ASCII ) ;
end else
begin
LU.Assign_From_String( L, ST_YTF8 ) ;
RU.Assign_From_String( R, ST_UTF8 ) ;
if( ( Flags and PSCAN_M_CASE_BLIND ) <> 0 ) then
begin
LU.Lowercase ;
RU.Lowercase ;
end ;
if( ( Flags and PSCAN_M_PREFIX_MATCH ) <> 0 ) then
begin
if( RU.Length > LU.Length ) then
begin
RU.Length := LU.Length ;
end ;
end ;
end ;
Compare_Values is used to compare the left and right values for a
filter. First we create unicode versions of both strings, because all string
comparisons assume UTF8. If the values being compared are binary, we load the
unicode strings as ASCII strings instead of UTF8. In the case of non-binary
values, if the PSCAB_M_CASE_BLIND flag is set, we convert the values
to lowercase so that the case is made irrelevant. Also, for non-binary values,
if the PSCAN_M_PREFIX_MATCH flag is set we minimize the right
string to the left string so that we are only comparing the prefix.
if( ( Flags and PSCAN_M_BIT_ALL ) <> 0 ) then
begin
for I := 1 to LU.Length do
begin
LC := LU.Get_Char( I ) ;
RC := RU.Get_Char( I ) ;
if( ( LC and RC ) <> RC ) then
begin
Result := False ;
exit ;
end ;
end ;
Result := True ;
exit ;
end ;
if( ( Flags and PSCAN_M_BIT_ANY ) <> 0 ) then
begin
Result := False ;
for I := 1 to LU.Length do
begin
LC := LU.Get_Char( I ) ;
RC := RU.Get_Char( I ) ;
if( ( LC and RC ) <> 0 ) then
begin
Result := True ;
exit ;
end ;
end ;
exit ;
end ;
Although VMS limits the PSCAN_M_BIT_ALL and PSCAN_M_BIT_ANY
flags to non-string comparisons (it generates an error in that case), UOS will
allow this kind of comparison on strings as well - in which case the strings are
treated as binary data. In the first case, the comparison is true if all of the
bits in the right string are set in the left string. In the second case, the
comparison is true if any of the bits in the right string are set in the left
string.
I := Compare( LU, RU, ( Flags and PSCAN_M_WILDCARD ) <> 0 ) ;
case Flags and PSCAN_M_COMPARISON_MASK of
PSCAN_M_EQL: Result := I = 0 ; // Equal to the value
PSCAN_M_LSS: Result := I < 0 ; // Less than the value
PSCAN_M_LEQ: Result := I <= 0 ; // Less than or equal to the value
PSCAN_M_GTR: Result := I > 0 ; // Greater than the value
PSCAN_M_GEQ: Result := I >= 0 ; // Greater than or equal to the value
PSCAN_M_NEQ: Result := I <> 0 ; // Not equal to the value
end ;
finally
LU.Free ;
RU.Free ;
end ;
end ; // Compare_Values
If we get down to this point of the function, we call the Compare function
to compare the unicode strings. We will talk about the comparison function in the
next article. It returns -1 if L is less than R , 1
if L is greater than R , and 0 if they are equal. Based on
the flags, we do a comparison and return a true/false result indicating if the
requested comparison evaluated to true or false.
function Get_Process( PID : TPID ) : TProcess ;
var Loop : integer ;
begin
for Loop := 0 to Processes.Count - 1 do
begin
Result := TProcess( Processes[ Loop ] ) ;
if( Result._PID = PID ) then
begin
exit ;
end ;
end ;
Result := nil ;
end ;
This function simply returns the process object instance for a given ID. The
code runs through the Processes list, looking for a match. If none is
found, we return nil. When we get to discussing clusters, we will expand this
function to handle remote processes.
In the next article we will look at the actual Process_Scan system call.
Copyright © 2019 by Alan Conroy. This article may be copied
in whole or in part as long as this copyright is included.
|