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
210 CREATE

Glossary/Index


Downloads

CREATE

In this article we will look at the CREATE CUSP. This is, perhaps, one of the simplest of the CUSPS. It creates a new file. First the documentation, and then the code.

CREATE
Creates one or more disk files.

Format

CREATE filespec{,...}

Arguments

filespec
The file specification for a file to create. There is no default for an omitted name or extension. If the specified file already exists, a new version is created. Multiple files can be specified.

Description

The CREATE command creates a new disk file. In interactive mode, each separate line that is entered after the command becomes a line in the new file. To terminate the file input, enter a control-Z.

When the CREATE command occurs in a command file, UCL reads input lines from the command file until a dollar sign ($) is encountered in the first position of a line, or Control-Z is encountered, or the end of the command file is encountered.

If multiple output files are specified, after the input is terminated for a file, the next file is created and following lines are written to that file until ended as above.
If the file specification includes a logical name list, the file is created in the first location in the list.

The new file will have the current user as the owner, unless the user has extended privileges, in which case the file's owner will be the owner of the parent directory or of the previous version of the file. Extended privileges include: SYSPRV, BYPASS, or user has a System User UIC.

Qualifiers

/{NO}LOG
Displays the file specification as each file is created.
/OWNER=owner
Assigns the specified user as owner of the file. The owner may be a user name or a UIC. SYSPRV is required to specify another user.
/PROTECTION(ownership[:access][,...])
Specified protection for the file. "ownership" is one of the following:
ValueMeaning
SSystem
OOwner
GGroup
WWorld
"access" is one or more of the following:
ValueMeaning
RRead
WWrite
EExecute
DDelete
If a value is not specified for each access category (or if /PROTECTION is not used), the protection is assigned from the existing file, or the default protection if there was no existing file.

Example


$ CREATE FILE1.TXT
Line one
Line two
.
.
.
Ctrl/Z
$
FILE1.TXT is created and lines are added to it until control-Z is pressed.


const UI =  'object Main:rectangle' + CR +
            '  top = 2' + CR +
            '  object olabel:label' + CR +
            '    text = "Output file(s):"' + CR +
            '    top = $margin' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object output:string' + CR +
            '    hint = "Output filespec"' + CR +
            '    top = $margin' + CR +
            '    left = olabel.width + $margin' + CR +
            '    require = "_Name:"' + CR +
            '  end' + CR +
            '' +
            '  object Options:rectangle' + CR +
            '    object label3:label' + CR +
            '      text = "User:"' + CR +
            '    end' + CR +
            '    object Owner:string' + CR +
            '      text = ""' + CR +
            '      hint = "User name/UIC"' + CR +
            '      left = label3.left + label3.width' + CR +
            '    end' + CR +
            '    object label5:label' + CR +
            '      text = "File protections:"' + CR +
            '    end' + CR +
            '    object Protection:string' + CR +
            '      text = "false"' + CR +
            '      hint = "Protection for output file(s)"' + CR +
            '      left = label5.left + label5.wigth' + CR +
            '    end' + CR +
            '    object Log:boolean' + CR +
            '      text = "false"' + CR +
            '      falsehint = "Click to display create operations"' + CR +
            '      truehint = "Click to disable display of create operations"' + CR +
            '    end' + CR +
            'end' ;
This is the UI specification for the CREATE CUSP.

function Run : int64 ;

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

