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

WRITE

We will start our survey of UCL commands with WRITE. The reason is that this will allow us to examine the values of symbols and the results of lexical functions that we've covered over the past numerous articles. Up until now, it has been theoretical that the lexical functions work properly, but now we can examine the output to see the results. In point of fact, I've been doing internal tests of the functions to verify proper operation, but now they can be checked from the command line as well. Here is the definition of the write command:

This command writes the specified data as one record to the specified open file.

Format

WRITE file expression{,...}

Parameters
file

This specifies a file - either a file handle returned by the OPEN command or one of the process permanent files identified by the SYS$INPUT, SYS$OUTPUT, SYS$ERROR, and SYS$COMMAND logicals.

expression

Specifies data to be written to the file. This can be a literal, or an expression consisting of variables, lexical functions, literals and operators. A list of expressions delimited by commas (,) can be specified. In that case, the expressions are concatenated and then written to the file.

Description

The WRITE command updates the file position when it completes. The file must have been opened with either the /WRITE or /APPEND qualifier on the OPEN command or the WRITE command will fail except for SYS$INPUT, SYS$OUTPUT, SYS$ERROR, and SYS$COMMAND files, which don't have to be explicitly opened in order to be written to.

Qualifiers
/ERROR=label

If an I/O error occurs during the operation, control transfers to the specified label. If this qualifier isn't defined, the current ON condition action is taken if an error occurs. The $STATUS global symbol contains the error code.

/SYMBOL

This qualifier is provided for compatibility with DCL, but has no effect in UCL. There is effectively no UCL limit on the size of data written with the WRITE command.

/UPDATE

Replaces the last record read with this data. This only applies to RMS files. The file must have been read prior to this command. If replacing a sequential file record, the data must be the same size.

/WAIT
/NOWAIT

If /NOWAIT is specified, the operation will complete immediately instead of synchronizing with another reader of the mailbox. /WAIT is the default.

Example

$ WRITE SYS$OUTPUT "Beginning update..."

                    if( Sym = 'write' ) then
                    begin
                        Process_Write ;
                    end else
This code is added to the Process procedure.

procedure Process_Write ;

var Err : integer ;
    Context, Error : string ;
    I, Index : integer ;
    I64 : int64 ;
    Result, S : string ;
    Status : integer ;
    Switches : TStringList ;
    Update : boolean ;
    Wait : boolean ;

