ntdll/kernel32: #25

Eric Pouech pouech-eric at wanadoo.fr
Sun May 18 14:04:34 CDT 2003


this patch revisits the management of process parameters
mainly:
- it fixes a couple of bugs in ntdll environment functions (one in 
trace, the other one in environment variable expansion)
- the process parameters, when passed thru wineserver, are now fully 
handled in ntdll. they are stored in the PROCESS_PARAMETERS structure.
- later on in process creation, those parameters are copied into 
STARTUPINFO shadow structure
- later modification to those paramters are now reflected to the 
PROCESS_PARAMETER structure (and STARTUPINFO is kept untouched) (for 
example, StdHandle setting) (Win 2k behaves like this)
- ENVDB handling has been moved to scheduler/process.c (it had merely 
nothing to do with environment)
- command line inheritance (from unix command line) is now purely in ntdll
- renamed RTL_USER_PROCESS_PARAMETERS into PROCESS_PARAMETERS (most of 
internet sources of information use the later form)

bad side effects & open question:
- we somehow no longer really use the ENVDB structure, so I really 
wonder if we should keep it around (are there really win32 programs 
relying on it ??)
- access to PROCESS_PARAMETERS from kernel32: currently this is done by 
exporting a function pointer from ntdll, but this is not a good solution 
IMO. Should we better: 1/ create a real PEB and store it at its real 
offset. 2/ store the pointer to PROCESS_PARAMETER in the PDB (even if it 
doesn't exist in PDB but in PEB) at an unused offset (I don't think it's 
really doable to merge PDB and PEB, or we'll have to trash PDB.exit_code)
- we no longer can provide a decent access to 16 bit environment block: 
mainly because the environment reference block is stored as unicode. 
Current code recreates a new 16 bit environment each time the 
environment is modified. However, this suffers from several issues: 1/ 
it's rather costly, 2/ it won't work when environment is modified thru 
ntdll APIs 3/ it won't work if the app modifies its own 16 bit 
environment. I wonder if current code is sufficient or not.

good side effects:
- fixed a bug in process creation when passing handles to the child, but 
asking the child to be detached from the console. in this case the 
handles weren't correctly passed. this is now fixed
- silenced a valgrind warning in process creation
- a few more kernel32 environment tests now pass

the patch also includes some modification to the environment tests 
(ntdll & kernel)

A+
-- 
Eric Pouech
-------------- next part --------------
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel24/kernel_main.c dlls/kernel/kernel_main.c
--- dlls/kernel24/kernel_main.c	2003-05-13 19:29:57.000000000 +0200
+++ dlls/kernel/kernel_main.c	2003-05-18 15:35:19.000000000 +0200
@@ -168,6 +168,14 @@
         if (RtlImageNtHeader(mod)->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)
             AllocConsole();
     }
+    else if (!(main_create_flags & DETACHED_PROCESS))
+    {
+        /* 1/ shall inherit console + handles
+         * 2/ shall create std handles, if handles are not inherited
+         * TBD when not using wineserver handles for console handles
+         */
+    }
+
     if (main_create_flags & CREATE_NEW_PROCESS_GROUP)
         SetConsoleCtrlHandler(NULL, TRUE);
 
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/kernel24/tests/environ.c dlls/kernel/tests/environ.c
--- dlls/kernel24/tests/environ.c	2002-12-19 22:06:52.000000000 +0100
+++ dlls/kernel/tests/environ.c	2003-05-16 22:41:03.000000000 +0200
@@ -141,9 +141,8 @@
 
     lstrcpyW(buf, fooW);
     ret_size = GetEnvironmentVariableW(name, buf, lstrlenW(value));
-    todo_wine {
-        ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer");
-    };
+    ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer");
+
     ok(ret_size == lstrlenW(value) + 1,
        "should return length with terminating 0 ret_size=%ld", ret_size);
 
@@ -190,10 +189,7 @@
     ok(ret_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND,
        "should not find variable but ret_size=%ld GetLastError=%ld",
        ret_size, GetLastError());
