Contributor: TURBOPOWER SOFTWARE {$R-,S-,V-,I-,B-,F-} {Disable the following define if you don't have Turbo Professional} {$DEFINE UseTpro} {*********************************************************} {* TPENV.PAS 1.02 *} {* by TurboPower Software *} {*********************************************************} { Version 1.01 11/7/88 Find master environment in Dos 3.3 and 4.0 Version 1.02 11/14/88 Correctly find master environment when run Within AUTOEXEC.BAT } Unit TpEnv; {-Manipulate the environment} Interface Uses Opus; Type EnvArray = Array[0..32767] of Char; EnvArrayPtr = ^EnvArray; EnvRec = Record EnvSeg : Word; {Segment of the environment} EnvLen : Word; {Usable length of the environment} EnvPtr : Pointer; {Nil except when allocated on heap} end; Const ShellUserProc : Pointer = nil; {Put address of ExecDos user proc here if desi Procedure MasterEnv(Var Env : EnvRec); {-Return master environment Record} Procedure CurrentEnv(Var Env : EnvRec); {-Return current environment Record} Procedure NewEnv(Var Env : EnvRec; Size : Word); {-Allocate a new environment on the heap} Procedure DisposeEnv(Var Env : EnvRec); {-Deallocate an environment previously allocated on heap} Procedure SetCurrentEnv(Env : EnvRec); {-Specify a different environment For the current Program} Procedure CopyEnv(Src, Dest : EnvRec); {-Copy contents of Src environment to Dest environment} Function EnvFree(Env : EnvRec) : Word; {-Return Bytes free in environment} Function GetEnvStr(Env : EnvRec; Search : String) : String; {-Return a String from the environment} Function SetEnvStr(Env : EnvRec; Search, Value : String) : Boolean; {-Set environment String, returning True if successful} Procedure DumpEnv(Env : EnvRec); {-Dump the environment to StdOut} Function ProgramStr : String; {-Return the complete path to the current Program, '' if Dos < 3.0} Function SetProgramStr(Env : EnvRec; Path : String) : Boolean; {-Add a Program name to the end of an environment if sufficient space} {$IFDEF UseTpro} Function ShellWithPrompt(Prompt : String) : Integer; {-Shell to Dos With a new prompt} {$endIF} Procedure DisposeEnv(Var Env : EnvRec); {-Deallocate an environment previously allocated on heap} begin With Env do if EnvPtr <> nil then begin FreeMem(EnvPtr, EnvLen+31); ClearEnvRec(Env); end; end; Procedure SetCurrentEnv(Env : EnvRec); {-Specify a different environment For the current Program} begin With Env do if EnvSeg <> 0 then MemW[PrefixSeg:$2C] := EnvSeg; end; Procedure CopyEnv(Src, Dest : EnvRec); {-Copy contents of Src environment to Dest environment} Var Size : Word; SPtr : EnvArrayPtr; DPtr : EnvArrayPtr; begin if (Src.EnvSeg = 0) or (Dest.EnvSeg = 0) then Exit; if Src.EnvLen <= Dest.EnvLen then {Space For the whole thing} Size := Src.EnvLen else {Take what fits} Size := Dest.EnvLen-1; SPtr := Ptr(Src.EnvSeg, 0); DPtr := Ptr(Dest.EnvSeg, 0); Move(SPtr^, DPtr^, Size); FillChar(DPtr^[Size], Dest.EnvLen-Size, 0); end; Procedure SkipAsciiZ(EPtr : EnvArrayPtr; Var EOfs : Word); {-Skip to end of current AsciiZ String} begin While EPtr^[EOfs] <> #0 do Inc(EOfs); end; Function EnvNext(EPtr : EnvArrayPtr) : Word; {-Return the next available location in environment at EPtr^} Var EOfs : Word; begin EOfs := 0; if EPtr <> nil then begin While EPtr^[EOfs] <> #0 do begin SkipAsciiZ(EPtr, EOfs); Inc(EOfs); end; end; EnvNext := EOfs; end; Function EnvFree(Env : EnvRec) : Word; {-Return Bytes free in environment} begin With Env do if EnvSeg <> 0 then EnvFree := EnvLen-EnvNext(Ptr(EnvSeg, 0))-1 else EnvFree := 0; end; {$IFNDEF UseTpro} Function StUpcase(S : String) : String; {-Uppercase a String} Var SLen : Byte Absolute S; I : Integer; begin For I := 1 to SLen do S[I] := UpCase(S[I]); StUpcase := S; end; Function SearchEnv(EPtr : EnvArrayPtr; Var Search : String) : Word; {-Return the position of Search in environment, or $FFFF if not found. Prior to calling SearchEnv, assure that EPtr is not nil, Search is not empty } Var SLen : Byte Absolute Search; EOfs : Word; MOfs : Word; SOfs : Word; Match : Boolean; begin {Force upper Case search} Search := Upper(Search); {Assure search String ends in =} if Search[SLen] <> '=' then begin Inc(SLen); Search[SLen] := '='; end; EOfs := 0; While EPtr^[EOfs] <> #0 do begin {At the start of a new environment element} SOfs := 1; MOfs := EOfs; Repeat Match := (EPtr^[EOfs] = Search[SOfs]); if Match then begin Inc(EOfs); Inc(SOfs); end; Until not Match or (SOfs > SLen); if Match then begin {Found a match, return index of start of match} SearchEnv := MOfs; Exit; end; {Skip to end of this environment String} SkipAsciiZ(EPtr, EOfs); {Skip to start of next environment String} Inc(EOfs); end; {No match} SearchEnv := $FFFF; end; Procedure GetAsciiZ(EPtr : EnvArrayPtr; Var EOfs : Word; Var EStr : String); {-Collect AsciiZ String starting at EPtr^[EOfs]} Var ELen : Byte Absolute EStr; begin ELen := 0; While (EPtr^[EOfs] <> #0) and (ELen < 255) do begin Inc(ELen); EStr[ELen] := EPtr^[EOfs]; Inc(EOfs); end; end; Function GetEnvStr(Env : EnvRec; Search : String) : String; {-Return a String from the environment} Var SLen : Byte Absolute Search; EPtr : EnvArrayPtr; EOfs : Word; EStr : String; ELen : Byte Absolute EStr; begin With Env do begin ELen := 0; if (EnvSeg <> 0) and (SLen <> 0) then begin {Find the search String} EPtr := Ptr(EnvSeg, 0); EOfs := SearchEnv(EPtr, Search); if EOfs <> $FFFF then begin {Skip over the search String} Inc(EOfs, SLen); {Build the result String} GetAsciiZ(EPtr, EOfs, EStr); end; end; GetEnvStr := EStr; end; end; Implementation Type SO = Record O : Word; S : Word; end; Procedure ClearEnvRec(Var Env : EnvRec); {-Initialize an environment Record} begin FillChar(Env, SizeOf(Env), 0); end; Procedure MasterEnv(Var Env : EnvRec); {-Return master environment Record} Var Owner : Word; Mcb : Word; Eseg : Word; Done : Boolean; begin With Env do begin ClearEnvRec(Env); {Interrupt $2E points into COMMAND.COM} Owner := MemW[0:(2+4*$2E)]; {Mcb points to memory control block For COMMAND} Mcb := Owner-1; if (Mem[Mcb:0] <> Byte('M')) or (MemW[Mcb:1] <> Owner) then Exit; {Read segment of environment from PSP of COMMAND} Eseg := MemW[Owner:$2C]; {Earlier versions of Dos don't store environment segment there} if Eseg = 0 then begin {Master environment is next block past COMMAND} Mcb := Owner+MemW[Mcb:3]; if (Mem[Mcb:0] <> Byte('M')) or (MemW[Mcb:1] <> Owner) then {Not the right memory control block} Exit; Eseg := Mcb+1; end else Mcb := Eseg-1; {Return segment and length of environment} EnvSeg := Eseg; EnvLen := MemW[Mcb:3] shl 4; end; end; Procedure CurrentEnv(Var Env : EnvRec); {-Return current environment Record} Var ESeg : Word; Mcb : Word; begin With Env do begin ClearEnvRec(Env); ESeg := MemW[PrefixSeg:$2C]; Mcb := ESeg-1; if (Mem[Mcb:0] <> Byte('M')) or (MemW[Mcb:1] <> PrefixSeg) then Exit; EnvSeg := ESeg; EnvLen := MemW[Mcb:3] shl 4; end; end; Procedure NewEnv(Var Env : EnvRec; Size : Word); {-Allocate a new environment (on the heap)} Var Mcb : Word; begin With Env do if MaxAvail < Size+31 then {Insufficient space} ClearEnvRec(Env) else begin {31 extra Bytes For paraGraph alignment, fake MCB} GetMem(EnvPtr, Size+31); EnvSeg := SO(EnvPtr).S+1; if SO(EnvPtr).O <> 0 then Inc(EnvSeg); EnvLen := Size; {Fill it With nulls} FillChar(EnvPtr^, Size+31, 0); {Make a fake MCB below it} Mcb := EnvSeg-1; Mem[Mcb:0] := Byte('M'); MemW[Mcb:1] := PrefixSeg; MemW[Mcb:3] := (Size+15) shr 4; end; end; Function SetEnvStr(Env : EnvRec; Search, Value : String) : Boolean; {-Set environment String, returning True if successful} Var SLen : Byte Absolute Search; VLen : Byte Absolute Value; EPtr : EnvArrayPtr; ENext : Word; EOfs : Word; MOfs : Word; OldLen : Word; NewLen : Word; NulLen : Word; begin With Env do begin SetEnvStr := False; if (EnvSeg = 0) or (SLen = 0) then Exit; EPtr := Ptr(EnvSeg, 0); {Find the search String} EOfs := SearchEnv(EPtr, Search); {Get the index of the next available environment location} ENext := EnvNext(EPtr); {Get total length of new environment String} NewLen := SLen+VLen; if EOfs <> $FFFF then begin {Search String exists} MOfs := EOfs+SLen; {Scan to end of String} SkipAsciiZ(EPtr, MOfs); OldLen := MOfs-EOfs; {No extra nulls to add} NulLen := 0; end else begin OldLen := 0; {One extra null to add} NulLen := 1; end; if VLen <> 0 then {Not a pure deletion} if ENext+NewLen+NulLen >= EnvLen+OldLen then {New String won't fit} Exit; if OldLen <> 0 then begin {OverWrite previous environment String} Move(EPtr^[MOfs+1], EPtr^[EOfs], ENext-MOfs-1); {More space free now} Dec(ENext, OldLen+1); end; {Append new String} if VLen <> 0 then begin Move(Search[1], EPtr^[ENext], SLen); Inc(ENext, SLen); Move(Value[1], EPtr^[ENext], VLen); Inc(ENext, VLen); end; {Clear out the rest of the environment} FillChar(EPtr^[ENext], EnvLen-ENext, 0); SetEnvStr := True; end; end; Procedure DumpEnv(Env : EnvRec); {-Dump the environment to StdOut} Var EOfs : Word; EPtr : EnvArrayPtr; begin With Env do begin if EnvSeg = 0 then Exit; EPtr := Ptr(EnvSeg, 0); EOfs := 0; WriteLn; While EPtr^[EOfs] <> #0 do begin While EPtr^[EOfs] <> #0 do begin Write(EPtr^[EOfs]); Inc(EOfs); end; WriteLn; Inc(EOfs); end; WriteLn('Bytes free: ', EnvFree(Env)); end; end; {$IFDEF UseTpro} Function ShellWithPrompt(Prompt : String) : Integer; {-Shell to Dos With a new prompt} Const PromptStr : String[7] = 'PROMPT='; Var PLen : Byte Absolute Prompt; NSize : Word; Status : Integer; CE : EnvRec; NE : EnvRec; OldP : String; OldPLen : Byte Absolute OldP; begin {Point to current environment} CurrentEnv(CE); if CE.EnvSeg = 0 then begin {Error getting environment} ShellWithPrompt := -5; Exit; end; {Compute size of new environment} OldP := GetEnvStr(CE, PromptStr); NSize := CE.EnvLen; if OldPLen < PLen then Inc(NSize, PLen-OldPLen); {Allocate and initialize a new environment} NewEnv(NE, NSize); if NE.EnvSeg = 0 then begin {Insufficient memory For new environment} ShellWithPrompt := -6; Exit; end; CopyEnv(CE, NE); {Get the Program name from the current environment} OldP := ProgramStr; {Set the new prompt String} if not SetEnvStr(NE, PromptStr, Prompt) then begin {Program error, should have enough space} ShellWithPrompt := -7; Exit; end; {Transfer Program name to new environment if possible} if not SetProgramStr(NE, OldP) then ; {Point to new environment} SetCurrentEnv(NE); {Shell to Dos With new prompt in place} {Status := Exec('', True, ShellUserProc);} {Restore previous environment} SetCurrentEnv(CE); {Release the heap space} if Status >= 0 then DisposeEnv(NE); {Return exec status} ShellWithPrompt := Status; end; {$endIF} end. { EXAMPLE PROGRAM } Function DosVersion : Word; {-Return the Dos version, major part in AX} Inline( $B4/$30/ {mov ah,$30} $CD/$21/ {int $21} $86/$C4); {xchg ah,al} Function ProgramStr : String; {-Return the name of the current Program, '' if Dos < 3.0} Var EOfs : Word; Env : EnvRec; EPtr : EnvArrayPtr; PStr : String; begin ProgramStr := ''; if DosVersion < $0300 then Exit; CurrentEnv(Env); if Env.EnvSeg = 0 then Exit; {Find the end of the current environment} EPtr := Ptr(Env.EnvSeg, 0); EOfs := EnvNext(EPtr); {Skip to start of path name} Inc(EOfs, 3); {Collect the path name} GetAsciiZ(EPtr, EOfs, PStr); ProgramStr := PStr; end; Function SetProgramStr(Env : EnvRec; Path : String) : Boolean; {-Add a Program name to the end of an environment if sufficient space} Var PLen : Byte Absolute Path; EOfs : Word; Numb : Word; EPtr : EnvArrayPtr; begin SetProgramStr := False; With Env do begin if EnvSeg = 0 then Exit; {Find the end of the current environment} EPtr := Ptr(EnvSeg, 0); EOfs := EnvNext(EPtr); {Assure space For path} if EnvLen < PLen+EOfs+4 then Exit; {Put in the count field} Inc(EOfs); Numb := 1; Move(Numb, EPtr^[EOfs], 2); {Skip to start of path name} Inc(EOfs, 2); {Move the path into place} Path := Upper(Path); Move(Path[1], EPtr^[EOfs], PLen); {Null terminate} Inc(EOfs, PLen); EPtr^[EOfs] := #0; SetProgramStr := True; end; end;