[comp.lang.c] DOS Environment Variables

AURPS@ASUACAD.BITNET (05/05/91)

Does anyone know how to permanently change a DOS environment variable from
within an executing program?  PUTENV() changes/creates a variable but it
is only in effect while the program is running.  As soon as I go back to
DOS, the environment is restored to its original variables.  I'm using
Turbo C.

Thanks for any help,
Rick Schatzman

mike@bria.UUCP (mike.stefanik) (05/07/91)

In an article, AURPS@ASUACAD.BITNET writes:
>Does anyone know how to permanently change a DOS environment variable from
>within an executing program?  PUTENV() changes/creates a variable but it
>is only in effect while the program is running.  As soon as I go back to
>DOS, the environment is restored to its original variables.  I'm using
>Turbo C.

The problem that you are running into is that a child process (and I use
the term "process" loosely insofar as DOS is concerned) cannot change
the environment of it's parent.  Of course, you can allways go hunting
through memory, but that wouldn't be portable, would it? :-)

Here is the situation that you have visually:

        +---------------+
        |  COMMAND.COM  |	(has the original copy of the environment)
        +---------------+
                |
        +---------------+
        |  PROGRAM.EXE  |	(has a copy of command.com's environment)
        +---------------+

So, when command.com created the child, it gave it a *copy* of the
environment table.  Changes that are made, are made to the copy of the
table, not the original.  When the child dies, so goes the table as well.

-- 
Michael Stefanik, MGI Inc, Los Angeles | Opinions stated are never realistic
Title of the week: Systems Engineer    | UUCP: ...!uunet!bria!mike
-------------------------------------------------------------------------------
If MS-DOS didn't exist, who would UNIX programmers have to make fun of?

shaunc@gold.gvg.tek.com (Shaun Case) (05/07/91)

In article <91125.051531AURPS@ASUACAD.BITNET> AURPS@ASUACAD.BITNET writes:
>Does anyone know how to permanently change a DOS environment variable from
>within an executing program?  PUTENV() changes/creates a variable but it
>is only in effect while the program is running.  As soon as I go back to
>DOS, the environment is restored to its original variables.  I'm using
>Turbo C.

I've tried a few things, and the following is what has worked for me.  It
also happens to be portable betweem different MSDOS C compilers and versions
of MSDOS.

Additionally, this is really an MSDOS-specific question, so I have directed
followups to comp.os.msdos.programmer.

To set a master environment variable, try using the following batchfile
with your program:


File GO.BAT:
------------- cut here --------------
echo off
REM use '@echo off' if you have dos 3.3 or higher
set

REM your program name here:
test

command /c setvar
REM use 'call setvar' if you have dos 3.3 or higher
set
------------- cut here --------------



here is test.c:
------------- cut here --------------
/*********************
   set_env_var.c
   Shaun Case, 1991
   Public Domain
 *********************/

#include <stdio.h>

int main()
{
    FILE *batfile;
    char varname[134];
    char value[134];

    printf("\nEnter variable name: ");
    gets(varname);
    printf("Enter value: ");
    gets(value);
    puts("");

    if ((batfile = fopen("SETVAR.BAT", "w")) == NULL)
    {
        puts("Unable to open SETVAR.BAT, Omot.  Bailing.\n\n");
        return 1;
    }

    fprintf(batfile, "SET %s=%s\n", varname, value);

    fclose(batfile);

    return 0;
}
------------- cut here --------------

I got the following output:

COMSPEC=C:\4DOS.COM
CMDLINE=go
PATH=f:\tmp;c:\sys\util;c:\sys\bat;d:\borlandc\bin;d:\tc;C:\DOS;C:\WIN386;C:\;C:\TCP;c:\vga;c:\f-prot
TEMP=f:\tmp
TMP=f:\tmp
FTPINIT=c:\tcp\init.tbl
FTP_ATTR=0x71,0x74,0x21
FTP_CONFIG=c:\tcp\ftp.cfg
USER=@Man
PROMPT=$p$g

Enter variable name: hodag
Enter value: badger_nemesis

COMSPEC=C:\4DOS.COM
CMDLINE=setvar
PATH=f:\tmp;c:\sys\util;c:\sys\bat;d:\borlandc\bin;d:\tc;C:\DOS;C:\WIN386;C:\;C:\TCP;c:\vga;c:\f-prot
TEMP=f:\tmp
TMP=f:\tmp
FTPINIT=c:\tcp\init.tbl
FTP_ATTR=0x71,0x74,0x21
FTP_CONFIG=c:\tcp\ftp.cfg
USER=@Man
PROMPT=$p$g
HODAG=badger_nemesis

-- 
shaunc@gold.gvg.tek.com  atman%ecst.csuchico.edu@RELAY.CS.NET 
Postmaster of 1:119/666  1@9651 (WWIVnet)

It's enough to destroy a young moose's faith! -- Bullwinkle

robc@cup.portal.com (Rob X Cowan) (05/07/91)

> Does anyone know how to permanently change a DOS environment variable from
> within an executing program?  PUTENV() changes/creates a variable but it
> is only in effect while the program is running.  As soon as I go back to
> DOS, the environment is restored to its original variables.  I'm using
> Turbo C.
> 
> Thanks for any help,
> Rick Schatzman

     The following code demonstrates how to access the global environment
string.  It's not the only way, nor the best, but it'll get you started.
masterenvstring is a pointer to the start of the environment list, in which
each variable is an ASCIIZ string of the format <NAME>=<VALUE>.  The list
is terminated by a double null.  masterenvsize is the size of the Memory
Control block that is allocated for the environment string; I didn't put
any code to deal with its manipulation, but you'll have to deal with it if
you plan on adding entries or you risk a system halt by COMMAND.COM.. fun.

/*
 *  Demonstrates traversal of master environment string
 *  	Rob S. Cowan  9105.07
 */

#include <dos.h>

char *env_seekend(char far *);

void main(void)
{
	union REGS		in, out;
   struct SREGS	segs;
   char far		  *masterenvstr;
   int				masterenvsize;

   /*
    *  I don't remember if 2E is a supported function or not.. Nothing
    *   must be linked ahead of COMMAND.COM, or that address will be
    *   returned.
    */
   in.x.ax = 0x352E;					/*  Get PSP of COMMAND.COM (sortof)  */
	intdosx(&in, &out, &segs);

   /*  Get env asciiz string : seg stored at offset 2C of PSP  */
	masterenvstr = MK_FP(*(unsigned *)MK_FP(segs.es, 0x2C), 0);

   /*  Get size of env block from MCB preceding it  */
	masterenvsize = (*(unsigned *)MK_FP(FP_SEG(masterenvstr) - 1, 3)) << 4;

   /*
    *  You must take care to accommodate the memory requirements of
    *   the environment string.  If you wish to add entries you will
    *   have to allocate/deallocate it with the MCB that precedes the
    *   environment.
    */
}

/*
 *  Environment strings are a list of consecutive ASCIIZ strings
 *   terminated by a null entry.  This function will traverse to the
 *   end of this list and return the address of the null entry.
 */
char *env_seekend(char far *envtrav)
{
	for(;;)
   {
   	if (!*envtrav++)
      	if (!*envtrav)
         	return envtrav;
   }
}

Prost,
-Rob
robc@cup.portal.com

robc@cup.portal.com (Rob X Cowan) (05/08/91)

> Does anyone know how to permanently change a DOS environment variable from
> within an executing program?  PUTENV() changes/creates a variable but it
> is only in effect while the program is running.  As soon as I go back to
> DOS, the environment is restored to its original variables.  I'm using
> Turbo C.
> 
> Thanks for any help,
> Rick Schatzman

     I forgot to mention that int 2Eh (command.com interface) also has a
way to actually access its own SET command.  Can't remember how right now,
but it should be easier than direct access, if your not up to that.

-Rob
robc@cup.portal.com

eyer@azu.informatik.uni-stuttgart.de (Eyer) (05/08/91)

In article <42119@cup.portal.com> robc@cup.portal.com (Rob X Cowan) writes:
>
>> Does anyone know how to permanently change a DOS environment variable from
>> within an executing program?  PUTENV() changes/creates a variable but it
>> is only in effect while the program is running.  As soon as I go back to
>> DOS, the environment is restored to its original variables.  I'm using
>> Turbo C.
>> 
>> Thanks for any help,
>> Rick Schatzman
>
>     I forgot to mention that int 2Eh (command.com interface) also has a
>way to actually access its own SET command.  Can't remember how right now,
>but it should be easier than direct access, if your not up to that.
>
>-Rob

The use of int 2Eh is following (I took it from Dave William's TechRef) :

entry   DS:DI   pointer to an ASCIIZ command line in the form :
                      count byte
                      ASCII string
                      carriage return
                      null byte

It is reported that this int will destroy all registers, including SP.

I wouldn't use this method, because it might *NOT* be suported by further
releases of DOS, and it is actually *NOT* supported by 4DOS. The other
thing is that the transient part of COMMAND.COM will be reloaded if necessary,
what is quite memory-wasting.

I would use the direct memory access : in your PSP (Programm Segment Prefix),
you will find the PSP of the father-process. You can so go up to the
command interpreter, even if it is not called COMMAND.COM (for instance 4DOS),
because it is his own father. And in the PSP, there is the pointer to the 
environment. So you can change the master environment.

Since I never tried this, I take no responsibility of what I wrote.

Manu  (Emmanuel Eyer)

elkassas@ebg.eb.ele.tue.nl (sherif el kassas) (05/08/91)

In article <42119@cup.portal.com>, robc@cup.portal.com (Rob X Cowan) writes:
=> [stuff deleted]
=>      I forgot to mention that int 2Eh (command.com interface) also has a
=> way to actually access its own SET command.  Can't remember how right now,
=> but it should be easier than direct access, if your not up to that.
=> 
=> -Rob
=> robc@cup.portal.com

Well, that's how i'd do it ! any better ideas ?
Sherif

#include <standard_disclaimer>
----8<--------------------------------------
/*
 * file: test2e.c
 * demo for changing environment vars ...etc
 * compile with:
 *   TCC  test2e.c
 * note that u'll need tasm or masm !
 */

#include <dos.h>
#include <string.h>

char *command = " set t=this is a test \r";

void int2e(char far *command_line)
{
  unsigned seg, ofs;

  seg = FP_SEG(command_line);
  ofs = FP_OFF(command_line);

  asm      jmp start
  asm      my_ss dw 00
  asm      my_sp dw 00
  start:
  asm      push bp
  asm      push ds
  asm      mov  ax, ss
  asm      mov  cs:my_ss, ax
  asm      mov  cs:my_sp, sp
  asm      mov  si, ofs
  asm      mov  ax, seg
  asm      mov  ds, ax
  asm      int  2Eh
  asm      cli
  asm      mov  sp, cs:my_sp
  asm      mov  ax, cs:my_ss
  asm      mov  ss, ax
  asm      sti
  asm      pop  ds
  asm      pop  bp
}

main()
{
  command[0] = strlen(command)-1;
  int2e(command);
}

defaria@hpcupt3.cup.hp.com (Andy DeFaria) (05/10/91)

>/ hpcupt3:comp.lang.c / AURPS@ASUACAD.BITNET /  5:15 am  May  5, 1991 /
>Does anyone know how to permanently change a DOS environment variable from
>within an executing program?  PUTENV() changes/creates a variable but it
>is only in effect while the program is running.  As soon as I go back to
>DOS, the environment is restored to its original variables.  I'm using
>Turbo C.

My understanding is that you have to change the "master environment" by
walking back the PSP (or something like that).  I had gotten some Turbo Pascal
code to do this but I recall some big problems with this whole approach and I
abandoned it.  It had something to do with the face that if you try to change
an existing environment variables value then the new value can't be larger
that the old value and if it is a new environment variable then you have to be
careful about running out of environment space (and there is only a couple of
bytes of extra environment space).

Like I said I could dig up the information if you like.