Moved a couple of locale functions from ole2nls.c into locale.c.
[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 "winternl.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++)
156     {
157         if (!memcmp( *e, "PATH=", 5 )) continue;
158         size += strlen(*e) + 1;
159     }
160
161     /* Now allocate the environment */
162
163     if (!(p = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
164     current_envdb.env = p;
165     env_sel = SELECTOR_AllocBlock( p, 0x10000, WINE_LDT_FLAGS_DATA );
166
167     /* And fill it with the Unix environment */
168
169     for (e = environ; *e; e++)
170     {
171         /* skip Unix PATH and store WINEPATH as PATH */
172         if (!memcmp( *e, "PATH=", 5 )) continue;
173         if (!memcmp( *e, "WINEPATH=", 9 )) strcpy( p, *e + 4 );
174         else strcpy( p, *e );
175         p += strlen(p) + 1;
176     }
177
178     /* Now add the program name */
179
180     *p++ = 0;
181     memcpy( p, &one, sizeof(WORD) );
182     strcpy( p + sizeof(WORD), ENV_program_name );
183     return TRUE;
184 }
185
186
187 /***********************************************************************
188  *           copy_str
189  *
190  * Small helper for ENV_InitStartupInfo.
191  */
192 inline static char *copy_str( char **dst, const char **src, size_t len )
193 {
194     char *ret;
195
196     if (!len) return NULL;
197     ret = *dst;
198     memcpy( ret, *src, len );
199     ret[len] = 0;
200     *dst += len + 1;
201     *src += len;
202     return ret;
203 }
204
205
206 /***********************************************************************
207  *           ENV_InitStartupInfo
208  *
209  * Fill the startup info structure from the server.
210  */
211 ENVDB *ENV_InitStartupInfo( size_t info_size, char *main_exe_name, size_t main_exe_size )
212 {
213     startup_info_t info;
214     void *data;
215     char *dst;
216     const char *src;
217     size_t len;
218
219     if (!build_environment()) return NULL;
220     if (!info_size) return &current_envdb;  /* nothing to retrieve */
221
222     if (!(data = HeapAlloc( GetProcessHeap(), 0, info_size ))) return NULL;
223
224     SERVER_START_REQ( get_startup_info )
225     {
226         wine_server_set_reply( req, data, info_size );
227         wine_server_call( req );
228         info_size = wine_server_reply_size( reply );
229     }
230     SERVER_END_REQ;
231     if (info_size < sizeof(info.size)) goto done;
232     len = min( info_size, ((startup_info_t *)data)->size );
233     memset( &info, 0, sizeof(info) );
234     memcpy( &info, data, len );
235     src = (char *)data + len;
236     info_size -= len;
237
238     /* fixup the lengths */
239     if (info.filename_len > info_size) info.filename_len = info_size;
240     info_size -= info.filename_len;
241     if (info.cmdline_len > info_size) info.cmdline_len = info_size;
242     info_size -= info.cmdline_len;
243     if (info.desktop_len > info_size) info.desktop_len = info_size;
244     info_size -= info.desktop_len;
245     if (info.title_len > info_size) info.title_len = info_size;
246
247     /* store the filename */
248     if (info.filename_len)
249     {
250         len = min( info.filename_len, main_exe_size-1 );
251         memcpy( main_exe_name, src, len );
252         main_exe_name[len] = 0;
253         src += info.filename_len;
254     }
255
256     /* copy the other strings */
257     len = info.cmdline_len + info.desktop_len + info.title_len;
258     if (len && (dst = HeapAlloc( GetProcessHeap(), 0, len + 3 )))
259     {
260         current_envdb.cmd_line = copy_str( &dst, &src, info.cmdline_len );
261         current_startupinfo.lpDesktop = copy_str( &dst, &src, info.desktop_len );
262         current_startupinfo.lpTitle = copy_str( &dst, &src, info.title_len );
263     }
264
265     current_startupinfo.dwX             = info.x;
266     current_startupinfo.dwY             = info.y;
267     current_startupinfo.dwXSize         = info.cx;
268     current_startupinfo.dwYSize         = info.cy;
269     current_startupinfo.dwXCountChars   = info.x_chars;
270     current_startupinfo.dwYCountChars   = info.y_chars;
271     current_startupinfo.dwFillAttribute = info.attribute;
272     current_startupinfo.wShowWindow     = info.cmd_show;
273     current_startupinfo.dwFlags         = info.flags;
274  done:
275     HeapFree( GetProcessHeap(), 0, data );
276     return &current_envdb;
277 }
278
279
280
281 /***********************************************************************
282  *              set_library_argv
283  *
284  * Set the Wine library argc/argv global variables.
285  */
286 static void set_library_argv( char **argv )
287 {
288     int argc;
289     WCHAR *p;
290     WCHAR **wargv;
291     DWORD total = 0;
292
293     for (argc = 0; argv[argc]; argc++)
294         total += MultiByteToWideChar( CP_ACP, 0, argv[argc], -1, NULL, 0 );
295
296     wargv = HeapAlloc( GetProcessHeap(), 0,
297                        total * sizeof(WCHAR) + (argc + 1) * sizeof(*wargv) );
298     p = (WCHAR *)(wargv + argc + 1);
299     for (argc = 0; argv[argc]; argc++)
300     {
301         DWORD len = MultiByteToWideChar( CP_ACP, 0, argv[argc], -1, p, total );
302         wargv[argc] = p;
303         p += len;
304         total -= len;
305     }
306     wargv[argc] = NULL;
307
308     __wine_main_argc  = argc;
309     __wine_main_argv  = argv;
310     __wine_main_wargv = wargv;
311 }
312
313
314 /***********************************************************************
315  *           ENV_BuildCommandLine
316  *
317  * Build the command line of a process from the argv array.
318  *
319  * Note that it does NOT necessarily include the file name.
320  * Sometimes we don't even have any command line options at all.
321  *
322  * We must quote and escape characters so that the argv array can be rebuilt
323  * from the command line:
324  * - spaces and tabs must be quoted
325  *   'a b'   -> '"a b"'
326  * - quotes must be escaped
327  *   '"'     -> '\"'
328  * - if '\'s are followed by a '"', they must be doubled and followed by '\"',
329  *   resulting in an odd number of '\' followed by a '"'
330  *   '\"'    -> '\\\"'
331  *   '\\"'   -> '\\\\\"'
332  * - '\'s that are not followed by a '"' can be left as is
333  *   'a\b'   == 'a\b'
334  *   'a\\b'  == 'a\\b'
335  */
336 BOOL ENV_BuildCommandLine( char **argv )
337 {
338     int len;
339     char *p, **arg;
340
341     set_library_argv( argv );
342
343     if (current_envdb.cmd_line) goto done;  /* already got it from the server */
344
345     len = 0;
346     for (arg = argv; *arg; arg++)
347     {
348         int has_space,bcount;
349         char* a;
350
351         has_space=0;
352         bcount=0;
353         a=*arg;
354         while (*a!='\0') {
355             if (*a=='\\') {
356                 bcount++;
357             } else {
358                 if (*a==' ' || *a=='\t') {
359                     has_space=1;
360                 } else if (*a=='"') {
361                     /* doubling of '\' preceeding a '"',
362                      * plus escaping of said '"'
363                      */
364                     len+=2*bcount+1;
365                 }
366                 bcount=0;
367             }
368             a++;
369         }
370         len+=(a-*arg)+1 /* for the separating space */;
371         if (has_space)
372             len+=2; /* for the quotes */
373     }
374
375     if (!(current_envdb.cmd_line = HeapAlloc( GetProcessHeap(), 0, len )))
376         return FALSE;
377
378     p = current_envdb.cmd_line;
379     for (arg = argv; *arg; arg++)
380     {
381         int has_space,has_quote;
382         char* a;
383
384         /* Check for quotes and spaces in this argument */
385         has_space=has_quote=0;
386         a=*arg;
387         while (*a!='\0') {
388             if (*a==' ' || *a=='\t') {
389                 has_space=1;
390                 if (has_quote)
391                     break;
392             } else if (*a=='"') {
393                 has_quote=1;
394                 if (has_space)
395                     break;
396             }
397             a++;
398         }
399
400         /* Now transfer it to the command line */
401         if (has_space)
402             *p++='"';
403         if (has_quote) {
404             int bcount;
405             char* a;
406
407             bcount=0;
408             a=*arg;
409             while (*a!='\0') {
410                 if (*a=='\\') {
411                     *p++=*a;
412                     bcount++;
413                 } else {
414                     if (*a=='"') {
415                         int i;
416
417                         /* Double all the '\\' preceeding this '"', plus one */
418                         for (i=0;i<=bcount;i++)
419                             *p++='\\';
420                         *p++='"';
421                     } else {
422                         *p++=*a;
423                     }
424                     bcount=0;
425                 }
426                 a++;
427             }
428         } else {
429             strcpy(p,*arg);
430             p+=strlen(*arg);
431         }
432         if (has_space)
433             *p++='"';
434         *p++=' ';
435     }
436     if (p > current_envdb.cmd_line)
437         p--;  /* remove last space */
438     *p = '\0';
439
440     /* now allocate the Unicode version */
441  done:
442     len = MultiByteToWideChar( CP_ACP, 0, current_envdb.cmd_line, -1, NULL, 0 );
443     if (!(cmdlineW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
444         return FALSE;
445     MultiByteToWideChar( CP_ACP, 0, current_envdb.cmd_line, -1, cmdlineW, len );
446     return TRUE;
447 }
448
449
450 /***********************************************************************
451  *           GetCommandLineA      (KERNEL32.@)
452  *
453  * WARNING: there's a Windows incompatibility lurking here !
454  * Win32s always includes the full path of the program file,
455  * whereas Windows NT only returns the full file path plus arguments
456  * in case the program has been started with a full path.
457  * Win9x seems to have inherited NT behaviour.
458  *
459  * Note that both Start Menu Execute and Explorer start programs with
460  * fully specified quoted app file paths, which is why probably the only case
461  * where you'll see single file names is in case of direct launch
462  * via CreateProcess or WinExec.
463  *
464  * Perhaps we should take care of Win3.1 programs here (Win32s "feature").
465  *
466  * References: MS KB article q102762.txt (special Win32s handling)
467  */
468 LPSTR WINAPI GetCommandLineA(void)
469 {
470     return current_envdb.cmd_line;
471 }
472
473 /***********************************************************************
474  *           GetCommandLineW      (KERNEL32.@)
475  */
476 LPWSTR WINAPI GetCommandLineW(void)
477 {
478     return cmdlineW;
479 }
480
481
482 /***********************************************************************
483  *           GetEnvironmentStrings    (KERNEL32.@)
484  *           GetEnvironmentStringsA   (KERNEL32.@)
485  */
486 LPSTR WINAPI GetEnvironmentStringsA(void)
487 {
488     return current_envdb.env;
489 }
490
491
492 /***********************************************************************
493  *           GetEnvironmentStringsW   (KERNEL32.@)
494  */
495 LPWSTR WINAPI GetEnvironmentStringsW(void)
496 {
497     INT size;
498     LPWSTR ret;
499
500     RtlAcquirePebLock();
501     size = HeapSize( GetProcessHeap(), 0, current_envdb.env );
502     if ((ret = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) )) != NULL)
503     {
504         LPSTR pA = current_envdb.env;
505         LPWSTR pW = ret;
506         while (size--) *pW++ = (WCHAR)(BYTE)*pA++;
507     }
508     RtlReleasePebLock();
509     return ret;
510 }
511
512
513 /***********************************************************************
514  *           FreeEnvironmentStringsA   (KERNEL32.@)
515  */
516 BOOL WINAPI FreeEnvironmentStringsA( LPSTR ptr )
517 {
518     if (ptr != current_envdb.env)
519     {
520         SetLastError( ERROR_INVALID_PARAMETER );
521         return FALSE;
522     }
523     return TRUE;
524 }
525
526
527 /***********************************************************************
528  *           FreeEnvironmentStringsW   (KERNEL32.@)
529  */
530 BOOL WINAPI FreeEnvironmentStringsW( LPWSTR ptr )
531 {
532     return HeapFree( GetProcessHeap(), 0, ptr );
533 }
534
535
536 /***********************************************************************
537  *           GetEnvironmentVariableA   (KERNEL32.@)
538  */
539 DWORD WINAPI GetEnvironmentVariableA( LPCSTR name, LPSTR value, DWORD size )
540 {
541     LPCSTR p;
542     INT ret = 0;
543
544     if (!name || !*name)
545     {
546         SetLastError( ERROR_ENVVAR_NOT_FOUND );
547         return 0;
548     }
549
550     RtlAcquirePebLock();
551     if ((p = ENV_FindVariable( current_envdb.env, name, strlen(name) )))
552     {
553         ret = strlen(p);
554         if (size <= ret)
555         {
556             /* If not enough room, include the terminating null
557              * in the returned size */
558             ret++;
559         }
560         else if (value) strcpy( value, p );
561     }
562     RtlReleasePebLock();
563     if (!ret)
564         SetLastError( ERROR_ENVVAR_NOT_FOUND );
565     return ret;
566 }
567
568
569 /***********************************************************************
570  *           GetEnvironmentVariableW   (KERNEL32.@)
571  */
572 DWORD WINAPI GetEnvironmentVariableW( LPCWSTR nameW, LPWSTR valW, DWORD size)
573 {
574     LPSTR name, val;
575     DWORD ret;
576
577     if (!nameW || !*nameW)
578     {
579         SetLastError( ERROR_ENVVAR_NOT_FOUND );
580         return 0;
581     }
582
583     name = HEAP_strdupWtoA( GetProcessHeap(), 0, nameW );
584     val  = valW ? HeapAlloc( GetProcessHeap(), 0, size ) : NULL;
585     ret  = GetEnvironmentVariableA( name, val, size );
586     if (ret && val)
587     {
588         if (size && !MultiByteToWideChar( CP_ACP, 0, val, -1, valW, size ))
589             valW[size-1] = 0;
590     }
591     HeapFree( GetProcessHeap(), 0, name );
592     if (val) HeapFree( GetProcessHeap(), 0, val );
593     return ret;
594 }
595
596
597 /***********************************************************************
598  *           SetEnvironmentVariableA   (KERNEL32.@)
599  */
600 BOOL WINAPI SetEnvironmentVariableA( LPCSTR name, LPCSTR value )
601 {
602     INT old_size, len, res;
603     LPSTR p, env, new_env;
604     BOOL ret = FALSE;
605
606     if (!name || !*name)
607     {
608         SetLastError( ERROR_INVALID_PARAMETER );
609         return FALSE;
610     }
611
612     RtlAcquirePebLock();
613     env = p = current_envdb.env;
614
615     /* Find a place to insert the string */
616
617     res = -1;
618     len = strlen(name);
619     while (*p)
620     {
621         if (!strncasecmp( name, p, len ) && (p[len] == '=')) break;
622         p += strlen(p) + 1;
623     }
624     if (!value && !*p) goto done;  /* Value to remove doesn't exist */
625
626     /* Realloc the buffer */
627
628     len = value ? strlen(name) + strlen(value) + 2 : 0;
629     if (*p) len -= strlen(p) + 1;  /* The name already exists */
630     old_size = HeapSize( GetProcessHeap(), 0, env );
631     if (len < 0)
632     {
633         LPSTR next = p + strlen(p) + 1;  /* We know there is a next one */
634         memmove( next + len, next, old_size - (next - env) );
635     }
636     if (!(new_env = HeapReAlloc( GetProcessHeap(), 0, env, old_size + len )))
637         goto done;
638     if (env_sel) env_sel = SELECTOR_ReallocBlock( env_sel, new_env, old_size + len );
639     p = new_env + (p - env);
640     if (len > 0) memmove( p + len, p, old_size - (p - new_env) );
641
642     /* Set the new string */
643
644     if (value)
645     {
646         strcpy( p, name );
647         strcat( p, "=" );
648         strcat( p, value );
649     }
650     current_envdb.env = new_env;
651     ret = TRUE;
652
653 done:
654     RtlReleasePebLock();
655     return ret;
656 }
657
658
659 /***********************************************************************
660  *           SetEnvironmentVariableW   (KERNEL32.@)
661  */
662 BOOL WINAPI SetEnvironmentVariableW( LPCWSTR name, LPCWSTR value )
663 {
664     LPSTR nameA  = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
665     LPSTR valueA = HEAP_strdupWtoA( GetProcessHeap(), 0, value );
666     BOOL ret = SetEnvironmentVariableA( nameA, valueA );
667     HeapFree( GetProcessHeap(), 0, nameA );
668     HeapFree( GetProcessHeap(), 0, valueA );
669     return ret;
670 }
671
672
673 /***********************************************************************
674  *           ExpandEnvironmentStringsA   (KERNEL32.@)
675  *
676  * Note: overlapping buffers are not supported; this is how it should be.
677  */
678 DWORD WINAPI ExpandEnvironmentStringsA( LPCSTR src, LPSTR dst, DWORD count )
679 {
680     DWORD len, total_size = 1;  /* 1 for terminating '\0' */
681     LPCSTR p, var;
682
683     if (!count) dst = NULL;
684     RtlAcquirePebLock();
685
686     while (*src)
687     {
688         if (*src != '%')
689         {
690             if ((p = strchr( src, '%' ))) len = p - src;
691             else len = strlen(src);
692             var = src;
693             src += len;
694         }
695         else  /* we are at the start of a variable */
696         {
697             if ((p = strchr( src + 1, '%' )))
698             {
699                 len = p - src - 1;  /* Length of the variable name */
700                 if ((var = ENV_FindVariable( current_envdb.env, src + 1, len )))
701                 {
702                     src += len + 2;  /* Skip the variable name */
703                     len = strlen(var);
704                 }
705                 else
706                 {
707                     var = src;  /* Copy original name instead */
708                     len += 2;
709                     src += len;
710                 }
711             }
712             else  /* unfinished variable name, ignore it */
713             {
714                 var = src;
715                 len = strlen(src);  /* Copy whole string */
716                 src += len;
717             }
718         }
719         total_size += len;
720         if (dst)
721         {
722             if (count < len) len = count;
723             memcpy( dst, var, len );
724             dst += len;
725             count -= len;
726         }
727     }
728     RtlReleasePebLock();
729
730     /* Null-terminate the string */
731     if (dst)
732     {
733         if (!count) dst--;
734         *dst = '\0';
735     }
736     return total_size;
737 }
738
739
740 /***********************************************************************
741  *           ExpandEnvironmentStringsW   (KERNEL32.@)
742  */
743 DWORD WINAPI ExpandEnvironmentStringsW( LPCWSTR src, LPWSTR dst, DWORD len )
744 {
745     LPSTR srcA = HEAP_strdupWtoA( GetProcessHeap(), 0, src );
746     LPSTR dstA = dst ? HeapAlloc( GetProcessHeap(), 0, len ) : NULL;
747     DWORD ret  = ExpandEnvironmentStringsA( srcA, dstA, len );
748     if (dstA)
749     {
750         ret = MultiByteToWideChar( CP_ACP, 0, dstA, -1, dst, len );
751         HeapFree( GetProcessHeap(), 0, dstA );
752     }
753     HeapFree( GetProcessHeap(), 0, srcA );
754     return ret;
755 }
756
757
758 /***********************************************************************
759  *           GetDOSEnvironment     (KERNEL.131)
760  */
761 SEGPTR WINAPI GetDOSEnvironment16(void)
762 {
763     return MAKESEGPTR( env_sel, 0 );
764 }
765
766
767 /***********************************************************************
768  *           GetStdHandle    (KERNEL32.@)
769  */
770 HANDLE WINAPI GetStdHandle( DWORD std_handle )
771 {
772     switch(std_handle)
773     {
774         case STD_INPUT_HANDLE:  return current_envdb.hStdin;
775         case STD_OUTPUT_HANDLE: return current_envdb.hStdout;
776         case STD_ERROR_HANDLE:  return current_envdb.hStderr;
777     }
778     SetLastError( ERROR_INVALID_PARAMETER );
779     return INVALID_HANDLE_VALUE;
780 }
781
782
783 /***********************************************************************
784  *           SetStdHandle    (KERNEL32.@)
785  */
786 BOOL WINAPI SetStdHandle( DWORD std_handle, HANDLE handle )
787 {
788     switch(std_handle)
789     {
790         case STD_INPUT_HANDLE:  current_envdb.hStdin = handle;  return TRUE;
791         case STD_OUTPUT_HANDLE: current_envdb.hStdout = handle; return TRUE;
792         case STD_ERROR_HANDLE:  current_envdb.hStderr = handle; return TRUE;
793     }
794     SetLastError( ERROR_INVALID_PARAMETER );
795     return FALSE;
796 }
797
798
799 /***********************************************************************
800  *              GetStartupInfoA         (KERNEL32.@)
801  */
802 VOID WINAPI GetStartupInfoA( LPSTARTUPINFOA info )
803 {
804     *info = current_startupinfo;
805 }
806
807
808 /***********************************************************************
809  *              GetStartupInfoW         (KERNEL32.@)
810  */
811 VOID WINAPI GetStartupInfoW( LPSTARTUPINFOW info )
812 {
813     UNICODE_STRING      usBuffer;
814     info->cb              = sizeof(STARTUPINFOW);
815     info->dwX             = current_startupinfo.dwX;
816     info->dwY             = current_startupinfo.dwY;
817     info->dwXSize         = current_startupinfo.dwXSize;
818     info->dwXCountChars   = current_startupinfo.dwXCountChars;
819     info->dwYCountChars   = current_startupinfo.dwYCountChars;
820     info->dwFillAttribute = current_startupinfo.dwFillAttribute;
821     info->dwFlags         = current_startupinfo.dwFlags;
822     info->wShowWindow     = current_startupinfo.wShowWindow;
823     info->cbReserved2     = current_startupinfo.cbReserved2;
824     info->lpReserved2     = current_startupinfo.lpReserved2;
825     info->hStdInput       = current_startupinfo.hStdInput;
826     info->hStdOutput      = current_startupinfo.hStdOutput;
827     info->hStdError       = current_startupinfo.hStdError;
828     RtlCreateUnicodeStringFromAsciiz (&usBuffer,current_startupinfo.lpReserved);
829     info->lpReserved = usBuffer.Buffer;
830     RtlCreateUnicodeStringFromAsciiz (&usBuffer,current_startupinfo.lpDesktop);
831     info->lpDesktop  = usBuffer.Buffer;
832     RtlCreateUnicodeStringFromAsciiz (&usBuffer,current_startupinfo.lpTitle);
833     info->lpTitle    = usBuffer.Buffer;
834 }