Fixed the Win16/Win32 structure size/alignment differences of
[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 "windef.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(void)
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( GetProcessHeap(), 0, size ))) return FALSE;
82     PROCESS_Current()->env_db->environ = p;
83     PROCESS_Current()->env_db->env_sel = SELECTOR_AllocBlock( p, 0x10000, SEGMENT_DATA,
84                                                               FALSE, FALSE );
85
86     /* And fill it with the Unix environment */
87
88     for (e = environ; *e; e++)
89     {
90         strcpy( p, *e );
91         p += strlen(p) + 1;
92     }
93
94     /* Now add the program name */
95
96     FILL_EXTRA_ENV( p );
97     return TRUE;
98 }
99
100
101 /***********************************************************************
102  *           GetCommandLineA      (KERNEL32.289)
103  */
104 LPSTR WINAPI GetCommandLineA(void)
105 {
106     return PROCESS_Current()->env_db->cmd_line;
107 }
108
109 /***********************************************************************
110  *           GetCommandLineW      (KERNEL32.290)
111  */
112 LPWSTR WINAPI GetCommandLineW(void)
113 {
114     PDB *pdb = PROCESS_Current();
115     EnterCriticalSection( &pdb->env_db->section );
116     if (!pdb->env_db->cmd_lineW)
117         pdb->env_db->cmd_lineW = HEAP_strdupAtoW( GetProcessHeap(), 0,
118                                                   pdb->env_db->cmd_line );
119     LeaveCriticalSection( &pdb->env_db->section );
120     return pdb->env_db->cmd_lineW;
121 }
122
123
124 /***********************************************************************
125  *           GetEnvironmentStringsA   (KERNEL32.319) (KERNEL32.320)
126  */
127 LPSTR WINAPI GetEnvironmentStringsA(void)
128 {
129     PDB *pdb = PROCESS_Current();
130     return pdb->env_db->environ;
131 }
132
133
134 /***********************************************************************
135  *           GetEnvironmentStringsW   (KERNEL32.321)
136  */
137 LPWSTR WINAPI GetEnvironmentStringsW(void)
138 {
139     INT size;
140     LPWSTR ret;
141     PDB *pdb = PROCESS_Current();
142
143     EnterCriticalSection( &pdb->env_db->section );
144     size = HeapSize( GetProcessHeap(), 0, pdb->env_db->environ );
145     if ((ret = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) )) != NULL)
146     {
147         LPSTR pA = pdb->env_db->environ;
148         LPWSTR pW = ret;
149         while (size--) *pW++ = (WCHAR)(BYTE)*pA++;
150     }
151     LeaveCriticalSection( &pdb->env_db->section );
152     return ret;
153 }
154
155
156 /***********************************************************************
157  *           FreeEnvironmentStringsA   (KERNEL32.268)
158  */
159 BOOL WINAPI FreeEnvironmentStringsA( LPSTR ptr )
160 {
161     PDB *pdb = PROCESS_Current();
162     if (ptr != pdb->env_db->environ)
163     {
164         SetLastError( ERROR_INVALID_PARAMETER );
165         return FALSE;
166     }
167     return TRUE;
168 }
169
170
171 /***********************************************************************
172  *           FreeEnvironmentStringsW   (KERNEL32.269)
173  */
174 BOOL WINAPI FreeEnvironmentStringsW( LPWSTR ptr )
175 {
176     return HeapFree( GetProcessHeap(), 0, ptr );
177 }
178
179
180 /***********************************************************************
181  *           GetEnvironmentVariableA   (KERNEL32.322)
182  */
183 DWORD WINAPI GetEnvironmentVariableA( LPCSTR name, LPSTR value, DWORD size )
184 {
185     LPCSTR p;
186     INT ret = 0;
187     PDB *pdb = PROCESS_Current();
188
189     if (!name || !*name)
190     {
191         SetLastError( ERROR_INVALID_PARAMETER );
192         return 0;
193     }
194     EnterCriticalSection( &pdb->env_db->section );
195     if ((p = ENV_FindVariable( pdb->env_db->environ, name, strlen(name) )))
196     {
197         ret = strlen(p);
198         if (size <= ret)
199         {
200             /* If not enough room, include the terminating null
201              * in the returned size and return an empty string */
202             ret++;
203             if (value) *value = '\0';
204         }
205         else if (value) strcpy( value, p );
206     }
207     LeaveCriticalSection( &pdb->env_db->section );
208     return ret;  /* FIXME: SetLastError */
209 }
210
211
212 /***********************************************************************
213  *           GetEnvironmentVariableW   (KERNEL32.323)
214  */
215 DWORD WINAPI GetEnvironmentVariableW( LPCWSTR nameW, LPWSTR valW, DWORD size)
216 {
217     LPSTR name = HEAP_strdupWtoA( GetProcessHeap(), 0, nameW );
218     LPSTR val  = valW ? HeapAlloc( GetProcessHeap(), 0, size ) : NULL;
219     DWORD res  = GetEnvironmentVariableA( name, val, size );
220     HeapFree( GetProcessHeap(), 0, name );
221     if (val)
222     {
223         lstrcpynAtoW( valW, val, size );
224         HeapFree( GetProcessHeap(), 0, val );
225     }
226     return res;
227 }
228
229
230 /***********************************************************************
231  *           SetEnvironmentVariableA   (KERNEL32.641)
232  */
233 BOOL WINAPI SetEnvironmentVariableA( LPCSTR name, LPCSTR value )
234 {
235     INT old_size, len, res;
236     LPSTR p, env, new_env;
237     BOOL ret = FALSE;
238     PDB *pdb = PROCESS_Current();
239
240     EnterCriticalSection( &pdb->env_db->section );
241     env = p = pdb->env_db->environ;
242
243     /* Find a place to insert the string */
244
245     res = -1;
246     len = strlen(name);
247     while (*p)
248     {
249         if (!lstrncmpiA( name, p, len ) && (p[len] == '=')) break;
250         p += strlen(p) + 1;
251     }
252     if (!value && !*p) goto done;  /* Value to remove doesn't exist */
253
254     /* Realloc the buffer */
255
256     len = value ? strlen(name) + strlen(value) + 2 : 0;
257     if (*p) len -= strlen(p) + 1;  /* The name already exists */
258     old_size = HeapSize( GetProcessHeap(), 0, env );
259     if (len < 0)
260     {
261         LPSTR next = p + strlen(p) + 1;  /* We know there is a next one */
262         memmove( next + len, next, old_size - (next - env) );
263     }
264     if (!(new_env = HeapReAlloc( GetProcessHeap(), 0, env, old_size + len )))
265         goto done;
266     if (pdb->env_db->env_sel)
267         SELECTOR_MoveBlock( pdb->env_db->env_sel, new_env );
268     p = new_env + (p - env);
269     if (len > 0) memmove( p + len, p, old_size - (p - new_env) );
270
271     /* Set the new string */
272
273     if (value)
274     {
275         strcpy( p, name );
276         strcat( p, "=" );
277         strcat( p, value );
278     }
279     pdb->env_db->environ = new_env;
280     ret = TRUE;
281
282 done:
283     LeaveCriticalSection( &pdb->env_db->section );
284     return ret;
285 }
286
287
288 /***********************************************************************
289  *           SetEnvironmentVariableW   (KERNEL32.642)
290  */
291 BOOL WINAPI SetEnvironmentVariableW( LPCWSTR name, LPCWSTR value )
292 {
293     LPSTR nameA  = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
294     LPSTR valueA = HEAP_strdupWtoA( GetProcessHeap(), 0, value );
295     BOOL ret = SetEnvironmentVariableA( nameA, valueA );
296     HeapFree( GetProcessHeap(), 0, nameA );
297     HeapFree( GetProcessHeap(), 0, valueA );
298     return ret;
299 }
300
301
302 /***********************************************************************
303  *           ExpandEnvironmentStringsA   (KERNEL32.216)
304  *
305  * Note: overlapping buffers are not supported; this is how it should be.
306  */
307 DWORD WINAPI ExpandEnvironmentStringsA( LPCSTR src, LPSTR dst, DWORD count )
308 {
309     DWORD len, total_size = 1;  /* 1 for terminating '\0' */
310     LPCSTR p, var;
311     PDB *pdb = PROCESS_Current();
312
313     if (!count) dst = NULL;
314     EnterCriticalSection( &pdb->env_db->section );
315
316     while (*src)
317     {
318         if (*src != '%')
319         {
320             if ((p = strchr( src, '%' ))) len = p - src;
321             else len = strlen(src);
322             var = src;
323             src += len;
324         }
325         else  /* we are at the start of a variable */
326         {
327             if ((p = strchr( src + 1, '%' )))
328             {
329                 len = p - src - 1;  /* Length of the variable name */
330                 if ((var = ENV_FindVariable( pdb->env_db->environ,
331                                              src + 1, len )))
332                 {
333                     src += len + 2;  /* Skip the variable name */
334                     len = strlen(var);
335                 }
336                 else
337                 {
338                     var = src;  /* Copy original name instead */
339                     len += 2;
340                     src += len;
341                 }
342             }
343             else  /* unfinished variable name, ignore it */
344             {
345                 var = src;
346                 len = strlen(src);  /* Copy whole string */
347                 src += len;
348             }
349         }
350         total_size += len;
351         if (dst)
352         {
353             if (count < len) len = count;
354             memcpy( dst, var, len );
355             dst += len;
356             count -= len;
357         }
358     }
359     LeaveCriticalSection( &pdb->env_db->section );
360
361     /* Null-terminate the string */
362     if (dst)
363     {
364         if (!count) dst--;
365         *dst = '\0';
366     }
367     return total_size;
368 }
369
370
371 /***********************************************************************
372  *           ExpandEnvironmentStringsW   (KERNEL32.217)
373  */
374 DWORD WINAPI ExpandEnvironmentStringsW( LPCWSTR src, LPWSTR dst, DWORD len )
375 {
376     LPSTR srcA = HEAP_strdupWtoA( GetProcessHeap(), 0, src );
377     LPSTR dstA = dst ? HeapAlloc( GetProcessHeap(), 0, len ) : NULL;
378     DWORD ret  = ExpandEnvironmentStringsA( srcA, dstA, len );
379     if (dstA)
380     {
381         lstrcpyAtoW( dst, dstA );
382         HeapFree( GetProcessHeap(), 0, dstA );
383     }
384     HeapFree( GetProcessHeap(), 0, srcA );
385     return ret;
386 }
387