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

Glossary/Index


Download sources
Download binaries

Lexical functions - F$FILE_ATTRIBUTES

Before we address the F$FILE_ATTRIBUTES function, we need to make note of the F$FID_TO_NAME function. The VMS file system works differently than the UOS file system and the DCL lexical function F$FID_TO_NAME is unique to that VMS file system. There is no analog in UOS to a file identifier, so this is one of those rare cases where this VMS feature simply is not present in UOS.

The F$FILE_ATTRIBUTES lexical function returns information about a file. This is UCL's interface to the SYS$DISPLAY system call. Here is the definition.

F$FILE_ATTRIBUTES returns attribute information for a specified file.

Format
F$FILE_ATTRIBUTES(filespec,item)

Return Value
Either an integer or a string, depending upon the item requested. The table below indicates the return values.

Arguments
filespec

Specifies the name of the file for which you are requesting information.

item

Specifies the type of information to return. The table below indicates the item values.

ItemInformation Returned
AITRUE if after-image journaling is is enabled, FALSE otherwise.
ALQRMS allocation quantity.
BDTBackup date/time.
BITRUE if before-image jouranling is enabled, FALSE otherwise.
BKSRMS bucket size.
BLSRMS block size.
CBTTRUE if contiguous-best-try, FALSE otherwise.
CDTCreation date/time.
CTGTRUE if contiguous, FALSE otherwise.
DEQRMS default extension quatity.
DIDThis doesn't have any meaning in UOS. It returns null.
DIRECTORYTRUE if file is a directory, FALSE otherwise.
DVIName of the device the file resides on.
EDTExpiration date/time.
EOFLogical end of file - logical file length in bytes.
ERASETRUE if file is erased when deleted, FALSE otherwise.
FFBRMS First Free Byte.
FIDAlways returns 0.
FILE_LENGTH_HINTRMS file length hint.
FSZRMS fixed control area size.
GBCGlobal buffer count.
GBC32Enhanced global buffer count.
GBCFLAGSGlobal buffer cache flags. Returns PERCENT, DEFAULT, or NONE.
GRPGroup name for file.
JOURNAL_FILETRUE if this is a journal file, FALSE otherwise.
KNOWNTRUE if this file was installed with the Install utility, FALSE otherwise.
LOCKEDTRUE if the file is deaccessed-locked, FALSE otherwise.
LRLRMS logest record length.
MBMAlways 0.
MOVETRUE if movefile operations are enabled, FALSE otherwise.
MRNRMS maximum record number.
MRSMaximum record size (record size for fixed-format RMS and non-RMS files).
NOARMS number of areas.
NOBACKUPFALSE if the file is marked for backup, TRUE otherwise.
NOKRMS number of keys.
ORGRMS file organization: SEQ, REL, IDX. Returns null if not an RMS file.
PRESHELVEDTRUE if file is pre-shelved, FALSE otherwise.
PROFile protection string.
PVNRMS prolog version number.
RATRMS record attributes. Returns null if not an RMS file.
RCKTRUE if file is marked Read-check, FALSE otherwise.
RDTRevision date/time.
RFMRMS record format string: VAR, FIX, VFC, UDF, STM, STMLF, or STMCR. Returns null if not an RMS file.
RUTRUE if recovery unit journaling is enabled, FALSE otherwise.
RVNRevision number.
SHELVABLETRUE if file is shelvable, FALSE otherwise.
SHELVEDTRUE if file is shelved, FALSE otherwise.
STORED_SEMANTICSRMS stored semantics.
UICFile owner UIC.
VERLIMITVersion limit number. 0 indicates no version limit.
WCKTRUE if file is write-checked, FALSE otherwise.

Example:
$ FILE_EOF = F$FILE_ATTRIBUTES("CALC.EXE","EOF")
This would assign the logical file size, in bytes, to the FILE_EOF symbol.

        Function_File_Attributes : begin
                                       if( Missing_Parentheses( '(' ) ) then
                                       begin
                                           exit ;
                                       end ;
                                       if( Parse_File_Attributes( Err, Context ) ) then
                                       begin
                                           exit ;
                                       end ;
                                       if( Missing_Parentheses( ')' ) ) then
                                       begin
                                           exit ;
                                       end ;
                                       S := Context ;
                                   end ;
This code is added to the Function_Reference function.

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

