unit setenv; interface type s24 = string; Function SetTheEnv (symbol, val : s24) : boolean; implementation {uses asciiz;} const arena_size = 16; NORMAL_ATYPE = #$4D; LAST_ATYPE = #$5A; COMSPEC : string[8] = 'COMSPEC='; type PSP = record fill1 : array [1..10] of char; PrevTermHandlerPtr : ^integer; PrevCtrlCptr : ^integer; PrevCritErrPtr : ^integer; fill2 : array [1..22] of char; EnvirSeg : word; end; Arena = record ArenaType : char; PspSegment : word; NumOfSegments : word; fill3 : array [1..11] of char; ArenaData : string;{ca} end; str4 = string[4]; var ap : ^arena; {$ifdef Debug} Function HexStr (n:word):str4; const ha:array[0..15] of char=('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'); var str : str4; begin str[0]:=chr(4); str[1]:=ha[hi(n) shr 4]; str[2]:=ha[hi(n) and $F]; str[3]:=ha[(n shr 4) and $F]; str[4]:=ha[n and $F]; HexStr := str; end; {$endif} Function GetNextArena (var ap:arena) : pointer; var tp : pointer; begin tp := Ptr( Seg(ap)+1+ap.NumOfSegments, 0); GetNextArena := tp; end {GetNextArena}; Function IsValidArena (var ar:arena) : boolean; var ap1 : ^arena; begin IsValidArena := false; if ar.ArenaType <> NORMAL_ATYPE then Exit; ap1 := GetNextArena (ar); if ap1^.ArenaType <> NORMAL_ATYPE then Exit; ap1 := GetNextArena (ap1^); if (ap1^.ArenaType <> NORMAL_ATYPE) and (ap1^.ArenaType <> LAST_ATYPE) then Exit; IsValidArena:=true; end {IsValidArena}; Function GetFirstArena : pointer; { return pointer to the first arena. scan memory for a 0x4D on a segment start, see if this points to another two levels of arena. } var ap, ap1 : ^arena; segment : word; begin for segment:=60 to Cseg do begin ap := ptr(segment, 0); if IsValidArena (ap^) then begin GetFirstArena := ap; Exit; end; end; GetFirstArena := nil; end {GetFirstArena}; Function IsValidEnv (var ad:ca; NumSegs:integer):boolean; var COMSPECa : ca; adp : cap; BaseAD : word; begin BaseAD := ofs (ad); adp := @ad; PtoA (COMSPEC, COMSPECa); while ( adp^[0] <> #0 ) and ( (ofs(adp^)-BaseAD) shr 4 < NumSegs ) do begin if (strnicmp(adp^, COMSPECa, 8) = 0) then begin IsValidEnv:=true; Exit; end; adp := @adp^[strlen(adp^) + 1]; end {while}; IsValidEnv := false; end {IsValidEnv}; Function GetArenaOfEnvironment : pointer; { First get segment of COMMAND.COM from segment of previous critical err code. then go to this COMMAND.COM, and go get its ENV block, check that it is an ENV block } Label L1, L2; var ap : ^arena; Mypsp : ^psp; CCpsp : ^psp; CCseg, i : word; EnvSeg : word; ad : cap; begin GetArenaOfEnvironment := NIL; { set Mypspp to psp of this program } Mypsp := Ptr (PrefixSeg, 0); { set CCpsp to psp of COMMAND.COM } CCseg := Seg (Mypsp^.PrevCritErrPtr^); i := CCseg - 32; if i<60 then i:=60; while CCseg > i do begin ap := Ptr (CCseg, 0); if IsValidArena (ap^) then goto L1; dec (CCseg); end; exit; {error} L1: inc (CCseg); CCpsp := Ptr (CCseg, 0); {$ifdef Debug} writeln ('prog psp=', HexStr(seg(Mypsp^)), ' prog crit_err_seg=', HexStr(CCseg) ); {$endif} {first see if the env seg in command.com points at a good env block?} EnvSeg := CCpsp^.EnvirSeg; ap := Ptr (EnvSeg-1, 0); {$ifdef Debug} writeln ('Env ', HexStr(seg(ap^)), ', psp in env=', HexStr(ap^.PspSegment)); {$endif} { if a valid arena, then search the entire arena for validity, if not a valid arena, then maybe it is one of these fabricated guys that shells like "4DOS" set up, search the first 128 bytes only } i := ap^.NumOfSegments-1; if not IsValidArena(ap^) then i := 9 else if ap^.PspSegment <> CCseg then goto L2; if IsValidEnv(ap^.ArenaData, i) then begin GetArenaOfEnvironment := ap; {$ifdef Debug} writeln('env found'); {$endif} Exit; end; {command.com did not have a good env segment, lets search all MCB's } L2: ap := GetFirstArena; if ap=NIL then Exit; while (ap^.ArenaType <> LAST_ATYPE) do begin {$ifdef Debug} Writeln ('arena ', HexStr(seg(ap^))); {$endif} if (ap^.PspSegment=CCseg) and IsValidEnv(ap^.ArenaData, ap^.NumOfSegments-1) then begin GetArenaOfEnvironment := ap; {$ifdef Debug} writeln('env found'); {$endif} Exit; end; ap := GetNextArena (ap^); end; end {GetArenaOfEnvironment}; {*****************************************************************************} Function SetTheEnv (symbol, val : s24) : boolean; var TotalEnvSize, NeededSize, strlength : integer; sp, op, envir : cap; SymbolLen : integer; SymbolA, ValA : ca; Found : boolean; ap : ^arena; begin NeededSize := 0; Found := false; SetTheEnv := false; PtoA (Symbol, SymbolA); PtoA (Val, ValA); strupr(symbolA); SymbolLen := strlen (symbolA); SymbolA [SymbolLen] := '='; SymbolA [SymbolLen+1] := #0; { first, can the COMMAND.COM envir block be found ? } ap := GetArenaOfEnvironment; if ( ap = NIL) then exit; { search to end of the envir block, get sizes } TotalEnvSize := 16 * ap^.NumOfSegments; envir := @ap^.ArenaData; op := envir; sp := envir; while sp^[0] <> #0 do begin strlength := strlen(sp^)+1; if ( strnicmp(sp^, symbolA, SymbolLen+1) = 0 ) then found := true else begin NeededSize := NeededSize + strlength; if found then strcpy(op^ , sp^); op := @op^[strlength]; end; sp := @sp^[strlength]; end; op^[0] := #0; if (strlen(valA) > 0) then begin NeededSize := NeededSize + 3 + SymbolLen + strlen(valA); if (NeededSize > TotalEnvSize) then Exit; {could mess with environment expansion here} strcpy(op^, symbolA); strcat(op^, valA); op := @op^[strlen(op^)+1]; end; op^[0] := #0; SetTheEnv := true; end {SetTheEnv}; end.