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

SETTERM utility, part 2

In the previous article, we documented the SETTERM utility and provided the UI definition for it. In this article, we will look at the code.

function Run : int64 ;

var C : string ;
    E : int64 ;
    OS : POS_UOS ;

begin
    OS := new( POS_UOS, Init ) ;
    C := LIB_Get_Symbol( 'setterm$defaults' ) ;
    E := SETTERM_SET( PChar( OS^.Command_Line ), PChar( C ), True ) ;
    if( E <> 0 ) then
    begin
        OS^.OutputLn( 0, LIB_Get_Exception_Text( 0, E ) ) ;
    end ;
    OS.Free ;
    SYS_EXIT( 0 ) ;
end ;
This is the main entry point for the SETTERM utility. Starting with this CUSP, we are going to take a slightly different approach than we have in the past. We want many of the system utilities to be callable directly from other programs. Rather than duplicate code, a given utility will simply wrap the same callable routine that other programs can call. In most cases, such as this, we simply get the command line and pass it to the routine (SETTERM_SET in this case). Callable utilities rarely output directly to sys$output; instead, they return a success/error code. Upon return to the Run routine, we check the code and print the error, if there is one.

Another thing we will see, starting with this utility, is the default options symbol. Most utilities can be customized by the user via a symbol that the user can define. The standard naming convention for this symbol is:
utility$defaults
where "utility" is the CUSP name. For instance, the SETTERM utility uses a symbol named "setterm$default". The symbol is optional, but if it is defined, the contents of the symbol are prepended to the command line. For example, if the user defines setterm$defaults to "/WIDTH=72", and they then type the following command:
SET TERM /SCOPE
The command will be treated as if they had typed:
SET TERM /WIDTH=132/SCOPE
The defaults are prepended in so that the command line can explicitly override the defaults (the actual prepending occurs inside SETTERM_SET).

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

var Buffer : IOSetMode_Buffer ;
    Component, Component1, Component2 : TUUI_Component ;
    D : string ;
    E : int64 ;
    F : TCOM_UOS_File ;
    I, P : int64 ;
    C, Device, S : string ;
    UUI : TUUI ;
    Flags_To_Set, Flags_To_Reset : int64 ;
    QIO_Function : int64 ;

begin
    // Setup...
    Result := 0 ;
    UUI := TUUI.Create ;
    UUI.Definition := UI ;
    D := Defaults ;
    S := Get_Devices ;
    UUI.Get_Component_By_Name( 'device_type' ).List := PChar( S ) ;
    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 ) ;
This is the callable SETTERM routine. One additional note about callable utilities is that they need to operate in one of two modes: UI (standalone) and callable. Standalone mode means that the full program user interface (whether command line or GUI) is shown. The Run routine passes true for the Standalone parameter to indicate this. When not in Standalone mode, there is little or no interaction with the user within the routine - the caller is expected to deal with the user interface.

This code sets up for further processing. Whether in standalone mode or not, we use the UUI to process the command line passed to us. We get a list of defined terminal devices and set the "device_type" component list to this. This has no effect if not in standalone mode and we could skip it otherwise. However, since it isn't a huge performance issue we will just do this in all cases for simplicity sake. One facet of the UUI that I neglected to mention in the last article is the Command_Line property which provides a means for a modified command line to be passed to the UUI. If null, the UUI will obtain the command line and process it. Thus, if you want to prevent any command line processing, simply set the UUI.Command_Line to a space. This will be treated as a null string, but without the UUI requesting the command line from UOS.

The last thing we do is prepend the defaults to the command line. Since there might be a device name before any switches, we are careful to insert the defaults (if any) just before the first switch in the command line (or at the end if no swithces present).

    if( Parse_Switch( 'DEV|ICE_TYPE', '', C, S ) = 1 ) then
    begin
        if( S = '' ) then
        begin
            Result := SETTERM_MISKEYW ;
            exit ;
        end ;
        S := Get_Device_Switches( S ) ;
        if( S = '' ) then
        begin
            Result := SETTERM_UNKDEV ;
            exit ;
        end ;

        UUI.Command_Line := Pchar( UUI.Command_Line + S ) ;
    end ;
    if( Parse_Switch( 'UNK|KNOWN', '', C, S ) = 1 ) then
    begin
        if( S <> '' ) then
        begin
            Result := SETTERM_IVKEYW ;
            exit ;
        end ;
        S := Get_Device_Switches( 'unknown' ) ;
        if( S = '' ) then
        begin
            Result := SETTERM_UNKDEV ;
            exit ;
        end ;

        UUI.Command_Line := PChar( UUI.Command_Line + S ) ;
    end ;
