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

Glossary/Index


Downloads

Lexical Functions - F$SEARCH

In this article, we examine the F$SEARCH lexical function, which is used to do file lookups. Here is the definition.

F$SEARCH searches a directory for a passed file specification and returns the first/next fully qualified file name matching that specification.

Format
F$SEARCH(filespec{,context})

Return Value
A string containing the fully qualified file name matching the specification. If no file is found, a null string ("") is returned.

Arguments
filespec

The string containing the file specification to be search for. If node/device/path is omitted, the current node/device/path is used. The filename is not defaulted. If the file specification includes wildcards, each time F$SEARCH is called, the next matching file is returned. When no more matches are found, a null string ("") is returned.

context

Specifies a positive integer value (between 0 and 2147483647, inclusive) representing the search context. This allows multiple multiple wildcard searches to be done simultaneously. If omitted, a default context is used.
If a different file specification is provided for a previously-used context (or for the default context), a new lookup is started.

Description
F$SEARCH can be used to iterate through files in a directory, if a wildcard is provided. Note: any loop should exit when a null string is returned. Any of the wildcards valid for a UOS file specification can be used here: *, ?, and **. Only files which exist during the call are returned. Files that are created or deleted during the iteration may or may not be returned.

Example
$ NAME = F$SEARCH("*.*")

    Search_Contexts : TStringList = nil ; // Name and user context
    UOS_Search_Contexts : TInteger64_List = nil ; // UOS contexts associated with Search_Contents
    Default_Search_Context : string ;
    Default_UOS_Search_Context : int64 = 0 ;
Because the UCL script can use an arbitrary integer context associated with an arbitrary file specification, we use a TStringList to hold the file specifications and associated the associated context value. Corresponding to this list is an integer list that contains the context from the UOS executive that corresponds to the script context in the same offset in the string list. Thus, UOS_Search_Contexts[10] would contain the uos context associated with the file specification and UCL context in Search_Contexts[10].
Because there is also the possibility of a default search context, we define the default search context (file specification) and default search context from the UOS executive associated with that file specification.

        Function_SEARCH : begin
                              if( Missing_Parentheses( '(' ) ) then
                              begin
                                  exit ;
                              end ;
                              if( Process_Search( Err, Context ) ) then
                              begin
                                  exit ;
                              end ;
                              if( Missing_Parentheses( ')' ) ) then
                              begin
                                  exit ;
                              end ;
                              S := Context ;
                          end ;
This code is added to the Function_Reference function.

function Process_Search( var Err : integer ; var Context : string ) : boolean ;

var Search_Context : int64 ;
    Filespec : string ;
    Have_Context : boolean ;
    I, Index : integer ;
    Node, Access, Secondary_Node, Device, Path, Name, Extension, Version : string ;

begin
    // Setup...
    Result := False ; // Assume no problems
    Context := '' ;

    // Get parameters...
    Filespec := Get_Parameter( Err, Context ) ;
    if( Err <> 0 ) then
    begin
        exit ;
    end ;
    Have_Context := False ;
    Search_Context := 0 ;
    if( Parser.Peek = ',' ) then
    begin
        Get_Token ; // Eat comma
        Search_Context := Get_Numeric_Parameter( Err, Context ) ;
        if( Err <> 0 ) then
        begin
            exit ;
        end ;
        if( ( Search_Context < 0 ) or ( Search_Context > 2147483647 ) ) then
        begin
            Err := UCL_IVCHAR ;
            Context := inttostr( Search_Context ) ;
            exit ;
        end ;
        if( Search_Contexts = nil ) then
        begin
            Search_Contexts := TStringList.Create ;
            UOS_Search_Contexts := TInteger64_List.Create ;
        end ;
        Have_Context := True ;
    end ;

First, we get the file specification. The second parameter is optional. If the next token is a comma then the second parameter is present, so we eat the comma and get the context. In that case, we validate that the context is between 0 and 2147483647. If not, we exit with an error. Note that DCL simply returns a null value if the provided context is invalid (such as negative). In this case, we are incompatible with VMS, but I try to avoid silent failures when there is no way to determine if there was an error. In DCL, a null result could indicate either an error or no matching files. This kind of ambiguity is generally a bad idea. I don't know if this was intentional (which would be a poor decision on someone's part) or a bug in DCL, but UCL will generate an error if there is an error.
If everything is fine, we construct the lists, if they haven't been constructed yet. Then we set the Have_Context flag.

    // Handle context...
    if( not Have_Context ) then
    begin
        if( Filespec = Default_Search_Context ) then // Same filename as last time
        begin
            Search_Context := Default_UOS_Search_Context ;
            Filespec := '' ;
        end else
        begin
            Search_Context := 0 ;
            Default_Search_Context := Filespec ;
            if( Default_UOS_Search_Context <> 0 ) then
            begin
                SYS_Lookup_Close( int64( @Default_UOS_Search_Context ) ) ;
            end ;
        end ;
        Index := -1 ;
    end else // Context provided...
