Added support for path wildcards of the form "*dllname" in load order
[wine] / loader / loadorder.c
1 /*
2  * Module/Library loadorder
3  *
4  * Copyright 1999 Bertho Stultiens
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
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26
27 #include "windef.h"
28 #include "winreg.h"
29 #include "winerror.h"
30 #include "file.h"
31 #include "module.h"
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(module);
35
36 #define LOADORDER_ALLOC_CLUSTER 32      /* Allocate with 32 entries at a time */
37
38 typedef struct module_loadorder
39 {
40     const char         *modulename;
41     enum loadorder_type loadorder[LOADORDER_NTYPES];
42 } module_loadorder_t;
43
44 struct loadorder_list
45 {
46     int                 count;
47     int                 alloc;
48     module_loadorder_t *order;
49 };
50
51 /* default load-order if nothing specified */
52 /* the list must remain sorted by dll name */
53 static module_loadorder_t default_order_list[] =
54 {
55     { "display",      { LOADORDER_BI,  0,             0, 0 } },
56     { "gdi.exe",      { LOADORDER_BI,  0,             0, 0 } },
57     { "gdi32",        { LOADORDER_BI,  0,             0, 0 } },
58     { "glide2x",      { LOADORDER_SO,  LOADORDER_DLL, 0, 0 } },
59     { "glide3x",      { LOADORDER_SO,  LOADORDER_DLL, 0, 0 } },
60     { "icmp",         { LOADORDER_BI,  0,             0, 0 } },
61     { "kernel",       { LOADORDER_BI,  0,             0, 0 } },
62     { "kernel32",     { LOADORDER_BI,  0,             0, 0 } },
63     { "keyboard",     { LOADORDER_BI,  0,             0, 0 } },
64     { "krnl386.exe",  { LOADORDER_BI,  0,             0, 0 } },
65     { "mmsystem",     { LOADORDER_BI,  0,             0, 0 } },
66     { "mouse",        { LOADORDER_BI,  0,             0, 0 } },
67     { "ntdll",        { LOADORDER_BI,  0,             0, 0 } },
68     { "odbc32",       { LOADORDER_BI,  0,             0, 0 } },
69     { "system",       { LOADORDER_BI,  0,             0, 0 } },
70     { "toolhelp",     { LOADORDER_BI,  0,             0, 0 } },
71     { "ttydrv",       { LOADORDER_BI,  0,             0, 0 } },
72     { "user.exe",     { LOADORDER_BI,  0,             0, 0 } },
73     { "user32",       { LOADORDER_BI,  0,             0, 0 } },
74     { "w32skrnl",     { LOADORDER_BI,  0,             0, 0 } },
75     { "winaspi",      { LOADORDER_BI,  0,             0, 0 } },
76     { "windebug",     { LOADORDER_DLL, LOADORDER_BI,  0, 0 } },
77     { "winedos",      { LOADORDER_BI,  0,             0, 0 } },
78     { "wineps",       { LOADORDER_BI,  0,             0, 0 } },
79     { "wing",         { LOADORDER_BI,  0,             0, 0 } },
80     { "winmm",        { LOADORDER_BI,  0,             0, 0 } },
81     { "winsock",      { LOADORDER_BI,  0,             0, 0 } },
82     { "wnaspi32",     { LOADORDER_BI,  0,             0, 0 } },
83     { "wow32",        { LOADORDER_BI,  0,             0, 0 } },
84     { "wprocs",       { LOADORDER_BI,  0,             0, 0 } },
85     { "ws2_32",       { LOADORDER_BI,  0,             0, 0 } },
86     { "wsock32",      { LOADORDER_BI,  0,             0, 0 } },
87     { "x11drv",       { LOADORDER_BI,  0,             0, 0 } }
88 };
89
90 static const struct loadorder_list default_list =
91 {
92     sizeof(default_order_list)/sizeof(default_order_list[0]),
93     sizeof(default_order_list)/sizeof(default_order_list[0]),
94     default_order_list
95 };
96
97 /* default if nothing else specified */
98 static const enum loadorder_type default_loadorder[LOADORDER_NTYPES] =
99 {
100     LOADORDER_DLL, LOADORDER_BI, 0, 0
101 };
102
103 static struct loadorder_list cmdline_list;
104
105
106 /***************************************************************************
107  *      cmp_sort_func   (internal, static)
108  *
109  * Sorting and comparing function used in sort and search of loadorder
110  * entries.
111  */
112 static int cmp_sort_func(const void *s1, const void *s2)
113 {
114     return FILE_strcasecmp(((module_loadorder_t *)s1)->modulename,
115                            ((module_loadorder_t *)s2)->modulename);
116 }
117
118
119 /***************************************************************************
120  *      get_tok (internal, static)
121  *
122  * strtok wrapper for non-destructive buffer writing.
123  * NOTE: strtok is not reentrant and therefore this code is neither.
124  */
125 static char *get_tok(const char *str, const char *delim)
126 {
127         static char *buf = NULL;
128         char *cptr;
129
130         if(!str && !buf)
131                 return NULL;
132
133         if(str && buf)
134         {
135                 HeapFree(GetProcessHeap(), 0, buf);
136                 buf = NULL;
137         }
138
139         if(str && !buf)
140         {
141                 buf = HeapAlloc(GetProcessHeap(), 0, strlen(str)+1);
142                 strcpy( buf, str );
143                 cptr = strtok(buf, delim);
144         }
145         else
146         {
147                 cptr = strtok(NULL, delim);
148         }
149
150         if(!cptr)
151         {
152                 HeapFree(GetProcessHeap(), 0, buf);
153                 buf = NULL;
154         }
155         return cptr;
156 }
157
158
159 /***************************************************************************
160  *      get_basename
161  *
162  * Return the base name of a file name (i.e. remove the path components).
163  */
164 static const char *get_basename( const char *name )
165 {
166     const char *ptr;
167
168     if (name[0] && name[1] == ':') name += 2;  /* strip drive specification */
169     if ((ptr = strrchr( name, '\\' ))) name = ptr + 1;
170     if ((ptr = strrchr( name, '/' ))) name = ptr + 1;
171     return name;
172 }
173
174
175 /***************************************************************************
176  *      debugstr_loadorder
177  *
178  * Return a loadorder in printable form.
179  */
180 static const char *debugstr_loadorder( enum loadorder_type lo[] )
181 {
182     int i;
183     char buffer[LOADORDER_NTYPES*3+1];
184
185     buffer[0] = 0;
186     for(i = 0; i < LOADORDER_NTYPES; i++)
187     {
188         if (lo[i] == LOADORDER_INVALID) break;
189         switch(lo[i])
190         {
191         case LOADORDER_DLL: strcat( buffer, "n," ); break;
192         case LOADORDER_SO:  strcat( buffer, "s," ); break;
193         case LOADORDER_BI:  strcat( buffer, "b," ); break;
194         default:            strcat( buffer, "?," ); break;
195         }
196     }
197     if (buffer[0]) buffer[strlen(buffer)-1] = 0;
198     return debugstr_a(buffer);
199 }
200
201
202 /***************************************************************************
203  *      ParseLoadOrder  (internal, static)
204  *
205  * Parses the loadorder options from the configuration and puts it into
206  * a structure.
207  */
208 static BOOL ParseLoadOrder(char *order, enum loadorder_type lo[])
209 {
210     static int warn;
211         char *cptr;
212         int n = 0;
213
214         cptr = get_tok(order, ", \t");
215         while(cptr)
216         {
217             enum loadorder_type type = LOADORDER_INVALID;
218
219                 if(n >= LOADORDER_NTYPES-1)
220                 {
221                         ERR("More than existing %d module-types specified, rest ignored\n", LOADORDER_NTYPES-1);
222                         break;
223                 }
224
225                 switch(*cptr)
226                 {
227                 case 'N':       /* Native */
228                 case 'n': type = LOADORDER_DLL; break;
229
230                 case 'E':       /* Elfdll */
231                 case 'e':
232                     if (!warn++) MESSAGE("Load order 'elfdll' no longer supported, ignored\n");
233                     break;
234                 case 'S':       /* So */
235                 case 's': type = LOADORDER_SO; break;
236
237                 case 'B':       /* Builtin */
238                 case 'b': type = LOADORDER_BI; break;
239
240                 default:
241                         ERR("Invalid load order module-type '%s', ignored\n", cptr);
242                 }
243
244                 if(type != LOADORDER_INVALID) lo[n++] = type;
245                 cptr = get_tok(NULL, ", \t");
246         }
247         lo[n] = LOADORDER_INVALID;
248         return TRUE;
249 }
250
251
252 /***************************************************************************
253  *      AddLoadOrder    (internal, static)
254  *
255  * Adds an entry in the list of command-line overrides.
256  */
257 static BOOL AddLoadOrder(module_loadorder_t *plo)
258 {
259         int i;
260
261         /* TRACE(module, "'%s' -> %08lx\n", plo->modulename, *(DWORD *)(plo->loadorder)); */
262
263         for(i = 0; i < cmdline_list.count; i++)
264         {
265             if(!cmp_sort_func(plo, &cmdline_list.order[i] ))
266             {
267                 /* replace existing option */
268                 memcpy( cmdline_list.order[i].loadorder, plo->loadorder, sizeof(plo->loadorder));
269                 return TRUE;
270             }
271         }
272
273         if (i >= cmdline_list.alloc)
274         {
275                 /* No space in current array, make it larger */
276                 cmdline_list.alloc += LOADORDER_ALLOC_CLUSTER;
277                 cmdline_list.order = HeapReAlloc(GetProcessHeap(), 0, cmdline_list.order,
278                                           cmdline_list.alloc * sizeof(module_loadorder_t));
279                 if(!cmdline_list.order)
280                 {
281                         MESSAGE("Virtual memory exhausted\n");
282                         exit(1);
283                 }
284         }
285         memcpy(cmdline_list.order[i].loadorder, plo->loadorder, sizeof(plo->loadorder));
286         cmdline_list.order[i].modulename = HeapAlloc(GetProcessHeap(), 0, strlen(plo->modulename)+1);
287         strcpy( (char *)cmdline_list.order[i].modulename, plo->modulename );
288         cmdline_list.count++;
289         return TRUE;
290 }
291
292
293 /***************************************************************************
294  *      AddLoadOrderSet (internal, static)
295  *
296  * Adds a set of entries in the list of command-line overrides from the key parameter.
297  */
298 static BOOL AddLoadOrderSet(char *key, char *order)
299 {
300         module_loadorder_t ldo;
301         char *cptr;
302
303         /* Parse the loadorder before the rest because strtok is not reentrant */
304         if(!ParseLoadOrder(order, ldo.loadorder))
305                 return FALSE;
306
307         cptr = get_tok(key, ", \t");
308         while(cptr)
309         {
310                 char *ext = strrchr(cptr, '.');
311                 if(ext && !FILE_strcasecmp( ext, ".dll" )) *ext = 0;
312                 ldo.modulename = cptr;
313                 if(!AddLoadOrder(&ldo)) return FALSE;
314                 cptr = get_tok(NULL, ", \t");
315         }
316         return TRUE;
317 }
318
319
320 /***************************************************************************
321  *      MODULE_AddLoadOrderOption
322  *
323  * The commandline option is in the form:
324  * name[,name,...]=native[,b,...]
325  */
326 void MODULE_AddLoadOrderOption( const char *option )
327 {
328     char *value, *key = HeapAlloc(GetProcessHeap(), 0, strlen(option)+1);
329
330     strcpy( key, option );
331     if (!(value = strchr(key, '='))) goto error;
332     *value++ = '\0';
333
334     TRACE("Commandline override '%s' = '%s'\n", key, value);
335
336     if (!AddLoadOrderSet(key, value)) goto error;
337     HeapFree(GetProcessHeap(), 0, key);
338
339     /* sort the array for quick lookup */
340     qsort(cmdline_list.order, cmdline_list.count, sizeof(cmdline_list.order[0]), cmp_sort_func);
341     return;
342
343  error:
344     MESSAGE( "Syntax: -dll name[,name[,...]]={native|so|builtin}[,{n|s|b}[,...]]\n"
345              "    - 'name' is the name of any dll without extension\n"
346              "    - the order of loading (native, so and builtin) can be abbreviated\n"
347              "      with the first letter\n"
348              "    - the option can be specified multiple times\n"
349              "    Example:\n"
350              "    -dll comdlg32,commdlg=n -dll shell,shell32=b\n" );
351     ExitProcess(1);
352 }
353
354
355 /***************************************************************************
356  *      get_list_load_order
357  *
358  * Get the load order for a given module from the command-line or
359  * default lists.
360  */
361 static BOOL get_list_load_order( const char *module, const struct loadorder_list *list,
362                                  enum loadorder_type lo[] )
363 {
364     module_loadorder_t tmp, *res = NULL;
365
366     tmp.modulename = module;
367     /* some bsearch implementations (Solaris) are buggy when the number of items is 0 */
368     if (list->count && (res = bsearch(&tmp, list->order, list->count, sizeof(list->order[0]), cmp_sort_func)))
369         memcpy( lo, res->loadorder, sizeof(res->loadorder) );
370     return (res != NULL);
371 }
372
373
374 /***************************************************************************
375  *      open_app_key
376  *
377  * Open the registry key to the app-specific DllOverrides list.
378  */
379 static HKEY open_app_key( const char *module )
380 {
381     HKEY hkey, appkey;
382     char buffer[MAX_PATH+16], *appname;
383
384     if (!GetModuleFileName16( GetCurrentTask(), buffer, MAX_PATH ) &&
385         !GetModuleFileNameA( 0, buffer, MAX_PATH ))
386     {
387         WARN( "could not get module file name loading %s\n", module );
388         return 0;
389     }
390     appname = (char *)get_basename( buffer );
391
392     TRACE( "searching '%s' in AppDefaults\\%s\\DllOverrides\n", module, appname );
393
394     if (RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\AppDefaults", &hkey ))
395         return 0;
396
397     /* open AppDefaults\\appname\\DllOverrides key */
398     strcat( appname, "\\DllOverrides" );
399     if (RegOpenKeyA( hkey, appname, &appkey )) appkey = 0;
400     RegCloseKey( hkey );
401     return appkey;
402 }
403
404
405 /***************************************************************************
406  *      get_registry_value
407  *
408  * Load the registry loadorder value for a given module.
409  */
410 static BOOL get_registry_value( HKEY hkey, const char *module, enum loadorder_type lo[] )
411 {
412     char buffer[80];
413     DWORD count, type;
414
415     count = sizeof(buffer);
416     if (RegQueryValueExA( hkey, module, NULL, &type, buffer, &count )) return FALSE;
417     return ParseLoadOrder( buffer, lo );
418 }
419
420
421 /***************************************************************************
422  *      MODULE_GetLoadOrder     (internal)
423  *
424  * Locate the loadorder of a module.
425  * Any path is stripped from the path-argument and so are the extension
426  * '.dll' and '.exe'. A lookup in the table can yield an override for
427  * the specific dll. Otherwise the default load order is returned.
428  */
429 void MODULE_GetLoadOrder( enum loadorder_type loadorder[], const char *path, BOOL win32 )
430 {
431     static HKEY std_key = (HKEY)-1;  /* key to standard section, cached */
432     HKEY app_key = 0;
433     char *module, *basename;
434     int len;
435
436     TRACE("looking for %s\n", path);
437
438     loadorder[0] = LOADORDER_INVALID;  /* in case something bad happens below */
439
440     /* Strip path information for 16 bit modules or if the module
441      * resides in the system directory */
442     if (!win32) path = get_basename( path );
443     else
444     {
445         char sysdir[MAX_PATH+1];
446         if (!GetSystemDirectoryA( sysdir, MAX_PATH )) return;
447         if (!FILE_strncasecmp( sysdir, path, strlen (sysdir) ))
448         {
449             path += strlen(sysdir);
450             while (*path == '\\' || *path == '/') path++;
451         }
452     }
453
454     if (!(len = strlen(path))) return;
455     if (!(module = HeapAlloc( GetProcessHeap(), 0, len + 2 ))) return;
456     strcpy( module+1, path );  /* reserve module[0] for the wildcard char */
457
458     if (len >= 4)
459     {
460         char *ext = module + 1 + len - 4;
461         if (!FILE_strcasecmp( ext, ".dll" )) *ext = 0;
462     }
463
464     /* check command-line first */
465     if (get_list_load_order( module+1, &cmdline_list, loadorder ))
466     {
467         TRACE( "got cmdline %s for %s\n",
468                debugstr_loadorder(loadorder), debugstr_a(path) );
469         goto done;
470     }
471
472     /* then explicit module name in AppDefaults */
473     app_key = open_app_key( module+1 );
474     if (app_key && get_registry_value( app_key, module+1, loadorder ))
475     {
476         TRACE( "got app defaults %s for %s\n",
477                debugstr_loadorder(loadorder), debugstr_a(path) );
478         goto done;
479     }
480
481     /* then explicit module name in standard section */
482     if (std_key == (HKEY)-1)
483         RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\DllOverrides", &std_key );
484
485     if (std_key && get_registry_value( std_key, module+1, loadorder ))
486     {
487         TRACE( "got standard entry %s for %s\n",
488                debugstr_loadorder(loadorder), debugstr_a(path) );
489         goto done;
490     }
491
492     /* then module basename preceded by '*' in AppDefaults */
493     basename = (char *)get_basename( module+1 );
494     basename[-1] = '*';
495     if (app_key && get_registry_value( app_key, basename-1, loadorder ))
496     {
497         TRACE( "got app defaults basename %s for %s\n",
498                debugstr_loadorder(loadorder), debugstr_a(path) );
499         goto done;
500     }
501
502     /* then module name preceded by '*' in standard section */
503     if (std_key && get_registry_value( std_key, basename-1, loadorder ))
504     {
505         TRACE( "got standard base name %s for %s\n",
506                debugstr_loadorder(loadorder), debugstr_a(path) );
507         goto done;
508     }
509
510     /* then base name matching compiled-in defaults */
511     if (get_list_load_order( basename, &default_list, loadorder ))
512     {
513         TRACE( "got compiled-in default %s for %s\n",
514                debugstr_loadorder(loadorder), debugstr_a(path) );
515         goto done;
516     }
517
518     if (basename == module+1)
519     {
520         /* then wildcard entry in AppDefaults (only if no explicit path) */
521         if (app_key && get_registry_value( app_key, "*", loadorder ))
522         {
523             TRACE( "got app defaults wildcard %s for %s\n",
524                    debugstr_loadorder(loadorder), debugstr_a(path) );
525             goto done;
526         }
527
528         /* then wildcard entry in standard section (only if no explicit path) */
529         if (std_key && get_registry_value( std_key, "*", loadorder ))
530         {
531             TRACE( "got standard wildcard %s for %s\n",
532                    debugstr_loadorder(loadorder), debugstr_a(path) );
533             goto done;
534         }
535     }
536
537     /* and last the hard-coded default */
538     memcpy( loadorder, default_loadorder, sizeof(default_loadorder) );
539     TRACE( "got hardcoded default %s for %s\n",
540            debugstr_loadorder(loadorder), debugstr_a(path) );
541
542
543  done:
544     if (app_key) RegCloseKey( app_key );
545     HeapFree( GetProcessHeap(), 0, module );
546 }