home *** CD-ROM | disk | FTP | other *** search
- {************************************************}
- { }
- { Turbo Pascal 6.0 }
- { Turbo Vision Demo }
- { Copyright (c) 1990 by Borland International }
- { }
- {************************************************}
-
- {===== TVHC ============================================================}
- { Turbo Vision help file compiler documentation. }
- {=======================================================================}
- { }
- { Refer to DEMOHELP.TXT for an example of a help source file. }
- { }
- { This program takes a help script and produces a help file (.HLP) }
- { and a help context file (.PAS). The format for the help file is }
- { very simple. Each context is given a symbolic name (i.e FileOpen) }
- { which is then put in the context file (i.e. hcFileOpen). The text }
- { following the topic line is put into the help file. Since the }
- { help file can be resized, some of the text will need to be wrapped }
- { to fit into the window. If a line of text is flush left with }
- { no preceeding white space, the line will be wrapped. All adjacent }
- { wrappable lines are wrapped as a paragraph. If a line begins with }
- { a space it will not be wrapped. For example, the following is a }
- { help topic for a File|Open menu item. }
- { }
- { |.topic FileOpen }
- { | File|Open }
- { | --------- }
- { |This menu item will bring up a dialog... }
- { }
- { The "File|Open" will not be wrapped with the "----" line since }
- { they both begin with a space, but the "This menu..." line will }
- { be wrapped. }
- { The syntax for a ".topic" line is: }
- { }
- { .topic symbol[=number][, symbol[=number][...]] }
- { }
- { Note a topic can have multiple symbols that define it so that one }
- { topic can be used by multiple contexts. The number is optional }
- { and will be the value of the hcXXX context in the context file }
- { Once a number is assigned all following topic symbols will be }
- { assigned numbers in sequence. For example, }
- { }
- { .topic FileOpen=3, OpenFile, FFileOpen }
- { }
- { will produce the follwing help context number definitions, }
- { }
- { hcFileOpen = 3; }
- { hcOpenFile = 4; }
- { hcFFileOpen = 5; }
- { }
- { Cross references can be imbedded in the text of a help topic which }
- { allows the user to quickly access related topics. The format for }
- { a cross reference is as follows, }
- { }
- (* {text[:alias]} *)
- { }
- { The text in the brackets is highlighted by the help viewer. This }
- { text can be selected by the user and will take the user to the }
- { topic by the name of the text. Sometimes the text will not be }
- { the same as a topic symbol. In this case you can use the optional }
- { alias syntax. The symbol you wish to use is placed after the text }
- { after a ':'. The following is a paragraph of text using cross }
- { references, }
- { }
- (* |The {file open dialog:FileOpen} allows you specify which *)
- { |file you wish to view. If it also allow you to navigate }
- { |directories. To change to a given directory use the }
- (* |{change directory dialog:ChDir}. *)
- { }
- { The user can tab or use the mouse to select more information about }
- { the "file open dialog" or the "change directory dialog". The help }
- { compiler handles forward references so a topic need not be defined }
- { before it is referenced. If a topic is referenced but not }
- { defined, the compiler will give a warning but will still create a }
- { useable help file. If the undefined reference is used, a message }
- { ("No help available...") will appear in the help window. }
- {=======================================================================}
-
- program TVHC;
-
- {$S-}
-
- {$M 8192,8192,655360}
-
- uses Drivers, Objects, Dos, HelpFile;
-
- procedure UpStr(var S: String);
- var
- I: Integer;
- begin
- for I := 1 to Length(S) do
- S[I] := UpCase(S[I]);
- end;
-
- {======================= File Management ===============================}
-
- procedure Error(Text: String); forward;
-
- type
- PProtectedStream = ^TProtectedStream;
- TProtectedStream = object(TBufStream)
- FileName: FNameStr;
- Mode: Word;
- constructor Init(AFileName: FNameStr; AMode, Size: Word);
- destructor Done; virtual;
- procedure Error(Code, Info: Integer); virtual;
- end;
-
- var
- TextStrm,
- SymbStrm: TProtectedStream;
- HelpStrm: PProtectedStream;
-
- constructor TProtectedStream.Init(AFileName: FNameStr; AMode, Size: Word);
- begin
- TBufStream.Init(AFileName, AMode, Size);
- FileName := AFileName;
- Mode := AMode;
- end;
-
- destructor TProtectedStream.Done;
- var
- F: File;
- begin
- TBufStream.Done;
- if (Mode = stCreate) and ((Status <> stOk) or (ExitCode <> 0)) then
- begin
- Assign(F, FileName);
- Erase(F);
- end;
- end;
-
- procedure TProtectedStream.Error(Code, Info: Integer);
- begin
- case Code of
- stError:
- TVHC.Error('Error encountered in file ' + FileName);
- stInitError:
- if Mode = stCreate then
- TVHC.Error('Could not create ' + FileName)
- else
- TVHC.Error('Could not find ' + FileName);
- stReadError: Status := Code; {EOF is "ok"}
- stWriteError:
- TVHC.Error('Disk full encountered writting file '+ FileName);
- else
- TVHC.Error('Internal error.');
- end;
- end;
-
- {----- ReplaceExt(FileName, NExt, Force) -------------------------------}
- { Replace the extension of the given file with the given extension. }
- { If the an extension already exists Force indicates if it should be }
- { replaced anyway. }
- {-----------------------------------------------------------------------}
-
- function ReplaceExt(FileName: PathStr; NExt: ExtStr; Force: Boolean):
- PathStr;
- var
- Dir: DirStr;
- Name: NameStr;
- Ext: ExtStr;
- begin
- FSplit(FileName, Dir, Name, Ext);
- if Force or (Ext = '') then
- ReplaceExt := Dir + Name + NExt else
- ReplaceExt := FileName;
- end;
-
- {----- FExist(FileName) ------------------------------------------------}
- { Returns true if the file exists false otherwise. }
- {-----------------------------------------------------------------------}
-
- function FExists(FileName: PathStr): Boolean;
- var
- F: file;
- Attr: Word;
- begin
- Assign(F, FileName);
- GetFAttr(F, Attr);
- FExists := DosError = 0;
- end;
-
-
- {======================== Line Management ==============================}
-
- {----- GetLine(S) ------------------------------------------------------}
- { Return the next line out of the stream. }
- {-----------------------------------------------------------------------}
-
- const
- Line: String = '';
- LineInBuffer: Boolean = False;
- Count: Integer = 0;
-
- function GetLine(var S: TStream): String;
- var
- C, I: Byte;
- begin
- if S.Status <> stOk then
- begin
- GetLine := #26;
- Exit;
- end;
- if not LineInBuffer then
- begin
- Line := '';
- C := 0;
- I := 0;
- while (Line[I] <> #13) and (I < 254) and (S.Status = stOk) do
- begin
- Inc(I);
- S.Read(Line[I], 1);
- end;
- Dec(I);
- S.Read(C, 1); { Skip #10 }
- Line[0] := Char(I);
- end;
- Inc(Count);
- GetLine := Line;
- LineInBuffer := False;
- end;
-
- {----- UnGetLine(S) ----------------------------------------------------}
- { Return given line into the stream. }
- {-----------------------------------------------------------------------}
-
- procedure UnGetLine(S: String);
- begin
- Line := S;
- LineInBuffer := True;
- Dec(Count);
- end;
-
- {========================= Error routines ==============================}
-
- {----- PrntMsg(Text) ---------------------------------------------------}
- { Used by Error and Warning to print the message. }
- {-----------------------------------------------------------------------}
-
- procedure PrntMsg(Pref: String; var Text: String);
- var
- S: String;
- L: array[0..3] of Longint;
- begin
- L[0] := Longint(@Pref);
- L[1] := Longint(@HelpStrm^.FileName);
- L[2] := Count;
- L[3] := Longint(@Text);
- if Count > 0 then FormatStr(S, '%s: %s(%d): %s'#13#10, L)
- else FormatStr(S, '%s: %s %#3%s', L);
- PrintStr(S);
- end;
-
- {----- Error(Text) -----------------------------------------------------}
- { Used to indicate an error. Terminates the program }
- {-----------------------------------------------------------------------}
-
- procedure Error(Text: String);
- begin
- PrntMsg('Error', Text);
- Halt(1);
- end;
-
- {----- Warning(Text) ---------------------------------------------------}
- { Used to indicate an warning. }
- {-----------------------------------------------------------------------}
-
- procedure Warning(Text: String);
- begin
- PrntMsg('Warning', Text);
- end;
-
- {====================== Topic Reference Management =====================}
-
- type
- PFixUp = ^TFixUp;
- TFixUp = record
- Pos: Longint;
- Next: PFixUp;
- end;
-
- PReference = ^TReference;
- TReference = record
- Topic: PString;
- case Resolved: Boolean of
- True: (Value: Word);
- False: (FixUpList: PFixUp);
- end;
-
- PRefTable = ^TRefTable;
- TRefTable = object(TSortedCollection)
- function Compare(Key1, Key2: Pointer): Integer; virtual;
- procedure FreeItem(Item: Pointer); virtual;
- function GetReference(var Topic: String): PReference;
- function KeyOf(Item: Pointer): Pointer; virtual;
- end;
-
- const
- RefTable: PRefTable = nil;
-
- procedure DisposeFixUps(P: PFixUp);
- var
- Q: PFixUp;
- begin
- while P <> nil do
- begin
- Q := P^.Next;
- Dispose(P);
- P := Q;
- end;
- end;
-
- {----- TRefTable -------------------------------------------------------}
- { TRefTable is a collection of PReference's used as a symbol table. }
- { If the topic has not been seen, a forward reference is inserted and }
- { a fix-up list is started. When the topic is seen all forward }
- { references are resolved. If the topic has been seen already the }
- { value it has is used. }
- {-----------------------------------------------------------------------}
-
- function TRefTable.Compare(Key1, Key2: Pointer): Integer;
- var
- K1,K2: String;
- begin
- K1 := PString(Key1)^;
- K2 := PString(Key2)^;
- UpStr(K1); UpStr(K2);
- if K1 > K2 then Compare := 1
- else if K1 < K2 then Compare := -1
- else Compare := 0;
- end;
-
- procedure TRefTable.FreeItem(Item: Pointer);
- var
- Ref: PReference absolute Item;
- P, Q: PFixUp;
- begin
- if not Ref^.Resolved then DisposeFixUps(Ref^.FixUpList);
- DisposeStr(Ref^.Topic);
- Dispose(Ref);
- end;
-
- function TRefTable.GetReference(var Topic: String): PReference;
- var
- Ref: PReference;
- I: Integer;
- begin
- if Search(@Topic, I) then
- Ref := At(I)
- else
- begin
- New(Ref);
- Ref^.Topic := NewStr(Topic);
- Ref^.Resolved := False;
- Ref^.FixUpList := nil;
- Insert(Ref);
- end;
- GetReference := Ref;
- end;
-
- function TRefTable.KeyOf(Item: Pointer): Pointer;
- begin
- KeyOf := PReference(Item)^.Topic;
- end;
-
- {----- InitRefTable ----------------------------------------------------}
- { Make sure the reference table is initialized. }
- {-----------------------------------------------------------------------}
-
- procedure InitRefTable;
- begin
- if RefTable = nil then
- RefTable := New(PRefTable, Init(5,5));
- end;
-
- {----- RecordReference -------------------------------------------------}
- { Record a reference to a topic to the given stream. This routine }
- { handles forward references. }
- {-----------------------------------------------------------------------}
-
- procedure RecordReference(var Topic: String; var S: TStream);
- var
- I: Integer;
- Ref: PReference;
- FixUp: PFixUp;
- begin
- InitRefTable;
- Ref := RefTable^.GetReference(Topic);
- if Ref^.Resolved then
- S.Write(Ref^.Value, SizeOf(Ref^.Value))
- else
- begin
- New(FixUp);
- FixUp^.Pos := S.GetPos;
- I := -1;
- S.Write(I, SizeOf(I));
- FixUp^.Next := Ref^.FixUpList;
- Ref^.FixUpList := FixUp;
- end;
- end;
-
- {----- ResolveReference ------------------------------------------------}
- { Resolve a reference to a topic to the given stream. This routine }
- { handles forward references. }
- {-----------------------------------------------------------------------}
-
- procedure ResolveReference(var Topic: String; Value: Word; var S: TStream);
- var
- I: Integer;
- Ref: PReference;
-
- procedure DoFixUps(P: PFixUp);
- var
- Pos: LongInt;
- begin
- Pos := S.GetPos;
- while P <> nil do
- begin
- S.Seek(P^.Pos);
- S.Write(Value, SizeOf(Value));
- P := P^.Next;
- end;
- S.Seek(Pos);
- end;
-
- begin
- InitRefTable;
- Ref := RefTable^.GetReference(Topic);
- if Ref^.Resolved then
- Error('Redefinition of ' + Ref^.Topic^)
- else
- begin
- DoFixUps(Ref^.FixUpList);
- DisposeFixUps(Ref^.FixUpList);
- Ref^.Resolved := True;
- Ref^.Value := Value;
- end;
- end;
-
- {======================== Help file parser =============================}
-
- {----- GetWord ---------------------------------------------------------}
- { Extract the next word from the given line at offset I. }
- {-----------------------------------------------------------------------}
-
- function GetWord(var Line: String; var I: Integer): String;
- var
- J: Integer;
- const
- WordChars = ['A'..'Z','a'..'z','0'..'9','_'];
-
- procedure SkipWhite;
- begin
- while (I <= Length(Line)) and (Line[I] = ' ') or (Line[I] = #8) do
- Inc(I);
- end;
-
- procedure SkipToNonWord;
- begin
- while (I <= Length(Line)) and (Line[I] in WordChars) do Inc(I);
- end;
-
- begin
- SkipWhite;
- J := I;
- if J > Length(Line) then GetWord := ''
- else
- begin
- Inc(I);
- if Line[J] in WordChars then SkipToNonWord;
- GetWord := Copy(Line, J, I - J);
- end;
- end;
-
- {----- TopicDefinition -------------------------------------------------}
- { Extracts the next topic definition from the given line at I. }
- {-----------------------------------------------------------------------}
-
- type
- PTopicDefinition = ^TTopicDefinition;
- TTopicDefinition = object(TObject)
- Topic: PString;
- Value: Word;
- Next: PTopicDefinition;
- constructor Init(var ATopic: String; AValue: Word);
- destructor Done; virtual;
- end;
-
- constructor TTopicDefinition.Init(var ATopic: String; AValue: Word);
- begin
- Topic := NewStr(ATopic);
- Value := AValue;
- Next := nil;
- end;
-
- destructor TTopicDefinition.Done;
- begin
- DisposeStr(Topic);
- if Next <> nil then Dispose(Next, Done);
- end;
-
- function TopicDefinition(var Line: String; var I: Integer): PTopicDefinition;
- var
- J,K: Integer;
- TopicDef: PTopicDefinition;
- Value: Word;
- Topic, W: String;
- const
- HelpCounter: Integer = 2; {1 is hcDragging}
- begin
- Topic := GetWord(Line, I);
- if Topic = '' then
- begin
- Error('Expected topic definition');
- TopicDefinition := nil;
- end
- else
- begin
- J := I;
- W := GetWord(Line, J);
- if W = '=' then
- begin
- I := J;
- W := GetWord(Line, I);
- Val(W, J, K);
- if K <> 0 then Error('Expected numeric')
- else HelpCounter := J;
- end else Inc(HelpCounter);
- TopicDefinition := New(PTopicDefinition, Init(Topic, HelpCounter));
- end;
- end;
-
- {----- TopicDefinitionList----------------------------------------------}
- { Extracts a list of topic definitions from the given line at I. }
- {-----------------------------------------------------------------------}
-
- function TopicDefinitionList(var Line: String; var I: Integer):
- PTopicDefinition;
- var
- J: Integer;
- W: String;
- TopicList, P: PTopicDefinition;
- begin
- J := I;
- TopicList := nil;
- repeat
- I := J;
- P := TopicDefinition(Line, I);
- if P = nil then
- begin
- if TopicList <> nil then Dispose(TopicList, Done);
- TopicDefinitionList := nil;
- Exit;
- end;
- P^.Next := TopicList;
- TopicList := P;
- J := I;
- W := GetWord(Line, J);
- until W <> ',';
- TopicDefinitionList := TopicList;
- end;
-
- {----- TopicHeader -----------------------------------------------------}
- { Parse a the Topic header }
- {-----------------------------------------------------------------------}
-
- const
- CommandChar = '.';
-
- function TopicHeader(var Line: String): PTopicDefinition;
- var
- I,J: Integer;
- W: String;
- TopicDef: PTopicDefinition;
-
- begin
- I := 1;
- W := GetWord(Line, I);
- if W <> CommandChar then
- begin
- TopicHeader := nil;
- Exit;
- end;
- W := GetWord(Line, I);
- UpStr(W);
- if W = 'TOPIC' then
- TopicHeader := TopicDefinitionList(Line, I)
- else
- begin
- Error('TOPIC expected');
- TopicHeader := nil;
- end;
- end;
-
- {----- ReadParagraph ---------------------------------------------------}
- { Read a paragraph of the screen. Returns the paragraph or nil if the }
- { paragraph was not found in the given stream. Searches for cross }
- { references and updates the XRefs variable. }
- {-----------------------------------------------------------------------}
- type
- PCrossRefNode = ^TCrossRefNode;
- TCrossRefNode = record
- Topic: PString;
- Offset: Integer;
- Length: Byte;
- Next: PCrossRefNode;
- end;
- const
- BufferSize = 1024;
- var
- Buffer: array[0..BufferSize-1] of Byte;
- Ofs: Integer;
-
- function ReadParagraph(var TextFile: TStream; var XRefs: PCrossRefNode;
- var Offset: Integer): PParagraph;
- var
- Line: String;
- State: (Undefined, Wrapping, NotWrapping);
- P: PParagraph;
-
- procedure AddToBuffer(var Line: String; Wrapping: Boolean); assembler;
- asm
- PUSH DS
- CLD
- PUSH DS
- POP ES
- MOV DI,OFFSET Buffer
- ADD DI,Ofs
- LDS SI,Line
- LODSB
- XOR AH,AH
- ADD ES:Ofs,AX
- XCHG AX,CX
- REP MOVSB
- XOR AL,AL
- TEST Wrapping,1 { Only add a #13, line terminator, if not }
- JE @@1 { currently wrapping the text. Otherwise }
- MOV AL,' '-13 { add a ' '. }
- @@1: ADD AL,13
- @@2: STOSB
- POP DS
- INC Ofs
- end;
-
- procedure ScanForCrossRefs(var Line: String);
- var
- I, BegPos, EndPos, Alias: Integer;
- const
- BegXRef = '{';
- EndXRef = '}';
- AliasCh = ':';
-
- procedure AddXRef(XRef: String; Offset: Integer; Length: Byte);
- var
- P: PCrossRefNode;
- PP: ^PCrossRefNode;
- begin
- New(P);
- P^.Topic := NewStr(XRef);
- P^.Offset := Offset;
- P^.Length := Length;
- P^.Next := nil;
- PP := @XRefs;
- while PP^ <> nil do
- PP := @PP^^.Next;
- PP^ := P;
- end;
-
- procedure ReplaceSpacesWithFF(var Line: String; Start: Integer;
- Length: Byte);
- var
- I: Integer;
- begin
- for I := Start to Start + Length do
- if Line[I] = ' ' then Line[I] := #$FF;
- end;
-
- begin
- I := 1;
- repeat
- BegPos := Pos(BegXRef, Copy(Line, I, 255));
- if BegPos = 0 then I := 0
- else
- begin
- Inc(I, BegPos);
- if Line[I + 1] = BegXRef then
- begin
- Delete(Line, I, 1);
- Inc(I);
- end
- else
- begin
- EndPos := Pos(EndXRef, Copy(Line, I, 255));
- if EndPos = 0 then
- begin
- Error('Unterminated topic reference.');
- Inc(I);
- end
- else
- begin
- Alias := Pos(AliasCh, Copy(Line, I, 255));
- if (Alias = 0) or (Alias > EndPos) then
- AddXRef(Copy(Line, I, EndPos - 1), Offset + Ofs + I - 1, EndPos - 1)
- else
- begin
- AddXRef(Copy(Line, I + Alias, EndPos - Alias - 1),
- Offset + Ofs + I - 1, Alias - 1);
- Delete(Line, I + Alias - 1, EndPos - Alias);
- EndPos := Alias;
- end;
- ReplaceSpacesWithFF(Line, I, EndPos-1);
- Delete(Line, I + EndPos - 1, 1);
- Delete(Line, I - 1, 1);
- Inc(I, EndPos - 2);
- end;
- end;
- end;
- until I = 0;
- end;
-
- function IsEndParagraph: Boolean;
- begin
- IsEndParagraph :=
- (Line = '') or
- (Line[1] = CommandChar) or
- (Line = #26) or
- ((Line[1] = ' ') and (State = Wrapping)) or
- ((Line[1] <> ' ') and (State = NotWrapping));
- end;
-
- begin
- Ofs := 0;
- ReadParagraph := nil;
- State := Undefined;
- Line := GetLine(TextFile);
- while Line = '' do
- begin
- AddToBuffer(Line, State = Wrapping);
- Line := GetLine(TextFile);
- end;
-
- if IsEndParagraph then
- begin
- ReadParagraph := nil;
- UnGetLine(Line);
- Exit;
- end;
- while not IsEndParagraph do
- begin
- if State = Undefined then
- if Line[1] = ' ' then State := NotWrapping
- else State := Wrapping;
- ScanForCrossRefs(Line);
- AddToBuffer(Line, State = Wrapping);
- Line := GetLine(TextFile);
- end;
- UnGetLine(Line);
- GetMem(P, SizeOf(P^) + Ofs);
- P^.Size := Ofs;
- P^.Wrap := State = Wrapping;
- Move(Buffer, P^.Text, Ofs);
- Inc(Offset, Ofs);
- ReadParagraph := P;
- end;
-
- {----- ReadTopic -------------------------------------------------------}
- { Read a topic from the source file and write it to the help file }
- {-----------------------------------------------------------------------}
- var
- XRefs: PCrossRefNode;
-
- procedure HandleCrossRefs(var S: TStream; XRefValue: Integer); far;
- var
- P: PCrossRefNode;
- begin
- P := XRefs;
- while XRefValue > 1 do
- begin
- if P <> nil then P := P^.Next;
- Dec(XRefValue);
- end;
- if P <> nil then RecordReference(P^.Topic^, S);
- end;
-
- procedure ReadTopic(var TextFile: TStream; var HelpFile: THelpFile);
- var
- Line: String;
- P: PParagraph;
- Topic: PHelpTopic;
- TopicDef: PTopicDefinition;
- I, J, Offset: Integer;
- Ref: TCrossRef;
- RefNode: PCrossRefNode;
-
- procedure SkipBlankLines(var S: TStream);
- var
- Line: String;
- begin
- Line := '';
- while Line = '' do
- Line := GetLine(S);
- UnGetLine(Line);
- end;
-
- function XRefCount: Integer;
- var
- I: Integer;
- P: PCrossRefNode;
- begin
- I := 0;
- P := XRefs;
- while P <> nil do
- begin
- Inc(I);
- P := P^.Next;
- end;
- XRefCount := I;
- end;
-
- procedure DisposeXRefs(P: PCrossRefNode);
- var
- Q: PCrossRefNode;
- begin
- while P <> nil do
- begin
- Q := P;
- P := P^.Next;
- Dispose(Q);
- end;
- end;
-
- procedure RecordTopicDefinitions(P: PTopicDefinition);
- begin
- while P <> nil do
- begin
- ResolveReference(P^.Topic^, P^.Value, HelpFile.Stream^);
- HelpFile.RecordPositionInIndex(P^.Value);
- P := P^.Next;
- end;
- end;
-
- begin
- { Get Screen command }
- SkipBlankLines(TextFile);
- Line := GetLine(TextFile);
-
- TopicDef := TopicHeader(Line);
-
- Topic := New(PHelpTopic, Init);
-
- { Read paragraphs }
- XRefs := nil;
- Offset := 0;
- P := ReadParagraph(TextFile, XRefs, Offset);
- while P <> nil do
- begin
- Topic^.AddParagraph(P);
- P := ReadParagraph(TextFile, XRefs, Offset);
- end;
-
- I := XRefCount;
- Topic^.SetNumCrossRefs(I);
- RefNode := XRefs;
- for J := 1 to I do
- begin
- Ref.Offset := RefNode^.Offset;
- Ref.Length := RefNode^.Length;
- Ref.Ref := J;
- Topic^.SetCrossRef(J, Ref);
- RefNode := RefNode^.Next;
- end;
-
- RecordTopicDefinitions(TopicDef);
-
- CrossRefHandler := HandleCrossRefs;
- HelpFile.PutTopic(Topic);
-
- if Topic <> nil then Dispose(Topic, Done);
- if TopicDef <> nil then Dispose(TopicDef, Done);
- DisposeXRefs(XRefs);
-
- SkipBlankLines(TextFile);
- end;
-
- {----- WriteSymbFile ---------------------------------------------------}
- { Write the .PAS file containing all screen titles as constants. }
- {-----------------------------------------------------------------------}
-
- procedure WriteSymbFile(var SymbFile: TProtectedStream);
- const
- HeaderText1 =
- 'unit ';
- HeaderText2 =
- ';'#13#10 +
- #13#10 +
- 'interface'#13#10 +
- #13#10 +
- 'const'#13#10 +
- #13#10;
- FooterText =
- #13#10 +
- 'implementation'#13#10 +
- #13#10 +
- 'end.'#13#10;
- Header1: array[1..Length(HeaderText1)] of Char = HeaderText1;
- Header2: array[1..Length(HeaderText2)] of Char = HeaderText2;
- Footer: array[1..Length(FooterText)] of Char = FooterText;
- var
- I, Count: Integer;
- Dir: DirStr;
- Name: NameStr;
- Ext: ExtStr;
-
- procedure DoWriteSymbol(P: PReference); far;
- var
- L: array[0..1] of LongInt;
- Line: String;
- begin
- if P^.Resolved then
- begin
- L[0] := Longint(P^.Topic);
- L[1] := P^.Value;
- FormatStr(Line, ' hc%-20s = %d;'#13#10, L);
- SymbFile.Write(Line[1], Length(Line));
- end
- else Warning('Unresolved forward reference "' + P^.Topic^ + '"');
- end;
-
- begin
- SymbFile.Write(Header1, SizeOf(Header1));
- FSplit(SymbFile.FileName, Dir, Name, Ext);
- SymbFile.Write(Name[1], Length(Name));
- SymbFile.Write(Header2, SizeOf(Header2));
-
- RefTable^.ForEach(@DoWriteSymbol);
-
- SymbFile.Write(Footer, SizeOf(Footer));
- end;
-
- {----- ProcessText -----------------------------------------------------}
- { Compile the given stream, and output a help file. }
- {-----------------------------------------------------------------------}
-
- procedure ProcessText(var TextFile, HelpFile, SymbFile: TProtectedStream);
- var
- HelpRez: THelpFile;
- begin
- HelpRez.Init(@HelpFile);
- while TextFile.Status = stOk do
- ReadTopic(TextFile, HelpRez);
- WriteSymbFile(SymbFile);
- HelpRez.Done;
- end;
-
- {========================== Program Block ==========================}
-
- var
- TextName,
- HelpName,
- SymbName: PathStr;
-
- procedure ExitClean; far;
- begin
- { Print a message if an out of memory error encountered }
- if ExitCode = 201 then
- begin
- Writeln('Error: Out of memory.');
- ErrorAddr := nil;
- ExitCode := 1;
- end;
-
- { Clean up files }
- TextStrm.Done;
- SymbStrm.Done;
- end;
-
- begin
- { Banner messages }
- PrintStr('Help Compiler Version 1.0 Copyright (c) 1990 Borland International.'#13#10);
- if ParamCount < 1 then
- begin
- PrintStr(
- #13#10 +
- ' Syntax: TVHC <Help text>[.TXT] [<Help file>[.HLP] [<Symbol file>[.PAS]]'#13#10 +
- #13#10+
- ' Help text = Help file source'#13#10 +
- ' Help file = Compiled help file'#13#10 +
- ' Symbol file = A Pascal file containing all the screen names as CONST''s'#13#10);
- Halt(0);
- end;
-
- { Calculate file names }
- TextName := ReplaceExt(ParamStr(1), '.TXT', False);
- if not FExists(TextName) then
- Error('File ' + TextName + ' not found.');
- if ParamCount >= 2 then
- HelpName := ReplaceExt(ParamStr(2), '.HLP', False) else
- HelpName := ReplaceExt(TextName, '.HLP', True);
- if ParamCount >= 3 then
- SymbName := ReplaceExt(ParamStr(3), '.PAS', False) else
- SymbName := ReplaceExt(HelpName, '.PAS', True);
-
- ExitProc := @ExitClean;
-
- RegisterHelpFile;
-
- TextStrm.Init(TextName, stOpenRead, 1024);
- SymbStrm.Init(SymbName, stCreate, 1024);
- HelpStrm := New(PProtectedStream, Init(HelpName, stCreate, 1024));
- ProcessText(TextStrm, HelpStrm^, SymbStrm);
- end.
-