advapi32: Fix a test that fails in win2k3 SP2.
[wine] / dlls / setupapi / dirid.c
1 /*
2  * Directory id handling
3  *
4  * Copyright 2002 Alexandre Julliard for CodeWeavers
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 <stdarg.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winreg.h"
26 #include "winternl.h"
27 #include "winerror.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "winnls.h"
31 #include "setupapi.h"
32 #include "shlobj.h"
33 #include "wine/unicode.h"
34 #include "setupapi_private.h"
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
38
39 #define MAX_SYSTEM_DIRID DIRID_PRINTPROCESSOR
40 #define MIN_CSIDL_DIRID 0x4000
41 #define MAX_CSIDL_DIRID 0x403f
42
43 struct user_dirid
44 {
45     int    id;
46     WCHAR *str;
47 };
48
49 static int nb_user_dirids;     /* number of user dirids in use */
50 static int alloc_user_dirids;  /* number of allocated user dirids */
51 static struct user_dirid *user_dirids;
52 static const WCHAR *system_dirids[MAX_SYSTEM_DIRID+1];
53 static const WCHAR *csidl_dirids[MAX_CSIDL_DIRID-MIN_CSIDL_DIRID+1];
54
55 /* retrieve the string for unknown dirids */
56 static const WCHAR *get_unknown_dirid(void)
57 {
58     static WCHAR *unknown_dirid;
59     static const WCHAR unknown_str[] = {'\\','u','n','k','n','o','w','n',0};
60
61     if (!unknown_dirid)
62     {
63         UINT len = GetSystemDirectoryW( NULL, 0 ) + strlenW(unknown_str);
64         if (!(unknown_dirid = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
65         GetSystemDirectoryW( unknown_dirid, len );
66         strcatW( unknown_dirid, unknown_str );
67     }
68     return unknown_dirid;
69 }
70
71 static const WCHAR *get_csidl_dir(DWORD csidl);
72
73 /* create the string for a system dirid */
74 static const WCHAR *create_system_dirid( int dirid )
75 {
76     static const WCHAR Null[]    = {0};
77     static const WCHAR C_Root[]  = {'C',':','\\',0};
78     static const WCHAR Drivers[] = {'\\','d','r','i','v','e','r','s',0};
79     static const WCHAR Inf[]     = {'\\','i','n','f',0};
80     static const WCHAR Help[]    = {'\\','h','e','l','p',0};
81     static const WCHAR Fonts[]   = {'\\','f','o','n','t','s',0};
82     static const WCHAR Viewers[] = {'\\','v','i','e','w','e','r','s',0};
83     static const WCHAR System[]  = {'\\','s','y','s','t','e','m',0};
84     static const WCHAR Spool[]   = {'\\','s','p','o','o','l',0};
85     static const WCHAR UserProfile[] = {'U','S','E','R','P','R','O','F','I','L','E',0};
86
87     WCHAR buffer[MAX_PATH+32], *str;
88     int len;
89
90     switch(dirid)
91     {
92     case DIRID_NULL:
93         return Null;
94     case DIRID_WINDOWS:
95         GetWindowsDirectoryW( buffer, MAX_PATH );
96         break;
97     case DIRID_SYSTEM:
98         GetSystemDirectoryW( buffer, MAX_PATH );
99         break;
100     case DIRID_DRIVERS:
101         GetSystemDirectoryW( buffer, MAX_PATH );
102         strcatW( buffer, Drivers );
103         break;
104     case DIRID_INF:
105         GetWindowsDirectoryW( buffer, MAX_PATH );
106         strcatW( buffer, Inf );
107         break;
108     case DIRID_HELP:
109         GetWindowsDirectoryW( buffer, MAX_PATH );
110         strcatW( buffer, Help );
111         break;
112     case DIRID_FONTS:
113         GetWindowsDirectoryW( buffer, MAX_PATH );
114         strcatW( buffer, Fonts );
115         break;
116     case DIRID_VIEWERS:
117         GetSystemDirectoryW( buffer, MAX_PATH );
118         strcatW( buffer, Viewers );
119         break;
120     case DIRID_APPS:
121         return C_Root;  /* FIXME */
122     case DIRID_SHARED:
123         GetWindowsDirectoryW( buffer, MAX_PATH );
124         break;
125     case DIRID_BOOT:
126         return C_Root;  /* FIXME */
127     case DIRID_SYSTEM16:
128         GetWindowsDirectoryW( buffer, MAX_PATH );
129         strcatW( buffer, System );
130         break;
131     case DIRID_SPOOL:
132     case DIRID_SPOOLDRIVERS:  /* FIXME */
133         GetWindowsDirectoryW( buffer, MAX_PATH );
134         strcatW( buffer, Spool );
135         break;
136     case DIRID_USERPROFILE:
137         if (GetEnvironmentVariableW( UserProfile, buffer, MAX_PATH )) break;
138         return get_csidl_dir(CSIDL_PROFILE);
139     case DIRID_LOADER:
140         return C_Root;  /* FIXME */
141     case DIRID_COLOR:  /* FIXME */
142     case DIRID_PRINTPROCESSOR:  /* FIXME */
143     default:
144         FIXME( "unknown dirid %d\n", dirid );
145         return get_unknown_dirid();
146     }
147     len = (strlenW(buffer) + 1) * sizeof(WCHAR);
148     if ((str = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( str, buffer, len );
149     return str;
150 }
151
152 static const WCHAR *get_csidl_dir( DWORD csidl )
153 {
154     WCHAR buffer[MAX_PATH], *str;
155     int len;
156
157     if (!SHGetSpecialFolderPathW( NULL, buffer, csidl, TRUE ))
158     {
159         FIXME( "CSIDL %x not found\n", csidl );
160         return get_unknown_dirid();
161     }
162     len = (strlenW(buffer) + 1) * sizeof(WCHAR);
163     if ((str = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( str, buffer, len );
164     return str;
165 }
166
167 /* retrieve the string corresponding to a dirid, or NULL if none */
168 const WCHAR *DIRID_get_string( int dirid )
169 {
170     int i;
171
172     if (dirid == DIRID_ABSOLUTE || dirid == DIRID_ABSOLUTE_16BIT) dirid = DIRID_NULL;
173
174     if (dirid >= DIRID_USER)
175     {
176         for (i = 0; i < nb_user_dirids; i++)
177             if (user_dirids[i].id == dirid) return user_dirids[i].str;
178         WARN("user id %d not found\n", dirid );
179         return NULL;
180     }
181     else if (dirid >= MIN_CSIDL_DIRID)
182     {
183         if (dirid > MAX_CSIDL_DIRID) return get_unknown_dirid();
184         dirid -= MIN_CSIDL_DIRID;
185         if (!csidl_dirids[dirid]) csidl_dirids[dirid] = get_csidl_dir( dirid );
186         return csidl_dirids[dirid];
187     }
188     else
189     {
190         if (dirid > MAX_SYSTEM_DIRID) return get_unknown_dirid();
191         if (!system_dirids[dirid]) system_dirids[dirid] = create_system_dirid( dirid );
192         return system_dirids[dirid];
193     }
194 }
195
196 /* store a user dirid string */
197 static BOOL store_user_dirid( HINF hinf, int id, WCHAR *str )
198 {
199     int i;
200
201     for (i = 0; i < nb_user_dirids; i++) if (user_dirids[i].id == id) break;
202
203     if (i < nb_user_dirids) HeapFree( GetProcessHeap(), 0, user_dirids[i].str );
204     else
205     {
206         if (nb_user_dirids >= alloc_user_dirids)
207         {
208             int new_size = max( 32, alloc_user_dirids * 2 );
209
210             struct user_dirid *new;
211
212             if (user_dirids)
213                 new = HeapReAlloc( GetProcessHeap(), 0, user_dirids,
214                                                   new_size * sizeof(*new) );
215             else
216                 new = HeapAlloc( GetProcessHeap(), 0, 
217                                                   new_size * sizeof(*new) );
218
219             if (!new) return FALSE;
220             user_dirids = new;
221             alloc_user_dirids = new_size;
222         }
223         nb_user_dirids++;
224     }
225     user_dirids[i].id  = id;
226     user_dirids[i].str = str;
227     TRACE("id %d -> %s\n", id, debugstr_w(str) );
228     return TRUE;
229 }
230
231
232 /***********************************************************************
233  *              SetupSetDirectoryIdA    (SETUPAPI.@)
234  */
235 BOOL WINAPI SetupSetDirectoryIdA( HINF hinf, DWORD id, PCSTR dir )
236 {
237     UNICODE_STRING dirW;
238     int i;
239
240     if (!id)  /* clear everything */
241     {
242         for (i = 0; i < nb_user_dirids; i++) HeapFree( GetProcessHeap(), 0, user_dirids[i].str );
243         nb_user_dirids = 0;
244         return TRUE;
245     }
246     if (id < DIRID_USER)
247     {
248         SetLastError( ERROR_INVALID_PARAMETER );
249         return FALSE;
250     }
251
252     /* duplicate the string */
253     if (!RtlCreateUnicodeStringFromAsciiz( &dirW, dir ))
254     {
255         SetLastError( ERROR_NOT_ENOUGH_MEMORY );
256         return FALSE;
257     }
258     return store_user_dirid( hinf, id, dirW.Buffer );
259 }
260
261
262 /***********************************************************************
263  *              SetupSetDirectoryIdW    (SETUPAPI.@)
264  */
265 BOOL WINAPI SetupSetDirectoryIdW( HINF hinf, DWORD id, PCWSTR dir )
266 {
267     int i, len;
268     WCHAR *str;
269
270     if (!id)  /* clear everything */
271     {
272         for (i = 0; i < nb_user_dirids; i++) HeapFree( GetProcessHeap(), 0, user_dirids[i].str );
273         nb_user_dirids = 0;
274         return TRUE;
275     }
276     if (id < DIRID_USER)
277     {
278         SetLastError( ERROR_INVALID_PARAMETER );
279         return FALSE;
280     }
281
282     /* duplicate the string */
283     len = (strlenW(dir)+1) * sizeof(WCHAR);
284     if (!(str = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE;
285     memcpy( str, dir, len );
286     return store_user_dirid( hinf, id, str );
287 }