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

Glossary/Index


Downloads

LIBRARY utility, part 2

In the previous article, we documented the LIBRARY utility. In this article, we'll cover the code for it.

const UI =  'object Main:rectangle' + CR +
            '  top = 2' + CR +
            '  object llabel:label' + CR +
            '    text = "Library file:"' + CR +
            '    top = $margin' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object input:string' + CR +
            '    hint = "Library filespec"' + CR +
            '    top = $margin' + CR +
            '    left = llabel.width + $margin' + CR +
            '    require = "_Library:"' + CR +
            '  end' + CR +
            '' + // Options...
            '  object before:date' + CR +
            '    hint = "List modules before specified time"' + CR +
            '  end' + CR +
            '  object compress:boolean' + CR +
            '    hint = "defragment library"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object create:boolean' + CR +
            '    hint = "Create new library"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object dellabel:label' + CR +
            '    text="Modules to delete:"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object delete:qualifier' + CR +
            '    hint = "Modules to delete"' + CR +
            '    left = dellabel.width + $margin' + CR +
            '  end' + CR +
            '  object extractlabel:label' + CR +
            '    text="Extract modules:"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object extract:qualifier' + CR +
            '    hint = "Modules to extract"' + CR +
            '    left = extractlabel.width + $margin' + CR +
            '  end' + CR +
            '  object full:boolean' + CR +
            '    hint = "Show full list"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object globals:boolean' + CR +
            '    hint = "Include global symbols"' + CR +
            '    left = $margin' + CR +
            '    inverse=noglobals' + CR +
            '  end' + CR +
            '  object noglobals:boolean' + CR +
            '    hint = "Do not include global symbols"' + CR +
            '    left = $margin' + CR +
            '    inverse=globals' + CR +
            '  end' + CR +
            '  object help:boolean' + CR +
            '    hint = "Create help library"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object history:boolean' + CR +
            '    hint = "List history"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object insert:boolean' + CR +
            '    hint = "Insert into the library"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object list:boolean' + CR +
            '    hint = "List modules"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object log:boolean' + CR +
            '    hint = "Log each operation"' + CR +
            '    left = $margin' + CR +
            '    inverse = nolog' + CR +
            '  end' + CR +
            '  object nolog:boolean' + CR +
            '    hint = "Do not log operation"' + CR +
            '    left = $margin' + CR +
            '    inverse = log' + CR +
            '  end' + CR +
            '  object macro:boolean' + CR +
            '    hint = "Macro library"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object module:qualifier' + CR +
            '    hint = "Module name"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object nameslabel:label' + CR +
            '    text="Symbol name sort order:"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object names:list' + CR +
            '    hint = "List symbol names"' + CR +
            '    left = nameslabel.width + $margin' + CR +
            '    list = "NONE","BYMODULE","BYSYMBOL"' + CR +
            '  end' + CR +
            '  object object:boolean' + CR +
            '    hint = "Object library"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object onlylabel:label' + CR +
            '    text="Select modules:"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object only:qualifier' + CR +
            '    hint = "Select modules"' + CR +
            '    left = onlylabel.width + $margin' + CR +
            '  end' + CR +
            '  object outputlabel:label' + CR +
            '    text="Output file:"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object output:qualifier' + CR +
            '    hint = "Output file"' + CR +
            '    left = outputlabel.width + $margin' + CR +
            '  end' + CR +
            '  object remove:boolean' + CR +
            '    hint = "Remove module"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object replace:boolean' + CR +
            '    hint = "Replace module"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object selective_search:boolean' + CR +
            '    hint = "Mark as selective search"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object since:date' + CR +
            '    hint = "Include modules inserted after specified time"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object size:integer' + CR +
            '    hint = "Initial size of new library"' + CR +
            '    text = "53248"' + CR +
            '  end' + CR +
            '  object text:boolean' + CR +
            '    hint = "Text library"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object widthlabel:label' + CR +
            '    text="Line width:"' + CR +
            '    left = $margin' + CR +
            '  end' + CR +
            '  object width:integer' + CR +
            '    hint = "Output line length"' + CR +
            '    left = widthlabel.width + $margin' + CR +
            '  end' + CR +
            '  object crossreference:label' + CR +
            '    text = "Cross-references:"' + CR +
            '  end' + CR +
            '  object cross_references:list' + CR +
            '    hint = "Cross-reference options"' + CR +
            '    left = crossreference.width + $margin' + CR +
            '    list = "NONE","ALL","MODULE","SYMBOL","VALUE"' + CR +
            '  end' + CR +
            'end' ;