-
-    todo_wine {
-        ok(lstrcmpW(buf, empty_strW) == 0, "should copy an empty string");
-    };
+    ok(lstrcmpW(buf, empty_strW) == 0, "should copy an empty string");
 
     /* Test the limits */
     ret_size = GetEnvironmentVariableW(NULL, NULL, 0);
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/ntdll24/env.c dlls/ntdll/env.c
--- dlls/ntdll24/env.c	2003-05-13 19:30:01.000000000 +0200
+++ dlls/ntdll/env.c	2003-05-18 18:37:01.000000000 +0200
@@ -130,6 +130,7 @@
     if (var != NULL)
     {
         value->Length = strlenW(var) * sizeof(WCHAR);
+
         if (value->Length <= value->MaximumLength)
         {
             memmove(value->Buffer, var, min(value->Length + sizeof(WCHAR), value->MaximumLength));
@@ -171,7 +172,9 @@
     NTSTATUS    nts = STATUS_VARIABLE_NOT_FOUND;
     MEMORY_BASIC_INFORMATION mbi;
 
-    TRACE("(%p,%s,%s): stub!\n", penv, debugstr_w(name->Buffer), debugstr_w(value->Buffer));
+    TRACE("(%p,%s,%s)\n", 
+          penv, debugstr_w(name->Buffer), 
+          value ? debugstr_w(value->Buffer) : "--nil--");
 
     if (!name || !name->Buffer || !name->Buffer[0])
         return STATUS_INVALID_PARAMETER_1;
@@ -245,7 +248,6 @@
         strcatW(p, equalW);
         strcatW(p, value->Buffer);
     }
-
 done:
     if (!penv) RtlReleasePebLock();
 
@@ -256,18 +258,23 @@
  *		RtlExpandEnvironmentStrings_U (NTDLL.@)
  *
  */
-NTSTATUS WINAPI RtlExpandEnvironmentStrings_U(PWSTR env, const UNICODE_STRING* us_src,
+NTSTATUS WINAPI RtlExpandEnvironmentStrings_U(PWSTR renv, const UNICODE_STRING* us_src,
                                               PUNICODE_STRING us_dst, PULONG plen)
 {
     DWORD       len, count, total_size = 1;  /* 1 for terminating '\0' */
-    LPCWSTR     src, p, var;
+    LPCWSTR     env, src, p, var;
     LPWSTR      dst;
 
     src = us_src->Buffer;
     count = us_dst->MaximumLength / sizeof(WCHAR);
     dst = count ? us_dst->Buffer : NULL;
 
-    RtlAcquirePebLock();
+    if (!renv)
+    {
+        RtlAcquirePebLock();
+        env = ntdll_get_process_pmts()->Environment;
+    }
+    else env = renv;
 
     while (*src)
     {
@@ -312,12 +319,12 @@
         }
     }
 
-    RtlReleasePebLock();
+    if (!renv) RtlReleasePebLock();
 
     /* Null-terminate the string */
     if (dst && count) *dst = '\0';
 
-    us_dst->Length = (dst) ? (dst - us_dst->Buffer) * sizeof(WCHAR): 0;
+    us_dst->Length = (dst) ? (dst - us_dst->Buffer) * sizeof(WCHAR) : 0;
     if (plen) *plen = total_size * sizeof(WCHAR);
 
     return (count) ? STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL;
@@ -328,7 +335,7 @@
  *
  * Build the Win32 environment from the Unix environment
  */
-BOOL build_initial_environment(void)
+static NTSTATUS build_initial_environment(void)
 {
     extern char **environ;
     LPSTR*      e, te;
@@ -349,9 +356,10 @@
     /* Now allocate the environment */
     nts = NtAllocateVirtualMemory(NtCurrentProcess(), (void**)&p, 0, &size, 
                                   MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
-    if (nts != STATUS_SUCCESS) return FALSE;
+    if (nts != STATUS_SUCCESS) return nts;
 
     ntdll_get_process_pmts()->Environment = p;
+
     /* And fill it with the Unix environment */
     for (e = environ; *e; e++)
     {
@@ -365,5 +373,264 @@
     }
     *p = 0;
 
+    return STATUS_SUCCESS;
+}
+
+static void init_unicode( UNICODE_STRING* us, const char** src, size_t len)
+{
+    if (len)
+    {
+        STRING ansi;
+        ansi.Buffer = (char*)*src;
+        ansi.Length = len;
+        ansi.MaximumLength = len;       
+        /* FIXME: should check value returned */
+        RtlAnsiStringToUnicodeString( us, &ansi, TRUE );
+        *src += len;
+    }
+}
+
+/***********************************************************************
+ *           init_user_process_pmts
+ *
+ * Fill the RTL_USER_PROCESS_ARAMETERS structure from the server.
+ */
+BOOL init_user_process_pmts( size_t info_size, char *main_exe_name, size_t main_exe_size )
+{
+    startup_info_t info;
+    void *data;
+    const char *src;
+    size_t len;
+    PROCESS_PARAMETERS *rupp;
+
+    if (build_initial_environment() != STATUS_SUCCESS) return FALSE;
+    if (!info_size) return TRUE;
+    if (!(data = RtlAllocateHeap( ntdll_get_process_heap(), 0, info_size )))
+        return FALSE;
+
+    SERVER_START_REQ( get_startup_info )
+    {
+        wine_server_set_reply( req, data, info_size );
+        wine_server_call( req );
+        info_size = wine_server_reply_size( reply );
+    }
+    SERVER_END_REQ;
+
+    if (info_size < sizeof(info.size)) goto done;
+    len = min( info_size, ((startup_info_t *)data)->size );
+    memset( &info, 0, sizeof(info) );
+    memcpy( &info, data, len );
+    src = (char *)data + len;
+    info_size -= len;
+
+    /* fixup the lengths */
+    if (info.filename_len > info_size) info.filename_len = info_size;
+    info_size -= info.filename_len;
+    if (info.cmdline_len > info_size) info.cmdline_len = info_size;
+    info_size -= info.cmdline_len;
+    if (info.desktop_len > info_size) info.desktop_len = info_size;
+    info_size -= info.desktop_len;
+    if (info.title_len > info_size) info.title_len = info_size;
+
+    RtlAcquirePebLock();
+
+    rupp = ntdll_get_process_pmts();
+
+    /* store the filename */
+    len = min( info.filename_len, main_exe_size-1 );
+    memcpy( main_exe_name, src, len );
+    main_exe_name[len] = 0;
+
+    init_unicode( &rupp->ImagePathName, &src, info.filename_len );
+    init_unicode( &rupp->CommandLine, &src, info.cmdline_len );
+    init_unicode( &rupp->Desktop, &src, info.desktop_len );
+    init_unicode( &rupp->WindowTitle, &src, info.title_len );
+
+    rupp->dwX             = info.x;
+    rupp->dwY             = info.y;
+    rupp->dwXSize         = info.cx;
+    rupp->dwYSize         = info.cy;
+    rupp->dwXCountChars   = info.x_chars;
+    rupp->dwYCountChars   = info.y_chars;
+    rupp->dwFillAttribute = info.attribute;
+    rupp->wShowWindow     = info.cmd_show;
+    rupp->dwFlags         = info.flags;
+
+    RtlReleasePebLock();
+
+ done:
+    RtlFreeHeap( ntdll_get_process_heap(), 0, data );
     return TRUE;
 }
+
+/***********************************************************************
+ *              set_library_argv
+ *
+ * Set the Wine library argc/argv global variables.
+ */
+static void set_library_argv( char **argv )
+{
+    int argc;
+    WCHAR *p;
+    WCHAR **wargv;
+    DWORD total = 0, len, reslen;
+
+    for (argc = 0; argv[argc]; argc++)
+    {
+        len = strlen(argv[argc]) + 1;
+        RtlMultiByteToUnicodeN(NULL, 0, &reslen, argv[argc], len);
+        total += reslen;
+    }
+    wargv = RtlAllocateHeap( ntdll_get_process_heap(), 0,
+                             total + (argc + 1) * sizeof(*wargv) );
+    p = (WCHAR *)(wargv + argc + 1);
+    for (argc = 0; argv[argc]; argc++)
+    {
+        len = strlen(argv[argc]) + 1;
+        RtlMultiByteToUnicodeN(p, total, &reslen, argv[argc], len);
+        wargv[argc] = p;
+        p += reslen / sizeof(WCHAR);
+        total -= reslen;
+    }
+    wargv[argc] = NULL;
+
+    __wine_main_argc  = argc;
+    __wine_main_argv  = argv;
+    __wine_main_wargv = wargv;
+}
+
+/***********************************************************************
+ *           build_command_line
+ *
+ * Build the command line of a process from the argv array.
+ *
+ * Note that it does NOT necessarily include the file name.
+ * Sometimes we don't even have any command line options at all.
+ *
+ * We must quote and escape characters so that the argv array can be rebuilt
+ * from the command line:
+ * - spaces and tabs must be quoted
+ *   'a b'   -> '"a b"'
+ * - quotes must be escaped
+ *   '"'     -> '\"'
+ * - if '\'s are followed by a '"', they must be doubled and followed by '\"',
+ *   resulting in an odd number of '\' followed by a '"'
+ *   '\"'    -> '\\\"'
+ *   '\\"'   -> '\\\\\"'
+ * - '\'s that are not followed by a '"' can be left as is
+ *   'a\b'   == 'a\b'
+ *   'a\\b'  == 'a\\b'
+ */
+BOOL build_command_line( char **argv )
+{
+    int len;
+    char **arg;
+    LPWSTR p;
+    PROCESS_PARAMETERS* rupp;
+
+    set_library_argv( argv );
+
+    rupp = ntdll_get_process_pmts();
+    if (rupp->CommandLine.Buffer) return TRUE; /* already got it from the server */
+
+    len = 0;
+    for (arg = argv; *arg; arg++)
+    {
+        int has_space,bcount;
+        char* a;
+
+        has_space=0;
+        bcount=0;
+        a=*arg;
+        while (*a!='\0') {
+            if (*a=='\\') {
+                bcount++;
+            } else {
+                if (*a==' ' || *a=='\t') {
+                    has_space=1;
+                } else if (*a=='"') {
+                    /* doubling of '\' preceeding a '"',
+                     * plus escaping of said '"'
+                     */
+                    len+=2*bcount+1;
+                }
+                bcount=0;
+            }
+            a++;
+        }
+        len+=(a-*arg)+1 /* for the separating space */;
+        if (has_space)
+            len+=2; /* for the quotes */
+    }
+
+    if (!(rupp->CommandLine.Buffer = RtlAllocateHeap( ntdll_get_process_heap(), 0, len * sizeof(WCHAR))))
+        return FALSE;
+
+    p = rupp->CommandLine.Buffer;
+    rupp->CommandLine.Length = (len - 1) * sizeof(WCHAR);
+    rupp->CommandLine.MaximumLength = len * sizeof(WCHAR);
+    for (arg = argv; *arg; arg++)
+    {
+        int has_space,has_quote;
+        char* a;
+
+        /* Check for quotes and spaces in this argument */
+        has_space=has_quote=0;
+        a=*arg;
+        while (*a!='\0') {
+            if (*a==' ' || *a=='\t') {
+                has_space=1;
+                if (has_quote)
+                    break;
+            } else if (*a=='"') {
+                has_quote=1;
+                if (has_space)
+                    break;
+            }
+            a++;
+        }
+
+        /* Now transfer it to the command line */
+        if (has_space)
+            *p++='"';
+        if (has_quote) {
+            int bcount;
+            char* a;
+
+            bcount=0;
+            a=*arg;
+            while (*a!='\0') {
+                if (*a=='\\') {
+                    *p++=*a;
+                    bcount++;
+                } else {
+                    if (*a=='"') {
+                        int i;
+
+                        /* Double all the '\\' preceeding this '"', plus one */
+                        for (i=0;i<=bcount;i++)
+                            *p++='\\';
+                        *p++='"';
+                    } else {
+                        *p++=*a;
+                    }
+                    bcount=0;
+                }
+                a++;
+            }
+        } else {
+            char* x = *arg;
+            while ((*p=*x++)) p++;
+        }
+        if (has_space)
+            *p++='"';
+        *p++=' ';
+    }
+    if (p > rupp->CommandLine.Buffer)
+        p--;  /* remove last space */
+    *p = '\0';
+
+    return TRUE;
+}
+
+
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/ntdll24/ntdll_misc.h dlls/ntdll/ntdll_misc.h
--- dlls/ntdll24/ntdll_misc.h	2003-05-08 10:25:00.000000000 +0200
+++ dlls/ntdll/ntdll_misc.h	2003-05-18 11:09:07.000000000 +0200
@@ -40,59 +40,9 @@
                                      FARPROC origfun, DWORD ordinal );
 extern void RELAY_SetupDLL( const char *module );
 
-typedef struct RTL_DRIVE_LETTER_CURDIR
-{
-    USHORT              Flags;
-    USHORT              Length;
-    ULONG               TimeStamp;
-    UNICODE_STRING      DosPath;
-} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
-
-typedef struct _RTL_USER_PROCESS_PARAMETERS
-{
-    ULONG               AllocationSize;
-    ULONG               Size;
-    ULONG               Flags;
-    ULONG               DebugFlags;
-    HANDLE              hConsole;
-    ULONG               ProcessGroup;
-    HANDLE              hStdInput;
-    HANDLE              hStdOutput;
-    HANDLE              hStdError;
-    UNICODE_STRING      CurrentDirectoryName;
-    HANDLE              CurrentDirectoryHandle;
-    UNICODE_STRING      DllPath;
-    UNICODE_STRING      ImagePathName;
-    UNICODE_STRING      CommandLine;
-    PWSTR               Environment;
-    ULONG               dwX;
-    ULONG               dwY;
-    ULONG               dwXSize;
-    ULONG               dwYSize;
-    ULONG               dwXCountChars;
-    ULONG               dwYCountChars;
-    ULONG               dwFillAttribute;
-    ULONG               dwFlags;
-    ULONG               wShowWindow;
-    UNICODE_STRING      WindowTitle;
-    UNICODE_STRING      DesktopInfo;
-    UNICODE_STRING      ShellInfo;
-    UNICODE_STRING      RuntimeInfo;
-    RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20];
-} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
-
 static inline HANDLE ntdll_get_process_heap(void)
 {
     HANDLE *pdb = (HANDLE *)NtCurrentTeb()->process;
     return pdb[0x18 / sizeof(HANDLE)];  /* get dword at offset 0x18 in pdb */
 }
-
-/* FIXME: this should be part of PEB, once it's defined */
-extern RTL_USER_PROCESS_PARAMETERS process_pmts;
-BOOL build_initial_environment(void);
-
-static inline RTL_USER_PROCESS_PARAMETERS* ntdll_get_process_pmts(void)
-{
-    return &process_pmts;
-}
 #endif
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/ntdll24/ntdll.spec dlls/ntdll/ntdll.spec
--- dlls/ntdll24/ntdll.spec	2003-05-15 19:32:26.000000000 +0200
+++ dlls/ntdll/ntdll.spec	2003-05-18 11:22:14.000000000 +0200
@@ -1073,3 +1073,4 @@
 # Wine dll separation hacks, these will go away, don't use them
 #
 @ cdecl VIRTUAL_SetFaultHandler(ptr ptr ptr)
+@ cdecl ntdll_get_process_pmts()
diff -u -N -r -x '*~' -x '.#*' -x CVS dlls/ntdll24/tests/env.c dlls/ntdll/tests/env.c
--- dlls/ntdll24/tests/env.c	2003-05-13 19:30:02.000000000 +0200
+++ dlls/ntdll/tests/env.c	2003-05-16 21:57:37.000000000 +0200
@@ -48,6 +48,7 @@
                                      'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
                                      'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',0,
 			     '=','o','O','H','=','I','I','I',0,
+                             'n','u','l','=',0,
                              0};
 
 static void testQuery(void)
