{
Subject: Enviro.pas Unit to change Dos Vars permanently


Had this floating round, hope it helps someone.
It works under Dos 5, NDos 6.01, and should work For any other Dos as well,
no guarantees tho' .

}
Unit Enviro;

Interface

Var EnvSeg,
    EnvOfs,
    EnvSize  :  Word;

Function  FindEnv:Boolean;
Function  IsEnvVar(Variable : String;Var Value : String):Boolean;
Procedure ChangeEnvVar(Variable,NewVal : String);

Implementation

Uses Dos;

Type MemoryControlBlock =     {MCB -- only needed fields are shown}
      Record
        Blocktag   :  Byte;
        BlockOwner :  Word;
        BlockSize  :  Word;
        misc       :  Array[1..3] of Byte;
        ProgramName:  Array[1..8] of Char;
      end;

    ProgramSegmentPrefix =   {PSP -- only needed fields are shown}
      Record                                           { offset }
        PSPtag     :  Word;  { $20CD or $27CD if PSP}  { 00 $00 }
        misc       :  Array[1..21] of Word;            { 02 $02 }
        Environment:  Word                             { 44 $2C }
      end;

Var
  MCB      : ^MemoryControlBlock;
  r        : Registers;
  Found    : Boolean;
  SegMent  : Word;
  EnvPtr   : Word;
  Startofs : Word;

Function FindEnvMCB:Boolean;
Var
  b        :  Char;
  BlockType:  String[12];
  Bytes    :  LongInt;
  i        :  Word;
  last     :  Char;
  MCBenv   :  ^MemoryControlBlock;
  MCBowner :  ^MemoryControlBlock;
  psp      :  ^ProgramSegmentPrefix;

begin
FindEnvMCB := False;

Bytes := LongInt(MCB^.BlockSize) SHL 4;    {size of MCB in Bytes}
if mcb^.blockowner = 0 then                { free space }
else begin
  psp := Ptr(MCB^.BlockOwner,0);            {possible PSP}
  if   (psp^.PSPtag = $20CD) or (psp^.PSPtag = $27CD) then begin
  MCBenv := Ptr(psp^.Environment-1,0);
  if   MCB^.Blockowner <> (segment + 1) then
    if psp^.Environment = (segment + 1) then
      if  MCB^.BlockOwner = MCBenv^.BlockOwner then begin
         EnvSize := MCBenv^.BlockSize SHL 4;      {multiply by 16}
         EnvSeg := PSP^.Environment;
         EnvOfs := 0;
         FindEnvMCB := True;
         end
    end
  end;
end;

Function FindEnv:Boolean;
begin
r.AH := $52;            {undocumented Dos Function that returns a Pointer}
Intr ($21,r);           {to the Dos 'list of lists'                      }
segment := MemW[r.ES:r.BX-2];  {segment address of first MCB found at}
                               {offset -2 from List of List Pointer  }
Repeat
MCB := Ptr(segment,0);    {MCB^ points to first MCB}
  Found := FindEnvMcb;    {Look at each MCB}
  segment := segment + MCB^.BlockSize + 1
Until (Found) or (MCB^.Blocktag = $5A);
FindEnv := Found;
end;

Function IsEnvVar(Variable : String;Var Value : String):Boolean;
Var Temp : String;
    ch   : Char;
    i    : Word;
    FoundIt : Boolean;
begin
Variable := Variable + '=';
FoundIt := False;
i := EnvOfs;
Repeat
  Temp := '';
  StartOfs := I;
  Repeat
    ch := Char(Mem[EnvSeg:i]);
    if Ch <> #0 then Temp := Temp + Ch;
    inc(i);
  Until (Ch = #0) or (I > EnvSize);
  if Ch = #0 then begin
    FoundIt := (Pos(Variable,Temp) = 1);
    if FoundIt then Value := Copy(Temp,Length(Variable)+1,255);
    end;
Until (FoundIt) or (I > EnvSize);
IsEnvVar := FoundIt;
end;

Procedure ChangeEnvVar(Variable,NewVal : String);
Var OldVal : String;
    p1,p2  : Pointer;
    i,j    : Word;
    ch,
    LastCh : Char;
begin
if IsEnvVar(Variable,OldVal) then begin
  p1 := Ptr(EnvSeg,StartOfs + Length(Variable)+1);
  if Length(OldVal) = Length(NewVal) then
     Move(NewVal[1],p1^,Length(NewVal))
  else if Length(OldVal) > Length(NewVal) then begin
     Move(NewVal[1],p1^,Length(NewVal));
     p1 := ptr(EnvSeg,StartOfs + Length(Variable)+Length(OldVal)+1);
     p2 := ptr(EnvSeg,StartOfs + Length(Variable)+Length(NewVal)+1);
     Move(p1^,p2^,EnvSize - ofs(p1^));
     end
  else begin   { newVar is longer than oldVar }
     p2 := ptr(EnvSeg,StartOfs + Length(Variable)+Length(NewVal)-length(OldVal)+1);
     Move(p1^,p2^,EnvSize - ofs(p2^));
     Move(NewVal[1],p1^,Length(NewVal));
     end;
  end
else      { creating a new Var }
  begin
  i := EnvOfs;
  ch := Char(Mem[EnvSeg:i]);
  Repeat
    LastCh := Ch;
    inc(i);
    ch := Char(Mem[EnvSeg:i]);
  Until (i > EnvSize) or ((LastCh = #0) and (Ch = #0));
  if i < EnvSize then begin
    j := 1;
    Variable := Variable + '=' + NewVal + #0 + #0;
    While (J < Length(Variable)) and (I <= EnvSize) do begin
      Mem[EnvSeg:i] := ord(Variable[j]);
      inc(i); Inc(j);
      end;
    end;
  end;
end;

begin
end.

{ TEST Program }
Uses Enviro;

Var EnvVar : String;

begin
if FindEnv then begin
  Writeln('Found the Enviroment !!');
  Writeln('Env is at address ',EnvSeg,':',EnvOfs);
  Writeln('And is ',EnvSize,' Bytes long');

  if IsEnvVar('COMSPEC',EnvVar) then Writeln('COMSPEC = ',EnvVar)
  else Writeln('COMSPEC is not set');

  if IsEnvVar('NewVar',EnvVar) then  Writeln('NewVar = ',EnvVar)
  else Writeln('NewVar is not set');

  ChangeEnvVar('NewVar','This is a new Var');

  if IsEnvVar('NewVar',EnvVar) then  Writeln('NewVar = ',EnvVar)
  else Writeln('NewVar is not set');

  ChangeEnvVar('NewVar','NewVar is now this');

  if IsEnvVar('NewVar',EnvVar) then  Writeln('NewVar = ',EnvVar)
  else Writeln('NewVar is not set');

  end;
end.
