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

AUTHORIZE utility, Part 4

In the last article, we looked at various support routines. In this article we will look at the code that implements the RENAME and REMOVE commands, and at the code that prepares for the handling of the other commands.

unit Authorizemain ;

interface

function Run : int64 ;
function Authorize_SET( Command, Defaults : PChar ; Standalone : boolean ) : int64 ;


implementation

uses // Delphi...
     sysutils, // inttostr

     // C&C...
     _ASCII, // CR
     _UE, // TUnified_Exception
     Misc, // Space
     Parse, // Parse_Switch
     Ustrings, // Left
     UI_Compiler, // TUUI
     _Unicode, // TT_UTF8
     Unicode, // WC_Match

     // UOS...
     _Starlet, // TCOM_UOS_File
     _UOS, // TSRB
     OS_UOS, // TOS_UOS
     PasStarlet, // Get_Command_With_Timeout
     _Stores, // TStore_Address64
     Starlet, // LIB_RUN
     Sys, // SYS_EXIT
     SysUAF, // Get_User
     _UOS_File_System, // TUOS_File
     UOS_Util ; // TStringList

type TOperation = ( Op_None, Op_Modify, Op_Copy ) ;

var Buf : array[ 0..1023 ] of char ;
    BufLen : int64 ;
    Highlight : integer = 0 ;
    IOSB : TIOSB ;
    Operation : TOperation ;
    Source_UIC : integer ;

function Run : int64 ;

var C : string ;
    OS : POS_UOS ;

begin
    OS := new( POS_UOS, Init ) ;
    C := LIB_Get_Symbol( 'authorize$defaults' ) ;
    Result := Authorize_SET( PChar( OS^.Command_Line ), PChar( C ), True ) ;
    if( Result <> 0 ) then
    begin
        OS^.OutputLn( 0, LIB_Get_Exception_Text( 0, Result ) ) ;
    end ;
    OS.Free ;
    SYS_EXIT( 0 ) ;
end ;
The Run procedure, as in other CUSPs, is the entry point when running the program (as opposed to calling it within another program). This is what we've seen before: we get any default settings and pass them along with the command line to the callable interface Authorize_SET. If that returns with an error, we display the error. Either way, we clean up and exit.

TOperation is used to indicate which operation we are performing.

function Authorize_SET( Command, Defaults : PChar ; Standalone : boolean ) : int64 ;

var Component : TUUI_Component ;
    Flags_To_Set, Flags_To_Reset : int64 ;
    F : TCOM_UOS_File ;
    File_Wrapper : TUOS_File ;
    D : string ;
    P : int64 ;
    C, S : string ;
    OS : POS_UOS ;
    UEC : TUnified_Exception ;

begin
    S := LIB_Get_Symbol( 'sysuaf' ) ;
    if( S = '' ) then
    begin
        S := 'sys$boot:\uos\sysuaf.dat' ;
    end ;
    if( not File_Exists( S ) ) then
    begin
        if( Standalone ) then
        begin
            OS := new( POS_UOS, Init ) ;
            OS^.Outputln( RH_SysError, '%UAF-E-NAOFIL, unable to open SYSUAF.DAT, creating a new one' ) ;
        end ;
        F := Open_Binary_File( S, FM_RW or FAB_V_CIF ) ;
        if( F.IO_Error <> nil ) then
        begin
            Result := F.IO_Error.Get_Error ;
            F.Free ;
            exit ;
        end ;
        F.Free ;
    end ;