@@ -77,6 +78,7 @@
         {"sr", 256, STATUS_SUCCESS, "an=ouo"},
 	{"=oOH", 256, STATUS_SUCCESS, "III"},
         {"", 256, STATUS_VARIABLE_NOT_FOUND, NULL},
+        {"nul", 256, STATUS_SUCCESS, ""},
         {NULL, 0, 0, NULL}
     };
 
diff -u -N -r -x '*~' -x '.#*' -x CVS include24/winternl.h include/winternl.h
--- include24/winternl.h	2003-05-15 19:34:06.000000000 +0200
+++ include/winternl.h	2003-05-18 14:34:13.000000000 +0200
@@ -588,6 +588,47 @@
     PVOID  pDebugInfo;
 } RTL_RWLOCK, *LPRTL_RWLOCK;
 
+typedef struct RTL_DRIVE_LETTER_CURDIR
+{
+    USHORT              Flags;
+    USHORT              Length;
+    ULONG               TimeStamp;
+    UNICODE_STRING      DosPath;
+} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
+
+typedef struct _PROCESS_PARAMETERS
+{
+    ULONG               AllocationSize;
+    ULONG               Size;
+    ULONG               Flags;
+    ULONG               DebugFlags;
+    HANDLE              hConsole;
+    ULONG               ProcessGroup;
+    HANDLE              hStdInput;
+    HANDLE              hStdOutput;
+    HANDLE              hStdError;
+    UNICODE_STRING      CurrentDirectoryName;
+    HANDLE              CurrentDirectoryHandle;
+    UNICODE_STRING      DllPath;
+    UNICODE_STRING      ImagePathName;
+    UNICODE_STRING      CommandLine;
+    PWSTR               Environment;
+    ULONG               dwX;
+    ULONG               dwY;
+    ULONG               dwXSize;
+    ULONG               dwYSize;
+    ULONG               dwXCountChars;
+    ULONG               dwYCountChars;
+    ULONG               dwFillAttribute;
+    ULONG               dwFlags;
+    ULONG               wShowWindow;
+    UNICODE_STRING      WindowTitle;
+    UNICODE_STRING      Desktop;
+    UNICODE_STRING      ShellInfo;
+    UNICODE_STRING      RuntimeInfo;
+    RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20];
+} PROCESS_PARAMETERS, *PPROCESS_PARAMETERS;
+
 /* System Information Class 0x00 */
 typedef struct _SYSTEM_BASIC_INFORMATION {
 #ifdef __WINESRC__
@@ -1309,6 +1350,9 @@
 NTSTATUS WINAPI LdrUnloadDll(HMODULE);
 NTSTATUS WINAPI LdrUnlockLoaderLock(ULONG,ULONG);
 
+/* tempory hack until we come up with a real PEB */
+PROCESS_PARAMETERS*    ntdll_get_process_pmts(void);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff -u -N -r -x '*~' -x '.#*' -x CVS memory24/environ.c memory/environ.c
--- memory24/environ.c	2003-05-15 20:26:54.000000000 +0200
+++ memory/environ.c	2003-05-18 18:46:43.000000000 +0200
@@ -23,6 +23,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 
 #include "windef.h"
 #include "winerror.h"
@@ -33,27 +34,13 @@
 #include "heap.h"
 #include "winternl.h"
 #include "selectors.h"
+#include "wine/debug.h"
 
-/* Win32 process environment database */
-typedef struct _ENVDB
-{
-    LPSTR            env;              /* 00 Process environment strings */
-    DWORD            unknown1;         /* 04 Unknown */
-    LPSTR            cmd_line;         /* 08 Command line */
-    LPSTR            cur_dir;          /* 0c Current directory */
-    STARTUPINFOA    *startup_info;     /* 10 Startup information */
-    HANDLE           hStdin;           /* 14 Handle for standard input */
-    HANDLE           hStdout;          /* 18 Handle for standard output */
-    HANDLE           hStderr;          /* 1c Handle for standard error */
-    DWORD            unknown2;         /* 20 Unknown */
-    DWORD            inherit_console;  /* 24 Inherit console flag */
-    DWORD            break_type;       /* 28 Console events flag */
-    void            *break_sem;        /* 2c SetConsoleCtrlHandler semaphore */
-    void            *break_event;      /* 30 SetConsoleCtrlHandler event */
-    void            *break_thread;     /* 34 SetConsoleCtrlHandler thread */
-    void            *break_handlers;   /* 38 List of console handlers */
-} ENVDB;
+WINE_DEFAULT_DEBUG_CHANNEL(environ);
 
+/* TODO:
+ * - 16 bit environment ??? (see generate_env_block16 for the details)
+ */
 
 /* Format of an environment block:
  * ASCIIZ   string 1 (xx=yy format)
@@ -67,386 +54,61 @@
  * - contrary to Microsoft docs, the environment strings do not appear
  *   to be sorted on Win95 (although they are on NT); so we don't bother
  *   to sort them either.
+ * - on Win2K (and likely most of NT versions) the last part (WORD 1 and 
+ *   program name no longer appear in the environment block (from the 32
+ *   bit interface)
  */
 
 static const char ENV_program_name[] = "C:\\WINDOWS\\SYSTEM\\KRNL386.EXE";
 
+static STARTUPINFOW startup_infoW;
+static STARTUPINFOA startup_infoA;
+
 /* Maximum length of a Win16 environment string (including NULL) */
 #define MAX_WIN16_LEN  128
 
-STARTUPINFOA current_startupinfo =
-{
-    sizeof(STARTUPINFOA),    /* cb */
-    0,                       /* lpReserved */
-    0,                       /* lpDesktop */
-    0,                       /* lpTitle */
-    0,                       /* dwX */
-    0,                       /* dwY */
-    0,                       /* dwXSize */
-    0,                       /* dwYSize */
-    0,                       /* dwXCountChars */
-    0,                       /* dwYCountChars */
-    0,                       /* dwFillAttribute */
-    0,                       /* dwFlags */
-    0,                       /* wShowWindow */
-    0,                       /* cbReserved2 */
-    0,                       /* lpReserved2 */
-    0,                       /* hStdInput */
-    0,                       /* hStdOutput */
-    0                        /* hStdError */
-};
-
-ENVDB current_envdb =
-{
-    0,                       /* environ */
-    0,                       /* unknown1 */
-    0,                       /* cmd_line */
-    0,                       /* cur_dir */
-    &current_startupinfo,    /* startup_info */
-    0,                       /* hStdin */
-    0,                       /* hStdout */
-    0,                       /* hStderr */
-    0,                       /* unknown2 */
-    0,                       /* inherit_console */
-    0,                       /* break_type */
-    0,                       /* break_sem */
-    0,                       /* break_event */
-    0,                       /* break_thread */
-    0                        /* break_handlers */
-};
-
-
-static WCHAR *cmdlineW;  /* Unicode command line */
-static WORD env_sel;     /* selector to the environment */
-
-/***********************************************************************
- *           ENV_FindVariable
- *
- * Find a variable in the environment and return a pointer to the value.
- * Helper function for GetEnvironmentVariable and ExpandEnvironmentStrings.
- */
-static LPCSTR ENV_FindVariable( LPCSTR env, LPCSTR name, INT len )
-{
-    while (*env)
-    {
-        if (!strncasecmp( name, env, len ) && (env[len] == '='))
-            return env + len + 1;
-        env += strlen(env) + 1;
-    }
-    return NULL;
-}
-
-
-/***********************************************************************
- *           build_environment
- *
- * Build the environment for the initial process
- */
-static BOOL build_environment(void)
-{
-    extern char **environ;
-    static const WORD one = 1;
-    LPSTR p, *e;
-    int size;
-
-    /* Compute the total size of the Unix environment */
-
-    size = sizeof(BYTE) + sizeof(WORD) + sizeof(ENV_program_name);
-    for (e = environ; *e; e++)
-    {
-        if (!memcmp( *e, "PATH=", 5 )) continue;
-        size += strlen(*e) + 1;
-    }
-
-    /* Now allocate the environment */
-
-    if (!(p = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
-    current_envdb.env = p;
-    env_sel = SELECTOR_AllocBlock( p, 0x10000, WINE_LDT_FLAGS_DATA );
-
-    /* And fill it with the Unix environment */
-
-    for (e = environ; *e; e++)
-    {
-        /* skip Unix PATH and store WINEPATH as PATH */
-        if (!memcmp( *e, "PATH=", 5 )) continue;
-        if (!memcmp( *e, "WINEPATH=", 9 )) strcpy( p, *e + 4 );
-        else strcpy( p, *e );
-        p += strlen(p) + 1;
-    }
-
-    /* Now add the program name */
-
-    *p++ = 0;
-    memcpy( p, &one, sizeof(WORD) );
-    strcpy( p + sizeof(WORD), ENV_program_name );
-    return TRUE;
-}
-
-
-/***********************************************************************
- *           copy_str
- *
- * Small helper for ENV_InitStartupInfo.
- */
-inline static char *copy_str( char **dst, const char **src, size_t len )
-{
-    char *ret;
-
-    if (!len) return NULL;
-    ret = *dst;
-    memcpy( ret, *src, len );
-    ret[len] = 0;
-    *dst += len + 1;
-    *src += len;
-    return ret;
-}
-
-
-/***********************************************************************
- *           ENV_InitStartupInfo
- *
- * Fill the startup info structure from the server.
- */
-ENVDB *ENV_InitStartupInfo( size_t info_size, char *main_exe_name, size_t main_exe_size )
-{
-    startup_info_t info;
-    void *data;
-    char *dst;
-    const char *src;
-    size_t len;
-
-    if (!build_environment()) return NULL;
-    if (!info_size) return &current_envdb;  /* nothing to retrieve */
+static WORD env_sel;     /* selector to the 16 bit environment */
 
-    if (!(data = HeapAlloc( GetProcessHeap(), 0, info_size ))) return NULL;
-
-    SERVER_START_REQ( get_startup_info )
-    {
-        wine_server_set_reply( req, data, info_size );
-        wine_server_call( req );
-        info_size = wine_server_reply_size( reply );
-    }
-    SERVER_END_REQ;
-    if (info_size < sizeof(info.size)) goto done;
-    len = min( info_size, ((startup_info_t *)data)->size );
-    memset( &info, 0, sizeof(info) );
-    memcpy( &info, data, len );
-    src = (char *)data + len;
-    info_size -= len;
-
-    /* fixup the lengths */
-    if (info.filename_len > info_size) info.filename_len = info_size;
-    info_size -= info.filename_len;
-    if (info.cmdline_len > info_size) info.cmdline_len = info_size;
-    info_size -= info.cmdline_len;
-    if (info.desktop_len > info_size) info.desktop_len = info_size;
-    info_size -= info.desktop_len;
-    if (info.title_len > info_size) info.title_len = info_size;
-
-    /* store the filename */
-    if (info.filename_len)
-    {
-        len = min( info.filename_len, main_exe_size-1 );
-        memcpy( main_exe_name, src, len );
-        main_exe_name[len] = 0;
-        src += info.filename_len;
-    }
-
-    /* copy the other strings */
-    len = info.cmdline_len + info.desktop_len + info.title_len;
-    if (len && (dst = HeapAlloc( GetProcessHeap(), 0, len + 3 )))
-    {
-        current_envdb.cmd_line = copy_str( &dst, &src, info.cmdline_len );
-        current_startupinfo.lpDesktop = copy_str( &dst, &src, info.desktop_len );
-        current_startupinfo.lpTitle = copy_str( &dst, &src, info.title_len );
-    }
-
-    current_startupinfo.dwX             = info.x;
-    current_startupinfo.dwY             = info.y;
-    current_startupinfo.dwXSize         = info.cx;
-    current_startupinfo.dwYSize         = info.cy;
-    current_startupinfo.dwXCountChars   = info.x_chars;
-    current_startupinfo.dwYCountChars   = info.y_chars;
-    current_startupinfo.dwFillAttribute = info.attribute;
-    current_startupinfo.wShowWindow     = info.cmd_show;
-    current_startupinfo.dwFlags         = info.flags;
- done:
-    HeapFree( GetProcessHeap(), 0, data );
-    return &current_envdb;
-}
-
-
-
-/***********************************************************************
- *              set_library_argv
- *
- * Set the Wine library argc/argv global variables.
- */
-static void set_library_argv( char **argv )
-{
-    int argc;
-    WCHAR *p;
-    WCHAR **wargv;
-    DWORD total = 0;
-
-    for (argc = 0; argv[argc]; argc++)
-        total += MultiByteToWideChar( CP_ACP, 0, argv[argc], -1, NULL, 0 );
-
-    wargv = HeapAlloc( GetProcessHeap(), 0,
-                       total * sizeof(WCHAR) + (argc + 1) * sizeof(*wargv) );
-    p = (WCHAR *)(wargv + argc + 1);
-    for (argc = 0; argv[argc]; argc++)
-    {
-        DWORD len = MultiByteToWideChar( CP_ACP, 0, argv[argc], -1, p, total );
-        wargv[argc] = p;
-        p += len;
-        total -= len;
-    }
-    wargv[argc] = NULL;
-
-    __wine_main_argc  = argc;
-    __wine_main_argv  = argv;
-    __wine_main_wargv = wargv;
-}
-
-
-/***********************************************************************
- *           ENV_BuildCommandLine
- *
- * Build the command line of a process from the argv array.
+/******************************************************************
+ *		generate_env_block16
  *
- * Note that it does NOT necessarily include the file name.
- * Sometimes we don't even have any command line options at all.
+ * This internal function generates a suitable environment for the 16 bit
+ * subsystem and returns the value as a segmented pointer.
  *
- * We must quote and escape characters so that the argv array can be rebuilt
- * from the command line:
- * - spaces and tabs must be quoted
- *   'a b'   -> '"a b"'
- * - quotes must be escaped
- *   '"'     -> '\"'
- * - if '\'s are followed by a '"', they must be doubled and followed by '\"',
- *   resulting in an odd number of '\' followed by a '"'
- *   '\"'    -> '\\\"'
- *   '\\"'   -> '\\\\\"'
- * - '\'s that are not followed by a '"' can be left as is
- *   'a\b'   == 'a\b'
- *   'a\\b'  == 'a\\b'
- */
-BOOL ENV_BuildCommandLine( char **argv )
-{
-    int len;
-    char *p, **arg;
-
-    set_library_argv( argv );
-
-    if (current_envdb.cmd_line) goto done;  /* already got it from the server */
-
-    len = 0;
-    for (arg = argv; *arg; arg++)
-    {
-        int has_space,bcount;
-        char* a;
-
-        has_space=0;
-        bcount=0;
-        a=*arg;
-        while (*a!='\0') {
-            if (*a=='\\') {
-                bcount++;
-            } else {
-                if (*a==' ' || *a=='\t') {
-                    has_space=1;
-                } else if (*a=='"') {
-                    /* doubling of '\' preceeding a '"',
-                     * plus escaping of said '"'
-                     */
-                    len+=2*bcount+1;
-                }
-                bcount=0;
-            }
-            a++;
-        }
-        len+=(a-*arg)+1 /* for the separating space */;
-        if (has_space)
-            len+=2; /* for the quotes */
-    }
+ * FIXME: current implementation will allocate a private copy of the
+ *        environment strings, but will not follow the modifications (if any)
+ *        from the unicode env block stored in the PEB
+ *              => how should this be updated from the ntdll modifications ?
+ *                 should we use the peb->EnvironmentUpdateCount field to 
+ *                 know when environment has changed ???
+ *        we currently regenerate this block each time an environment 
+ *        variable is modified from a Win32 API call, but we'll miss all
+ *        direct access to the NTDLL APIs
+ */
+static SEGPTR generate_env_block16(void)
+{
+    static LPSTR env16;
+
+    DWORD       size, new_size;
+    WORD        one = 1;
+
+    if (env16) FreeEnvironmentStringsA( env16 );
+
+    env16 = GetEnvironmentStringsA();
+    size = HeapSize(GetProcessHeap(), 0, env16);
+    new_size = size + sizeof(WORD) + sizeof(ENV_program_name);
+    if (!(env16 = HeapReAlloc( GetProcessHeap(), 0, env16, new_size ))) return 0;
+
+    memcpy(env16 + size, &one, sizeof(one));
+    memcpy(env16 + size + sizeof(WORD), ENV_program_name, sizeof(ENV_program_name));
+    if (env_sel)
+        env_sel = SELECTOR_ReallocBlock( env_sel, env16, new_size );
+    else
+        env_sel = SELECTOR_AllocBlock( env16, 0x10000, WINE_LDT_FLAGS_DATA );
 
-    if (!(current_envdb.cmd_line = HeapAlloc( GetProcessHeap(), 0, len )))
-        return FALSE;
-
-    p = current_envdb.cmd_line;
-    for (arg = argv; *arg; arg++)
-    {
-        int has_space,has_quote;
-        char* a;
-
-        /* Check for quotes and spaces in this argument */
-        has_space=has_quote=0;
-        a=*arg;
-        while (*a!='\0') {
-            if (*a==' ' || *a=='\t') {
-                has_space=1;
-                if (has_quote)
-                    break;
-            } else if (*a=='"') {
-                has_quote=1;
-                if (has_space)
-                    break;
-            }
-            a++;
-        }
-
-        /* Now transfer it to the command line */
-        if (has_space)
-            *p++='"';
-        if (has_quote) {
-            int bcount;
-            char* a;
-
-            bcount=0;
-            a=*arg;
-            while (*a!='\0') {
-                if (*a=='\\') {
-                    *p++=*a;
-                    bcount++;
-                } else {
-                    if (*a=='"') {
-                        int i;
-
-                        /* Double all the '\\' preceeding this '"', plus one */
-                        for (i=0;i<=bcount;i++)
-                            *p++='\\';
-                        *p++='"';
-                    } else {
-                        *p++=*a;
-                    }
-                    bcount=0;
-                }
-                a++;
-            }
-        } else {
-            strcpy(p,*arg);
-            p+=strlen(*arg);
-        }
-        if (has_space)
-            *p++='"';
-        *p++=' ';
-    }
-    if (p > current_envdb.cmd_line)
-        p--;  /* remove last space */
-    *p = '\0';
-
-    /* now allocate the Unicode version */
- done:
-    len = MultiByteToWideChar( CP_ACP, 0, current_envdb.cmd_line, -1, NULL, 0 );
-    if (!(cmdlineW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
-        return FALSE;
-    MultiByteToWideChar( CP_ACP, 0, current_envdb.cmd_line, -1, cmdlineW, len );
-    return TRUE;
+    return MAKESEGPTR( env_sel, 0 );
 }
 
-
 /***********************************************************************
  *           GetCommandLineA      (KERNEL32.@)
  *
@@ -467,7 +129,18 @@
  */
 LPSTR WINAPI GetCommandLineA(void)
 {
-    return current_envdb.cmd_line;
+    static char *cmdlineA;  /* ASCII command line */
+    
+    if (!cmdlineA) /* make an ansi version if we don't have it */
+    {
+        ANSI_STRING     ansi;
+        RtlAcquirePebLock();
+
+        cmdlineA = (RtlUnicodeStringToAnsiString( &ansi, &ntdll_get_process_pmts()->CommandLine, TRUE) == STATUS_SUCCESS) ?
+            ansi.Buffer : NULL;
+        RtlReleasePebLock();
+    }
+    return cmdlineA;
 }
 
 /***********************************************************************
@@ -475,7 +148,7 @@
  */
 LPWSTR WINAPI GetCommandLineW(void)
 {
-    return cmdlineW;
+    return ntdll_get_process_pmts()->CommandLine.Buffer;
 }
 
 
@@ -485,7 +158,38 @@
  */
 LPSTR WINAPI GetEnvironmentStringsA(void)
 {
-    return current_envdb.env;
+    LPWSTR      ptrW;
+    unsigned    len, slen;
+    LPSTR       ret, ptrA;
+
+    RtlAcquirePebLock();
+
+    len = 1; 
+
+    ptrW = ntdll_get_process_pmts()->Environment;
+    while (*ptrW)
+    {
+        slen = lstrlenW(ptrW) + 1;
+        len += WideCharToMultiByte( CP_ACP, 0, ptrW, slen, NULL, 0, NULL, NULL );
+        ptrW += slen;
+    }
+
+    if ((ret = HeapAlloc( GetProcessHeap(), 0, len )) != NULL)
+    {
+        ptrW = ntdll_get_process_pmts()->Environment;
+        ptrA = ret;
+        while (*ptrW)
+        {
+            slen = lstrlenW(ptrW) + 1;
+            WideCharToMultiByte( CP_ACP, 0, ptrW, slen, ptrA, len, NULL, NULL );
+            ptrW += slen;
+            ptrA += strlen(ptrA) + 1;
+        }
+        *ptrA = 0;
+    }
+
+    RtlReleasePebLock();
+    return ret;
 }
 
 
@@ -494,19 +198,7 @@
  */
 LPWSTR WINAPI GetEnvironmentStringsW(void)
 {
-    INT size;
-    LPWSTR ret;
-
-    RtlAcquirePebLock();
-    size = HeapSize( GetProcessHeap(), 0, current_envdb.env );
-    if ((ret = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) )) != NULL)
-    {
-        LPSTR pA = current_envdb.env;
-        LPWSTR pW = ret;
-        while (size--) *pW++ = (WCHAR)(BYTE)*pA++;
-    }
-    RtlReleasePebLock();
-    return ret;
+    return ntdll_get_process_pmts()->Environment;
 }
 
 
@@ -515,12 +207,7 @@
  */
 BOOL WINAPI FreeEnvironmentStringsA( LPSTR ptr )
 {
-    if (ptr != current_envdb.env)
-    {
-        SetLastError( ERROR_INVALID_PARAMETER );
-        return FALSE;
-    }
-    return TRUE;
+    return HeapFree( GetProcessHeap(), 0, ptr );
 }
 
 
@@ -529,7 +216,7 @@
  */
 BOOL WINAPI FreeEnvironmentStringsW( LPWSTR ptr )
 {
-    return HeapFree( GetProcessHeap(), 0, ptr );
+    return TRUE;
 }
 
 
@@ -538,30 +225,36 @@
  */
 DWORD WINAPI GetEnvironmentVariableA( LPCSTR name, LPSTR value, DWORD size )
 {
-    LPCSTR p;
-    INT ret = 0;
+    UNICODE_STRING      us_name;
+    PWSTR               valueW;
+    DWORD               ret;
 
     if (!name || !*name)
     {
-        SetLastError( ERROR_ENVVAR_NOT_FOUND );
+        SetLastError(ERROR_ENVVAR_NOT_FOUND);
         return 0;
     }
 
-    RtlAcquirePebLock();
-    if ((p = ENV_FindVariable( current_envdb.env, name, strlen(name) )))
-    {
-        ret = strlen(p);
-        if (size <= ret)
-        {
-            /* If not enough room, include the terminating null
-             * in the returned size */
-            ret++;
-        }
-        else if (value) strcpy( value, p );
-    }
-    RtlReleasePebLock();
-    if (!ret)
-	SetLastError( ERROR_ENVVAR_NOT_FOUND );
+    if (!(valueW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR))))
+        return 0;
+
+    RtlCreateUnicodeStringFromAsciiz( &us_name, name );
+    SetLastError(0);
+    ret = GetEnvironmentVariableW( us_name.Buffer, valueW, size);
+    if (ret && ret < size)
+    {
+        WideCharToMultiByte( CP_ACP, 0, valueW, ret + 1, value, size, NULL, NULL );
+    }
+    /* this is needed to tell, with 0 as a return value, the difference between:
+     * - an error (GetLastError() != 0)
+     * - returning an empty string (in this case, we need to update the buffer)
+     */
+    if (ret == 0 && size && GetLastError() == 0)
+        value[0] = '\0';
+
+    RtlFreeUnicodeString( &us_name );
+    HeapFree(GetProcessHeap(), 0, valueW);
+
     return ret;
 }
 
@@ -569,28 +262,36 @@
 /***********************************************************************
  *           GetEnvironmentVariableW   (KERNEL32.@)
  */
-DWORD WINAPI GetEnvironmentVariableW( LPCWSTR nameW, LPWSTR valW, DWORD size)
+DWORD WINAPI GetEnvironmentVariableW( LPCWSTR name, LPWSTR val, DWORD size )
 {
-    LPSTR name, val;
-    DWORD ret;
+    UNICODE_STRING      us_name;
+    UNICODE_STRING      us_value;
+    NTSTATUS            status;
+    unsigned            len;
+
+    TRACE("(%s %p %lu)\n", debugstr_w(name), val, size);
 
-    if (!nameW || !*nameW)
+    if (!name || !*name)
     {
-        SetLastError( ERROR_ENVVAR_NOT_FOUND );
+        SetLastError(ERROR_ENVVAR_NOT_FOUND);
         return 0;
     }
 
-    name = HEAP_strdupWtoA( GetProcessHeap(), 0, nameW );
-    val  = valW ? HeapAlloc( GetProcessHeap(), 0, size ) : NULL;
-    ret  = GetEnvironmentVariableA( name, val, size );
-    if (ret && val)
+    RtlInitUnicodeString(&us_name, name);
+    us_value.Length = 0;
+    us_value.MaximumLength = (size ? size - 1 : 0) * sizeof(WCHAR);
+    us_value.Buffer = val;
+
+    status = RtlQueryEnvironmentVariable_U(NULL, &us_name, &us_value);
+    len = us_value.Length / sizeof(WCHAR);
+    if (status != STATUS_SUCCESS)
     {
-        if (size && !MultiByteToWideChar( CP_ACP, 0, val, -1, valW, size ))
-            valW[size-1] = 0;
+        SetLastError( RtlNtStatusToDosError(status) );
+        return (status == STATUS_BUFFER_TOO_SMALL) ? len + 1 : 0;
     }
-    HeapFree( GetProcessHeap(), 0, name );
-    if (val) HeapFree( GetProcessHeap(), 0, val );
-    return ret;
+    if (size) val[len] = '\0';
+
+    return us_value.Length / sizeof(WCHAR);
 }
 
 
@@ -599,59 +300,28 @@
  */
 BOOL WINAPI SetEnvironmentVariableA( LPCSTR name, LPCSTR value )
 {
-    INT old_size, len, res;
-    LPSTR p, env, new_env;
-    BOOL ret = FALSE;
+    UNICODE_STRING      us_name;
+    BOOL                ret;
 
-    if (!name || !*name)
+    if (!name)
     {
-        SetLastError( ERROR_INVALID_PARAMETER );
-        return FALSE;
+        SetLastError(ERROR_ENVVAR_NOT_FOUND);
+        return 0;
     }
 
-    RtlAcquirePebLock();
-    env = p = current_envdb.env;
-
-    /* Find a place to insert the string */
-
-    res = -1;
-    len = strlen(name);
-    while (*p)
+    RtlCreateUnicodeStringFromAsciiz( &us_name, name );
+    if (value)
     {
-        if (!strncasecmp( name, p, len ) && (p[len] == '=')) break;
-        p += strlen(p) + 1;
-    }
-    if (!value && !*p) goto done;  /* Value to remove doesn't exist */
-
-    /* Realloc the buffer */
+        UNICODE_STRING      us_value;
 
-    len = value ? strlen(name) + strlen(value) + 2 : 0;
-    if (*p) len -= strlen(p) + 1;  /* The name already exists */
-    old_size = HeapSize( GetProcessHeap(), 0, env );
-    if (len < 0)
-    {
-        LPSTR next = p + strlen(p) + 1;  /* We know there is a next one */
-        memmove( next + len, next, old_size - (next - env) );
+        RtlCreateUnicodeStringFromAsciiz( &us_value, value );
+        ret = SetEnvironmentVariableW( us_name.Buffer, us_value.Buffer );
+        RtlFreeUnicodeString( &us_value );
     }
-    if (!(new_env = HeapReAlloc( GetProcessHeap(), 0, env, old_size + len )))
-        goto done;
-    if (env_sel) env_sel = SELECTOR_ReallocBlock( env_sel, new_env, old_size + len );
-    p = new_env + (p - env);
-    if (len > 0) memmove( p + len, p, old_size - (p - new_env) );
-
-    /* Set the new string */
+    else ret = SetEnvironmentVariableW( us_name.Buffer, NULL );
 
-    if (value)
-    {
-        strcpy( p, name );
-        strcat( p, "=" );
-        strcat( p, value );
-    }
-    current_envdb.env = new_env;
-    ret = TRUE;
+    RtlFreeUnicodeString( &us_name );
 
-done:
-    RtlReleasePebLock();
     return ret;
 }
 
@@ -661,12 +331,36 @@
  */
 BOOL WINAPI SetEnvironmentVariableW( LPCWSTR name, LPCWSTR value )
 {
-    LPSTR nameA  = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
-    LPSTR valueA = HEAP_strdupWtoA( GetProcessHeap(), 0, value );
-    BOOL ret = SetEnvironmentVariableA( nameA, valueA );
-    HeapFree( GetProcessHeap(), 0, nameA );
-    HeapFree( GetProcessHeap(), 0, valueA );
-    return ret;
+    UNICODE_STRING      us_name;
+    NTSTATUS            status;
+
+    TRACE("(%s %s)\n", debugstr_w(name), debugstr_w(value));
+
+    if (!name)
+    {
+        SetLastError(ERROR_ENVVAR_NOT_FOUND);
+        return 0;
+    }
+
+    RtlInitUnicodeString(&us_name, name);
+    if (value)
+    {
+        UNICODE_STRING      us_value;
+
+        RtlInitUnicodeString(&us_value, value);
+        status = RtlSetEnvironmentVariable(NULL, &us_name, &us_value);
+    }
+    else status = RtlSetEnvironmentVariable(NULL, &us_name, NULL);
+
+    if (status != STATUS_SUCCESS)
+    {
+        SetLastError( RtlNtStatusToDosError(status) );
+        return FALSE;
+    }
+
+    /* FIXME: see comments in generate_env_block16 */
+    if (env_sel) generate_env_block16();
+    return TRUE;
 }
 
 
@@ -677,63 +371,25 @@
  */
 DWORD WINAPI ExpandEnvironmentStringsA( LPCSTR src, LPSTR dst, DWORD count )
 {
-    DWORD len, total_size = 1;  /* 1 for terminating '\0' */
-    LPCSTR p, var;
-
-    if (!count) dst = NULL;
-    RtlAcquirePebLock();
-
-    while (*src)
-    {
-        if (*src != '%')
-        {
-            if ((p = strchr( src, '%' ))) len = p - src;
-            else len = strlen(src);
-            var = src;
-            src += len;
-        }
-        else  /* we are at the start of a variable */
-        {
-            if ((p = strchr( src + 1, '%' )))
-            {
-                len = p - src - 1;  /* Length of the variable name */
-                if ((var = ENV_FindVariable( current_envdb.env, src + 1, len )))
-                {
-                    src += len + 2;  /* Skip the variable name */
-                    len = strlen(var);
-                }
-                else
-                {
-                    var = src;  /* Copy original name instead */
-                    len += 2;
-                    src += len;
-                }
-            }
-            else  /* unfinished variable name, ignore it */
-            {
-                var = src;
-                len = strlen(src);  /* Copy whole string */
-                src += len;
-            }
-        }
-        total_size += len;
-        if (dst)
-        {
-            if (count < len) len = count;
-            memcpy( dst, var, len );
-            dst += len;
-            count -= len;
-        }
-    }
-    RtlReleasePebLock();
+    UNICODE_STRING      us_src;
+    PWSTR               dstW = NULL;
+    DWORD               ret;
+
+    RtlCreateUnicodeStringFromAsciiz( &us_src, src );
+    if (count)
+    {
+        if (!(dstW = HeapAlloc(GetProcessHeap(), 0, count * sizeof(WCHAR))))
+            return 0;
+        ret = ExpandEnvironmentStringsW( us_src.Buffer, dstW, count);
+        if (ret)
+            WideCharToMultiByte( CP_ACP, 0, dstW, ret, dst, count, NULL, NULL );
+    }
+    else ret = ExpandEnvironmentStringsW( us_src.Buffer, NULL, 0);
+    
+    RtlFreeUnicodeString( &us_src );
+    if (dstW) HeapFree(GetProcessHeap(), 0, dstW);
 
-    /* Null-terminate the string */
-    if (dst)
-    {
-        if (!count) dst--;
-        *dst = '\0';
-    }
-    return total_size;
+    return ret;
 }
 
 
@@ -742,16 +398,29 @@
  */
 DWORD WINAPI ExpandEnvironmentStringsW( LPCWSTR src, LPWSTR dst, DWORD len )
 {
-    LPSTR srcA = HEAP_strdupWtoA( GetProcessHeap(), 0, src );
-    LPSTR dstA = dst ? HeapAlloc( GetProcessHeap(), 0, len ) : NULL;
-    DWORD ret  = ExpandEnvironmentStringsA( srcA, dstA, len );
-    if (dstA)
+    UNICODE_STRING      us_src;
+    UNICODE_STRING      us_dst;
+    NTSTATUS            status;
+    DWORD               res;
+
+    TRACE("(%s %p %lu)\n", debugstr_w(src), dst, len);
+
+    RtlInitUnicodeString(&us_src, src);
+    us_dst.Length = 0;
+    us_dst.MaximumLength = len * sizeof(WCHAR);
+    us_dst.Buffer = dst;
+
+    res = 0;
+    status = RtlExpandEnvironmentStrings_U(NULL, &us_src, &us_dst, &res);
+    res /= sizeof(WCHAR);
+    if (status != STATUS_SUCCESS)
     {
-        ret = MultiByteToWideChar( CP_ACP, 0, dstA, -1, dst, len );
-        HeapFree( GetProcessHeap(), 0, dstA );
+        SetLastError( RtlNtStatusToDosError(status) );
+        if (status != STATUS_BUFFER_TOO_SMALL) return 0;
+        if (len && dst) dst[len - 1] = '\0';
     }
-    HeapFree( GetProcessHeap(), 0, srcA );
-    return ret;
+
+    return res;
 }
 
 
@@ -760,7 +429,7 @@
  */
 SEGPTR WINAPI GetDOSEnvironment16(void)
 {
-    return MAKESEGPTR( env_sel, 0 );
+    return generate_env_block16();
 }
 
 
@@ -769,11 +438,11 @@
  */
 HANDLE WINAPI GetStdHandle( DWORD std_handle )
 {
-    switch(std_handle)
+    switch (std_handle)
     {
-        case STD_INPUT_HANDLE:  return current_envdb.hStdin;
-        case STD_OUTPUT_HANDLE: return current_envdb.hStdout;
-        case STD_ERROR_HANDLE:  return current_envdb.hStderr;
+        case STD_INPUT_HANDLE:  return ntdll_get_process_pmts()->hStdInput;
+        case STD_OUTPUT_HANDLE: return ntdll_get_process_pmts()->hStdOutput;
+        case STD_ERROR_HANDLE:  return ntdll_get_process_pmts()->hStdError;
     }
     SetLastError( ERROR_INVALID_PARAMETER );
     return INVALID_HANDLE_VALUE;
@@ -785,23 +454,23 @@
  */
 BOOL WINAPI SetStdHandle( DWORD std_handle, HANDLE handle )
 {
-    switch(std_handle)
+    switch (std_handle)
     {
-        case STD_INPUT_HANDLE:  current_envdb.hStdin = handle;  return TRUE;
-        case STD_OUTPUT_HANDLE: current_envdb.hStdout = handle; return TRUE;
-        case STD_ERROR_HANDLE:  current_envdb.hStderr = handle; return TRUE;
+        case STD_INPUT_HANDLE:  ntdll_get_process_pmts()->hStdInput = handle;  return TRUE;
+        case STD_OUTPUT_HANDLE: ntdll_get_process_pmts()->hStdOutput = handle; return TRUE;
+        case STD_ERROR_HANDLE:  ntdll_get_process_pmts()->hStdError = handle;  return TRUE;
     }
     SetLastError( ERROR_INVALID_PARAMETER );
     return FALSE;
 }
 
-
 /***********************************************************************
  *              GetStartupInfoA         (KERNEL32.@)
  */
 VOID WINAPI GetStartupInfoA( LPSTARTUPINFOA info )
 {
-    *info = current_startupinfo;
+    assert(startup_infoA.cb);
+    memcpy(info, &startup_infoA, sizeof(startup_infoA));
 }
 
 
@@ -810,25 +479,67 @@
  */
 VOID WINAPI GetStartupInfoW( LPSTARTUPINFOW info )
 {
-    UNICODE_STRING      usBuffer;
-    info->cb              = sizeof(STARTUPINFOW);
-    info->dwX             = current_startupinfo.dwX;
-    info->dwY             = current_startupinfo.dwY;
-    info->dwXSize         = current_startupinfo.dwXSize;
-    info->dwXCountChars   = current_startupinfo.dwXCountChars;
-    info->dwYCountChars   = current_startupinfo.dwYCountChars;
-    info->dwFillAttribute = current_startupinfo.dwFillAttribute;
-    info->dwFlags         = current_startupinfo.dwFlags;
-    info->wShowWindow     = current_startupinfo.wShowWindow;
-    info->cbReserved2     = current_startupinfo.cbReserved2;
-    info->lpReserved2     = current_startupinfo.lpReserved2;
-    info->hStdInput       = current_startupinfo.hStdInput;
-    info->hStdOutput      = current_startupinfo.hStdOutput;
-    info->hStdError       = current_startupinfo.hStdError;
-    RtlCreateUnicodeStringFromAsciiz (&usBuffer,current_startupinfo.lpReserved);
-    info->lpReserved = usBuffer.Buffer;
-    RtlCreateUnicodeStringFromAsciiz (&usBuffer,current_startupinfo.lpDesktop);
-    info->lpDesktop  = usBuffer.Buffer;
-    RtlCreateUnicodeStringFromAsciiz (&usBuffer,current_startupinfo.lpTitle);
-    info->lpTitle    = usBuffer.Buffer;
+    assert(startup_infoW.cb);
+    memcpy(info, &startup_infoW, sizeof(startup_infoW));
+}
+
+/******************************************************************
+ *		ENV_CopyStartupInformation (internal)
+ *
+ * Creates the STARTUPINFO information from the ntdll information
+ */
+STARTUPINFOA* ENV_CopyStartupInformation(void)
+{
+    PROCESS_PARAMETERS* rupp;
+    ANSI_STRING         ansi;
+
+    RtlAcquirePebLock();
+    
+    rupp = ntdll_get_process_pmts();
+
+    startup_infoW.cb                   = sizeof(startup_infoW);
+    startup_infoW.lpReserved           = NULL;
+    startup_infoW.lpDesktop            = rupp->Desktop.Buffer;
+    startup_infoW.lpTitle              = rupp->WindowTitle.Buffer;
+    startup_infoW.dwX                  = rupp->dwX;
+    startup_infoW.dwY                  = rupp->dwY;
+    startup_infoW.dwXSize              = rupp->dwXSize;
+    startup_infoW.dwYSize              = rupp->dwYSize;
+    startup_infoW.dwXCountChars        = rupp->dwXCountChars;
+    startup_infoW.dwYCountChars        = rupp->dwYCountChars;
+    startup_infoW.dwFillAttribute      = rupp->dwFillAttribute;
+    startup_infoW.dwFlags              = rupp->dwFlags;
+    startup_infoW.wShowWindow          = rupp->wShowWindow;
+    startup_infoW.cbReserved2          = 0;
+    startup_infoW.lpReserved2          = NULL;
+    startup_infoW.hStdInput            = rupp->hStdInput;
+    startup_infoW.hStdOutput           = rupp->hStdOutput;
+    startup_infoW.hStdError            = rupp->hStdError;
+
+    startup_infoA.cb                   = sizeof(startup_infoW);
+    startup_infoA.lpReserved           = NULL;
+    startup_infoA.lpDesktop = (rupp->Desktop.Length &&
+                               RtlUnicodeStringToAnsiString( &ansi, &rupp->Desktop, TRUE) == STATUS_SUCCESS) ?
+        ansi.Buffer : NULL;
+    startup_infoA.lpTitle = (rupp->WindowTitle.Length &&
+                             RtlUnicodeStringToAnsiString( &ansi, &rupp->WindowTitle, TRUE) == STATUS_SUCCESS) ?
+        ansi.Buffer : NULL;
+    startup_infoA.dwX                  = rupp->dwX;
+    startup_infoA.dwY                  = rupp->dwY;
+    startup_infoA.dwXSize              = rupp->dwXSize;
+    startup_infoA.dwYSize              = rupp->dwYSize;
+    startup_infoA.dwXCountChars        = rupp->dwXCountChars;
+    startup_infoA.dwYCountChars        = rupp->dwYCountChars;
+    startup_infoA.dwFillAttribute      = rupp->dwFillAttribute;
+    startup_infoA.dwFlags              = rupp->dwFlags;
+    startup_infoA.wShowWindow          = rupp->wShowWindow;
+    startup_infoA.cbReserved2          = 0;
+    startup_infoA.lpReserved2          = NULL;
+    startup_infoA.hStdInput            = rupp->hStdInput;
+    startup_infoA.hStdOutput           = rupp->hStdOutput;
+    startup_infoA.hStdError            = rupp->hStdError;
+
+    RtlReleasePebLock();
+
+    return &startup_infoA;
 }
diff -u -N -r -x '*~' -x '.#*' -x CVS scheduler24/process.c scheduler/process.c
--- scheduler24/process.c	2003-05-17 11:29:13.000000000 +0200
+++ scheduler/process.c	2003-05-18 17:59:21.000000000 +0200
@@ -53,7 +53,25 @@
 WINE_DECLARE_DEBUG_CHANNEL(snoop);
 WINE_DECLARE_DEBUG_CHANNEL(win32);
 
-struct _ENVDB;
+/* Win32 process environment database */
+typedef struct _ENVDB
+{
+    LPSTR            env;              /* 00 Process environment strings */
+    DWORD            unknown1;         /* 04 Unknown */
+    LPSTR            cmd_line;         /* 08 Command line */
+    LPSTR            cur_dir;          /* 0c Current directory */
+    STARTUPINFOA    *startup_info;     /* 10 Startup information */
+    HANDLE           hStdin;           /* 14 Handle for standard input */
+    HANDLE           hStdout;          /* 18 Handle for standard output */
+    HANDLE           hStderr;          /* 1c Handle for standard error */
+    DWORD            unknown2;         /* 20 Unknown */
+    DWORD            inherit_console;  /* 24 Inherit console flag */
+    DWORD            break_type;       /* 28 Console events flag */
+    void            *break_sem;        /* 2c SetConsoleCtrlHandler semaphore */
+    void            *break_event;      /* 30 SetConsoleCtrlHandler event */
+    void            *break_thread;     /* 34 SetConsoleCtrlHandler thread */
+    void            *break_handlers;   /* 38 List of console handlers */
+} ENVDB;
 
 /* Win32 process database */
 typedef struct _PDB
@@ -105,9 +123,15 @@
     LCID             locale;           /* c4 Locale to be queried by GetThreadLocale (NT) */
 } PDB;
 
+static ENVDB current_envdb;
+
 PDB current_process;
 
-RTL_USER_PROCESS_PARAMETERS     process_pmts;
+static PROCESS_PARAMETERS     process_pmts;
+PROCESS_PARAMETERS*    ntdll_get_process_pmts(void)
+{
+    return &process_pmts;
+}
 
 /* Process flags */
 #define PDB32_DEBUGGED      0x0001  /* Process is being debugged */
@@ -125,14 +149,15 @@
 int main_create_flags = 0;
 
 /* memory/environ.c */
-extern struct _ENVDB *ENV_InitStartupInfo( size_t info_size, char *main_exe_name,
-                                           size_t main_exe_size );
-extern BOOL ENV_BuildCommandLine( char **argv );
-extern STARTUPINFOA current_startupinfo;
+STARTUPINFOA* ENV_CopyStartupInformation(void);
 
 /* scheduler/pthread.c */
 extern void PTHREAD_init_done(void);
 
+/* dlls/ntdll/env.c */
+extern BOOL init_user_process_pmts( size_t info_size, char *main_exe_name, size_t main_exe_size );
+extern BOOL build_command_line( char **argv );
+
 extern void RELAY_InitDebugLists(void);
 extern BOOL MAIN_MainInit(void);
 extern void VERSION_Init( const char *appname );
@@ -302,6 +327,7 @@
     current_process.ring0_threads   = 1;
     current_process.group           = &current_process;
     current_process.priority        = 8;  /* Normal */
+    current_process.env_db          = &current_envdb;
 
     /* Setup the server connection */
     CLIENT_InitServer();
@@ -316,9 +342,9 @@
             main_create_flags = reply->create_flags;
             info_size         = reply->info_size;
             server_startticks = reply->server_start;
-            current_startupinfo.hStdInput   = reply->hstdin;
-            current_startupinfo.hStdOutput  = reply->hstdout;
-            current_startupinfo.hStdError   = reply->hstderr;
+            process_pmts.hStdInput   = reply->hstdin;
+            process_pmts.hStdOutput  = reply->hstdout;
+            process_pmts.hStdError   = reply->hstderr;
         }
     }
     SERVER_END_REQ;
