🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: net.micro.pc
Article #8167

You can get argv[0] in DOS 3.x

#8167
From: Mike Morearty
Date: Mon, 06 Oct 1986 10:18
111 lines
5061 bytes
I just discovered an undocumented feature of the more recent
versions of DOS: it is at last possible to obtain argv[0].

I discovered this because I was surprised to read in the DOS 3.2
manual that XCOPY.EXE behaves differently if you rename it as
MCOPY.EXE.  So I disassembled it, and found the following method
to determine the name of the program that is being run.

As you probably know, in all versions of DOS from 2.0 on, there
is an environment area in memory with strings that can be created
with the SET command, such as "set PATH=\".  The segment of this
environment area is stored as a word at offset 2Ch into the
Program Segment Prefix.  If, for example, the word at address 2Ch
is 1000, then the environment area begins at offset 1000:0000.

This environment area consists of null-terminated strings of the
form "NAME=value" (with NAME all upper case), followed by a null
byte.  If the next byte is also a null, then that marks the end
of the environment area.  (Thus, if the very first byte in the
environment area is a null, there are no strings present.)

Now, the good stuff: in DOS 3.1 and 3.2, and possibly in 3.0 (I
haven't been able to test it), this environment area is followed
by the complete path and filename of the program that has been
invoked.  Here is the exact format (#x indicates a byte with
value x):

     NAME=value#0        ;one or more null-terminated strings
     #0                  ;the terminating null byte
     #1                  ;followed by a byte with value one
     #0                  ;followed by another null byte
     C:\PAS\ENV.COM#0    ;argv[0], null-terminated

Note the following changes from older versions of DOS:

1.   There are always TWO null bytes at the end of the
     environment area.  In older versions, there may have been
     only one null byte if there were no environment strings
     present.

2.   The environment area is immediately followed by a 1 and then
     a 0.  If this is not present, assume that argv[0] is not
     present either.

Note that the drive specifier and/or certain subdirectory names
in the path may be lower case, and there may be "." and/or ".."
entries in the path, depending on the line that the user typed to
invoke the program.  Some examples follow, assuming that the user
is in the directory C:\DIR, and there is a program in that
directory called PROGRAM.COM:

     User typed               argv[0] contains
     ----------               ----------------
     program                  C:\DIR\PROGRAM.COM
     PROGRAM                  C:\DIR\PROGRAM.COM
     .\program                C:\DIR\.\PROGRAM.COM
     \dir\program             C:\dir\PROGRAM.COM
     c:program                c:\DIR\PROGRAM.COM
     program.exe              C:\DIR\PROGRAM.COM

Here is a sample program written in Turbo Pascal (version 3.x),
to display argv[0].

=================================================================

{
  Argv0.pas - Display argv[0], in other words, the path and filename
              that invoked this program.  Works under DOS 3.1 and 3.2,
              and possibly 3.0.
}

program Argv0;

var
  env: integer absolute cseg:$2C;       (* segment of environment *)
  cp:  integer;                         (* pointer into environment segment *)
  r: record                             (* record of registers *)
       ax,bx,cx,dx,bp,si,di,ds,es,flags: integer;
     end;

begin
  r.ax := $3000;
  MsDos(r);                             (* get DOS version number *)
  if (lo(r.ax) <> 3) or (hi(r.ax) > $20) then begin
    writeln('Needs DOS 3.0, 3.1, or 3.2');
    halt;
  end;

  cp := 0;                              (* point to beginning of segment *)

  (* find the end of the environment space, e.g., where two nulls are
     found in a row. *)
  while (memw[env:cp] <> 0) do begin
    cp := cp + 1;
  end;
  cp := cp + 2;                         (* move past the two nulls *)

  (* If this version of DOS has argv[0], then the two nulls are
     immediately followed by an ASCII 1 and then another null. *)
  if (memw[env:cp] <> 1) then begin
    writeln('Invalid memory format');
  end else begin
    cp := cp + 2;                       (* move past the 1 and the 0 *)
    while mem[env:cp] <> 0 do begin     (* argv[0] is null-terminated *)
      write(chr(mem[env:cp]));
      cp := cp+1;
    end;
  end;
end.

                                   -- Mike Morearty
Technical Details
Message-ID: <468@zen.BERKELEY.EDU>
Path: arc1-ca.pugleaf.net!utzoo!mnetor!seismo!lll-crg!nike!ucbcad!zen!cory.Berkeley.EDU!morearty

Show Full Headers
Complete Article Headers
Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: arc1-ca.pugleaf.net!utzoo!mnetor!seismo!lll-crg!nike!ucbcad!zen!cory.Berkeley.EDU!morearty
From: morearty@cory.Berkeley.EDU (Mike Morearty)
Newsgroups: net.micro.pc
Subject: You can get argv[0] in DOS 3.x
Message-ID: <468@zen.BERKELEY.EDU>
Date: Mon, 6-Oct-86 06:18:52 EDT
Article-I.D.: zen.468
Posted: Mon Oct  6 06:18:52 1986
Date-Received: Tue, 7-Oct-86 21:00:29 EDT
Sender: news@zen.BERKELEY.EDU
Reply-To: morearty@cory.Berkeley.EDU (Mike Morearty)
Organization: University of California, Berkeley
Lines: 111