This is the UI definition for the utility.

type TOperation = ( Op_None, Op_Create, Op_Insert, Op_Replace ) ;

var Lib_Data : string ;
    ResLen : int64 ;


function Run : int64 ;

var C : string ;
    OS : POS_UOS ;

begin
    OS := new( POS_UOS, Init ) ;
    C := LIB_Get_Symbol( 'library$defaults' ) ;
    Result := _Library( PChar( OS^.Command_Line ), PChar( C ), True ) ;
    if( Result <> 0 ) then
    begin
        OS^.OutputLn( 0, LIB_Get_Exception_Text( 0, Result ) ) ;
    end ;
    OS.Free ;
    SYS_EXIT( 0 ) ;
end ;
First we define the TOperation enumeration, and some global variables. The Run procedure follows the model of the previous CUSPs we've covered before, so we won't say anything about it here.

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

var Buf : array[ 0..1023 ] of char ;
    BufLen : int64 ;
    C : string ;
    Component : TUUI_Component ;
    Context : int64 ;
    OS : POS_UOS ;
    Disk, S, Temp : string ;
    Default_Extension, Lib_Extension : string ;
    E, Err, F : int64 ;
    Fil : TCOM_UOS_File ;
    I, Loop : integer ;
    Options : array[ 0..2 ] of int64 ;
    Typ : int64 ;
    Operation : TOperation ;
    P : TPID ;
    UUI : TUUI ;
    Flags : int64 ;
    SRB, SRB1 : TSRB ;
    MList : TStringList ;
    Files : string ;
    This_File : string ;
    Before : int64 ;
    Compress : boolean ;
    XRef : string ;
    Delete : string ;
    Extract : string ;
    Full : boolean ;
    Globals : boolean ;
    Help : boolean ;
    History : boolean ;
    Insert : boolean ;
    List : boolean ;
    Log : boolean ;
    Macro : boolean ;
    Module : string ; // Module name for the inserted module
    Names : string ;
    Objects : boolean ;
    Only : string ;
    Output : string ;
    Remove : boolean ;
    Selective_Search : boolean ;
    Since, Size : int64 ;
    Text : boolean ;
    Width : integer ;
    Search_Context : int64 ;

begin
    // Setup...
    Result := 0 ;
    UUI := TUUI.Create ;
    UUI.Definition := UI ;

    // 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 ) ;
The _Library routine does the main processing for the LIBRARY utility. We start with the initialization of the UUI and the result value. Then we get the default library options, if they are defined, and insert them into the command line. This is what we've done in the previous CUSPs as well. Then we pass the command line to the UUI.

    // Processing...
    if( UUI.Execute( '', Standalone ) ) then
    begin
        Files := '' ;
        Operation := Op_None ;
        Component := UUI.Get_Component_By_Name( 'replace' ) ;
        if( Component.UUI ) then
        begin
            Operation := Op_Replace ;
        end ;
        Component := UUI.Get_Component_By_Name( 'insert' ) ;
        if( Component.UUI ) then
        begin
            Operation := Op_Insert ;
        end ;
        Component := UUI.Get_Component_By_Name( 'create' ) ;
        if( Component.UUI ) then
        begin
            Operation := Op_Create ;
        end ;
        Before := 0 ;
        Since := 0 ;
        Component := UUI.Get_Component_By_Name( 'before' ) ;
        if( Component.UUI ) then
        begin
            Before := BINTIM( Component.Text ) ;
            if( Before = 0 ) then
            begin
                Result := COPY_INVQUAVAL ;
                exit ;
            end ;
        end ;
        Component := UUI.Get_Component_By_Name( 'since' ) ;
        if( Component.UUI ) then
        begin
            Since := BINTIM( Component.Text ) ;
            if( Since = 0 ) then
            begin
                Result := COPY_INVQUAVAL ;
                exit ;
            end ;
        end ;
        if( ( Before <> 0 ) and ( Since <> 0 ) ) then
        begin
            Result := LIBRARY_INCQUAL ;
            exit ;
        end ;
