Added regedit unit test, a couple minor changes to regedit.
[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
27 #include "windef.h"
28 #include "winerror.h"
29
30 #include "wine/winbase16.h"
31 #include "wine/server.h"
32 #include "wine/library.h"
33 #include "heap.h"
34 #include "ntddk.h"
35 #include "selectors.h"
36
37 /* Win32 process environment database */
38 typedef struct _ENVDB
39 {
40     LPSTR            env;              /* 00 Process environment strings */
41     DWORD            unknown1;         /* 04 Unknown */
42     LPSTR            cmd_line;         /* 08 Command line */
43     LPSTR            cur_dir;          /* 0c Current directory */
44     STARTUPINFOA    *startup_info;     /* 10 Startup information */
45     HANDLE           hStdin;           /* 14 Handle for standard input */
46     HANDLE           hStdout;          /* 18 Handle for standard output */
47     HANDLE           hStderr;          /* 1c Handle for standard error */
48     DWORD            unknown2;         /* 20 Unknown */
49     DWORD            inherit_console;  /* 24 Inherit console flag */
50     DWORD            break_type;       /* 28 Console events flag */
51     void            *break_sem;        /* 2c SetConsoleCtrlHandler semaphore */
52     void            *break_event;      /* 30 SetConsoleCtrlHandler event */
53     void            *break_thread;     /* 34 SetConsoleCtrlHandler thread */
54     void            *break_handlers;   /* 38 List of console handlers */
55 } ENVDB;
56
57
58 /* Format of an environment block:
59  * ASCIIZ   string 1 (xx=yy format)
60  * ...
61  * ASCIIZ   string n
62  * BYTE     0
63  * WORD     1
64  * ASCIIZ   program name (e.g. C:\WINDOWS\SYSTEM\KRNL386.EXE)
65  *
66  * Notes:
67  * - contrary to Microsoft docs, the environment strings do not appear
68  *   to be sorted on Win95 (although they are on NT); so we don't bother
69  *   to sort them either.
70  */
71
72 static const char ENV_program_name[] = "C:\\WINDOWS\\SYSTEM\\KRNL386.EXE";
73
74 /* Maximum length of a Win16 environment string (including NULL) */
75 #define MAX_WIN16_LEN  128
76
77 STARTUPINFOA current_startupinfo =
78 {
79     sizeof(STARTUPINFOA),    /* cb */
80     0,                       /* lpReserved */
81     0,                       /* lpDesktop */
82     0,                       /* lpTitle */
83     0,                       /* dwX */
84     0,                       /* dwY */
85     0,                       /* dwXSize */
86     0,                       /* dwYSize */
87     0,                       /* dwXCountChars */
88     0,                       /* dwYCountChars */
89     0,                       /* dwFillAttribute */
90     0,                       /* dwFlags */
91     0,                       /* wShowWindow */
92     0,                       /* cbReserved2 */
93     0,                       /* lpReserved2 */
94     0,                       /* hStdInput */
95     0,                       /* hStdOutput */
96     0                        /* hStdError */
97 };
98
99 ENVDB current_envdb =
100 {
101     0,                       /* environ */
102     0,                       /* unknown1 */
103     0,                       /* cmd_line */
104     0,                       /* cur_dir */
105     &current_startupinfo,    /* startup_info */
106     0,                       /* hStdin */
107     0,                       /* hStdout */
108     0,                       /* hStderr */
109     0,                       /* unknown2 */
110     0,                       /* inherit_console */
111     0,                       /* break_type */
112     0,                       /* break_sem */
113     0,                       /* break_event */
114     0,                       /* break_thread */
115     0                        /* break_handlers */
116 };
117
118
119 static WCHAR *cmdlineW;  /* Unicode command line */
120 static WORD env_sel;     /* selector to the environment */
121
122 /***********************************************************************
123  *           ENV_FindVariable
124  *
125  * Find a variable in the environment and return a pointer to the value.
126  * Helper function for GetEnvironmentVariable and ExpandEnvironmentStrings.
127  */
128 static LPCSTR ENV_FindVariable( LPCSTR env, LPCSTR name, INT len )
129 {
130     while (*env)
131     {
132         if (!strncasecmp( name, env, len ) && (env[len] == '='))
133             return env + len + 1;
134         env += strlen(env) + 1;
135     }
136     return NULL;
137 }
138
139
140 /***********************************************************************
141  *           build_environment
142  *
143  * Build the environment for the initial process
144  */
145 static BOOL build_environment(void)
146 {
147     extern char **environ;
148     static const WORD one = 1;
149     LPSTR p, *e;
150     int size;
151
152     /* Compute the total size of the Unix environment */
153
154     size = sizeof(BYTE) + sizeof(WORD) + sizeof(ENV_program_name);
155     for (e = environ; *e; e++) size += strlen(*e) + 1;
156
157     /* Now allocate the environment */
158
159     if (!(p = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
160     current_envdb.env = p;
161     env_sel = SELECTOR_AllocBlock( p, 0x10000, WINE_LDT_FLAGS_DATA );
162
163     /* And fill it with the Unix environment */
164
165     for (e = environ; *e; e++)
166     {
167         strcpy( p, *e );
168         p += strlen(p) + 1;
169     }
170
171     /* Now add the program name */
172
173     *p++ = 0;
174     memcpy( p, &one, sizeof(WORD) );
175     strcpy( p + sizeof(WORD), ENV_program_name );
176     return TRUE;
177 }
178
179
180 /***********************************************************************
181  *           copy_str
182  *
183  * Small helper for ENV_InitStartupInfo.
184  */
185 inline static char *copy_str( char **dst, const char **src, size_t len )
186 {
187     char *ret;
188
189     if (!len) return NULL;
190     ret = *dst;
191     memcpy( ret, *src, len );
192     ret[len] = 0;
193     *dst += len + 1;
194     *src += len;
195     return ret;
196 }
197
198
199 /***********************************************************************
200  *           ENV_InitStartupInfo
201  *
202  * Fill the startup info structure from the server.
203  */
204 ENVDB *ENV_InitStartupInfo( size_t info_size, char *main_exe_name, size_t main_exe_size )
205 {
206     startup_info_t info;
207     void *data;
208     char *dst;
209     const char *src;
210     size_t len;
211
212     if (!build_environment()) return NULL;
213     if (!info_size) return &current_envdb;  /* nothing to retrieve */
214
215     if (!(data = HeapAlloc( GetProcessHeap(), 0, info_size ))) return NULL;
216
217     SERVER_START_REQ( get_startup_info )
218     {
219         wine_server_set_reply( req, data, info_size );
220         wine_server_call( req );
221         info_size = wine_server_reply_size( reply );
222     }
223     SERVER_END_REQ;
224     if (info_size < sizeof(info.size)) goto done;
225     len = min( info_size, ((startup_info_t *)data)->size );
226     memset( &info, 0, sizeof(info) );
227     memcpy( &info, data, len );
228     src = (char *)data + len;
229     info_size -= len;
230
231     /* fixup the lengths */
232     if (info.filename_len > info_size) info.filename_len = info_size;
233     info_size -= info.filename_len;
234     if (info.cmdline_len > info_size) info.cmdline_len = info_size;
235     info_size -= info.cmdline_len;
236     if (info.desktop_len > info_size) info.desktop_len = info_size;
237     info_size -= info.desktop_len;
238     if (info.title_len > info_size) info.title_len = info_size;
239
240     /* store the filename */
241     if (info.filename_len)
242     {
243         len = min( info.filename_len, main_exe_size-1 );
244         memcpy( main_exe_name, src, len );
245         main_exe_name[len] = 0;
246         src += info.filename_len;
247     }
248
249     /* copy the other strings */
250     len = info.cmdline_len + info.desktop_len + info.title_len;
251     if (len && (dst = HeapAlloc( GetProcessHeap(), 0, len + 3 )))
252     {
253         current_envdb.cmd_line = copy_str( &dst, &src, info.cmdline_len );
254         current_startupinfo.lpDesktop = copy_str( &dst, &src, info.desktop_len );
255         current_startupinfo.lpTitle = copy_str( &dst, &src, info.title_len );
256     }
257
258     current_startupinfo.dwX             = info.x;
259     current_startupinfo.dwY             = info.y;
260     current_startupinfo.dwXSize         = info.cx;
261     current_startupinfo.dwYSize         = info.cy;
262     current_startupinfo.dwXCountChars   = info.x_chars;
263     current_startupinfo.dwYCountChars   = info.y_chars;
264     current_startupinfo.dwFillAttribute = info.attribute;
265     current_startupinfo.wShowWindow     = info.cmd_show;
266     current_startupinfo.dwFlags         = info.flags;
267  done:
268     HeapFree( GetProcessHeap(), 0, data );
269     return &current_envdb;
270 }
271
272
273
274 /***********************************************************************
275  *              set_library_argv
276  *
277  * Set the Wine library argc/argv global variables.
278  */
279 static void set_library_argv( char **argv )
280 {
281     int argc;
282     WCHAR *p;
283     WCHAR **wargv;
284     DWORD total = 0;
285
286     for (argc = 0; argv[argc]; argc++)
287         total += MultiByteToWideChar( CP_ACP, 0, argv[argc], -1, NULL, 0 );
288
289     wargv = HeapAlloc( GetProcessHeap(), 0,
290                        total * sizeof(WCHAR) + (argc + 1) * sizeof(*wargv) );
291     p = (WCHAR *)(wargv + argc + 1);
292     for (argc = 0; argv[argc]; argc++)
293     {
294         DWORD len = MultiByteToWideChar( CP_ACP, 0, argv[argc], -1, p, total );
295         wargv[argc] = p;
296         p += len;
297         total -= len;
298     }
299     wargv[argc] = NULL;
300
301     __wine_main_argc  = argc;
302     __wine_main_argv  = argv;
303     __wine_main_wargv = wargv;
304 }
305
306
307 /***********************************************************************
308  *           ENV_BuildCommandLine
309  *
310  * Build the command line of a process from the argv array.
311  *
312  * Note that it does NOT necessarily include the file name.
313  * Sometimes we don't even have any command line options at all.
314  *
315  * We must quote and escape characters so that the argv array can be rebuilt
316  * from the command line:
317  * - spaces and tabs must be quoted
318  *   'a b'   -> '"a b"'
319  * - quotes must be escaped
320  *   '"'     -> '\"'
321  * - if '\'s are followed by a '"', they must be doubled and followed by '\"',
322  *   resulting in an odd number of '\' followed by a '"'
323  *   '\"'    -> '\\\"'
324  *   '\\"'   -> '\\\\\"'
325  * - '\'s that are not followed by a '"' can be left as is
326  *   'a\b'   == 'a\b'
327  *   'a\\b'  == 'a\\b'
328  */
329 BOOL ENV_BuildCommandLine( char **argv )
330 {
331     int len;
332     char *p, **arg;
333
334     set_library_argv( argv );
335
336     if (current_envdb.cmd_line) goto done;  /* already got it from the server */
337
338     len = 0;
339     for (arg = argv; *arg; arg++)
340     {
341         int has_space,bcount;
342         char* a;
343
344         has_space=0;
345         bcount=0;
346         a=*arg;
347         while (*a!='\0') {
348             if (*a=='\\') {
349                 bcount++;
350             } else {
351                 if (*a==' ' || *a=='\t') {
352                     has_space=1;
353                 } else if (*a=='"') {
354                     /* doubling of '\' preceeding a '"',
355                      * plus escaping of said '"'
356                      */
357                     len+=2*bcount+1;
358                 }
359                 bcount=0;
360             }
361             a++;
362         }
363         len+=(a-*arg)+1 /* for the separating space */;
364         if (has_space)
365             len+=2; /* for the quotes */
366     }
367
368     if (!(current_envdb.cmd_line = HeapAlloc( GetProcessHeap(), 0, len )))
369         return FALSE;
370
371     p = current_envdb.cmd_line;
372     for (arg = argv; *arg; arg++)
373     {
374         int has_space,has_quote;
375         char* a;
376
377         /* Check for quotes and spaces in this argument */
378         has_space=has_quote=0;
379         a=*arg;
380         while (*a!='\0') {
381             if (*a==' ' || *a=='\t') {
382                 has_space=1;
383                 if (has_quote)
384                     break;
385             } else if (*a=='"') {
386                 has_quote=1;
387                 if (has_space)
388                     break;
389             }
390             a++;
391         }
392
393         /* Now transfer it to the command line */
394         if (has_space)
395             *p++='"';
396         if (has_quote) {
397             int bcount;
398             char* a;
399
400             bcount=0;
401             a=*arg;
402             while (*a!='\0') {
403                 if (*a=='\\') {
404                     *p++=*a;
405                     bcount++;
406                 } else {
407                     if (*a=='"') {
408                         int i;
409
410                         /* Double all the '\\' preceeding this '"', plus one */
411                         for (i=0;i<=bcount;i++)
412                             *p++='\\';
413                         *p++='"';
414                     } else {
415                         *p++=*a;
416                     }
417                     bcount=0;
418                 }
419                 a++;
420             }
421         } else {
422             strcpy(p,*arg);
423             p+=strlen(*arg);
424         }
425         if (has_space)
426             *p++='"';
427         *p++=' ';
428     }
429     if (p > current_envdb.cmd_line)
430         p--;  /* remove last space */
431     *p = '\0';
432
433     /* now allocate the Unicode version */
434  done:
435     len = MultiByteToWideChar( CP_ACP, 0, current_envdb.cmd_line, -1, NULL, 0 );
436     if (!(cmdlineW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
437         return FALSE;
438     MultiByteToWideChar( CP_ACP, 0, current_envdb.cmd_line, -1, cmdlineW, len );
439     return TRUE;
440 }
441
442
443 /***********************************************************************
444  *           GetCommandLineA      (KERNEL32.@)
445  *
446  * WARNING: there's a Windows incompatibility lurking here !
447  * Win32s always includes the full path of the program file,
448  * whereas Windows NT only returns the full file path plus arguments
449  * in case the program has been started with a full path.
450  * Win9x seems to have inherited NT behaviour.
451  *
452  * Note that both Start Menu Execute and Explorer start programs with
453  * fully specified quoted app file paths, which is why probably the only case
454  * where you'll see single file names is in case of direct launch
455  * via CreateProcess or WinExec.
456  *
457  * Perhaps we should take care of Win3.1 programs here (Win32s "feature").
458  *
459  * References: MS KB article q102762.txt (special Win32s handling)
460  */
461 LPSTR WINAPI GetCommandLineA(void)
462 {
463     return current_envdb.cmd_line;
464 }
465
466 /***********************************************************************
467  *           GetCommandLineW      (KERNEL32.@)
468  */
469 LPWSTR WINAPI GetCommandLineW(void)
470 {
471     return cmdlineW;
472 }
473
474
475 /***********************************************************************
476  *           GetEnvironmentStrings    (KERNEL32.@)
477  *           GetEnvironmentStringsA   (KERNEL32.@)
478  */
479 LPSTR WINAPI GetEnvironmentStringsA(void)
480 {
481     return current_envdb.env;
482 }
483
484
485 /***********************************************************************
486  *           GetEnvironmentStringsW   (KERNEL32.@)
487  */
488 LPWSTR WINAPI GetEnvironmentStringsW(void)
489 {
490     INT size;
491     LPWSTR ret;
492
493     RtlAcquirePebLock();
494     size = HeapSize( GetProcessHeap(), 0, current_envdb.env );
495     if ((ret = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) )) != NULL)
496     {
497         LPSTR pA = current_envdb.env;
498         LPWSTR pW = ret;
499         while (size--) *pW++ = (WCHAR)(BYTE)*pA++;
500     }
501     RtlReleasePebLock();
502     return ret;
503 }
504
505
506 /***********************************************************************
507  *           FreeEnvironmentStringsA   (KERNEL32.@)
508  */
509 BOOL WINAPI FreeEnvironmentStringsA( LPSTR ptr )
510 {
511     if (ptr != current_envdb.env)
512     {
513         SetLastError( ERROR_INVALID_PARAMETER );
514         return FALSE;
515     }
516     return TRUE;
517 }
518
519
520 /***********************************************************************
521  *           FreeEnvironmentStringsW   (KERNEL32.@)
522  */
523 BOOL WINAPI FreeEnvironmentStringsW( LPWSTR ptr )
524 {
525     return HeapFree( GetProcessHeap(), 0, ptr );
526 }
527
528
529 /***********************************************************************
530  *           GetEnvironmentVariableA   (KERNEL32.@)
531  */
532 DWORD WINAPI GetEnvironmentVariableA( LPCSTR name, LPSTR value, DWORD size )
533 {
534     LPCSTR p;
535     INT ret = 0;
536
537     if (!name || !*name)
538     {
539         SetLastError( ERROR_INVALID_PARAMETER );
540         return 0;
541     }
542     RtlAcquirePebLock();
543     if ((p = ENV_FindVariable( current_envdb.env, name, strlen(name) )))
544     {
545         ret = strlen(p);
546         if (size <= ret)
547         {
548             /* If not enough room, include the terminating null
549              * in the returned size and return an empty string */
550             ret++;
551             if (value) *value = '\0';
552         }
553         else if (value) strcpy( value, p );
554     }
555     RtlReleasePebLock();
556     if (!ret)
557         SetLastError( ERROR_ENVVAR_NOT_FOUND );
558     return ret;
559 }
560
561
562 /***********************************************************************
563  *           GetEnvironmentVariableW   (KERNEL32.@)
564  */
565 DWORD WINAPI GetEnvironmentVariableW( LPCWSTR nameW, LPWSTR valW, DWORD size)
566 {
567     LPSTR name = HEAP_strdupWtoA( GetProcessHeap(), 0, nameW );
568     LPSTR val  = valW ? HeapAlloc( GetProcessHeap(), 0, size ) : NULL;
569     DWORD res  = GetEnvironmentVariableA( name, val, size );
570     HeapFree( GetProcessHeap(), 0, name );
571     if (val)
572     {
573         if (size > 0 && !MultiByteToWideChar( CP_ACP, 0, val, -1, valW, size ))
574             valW[size-1] = 0;
575         HeapFree( GetProcessHeap(), 0, val );
576     }
577     return res;
578 }
579
580
581 /***********************************************************************
582  *           SetEnvironmentVariableA   (KERNEL32.@)
583  */
584 BOOL WINAPI SetEnvironmentVariableA( LPCSTR name, LPCSTR value )
585 {
586     INT old_size, len, res;
587     LPSTR p, env, new_env;
588     BOOL ret = FALSE;
589
590     RtlAcquirePebLock();
591     env = p = current_envdb.env;
592
593     /* Find a place to insert the string */
594
595     res = -1;
596     len = strlen(name);
597     while (*p)
598     {
599         if (!strncasecmp( name, p, len ) && (p[len] == '=')) break;
600         p += strlen(p) + 1;
601     }
602     if (!value && !*p) goto done;  /* Value to remove doesn't exist */
603
604     /* Realloc the buffer */
605
606     len = value ? strlen(name) + strlen(value) + 2 : 0;
607     if (*p) len -= strlen(p) + 1;  /* The name already exists */
608     old_size = HeapSize( GetProcessHeap(), 0, env );
609     if (len < 0)
610     {
611         LPSTR next = p + strlen(p) + 1;  /* We know there is a next one */
612         memmove( next + len, next, old_size - (next - env) );
613     }
614     if (!(new_env = HeapReAlloc( GetProcessHeap(), 0, env, old_size + len )))
615         goto done;
616     if (env_sel) env_sel = SELECTOR_ReallocBlock( env_sel, new_env, old_size + len );
617     p = new_env + (p - env);
618     if (len > 0) memmove( p + len, p, old_size - (p - new_env) );
619
620     /* Set the new string */
621
622     if (value)
623     {
624         strcpy( p, name );
625         strcat( p, "=" );
626         strcat( p, value );
627     }
628     current_envdb.env = new_env;
629     ret = TRUE;
630
631 done:
632     RtlReleasePebLock();
633     return ret;
634 }
635
636
637 /***********************************************************************
638  *           SetEnvironmentVariableW   (KERNEL32.@)
639  */
640 BOOL WINAPI SetEnvironmentVariableW( LPCWSTR name, LPCWSTR value )
641 {
642     LPSTR nameA  = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
643     LPSTR valueA = HEAP_strdupWtoA( GetProcessHeap(), 0, value );
644     BOOL ret = SetEnvironmentVariableA( nameA, valueA );
645     HeapFree( GetProcessHeap(), 0, nameA );
646     HeapFree( GetProcessHeap(), 0, valueA );
647     return ret;
648 }
649
650
651 /***********************************************************************
652  *           ExpandEnvironmentStringsA   (KERNEL32.@)
653  *
654  * Note: overlapping buffers are not supported; this is how it should be.
655  */
656 DWORD WINAPI ExpandEnvironmentStringsA( LPCSTR src, LPSTR dst, DWORD count )
657 {
658     DWORD len, total_size = 1;  /* 1 for terminating '\0' */
659     LPCSTR p, var;
660
661     if (!count) dst = NULL;
662     RtlAcquirePebLock();
663
664     while (*src)
665     {
666         if (*src != '%')
667         {
668             if ((p = strchr( src, '%' ))) len = p - src;
669             else len = strlen(src);
670             var = src;
671             src += len;
672         }
673         else  /* we are at the start of a variable */
674         {
675             if ((p = strchr( src + 1, '%' )))
676             {
677                 len = p - src - 1;  /* Length of the variable name */
678                 if ((var = ENV_FindVariable( current_envdb.env, src + 1, len )))
679                 {
680                     src += len + 2;  /* Skip the variable name */
681                     len = strlen(var);
682                 }
683                 else
684                 {
685                     var = src;  /* Copy original name instead */
686                     len += 2;
687                     src += len;
688                 }
689             }
690             else  /* unfinished variable name, ignore it */
691             {
692                 var = src;
693                 len = strlen(src);  /* Copy whole string */
694                 src += len;
695             }
696         }
697         total_size += len;
698         if (dst)
699         {
700             if (count < len) len = count;
701             memcpy( dst, var, len );
702             dst += len;
703             count -= len;
704         }
705     }
706     RtlReleasePebLock();
707
708     /* Null-terminate the string */
709     if (dst)
710     {
711         if (!count) dst--;
712         *dst = '\0';
713     }
714     return total_size;
715 }
716
717
718 /***********************************************************************
719  *           ExpandEnvironmentStringsW   (KERNEL32.@)
720  */
721 DWORD WINAPI ExpandEnvironmentStringsW( LPCWSTR src, LPWSTR dst, DWORD len )
722 {
723     LPSTR srcA = HEAP_strdupWtoA( GetProcessHeap(), 0, src );
724     LPSTR dstA = dst ? HeapAlloc( GetProcessHeap(), 0, len ) : NULL;
725     DWORD ret  = ExpandEnvironmentStringsA( srcA, dstA, len );
726     if (dstA)
727     {
728         ret = MultiByteToWideChar( CP_ACP, 0, dstA, -1, dst, len );
729         HeapFree( GetProcessHeap(), 0, dstA );
730     }
731     HeapFree( GetProcessHeap(), 0, srcA );
732     return ret;
733 }
734
735
736 /***********************************************************************
737  *           GetDOSEnvironment     (KERNEL.131)
738  *           GetDOSEnvironment16   (KERNEL32.@)
739  */
740 SEGPTR WINAPI GetDOSEnvironment16(void)
741 {
742     return MAKESEGPTR( env_sel, 0 );
743 }
744
745
746 /***********************************************************************
747  *           GetStdHandle    (KERNEL32.@)
748  */
749 HANDLE WINAPI GetStdHandle( DWORD std_handle )
750 {
751     switch(std_handle)
752     {
753         case STD_INPUT_HANDLE:  return current_envdb.hStdin;
754         case STD_OUTPUT_HANDLE: return current_envdb.hStdout;
755         case STD_ERROR_HANDLE:  return current_envdb.hStderr;
756     }
757     SetLastError( ERROR_INVALID_PARAMETER );
758     return INVALID_HANDLE_VALUE;
759 }
760
761
762 /***********************************************************************
763  *           SetStdHandle    (KERNEL32.@)
764  */
765 BOOL WINAPI SetStdHandle( DWORD std_handle, HANDLE handle )
766 {
767     switch(std_handle)
768     {
769         case STD_INPUT_HANDLE:  current_envdb.hStdin = handle;  return TRUE;
770         case STD_OUTPUT_HANDLE: current_envdb.hStdout = handle; return TRUE;
771         case STD_ERROR_HANDLE:  current_envdb.hStderr = handle; return TRUE;
772     }
773     SetLastError( ERROR_INVALID_PARAMETER );
774     return FALSE;
775 }
776
777
778 /***********************************************************************
779  *              GetStartupInfoA         (KERNEL32.@)
780  */
781 VOID WINAPI GetStartupInfoA( LPSTARTUPINFOA info )
782 {
783     *info = current_startupinfo;
784 }
785
786
787 /***********************************************************************
788  *              GetStartupInfoW         (KERNEL32.@)
789  */
790 VOID WINAPI GetStartupInfoW( LPSTARTUPINFOW info )
791 {
792     info->cb              = sizeof(STARTUPINFOW);
793     info->dwX             = current_startupinfo.dwX;
794     info->dwY             = current_startupinfo.dwY;
795     info->dwXSize         = current_startupinfo.dwXSize;
796     info->dwXCountChars   = current_startupinfo.dwXCountChars;
797     info->dwYCountChars   = current_startupinfo.dwYCountChars;
798     info->dwFillAttribute = current_startupinfo.dwFillAttribute;
799     info->dwFlags         = current_startupinfo.dwFlags;
800     info->wShowWindow     = current_startupinfo.wShowWindow;
801     info->cbReserved2     = current_startupinfo.cbReserved2;
802     info->lpReserved2     = current_startupinfo.lpReserved2;
803     info->hStdInput       = current_startupinfo.hStdInput;
804     info->hStdOutput      = current_startupinfo.hStdOutput;
805     info->hStdError       = current_startupinfo.hStdError;
806     info->lpReserved = HEAP_strdupAtoW (GetProcessHeap(), 0, current_startupinfo.lpReserved );
807     info->lpDesktop  = HEAP_strdupAtoW (GetProcessHeap(), 0, current_startupinfo.lpDesktop );
808     info->lpTitle    = HEAP_strdupAtoW (GetProcessHeap(), 0, current_startupinfo.lpTitle );
809 }