home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / t_power / makehelp.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1988-02-01  |  13.0 KB  |  507 lines

  1. {BLDHELP.PAS Copyright (C) 1988, by TurboPower Software}
  2.  
  3. {$R-,S-,I-,V-}
  4.  
  5. program BuildHelp;
  6.   {-Build indexed binary help file from text file}
  7. uses
  8.   Dos,
  9.   TPDos,
  10.   TPString,
  11.   TPCrt,
  12.   TPWindow,
  13.   TPHelp;
  14.  
  15. const
  16.   FileBuffSize = 4096;       {Size of input and output file buffers}
  17.   CommandMark = '!';         {Marks help metacommand in text file}
  18.   CommentMark = ';';         {At start of line, marks comment in text file}
  19.  
  20. type
  21.   FileBuff = array[1..FileBuffSize] of Char;
  22.   String80 = string[80];
  23.  
  24. var
  25.   InName : String80;         {Input file name}
  26.   OutName : String80;        {Output file name}
  27.   InF : Text;                {Input file}
  28.   OutF : file;               {Output file}
  29.   InBuff : FileBuff;         {Buffer for input text}
  30.   OutBuff : FileBuff;        {Buffer for binary output}
  31.   OutPos : Word;             {Bytes used in output buffer}
  32.  
  33.   Hdr : HelpHeader;          {Header of help file}
  34.   CP : CharArrayPtr;         {Points to pick array}
  35.   HI : HelpIndexPtr;         {Points to help index}
  36.  
  37.   C : String80;              {Command or command parameter}
  38.   S : string;                {Raw input line}
  39.   Spos : Byte;               {Position in input line}
  40.   LineNum : LongInt;         {Current input line number}
  41.   SectPos : LongInt;         {File offset of current section}
  42.   TextWid : Byte;            {Max characters in a line}
  43.   CurSect : Word;            {Current section number}
  44.   LineLen : Byte;            {Current line width}
  45.   SectLen : Word;            {Bytes in current section}
  46.  
  47.   procedure Error(Msg : string);
  48.     {-Write error message and halt}
  49.   begin
  50.     WriteLn(Msg);
  51.     Halt(1);
  52.   end;
  53.  
  54.   procedure ErrorLine(Msg : string);
  55.     {-Report error position and message}
  56.   var
  57.     IO : Word;
  58.   begin
  59.     WriteLn(^M'Line number: ', LineNum);
  60.     WriteLn(S);
  61.     Close(OutF);
  62.     Erase(OutF);
  63.     IO := IoResult;
  64.     Error(Msg);
  65.   end;
  66.  
  67.   procedure ErrorOut;
  68.     {-Report output writing error}
  69.   begin
  70.     Error('Error writing to '+OutName);
  71.   end;
  72.  
  73.   procedure UpdateLineNum;
  74.     {-Increment counter and update status display}
  75.   begin
  76.     Inc(LineNum);
  77.     if LineNum and $F = 0 then
  78.       Write(^M, LineNum);
  79.   end;
  80.  
  81.   procedure Initialize;
  82.     {-Prepare for analysis of help file}
  83.   begin
  84.     {Open the files}
  85.     if ParamCount <> 2 then
  86.       Error('Usage: BLDHELP InFile OutFile');
  87.     InName := stupcase(CleanPathName(ParamStr(1)));
  88.     OutName := stupcase(CleanPathName(ParamStr(2)));
  89.     Assign(InF, InName);
  90.     Reset(InF);
  91.     if IoResult <> 0 then
  92.       Error(InName+' not found');
  93.     SetTextBuf(InF, InBuff, FileBuffSize);
  94.     Assign(OutF, OutName);
  95.     Rewrite(OutF, 1);
  96.     if IoResult <> 0 then
  97.       Error('Cannot create '+OutName);
  98.  
  99.     {Default help header}
  100.     with Hdr do begin
  101.       ID := LongInt(HelpId);
  102.       MaxSection := 0;
  103.       ItemCnt := 0;
  104.       NameSize := 0;
  105.       Width := 40;
  106.     end;
  107.   end;
  108.  
  109.   {$F+}
  110.   function HeapFunc(Size : Word) : Integer;
  111.     {-Return nil pointer if insufficient memory}
  112.   begin
  113.     HeapFunc := 1;
  114.   end;
  115.   {$F-}
  116.  
  117.   function GetMemCheck(var P; Bytes : Word) : Boolean;
  118.     {-Allocate heap space, returning true if successful}
  119.   var
  120.     SaveHeapError : Pointer;
  121.     Pt : Pointer absolute P;
  122.   begin
  123.     {Take over heap error control}
  124.     SaveHeapError := HeapError;
  125.     HeapError := @HeapFunc;
  126.     GetMem(Pt, Bytes);
  127.     GetMemCheck := (Pt <> nil);
  128.     {Restore heap error control}
  129.     HeapError := SaveHeapError;
  130.   end;
  131.  
  132.   procedure SkipWhite(var S : string; var Spos : Byte);
  133.     {-Advance Spos past white space}
  134.   begin
  135.     while (Spos <= Length(S)) and (S[Spos] <= ' ') do
  136.       Inc(Spos);
  137.   end;
  138.  
  139.   procedure ParseWord(var S : string; var Spos : Byte; var C : string; MaxLen : Byte);
  140.     {-Parse next word from S, returning it in C}
  141.   var
  142.     Clen : Byte absolute C;
  143.   begin
  144.     SkipWhite(S, Spos);
  145.     Clen := 0;
  146.     while (Spos <= Length(S)) and (S[Spos] > ' ') do begin
  147.       if Clen < MaxLen then begin
  148.         Inc(Clen);
  149.         C[Clen] := S[Spos];
  150.       end;
  151.       Inc(Spos);
  152.     end;
  153.   end;
  154.  
  155.   function ParseNumber(var S : string; var Spos : Byte; Name : string) : Word;
  156.     {-Parse a word from the line}
  157.   var
  158.     C : string[8];
  159.     N : Word;
  160.   begin
  161.     ParseWord(S, Spos, C, 8);
  162.     if Length(C) = 0 then
  163.       ErrorLine(Name+' expected');
  164.     if not Str2Word(C, N) then
  165.       ErrorLine('Invalid '+Name+' specified');
  166.     ParseNumber := N;
  167.   end;
  168.  
  169.   procedure ParseColors(var S : string; var Spos : Byte; var Attr : HelpAttrArray);
  170.     {-Store new color set}
  171.   var
  172.     A : HelpAttrType;
  173.   begin
  174.     for A := FrAttr to SpAtt3 do
  175.       Attr[A] := ParseNumber(S, Spos, 'Color');
  176.   end;
  177.  
  178.   function ClassifyCommand(C : string) : Word;
  179.     {-Classify valid help metacommands}
  180.   const
  181.     NumCommands = 4;
  182.     CommandNames : array[1..NumCommands] of string[5] =
  183.     ('TOPIC', 'LINE', 'PAGE', 'WIDTH');
  184.   var
  185.     I : Integer;
  186.   begin
  187.     C := stupcase(C);
  188.     for I := 1 to NumCommands do
  189.       if C = CommandNames[I] then begin
  190.         ClassifyCommand := I;
  191.         Exit;
  192.       end;
  193.     ClassifyCommand := 0;
  194.   end;
  195.  
  196.   function BlockWriteOK(var B; Bytes : Word) : Boolean;
  197.     {-Write a block to output and error check}
  198.   var
  199.     BytesWritten : Word;
  200.   begin
  201.     BlockWrite(OutF, B, Bytes, BytesWritten);
  202.     BlockWriteOK := (IoResult = 0) and (BytesWritten = Bytes);
  203.   end;
  204.  
  205.   procedure WriteHeaders;
  206.     {-Write the binary header structures to the help file}
  207.   begin
  208.     with Hdr do begin
  209.       if not BlockWriteOK(Hdr, SizeOf(HelpHeader)) then
  210.         ErrorOut;
  211.       if not BlockWriteOK(CP^, ItemCnt*NameSize) then
  212.         ErrorOut;
  213.       if not BlockWriteOK(HI^, ItemCnt*SizeOf(LongInt)) then
  214.         ErrorOut;
  215.       {Store position for first help section}
  216.       SectPos := SizeOf(HelpHeader)+ItemCnt*(NameSize+SizeOf(LongInt));
  217.     end;
  218.   end;
  219.  
  220.   procedure CountFile;
  221.     {-Scan input file once to determine number of items}
  222.   begin
  223.     with Hdr do begin
  224.       LineNum := 0;
  225.  
  226.       while not eof(InF) do begin
  227.         ReadLn(InF, S);
  228.         UpdateLineNum;
  229.  
  230.         if (Length(S) > 0) and (S[1] = CommandMark) then begin
  231.           {Line is a help metacommand}
  232.           Spos := 2;
  233.           ParseWord(S, Spos, C, 8);
  234.           case ClassifyCommand(C) of
  235.             1 :              {TOPIC}
  236.               begin
  237.                 {New section, get section number}
  238.                 CurSect := ParseNumber(S, Spos, 'Topic number');
  239.                 if CurSect > ItemCnt then
  240.                   ItemCnt := CurSect;
  241.                 {Get optional pick name}
  242.                 SkipWhite(S, Spos);
  243.                 C := Copy(S, Spos, 64);
  244.                 if Length(C)+1 > NameSize then
  245.                   NameSize := Length(C)+1;
  246.               end;
  247.             {Ignore other metacommands this pass}
  248.           end;
  249.         end;
  250.       end;
  251.  
  252.       {Clear the status}
  253.       Write(^M'         '^M);
  254.       {Allocate space for name and index arrays}
  255.       if ItemCnt = 0 then
  256.         Error('No help topics specified');
  257.  
  258.       if NameSize = 0 then
  259.         CP := nil
  260.       else if LongInt(ItemCnt)*NameSize > 65520 then
  261.         Error('Pick name array exceeds 64K')
  262.       else if not GetMemCheck(CP, ItemCnt*NameSize) then
  263.         Error('Insufficient memory for name array');
  264.  
  265.       if not GetMemCheck(HI, ItemCnt*SizeOf(LongInt)) then
  266.         Error('Insufficient memory for index array');
  267.  
  268.       {Initialize the arrays}
  269.       FillChar(CP^, ItemCnt*NameSize, 0);
  270.       FillChar(HI^, ItemCnt*SizeOf(LongInt), lo(NoHelpAvailable));
  271.  
  272.       {Reserve disk space for initial binary help structures}
  273.       WriteHeaders;
  274.     end;
  275.   end;
  276.  
  277.   procedure FlushBuffer;
  278.     {-Write the output buffer to file}
  279.   begin
  280.     if OutPos > 0 then begin
  281.       if not BlockWriteOK(OutBuff, OutPos) then
  282.         ErrorOut;
  283.       OutPos := 0;
  284.     end;
  285.   end;
  286.  
  287.   procedure CharOut(Ch : Char);
  288.     {-Write a single character to output (with buffering)}
  289.   begin
  290.     if OutPos >= FileBuffSize then
  291.       FlushBuffer;
  292.     Inc(OutPos);
  293.     OutBuff[OutPos] := Ch;
  294.     Inc(SectLen);
  295.   end;
  296.  
  297.   procedure NewSection;
  298.     {-End the current section and prepare for the new}
  299.   begin
  300.     CharOut(SectEndMark);
  301.     Inc(SectPos, SectLen);
  302.     with Hdr do
  303.       if SectLen > MaxSection then
  304.         MaxSection := SectLen;
  305.     SectLen := 0;
  306.     LineLen := 0;
  307.   end;
  308.  
  309.   procedure NewPage;
  310.     {-End the current page}
  311.   begin
  312.     CharOut(PageBrkMark);
  313.     LineLen := 0;
  314.   end;
  315.  
  316.   procedure NewLine;
  317.     {-End the current line}
  318.   begin
  319.     CharOut(LineBrkMark);
  320.     LineLen := 0;
  321.   end;
  322.  
  323.   procedure StorePickName(C : string);
  324.     {-Store pick name for CurSect}
  325.   begin
  326.     with Hdr do
  327.       Move(C, CP^[(CurSect-1)*NameSize], Length(C)+1);
  328.   end;
  329.  
  330.   function LenCount(Ch : Char) : Byte;
  331.     {-Return length to count for character}
  332.   begin
  333.     case Ch of
  334.       Attr1Toggle,
  335.       Attr2Toggle,
  336.       Attr3Toggle :
  337.         LenCount := 0;
  338.     else
  339.       LenCount := 1;
  340.     end;
  341.   end;
  342.  
  343.   procedure LineOut;
  344.     {-Wrap and write text lines}
  345.   var
  346.     Tpos : Byte;
  347.     Tlen : Byte;
  348.   begin
  349.     if Length(S) = 0 then begin
  350.       {Empty line, finish previous line}
  351.       if LineLen > 0 then
  352.         NewLine;
  353.       {Insert blank line}
  354.       NewLine;
  355.       Exit;
  356.     end;
  357.  
  358.     {Non-empty line}
  359.     if (S[1] = ' ') then
  360.       {Finish previous line}
  361.       if LineLen > 0 then
  362.         NewLine;
  363.  
  364.     Spos := 1;
  365.     repeat
  366.  
  367.       {Write white space}
  368.       while (Spos <= Length(S)) and (S[Spos] = ' ') do begin
  369.         if LineLen < TextWid then begin
  370.           CharOut(S[Spos]);
  371.           Inc(LineLen, LenCount(S[Spos]));
  372.         end;
  373.         Inc(Spos);
  374.       end;
  375.       if Spos > Length(S) then
  376.         Exit;
  377.  
  378.       {See if next word fits on line}
  379.       Tpos := Spos;
  380.       Tlen := 0;
  381.       repeat
  382.         Inc(Tlen, LenCount(S[Tpos]));
  383.         Inc(Tpos);
  384.       until (Tpos > Length(S)) or (S[Tpos] = ' ');
  385.  
  386.       if LineLen+Tlen > TextWid then
  387.         {Word won't fit on line, start a new one}
  388.         NewLine;
  389.  
  390.       {Write the word}
  391.       while Spos < Tpos do begin
  392.         CharOut(S[Spos]);
  393.         Inc(LineLen, LenCount(S[Spos]));
  394.         Inc(Spos);
  395.       end
  396.  
  397.     until Spos > Length(S);
  398.  
  399.     {End line with blank}
  400.     if LineLen < TextWid then begin
  401.       CharOut(' ');
  402.       Inc(LineLen);
  403.     end;
  404.  
  405.   end;
  406.  
  407.   procedure ScanFile;
  408.     {-Scan input file to create help text}
  409.   var
  410.     Ch : Char;
  411.   begin
  412.     with Hdr do begin
  413.  
  414.       {Reread the input file}
  415.       Reset(InF);
  416.       SetTextBuf(InF, InBuff, FileBuffSize);
  417.  
  418.       {Correct default text dimensions for frames and spacing}
  419.       TextWid := Width-4;
  420.  
  421.       {Initialize counters}
  422.       LineNum := 0;
  423.       CurSect := 0;
  424.       LineLen := 0;
  425.       SectLen := 0;
  426.       OutPos := 0;
  427.  
  428.       while not eof(InF) do begin
  429.         ReadLn(InF, S);
  430.         UpdateLineNum;
  431.         if (Length(S) = 0) or (S[1] <> CommentMark) then begin
  432.           {Line is not a comment}
  433.           if (Length(S) > 0) and (S[1] = CommandMark) then begin
  434.             {A help metacommand}
  435.             Spos := 2;
  436.             ParseWord(S, Spos, C, 8);
  437.  
  438.             case ClassifyCommand(C) of
  439.               1 :            {TOPIC}
  440.                 begin
  441.                   if CurSect <> 0 then
  442.                     {Complete previous section}
  443.                     NewSection;
  444.                   {Get section number}
  445.                   CurSect := ParseNumber(S, Spos, 'Topic number');
  446.                   {Error check}
  447.                   if HI^[CurSect] <> NoHelpAvailable then
  448.                     ErrorLine('Duplicate help topic number');
  449.                   {Store file index}
  450.                   HI^[CurSect] := SectPos;
  451.                   {Get optional pick name}
  452.                   SkipWhite(S, Spos);
  453.                   C := Copy(S, Spos, 64);
  454.                   if Length(C) > 0 then
  455.                     {Store pick name}
  456.                     StorePickName(C);
  457.                 end;
  458.  
  459.               2 :            {LINE}
  460.                 NewLine;
  461.  
  462.               3 :            {PAGE}
  463.                 NewPage;
  464.  
  465.               4 :            {WIDTH}
  466.                 if CurSect <> 0 then
  467.                   ErrorLine('WIDTH statement must precede first help topic')
  468.                 else begin
  469.                   {Parse width}
  470.                   Width := ParseNumber(S, Spos, 'Width');
  471.                   {Correct dimension for frame and spacing}
  472.                   TextWid := Width-4;
  473.                 end;
  474.  
  475.             else
  476.               ErrorLine('Unrecognized metacommand');
  477.             end;
  478.  
  479.           end else
  480.             {A text line - wrap and output}
  481.             LineOut;
  482.         end;
  483.       end;
  484.  
  485.       {Finalize status}
  486.       WriteLn(^M, LineNum, ' total lines in help file');
  487.  
  488.       {Store last section}
  489.       if SectLen > 0 then
  490.         NewSection;
  491.       {Assure output goes to disk}
  492.       FlushBuffer;
  493.  
  494.       {Write the updated header and indexes}
  495.       Reset(OutF, 1);
  496.       WriteHeaders;
  497.       Close(OutF);
  498.  
  499.     end;
  500.   end;
  501.  
  502. begin
  503.   Initialize;
  504.   CountFile;
  505.   ScanFile;
  506. end.
  507.