This is the callable interface for Authorize. It first looks for the SYSUAF symbol. If not found, it defaults to the current UOS boot folder. Next we check to see if the file exists. If not, we indicate such to the user (if we're in stand-alone mode), and then create a new file.

    F := Open_Binary_File( S, FM_RW ) ;
    if( F.IO_Error <> nil ) then
    begin
        Result := F.IO_Error.Get_Error ;
        F.Free ;
        exit ;
    end ;
    File_Wrapper := Create_UOS_File_Wrapper( F ) ;
    UEC := Set_Sysuaf_File( File_Wrapper ) ;
    if( UEC <> nil ) then
    begin
        Result := UEC.Get_Error ;
        exit ;
    end ;
Next, we open the file, create a wrapper for it, as discussed in the previous article, and then set the SYSUAF file (in the SYSUAF module) to this wrapper. If there is an error, we exit.

    // Setup...
    Result := 0 ;
    UUI := TUUI.Create ;
    UUI.Set_Variable( '$prompt', 'UAF> ' ) ;
    UUI.Definition := UI ;
    S := UUI.Error ;
    if( S <> '' ) then
    begin
        OS^.Outputln( RH_SysError, S ) ;
        OS^.Outputln( RH_SysError, UUI.Error_Line ) ;
        exit ;
    end ;
    D := Defaults ;

    // Prepend command line with defaults...
    P := Switch_Present( Command ) ;
    if( P = 0 ) then
    begin
        P := length( Command ) + 1 ;
    end ;
    C := copy( Command, 1, P - 1 ) + D + copy( Command, P, length( Command ) ) ;
    UUI.Command_Line := PChar( C ) ;
    UUI.Responder := TAuth_Responder.Create ;
Next, we create a UUI instance and set the prompt variable value. Then we set the UI definition, which we included a couple of articles ago. If there was an error processing the UI, we return the error and exit. Otherwise, we insert the default switches prior to any existing command-line switches (or at the end of the command-line if no switches). Then we set the command line and a responder. We will cover the responder in a bit. This is a feature of the UUI that we introduce here. The UUI makes calls to a responder (if defined) at certain points to allow customization of UI handling - because no matter how flexible the UUI is, there will always be some situation that it doesn't cover. Thus, it only handles common UOS UI issues and leaves handling of the exceptional circumstances to those programs that need them.

    // Processing...
    UUI.Execute( '', Standalone ) ;
    Result := Error_Code ;
end ; // Run
Finally, we execute the UI and return any errors upon return.

TUUI_Responder = class
                     public // API...
                         function Callback( Component : TUUI_Component ;
                             Action : TAction ; Command : PChar ;
                             var Abort : boolean ; var Handled : boolean ) : PChar ;
                             virtual ; abstract ;
                         function Post_Processing : boolean ;
                             virtual ; abstract ;
                 end ;

const Highlight_Blink = 1 ;
const Highlight_Bold = 2 ;
const Highlight_Reverse = 3 ;
const Highlight_Underline = 4 ;
const Pages_Clear_Screen = -1 ;
const Pages_Scroll = -2 ;

type TAuth_Responder = class( TUUI_Responder )
                           public // API...
                               Auth_Command : string ; // Authorize command used
                               Target : string ; // Target username
                               Temp : string ;

                               function Callback( Component : TUUI_Component ;
                                   Action : TAction ; Command : PChar ;
                                   var Abort : boolean ;
                                   var Handled : boolean ) : PChar ; override ;
                               function Post_Processing : boolean ; override ;
                       end ;
The TUUI_Responder class is what the UUI uses to handle customization. TAuth_Responder is the implementation of this class for Authorize. If there are any actions defined and a responder set, the UUI calls the responder's Callback method. This is called after the UI definition is parsed and before the command line is processed. The method takes the current command line and can return a modified command line back to UUI. If Abort is set to true, the UUI processing is aborted. This is only used if there was a serious error. Handled is set to true if the responder handles the current command line and false to allow the UUI to process the returned command line. Unless handled, the method typically returns the command line that was passed. Command contains the name of the action component that matches the command in the command line. Action indicates the type of callback (we ignore that for now).

Post_Processing is called after the command line is processed. It takes no parameters and returns true if the post processing is successful and false if there was an error. In the case of an error, the UUI will exit.

function TAuth_Responder.Callback( Component : TUUI_Component ;
    Action : TAction ; Command : PChar ; var Abort : boolean ;
    var Handled : boolean ) : PChar ;

var C, P, S, Other_Switches : string ;
    I : integer ;
    U : TUser ;

begin
    // Set up...
    Result := nil ;
    C := Command ;
    Access_Switches := '' ;
    Other_Switches := '' ;
    Operation := Op_None ;
This method starts with processing any/all access modifiers - which are not handled by the UUI due to being rather complicated. First we set up things, including setting the operation to Op_None to indicate that there is no further processing to be done, which we will set under certain circumstances.

    // Handle access switches...
    while( true ) do
    begin
        S := Next_Switch( C, P ) ;
        if( S = '' ) then
        begin
            break ;
        end ;
        S := '/' + S ;
        if( MinMatch( '/ACCESS', S, 5 )
            or
            MinMatch( '/NOACCESS', S, 7 )
            or
            MinMatch( '/BATCH', S, 3 )
            or
            MinMatch( '/INTERACTIVE', S, 2 )
            or
            MinMatch( '/NOINTERACTIVE', S, 4 )
            or
            MinMatch( '/LOCAL', S, 3 )
            or
            MinMatch( '/NETWORK', S, 2 )
            or
            MinMatch( '/REMOTE', S, 2 )
            or
            MinMatch( '/PRIMEDAYS', S, 2 )
          ) then
        begin
            if( P <> '' ) then
            begin
                S := S + '=' + P ;
            end ;
            Access_Switches := Access_Switches + S ;
        end else
        begin
            if( P <> '' ) then
            begin
                S := S + '=' + P ;
            end ;
            Other_Switches := Other_Switches + S ;
        end ;
    end ; // while( true )
    C := C + Other_Switches ;
    Temp := C ;
    Result := Pchar( Temp ) ;
This loop pulls all access restriction switches out of the command line, placing them in the Access_Switches string. Other switches are placed in Other_Switches, and sets up the command line to include only those switches. MinMatch is used to allow the access switches to be abbreviated.

    // Process command...
    Auth_Command := Component.Name ;
    if( Component.Name = 'exit' ) then
    begin
        Handled := True ;
        Abort := True ;
        exit ;
    end ;
    if( Component.Name = 'help' ) then
    begin
        Handled := True ;
        //TODO
        exit ;
    end ;
    if( Component.Name = 'remove' ) then
    begin
        Handled := True ;
        S := UUI.Get_Parameter( 'Authorize', 'User to remove: ' ) ;
        if( ( S = Control_Z ) or ( S = Control_C ) or ( S = Control_Y ) or ( S = ESC ) ) then
        begin
            Abort := True ;
            exit ;
        end ;
        Target := S ;
        U := Get_User( S ) ;
        if( U = nil ) then
        begin
            Error_Code := AUTH_NOTFOUND ;
            Abort := True ;
            exit ;
        end ;
        Delete_User( U ) ;
        exit ;
    end ;
If the command is EXIT, we set Abort and Handled so that the UUI will immediately exit back to the calling Authorize_SET method. The HELP command is something that we will cover in a future article.

The REMOVE command is handled here. The processing is simple because it takes no switches. The Get_Parameter method is used to obtain a parameter for the REMOVE command. If the parameter is in the command line, it is extracted from there, otherwise the user is prompted. The parameter is the user to remove. If the result is a special character, we exit with an abort. Otherwise we take the name and obtain the TUser instance for the user with that name. If the returned user is nil, the user doesn't exist and we exit with an error. Otherwise, we delete the user by calling Delete_User in the SYSUAF module.

    if( Component.Name = 'rename' ) then
    begin
        Handled := True ;
        S := UUI.Get_Parameter( 'Authorize', 'Username: ' ) ;
        if( ( S = Control_Z ) or ( S = Control_C ) or ( S = Control_Y ) or ( S = ESC ) ) then
        begin
            Abort := True ;
            exit ;
        end ;
        Target := S ;
        U := Get_User( S ) ;
        if( U = nil ) then
        begin
            UUI.Show_Error( AUTH_NOTFOUND, 'User not found' ) ;
            exit ;
        end ;

        S := UUI.Get_Parameter( 'Authorize', 'New name: ' ) ;
        if( ( S = Control_Z ) or ( S = Control_C ) or ( S = Control_Y ) or ( S = ESC ) ) then
        begin
            Abort := True ;
            exit ;
        end ;
        Target := S ;
        U := Get_User( S ) ;
        if( U <> nil ) then
        begin
            UUI.Show_Error( AUTH_EXISTS, 'User already exists' ) ;
            exit ;
        end ;
        if( not Valid_Username( S ) ) then
        begin
            UUI.Show_Error( AUTH_INVNAM, 'Invalid name' ) ;
            exit ;
        end ;
        Rename_User( Target, S ) ;
        exit ;
    end ;
The RENAME command is only slightly more involved than the REMOVE command. In this case, we get two parameters: the current user name and the new user name. If the current user doesn't exist, we exit with an error. If the new username is invalid, we exit with an error. If the new username matches an existing user, we exit with an error. Otherwise, we call Rename_User to change the user's name.

    if( Component.Name = 'add' ) then
    begin
        Handled := True ;
        Result := nil ;
        S := UUI.Get_Parameter( 'Authorize', 'New username: ' ) ;
        if( ( S = Control_Z ) or ( S = Control_C ) or ( S = Control_Y ) or ( S = ESC ) ) then
        begin
            Abort := True ;
            exit ;
        end ;
        if( not Valid_Username( S ) ) then
        begin
            UUI.Show_Error( AUTH_INVNAM, 'Invalid name' ) ;
            exit ;
        end ;
        Target := S ;
        U := Get_User( S ) ;
        if( U <> nil ) then
        begin
            UUI.Show_Error( AUTH_EXISTS, 'User already exists' ) ;
            exit ;
        end ;
        Source_UIC := 0 ;
        Operation := Op_Copy ;
        U := Get_User( 'default' ) ; // Get Default user
        Update_User_UI( U ) ;
        exit ;
    end ; // if( Component.Name = 'Add' )
The ADD command depends upon the multitude of Authorize switches, so we only do a little bit of processing here, and then let the UUI process the switches. Remember that Add is simply a form of copy, so we set Operation to Op_Copy, set the source UIC to 0 (the default user), and set the target to the name of the new user, which we get by calling Get_Parameter. We validate that the target user name is valid and that the user doesn't already exist.

    if( Component.Name = 'copy' ) then
    begin
        Handled := True ;
        S := UUI.Get_Parameter( 'Authorize', 'Username to copy: ' ) ;
        if( ( S = Control_Z ) or ( S = Control_C ) or ( S = Control_Y ) or ( S = ESC ) ) then
        begin
            Abort := True ;
            exit ;
        end ;
        U := Get_User( S ) ;
        if( U = nil ) then
        begin
            UUI.Show_Error( AUTH_NOTFOUND, 'User not found' ) ;
            exit ;
        end ;
        Source_UIC := U.UIC ;
        Update_User_UI( U ) ;

        S := UUI.Get_Parameter( 'Authorize', 'New username: ' ) ;
        if( ( S = Control_Z ) or ( S = Control_C ) or ( S = Control_Y ) or ( S = ESC ) ) then
        begin
            Abort := True ;
            exit ;
        end ;
        if( not Valid_Username( S ) ) then
        begin
            UUI.Show_Error( AUTH_INVNAM, 'Invalid name' ) ;
            exit ;
        end ;
        Target := S ;
        U := Get_User( S ) ;
        if( U <> nil ) then
        begin
            UUI.Show_Error( AUTH_EXISTS, 'User already exists' ) ;
            exit ;
        end ;
        Operation := Op_Copy ;
        exit ;
    end ; // if( Component.Name = 'Copy' )
Like the ADD command, the COPY command does a copy operation. In this case, we prompt for both the source and Target user names. We use Get_Parameter to get the source and target user names. If the source user doesn't exist, we exit with an error. If the new (target) user name is invalid or already exists, we exit with the appropriate error. Otherwise, we set Target, Source_UIC, and Operation.

    if( Component.Name = 'default' ) then
    begin
        Handled := True ;
        Target := 'DEFAULT' ;
        Operation := Op_Modify ;
        exit ;
    end ; // if( Component.Name = 'Add' )
    if( Component.Name = 'modify' ) then
    begin
        Handled := True ;
        S := UUI.Get_Parameter( 'Authorize', 'User to Modify: ' ) ;
        if( ( S = Control_Z ) or ( S = Control_C ) or ( S = Control_Y ) or ( S = ESC ) ) then
        begin
            Abort := True ;
            exit ;
        end ;
        Target := S ;
        U := Get_User( S ) ;
        if( U = nil ) then
        begin
            UUI.Show_Error( AUTH_NOTFOUND, 'User not found' ) ;
            exit ;
        end ;
        Operation := Op_Modify ;
        exit ;
    end ; // if( Component.Name = 'Modify' )
As discussed previously, the DEFAULT and MODIFY commands are identical except in terms of which account is modified. In the case of DEFAULT, the processing is easy - we set the target and operation. For the MODIFY command, we get the target user and validate that the user exists. As with ADD and COPY, we let UUI handle the switches and we'll process things after that.

    if( Component.Name = 'show' ) then
    begin
        Handled := True ;
        S := UUI.Get_Parameter( 'Authorize', 'User to show: ' ) ;
        if( ( S = Control_Z ) or ( S = Control_C ) or ( S = Control_Y ) or ( S = ESC ) ) then
        begin
            Abort := True ;
            exit ;
        end ;
        Target := S ;
        if( pos( '*', S ) + pos( '?', S ) = 0 ) then // No wildcard
        begin
            U := Get_User( S ) ;
            if( U = nil ) then
            begin
                UUI.Show_Error( AUTH_NOTFOUND, 'User not found' ) ;
                exit ;
            end ;
        end ;
        Highlight := 0 ;
        Pages := 0 ;
        Search_Text := '' ;
        Brief := False ;
        Full := False ;
        case Parse_Switch( 'H|IGHLIGHT', 'NOH|IGHLIGHT', C, P ) of
            1 : // Switch matching /HIGHLIGHT
                begin
                    P := trim( lowercase( P ) ) ;
                    if( MinMatch( 'blink', P, 2 ) ) then
                    begin
                        Highlight := Highlight_Blink ;
                    end else
                    if( MinMatch( 'bold', P, 2 ) or ( P = '' ) ) then
                    begin
                        Highlight := Highlight_Bold ;
                    end else
                    if( MinMatch( 'reverse', P, 1 ) ) then
                    begin
                        Highlight := Highlight_Reverse ;
                    end else
                    if( MinMatch( 'underline', P, 1 ) ) then
                    begin
                        Highlight := Highlight_Underline ;
                    end else
                    begin
                        UUI.Show_Error( UUI_INVSWVAL, 'Invalid switch value' ) ;
                        exit ;
                    end ;
                end ;
            2 : // Switch matching /NOHIGHLIGHT
                begin
                    if( P <> '' ) then
                    begin
                        UUI.Show_Error( UUI_NOSWITCH, 'Parameter not allowed on switch' ) ;
                        exit ;
                    end ;
                    Highlight := 0 ;
                end ;
        end ;
        case Parse_Switch( 'P|AGE', 'NOP|AGE', C, P ) of
            1 : // Switch matching /PAGE
                begin
                    P := trim( lowercase( P ) ) ;
                    if( MinMatch( 'clear_screen', P, 1 ) ) then
                    begin
                        Pages := Pages_Clear_Screen ;
                    end else
                    if( MinMatch( 'scroll', P, 2 ) ) then
                    begin
                        Pages := Pages_Scroll ;
                    end else
                    begin
                        I := pos( ':', p + ':' ) ;
                        S := copy( P, I + 1, length( S ) ) ;
                        P := copy( P, 1, I - 1 ) ;
                        if( S = '' ) then
                        begin
                            S := '5' ;
                        end ;
                        if( MinMatch( 'save', P, 2 ) ) then
                        begin
                            if( not trystrtoint( S, Pages ) ) then
                            begin
                                Error_Code := UUI_INVSWVAL ;
                                Abort := True ;
                                exit ;
                            end ;
                            if( Pages < 1 ) then
                            begin
                                Error_Code := UUI_INVSWVAL ;
                                Abort := True ;
                                exit ;
                            end ;
                        end else
                        begin
                            UUI.Show_Error( UUI_INVSWVAL, 'Invalid switch value' ) ;
                            exit ;
                        end ;
                    end ;
                end ;
            2 : // Switch matching /NOPAGE
                begin
                    if( P <> '' ) then
                    begin
                        UUI.Show_Error( UUI_NOSWITCH, 'Parameter not allowed on switch' ) ;
                        exit ;
                    end ;
                    Pages := 0 ;
                end ;
        end ;
        if( Parse_Switch( 'S|EARCH', '', C, Search_Text ) = 1 ) then
        begin
            if( Search_Text = '' ) then
            begin
                UUI.Show_Error( UUI_NOSWITCH, 'Parameter required on switch' ) ;
                exit ;
            end ;
        end ;
        if( Parse_Switch( 'B|RIEF', '', C, Search_Text ) = 1 ) then
        begin
            if( Search_Text <> '' ) then
            begin
                UUI.Show_Error( UUI_NOSWITCH, 'No parameter allowed on switch' ) ;
                exit ;
            end ;
            Brief := True ;
        end ;
        if( Parse_Switch( 'F|ULL', '', C, Search_Text ) = 1 ) then
        begin
            if( Search_Text <> '' ) then
            begin
                UUI.Show_Error( UUI_NOSWITCH, 'No parameter allowed on switch' ) ;
                exit ;
            end ;
            Full := True ;
        end ;
        if( Full and Brief ) then
        begin
            UUI.Show_Error( UUI_CONFLICT, 'Conflicting switches' ) ;
            exit ;
        end ;
        Error_Code := Show_Report( Target, '' ) ;
        if( Error_Code <> 0 ) then
        begin
            UUI.Show_Error( Error_Code, 'No matches found' ) ;
        end ;
        exit ;
    end ; // if( Component.Name = 'Show' )
    if( Component.Name = 'list' ) then
    begin
        Handled := True ;
        S := UUI.Get_Parameter( 'Authorize', 'User to list: ' ) ;
        if( ( S = Control_Z ) or ( S = Control_C ) or ( S = Control_Y ) or ( S = ESC ) ) then
        begin
            Abort := True ;
            exit ;
        end ;
        Target := S ;
        if( pos( '*', S ) + pos( '?', S ) = 0 ) then // No wildcard
        begin
            U := Get_User( S ) ;
            if( U = nil ) then
            begin
                UUI.Show_Error( AUTH_NOTFOUND, 'No matches found' ) ;
                exit ;
            end ;
        end ;
        Highlight := 0 ;
        Pages := 0 ;
        Search_Text := '' ;
        Brief := False ;
        Full := False ;
        if( Parse_Switch( 'B|RIEF', '', C, Search_Text ) = 1 ) then
        begin
            if( Search_Text <> '' ) then
            begin
                UUI.Show_Error( UUI_NOSWITCH, 'No parameter allowed on switch' ) ;
                exit ;
            end ;
            Brief := True ;
        end ;
        if( Parse_Switch( 'F|ULL', '', C, Search_Text ) = 1 ) then
        begin
            if( Search_Text <> '' ) then
            begin
                UUI.Show_Error( UUI_NOSWITCH, 'No parameter allowed on switch' ) ;
                exit ;
            end ;
            Full := True ;
        end ;
        if( Full and Brief ) then
        begin
            UUI.Show_Error( UUI_CONFLICT, 'Conflicting switches' ) ;
            exit ;
        end ;
        Error_Code := Show_Report( Target, 'sysuaf.lis' ) ;
    end ; // if( Component.Name = 'List' )
end ; // TAuth_Responder.Callback
The SHOW and LIST commands differ primarily in terms of which switches are handled and what parameters are passed to Show_Report. In both cases, we get the user specification. If it doesn't include a wildcard, we validate that the user exists and then process the various switches.

We will cover the Show_Report function and the Post_Processing method in the next article.

 

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