winemac: Associate an event queue with each thread that creates windows and with...
[wine] / dlls / msvcrt / data.c
1 /*
2  * msvcrt.dll dll data items
3  *
4  * Copyright 2000 Jon Griffiths
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <math.h>
25 #include "msvcrt.h"
26 #include "wine/library.h"
27 #include "wine/unicode.h"
28 #include "wine/debug.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
31
32 int MSVCRT___argc = 0;
33 static int argc_expand;
34 static int wargc_expand;
35 unsigned int MSVCRT_basemajor = 0;/* FIXME: */
36 unsigned int MSVCRT_baseminor = 0;/* FIXME: */
37 unsigned int MSVCRT_baseversion = 0; /* FIXME: */
38 unsigned int MSVCRT__commode = 0;
39 unsigned int MSVCRT__fmode = 0;
40 unsigned int MSVCRT_osmajor = 0;/* FIXME: */
41 unsigned int MSVCRT_osminor = 0;/* FIXME: */
42 unsigned int MSVCRT_osmode = 0;/* FIXME: */
43 unsigned int MSVCRT__osver = 0;
44 unsigned int MSVCRT__osplatform = 0;
45 unsigned int MSVCRT_osversion = 0; /* FIXME: */
46 unsigned int MSVCRT__winmajor = 0;
47 unsigned int MSVCRT__winminor = 0;
48 unsigned int MSVCRT__winver = 0;
49 unsigned int MSVCRT___setlc_active = 0;
50 unsigned int MSVCRT___unguarded_readlc_active = 0;
51 double MSVCRT__HUGE = 0;
52 char **MSVCRT___argv = NULL;
53 static char **argv_expand;
54 MSVCRT_wchar_t **MSVCRT___wargv = NULL;
55 static MSVCRT_wchar_t **wargv_expand;
56 char *MSVCRT__acmdln = NULL;
57 MSVCRT_wchar_t *MSVCRT__wcmdln = NULL;
58 char **MSVCRT__environ = NULL;
59 MSVCRT_wchar_t **MSVCRT__wenviron = NULL;
60 char **MSVCRT___initenv = NULL;
61 MSVCRT_wchar_t **MSVCRT___winitenv = NULL;
62 int MSVCRT_app_type = 0;
63 char* MSVCRT__pgmptr = NULL;
64 WCHAR* MSVCRT__wpgmptr = NULL;
65
66 /* Get a snapshot of the current environment
67  * and construct the __p__environ array
68  *
69  * The pointer returned from GetEnvironmentStrings may get invalid when
70  * some other module cause a reallocation of the env-variable block
71  *
72  * blk is an array of pointers to environment strings, ending with a NULL
73  * and after that the actual copy of the environment strings, ending in a \0
74  */
75 char ** msvcrt_SnapshotOfEnvironmentA(char **blk)
76 {
77   char* environ_strings = GetEnvironmentStringsA();
78   int count = 1, len = 1, i = 0; /* keep space for the trailing NULLS */
79   char *ptr;
80
81   for (ptr = environ_strings; *ptr; ptr += strlen(ptr) + 1)
82   {
83     count++;
84     len += strlen(ptr) + 1;
85   }
86   if (blk)
87       blk = HeapReAlloc( GetProcessHeap(), 0, blk, count* sizeof(char*) + len );
88   else
89     blk = HeapAlloc(GetProcessHeap(), 0, count* sizeof(char*) + len );
90
91   if (blk)
92     {
93       if (count)
94         {
95           memcpy(&blk[count],environ_strings,len);
96           for (ptr = (char*) &blk[count]; *ptr; ptr += strlen(ptr) + 1)
97             {
98               blk[i++] = ptr;
99             }
100         }
101       blk[i] = NULL;
102     }
103   FreeEnvironmentStringsA(environ_strings);
104   return blk;
105 }
106
107 MSVCRT_wchar_t ** msvcrt_SnapshotOfEnvironmentW(MSVCRT_wchar_t **wblk)
108 {
109   MSVCRT_wchar_t* wenviron_strings = GetEnvironmentStringsW();
110   int count = 1, len = 1, i = 0; /* keep space for the trailing NULLS */
111   MSVCRT_wchar_t *wptr;
112
113   for (wptr = wenviron_strings; *wptr; wptr += strlenW(wptr) + 1)
114   {
115     count++;
116     len += strlenW(wptr) + 1;
117   }
118   if (wblk)
119       wblk = HeapReAlloc( GetProcessHeap(), 0, wblk, count* sizeof(MSVCRT_wchar_t*) + len * sizeof(MSVCRT_wchar_t));
120   else
121     wblk = HeapAlloc(GetProcessHeap(), 0, count* sizeof(MSVCRT_wchar_t*) + len * sizeof(MSVCRT_wchar_t));
122   if (wblk)
123     {
124       if (count)
125         {
126           memcpy(&wblk[count],wenviron_strings,len * sizeof(MSVCRT_wchar_t));
127           for (wptr = (MSVCRT_wchar_t*)&wblk[count]; *wptr; wptr += strlenW(wptr) + 1)
128             {
129               wblk[i++] = wptr;
130             }
131         }
132       wblk[i] = NULL;
133     }
134   FreeEnvironmentStringsW(wenviron_strings);
135   return wblk;
136 }
137
138 typedef void (CDECL *_INITTERMFUN)(void);
139 typedef int (CDECL *_INITTERM_E_FN)(void);
140
141 /***********************************************************************
142  *              __p___argc (MSVCRT.@)
143  */
144 int* CDECL __p___argc(void) { return &MSVCRT___argc; }
145
146 /***********************************************************************
147  *              __p__commode (MSVCRT.@)
148  */
149 unsigned int* CDECL __p__commode(void) { return &MSVCRT__commode; }
150
151
152 /***********************************************************************
153  *              __p__pgmptr (MSVCRT.@)
154  */
155 char** CDECL __p__pgmptr(void) { return &MSVCRT__pgmptr; }
156
157 /***********************************************************************
158  *              __p__wpgmptr (MSVCRT.@)
159  */
160 WCHAR** CDECL __p__wpgmptr(void) { return &MSVCRT__wpgmptr; }
161
162 /***********************************************************************
163  *              _get_pgmptr (MSVCRT.@)
164  */
165 int CDECL _get_pgmptr(char** p)
166 {
167   if (!MSVCRT_CHECK_PMT(p)) return MSVCRT_EINVAL;
168
169   *p = MSVCRT__pgmptr;
170   return 0;
171 }
172
173 /***********************************************************************
174  *              _get_wpgmptr (MSVCRT.@)
175  */
176 int CDECL _get_wpgmptr(WCHAR** p)
177 {
178   if (!MSVCRT_CHECK_PMT(p)) return MSVCRT_EINVAL;
179   *p = MSVCRT__wpgmptr;
180   return 0;
181 }
182
183 /***********************************************************************
184  *              __p__fmode (MSVCRT.@)
185  */
186 unsigned int* CDECL __p__fmode(void) { return &MSVCRT__fmode; }
187
188 /***********************************************************************
189  *              _set_fmode (MSVCRT.@)
190  */
191 int CDECL _set_fmode(int mode)
192 {
193     /* TODO: support _O_WTEXT */
194     if(!MSVCRT_CHECK_PMT(mode==MSVCRT__O_TEXT || mode==MSVCRT__O_BINARY))
195         return MSVCRT_EINVAL;
196
197     MSVCRT__fmode = mode;
198     return 0;
199 }
200
201 /***********************************************************************
202  *              _get_fmode (MSVCRT.@)
203  */
204 int CDECL _get_fmode(int *mode)
205 {
206     if(!MSVCRT_CHECK_PMT(mode))
207         return MSVCRT_EINVAL;
208
209     *mode = MSVCRT__fmode;
210     return 0;
211 }
212
213 /***********************************************************************
214  *              __p__osver (MSVCRT.@)
215  */
216 unsigned int* CDECL __p__osver(void) { return &MSVCRT__osver; }
217
218 /***********************************************************************
219  *              __p__winmajor (MSVCRT.@)
220  */
221 unsigned int* CDECL __p__winmajor(void) { return &MSVCRT__winmajor; }
222
223 /***********************************************************************
224  *              __p__winminor (MSVCRT.@)
225  */
226 unsigned int* CDECL __p__winminor(void) { return &MSVCRT__winminor; }
227
228 /***********************************************************************
229  *              __p__winver (MSVCRT.@)
230  */
231 unsigned int* CDECL __p__winver(void) { return &MSVCRT__winver; }
232
233 /*********************************************************************
234  *              __p__acmdln (MSVCRT.@)
235  */
236 char** CDECL __p__acmdln(void) { return &MSVCRT__acmdln; }
237
238 /*********************************************************************
239  *              __p__wcmdln (MSVCRT.@)
240  */
241 MSVCRT_wchar_t** CDECL __p__wcmdln(void) { return &MSVCRT__wcmdln; }
242
243 /*********************************************************************
244  *              __p___argv (MSVCRT.@)
245  */
246 char*** CDECL __p___argv(void) { return &MSVCRT___argv; }
247
248 /*********************************************************************
249  *              __p___wargv (MSVCRT.@)
250  */
251 MSVCRT_wchar_t*** CDECL __p___wargv(void) { return &MSVCRT___wargv; }
252
253 /*********************************************************************
254  *              __p__environ (MSVCRT.@)
255  */
256 char*** CDECL MSVCRT___p__environ(void)
257 {
258   return &MSVCRT__environ;
259 }
260
261 /*********************************************************************
262  *              __p__wenviron (MSVCRT.@)
263  */
264 MSVCRT_wchar_t*** CDECL MSVCRT___p__wenviron(void)
265 {
266   return &MSVCRT__wenviron;
267 }
268
269 /*********************************************************************
270  *              __p___initenv (MSVCRT.@)
271  */
272 char*** CDECL __p___initenv(void) { return &MSVCRT___initenv; }
273
274 /*********************************************************************
275  *              __p___winitenv (MSVCRT.@)
276  */
277 MSVCRT_wchar_t*** CDECL __p___winitenv(void) { return &MSVCRT___winitenv; }
278
279 /*********************************************************************
280  *              _get_osplatform (MSVCRT.@)
281  */
282 int CDECL MSVCRT__get_osplatform(int *pValue)
283 {
284     if (!MSVCRT_CHECK_PMT(pValue != NULL)) return MSVCRT_EINVAL;
285     *pValue = MSVCRT__osplatform;
286     return 0;
287 }
288
289 /* INTERNAL: Create a wide string from an ascii string */
290 MSVCRT_wchar_t *msvcrt_wstrdupa(const char *str)
291 {
292   const unsigned int len = strlen(str) + 1 ;
293   MSVCRT_wchar_t *wstr = MSVCRT_malloc(len* sizeof (MSVCRT_wchar_t));
294   if (!wstr)
295     return NULL;
296    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,str,len,wstr,len);
297   return wstr;
298 }
299
300 /*********************************************************************
301  *              ___unguarded_readlc_active_add_func (MSVCRT.@)
302  */
303 unsigned int * CDECL MSVCRT____unguarded_readlc_active_add_func(void)
304 {
305   return &MSVCRT___unguarded_readlc_active;
306 }
307
308 /*********************************************************************
309  *              ___setlc_active_func (MSVCRT.@)
310  */
311 unsigned int CDECL MSVCRT____setlc_active_func(void)
312 {
313   return MSVCRT___setlc_active;
314 }
315
316 /* INTERNAL: Since we can't rely on Winelib startup code calling w/getmainargs,
317  * we initialise data values during DLL loading. When called by a native
318  * program we simply return the data we've already initialised. This also means
319  * you can call multiple times without leaking
320  */
321 void msvcrt_init_args(void)
322 {
323   OSVERSIONINFOW osvi;
324
325   MSVCRT__acmdln = MSVCRT__strdup( GetCommandLineA() );
326   MSVCRT__wcmdln = msvcrt_wstrdupa(MSVCRT__acmdln);
327   MSVCRT___argc = __wine_main_argc;
328   MSVCRT___argv = __wine_main_argv;
329   MSVCRT___wargv = __wine_main_wargv;
330
331   TRACE("got %s, wide = %s argc=%d\n", debugstr_a(MSVCRT__acmdln),
332         debugstr_w(MSVCRT__wcmdln),MSVCRT___argc);
333
334   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
335   GetVersionExW( &osvi );
336   MSVCRT__winver     = (osvi.dwMajorVersion << 8) | osvi.dwMinorVersion;
337   MSVCRT__winmajor   = osvi.dwMajorVersion;
338   MSVCRT__winminor   = osvi.dwMinorVersion;
339   MSVCRT__osver      = osvi.dwBuildNumber;
340   MSVCRT__osplatform = osvi.dwPlatformId;
341   MSVCRT_osversion   = MSVCRT__winver;
342   MSVCRT_osmajor     = MSVCRT__winmajor;
343   MSVCRT_osminor     = MSVCRT__winminor;
344   MSVCRT_baseversion = MSVCRT__osver;
345   MSVCRT_baseminor   = MSVCRT_baseversion & 0xFF;
346   MSVCRT_basemajor   = (MSVCRT_baseversion >> 8) & 0xFF;
347   TRACE( "winver %08x winmajor %08x winminor %08x osver%08x baseversion %08x basemajor %08x baseminor %08x\n",
348           MSVCRT__winver, MSVCRT__winmajor, MSVCRT__winminor, MSVCRT__osver, MSVCRT_baseversion,
349           MSVCRT_basemajor, MSVCRT_baseminor);
350   TRACE( "osversion %08x osmajor %08x osminor %08x\n", MSVCRT_osversion, MSVCRT_osmajor, MSVCRT_osminor);
351
352   MSVCRT__HUGE = HUGE_VAL;
353   MSVCRT___setlc_active = 0;
354   MSVCRT___unguarded_readlc_active = 0;
355   MSVCRT__fmode = MSVCRT__O_TEXT;
356
357   MSVCRT__environ = msvcrt_SnapshotOfEnvironmentA(NULL);
358   MSVCRT___initenv = msvcrt_SnapshotOfEnvironmentA(NULL);
359   MSVCRT___winitenv = msvcrt_SnapshotOfEnvironmentW(NULL);
360
361   MSVCRT__pgmptr = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
362   if (MSVCRT__pgmptr)
363   {
364     if (!GetModuleFileNameA(0, MSVCRT__pgmptr, MAX_PATH))
365       MSVCRT__pgmptr[0] = '\0';
366     else
367       MSVCRT__pgmptr[MAX_PATH - 1] = '\0';
368   }
369
370   MSVCRT__wpgmptr = HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
371   if (MSVCRT__wpgmptr)
372   {
373     if (!GetModuleFileNameW(0, MSVCRT__wpgmptr, MAX_PATH))
374       MSVCRT__wpgmptr[0] = '\0';
375     else
376       MSVCRT__wpgmptr[MAX_PATH - 1] = '\0';
377   }
378 }
379
380 /* INTERNAL: free memory used by args */
381 void msvcrt_free_args(void)
382 {
383   /* FIXME: more things to free */
384   HeapFree(GetProcessHeap(), 0, MSVCRT___initenv);
385   HeapFree(GetProcessHeap(), 0, MSVCRT___winitenv);
386   HeapFree(GetProcessHeap(), 0, MSVCRT__environ);
387   HeapFree(GetProcessHeap(), 0, MSVCRT__wenviron);
388   HeapFree(GetProcessHeap(), 0, MSVCRT__pgmptr);
389   HeapFree(GetProcessHeap(), 0, MSVCRT__wpgmptr);
390   HeapFree(GetProcessHeap(), 0, argv_expand);
391   HeapFree(GetProcessHeap(), 0, wargv_expand);
392 }
393
394 static int build_expanded_argv(int *argc, char **argv)
395 {
396     int i, size=0, args_no=0, path_len;
397     BOOL is_expandable;
398     HANDLE h;
399
400     args_no = 0;
401     for(i=0; i<__wine_main_argc; i++) {
402         WIN32_FIND_DATAA data;
403         int len = 0;
404
405         is_expandable = FALSE;
406         for(path_len = strlen(__wine_main_argv[i])-1; path_len>=0; path_len--) {
407             if(__wine_main_argv[i][path_len]=='*' || __wine_main_argv[i][path_len]=='?')
408                 is_expandable = TRUE;
409             else if(__wine_main_argv[i][path_len]=='\\' || __wine_main_argv[i][path_len]=='/')
410                 break;
411         }
412         path_len++;
413
414         if(is_expandable)
415             h = FindFirstFileA(__wine_main_argv[i], &data);
416         else
417             h = INVALID_HANDLE_VALUE;
418
419         if(h != INVALID_HANDLE_VALUE) {
420             do {
421                 if(data.cFileName[0]=='.' && (data.cFileName[1]=='\0' ||
422                             (data.cFileName[1]=='.' && data.cFileName[2]=='\0')))
423                     continue;
424
425                 len = strlen(data.cFileName)+1;
426                 if(argv) {
427                     argv[args_no] = (char*)(argv+*argc+1)+size;
428                     memcpy(argv[args_no], __wine_main_argv[i], path_len*sizeof(char));
429                     memcpy(argv[args_no]+path_len, data.cFileName, len*sizeof(char));
430                 }
431                 args_no++;
432                 size += len+path_len;
433             }while(FindNextFileA(h, &data));
434             CloseHandle(h);
435         }
436
437         if(!len) {
438             len = strlen(__wine_main_argv[i])+1;
439             if(argv) {
440                 argv[args_no] = (char*)(argv+*argc+1)+size;
441                 memcpy(argv[args_no], __wine_main_argv[i], len*sizeof(char));
442             }
443             args_no++;
444             size += len;
445         }
446     }
447
448     if(argv)
449         argv[args_no] = NULL;
450     size += (args_no+1)*sizeof(char*);
451     *argc = args_no;
452     return size;
453 }
454
455 /*********************************************************************
456  *              __getmainargs (MSVCRT.@)
457  */
458 void CDECL __getmainargs(int *argc, char** *argv, char** *envp,
459                          int expand_wildcards, int *new_mode)
460 {
461     TRACE("(%p,%p,%p,%d,%p).\n", argc, argv, envp, expand_wildcards, new_mode);
462
463     if (expand_wildcards) {
464         HeapFree(GetProcessHeap(), 0, argv_expand);
465         argv_expand = NULL;
466
467         argv_expand = HeapAlloc(GetProcessHeap(), 0,
468                 build_expanded_argv(&argc_expand, NULL));
469         if (argv_expand) {
470             build_expanded_argv(&argc_expand, argv_expand);
471
472             MSVCRT___argc = argc_expand;
473             MSVCRT___argv = argv_expand;
474         }else {
475             expand_wildcards = 0;
476         }
477     }
478     if (!expand_wildcards) {
479         MSVCRT___argc = __wine_main_argc;
480         MSVCRT___argv = __wine_main_argv;
481     }
482
483     *argc = MSVCRT___argc;
484     *argv = MSVCRT___argv;
485     *envp = MSVCRT___initenv;
486
487     if (new_mode)
488         MSVCRT__set_new_mode( *new_mode );
489 }
490
491 static int build_expanded_wargv(int *argc, MSVCRT_wchar_t **argv)
492 {
493     int i, size=0, args_no=0, path_len;
494     BOOL is_expandable;
495     HANDLE h;
496
497     args_no = 0;
498     for(i=0; i<__wine_main_argc; i++) {
499         WIN32_FIND_DATAW data;
500         int len = 0;
501
502         is_expandable = FALSE;
503         for(path_len = strlenW(__wine_main_wargv[i])-1; path_len>=0; path_len--) {
504             if(__wine_main_wargv[i][path_len]=='*' || __wine_main_wargv[i][path_len]=='?')
505                 is_expandable = TRUE;
506             else if(__wine_main_wargv[i][path_len]=='\\' || __wine_main_wargv[i][path_len]=='/')
507                 break;
508         }
509         path_len++;
510
511         if(is_expandable)
512             h = FindFirstFileW(__wine_main_wargv[i], &data);
513         else
514             h = INVALID_HANDLE_VALUE;
515
516         if(h != INVALID_HANDLE_VALUE) {
517             do {
518                 if(data.cFileName[0]=='.' && (data.cFileName[1]=='\0' ||
519                             (data.cFileName[1]=='.' && data.cFileName[2]=='\0')))
520                     continue;
521
522                 len = strlenW(data.cFileName)+1;
523                 if(argv) {
524                     argv[args_no] = (MSVCRT_wchar_t*)(argv+*argc+1)+size;
525                     memcpy(argv[args_no], __wine_main_wargv[i], path_len*sizeof(MSVCRT_wchar_t));
526                     memcpy(argv[args_no]+path_len, data.cFileName, len*sizeof(MSVCRT_wchar_t));
527                 }
528                 args_no++;
529                 size += len+path_len;
530             }while(FindNextFileW(h, &data));
531             CloseHandle(h);
532         }
533
534         if(!len) {
535             len = strlenW(__wine_main_wargv[i])+1;
536             if(argv) {
537                 argv[args_no] = (MSVCRT_wchar_t*)(argv+*argc+1)+size;
538                 memcpy(argv[args_no], __wine_main_wargv[i], len*sizeof(MSVCRT_wchar_t));
539             }
540             args_no++;
541             size += len;
542         }
543     }
544
545     if(argv)
546         argv[args_no] = NULL;
547     size *= sizeof(MSVCRT_wchar_t);
548     size += (args_no+1)*sizeof(MSVCRT_wchar_t*);
549     *argc = args_no;
550     return size;
551 }
552
553 /*********************************************************************
554  *              __wgetmainargs (MSVCRT.@)
555  */
556 void CDECL __wgetmainargs(int *argc, MSVCRT_wchar_t** *wargv, MSVCRT_wchar_t** *wenvp,
557                           int expand_wildcards, int *new_mode)
558 {
559     TRACE("(%p,%p,%p,%d,%p).\n", argc, wargv, wenvp, expand_wildcards, new_mode);
560
561     if (expand_wildcards) {
562         HeapFree(GetProcessHeap(), 0, wargv_expand);
563         wargv_expand = NULL;
564
565         wargv_expand = HeapAlloc(GetProcessHeap(), 0,
566                 build_expanded_wargv(&wargc_expand, NULL));
567         if (wargv_expand) {
568             build_expanded_wargv(&wargc_expand, wargv_expand);
569
570             MSVCRT___argc = wargc_expand;
571             MSVCRT___wargv = wargv_expand;
572         }else {
573             expand_wildcards = 0;
574         }
575     }
576     if (!expand_wildcards) {
577         MSVCRT___argc = __wine_main_argc;
578         MSVCRT___wargv = __wine_main_wargv;
579     }
580
581     /* Initialize the _wenviron array if it's not already created. */
582     if (!MSVCRT__wenviron)
583         MSVCRT__wenviron = msvcrt_SnapshotOfEnvironmentW(NULL);
584     *argc = MSVCRT___argc;
585     *wargv = MSVCRT___wargv;
586     *wenvp = MSVCRT___winitenv;
587     if (new_mode)
588         MSVCRT__set_new_mode( *new_mode );
589 }
590
591 /*********************************************************************
592  *              _initterm (MSVCRT.@)
593  */
594 void CDECL _initterm(_INITTERMFUN *start,_INITTERMFUN *end)
595 {
596   _INITTERMFUN* current = start;
597
598   TRACE("(%p,%p)\n",start,end);
599   while (current<end)
600   {
601     if (*current)
602     {
603       TRACE("Call init function %p\n",*current);
604       (**current)();
605       TRACE("returned\n");
606     }
607     current++;
608   }
609 }
610
611 /*********************************************************************
612  *  _initterm_e (MSVCRT.@)
613  *
614  * call an array of application initialization functions and report the return value
615  */
616 int CDECL _initterm_e(_INITTERM_E_FN *table, _INITTERM_E_FN *end)
617 {
618     int res = 0;
619
620     TRACE("(%p, %p)\n", table, end);
621
622     while (!res && table < end) {
623         if (*table) {
624             TRACE("calling %p\n", **table);
625             res = (**table)();
626             if (res)
627                 TRACE("function %p failed: 0x%x\n", *table, res);
628
629         }
630         table++;
631     }
632     return res;
633 }
634
635 /*********************************************************************
636  *              __set_app_type (MSVCRT.@)
637  */
638 void CDECL MSVCRT___set_app_type(int app_type)
639 {
640   TRACE("(%d) %s application\n", app_type, app_type == 2 ? "Gui" : "Console");
641   MSVCRT_app_type = app_type;
642 }