There are some switches that we need to process outside of the UUI. /DEVICE_TYPE and /UNKNOWN are two of these special cases. /UNKNOWN is equivalent to /DEVICE_TYPE=UNKNOWN. Basically, we take the switches appropriate to a given terminal type (model) and prepend them to the UUI command line. We will cover this translation process in the following article.

    // Get/default the device...
    Device := trim( Parse_Parameter( ' ', C ) ) ; // Get device
    if( Device = '' ) then // Use default device
    begin
        Device := Get_JPI( JPI_Terminal ) ;
        Component := UUI.Get_Component_By_Name( 'Device' ) ;
        Component.Text := PChar( Device ) ;
    end ;
Next, we get the device (terminal) to change. If the user doesn't specify one, we use the process' associated terminal - and we set the UUI component to the appropriate value.

    if( Parse_Switch( 'IN|QUIRE', '', C, S ) = 1 ) then
    begin
        if( S <> '' ) then
        begin
            Result := SETTERM_IVKEYW ;
            exit ;
        end ;

        // Query device attributes...
        F := Open_Binary_File( Device, FAB_V_SHRALL ) ;
        if( F = nil ) then
        begin
            E := LIB_Get_Exception( 0 ) ;
            Result := LIB_Get_Exception_Code( 0, E ) ;
            exit ;
        end ;
        S := ESC + '[c' ;
        SYS_QIOW( 0, F.Handle, IO_WRITEPBL, integer( @IOSB ), 0, 0,
            integer( PChar( S ) ), length( S ), 0, 0, 0, 0 ) ;
        S := Get_Command_With_Timeout( '', 3 ) ;
        E := LIB_Get_Exception( 0 ) ;
        if( E <> 0 ) then
        begin
            E := LIB_Get_Exception_Code( 0, E ) ;
            if( E = SS_TIMEOUT ) then // No response
            begin
                S := ESC + 'Z' ;
                SYS_QIOW( 0, F.Handle, IO_WRITEPBL, integer( @IOSB ), 0, 0,
                    integer( PChar( S ) ), length( S ), 0, 0, 0, 0 ) ;
                S := Get_Command_With_Timeout( '', 3 ) ;
                E := LIB_Get_Exception( 0 ) ;
                if( E <> 0 ) then
                begin
                    E := LIB_Get_Exception_Code( 0, E ) ;
                    if( E = SS_TIMEOUT ) then // No response
                    begin
                        S := ESC + '[0c' ;
                        SYS_QIOW( F.Handle, RH_SysCommand, IO_WRITEPBL, integer( @IOSB ), 0, 0,
                            integer( PChar( S ) ), length( S ), 0, 0, 0, 0 ) ;
                        S := Get_Command_With_Timeout( '', 3 ) ;
                        E := LIB_Get_Exception( 0 ) ;
                        if( E <> 0 ) then
                        begin
                            E := LIB_Get_Exception_Code( 0, E ) ;
                            if( E = SS_TIMEOUT ) then // No response
                            begin
                                Result := SETTERM_UNKTERM ;
                                F.Free ;
                                exit ;
                            end ;
                        end ;
                    end ;
                end ;
            end ;
        end ;
        F.Free ;
        if( E <> 0 ) then
        begin
            Result := E ;
            exit ;
        end ;
        UUI.Command_Line := PChar( UUI.Command_Line + Translate_Identification( S ) ) ;
    end ;