Next we execute the UI and, if successful, start processing. We get various UI components and determine the operation from them. Then we get the time components and validate them.

        Default_Extension := '' ;
        Compress := UUI.Get_Component_By_Name( 'compress' ).Selected ;
        XRef := UUI.Get_Component_By_Name( 'cross_references' ).Text ;
        Delete := UUI.Get_Component_By_Name( 'delete' ).Text ;
        Extract := UUI.Get_Component_By_Name( 'extract' ).Text ;
        Full := UUI.Get_Component_By_Name( 'full' ).Selected ;
        Globals := UUI.Get_Component_By_Name( 'globals' ).Selected ;
        Help := UUI.Get_Component_By_Name( 'help' ).Selected ;
        History := UUI.Get_Component_By_Name( 'history' ).Selected ;
        Insert := UUI.Get_Component_By_Name( 'insert' ).Selected ;
        List := UUI.Get_Component_By_Name( 'list' ).Selected ;
        Log := UUI.Get_Component_By_Name( 'log' ).Selected ;
        Macro := UUI.Get_Component_By_Name( 'macro' ).Selected ;
        Module := UUI.Get_Component_By_Name( 'module' ).Text ;
        Names := UUI.Get_Component_By_Name( 'names' ).Text ;
        Objects := UUI.Get_Component_By_Name( 'object' ).Selected ;
        Only := UUI.Get_Component_By_Name( 'only' ).Text ;
        Output := UUI.Get_Component_By_Name( 'output' ).Text ;
        Remove := UUI.Get_Component_By_Name( 'remove' ).Selected ;
        Selective_Search := UUI.Get_Component_By_Name( 'selective_search' ).Selected ;
        Text := UUI.Get_Component_By_Name( 'text' ).Selected ;
        Width := 0 ;
        Temp := UUI.Get_Component_By_Name( 'width' ).Text ;
        if( Temp <> '' ) then
        begin
            Width := strtoint( Temp ) ;
        end ;
        Size := 53248 ;
        Temp := UUI.Get_Component_By_Name( 'size' ).Text ;
        if( Temp <> '' ) then
        begin
            Size := strtoint( Temp ) ;
        end ;
        if( Size < 8192 ) then
        begin
            Size := 8192 ; // Minimum library file size
        end ;
Next we get the boolean values from the UI, and the value for the /SIZE qualifier. We default the size if not specified, and don't allow it to be less than 8Kb.

        case Operation of
            Op_Replace :
                begin
                    if( UUI.Get_Component_By_Name( 'extract' ).UUI ) then
                    begin
                        Result := LIBRARY_INCQUAL ;
                        exit ;
                    end ;
                    F := LBR_C_Update ;
                end ;
            Op_Insert :
                begin
                    F := LBR_C_Update ;
                end ;
            Op_Create :
                begin
                    // Clear UUI flag so we don't recreate if caller calls again in a loop
                    UUI.Get_Component_By_Name( 'create' ).UUI := False ;
                    F := LBR_C_Create ;
                    Files := UUI.Get_Parameter( '', '', False ) ;
                end ;
            else
                if( Insert or Remove ) then
                begin
                    F := LBR_C_Update ;
                end else
                begin
                    F := LBR_C_Read ;
                end ;
        end ; // case Operation
        if( UUI.Get_Component_By_Name( 'size' ).UUI and ( Operation <> Op_Create ) ) then
        begin // /SIZE only makes sense with /CREATE
            Result := LIBRARY_INCQUAL ;
            exit ;
        end ;
        if( not List ) then
        begin
            if( ( Before <> 0 ) or ( Since <> 0 ) ) then // Only valid on /LIST
            begin
                Result := LIBRARY_INCQUAL ;
                exit ;
            end ;
        end ;
        if( Module <> '' ) then // /MODULE specified
        begin
            if( ( Extract <> '' ) or ( Delete <> '' ) or Remove ) then
            begin
                Result := LIBRARY_INCQUAL ;
                exit ;
            end ;
        end ;
