Enable for GNU/kFreeBSD the same kernel-specific interface/parameter
[wine/testsucceed.git] / dlls / ntdll / loadorder.c
blobdfc852f732acf782ad5fdcd713a2bcafc4101735
1 /*
2 * Dlls load order support
4 * Copyright 1999 Bertho Stultiens
5 * Copyright 2003 Alexandre Julliard
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.
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.
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
22 #include "config.h"
23 #include "wine/port.h"
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <assert.h>
30 #include "windef.h"
31 #include "winternl.h"
32 #include "ntdll_misc.h"
33 #include "module.h"
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(module);
40 #define LOADORDER_ALLOC_CLUSTER 32 /* Allocate with 32 entries at a time */
42 typedef struct module_loadorder
44 const WCHAR *modulename;
45 enum loadorder_type loadorder[LOADORDER_NTYPES];
46 } module_loadorder_t;
48 struct loadorder_list
50 int count;
51 int alloc;
52 module_loadorder_t *order;
55 /* dll to load as builtins if not explicitly specified otherwise */
56 /* the list must remain sorted by dll name */
57 static const WCHAR default_builtins[][10] =
59 { 'g','d','i','3','2',0 },
60 { 'i','c','m','p',0 },
61 { 'k','e','r','n','e','l','3','2',0 },
62 { 'n','t','d','l','l',0 },
63 { 'o','d','b','c','3','2',0 },
64 { 't','t','y','d','r','v',0 },
65 { 'u','s','e','r','3','2',0 },
66 { 'w','3','2','s','k','r','n','l',0 },
67 { 'w','i','n','e','d','o','s',0 },
68 { 'w','i','n','e','p','s',0 },
69 { 'w','i','n','m','m',0 },
70 { 'w','n','a','s','p','i','3','2',0 },
71 { 'w','o','w','3','2',0 },
72 { 'w','s','2','_','3','2',0 },
73 { 'w','s','o','c','k','3','2',0 },
74 { 'x','1','1','d','r','v',0 }
77 /* default if nothing else specified */
78 static const enum loadorder_type default_loadorder[LOADORDER_NTYPES] =
80 LOADORDER_BI, LOADORDER_DLL, 0
83 static const WCHAR separatorsW[] = {',',' ','\t',0};
85 static int init_done;
86 static struct loadorder_list env_list;
89 /***************************************************************************
90 * cmp_sort_func (internal, static)
92 * Sorting and comparing function used in sort and search of loadorder
93 * entries.
95 static int cmp_sort_func(const void *s1, const void *s2)
97 return strcmpiW(((const module_loadorder_t *)s1)->modulename, ((const module_loadorder_t *)s2)->modulename);
101 /***************************************************************************
102 * strcmp_func
104 static int strcmp_func(const void *s1, const void *s2)
106 return strcmpiW( (const WCHAR *)s1, (const WCHAR *)s2 );
110 /***************************************************************************
111 * get_basename
113 * Return the base name of a file name (i.e. remove the path components).
115 static const WCHAR *get_basename( const WCHAR *name )
117 const WCHAR *ptr;
119 if (name[0] && name[1] == ':') name += 2; /* strip drive specification */
120 if ((ptr = strrchrW( name, '\\' ))) name = ptr + 1;
121 if ((ptr = strrchrW( name, '/' ))) name = ptr + 1;
122 return name;
125 /***************************************************************************
126 * remove_dll_ext
128 * Remove extension if it is ".dll".
130 static inline void remove_dll_ext( WCHAR *ext )
132 if (ext[0] == '.' &&
133 toupperW(ext[1]) == 'D' &&
134 toupperW(ext[2]) == 'L' &&
135 toupperW(ext[3]) == 'L' &&
136 !ext[4]) ext[0] = 0;
140 /***************************************************************************
141 * debugstr_loadorder
143 * Return a loadorder in printable form.
145 static const char *debugstr_loadorder( enum loadorder_type lo[] )
147 int i;
148 char buffer[LOADORDER_NTYPES*3+1];
150 buffer[0] = 0;
151 for(i = 0; i < LOADORDER_NTYPES; i++)
153 if (lo[i] == LOADORDER_INVALID) break;
154 switch(lo[i])
156 case LOADORDER_DLL: strcat( buffer, "n," ); break;
157 case LOADORDER_BI: strcat( buffer, "b," ); break;
158 default: strcat( buffer, "?," ); break;
161 if (buffer[0]) buffer[strlen(buffer)-1] = 0;
162 return debugstr_a(buffer);
166 /***************************************************************************
167 * append_load_order
169 * Append a load order to the list if necessary.
171 static void append_load_order(enum loadorder_type lo[], enum loadorder_type append)
173 int i;
175 for (i = 0; i < LOADORDER_NTYPES; i++)
177 if (lo[i] == LOADORDER_INVALID) /* append it here */
179 lo[i++] = append;
180 lo[i] = LOADORDER_INVALID;
181 return;
183 if (lo[i] == append) return; /* already in the list */
185 assert(0); /* cannot get here */
189 /***************************************************************************
190 * parse_load_order
192 * Parses the loadorder options from the configuration and puts it into
193 * a structure.
195 static void parse_load_order( const WCHAR *order, enum loadorder_type lo[] )
197 lo[0] = LOADORDER_INVALID;
198 while (*order)
200 order += strspnW( order, separatorsW );
201 switch(*order)
203 case 'N': /* Native */
204 case 'n':
205 append_load_order( lo, LOADORDER_DLL );
206 break;
207 case 'B': /* Builtin */
208 case 'b':
209 append_load_order( lo, LOADORDER_BI );
210 break;
212 order += strcspnW( order, separatorsW );
217 /***************************************************************************
218 * add_load_order
220 * Adds an entry in the list of environment overrides.
222 static void add_load_order( const module_loadorder_t *plo )
224 int i;
226 for(i = 0; i < env_list.count; i++)
228 if(!cmp_sort_func(plo, &env_list.order[i] ))
230 /* replace existing option */
231 memcpy( env_list.order[i].loadorder, plo->loadorder, sizeof(plo->loadorder));
232 return;
236 if (i >= env_list.alloc)
238 /* No space in current array, make it larger */
239 env_list.alloc += LOADORDER_ALLOC_CLUSTER;
240 if (env_list.order)
241 env_list.order = RtlReAllocateHeap(GetProcessHeap(), 0, env_list.order,
242 env_list.alloc * sizeof(module_loadorder_t));
243 else
244 env_list.order = RtlAllocateHeap(GetProcessHeap(), 0,
245 env_list.alloc * sizeof(module_loadorder_t));
246 if(!env_list.order)
248 MESSAGE("Virtual memory exhausted\n");
249 exit(1);
252 memcpy(env_list.order[i].loadorder, plo->loadorder, sizeof(plo->loadorder));
253 env_list.order[i].modulename = plo->modulename;
254 env_list.count++;
258 /***************************************************************************
259 * add_load_order_set
261 * Adds a set of entries in the list of command-line overrides from the key parameter.
263 static void add_load_order_set( WCHAR *entry )
265 module_loadorder_t ldo;
266 WCHAR *end = strchrW( entry, '=' );
268 if (!end) return;
269 *end++ = 0;
270 parse_load_order( end, ldo.loadorder );
272 while (*entry)
274 entry += strspnW( entry, separatorsW );
275 end = entry + strcspnW( entry, separatorsW );
276 if (*end) *end++ = 0;
277 if (*entry)
279 WCHAR *ext = strrchrW(entry, '.');
280 if (ext) remove_dll_ext( ext );
281 ldo.modulename = entry;
282 add_load_order( &ldo );
283 entry = end;
289 /***************************************************************************
290 * init_load_order
292 static void init_load_order(void)
294 const char *order = getenv( "WINEDLLOVERRIDES" );
295 UNICODE_STRING strW;
296 WCHAR *entry, *next;
298 init_done = 1;
299 if (!order) return;
301 if (!strcmp( order, "help" ))
303 MESSAGE( "Syntax:\n"
304 " WINEDLLOVERRIDES=\"entry;entry;entry...\"\n"
305 " where each entry is of the form:\n"
306 " module[,module...]={native|builtin}[,{b|n}]\n"
307 "\n"
308 " Only the first letter of the override (native or builtin)\n"
309 " is significant.\n\n"
310 "Example:\n"
311 " WINEDLLOVERRIDES=\"comdlg32=n,b;shell32,shlwapi=b\"\n" );
312 exit(0);
315 RtlCreateUnicodeStringFromAsciiz( &strW, order );
316 entry = strW.Buffer;
317 while (*entry)
319 while (*entry && *entry == ';') entry++;
320 if (!*entry) break;
321 next = strchrW( entry, ';' );
322 if (next) *next++ = 0;
323 else next = entry + strlenW(entry);
324 add_load_order_set( entry );
325 entry = next;
328 /* sort the array for quick lookup */
329 if (env_list.count)
330 qsort(env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func);
332 /* Note: we don't free the Unicode string because the
333 * stored module names point inside it */
337 /***************************************************************************
338 * get_env_load_order
340 * Get the load order for a given module from the WINEDLLOVERRIDES environment variable.
342 static inline BOOL get_env_load_order( const WCHAR *module, enum loadorder_type lo[] )
344 module_loadorder_t tmp, *res = NULL;
346 tmp.modulename = module;
347 /* some bsearch implementations (Solaris) are buggy when the number of items is 0 */
348 if (env_list.count &&
349 (res = bsearch(&tmp, env_list.order, env_list.count, sizeof(env_list.order[0]), cmp_sort_func)))
350 memcpy( lo, res->loadorder, sizeof(res->loadorder) );
351 return (res != NULL);
355 /***************************************************************************
356 * get_default_load_order
358 * Get the load order for a given module from the default list.
360 static inline BOOL get_default_load_order( const WCHAR *module, enum loadorder_type lo[] )
362 const int count = sizeof(default_builtins) / sizeof(default_builtins[0]);
363 if (!bsearch( module, default_builtins, count, sizeof(default_builtins[0]), strcmp_func ))
364 return FALSE;
365 lo[0] = LOADORDER_BI;
366 lo[1] = LOADORDER_INVALID;
367 TRACE( "got compiled-in default %s for %s\n", debugstr_loadorder(lo), debugstr_w(module) );
368 return TRUE;
372 /***************************************************************************
373 * get_standard_key
375 * Return a handle to the standard DllOverrides registry section.
377 static HANDLE get_standard_key(void)
379 static const WCHAR DllOverridesW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
380 'D','l','l','O','v','e','r','r','i','d','e','s',0};
381 static HANDLE std_key = (HANDLE)-1;
383 if (std_key == (HANDLE)-1)
385 OBJECT_ATTRIBUTES attr;
386 UNICODE_STRING nameW;
387 HANDLE root;
389 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
390 attr.Length = sizeof(attr);
391 attr.RootDirectory = root;
392 attr.ObjectName = &nameW;
393 attr.Attributes = 0;
394 attr.SecurityDescriptor = NULL;
395 attr.SecurityQualityOfService = NULL;
396 RtlInitUnicodeString( &nameW, DllOverridesW );
398 /* @@ Wine registry key: HKCU\Software\Wine\DllOverrides */
399 if (NtOpenKey( &std_key, KEY_ALL_ACCESS, &attr )) std_key = 0;
400 NtClose( root );
402 return std_key;
406 /***************************************************************************
407 * get_app_key
409 * Get the registry key for the app-specific DllOverrides list.
411 static HANDLE get_app_key( const WCHAR *app_name )
413 OBJECT_ATTRIBUTES attr;
414 UNICODE_STRING nameW;
415 HANDLE root;
416 WCHAR *str;
417 static const WCHAR AppDefaultsW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
418 'A','p','p','D','e','f','a','u','l','t','s','\\',0};
419 static const WCHAR DllOverridesW[] = {'\\','D','l','l','O','v','e','r','r','i','d','e','s',0};
420 static HANDLE app_key = (HANDLE)-1;
422 if (app_key != (HANDLE)-1) return app_key;
424 str = RtlAllocateHeap( GetProcessHeap(), 0,
425 sizeof(AppDefaultsW) + sizeof(DllOverridesW) +
426 strlenW(app_name) * sizeof(WCHAR) );
427 if (!str) return 0;
428 strcpyW( str, AppDefaultsW );
429 strcatW( str, app_name );
430 strcatW( str, DllOverridesW );
432 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
433 attr.Length = sizeof(attr);
434 attr.RootDirectory = root;
435 attr.ObjectName = &nameW;
436 attr.Attributes = 0;
437 attr.SecurityDescriptor = NULL;
438 attr.SecurityQualityOfService = NULL;
439 RtlInitUnicodeString( &nameW, str );
441 /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DllOverrides */
442 if (NtOpenKey( &app_key, KEY_ALL_ACCESS, &attr )) app_key = 0;
443 NtClose( root );
444 RtlFreeHeap( GetProcessHeap(), 0, str );
445 return app_key;
449 /***************************************************************************
450 * get_registry_value
452 * Load the registry loadorder value for a given module.
454 static BOOL get_registry_value( HANDLE hkey, const WCHAR *module, enum loadorder_type lo[] )
456 UNICODE_STRING valueW;
457 char buffer[80];
458 DWORD count;
459 BOOL ret;
461 RtlInitUnicodeString( &valueW, module );
463 if ((ret = !NtQueryValueKey( hkey, &valueW, KeyValuePartialInformation,
464 buffer, sizeof(buffer), &count )))
466 int i, n = 0;
467 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data;
469 while (*str)
471 enum loadorder_type type = LOADORDER_INVALID;
473 while (*str == ',' || isspaceW(*str)) str++;
474 if (!*str) break;
476 switch(tolowerW(*str))
478 case 'n': type = LOADORDER_DLL; break;
479 case 'b': type = LOADORDER_BI; break;
480 case 's': break; /* no longer supported, ignore */
481 case 0: break; /* end of string */
482 default:
483 ERR("Invalid load order module-type %s, ignored\n", debugstr_w(str));
484 break;
486 if (type != LOADORDER_INVALID)
488 for (i = 0; i < n; i++) if (lo[i] == type) break; /* already specified */
489 if (i == n) lo[n++] = type;
491 while (*str && *str != ',' && !isspaceW(*str)) str++;
493 lo[n] = LOADORDER_INVALID;
495 return ret;
499 /***************************************************************************
500 * get_load_order_value
502 * Get the load order for the exact specified module string, looking in:
503 * 1. The WINEDLLOVERRIDES environment variable
504 * 2. The per-application DllOverrides key
505 * 3. The standard DllOverrides key
507 static BOOL get_load_order_value( HANDLE std_key, HANDLE app_key, const WCHAR *module,
508 enum loadorder_type loadorder[] )
510 if (get_env_load_order( module, loadorder ))
512 TRACE( "got environment %s for %s\n",
513 debugstr_loadorder(loadorder), debugstr_w(module) );
514 return TRUE;
517 if (app_key && get_registry_value( app_key, module, loadorder ))
519 TRACE( "got app defaults %s for %s\n",
520 debugstr_loadorder(loadorder), debugstr_w(module) );
521 return TRUE;
524 if (std_key && get_registry_value( std_key, module, loadorder ))
526 TRACE( "got standard key %s for %s\n",
527 debugstr_loadorder(loadorder), debugstr_w(module) );
528 return TRUE;
530 return FALSE;
534 /***************************************************************************
535 * MODULE_GetLoadOrderW (internal)
537 * Return the loadorder of a module.
538 * The system directory and '.dll' extension is stripped from the path.
540 void MODULE_GetLoadOrderW( enum loadorder_type loadorder[], const WCHAR *app_name,
541 const WCHAR *path )
543 static const WCHAR wildcardW[] = {'*',0};
545 HANDLE std_key, app_key = 0;
546 WCHAR *module, *basename;
547 UNICODE_STRING path_str;
548 int len;
550 if (!init_done) init_load_order();
551 std_key = get_standard_key();
552 if (app_name) app_key = get_app_key( app_name );
554 TRACE("looking for %s\n", debugstr_w(path));
556 loadorder[0] = LOADORDER_INVALID; /* in case something bad happens below */
558 /* Strip path information if the module resides in the system directory
560 RtlInitUnicodeString( &path_str, path );
561 if (RtlPrefixUnicodeString( &system_dir, &path_str, TRUE ))
563 const WCHAR *p = path + system_dir.Length / sizeof(WCHAR);
564 while (*p == '\\' || *p == '/') p++;
565 if (!strchrW( p, '\\' ) && !strchrW( p, '/' )) path = p;
568 if (!(len = strlenW(path))) return;
569 if (!(module = RtlAllocateHeap( GetProcessHeap(), 0, (len + 2) * sizeof(WCHAR) ))) return;
570 strcpyW( module+1, path ); /* reserve module[0] for the wildcard char */
571 basename = (WCHAR *)get_basename( module+1 );
573 if (len >= 4) remove_dll_ext( module + 1 + len - 4 );
575 /* first explicit module name */
576 if (get_load_order_value( std_key, app_key, module+1, loadorder ))
577 goto done;
579 /* then module basename preceded by '*' */
580 basename[-1] = '*';
581 if (get_load_order_value( std_key, app_key, basename-1, loadorder ))
582 goto done;
584 /* then module basename without '*' (only if explicit path) */
585 if (basename != module+1 && get_load_order_value( std_key, app_key, basename, loadorder ))
586 goto done;
588 /* then compiled-in defaults */
589 if (get_default_load_order( basename, loadorder ))
590 goto done;
592 /* then wildcard entry (only if no explicit path) */
593 if (basename == module+1 && get_load_order_value( std_key, app_key, wildcardW, loadorder ))
594 goto done;
596 /* and last the hard-coded default */
597 memcpy( loadorder, default_loadorder, sizeof(default_loadorder) );
598 TRACE( "got hardcoded default %s for %s\n",
599 debugstr_loadorder(loadorder), debugstr_w(path) );
601 done:
602 RtlFreeHeap( GetProcessHeap(), 0, module );