home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 9 / CDACTUAL9.iso / share / Dos / VARIOS / pascal / SWAG9605.DDD / 0096_DOS Environment handling.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-05-31  |  26.9 KB  |  833 lines

  1. { ENVIRON.PAS                                            Revision 1.00 }
  2. { Written 4 Nov 1994 by Robert B. Clark <rclark@iquest.net>            }
  3. { ──────────────────────────────────────────────────────────────────── }
  4. { A collection of DOS environment routines for Turbo Pascal v4.0.      }
  5. { Requires DOS v3.0+.  Tested on 486/P5 MS-DOS 5/6.22/NW 3.11          }
  6. {                                                                      }
  7. { Donated to the public domain 17 Jan 96 by Robert B. Clark.           }
  8. { May be included in SWAG if so desired.                               }
  9. {                                                                      }
  10. { WARNING:  High-ASCII line-drawing characters are used in the Shell() }
  11. {           function near the end of this listing. Use the appropriate }
  12. {           emulation for your printer if you print this code.         }
  13. {                                                                      }
  14. { Last updated: 04 Apr 95                                              }
  15. { ──────────────────────────────────────────────────────────────────── }
  16.  
  17. UNIT Environ;  { SEE DEMO AT THE BOTTOM ! }
  18. {$B+ Boolean short-circuit
  19.   D- No debug information
  20.   S- No stack overflow checking
  21.   R- Range checking off
  22.   V- VAR string length checking off
  23.   I- I/O checking off }
  24.  
  25. INTERFACE
  26.  
  27. Uses Dos
  28. {$IFDEF UseLib} ,Files    { For FNStrip(), MAXPATHLEN and fileSpecType }
  29. {$ENDIF}        ;
  30.  
  31. { ──────────────────────Start personal lib interface────────────────── }
  32. {$IFNDEF UseLib   Definitions from my FILES.TPU unit }
  33.  
  34. CONST MAXPATHLEN   = 64;
  35. TYPE  fileSpecType = string[MAXPATHLEN];
  36.  
  37. {$ENDIF}
  38. { ──────────────────────End personal lib functions──────────────────── }
  39.  
  40. CONST MAX_EVAR_LEN  = 127;      { Maximum environment variable length }
  41.       MAX_EVAR_BLEN = 32768;    { Maximum size of environment block }
  42.  
  43. TYPE evarType    = string[MAX_EVAR_LEN];
  44.      envSizeType = 0..32768;
  45.      MCBType     = record
  46.          BlockID   : byte;
  47.          OwnerPSP  : word;
  48.          ParentPSP : word;
  49.          BlockSize : longint;
  50.          OwnerName : string[8];
  51.          MCB_Seg   : word;
  52.          MCB_Ofs   : word
  53.      end;
  54.  
  55. VAR   MASTER_MCB      : MCBType;
  56.       MASTER_ENVSEG,
  57.       CURRENT_ENVSEG  : word;
  58.       COMSPEC         : evarType;      { Value of COMSPEC evar }
  59.       PROGRAMNAME     : fileSpecType;  { Name of executing program }
  60. { ──────────────────────────────────────────────────────────────────── }
  61.  
  62. FUNCTION  EnvSize(envSeg: word): envSizeType;
  63. FUNCTION  MaxEnvSize(envSeg: word): envSizeType;
  64. FUNCTION  GetEnv(evar:evarType; envSeg: word): evarType;
  65. PROCEDURE DelEnv(evar:evarType; envSeg: word);
  66. FUNCTION  GetFirstMCB: word;
  67. PROCEDURE InitMCBType(var mcb: MCBType);
  68. PROCEDURE ReadMCB(var mcb: MCBType; var last, root: boolean);
  69. PROCEDURE FindRootEnv(var mcb: MCBType);
  70. FUNCTION  PutEnv(evar,value: evarType; envSeg: word): boolean;
  71. PROCEDURE AllocateBlock(var blockSize: longint; var segment: word);
  72. FUNCTION  DeallocateBlock(segment: word): boolean;
  73. FUNCTION  Shell(prompt: evarType): integer;
  74.  
  75. {$IFNDEF UseLib   Normally in Files.TPU }
  76. FUNCTION FNStrip(s: fileSpecType; specifier: byte): fileSpecType;
  77. {$ENDIF}
  78. { ──────────────────────────────────────────────────────────────────── }
  79.  
  80. IMPLEMENTATION
  81.  
  82. { ──────────────────────Start personal lib implementation───────────── }
  83. {$IFNDEF UseLib   Functions from my FILES.TPU unit }
  84.  
  85. FUNCTION FNStrip(s: fileSpecType; specifier: byte): fileSpecType;
  86. { Extracts (strips) specific portions of a fully-qualified filename.
  87.   The specifier is the sum of the desired portions:
  88.  
  89.                        Bit
  90.                      76543210               Dec
  91.                      .......x  Extension     1
  92.                      ......x.  Basename      2
  93.                      .....x..  Path          4
  94.                      ....x...  Disk letter   8
  95.  
  96.   A specifier of 0 is same as a specifier of 15 (all parts returned). }
  97.  
  98. var j,len,lastSlash, lastDot: integer;
  99.     disk: string[2];
  100.     path,temp: fileSpecType;
  101.     baseName: string[8];
  102.     ext: string[4];
  103.  
  104. begin
  105.    disk:=''; path:=''; baseName:='';
  106.    ext:=''; temp:='';
  107.    specifier:=specifier and $0f;       { Strip high bits }
  108.    {TrueName(s);}                      { Canonize filespec }
  109.    len:=Length(s);
  110.    if (specifier=0) or (specifier=15) then   { Return full name }
  111.    begin
  112.       FNStrip:=s;
  113.       exit
  114.    end;
  115.  
  116.    lastSlash:=0; lastDot:=0; j:=len;
  117.    while (lastSlash=0) and (j>0) do  { Get lastSlash & lastDot indices }
  118.    begin
  119.       if s[j]='\' then lastSlash:=j;
  120.       if (lastDot=0) and (s[j]='.') then lastDot:=j;
  121.       dec(j)
  122.    end;
  123.  
  124.    if (len>0) and (s[2] in [':','\']) then disk:=s[1]+s[2];
  125.    if (lastSlash>0) then
  126.    begin
  127.       if (disk<>'') then j:=3 else j:=1;
  128.       path:=Copy(s,j,lastSlash-j+1)
  129.    end;
  130.    if (lastDot > lastSlash) then j:=lastDot-1 else j:=len;
  131.    baseName:=Copy(s,lastSlash+1,j-lastSlash);
  132.    if (lastDot>0) then ext:=Copy(s,lastDot,len-lastDot+1);
  133.  
  134.    if (specifier and 8) >0 then temp:=temp+disk;
  135.    if (specifier and 4) >0 then temp:=temp+path;
  136.    if (specifier and 2) >0 then temp:=temp+baseName;
  137.    if (specifier and 1) >0 then temp:=temp+ext;
  138.  
  139.    FNStrip:=temp
  140. end; {FNStrip}
  141.  
  142. {$ENDIF}
  143. { ──────────────────────End personal lib implementation─────────────── }
  144.  
  145. FUNCTION EnvSize(envSeg: word): envSizeType;
  146. { Returns current size of environment segment 'envSeg' NOT INCL 2nd 00.}
  147. var i: envSizeType;
  148. begin
  149.    i:=0;
  150.    while (Mem[envSeg:i] <> 0) or (Mem[envSeg:i+1] <> 0) and
  151.       (i<MAX_EVAR_BLEN) do Inc(i);
  152.    EnvSize:=i+1
  153. end; {EnvSize}
  154.  
  155.  
  156. FUNCTION MaxEnvSize(envSeg: word): envSizeType;
  157. { Returns maximum size of environment segment 'envSeg' by reading the
  158.   word at offset 03 in its preceding MCB paragraph. }
  159. begin
  160.    MaxEnvSize:=MemW[envSeg-1:$003]*16  { size in bytes }
  161. end; {MaxEnvSize}
  162.  
  163.  
  164. type pType=^char;
  165.  
  166. procedure IncPtr(var p: pType);         { Increment evar char pointer }
  167. begin
  168.    p:=Ptr(seg(p^),ofs(p^)+1)
  169. end;
  170.  
  171.  
  172. function MatchEvar(evar: evarType; var p: pType): boolean;
  173. { Returns true if "evar" matches environment string data exactly (case-
  174.   sensitive). If found, p points to the '=' char after the evar name. }
  175. var i: integer;
  176.  
  177. begin
  178.    for i:=1 to length(evar) do
  179.    begin
  180.       if p^ <> evar[i] then    { Mismatch; exit and return false }
  181.       begin
  182.          MatchEvar:=false;
  183.          exit
  184.       end;
  185.       IncPtr(p)               { OK so far; increment pointer }
  186.    end;
  187.    MatchEvar:=p^='='          { True if p points to '=' }
  188. end; {MatchEvar}
  189.  
  190.  
  191. FUNCTION GetEnv(evar:evarType; envSeg: word): evarType;
  192. { Returns value of environment string 'evar' in the 'envSeg' segment.
  193.   If 'evar' does not exist, returns an empty string. Note that the match
  194.   is case-sensitive in order to accomodate the infamous "windir"
  195.   environment string. }
  196.  
  197. var done : boolean;
  198.     p    : pType;
  199.     i    : integer;
  200.     s    : evarType;
  201.  
  202. begin {GetEnv}
  203.    p:=ptr(envSeg,0);                    { Point to start of evar block }
  204.    i:=0; done:=false; s[0]:=#0;
  205.  
  206.    while (p^ <> chr(0)) and not done do   { while not EOBlock }
  207.    begin
  208.       if MatchEvar(evar,p) then
  209.       begin
  210.          IncPtr(p);                          { Skip past '=' char }
  211.          while (p^ <> chr(0)) and (i<MAX_EVAR_LEN) do
  212.          begin                               { Read chars into s until }
  213.             Inc(i);                          { end of ASCIIZ string }
  214.             s[i]:=p^;
  215.             IncPtr(p)
  216.          end;
  217.          s[0]:=chr(i);              { Store string length byte }
  218.          done:=true                 { Exit condition--we're done! }
  219.       end else
  220.       begin
  221.          while (p^ <> chr(0)) do    { No match; skip to end of ASCIIZ }
  222.             IncPtr(p);
  223.          IncPtr(p)                  { Advance pointer to next string }
  224.       end;
  225.    end; {while}
  226.    GetEnv := s
  227. end; {GetEnv}
  228.  
  229.  
  230. PROCEDURE DelEnv(evar:evarType; envSeg: word);
  231. { Removes environment variable 'evar' from environment table at
  232.  'envSeg'. }
  233.  
  234. var found     : boolean;
  235.     p         : pType;
  236.     i         : integer;
  237.     s         : evarType;
  238.     b0,b1,len : word;
  239.  
  240. begin {DelEnv}
  241.    p:=ptr(envSeg,0);                    { Point to start of evar table }
  242.    i:=0; found:=false; s[0]:=#0;
  243.  
  244.    while (p^ <> chr(0)) and not found do
  245.    begin
  246.       if MatchEvar(evar,p) then
  247.       begin
  248.          b1:=ofs(p^)-length(evar);  { First byte of evar (dest)}
  249.          while(p^ <> chr(0)) do
  250.             IncPtr(p);
  251.          IncPtr(p);
  252.          b0:=ofs(p^);               { Next evar (start) }
  253.          len:=EnvSize(envSeg)-b0+1; { Length of region }
  254.          if (len>0) then begin
  255.             Move(Mem[envSeg:b0],Mem[envSeg:b1],len)
  256.          end
  257.          else begin
  258.             FillChar(Mem[envSeg:b1],2,0)
  259.          end;
  260.          found:=true
  261.       end else
  262.       begin
  263.          while (p^ <> chr(0)) do    { No match; skip to end of ASCIIZ }
  264.             IncPtr(p);
  265.          IncPtr(p)                  { Advance pointer to next string }
  266.       end;
  267.    end; {while}
  268. end; {DelEnv}
  269.  
  270.  
  271. FUNCTION GetFirstMCB: word;
  272. { Get segment address of first MCB using the undocumented DOS
  273.   Interrupt 21/52 Get List of Lists. }
  274. var r: Registers;
  275. begin
  276.    r.AH:=$52;
  277.    MsDos(r);   { Get List of Lists in ES:BX; 1st MCB seg is at [BX-2] }
  278.    GetFirstMCB:=MemW[r.ES:r.BX-2]
  279. end; {GetFirstMCB}
  280.  
  281.  
  282. PROCEDURE InitMCBType(var mcb: MCBType);
  283. { Resets MCB record data to zero; segment to that of the first MCB }
  284. begin
  285.    with mcb do begin
  286.       MCB_Seg := GetFirstMCB;
  287.       MCB_Ofs := 0;
  288.       BlockID := 0;
  289.       OwnerPSP:= 0;
  290.       ParentPSP:=0;
  291.       BlockSize:=0;
  292.       OwnerName[0]:=chr(0)
  293.    end;
  294. end; {InitMCBType}
  295.  
  296.  
  297. PROCEDURE ReadMCB(var mcb: MCBType; var last, root: boolean);
  298. { Collects info about the MCB pointed to by mcb_seg:mcb_ofs.
  299.   'last' will be true if this is the last MCB in the chain;
  300.   'root' will be true if this MCB's owner is the same as the PSP owner.}
  301.  
  302. var p : ^MCBType;
  303.     i : integer;
  304.  
  305. begin {ReadMCB}
  306.    p:=Ptr(seg(mcb),ofs(mcb));
  307.    with mcb do
  308.    begin
  309.       blockID  := Mem[MCB_Seg:MCB_Ofs];     { Block type = 'M' or 'Z' }
  310.       p^.ownerPSP:=MemW[MCB_Seg:MCB_Ofs+1]; { PSP segment of MCB owner }
  311.       parentPSP:= MemW[ownerPSP:$0016];     { Parent/self PSP segment }
  312.       blockSize:= MemW[MCB_Seg:MCB_Ofs+3];  { Size of MCB in paragraphs}
  313.  
  314.       for i:=$08 to $0f do ownerName[i-7]:=Chr(Mem[MCB_Seg:MCB_Ofs+i]);
  315.       ownerName[0]:=chr(8);            { DOS v4.0+ }
  316.  
  317.       last := blockID <> $4D;          { True if this is the last MCB }
  318.       root := (parentPSP = ownerPSP)   { True if this is the root MCB }
  319.    end; {with}
  320. end; {ReadMCB}
  321.  
  322.  
  323. PROCEDURE FindRootEnv(var mcb: MCBType);
  324. { Walks the MCB chain until root environment is found (MCB owner =
  325.   parent_id). Returns the segment of that process' environment in the
  326.   MCB record. }
  327.  
  328. var last,root : boolean;
  329.     offset    : longint;
  330.     block     : integer;
  331. begin
  332.    InitMCBType(mcb);
  333.    block:=0;
  334.    repeat
  335.       ReadMCB(mcb,last,root);
  336.       Inc(block);
  337.       if not root then
  338.       begin
  339.         offset := mcb.MCB_Ofs+16+(mcb.BlockSize*16);
  340.         mcb.MCB_Ofs := offset mod $10000;
  341.         mcb.MCB_Seg := mcb.MCB_Seg + (offset div $10000)
  342.      end;
  343.    until root or (block>100)  { Til root found or 100 blocks examined }
  344. end; {FindRootEnv}
  345.  
  346.  
  347. FUNCTION PutEnv(evar,value: evarType; envSeg: word): boolean;
  348. { Put environment variable 'evar' into environment segment 'envSeg'
  349.   and give it the value 'value'. If 'value' is null, effect is same as
  350.   if DelEnv() was called. Returns true if successful. }
  351.  
  352. var len, origLen, i     : integer;
  353.     maxSize, currentSize: envSizeType;
  354.     s: evarType;
  355. begin
  356.    s:=evar+'='+value+chr(0)+chr(0);   { Make evar string }
  357.    len:=length(s);                    { Length includes terminal 0000 }
  358.    origLen:=length(GetEnv(evar,envSeg))+length(evar)+2;
  359.    currentSize:=EnvSize(envSeg);
  360.    maxSize:=MaxEnvSize(envSeg);
  361.  
  362.    if (currentSize-origLen+len > maxSize) then
  363.    begin
  364.       PutEnv:=false;                { Insufficient space }
  365.       exit
  366.    end;
  367.  
  368.    DelEnv(evar,envSeg);             { Delete evar if exists }
  369.    if value[0]=chr(0) then begin    { Empty evar value string }
  370.       PutEnv:=true;                 { Same as calling DelEnv() }
  371.       exit
  372.    end;
  373.    currentSize:=EnvSize(envSeg);
  374.  
  375.    for i:=1 to length(s) do      { Write string to environment }
  376.       Mem[envSeg:currentSize-1+i] :=ord(s[i]);
  377.    PutEnv:=true
  378. end; {PutEnv}
  379.  
  380.  
  381. function GetProgramName: fileSpecType;
  382. { Returns fully-qualified filespec of the currently-executing program.
  383.   This function should be called before any PutEnv() operations.
  384.   Req. DOS v3.0+ }
  385.  
  386. var envSeg: word;
  387.     p: ^char;
  388.     i: integer;
  389.     s: string;
  390. begin
  391.    envSeg:=MemW[PrefixSeg:$002C];    { PSP:002C == environment segment }
  392.    p:=Ptr(envSeg,EnvSize(envSeg)+3); { Points to 1st char of filename }
  393.    i:=0;                             
  394.    while (p^ <> chr(0)) and (i<MAXPATHLEN) do   { Read filename chars }
  395.    begin
  396.       Inc(i);
  397.       s[i]:=p^;
  398.       p:=Ptr(seg(p^),ofs(p^)+1)
  399.    end;
  400.    s[0]:=chr(i);
  401.    GetProgramName:=s
  402. end; {GetProgramName}
  403.  
  404.  
  405. PROCEDURE AllocateBlock(var blockSize: longint;
  406.                        var segment: word);
  407. { Allocates 'blockSize' bytes (rounded up to nearest paragraph) of
  408.   memory. If there is insufficient free memory available, ALL free
  409.   memory will be appropriated. The returned value 'segment' will be the
  410.   initial segment of the allocated block. }
  411.  
  412. var regs: Registers;
  413.     para: longint;
  414.  
  415. begin
  416.    para := blockSize div 16;     { Requested paragraphs of memory }
  417.    if (blockSize mod 16) > 0 then para:=para+1;
  418.    with regs do
  419.    begin
  420.       AH := $48;    { Int 21/48 - Allocate Memory  }
  421.       BX := para;   { Returns NC if ok, AX=segment; otherwise CY }
  422.       MsDos(regs);  { If CY, AX=7 MCB destroyed, 8=insuff memory }
  423.       para:=BX;     { BX=largest available block }
  424.       blockSize:=para*16;   { Return adjusted block size in bytes }
  425.       if Flags and FCarry <> 0 then  { Allocation error }
  426.          AllocateBlock(blockSize,segment)
  427.       else
  428.       begin
  429.          segment:=AX    { Segment of allocated memory block }
  430.       end;
  431.    end;
  432. end; {AllocateBlock}
  433.  
  434.  
  435. FUNCTION DeallocateBlock(segment: word): boolean;
  436. { Releases a block of memory reserved by Int 21/48 to the DOS pool.
  437.   Returns true if no error. }
  438.  
  439. var regs: Registers;
  440.  
  441. begin
  442.    with regs do
  443.    begin
  444.       AH := $49;      { Int 21/49 - Release Memory  }
  445.       ES := segment;  { Returns NC if ok, otherwise CY set and   }
  446.       MsDos(regs);    { AX=7 MCB destroyed, 9=invalid MCB address }
  447.    end;
  448.    DeallocateBlock:=not (regs.Flags and FCarry <> 0);
  449. end; {DeallocateBlock}
  450.  
  451.  
  452. FUNCTION Shell(prompt: evarType): integer;
  453. { Invokes an OS command shell with custom prompt string.  In order to
  454.   make enough room for a custom prompt evar, a new environment block for
  455.   this process is created, assigned to the current PSP, and is then
  456.   inherited by the child COMSPEC process.  If the prompt is null, the
  457.   default prompt "[progname] $p$g" will be used.
  458.  
  459.   Returns the DOS error code from the Exec function:
  460.  
  461.               0 = No error
  462.               2 = File not found
  463.               3 = Path not found
  464.               5 = Access denied
  465.               6 = Invalid handle
  466.               8 = Not enough memory
  467.              10 = Invalid environment
  468.              11 = Invalid format
  469.              18 = No more files
  470. }
  471. var ShellEnvSeg        : word;
  472.     len                : envSizeType;
  473.     bytesRequested     : longint;
  474.     rcode              : integer;
  475.  
  476. begin
  477.    if prompt='' then
  478.       prompt:='['+FNStrip(PROGRAMNAME,2)+'] ' +
  479.          GetEnv('PROMPT',CURRENT_ENVSEG);
  480.    ShellEnvSeg:=0;
  481.    if COMSPEC<>'' then
  482.    begin
  483.       len := EnvSize(CURRENT_ENVSEG)+1;
  484.       bytesRequested := len + Length(prompt)+8;
  485.       AllocateBlock(bytesRequested,ShellEnvSeg);
  486.       Move(Mem[CURRENT_ENVSEG:0], Mem[ShellEnvSeg:0], len);
  487.       MemW[PrefixSeg:$002c] := ShellEnvSeg;
  488.       if not PutEnv('PROMPT',prompt,ShellEnvSeg) then
  489.          writeln(#10#13#7'*** Insufficient environment space ',
  490.             'for custom prompt!');
  491.  
  492.       writeln;
  493.                {  Yes, this is ugly.  Sorry. :-) }
  494. writeln(
  495. '╔══╡ DOS Shell ╞═════════════════════════════════════════════════════╗');
  496. writeln(
  497. '║                                                                    ║');
  498. writeln(
  499. '║    You are in a temporary DOS Shell.  Do not load any resident     ║');
  500. writeln(
  501. '║   programs (such as PRINT or DOSKEY) while you are in this shell.  ║');
  502. writeln(
  503. '║                                                                    ║');
  504. writeln(
  505. '║       When done, type EXIT┘ to return to your application.        ║');
  506. writeln(
  507. '║                                                                    ║');
  508. writeln(
  509. '╚════════════════════════════════════════════════════════════════════╝');
  510.  
  511.       Exec(COMSPEC,''); rcode:=DosError;       { Needs 64k to load }
  512.       MemW[PrefixSeg:$002C]:=CURRENT_ENVSEG;   { Restore original env }
  513.  
  514.       if not DeAllocateBlock(ShellEnvSeg) then
  515.       begin
  516.          writeln(#7'*** Memory deallocation problem. Aborting....');
  517.          halt(7)
  518.       end;
  519.    end {if comspec}
  520.    else rcode:=-1;
  521.    Shell:=rcode
  522. end; {Shell}
  523. { ───────────────────────────────────────────────────────────────────── }
  524. {
  525.    Initialize public variables:
  526.  
  527.       MASTER_MCB        Root memory control block record
  528.       MASTER_ENVSEG     Segment of master environment block
  529.       CURRENT_ENVSEG    Segment of current process' environment block
  530.       COMSPEC           String set to value of "COMSPEC" evar.
  531.       PROGRAMNAME       Fully-qualified name of executing program.
  532. }
  533. BEGIN
  534.    FindRootEnv(MASTER_MCB);
  535.    MASTER_ENVSEG := MemW[MASTER_MCB.OwnerPSP:$002c];
  536.    CURRENT_ENVSEG := MemW[PrefixSeg:$002C];
  537.    COMSPEC:=GetEnv('COMSPEC',MASTER_ENVSEG);
  538.    PROGRAMNAME := GetProgramName
  539. END. {unit}
  540.  
  541. { -------------------------  DEMO ---------------------- }
  542.  
  543. (***********************************************************************
  544.    Walk Memory Control Block chain                Version 1.00
  545.  
  546.    Demonstration of Environ.TPU (and other stuff too, I guess).
  547.    Written Jan 17 1996 Robert B. Clark <rclark@iquest.net>
  548.  
  549.    Donated to the public domain; inclusion in SWAG freely permitted.
  550.  
  551.    Usage: WALKMCB [evar] [new_value]
  552.    =================================
  553.    If 'evar' is not specified, WALKMCB simply demonstrates how to walk
  554.    the MCB chain.
  555.  
  556.    If 'evar' _is_ specified, WALKMCB displays the master environment
  557.    value of 'evar' and sets the current value of 'evar' to 'new_value.'
  558.    It then demonstrates the shell to DOS function Shell() so that you
  559.    may verify the changed environment variable by typing SET at the
  560.    shelled command line.
  561.  
  562.    Note that the 'evar' argument IS case-sensitive, to accomodate the
  563.    infamous "windir" evar Microsoft foisted upon us.
  564.    ********************************************************************)
  565.  
  566. Program WalkMCB;
  567.  
  568. {$M 8096,0,1024}          { Stack, min heap, max heap}
  569. {$DEFINE DispMCB}         { Display MCBs while walking }
  570.  
  571. Uses Dos, Environ         { FOUND IN DOS.SWG ! }
  572. {$IFDEF UseLib}   ,Convert,Global   { Hex conversions, various }
  573. {$ELSE}           ,Crt
  574. {$ENDIF}          ;
  575.  
  576. CONST  CREDIT      = ' v1.00 Written Jan 17 1996 Robert B. Clark';
  577. (**********************************************************************)
  578. {$IFNDEF UseLib}     { Selected functions from personal units }
  579.  
  580. (* KeyBd.TPU *)
  581.  
  582. PROCEDURE ClearKeybd;
  583. inline($FA/             { cli               ; Disable interrupts     }
  584.        $33/$C0/         { xor ax,ax         ; Head/tail keybuf ptrs  }
  585.        $8E/$C0/         { mov es,ax         ; at 40:001A and 40:001C }
  586.        $26/$A0/$1A/$04/ { es mov al,b[041a] ; Head ptr in AL         }
  587.        $26/$A2/$1C/$04/ { es mov b[041c],al ; Now tail=head          }
  588.        $FB);            { sti               ; Reenable interrupts    }
  589. {ClearKeybd}
  590.  
  591. (* Convert.TPU *)
  592.  
  593. FUNCTION HexByte(b:byte):string;
  594. { Converts decimal to hexadecimal byte string }
  595. const hexDigits: array [0..15] of char = '0123456789ABCDEF';
  596. begin
  597.   HexByte:=hexDigits[b shr 4] + hexDigits[b and $F]
  598. end; {HexByte}
  599.  
  600.  
  601. FUNCTION HexWord(w:word): string;
  602. { Converts decimal to hexadecimal word string }
  603. begin
  604.   HexWord:=HexByte(hi(w)) + HexByte(lo(w))
  605. end; {HexWord}
  606.  
  607.  
  608. FUNCTION HexDWord(w:longint): string;
  609. { Converts decimal to hexadecimal doubleword string. }
  610. begin
  611.   if (w<0) then w:=w-$10000;
  612.   HexDWord:=HexWord(w div 65536)  + HexWord(w mod 65536)
  613. end; {HexDWord}
  614.  
  615. (* Global.TPU *)
  616.  
  617. PROCEDURE SetRedirect(var infile,outfile: string);
  618. { Sets Input/Output to DOS STDIN/OUT routines. }
  619. begin
  620.    Assign(Output,outFile);        { Set up for STDOUT output }
  621.    Rewrite(Output);
  622.    Assign(Input,inFile);          { Set up for STDIN input }
  623.    Reset(Input)
  624. end; {SetRedirect}
  625.  
  626.  
  627. FUNCTION CurSize:word;
  628. { Returns current size of cursor. The high byte is the beginning scan
  629.   line; the low byte is the ending scan line. }
  630. var regs: Registers;
  631.  
  632. begin
  633.    with regs do           { Get current cursor size }
  634.    begin
  635.       AH:=$03;            { Want BIOS Int 10h/3, Read Cursor Pos/Size }
  636.       BH:=$00;            { Video page number }
  637.       Intr($10,regs);     { BH=page #, CX=beg/end scan line, DX=row/col}
  638.       CurSize:=CX
  639.    end;
  640. end; {CurSize}
  641.  
  642.  
  643. PROCEDURE Cursor_OnOff(on:boolean);
  644. { Toggles the cursor on and off. }
  645. var regs: Registers;
  646.     sbeg:byte;
  647.  
  648. begin
  649.   sbeg:=hi(CurSize);                 { Get starting scan row }
  650.   if (on) then sbeg:=sbeg and $df    { Toggle bit 5 }
  651.   else sbeg:=sbeg or $20;
  652.  
  653.   with regs do
  654.   begin
  655.     AH:=$01;                  { Want BIOS Int 10h/1 Set cursor size }
  656.     CH:=sbeg;                 { Beginning cursor scan line }
  657.     CL:=lo(CurSize);          { Ending cursor scan line }
  658.     Intr($10,regs)
  659.   end;
  660. end; {Cursor_OnOff}
  661.  
  662.  
  663. PROCEDURE Pause;
  664. { Simply waits for the user to press [Enter] while displaying a
  665.   spinning cursor. Invalid keypresses cause a tone to sound.
  666.   The keyboard buffer is cleared upon entry and exit. }
  667.  
  668.    procedure Tone(hz,duration:word);
  669.    { Produces tone at 'hz' frequency and of 'duration' ms }
  670.    begin
  671.       Sound(hz); Delay(duration); NoSound
  672.    end; {Tone}
  673.  
  674. const cursor: array[0..6] of char = '-\|/-\|';
  675. var   okChar: boolean;
  676.            c: char;
  677.        i,x,y: shortint;
  678.  
  679. begin
  680.    Cursor_OnOff(false);
  681.    write(#10#13'Press Enter'#17#217' to continue... ');
  682.    x:=WhereX; y:=WhereY;
  683.    ClearKeybd; okChar:=false;
  684.    repeat
  685.       inc(i); i:=i mod 7;
  686.       write(cursor[i]); gotoxy(x,y);
  687.       Delay(55);
  688.       if KeyPressed then
  689.       begin
  690.          c:=ReadKey; if c=#0 then c:=ReadKey;  { Toss extended byte }
  691.          if c=chr(13) then okChar:=true
  692.          else Tone(2000,100)
  693.       end;
  694.    until okChar;
  695.    gotoxy(1,y); ClrEol; gotoXY(1,y);
  696.    ClearKeybd; Cursor_OnOff(true);
  697. end; {Pause}
  698.  
  699. {$ENDIF}  (* End of unit functions from personal libs *)
  700.  
  701. (* ******************************************************************* *)
  702. procedure DisplayMCB(mcb: MCBType; block_num: integer);
  703. begin
  704.    with mcb do
  705.    begin
  706.       writeln('MCB Block #',block_num:3,': Address ',HexWord(MCB_Seg),
  707.          ':', HexWord(MCB_Ofs), '   Absolute: ',
  708.          HexDWord(MCB_Seg*16+MCB_Ofs));
  709.       write('   Block Type    : ',HexByte(blockID),'   (');
  710.       if (blockID<>$4D) and (blockID<>$5A) then
  711.          writeln('ERROR)')
  712.       else
  713.          writeln(chr(blockID),')');
  714.       write('   PSP of Owner  : ',HexWord(ownerPSP));
  715.       if ownerPSP=0 then      write(' (free)')
  716.       else if ownerPSP=8 then write(' (DOS) ')
  717.       else write('       ');
  718.       writeln(' Owner: ',ownerName);   { Garbage if DOS <4.0 }
  719.       writeln('   PSP PARENT_ID : ',HexWord(parentPSP));
  720.       writeln('   ENVSEG        : ',HexWord(MemW[ownerPSP:$002c]));
  721.       writeln('   Size of MCB   : ',HexWord(blockSize),' paragraphs (',
  722.          blockSize*16,' bytes).');
  723.       writeln
  724.    end;
  725. end; {DisplayMCB}
  726.  
  727.  
  728. procedure WalkChain(var mcb: MCBType);
  729. { Walks the MCB chain until block type is no longer 4D (M).}
  730. var last,root : boolean;
  731.     offset    : longint;
  732.     block     : integer;
  733. begin
  734.    InitMCBType(mcb);
  735.    block:=0;
  736.    repeat
  737.       ReadMCB(mcb,last,root);
  738.       Inc(block);
  739. {$IFDEF DispMCB}
  740.       DisplayMCB(mcb,block);
  741. {$ENDIF}
  742.       if not last then
  743.       begin
  744.          offset := mcb.MCB_Ofs+16+(mcb.BlockSize*16);
  745.          mcb.MCB_Ofs := offset mod $10000;
  746.          mcb.MCB_Seg := mcb.MCB_Seg + (offset div $10000)
  747.       end;
  748.    until last
  749. end; {WalkChain}
  750.  
  751.  
  752. procedure Header(walk:boolean);
  753. begin
  754.    writeln;
  755.    if walk then
  756.    begin
  757.       writeln('WALK MEMORY CONTROL BLOCK CHAIN');
  758.       writeln('===============================')
  759.    end
  760.    else begin
  761.       writeln('ENVIRONMENT MANIPULATION AND THE DOS SHELL');
  762.       writeln('===========================================')
  763.    end;
  764.  
  765.    writeln('Current PSP (PrefixSeg) is ',HexWord(PrefixSeg));
  766.    writeln('The parent PSP segment  is ',HexWord(MemW[prefixSeg:$0016]));
  767.    writeln('The environment segment is ',HexWord(CURRENT_ENVSEG));
  768.    writeln;
  769. end; {Header}
  770.  
  771.  
  772. procedure GetParms(var p1,p2: evarType);
  773. { Get command line parameters 1 and 2 }
  774. var i:integer;
  775. begin
  776.    p1:=''; p2:='';
  777.    p1:=ParamStr(1);
  778.    i:=2;
  779.    while ParamStr(i) <> '' do    { Param 2 is concatenated p2, p3, ... }
  780.    begin
  781.       p2:=p2 + ParamStr(i);
  782.       if ParamStr(i+1) <> '' then p2:=p2+' ';
  783.       Inc(i)
  784.    end;
  785. end;
  786. (**************************************************************************)
  787. var
  788.     mcb : MCBType;
  789.     walk: boolean;
  790.     x   : integer;
  791.     evar,value: evarType;
  792.     prompt: evarType;
  793.     infile,outfile: string;
  794.  
  795. begin {main}
  796.    infile:=''; outfile:='';
  797.    SetRedirect(infile,outfile);  { Use STDIN/OUT }
  798.    GetParms(evar,value);
  799.    prompt:='$e[1m['+FNStrip(PROGRAMNAME,2)+'] $e[0m$p$g';
  800.    walk:=evar='';
  801.    Header(walk);
  802.  
  803.    if walk then
  804.    begin
  805.       WalkChain(mcb);
  806.       writeln('The last MCB in the chain is at ',
  807.          HexWord(mcb.MCB_Seg),':', HexWord(mcb.MCB_Ofs),'.');
  808.    end
  809.    else begin
  810.       writeln('The master (root) Memory Control Block is at ',
  811.          HexWord(MASTER_MCB.MCB_Seg),':',
  812.          HexWord(MASTER_MCB.MCB_Ofs),'.');
  813.       writeln('The root environment is at ',HexWord(MASTER_ENVSEG),
  814.          ':0000 and its maximum size is ',MaxEnvSize(MASTER_ENVSEG),
  815.          ' bytes.');
  816.       writeln('The master environment size is ',
  817.          EnvSize(MASTER_ENVSEG),' bytes.');
  818.       writeln('Current environment (',HexWord(CURRENT_ENVSEG),
  819.          ') size is ',EnvSize(CURRENT_ENVSEG),' bytes.');
  820.  
  821.       writeln('Master  : ',evar,'="', GetEnv(evar,MASTER_ENVSEG),'"');
  822.       writeln('Current : ',evar,'="', GetEnv(evar,CURRENT_ENVSEG),'"');
  823.       if not PutEnv(evar,value,CURRENT_ENVSEG) then
  824.          writeln(#10#13#7'*** Insufficient environment space!');
  825.       writeln('After   : ',evar,'="', GetEnv(evar,CURRENT_ENVSEG),'"');
  826.  
  827.       Pause;
  828.       x:=Shell(''); {prompt);}   { Try both }
  829.       writeln; writeln('Shell() returned DOS code ',x)
  830.    end;
  831.    writeln(FNStrip(PROGRAMNAME,2),CREDIT)
  832. end.
  833.