Fixed small ==, != mixup.
[wine] / memory / environ.c
1 /*
2  * Process environment management
3  *
4  * Copyright 1996, 1998 Alexandre Julliard
5  */
6
7 #include <stdlib.h>
8 #include <string.h>
9 #include "winuser.h"
10 #include "wine/winestring.h"
11 #include "process.h"
12 #include "heap.h"
13 #include "selectors.h"
14 #include "winerror.h"
15
16 /* Format of an environment block:
17  * ASCIIZ   string 1 (xx=yy format)
18  * ...
19  * ASCIIZ   string n
20  * BYTE     0
21  * WORD     1
22  * ASCIIZ   program name (e.g. C:\WINDOWS\SYSTEM\KRNL386.EXE)
23  *
24  * Notes:
25  * - contrary to Microsoft docs, the environment strings do not appear
26  *   to be sorted on Win95 (although they are on NT); so we don't bother
27  *   to sort them either.
28  */
29
30 static const char ENV_program_name[] = "C:\\WINDOWS\\SYSTEM\\KRNL386.EXE";
31
32 /* Maximum length of a Win16 environment string (including NULL) */
33 #define MAX_WIN16_LEN  128
34
35 /* Extra bytes to reserve at the end of an environment */
36 #define EXTRA_ENV_SIZE (sizeof(BYTE) + sizeof(WORD) + sizeof(ENV_program_name))
37
38 /* Fill the extra bytes with the program name and stuff */
39 #define FILL_EXTRA_ENV(p) \
40     *(p) = '\0'; \
41     PUT_WORD( (p) + 1, 1 ); \
42     strcpy( (p) + 3, ENV_program_name );
43
44
45 /***********************************************************************
46  *           ENV_FindVariable
47  *
48  * Find a variable in the environment and return a pointer to the value.
49  * Helper function for GetEnvironmentVariable and ExpandEnvironmentStrings.
50  */
51 static LPCSTR ENV_FindVariable( LPCSTR env, LPCSTR name, INT len )
52 {
53     while (*env)
54     {
55         if (!lstrncmpiA( name, env, len ) && (env[len] == '='))
56             return env + len + 1;
57         env += strlen(env) + 1;
58     }
59     return NULL;
60 }
61
62
63 /***********************************************************************
64  *           ENV_BuildEnvironment
65  *
66  * Build the environment for the initial process
67  */
68 BOOL ENV_BuildEnvironment( PDB *pdb )
69 {
70     extern char **environ;
71     LPSTR p, *e;
72     int size;
73
74     /* Compute the total size of the Unix environment */
75
76     size = EXTRA_ENV_SIZE;
77     for (e = environ; *e; e++) size += strlen(*e) + 1;
78
79     /* Now allocate the environment */
80
81     if (!(p = HeapAlloc( SystemHeap, 0, size ))) return FALSE;
82     pdb->env_db->environ = p;
83
84     /* And fill it with the Unix environment */
85
86     for (e = environ; *e; e++)
87     {
88         strcpy( p, *e );
89         p += strlen(p) + 1;
90     }
91
92     /* Now add the program name */
93
94     FILL_EXTRA_ENV( p );
95     return TRUE;
96 }
97
98
99 /***********************************************************************
100  *           ENV_InheritEnvironment
101  *
102  * Make a process inherit the environment from its parent or from an
103  * explicit environment.
104  */
105 BOOL ENV_InheritEnvironment( PDB *pdb, LPCSTR env )
106 {
107     DWORD size;
108     LPCSTR src;
109     LPSTR dst;
110
111     /* FIXME: should lock the parent environment */
112     if (!env) env = pdb->parent->env_db->environ;
113
114     /* Compute the environment size */
115
116     src = env;
117     size = EXTRA_ENV_SIZE;
118     while (*src)
119     {
120         int len = strlen(src) + 1;
121         src += len;
122         if ((len > MAX_WIN16_LEN) && (pdb->flags & PDB32_WIN16_PROC))
123             len = MAX_WIN16_LEN;
124         size += len;
125     }
126
127     /* Copy the environment */
128
129     if (!(pdb->env_db->environ = HeapAlloc( pdb->heap, 0,
130                                             size + EXTRA_ENV_SIZE )))
131         return FALSE;
132     pdb->env_db->env_sel = SELECTOR_AllocBlock( pdb->env_db->environ,
133                                                 0x10000, SEGMENT_DATA,
134                                                 FALSE, FALSE );
135     src = env;
136     dst = pdb->env_db->environ;
137     while (*src)
138     {
139         if (pdb->flags & PDB32_WIN16_PROC)
140             lstrcpynA( dst, src, MAX_WIN16_LEN );
141         else
142             strcpy( dst, src );
143         src += strlen(src) + 1;
144         dst += strlen(dst) + 1;
145     }
146     FILL_EXTRA_ENV( dst );
147     return TRUE;
148 }
149
150
151 /***********************************************************************
152  *           ENV_FreeEnvironment
153  *
154  * Free a process environment.
155  */
156 void ENV_FreeEnvironment( PDB *pdb )
157 {
158     if (!pdb->env_db) return;
159     if (pdb->env_db->env_sel) SELECTOR_FreeBlock( pdb->env_db->env_sel, 1 );
160     DeleteCriticalSection( &pdb->env_db->section );
161     HeapFree( pdb->heap, 0, pdb->env_db );
162 }
163
164
165 /***********************************************************************
166  *           GetCommandLine32A      (KERNEL32.289)
167  */
168 LPCSTR WINAPI GetCommandLineA(void)
169 {
170     return PROCESS_Current()->env_db->cmd_line;
171 }
172
173 /***********************************************************************
174  *           GetCommandLine32W      (KERNEL32.290)
175  */
176 LPCWSTR WINAPI GetCommandLineW(void)
177 {
178     PDB *pdb = PROCESS_Current();
179     EnterCriticalSection( &pdb->env_db->section );
180     if (!pdb->env_db->cmd_lineW)
181         pdb->env_db->cmd_lineW = HEAP_strdupAtoW( pdb->heap, 0,
182                                                   pdb->env_db->cmd_line );
183     LeaveCriticalSection( &pdb->env_db->section );
184     return pdb->env_db->cmd_lineW;
185 }
186
187
188 /***********************************************************************
189  *           GetEnvironmentStrings32A   (KERNEL32.319) (KERNEL32.320)
190  */
191 LPSTR WINAPI GetEnvironmentStringsA(void)
192 {
193     PDB *pdb = PROCESS_Current();
194     return pdb->env_db->environ;
195 }
196
197
198 /***********************************************************************
199  *           GetEnvironmentStrings32W   (KERNEL32.321)
200  */
201 LPWSTR WINAPI GetEnvironmentStringsW(void)
202 {
203     INT size;
204     LPWSTR ret;
205     PDB *pdb = PROCESS_Current();
206
207     EnterCriticalSection( &pdb->env_db->section );
208     size = HeapSize( pdb->heap, 0, pdb->env_db->environ );
209     if ((ret = HeapAlloc( pdb->heap, 0, size * sizeof(WCHAR) )) != NULL)
210     {
211         LPSTR pA = pdb->env_db->environ;
212         LPWSTR pW = ret;
213         while (size--) *pW++ = (WCHAR)(BYTE)*pA++;
214     }
215     LeaveCriticalSection( &pdb->env_db->section );
216     return ret;
217 }
218
219
220 /***********************************************************************
221  *           FreeEnvironmentStrings32A   (KERNEL32.268)
222  */
223 BOOL WINAPI FreeEnvironmentStringsA( LPSTR ptr )
224 {
225     PDB *pdb = PROCESS_Current();
226     if (ptr != pdb->env_db->environ)
227     {
228         SetLastError( ERROR_INVALID_PARAMETER );
229         return FALSE;
230     }
231     return TRUE;
232 }
233
234
235 /***********************************************************************
236  *           FreeEnvironmentStrings32W   (KERNEL32.269)
237  */
238 BOOL WINAPI FreeEnvironmentStringsW( LPWSTR ptr )
239 {
240     return HeapFree( GetProcessHeap(), 0, ptr );
241 }
242
243
244 /***********************************************************************
245  *           GetEnvironmentVariable32A   (KERNEL32.322)
246  */
247 DWORD WINAPI GetEnvironmentVariableA( LPCSTR name, LPSTR value, DWORD size )
248 {
249     LPCSTR p;
250     INT ret = 0;
251     PDB *pdb = PROCESS_Current();
252
253     if (!name || !*name)
254     {
255         SetLastError( ERROR_INVALID_PARAMETER );
256         return 0;
257     }
258     EnterCriticalSection( &pdb->env_db->section );
259     if ((p = ENV_FindVariable( pdb->env_db->environ, name, strlen(name) )))
260     {
261         ret = strlen(p);
262         if (size <= ret)
263         {
264             /* If not enough room, include the terminating null
265              * in the returned size and return an empty string */
266             ret++;
267             if (value) *value = '\0';
268         }
269         else if (value) strcpy( value, p );
270     }
271     LeaveCriticalSection( &pdb->env_db->section );
272     return ret;  /* FIXME: SetLastError */
273 }
274
275
276 /***********************************************************************
277  *           GetEnvironmentVariable32W   (KERNEL32.323)
278  */
279 DWORD WINAPI GetEnvironmentVariableW( LPCWSTR nameW, LPWSTR valW, DWORD size)
280 {
281     LPSTR name = HEAP_strdupWtoA( GetProcessHeap(), 0, nameW );
282     LPSTR val  = valW ? HeapAlloc( GetProcessHeap(), 0, size ) : NULL;
283     DWORD res  = GetEnvironmentVariableA( name, val, size );
284     HeapFree( GetProcessHeap(), 0, name );
285     if (val)
286     {
287         lstrcpynAtoW( valW, val, size );
288         HeapFree( GetProcessHeap(), 0, val );
289     }
290     return res;
291 }
292
293
294 /***********************************************************************
295  *           SetEnvironmentVariable32A   (KERNEL32.641)
296  */
297 BOOL WINAPI SetEnvironmentVariableA( LPCSTR name, LPCSTR value )
298 {
299     INT old_size, len, res;
300     LPSTR p, env, new_env;
301     BOOL ret = FALSE;
302     PDB *pdb = PROCESS_Current();
303
304     EnterCriticalSection( &pdb->env_db->section );
305     env = p = pdb->env_db->environ;
306
307     /* Find a place to insert the string */
308
309     res = -1;
310     len = strlen(name);
311     while (*p)
312     {
313         if (!lstrncmpiA( name, p, len ) && (p[len] == '=')) break;
314         p += strlen(p) + 1;
315     }
316     if (!value && !*p) goto done;  /* Value to remove doesn't exist */
317
318     /* Realloc the buffer */
319
320     len = value ? strlen(name) + strlen(value) + 2 : 0;
321     if (*p) len -= strlen(p) + 1;  /* The name already exists */
322     old_size = HeapSize( pdb->heap, 0, env );
323     if (len < 0)
324     {
325         LPSTR next = p + strlen(p) + 1;  /* We know there is a next one */
326         memmove( next + len, next, old_size - (next - env) );
327     }
328     if (!(new_env = HeapReAlloc( pdb->heap, 0, env, old_size + len )))
329         goto done;
330     if (pdb->env_db->env_sel)
331         SELECTOR_MoveBlock( pdb->env_db->env_sel, new_env );
332     p = new_env + (p - env);
333     if (len > 0) memmove( p + len, p, old_size - (p - new_env) );
334
335     /* Set the new string */
336
337     if (value)
338     {
339         strcpy( p, name );
340         strcat( p, "=" );
341         strcat( p, value );
342     }
343     pdb->env_db->environ = new_env;
344     ret = TRUE;
345
346 done:
347     LeaveCriticalSection( &pdb->env_db->section );
348     return ret;
349 }
350
351
352 /***********************************************************************
353  *           SetEnvironmentVariable32W   (KERNEL32.642)
354  */
355 BOOL WINAPI SetEnvironmentVariableW( LPCWSTR name, LPCWSTR value )
356 {
357     LPSTR nameA  = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
358     LPSTR valueA = HEAP_strdupWtoA( GetProcessHeap(), 0, value );
359     BOOL ret = SetEnvironmentVariableA( nameA, valueA );
360     HeapFree( GetProcessHeap(), 0, nameA );
361     HeapFree( GetProcessHeap(), 0, valueA );
362     return ret;
363 }
364
365
366 /***********************************************************************
367  *           ExpandEnvironmentStrings32A   (KERNEL32.216)
368  *
369  * Note: overlapping buffers are not supported; this is how it should be.
370  */
371 DWORD WINAPI ExpandEnvironmentStringsA( LPCSTR src, LPSTR dst, DWORD count )
372 {
373     DWORD len, total_size = 1;  /* 1 for terminating '\0' */
374     LPCSTR p, var;
375     PDB *pdb = PROCESS_Current();
376
377     if (!count) dst = NULL;
378     EnterCriticalSection( &pdb->env_db->section );
379
380     while (*src)
381     {
382         if (*src != '%')
383         {
384             if ((p = strchr( src, '%' ))) len = p - src;
385             else len = strlen(src);
386             var = src;
387             src += len;
388         }
389         else  /* we are at the start of a variable */
390         {
391             if ((p = strchr( src + 1, '%' )))
392             {
393                 len = p - src - 1;  /* Length of the variable name */
394                 if ((var = ENV_FindVariable( pdb->env_db->environ,
395                                              src + 1, len )))
396                 {
397                     src += len + 2;  /* Skip the variable name */
398                     len = strlen(var);
399                 }
400                 else
401                 {
402                     var = src;  /* Copy original name instead */
403                     len += 2;
404                     src += len;
405                 }
406             }
407             else  /* unfinished variable name, ignore it */
408             {
409                 var = src;
410                 len = strlen(src);  /* Copy whole string */
411                 src += len;
412             }
413         }
414         total_size += len;
415         if (dst)
416         {
417             if (count < len) len = count;
418             memcpy( dst, var, len );
419             dst += len;
420             count -= len;
421         }
422     }
423     LeaveCriticalSection( &pdb->env_db->section );
424
425     /* Null-terminate the string */
426     if (dst)
427     {
428         if (!count) dst--;
429         *dst = '\0';
430     }
431     return total_size;
432 }
433
434
435 /***********************************************************************
436  *           ExpandEnvironmentStrings32W   (KERNEL32.217)
437  */
438 DWORD WINAPI ExpandEnvironmentStringsW( LPCWSTR src, LPWSTR dst, DWORD len )
439 {
440     LPSTR srcA = HEAP_strdupWtoA( GetProcessHeap(), 0, src );
441     LPSTR dstA = dst ? HeapAlloc( GetProcessHeap(), 0, len ) : NULL;
442     DWORD ret  = ExpandEnvironmentStringsA( srcA, dstA, len );
443     if (dstA)
444     {
445         lstrcpyAtoW( dst, dstA );
446         HeapFree( GetProcessHeap(), 0, dstA );
447     }
448     HeapFree( GetProcessHeap(), 0, srcA );
449     return ret;
450 }
451