home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / pascal / tsrsrc.zip / DISABLE.PAS < prev    next >
Pascal/Delphi Source File  |  1992-02-14  |  19KB  |  581 lines

  1. {**************************************************************************
  2. *   DISABLE - Activates or deactivates TSRs.                              *
  3. *   Copyright (c) 1987,1991 Kim Kokkonen, TurboPower Software.            *
  4. *   May be freely distributed and used but not sold except by permission. *
  5. ***************************************************************************
  6. *   version 2.3 5/4/87                                                    *
  7. *     first release. version number matches other TSR Utilities           *
  8. *   :                                                                     *
  9. *   long intervening history                                              *
  10. *   :                                                                     *
  11. *   version 3.0 9/24/91                                                   *
  12. *     update for DOS 5                                                    *
  13. *     add Quiet option                                                    *
  14. *     add support for high memory                                         *
  15. *   version 3.1 11/4/91                                                   *
  16. *     update for new WATCH detection method                               *
  17. *   version 3.2 11/22/91                                                  *
  18. *     change method of accessing high memory                              *
  19. *   version 3.3 1/8/92                                                    *
  20. *     find TSRs by name just like MAPMEM does                             *
  21. *     increase stack space                                                *
  22. *     add /H to use high memory optionally                                *
  23. *     new features for parsing and getting command line options           *
  24. *   version 3.4 2/14/92                                                   *
  25. *     add /L option to turn off low memory checking                       *
  26. ***************************************************************************
  27. *   telephone: 719-260-6641, CompuServe: 76004,2611.                      *
  28. *   requires Turbo Pascal version 6 to compile.                           *
  29. ***************************************************************************}
  30.  
  31. {$R-,S-,I-,V-,B-,F-,A-,E-,N-,G-,X-}
  32. {$M 4096,0,655360}
  33. {.$DEFINE MeasureStack}  {Activate to measure stack usage}
  34.  
  35. program DisableTSR;
  36.   {-Deactivate and reactivate memory resident programs}
  37.   {-Leaving them in memory all the while}
  38.  
  39. uses
  40.   Dos,
  41.   MemU;
  42.  
  43. var
  44.   Blocks : BlockArray;
  45.   BlockMax : BlockType;
  46.   WatchPsp : Word;
  47.   CommandSeg : Word;
  48.   HiMemSeg : Word;
  49.   Changes : ChangeArray;
  50.   ChangeMax, ActualMax, PspHex, StartMCB : Word;
  51.   Action : (aDeactivate, aActivate, aCheckFor);
  52.   Override : Boolean;
  53.   Quiet : Boolean;
  54.   UseLoMem, OptUseHiMem, UseHiMem : Boolean;
  55.   TsrName : PathStr;
  56.   {$IFDEF MeasureStack}
  57.   I : Word;
  58.   {$ENDIF}
  59.  
  60.   procedure Abort(msg : String; ErrorLevel : Byte);
  61.     {-Halt in case of error}
  62.   begin
  63.     WriteLn(msg);
  64.     Halt(ErrorLevel);
  65.   end;
  66.  
  67.   function ExecutableBlock(PspHex : Word) : Boolean;
  68.     {-Return true if psphex corresponds to an executable code block}
  69.   var
  70.     b : BlockType;
  71.   begin
  72.     for b := BlockMax downto 1 do
  73.       {Search back to find executable rather than environment block}
  74.       if Blocks[b].psp = PspHex then begin
  75.         ExecutableBlock := True;
  76.         Exit;
  77.       end;
  78.     ExecutableBlock := False;
  79.   end;
  80.  
  81.   procedure InitChangeArray(WatchPsp : Word);
  82.     {-Initialize information regarding the WATCH data block}
  83.   var
  84.     watchindex : Word;
  85.     p : ^ChangeBlock;
  86.   begin
  87.     {Maximum offset in WATCH data area}
  88.     ActualMax := MemW[WatchPsp:NextChange];
  89.  
  90.     {Transfer changes from WATCH into a buffer array}
  91.     watchindex := 0;
  92.     ChangeMax := 0;
  93.     while watchindex < ActualMax do begin
  94.       p := Ptr(WatchPsp, ChangeVectors+watchindex);
  95.       Move(p^, Changes[ChangeMax], SizeOf(ChangeBlock));
  96.       Inc(watchindex, SizeOf(ChangeBlock));
  97.       if watchindex < ActualMax then
  98.         inc(ChangeMax);
  99.     end;
  100.   end;
  101.  
  102.   procedure PutWatch(chg : ChangeBlock; var watchindex : Word);
  103.     {-Put a change block back into WATCH}
  104.   var
  105.     p : ^ChangeBlock;
  106.   begin
  107.     p := Ptr(WatchPsp, ChangeVectors+watchindex);
  108.     Move(chg, p^, SizeOf(ChangeBlock));
  109.     Inc(watchindex, SizeOf(ChangeBlock));
  110.   end;
  111.  
  112.   procedure ActivateTSR(PspHex : Word);
  113.     {-Patch out the active interrupt vectors of a specified TSR}
  114.   var
  115.     nextchg, chg, watchindex : Word;
  116.     checking, didsomething : Boolean;
  117.   begin
  118.     didsomething := False;
  119.     watchindex := 0;
  120.     chg := 0;
  121.  
  122.     {Scan looking for the specified PSP}
  123.     while chg <= ChangeMax do begin
  124.       with Changes[chg] do
  125.         case ID of
  126.  
  127.           $FF :               {This record starts a new PSP}
  128.             begin
  129.               checking := (PspAdd = PspHex);
  130.               nextchg := Succ(chg);
  131.               if checking then
  132.                 {Turn off interrupts}
  133.                 inline($FA)
  134.               else
  135.                 {Turn on interrupts}
  136.                 inline($FB);
  137.             end;
  138.  
  139.           $01 :               {This record has an inactive vector redefinition}
  140.             if checking then begin
  141.               {We're in the proper PSP}
  142.               didsomething := True;
  143.               {Change the ID to indicate that vector is active}
  144.               ID := 0;
  145.               {Put the original vector code back in place}
  146.               nextchg := Succ(chg);
  147.               if (Changes[nextchg].ID <> 2) or (Changes[nextchg].VecNum <> VecNum) then
  148.                 Abort('Program error in Activate, patch record not found', 255);
  149.               {Restore the patched over code}
  150.               Move(Changes[nextchg].SaveCode, Mem[VecSeg:VecOfs], 6);
  151.               {Don't output the following patch record}
  152.               inc(nextchg);
  153.             end else
  154.               nextchg := Succ(chg);
  155.  
  156.         else
  157.           nextchg := Succ(chg);
  158.         end;
  159.  
  160.       {Put the change block back into WATCH}
  161.       PutWatch(Changes[chg], watchindex);
  162.       {Advance to the next change record}
  163.       chg := nextchg;
  164.     end;
  165.  
  166.     {Store the count back into WATCH}
  167.     MemW[WatchPsp:NextChange] := watchindex;
  168.  
  169.     if not(didsomething) then
  170.       Abort('No changes were needed to activate '+HexW(PspHex), 1);
  171.  
  172.   end;
  173.  
  174.   procedure DeactivateTSR(PspHex : Word);
  175.     {-Patch out the active interrupt vectors of a specified TSR}
  176.   var
  177.     newchange : ChangeBlock;
  178.     chg, watchindex, curpsp : Word;
  179.     putrec, checking, didsomething : Boolean;
  180.  
  181.     procedure PutPatch(vecn : Byte; vecs, veco, curpsp : Word);
  182.       {-Patch vector entry point with JMP to previous controlling vector}
  183.     label
  184.       ExitPoint;
  185.     var
  186.       vec : ^Word;
  187.       chg : Word;
  188.     begin
  189.       {Get the original vector from WATCH}
  190.       Move(Mem[WatchPsp:(OrigVectors+(vecn shl 2))], vec, 4);
  191.  
  192.       {Scan the Changes array to look for redefinition of this vector}
  193.       for chg := 0 to ChangeMax do begin
  194.         with Changes[chg] do
  195.           case ID of
  196.             0, 1 :            {This is or was a redefined vector}
  197.               if vecn = VecNum then
  198.                 {It's the vector we're interested in}
  199.                 {Store the latest value of the vector}
  200.                 Move(VecOfs, vec, 4);
  201.             $FF :             {This record starts a new PSP}
  202.               if PspAdd = curpsp then
  203.                 {Stop when we get to the PSP that is being disabled}
  204.                 goto ExitPoint;
  205.           end;
  206.       end;
  207. ExitPoint:
  208.       {Patch the vector entry point into a JMP FAR vec}
  209.       Mem[vecs:veco] := $EA;
  210.       Move(vec, Mem[vecs:Succ(veco)], 4);
  211.     end;
  212.  
  213.     function CountVecs(chg : Word) : Word;
  214.       {-Return count of vectors taken over by the PSP starting at changeblock chg}
  215.     var
  216.       count : Word;
  217.       ID : Byte;
  218.     begin
  219.       count := 0;
  220.       repeat
  221.         {Skip over the first one, which defines the current PSP}
  222.         inc(chg);
  223.         ID := Changes[chg].ID;
  224.         if (ID = 0) and (chg <= ChangeMax) then
  225.           inc(count);
  226.       until (ID = $FF) or (chg >= ChangeMax);
  227.       CountVecs := count;
  228.     end;
  229.  
  230.     function ValidToPatch(chg : Word) : Boolean;
  231.       {-Assure that there is space to place 6-byte patches}
  232.     var
  233.       First : Word;
  234.       Next : Word;
  235.       I : Word;
  236.       J : Word;
  237.       IAddr : LongInt;
  238.       JAddr : LongInt;
  239.     begin
  240.       ValidToPatch := True;
  241.       if Override then
  242.         Exit;
  243.  
  244.       {First vector to patch}
  245.       First := chg+1;
  246.  
  247.       {Last vector to patch}
  248.       Next := First;
  249.       while (Next <= ChangeMax) and (Changes[Next].ID <> $FF) do
  250.         inc(Next);
  251.  
  252.       {Any to patch?}
  253.       if Next = First then
  254.         Exit;
  255.  
  256.       {Compare each pair to assure enough space for patch}
  257.       for I := First to Next-1 do begin
  258.         with Changes[I] do
  259.           IAddr := (LongInt(VecSeg) shl 4)+VecOfs;
  260.         for J := First to Next-1 do
  261.           if I <> J then begin
  262.             with Changes[J] do
  263.               JAddr := (LongInt(VecSeg) shl 4)+VecOfs;
  264.             if Abs(IAddr-JAddr) < 6 then begin
  265.               ValidToPatch := False;
  266.               Exit;
  267.             end;
  268.           end;
  269.       end;
  270.     end;
  271.  
  272.   begin
  273.  
  274.     {Scan looking for the specified PSP}
  275.     didsomething := False;
  276.     watchindex := 0;
  277.  
  278.     for chg := 0 to ChangeMax do begin
  279.       putrec := True;
  280.       with Changes[chg] do
  281.         case ID of
  282.  
  283.           $FF :               {This record starts a new PSP}
  284.             begin
  285.               checking := (PspAdd = PspHex);
  286.               if checking then begin
  287.                 {Store the current PSP}
  288.                 curpsp := PspAdd;
  289.                 {Make sure WATCH has room for the extra changes}
  290.                 if watchindex+(CountVecs(chg)*SizeOf(ChangeBlock)) >
  291.                 MaxChanges*SizeOf(ChangeBlock) then
  292.                   Abort('Insufficient space in WATCH data area', 255);
  293.                 {Make sure the patches will be valid}
  294.                 if not ValidToPatch(chg) then
  295.                   Abort('Insufficient space between vectors to patch TSR', 255);
  296.                 {Turn off interrupts}
  297.                 inline($FA);
  298.               end else
  299.                 {Turn on interrupts}
  300.                 inline($FB);
  301.             end;
  302.  
  303.           $00 :               {This record has an active vector redefinition}
  304.             if checking then begin
  305.               {We're in the proper PSP}
  306.               didsomething := True;
  307.  
  308.               {Change the ID to indicate that vector is inactive}
  309.               ID := 1;
  310.               {Output the record now so that the new record can immediately follow}
  311.               PutWatch(Changes[chg], watchindex);
  312.               putrec := False;
  313.  
  314.               {Output a new change record so we can reactivate later}
  315.               {Indicate this is a patch record}
  316.               newchange.ID := 2;
  317.               {Save which vector it goes with}
  318.               newchange.VecNum := VecNum;
  319.               {Save the code we'll patch over}
  320.               Move(Mem[VecSeg:VecOfs], newchange.SaveCode, 6);
  321.               {Output the record to the WATCH area}
  322.               PutWatch(newchange, watchindex);
  323.               {Patch in a JMP to the previous vector}
  324.               PutPatch(VecNum, VecSeg, VecOfs, curpsp);
  325.             end;
  326.  
  327.         end;
  328.       if putrec then
  329.         {Put the change block back into WATCH}
  330.         PutWatch(Changes[chg], watchindex);
  331.     end;
  332.  
  333.     {Store the count back into WATCH}
  334.     MemW[WatchPsp:NextChange] := watchindex;
  335.  
  336.     if not(didsomething) then
  337.       Abort('No changes were needed to deactivate '+tsrname, 1);
  338.  
  339.   end;
  340.  
  341.   procedure CheckUpperLowerOptions;
  342.     {-Set low and high memory options}
  343.   var
  344.     Arg : String[127];
  345.  
  346.     procedure GetArgs(S : String);
  347.     var
  348.       SPos : Word;
  349.     begin
  350.       SPos := 1;
  351.       repeat
  352.         Arg := StUpcase(NextArg(S, SPos));
  353.         if Arg = '' then
  354.           Exit;
  355.         if (Arg = '-U') or (Arg = '/U') then
  356.           UseHiMem := True
  357.         else if (Arg = '-H') or (Arg = '/H') then
  358.           OptUseHiMem := True
  359.         else if (Arg = '-L') or (Arg = '/L') then
  360.           UseLoMem := False;
  361.       until False;
  362.     end;
  363.  
  364.   begin
  365.     UseHiMem := False;
  366.     OptUseHiMem := False;
  367.     UseLoMem := True;
  368.  
  369.     {Get arguments from the command line and the environment}
  370.     GetArgs(StringPtr(Ptr(PrefixSeg, $80))^);
  371.     GetArgs(GetEnv('DISABLE'));
  372.   end;
  373.  
  374.   procedure GetOptions;
  375.     {-Analyze command line for options}
  376.  
  377.     procedure WriteCopyright;
  378.     begin
  379.       WriteLn('DISABLE ', Version, ', Copyright 1991 TurboPower Software');
  380.     end;
  381.  
  382.     procedure WriteHelp;
  383.       {-Show the options}
  384.     begin
  385.       WriteCopyright;
  386.       WriteLn;
  387.       WriteLn('DISABLE allows you to selectively disable and reenable a TSR while leaving it');
  388.       WriteLn('in memory. To run DISABLE, you must have previously installed the TSR utility');
  389.       WriteLn('WATCH.');
  390.       WriteLn;
  391.       WriteLn('DISABLE is command-line driven. You specify a single TSR by its name (if you');
  392.       WriteLn('are running DOS 3.0 or later) or by its address as determined from a MAPMEM');
  393.       WriteLn('report. Addresses must be preceded by a dollar sign "$" and specified in hex.');
  394.       WriteLn;
  395.       WriteLn('DISABLE accepts the following command line syntax:');
  396.       WriteLn;
  397.       WriteLn('  DISABLE TSRname|$PSPaddress [Options]');
  398.       WriteLn;
  399.       WriteLn('Options may be preceded by either / or -. Valid options are as follows:');
  400.       WriteLn;
  401.       WriteLn('  /A         reactivate the specified TSR.');
  402.       WriteLn('  /C         check whether TSR is installed.');
  403.       WriteLn('  /H         work with upper memory if available.');
  404.       WriteLn('  /O         disable the TSR even if dangerous.');
  405.       WriteLn('  /Q         write no screen output.');
  406.       WriteLn('  /U         work with upper memory, but halt if none found.');
  407.       WriteLn('  /?         write this help screen.');
  408.       Halt(1);
  409.     end;
  410.  
  411.     function FindOwner(tname : String) : Word;
  412.       {-Return segment of executable block with specified name}
  413.     var
  414.       b : BlockType;
  415.       IsCmd : Boolean;
  416.       M : McbPtr;
  417.       Name : String[79];
  418.     begin
  419.       tname := StUpcase(tname);
  420.  
  421.       {Scan the blocks in reverse order}
  422.       for b := BlockMax downto 1 do
  423.         with Blocks[b] do
  424.           if Succ(mcb) = psp then begin
  425.             {This block is an executable block}
  426.             IsCmd := (Psp = MemW[Psp:$16]);
  427.             M := Ptr(Mcb, 0);
  428.             if (not IsCmd) and (DosV > 2) and HasEnvironment(HiMemSeg, M) then
  429.               Name := NameFromEnv(M)
  430.             else if DosV >= 4 then
  431.               Name := NameFromMcb(M)
  432.             else if (not IsCmd) and (DosVT >= $031E) then
  433.               Name := NameFromMcb(M)
  434.             else
  435.               Name := '';
  436.             if StUpcase(Name) = tname then begin
  437.               FindOwner := Psp;
  438.               Exit;
  439.             end;
  440.           end;
  441.       FindOwner := $FFFF;
  442.     end;
  443.  
  444.     procedure GetArgs(S : String);
  445.     var
  446.       SPos : Word;
  447.       Code : Word;
  448.       Arg : String[127];
  449.     begin
  450.       SPos := 1;
  451.       repeat
  452.         Arg := NextArg(S, SPos);
  453.         if Arg = '' then
  454.           Exit;
  455.         if (Arg[1] = '?') then
  456.           WriteHelp
  457.         else if (Arg[1] = '-') or (Arg[1] = '/') then
  458.           case Length(Arg) of
  459.             1 : Abort('Missing command option following '+Arg, 254);
  460.             2 : case UpCase(Arg[2]) of
  461.                   '?' : WriteHelp;
  462.                   'A' : Action := aActivate;
  463.                   'C' : Action := aCheckFor;
  464.                   'E' : Action := aActivate;
  465.                   'H' : ; {ignore, but allow, here}
  466.                   'L' : ; {ignore, but allow, here}
  467.                   'O' : Override := True;
  468.                   'Q' : Quiet := True;
  469.                   'U' : ; {ignore, but allow, here}
  470.                 else
  471.                   Abort('Unknown command option: '+Arg, 254);
  472.                 end;
  473.           else
  474.             Abort('Unknown command option: '+Arg, 254);
  475.           end
  476.         else begin
  477.           {TSR to change}
  478.           if Arg[1] = '$' then begin
  479.             {Treat as hex address}
  480.             Val(Arg, PspHex, Code);
  481.             if Code <> 0 then
  482.               Abort('Invalid hex address specification: '+Arg, 254);
  483.           end else if DosV >= 3 then
  484.             {Treat as PSP owner name - scan to find proper PSP}
  485.             PspHex := FindOwner(Arg)
  486.           else
  487.             Abort('Must have DOS 3.0+ to find TSRs by name', 254);
  488.           TsrName := StUpcase(Arg);
  489.         end;
  490.       until False;
  491.     end;
  492.  
  493.   begin
  494.     {Initialize defaults}
  495.     PspHex := 0;
  496.     Action := aDeactivate;
  497.     Override := False;
  498.     Quiet := False;
  499.  
  500.     {Get arguments from the command line and the environment}
  501.     GetArgs(StringPtr(Ptr(PrefixSeg, $80))^);
  502.     GetArgs(GetEnv('DISABLE'));
  503.  
  504.     if not Quiet then
  505.       WriteCopyright;
  506.     if PspHex = 0 then
  507.       Abort('No TSR name or address specified', 254)
  508.     else if PspHex = $FFFF then
  509.       Abort('Did not find '+TsrName, 2);
  510.   end;
  511.  
  512. begin
  513.   {$IFDEF MeasureStack}
  514.   FillChar(Mem[SSeg:0], SPtr-16, $AA);
  515.   {$ENDIF}
  516.  
  517.   {Determine whether upper memory control is desired}
  518.   CheckUpperLowerOptions;
  519.  
  520.   {Initialize for high memory access}
  521.   if not UseLoMem then
  522.     OptUseHiMem := True;
  523.   if OptUseHiMem or UseHiMem then begin
  524.     HiMemSeg := FindHiMemStart;
  525.     if HiMemSeg = 0 then begin
  526.       if UseHiMem then
  527.         Abort('No upper memory blocks found', 255);
  528.     end else
  529.       UseHiMem := True;
  530.   end else
  531.     HiMemSeg := 0;
  532.  
  533.   {Get all allocated memory blocks in normal memory}
  534.   {Must do first to support TSRs by name in GetOptions}
  535.   FindTheBlocks(UseLoMem, HiMemSeg, Blocks, BlockMax, StartMcb, CommandSeg);
  536.  
  537.   {Analyze command line for options}
  538.   GetOptions;
  539.  
  540.   {Find the watch block}
  541.   WatchPsp := WatchPspSeg;
  542.   if WatchPsp = 0 then
  543.     Abort('WATCH must be installed in order to use DISABLE', 255);
  544.  
  545.   {Assure PspHex corresponds to an executable block}
  546.   if not ExecutableBlock(PspHex) then
  547.     Abort('No such TSR found', 2);
  548.  
  549.   {Initialize information regarding the WATCH data block}
  550.   InitChangeArray(WatchPsp);
  551.  
  552.   {Activate or deactivate the TSR}
  553.   case Action of
  554.     aDeactivate:DeactivateTSR(PspHex);
  555.     aActivate:ActivateTSR(PspHex);
  556.   end;
  557.  
  558.   {Write success message}
  559.   if not Quiet then begin
  560.     case Action of
  561.       aDeactivate:Write('Deactivated');
  562.       aActivate:Write('Activated');
  563.       aCheckFor:Write('Found');
  564.     end;
  565.     Write(' ');
  566.     if TsrName[1] = '$' then
  567.       Write('TSR at ');
  568.     WriteLn(TsrName);
  569.   end;
  570.  
  571.   {$IFDEF MeasureStack}
  572.   I := 0;
  573.   while I < SPtr-16 do
  574.     if Mem[SSeg:i] <> $AA then begin
  575.       writeln('Unused stack ', i, ' bytes');
  576.       I := SPtr;
  577.     end else
  578.       inc(I);
  579.   {$ENDIF}
  580. end.
  581.