Now, we determine the opening flags for the library based on the operation we are going to do. In the case of creation, we clear the flag in the UI. Remember that this routine is the callable LIBRARY interface and a program may make subsequent calls to this routine. On the subsequent call, if the CREATE boolean is still set, then the library file will be recreated. We want to make sure it only happens if the user/caller specifically sets the CREATE boolean again. We also make sure that the /SIZE qualifier is only used with /CREATE (it makes no sense otherwise), and return an error in the nonsensical case. Then we check for other incompatible qualifiers, exiting with an error if found.

        // Determine extensions...
        Typ := LBR_C_TYP_HLP ; // Default to help
        Default_Extension := '.hlp' ;
        Lib_Extension := '.hlb' ;
        I := 0 ;
        if( Help ) then
        begin
            inc( I ) ;
            Typ := LBR_C_TYP_HLP ;
            Default_Extension := '.hlp' ;
            Lib_Extension := '.hlb' ;
        end ;
        if( Macro ) then
        begin
            inc( I ) ;
            Typ := LBR_C_TYP_MLB ;
            Default_Extension := '.mar' ;
            Lib_Extension := '.mlb' ;
        end ;
        if( Objects ) then
        begin
            inc( I ) ;
            Typ := LBR_C_TYP_OBJ ;
            Default_Extension := '.obj' ;
            Lib_Extension := '.olb' ;
        end ;
        if( Text ) then
        begin
            inc( I ) ;
            Typ := LBR_C_TYP_TXT ;
            Default_Extension := '.txt' ;
            Lib_Extension := '.tlb' ;
        end ;
        if( I > 1 ) then // Multiple types specified
        begin
            Result := LIBRARY_INCQUAL ;
            exit ;
        end ;
Now we need to determine the library file extension and the input file extensions. We default to the extensions for help libraries, overriding that if a specific type was specified. However, if more than one type was specified, we exit with an error.

        // Now open/create the library...
        fillchar( Options, sizeof( Options ), 0 ) ;
        Options[ 0 ] := Size ;
        Temp := trim( UUI.Get_Component_By_Name( 'input' ).Text ) ;
        Result := LBRPas.Ini_Control( Context, F, Typ, Temp, Lib_Extension, Temp ) ;
        if( Result <> 0 ) then
        begin
            exit ;
        end ;
        Result := LBRPas.Open( Context, Options, Temp ) ;
        if( Result <> 0 ) then
        begin
            exit ;
        end ;

        // Do processing...
        if( Compress ) then
        begin
            //TODO:defragment library
        end ;
Now we are ready to initialize the LBR services and open/create the library. For now, we will do nothing for the /COMPRESS qualifier. The compress operation will defragment the library file's file system. We will be covering the defragmenting code in the future.

        if( Extract <> '' ) then
        begin
            MList := Parse_Module_List( Extract ) ;
            if( MList = nil ) then
            begin
                LBRPas.Close( Context ) ;
                Result := LIBRARY_INVLIST ;
                exit ;
            end ;
            if( MList.Count = 0 ) then
            begin
                LBRPas.Close( Context ) ;
                MList.Free ;
                Result := LIBRARY_INVLIST ;
                exit ;
            end ;
            if( Output <> '' ) then
            begin
                Fil := Open_Binary_File( Output, FAB_V_SUP ) ;
                if( Fil = nil ) then
                begin
                    Err := LIB_Get_Exception( 0 ) ;
                    Result := LIB_Get_Exception_Code( 0, Err ) ;
                    LBRPas.Close( Context ) ;
                    exit ;
                end ;
            end ;
            for I := 0 to MList.Count - 1 do
            begin
                Temp := MList[ I ]  ;
                if( Temp = '' ) then // A null name (",," or ",)") is a syntax error
                begin
                    LBRPas.Close( Context ) ;
                    MList.Free ;
                    Result := LIBRARY_INVLIST ;
                    exit ;
                end ;
                Result := LBRPas.Get_Module( Context, int64( @LBR_Callback ), 0, Temp ) ;
                if( Result <> 0 ) then
                begin
                    LBRPas.Close( Context ) ;
                    exit ;
                end ;
                if( Output = '' ) then
                begin
                    Fil := Open_Binary_File( Temp + Default_Extension, FAB_V_SUP ) ;
                    if( Fil = nil ) then
                    begin
                        Err := LIB_Get_Exception( 0 ) ;
                        Result := LIB_Get_Exception_Code( 0, Err ) ;
                        LBRPas.Close( Context ) ;
                        exit ;
                    end ;
                end ;
                Fil.Write( Lib_Data ) ;
                if( Output = '' ) then
                begin
                    Fil.Free ;
                end ;
            end ; // for I := 0 to MList.Count - 1
            MList.Free ;
            if( Output <> '' ) then
            begin
                Fil.Free ;
            end ;
        end ;