@@ -328,41 +354,32 @@
     current_process.heap = HeapCreate( HEAP_GROWABLE, 0, 0 );
 
     if (main_create_flags == 0 &&
-	current_startupinfo.hStdInput  == 0 &&
-	current_startupinfo.hStdOutput == 0 &&
-	current_startupinfo.hStdError  == 0)
+	process_pmts.hStdInput  == 0 &&
+	process_pmts.hStdOutput == 0 &&
+	process_pmts.hStdError  == 0)
     {
-	/* no parent, and no new console requested, create a simple console with bare handles to
+	/* This is wine specific:
+         * no parent, and no new console requested, create a simple console with bare handles to
 	 * unix stdio input & output streams (aka simple console)
-	 */
-        HANDLE handle;
-        wine_server_fd_to_handle( 0, GENERIC_READ|SYNCHRONIZE, TRUE, &handle );
-        SetStdHandle( STD_INPUT_HANDLE, handle );
-        wine_server_fd_to_handle( 1, GENERIC_WRITE|SYNCHRONIZE, TRUE, &handle );
-        SetStdHandle( STD_OUTPUT_HANDLE, handle );
-        wine_server_fd_to_handle( 1, GENERIC_WRITE|SYNCHRONIZE, TRUE, &handle );
-        SetStdHandle( STD_ERROR_HANDLE, handle );
-    }
-    else if (!(main_create_flags & (DETACHED_PROCESS|CREATE_NEW_CONSOLE)))
-    {
-	SetStdHandle( STD_INPUT_HANDLE,  current_startupinfo.hStdInput  );
-	SetStdHandle( STD_OUTPUT_HANDLE, current_startupinfo.hStdOutput );
-	SetStdHandle( STD_ERROR_HANDLE,  current_startupinfo.hStdError  );
+	 */     
+        wine_server_fd_to_handle( 0, GENERIC_READ|SYNCHRONIZE,  TRUE, &process_pmts.hStdInput );
+        wine_server_fd_to_handle( 1, GENERIC_WRITE|SYNCHRONIZE, TRUE, &process_pmts.hStdOutput );
+        wine_server_fd_to_handle( 1, GENERIC_WRITE|SYNCHRONIZE, TRUE, &process_pmts.hStdError );
     }
 
     /* Now we can use the pthreads routines */
     PTHREAD_init_done();
 
     /* Copy the parent environment */
