home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / help / tphelp / 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.