/INQUIRE is also a special case that isn't handled by the UUI. This code may seem a little convoluted, but it is actually simple. First, some background: some terminals will respond to special commands by sending an identification - or attribute - string back to the computer. /INQUIRE will send a code and process the response. Since we have no idea what kind of terminal this may be, and different terminals have different query commands, we will have to make multiple efforts in an attempt to get the terminal to respond. The special commands we send are called escape codes (or "escape sequences") because they begin with an escape (ASCII 31) character. There are three escape codes we will attempt that cover all of the known DEC terminals (and emulators thereof). They are, in order sent:
$[c
$Z
$[0c
(where "$" indicates the escape character). The terminal needs time to respond, so we will wait for 3 seconds for a response after sending each code. If a response is received, we can proceed to translatating that response to a terminal model. If no response is received, we try the next code. If none of the codes elicit a response, we exit with an error.

In order to send codes to the specified terminal, we need a handle for it. Since the user can specify any terminal, we cannot assume that he is referring to sys$command. So we open the terminal and exit if there was an error (most likely due to an invalid terminal name or a lack of privileges to alter a different terminal). Then we repeat the following steps for each code:
Write the code to the terminal
Read the response with a 3 second timeout
If no response, try the next code

If no response is forthcoming after the third code is sent, we exit with an error. Otherwise, S now contains the response from the terminal. We translate this to the switches necessary to properly configure that kind of terminal. We will cover the Translate_Identification function in the next article.

    // Processing...
    if( UUI.Execute( '', Standalone ) ) then
    begin
        // Process the specified options...
        Flags_To_Set := 0 ;
        Flags_To_Reset := 0 ;
        Set_Flags( 'Advanced_Video', TT2_M_AVO ) ;
        Set_Flags( 'ANSI_CRT', TT2_M_ANSICRT ) ;
        Set_Flags( 'Application_Keypad', TT2_M_APP_KEYPAD ) ;
        Set_Flags( 'Autobaud', TT2_M_AUTOBAUD ) ;
        Set_Flags( 'Block_mode', TT2_M_BLOCK ) ;
        Set_Flags( 'Brdcstmbx', TT2_M_BRDCSTMBX ) ;
        Set_Flags( 'Broadcast', TT_M_NOBRDCST, True ) ;
        Set_Flags( 'Color', TF_Color ) ;
        Set_Flags( 'Commsync', TT2_M_COMMSYNC ) ;
        Set_Flags( 'DEC_CRT', TT2_M_DECCRT ) ;
        Set_Flags( 'DEC_CRT2', TT2_M_DECCRT2 ) ;
        Set_Flags( 'DEC_CRT3', TT2_M_DECCRT3 ) ;
        Set_Flags( 'DEC_CRT4', TT2_M_DECCRT4 ) ;
        Set_Flags( 'Dialup', TT2_M_DIALUP ) ;
        Set_Flags( 'Disconnect', TT2_M_DISCONNECT ) ;
        Set_Flags( 'DMA', TT2_M_DMA ) ;
        Set_Flags( 'Echo', TT_M_NOECHO, True ) ;
        Set_Flags( 'Edit_mode', TT2_M_EDIT ) ;
        Set_Flags( 'Eight_bit', TT_M_EIGHTBIT ) ;
        Set_Flags( 'Escape', TT_M_ESCAPE ) ;
        Set_Flags( 'Fallback', TT2_M_FALLBACK ) ;
        Set_Flags( 'Form', TT_M_MECHFORM ) ;
        Set_Flags( 'Halfdup', TT_M_HALFDUP ) ;
        Set_Flags( 'Hostsync', TT_M_HOSTSYNC ) ;
        Set_Flags( 'Insert', TT2_M_INSERT ) ;
        Set_Flags( 'Line_editing', TT2_M_EDITING ) ;
        Set_Flags( 'Local_Echo', TT2_M_LOCALECHO ) ;
        Set_Flags( 'Modem', TT_M_MODEM ) ;
        Set_Flags( 'Passthru', TT2_M_PASTHRU ) ;
        Set_Flags( 'Printer_port', TT2_M_PRINTER ) ;
        Set_Flags( 'Readsync', TT_M_READSYNC ) ;
        Set_Flags( 'Regis', TT2_M_REGIS ) ;
        Set_Flags( 'Scope', TT_M_SCOPE ) ;
        Set_Flags( 'Secure_server', TT2_M_SECURE ) ;
        Set_Flags( 'Set_speed', TT2_M_SETSPEED ) ;
        Set_Flags( 'Sixel_graphics', TT2_M_SIXEL ) ;
        Set_Flags( 'Soft_Characters', TT2_M_DRCS ) ;
        Set_Flags( 'Syspassword', TT2_M_SYSPWD ) ;
        Set_Flags( 'Tab', TT_M_MECHTAB ) ;
        Set_Flags( 'TTSync', TT_M_TTSYNC ) ;
        Set_Flags( 'Type_ahead', TT_M_NOTYPEAHD, True ) ;
        Set_Flags( 'Uppercase', TT_M_UPPERCASE ) ;
        Set_Flags( 'Wrap', TT_M_WRAP ) ;
Now we execute the UI, then process each of the boolean flags.

        Component := UUI.Get_Component_By_Name( 'Backspace' ) ;
        if( Component.UUI ) then
        begin
            if( Component.Text = 'Delete' ) then
            begin
                Flags_To_Set := Flags_To_Set or TF_Map_Backspace_To_Delete ;
            end else
            begin
                Flags_To_Reset := Flags_To_Reset or TF_Map_Backspace_To_Delete ;
            end ;
        end ;
Because the backspace processing is a list with two possibilities, we have to process it a little bit different. Flags_To_Set is a collection of the terminal characteristics that we want to set, and Flags_To_Rest is a collection of the terminal characteristics to clear. Set_Flags handles these values and we'll cover that later in the article.

        Component := UUI.Get_Component( 'Device' ) ;
        Device := Component.Text ;
        F := Open_Binary_File( Device, FAB_V_GET or FAB_V_SHRALL ) ;
        
        // Get flags
        SYS_QIOW( 0, F.Handle, IO_SENSEMODE, int64( @iosb ), 0, 0, int64( @Buffer ), 0, 0, 0, 0, 0 ) ;
        E := LIB_Get_Exception( 0 ) ;
        if( E <> 0 ) then
        begin
            Result := LIB_Get_Exception_Code( 0, E ) ;
            exit ;
        end ;
        Buffer.Characteristics := Buffer.Characteristics or Flags_To_Set ;
        Buffer.Characteristics := Buffer.Characteristics and ( not Flags_To_Reset ) ;
        Component := UUI.Get_Component_By_Name( 'Permanent' ) ;
        if( Component.UUI and Component.Selected ) then // Set permanent characteristics
        begin
            QIO_Function := IO_SETCHAR ;
        end else
        begin
            QIO_Function := IO_SETMODE ;
        end ;
        Component := UUI.Get_Component_By_Name( 'Page' ) ;
        if( Component.UUI ) then
        begin
            Buffer.PageLength := strtoint( Component.Text ) ;
        end ;
        Component := UUI.Get_Component_By_Name( 'Width' ) ;
        if( Component.UUI ) then
        begin
            Buffer.PageWidth := strtoint( Component.Text ) ;
        end ;
        
        // Set flags/sizes
        SYS_QIOW( 0, F.Handle, QIO_Function, int64( @iosb ), 0, 0, int64( @Buffer ), 0, 0, 0, 0, 0 ) ;
        E := LIB_Get_Exception( 0 ) ;
        if( E <> 0 ) then
        begin
            Result := LIB_Get_Exception_Code( 0, E ) ;
            F.Free ;
            exit ;
        end ;
        Buffer.PageWidth := 0 ;
        Buffer.PageLength := 0 ;
Next we get the current terminal characteristics so we can modify them. If the user specifies /PERMANENT, we use the IO_SETCHAR function of QIO - otherwise we use the IO_SETMODE function. We also set the page width and height to what the user specified (otherwise they are 0 to indicate no change). Then we make the QIO call to alter the terminal settings.

        // Additional settings...
        Component := UUI.Get_Component_By_Name( 'Altypeahd' ) ;
        if( Component.UUI ) then
        begin
            I := strtoint( Component.Text ) ;
            SYS_QIOW( 0, F.Handle, QIO_Function, int64( @iosb ), 0, 0, int64( @Buffer ), 0, 0, 0, 0, 
                TT_M_ALTYPEAHD or I ) ; // Set alternate type ahead size
            E := LIB_Get_Exception( 0 ) ;
            if( E <> 0 ) then
            begin
                Result := LIB_Get_Exception_Code( 0, E ) ;
                F.Free ;
                exit ;
            end ;
        end ;
Some of the terminal settings are not set via flags, but have numeric values. These are packed into other QIO parameters. We could have set up these other parameters to pass along with the flags in the above QIO call. But I thought it would be more illustrative to make a separate call for each of the different QIO parameters. In VMS the P6 parameter is not used for terminals in QIO. UOS, however, passes the size of the the type-ahead buffer in parameter P6. Additionally, although VMS allows a custom type-ahead buffer size to be set, it is set system-wide at system startup. UOS allows each terminal to have a separate buffer size.

        Component := UUI.Get_Component_By_Name( 'CRFill' ) ;
        Component1 := UUI.Get_Component_By_Name( 'LFFill' ) ;
        if( Component.UUI or Component1.UUI ) then
        begin
            I := 0 ;
            if( Component.UUI ) then
            begin
                I := strtoint( Component.Text ) ;
            end ;
            if( Component1.UUI ) then
            begin
                I := I or ( strtoint( Component.Text ) shl 8 ) ;
            end ;
            // Set alternate type ahead size
            SYS_QIOW( 0, F.Handle, QIO_Function, int64( @iosb ), 0, 0, int64( @Buffer ), 0, 0, I, 0, 0 ) ;
            E := LIB_Get_Exception( 0 ) ;
            if( E <> 0 ) then
            begin
                Result := LIB_Get_Exception_Code( 0, E ) ;
                F.Free ;
                exit ;
            end ;
        end ;
This code handles the CR and LF fill values, which are packed into an integer value and passed to QIOW via the P3 parameter.

        Component := UUI.Get_Component_By_Name( 'Parity' ) ;
        Component1 := UUI.Get_Component_By_Name( 'Dismiss' ) ;
        Component2 := UUI.Get_Component_By_Name( 'Frame' ) ;
        if( Component.UUI or Component1.UUI or Component2.UUI ) then
        begin
            S := Component.Text ;
            I := 0 ;
            if( Component.UUI ) then
            begin
                I := TT_M_ALTRPAR ;
                if( S <> 'None' ) then
                begin
                    I := I or TT_M_PARITY ;
                    if( S = 'Odd' ) then
                    begin
                        I := I or TT_M_ODD ;
                    end ;
                end ;
            end ;
            if( Component1.UUI ) then
            begin
                I := I or TT_M_ALTDISPAR ;
                if( Component.Selected ) then
                begin
                    I := I or TT_M_DISPARERR ;
                end ;
            end ;
            if( Component2.UUI ) then
            begin
                I := TT_M_ALTFRAME or I or strtoint( Component2.Text ) ;
            end ;
            
            // Set parity
            SYS_QIOW( 0, F.Handle, QIO_Function, int64( @iosb ), 0, 0, int64( @Buffer ), 0, 0, 0, I, 0 ) ;
            E := LIB_Get_Exception( 0 ) ;
            if( E <> 0 ) then
            begin
                Result := LIB_Get_Exception_Code( 0, E ) ;
                F.Free ;
                exit ;
            end ;
        end ;
Next, we'll deal with the parity settings, which are set via the P5 parameter. The frame bits are packed into the low bits, and we set the appropriate P5 flags to match the parity settings specified by the user.

        F.Free ;
    end ; // if( UUI.Execute )
end ; // Run
Finally, we free the terminal handle and end the routine.

    procedure Set_Flags( const Name : string ; Flag : int64 ;
        Inverted : boolean = False ) ;

    begin
        Component := UUI.Get_Component_By_Name( PChar( Name ) ) ;
        if( Component.UUI ) then
        begin
            if( Component.Selected and ( not Inverted ) ) then
            begin
                Flags_To_Set := Flags_To_Set or Flag ;
            end else
            begin
                Flags_To_Reset := Flags_To_Reset or Flag ;
            end ;
        end ;
    end ;
This local function is used to set one of the flag sets based on the passed component.

In the next article, we will look at the other functions called by the code in this article. Also, we'll cover the user documentation for the IO_SETMODE and IO_SETCHAR functions.

 

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