home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 19 / CD_ASCQ_19_010295.iso / vrac / addhlp.zip / ADDHELP.PAS < prev    next >
Pascal/Delphi Source File  |  1994-09-03  |  25KB  |  703 lines

  1. program  addhelp; {adds help screen invoked by /? to .COM utilities,
  2.                    especially those created from DEBUG scripts}
  3.  
  4. {   version 1.0, August 1994.
  5.     written by John Nurick, 70162.2472@compuserve.com
  6.     written for Turbo Pascal v6.0 but should compile with v5.x
  7.     and later.
  8.  
  9.     May be used, copied and distributed freely.
  10.  
  11.     History: Versions 0.x first written in 1992 and in use since
  12.        with no problems but no formal testing.
  13.             For Version 1.0 I restructured the TP code and added 
  14.        error-handling but did not change the tried if not formally
  15.        tested routines for actually modifying files, or the machine
  16.        code that is inserted into the .COM files. 
  17.  
  18.     Syntax:  ADDHELP  /A STANDARD.COM  HELP.TXT  MODIFIED.COM
  19.              ADDHELP  /E MODIFIED.COM  HELP.TXT  STANDARD.COM
  20.              ADDHELP  /?
  21.  
  22.     STANDARD.COM  : path to .COM file without help screen
  23.     MODIFIED.COM  : path to modified .COM file with help screen added
  24.     HELP.TXT      : path to ASCII file containing help text
  25.  
  26.       /A switch  : add help text display to .COM file or substitute a
  27.                    new help screen in an already-modified file.
  28.       /E switch  : extract original .COM and help text files from
  29.                    a .COM file already modified by ADDHELP
  30.       /? switch  : display help screens.
  31.  
  32. *** OPERATION ********************************
  33.   (1) ADDHELP checks command line parameters and disk and memory space.
  34.       If the first parameter is '/?' it displays two screens of help text.
  35.       If there is no parameter it displays syntax summary.
  36.       If an error is identified it displays error message and syntax summary.
  37.   (2) Progress messages and error messages are displayed via stdout, so can
  38.       be redirected to a log file if ADDHELP is used in batch files.
  39.   (4) ADDHELP returns DOS ERRORLEVEL 0 if successful and 1 if an error
  40.       was identified.
  41.  
  42. *** with /A switch ***************************
  43.  
  44.   (1) ADDHELP checks that MODIFIED.COM will fit in the 64k limit for .COM
  45.       files.
  46.   (2) It reads STANDARD.COM into memory (comarray^) and checks for an
  47.       ADDHELP signature. If the first two bytes are the .EXE format
  48.       signature of 'MZ' it reports an error. If STANDARD.COM is shorter
  49.       than 32 bytes it is padded with NOPs.
  50.   (4) If signature was found, it substitutes the new help text and adjusts
  51.       addresses rather than adding a second envelope of help code.
  52.   (5) If adding to a plain .COM file, ADDHELP writes a 32-byte header:
  53.  
  54.           Offset Bytes   Item
  55.           0h     3h     JUMP instruction to beginning of the ADDHELP code.
  56.           3h     1h     NOP
  57.           4h     2h     address (offset + 100h) of the displaced first byte
  58.                         of the original .COM file
  59.           6h     2h     address (offset + 100h) of the beginning of the help
  60.                         text
  61.           8h     2h     number of bytes of help text
  62.           Ah     2h     spare
  63.           Ch     14h    ADDHELP signature (20 bytes)
  64.  
  65.   (6) Next, it writes bytes 33 to the end (offset 20h to end) of STANDARD.COM
  66.       to MODIFIED.COM, followed immediately by bytes 1 to 32.
  67.   (5) This is followed by the code to display the help message (see below),
  68.       and finally by the help text.
  69.  
  70. *** with /E switch ***************************
  71.   (1) ADDHELP reads MODIFIED.COM into memory.
  72.   (2) If the ADDHELP signature is found, it reconstructs the code of
  73.       the original program and writes it to STANDARD.COM.
  74.   (3) It writes the text of the help screen to HELP.TXT.
  75.  
  76. *** When you run MODIFIED.COM **************************************
  77.  
  78.  DOS (as usual) loads MODIFIED.COM into the memory  beginning at CS:0100,
  79.  and puts a copy of the command line tail beginning at CS:0081. It then
  80.  executes the instruction at CS:0100, which is the JUMP with which the
  81.  32-byte header in MODIFIED.COM begins. This takes us to the beginning of
  82.  the ADDHELP code, which does the following:
  83.  
  84.   (1) Retrieve the DOS switch character (usually '/');
  85.   (2) Check that there is a DOS command tail. If not, go to (5).
  86.   (3) Read the DOS command tail (at $0081 onwards) looking for the
  87.       switch character. If found, go to (4); if not, go to (5).
  88.   (4) If character after switch character is '?', write the help text
  89.       to DOS standard output and return control to DOS. Otherwise:
  90.   (5) Copy the first 32 bytes of STANDARD.COM from from their present
  91.       location in memory to to $0100 .. $011F, thus producing a complete
  92.       a complete memory image of STANDARD.COM starting at $0100.
  93.   (6) Jump to $0100 to begin execution of STANDARD.COM exactly as if that
  94.       was what had been loaded in the first place.
  95.  
  96. *** Code to add to MODIFIED.COM ******************************
  97.  
  98.  NB: this was assembled using DEBUG, starting at 0200h, but it will end
  99.  up somewhere else in CS: depending on the length of STANDARD.COM. The
  100.  jump commands (JMP, JZ, etc.) are not affected by this "relocation",
  101.  because they assemble into jumps relative to the starting point of the
  102.  jump, not jumps to a set address. Where it *is* necessary to goto a
  103.  particular address, this is done with an indirect jump.
  104.  
  105.  The Turbo Pascal array constant asmcode (below) contains the actual
  106.  bytes of code which the compiler will incorporate into ADDHELP.EXE
  107.  ready for ADDHELP to write them into MODIFIED.COM.
  108.  
  109. Addr Machine code  Mnemonics      comments
  110.  
  111. 0200 31DB          XOR    BX,BX    ;clear BX
  112. 0202 B437          MOV    AH,37    ;DOS function 37
  113. 0204 B000          MOV    AL,00    ;sub-function 00
  114. 0206 CD21          INT    21       ;returns switch character in DOS v. >=2.0
  115. 0208 88D3          MOV    BL,DL    ;put switch character in BL
  116. 020A 31D2          XOR    DX,DX    ;zero DX
  117. 020C 8A168000      MOV    DL,[0080];get length of command tail
  118. 0210 83FA00        CMP    DX,+00   ;if zero
  119. 0213 7419          JZ    022E     ;go prepare to run original program
  120. 0215 81C28000      ADD    DX,0080  ;= address of end of command tail
  121. 0219 BF8000        MOV    DI,0080  ;one before beginning of tail
  122. 021C 47            INC    DI       ;begin first loop: next char in tail
  123. 021D 3A1D          CMP    BL,[DI]  ;is it switch character?
  124. 021F 7406          JZ    0227     ;yes: go check for '?'
  125. 0221 39D7          CMP    DI,DX    ;no: are we at end of tail?
  126. 0223 75F7          JNZ    021C     ;no: loop back
  127. 0225 EB07          JMP    022E     ;yes: go prepare to run original program
  128. 0227 B03F          MOV    AL,3F    ;get a '?'
  129. 0229 47            INC    DI       ;next character in command tail
  130. 022A 3A05          CMP    AL,[DI]  ;is it a '?'
  131. 022C 7411          JZ    023F     ;yes: go display help screen;
  132.                                  ;no: prepare to run original
  133. 022E 8B360401      MOV    SI,[0104];get address of first 32 bytes
  134.                                  ;of STANDARD.COM
  135. 0232 BF0001        MOV    DI,0100  ;where to put them
  136. 0235 B92000        MOV    CX,0020  ;number of bytes to move
  137. 0238 F3            REPZ
  138. 0239 A4            MOVSB         ;move them
  139. 023A BF0001        MOV    DI,0100  ;address to begin execution
  140. 023D FFE7          JMP    DI       ;run the original
  141.                                  ;display help text:
  142. 023F B440          MOV    AH,40    ;DOS write function
  143. 0241 BB0100        MOV    BX,0001  ;handle for STDOUT
  144. 0244 8B0E0801      MOV    CX,[0108];get bytes in help text
  145. 0248 8B160601      MOV    DX,[0106];get address of help text
  146. 024C CD21          INT    21       ;do it
  147. 024E B44C          MOV    AH,4C    ;DOS exit function
  148. 0250 B040          MOV    AL,40    ;errorlevel 64 in case it's useful
  149. 0252 CD21          INT    21
  150. 0254 90            NOP             ;padding
  151. 0255 90            NOP             
  152. 0256 90            NOP             
  153. 0257 90            NOP             
  154.  
  155. ********************************************************}
  156.  
  157. uses dos, crt;
  158.  
  159. const asmarraycount = 11; {adjust this if the machine code changes}
  160.       comlimit = $FF00;   {the biggest .COM file we will consider}
  161.       numhelpmessages = 12;
  162.  
  163. type  asmarray = array[1..asmarraycount] of array[1..8] of byte;
  164.       bigarray = array[1..comlimit] of byte; 
  165.       bigarrayptr = ^bigarray;
  166.       messagearray = array[1 .. numhelpmessages] of string[79];
  167.       file_of_byte = file of byte;
  168.       mode_type = (adding, extracting, helping, no_parameters);
  169.                                  
  170. const notice1 = 'ADDHELP is copyright (C) John Nurick 1994.';
  171.       notice2 = 'John Nurick asserts the moral right to be identified';
  172.       notice3 = 'as the author of this work. Subject to this, ADDHELP';
  173.       notice4 = 'may freely be used, copied and distributed.';
  174.       JMP : byte = $E9;    {machine code}
  175.       NOP : byte = $90;
  176.       JMPsize = 3;         {bytes in a JMP nnnn instruction}
  177.       headersize = $20;    {bytes in the ADDHELP header}
  178.       signature = 'ADDHELP(C)J.NURICK94'; {this must be the right length
  179.                            to bring the length of the header to headersize;
  180.                            currently, this is headersize - 12, i.e. 20 bytes}
  181.  
  182.       asmcode : asmarray = {this contains the machine code that ADDHELP
  183.                            inserts into the new .COM file, padded with
  184.                            $90 (NOP) instructions}
  185.               ( ( $31 , $DB , $B4 , $37 , $B0 , $00 , $CD , $21 ),
  186.                 ( $88 , $D3 , $31 , $D2 , $8A , $16 , $80 , $00 ),
  187.                 ( $83 , $FA , $00 , $74 , $19 , $81 , $C2 , $80 ),
  188.                 ( $00 , $BF , $80 , $00 , $47 , $3A , $1D , $74 ),
  189.                 ( $06 , $39 , $D7 , $75 , $F7 , $EB , $07 , $B0 ),
  190.                 ( $3F , $47 , $3A , $05 , $74 , $11 , $8B , $36 ),
  191.                 ( $04 , $01 , $BF , $00 , $01 , $B9 , $20 , $00 ),
  192.                 ( $F3 , $A4 , $BF , $00 , $01 , $FF , $E7 , $B4 ),
  193.                 ( $40 , $BB , $01 , $00 , $8B , $0E , $08 , $01 ),
  194.                 ( $8B , $16 , $06 , $01 , $CD , $21 , $B4 , $4C ),
  195.                 ( $B0 , $40 , $CD , $21 , $90 , $90 , $90 , $90 ) );
  196.  
  197.       err_insufficientmemory = $1;
  198.       err_numparameters = $2;
  199.       err_unrecognisedswitch = $3;
  200.       err_dupname = $4;
  201.       err_open_infile = $5;
  202.       err_open_helpfile = $6;
  203.       err_open_outfile = $7;
  204.       err_filenotrecognised = $8;
  205.       err_closingfile =  $9;
  206.       err_nodiskspace = $A;
  207.       err_COMtoobig = $B;
  208.       err_EXEfile = $C;
  209.  
  210.       errs : messagearray =
  211.         ('Insufficient memory to load .COM file' ,
  212.          'Too few or too many command line parameters' ,
  213.          'Unrecognised command line switch or parameter' ,
  214.          'Two command line parameters point to the same file' ,
  215.          'Could not open input file ' ,
  216.          'Could not open help text file ' ,
  217.          'Could not open output file ' ,
  218.          'Cannot find ADDHELP code in input file ',
  219.          'Error closing file ' ,
  220.          'Insufficient disk space to write file(s)' ,
  221.          'Input .COM file is so big there is no room to add help screen',
  222.          'Input file appears to be in .EXE format' );
  223.  
  224. var   comarray : bigarrayptr;
  225.       insize, helpsize, outsize, errorcode, gaugeunit: word;
  226.       inname, helpname, outname : PathStr;
  227.       infile, helpfile, outfile : file_of_byte;
  228.       already_modified: boolean;
  229.       sw: char; {DOS switch character '/' or '-' or whatever}
  230.       mode: mode_type;
  231.       exitsave: pointer;
  232.  
  233.  
  234. {$F+}
  235. function HeapFunc(Size: word) : integer;
  236. {make new() return a nil pointer if insufficient memory}
  237. begin
  238.   HeapFunc := 1; 
  239. end;
  240.  
  241. procedure MyExitProc; {sets DOS errorlevel to 0 if OK, 1 if any error}
  242.   begin
  243.     ExitProc := exitsave;   {restore TP exit procedure}
  244.     if not ((ExitCode = 0) and (errorcode = 0))
  245.       then ExitCode := 1;
  246.   end;
  247.  
  248.  
  249. function get_switch: char; {gets DOS switch character}
  250. var R: registers;
  251. begin
  252.   with R do
  253.     begin
  254.       AX := $3700;
  255.       MsDos(R);
  256.       get_switch:= char(lo(DX));
  257.     end;
  258. end;
  259.  
  260.  
  261. procedure sign_on;
  262. begin
  263.   writeln; writeln;
  264.   writeln('ADDHELP.EXE : adds help facility to .COM files');
  265.   writeln('     Version 1.0 : John Nurick 1994');
  266.   writeln;
  267. end;
  268.  
  269. procedure read_infile;  {reads infile, looks for signature string
  270.                          and sets or clears already_modified flag}
  271. var n: word;
  272.     sig_test: string[20];
  273.  
  274. begin
  275.   write('      Reading ', inname, ' .');
  276.   read(infile, comarray^[1]);
  277.   read(infile, comarray^[2]);
  278.   if char(comarray^[1]) + char(comarray^[2]) = 'MZ' then
  279.     begin
  280.       errorcode := err_EXEfile;
  281.       exit;
  282.     end;
  283.   for n:= 3 to insize do
  284.     begin
  285.       read(infile, comarray^[n]);
  286.       if n mod gaugeunit = 0 then write('.');
  287.     end;
  288.   writeln('.');
  289.   if (mode = adding) and (insize < 32) then
  290.     begin
  291.       writeln('      Padding tiny file to 32 bytes ..');
  292.       while insize < 32 do
  293.         begin
  294.           inc(insize);
  295.           comarray^[insize] := NOP;
  296.         end;
  297.     end;
  298.   writeln('      Looking for ADDHELP signature in input file ...');
  299.   sig_test := '';
  300.   for n := 13 to 32 do sig_test := sig_test + char(comarray^[n]);
  301.   already_modified := (sig_test = signature);
  302. end;
  303.  
  304.  
  305. procedure initialise;
  306. var s: String;  dummy: integer;
  307. begin
  308.   exitsave :=  ExitProc;
  309.   ExitProc :=  @MyExitProc;
  310.   HeapError := @HeapFunc;
  311.   errorcode := 0;
  312.   FileMode := 0;
  313.   mode := no_parameters;
  314.   if ParamCount = 0 then
  315.     exit {and display syntax message}
  316.   else
  317.     begin
  318.       s := ParamStr(1);
  319.       if s[1] = sw then
  320.         begin
  321.           case UpCase(s[2]) of
  322.             'E' : mode := extracting;
  323.             'A' : mode := adding;
  324.             '?' : begin
  325.                     mode := helping;
  326.                     exit; {to show help screens}
  327.                   end;
  328.           else errorcode := err_unrecognisedswitch;
  329.           end {case}
  330.         end
  331.       else errorcode := err_unrecognisedswitch;
  332.     end;
  333.   if errorcode <> 0 then exit;
  334.   if ParamCount <> 4 then
  335.     errorcode := err_numparameters
  336.   else
  337.     begin
  338.       inname := FExpand(ParamStr(2));
  339.       helpname := FExpand(ParamStr(3));
  340.       outname := FExpand(ParamStr(4));
  341.       {check for duplicated names}
  342.       if inname = outname then errorcode := err_dupname;
  343.       if inname = helpname then errorcode := err_dupname;
  344.       if outname = helpname then errorcode := err_dupname;
  345.     end;
  346.   if errorcode <> 0 then exit;
  347.   {check files can be opened}
  348.   {$I-}
  349.   assign(infile, inname);
  350.   reset(infile);
  351.   if IOResult> 0 then
  352.     begin
  353.       errorcode := err_open_infile;
  354.       exit;
  355.     end;
  356.   insize := FileSize(infile);
  357.   if insize >= comlimit then
  358.     begin
  359.       errorcode := err_COMtoobig;
  360.       exit;
  361.     end;
  362.   case insize of
  363.     1 .. 256 :          gaugeunit := 24;
  364.     256 .. 1023 :       gaugeunit := 96;
  365.     1024 .. 4095:       gaugeunit := 384;
  366.     4096 .. 16383:      gaugeunit := 1536;
  367.   else gaugeunit := 5124;
  368.   end; {case}
  369.   new(comarray);
  370.   if comarray = nil then
  371.     begin
  372.       errorcode := err_insufficientmemory;
  373.       exit;
  374.     end;
  375.   read_infile;
  376.   if errorcode <> 0 then exit;
  377.   if (mode = extracting) and not already_modified then
  378.     begin
  379.       errorcode := err_filenotrecognised;
  380.       exit;
  381.     end;
  382.   assign(helpfile, helpname);
  383.   if mode = adding then reset(helpfile);
  384.   if mode = extracting then rewrite(helpfile);
  385.   if IOResult > 0 then
  386.     begin
  387.       errorcode := err_open_helpfile;
  388.       exit;
  389.     end;
  390.   assign(outfile, outname);
  391.   if errorcode = 0 then rewrite(outfile);
  392.   if IOResult > 0 then
  393.     begin
  394.       errorcode := err_open_outfile;
  395.       exit;
  396.     end;
  397.   {$I+}{check file sizes and disk space}
  398.   case mode of
  399.     adding :
  400.       begin
  401.         helpsize := FileSize(helpfile);
  402.         outsize := insize + helpsize + SizeOf(asmcode) + headersize;
  403.         if outsize >= comlimit then
  404.           begin
  405.             errorcode := err_COMtoobig;
  406.             exit;
  407.           end;
  408.         {add a bit to allow for cluster boundaries}
  409.         if outsize + $400 > DiskFree(ord(outname[1]) - 64)
  410.           then
  411.             begin
  412.               errorcode := err_nodiskspace;
  413.               exit;
  414.             end;
  415.       end;
  416.     extracting :
  417.       begin
  418.         outsize := insize + $400;
  419.         helpsize := $400;
  420.         if (outsize > DiskFree(ord(outname[1]) - 64)) or
  421.            (helpsize > DiskFree(ord(helpname[1]) - 64)) then
  422.              begin
  423.                errorcode := err_nodiskspace;
  424.                exit
  425.              end;
  426.       end;
  427.     end; {case}
  428. end;
  429.  
  430.  
  431. procedure write_word(w: word); {writes a word in byte-reversed order }
  432. var h,l: byte; ww: word;
  433. begin
  434.   h := hi(w);
  435.   l := lo(w);
  436.   write(outfile, l, h);
  437. end;
  438.  
  439. procedure write_str(p: string); {writes string to file}
  440. var n: integer;
  441. begin
  442.   for n:= 1 to length(p) do
  443.     write(outfile, byte(p[n]));
  444. end;
  445.  
  446. procedure write_header; {writes header to MODIFIED.COM}
  447. begin
  448.   writeln('      Writing header for new .COM file ...');
  449.   write(outfile,JMP);         {this and next line write the JMP instruction}
  450.   write_word(insize + headersize - JMPsize);
  451.   write(outfile, NOP);        {unused byte to maintain word bounds}
  452.   write_word($100 + insize); {address of displaced first 32 bytes}
  453.   write_word($100 + headersize + insize + SizeOf(asmcode));
  454.                               {address of beginning of help text}
  455.   write_word(helpsize);       {length of help text}
  456.   write_word($100);           {spare word}
  457.   write_str(signature);
  458. end;
  459.  
  460. procedure write_old; {writes STANDARD.COM to MODIFIED.COM,
  461.                       first 32 bytes displaced to end}
  462. var n: word;
  463. begin
  464.   write('      Writing original program to ', outname, '.');
  465.   for n:= headersize + 1 to insize do  {write the bulk of the .COM file}
  466.     begin
  467.       write(outfile, comarray^[n]);
  468.       if n mod gaugeunit = 0 then write('.');
  469.     end;
  470.   for n:= 1 to headersize do  {append the beginning of the .COM file}
  471.     write(outfile, comarray^[n]);
  472.   writeln('.');
  473. end;
  474.  
  475. procedure write_code; {writes ADDHELP machine code to MODIFIED.COM }
  476. var i,j: word;
  477. begin
  478.   writeln('      Writing help code ...');
  479.   for i:= 1 to asmarraycount do
  480.     for j := 1 to 8 do
  481.       write(outfile,asmcode[i,j]);
  482. end;
  483.  
  484. procedure modify_old; {this procedure used when ADDHELP finds its
  485.                        signature in STANDARD.COM}
  486.   var l, h: byte;
  487.       oldhelpstart, n: word;
  488.   begin
  489.     l := comarray^[7]; h := comarray^[8];
  490.     oldhelpstart := (l + $100 * h) - $100;
  491.     {change helpsize in comarray^ header}
  492.     comarray^[9] := lo(helpsize);
  493.     comarray^[10]:= hi(helpsize);
  494.     write('      Writing original program to ', outname, '.');
  495.     for n:= 1 to oldhelpstart do  {write the bulk of the .COM file}
  496.       begin
  497.         write(outfile, comarray^[n]);
  498.         if n mod gaugeunit = 0 then write('.');
  499.       end;
  500.     writeln('.');
  501.   end;
  502.  
  503. procedure write_help;  {writes help text to MODIFIED.COM}
  504. var b: byte;
  505. begin
  506.   writeln('      Writing help text ...');
  507.   repeat
  508.     read(helpfile, b);
  509.     write(outfile, b);
  510.   until eof(helpfile);
  511. end;
  512.  
  513. procedure extract_COM; {get STANDARD.COM out of comarray^}
  514. var n, c_len: word;
  515. begin
  516.   write('      Extracting ', outname, '..');
  517.   c_len := comarray^[5] + (256 * comarray^[6]) - $100;
  518.   for n := c_len + 1 to c_len + 32 do write(outfile, comarray^[n]);
  519.   for n := 33 to c_len do
  520.       begin
  521.       write(outfile, comarray^[n]);
  522.       if n mod gaugeunit = 0 then write('.');
  523.     end;
  524.   writeln('.');
  525. end;
  526.  
  527. procedure extract_text; {get help text out of comarray^}
  528. var n, h_loc, h_len: word;
  529. begin
  530.   writeln('      Extracting ', helpname, ' ...');
  531.   h_loc := comarray^[7] + (256 * comarray^[8]) - $100 + 1;
  532.                               {+1 to adjust for the difference between
  533.                                base 0 addressing in the file and and
  534.                                base 1 addressing in the array}
  535.   h_len := comarray^[9] + (256 * comarray^[10]);
  536.   for n := h_loc to (h_loc + h_len -1) do write(helpfile, comarray^[n]);
  537. end;
  538.  
  539.  
  540. procedure show_syntax;
  541.  
  542. begin
  543.   writeln(' Syntax:  ADDHELP  ', sw, 'A[dd]  STANDARD.COM  HELP.TXT  MODIFIED.COM');
  544.   writeln('          ADDHELP  ', sw, 'E[xtract] MODIFIED.COM  HELP.TXT  STANDARD.COM');
  545.   writeln('          ADDHELP  ', sw, '?   (display full help screens)');
  546. end;
  547.  
  548.  
  549. procedure show_help;
  550. var dummy: char;
  551. begin
  552. AssignCRT(Output); Rewrite(Output); {enable fast writing and ReadKey}
  553. ClrScr;
  554. sign_on;
  555. writeln;
  556. writeln(' ADDHELP will take a .COM program file and add to it the ability to display');
  557. writeln(' a help screen when it is run with the ', sw, '? switch, just like a standard');
  558. writeln(' DOS internal or external command. ');
  559. writeln;
  560. show_syntax;
  561. writeln;
  562. writeln(' The three filespecs can include paths. You must specify the .COM or other');
  563. writeln(' extensions. STANDARD.COM must be a .COM program (see next page of help).');
  564. writeln(' HELP.TXT must be a plain ASCII file of not more than 23 lines of text. If');
  565. writeln(' it looks right when displayed with the command TYPE helpfile, it will work.');
  566. writeln;
  567. writeln;
  568. write(' Press any key to continue');
  569. dummy := readkey;
  570. if dummy = #0 then dummy := readkey; {this is to deal with two-byte key codes}
  571. ClrScr;
  572. writeln;
  573. writeln(' ADDHELP seems safe to use on most .COM files including TSRs. It does not');
  574. writeln(' increase the amount of RAM occupied by TSRs. It is not suitable for .COM');
  575. writeln(' files that are merely part of a large program, such as WIN.COM in Windows.');
  576. writeln(' ADDHELP is not recommended for .COM files that modify themselves, e.g.');
  577. writeln(' to store configuration information.');
  578. writeln(' Programs modified by ADDHELP may fail if they are subsequently "patched",');
  579. writeln(' e.g. to change screen colours or hotkeys, either by a configuration program');
  580. writeln(' or with a disk editor. Avoid this problem by configuring a copy of the ');
  581. writeln(' original program and using ADDHELP to add the help facility to that copy.');
  582. writeln(' You can use ADDHELP with the ', sw, 'E command line switch to reconstruct copies ');
  583. writeln(' of the original program and helptext files.');
  584. writeln;
  585. writeln(' WARNING: The author gives no undertaking or warranty whatever about ADDHELP''s');
  586. writeln(' reliability or performance or its suitability for any purpose whatever.');
  587. writeln;
  588. writeln('   USE IT AT YOUR OWN RISK!! KEEP BACKUPS OF ANYTHING IMPORTANT!!!!');
  589. writeln;
  590. writeln(' ADDHELP v. 1.0 was written by John Nurick, who can sometimes be reached at');
  591. writeln(' 70162.2472@compuserve.com. It may be freely used, copied and distributed.');
  592. writeln;
  593. writeln;
  594. write(' Press any key to continue');
  595. dummy := readkey;
  596. if dummy = #0 then dummy := readkey;
  597. ClrScr;
  598. writeln;
  599. show_syntax;
  600. assign(Output,''); rewrite(output); {back to StdOut}
  601. end;
  602.  
  603.  
  604. procedure handle_error;
  605. var dummy: word; message: string[79];
  606.  
  607. begin
  608.   {$I-}
  609.   close(outfile);
  610.   close(infile);     {not all these files are necessarily open, so trying }
  611.   close(helpfile);   {to close them is likely to produce an IO error}
  612.   dummy := IOResult; {which this line ignores}
  613.   {$I+}
  614.   writeln(^G);
  615.   case errorcode of
  616.     5:  writeln(errs[errorcode], inname);
  617.     6:  writeln(errs[errorcode], helpname);
  618.     7:  writeln(errs[errorcode], outname);
  619.   else  writeln(errs[errorcode]);
  620.   end;
  621.   writeln;
  622.   show_syntax;
  623. end;
  624.  
  625. procedure tidy_up;
  626. var ok: boolean;
  627. begin
  628.   {$I-}
  629.   case mode of
  630.     adding .. extracting :
  631.       begin
  632.         close(infile);
  633.         if IOResult > 0 then errorcode := err_closingfile;
  634.         close(helpfile);
  635.         if IOResult > 0 then errorcode := err_closingfile;
  636.         close(outfile);
  637.         if IOResult > 0 then errorcode := err_closingfile;
  638.       end;
  639.   end;
  640.   {$I+}
  641.   if errorcode = 0 then
  642.     begin
  643.       writeln;
  644.       case mode of
  645.         adding :
  646.           begin
  647.             writeln('    ADDHELP finished.');
  648.             writeln('      Modified program is ', outname, '.')
  649.           end;
  650.         extracting :
  651.           begin
  652.             writeln('    ADDHELP finished.');
  653.             writeln('      Extracted original program is ', outname, ';');
  654.             writeln('      Extracted help text is ', helpname, '.');
  655.           end;
  656.       end;
  657.     end
  658.   else handle_error;
  659. end;
  660.  
  661. procedure addproc;
  662.   begin
  663.     if not already_modified then
  664.       begin
  665.         write_header;
  666.         write_old;
  667.         write_code;
  668.         write_help;
  669.       end
  670.     else {adding revised help to an already modified .COM file}
  671.       begin
  672.         writeln('      File has already been modified by ADDHELP');
  673.         writeln('      Substituting new help text ...');
  674.         modify_old;
  675.         write_help;
  676.       end;
  677.   end;
  678.  
  679.  
  680. procedure extractproc;
  681. begin
  682.   extract_COM;
  683.   extract_text;
  684. end;
  685.  
  686.  
  687. begin  {main}
  688.   assign(output,''); rewrite(output); {allow DOS I/O redirection}
  689.   sw:= get_switch;
  690.   sign_on;
  691.   initialise;
  692.   if errorcode = 0 then
  693.     case mode of
  694.       adding: addproc;
  695.       extracting: extractproc;
  696.       helping: show_help;
  697.       no_parameters: show_syntax;
  698.     end; {case}
  699.   if errorcode = 0
  700.     then tidy_up
  701.     else handle_error;
  702. end.
  703.