asmodeus@tree.UUCP (Jonathan Ballard) (02/17/89)
Hi! For some reason putenv() is not setting the environment variables right with certain array declarations. I wrote a putenv() replacement which works fine. So I assume that either putenv() has a bug in it or that the format is different then normal in this version of UNIX. Which is it? I'm using System V/AT 2.4. Here are some examples of what went wrong. In this example, putenv will return ok but it never sets $USERNAME. > char envbuf[100]; > > sprintf(envbuf,"USERNAME=%s","hi"); > putenv(envbuf); But it works right if done like this... > putenv("USERNAME=hi"); The strange part is it works with numbers when doing a sprintf... > sprintf(envbuf,"USERNUM=%d",13); > putenv(envbuf); I made my own putenv() routine (called "PutEnv()") that does what putenv() is susposed to do and it works fine. If you need to use this then go ahead and modify it in any way. I'm not responsible for any damage done if you do use this. I have no way of throughly testing it against everything that putenv() does so this shouldn't be used as total replacement of putenv(), unless someone can say it does the full job. -----------------------------------8<---------------------------------------- PutEnv(str) char *str; { int i,len; extern char **environ; char **tmpenvp,*envp; /* this is used just in case malloc or realloc fails and the orginal pointer value could be saved */ len=strcspn(str,"="); /* check first to see if there is an environment, if not then make one */ if(environ==(char **)0) { if((environ=(char **)malloc(sizeof(char *)))==(char **)0) return(-1); environ[0]=(char *)0; } /* now check to see if the enviromental varible is already there if it is then replace the value with the new one */ for(i=0;environ[i]!=(char *)0;i++) if(strncmp(str,environ[i],len+1)==0) { envp=environ[i]; if((environ[i]=(char *)realloc(environ[i],strlen(str)+1))==(char *)0) { environ[i]=envp; return(-1); } strcpy(environ[i],str); return(0); } tmpenvp=environ; /* if there isn't a matching environmental varible then create it */ if((environ=(char **)realloc(environ,(i+2)*sizeof(char *)))==(char **)0) { environ=tmpenvp; return(-1); } if((environ[i]=(char *)malloc(strlen(str)+1))==(char *)0) return(-1); environ[i+1]=(char *)0; strcpy(environ[i],str); return(0); } -- ----Asmodeus - Jonathan Ballard ..!csusac!tree!asmodeus ..!pacbell!sactoh0!tree!asmodeus "I'm going to create the best game ever heard of! Might take a few years thou..." -me
mlandau@bbn.com (Matt Landau) (02/18/89)
In comp.unix.wizards, asmodeus@tree.UUCP (Jonathan Ballard) writes: >For some reason putenv() is not setting the environment variables right >In this example, putenv will return ok but it never sets $USERNAME. >> char envbuf[100]; >> >> sprintf(envbuf,"USERNAME=%s","hi"); >> putenv(envbuf); Is it possible your version of putenv doesn't actually copy the buffer passed in, but instead maintains a pointer to the original argument in the environment? If that's true, you cannot pass an automatic to putenv and expect anything reasonable to happen - you have to pass either global or allocated storage. The SunOS 3.5 man page on putenv, for example, says this: A potential error is to call putenv with an automatic vari- able as the argument, then exit the calling function while string is still part of the environment. Don't know if SysV/AT does the same thing or not, but it's worth a look. -- Matt Landau Waiting for a flash of enlightenment mlandau@bbn.com in all this blood and thunder
guy@auspex.UUCP (Guy Harris) (02/18/89)
>> char envbuf[100]; >> >> sprintf(envbuf,"USERNAME=%s","hi"); >> putenv(envbuf); To quote from the SunOS 4.0 PUTENV(3) man page (which probably describes most "putenv" implementations, since the code hasn't changed much as far as I know - I think the 4.0 one is just the S5R2 one with a Sun SCCS header stuck in...): DESCRIPTION 'string' points to a string of the form `name = value' putenv() makes the value of the environment variable name equal to value by altering an existing variable or creating a new one. In either case, the string pointed to by 'string' becomes part of the environment, so altering the string will change the environment. The space used by 'string' is no longer used once a new string-defining name is passed to putenv. WARNINGS ... A potential error is to call putenv() with an automatic variable as the argument, then exit the calling function while 'string' is still part of the environment. It sure looks as if you're handing "putenv" an automatic variable in your example; do you return from the function in which that automatic is declared before doing another "putenv" to modify "USERNAME"? If so, that's probably your problem. >But it works right if done like this... >> putenv("USERNAME=hi"); The argument is static, not automatic, so the string stays around after the function returns. >The strange part is it works with numbers when doing a sprintf... >> sprintf(envbuf,"USERNUM=%d",13); >> putenv(envbuf); Sheer luck, possibly.
asmodeus@tree.UUCP (Jonathan Ballard) (02/21/89)
In article <12649@jade.BBN.COM>, mlandau@bbn.com (Matt Landau) writes: > In comp.unix.wizards, asmodeus@tree.UUCP (Jonathan Ballard) writes: [stuff deleted] > putenv and expect anything reasonable to happen - you have to pass > either global or allocated storage. > > The SunOS 3.5 man page on putenv, for example, says this: > > A potential error is to call putenv with an automatic vari- > able as the argument, then exit the calling function while > string is still part of the environment. Ours says the same thing. I've tried global varible and pointers also. They didn't work right either. The strange part is everybody says the a automatic varible won't work. Why is it then when I use this next example that it works fine. Notice it is defining the enviromental varible to a number. sprintf(envbuf,"UID=%d",12); putenv(envbuf); It worked fine and stayed in the enviroment. -- ----Asmodeus - Jonathan Ballard ..!csusac!tree!asmodeus ..!pacbell!sactoh0!tree!asmodeus "I'm going to create the best game ever heard of! Might take a few years thou..." -me
asmodeus@tree.UUCP (Jonathan Ballard) (02/21/89)
From what you people have been saying is that I can't pass in a automatic varible because putenv uses that space, right? This seems to be a dumb way to go. Why doesn't putenv just alloc a new spot for the enviroment varible to go? (Like mine does.) But if I do understand this right - I have to make several global or static varible just to handle space for the enviroment. This to me seems to make things clutter up. -- ----Asmodeus - Jonathan Ballard ..!csusac!tree!asmodeus ..!pacbell!sactoh0!tree!asmodeus "I'm going to create the best game ever heard of! Might take a few years thou..." -me
cjc@ulysses.homer.nj.att.com (Chris Calabrese[mav]) (02/22/89)
In article <233@tree.UUCP>, asmodeus@tree.UUCP (Jonathan Ballard) writes: > From what you people have been saying is that I can't pass in a automatic > varible because putenv uses that space, right? > This seems to be a dumb way to go. Why doesn't putenv just alloc a new > spot for the enviroment varible to go? (Like mine does.) > But if I do understand this right - I have to make several global or > static varible just to handle space for the enviroment. This to me > seems to make things clutter up. No, just malloc them before passing them to putenv, as you are doing now. It seems to me that putenv is a useful function which operates on a fairly low level on a very simple data structure (array of pointers). It also works. The user, however, may opt to develop an interface to putenv which will optimize for speed, memory usage, etc. If putenv always malloc'd space, it would start eating up some serious memory in something like the shell, where you might change the value of an environment variable several hundred times in a particularly large script (please don't ask for examples, you get the general idea :-). It could free the old space, however, there's no way of telling if it was created by malloc in the first place, to it would either have to maintain a list of what's freeable, or malloc new space for the whole environment at the start of the program. Perhaps the real answer is a better data structure for the environment, but I thing UNIX (all flavors) is pretty much stuck with the current one. -- Name: Christopher J. Calabrese Brain loaned to: AT&T Bell Laboratories, Murray Hill, NJ att!ulysses!cjc cjc@ulysses.att.com Obligatory Quote: ``Now, where DID I put that bagel?''
chris@mimsy.UUCP (Chris Torek) (02/22/89)
In article <233@tree.UUCP> asmodeus@tree.UUCP (Jonathan Ballard) writes: >From what you people have been saying is that I can't pass in a automatic >varible because putenv uses that space, right? No, not right; see below. >... Why doesn't putenv just alloc a new spot for the enviroment >varible to go? It expects *you* to do that, if it is necessary (often it is not). >But if I do understand this right - I have to make several global or >static varible just to handle space for the enviroment. Not so. A real treatment of the issue belongs in comp.lang.c. The important part, however, is the storage for the *characters* to which the thing you hand to putenv() will point must remain undisturbed. Thus, the following is wrong (but may work, if you are unlucky, depending on how much stack junk you might have:) main() { set(); ... refer to environment ... } set() { char env[30]; strcpy(env, "FOO=bar"); putenv(env); } But if main() does not return to its caller (if it ends with exit(0)), or if its caller (the C library startup code) does not look at the environment, the following is correct: main() { char env[30]; strcpy(env, "FOO=bar"); putenv(env); } In both examples, `env' provides space for 30 characters. The environment will retain a pointer to the first of those thirty characters (the `F' in FOO=bar). This space-for-30-chars is `alive' (guaranteed to remain undisturbed) for the duration of main() and any functions called by main()---in other words, for the entire program. If `env' is declared in `set()', the space is alive only for the duration of the call to set(). When set() returns to main(), the space becomes dead and is free to be reused. Whether it *is* reused is compiler- and system-dependent (and may even vary from one run to another). As another example, putenv("FOO=bar"); would be correct whether it were in set() or in main(). This is because a double-quoted string has static storage duration: that is, there is one copy of the characters making up that string, and it sits somewhere in text or data space, never moving and (unless you do something technically illegal) never changing. You can make set() work by changing it from set() { char env[30]; to set() { static char env[30]; Finally, at any time, you can always call p = strdup(pointer); if (p == NULL) ... error ... putenv(p); (you may need to write strdup() first if you have an old support library). strdup() calls malloc() to allocate space for the string at which `pointer' is assumed to point. The space allocated by malloc() will not be reused unless you allow it. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris