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$EXTRACT and F$IDENTIFIER

The next lexical function is F$EXTRACT. Here is the definition of the function.

F$EXTRACT extracts the specified characters from the specified string.

Format
F$EXTRACT(start,length,value)

Return Value
A string containing the characters delimited by the start and length arguments. The source value is not modified.

Arguments
start
Specifies the offset of the starting character of the value that you want to extract. The offset of the first character is 0.

length

Specifies the number of characters from the value that you want to extract. The length is an integer expression that is 0 or larger. If the length exceeds the number of characters from the offset to the end of the string, F$EXTRACT returns the characters from the start to the end of the string.

value

Specifies the character string to extract characters from.

Example
$ X = F$EXTRACT(0,4,"ABCDEFG")
This would set X to "ABCD".

        Function_Extract : begin
                               if( Missing_Parentheses( '(' ) ) then
                               begin
                                   exit ;
                               end ;
                               if( Parse_Extract( Err, Context ) ) then
                               begin
                                   exit ;
                               end ;
                               if( Missing_Parentheses( ')' ) ) then
                               begin
                                   exit ;
                               end ;
                               S := Context ;
                           end ;
We add this code to Function_Reference.

function Parse_Extract( var Err : integer ; var Context : string ) : boolean ;

var Length, Start : int64 ;
    S : string ;

begin
    Result := False ; // Assume success

    // Get starting offset...
    Start := Get_Numeric_Parameter( Err, Context ) ;
    if( Err <> 0 ) then
    begin
        Result := True ; // Assume error
        exit ;
    end ;
    if( Missing_Comma( Err ) ) then
    begin
        exit ;
    end ;

    // Get length...
    Length := Get_Numeric_Parameter( Err, Context ) ;
    if( Err <> 0 ) then
    begin
        Result := True ; // Assume error
        exit ;
    end ;
    if( Missing_Comma( Err ) ) then
    begin
        exit ;
    end ;

    // Get value...
    S := Get_Parameter( Err, Context ) ;
    if( Err <> 0 ) then
    begin
        exit ;
    end ;

    // Check special conditions...
    if( ( Start < 0 ) or ( Length < 1 ) ) then
    begin
        Context := '' ;
        exit ;
    end ;

    Context := copy( S, Start + 1, Length ) ;
end ; // Parse_Extract
This function gets the parameters and returns the appropriate substring.


The next lexical function is F$IDENTIFIER. Here is the definition of the function.

F$IDENTIFIER converts a UIC to a username or a username to a UIC.

Format
F$IDENTIFIER(value,type)

Return Value
A string containing the username for the passed UIC or an integer containing the UIC for the passed username. If the value is invalid or represents a user or UIC that doesn't exist, the funtion returns a null string ("") if you are converting from UIC to name, or 0 if you are converting from name to UIC.

Arguments
value

Specifies the offset of the starting character of the value that you want to extract. The offset of the first character is 0.

type

Specifies the type of conversion to perform. If this is "NAME_TO_NUMBER", the value parameter is treated as a username and the function returns the UIC for that user. If this is "NUMBER_TO_NAME", the value parameter is treated as a UIC and the function returns the username for that UIC.

        Function_IDENTIFIER : begin
                                  if( Missing_Parentheses( '(' ) ) then
                                  begin
                                      exit ;
                                  end ;
                                  if( Parse_Identifier( Err, Context ) ) then
                                  begin
                                      exit ;
                                  end ;
                                  if( Missing_Parentheses( ')' ) ) then
                                  begin
                                      exit ;
                                  end ;
                                  S := Context ;
                              end ;
We add this code to the Function_Reference function.

function Parse_Identifier( var Err : integer ; var Context : string ) : boolean ;

var I : int64 ;
    S, S1, Value : string ;

begin
    Result := False ; // Assume success

    // Get value...
    Value := Get_Parameter( Err, Context ) ;
    if( Err <> 0 ) then
    begin
        Result := True ; // Assume error
        exit ;
    end ;
    if( Missing_Comma( Err ) ) then
    begin
        exit ;
    end ;

    // Get conversion type...
    S1 := Get_Parameter( Err, Context ) ;
    S := lowercase( S1 ) ;
    if( Err <> 0 ) then
    begin
        Result := True ; // Assume error
        exit ;
    end ;
This function handles the work of the lexical function. First, we obtain the arguments.

    // Do the conversion...
    if( S = 'name_to_number' ) then
    begin
        I := Get_UAF( UAI_UIC, Err, Value ) ;
        if( Err <> 0 ) then
        begin
            Context := '' ;
        end else
        begin
            Context := inttostr( I ) ;
        end ;
    end else
    if( S = 'number_to_name' ) then
    begin
        if( Valid_Int( Value, I ) ) then
        begin
             Context := GETNAME( I ) ;
        end else
        begin
             Context := '' ;
        end ;
    end else
    begin
        Err := UCL_IVKEYW ;
        Context := S1 ;
    end ;