Now it is time to deal with the script context and UOS context. First, we need to recall from the previous article that when the Lookup service is used, a new lookup takes a filename, but if we're continuing the search, we pass a null string. So, there are four things we need to deal with: 1) the script context, 2) the associated UOS file lookup context, 3) the passed specification, and 4) the specification string we will pass to the Lookup service.

If a context wasn't passed to the function, we use the default context. If the Filespec is different from the previous default value, we want to start a new search using the default context. So we delete the old context (if we had one), set the default filespec to compare against next time, and set Search_Context to 0 to indicate a new context (Search_Context now serves as the UOS search context).

If the filespec is the same as last time, we set the filespec to null, so that the lookup service looks for the next match (in the other case, we leave Filespec alone so that the first lookup is done). We also set Search_Context to the current default UOS context. In any case, if we are dealing with the default context, we set Index to -1.

    begin
        // Find script context in list...
        Index := -1 ;
        for I := 0 to Search_Contexts.Count - 1 do
        begin
            if( integer( Search_Contexts.Objects[ I ] ) = Search_Context ) then
            begin
                Index := I ;
                break ;
            end ;
        end ;

        if( Index = -1 ) then // New context
        begin
            Search_Contexts.AddObject( Filespec, pointer( Search_Context ) ) ;
            Search_Context := 0 ;
            Index := Search_Contexts.Count - 1 ;
            UOS_Search_Contexts.Add( 0 ) ;
        end else
        begin
            Search_Context := UOS_Search_Contexts[ Index ] ;
            if( lowercase( Filespec ) = lowercase( Search_Contexts[ Index ] ) ) then
            begin
                Filespec := '' ; // Continue current search
            end else
            if( Search_Context <> 0 ) then
            begin
                SYS_Lookup_Close( int64( @Search_Context ) ) ;
                Search_Context := 0 ;
            end ;
        end ;
    end ; // if( not Have_Context )
In the case when a context is provided, we look up the script context in the Search_Contexts. If found, Index is the index in the list, otherwise it is -1 if not found. If not found, we add the filespec and script context to the list. We also add 0 to the UOS_Search_Contexts to reserve a spot for the UOS context, and set the Index to the index of the new items in the list(s).

If the script context is found in the list, we compare the previous filespec to the one passed to the function. If they are the same, we clear the filespec so the lookup continues the search. Otherwise, we leave the filespec alone so a new lookup starts. And if the UOS context is in use (not 0), we close the old one and set the context to 0.

    // Default the device and path...
    if( Filespec <> '' ) then
    begin
        Parse_Filename( Filespec, Node, Access, Secondary_Node, Device, Path, Name, Extension, Version ) ;
        if( Node = '' ) then
        begin
            if( Device = '' ) then
            begin
                Device := 'sys$disk:' ;
            end ;
            if( Path = '' ) then
            begin
                Path := GETDDIR ;
            end ;
            Filespec := Device + Path + Name + Extension + Version ;
        end ;
    end ; // if( Filespec <> '' )
Next, we must default the device and path if they are not provided. Obviously if the filespec is null, we don't need to do this since we're continuing a search we already started. But if a filespec is provided, we parse it set the values for any missing device or path, then rebuild the filespec. Note that we do nothing if the node is specified since there is no default device/path on remote nodes. But what if the node is specified and is the current node? Shouldn't we default the device and path in that case? No, because if we are referring to a node, even our own, as a remote node, we let that node define the defaults.

    // Do the lookup...
    Context := Lookup( Filespec, Search_Context ) ;
    if( Filespec <> '' ) then // Initial lookup
    begin
        if( Index = -1 ) then // Default
        begin
            Default_UOS_Search_Context := Search_Context ;
        end else
        begin
            UOS_Search_Contexts[ Index ] := Search_Context ;
        end ;
    end ; // if( Filespec <> '' )
end ; // Process_Search
Finally, we have the filespec and context to pass to the Lookup service. We make the call, setting the result to the lookup value. If the filespec isn't null, this is the initial search, so we update the context - in the case of the default context we set Default_UOS_Search_Context, otherwise we update the UOS_Search_Contexts list value for the appropriate index.

procedure Parse_Filename( const S : string ;
    var Node, Access, Secondary_Node, Device, Path, Name, Extension, Version : string ) ;

begin
    UOS_Util.Parse_Filename( s, Node, Access, Secondary_Node, Device, Path, Name, Extension, Version ) ;
end ;
To support UCL being able to parse filenames without the extra work of calling the Parse service, we add the Parse_Filename function to PasStarlet, which simply surfaces the function from the UOS_Util unit.

In the next article, we will examine the SEARCH service.

 

Copyright © 2020 by Alan Conroy. This article may be copied in whole or in part as long as this copyright is included.