home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / SYSUTL / TSRSRC31.ZIP / RELEASE.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1991-11-04  |  21.9 KB  |  698 lines

  1. {**************************************************************************
  2. *   RELEASE - Releases memory above the last MARK call made.              *
  3. *   Copyright (c) 1986,1991 Kim Kokkonen, TurboPower Software.            *
  4. *   May be freely distributed and used but not sold except by permission. *
  5. ***************************************************************************
  6. *   version 1.0 2/8/86                                                    *
  7. *     original public release                                             *
  8. *     (thanks to Neil Rubenking for an outline of the method used)        *
  9. *   :                                                                     *
  10. *   long intervening history                                              *
  11. *   :                                                                     *
  12. *   version 3.0 9/24/91                                                   *
  13. *     make compatible with DOS 5                                          *
  14. *     add Quiet option                                                    *
  15. *     close open file handles of released blocks                          *
  16. *     update for new WATCH behavior                                       *
  17. *     increase number of supported memory blocks to 256                   *
  18. *     add support for upper memory blocks                                 *
  19. *   version 3.1 11/4/91                                                   *
  20. *     no change                                                           *
  21. ***************************************************************************
  22. *   telephone: 719-260-6641, CompuServe: 76004,2611.                      *
  23. *   requires Turbo version 6 to compile.                                  *
  24. ***************************************************************************}
  25.  
  26. {$R-,S-,I-,V-,B-,F-,A-,E-,N-,G-,X-}
  27. {$M 16384,0,655360}
  28.  
  29. program ReleaseTSR;
  30.   {-Restore system to state it had when a MARK was placed}
  31.  
  32. uses
  33.   Dos,
  34.   MemU,
  35.   Ems;
  36.  
  37. const
  38.   Version = '3.1';
  39.   MarkID = 'M3.1 PARAMETER BLOCK FOLLOWS'; {Marking string for TSR MARK}
  40.   FmarkID = 'FM3.1 TSR';          {Marking string for TSR file mark}
  41.   NmarkID = 'MN3.1 TSR';          {Marking string for TSR NET file mark}
  42.  
  43.   ProtectChar = '!';          {Marks whose name begins with this will be
  44.                               released ONLY if an exact name match occurs}
  45.  
  46.   {Offsets into resident copy of MARK.COM for data storage}
  47.   MarkOffset = $103;          {Where markID is found in MARK TSR}
  48.   FmarkOffset = $60;          {Where FmarkID is found in FMARK TSR}
  49.   NmarkOffset = $60;          {Where NmarkID is found in MARKNET TSR}
  50.   VectorOffset = $120;        {Where vector table is stored}
  51.   EGAsavOffset = $520;        {Where the EGA save save is stored}
  52.   IntComOffset = $528;        {Where the interapps comm area is stored}
  53.   ParentOffset = $538;        {(TER) Where parent's PSP segment is stored}
  54.   EMScntOffset = $53A;        {Where count of EMS active pages is stored}
  55.   EMSmapOffset = $53C;        {Where the page map is stored}
  56.  
  57. var
  58.   Blocks : BlockArray;
  59.   markBlock, BlockMax : BlockType;
  60.   CommandSeg : Word;
  61.   StartMcb : Word;
  62.  
  63.   markName : String[127];
  64.  
  65.   FilMarkHandles, ReturnCode : Word;
  66.   ShowHiMem, DealWithEMS, KeepMark, MemMark, FilMark, Quiet : Boolean;
  67.   UmbLinkStatus : Boolean;
  68.   Keys : string[16];
  69.   SaveExit : pointer;
  70.  
  71.   TrappedBytes : LongInt;
  72.  
  73.   MarkEHandles : Word;
  74.   CurrEHandles : Word;
  75.   MarkEmsHandles : PageArrayPtr;
  76.   CurrEmsHandles : PageArrayPtr;
  77.  
  78.   {Save areas read in from file mark}
  79.   Vectors : array[0..1023] of Byte;
  80.   EGAsavTable : array[0..7] of Byte;
  81.   IntComTable : array[0..15] of Byte;
  82.   ParentTable : array[0..1] of Byte;
  83.   McbP : ^McbGroup;
  84.  
  85.   procedure SafeExit; far;
  86.   var
  87.     Status : Word;
  88.   begin
  89.     ExitProc := SaveExit;
  90.     if HiMemAvailable(DosV) then
  91.       Status := SetUmbLinkStatus(UmbLinkStatus);
  92.   end;
  93.  
  94.   procedure Abort(msg : String);
  95.     {-Halt in case of error}
  96.   begin
  97.     WriteLn(msg);
  98.     Halt(1);
  99.   end;
  100.  
  101.   procedure NoRestoreHalt(ReturnCode : Word);
  102.     {-Replace Turbo halt with one that doesn't restore any interrupts}
  103.   begin
  104.     SafeExit;
  105.     Close(Output);
  106.     asm
  107.       mov ah,$4C
  108.       mov al, byte(ReturnCode)
  109.       int $21
  110.     end;
  111.   end;
  112.  
  113.   function FindMark(markName, MarkID : String;
  114.                     MarkOffset : Word;
  115.                     var MemMark, FilMark : Boolean;
  116.                     var b : BlockType) : Boolean;
  117.     {-Find the last memory block matching idstring at offset idoffset}
  118.   var
  119.     BPsp : Word;
  120.     PassedFileMark : Boolean;
  121.  
  122.     function HasIDstring(segment : Word;
  123.                          idString : String;
  124.                          idOffset : Word) : Boolean;
  125.       {-Return true if idstring is found at segment:idoffset}
  126.     var
  127.       len : Byte;
  128.       tString : String;
  129.     begin
  130.       len := Length(idString);
  131.       tString[0] := Chr(len);
  132.       Move(Mem[segment:idOffset], tString[1], len);
  133.       HasIDstring := (tString = idString);
  134.     end;
  135.  
  136.     function GetMarkName(segment : Word) : String;
  137.       {-Return a cleaned up mark name from the segment's PSP}
  138.     var
  139.       tString : String;
  140.       tlen : Byte absolute tString;
  141.     begin
  142.       Move(Mem[segment:$80], tString[0], 128);
  143.       while (tlen > 0) and ((tString[1] = ' ') or (tString[1] = ^I)) do
  144.         Delete(tString, 1, 1);
  145.       while (tlen > 0) and ((tString[tlen] = ' ') or (tString[tlen] = ^I)) do
  146.         dec(tlen);
  147.       GetMarkName := StUpcase(tString);
  148.     end;
  149.  
  150.     function MatchMemMark(segment : Word;
  151.                           markName : String;
  152.                           var b : BlockType) : Boolean;
  153.       {-Return true if MemMark is unnamed or matches current name}
  154.     var
  155.       FoundIt : Boolean;
  156.       tString : String;
  157.     begin
  158.       tString := GetMarkName(segment);
  159.       if markName <> '' then begin
  160.         FoundIt := (tString = markName);
  161.         if not FoundIt and not ShowHiMem then
  162.           if (tString <> '') and (tString[1] = ProtectChar) then
  163.             {Current mark is protected, stop searching}
  164.             b := 1;
  165.       end else if (tString <> '') and (tString[1] = ProtectChar) then begin
  166.         {Stored mark name is protected}
  167.         FoundIt := False;
  168.         {Stop checking}
  169.         b := 1;
  170.       end else if tString = '' then
  171.         {Unnamed release and unnamed mark}
  172.         FoundIt := True
  173.       else begin
  174.         {Unnamed release and named mark, match only if didn't pass file mark}
  175.         FoundIt := not PassedFileMark;
  176.         {Stop searching if no match}
  177.         if not FoundIt then
  178.           B := 1;
  179.       end;
  180.       if not FoundIt then
  181.         dec(b);
  182.       MatchMemMark := FoundIt;
  183.     end;
  184.  
  185.     function MatchFilMark(segment : Word;
  186.                           markName : String;
  187.                           var b : BlockType) : Boolean;
  188.       {-Return true if FilMark is unnamed or matches current name}
  189.     var
  190.       FoundIt : Boolean;
  191.     begin
  192.       if markName <> '' then begin
  193.         FoundIt := (GetMarkName(segment) = markName);
  194.         if FoundIt then
  195.           {Assure named file exists}
  196.           FoundIt := ExistFile(markName);
  197.       end else begin
  198.         {File marks must be named on RELEASE command line}
  199.         FoundIt := False;
  200.         PassedFileMark := True;
  201.       end;
  202.       if not FoundIt then
  203.         dec(B);
  204.       MatchFilMark := FoundIt;
  205.     end;
  206.  
  207.   begin
  208.     {Scan from the last block down to find the last MARK TSR}
  209.     b := BlockMax;
  210.     MemMark := False;
  211.     FilMark := False;
  212.     PassedFileMark := False;
  213.     repeat
  214.       BPsp := Blocks[B].Psp;
  215.       if (Blocks[B].Mcb+1 <> BPsp) or (BPsp = PrefixSeg) then
  216.         {Don't match any non-program block or this program}
  217.         dec(b)
  218.       else if HasIDstring(BPsp, NmarkID, NmarkOffset) then begin
  219.         {A net mark, can't release it here}
  220.         if ShowHiMem then
  221.           {Keep looking}
  222.           dec(b)
  223.         else
  224.           {Stop looking}
  225.           b := 0;
  226.       end else if HasIDstring(BPsp, MarkID, MarkOffset) then
  227.         {An in-memory mark}
  228.         MemMark := MatchMemMark(BPsp, markName, b)
  229.       else if HasIDstring(BPsp, FmarkID, FmarkOffset) then
  230.         {A file mark}
  231.         FilMark := MatchFilMark(BPsp, markName, b)
  232.       else
  233.         {Not a mark}
  234.         dec(b);
  235.     until (b < 1) or MemMark or FilMark;
  236.     FindMark := MemMark or FilMark;
  237.   end;
  238.  
  239.   procedure ReadMarkFile(markName : String);
  240.     {-Read the mark file info into memory}
  241.   var
  242.     McbCount : Word;
  243.     f : file;
  244.   begin
  245.     Assign(f, markName);
  246.     Reset(f, 1);
  247.     if IoResult <> 0 then
  248.       Abort('Error opening mark file');
  249.  
  250.     {Read the vector table from the mark file, into a temporary memory area}
  251.     BlockRead(f, Vectors, 1024);
  252.  
  253.     {Read the BIOS miscellaneous save areas into temporary tables}
  254.     BlockRead(f, EGAsavTable, 8);
  255.     BlockRead(f, IntComTable, 16);
  256.     BlockRead(f, ParentTable, 2);
  257.  
  258.     {Read the stored EMS handles, if any}
  259.     BlockRead(f, MarkEHandles, SizeOf(Word));
  260.     GetMem(MarkEmsHandles, SizeOf(HandlePageRecord)*MarkEHandles);
  261.     BlockRead(f, MarkEmsHandles^, SizeOf(HandlePageRecord)*MarkEHandles);
  262.  
  263.     {Read the stored Mcb table}
  264.     BlockRead(f, McbCount, SizeOf(Word));
  265.     GetMem(McbP, SizeOf(Word)+2*SizeOf(Word)*McbCount);
  266.     BlockRead(f, McbP^.Mcbs, 2*SizeOf(Word)*McbCount);
  267.     McbP^.Count := McbCount;
  268.  
  269.     if IoResult <> 0 then
  270.       Abort('Error reading mark file');
  271.     Close(f);
  272.  
  273.     if not KeepMark then
  274.       {Delete the mark file so it causes no mischief later}
  275.       Erase(f);
  276.   end;
  277.  
  278.   procedure InitMarkInfo;
  279.     {-Set up information from mark in memory}
  280.   var
  281.     markPsp : Word;
  282.   begin
  283.     markPsp := Blocks[markBlock].psp;
  284.     MarkEHandles := MemW[markPsp:EMScntOffset];
  285.     MarkEmsHandles := Ptr(markPsp, EMSmapOffset);
  286.     McbP := Ptr(markPsp, EMSmapOffset+4*MarkEHandles);
  287.   end;
  288.  
  289.   procedure CopyVectors(markBlock : BlockType);
  290.     {-Put interrupt vectors back into table}
  291.   var
  292.     markPsp : Word;
  293.     Junk : Byte;
  294.   begin
  295.     {Interrupts off}
  296.     inline($FA);
  297.  
  298.     {Restore the main interrupt vector table and the misc save areas}
  299.     if FilMark then begin
  300.       Move(Vectors, Mem[0:0], 1024);
  301.       Move(EGAsavTable, Mem[$40:$A8], 8);
  302.       Move(IntComTable, Mem[$40:$F0], 16);
  303.       Move(ParentTable, Mem[PrefixSeg:$16], 2);
  304.     end else begin
  305.       markPsp := Blocks[markBlock].psp;
  306.       Move(Mem[markPsp:VectorOffset], Mem[0:0], 1024);
  307.       Move(Mem[markPsp:EGAsavOffset], Mem[$40:$A8], 8);
  308.       Move(Mem[markPsp:IntComOffset], Mem[$40:$F0], 16);
  309.       Move(Mem[markPsp:ParentOffset], Mem[PrefixSeg:$16], 2);
  310.     end;
  311.  
  312.     {Interrupts on}
  313.     inline($FB);
  314.  
  315.     {Move the old termination/break/error addresses into this program}
  316.     Move(Mem[0:$88], Mem[PrefixSeg:$0A], 12);
  317.  
  318.     {Give WATCH an opportunity to adjust its vector stubs}
  319.     Junk := DosVersion;
  320.   end;
  321.  
  322.   procedure MarkBlocks(markBlock : BlockType);
  323.     {-Mark those blocks to be released}
  324.  
  325.     procedure BatchWarning(b : BlockType);
  326.       {-Warn about the trapping effect of batch files}
  327.     var
  328.       t : BlockType;
  329.     begin
  330.       WriteLn('Memory space for TSRs installed prior to batch file');
  331.       WriteLn('will not be released until batch file completes.');
  332.       WriteLn;
  333.       ReturnCode := 1;
  334.       {Accumulate number of bytes temporarily trapped}
  335.       for t := 1 to b do
  336.         if Blocks[t].releaseIt then
  337.           inc(TrappedBytes, LongInt(MemW[Blocks[t].mcb:3]) shl 4);
  338.     end;
  339.  
  340.     procedure MarkBlocksAbove;
  341.       {-Mark blocks above the mark}
  342.     var
  343.       markPsp : Word;
  344.       b : BlockType;
  345.     begin
  346.       markPsp := Blocks[markBlock].psp;
  347.       for b := 1 to BlockMax do
  348.         with Blocks[b] do
  349.           if (b >= markBlock) and (psp = CommandSeg) then begin
  350.             {Don't release blocks owned by master COMMAND.COM}
  351.             releaseIt := False;
  352.             BatchWarning(b);
  353.           end else if KeepMark then
  354.             {Release all but RELEASE and the mark}
  355.             releaseIt := (psp <> PrefixSeg) and (psp > markPsp)
  356.           else
  357.             releaseIt := (psp <> PrefixSeg) and (psp >= markPsp);
  358.     end;
  359.  
  360.     procedure MarkUnallocatedBlocks;
  361.       {-Mark blocks that weren't allocated at time of mark}
  362.     var
  363.       markPsp : Word;
  364.       TopSeg : Word;
  365.       b : BlockType;
  366.       m : BlockType;
  367.       Found : Boolean;
  368.     begin
  369.       markPsp := Blocks[markBlock].psp;
  370.  
  371.       {Find last low memory mcb}
  372.       TopSeg := TopOfMemSeg-1;
  373.       m := 1;
  374.       Found := False;
  375.       while (not Found) and (m <= McbP^.Count) do
  376.         if McbP^.Mcbs[m].mcb >= TopSeg then
  377.           Found := True
  378.         else
  379.           inc(m);
  380.  
  381.       {Mark out all mcbs associated with psp of last low memory mcb}
  382.       TopSeg := McbP^.Mcbs[m-1].psp;
  383.       if TopSeg <> markPsp then
  384.         for m := 1 to McbP^.Count do
  385.           with McbP^.Mcbs[m] do
  386.             if psp = TopSeg then
  387.               psp := 0;
  388.  
  389.       for b := 1 to BlockMax do
  390.         with Blocks[b] do begin
  391.           Found := False;
  392.           m := 1;
  393.           while (not Found) and (m <= McbP^.Count) do begin
  394.             Found := (McbP^.Mcbs[m].psp <> 0) and (McbP^.Mcbs[m].mcb = mcb);
  395.             inc(m);
  396.           end;
  397.           if Found then
  398.             {was allocated at time of mark, keep it now unless a mark to be released}
  399.             releaseIt := not KeepMark and (psp = markPsp)
  400.           else if psp = CommandSeg then
  401.             {Don't release blocks owned by master COMMAND.COM}
  402.             releaseIt := False
  403.           else
  404.             {not allocated at time of mark}
  405.             releaseIt := (psp <> 0) and (psp <> PrefixSeg);
  406.         end;
  407.     end;
  408.  
  409.   begin
  410.     if ShowHiMem then
  411.       MarkUnallocatedBlocks
  412.     else
  413.       MarkBlocksAbove;
  414.  
  415.     {$IFDEF Debug}
  416.     for b := 1 to BlockMax do
  417.       with Blocks[b] do
  418.         WriteLn(b:3, ' ', HexW(psp), ' ', HexW(mcb), ' ', releaseIt);
  419.     {$ENDIF}
  420.   end;
  421.  
  422.   function ReleaseBlock(Segm : Word) : Word; assembler;
  423.     {-Use DOS services to release memory block}
  424.   asm
  425.     mov ah,$49
  426.     mov es,Segm
  427.     int $21
  428.     jc  @Done
  429.     xor ax,ax
  430. @Done:
  431.   end;
  432.  
  433.   procedure ReleaseMem;
  434.     {-Release DOS memory marked for release}
  435.   var
  436.     Status : Word;
  437.     B : BlockType;
  438.   begin
  439.     for B := 1 to BlockMax do
  440.       with Blocks[B] do
  441.         if releaseIt then
  442.           if ReleaseBlock(mcb+1) <> 0 then begin
  443.             WriteLn('Could not release block at segment ', HexW(mcb+1));
  444.             Abort('Memory may be a mess... Please reboot');
  445.           end;
  446.   end;
  447.  
  448.   procedure SetPSP(PSP : Word); assembler;
  449.     {-Sets current PSP}
  450.   asm
  451.     mov bx,psp
  452.     mov ax,$5000
  453.     int $21
  454.   end;
  455.  
  456.   procedure CloseHandles;
  457.     {-Close any handles of blocks marked for release}
  458.   type
  459.     HandleTable = array[0..65520] of Byte;
  460.   var
  461.     O : Word;
  462.     FileMax : Word;
  463.     TablePtr : ^HandleTable;
  464.     b : BlockType;
  465.     H : Byte;
  466.   begin
  467.     for b := 1 to BlockMax do
  468.       with Blocks[b] do
  469.         if releaseIt and (psp = mcb+1) and (memw[psp:0] = $20CD) then begin
  470.           {A released block with a program segment prefix}
  471.           {set psp to this block}
  472.           setpsp(psp);
  473.  
  474.           {Deal with expanded handle tables in DOS 3.0 and later}
  475.           if DosV >= 3 then begin
  476.             FileMax := MemW[Psp:$32];
  477.             TablePtr := Pointer(MemL[Psp:$34]);
  478.           end else begin
  479.             FileMax := 20;
  480.             TablePtr := Ptr(Psp, $18);
  481.           end;
  482.  
  483.           for O := 0 to FileMax-1 do begin
  484.             H := TablePtr^[O];
  485.             case H of
  486.               0, 1, 2, $FF : {standard handle or not open} ;
  487.             else
  488.               asm
  489.                 mov ah,$3E
  490.                 mov bx,O
  491.                 int $21      {ignore errors}
  492.               end;
  493.             end;
  494.           end;
  495.         end;
  496.  
  497.     {reset psp}
  498.     setpsp(prefixseg);
  499.   end;
  500.  
  501.   procedure RestoreEMSmap;
  502.     {-Restore EMS to state at time of mark}
  503.   var
  504.     O, N, NHandle : Word;
  505.  
  506.     procedure EmsError;
  507.     begin
  508.       WriteLn('Program error or EMS device not responding');
  509.       Abort('EMS memory may be a mess... Please reboot');
  510.     end;
  511.  
  512.   begin
  513.     {Get the existing EMS page map}
  514.     GetMem(CurrEmsHandles, MaxHandles*SizeOf(HandlePageRecord));
  515.     CurrEHandles := EmsHandles(CurrEmsHandles^);
  516.  
  517.     if CurrEHandles > MaxHandles then
  518.       WriteLn('EMS handle count exceeds capacity of RELEASE -- no action taken')
  519.  
  520.     else if CurrEHandles <> 0 then begin
  521.       {Compare the two maps and deallocate pages not in the stored map}
  522.       for N := 1 to CurrEHandles do begin
  523.         {Scan all current handles}
  524.         NHandle := CurrEmsHandles^[N].Handle;
  525.         if MarkEHandles > 0 then begin
  526.           {See if current handle matches one stored by MARK}
  527.           O := 1;
  528.           while (MarkEmsHandles^[O].Handle <> NHandle) and (O <= MarkEHandles) do
  529.             Inc(O);
  530.           {If not, deallocate the current handle}
  531.           if (O > MarkEHandles) then
  532.             if not FreeEms(NHandle) then
  533.               EmsError;
  534.         end else
  535.           {No handles stored by MARK, deallocate all current handles}
  536.           if not FreeEms(NHandle) then
  537.             EmsError;
  538.       end;
  539.     end;
  540.   end;
  541.  
  542.   procedure GetOptions;
  543.     {-Analyze command line for options}
  544.   var
  545.     i : Word;
  546.     Status : Word;
  547.     arg : String[127];
  548.  
  549.     procedure WriteCopyright;
  550.     begin
  551.       WriteLn('RELEASE ', Version, ', Copyright 1991 TurboPower Software');
  552.     end;
  553.  
  554.     procedure WriteHelp;
  555.       {-Show the options}
  556.     begin
  557.       WriteCopyright;
  558.       WriteLn;
  559.       WriteLn('RELEASE removes memory-resident programs from memory and restores the');
  560.       WriteLn('interrupt vectors to their state as found prior to the installation of a MARK.');
  561.       WriteLn('RELEASE manages both normal DOS memory and also Lotus/Intel Expanded Memory.');
  562.       WriteLn('If WATCH has been installed, RELEASE will update the WATCH data area for the');
  563.       WriteLn('TSRs released.');
  564.       WriteLn;
  565.       WriteLn('RELEASE accepts the following command line syntax:');
  566.       WriteLn;
  567.       WriteLn('  RELEASE [MarkName] [Options]');
  568.       WriteLn;
  569.       WriteLn('Options may be preceded by either / or -. Valid options are as follows:');
  570.       WriteLn;
  571.       WriteLn('  /E         do NOT access EMS memory.');
  572.       WriteLn('  /K         release memory, but keep the mark in place.');
  573.       WriteLn('  /Q         write no screen output.');
  574.       WriteLn('  /S chars   stuff string (<16 chars) into keyboard buffer on exit.');
  575.       WriteLn('  /U         consider upper memory blocks for release (DOS 5).');
  576.       WriteLn('  /?         write this help screen.');
  577.       WriteLn;
  578.       WriteLn('When /U is requested, a MarkName must always be specified.');
  579.       Halt(1);
  580.     end;
  581.  
  582.   begin
  583.     {Initialize defaults}
  584.     markName := '';
  585.     Keys := '';
  586.     ReturnCode := 0;
  587.     TrappedBytes := 00;
  588.  
  589.     KeepMark := False;
  590.     Quiet := False;
  591.     DealWithEMS := True;
  592.     ShowHiMem := False;
  593.  
  594.     i := 1;
  595.     while i <= ParamCount do begin
  596.       arg := ParamStr(i);
  597.       if (arg[1] = '?') then
  598.         WriteHelp
  599.       else if (arg[1] = '-') or (arg[1] = '/') then
  600.         case Length(Arg) of
  601.           1 : Abort('Missing command option following '+arg);
  602.           2 : case UpCase(arg[2]) of
  603.                 '?' : WriteHelp;
  604.                 'E' : DealWithEMS := False;
  605.                 'K' : KeepMark := True;
  606.                 'Q' : Quiet := True;
  607.                 'S' : begin
  608.                         if I >= ParamCount then
  609.                           Abort('Key string missing');
  610.                         inc(I);
  611.                         Arg := ParamStr(I);
  612.                         if Length(Arg) > 15 then
  613.                           Abort('No more than 15 keys may be stuffed');
  614.                         Keys := Arg+^M;
  615.                       end;
  616.                 'U' : ShowHiMem := True;
  617.               else
  618.                 Abort('Unknown command option: '+arg);
  619.               end;
  620.         else
  621.           Abort('Unknown command option: '+arg);
  622.         end
  623.       else
  624.         {Named mark}
  625.         markName := stupcase(arg);
  626.       inc(i);
  627.     end;
  628.  
  629.     if not Quiet then
  630.       WriteCopyright;
  631.  
  632.     {Initialize for high memory access}
  633.     if HiMemAvailable(DosV) then begin
  634.       UmbLinkStatus := GetUmbLinkStatus;
  635.       Status := SetUmbLinkStatus(ShowHiMem);
  636.       if ShowHiMem and (Status = 1) then
  637.         Abort('To access upper memory you must have DOS=[HIGH,]UMB in CONFIG.SYS');
  638.     end else
  639.       ShowHiMem := False;
  640.     SaveExit := ExitProc;
  641.     ExitProc := @SafeExit;
  642.  
  643.     if ShowHiMem then
  644.       if MarkName = '' then
  645.         Abort('Upper memory releases must refer to named marks');
  646.   end;
  647.  
  648. begin
  649.   {Analyze command line for options}
  650.   GetOptions;
  651.  
  652.   {Get all allocated memory blocks in normal memory}
  653.   FindTheBlocks(Blocks, BlockMax, StartMcb, CommandSeg);
  654.  
  655.   {Find the last one marked with the MARK idstring, and MarkName if specified}
  656.   if not FindMark(markName, MarkID, MarkOffset, MemMark, FilMark, markBlock) then
  657.     Abort('No matching marker found, or protected marker encountered.');
  658.  
  659.   {Get file mark information into memory}
  660.   if FilMark then
  661.     ReadMarkFile(markName)
  662.   else
  663.     InitMarkInfo;
  664.  
  665.   {Mark those blocks to be released}
  666.   MarkBlocks(markBlock);
  667.  
  668.   {Copy the vector table from the MARK copy}
  669.   CopyVectors(markBlock);
  670.  
  671.   {Close open file handles}
  672.   CloseHandles;
  673.  
  674.   {Release normal memory marked for release}
  675.   ReleaseMem;
  676.  
  677.   {Deal with expanded memory}
  678.   if DealWithEMS then
  679.     if EMSpresent then
  680.       RestoreEMSmap;
  681.  
  682.   {Write success message}
  683.   if not Quiet then begin
  684.     Write('Memory released after MARK');
  685.     if markName <> '' then
  686.       Write(' (', markName, ')');
  687.     WriteLn;
  688.     if ReturnCode <> 0 then
  689.       WriteLn(TrappedBytes, ' bytes temporarily trapped until batch file completes');
  690.   end;
  691.  
  692.   {Stuff keyboard buffer if requested}
  693.   if Length(Keys) > 0 then
  694.     StuffKeys(Keys, True);
  695.  
  696.   NoRestoreHalt(ReturnCode);
  697. end.
  698.