comctl32: A couple fixes for tab icon offsets.
[wine] / dlls / ntdll / loadorder.c
1 /*
2  * Dlls load order support
3  *
4  * Copyright 1999 Bertho Stultiens
5  * Copyright 2003 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <assert.h>
29
30 #include "windef.h"
31 #include "winternl.h"
32 #include "ntdll_misc.h"
33
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(module);
38
39 #define LOADORDER_ALLOC_CLUSTER 32      /* Allocate with 32 entries at a time */
40
41 typedef struct module_loadorder
42 {
43     const WCHAR        *modulename;
44     enum loadorder      loadorder;
45 } module_loadorder_t;
46
47 struct loadorder_list
48 {
49     int                 count;
50     int                 alloc;
51     module_loadorder_t *order;
52 };
53
54 /* dll to load as builtins if not explicitly specified otherwise */
55 /* the list must remain sorted by dll name */
56 static const WCHAR default_builtins[][10] =
57 {
58     { 'g','d','i','3','2',0 },
59     { 'i','c','m','p',0 },
60     { 'k','e','r','n','e','l','3','2',0 },
61     { 'n','t','d','l','l',0 },
62     { 'o','d','b','c','3','2',0 },
63     { 't','t','y','d','r','v',0 },
64     { 'u','s','e','r','3','2',0 },
65     { 'w','3','2','s','k','r','n','l',0 },
66     { 'w','i','n','e','d','o','s',0 },
67     { 'w','i','n','e','p','s',0 },
68     { 'w','i','n','m','m',0 },
69     { 'w','n','a','s','p','i','3','2',0 },
70     { 'w','o','w','3','2',0 },
71     { 'w','s','2','_','3','2',0 },
72     { 'w','s','o','c','k','3','2',0 },
73     { 'x','1','1','d','r','v',0 }
74 };
75
76 static const WCHAR separatorsW[] = {',',' ','\t',0};
77
78 static int init_done;
79 static struct loadorder_list env_list;
80
81
82 /***************************************************************************
83  *      cmp_sort_func   (internal, static)
84  *
85  * Sorting and comparing function used in sort and search of loadorder
86  * entries.
87  */
88 static int cmp_sort_func(const void *s1, const void *s2)
89 {
90     return strcmpiW(((const module_loadorder_t *)s1)->modulename, ((const module_loadorder_t *)s2)->modulename);
91 }
92
93
94 /***************************************************************************
95  *      strcmp_func
96  */
97 static int strcmp_func(const void *s1, const void *s2)
98 {
99     return strcmpiW( (const WCHAR *)s1, (const WCHAR *)s2 );
100 }
101
102
103 /***************************************************************************
104  *      get_basename
105  *
106  * Return the base name of a file name (i.e. remove the path components).
107  */
108 static const WCHAR *get_basename( const WCHAR *name )
109 {
110     const WCHAR *ptr;
111
112     if (name[0] && name[1] == ':') name += 2;  /* strip drive specification */
113     if ((ptr = strrchrW( name, '\\' ))) name = ptr + 1;
114     if ((ptr = strrchrW( name, '/' ))) name = ptr + 1;
115     return name;
116 }
117
118 /***************************************************************************
119  *      remove_dll_ext
120  *
121  * Remove extension if it is ".dll".
122  */
123 static inline void remove_dll_ext( WCHAR *ext )
124 {
125     if (ext[0] == '.' &&
126         toupperW(ext[1]) == 'D' &&
127         toupperW(ext[2]) == 'L' &&
128         toupperW(ext[3]) == 'L' &&
129         !ext[4]) ext[0] = 0;
130 }
131
132
133 /***************************************************************************
134  *      debugstr_loadorder
135  *
136  * Return a loadorder in printable form.
137  */
138 static const char *debugstr_loadorder( enum loadorder lo )
139 {
140     switch(lo)
141     {
142     case LO_DISABLED: return "";
143     case LO_NATIVE: return "n";
144     case LO_BUILTIN: return "b";
145     case LO_NATIVE_BUILTIN: return "n,b";
146     case LO_BUILTIN_NATIVE: return "b,n";
147     case LO_DEFAULT: return "default";
148     default: return "??";
149     }
150 }
151
152
153 /***************************************************************************
154  *      parse_load_order
155  *
156  * Parses the loadorder options from the configuration and puts it into
157  * a structure.
158  */
159 static enum loadorder parse_load_order( const WCHAR *order )
160 {
161     enum loadorder ret = LO_DISABLED;
162
163     while (*order)
164     {
165         order += strspnW( order, separatorsW );
166         switch(*order)
167         {
168         case 'N':  /* native */
169         case 'n':
170             if (ret == LO_DISABLED) ret = LO_NATIVE;
171             else if (ret == LO_BUILTIN) return LO_BUILTIN_NATIVE;
172             break;
173         case 'B':  /* builtin */
174         case 'b':
175             if (ret == LO_DISABLED) ret = LO_BUILTIN;
176             else if (ret == LO_NATIVE) return LO_NATIVE_BUILTIN;
177             break;
178         }
179         order += strcspnW( order, separatorsW );
180     }
181     return ret;
182 }
183
184
185 /***************************************************************************
186  *      add_load_order
187  *
188  * Adds an entry in the list of environment overrides.
189  */
190 static void add_load_order( const module_loadorder_t *plo )
191 {
192     int i;
193
194     for(i = 0; i < env_list.count; i++)
195     {
196         if(!cmp_sort_func(plo, &env_list.order[i] ))
197         {
198             /* replace existing option */
199             env_list.order[i].loadorder = plo->loadorder;
200             return;
201         }
202     }
203
204     if (i >= env_list.alloc)
205     {
206         /* No space in current array, make it larger */
207         env_list.alloc += LOADORDER_ALLOC_CLUSTER;
208         if (env_list.order)
209             env_list.order = RtlReAllocateHeap(GetProcessHeap(), 0, env_list.order,
210                                                env_list.alloc * sizeof(module_loadorder_t));
211         else
212             env_list.order = RtlAllocateHeap(GetProcessHeap(), 0,
213                                              env_list.alloc * sizeof(module_loadorder_t));
214         if(!env_list.order)
215         {
216             MESSAGE("Virtual memory exhausted\n");
217             exit(1);
218         }
219     }
220     env_list.order[i].loadorder  = plo->loadorder;
221     env_list.order[i].modulename = plo->modulename;
222     env_list.count++;
223 }
224
225
226 /***************************************************************************
227  *      add_load_order_set
228  *
229  * Adds a set of entries in the list of command-line overrides from the key parameter.
230  */
231 static void add_load_order_set( WCHAR *entry )
232 {
233     module_loadorder_t ldo;
234     WCHAR *end = strchrW( entry, '=' );
235
236     if (!end) return;
237     *end++ = 0;
238     ldo.loadorder = parse_load_order( end );
239
240     while (*entry)
241     {
242         entry += strspnW( entry, separatorsW );
243         end = entry + strcspnW( entry, separatorsW );
244         if (*end) *end++ = 0;
245         if (*entry)
246         {
247             WCHAR *ext = strrchrW(entry, '.');
248             if (ext) remove_dll_ext( ext );
249             ldo.modulename = entry;
250             add_load_order( &ldo );
251             entry = end;
252         }
253     }
254 }
255
256
257 /***************************************************************************
258  *      init_load_order
259  */
260 static void init_load_order(void)
261 {
262     const char *order = getenv( "WINEDLLOVERRIDES" );
263     UNICODE_STRING strW;
264     WCHAR *entry, *next;
265
266     init_done = 1;
267     if (!order) return;
268
269     if (!strcmp( order, "help" ))
270     {
271         MESSAGE( "Syntax:\n"
272                  "  WINEDLLOVERRIDES=\"entry;entry;entry...\"\n"
273                  "    where each entry is of the form:\n"
274                  "        module[,module...]={native|builtin}[,{b|n}]\n"
275                  "\n"
276                  "    Only the first letter of the override (native or builtin)\n"
277                  "    is significant.\n\n"
278                  "Example:\n"
279                  "  WINEDLLOVERRIDES=\"comdlg32=n,b;shell32,shlwapi=b\"\n" );
280         exit(0);
281     }
282
283     RtlCreateUnicodeStringFromAsciiz( &strW, order );
284     entry = strW.Buffer;
285     while (*entry)
286     {
287         while (*entry && *entry == ';') entry++;
288         if (!*entry) break;
289         next = strchrW( entry, ';' );
290         if (next) *next++ = 0;
291         else next = entry + strlenW(entry);
292         add_load_order_set( entry );
293         entry = next;
294     }
295
296     /* sort the array for quick lookup */
297     if (env_list.count)
298         qsort(env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func);
299
300     /* Note: we don't free the Unicode string because the
301      * stored module names point inside it */
302 }
303
304
305 /***************************************************************************
306  *      get_env_load_order
307  *
308  * Get the load order for a given module from the WINEDLLOVERRIDES environment variable.
309  */
310 static inline enum loadorder get_env_load_order( const WCHAR *module )
311 {
312     module_loadorder_t tmp, *res;
313
314     tmp.modulename = module;
315     /* some bsearch implementations (Solaris) are buggy when the number of items is 0 */
316     if (env_list.count &&
317         (res = bsearch(&tmp, env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func)))
318         return res->loadorder;
319     return LO_INVALID;
320 }
321
322
323 /***************************************************************************
324  *      get_default_load_order
325  *
326  * Get the load order for a given module from the default list.
327  */
328 static inline enum loadorder get_default_load_order( const WCHAR *module )
329 {
330     const int count = sizeof(default_builtins) / sizeof(default_builtins[0]);
331     if (!bsearch( module, default_builtins, count, sizeof(default_builtins[0]), strcmp_func ))
332         return FALSE;
333     TRACE( "got compiled-in builtin default for %s\n", debugstr_w(module) );
334     return LO_BUILTIN;
335 }
336
337
338 /***************************************************************************
339  *      get_standard_key
340  *
341  * Return a handle to the standard DllOverrides registry section.
342  */
343 static HANDLE get_standard_key(void)
344 {
345     static const WCHAR DllOverridesW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
346                                           'D','l','l','O','v','e','r','r','i','d','e','s',0};
347     static HANDLE std_key = (HANDLE)-1;
348
349     if (std_key == (HANDLE)-1)
350     {
351         OBJECT_ATTRIBUTES attr;
352         UNICODE_STRING nameW;
353         HANDLE root;
354
355         RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
356         attr.Length = sizeof(attr);
357         attr.RootDirectory = root;
358         attr.ObjectName = &nameW;
359         attr.Attributes = 0;
360         attr.SecurityDescriptor = NULL;
361         attr.SecurityQualityOfService = NULL;
362         RtlInitUnicodeString( &nameW, DllOverridesW );
363
364         /* @@ Wine registry key: HKCU\Software\Wine\DllOverrides */
365         if (NtOpenKey( &std_key, KEY_ALL_ACCESS, &attr )) std_key = 0;
366         NtClose( root );
367     }
368     return std_key;
369 }
370
371
372 /***************************************************************************
373  *      get_app_key
374  *
375  * Get the registry key for the app-specific DllOverrides list.
376  */
377 static HANDLE get_app_key( const WCHAR *app_name )
378 {
379     OBJECT_ATTRIBUTES attr;
380     UNICODE_STRING nameW;
381     HANDLE root;
382     WCHAR *str;
383     static const WCHAR AppDefaultsW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
384                                          'A','p','p','D','e','f','a','u','l','t','s','\\',0};
385     static const WCHAR DllOverridesW[] = {'\\','D','l','l','O','v','e','r','r','i','d','e','s',0};
386     static HANDLE app_key = (HANDLE)-1;
387
388     if (app_key != (HANDLE)-1) return app_key;
389
390     str = RtlAllocateHeap( GetProcessHeap(), 0,
391                            sizeof(AppDefaultsW) + sizeof(DllOverridesW) +
392                            strlenW(app_name) * sizeof(WCHAR) );
393     if (!str) return 0;
394     strcpyW( str, AppDefaultsW );
395     strcatW( str, app_name );
396     strcatW( str, DllOverridesW );
397
398     RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
399     attr.Length = sizeof(attr);
400     attr.RootDirectory = root;
401     attr.ObjectName = &nameW;
402     attr.Attributes = 0;
403     attr.SecurityDescriptor = NULL;
404     attr.SecurityQualityOfService = NULL;
405     RtlInitUnicodeString( &nameW, str );
406
407     /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DllOverrides */
408     if (NtOpenKey( &app_key, KEY_ALL_ACCESS, &attr )) app_key = 0;
409     NtClose( root );
410     RtlFreeHeap( GetProcessHeap(), 0, str );
411     return app_key;
412 }
413
414
415 /***************************************************************************
416  *      get_registry_value
417  *
418  * Load the registry loadorder value for a given module.
419  */
420 static enum loadorder get_registry_value( HANDLE hkey, const WCHAR *module )
421 {
422     UNICODE_STRING valueW;
423     char buffer[80];
424     DWORD count;
425     BOOL ret;
426
427     RtlInitUnicodeString( &valueW, module );
428
429     if ((ret = !NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation,
430                                  buffer, sizeof(buffer), &count )))
431     {
432         WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data;
433         return parse_load_order( str );
434     }
435     return LO_INVALID;
436 }
437
438
439 /***************************************************************************
440  *      get_load_order_value
441  *
442  * Get the load order for the exact specified module string, looking in:
443  * 1. The WINEDLLOVERRIDES environment variable
444  * 2. The per-application DllOverrides key
445  * 3. The standard DllOverrides key
446  */
447 static enum loadorder get_load_order_value( HANDLE std_key, HANDLE app_key, const WCHAR *module )
448 {
449     enum loadorder ret;
450
451     if ((ret = get_env_load_order( module )) != LO_INVALID)
452     {
453         TRACE( "got environment %s for %s\n", debugstr_loadorder(ret), debugstr_w(module) );
454         return ret;
455     }
456
457     if (app_key && ((ret = get_registry_value( app_key, module )) != LO_INVALID))
458     {
459         TRACE( "got app defaults %s for %s\n", debugstr_loadorder(ret), debugstr_w(module) );
460         return ret;
461     }
462
463     if (std_key && ((ret = get_registry_value( std_key, module )) != LO_INVALID))
464     {
465         TRACE( "got standard key %s for %s\n", debugstr_loadorder(ret), debugstr_w(module) );
466         return ret;
467     }
468
469     return ret;
470 }
471
472
473 /***************************************************************************
474  *      get_load_order   (internal)
475  *
476  * Return the loadorder of a module.
477  * The system directory and '.dll' extension is stripped from the path.
478  */
479 enum loadorder get_load_order( const WCHAR *app_name, const WCHAR *path )
480 {
481     static const WCHAR wildcardW[] = {'*',0};
482
483     enum loadorder ret = LO_INVALID;
484     HANDLE std_key, app_key = 0;
485     WCHAR *module, *basename;
486     UNICODE_STRING path_str;
487     int len;
488
489     if (!init_done) init_load_order();
490     std_key = get_standard_key();
491     if (app_name) app_key = get_app_key( app_name );
492
493     TRACE("looking for %s\n", debugstr_w(path));
494
495     /* Strip path information if the module resides in the system directory
496      */
497     RtlInitUnicodeString( &path_str, path );
498     if (RtlPrefixUnicodeString( &system_dir, &path_str, TRUE ))
499     {
500         const WCHAR *p = path + system_dir.Length / sizeof(WCHAR);
501         while (*p == '\\' || *p == '/') p++;
502         if (!strchrW( p, '\\' ) && !strchrW( p, '/' )) path = p;
503     }
504
505     if (!(len = strlenW(path))) return ret;
506     if (!(module = RtlAllocateHeap( GetProcessHeap(), 0, (len + 2) * sizeof(WCHAR) ))) return ret;
507     strcpyW( module+1, path );  /* reserve module[0] for the wildcard char */
508     basename = (WCHAR *)get_basename( module+1 );
509
510     if (len >= 4) remove_dll_ext( module + 1 + len - 4 );
511
512     /* first explicit module name */
513     if ((ret = get_load_order_value( std_key, app_key, module+1 )) != LO_INVALID)
514         goto done;
515
516     /* then module basename preceded by '*' */
517     basename[-1] = '*';
518     if ((ret = get_load_order_value( std_key, app_key, basename-1 )) != LO_INVALID)
519         goto done;
520
521     /* then module basename without '*' (only if explicit path) */
522     if (basename != module+1 && ((ret = get_load_order_value( std_key, app_key, basename )) != LO_INVALID))
523         goto done;
524
525     /* then compiled-in defaults */
526     if ((ret = get_default_load_order( basename )) != LO_INVALID)
527         goto done;
528
529     /* then wildcard entry (only if no explicit path) */
530     if (basename == module+1 && ((ret = get_load_order_value( std_key, app_key, wildcardW )) != LO_INVALID))
531         goto done;
532
533     /* and last the hard-coded default */
534     ret = LO_DEFAULT;
535     TRACE( "got hardcoded default %s for %s\n", debugstr_loadorder(ret), debugstr_w(path) );
536
537  done:
538     RtlFreeHeap( GetProcessHeap(), 0, module );
539     return ret;
540 }