end ; // Parse_Identifier
Depending upon the conversion specification, we either call Get_UAF to get the UIC, or GETNAME to get the user name, or return an error. Note that VMS could have many usernames assigned to a single UIC, so there is no system call that would allow a UIC-to-username conversion (you had to scan the SYSUAF file). UOS doesn't allow this situation, so we add a system call that is unique to UOS for this purpose. We covered Get_UAF in previous articles. We will examine GETNAME here.

function GETNAME( I : int64 ) : string ;

var SRB : TSRB ;
    Len : int64 ;

begin
    fillchar( SRB, sizeof( SRB ), 0 ) ;
    SYS_GETNAME( SRB, I, int64( @Len ) ) ;
    setlength( Result, Len ) ;
    Set_String( Result, SRB ) ;
    SYS_GETNAME( SRB, I, int64( @Len ) ) ;
end ;
This new PasStarlet function wraps the SYS_GETNAME system call. First, we make a call with a null SRB to obtain the length of the user name. Then we set the receiving buffer (Result) length, set up the SRB structure, and call SYS_GETNAME to get the name.

function SYS_GETNAME( SRB : TSRB ; UIC, LenDst : int64 ) : int64 ;

var SysRequest : TS1I3_Request ;
    IOSB : TIOSB ;
    Status : byte ;

begin
    fillchar( SysRequest, sizeof( SysRequest ), 0 ) ;
    SysRequest.Request.Subsystem := UOS_Subsystem_USC ;
    SysRequest.Request.Request := UOS_USC_GetNAME ;
    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 := UIC ;
    SysRequest.Integer2 := LenDst ;
    SysRequest.Integer3 := int64( @IOSB ) ;

    Call_To_Ring0( integer( @SysRequest ) ) ;
    Result := IOSB.r_io_64.w_status ;
end ;
SYS_GETNAME is a wrapper for the system call, just like all the others before.

        UOS_USC_GetNAME:
            begin
                UE := Enter_System_Call( Request, SReq, PID, MMC, sizeof( S1I3_Request ) - sizeof( SReq ), 
                    Address ) ;
                if( UE <> nil ) then
                begin
                    Set_Last_Error( UE ) ;
                    exit ;
                end ;
                try
                    S1I3_Request := PS1I3_Request( Address ) ;
                    Get_UIC_Name( Kernel, PID, S1I3_Request.SRB, S1I3_Request.Integer1, 
                        S1I3_Request.Integer2, IOSB ) ;
                    Write_User( Kernel, PID, S1I3_Request.Integer3, sizeof( IOSB ), IOSB ) ;
                finally
                    Exit_System_Call( Request, PID, MMC, sizeof( S1I3_Request ) - sizeof( SReq ) ) ;
                end ;
            end ;
This code is added to the TUSC.API method to handle the new system call.

procedure TUSC.Get_UIC_Name( Kernel : TUOS_Kernel ; PID : TPID ;
    SRB : TSRB ; UIC, LenDst : int64 ; var IOSB : TIOSB ) ;

var Len : int64 ;
    S : string ;
    User : TUser ;

begin
    User := Get_User( UIC ) ;
    if( User = nil ) then
    begin
        IOSB.r_io_64.w_status := UE_Error ;
        Generate_Exception( UOSErr_User_Not_Found ) ;
        exit ;
    end ;
    try
        S := User.Name ;
        if( LenDst <> 0 ) then
        begin
            Len := length( S ) ;
            IOSB.r_io_64.w_status := Write_User( Kernel, PID, LenDst, sizeof( int64 ), Len ) ;
            if( IOSB.r_io_64.w_status <> 0 ) then
            begin
                exit ;
            end ;
        end ;
        if( ( SRB.Buffer <> 0 ) and ( SRB.Length <> 0 ) ) then
        begin
            if( SRB.Length < length( S ) ) then
            begin
                SRB.Length := length( S ) ;
            end ;
            IOSB.r_io_64.w_status := Set_User_String( Kernel, PID, SRB, S ) ;
            if( IOSB.r_io_64.w_status <> 0 ) then
            begin
                exit ;
            end ;
        end ;
    finally
        User.Detach ;
    end ;
end ; // TUSC.Get_UIC_Name
This method get the user for the passed UIC. If the result of Get_User is nil, the UIC doesn't exist and we return an exception. Otherwise, we get the name from the user instance. If there is a non-zero length destination address, we write the length to that address in the user space. If the SRB buffer and length are non-zero we then write the user name to the user memory space, at least up to the length of the receiving buffer.

In the next article, we'll look at the FAO service.

 

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