summaryrefslogtreecommitdiffstats
path: root/private/crt32/misc/putenv.c
blob: 0e74fcbb58dbdfb3e01ebe337c718c5258f605ba (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
/***
*putenv.c - put an environment variable into the environment
*
*	Copyright (c) 1985-1992, Microsoft Corporation. All rights reserved.
*
*Purpose:
*	defines _putenv() - adds a new variable to environment; does not
*	change global environment, only the process' environment.
*
*Revision History:
*	08-08-84  RN	initial version
*	02-23-88  SKS	check for environment containing only the NULL string
*	05-31-88  PHG	Merged DLL and normal versions
*	07-14-88  JCR	Much simplified since (1) __setenvp always uses heap, and
*			(2) envp array and env strings are in seperate heap blocks
*	07-03-89  PHG	Now "option=" string removes string from environment
*	08-17-89  GJF	Removed _NEAR_, _LOAD_DS and fixed indents.
*	09-14-89  KRS	Don't give error if 'option' not defined in "option=".
*	11-20-89  GJF	Added const to arg type. Also, fixed copyright.
*	03-15-90  GJF	Made the calling type _CALLTYPE1, added #include
*			<cruntime.h> and removed #include <register.h>.
*	04-05-90  GJF	Made findenv() _CALLTYPE4.
*	04-26-90  JCR	Bug fix if environ is NULL (stubbed out _setenvp)
*	07-25-90  SBM	Removed redundant include (stdio.h)
*	10-04-90  GJF	New-style function declarators.
*	01-21-91  GJF	ANSI naming.
*       02-06-91  SRW   Added _WIN32_ conditional for SetEnvironmentVariable
*       02-18-91  SRW   Changed _WIN32_ conditional for SetEnvironmentVariable
*			to be in addition to old logic instead of replacement
*	04-23-92  GJF	Made findenv insensitive to the case of name for Win32.
*			Also added support for 'current drive' environment
*			strings in Win32.
*	04-29-92  GJF	Repackaged so that _putenv_lk could be easily added for
*			for Win32.
*	05-05-92  DJM	POSIX not supported.
*
*******************************************************************************/

#ifndef _POSIX_

#include <cruntime.h>
#include <errno.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <internal.h>
#include <os2dll.h>
#include <memory.h>
#include <oscalls.h>

static int _CALLTYPE4 findenv(const char *name, int len);


#ifdef	_CRUISER_

/***
*int _putenv(option) - add/replace/remove variable in environment
*
*Purpose:
*	option should be of the form "option=value".  If a string with the
*	given option part already exists, it is replaced with the given
*	string; otherwise the given string is added to the environment.
*	If the string is of the form "option=", then the string is
*	removed from the environment, if it exists.  If the string has
*	no equals sign, error is returned.
*
*Entry:
*	char *option - option string to set in the environment list.
*		should be of the form "option=value".
*
*Exit:
*	returns 0 if OK, -1 if fails.
*
*Exceptions:
*
*Warning:
*	This code will not work if variables are removed from the
*	environment by deleting them from environ[].  Use _putenv("option=")
*	to remove a variable.
*
*******************************************************************************/

int _CALLTYPE1 _putenv (
	REG3 const char *option
	)
{
	REG1 char **env;
	REG4 const char *equal;
	REG2 int ix;
	int remove;		/* 1 means remove string from environment */

	if (!option)
		return(-1);

	_mlock( _ENV_LOCK );

	/* find the equal sign to delimit the name being searched for.
	 * If no equal sign, then return error
	 */

	for (equal = option; *equal != '='; equal++)
		if (*equal == '\0')
			goto unlock_error;

	/* see if removing or adding */
	remove = (equal[1] == '\0');

	/* see if _environ array exists */
	if (_environ == NULL) {
		if (remove)
			goto unlock_good;
		else {
			/* get an array and init it to NULL */
			if ( (_environ = malloc(sizeof(void *))) == NULL)
				goto unlock_error;
			*_environ = NULL;
		}
	}

	/* init env pointer */

	env = _environ;

	/* See if the string is already in the environment */

	ix = findenv(option, equal - option);

	if ((ix >= 0) && (*env != NULL)) {
		/* String is already in the environment -- overwrite/remove it.
		 */
		if (remove) {
			/* removing -- move all the later strings up */
			for ( ; env[ix] != NULL; ++ix) {
				env[ix] = env[ix+1];
			}

			/* shrink the environment memory block
			   (ix now has number of strings, including NULL) --
			   this realloc probably can't fail, since we're
			   shrinking a mem block, but we're careful anyway. */
			if (env = (char **) realloc(env, ix * sizeof(char *)))
				_environ = env;
		}
		else {
			/* replace the option */
			env[ix] = (char *) option;
		}
	}
	else {
		/* String is NOT in the environment */

		if (!remove) {	/* can't remove something that's not there */

			/* Grow vector table by one */
			if (ix < 0)
				ix = -ix;	/* ix = length of environ table */

			if (!(env = (char **)realloc(env, sizeof(char *) * (ix + 2))))
				goto unlock_error;

			env[ix] = (char *)option;
			env[ix + 1] = NULL;
			_environ = env;
		}
	}

unlock_good:
	_munlock( _ENV_LOCK );
	return(0);

unlock_error:
	_munlock( _ENV_LOCK );
	return -1;
}


/***
*int findenv(name, len) - [STATIC]
*
*Purpose:
*	Scan for the given string within the environment
*
*Entry:
*
*Exit:
*	Returns the offset in "environ[]" of the given variable
*	Returns the negative of the length of environ[] if not found.
*	Returns 0 if the environment is empty.
*
*	[NOTE: That a 0 return can mean that the environment is empty
*	or that the string was found as the first entry in the array.]
*
*Exceptions:
*
*******************************************************************************/

static int _CALLTYPE4 findenv (
	const char *name,
	int len
	)
{
	REG4 char **env = _environ;
	REG2 const char *nm;
	REG1 char *envname;
	REG3 int l;


	while (envname = *env) {
		nm = name;
		l = len;
		while (l && *envname++ == *nm++)
			l--;

		if (l == 0 && ( *envname == '=' || !*envname ) )
			return(env - _environ);
		env++;
	}

	return(-(env - _environ));

}

#else	/* _CRUISER_ */

#ifdef	_WIN32_

/***
*int _putenv(option) - add/replace/remove variable in environment
*
*Purpose:
*	option should be of the form "option=value".  If a string with the
*	given option part already exists, it is replaced with the given
*	string; otherwise the given string is added to the environment.
*	If the string is of the form "option=", then the string is
*	removed from the environment, if it exists.  If the string has
*	no equals sign, error is returned.
*
*Entry:
*	char *option - option string to set in the environment list.
*		should be of the form "option=value".
*
*Exit:
*	returns 0 if OK, -1 if fails.
*
*Exceptions:
*
*Warning:
*	This code will not work if variables are removed from the
*	environment by deleting them from environ[].  Use _putenv("option=")
*	to remove a variable.
*
*******************************************************************************/

#ifdef	MTHREAD

int _CALLTYPE1 _putenv (
	const char *option
	)
{
	int retval;

	_mlock(_ENV_LOCK);

	retval = _putenv_lk(option);

	_munlock(_ENV_LOCK);

	return retval;
}

int _CALLTYPE1 _putenv_lk (
	const char *option
	)

#else	/* ndef MTHREAD */

int _CALLTYPE1 _putenv (
	const char *option
	)

#endif	/* MTHREAD */

{
	char **env;
	const char *equal;
	char *name, *value;
	int ix;
	int remove;		/* 1 means remove string from environment */

	/* check that the option string is valid and find the equal sign
	 */
	if ( (option == NULL) || ((equal = strchr(option, '=')) == NULL) )
		return(-1);

	/* check for the special case of '=' being the very first character
	 * of option. though the use of '=' in an environment variable name
	 * is documented as being illegal, the 'current directory' strings
	 * all look like this:
	 *
	 *	=<Drive Letter>:=<Drive Letter><fully qualified path>
	 *
	 * handle this by setting the equal pointer to point to the second
	 * '=' if it exists. Otherwise, handle as before.
	 */
	if ( option == equal )
		if ( (equal = strchr(option + 1, '=')) == NULL )
			equal = option;

	/* if the character following '=' is null, we are removing the
	 * the environment variable. Otherwise, we are adding or updating
	 * an environment variable.
	 */
	remove = (*(equal + 1) == '\0');

	/* see if _environ array exists */
	if (_environ == NULL) {
		if ( remove )
			return 0;
		else {
			/* get an array and init it to NULL */
			if ( (_environ = malloc(sizeof(void *))) == NULL)
				return -1;
			*_environ = NULL;
		}
	}

	/* init env pointer */

	env = _environ;

	/* See if the string is already in the environment */

	ix = findenv(option, equal - option);

	if ((ix >= 0) && (*env != NULL)) {
		/* String is already in the environment -- overwrite/remove it.
		 */
		if (remove) {
			/* removing -- move all the later strings up */
			for ( ; env[ix] != NULL; ++ix) {
				env[ix] = env[ix+1];
			}

			/* shrink the environment memory block
			   (ix now has number of strings, including NULL) --
			   this realloc probably can't fail, since we're
			   shrinking a mem block, but we're careful anyway. */
			if (env = (char **) realloc(env, ix * sizeof(char *)))
				_environ = env;
		}
		else {
			/* replace the option */
			env[ix] = (char *) option;
		}
	}
	else {
		/*
		 * String is NOT in the environment
		 */
		if ( !remove )	{

			/*
			 * Append the string to the environ table. Note that
			 * table must be grown to do this.
			 */
			if (ix < 0)
				ix = -ix;    /* ix = length of environ table */

			if ( (env = (char **)realloc(env, sizeof(char *) *
			    (ix + 2))) == NULL )
				return -1;

			env[ix] = (char *)option;
			env[ix + 1] = NULL;
			_environ = env;
		}
		else
			/*
			 * We are asked to remove an environment var that
			 * isn't there...just return success
			 */
			return 0;
	}

	/*
	 * Update the real environment. Don't give an error if this fails
	 * since the failure will not affect the user unless he/she is making
	 * direct API calls.
	 */
	if ( (name = malloc(strlen(option) + 2)) != NULL ) {
		strcpy(name, option);
		value = name + (equal - option);
		*value++ = '\0';
		SetEnvironmentVariable(name, remove ? NULL : value);
		free(name);
        }

	return 0;
}


/***
*int findenv(name, len) - [STATIC]
*
*Purpose:
*	Scan for the given string within the environment
*
*Entry:
*
*Exit:
*	Returns the offset in "environ[]" of the given variable
*	Returns the negative of the length of environ[] if not found.
*	Returns 0 if the environment is empty.
*
*	[NOTE: That a 0 return can mean that the environment is empty
*	or that the string was found as the first entry in the array.]
*
*Exceptions:
*
*******************************************************************************/

static int _CALLTYPE4 findenv (
	const char *name,
	int len
	)
{
	char **env;

	for ( env = _environ ; *env != NULL ; env++ ) {
		/*
		 * See if first len characters match, up to case
		 */
		if ( _strnicmp(name, *env, len) == 0 )
			/*
			 * the next character of the environment string must
			 * be an '=' or a '\0'
			 */
			if ( (*env)[len] == '=' || (*env)[len] == '\0' )
				return(env - _environ);
//
// We cannot break here since findenv must report the total number of strings.
//			else
//				break;
	}

	return(-(env - _environ));
}


#endif	/* _WIN32_ */
#endif	/* _CRUISER_ */

#endif	/* _POSIX_ */