ted@imsvax.UUCP (02/16/87)
At least two people have requested a copy of a TP fork/exec routine. This one is available on many BBS's and appears to have originated at Borland and to have been fine-tuned at one or two other sites. { EXEC.PAS version 1.2 } { This file contains 2 functions for Turbo Pascal that allow you to } { run other programs from within a Turbo program. The first function, } { SubProcess, actually calls up a different program using MS-DOS call } { 4BH, EXEC. The second function, GetComSpec, returns the path name } { of the command interpreter, which is necessary to do certain } { operations. There is also a main program that allows you to test the } { functions. } {----------------------------------------------------------------------} { Version 1.1 works with DOS 2.0 and 2.1. Version 1.0 only worked } { with DOS 3.0 due to a subtle bug in DOS 2.x. } { - Bela Lubkin } { Borland International Technical Support } { CompuServe 71016,1573 } {----------------------------------------------------------------------} { Version 1.2 corrects a compiling problem in the INLINE code area of } { SubProcess. The line: } { INLINE ($8D/$96/ PathName+1 / } { will always grenerate a ") required" at the + sign. Apparently } { Turbo only allows displacements on location counter references } { within the INLINE code (i.e. not on variable identifiers). } { - James Tuksal } { Burroughs Corporation } { 14115 Farmington Rd. } { Livonia, Michigan } { 48154 } {----------------------------------------------------------------------} TYPE Str66 = STRING [66]; Str255 = STRING [255]; { Pass SubProcess a string of the form: } { 'D:\FULL\PATH\NAME\OF\FILE.TYP parameter1 parameter2 ...' } { For example, } { 'C:\SYSTEM\CHKDSK.COM' } { 'A:\WS.COM DOCUMENT.1' } { 'C:\DOS\LINK.EXE TEST;' } { 'C:\COMMAND.COM /C COPY *.* B:\BACKUP >FILESCOP.IED' } { The fourth example shows several things. To do any of the } { following, you must invoke the command processor and let it do the } { work: } { redirection } { piping } { path searching } { searching for the extension of a program (.COM, .EXE, or .BAT) } { batch files; } { internal DOS commands } { The name of the command processor file is stored in the DOS } { environment. The function GetComSpec in this file returns the path } { name of the command processor. Also note that you must use the /C } { parameter or COMMAND will not work correctly. You can also call } { COMMAND with no parameters. This will allow the user to use the DOS } { prompt to run anything (as long as there is enough memory). To get } { back to your program, he can type the command EXIT. } { Actual example: } { I:=SubProcess (GetComSpec+' /C COPY *.* B:\BACKUP >FILESCOP.IED'); } { The value returned is the result returned by DOS after the EXEC } { call. The most common values are: } { 0: Success } { 1: Invalid function (should never happen with this routine) } { 2: File/path not found } { 8: Not enough memory to load program } { 10: Bad environment (greater than 32K) } { 11: Illegal .EXE file format } { If you get any other result, consult an MS-DOS Technical Reference } { manual. } { VERY IMPORTANT NOTE: you MUST use the Options menu of Turbo Pascal } { to restrict the amount of free dynamic memory used by your } { program. Only the memory that is not used by the heap is } { available for use by other programs. } FUNCTION SubProcess (CommandLine : Str255): INTEGER; CONST SSSave: INTEGER=0; SPSave: INTEGER=0; VAR Regs : RECORD CASE INTEGER OF 1: (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags : INTEGER); 2: (AL, AH, BL, BH, CL, CH, DL, DH : BYTE); END; FCB1 : ARRAY [0..36] OF BYTE; FCB2 : ARRAY [0..36] OF BYTE; PathName : Str66; CommandTail : Str255; ParmTable : RECORD EnvSeg : INTEGER; ComLin : ^INTEGER; FCB1Pr : ^INTEGER; FCB2Pr : ^INTEGER; END; BEGIN IF POS (' ', CommandLine)=0 THEN BEGIN PathName:=CommandLine+#0; CommandTail:=^M; END { if } ELSE BEGIN PathName:=COPY (CommandLine, 1, POS (' ', CommandLine)-1)+#0; CommandTail:=COPY (CommandLine, POS (' ', CommandLine), 255)+^M; END; { else } CommandTail [0]:=PRED (CommandTail [0]); WITH Regs Do BEGIN FILLCHAR (FCB1, SIZEOF (FCB1), 0); AX:=$2901; DS:=SEG (CommandTail [1]); SI:=OFS (CommandTail [1]); ES:=SEG (FCB1); DI:=OFS (FCB1); MSDOS (Regs); { Create FCB 1 } FILLCHAR (FCB2, SIZEOF (FCB2), 0); AX:=$2901; ES:=SEG (FCB2); DI:=OFS (FCB2); MSDOS (Regs); { Create FCB 2 } ES:=CSeg; BX:=SSEG-CSEG+MEMW [CSEG:MEMW [CSEG:$0101]+$112]; AH:=$4A; MSDOS (Regs); { Deallocate unused memory } WITH ParmTable DO BEGIN EnvSeg:=MEMW [CSEG:$002C]; ComLin:=ADDR (CommandTail); FCB1Pr:=ADDR (FCB1); FCB2Pr:=ADDR (FCB2); END; { with } INLINE ($BF/$01/$00/ {+MOV DI,0001h } $8D/$93/PathName/ {>LEA DX,[BP+DI+DS:PathName] } $8D/$9E/ParmTable/ { LEA BX,[BP+DS:ParmTable] } $B8/$00/$4B/ { MOV AX,4B00h } $1E/ { PUSH DS } $55/ { PUSH BP } $16/ { PUSH SS } $1F/ { POP DS } $16/ { PUSH SS } $07/ { POP ES } $2E/$8C/$16/SSSave/ { MOV CS:SSSave,SS } $2E/$89/$26/SPSave/ { MOV CS:SPSave,SP } $FA/ { CLI } $CD/$21/ { INT 21h } $FA/ { CLI } $2E/$8B/$26/SPSave/ { MOV SP,CS:SPSave } $2E/$8E/$16/SSSave/ { MOV SS,CS:SSSave } $FB/ { STI } $9C/ { PUSHF } $BF/$12/$00/ {+MOV DI,0012h } $3E/$8F/$83/Regs/ {>POP [BP+DI+DS:Regs] } $3E/$89/$86/Regs/ { MOV [BP+DS:Regs],AX } $5D/ { POP BP } $1F); { POP DS } { + Line added to correct compile problem in 1.1 } { > Line modified to correct compile problem in 1.1 } { The messing around with SS and SP is necessary because under DOS 2.x } { after returning from an EXEC call, ALL registers are destroyed } { except CS and IP! I wish I'd known that before I released this } { package the first time... } IF (Flags AND 1)<>0 THEN SubProcess:=AX ELSE SubProcess:=0; END; { with } END; { SubProcess } FUNCTION GetComSpec : Str66; TYPE Env=ARRAY [0..32767] OF CHAR; VAR EPtr : ^Env; EStr : Str255; Done : BOOLEAN; I : INTEGER; BEGIN EPtr:=PTR (MEMW [CSEG:$002C],0); I:=0; Done:=FALSE; EStr:=''; REPEAT IF EPtr^[I]=#0 THEN BEGIN IF EPtr^ [I+1]=#0 THEN Done:=TRUE; IF COPY (EStr, 1, 8)='COMSPEC=' THEN BEGIN GetComSpec:=COPY (EStr, 9, 100); Done:=TRUE; END; { if } EStr:=''; END { if } ELSE EStr:=EStr+EPtr^[I]; I:=I+1; UNTIL Done; END; { GetComSpec } { Example program. Set both mInimum and mAximum free dynamic memory } { to 100 and compile this to a .COM file. Delete the next line to } { enable: } VAR Command : Str255; I : INTEGER; BEGIN WRITELN ('Enter a * to quit; put a * before a command to use COMMAND.COM.'); REPEAT WRITE ('=->'); READLN (Command); IF Command='*' THEN HALT; IF Command<>'' THEN BEGIN IF Command [1]='*' THEN Command:=GetComSpec+' /C '+COPY (Command, 2, 255); I:=SubProcess (Command); IF I<>0 THEN WRITELN ('Error - ',I); END; UNTIL FALSE; END.