Fix CreateProcess("c:\Program Files\hello.bat").
[wine] / scheduler / process.c
1 /*
2  * Win32 processes
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 <assert.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #include "wine/winbase16.h"
36 #include "wine/winuser16.h"
37 #include "wine/exception.h"
38 #include "wine/library.h"
39 #include "drive.h"
40 #include "module.h"
41 #include "file.h"
42 #include "heap.h"
43 #include "thread.h"
44 #include "winerror.h"
45 #include "wincon.h"
46 #include "wine/server.h"
47 #include "options.h"
48 #include "wine/debug.h"
49 #include "ntdll_misc.h"
50
51 WINE_DEFAULT_DEBUG_CHANNEL(process);
52 WINE_DECLARE_DEBUG_CHANNEL(relay);
53 WINE_DECLARE_DEBUG_CHANNEL(snoop);
54 WINE_DECLARE_DEBUG_CHANNEL(win32);
55
56 struct _ENVDB;
57
58 /* Win32 process database */
59 typedef struct _PDB
60 {
61     LONG             header[2];        /* 00 Kernel object header */
62     HMODULE          module;           /* 08 Main exe module (NT) */
63     void            *event;            /* 0c Pointer to an event object (unused) */
64     DWORD            exit_code;        /* 10 Process exit code */
65     DWORD            unknown2;         /* 14 Unknown */
66     HANDLE           heap;             /* 18 Default process heap */
67     HANDLE           mem_context;      /* 1c Process memory context */
68     DWORD            flags;            /* 20 Flags */
69     void            *pdb16;            /* 24 DOS PSP */
70     WORD             PSP_sel;          /* 28 Selector to DOS PSP */
71     WORD             imte;             /* 2a IMTE for the process module */
72     WORD             threads;          /* 2c Number of threads */
73     WORD             running_threads;  /* 2e Number of running threads */
74     WORD             free_lib_count;   /* 30 Recursion depth of FreeLibrary calls */
75     WORD             ring0_threads;    /* 32 Number of ring 0 threads */
76     HANDLE           system_heap;      /* 34 System heap to allocate handles */
77     HTASK            task;             /* 38 Win16 task */
78     void            *mem_map_files;    /* 3c Pointer to mem-mapped files */
79     struct _ENVDB   *env_db;           /* 40 Environment database */
80     void            *handle_table;     /* 44 Handle table */
81     struct _PDB     *parent;           /* 48 Parent process */
82     void            *modref_list;      /* 4c MODREF list */
83     void            *thread_list;      /* 50 List of threads */
84     void            *debuggee_CB;      /* 54 Debuggee context block */
85     void            *local_heap_free;  /* 58 Head of local heap free list */
86     DWORD            unknown4;         /* 5c Unknown */
87     CRITICAL_SECTION crit_section;     /* 60 Critical section */
88     DWORD            unknown5[3];      /* 78 Unknown */
89     void            *console;          /* 84 Console */
90     DWORD            tls_bits[2];      /* 88 TLS in-use bits */
91     DWORD            process_dword;    /* 90 Unknown */
92     struct _PDB     *group;            /* 94 Process group */
93     void            *exe_modref;       /* 98 MODREF for the process EXE */
94     void            *top_filter;       /* 9c Top exception filter */
95     DWORD            priority;         /* a0 Priority level */
96     HANDLE           heap_list;        /* a4 Head of process heap list */
97     void            *heap_handles;     /* a8 Head of heap handles list */
98     DWORD            unknown6;         /* ac Unknown */
99     void            *console_provider; /* b0 Console provider (??) */
100     WORD             env_selector;     /* b4 Selector to process environment */
101     WORD             error_mode;       /* b6 Error mode */
102     HANDLE           load_done_evt;    /* b8 Event for process loading done */
103     void            *UTState;          /* bc Head of Univeral Thunk list */
104     DWORD            unknown8;         /* c0 Unknown (NT) */
105     LCID             locale;           /* c4 Locale to be queried by GetThreadLocale (NT) */
106 } PDB;
107
108 PDB current_process;
109
110 RTL_USER_PROCESS_PARAMETERS     process_pmts;
111
112 /* Process flags */
113 #define PDB32_DEBUGGED      0x0001  /* Process is being debugged */
114 #define PDB32_WIN16_PROC    0x0008  /* Win16 process */
115 #define PDB32_DOS_PROC      0x0010  /* Dos process */
116 #define PDB32_CONSOLE_PROC  0x0020  /* Console process */
117 #define PDB32_FILE_APIS_OEM 0x0040  /* File APIs are OEM */
118 #define PDB32_WIN32S_PROC   0x8000  /* Win32s process */
119
120 static char main_exe_name[MAX_PATH];
121 static char *main_exe_name_ptr = main_exe_name;
122 static HANDLE main_exe_file;
123 static unsigned int server_startticks;
124
125 int main_create_flags = 0;
126
127 /* memory/environ.c */
128 extern struct _ENVDB *ENV_InitStartupInfo( size_t info_size, char *main_exe_name,
129                                            size_t main_exe_size );
130 extern BOOL ENV_BuildCommandLine( char **argv );
131 extern STARTUPINFOA current_startupinfo;
132
133 /* scheduler/pthread.c */
134 extern void PTHREAD_init_done(void);
135
136 extern void RELAY_InitDebugLists(void);
137 extern BOOL MAIN_MainInit(void);
138 extern void VERSION_Init( const char *appname );
139
140 /***********************************************************************
141  *           get_basename
142  */
143 inline static const char *get_basename( const char *name )
144 {
145     char *p;
146
147     if ((p = strrchr( name, '/' ))) name = p + 1;
148     if ((p = strrchr( name, '\\' ))) name = p + 1;
149     return name;
150 }
151
152
153 /***********************************************************************
154  *           open_builtin_exe_file
155  *
156  * Open an exe file for a builtin exe.
157  */
158 static void *open_builtin_exe_file( const char *name, char *error, int error_size, int test_only )
159 {
160     char exename[MAX_PATH], *p;
161     const char *basename = get_basename(name);
162
163     if (strlen(basename) >= sizeof(exename)) return NULL;
164     strcpy( exename, basename );
165     for (p = exename; *p; p++) *p = FILE_tolower(*p);
166     return wine_dll_load_main_exe( exename, error, error_size, test_only );
167 }
168
169
170 /***********************************************************************
171  *           open_exe_file
172  *
173  * Open a specific exe file, taking load order into account.
174  * Returns the file handle or 0 for a builtin exe.
175  */
176 static HANDLE open_exe_file( const char *name )
177 {
178     enum loadorder_type loadorder[LOADORDER_NTYPES];
179     char buffer[MAX_PATH];
180     HANDLE handle;
181     int i;
182
183     TRACE("looking for %s\n", debugstr_a(name) );
184
185     if ((handle = CreateFileA( name, GENERIC_READ, FILE_SHARE_READ,
186                                NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE)
187     {
188         /* file doesn't exist, check for builtin */
189         if (!FILE_contains_path( name )) goto error;
190         if (!MODULE_GetBuiltinPath( name, "", buffer, sizeof(buffer) )) goto error;
191         name = buffer;
192     }
193
194     MODULE_GetLoadOrder( loadorder, name, TRUE );
195
196     for(i = 0; i < LOADORDER_NTYPES; i++)
197     {
198         if (loadorder[i] == LOADORDER_INVALID) break;
199         switch(loadorder[i])
200         {
201         case LOADORDER_DLL:
202             TRACE( "Trying native exe %s\n", debugstr_a(name) );
203             if (handle != INVALID_HANDLE_VALUE) return handle;
204             break;
205         case LOADORDER_BI:
206             TRACE( "Trying built-in exe %s\n", debugstr_a(name) );
207             if (open_builtin_exe_file( name, NULL, 0, 1 ))
208             {
209                 if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle);
210                 return 0;
211             }
212         default:
213             break;
214         }
215     }
216     if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle);
217
218  error:
219     SetLastError( ERROR_FILE_NOT_FOUND );
220     return INVALID_HANDLE_VALUE;
221 }
222
223
224 /***********************************************************************
225  *           find_exe_file
226  *
227  * Open an exe file, and return the full name and file handle.
228  * Returns FALSE if file could not be found.
229  * If file exists but cannot be opened, returns TRUE and set handle to INVALID_HANDLE_VALUE.
230  * If file is a builtin exe, returns TRUE and sets handle to 0.
231  */
232 static BOOL find_exe_file( const char *name, char *buffer, int buflen, HANDLE *handle )
233 {
234     enum loadorder_type loadorder[LOADORDER_NTYPES];
235     int i;
236
237     TRACE("looking for %s\n", debugstr_a(name) );
238
239     if (!SearchPathA( NULL, name, ".exe", buflen, buffer, NULL ) &&
240         !MODULE_GetBuiltinPath( name, ".exe", buffer, buflen ))
241     {
242         /* no builtin found, try native without extension in case it is a Unix app */
243
244         if (SearchPathA( NULL, name, NULL, buflen, buffer, NULL ))
245         {
246             TRACE( "Trying native/Unix binary %s\n", debugstr_a(buffer) );
247             if ((*handle = CreateFileA( buffer, GENERIC_READ, FILE_SHARE_READ,
248                                         NULL, OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE)
249                 return TRUE;
250         }
251         return FALSE;
252     }
253
254     MODULE_GetLoadOrder( loadorder, buffer, TRUE );
255
256     for(i = 0; i < LOADORDER_NTYPES; i++)
257     {
258         if (loadorder[i] == LOADORDER_INVALID) break;
259         switch(loadorder[i])
260         {
261         case LOADORDER_DLL:
262             TRACE( "Trying native exe %s\n", debugstr_a(buffer) );
263             if ((*handle = CreateFileA( buffer, GENERIC_READ, FILE_SHARE_READ,
264                                         NULL, OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE)
265                 return TRUE;
266             if (GetLastError() != ERROR_FILE_NOT_FOUND) return TRUE;
267             break;
268         case LOADORDER_BI:
269             TRACE( "Trying built-in exe %s\n", debugstr_a(buffer) );
270             if (open_builtin_exe_file( buffer, NULL, 0, 1 ))
271             {
272                 *handle = 0;
273                 return TRUE;
274             }
275             break;
276         default:
277             break;
278         }
279     }
280     SetLastError( ERROR_FILE_NOT_FOUND );
281     return FALSE;
282 }
283
284
285 /***********************************************************************
286  *           process_init
287  *
288  * Main process initialisation code
289  */
290 static BOOL process_init( char *argv[] )
291 {
292     BOOL ret;
293     size_t info_size = 0;
294
295     /* store the program name */
296     argv0 = argv[0];
297
298     /* Fill the initial process structure */
299     current_process.exit_code       = STILL_ACTIVE;
300     current_process.threads         = 1;
301     current_process.running_threads = 1;
302     current_process.ring0_threads   = 1;
303     current_process.group           = &current_process;
304     current_process.priority        = 8;  /* Normal */
305
306     /* Setup the server connection */
307     CLIENT_InitServer();
308
309     /* Retrieve startup info from the server */
310     SERVER_START_REQ( init_process )
311     {
312         req->ldt_copy  = &wine_ldt_copy;
313         if ((ret = !wine_server_call_err( req )))
314         {
315             main_exe_file     = reply->exe_file;
316             main_create_flags = reply->create_flags;
317             info_size         = reply->info_size;
318             server_startticks = reply->server_start;
319             current_startupinfo.hStdInput   = reply->hstdin;
320             current_startupinfo.hStdOutput  = reply->hstdout;
321             current_startupinfo.hStdError   = reply->hstderr;
322         }
323     }
324     SERVER_END_REQ;
325     if (!ret) return FALSE;
326
327     /* Create the process heap */
328     current_process.heap = HeapCreate( HEAP_GROWABLE, 0, 0 );
329
330     if (main_create_flags == 0 &&
331         current_startupinfo.hStdInput  == 0 &&
332         current_startupinfo.hStdOutput == 0 &&
333         current_startupinfo.hStdError  == 0)
334     {
335         /* no parent, and no new console requested, create a simple console with bare handles to
336          * unix stdio input & output streams (aka simple console)
337          */
338         HANDLE handle;
339         wine_server_fd_to_handle( 0, GENERIC_READ|SYNCHRONIZE, TRUE, &handle );
340         SetStdHandle( STD_INPUT_HANDLE, handle );
341         wine_server_fd_to_handle( 1, GENERIC_WRITE|SYNCHRONIZE, TRUE, &handle );
342         SetStdHandle( STD_OUTPUT_HANDLE, handle );
343         wine_server_fd_to_handle( 1, GENERIC_WRITE|SYNCHRONIZE, TRUE, &handle );
344         SetStdHandle( STD_ERROR_HANDLE, handle );
345     }
346     else if (!(main_create_flags & (DETACHED_PROCESS|CREATE_NEW_CONSOLE)))
347     {
348         SetStdHandle( STD_INPUT_HANDLE,  current_startupinfo.hStdInput  );
349         SetStdHandle( STD_OUTPUT_HANDLE, current_startupinfo.hStdOutput );
350         SetStdHandle( STD_ERROR_HANDLE,  current_startupinfo.hStdError  );
351     }
352
353     /* Now we can use the pthreads routines */
354     PTHREAD_init_done();
355
356     /* Copy the parent environment */
357     if (!(current_process.env_db = ENV_InitStartupInfo( info_size, main_exe_name,
358                                                         sizeof(main_exe_name) )))
359         return FALSE;
360
361     /* Parse command line arguments */
362     OPTIONS_ParseOptions( !info_size ? argv : NULL );
363
364     /* <hack: to be changed later on> */
365     build_initial_environment();
366     process_pmts.CurrentDirectoryName.Length = 3 * sizeof(WCHAR);
367     process_pmts.CurrentDirectoryName.MaximumLength = RtlGetLongestNtPathLength() * sizeof(WCHAR);
368     process_pmts.CurrentDirectoryName.Buffer = RtlAllocateHeap( ntdll_get_process_heap(), 0, process_pmts.CurrentDirectoryName.MaximumLength);
369     process_pmts.CurrentDirectoryName.Buffer[0] = 'C';
370     process_pmts.CurrentDirectoryName.Buffer[1] = ':';
371     process_pmts.CurrentDirectoryName.Buffer[2] = '\\';
372     process_pmts.CurrentDirectoryName.Buffer[3] = '\0';
373     /* </hack: to be changed later on> */
374
375     ret = MAIN_MainInit();
376     if (TRACE_ON(relay) || TRACE_ON(snoop)) RELAY_InitDebugLists();
377
378     return ret;
379 }
380
381
382 /***********************************************************************
383  *           start_process
384  *
385  * Startup routine of a new process. Runs on the new process stack.
386  */
387 static void start_process(void)
388 {
389     int debugged, console_app;
390     LPTHREAD_START_ROUTINE entry;
391     WINE_MODREF *wm;
392     HANDLE main_file = main_exe_file;
393     IMAGE_NT_HEADERS *nt;
394
395     /* use original argv[0] as name for the main module */
396     if (!main_exe_name[0])
397     {
398         if (!GetLongPathNameA( full_argv0, main_exe_name, sizeof(main_exe_name) ))
399             lstrcpynA( main_exe_name, full_argv0, sizeof(main_exe_name) );
400     }
401
402     if (main_file)
403     {
404         UINT drive_type = GetDriveTypeA( main_exe_name );
405         /* don't keep the file handle open on removable media */
406         if (drive_type == DRIVE_REMOVABLE || drive_type == DRIVE_CDROM) main_file = 0;
407     }
408
409     /* Retrieve entry point address */
410     nt = RtlImageNtHeader( current_process.module );
411     entry = (LPTHREAD_START_ROUTINE)((char*)current_process.module +
412                                      nt->OptionalHeader.AddressOfEntryPoint);
413     console_app = (nt->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI);
414     if (console_app) current_process.flags |= PDB32_CONSOLE_PROC;
415
416     /* Install signal handlers; this cannot be done before, since we cannot
417      * send exceptions to the debugger before the create process event that
418      * is sent by REQ_INIT_PROCESS_DONE.
419      * We do need the handlers in place by the time the request is over, so
420      * we set them up here. If we segfault between here and the server call
421      * something is very wrong... */
422     if (!SIGNAL_Init()) goto error;
423
424     /* Signal the parent process to continue */
425     SERVER_START_REQ( init_process_done )
426     {
427         req->module      = (void *)current_process.module;
428         req->module_size = nt->OptionalHeader.SizeOfImage;
429         req->entry    = entry;
430         /* API requires a double indirection */
431         req->name     = &main_exe_name_ptr;
432         req->exe_file = main_file;
433         req->gui      = !console_app;
434         wine_server_add_data( req, main_exe_name, strlen(main_exe_name) );
435         wine_server_call( req );
436         debugged = reply->debugged;
437     }
438     SERVER_END_REQ;
439
440     /* create the main modref and load dependencies */
441     if (!(wm = PE_CreateModule( current_process.module, main_exe_name, 0, 0, FALSE )))
442         goto error;
443
444     if (main_exe_file) CloseHandle( main_exe_file ); /* we no longer need it */
445
446     MODULE_DllProcessAttach( NULL, (LPVOID)1 );
447
448     if (TRACE_ON(relay))
449         DPRINTF( "%04lx:Starting process %s (entryproc=%p)\n",
450                  GetCurrentThreadId(), main_exe_name, entry );
451     if (debugged) DbgBreakPoint();
452     /* FIXME: should use _PEB as parameter for NT 3.5 programs !
453      * Dunno about other OSs */
454     SetLastError(0);  /* clear error code */
455     ExitThread( entry(NULL) );
456
457  error:
458     ExitProcess( GetLastError() );
459 }
460
461
462 /***********************************************************************
463  *           __wine_process_init
464  *
465  * Wine initialisation: load and start the main exe file.
466  */
467 void __wine_process_init( int argc, char *argv[] )
468 {
469     char error[1024], *p;
470     DWORD stack_size = 0;
471
472     /* Initialize everything */
473     if (!process_init( argv )) exit(1);
474
475     argv++;  /* remove argv[0] (wine itself) */
476
477     TRACE( "starting process name=%s file=%p argv[0]=%s\n",
478            debugstr_a(main_exe_name), main_exe_file, debugstr_a(argv[0]) );
479
480     if (!main_exe_name[0])
481     {
482         if (!argv[0]) OPTIONS_Usage();
483
484         if (!find_exe_file( argv[0], main_exe_name, sizeof(main_exe_name), &main_exe_file ))
485         {
486             MESSAGE( "%s: cannot find '%s'\n", argv0, argv[0] );
487             ExitProcess(1);
488         }
489         if (main_exe_file == INVALID_HANDLE_VALUE)
490         {
491             MESSAGE( "%s: cannot open '%s'\n", argv0, main_exe_name );
492             ExitProcess(1);
493         }
494     }
495
496     if (!main_exe_file)  /* no file handle -> Winelib app */
497     {
498         TRACE( "starting Winelib app %s\n", debugstr_a(main_exe_name) );
499         if (open_builtin_exe_file( main_exe_name, error, sizeof(error), 0 ))
500             goto found;
501         MESSAGE( "%s: cannot open builtin library for '%s': %s\n", argv0, main_exe_name, error );
502         ExitProcess(1);
503     }
504     VERSION_Init( main_exe_name );
505
506     switch( MODULE_GetBinaryType( main_exe_file ))
507     {
508     case BINARY_PE_EXE:
509         TRACE( "starting Win32 binary %s\n", debugstr_a(main_exe_name) );
510         if ((current_process.module = PE_LoadImage( main_exe_file, main_exe_name, 0 ))) goto found;
511         MESSAGE( "%s: could not load '%s' as Win32 binary\n", argv0, main_exe_name );
512         ExitProcess(1);
513     case BINARY_PE_DLL:
514         MESSAGE( "%s: '%s' is a DLL, not an executable\n", argv0, main_exe_name );
515         ExitProcess(1);
516     case BINARY_UNKNOWN:
517         /* check for .com extension */
518         if (!(p = strrchr( main_exe_name, '.' )) || FILE_strcasecmp( p, ".com" ))
519         {
520             MESSAGE( "%s: cannot determine executable type for '%s'\n", argv0, main_exe_name );
521             ExitProcess(1);
522         }
523         /* fall through */
524     case BINARY_WIN16:
525     case BINARY_DOS:
526         TRACE( "starting Win16/DOS binary %s\n", debugstr_a(main_exe_name) );
527         CloseHandle( main_exe_file );
528         main_exe_file = 0;
529         argv--;
530         argv[0] = "winevdm.exe";
531         if (open_builtin_exe_file( "winevdm.exe", error, sizeof(error), 0 ))
532             goto found;
533         MESSAGE( "%s: trying to run '%s', cannot open builtin library for 'winevdm.exe': %s\n",
534                  argv0, main_exe_name, error );
535         ExitProcess(1);
536     case BINARY_OS216:
537         MESSAGE( "%s: '%s' is an OS/2 binary, not supported\n", argv0, main_exe_name );
538         ExitProcess(1);
539     case BINARY_UNIX_EXE:
540         MESSAGE( "%s: '%s' is a Unix binary, not supported\n", argv0, main_exe_name );
541         ExitProcess(1);
542     case BINARY_UNIX_LIB:
543         {
544             DOS_FULL_NAME full_name;
545             const char *name = main_exe_name;
546             UNICODE_STRING nameW;
547
548             TRACE( "starting Winelib app %s\n", debugstr_a(main_exe_name) );
549             RtlCreateUnicodeStringFromAsciiz(&nameW, name);
550             if (DOSFS_GetFullName( nameW.Buffer, TRUE, &full_name )) name = full_name.long_name;
551             RtlFreeUnicodeString(&nameW);
552             CloseHandle( main_exe_file );
553             main_exe_file = 0;
554             if (wine_dlopen( name, RTLD_NOW, error, sizeof(error) ))
555             {
556                 if ((p = strrchr( main_exe_name, '.' )) && !strcmp( p, ".so" )) *p = 0;
557                 goto found;
558             }
559             MESSAGE( "%s: could not load '%s': %s\n", argv0, main_exe_name, error );
560             ExitProcess(1);
561         }
562     }
563
564  found:
565     /* build command line */
566     if (!ENV_BuildCommandLine( argv )) goto error;
567
568     /* create 32-bit module for main exe */
569     if (!(current_process.module = BUILTIN32_LoadExeModule( current_process.module ))) goto error;
570     stack_size = RtlImageNtHeader(current_process.module)->OptionalHeader.SizeOfStackReserve;
571
572     /* allocate main thread stack */
573     if (!THREAD_InitStack( NtCurrentTeb(), stack_size )) goto error;
574
575     /* switch to the new stack */
576     SYSDEPS_SwitchToThreadStack( start_process );
577
578  error:
579     ExitProcess( GetLastError() );
580 }
581
582
583 /***********************************************************************
584  *           build_argv
585  *
586  * Build an argv array from a command-line.
587  * The command-line is modified to insert nulls.
588  * 'reserved' is the number of args to reserve before the first one.
589  */
590 static char **build_argv( char *cmdline, int reserved )
591 {
592     int argc;
593     char** argv;
594     char *arg,*s,*d;
595     int in_quotes,bcount;
596
597     argc=reserved+1;
598     bcount=0;
599     in_quotes=0;
600     s=cmdline;
601     while (1) {
602         if (*s=='\0' || ((*s==' ' || *s=='\t') && !in_quotes)) {
603             /* space */
604             argc++;
605             /* skip the remaining spaces */
606             while (*s==' ' || *s=='\t') {
607                 s++;
608             }
609             if (*s=='\0')
610                 break;
611             bcount=0;
612             continue;
613         } else if (*s=='\\') {
614             /* '\', count them */
615             bcount++;
616         } else if ((*s=='"') && ((bcount & 1)==0)) {
617             /* unescaped '"' */
618             in_quotes=!in_quotes;
619             bcount=0;
620         } else {
621             /* a regular character */
622             bcount=0;
623         }
624         s++;
625     }
626     argv=malloc(argc*sizeof(*argv));
627     if (!argv)
628         return NULL;
629
630     arg=d=s=cmdline;
631     bcount=0;
632     in_quotes=0;
633     argc=reserved;
634     while (*s) {
635         if ((*s==' ' || *s=='\t') && !in_quotes) {
636             /* Close the argument and copy it */
637             *d=0;
638             argv[argc++]=arg;
639
640             /* skip the remaining spaces */
641             do {
642                 s++;
643             } while (*s==' ' || *s=='\t');
644
645             /* Start with a new argument */
646             arg=d=s;
647             bcount=0;
648         } else if (*s=='\\') {
649             /* '\\' */
650             *d++=*s++;
651             bcount++;
652         } else if (*s=='"') {
653             /* '"' */
654             if ((bcount & 1)==0) {
655                 /* Preceeded by an even number of '\', this is half that
656                  * number of '\', plus a '"' which we discard.
657                  */
658                 d-=bcount/2;
659                 s++;
660                 in_quotes=!in_quotes;
661             } else {
662                 /* Preceeded by an odd number of '\', this is half that
663                  * number of '\' followed by a '"'
664                  */
665                 d=d-bcount/2-1;
666                 *d++='"';
667                 s++;
668             }
669             bcount=0;
670         } else {
671             /* a regular character */
672             *d++=*s++;
673             bcount=0;
674         }
675     }
676     if (*arg) {
677         *d='\0';
678         argv[argc++]=arg;
679     }
680     argv[argc]=NULL;
681
682     return argv;
683 }
684
685
686 /***********************************************************************
687  *           build_envp
688  *
689  * Build the environment of a new child process.
690  */
691 static char **build_envp( const char *env, const char *extra_env )
692 {
693     const char *p;
694     char **envp;
695     int count = 0;
696
697     if (extra_env) for (p = extra_env; *p; count++) p += strlen(p) + 1;
698     for (p = env; *p; count++) p += strlen(p) + 1;
699     count += 3;
700
701     if ((envp = malloc( count * sizeof(*envp) )))
702     {
703         extern char **environ;
704         char **envptr = envp;
705         char **unixptr = environ;
706         /* first the extra strings */
707         if (extra_env) for (p = extra_env; *p; p += strlen(p) + 1) *envptr++ = (char *)p;
708         /* then put PATH, HOME and WINEPREFIX from the unix env */
709         for (unixptr = environ; unixptr && *unixptr; unixptr++)
710             if (!memcmp( *unixptr, "PATH=", 5 ) ||
711                 !memcmp( *unixptr, "HOME=", 5 ) ||
712                 !memcmp( *unixptr, "WINEPREFIX=", 11 )) *envptr++ = *unixptr;
713         /* now put the Windows environment strings */
714         for (p = env; *p; p += strlen(p) + 1)
715         {
716             if (!memcmp( p, "PATH=", 5 ))  /* store PATH as WINEPATH */
717             {
718                 char *winepath = malloc( strlen(p) + 5 );
719                 strcpy( winepath, "WINE" );
720                 strcpy( winepath + 4, p );
721                 *envptr++ = winepath;
722             }
723             else if (memcmp( p, "HOME=", 5 ) &&
724                      memcmp( p, "WINEPATH=", 9 ) &&
725                      memcmp( p, "WINEPREFIX=", 11 )) *envptr++ = (char *)p;
726         }
727         *envptr = 0;
728     }
729     return envp;
730 }
731
732
733 /***********************************************************************
734  *           exec_wine_binary
735  *
736  * Locate the Wine binary to exec for a new Win32 process.
737  */
738 static void exec_wine_binary( char **argv, char **envp )
739 {
740     const char *path, *pos, *ptr;
741
742     /* first, try for a WINELOADER environment variable */
743     argv[0] = getenv("WINELOADER");
744     if (argv[0])
745         execve( argv[0], argv, envp );
746
747     /* next, try bin directory */
748     argv[0] = BINDIR "/wine";
749     execve( argv[0], argv, envp );
750
751     /* now try the path of argv0 of the current binary */
752     if (!(argv[0] = malloc( strlen(full_argv0) + 6 ))) return;
753     if ((ptr = strrchr( full_argv0, '/' )))
754     {
755         memcpy( argv[0], full_argv0, ptr - full_argv0 );
756         strcpy( argv[0] + (ptr - full_argv0), "/wine" );
757         execve( argv[0], argv, envp );
758     }
759     free( argv[0] );
760
761     /* now search in the Unix path */
762     if ((path = getenv( "PATH" )))
763     {
764         if (!(argv[0] = malloc( strlen(path) + 6 ))) return;
765         pos = path;
766         for (;;)
767         {
768             while (*pos == ':') pos++;
769             if (!*pos) break;
770             if (!(ptr = strchr( pos, ':' ))) ptr = pos + strlen(pos);
771             memcpy( argv[0], pos, ptr - pos );
772             strcpy( argv[0] + (ptr - pos), "/wine" );
773             execve( argv[0], argv, envp );
774             pos = ptr;
775         }
776     }
777     free( argv[0] );
778 }
779
780
781 /***********************************************************************
782  *           fork_and_exec
783  *
784  * Fork and exec a new Unix binary, checking for errors.
785  */
786 static int fork_and_exec( const char *filename, char *cmdline,
787                           const char *env, const char *newdir )
788 {
789     int fd[2];
790     int pid, err;
791
792     if (!env) env = GetEnvironmentStringsA();
793
794     if (pipe(fd) == -1)
795     {
796         FILE_SetDosError();
797         return -1;
798     }
799     fcntl( fd[1], F_SETFD, 1 );  /* set close on exec */
800     if (!(pid = fork()))  /* child */
801     {
802         char **argv = build_argv( cmdline, 0 );
803         char **envp = build_envp( env, NULL );
804         close( fd[0] );
805
806         /* Reset signals that we previously set to SIG_IGN */
807         signal( SIGPIPE, SIG_DFL );
808         signal( SIGCHLD, SIG_DFL );
809
810         if (newdir) chdir(newdir);
811
812         if (argv && envp) execve( filename, argv, envp );
813         err = errno;
814         write( fd[1], &err, sizeof(err) );
815         _exit(1);
816     }
817     close( fd[1] );
818     if ((pid != -1) && (read( fd[0], &err, sizeof(err) ) > 0))  /* exec failed */
819     {
820         errno = err;
821         pid = -1;
822     }
823     if (pid == -1) FILE_SetDosError();
824     close( fd[0] );
825     return pid;
826 }
827
828
829 /***********************************************************************
830  *           create_process
831  *
832  * Create a new process. If hFile is a valid handle we have an exe
833  * file, otherwise it is a Winelib app.
834  */
835 static BOOL create_process( HANDLE hFile, LPCSTR filename, LPSTR cmd_line, LPCSTR env,
836                             LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
837                             BOOL inherit, DWORD flags, LPSTARTUPINFOA startup,
838                             LPPROCESS_INFORMATION info, LPCSTR unixdir )
839 {
840     BOOL ret, success = FALSE;
841     HANDLE process_info;
842     startup_info_t startup_info;
843     char *extra_env = NULL;
844     int startfd[2];
845     int execfd[2];
846     pid_t pid;
847     int err;
848     char dummy;
849
850     if (!env)
851     {
852         env = GetEnvironmentStringsA();
853         extra_env = DRIVE_BuildEnv();
854     }
855
856     /* create the synchronization pipes */
857
858     if (pipe( startfd ) == -1)
859     {
860         FILE_SetDosError();
861         return FALSE;
862     }
863     if (pipe( execfd ) == -1)
864     {
865         close( startfd[0] );
866         close( startfd[1] );
867         FILE_SetDosError();
868         return FALSE;
869     }
870     fcntl( execfd[1], F_SETFD, 1 );  /* set close on exec */
871
872     /* create the child process */
873
874     if (!(pid = fork()))  /* child */
875     {
876         char **argv = build_argv( cmd_line, 1 );
877         char **envp = build_envp( env, extra_env );
878
879         close( startfd[1] );
880         close( execfd[0] );
881
882         /* wait for parent to tell us to start */
883         if (read( startfd[0], &dummy, 1 ) != 1) _exit(1);
884
885         close( startfd[0] );
886         /* Reset signals that we previously set to SIG_IGN */
887         signal( SIGPIPE, SIG_DFL );
888         signal( SIGCHLD, SIG_DFL );
889
890         if (unixdir) chdir(unixdir);
891
892         if (argv && envp) exec_wine_binary( argv, envp );
893
894         err = errno;
895         write( execfd[1], &err, sizeof(err) );
896         _exit(1);
897     }
898
899     /* this is the parent */
900
901     close( startfd[0] );
902     close( execfd[1] );
903     if (extra_env) HeapFree( GetProcessHeap(), 0, extra_env );
904     if (pid == -1)
905     {
906         close( startfd[1] );
907         close( execfd[0] );
908         FILE_SetDosError();
909         return FALSE;
910     }
911
912     /* fill the startup info structure */
913
914     startup_info.size        = sizeof(startup_info);
915     /* startup_info.filename_len is set below */
916     startup_info.cmdline_len = cmd_line ? strlen(cmd_line) : 0;
917     startup_info.desktop_len = startup->lpDesktop ? strlen(startup->lpDesktop) : 0;
918     startup_info.title_len   = startup->lpTitle ? strlen(startup->lpTitle) : 0;
919     startup_info.x           = startup->dwX;
920     startup_info.y           = startup->dwY;
921     startup_info.cx          = startup->dwXSize;
922     startup_info.cy          = startup->dwYSize;
923     startup_info.x_chars     = startup->dwXCountChars;
924     startup_info.y_chars     = startup->dwYCountChars;
925     startup_info.attribute   = startup->dwFillAttribute;
926     startup_info.cmd_show    = startup->wShowWindow;
927     startup_info.flags       = startup->dwFlags;
928
929     /* create the process on the server side */
930
931     SERVER_START_REQ( new_process )
932     {
933         char buf[MAX_PATH];
934         LPCSTR nameptr;
935
936         req->inherit_all  = inherit;
937         req->create_flags = flags;
938         req->use_handles  = (startup->dwFlags & STARTF_USESTDHANDLES) != 0;
939         req->unix_pid     = pid;
940         req->exe_file     = hFile;
941         if (startup->dwFlags & STARTF_USESTDHANDLES)
942         {
943             req->hstdin  = startup->hStdInput;
944             req->hstdout = startup->hStdOutput;
945             req->hstderr = startup->hStdError;
946         }
947         else
948         {
949             req->hstdin  = GetStdHandle( STD_INPUT_HANDLE );
950             req->hstdout = GetStdHandle( STD_OUTPUT_HANDLE );
951             req->hstderr = GetStdHandle( STD_ERROR_HANDLE );
952         }
953
954         if (GetLongPathNameA( filename, buf, MAX_PATH ))
955             nameptr = buf;
956         else
957             nameptr = filename;
958
959         startup_info.filename_len = strlen(nameptr);
960         wine_server_add_data( req, &startup_info, sizeof(startup_info) );
961         wine_server_add_data( req, nameptr, startup_info.filename_len );
962         wine_server_add_data( req, cmd_line, startup_info.cmdline_len );
963         wine_server_add_data( req, startup->lpDesktop, startup_info.desktop_len );
964         wine_server_add_data( req, startup->lpTitle, startup_info.title_len );
965
966         ret = !wine_server_call_err( req );
967         process_info = reply->info;
968     }
969     SERVER_END_REQ;
970
971     if (!ret)
972     {
973         close( startfd[1] );
974         close( execfd[0] );
975         return FALSE;
976     }
977
978     /* tell child to start and wait for it to exec */
979
980     write( startfd[1], &dummy, 1 );
981     close( startfd[1] );
982
983     if (read( execfd[0], &err, sizeof(err) ) > 0) /* exec failed */
984     {
985         errno = err;
986         FILE_SetDosError();
987         close( execfd[0] );
988         CloseHandle( process_info );
989         return FALSE;
990     }
991
992     /* wait for the new process info to be ready */
993
994     WaitForSingleObject( process_info, INFINITE );
995     SERVER_START_REQ( get_new_process_info )
996     {
997         req->info     = process_info;
998         req->pinherit = (psa && (psa->nLength >= sizeof(*psa)) && psa->bInheritHandle);
999         req->tinherit = (tsa && (tsa->nLength >= sizeof(*tsa)) && tsa->bInheritHandle);
1000         if ((ret = !wine_server_call_err( req )))
1001         {
1002             info->dwProcessId = (DWORD)reply->pid;
1003             info->dwThreadId  = (DWORD)reply->tid;
1004             info->hProcess    = reply->phandle;
1005             info->hThread     = reply->thandle;
1006             success           = reply->success;
1007         }
1008     }
1009     SERVER_END_REQ;
1010
1011     if (ret && !success)  /* new process failed to start */
1012     {
1013         DWORD exitcode;
1014         if (GetExitCodeProcess( info->hProcess, &exitcode )) SetLastError( exitcode );
1015         CloseHandle( info->hThread );
1016         CloseHandle( info->hProcess );
1017         ret = FALSE;
1018     }
1019     CloseHandle( process_info );
1020     return ret;
1021 }
1022
1023
1024 /***********************************************************************
1025  *           create_vdm_process
1026  *
1027  * Create a new VDM process for a 16-bit or DOS application.
1028  */
1029 static BOOL create_vdm_process( LPCSTR filename, LPSTR cmd_line, LPCSTR env,
1030                                 LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
1031                                 BOOL inherit, DWORD flags, LPSTARTUPINFOA startup,
1032                                 LPPROCESS_INFORMATION info, LPCSTR unixdir )
1033 {
1034     BOOL ret;
1035     LPSTR new_cmd_line = HeapAlloc( GetProcessHeap(), 0, strlen(filename) + strlen(cmd_line) + 30 );
1036
1037     if (!new_cmd_line)
1038     {
1039         SetLastError( ERROR_OUTOFMEMORY );
1040         return FALSE;
1041     }
1042     sprintf( new_cmd_line, "winevdm.exe --app-name \"%s\" %s", filename, cmd_line );
1043     ret = create_process( 0, "winevdm.exe", new_cmd_line, env, psa, tsa, inherit,
1044                           flags, startup, info, unixdir );
1045     HeapFree( GetProcessHeap(), 0, new_cmd_line );
1046     return ret;
1047 }
1048
1049
1050 /*************************************************************************
1051  *               get_file_name
1052  *
1053  * Helper for CreateProcess: retrieve the file name to load from the
1054  * app name and command line. Store the file name in buffer, and
1055  * return a possibly modified command line.
1056  * Also returns a handle to the opened file if it's a Windows binary.
1057  */
1058 static LPSTR get_file_name( LPCSTR appname, LPSTR cmdline, LPSTR buffer,
1059                             int buflen, HANDLE *handle )
1060 {
1061     char *name, *pos, *ret = NULL;
1062     const char *p;
1063
1064     /* if we have an app name, everything is easy */
1065
1066     if (appname)
1067     {
1068         /* use the unmodified app name as file name */
1069         lstrcpynA( buffer, appname, buflen );
1070         *handle = open_exe_file( buffer );
1071         if (!(ret = cmdline) || !cmdline[0])
1072         {
1073             /* no command-line, create one */
1074             if ((ret = HeapAlloc( GetProcessHeap(), 0, strlen(appname) + 3 )))
1075                 sprintf( ret, "\"%s\"", appname );
1076         }
1077         return ret;
1078     }
1079
1080     if (!cmdline)
1081     {
1082         SetLastError( ERROR_INVALID_PARAMETER );
1083         return NULL;
1084     }
1085
1086     /* first check for a quoted file name */
1087
1088     if ((cmdline[0] == '"') && ((p = strchr( cmdline + 1, '"' ))))
1089     {
1090         int len = p - cmdline - 1;
1091         /* extract the quoted portion as file name */
1092         if (!(name = HeapAlloc( GetProcessHeap(), 0, len + 1 ))) return NULL;
1093         memcpy( name, cmdline + 1, len );
1094         name[len] = 0;
1095
1096         if (find_exe_file( name, buffer, buflen, handle ))
1097             ret = cmdline;  /* no change necessary */
1098         goto done;
1099     }
1100
1101     /* now try the command-line word by word */
1102
1103     if (!(name = HeapAlloc( GetProcessHeap(), 0, strlen(cmdline) + 1 ))) return NULL;
1104     pos = name;
1105     p = cmdline;
1106
1107     while (*p)
1108     {
1109         do *pos++ = *p++; while (*p && *p != ' ');
1110         *pos = 0;
1111         if (find_exe_file( name, buffer, buflen, handle ))
1112         {
1113             ret = cmdline;
1114             break;
1115         }
1116     }
1117
1118     if (!ret || !strchr( name, ' ' )) goto done;  /* no change necessary */
1119
1120     /* now build a new command-line with quotes */
1121
1122     if (!(ret = HeapAlloc( GetProcessHeap(), 0, strlen(cmdline) + 3 ))) goto done;
1123     sprintf( ret, "\"%s\"%s", name, p );
1124
1125  done:
1126     HeapFree( GetProcessHeap(), 0, name );
1127     return ret;
1128 }
1129
1130
1131 /**********************************************************************
1132  *       CreateProcessA          (KERNEL32.@)
1133  */
1134 BOOL WINAPI CreateProcessA( LPCSTR app_name, LPSTR cmd_line, LPSECURITY_ATTRIBUTES process_attr,
1135                             LPSECURITY_ATTRIBUTES thread_attr, BOOL inherit,
1136                             DWORD flags, LPVOID env, LPCSTR cur_dir,
1137                             LPSTARTUPINFOA startup_info, LPPROCESS_INFORMATION info )
1138 {
1139     BOOL retv = FALSE;
1140     HANDLE hFile = 0;
1141     const char *unixdir = NULL;
1142     DOS_FULL_NAME full_dir;
1143     char name[MAX_PATH];
1144     LPSTR tidy_cmdline;
1145     char *p;
1146
1147     /* Process the AppName and/or CmdLine to get module name and path */
1148
1149     TRACE("app %s cmdline %s\n", debugstr_a(app_name), debugstr_a(cmd_line) );
1150
1151     if (!(tidy_cmdline = get_file_name( app_name, cmd_line, name, sizeof(name), &hFile )))
1152         return FALSE;
1153     if (hFile == INVALID_HANDLE_VALUE) goto done;
1154
1155     /* Warn if unsupported features are used */
1156
1157     if (flags & NORMAL_PRIORITY_CLASS)
1158         FIXME("(%s,...): NORMAL_PRIORITY_CLASS ignored\n", name);
1159     if (flags & IDLE_PRIORITY_CLASS)
1160         FIXME("(%s,...): IDLE_PRIORITY_CLASS ignored\n", name);
1161     if (flags & HIGH_PRIORITY_CLASS)
1162         FIXME("(%s,...): HIGH_PRIORITY_CLASS ignored\n", name);
1163     if (flags & REALTIME_PRIORITY_CLASS)
1164         FIXME("(%s,...): REALTIME_PRIORITY_CLASS ignored\n", name);
1165     if (flags & CREATE_NEW_PROCESS_GROUP)
1166         FIXME("(%s,...): CREATE_NEW_PROCESS_GROUP ignored\n", name);
1167     if (flags & CREATE_UNICODE_ENVIRONMENT)
1168         FIXME("(%s,...): CREATE_UNICODE_ENVIRONMENT ignored\n", name);
1169     if (flags & CREATE_SEPARATE_WOW_VDM)
1170         FIXME("(%s,...): CREATE_SEPARATE_WOW_VDM ignored\n", name);
1171     if (flags & CREATE_SHARED_WOW_VDM)
1172         FIXME("(%s,...): CREATE_SHARED_WOW_VDM ignored\n", name);
1173     if (flags & CREATE_DEFAULT_ERROR_MODE)
1174         FIXME("(%s,...): CREATE_DEFAULT_ERROR_MODE ignored\n", name);
1175     if (flags & CREATE_NO_WINDOW)
1176         FIXME("(%s,...): CREATE_NO_WINDOW ignored\n", name);
1177     if (flags & PROFILE_USER)
1178         FIXME("(%s,...): PROFILE_USER ignored\n", name);
1179     if (flags & PROFILE_KERNEL)
1180         FIXME("(%s,...): PROFILE_KERNEL ignored\n", name);
1181     if (flags & PROFILE_SERVER)
1182         FIXME("(%s,...): PROFILE_SERVER ignored\n", name);
1183     if (startup_info->lpDesktop)
1184         FIXME("(%s,...): startup_info->lpDesktop %s ignored\n",
1185               name, debugstr_a(startup_info->lpDesktop));
1186     if (startup_info->dwFlags & STARTF_RUNFULLSCREEN)
1187         FIXME("(%s,...): STARTF_RUNFULLSCREEN ignored\n", name);
1188     if (startup_info->dwFlags & STARTF_FORCEONFEEDBACK)
1189         FIXME("(%s,...): STARTF_FORCEONFEEDBACK ignored\n", name);
1190     if (startup_info->dwFlags & STARTF_FORCEOFFFEEDBACK)
1191         FIXME("(%s,...): STARTF_FORCEOFFFEEDBACK ignored\n", name);
1192     if (startup_info->dwFlags & STARTF_USEHOTKEY)
1193         FIXME("(%s,...): STARTF_USEHOTKEY ignored\n", name);
1194
1195     if (cur_dir)
1196     {
1197         UNICODE_STRING cur_dirW;
1198         RtlCreateUnicodeStringFromAsciiz(&cur_dirW, cur_dir);
1199         if (DOSFS_GetFullName( cur_dirW.Buffer, TRUE, &full_dir ))
1200             unixdir = full_dir.long_name;
1201         RtlFreeUnicodeString(&cur_dirW);
1202     }
1203     else
1204     {
1205         WCHAR buf[MAX_PATH];
1206         if (GetCurrentDirectoryW(MAX_PATH, buf))
1207         {
1208             if (DOSFS_GetFullName( buf, TRUE, &full_dir )) unixdir = full_dir.long_name;
1209         }
1210     }
1211
1212     info->hThread = info->hProcess = 0;
1213     info->dwProcessId = info->dwThreadId = 0;
1214
1215     /* Determine executable type */
1216
1217     if (!hFile)  /* builtin exe */
1218     {
1219         TRACE( "starting %s as Winelib app\n", debugstr_a(name) );
1220         retv = create_process( 0, name, tidy_cmdline, env, process_attr, thread_attr,
1221                                inherit, flags, startup_info, info, unixdir );
1222         goto done;
1223     }
1224
1225     switch( MODULE_GetBinaryType( hFile ))
1226     {
1227     case BINARY_PE_EXE:
1228         TRACE( "starting %s as Win32 binary\n", debugstr_a(name) );
1229         retv = create_process( hFile, name, tidy_cmdline, env, process_attr, thread_attr,
1230                                inherit, flags, startup_info, info, unixdir );
1231         break;
1232     case BINARY_WIN16:
1233     case BINARY_DOS:
1234         TRACE( "starting %s as Win16/DOS binary\n", debugstr_a(name) );
1235         retv = create_vdm_process( name, tidy_cmdline, env, process_attr, thread_attr,
1236                                    inherit, flags, startup_info, info, unixdir );
1237         break;
1238     case BINARY_OS216:
1239         FIXME( "%s is OS/2 binary, not supported\n", debugstr_a(name) );
1240         SetLastError( ERROR_BAD_EXE_FORMAT );
1241         break;
1242     case BINARY_PE_DLL:
1243         TRACE( "not starting %s since it is a dll\n", debugstr_a(name) );
1244         SetLastError( ERROR_BAD_EXE_FORMAT );
1245         break;
1246     case BINARY_UNIX_LIB:
1247         TRACE( "%s is a Unix library, starting as Winelib app\n", debugstr_a(name) );
1248         retv = create_process( hFile, name, tidy_cmdline, env, process_attr, thread_attr,
1249                                inherit, flags, startup_info, info, unixdir );
1250         break;
1251     case BINARY_UNKNOWN:
1252         /* check for .com or .bat extension */
1253         if ((p = strrchr( name, '.' )))
1254         {
1255             if (!FILE_strcasecmp( p, ".com" ))
1256             {
1257                 TRACE( "starting %s as DOS binary\n", debugstr_a(name) );
1258                 retv = create_vdm_process( name, tidy_cmdline, env, process_attr, thread_attr,
1259                                            inherit, flags, startup_info, info, unixdir );
1260                 break;
1261             }
1262             if (!FILE_strcasecmp( p, ".bat" ))
1263             {
1264                 char comspec[MAX_PATH];
1265                 if (GetEnvironmentVariableA("COMSPEC", comspec, sizeof(comspec)))
1266                 {
1267                     char *newcmdline;
1268                     if ((newcmdline = HeapAlloc( GetProcessHeap(), 0,
1269                                                  strlen(comspec) + 4 + strlen(tidy_cmdline) + 1)))
1270                     {
1271                         sprintf( newcmdline, "%s /c %s", comspec,  tidy_cmdline);
1272                         TRACE( "starting %s as batch binary: %s\n",
1273                                debugstr_a(name), debugstr_a(newcmdline) );
1274                         retv = CreateProcessA( comspec, newcmdline, process_attr, thread_attr,
1275                                                inherit, flags, env, cur_dir, startup_info, info );
1276                         HeapFree( GetProcessHeap(), 0, newcmdline );
1277                         break;
1278                     }
1279                 }
1280             }
1281         }
1282         /* fall through */
1283     case BINARY_UNIX_EXE:
1284         {
1285             /* unknown file, try as unix executable */
1286             UNICODE_STRING nameW;
1287             DOS_FULL_NAME full_name;
1288             const char *unixfilename = name;
1289
1290             TRACE( "starting %s as Unix binary\n", debugstr_a(name) );
1291
1292             RtlCreateUnicodeStringFromAsciiz(&nameW, name);
1293             if (DOSFS_GetFullName( nameW.Buffer, TRUE, &full_name )) unixfilename = full_name.long_name;
1294             RtlFreeUnicodeString(&nameW);
1295             retv = (fork_and_exec( unixfilename, tidy_cmdline, env, unixdir ) != -1);
1296         }
1297         break;
1298     }
1299     CloseHandle( hFile );
1300
1301  done:
1302     if (tidy_cmdline != cmd_line) HeapFree( GetProcessHeap(), 0, tidy_cmdline );
1303     return retv;
1304 }
1305
1306
1307 /**********************************************************************
1308  *       CreateProcessW          (KERNEL32.@)
1309  * NOTES
1310  *  lpReserved is not converted
1311  */
1312 BOOL WINAPI CreateProcessW( LPCWSTR app_name, LPWSTR cmd_line, LPSECURITY_ATTRIBUTES process_attr,
1313                             LPSECURITY_ATTRIBUTES thread_attr, BOOL inherit, DWORD flags,
1314                             LPVOID env, LPCWSTR cur_dir, LPSTARTUPINFOW startup_info,
1315                             LPPROCESS_INFORMATION info )
1316 {
1317     BOOL ret;
1318     STARTUPINFOA StartupInfoA;
1319
1320     LPSTR app_nameA = HEAP_strdupWtoA (GetProcessHeap(),0,app_name);
1321     LPSTR cmd_lineA = HEAP_strdupWtoA (GetProcessHeap(),0,cmd_line);
1322     LPSTR cur_dirA = HEAP_strdupWtoA (GetProcessHeap(),0,cur_dir);
1323
1324     memcpy (&StartupInfoA, startup_info, sizeof(STARTUPINFOA));
1325     StartupInfoA.lpDesktop = HEAP_strdupWtoA (GetProcessHeap(),0,startup_info->lpDesktop);
1326     StartupInfoA.lpTitle = HEAP_strdupWtoA (GetProcessHeap(),0,startup_info->lpTitle);
1327
1328     TRACE_(win32)("(%s,%s,...)\n", debugstr_w(app_name), debugstr_w(cmd_line));
1329
1330     if (startup_info->lpReserved)
1331       FIXME_(win32)("StartupInfo.lpReserved is used, please report (%s)\n",
1332                     debugstr_w(startup_info->lpReserved));
1333
1334     ret = CreateProcessA( app_nameA,  cmd_lineA, process_attr, thread_attr,
1335                           inherit, flags, env, cur_dirA, &StartupInfoA, info );
1336
1337     HeapFree( GetProcessHeap(), 0, cur_dirA );
1338     HeapFree( GetProcessHeap(), 0, cmd_lineA );
1339     HeapFree( GetProcessHeap(), 0, StartupInfoA.lpDesktop );
1340     HeapFree( GetProcessHeap(), 0, StartupInfoA.lpTitle );
1341
1342     return ret;
1343 }
1344
1345
1346 /***********************************************************************
1347  *           ExitProcess   (KERNEL32.@)
1348  */
1349 void WINAPI ExitProcess( DWORD status )
1350 {
1351     LdrShutdownProcess();
1352     SERVER_START_REQ( terminate_process )
1353     {
1354         /* send the exit code to the server */
1355         req->handle    = GetCurrentProcess();
1356         req->exit_code = status;
1357         wine_server_call( req );
1358     }
1359     SERVER_END_REQ;
1360     exit( status );
1361 }
1362
1363 /***********************************************************************
1364  *           ExitProcess   (KERNEL.466)
1365  */
1366 void WINAPI ExitProcess16( WORD status )
1367 {
1368     DWORD count;
1369     ReleaseThunkLock( &count );
1370     ExitProcess( status );
1371 }
1372
1373 /******************************************************************************
1374  *           TerminateProcess   (KERNEL32.@)
1375  */
1376 BOOL WINAPI TerminateProcess( HANDLE handle, DWORD exit_code )
1377 {
1378     NTSTATUS status = NtTerminateProcess( handle, exit_code );
1379     if (status) SetLastError( RtlNtStatusToDosError(status) );
1380     return !status;
1381 }
1382
1383
1384 /***********************************************************************
1385  *           GetProcessDword    (KERNEL.485)
1386  *           GetProcessDword    (KERNEL32.18)
1387  * 'Of course you cannot directly access Windows internal structures'
1388  */
1389 DWORD WINAPI GetProcessDword( DWORD dwProcessID, INT offset )
1390 {
1391     DWORD x, y;
1392
1393     TRACE_(win32)("(%ld, %d)\n", dwProcessID, offset );
1394
1395     if (dwProcessID && dwProcessID != GetCurrentProcessId())
1396     {
1397         ERR("%d: process %lx not accessible\n", offset, dwProcessID);
1398         return 0;
1399     }
1400
1401     switch ( offset )
1402     {
1403     case GPD_APP_COMPAT_FLAGS:
1404         return GetAppCompatFlags16(0);
1405
1406     case GPD_LOAD_DONE_EVENT:
1407         return (DWORD)current_process.load_done_evt;
1408
1409     case GPD_HINSTANCE16:
1410         return GetTaskDS16();
1411
1412     case GPD_WINDOWS_VERSION:
1413         return GetExeVersion16();
1414
1415     case GPD_THDB:
1416         return (DWORD)NtCurrentTeb() - 0x10 /* FIXME */;
1417
1418     case GPD_PDB:
1419         return (DWORD)&current_process;
1420
1421     case GPD_STARTF_SHELLDATA: /* return stdoutput handle from startupinfo ??? */
1422         return (DWORD)current_startupinfo.hStdOutput;
1423
1424     case GPD_STARTF_HOTKEY: /* return stdinput handle from startupinfo ??? */
1425         return (DWORD)current_startupinfo.hStdInput;
1426
1427     case GPD_STARTF_SHOWWINDOW:
1428         return current_startupinfo.wShowWindow;
1429
1430     case GPD_STARTF_SIZE:
1431         x = current_startupinfo.dwXSize;
1432         if ( (INT)x == CW_USEDEFAULT ) x = CW_USEDEFAULT16;
1433         y = current_startupinfo.dwYSize;
1434         if ( (INT)y == CW_USEDEFAULT ) y = CW_USEDEFAULT16;
1435         return MAKELONG( x, y );
1436
1437     case GPD_STARTF_POSITION:
1438         x = current_startupinfo.dwX;
1439         if ( (INT)x == CW_USEDEFAULT ) x = CW_USEDEFAULT16;
1440         y = current_startupinfo.dwY;
1441         if ( (INT)y == CW_USEDEFAULT ) y = CW_USEDEFAULT16;
1442         return MAKELONG( x, y );
1443
1444     case GPD_STARTF_FLAGS:
1445         return current_startupinfo.dwFlags;
1446
1447     case GPD_PARENT:
1448         return 0;
1449
1450     case GPD_FLAGS:
1451         return current_process.flags;
1452
1453     case GPD_USERDATA:
1454         return current_process.process_dword;
1455
1456     default:
1457         ERR_(win32)("Unknown offset %d\n", offset );
1458         return 0;
1459     }
1460 }
1461
1462 /***********************************************************************
1463  *           SetProcessDword    (KERNEL.484)
1464  * 'Of course you cannot directly access Windows internal structures'
1465  */
1466 void WINAPI SetProcessDword( DWORD dwProcessID, INT offset, DWORD value )
1467 {
1468     TRACE_(win32)("(%ld, %d)\n", dwProcessID, offset );
1469
1470     if (dwProcessID && dwProcessID != GetCurrentProcessId())
1471     {
1472         ERR("%d: process %lx not accessible\n", offset, dwProcessID);
1473         return;
1474     }
1475
1476     switch ( offset )
1477     {
1478     case GPD_APP_COMPAT_FLAGS:
1479     case GPD_LOAD_DONE_EVENT:
1480     case GPD_HINSTANCE16:
1481     case GPD_WINDOWS_VERSION:
1482     case GPD_THDB:
1483     case GPD_PDB:
1484     case GPD_STARTF_SHELLDATA:
1485     case GPD_STARTF_HOTKEY:
1486     case GPD_STARTF_SHOWWINDOW:
1487     case GPD_STARTF_SIZE:
1488     case GPD_STARTF_POSITION:
1489     case GPD_STARTF_FLAGS:
1490     case GPD_PARENT:
1491     case GPD_FLAGS:
1492         ERR_(win32)("Not allowed to modify offset %d\n", offset );
1493         break;
1494
1495     case GPD_USERDATA:
1496         current_process.process_dword = value;
1497         break;
1498
1499     default:
1500         ERR_(win32)("Unknown offset %d\n", offset );
1501         break;
1502     }
1503 }
1504
1505
1506 /*********************************************************************
1507  *           OpenProcess   (KERNEL32.@)
1508  */
1509 HANDLE WINAPI OpenProcess( DWORD access, BOOL inherit, DWORD id )
1510 {
1511     HANDLE ret = 0;
1512     SERVER_START_REQ( open_process )
1513     {
1514         req->pid     = id;
1515         req->access  = access;
1516         req->inherit = inherit;
1517         if (!wine_server_call_err( req )) ret = reply->handle;
1518     }
1519     SERVER_END_REQ;
1520     return ret;
1521 }
1522
1523 /*********************************************************************
1524  *           MapProcessHandle   (KERNEL.483)
1525  */
1526 DWORD WINAPI MapProcessHandle( HANDLE handle )
1527 {
1528     DWORD ret = 0;
1529     SERVER_START_REQ( get_process_info )
1530     {
1531         req->handle = handle;
1532         if (!wine_server_call_err( req )) ret = (DWORD)reply->pid;
1533     }
1534     SERVER_END_REQ;
1535     return ret;
1536 }
1537
1538 /***********************************************************************
1539  *           SetPriorityClass   (KERNEL32.@)
1540  */
1541 BOOL WINAPI SetPriorityClass( HANDLE hprocess, DWORD priorityclass )
1542 {
1543     BOOL ret;
1544     SERVER_START_REQ( set_process_info )
1545     {
1546         req->handle   = hprocess;
1547         req->priority = priorityclass;
1548         req->mask     = SET_PROCESS_INFO_PRIORITY;
1549         ret = !wine_server_call_err( req );
1550     }
1551     SERVER_END_REQ;
1552     return ret;
1553 }
1554
1555
1556 /***********************************************************************
1557  *           GetPriorityClass   (KERNEL32.@)
1558  */
1559 DWORD WINAPI GetPriorityClass(HANDLE hprocess)
1560 {
1561     DWORD ret = 0;
1562     SERVER_START_REQ( get_process_info )
1563     {
1564         req->handle = hprocess;
1565         if (!wine_server_call_err( req )) ret = reply->priority;
1566     }
1567     SERVER_END_REQ;
1568     return ret;
1569 }
1570
1571
1572 /***********************************************************************
1573  *          SetProcessAffinityMask   (KERNEL32.@)
1574  */
1575 BOOL WINAPI SetProcessAffinityMask( HANDLE hProcess, DWORD affmask )
1576 {
1577     BOOL ret;
1578     SERVER_START_REQ( set_process_info )
1579     {
1580         req->handle   = hProcess;
1581         req->affinity = affmask;
1582         req->mask     = SET_PROCESS_INFO_AFFINITY;
1583         ret = !wine_server_call_err( req );
1584     }
1585     SERVER_END_REQ;
1586     return ret;
1587 }
1588
1589 /**********************************************************************
1590  *          GetProcessAffinityMask    (KERNEL32.@)
1591  */
1592 BOOL WINAPI GetProcessAffinityMask( HANDLE hProcess,
1593                                       LPDWORD lpProcessAffinityMask,
1594                                       LPDWORD lpSystemAffinityMask )
1595 {
1596     BOOL ret = FALSE;
1597     SERVER_START_REQ( get_process_info )
1598     {
1599         req->handle = hProcess;
1600         if (!wine_server_call_err( req ))
1601         {
1602             if (lpProcessAffinityMask) *lpProcessAffinityMask = reply->process_affinity;
1603             if (lpSystemAffinityMask) *lpSystemAffinityMask = reply->system_affinity;
1604             ret = TRUE;
1605         }
1606     }
1607     SERVER_END_REQ;
1608     return ret;
1609 }
1610
1611
1612 /***********************************************************************
1613  *           GetProcessVersion    (KERNEL32.@)
1614  */
1615 DWORD WINAPI GetProcessVersion( DWORD processid )
1616 {
1617     IMAGE_NT_HEADERS *nt;
1618
1619     if (processid && processid != GetCurrentProcessId())
1620     {
1621         FIXME("should use ReadProcessMemory\n");
1622         return 0;
1623     }
1624     if ((nt = RtlImageNtHeader( current_process.module )))
1625         return ((nt->OptionalHeader.MajorSubsystemVersion << 16) |
1626                 nt->OptionalHeader.MinorSubsystemVersion);
1627     return 0;
1628 }
1629
1630 /***********************************************************************
1631  *           GetProcessFlags    (KERNEL32.@)
1632  */
1633 DWORD WINAPI GetProcessFlags( DWORD processid )
1634 {
1635     if (processid && processid != GetCurrentProcessId()) return 0;
1636     return current_process.flags;
1637 }
1638
1639
1640 /***********************************************************************
1641  *              SetProcessWorkingSetSize        [KERNEL32.@]
1642  * Sets the min/max working set sizes for a specified process.
1643  *
1644  * PARAMS
1645  *    hProcess [I] Handle to the process of interest
1646  *    minset   [I] Specifies minimum working set size
1647  *    maxset   [I] Specifies maximum working set size
1648  *
1649  * RETURNS  STD
1650  */
1651 BOOL WINAPI SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T minset,
1652                                      SIZE_T maxset)
1653 {
1654     FIXME("(%p,%ld,%ld): stub - harmless\n",hProcess,minset,maxset);
1655     if(( minset == (SIZE_T)-1) && (maxset == (SIZE_T)-1)) {
1656         /* Trim the working set to zero */
1657         /* Swap the process out of physical RAM */
1658     }
1659     return TRUE;
1660 }
1661
1662 /***********************************************************************
1663  *           GetProcessWorkingSetSize    (KERNEL32.@)
1664  */
1665 BOOL WINAPI GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T minset,
1666                                      PSIZE_T maxset)
1667 {
1668         FIXME("(%p,%p,%p): stub\n",hProcess,minset,maxset);
1669         /* 32 MB working set size */
1670         if (minset) *minset = 32*1024*1024;
1671         if (maxset) *maxset = 32*1024*1024;
1672         return TRUE;
1673 }
1674
1675 /***********************************************************************
1676  *           SetProcessShutdownParameters    (KERNEL32.@)
1677  *
1678  * CHANGED - James Sutherland (JamesSutherland@gmx.de)
1679  * Now tracks changes made (but does not act on these changes)
1680  */
1681 static DWORD shutdown_flags = 0;
1682 static DWORD shutdown_priority = 0x280;
1683
1684 BOOL WINAPI SetProcessShutdownParameters(DWORD level, DWORD flags)
1685 {
1686     FIXME("(%08lx, %08lx): partial stub.\n", level, flags);
1687     shutdown_flags = flags;
1688     shutdown_priority = level;
1689     return TRUE;
1690 }
1691
1692
1693 /***********************************************************************
1694  * GetProcessShutdownParameters                 (KERNEL32.@)
1695  *
1696  */
1697 BOOL WINAPI GetProcessShutdownParameters( LPDWORD lpdwLevel, LPDWORD lpdwFlags )
1698 {
1699     *lpdwLevel = shutdown_priority;
1700     *lpdwFlags = shutdown_flags;
1701     return TRUE;
1702 }
1703
1704
1705 /***********************************************************************
1706  *           SetProcessPriorityBoost    (KERNEL32.@)
1707  */
1708 BOOL WINAPI SetProcessPriorityBoost(HANDLE hprocess,BOOL disableboost)
1709 {
1710     FIXME("(%p,%d): stub\n",hprocess,disableboost);
1711     /* Say we can do it. I doubt the program will notice that we don't. */
1712     return TRUE;
1713 }
1714
1715
1716 /***********************************************************************
1717  *              ReadProcessMemory (KERNEL32.@)
1718  */
1719 BOOL WINAPI ReadProcessMemory( HANDLE process, LPCVOID addr, LPVOID buffer, SIZE_T size,
1720                                SIZE_T *bytes_read )
1721 {
1722     DWORD res;
1723
1724     SERVER_START_REQ( read_process_memory )
1725     {
1726         req->handle = process;
1727         req->addr   = (void *)addr;
1728         wine_server_set_reply( req, buffer, size );
1729         if ((res = wine_server_call_err( req ))) size = 0;
1730     }
1731     SERVER_END_REQ;
1732     if (bytes_read) *bytes_read = size;
1733     return !res;
1734 }
1735
1736
1737 /***********************************************************************
1738  *           WriteProcessMemory                 (KERNEL32.@)
1739  */
1740 BOOL WINAPI WriteProcessMemory( HANDLE process, LPVOID addr, LPCVOID buffer, SIZE_T size,
1741                                 SIZE_T *bytes_written )
1742 {
1743     static const int zero;
1744     unsigned int first_offset, last_offset, first_mask, last_mask;
1745     DWORD res;
1746
1747     if (!size)
1748     {
1749         SetLastError( ERROR_INVALID_PARAMETER );
1750         return FALSE;
1751     }
1752
1753     /* compute the mask for the first int */
1754     first_mask = ~0;
1755     first_offset = (unsigned int)addr % sizeof(int);
1756     memset( &first_mask, 0, first_offset );
1757
1758     /* compute the mask for the last int */
1759     last_offset = (size + first_offset) % sizeof(int);
1760     last_mask = 0;
1761     memset( &last_mask, 0xff, last_offset ? last_offset : sizeof(int) );
1762
1763     SERVER_START_REQ( write_process_memory )
1764     {
1765         req->handle     = process;
1766         req->addr       = (char *)addr - first_offset;
1767         req->first_mask = first_mask;
1768         req->last_mask  = last_mask;
1769         if (first_offset) wine_server_add_data( req, &zero, first_offset );
1770         wine_server_add_data( req, buffer, size );
1771         if (last_offset) wine_server_add_data( req, &zero, sizeof(int) - last_offset );
1772
1773         if ((res = wine_server_call_err( req ))) size = 0;
1774     }
1775     SERVER_END_REQ;
1776     if (bytes_written) *bytes_written = size;
1777     {
1778         char dummy[32];
1779         SIZE_T read;
1780         ReadProcessMemory( process, addr, dummy, sizeof(dummy), &read );
1781     }
1782     return !res;
1783 }
1784
1785
1786 /***********************************************************************
1787  *              RegisterServiceProcess (KERNEL.491)
1788  *              RegisterServiceProcess (KERNEL32.@)
1789  *
1790  * A service process calls this function to ensure that it continues to run
1791  * even after a user logged off.
1792  */
1793 DWORD WINAPI RegisterServiceProcess(DWORD dwProcessId, DWORD dwType)
1794 {
1795         /* I don't think that Wine needs to do anything in that function */
1796         return 1; /* success */
1797 }
1798
1799 /***********************************************************************
1800  * GetExitCodeProcess [KERNEL32.@]
1801  *
1802  * Gets termination status of specified process
1803  *
1804  * RETURNS
1805  *   Success: TRUE
1806  *   Failure: FALSE
1807  */
1808 BOOL WINAPI GetExitCodeProcess(
1809     HANDLE hProcess,    /* [in] handle to the process */
1810     LPDWORD lpExitCode) /* [out] address to receive termination status */
1811 {
1812     BOOL ret;
1813     SERVER_START_REQ( get_process_info )
1814     {
1815         req->handle = hProcess;
1816         ret = !wine_server_call_err( req );
1817         if (ret && lpExitCode) *lpExitCode = reply->exit_code;
1818     }
1819     SERVER_END_REQ;
1820     return ret;
1821 }
1822
1823
1824 /***********************************************************************
1825  *           SetErrorMode   (KERNEL32.@)
1826  */
1827 UINT WINAPI SetErrorMode( UINT mode )
1828 {
1829     UINT old = current_process.error_mode;
1830     current_process.error_mode = mode;
1831     return old;
1832 }
1833
1834
1835 /**************************************************************************
1836  *              SetFileApisToOEM   (KERNEL32.@)
1837  */
1838 VOID WINAPI SetFileApisToOEM(void)
1839 {
1840     current_process.flags |= PDB32_FILE_APIS_OEM;
1841 }
1842
1843
1844 /**************************************************************************
1845  *              SetFileApisToANSI   (KERNEL32.@)
1846  */
1847 VOID WINAPI SetFileApisToANSI(void)
1848 {
1849     current_process.flags &= ~PDB32_FILE_APIS_OEM;
1850 }
1851
1852
1853 /******************************************************************************
1854  * AreFileApisANSI [KERNEL32.@]  Determines if file functions are using ANSI
1855  *
1856  * RETURNS
1857  *    TRUE:  Set of file functions is using ANSI code page
1858  *    FALSE: Set of file functions is using OEM code page
1859  */
1860 BOOL WINAPI AreFileApisANSI(void)
1861 {
1862     return !(current_process.flags & PDB32_FILE_APIS_OEM);
1863 }
1864
1865
1866 /***********************************************************************
1867  *           GetTickCount       (KERNEL32.@)
1868  *
1869  * Returns the number of milliseconds, modulo 2^32, since the start
1870  * of the wineserver.
1871  */
1872 DWORD WINAPI GetTickCount(void)
1873 {
1874     struct timeval t;
1875     gettimeofday( &t, NULL );
1876     return ((t.tv_sec * 1000) + (t.tv_usec / 1000)) - server_startticks;
1877 }
1878
1879
1880 /**********************************************************************
1881  * TlsAlloc [KERNEL32.@]  Allocates a TLS index.
1882  *
1883  * Allocates a thread local storage index
1884  *
1885  * RETURNS
1886  *    Success: TLS Index
1887  *    Failure: 0xFFFFFFFF
1888  */
1889 DWORD WINAPI TlsAlloc( void )
1890 {
1891     DWORD i, mask, ret = 0;
1892     DWORD *bits = current_process.tls_bits;
1893     RtlAcquirePebLock();
1894     if (*bits == 0xffffffff)
1895     {
1896         bits++;
1897         ret = 32;
1898         if (*bits == 0xffffffff)
1899         {
1900             RtlReleasePebLock();
1901             SetLastError( ERROR_NO_MORE_ITEMS );
1902             return 0xffffffff;
1903         }
1904     }
1905     for (i = 0, mask = 1; i < 32; i++, mask <<= 1) if (!(*bits & mask)) break;
1906     *bits |= mask;
1907     RtlReleasePebLock();
1908     NtCurrentTeb()->tls_array[ret+i] = 0; /* clear the value */
1909     return ret + i;
1910 }
1911
1912
1913 /**********************************************************************
1914  * TlsFree [KERNEL32.@]  Releases a TLS index.
1915  *
1916  * Releases a thread local storage index, making it available for reuse
1917  *
1918  * RETURNS
1919  *    Success: TRUE
1920  *    Failure: FALSE
1921  */
1922 BOOL WINAPI TlsFree(
1923     DWORD index) /* [in] TLS Index to free */
1924 {
1925     DWORD mask = (1 << (index & 31));
1926     DWORD *bits = current_process.tls_bits;
1927     if (index >= 64)
1928     {
1929         SetLastError( ERROR_INVALID_PARAMETER );
1930         return FALSE;
1931     }
1932     if (index >= 32) bits++;
1933     RtlAcquirePebLock();
1934     if (!(*bits & mask))  /* already free? */
1935     {
1936         RtlReleasePebLock();
1937         SetLastError( ERROR_INVALID_PARAMETER );
1938         return FALSE;
1939     }
1940     *bits &= ~mask;
1941     NtCurrentTeb()->tls_array[index] = 0;
1942     /* FIXME: should zero all other thread values */
1943     RtlReleasePebLock();
1944     return TRUE;
1945 }
1946
1947
1948 /**********************************************************************
1949  * TlsGetValue [KERNEL32.@]  Gets value in a thread's TLS slot
1950  *
1951  * RETURNS
1952  *    Success: Value stored in calling thread's TLS slot for index
1953  *    Failure: 0 and GetLastError returns NO_ERROR
1954  */
1955 LPVOID WINAPI TlsGetValue(
1956     DWORD index) /* [in] TLS index to retrieve value for */
1957 {
1958     if (index >= 64)
1959     {
1960         SetLastError( ERROR_INVALID_PARAMETER );
1961         return NULL;
1962     }
1963     SetLastError( ERROR_SUCCESS );
1964     return NtCurrentTeb()->tls_array[index];
1965 }
1966
1967
1968 /**********************************************************************
1969  * TlsSetValue [KERNEL32.@]  Stores a value in the thread's TLS slot.
1970  *
1971  * RETURNS
1972  *    Success: TRUE
1973  *    Failure: FALSE
1974  */
1975 BOOL WINAPI TlsSetValue(
1976     DWORD index,  /* [in] TLS index to set value for */
1977     LPVOID value) /* [in] Value to be stored */
1978 {
1979     if (index >= 64)
1980     {
1981         SetLastError( ERROR_INVALID_PARAMETER );
1982         return FALSE;
1983     }
1984     NtCurrentTeb()->tls_array[index] = value;
1985     return TRUE;
1986 }
1987
1988
1989 /***********************************************************************
1990  *           GetCurrentProcess   (KERNEL32.@)
1991  */
1992 #undef GetCurrentProcess
1993 HANDLE WINAPI GetCurrentProcess(void)
1994 {
1995     return (HANDLE)0xffffffff;
1996 }