home *** CD-ROM | disk | FTP | other *** search
/ RBBS in a Box Volume 1 #3.1 / RBBSIABOX31.cdr / fasm / exec.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1990-09-29  |  17.6 KB  |  418 lines

  1. (*[76703,3015]
  2. EXEC.PAS                  21-Jan-86 17305              345
  3.  
  4.     Keywords: MS-DOS PC-DOS EXECUTE SUBPROCESS CALL RUN COMMAND READ
  5.     ENVIRONMENT STRING SET
  6.     
  7.     Version 1.6:
  8.       o Call MS-DOS .COM or .EXE programs using the Exec function call (4Bh).
  9.       o Call COMMAND.COM to execute .BAT files, internal DOS commands, .COM or
  10.         .EXE files found in the PATH, allow redirection or piping, or give a
  11.         DOS prompt.
  12.       o Get the return codes from those programs.
  13.       o Get a SET parameter from the MS-DOS environment.
  14.       (Based on TURRUN.ASM in DL1, but in Pascal).
  15.       REQUIRES DOS 2.0 or greater.  17394 bytes.
  16.       Previous downloads: 1355
  17.       -  Bela Lubkin
  18. *)
  19.  
  20. { EXEC.PAS version 1.6
  21.   Copyright (C) 1986 by Bela Lubkin  (1/21/86)
  22.   Noncommercial use only EXCEPT with permission from Bela Lubkin; send
  23.   EasyPlex to CompuServe ID 76703,3015 for permission.
  24.  
  25.   See "VERY IMPORTANT NOTES" below before using these functions.  This is
  26.   especially important if you are using Turbo version 1.0 or 2.0.
  27.  
  28.   Allows you to
  29.     o Call MS-DOS programs
  30.     o Get the return codes from those programs
  31.     o Get strings from the MS-DOS environment
  32.  
  33.   Calling information
  34.   -------------------
  35.     Procedure FreeUpMemory;
  36.         This procedure calls MS-DOS to free up memory that is not used by the
  37.       running program.  It is needed only under Turbo 1.0 or 2.0.  It is
  38.       commented out in this file.  Uncomment it only if you are using a pre-
  39.       3.0 version of Turbo Pascal.  It must be called once and ONLY ONCE
  40.       before calling any of SubProcess, SubProcessViaCOMMAND, or Shell.  It is
  41.       a good idea to place this call early in the execution of the program.
  42.  
  43.     Function SubProcess(CommandLine: _Exec_Str255): Integer;
  44.         Calls an executable image (.COM or .EXE file) using MS-DOS function
  45.       4Bh, Exec.  The parameter CommandLine must contain both the name of the
  46.       program to run and the arguments to be passed to it, seperated by a
  47.       space.  Path searching and other amenities are not performed; the passed
  48.       in name must be specific enough to allow the file to be found, i.e.
  49.       'CHKDSK' will NOT work.  At least 'CHKDSK.COM' must be specified, and a
  50.       drive and path name will help even more.  For example,
  51.         'C:\SYSTEM\CHKDSK.COM'
  52.         'A:\WS.COM DOCUMENT.1'
  53.         'C:\DOS\LINK.EXE TEST;'
  54.         'D:\ASSEM\MASM.EXE PROG1 PROG1.OBJ NUL PROG1.MAP'
  55.         'C:\COMMAND.COM /C COPY *.* B:\BACKUP >FILESCOP.IED'
  56.         The last example uses COMMAND.COM to invoke a DOS internal command and
  57.       to perform redirection.  Only with the use of COMMAND.COM can the
  58.       following be done: redirection; piping; path searching; searching for
  59.       the extension of a program (.COM, .EXE, or .BAT); batch files; and
  60.       internal DOS commands.
  61.         Because the COMMAND-assisted Exec function is so useful, a seperate
  62.       function, SubProcessViaCOMMAND, is provided for that purpose.
  63.         The integer return value of SubProcess is the error value returned by
  64.       DOS on completion of the Exec call.  If it is nonzero, the call failed.
  65.       Here is a list of likely error values:
  66.          0: Success
  67.          2: File/path not found
  68.          3: Path not found
  69.          4: Too many files open (no handles left)
  70.          5: Access denied
  71.          8: Not enough memory to load program
  72.         10: Illegal environment (greater than 32K)
  73.         11: Illegal .EXE file format
  74.         32: Sharing violation
  75.         33: Lock violation
  76.       If you get any other result, consult an MS-DOS Technical Reference
  77.       manual.
  78.  
  79.     Function GetEnvStr(SearchString: _Exec_Str255): _Exec_Str255;
  80.         Gets a string from the MS-DOS environment.  The parameter SearchString
  81.       specifies the desired environment string.  The function result returns
  82.       the value of that string from the environment.  If the string is not
  83.       found, a null string is returned.  SearchString may have one special
  84.       value, '='.  This returns garbage under MS-DOS 2.x.  Under MS-DOS 3.x,
  85.       it returns the pathname under which the currently running program was
  86.       invoked.  Examples:
  87.         GetEnvStr('COMSPEC')   might = 'C:\COMMAND.COM'
  88.         GetEnvStr('PROMPT')    might = '$p $g'
  89.         GetEnvStr('REFLEX')    might = 'Herc'
  90.         GetEnvStr('=')         might = 'C:\TURBO\exectest.COM'
  91.       Only an exact match will succeed; case IS significant.  Do not include
  92.       an equal sign in the search string (GetEnvStr('COMSPEC=') will fail).
  93.         Note: if you are wondering why there is no SetEnvStr procedure, read
  94.       an MS-DOS Technical Reference manual.
  95.  
  96.     Function GetComSpec: _Exec_Str66;
  97.         This is a special case of GetEnvStr and simply returns the COMSPEC
  98.       environment string.  It is included for compatability with previous
  99.       EXEC.PAS versions.
  100.  
  101.     Function SubProcessViaCOMMAND(CommandLine: _Exec_Str255): Integer;
  102.         This is a special case of SubProcess.  The CommandLine is passed to
  103.       COMMAND.COM, which does all further processing.  Command lines invoked
  104.       via this function can do redirection and piping; undergo the normal DOS
  105.       PATH search; may be batch files; and may be internal DOS commands such
  106.       as COPY and RENAME.
  107.         Disadvantages of this approach are: a copy of COMMAND.COM must be
  108.       present (not always true on a floppy-based system); a slight time and
  109.       memory penalty is involved due to the loading of an extra copy of
  110.       COMMAND.COM (about 3K under DOS 3.1); the subprocess return code
  111.       (Errorlevel) is lost.  In most cases the benefits will outweight the
  112.       disadvantages.
  113.         The integer return code is the same as for SubProcess.
  114.         Note: you may be wondering why there is not
  115.  
  116.     Function Shell: Integer;
  117.         This is a special case of SubProcess.  It gives a DOS prompt to the
  118.       user.  Typing EXIT returns to the Turbo program.  The integer return
  119.       code is the same as for SubProcess.
  120.  
  121.     Function SubProcessReturnCode: Integer;
  122.         This function calls MS-DOS function 4Dh, Get Return Code of a
  123.       Sub-process.  The integer return value is the return code set by the
  124.       last subprocess you called.  Like Turbo's IOResult, SubProcessReturnCode
  125.       is only valid once after a SubProcess call, reverting to 0 on successive
  126.       calls.  The return code obtained after using SubProcessViaCOMMAND or
  127.       Shell is the code returned by COMMAND.COM, not by any other program, and
  128.       is not likely to be useful.
  129.         Note: Turbo 3.0 programs can set the return code by using the Halt
  130.       procedure with a parameter, e.g. Halt(20);.  Other languages can call
  131.       DOS function 4Ch (Terminate) with the return code in AL.  Use Inline
  132.       code or the MsDos procedure under Turbo 1.0 or 2.0.
  133.  
  134.   VERY IMPORTANT NOTES
  135.   --------------------
  136.   The Exec calls (SubProcess, SubProcessViaCOMMAND, Shell) will not work
  137.   unless you restrict Turbo's heap.  To do this, lower "mAximum dynamic free
  138.   memory" on the compiler Options menu to a reasonable value.  What is
  139.   reasonable depends on your program's use of the heap and the stack, and must
  140.   be determined by you.  If you use neither the heap nor recursion, as low as
  141.   400h (16K bytes) is probably more than enough.
  142.  
  143.   The Exec calls CANNOT be called from within the interactive Turbo compiler
  144.   system.  They can only be called from .COM or .CHN files running outside of
  145.   the Turbo environment.
  146.  
  147.   If you are using Turbo 1.0 or 2.0, you must call FreeUpMemory once and ONLY
  148.   ONCE before making any calls to SubProcess, SubProcessViaCOMMAND, or Shell.
  149.   Uncomment the FreeUpMemory procedure, remove the Exit statement from
  150.   SubProcess, and make sure you call FreeUpMemory before attempting to do any
  151.   Exec calls!!
  152.  
  153.   Revision history
  154.   ----------------
  155.   Version 1.6 1/21/86 re-adds support for Turbo 1.0, 2.0 by providing the
  156.               procedure FreeUpMemory.  Adds the Shell function that was
  157.               inadvertantly left out of 1.5.
  158.   Version 1.5 1/14/86 fixes the memory freeing bug by removing support for
  159.               Turbo 2.0.  String types changed to minimize chances of
  160.               collision.  General environment support added.  Explicit calls
  161.               for Exec-via-COMMAND.COM and Exec-to-DOS-prompt added.  Support
  162.               for getting the subprocess return code added.  Major
  163.               documentation overhaul.  NOW REQUIRES TURBO 3.0!
  164.               (Thanks to Stu Fuller 76703,501 for pointing out how easy it
  165.               was to add full environment support).
  166.   Version 1.4 attempts to fix a bug in the freeing of memory before the
  167.               Exec call.
  168.   Version 1.3 works with MS-DOS 2.0 and up, TURBO PASCAL version 1.0 and up.
  169.   Version 1.2 had a subtle but dangerous bug: I set a variable that was
  170.               addressed relative to BP, using a destroyed BP!
  171.   Version 1.1 didn't work with Turbo 2.0 because I used Turbo 3.0 features
  172.   Version 1.0 only worked with DOS 3.0 due to a subtle bug in DOS 2.x
  173.  
  174.     -  Bela Lubkin
  175.        CompuServe 76703,3015
  176. }
  177.  
  178. Type
  179.   _Exec_Str66=String[66];
  180.   _Exec_Str255=String[255];
  181.  
  182. Var
  183.   _Exec_Regs: Record Case Integer Of
  184.                 1: (AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags: Integer);
  185.                 2: (AL,AH,BL,BH,CL,CH,DL,DH: Byte);
  186.               End;
  187.   { NOTE: the above variable is referenced in an Inline statement.  It MUST
  188.     be a global variable (not a local variable or a typed constant)!! }
  189.  
  190. (* THIS PROCEDURE MUST BE CALLED ONCE AND ONLY ONCE, BEFORE ANY CALLS TO THE
  191.    SUBPROCESS PROCEDURES, AND ONLY IF TURBO 1.0 OR 2.0 IS BEING USED!!!!!!!!
  192.    Uncomment it only if necessary.
  193.  
  194. Procedure FreeUpMemory;
  195.   { Paraphrases part of Turbo 3.0's startup code:
  196.       (CS:[101h]+106h) is the address of the compiler Options.
  197.       Options[6] is the code segment size the program (paragraphs).
  198.       Options[8] is the data segment size.
  199.       (CS+Options[6]+Options[8]) is the highwater mark of the program, sans
  200.         stack/heap.
  201.       CS:[2] is supplied by DOS: the number of paragraphs available to the
  202.         program.
  203.       (CS:[2]-High water mark) is the number of paragraphs available to the
  204.         stack/heap.
  205.       Options[0Ch] is the maximum stack/heap size needed by the program.
  206.       The heap size is taken as the lesser of Options[0Ch] and
  207.         (CS:[2]-highwater mark).
  208.       The highwater mark including the stack/heap is the no-heap highwater
  209.         mark + the calculated heap size.
  210.       The size in paragraphs of the program, including code, data, stack and
  211.         heap, is the full highwater mark - the original CS.
  212.       DOS function 4Ah, Set memory block, is called to adjust the size of the
  213.         program's memory block to the calculated size of the entire program. }
  214.   Begin
  215.     InLine($2E/$8B/$36/$01/$01/     {     ADD SI,106h         }
  216.            $81/$C6/$06/$01/         {     ADD SI,106h         }
  217.            $8C/$CB/                 {     MOV BX,CS           }
  218.            $2E/$03/$5C/$06/         {     ADD BX,CS:[SI+6]    }
  219.            $2E/$03/$5C/$08/         {     ADD BX,CS:[SI+8]    }
  220.            $2E/$8B/$16/$02/$00/     {     MOV DX,CS:[2]       }
  221.            $29/$DA/                 {     SUB DX,BX           }
  222.            $2E/$3B/$54/$0C/         {     CMP DX,CS:[SI+0Ch]  }
  223.            $72/$04/                 {     JB  .1              }
  224.            $2E/$8B/$54/$0C/         {     MOV DX,CS:[SI+0Ch]  }
  225.            $8C/$C8/                 { .1: MOV AX,CS           }
  226.            $8E/$C0/                 {     MOV ES,AX           }
  227.            $01/$D3/                 {     ADD BX,DX           }
  228.            $2B/$D8/                 {     SUB BX,AX           }
  229.            $B4/$4A/                 {     MOV AH,4Ah          }
  230.            $CD/$21);                {     INT 21h             }
  231.   End; *)
  232.  
  233. Function SubProcess(CommandLine: _Exec_Str255): Integer;
  234.   Const
  235.     SSSave: Integer=0;
  236.     SPSave: Integer=0;
  237.  
  238.   Var
  239.     FCB1,FCB2: Array [0..36] Of Byte; {*}
  240.     PathName: _Exec_Str66;            {*}
  241.     CommandTail: _Exec_Str255;
  242.     ParmTable: Record                 {*}
  243.                  EnvSeg: Integer;
  244.                  ComLin: ^Integer;
  245.                  FCB1Pr: ^Integer;
  246.                  FCB2Pr: ^Integer;
  247.                End;
  248.     RegsFlags: Integer;               {*}
  249.     {*: these variables are accessed in an Inline statement; their
  250.         declarations must not be changed }
  251.  
  252.   Begin
  253.     If Pos(' ',CommandLine)=0 Then
  254.      Begin
  255.       PathName:=CommandLine+#0;
  256.       CommandTail:=^M;
  257.      End
  258.     Else
  259.      Begin
  260.       PathName:=Copy(CommandLine,1,Pred(Pos(' ',CommandLine)))+#0;
  261.       CommandTail:=Copy(CommandLine,Pos(' ',CommandLine),255)+^M;
  262.      End;
  263.     CommandTail[0]:=Pred(CommandTail[0]);
  264.     With _Exec_Regs Do
  265.      Begin
  266.       FillChar(FCB1,Sizeof(FCB1),0);
  267.       AX:=$2901;
  268.       DS:=Seg(CommandTail[1]);
  269.       SI:=Ofs(CommandTail[1]);
  270.       ES:=Seg(FCB1);
  271.       DI:=Ofs(FCB1);
  272.       MsDos(_Exec_Regs); { Create FCB 1 }
  273.       FillChar(FCB2,Sizeof(FCB2),0);
  274.       AX:=$2901;
  275.       ES:=Seg(FCB2);
  276.       DI:=Ofs(FCB2);
  277.       MsDos(_Exec_Regs); { Create FCB 2 }
  278.       With ParmTable Do
  279.        Begin
  280.         EnvSeg:=MemW[CSeg:$002C];
  281.         ComLin:=Addr(CommandTail);
  282.         FCB1Pr:=Addr(FCB1);
  283.         FCB2Pr:=Addr(FCB2);
  284.        End;
  285.       InLine($8D/$96/ PathName /$42/  { <DX>:=Ofs(PathName[1]); }
  286.              $8D/$9E/ ParmTable /     { <BX>:=Ofs(ParmTable);   }
  287.              $B8/$00/$4B/             { <AX>:=$4B00;            }
  288.              $1E/$55/                 { Save <DS>, <BP>         }
  289.              $16/$1F/                 { <DS>:=Seg(PathName[1]); }
  290.              $16/$07/                 { <ES>:=Seg(ParmTable);   }
  291.              $2E/$8C/$16/ SSSave /    { Save <SS> in SSSave     }
  292.              $2E/$89/$26/ SPSave /    { Save <SP> in SPSave     }
  293.              $FA/                     { Disable interrupts      }
  294.              $CD/$21/                 { Call MS-DOS             }
  295.              $FA/                     { Disable interrupts      }
  296.              $2E/$8B/$26/ SPSave /    { Restore <SP>            }
  297.              $2E/$8E/$16/ SSSave /    { Restore <SS>            }
  298.              $FB/                     { Enable interrupts       }
  299.              $5D/$1F/                 { Restore <BP>,<DS>       }
  300.              $9C/$8F/$86/ RegsFlags / { Flags:=<CPU flags>      }
  301.              $A3/ _Exec_Regs );       { _Exec_Regs.AX:=<AX>;    }
  302.       { The messing around with SS and SP is necessary because under DOS 2.x,
  303.         after returning from an EXEC call, ALL registers are destroyed except
  304.         CS and IP!  I wish I'd known that before I released this package the
  305.         first time... }
  306.       If (RegsFlags And 1)<>0 Then SubProcess:=AX
  307.       Else SubProcess:=0;
  308.      End;
  309.     Exit; { This line is here for one reason only: to cause compilation to
  310.             fail under Turbo 1.0 or 2.0 and force you to read this comment.
  311.             Go back and read the VERY IMPORTANT NOTES section.  Then comment
  312.             out this Exit, uncomment Procedure FreeUpMemory, and add a call to
  313.             it to your code before attempting any calls to any of the Exec
  314.             functions!! }
  315.   End;
  316.  
  317. Function GetEnvStr(SearchString: _Exec_Str255): _Exec_Str255;
  318.   Type
  319.     Env=Array [0..32767] Of Char;
  320.   Var
  321.     EPtr: ^Env;
  322.     EStr: _Exec_Str255;
  323.     Done: Boolean;
  324.     I: Integer;
  325.  
  326.   Begin
  327.     GetEnvStr:='';
  328.     If SearchString<>'' Then
  329.      Begin
  330.       EPtr:=Ptr(MemW[CSeg:$002C],0);
  331.       I:=0;
  332.       SearchString:=SearchString+'=';
  333.       Done:=False;
  334.       EStr:='';
  335.       Repeat
  336.         If EPtr^[I]=#0 Then
  337.          Begin
  338.           If EPtr^[Succ(I)]=#0 Then
  339.            Begin
  340.             Done:=True;
  341.             If SearchString='==' Then
  342.              Begin
  343.               EStr:='';
  344.               I:=I+4;
  345.               While EPtr^[I]<>#0 Do
  346.                Begin
  347.                 EStr:=EStr+EPtr^[I];
  348.                 I:=Succ(I);
  349.                End;
  350.               GetEnvStr:=EStr;
  351.              End;
  352.            End;
  353.           If Copy(EStr,1,Length(SearchString))=SearchString Then
  354.            Begin
  355.             GetEnvStr:=Copy(EStr,Succ(Length(SearchString)),255);
  356.             Done:=True;
  357.            End;
  358.           EStr:='';
  359.          End
  360.         Else EStr:=EStr+EPtr^[I];
  361.         I:=Succ(I);
  362.       Until Done;
  363.      End;
  364.   End;
  365.  
  366. Function GetComSpec: _Exec_Str66;
  367.   Begin
  368.     GetComSpec:=GetEnvStr('COMSPEC');
  369.   End;
  370.  
  371. Function SubProcessViaCOMMAND(CommandLine: _Exec_Str255): Integer;
  372.   Begin
  373.     SubProcessViaCOMMAND:=SubProcess(GetComSpec+' /C '+CommandLine);
  374.   End;
  375.  
  376. Function Shell: Integer;
  377.   Begin
  378.     Shell:=SubProcess(GetComSpec);
  379.   End;
  380.  
  381. Function SubProcessReturnCode: Integer;
  382.   Begin
  383.     _Exec_Regs.AH:=$4D;
  384.     MsDos(_Exec_Regs);
  385.     SubProcessReturnCode:=_Exec_Regs.AX;
  386.   End;
  387.  
  388. { Example program.  Set both mInimum and mAximum free dynamic memory to 100
  389.   and compile this to a .COM file.  Delete the next line to enable: }
  390. (*
  391.  
  392. Var Command: _Exec_Str255;
  393.     I: Integer;
  394.  
  395. Begin
  396.   { FreeUpMemory; }  { NECESSARY only for Turbo 1.0, 2.0 }
  397.   WriteLn('Enter * to quit; ** to call a subsidiary command shell.  Put a * before a');
  398.   WriteLn('command to call it via COMMAND.COM.');
  399.   Repeat
  400.     Write('=->');
  401.     ReadLn(Command);
  402.     If Command='*' Then Halt
  403.     Else If Command<>'' Then
  404.      Begin
  405.       If Command='**' Then
  406.        Begin
  407.         WriteLn('Type EXIT to return to Exec');
  408.         I:=Shell;
  409.        End
  410.       Else If Command[1]='*' Then I:=SubProcessViaCOMMAND(Copy(Command,2,255))
  411.       Else I:=SubProcess(Command);
  412.       If I<>0 Then WriteLn('Error - ',I);
  413.       WriteLn('Return code = ',SubProcessReturnCode);
  414.      End;
  415.   Until False;
  416. End.
  417. *)
  418.