Re: Changing the ENV
Posted in 1995
I'm sorry, but this code is dangerous if treated as a generic environment variable setting routine. It is a good example of the problems associated with using putenv(). You need to read the specification of putenv() very carefully. I originally wrote: (1) The extern declaration of unixlpdest means that some other file should allocate the space. Many (but not all) C compilers and linkers will be kind enough to allocate the space for you, but you shouldn't rely on it. Remove the extern; better, replace it with static. I then noticed that the I4GL code does define the space, so some of this complaint is irrelevant. As an aside, it means that the I4GL code can adjust the value of the environment variable by simply setting unixlpdest, and can remove LPDEST from the environment as described in (3) below. (2) I don't think the interface to chglpdest() is very good -- why mess around with the length as a parameter, especially since you don't check that there is enough space in unixlpdest for the resulting value. A better interface takes a single string value and pops it into the unixlpdest using: /* 4.13 or 6.01 or later */ popstring(unixlpdest, sizeof(unixlpdest)); /* 4.12 or 6.00 or earlier */ popquote(unixlpdest, sizeof(unixlpdest)); unixlpdest[stleng(unixlpdest, sizeof(unixlpdest))] = '\\0'; This removes the trailing blanks on the argument. Granted, the length argument does that too, but it is moderately clumsy for fixed arguments such as the ones illustrated below. Scott Ellard (Hi Scott) would say "I need to be able to look at the value on the stack and find out how big it is", and I'd sympathise with this -- it can't be done at the moment. Since the value of the printer is likely to be dug out of the database, maybe the 'fixed string' comment is not relevant, but the lack of checking on the space required is (potentially) serious. (3) This function can be used precisely once, to set one environment variable. To change a second environment variable, you have to use some other function. Why? Because putenv() uses the pointer you specify. So with this code, you would override LPDEST with the first call, and then casually remove LPDEST from the environment with the second call -- hardly what you had in mind. MAIN ... IF chglpdest("LPDEST=printer01") != 0 THEN ... END IF ... IF chglpdest("SOMEOTHERVAR=abelone") THEN ... END IF ... END MAIN We can fix this to some extent by making the unixlpdest variable local, by using malloc() to allocate enough space for the environment variable, and then copying the value from unixlpdest to the malloced space and finally calling putenv(). However, we now have a memory leak for every time that we reset a variable, we will lose the previously allocated memory for that variable. Obviously, most programs do not overwrite environment variables many times, but in the context of sending reports to multiple different printers at different times, this can be a problem. To fix this up, you have to keep track of which environment variables have been set by your code, and which space was allocated for them, so that you can release them correctly. Ugh! Fixing the code to work properly without memory leaks is very difficult. If the memory leaks are acceptable, then using malloc() etc ensures that you can write a generic environment setting routine which will work for multiple environment variables, each potentially set many times. Oh, and one final point to beware of -- some routines trap the environment variable they use when they are called for the first time and do not check whether the environment changes subsequently. This can cause some interesting problems too. Yours, Jonathan Leffler (johnl@informix.com) #include <disclaimer.h> >Date: 24 May 95 14:03:00 EST >From: "NAVMTO_043" <DTTS@nctamslant.navy.mil> >X-Informix-List-Id: <list.6376> > > > How do you alter the environment while running a 4gl program? > >Try this. > > ############################ > # 4GL filename: testprog.4gl > globals > define unixlpdest char(80) > end globals > > main > define flen integer, > fenv char(80), > freturn integer > > let fenv="LPDEST="clipped,"lp_daisy"clipped > # "lp_daisy" = new value for LPDEST. Good for life of this program. > let flen=length(fenv) + 1 > call chglpdest(fenv,flen) returning freturn > > run "lp ./testprog.4gl" # test by printing file to default printer > > end main > ############################ > > /* C function called by 4gl program to set env value > Filename: chglpdest.c > */ > #include <stdio.h> > extern char unixlpdest[80]; > > int chglpdest(nargs) > int nargs; > { > int vlen; > int retcode=1; > int p_rep; > > if(nargs == 2){ > popint(&vlen); > popquote(unixlpdest,vlen); > > if((p_rep=putenv(unixlpdest)) == 0) > retcode=0; > } > retint(retcode); > return(1); > } > > ############################ > > // script to compile 4gl and c program > c4gl testprog.4gl chglpdest.c -o testprog.4ge > > ############################