-    if (!(current_process.env_db = ENV_InitStartupInfo( info_size, main_exe_name,
-                                                        sizeof(main_exe_name) )))
+    if (!init_user_process_pmts( info_size, main_exe_name, sizeof(main_exe_name) ))
         return FALSE;
+    current_process.env_db->startup_info = ENV_CopyStartupInformation();
+    if (!current_process.env_db->startup_info) return FALSE;
 
     /* Parse command line arguments */
     OPTIONS_ParseOptions( !info_size ? argv : NULL );
 
     /* <hack: to be changed later on> */
-    build_initial_environment();
     process_pmts.CurrentDirectoryName.Length = 3 * sizeof(WCHAR);
     process_pmts.CurrentDirectoryName.MaximumLength = RtlGetLongestNtPathLength() * sizeof(WCHAR);
     process_pmts.CurrentDirectoryName.Buffer = RtlAllocateHeap( ntdll_get_process_heap(), 0, process_pmts.CurrentDirectoryName.MaximumLength);
@@ -563,7 +580,7 @@
 
  found:
     /* build command line */
-    if (!ENV_BuildCommandLine( argv )) goto error;
+    if (!build_command_line( argv )) goto error;
 
     /* create 32-bit module for main exe */
     if (!(current_process.module = BUILTIN32_LoadExeModule( current_process.module ))) goto error;
