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
209 APPEND and CONTINUE

Glossary/Index


Downloads

APPEND and CONTINUE

In the previous article, we looked at the documentation for the APPEND CUSP. In this article, we look at the source code. We also look at the CONTINUE command in UCL. These two things are unrelated, but we include them together here because this would be a very short article if I only did one of them. First we'll look at the APPEND code. The reason this is so simple is that the APPEND CUSP is merely a wrapper for the COPY CUSP. COPY does append operations, so there is no need to copy that functionality in another CUSP when we can just tell the COPY CUSP to do the work for us. The only difference between the two is that APPEND allows the use of plus signs (+) instead of commas between the list of files to append. So we merely translate the command line into a comma-delimited list and pass it on to COPY for the actual work.

function Run : int64 ;

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

begin
    Result := 0 ;
    OS := new( POS_UOS, Init ) ;
    C := Get_Symbol( 'append$defaults' ) ;
    E := _Append( 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 should be familar from all the CUSPs we've covered before.

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

var C, S : string ;
    P : integer ;

begin
    // 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 ) + Defaults + copy( Command, P, length( Command ) ) ;

    // Convert plus sign delimiters with commas...
    S := '' ;
    while( C <> '' ) do
    begin
        S := S + Parse_Parameter( '+', C ) ;
        if( C <> '' ) then
        begin
            S := S + ',' ;
        end ;
    end ;
    Result := PasStarlet.Run( 'SYS$SYSTEM:COPY', S ) ;
end ; // _Append
This code obtains the command line, prepends it with any defaults passed in, and converts pluses (+) to commas (,). Note that we use the Parse_Parameter function to make sure we don't parse on plus signs contained within quotes. When done, we run COPY, passing the modified command line. This includes any defaults for the APPEND CUSP. Thus, any APPEND defaults will take precedence over COPY defaults. Note that this approach has the side-effect of allowing the user to specify any COPY qualifiers - not just those documented for APPEND. I don't see a problem with this.


Now we cover the CONTINUE command. The idea is to allow the user to interrupt a script or program using Control-C or Control-Y, and then continue the interrupted script/program thereafter. Like DCL, UCL will terminate the interrupted script or program if the user starts another script or program. However, there is a difference between UOS and VMS to be aware of. All of the DCL commands are inherent to DCL and using them will not stop an interrupted script/program. However, in UCL some of these commands are implemented via CUSPs (to allow them to be used by other shells). Thus, while use of something like SET SYMBOL in DCL is allowed, in UCL, it would terminate the interrupted code/script. This is one reason we've been careful to distinguish between UCL commands and CUSPs.

One last note before we look at the documentation and code: we don't include the code necessary to resume an interrupted program - just an interrupted script for now. There is more involved in continuing an interrupted program and we will cover that in the future. I had actually planned to hold off on CONTINUE until then, but I needed the ability to interrupt/continue scripts as a part of current development efforts.

CONTINUE
Resume program or script execution.

Format

CONTINUE

Description

CONTINUE allows an interrupted program or script to continue from where it was interrupted. Note that if another script or program is executed during an interrupted run, the script or program is terminated and cannot be continued thereafter.

procedure CC_AST( P : int64 ) ; stdcall ;

begin
    CC_Interrupt := True ;
    Enter_Interrupt ;
end ;


procedure CY_AST( P : int64 ) ; stdcall ;

begin
    CY_Interrupt := True ;
    Enter_Interrupt ;
end ;
We have modified these routines to call the Enter_Interrupt function rather than duplicate the code in both places.

var Interrupt_Context : TUCL_Context = nil ;

procedure CC_AST( P : int64 ) ; stdcall ; forward ;
procedure CY_AST( P : int64 ) ; stdcall ; forward ;

procedure Enter_Interrupt ;

var S : string ;

begin
    SYS_QIOW( 0, RH_SysCommand, IO_SETMODE or IOM_CLEAR_BUFFER, integer( @IOSB ),
        0, 0, 0, 0, 0, 0, 0, 0 ) ; // Clear any pending input
    Waiting_For_Label := False ; // Cancel any input wait
    SYS_Cancel( RH_SysCommand ) ; // Cancel any I/O operation
    SYS_Cancel( RH_SysInput ) ;
    SYS_Cancel( RH_SysOutput ) ;
    Output( RH_SYSOutput, CRLF + '*INTERRUPT*' + CRLF ) ;

    SYS_QIOW( 0, RH_SysCommand, IO_SETMODE or IOM_CLI_CTRLCAST, integer( @IOSB ),
        0, 0, integer( @CC_AST ), 0, 0, 0, 0, 0 ) ;
    SYS_QIOW( 0, RH_SysCommand, IO_SETMODE or IOM_CLI_CTRLYAST, integer( @IOSB ),
        0, 0, integer( @CY_AST ), 0, 0, 0, 0, 0 ) ;
    if( Interrupt_Context = nil ) then // Not already interrupted
    begin
        { The following shouldn't be done if we are not interactive.  However,
          we shouldn't get an interrupt if we aren't, so we should never get
          here under that circumstance. }
        Interrupt_Context := TUCL_Context.Create ;
        Interrupt_Context.Nest ;
        S := GetJPI( 0, JPI_TERMINAL ) ;
        Set_Symbol( 'sys$command', S, LNM_PROCESS ) ;
        Set_Symbol( 'sys$input', S, LNM_PROCESS ) ;
        Set_Symbol( 'sys$output', S, LNM_PROCESS ) ;
        Set_Symbol( 'sys$error', S, LNM_PROCESS ) ;
    end ;
    //TODO: Interrupt program
end ;
This routine contains the code extracted from the two above procedures. What is new here is the code having to do with Interrrupt_Context. You may recall that when we nest a call to another script, we create a new nested context so we can reset to the current context when the called script exits. The idea is similar here: we want to save the current context and then set our defaults to our terminal so commands and output go there until we decide to continue our interrupted script. If Interrupt_Context is nil, we are not already in an interrupt state. In this case, we create a context and call .Nest, which will save our current state. Then we reset our standard I/O channels to our terminal. If we are already in an interrupt state, we don't need to repeat this - it makes no sense to interrupt an interrupt state.

procedure Cancel_Interruption ;

var X : int64 ;

begin
    if( Interrupt_Context <> nil ) then
    begin
        while( Contexts.Count > 1 ) do // While nested
        begin
            Process_Exit( X ) ;
        end ;
        Interrupt_Context.Free ;
        Interrupt_Context := nil ;
    end ;
end ;
This function is called from Process_At, and just before running any program. It does nothing if there is no current interrupt context. If there is, we clear the context by getting rid of the interrupt context and unwinding all nested contexts, if there are any.

                    if( Sym = 'continue' ) then
                    begin
                        Process_Continue ;
                    end else
This code is added to the Process procedure to handle the continue command.

procedure Process_Continue ;

begin
    if( Interrupt_Context <> nil ) then
    begin
        Interrupt_Context.Unnest ;
        Interrupt_Context.Free ;
        Interrupt_Context := nil ;
    end ;
end ;
This routine continues execution after an interruption. It does nothing if we aren't in an interrupted state. Otherwise, we return to the previous context by calling the Unnest method. Then we free the interrupt context.

In the next article, we will cover the CREATE CUSP.