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 UCL Command execution
62 UCL Command execution, part 2
63 UCL Command Abbreviation
64 ASTs
65 UCL Expressions, Part 1
66 UCL Expressions, Part 2: Support code
67 UCL Expressions, part 3: Parsing
68 SYS_GETJPIW and SYS_TRNLNM
69 UCL 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 UCL Lexical functions: F$GETDVI
98 Parse_GetDVI

Glossary/Index


Download sources
Download binaries

Lexical functions - F$DIRECTORY

The next lexical function is F$DIRECTORY. Each UOS process has a default directory. On creation, the process' default directory is set to the user's home directory. Here is the function description:

Returns the currrent default directory name. F$DIRECTORY has no arguments, but the parentheses must follow the function name.

Format
F$DIRECTORY()

Return Value
The name of the current default directory name.

Arguments
None.

Description
The F$DIRECTORY lexical function can be used to save the name of the current default directory so that it can be used to restore the default directory later.

Example
$ X = F$DIRECTORY() This would return the process' current default directory.

        Function_Directory : begin 
                                 if( Missing_Parentheses( '(' ) ) then
                                 begin
                                     exit ;
                                 end ;
                                 if( Missing_Parentheses( ')' ) ) then
                                 begin
                                     exit ;
                                 end ;
                                 S := Parse_Directory( Err, Context ) ;
                             end ;
We add handling for this lexical function in Function_Reference.

function Parse_Directory( var Err : integer ; var Context : string ) : string ;

begin
    Err := 0 ;
    Result := GETDDIR ;
end ;
For now, Parse_Directory simply calls the PasStarlet GETDIR function.

function GETDDIR : string ;

var Len : int64 ;
    SRB : TSRB ;
    Status : integer ;

begin
    fillchar( SRB, sizeof( SRB ), 0 ) ;
    Len := 0 ;
    Status := SYS_GETDDIR( 0, int64( @SRB ), int64( @Len ) ) ; // Get length
    if( Status <> 0 ) then
    begin
        Result := '' ;
        exit ;
    end ;

    setlength( Result, Len ) ;
    Set_String( Result, SRB ) ;
    Status := SYS_GETDDIR( 0, int64( @SRB ), int64( @Len ) ) ;
    if( Status <> 0 ) then
    begin
        Result := '' ;
        exit ;
    end ;
end ; // GETDDIR
This function creates a TSRB structure with a zero address, which is passed to SYS_GETDDIR. The use of a zero address means that we want the length of the default directory string returned to us. VMS directory paths had a maximum length of 64, but UOS has no limit on the length of paths (other than the capacity of memory to contain them). Thus, we cannot set aside some buffer and always expect the path to fit into it. That is why we query the length first. We then set the Result length and construct a new TSRB that points to that result. Finally we call SYS_GETDDIR again, which writes the path to the function result.

function SYS_GETDDIR( PID : TPID ; Res, Len : int64 ) : int64 ;

var SRB : PSRB ;
    Status : int64 ;
    SysRequest : TS1I3_Request ;

begin
    SRB := PSRB( pointer( Res ) ) ;
    fillchar( SysRequest, sizeof( SysRequest ), 0 ) ;
    Status := 0 ;
    SysRequest.Request.Subsystem :=  UOS_Subsystem_USC ;
    SysRequest.Request.Request := UOS_USC_Get_Default_Directory ;
    SysRequest.Request.Length := sizeof( SysRequest ) - sizeof( Sysrequest.Request ) ;
    SysRequest.Request.Status := integer( @Status ) ;
    SysRequest.SRB.Buffer := SRB.Buffer ;
    SysRequest.SRB.Length := SRB.Length ;
    SysRequest.Integer1 := PID ;
    SysRequest.Integer2 := Res ;
    SysRequest.Integer3 := Len ;

    Call_To_Ring0( integer( @SysRequest ) ) ;

    Result := Status ;
end ;
This function merely packages the parameters into a system request structure and calls the executive, like most other SYS calls we've looked at in the past.

        UOS_USC_Get_Default_Directory:
            begin
                UE := Enter_System_Call( Request, SReq, PID, MMC, 
                    sizeof( TS1I3_Request ) - sizeof( SReq ), Address ) ;
                if( UE <> nil ) then
                begin
                    Set_Last_Error( UE ) ;
                    exit ;
                end ;
                try
                    S1I3_Request := PS1I3_Request( Address ) ;
                    Get_Default_Directory( PID, S1I3_Request.SRB,
                        S1I3_Request.Integer1, S1I3_Request.Integer2, S1I3_Request.Integer3, IOSB ) ;
                    Write_User( Kernel, PID, S1I3_Request.Request.Status, sizeof( IOSB.r_io_64.w_status ), 
                        IOSB.r_io_64.w_status ) ;
                finally
                    Exit_System_Call( Request, PID, MMC, sizeof( TS1I3_Request ) - sizeof( SReq ) ) ;
                end ;
            end ;
