Avoid including stackframe.h if it's not needed.
[wine] / memory / environ.c
1 /*
2  * Process environment management
3  *
4  * Copyright 1996, 1998 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27
28 #include "windef.h"
29 #include "winerror.h"
30
31 #include "wine/winbase16.h"
32 #include "wine/server.h"
33 #include "wine/library.h"
34 #include "heap.h"
35 #include "winternl.h"
36 #include "selectors.h"
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(environ);
41
42 /* TODO:
43  * - 16 bit environment ??? (see generate_env_block16 for the details)
44  */
45
46 /* Format of an environment block:
47  * ASCIIZ   string 1 (xx=yy format)
48  * ...
49  * ASCIIZ   string n
50  * BYTE     0
51  * WORD     1
52  * ASCIIZ   program name (e.g. C:\WINDOWS\SYSTEM\KRNL386.EXE)
53  *
54  * Notes:
55  * - contrary to Microsoft docs, the environment strings do not appear
56  *   to be sorted on Win95 (although they are on NT); so we don't bother
57  *   to sort them either.
58  * - on Win2K (and likely most of NT versions) the last part (WORD 1 and 
59  *   program name no longer appear in the environment block (from the 32
60  *   bit interface)
61  */
62
63 static const char ENV_program_name[] = "C:\\WINDOWS\\SYSTEM\\KRNL386.EXE";
64
65 static STARTUPINFOW startup_infoW;
66 static STARTUPINFOA startup_infoA;
67
68 /* Maximum length of a Win16 environment string (including NULL) */
69 #define MAX_WIN16_LEN  128
70
71 static WORD env_sel;     /* selector to the 16 bit environment */
72
73 /******************************************************************
74  *              generate_env_block16
75  *
76  * This internal function generates a suitable environment for the 16 bit
77  * subsystem and returns the value as a segmented pointer.
78  *
79  * FIXME: current implementation will allocate a private copy of the
80  *        environment strings, but will not follow the modifications (if any)
81  *        from the unicode env block stored in the PEB
82  *              => how should this be updated from the ntdll modifications ?
83  *                 should we use the peb->EnvironmentUpdateCount field to 
84  *                 know when environment has changed ???
85  *        we currently regenerate this block each time an environment 
86  *        variable is modified from a Win32 API call, but we'll miss all
87  *        direct access to the NTDLL APIs
88  */
89 static SEGPTR generate_env_block16(void)
90 {
91     static LPSTR env16;
92
93     DWORD       size, new_size;
94     WORD        one = 1;
95
96     if (env16) FreeEnvironmentStringsA( env16 );
97
98     env16 = GetEnvironmentStringsA();
99     size = HeapSize(GetProcessHeap(), 0, env16);
100     new_size = size + sizeof(WORD) + sizeof(ENV_program_name);
101     if (!(env16 = HeapReAlloc( GetProcessHeap(), 0, env16, new_size ))) return 0;
102
103     memcpy(env16 + size, &one, sizeof(one));
104     memcpy(env16 + size + sizeof(WORD), ENV_program_name, sizeof(ENV_program_name));
105     if (env_sel)
106         env_sel = SELECTOR_ReallocBlock( env_sel, env16, new_size );
107     else
108         env_sel = SELECTOR_AllocBlock( env16, 0x10000, WINE_LDT_FLAGS_DATA );
109
110     return MAKESEGPTR( env_sel, 0 );
111 }
112
113 /***********************************************************************
114  *           GetCommandLineA      (KERNEL32.@)
115  *
116  * WARNING: there's a Windows incompatibility lurking here !
117  * Win32s always includes the full path of the program file,
118  * whereas Windows NT only returns the full file path plus arguments
119  * in case the program has been started with a full path.
120  * Win9x seems to have inherited NT behaviour.
121  *
122  * Note that both Start Menu Execute and Explorer start programs with
123  * fully specified quoted app file paths, which is why probably the only case
124  * where you'll see single file names is in case of direct launch
125  * via CreateProcess or WinExec.
126  *
127  * Perhaps we should take care of Win3.1 programs here (Win32s "feature").
128  *
129  * References: MS KB article q102762.txt (special Win32s handling)
130  */
131 LPSTR WINAPI GetCommandLineA(void)
132 {
133     static char *cmdlineA;  /* ASCII command line */
134     
135     if (!cmdlineA) /* make an ansi version if we don't have it */
136     {
137         ANSI_STRING     ansi;
138         RtlAcquirePebLock();
139
140         cmdlineA = (RtlUnicodeStringToAnsiString( &ansi, &NtCurrentTeb()->Peb->ProcessParameters->CommandLine, TRUE) == STATUS_SUCCESS) ?
141             ansi.Buffer : NULL;
142         RtlReleasePebLock();
143     }
144     return cmdlineA;
145 }
146
147 /***********************************************************************
148  *           GetCommandLineW      (KERNEL32.@)
149  */
150 LPWSTR WINAPI GetCommandLineW(void)
151 {
152     return NtCurrentTeb()->Peb->ProcessParameters->CommandLine.Buffer;
153 }
154
155
156 /***********************************************************************
157  *           GetEnvironmentStrings    (KERNEL32.@)
158  *           GetEnvironmentStringsA   (KERNEL32.@)
159  */
160 LPSTR WINAPI GetEnvironmentStringsA(void)
161 {
162     LPWSTR      ptrW;
163     unsigned    len, slen;
164     LPSTR       ret, ptrA;
165
166     RtlAcquirePebLock();
167
168     len = 1;
169
170     ptrW = NtCurrentTeb()->Peb->ProcessParameters->Environment;
171     while (*ptrW)
172     {
173         slen = strlenW(ptrW) + 1;
174         len += WideCharToMultiByte( CP_ACP, 0, ptrW, slen, NULL, 0, NULL, NULL );
175         ptrW += slen;
176     }
177
178     if ((ret = HeapAlloc( GetProcessHeap(), 0, len )) != NULL)
179     {
180         ptrW = NtCurrentTeb()->Peb->ProcessParameters->Environment;
181         ptrA = ret;
182         while (*ptrW)
183         {
184             slen = strlenW(ptrW) + 1;
185             WideCharToMultiByte( CP_ACP, 0, ptrW, slen, ptrA, len, NULL, NULL );
186             ptrW += slen;
187             ptrA += strlen(ptrA) + 1;
188         }
189         *ptrA = 0;
190     }
191
192     RtlReleasePebLock();
193     return ret;
194 }
195
196
197 /***********************************************************************
198  *           GetEnvironmentStringsW   (KERNEL32.@)
199  */
200 LPWSTR WINAPI GetEnvironmentStringsW(void)
201 {
202     return NtCurrentTeb()->Peb->ProcessParameters->Environment;
203 }
204
205
206 /***********************************************************************
207  *           FreeEnvironmentStringsA   (KERNEL32.@)
208  */
209 BOOL WINAPI FreeEnvironmentStringsA( LPSTR ptr )
210 {
211     return HeapFree( GetProcessHeap(), 0, ptr );
212 }
213
214
215 /***********************************************************************
216  *           FreeEnvironmentStringsW   (KERNEL32.@)
217  */
218 BOOL WINAPI FreeEnvironmentStringsW( LPWSTR ptr )
219 {
220     return TRUE;
221 }
222
223
224 /***********************************************************************
225  *           GetEnvironmentVariableA   (KERNEL32.@)
226  */
227 DWORD WINAPI GetEnvironmentVariableA( LPCSTR name, LPSTR value, DWORD size )
228 {
229     UNICODE_STRING      us_name;
230     PWSTR               valueW;
231     DWORD               ret;
232
233     if (!name || !*name)
234     {
235         SetLastError(ERROR_ENVVAR_NOT_FOUND);
236         return 0;
237     }
238
239     if (!(valueW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR))))
240         return 0;
241
242     RtlCreateUnicodeStringFromAsciiz( &us_name, name );
243     SetLastError(0);
244     ret = GetEnvironmentVariableW( us_name.Buffer, valueW, size);
245     if (ret && ret < size)
246     {
247         WideCharToMultiByte( CP_ACP, 0, valueW, ret + 1, value, size, NULL, NULL );
248     }
249     /* this is needed to tell, with 0 as a return value, the difference between:
250      * - an error (GetLastError() != 0)
251      * - returning an empty string (in this case, we need to update the buffer)
252      */
253     if (ret == 0 && size && GetLastError() == 0)
254         value[0] = '\0';
255
256     RtlFreeUnicodeString( &us_name );
257     HeapFree(GetProcessHeap(), 0, valueW);
258
259     return ret;
260 }
261
262
263 /***********************************************************************
264  *           GetEnvironmentVariableW   (KERNEL32.@)
265  */
266 DWORD WINAPI GetEnvironmentVariableW( LPCWSTR name, LPWSTR val, DWORD size )
267 {
268     UNICODE_STRING      us_name;
269     UNICODE_STRING      us_value;
270     NTSTATUS            status;
271     unsigned            len;
272
273     TRACE("(%s %p %lu)\n", debugstr_w(name), val, size);
274
275     if (!name || !*name)
276     {
277         SetLastError(ERROR_ENVVAR_NOT_FOUND);
278         return 0;
279     }
280
281     RtlInitUnicodeString(&us_name, name);
282     us_value.Length = 0;
283     us_value.MaximumLength = (size ? size - 1 : 0) * sizeof(WCHAR);
284     us_value.Buffer = val;
285
286     status = RtlQueryEnvironmentVariable_U(NULL, &us_name, &us_value);
287     len = us_value.Length / sizeof(WCHAR);
288     if (status != STATUS_SUCCESS)
289     {
290         SetLastError( RtlNtStatusToDosError(status) );
291         return (status == STATUS_BUFFER_TOO_SMALL) ? len + 1 : 0;
292     }
293     if (size) val[len] = '\0';
294
295     return us_value.Length / sizeof(WCHAR);
296 }
297
298
299 /***********************************************************************
300  *           SetEnvironmentVariableA   (KERNEL32.@)
301  */
302 BOOL WINAPI SetEnvironmentVariableA( LPCSTR name, LPCSTR value )
303 {
304     UNICODE_STRING      us_name;
305     BOOL                ret;
306
307     if (!name)
308     {
309         SetLastError(ERROR_ENVVAR_NOT_FOUND);
310         return 0;
311     }
312
313     RtlCreateUnicodeStringFromAsciiz( &us_name, name );
314     if (value)
315     {
316         UNICODE_STRING      us_value;
317
318         RtlCreateUnicodeStringFromAsciiz( &us_value, value );
319         ret = SetEnvironmentVariableW( us_name.Buffer, us_value.Buffer );
320         RtlFreeUnicodeString( &us_value );
321     }
322     else ret = SetEnvironmentVariableW( us_name.Buffer, NULL );
323
324     RtlFreeUnicodeString( &us_name );
325
326     return ret;
327 }
328
329
330 /***********************************************************************
331  *           SetEnvironmentVariableW   (KERNEL32.@)
332  */
333 BOOL WINAPI SetEnvironmentVariableW( LPCWSTR name, LPCWSTR value )
334 {
335     UNICODE_STRING      us_name;
336     NTSTATUS            status;
337
338     TRACE("(%s %s)\n", debugstr_w(name), debugstr_w(value));
339
340     if (!name)
341     {
342         SetLastError(ERROR_ENVVAR_NOT_FOUND);
343         return 0;
344     }
345
346     RtlInitUnicodeString(&us_name, name);
347     if (value)
348     {
349         UNICODE_STRING      us_value;
350
351         RtlInitUnicodeString(&us_value, value);
352         status = RtlSetEnvironmentVariable(NULL, &us_name, &us_value);
353     }
354     else status = RtlSetEnvironmentVariable(NULL, &us_name, NULL);
355
356     if (status != STATUS_SUCCESS)
357     {
358         SetLastError( RtlNtStatusToDosError(status) );
359         return FALSE;
360     }
361
362     /* FIXME: see comments in generate_env_block16 */
363     if (env_sel) generate_env_block16();
364     return TRUE;
365 }
366
367
368 /***********************************************************************
369  *           ExpandEnvironmentStringsA   (KERNEL32.@)
370  *
371  * Note: overlapping buffers are not supported; this is how it should be.
372  * FIXME: return value is wrong for MBCS
373  */
374 DWORD WINAPI ExpandEnvironmentStringsA( LPCSTR src, LPSTR dst, DWORD count )
375 {
376     UNICODE_STRING      us_src;
377     PWSTR               dstW = NULL;
378     DWORD               ret;
379
380     RtlCreateUnicodeStringFromAsciiz( &us_src, src );
381     if (count)
382     {
383         if (!(dstW = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR))))
384             return 0;
385         ret = ExpandEnvironmentStringsW( us_src.Buffer, dstW, count);
386         if (ret)
387             WideCharToMultiByte( CP_ACP, 0, dstW, ret, dst, count, NULL, NULL );
388     }
389     else ret = ExpandEnvironmentStringsW( us_src.Buffer, NULL, 0);
390
391     RtlFreeUnicodeString( &us_src );
392     if (dstW) HeapFree(GetProcessHeap(), 0, dstW);
393
394     return ret;
395 }
396
397
398 /***********************************************************************
399  *           ExpandEnvironmentStringsW   (KERNEL32.@)
400  */
401 DWORD WINAPI ExpandEnvironmentStringsW( LPCWSTR src, LPWSTR dst, DWORD len )
402 {
403     UNICODE_STRING      us_src;
404     UNICODE_STRING      us_dst;
405     NTSTATUS            status;
406     DWORD               res;
407
408     TRACE("(%s %p %lu)\n", debugstr_w(src), dst, len);
409
410     RtlInitUnicodeString(&us_src, src);
411     us_dst.Length = 0;
412     us_dst.MaximumLength = len * sizeof(WCHAR);
413     us_dst.Buffer = dst;
414
415     res = 0;
416     status = RtlExpandEnvironmentStrings_U(NULL, &us_src, &us_dst, &res);
417     res /= sizeof(WCHAR);
418     if (status != STATUS_SUCCESS)
419     {
420         SetLastError( RtlNtStatusToDosError(status) );
421         if (status != STATUS_BUFFER_TOO_SMALL) return 0;
422         if (len && dst) dst[len - 1] = '\0';
423     }
424
425     return res;
426 }
427
428
429 /***********************************************************************
430  *           GetDOSEnvironment     (KERNEL.131)
431  */
432 SEGPTR WINAPI GetDOSEnvironment16(void)
433 {
434     return generate_env_block16();
435 }
436
437
438 /***********************************************************************
439  *           GetStdHandle    (KERNEL32.@)
440  */
441 HANDLE WINAPI GetStdHandle( DWORD std_handle )
442 {
443     switch (std_handle)
444     {
445         case STD_INPUT_HANDLE:  return NtCurrentTeb()->Peb->ProcessParameters->hStdInput;
446         case STD_OUTPUT_HANDLE: return NtCurrentTeb()->Peb->ProcessParameters->hStdOutput;
447         case STD_ERROR_HANDLE:  return NtCurrentTeb()->Peb->ProcessParameters->hStdError;
448     }
449     SetLastError( ERROR_INVALID_PARAMETER );
450     return INVALID_HANDLE_VALUE;
451 }
452
453
454 /***********************************************************************
455  *           SetStdHandle    (KERNEL32.@)
456  */
457 BOOL WINAPI SetStdHandle( DWORD std_handle, HANDLE handle )
458 {
459     switch (std_handle)
460     {
461         case STD_INPUT_HANDLE:  NtCurrentTeb()->Peb->ProcessParameters->hStdInput = handle;  return TRUE;
462         case STD_OUTPUT_HANDLE: NtCurrentTeb()->Peb->ProcessParameters->hStdOutput = handle; return TRUE;
463         case STD_ERROR_HANDLE:  NtCurrentTeb()->Peb->ProcessParameters->hStdError = handle;  return TRUE;
464     }
465     SetLastError( ERROR_INVALID_PARAMETER );
466     return FALSE;
467 }
468
469 /***********************************************************************
470  *              GetStartupInfoA         (KERNEL32.@)
471  */
472 VOID WINAPI GetStartupInfoA( LPSTARTUPINFOA info )
473 {
474     assert(startup_infoA.cb);
475     memcpy(info, &startup_infoA, sizeof(startup_infoA));
476 }
477
478
479 /***********************************************************************
480  *              GetStartupInfoW         (KERNEL32.@)
481  */
482 VOID WINAPI GetStartupInfoW( LPSTARTUPINFOW info )
483 {
484     assert(startup_infoW.cb);
485     memcpy(info, &startup_infoW, sizeof(startup_infoW));
486 }
487
488 /******************************************************************
489  *              ENV_CopyStartupInformation (internal)
490  *
491  * Creates the STARTUPINFO information from the ntdll information
492  */
493 void ENV_CopyStartupInformation(void)
494 {
495     RTL_USER_PROCESS_PARAMETERS* rupp;
496     ANSI_STRING         ansi;
497
498     RtlAcquirePebLock();
499     
500     rupp = NtCurrentTeb()->Peb->ProcessParameters;
501
502     startup_infoW.cb                   = sizeof(startup_infoW);
503     startup_infoW.lpReserved           = NULL;
504     startup_infoW.lpDesktop            = rupp->Desktop.Buffer;
505     startup_infoW.lpTitle              = rupp->WindowTitle.Buffer;
506     startup_infoW.dwX                  = rupp->dwX;
507     startup_infoW.dwY                  = rupp->dwY;
508     startup_infoW.dwXSize              = rupp->dwXSize;
509     startup_infoW.dwYSize              = rupp->dwYSize;
510     startup_infoW.dwXCountChars        = rupp->dwXCountChars;
511     startup_infoW.dwYCountChars        = rupp->dwYCountChars;
512     startup_infoW.dwFillAttribute      = rupp->dwFillAttribute;
513     startup_infoW.dwFlags              = rupp->dwFlags;
514     startup_infoW.wShowWindow          = rupp->wShowWindow;
515     startup_infoW.cbReserved2          = 0;
516     startup_infoW.lpReserved2          = NULL;
517     startup_infoW.hStdInput            = rupp->hStdInput;
518     startup_infoW.hStdOutput           = rupp->hStdOutput;
519     startup_infoW.hStdError            = rupp->hStdError;
520
521     startup_infoA.cb                   = sizeof(startup_infoW);
522     startup_infoA.lpReserved           = NULL;
523     startup_infoA.lpDesktop = (rupp->Desktop.Length &&
524                                RtlUnicodeStringToAnsiString( &ansi, &rupp->Desktop, TRUE) == STATUS_SUCCESS) ?
525         ansi.Buffer : NULL;
526     startup_infoA.lpTitle = (rupp->WindowTitle.Length &&
527                              RtlUnicodeStringToAnsiString( &ansi, &rupp->WindowTitle, TRUE) == STATUS_SUCCESS) ?
528         ansi.Buffer : NULL;
529     startup_infoA.dwX                  = rupp->dwX;
530     startup_infoA.dwY                  = rupp->dwY;
531     startup_infoA.dwXSize              = rupp->dwXSize;
532     startup_infoA.dwYSize              = rupp->dwYSize;
533     startup_infoA.dwXCountChars        = rupp->dwXCountChars;
534     startup_infoA.dwYCountChars        = rupp->dwYCountChars;
535     startup_infoA.dwFillAttribute      = rupp->dwFillAttribute;
536     startup_infoA.dwFlags              = rupp->dwFlags;
537     startup_infoA.wShowWindow          = rupp->wShowWindow;
538     startup_infoA.cbReserved2          = 0;
539     startup_infoA.lpReserved2          = NULL;
540     startup_infoA.hStdInput            = rupp->hStdInput;
541     startup_infoA.hStdOutput           = rupp->hStdOutput;
542     startup_infoA.hStdError            = rupp->hStdError;
543
544     RtlReleasePebLock();
545 }