For extraction operations, we parse the list of modules to a string list. If no modules were specified, we exit with an error. If an output file is specified, we create it, exiting on error. Then we iterate through the list of modules. For each module, if the name is null, it means there was a double-comma in the module list, so we exit with an error. Next we ask the LBR services to get the module. If there is an error, we close the LBR context and exit. We pass a callback routine to the service, which we will cover later in the article. This callback writes the module data to the Lib_Data string. If no output file was specified, we create an output file that matches the current module name. Then we then write the data to the output file. If the Output file wasn't specified, we close the output file. Otherwise, we just keep appending to the single output file that we opened before the loop. When done with the module list, we clear the list, and close the output file if it is still open. In other words, if an specific output file is specified, all the selected modules are written to that file. Otherwise, individual files are created for each extracted module.

        if( Delete <> '' ) then
        begin
            MList := Parse_Module_List( Delete ) ;
            if( MList = nil ) then
            begin
                LBRPas.Close( Context ) ;
                Result := LIBRARY_INVLIST ;
                exit ;
            end ;
            if( MList.Count = 0 ) then
            begin
                LBRPas.Close( Context ) ;
                MList.Free ;
                Result := LIBRARY_INVLIST ;
                exit ;
            end ;
            for I := 0 to MList.Count - 1 do
            begin
                Temp := MList[ I ]  ;
                if( Temp = '' ) then // A null name (",," or ",)") is a syntax error
                begin
                    LBRPas.Close( Context ) ;
                    MList.Free ;
                    Result := LIBRARY_INVLIST ;
                    exit ;
                end ;
                Result := LBRPas.Delete( Context, Temp ) ;
                if( Result <> 0 ) then
                begin
                    LBRPas.Close( Context ) ;
                    MList.Free ;
                    exit ;
                end ;
                if( Log ) then
                begin
                    Put_Output( '%LIBRAR-S-DELETED, MODULE ' + Temp +
                        ' DELETED FROM ' +
                        UUI.Get_Component_By_Name( 'input' ).Text ) ;
                end ;
            end ;
            MList.Free ;
        end ; // if( Delete <> '' )
For the delete operation, we parse the module list, exiting with an error if no modules were specified. Then we iterate through the module list, exiting with an error if a module name is null (indicating double commas as above). We then delete the module, exiting with an error if the service returns one. Finally, if /LOG was specified, we write a confirmation of the operation.

        if( ( Operation = Op_Insert ) or ( Operation = Op_Replace ) ) then
        begin
            // Setup...
            MList := Parse_Module_List( Module ) ;
            if( ( MList = nil )
                or
                ( MList.Count > 1 )
                or
                ( ( MList.Count = 1 ) and ( MList[ 0 ] = '' ) )
              ) then // This is a single-module operation
            begin
                LBRPas.Close( Context ) ;
                MList.Free ;
                Result := LIBRARY_INVLIST ;
                exit ;
            end ;
            Temp := '' ;
            if( MList.Count = 1 ) then
            begin
                Temp := MList[ 0 ] ;
            end ;
            MList.Free ;
            if( Files = '' ) then
            begin
                Files := UUI.Get_Parameter( '', '', False ) ;
            end ;
            if( Files = '' ) then
            begin
                Files := UUI.Get_Component_By_Name( 'input' ).Text + Default_Extension ;
            end ;
Much of the processing for both Insert and Replace operations is the same. First we parse the module list. However, only one module may be specified for these operations if any modules are specified at all. So, if the number of modules is specified but not one, we exit with an error. If no input file was specified, we call UUI.Get_Parameter to prompt for it.

            // Get the input file...
            Module := Temp ;
            while( Files <> '' ) do // Loop through all input files...
            begin
                This_File := Parse_Parameter( ',', Files ) ;
                if( This_File = '' ) then
                begin
                    continue ;
                end ;
                if( Module = '' ) then
                begin
                    Temp := This_File ;
                    I := Extension_Pos( Temp ) ;
                    setlength( This_File, I - 1 ) ;
                end ;
                if( ( Operation = Op_Insert ) and Module_Exists( Context, Temp ) ) then
                begin
                    Result := LIBRARY_EXISTS ;
                    LBRPas.Close( Context ) ;
                    exit ;
                end ;
