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