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
Glossary/Index
Downloads
|
HELP, Part 2
In this article, we will implement the HELP CUSP, which we documented in the previous
article.
function Run : int64 ;
var C : string ;
OS : POS_UOS ;
begin
OS := new( POS_UOS, Init ) ;
C := LIB_Get_Symbol( 'help$defaults' ) ;
Result := _Help( 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 ;
This is the main program entry point which, like previous CUSPs, gets default switches
and calls the callable version of the CUSP - in this case, _Help .
const UI = 'object Main:rectangle' + CR +
' top = 2' + CR +
' object llabel:label' + CR +
' text = "Topic?"' + CR +
' top = $margin' + CR +
' left = $margin' + CR +
' end' + CR +
' object topic:string' + CR +
' hint = "Topic name"' + CR +
' top = $margin' + CR +
' left = llabel.width + $margin' + CR +
' end' + CR +
'' + // Options...
' object exact:boolean' + CR +
' hint = "Exact search match"' + CR +
' left = $margin' + CR +
' end' + CR +
' object highlight:list' + CR +
' hint = "List symbol names"' + CR +
' left = nameslabel.width + $margin' + CR +
' list = "BOLD","BLINK","REVERSE","UNDERLINE"' + CR +
' end' + CR +
' object instructions:boolean' + CR +
' hint = "Create new library"' + CR +
' left = $margin' + CR +
' end' + CR +
' object liblist:boolean' + CR +
' hint = "List libraries"' + CR +
' left = $margin' + CR +
' end' + CR +
' object librarylabel:label' + CR +
' text="Symbol name sort order:"' + CR +
' left = $margin' + CR +
' end' + CR +
' object library:string' + CR +
' hint = "Library name"' + CR +
' left = librarylabel.width + $margin' + CR +
' end' + CR +
' object outputlabel:label' + CR +
' text="Output:"' + CR +
' left = $margin' + CR +
' end' + CR +
' object output:string' + CR +
' hint = "Output"' + CR +
' left = outputlabel.width + $margin' + CR +
' end' + CR +
' object page:list' + CR +
' hint = "Paging mode"' + CR +
' left = $margin' + CR +
' list = "CLEAR_SCREEN","SCROLL","SAVE"' + CR +
' end' + CR +
' object prompt:boolean' + CR +
' hint = "Interactive help"' + CR +
' left = $margin' + CR +
' end' + CR +
' object searchlabel:label' + CR +
' text="Search text:"' + CR +
' left = $margin' + CR +
' end' + CR +
' object search:string' + CR +
' hint = "Search text"' + CR +
' left = searchlabel.width + $margin' + CR +
' end' + CR +
' object userlibrarylist:list' + CR +
' hint = "User libraries to include' + CR +
' text = "ALL"' + CR +
' left = $margin' + CR +
' list = "PROCESS","GROUP","SYSTEM","ALL","NONE"' + CR +
' multiselect = True' + CR +
' end' + CR +
' object wrap:boolean' + CR +
' hint = "Wrap text"' + CR +
' left = userlibrarylist.width + $margin' + CR +
' end' + CR +
'end' ;
This is the UI definition for HELP.
type TPage = ( Page_None, Page_Save, Page_Scroll, Page_Clear ) ;
var Exact : boolean ;
F : TCOM_UOS_File ;
Highlight : string ;
Page : TPage ;
Pages : integer ;
Search : string ;
UUI : TUUI ;
Wrap : boolean ;
function _Help( Command, Defaults : PChar ; Standalone : boolean ) : int64 ;
var C : string ;
I : integer ;
P : TPID ;
Flags : int64 ;
Instructions : boolean ;
Liblist : boolean ;
_Library : string ;
Output : string ;
Parameter, Topic : string ;
Prompt : boolean ;
SRB : TSRB ;
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 ) ) ;
First we define some global variables and then the _Help routine with
local variables. After set up, we prepend the command with any defaults.
// Process /PAGE (special case)...
Page := Page_None ;
Pages := 5 ;
while( true ) do
begin
case Parse_Switch( 'PA|GE', 'NOPA|GE', C, Parameter ) of
1 : begin
if( Is_Prefix( 'CLEAR_SCREEN', Parameter ) ) then
begin
Page := Page_Clear ;
if( Parameter <> '' ) then
begin
Result := UUI_NOSWITCH ;
exit ;
end
end else
if(
Is_Prefix( 'SCROLL', Parameter )
and
( copy( lowercase( Parameter ), 1, 2 ) = 'sc' )
) then
begin
Page := Page_Scroll ;
if( Parameter <> '' ) then
begin
Result := UUI_NOSWITCH ;
exit ;
end
end else
begin
I := pos( '=', Parameter + '=' ) ;
if(
Is_Prefix( 'SAVE', copy( Parameter, 1, I - 1 ) )
and
( copy( lowercase( Parameter ), 1, 2 ) = 'sa' )
) then
begin
Page := Page_Save ;
if( I < length( Parameter ) ) then
begin
if( trystrtoint( Right_Of( Parameter, I ), I ) ) then
begin
Pages := I ;
end else
if( Parameter <> '' ) then
begin
Result := UUI_INVSWVAL ;
exit ;
end ;
end ;
end else
begin
Result := UUI_INVSWVAL ;
exit ;
end ;
end ;
end ; // case 1
0 : break ;
end ; // case Parse_Switch...
end ; // while( true )
First we handle the /PAGE switch, which doesn't fit well into the UUI model. If
present, we parse it and set up the appropriate variables. This will also remove
the switch from the command string. We loop through until the Parse_Switch
function returns 0 (indicating no such switch present). We have to do this because
the user could include the switch multiple times, and we only want to pay attention
to the last instance of the switch. More likely, the default switches may include the
/PAGE switch but the user wants to override the default with their own desired behavior.
By the time the loop ends, we will have the variables set to the last instance of the
/PAGE switch and all instances of that switch will have been removed from the command
line.
// Processing...
UUI.Command_Line := PChar( C ) ;
if( UUI.Execute( '', Standalone ) ) then
begin
// Get parameters...
Topic := UUI.Get_Component_By_Name( 'topic' ).Text ;
_Library := UUI.Get_Component_By_Name( 'library' ).Text ;
Search := UUI.Get_Component_By_Name( 'search' ).Text ;
Exact := UUI.Get_Component_By_Name( 'exact' ).Selected ;
Instructions := UUI.Get_Component_By_Name( 'instructions' ).Selected ;
Prompt := UUI.Get_Component_By_Name( 'prompt' ).Selected ;
Liblist := UUI.Get_Component_By_Name( 'liblist' ).Selected ;
Wrap := UUI.Get_Component_By_Name( 'wrap' ).Selected ;
Highlight := UUI.Get_Component_By_Name( 'highlight' ).Text ;
Output := UUI.Get_Component_By_Name( 'output' ).Text ;
Next we execute the UI and grab the various values from it.
if( Output = '' ) then
begin
Output := 'sys$output:' ;
end ;
Flags := 0 ;
if( UUI.Get_Component_By_Name( 'userlibrarylist' ).Item_Selected( 'PROCESS' ) ) then
begin
Flags := HLP_M_PROCESS ;
end ;
if( UUI.Get_Component_By_Name( 'userlibrarylist' ).Item_Selected( 'GROUP' ) ) then
begin
Flags := HLP_M_GROUP ;
end ;
if( UUI.Get_Component_By_Name( 'userlibrarylist' ).Item_Selected( 'SYSTEM' ) ) then
begin
Flags := HLP_M_SYSTEM ;
end ;
if( UUI.Get_Component_By_Name( 'userlibrarylist' ).Item_Selected( 'ALL' ) ) then
begin
Flags := HLP_M_PROCESS or HLP_M_GROUP or HLP_M_SYSTEM ;
end ;
if( Prompt ) then
begin
Flags := Flags or HLP_M_PROMPT ;
end ;
if( Instructions ) then
begin
Topic := 'HELP' ;
end ;
if( Liblist ) then
begin
Flags := Flags or HLP_M_LIBLIST ;
end ;
if( _Library = '' ) then
begin
_Library := 'sys$help:helplib.hlb' ;
end ;
if( not Exact ) then
begin
Search := trim( lowercase( Search ) ) ;
end ;
Next we handle the values from the UI, defaulting them as needed.
Set_String( Output, SRB ) ;
F := Open_File( int64( @SRB ), 0 ) ;
// Show help
Result := Output_Help( int64( @LBR_Callback ), 0, Topic, _Library, Flags,
int64( @LBR_Input ) ) ;
F.Free ;
end ; // if( UUI.Execute( '', Standalone ) )
end ; // _Help
Next we open the output file (defaults to sys$output). Then we call Output_Help
which you may recall is the Pascal wrapper for the LBR_Output_Help service. Note
that we provide both input and output callback routines.
procedure LBR_Callback( Context, Data : int64 ) ;
var S, L, MS, ME : string ;
Starting, Ending : integer ;
begin
S := Get_String( PSRB( Data )^ ) ;
if( Search <> '' ) then
begin
L := S ;
if( Exact ) then
begin
L := lowercase( S ) ;
end ;
Search_HTML( Search, L, Starting, Ending, 0 ) ;
if( Starting > 0 ) then // Found a match
begin
if( copy( Highlight, 1, 2 ) = 'BO' ) then
begin
MS := '<b>' ;
ME := '</b>' ;
end else
if( copy( Highlight, 1, 2 ) = 'BL' ) then
begin
MS := '<blink>' ;
ME := '</blink>' ;
end else
if( copy( Highlight, 1, 1 ) = 'R' ) then
begin
MS := '<reverse>' ;
ME := '</reverse>' ;
end else
if( copy( Highlight, 1, 1 ) = 'U' ) then
begin
MS := '<u>' ;
ME := '</u>' ;
end ;
S := copy( S, 1, Starting - 1 ) + MS + copy( S, Starting, Ending - Starting + 1 ) +
ME + copy( S, Ending + 1, length( S ) ) ;
end ;
end ;
This output callback routine is used by the Output_Help service to write help text
to the user. If the user requested a search, we compare the output text with the
search text. Unless /EXACT was specified, we convert the text to lowercase in order
to do case-insensitive searches. The text search/comparison is done via Search_HTML
which compares plain text to HTML, skipping tags and handling entities. Since a match
in the HTML might span multiple tags, etc, the function returns both starting and
ending offsets of the match. The match is surrounded by tags that match the specified
highlighting. Note that we insert HTML tags into HTML text. More on this later.
if( Wrap ) then
begin
S := Wrap_HTML( S, F.Get_Width ) ;
end ;
if( Page = Page_Save ) then
begin
if( Page_List = nil ) then
begin
Page_List := TStringList.Create ;
end ;
Page_List.Add( S ) ;
if( Page_List.Count > Pages ) then
begin
Page_List.Delete( 0 ) ;
end ;
end ;
UUI.Put_Output( F, S ) ;
end ; // LBR_Callback
Next, we wrap the HTML text based on the width of the output device. Note that if
the output device isn't a terminal, the width will be 0, which effectively disables
wrapping even if the user requested it. Next, if the user requested /PAGE=SAVE,
we will add this page of text to a list of pages. If the number of pages exceeds
the maximum, we delete the first (oldest) page. Finally, we call UUI.Put_Output
which writes the HTML text to the output device. We use the UUI to do this rather
than the Starlet Put_Output service. This routine calls the Put_Formatted_Out service,
which we will cover in the next article.
var Temp : string ;
function LBR_Input() : int64 ;
var Current : integer ;
Key, S : string ;
begin
Current := Page_List.Count ;
while( true ) do
begin
This input routine is called by Output_Help. We have to preview user input in order
to handle scrolling around saved pages if the user specified /PAGE=SAVE. Since we
may need to handle multiple scroll commands before returning to Output_Help, we will loop until we
are done.
S := UUI.Get_Next_Command( 'Topic? ' ) ;
S := Unicode.Edit( S, 8 or 128 ) ; // Trim
Key := Get_Key( S ) ;
if( ( S = '' ) or ( Key = 'PAGEDOWN' ) or ( Key = 'DOWN' ) ) then // Next page
begin
if( ( Page <> Page_Save ) or ( Page_List = nil ) ) then
begin
break ; // Not in page mode, or no pages
end ;
if( Current >= Page_List.Count - 1 ) then // No more pages
begin
break ;
end ;
inc( Current ) ;
UUI.Put_Output( F, Page_List[ Current ] ) ;
end else
if( ( S = Control_B ) or ( Key = 'PAGEUP' ) or ( Key = 'UP' ) ) then
begin
if(
( Page = Page_Save )
and
( Page_List <> nil )
and
( Current > 0 )
and
( Page_List.Count > 0 )
) then
begin
dec( Current ) ;
UUI.Put_Output( F, Page_List[ Current ] ) ;
end ;
end ;
end ; // while( true )
Temp := S ;
Set_String( Temp, SRB ) ;
Result := int64( @SRB ) ;
end ; // LBR_Input()
First we prompt the user, using the prompt "Topic? ". We trim the input and then
handle the two operations that we care about. The first one is displaying the next
page and second one is displaying the previous page. We use Current
to keep track of the current page in the page list (built by the output callback,
as described above). If the input is not recognized, we break out of the loop and
pass the input back to the caller so it can handle it.
The use of the Key variable is used to handle special keyboard codes.
Key is set by calling Get_Key, which is a Pascal wrapper to the system
service DMG_Get_Key. Why is this necessary? Remember that UOS deals with input as
Unicode (specifically UTF-8) characters. Some keys on the keyboard (such as the
arrow keys) do not correspond to a glyph and therefore have no Unicode representation.
So, to indicate one of these key presses, they are converted to escape sequences (a
sequence of characters preceded by the ASCII escape character). Get_Key will return
a string representing the keypress defined by an escape sequence. If the string doesn't
start with a valid escape sequence, only a normal Unicode character is returned (the
first character in the string).
In the next article, we will look at the new system services used in this code.
|