begin
    // Setup...
    Switches := Parse_Switches ;
    Update := False ;
    Wait := True ;
    Error := '' ;

    // Process switches...
    for I := 0 to Switches.Count - 1 do
    begin
        S := lowercase( Switches[ I ] ) ;
        if( S = 'symbol' ) then
        begin
            // This switch intentionally ignored
        end else
        if( S = 'update' ) then
        begin
            Update := True ;
        end else
        if( S = 'wait' ) then
        begin
            Wait := True ;
        end else
        if( S = 'nowait' ) then
        begin
            Wait := False ;
        end else
        if( copy( S, 1, 6 ) = 'error=' ) then
        begin
            Error := copy( S, 7, length( S ) ) ;
        end else
        begin
            Output_Line( RH_SysError, UCL_ERT( UCL_IVQUAL ) ) ;
            Output_Line( RH_SysError, '    \' + S + '\' ) ;
            Switches.Free ;
            exit ;
        end ;
    end ;
    Switches.Free ;
After initializing variables and obtaining the switches, if any, we process through the list of switches. We will look at the Parse_Switches function later in the article. We iterate through the list, setting the appropriate flags and ignoring the /SYMBOL switch. We exit with an error if we encounter a switch we don't support. /SYMBOL is used in DCL to allow symbols that contain more than 255 characters to overcome a limitation in DCL. We have no such limitation in UCL, so /SYMBOL does nothing for us.

    // Get output file...
    S := lowercase( Get_Token ) ;

    // Get text...
    Result := '' ;
    while( true ) do
    begin
        Result := Result + Get_Expression( err, context ) ;
        if( Err <> 0 ) then
        begin
            if( ( Err = UCL_EXPSYN ) and ( Parser.Peek <> ',' ) ) then
            begin
                Output_Line( RH_SysError, UCL_ERT( Err ) ) ;
                Output_Line( RH_SysError, '    \' + Context + '\' ) ;
                exit ;
            end ;
        end ;
        if( Parser.Peek <> ',' ) then
        begin
            break ;
        end ;
        Get_Token ; // eat the comma and loop back for next expression
    end ;
Next we obtain the file specification. Then we obtain the data to write out. We use Get_Expression to evaluate the expression and obtain a string to write. If the expression returns a UCL_EXPSYN (syntax error) and the next token is a comma, then we ignore the error (since the comma is what caused the error), skip over the comma, and then loop back to get the next expression. Each time through the loop we accumulate the data to output into the Result buffer.

    // Do the output...
    if( S = 'sys$output' ) then
    begin
        Output_Line( RH_sysOutput, Result ) ;
        Check_Error ;
    end else
    if( S = 'sys$input' ) then
    begin
        Output_Line( RH_sysInput, Result ) ;
        Check_Error ;
    end else
    if( S = 'sys$error' ) then
    begin
        Output_Line( RH_sysError, Result ) ;
        Check_Error ;
    end else
    if( S = 'sys$command' ) then
    begin
        Output_Line( RH_sysCommand, Result ) ;
        Check_Error ;
    end else
We check for the special cases and call Output_Line to write the data and check for errors.

    begin
        S := LIB_Get_Symbol( S ) ;
        if( not Valid_Int( S, I64 ) ) then
        begin
            Output_Line( RH_sysError, UCL_ERT( UCL_UNDSYM ) ) ;
            exit ;
        end ;
        Index := File_Handles.Indexof( pointer( I64 ) ) ;
        if( Index = -1 ) then
        begin
            Output_Line( RH_sysError, UCL_ERT( UCL_UNDSYM ) ) ;
            exit ;
        end ;
        Result := Result + CRLF ;
        if( Update ) then
        begin
            TCOM_File64( I64 ).Seek( Last_Read_Positions[ Index ] ) ;
        end ;
        TCOM_File64( I64 ).Write( PAnsichar( Result )[ 0 ] ) ;
        if( TCOM_File64( I64 ).IO_Error <> nil ) then
        begin
            Status := LIB_Set_Symbol( '$STATUS',
                '%X' + CVTB( 10, 16, inttostr( TCOM_File64( I64 ).IO_Error.Get_Error ) ), LNM_JOB ) ;
            if( Error <> '' ) then
            begin
                Parser.Put_Token( 'goto ' + Error ) ;
            end ;
        end else
        begin
            Status := LIB_Set_Symbol( '$STATUS', '%X0000000', LNM_JOB ) ;
        end ;
    end ;
end ; // Process_Write
In the other case, we assume that the symbol indicates a file handle created by the OPEN command, that we will use to access the file. First we translate the symbol and verify that it is numeric. If not, we exit with an error. We then look at our list of file handles to see if the value represents an actual handle. If not, we again exit with an error. Finally we add a CRLF to the data and write the buffer to the file. We then check for an error result and set the $STATUS symbol appropriately. If there was an error and the /ERROR qualifier was used, we push the Error code so it will execute next.

Note that we aren't handling any of the switches at this point. /UPDATE has to do with RMS files, and /WAIT and /NOWAIT have to do with mailboxes. We will address those in the future.

    procedure Check_Error ;

    var Status, Err : integer ;
        Work : string ;

    begin
        Err := LIB_Get_Exception( 0 ) ;
        if( Err <> 0 ) then
        begin
            Set_Exception( LIB_Get_Exception_Text( 0, Err ) ) ;
            Err := LIB_Get_Exception_Code( 0, Err ) ;
        end ;
        Status := LIB_Set_Symbol( '$STATUS',
            '%X' + CVTB( 10, 16, inttostr( Err ) ), LNM_JOB ) ;
        if( ( Error <> '' ) and ( Err <> 0 ) ) then
        begin
            Parser.Put_Token( 'goto ' + Error ) ;
        end ;
    end ;
This local function checks for an exception due to writing data to the output device. If so, the $STATUS variable is set and we push any jump to an error handler onto the token stack.

function Parse_Switches : TStringList ;

var S : string ;

begin
    Result := TStringList.Create ;
    S := Parser.Peek ;
    while( S = '/' ) do
    begin
        Get_Token ; // Eat slash
        S := Get_Token ;
        if( Parser.Peek = '=' ) then
        begin
            S := S + Get_Token + Get_Token ;
        end ;
        Result.Add( S ) ;
        S := Parser.Peek ;
    end ;
end ;
This function parses a list of qualifiers (switches) and returns a string list containing the switches.

In the next article, we will look at symbol assignment.

 

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