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