We save the module name (from Temp) and then we loop until we've finished all input files. First, we extract the next file from the input list. If the name is null, it indicates a double comma. In this case, we'll just continue to the next loop iteration. If no module name was specified, we set Temp to the current file name, minus the extension. Otherwise, Temp is already set to the module name. Next, we check to see if the module already exists if we're doing an insert operation. If so, we exit with an error.

                if( pos( '?', This_File ) + pos( '*', This_File ) > 0 ) then // Wildcards
                begin
                    // Loop through all matching wildcards...
                    This_File := Lookup( This_File, Search_Context ) ;
                    while( This_File <> '' ) do
                    begin
                        if( not Import_File( This_File, Result ) ) then
                        begin
                            exit ;
                        end ;
                        This_File := Lookup( '', Search_Context ) ;
                    end ;
                end else
                begin
                    if( not Import_File( This_File, Result ) ) then
                    begin
                        exit ;
                    end ;
                end ;
            end ; // while( Files <> '' )
        end ; // if( Insert )
If a wildcard exists in the file name, we go through a lookup loop, finding all matching files. For each file found, we import it. If there are no wildcards, we simply import the file. The Import_File routine is covered below. This ends the section of code for import/replace.

        // Cross-references...
        if( XRef <> 'NONE' ) then
        begin
            //TODO
        end ;

        // Close library...
        LBRPas.Close( Context ) ;
    end ; // if( UUI.Execute( '', Standalone ) )
end ; // _Library
We don't handle the cross-reference operation at present, hence the TODO comment. Finally, at the end of the routine, we close the LBR context.

procedure LBR_Callback( Context, Data : int64 ) ;

begin
    Lib_Data := Get_String( PSRB( Data )^ ) ;
end ;
This is the callback routine that receives module data and writes it to the Lib_Data variable.

    function Parse_Module_List( S : string ) : TStringList ;

    var X : string ;

    begin
        Result := TStringList.Create ;
        if( copy( S, 1, 1 ) = '(' ) then
        begin
            S := copy( S, 2, length( S ) ) ; // Trim opening parenthesis
        end ;
        while( S <> '' ) do
        begin
            X := trim( Parse_Parameter( ',', S ) ) ;
            if( copy( X, length( X ), 1 ) = ')' ) then
            begin
                setlength( X, length( X ) - 1 ) ;
                if( S <> '' ) then // Syntax error
                begin
                    Result.Free ;
                    Result := nil ;
                    exit ;
                end ;
            end ;
            Result.Add( X ) ;
        end ;
    end ;
This routine parses a comma-delimited module list into a string list. We ignore any starting or ending parentheses.

    function Write_Module( Context : int64 ; const Temp, S : string ) : int64 ;

    begin
        // Write module to library...
        Result := Put_Module( Context, Temp, S ) ;
        if( Result <> 0 ) then
        begin
            LBRPas.Close( Context ) ;
            exit ;
        end ;
        if( Log ) then
        begin
            if( Operation = Op_Insert ) then
            begin
                Put_Output( '%LIBRAR-S-INSERTED, MODULE ' + Temp +
                    ' INSERTED INTO ' +
                    UUI.Get_Component_By_Name( 'input' ).Text ) ;
            end else
            begin
                Put_Output( '%LIBRAR-S-REPLACED, MODULE ' + Temp +
                    ' REPLACED IN ' +
                    UUI.Get_Component_By_Name( 'input' ).Text ) ;
            end ;
        end ;
    end ;
This routine inserts or replaces a module in the library. If /LOG was specified, we log the operation.

    function Import_File( This_File : string ; var Res : int64 ) : boolean ;

    var R : int64 ;

    begin
        Result := True ;
        Fil := Open_Binary_File( This_File, 0 ) ;
        if( Fil = nil ) then
        begin
            Err := LIB_Get_Exception( 0 ) ;
            Res := LIB_Get_Exception_Code( 0, Err ) ;
            LBRPas.Close( Context ) ;
            Result := False ;
            exit ;
        end ;
        setlength( S, Fil.Max_Storage ) ;
        Fil.Read_Stream( 0, 0, length( S ), int64( @PChar( S )[ 0 ] ) ) ;
        Fil.Free ;
        Res := Write_Module( Context, Temp, S ) ;
        if( Res <> 0 ) then
        begin
            Result := False ;
            exit ;
        end ;
    end ;
This module opens an input file, reads the contents and then writes the data to the library module. We return True if the operation succeeds, and false otherwise.

In the next article, we will look at another system service.