program MenuBuild; imports MenuUtils from MenuUtils; imports Memory from Memory; imports System from System; imports FileSystem from FileSystem; imports CmdParse from CmdParse; imports Perq_String from Perq_String; { Abstract: This program build a menu structure in a user defined data segment. The data segment is written to a user specified .MSEG file. Such .MSEG files may subsequently be utilized by the MenuUtils.GetMenu procedure to load the menues for a given application in a high-speed fashion. ( Using MultiRead. ) In parallel with the generation of the data segment, a .HLP-file is generated, which will contain the helptext from the menu text file. The .MSEG-file will contain blocknumbers referencing this .HLP-file, and the .HLP-file will NOT be loaded when the menues are loaded by the application, just read for the wanted information when HELP is requested from any menu. Usage: BuildMenu [~ [,511 then begin FSBlkWrite( HelpF, BlockNo, DiskBuff ); Offset := 0; BlockNo := BlockNo + 1; end; end; end; procedure PutHelp( Txt : String ); var I:integer; begin for I := 1 to length( Txt ) do PutInBuffer( Txt[I]); PutInBuffer( chr(13) ); end; procedure CloseHelpFile; begin with HelpFree do begin if HelpFree.Offset>0 then begin { last buffer partially full } FSBlkWrite( HelpF, BlockNo, DiskBuff ); FSClose( HelpF, BlockNo+1, Offset*8 ); end else { last buffer is empty } FSClose( HelpF, BlockNo, 0 ); end; end; procedure Allocate( s : integer ); begin if (MMMaxExtSize div 2) > ((FreePtr.offset+S+255) div 256) then FreePtr.Offset := FreePtr.Offset + S else raise FullMenuSeg; end; function NewMenuEntry( NType : NodeType; NumComm : integer ):pMenuEntry; var ret : pMenuEntry; fixed : integer; begin Fixed := WordSize( HelpAddress )+ WordSize( String )+ WordSize( NodeType); Ret := MakePtr( FreePtr.Segmen, FreePtr.Offset, pMenuEntry ); case NType of ParmNode: Allocate( Fixed ); EndNode : Allocate( Fixed ); MenuNode: Allocate( Fixed + WordSize( pNameDesc ) + NumComm*WordSize( pMenuEntry ) ); end; ret^.Node := NType; NewMenuEntry := ret; end; function NewNameDesc( NumComm : integer ):pNameDesc; var ret:pNameDesc; begin Ret := MakePtr( FreePtr.Segmen, FreePtr.Offset, pNameDesc ); Allocate( WordSize( Integer ) + (NumComm+1)*WordSize( S25 ) ); Ret^.NumCommands := NumComm; NewNameDesc := ret; end; function GetMenu : pMenuEntry; VAR ME : pMenuEntry; NC : integer; CI : integer; begin indent := indent+4; readln( MenuFile, NC ); ReadLn( MenuFile, Line ); { determine what kind of a node has been encountered } if NC=0 then ME := NewMenuEntry( EndNode, 0 ) else if NC<0 then ME := NewMenuEntry( ParmNode, 0 ) else ME := NewMenuEntry( MenuNode, NC+1 ); { build node } with ME^ do begin if Node=MenuNode then begin MPtr := NewNameDesc( NC+1 ); if Line<>'>' then begin MPtr^.Header := Line; ReadLn( MenuFile, Line ); end else MPtr^.Header := ''; MPtr^.Commands[1] := 'HELP'; { Always a HELP entry } end; if Line<>'>' then begin Prompt := Line; ReadLn( MenuFile, Line ); end else Prompt := ''; Help := HelpFree; while line<>'>' do begin PutHelp( Line ); ReadLn( MenuFile, Line ); end; PutHelp( Line ); if Node=MenuNode then for CI := 2 to NC+1 do begin ReadLn( MenuFile, Line ); if ShowMenues then writeln( '':indent, Line ); {$range-} MPtr^.Commands[ CI ] := Line; NextLevel[ CI ] := GetMenu; {$range+} end; end; GetMenu := ME; Indent := Indent-4; end; begin { Open menu source file } if FSLookUp( MenuFName, Blk, Bits )=0 then raise InvMenuFile else begin reset( MenuFile, MenuFName); { Allocate a BIG segment to build menues in } { Use half of max. size to avoid trouble with } { two's complement integer arithmetic } CreateSegment( MenuSeg, MMMaxExtSize div 2, 1, MMMaxExtSize div 2 ); with FreePtr do begin Offset := WordSize( Integer ); Segmen := MenuSeg; end; CreateHelpFile( HelpFName ); LineNo := 0; Indent := 0; ReadLn( MenuFile, Line ); ShowMenues := Line<>''; { Now go for it!! } Root := GetMenu; CloseHelpFile; WriteSegment( SegFName, FreePtr ); end; end; function StripOff( InStr, Tail : Pstring ):Pstring; { Strip from if the last characters of matches } var InL,TailL : integer; T1, T2 : String; begin InL := Length( InStr ); while InStr[InL]=' ' do begin InL := InL - 1; Adjust( InStr, InL ); end; TailL := Length( Tail ); if TailL>InL then begin StripOff := InStr end else begin T1 := SubStr( InStr,InL+1-TailL,TailL ); ConvUpper( T1 ); T2 := Tail; ConvUpper( T2 ); if T1=T2 then begin StripOff := SubStr( InStr, 1, InL-TailL ) end else begin StripOff := InStr; end; end; end; procedure ParseArgs; handler InvMenuFile; begin writeln('Menu file: ',Mnu,' is invalid name or does not exist!'); exit( ParseArgs ); end; handler InvSegFile; begin writeln('Segment file name: ',Mseg,' is invalid name!'); exit( ParseArgs ); end; handler InvHelpFile; begin writeln('Help file name: ',Help,' is invalid name!'); exit( ParseArgs ); end; handler BadArgs; begin exit( ParseArgs ); end; begin Sep := NextId( Comm, isSwitch ); if ParseCmdArgs( Inputs, Outputs, Switches, Err ) then begin Mnu := ''; Mseg := ''; Help := ''; if Inputs<>NIL then Mnu := StripOff( Inputs^.Name, '.MENU' ); if Outputs<>NIL then begin Mseg := StripOff( Outputs^.Name, '.MSEG' ); if Outputs^.Next<>NIL then begin Help := StripOff( Outputs^.Next^.Name, '.HLP' ); end; end; if Mnu='' then Mnu := StripOff( LastFileName, '.MENU' ); if Mnu='' then if Mseg='' then begin if Help='' then begin writeln('No filename given!'); Raise BadArgs; end else begin Mseg := Help; Mnu := Help; end; end else begin Mnu := Mseg; end; if Mseg='' then Mseg := Mnu; if Help='' then Help := Mseg; Mnu := Concat( Mnu, '.MENU' ); Mseg := Concat( Mseg, '.MSEG' ); Help := Concat( Help, '.HLP' ); Writeln( 'Reading: ',Mnu, ','); Writeln( ' ==> ', MSeg, ', ', Help ); MakeMenues( Mnu, Mseg, Help ); end else begin writeln(Err); writeln; writeln ('Usage: MenuBuild <.MENU file> [~<.MSEG file> [,<.HLP file>] ]'); end; end; begin Inputs := NIL; Outputs := NIL; Switches := NIL; ParseArgs; DstryArgRec( Inputs ); DstryArgRec( Outputs ); DstrySwitchRec( Switches ); end.