HEAP_Commit/Decommit were assuming a page size of 4KB
[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 static 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)
113     {
114         if (!pdb->parent)  /* initial process */
115             return ENV_BuildEnvironment( pdb );
116         env = pdb->parent->env_db->environ;
117     }
118
119     /* Compute the environment size */
120
121     src = env;
122     size = EXTRA_ENV_SIZE;
123     while (*src)
124     {
125         int len = strlen(src) + 1;
126         src += len;
127         if ((len > MAX_WIN16_LEN) && (pdb->flags & PDB32_WIN16_PROC))
128             len = MAX_WIN16_LEN;
129         size += len;
130     }
131
132     /* Copy the environment */
133
134     if (!(pdb->env_db->environ = HeapAlloc( pdb->heap, 0,
135                                             size + EXTRA_ENV_SIZE )))
136         return FALSE;
137     pdb->env_db->env_sel = SELECTOR_AllocBlock( pdb->env_db->environ,
138                                                 0x10000, SEGMENT_DATA,
139                                                 FALSE, FALSE );
140     src = env;
141     dst = pdb->env_db->environ;
142     while (*src)
143     {
144         if (pdb->flags & PDB32_WIN16_PROC)
145             lstrcpynA( dst, src, MAX_WIN16_LEN );
146         else
147             strcpy( dst, src );
148         src += strlen(src) + 1;
149         dst += strlen(dst) + 1;
150     }
151     FILL_EXTRA_ENV( dst );
152     return TRUE;
153 }
154
155
156 /***********************************************************************
157  *           ENV_FreeEnvironment
158  *
159  * Free a process environment.
160  */
161 void ENV_FreeEnvironment( PDB *pdb )
162 {
163     if (!pdb->env_db) return;
164     if (pdb->env_db->env_sel) SELECTOR_FreeBlock( pdb->env_db->env_sel, 1 );
165     DeleteCriticalSection( &pdb->env_db->section );
166     HeapFree( pdb->heap, 0, pdb->env_db );
167 }
168
169
170 /***********************************************************************
171  *           GetCommandLine32A      (KERNEL32.289)
172  */
173 LPCSTR WINAPI GetCommandLineA(void)
174 {
175     return PROCESS_Current()->env_db->cmd_line;
176 }
177
178 /***********************************************************************
179  *           GetCommandLine32W      (KERNEL32.290)
180  */
181 LPCWSTR WINAPI GetCommandLineW(void)
182 {
183     PDB *pdb = PROCESS_Current();
184     EnterCriticalSection( &pdb->env_db->section );
185     if (!pdb->env_db->cmd_lineW)
186         pdb->env_db->cmd_lineW = HEAP_strdupAtoW( pdb->heap, 0,
187                                                   pdb->env_db->cmd_line );
188     LeaveCriticalSection( &pdb->env_db->section );
189     return pdb->env_db->cmd_lineW;
190 }
191
192
193 /***********************************************************************
194  *           GetEnvironmentStrings32A   (KERNEL32.319) (KERNEL32.320)
195  */
196 LPSTR WINAPI GetEnvironmentStringsA(void)
197 {
198     PDB *pdb = PROCESS_Current();
199     return pdb->env_db->environ;
200 }
201
202
203 /***********************************************************************
204  *           GetEnvironmentStrings32W   (KERNEL32.321)
205  */
206 LPWSTR WINAPI GetEnvironmentStringsW(void)
207 {
208     INT size;
209     LPWSTR ret;
210     PDB *pdb = PROCESS_Current();
211
212     EnterCriticalSection( &pdb->env_db->section );
213     size = HeapSize( pdb->heap, 0, pdb->env_db->environ );
214     if ((ret = HeapAlloc( pdb->heap, 0, size * sizeof(WCHAR) )) != NULL)
215     {
216         LPSTR pA = pdb->env_db->environ;
217         LPWSTR pW = ret;
218         while (size--) *pW++ = (WCHAR)(BYTE)*pA++;
219     }
220     LeaveCriticalSection( &pdb->env_db->section );
221     return ret;
222 }
223
224
225 /***********************************************************************
226  *           FreeEnvironmentStrings32A   (KERNEL32.268)
227  */
228 BOOL WINAPI FreeEnvironmentStringsA( LPSTR ptr )
229 {
230     PDB *pdb = PROCESS_Current();
231     if (ptr != pdb->env_db->environ)
232     {
233         SetLastError( ERROR_INVALID_PARAMETER );
234         return FALSE;
235     }
236     return TRUE;
237 }
238
239
240 /***********************************************************************
241  *           FreeEnvironmentStrings32W   (KERNEL32.269)
242  */
243 BOOL WINAPI FreeEnvironmentStringsW( LPWSTR ptr )
244 {
245     return HeapFree( GetProcessHeap(), 0, ptr );
246 }
247
248
249 /***********************************************************************
250  *           GetEnvironmentVariable32A   (KERNEL32.322)
251  */
252 DWORD WINAPI GetEnvironmentVariableA( LPCSTR name, LPSTR value, DWORD size )
253 {
254     LPCSTR p;
255     INT ret = 0;
256     PDB *pdb = PROCESS_Current();
257
258     if (!name || !*name)
259     {
260         SetLastError( ERROR_INVALID_PARAMETER );
261         return 0;
262     }
263     EnterCriticalSection( &pdb->env_db->section );
264     if ((p = ENV_FindVariable( pdb->env_db->environ, name, strlen(name) )))
265     {
266         ret = strlen(p);
267         if (size <= ret)
268         {
269             /* If not enough room, include the terminating null
270              * in the returned size and return an empty string */
271             ret++;
272             if (value) *value = '\0';
273         }
274         else if (value) strcpy( value, p );
275     }
276     LeaveCriticalSection( &pdb->env_db->section );
277     return ret;  /* FIXME: SetLastError */
278 }
279
280
281 /***********************************************************************
282  *           GetEnvironmentVariable32W   (KERNEL32.323)
283  */
284 DWORD WINAPI GetEnvironmentVariableW( LPCWSTR nameW, LPWSTR valW, DWORD size)
285 {
286     LPSTR name = HEAP_strdupWtoA( GetProcessHeap(), 0, nameW );
287     LPSTR val  = valW ? HeapAlloc( GetProcessHeap(), 0, size ) : NULL;
288     DWORD res  = GetEnvironmentVariableA( name, val, size );
289     HeapFree( GetProcessHeap(), 0, name );
290     if (val)
291     {
292         lstrcpynAtoW( valW, val, size );
293         HeapFree( GetProcessHeap(), 0, val );
294     }
295     return res;
296 }
297
298
299 /***********************************************************************
300  *           SetEnvironmentVariable32A   (KERNEL32.641)
301  */
302 BOOL WINAPI SetEnvironmentVariableA( LPCSTR name, LPCSTR value )
303 {
304     INT old_size, len, res;
305     LPSTR p, env, new_env;
306     BOOL ret = FALSE;
307     PDB *pdb = PROCESS_Current();
308
309     EnterCriticalSection( &pdb->env_db->section );
310     env = p = pdb->env_db->environ;
311
312     /* Find a place to insert the string */
313
314     res = -1;
315     len = strlen(name);
316     while (*p)
317     {
318         if (!lstrncmpiA( name, p, len ) && (p[len] == '=')) break;
319         p += strlen(p) + 1;
320     }
321     if (!value && !*p) goto done;  /* Value to remove doesn't exist */
322
323     /* Realloc the buffer */
324
325     len = value ? strlen(name) + strlen(value) + 2 : 0;
326     if (*p) len -= strlen(p) + 1;  /* The name already exists */
327     old_size = HeapSize( pdb->heap, 0, env );
328     if (len < 0)
329     {
330         LPSTR next = p + strlen(p) + 1;  /* We know there is a next one */
331         memmove( next + len, next, old_size - (next - env) );
332     }
333     if (!(new_env = HeapReAlloc( pdb->heap, 0, env, old_size + len )))
334         goto done;
335     if (pdb->env_db->env_sel)
336         SELECTOR_MoveBlock( pdb->env_db->env_sel, new_env );
337     p = new_env + (p - env);
338     if (len > 0) memmove( p + len, p, old_size - (p - new_env) );
339
340     /* Set the new string */
341
342     if (value)
343     {
344         strcpy( p, name );
345         strcat( p, "=" );
346         strcat( p, value );
347     }
348     pdb->env_db->environ = new_env;
349     ret = TRUE;
350
351 done:
352     LeaveCriticalSection( &pdb->env_db->section );
353     return ret;
354 }
355
356
357 /***********************************************************************
358  *           SetEnvironmentVariable32W   (KERNEL32.642)
359  */
360 BOOL WINAPI SetEnvironmentVariableW( LPCWSTR name, LPCWSTR value )
361 {
362     LPSTR nameA  = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
363     LPSTR valueA = HEAP_strdupWtoA( GetProcessHeap(), 0, value );
364     BOOL ret = SetEnvironmentVariableA( nameA, valueA );
365     HeapFree( GetProcessHeap(), 0, nameA );
366     HeapFree( GetProcessHeap(), 0, valueA );
367     return ret;
368 }
369
370
371 /***********************************************************************
372  *           ExpandEnvironmentStrings32A   (KERNEL32.216)
373  *
374  * Note: overlapping buffers are not supported; this is how it should be.
375  */
376 DWORD WINAPI ExpandEnvironmentStringsA( LPCSTR src, LPSTR dst, DWORD count )
377 {
378     DWORD len, total_size = 1;  /* 1 for terminating '\0' */
379     LPCSTR p, var;
380     PDB *pdb = PROCESS_Current();
381
382     if (!count) dst = NULL;
383     EnterCriticalSection( &pdb->env_db->section );
384
385     while (*src)
386     {
387         if (*src != '%')
388         {
389             if ((p = strchr( src, '%' ))) len = p - src;
390             else len = strlen(src);
391             var = src;
392             src += len;
393         }
394         else  /* we are at the start of a variable */
395         {
396             if ((p = strchr( src + 1, '%' )))
397             {
398                 len = p - src - 1;  /* Length of the variable name */
399                 if ((var = ENV_FindVariable( pdb->env_db->environ,
400                                              src + 1, len )))
401                 {
402                     src += len + 2;  /* Skip the variable name */
403                     len = strlen(var);
404                 }
405                 else
406                 {
407                     var = src;  /* Copy original name instead */
408                     len += 2;
409                     src += len;
410                 }
411             }
412             else  /* unfinished variable name, ignore it */
413             {
414                 var = src;
415                 len = strlen(src);  /* Copy whole string */
416                 src += len;
417             }
418         }
419         total_size += len;
420         if (dst)
421         {
422             if (count < len) len = count;
423             memcpy( dst, var, len );
424             dst += len;
425             count -= len;
426         }
427     }
428     LeaveCriticalSection( &pdb->env_db->section );
429
430     /* Null-terminate the string */
431     if (dst)
432     {
433         if (!count) dst--;
434         *dst = '\0';
435     }
436     return total_size;
437 }
438
439
440 /***********************************************************************
441  *           ExpandEnvironmentStrings32W   (KERNEL32.217)
442  */
443 DWORD WINAPI ExpandEnvironmentStringsW( LPCWSTR src, LPWSTR dst, DWORD len )
444 {
445     LPSTR srcA = HEAP_strdupWtoA( GetProcessHeap(), 0, src );
446     LPSTR dstA = dst ? HeapAlloc( GetProcessHeap(), 0, len ) : NULL;
447     DWORD ret  = ExpandEnvironmentStringsA( srcA, dstA, len );
448     if (dstA)
449     {
450         lstrcpyAtoW( dst, dstA );
451         HeapFree( GetProcessHeap(), 0, dstA );
452     }
453     HeapFree( GetProcessHeap(), 0, srcA );
454     return ret;
455 }
456