@@ -845,7 +862,7 @@
     int execfd[2];
     pid_t pid;
     int err;
-    char dummy;
+    char dummy = 0;
 
     if (!env)
     {
@@ -1389,7 +1406,8 @@
  */
 DWORD WINAPI GetProcessDword( DWORD dwProcessID, INT offset )
 {
-    DWORD x, y;
+    DWORD               x, y;
+    STARTUPINFOW        siw;
 
     TRACE_(win32)("(%ld, %d)\n", dwProcessID, offset );
 
@@ -1420,30 +1438,32 @@
         return (DWORD)&current_process;
 
     case GPD_STARTF_SHELLDATA: /* return stdoutput handle from startupinfo ??? */
-        return (DWORD)current_startupinfo.hStdOutput;
+        GetStartupInfoW(&siw);
+        return (DWORD)siw.hStdOutput;
 
     case GPD_STARTF_HOTKEY: /* return stdinput handle from startupinfo ??? */
-        return (DWORD)current_startupinfo.hStdInput;
+        GetStartupInfoW(&siw);
+        return (DWORD)siw.hStdInput;
 
     case GPD_STARTF_SHOWWINDOW:
-        return current_startupinfo.wShowWindow;
+        return process_pmts.wShowWindow;
 
     case GPD_STARTF_SIZE:
-        x = current_startupinfo.dwXSize;
+        x = process_pmts.dwXSize;
         if ( (INT)x == CW_USEDEFAULT ) x = CW_USEDEFAULT16;
-        y = current_startupinfo.dwYSize;
+        y = process_pmts.dwYSize;
         if ( (INT)y == CW_USEDEFAULT ) y = CW_USEDEFAULT16;
         return MAKELONG( x, y );
 
     case GPD_STARTF_POSITION:
-        x = current_startupinfo.dwX;
+        x = process_pmts.dwX;
         if ( (INT)x == CW_USEDEFAULT ) x = CW_USEDEFAULT16;
-        y = current_startupinfo.dwY;
+        y = process_pmts.dwY;
         if ( (INT)y == CW_USEDEFAULT ) y = CW_USEDEFAULT16;
         return MAKELONG( x, y );
 
     case GPD_STARTF_FLAGS:
-        return current_startupinfo.dwFlags;
+        return process_pmts.dwFlags;
 
     case GPD_PARENT:
         return 0;


More information about the wine-patches mailing list