begin
    Result := 0 ;
    OS := new( POS_UOS, Init ) ;
    C := Get_Symbol( 'create$defaults' ) ;
    E := _Create( 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 similar to the Run method of other CUSPs, so we won't belabor it here.

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

var C, S : string ;
    Component : TUUI_Component ;
    DefDir, DefDev : string ;
    P : integer ;
    Priv : int64 ;
    FileOwner : cardinal ;
    DefProt, FileProt : int64 ;
    Protection_Mask : integer ;
    I : integer ;
    Interactive : boolean ;
    Log : boolean ;
    Node, Access, Secondary_Node, Device, Path, Name, Extension, Version : string ;
    OS : POS_UOS ;
    Outf : string ; // Name of output file(s)
    OutFile : TCOM_UOS_File ;
    Privileges : boolean ;
    Protection : int64 ;
    SL : TStringList ;
    UIC, Owner : Cardinal ;
    Use_Protection : boolean ;
    UUI : TUUI ;

begin
    // Setup...
    Result := 0 ;
    UUI := TUUI.Create ;
    UUI.Definition := UI ;
    DefDir := GETDDIR ;
    DefDev := Get_Symbol( 'sys$disk:' ) ;
    Priv := Our_Current_Privilege ;
    Protection_Mask := $FFFF ; // Assume nothing specified by user
    DefProt := Get_Default_File_Protection( 0 ) ;
    UIC := Our_UIC ;
    Interactive := Interactive_Input ; // sys%input is a terminal
    Privileges := ( Priv and ( SYSPRV or BYPASS ) ) <> 0 ; // Have privileges
    if( not Privileges ) then
    begin
        if( UIC < 8 ) then // System UIC
        begin
            Privileges := True ;
        end ;
    end ;
    OS := new( POS_UOS, Init ) ;
The main code for CREATE sets up some variables that we will use below. The Privileges boolean indicates if the user has either SYSPRV or BYPASS.

// 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 ) ) ;
    UUI.Command_Line := PChar( C ) ;

    // Processing...
    if( UUI.Execute( UI, Standalone ) ) then
    begin
Next we get the command line with defaults, and then execute the UI.

        // Parse and build output file list...
        Component := UUI.Get_Component_By_Name( 'Output' ) ;
        Outf := trim( Component.Text ) ;
        if( Outf = '' ) then
        begin
            Outf := UUI.Get_Parameter( 'Create', 'Output file' )
        end ;
        if( Outf = '' ) then
        begin
            Result := CUSP_OUTREQ ;
            exit ;
        end ;
        SL := TStringList.Create ;
        while( Outf <> '' ) do
        begin
            S := Parse_Parameter( ',', Outf ) ;
            PasStarlet.Parse_Filename( S, Node, Access, Secondary_Node, Device, Path,
                Name, Extension, Version ) ;
            if( ( Name = '' ) and ( Extension = '' ) ) then // No name or type specified
            begin
                Result := CUSP_OUTREQ ;
                SL.Free ;
                exit ;
            end ;
            if( Device = '' ) then
            begin
                Device := DefDev ;
            end ;
            if( Path = '' ) then
            begin
                Path := DefDir ;
            end ;
            S := Node + Access + Device + Path + Name + Extension + Version ;
            if( ( pos( '?', S ) + pos( '*', S ) ) > 0 ) then
            begin
                Result := CUSP_IVWILD ;
                SL.Free ;
                exit ;
            end ;
            SL.Add( S ) ;
        end ; // while( Output <> '' )
The output can be a single file or a comma-delimited list of files. So we must parse the output specification, defaulting the device and path, and adding each output file to the SL list. Wildcards are not valid in the output, so we exit with an error if such is encountered.

        Component := UUI.Get_Component_By_Name( 'Log' ) ;
        Log := Component.Text = 'true' ;
        Component := UUI.Get_Component_By_Name( 'Protection' ) ;
        Use_Protection := Component.UUI ;
        if( Use_Protection ) then
        begin
            S := Component.Text ;
            Protection := Parse_Protection( S, Protection_Mask ) ;
            if( Protection = -3 ) then
            begin
                Result := CUSP_CONFLICT ;
                exit ;
            end ;
            if( Protection = -2 ) then
            begin
                Result := CUSP_CONFLICT ;
                exit ;
            end ;
            if( Protection < 0 ) then
            begin
                Result := CUSP_CONFLICT ;
                exit ;
            end ;
        end ;
        Owner := 0 ;
        Component := UUI.Get_Component_By_Name( 'Owner' ) ;
        if( Component.UUI ) then
        begin
            Owner := Get_UIC_By_Name( Component.Text ) ;
            if( Owner = 0 ) then
            begin
                Result := CUSP_UNKUSE ;
                SL.Free ;
                exit ;
            end ;
            if( UIC <> Owner ) then // Setting owner different than us
            begin
                if( ( Priv and SYSPRV ) = 0 ) then // Do not have SYSPRV
                begin
                    Result := SS_NOPRIV ;
                    SL.Free ;
                    exit ;
                end ;
            end ;
        end ;
Next we get the log, protection, and owner qualifier values. A different owner is not allowed unless the user has SYSPRV. We exit if there are any errors.

        for I := 0 to SL.Count - 1 do
        begin
            S := SL[ I ] ;

            // Get file protection and owner, or directory owner if no file...
            FileProt := DefProt ;
            if( File_Exists( S ) ) then
            begin
                OutFile := Open_Binary_File( S, FAB_V_UFO ) ;
                if( Owner = 0 ) then // No owner specified
                begin
                    if( Privileges ) then
                    begin
                        FileOwner := Outfile.Owner ;
                    end else
                    begin
                        FileOwner := UIC ;
                    end ;
                end else
                begin
                    FileOwner := Owner ;
                end ;
                FileProt := OutFile.Protection ;
                OutFile.Free ;
            end else
            begin
                OutFile := Open_Binary_File( Path_For( S ), FAB_V_UFO ) ;
                if( Owner = 0 ) then // No owner specified
                begin
                    if( Privileges ) then
                    begin
                        FileOwner := Outfile.Owner ;
                    end else
                    begin
                        FileOwner := UIC ;
                    end ;
                end else
                begin
                    FileOwner := Owner ;
                end ;
                OutFile.Free ;
            end ;
Now we iterate through the output files. If the file already exists, we get the file's current owner and protection. If it doesn't, we get the owner of the parent directory. If we have privileges, and no owner qualifier was used, we use the file/directory owner, otherwise, we will use our own UIC as the owner.

            // Create file...
            OutFile := Open_Binary_File( S, FAB_V_CIF or FAB_V_SUP or FAB_V_UFO ) ;
            if( OutFile = nil ) then
            begin
                Result := LIB_Get_Exception( 0 ) ;
                exit ;
            end ;
            Outfile.Owner := FileOwner ;
            if( Use_Protection ) then
            begin
                OutFile.Protection := Protection or ( FileProt and Protection_Mask ) ;
            end ;

            // Get file contents from input...
            while( True ) do
            begin
                S := Get_Input( '' ) ;
                if( S = Control_Z ) then
                begin
                    break ;
                end ;
                if( not Interactive_Input ) then
                begin
                    if( copy( S, 1, 1 ) = '$' ) then
                    begin
                        break ;
                    end ;
                end ;
                S := Replace( S, CR, '', 0 ) ;
                OutFile.Writeln( PChar( S ) ) ;
                if( Outfile.IO_Error <> nil ) then
                begin
                    Result := Outfile.IO_Error.Get_Error ;
                    exit ;
                end ;
            end ;

            // Finish this file...
            OutFile.Free ;

            if( Log ) then
            begin
                OS^.OutputLn( RH_SysOutput, '%CREATE-I-CREATED, ' + S + ' created' ) ;
            end ;
        end ; // for I := 0 to SL.Count - 1
    end ; // if( UUI.Execute )
end ; // _Create
Next, we create the output file, setting the owner and/or protection code. Then we get the input to put into the file. A control-Z will exit the process. If we are not interactive (input is not from a terminal), then if the line begins with a dollar sign ($), the input is terminated. If there is a write error, we exit.

When done with the input, we close the file, and then write a log of the creation if requested by the user. Then we loop back for the next file, if one.


Finally, we've added a few routines to the CUSPLib module to support CREATE and other CUSPS.

function Our_UIC : cardinal ;

var s : string ;

begin
    S := GetJPI( 0, JPI_UIC ) ;
    Result := 0 ;
    move( PChar( S )[ 0 ], Result, sizeof( Result ) ) ;
end ;


function Our_Current_Privilege : int64 ;

var S : string ;

begin
    S := GetJPI( 0, JPI_CURPRIV ) ;
    move( PChar( S )[ 0 ], Result, sizeof( Result ) ) ;
end ;


function Path_For( S : string ) : string ;

var I : integer ;

begin
    I := length( S ) ;
    while( ( I > 0 ) and ( S[ I ] <> '\' ) ) do
    begin
        dec( I ) ;
    end ;
    Result := copy( S, 1, I ) ;
    if( Result = '' ) then
    begin
        Result := '\' ;
    end ;
end ;


var Descriptor : array[ 0..1 ] of TDVI_Descriptor ;
    SYS_Descriptor : array[ 0..3 ] of TSYS_Descriptor ;
    Buff, BufLen, Buff1, BufLen1 : int64 ;
    Buf : array[ 0..1023 ] of byte ;

function Interactive_Input : boolean ;

var IOSB : TIOSB ;

begin
    Buff := 0 ;
    BufLen := 0 ;
    fillchar( Descriptor, sizeof( Descriptor ), 0 ) ;
    Descriptor[ 0 ].Buffer_Length := sizeof( Buff ) ;
    Descriptor[ 0 ].Item_Code := DVI_DEVCHAR ; // DVIDEF item
    Descriptor[ 0 ].Buffer_Address := integer( @Buff ) ;
    Descriptor[ 0 ].Return_Length_Address := integer( @BufLen ) ;
    GETDVIW( 0, RH_SysInput, '', integer( @Descriptor ), integer( @IOSB ),
        0, 0, '', 1 ) ;
    Result := ( ( Buff and DEV_V_TRM ) <> 0 ) ; // Terminal device
end ;


var Descriptors : array[ 0..1 ] of TSYS_Descriptor ;
    Len : int64 ;

function Get_UIC_By_Name( S : string ) : cardinal ;

var I : integer ;

begin
    fillchar( Descriptors, sizeof( Descriptors ), 0 ) ;
    Descriptors[ 0 ].Item_Code := UAI_UIC ;
    Descriptors[ 0 ].Buffer_Length := sizeof( Result ) ;
    Descriptors[ 0 ].Buffer_Address := int64( @Result ) ;
    Descriptors[ 0 ].Return_Length_Address := int64( @Len ) ;
    Descriptors[ 0 ].MBO := 1 ;
    Descriptors[ 0 ].MBMO := $FFFFFFFF ;
    Len := 0 ;
    if( GETUAI( 0, S, int64( @Descriptors ) ) <> 0 ) then // Name lookup failed
    begin
        if( not trystrtoint( S, I ) ) then // Try S as UIC value
        begin
            Result := 0 ;
            exit ;
        end ;
        Result := I ;
    end ;
end ;
Our_UIC gets the current user's UIC. Out_Current_Privilege gets the current process privilege. Path_For extracts the device and path from a file specification. Interactive_User returns true if SYS$INPUT is from a terminal. Get_UIC_By_Name converts a user name to a UIC.

In the next article, we will cover CREATE/DIRECTORY.