var E : int64 ;
    Filespec, Item, Dev, Dir, Ext, Node, Ver : string ;
    P : integer ;
    X : int64 ;
    FAB : TFAB ;
    NAM : TNAML ;
    XABALL : TXABALL ;
    XABDAT : TXABDAT ;
    XABFHC : TXABFHC ;
    XABKEY : TXABKEY ;
    XABPRO : TXABPRO ;
    XABRDT : TXABRDT ;
    XABSUM : TXABSUM ;

begin
    Result := False ; // Assume success

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

    // Get item value...
    Item := lowercase( Get_Parameter( Err, Context ) ) ;
    if( Err <> 0 ) then
    begin
        Result := True ; // Assume error
        exit ;
    end ;
First, this function gets the two function parameters and exit if there's an error.

    // Parse the file name...
    P := pos( '::', Filespec ) ;
    Node := copy( Filespec, 1, P - 1 ) ;
    if( P > 0 ) then
    begin
        Filespec := copy( Filespec, P + 2, length( Filespec ) ) ;
    end ;
    P := pos( ':', Filespec ) ;
    Dev := copy( Filespec, 1, P - 1 ) ;
    Filespec := copy( Filespec, P + 1, length( Filespec ) ) ;
    P := RInstr( length( Filespec ), Filespec, '\' ) ;
    Dir := copy( Filespec, 1, P ) ;
    Filespec := copy( Filespec, P + 1, length( Filespec ) ) ;
    P := RInstr( length( Filespec ), Filespec, '.' ) ;
    Ext := copy( Filespec, P, length( Filespec ) ) ;
    Filespec := copy( Filespec, 1, P - 1 ) ;
    Ver := '' ;
    P := pos( ';', Ext ) ;
    if( P > 0 ) then
    begin
        if( trystrtoint64( copy( Ext, P + 1, length( Ext ) ), X ) ) then
        begin
            Ver := copy( Ext, P + 1, length( Ext ) ) ;
            setlength( Ext, P - 1 ) ;
        end ;
    end ;
This code parses a filename into its constituent pieces.

    // Set up structures...
    fillchar( FAB, sizeof( FAB ), 0 ) ;
    fillchar( NAM, sizeof( NAM ), 0 ) ;
    fillchar( XABALL, sizeof( XABALL ), 0 ) ;
    fillchar( XABDAT, sizeof( XABDAT ), 0 ) ;
    fillchar( XABFHC, sizeof( XABFHC ), 0 ) ;
    fillchar( XABKEY, sizeof( XABKEY ), 0 ) ;
    fillchar( XABPRO, sizeof( XABPRO ), 0 ) ;
    fillchar( XABRDT, sizeof( XABRDT ), 0 ) ;
    fillchar( XABSUM, sizeof( XABSUM ), 0 ) ;

    FAB.FAB_B_BLN := sizeof( FAB ) ;
    FAB.FAB_L_NAM := int64( @NAM ) ;
    FAB.FAB_L_XAB := int64( @XABALL ) ;

    NAM.NAML_B_BID := NAML_C_BID ;
    NAM.NAML_B_BLN := sizeof( NAM ) ;
    NAM.NAML_L_LONG_DEV := int64( PChar( Dev ) ) ;
    NAM.NAML_B_LONG_DEV_SIZE := length( Dev ) ;
    NAM.NAML_L_LONG_DIR := int64( PChar( Dir ) ) ;
    NAM.NAML_L_LONG_DIR_SIZE := length( Dir ) ;
    NAM.NAML_L_LONG_NAME := int64( PChar( Filespec ) ) ;
    NAM.NAML_L_LONG_NAME_SIZE := length( Filespec ) ;
    NAM.NAML_L_LONG_NODE := int64( PChar( Node ) ) ;
    NAM.NAML_L_LONG_NODE_SIZE := length( Node ) ;
    NAM.NAML_L_LONG_TYPE := int64( PChar( Ext ) ) ;
    NAM.NAML_L_LONG_TYPE_SIZE := length( Ext ) ;
    NAM.NAML_L_LONG_VER := int64( PChar( Ver ) ) ;
    NAM.NAML_L_LONG_VER_SIZE := length( Ver ) ;

    XABALL.XAB_B_COD := XAB_C_ALL ;
    XABALL.XAB_B_BLN := sizeof( XABALL ) ;
    XABALL.XAB_L_NXT := int64( @XABDAT ) ;

    XABDAT.XAB_B_COD := XAB_C_DAT ;
    XABDAT.XAB_B_BLN := sizeof( XABDAT ) ;
    XABDAT.XAB_L_NXT := int64( @XABFHC ) ;

    XABFHC.XAB_B_COD := XAB_C_FHC ;
    XABFHC.XAB_B_BLN := sizeof( XABFHC ) ;
    XABFHC.XAB_L_NXT := int64( @XABKEY ) ;

    XABKEY.XAB_B_COD := XAB_C_KEY ;
    XABKEY.XAB_B_BLN := sizeof( XABKEY ) ;
    XABKEY.XAB_L_NXT := int64( @XABPRO ) ;

    XABPRO.XAB_B_COD := XAB_C_PRO ;
    XABPRO.XAB_B_BLN := sizeof( XABPRO ) ;
    XABPRO.XAB_L_NXT := int64( @XABRDT ) ;

    XABRDT.XAB_B_COD := XAB_C_RDT ;
    XABRDT.XAB_B_BLN := sizeof( XABRDT ) ;
    XABRDT.XAB_L_NXT := int64( @XABSUM ) ;

    XABSUM.XAB_B_COD := XAB_C_SUM ;
    XABSUM.XAB_B_BLN := sizeof( XABSUM ) ;
Next we create our FAB, NAML, and XAB structures. We described these structures in the previous article. First, we zero-out all of the structures. Then we fill the FAB fields with the FAB size and pointers to the NAML and XAB blocks. Then we put pointers into the NAML block to the strings we parsed the filename into. Finally we construct the XAB chain by setting the pointers and sizes of the various XAB structures.

    if( SYS_DISPLAY( int64( @FAB ), 0, 0 ) <> 0 ) then
    begin
        E := LIB_Get_Exception( 0 ) ; // Get handle
        Set_Exception( LIB_Get_Exception_Text( 0, E ) ) ;
        exit ;
    end ;
Now we call the SYS_DISPLAY routine to fill our structures. If there was an error, we set our exception state and exit.

    // Provide requested data...
    if( Item = 'ai' ) then
    begin
        Context := 'FALSE' ;
    end else
    if( Item = 'alq' ) then
    begin
        Context := inttostr( XABALL.XAB_L_ALQ ) ;
    end else
    if( Item = 'bdt' ) then
    begin
        Context := ASCTIM( XABDAT.XAB_Q_BDT ) ;
    end else
    if( Item = 'bi' ) then
    begin
        Context := 'FALSE' ;
    end else
    if( Item = 'bks' ) then
    begin
        Context := inttostr( XABFHC.XAB_B_BKS ) ;
    end else
    if( Item = 'bls' ) then
    begin
        Context := inttostr( FAB.FAB_W_BLS ) ;
    end else
    if( Item = 'cbt' ) then  // Same as 'ctg'
    begin
        if( ( XABFHC.XAB_W_FLG and FAF_CONTIGUOUS ) = 0 ) then
        begin
            Context := 'FALSE' ;
        end else
        begin
            Context := 'TRUE' ;
        end ;
    end else
    if( Item = 'cdt' ) then
    begin
        Context := ASCTIM( XABDAT.XAB_Q_CDT ) ;
    end else
    if( Item = 'ctg' ) then
    begin
        if( ( XABFHC.XAB_W_FLG and FAF_CONTIGUOUS ) = 0 ) then
        begin
            Context := 'FALSE' ;
        end else
        begin
            Context := 'TRUE' ;
        end ;
    end else
    if( Item = 'deq' ) then
    begin
        Context := inttostr( XABALL.XAB_W_DEQ ) ;
    end else
    if( Item = 'did' ) then
    begin
        Context := '' ; // No meaning in UOS
    end else
    if( Item = 'directory' ) then
    begin
        if( ( XABFHC.XAB_W_FLG and FAF_DIRECTORY ) = 0 ) then
        begin
            Context := 'FALSE' ;
        end else
        begin
            Context := 'TRUE' ;
        end ;
    end else
    if( Item = 'dvi' ) then
    begin
        Context := Dev ;
    end else
    if( Item = 'edt' ) then
    begin
        Context := ASCTIM( XABDAT.XAB_Q_EDT ) ;
    end else
    if( Item = 'eof' ) then
    begin
        Context := inttostr( XABFHC.XAB_Q_EOF ) ;
    end else
    if( Item = 'erase' ) then
    begin
        if( ( XABFHC.XAB_W_FLG and FAF_DSM_MASK ) < 2 ) then
        begin
            Context := 'FALSE' ;
        end else
        begin
            Context := 'TRUE' ;
        end ;
    end else
    if( Item = 'ffb' ) then
    begin
        Context := inttostr( XABFHC.XAB_Q_EOF ) ; // Same as 'eof' for now
    end else
    if( Item = 'fid' ) then
    begin
        Context := '0' ;
    end else
    if( Item = 'file_length_hint' ) then
    begin
        Context := '' ; // RMS-specific
    end else
    if( Item = 'fsz' ) then
    begin
        Context := inttostr( XABFHC.XAB_B_HSZ ) ;
    end else
    if( ( Item = 'gbc' ) or ( Item = 'gbc32' ) ) then
    begin
        Context := inttostr( XABFHC.XAB_W_GBC ) ;
    end else
    if( Item = 'gbcflags' ) then
    begin
        Context := 'NONE' ; //TODO
    end else
    if( Item = 'grp' ) then
    begin
        Context := '0' ; //TODO
    end else
    if( Item = 'journal_file' ) then
    begin
        Context := 'FALSE' ;
    end else
    if( Item = 'known' ) then
    begin
        Context := 'FALSE' ; // TODO
    end else
    if( Item = 'locked' ) then
    begin
        Context := 'FALSE' ; // TODO
    end else
    if( Item = 'lrl' ) then
    begin
        Context := inttostr( XABFHC.XAB_W_LRL ) ;
    end else
    if( Item = 'mbm' ) then
    begin
        Context := '' ; // Not supported on UOS
    end else
    if( Item = 'move' ) then
    begin
        Context := 'FALSE' ; //TODO
    end else
    if( Item = 'mrn' ) then
    begin
        Context := inttostr( FAB.FAB_L_MRN ) ;
    end else
    if( Item = 'mrs' ) then
    begin
        Context := inttostr( XABFHC.XAB_W_MRS ) ;
    end else
    if( Item = 'noa' ) then
    begin
        Context := inttostr( XABSUM.XAB_B_NOA ) ;
    end else
    if( Item = 'nobackup' ) then
    begin
        if( ( XABFHC.XAB_W_FLG and FAF_DSM_MASK ) = 0 ) then
        begin
            Context := 'TRUE' ;
        end else
        begin
            Context := 'FALSE' ;
        end ;
    end else
    if( Item = 'nok' ) then
    begin
        Context := inttostr( XABSUM.XAB_B_NOK ) ;
    end else
    if( Item = 'org' ) then
    begin
        Context := '' ; // TODO
    end else
    if( Item = 'preshelved' ) then
    begin
        Context := 'FALSE' ; //TODO
    end else
    if( Item = 'pro' ) then
    begin
        Context := Protection_To_Text( XABPRO.XAB_W_PRO shl 16 ) ;
    end else
    if( Item = 'pvn' ) then
    begin
        Context := inttostr( XABSUM.XAB_W_PVN ) ;
    end else
    if( Item = 'rat' ) then
    begin
        Context := '' ; //TODO
    end else
    if( Item = 'rck' ) then
    begin
        Context := 'FALSE' ; //TODO
    end else
    if( Item = 'rdt' ) then
    begin
        Context := ASCTIM( XABDAT.XAB_Q_RDT ) ;
    end else
    if( Item = 'rfm' ) then
    begin
        Context := '' ; //TODO
    end else
    if( Item = 'ru' ) then
    begin
        Context := '' ; //TODO
    end else
    if( Item = 'rvn' ) then
    begin
        Context := '' ; //TODO
    end else
    if( Item = 'shelvable' ) then
    begin
        Context := 'FALSE' ; //TODO
    end else
    if( Item = 'shelved' ) then
    begin
        Context := 'FALSE' ; //TODO
    end else
    if( Item = 'stored_semanticcs' ) then
    begin
        Context := '' ; //TODO
    end else
    if( Item = 'uic' ) then
    begin
        Context := inttostr( XABPRO.XAB_L_UIC ) ;
    end else
    if( Item = 'verlimit' ) then
    begin
        Context := inttostr( XABFHC.XAB_W_VERLIMIT ) ;
    end else
    if( Item = 'wck' ) then
    begin
        Context := 'FALSE' ; //TODO
    end else
    begin
        Context := Item ;
        Err := UCL_IVKEYW ;
    end ;
end ; // Parse_File_Attributes
Finally, we set the result based on the item. If the item is not recognized, we set the error.
You may note the //TODO comments on some lines. These items are related to features that we haven't covered yet. We will revisit them in the future.

In the next article, we'll cover the code for the system call.

 

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