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