[comp.sys.ibm.pc] Changing environment PERMANENTLY from within a C program

lupin3@ucscb.UCSC.EDU (-=/ Count Lupin III /=-) (04/04/88)

  My older brother has a request; since he does not bother to read the net
(he feels my time is worthless enough :) he had me post this, and read all
the replies.  I have been watching for subjects which addressed this question,
and have seen none; so I have posted this article.  If you feel that this info
is current enough to do a followup, fine; otherwise, you can mail me your
answer, or mail it directly to him (acct. "webster", same path).
  Here is his question:
|     o /
+------X---cut-here-----------------------------------
|     O \
Can someone tell me a way to change an environment variable from
within a C program, and have the change be permanent when the program
terminates?  Using the putenv() call only changes the copy of the
environment local to the C program; I need a way to change the environment
of the COMMAND shell which exec'ed the C program.

A magazine I read suggested this as the solution:

system( "SET FOO=BAR");

but this did not work for me.  The environment remained unchanged when
the program terminated.
|     o /
+------X---cut-here-----------------------------------
|     O \

  aTdHvAaNnKcSe.

--
 /|\ /|\   .. .  .   .    .     .      .       .        .         .          . 
| |\| |\|  .. .  .   .    .     .      .       .        .         .          .
|/|\|/|\|/||   _  _ _   _ |_| _  _ |_ -__  _  _ARPA: lupin3@ucscb.ucsc.EDU      
  | |/| |/|L_ (_\( ( (_/  | |(_\_) (_ || )(_)_)UUCP: *!ucbvax!ucscc!ucscb!lupin3
   \|/ \|/ larry      /   hastings        _/   BITNET: lupin3@ucscb@ucscc.BITNET
MetaWare Inc     "I saved Esquire readers from exploding pies." --Michael J. Fox
Sail the High C! Disclaimer:[MetaWare, UCSC] -> opinion != lhastings -> opinion

ajmyrvold@violet.waterloo.edu (Alan Myrvold) (04/06/88)

In article <2621@saturn.ucsc.edu> lupin3@ucscb.UCSC.EDU 
           (-=/ Count Lupin III /=-) writes:

>Can someone tell me a way to change an environment variable from
>within a C program, and have the change be permanent when the program
>terminates?  Using the putenv() call only changes the copy of the
>environment local to the C program; I need a way to change the environment
>of the COMMAND shell which exec'ed the C program.

I often wondered that myself. Perhaps the correct attitude is :

"If Microsoft intended you to change the COMMAND.COM environment
 from within an application program, they would have made it easier
 to do"

Anyway, attached is a Turbo C version 1.0 program which changes the  
parent's environment area.

This program defines a variable called TODAY, with a date string
that will match those put out by the DOS dir command.

When the program terminates, the variable is still defined.

A batch file called TODAY.BAT could contain :
     dir *.* | find "%TODAY%"
and this would show the files modified today, provided the TODAY
environment variable has been set.

Happy hacking.
                           Alan Myrvold
                           ajmyrvold@violet.waterloo.edu


-------------------------------------------------------------------
Si je t'aime? Bien sur que je t'aime! Ne suis-je pas en train de
te le prouver encore une fois, dans ce lit? 
-------------------------------------------------------------------
Alan Myrvold     ajmyrvold@violet.waterloo.edu
-------------------------------------------------------------------

- setenv.c --------------------------------------------------------

/*
   Demonstrate changing parent's environment with Turbo C v. 1.0
   written 4-05-88 by Alan J. Myrvold
                      ajmyrvold@violet.waterloo.edu

   WARNING WARNING WARNING - virtually no error checking is done !!
                             use at own risk !!

   This program defines an environment variable called TODAY, which
   contains today's date in the same format DOS uses in directory
   listings. So a file called TODAY.BAT containing :
         dir *.* | find "%TODAY%"
   will show files in the current directory which were modified today,
   if the TODAY environment variable has been set.
*/

/* Technical information : A program's PSP contains a pointer at
   offset 44 (decimal) to a COPY of the parent's environment.
   The environment is a set of strings of the form NAME=value,
   each terminated by a NULL byte.
   An additional NULL byte marks the end of the environment.
   The environment area is ALWAYS paragraph aligned
   i.e. on a 16 byte boundary.

   Searching backwards from the PSP, I consistently find
   two copies of the envronment area.

   The program : finds the two areas
                 reads one into memory
                 udpates the TODAY variable
                 writes BOTH out to memory
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <process.h>
#include <conio.h>
#include <dos.h>

int env_size_bytes(unsigned env_seg)
/* Determine the length of the environment area in bytes */
{
    int n;

    n = 0;
    while (peekb(env_seg,n) != 0) {
          while (peekb(env_seg,n) != 0) n++;
          n++;
    }
    return(n);
}

int env_size_strings(unsigned env_seg)
/* Determine how many strings are in the environment area */
{
    int k,n;

    k = n = 0;
    while (peekb(env_seg,n) != 0) {
          k++;
          while (peekb(env_seg,n) != 0) n++;
          n++;
    }
    return(k);
}

int peek_cmp(unsigned seg1,unsigned seg2,int nbytes)
/* A trivial compare routine for segement aligned data items */
{
   int i;

   for (i = 0; (i < nbytes) && (peekb(seg1,i) == peekb(seg2,i)); i++);
   return(i == nbytes);
}

void find_env(unsigned seg_ray[2])
{
    unsigned psp_seg,copy_of_seg,env_seg;
    int k,n;

/* Find first copy of environment */
    psp_seg = getpsp();
    copy_of_seg = peek(psp_seg,44);

/* Set return value to non-garabage */
    seg_ray[0] = seg_ray[1] = copy_of_seg;

/* Search back to find 2 copies of environment */
    n = env_size_bytes(copy_of_seg);
    env_seg = copy_of_seg - 1;
    for (k = 0; (env_seg != 0) && (k < 2); k++) {
          while ((env_seg != 0) &&
                 (peek_cmp(copy_of_seg,env_seg,n) == 0)) {
                     env_seg--;
          }
          if (env_seg != 0) {
              seg_ray[k] = env_seg;
              env_seg--;
          }
    }

/* If not found, display error message and abort */
    if (k != 2) {
       printf("ERROR : Two copies of the environment were not found\n");
       exit(0);
    }
}

void read_env(unsigned env_seg,int *k,char ***s,char ***t)
/* Read environment into a malloc'd array of malloc'd strings */
{
    int i,j,n;

    *k = env_size_strings(env_seg);
    *s = (char **) malloc((*k)*sizeof(char *));
    *t = (char **) malloc((*k)*sizeof(char *));

    n = 0;
    for (i = 0; i < *k; i++) {
        for (j = 0; peekb(env_seg,n+j) != '='; j++);
        (*s)[i] = (char *) malloc(j+1);
        for (j = 0; peekb(env_seg,n+j) != '='; j++)
            ((*s)[i])[j] = peekb(env_seg,n+j);
        ((*s)[i])[j] = 0;
        n += j + 1;
        for (j = 0; peekb(env_seg,n+j) != 0; j++);
        (*t)[i] = (char *) malloc(j+1);
        for (j = 0; peekb(env_seg,n+j) != 0; j++)
            ((*t)[i])[j] = peekb(env_seg,n+j);
        ((*t)[i])[j] = 0;
        n += j + 1;
    }
}

void write_env(unsigned env_seg,int k,char **s,char **t)
/* Write the environment back out to memory */
{
    int i,j,n;

    n = 0;
    for (i = 0; i < k; i++) {
        for (j = 0; (s[i])[j] != 0; j++) {
            pokeb(env_seg,n,(s[i])[j]);
            n++;
        }
        pokeb(env_seg,n,'=');
        n++;
        for (j = 0; (t[i])[j] != 0; j++) {
            pokeb(env_seg,n,(t[i])[j]);
            n++;
        }
        pokeb(env_seg,n,0);
        n++;
    }
    pokeb(env_seg,n,0);
}

char *get_env_var(int k,char **s,char **t,char *var)
/* Return the value of the environment variable or NULL if not found */
{
    char *val;
    int i;

    val = NULL;
    for (i = 0; i < k; i++) if (stricmp(s[i],var) == 0) val = t[i];

    return(val);
}

void set_env_var(int *k,char ***s,char ***t,char *var,char *val)
/* Set a new or existing environment variable to a new value */
{
    int i,done;

    done = 0;
    for (i = 0; i < *k; i++)
        if (stricmp((*s)[i],var) == 0) {
           /* Existing variable */
           done = 1;
           free((*t)[i]);
           (*t)[i] = (char *) malloc(1+strlen(val));
           strcpy((*t)[i],val);
        }

    if (!done) {
       /* New environment varaible */
       (*k)++;
       *s = realloc(*s,(*k)*sizeof(char *));
       *t = realloc(*t,(*k)*sizeof(char *));
       (*s)[*k-1] = (char *) malloc(1+strlen(var));
       strcpy((*s)[*k-1],var);
       strupr((*s)[*k-1]);
       (*t)[*k-1] = (char *) malloc(1+strlen(val));
       strcpy((*t)[*k-1],val);
    }
}

void show_env(int k,char **s,char **t)
/* Display the array of environment strings */
{
   int i;
   for (i = 0; i < k; i++) printf("%s=%s\n",s[i],t[i]);
}

main()
{
    unsigned env_seg[2];
    char **s,**t,tmp[20];
    int k;
    long now;
    struct tm *local;

/* Find and read environment */
    find_env(env_seg);
    read_env(env_seg[1],&k,&s,&t);

/* Get today's date into a string */
    time(&now);
    local = localtime(&now);
    sprintf(tmp,"%d-%02d-%02d",1 + local -> tm_mon,local -> tm_mday,
            local -> tm_year);

/* Set the TODAY variable in the environment */
    set_env_var(&k,&s,&t,"TODAY",tmp);

/* Update BOTH copies of the environment */
    write_env(env_seg[0],k,s,t);
    write_env(env_seg[1],k,s,t);
}

ajmyrvold@violet.waterloo.edu (Alan Myrvold) (04/06/88)

OOPS ... bug in my earlier posting!

Re:  <6244@watdragon.waterloo.edu>
on changing the environment areas. Change :

>/* Update BOTH copies of the environment */
>    write_env(env_seg[0],k,s,t);
>    write_env(env_seg[1],k,s,t);

to :

>/* Update ONE copy of the environment */
>    write_env(env_seg[1],k,s,t);

Sorry about my initial confusion, but I still don't understand it all
completely.


-------------------------------------------------------------------
Si je t'aime? Bien sur que je t'aime! Ne suis-je pas en train de
te le prouver encore une fois, dans ce lit? 
-------------------------------------------------------------------
Alan Myrvold     ajmyrvold@violet.waterloo.edu
-------------------------------------------------------------------