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