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