We add code to the TUSC.API method to handle the new request. It simply calls the Get_Default_Directory method with the passed values.

procedure TUSC.Get_Default_Directory( PID : TPID ; SRB : TSRB ;
    rPID, rRes, rLen : int64 ; var IOSB : TIOSB ) ;

var Process : TProcess ;
    S : string ;
    Status : integer ;

begin
    // Seutp...
    if( rPID = 0 ) then
    begin
        rPID := PID ;
    end ;
The Get_Default_Directory method returns the default directory for a process. If the requested process is 0, we return the default directory of the current process.

    Process := Get_Process( rPID ) ;
    if( Process = nil ) then
    begin
        IOSB.r_io_64.w_status := UE_Error ;
        Generate_Exception( UOSErr_Nonexistent_Process ) ;
        exit ;
    end ;
Next we get the requested process instance. If it is nil, the process ID does not denote an existing process so we return an error.

    // Get directory and make sure it doesn't exceed return buffer length...
    S := Process.Default_Directory ;
    if( SRB.Buffer = 0 ) then // Requesting length
    begin
        Status := Write_User_int64( Kernel, PID, rLen, length( S ) ) ;
        if( Status = UE_Error ) then
        begin
            IOSB.r_io_64.w_status := Status ;
            if( MMC.Last_Error = nil ) then
            begin
                Generate_Exception( UOSErr_Memory_Address_Error ) ;
            end ;
        end ;
        exit ;
    end ; // if( SRB.Address = 0 )
    if( length( S ) > SRB.Length ) then
    begin
        setlength( S, SRB.Length ) ;
    end ;
Next, we get the default directory string. If the target address is 0, the user only wants the length. In that case, we write the length to the user space and exit. Otherwise, we make sure the string doesn't exceed the passed buffer length. If it does, we truncate it to that length.

    // Write length back to user space...
    Status := Write_User_int64( Kernel, PID, rLen, length( S ) ) ;
    if( Status = UE_Error ) then
    begin
        IOSB.r_io_64.w_status := Status ;
        if( MMC.Last_Error = nil ) then
        begin
            Generate_Exception( UOSErr_Memory_Address_Error ) ;
        end ;
        exit ;
    end ;

    // Write the string back to user space...
    Status := Set_User_String( Kernel, PID, SRB, S ) ;
    if( Status = UE_Error ) then
    begin
        IOSB.r_io_64.w_status := Status ;
        if( MMC.Last_Error = nil ) then
        begin
            Generate_Exception( UOSErr_Memory_Address_Error ) ;
        end ;
    end ;
end ; // TUSC.Get_Default_Directory
Finally, we write the directory string and its length to the user's memory space.

    if( UIC = 1 ) then
    begin
        UString := SSC.Resolve_Symbol( PID, 'sys$system:' ) ;
        // Note: don't free result - it points to a persistent string
        S := UString.Contents ;
    end else
    begin
        S := User.Get_Home ;
    end ;
    Loop := pos( ':', S ) ;
    US := Pascal_To_UOS_String( copy( S, 1, Loop ) ) ;
    SSC.Set_Symbol( LNM_PROCESS, PID, 'SYS$DISK:', US ) ;
    US.Free ;
    Process.Default_Directory := copy( S, Loop + 1, length( S ) ) ;
We've talked about how to get a process' default directory in this article, and in an earlier article we talked about the presence of the home directory in the SYSUAF file. Now we will add code to the end of TUSC.Force_Login that will copy the home directory into the process' default directory. These are two different values: a process can change its default directory at will without affecting the user's home directory. Likewise, the user's home directory can be changed without altering any existing processes. The home directory is copied to the process at the point that the process is logged-in and associated with a user. Obviously, we can't do this prior to knowing for which user to copy the home directory. You may also recall that the Startup user (UIC=1) is a special case. Startup will always start with a home directory of "sys$system:". Note that the default directory doesn't include the device - instead the process SYS$DISK symbol is set to the device portion of the default directory.

In the next article, we'll look at the next lexical function.

 

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