Rewritten cache code and fixed a bug that was in it.
[wine] / dlls / shell32 / enumidlist.c
1 /*
2  *      IEnumIDList
3  *
4  *      Copyright 1998  Juergen Schmied <juergen.schmied@metronet.de>
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 <stdarg.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "wine/debug.h"
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winreg.h"
28 #include "undocshell.h"
29 #include "shlwapi.h"
30 #include "winerror.h"
31 #include "objbase.h"
32
33 #include "pidl.h"
34 #include "shlguid.h"
35 #include "shell32_main.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(shell);
38
39 typedef struct tagENUMLIST
40 {
41         struct tagENUMLIST      *pNext;
42         LPITEMIDLIST            pidl;
43
44 } ENUMLIST, *LPENUMLIST;
45
46 typedef struct
47 {
48         ICOM_VFIELD(IEnumIDList);
49         DWORD                           ref;
50         LPENUMLIST                      mpFirst;
51         LPENUMLIST                      mpLast;
52         LPENUMLIST                      mpCurrent;
53
54 } IEnumIDListImpl;
55
56 static struct ICOM_VTABLE(IEnumIDList) eidlvt;
57
58 /**************************************************************************
59  *  AddToEnumList()
60  */
61 static BOOL AddToEnumList(
62         IEnumIDList * iface,
63         LPITEMIDLIST pidl)
64 {
65         ICOM_THIS(IEnumIDListImpl,iface);
66
67         LPENUMLIST  pNew;
68
69         TRACE("(%p)->(pidl=%p)\n",This,pidl);
70         pNew = (LPENUMLIST)SHAlloc(sizeof(ENUMLIST));
71         if(pNew)
72         {
73           /*set the next pointer */
74           pNew->pNext = NULL;
75           pNew->pidl = pidl;
76
77           /*is This the first item in the list? */
78           if(!This->mpFirst)
79           {
80             This->mpFirst = pNew;
81             This->mpCurrent = pNew;
82           }
83
84           if(This->mpLast)
85           {
86             /*add the new item to the end of the list */
87             This->mpLast->pNext = pNew;
88           }
89
90           /*update the last item pointer */
91           This->mpLast = pNew;
92           TRACE("-- (%p)->(first=%p, last=%p)\n",This,This->mpFirst,This->mpLast);
93           return TRUE;
94         }
95         return FALSE;
96 }
97
98 /**************************************************************************
99  *  CreateFolderEnumList()
100  */
101 static BOOL CreateFolderEnumList(
102         IEnumIDList * iface,
103         LPCSTR lpszPath,
104         DWORD dwFlags)
105 {
106         ICOM_THIS(IEnumIDListImpl,iface);
107
108         LPITEMIDLIST    pidl=NULL;
109         WIN32_FIND_DATAA stffile;
110         HANDLE hFile;
111         CHAR  szPath[MAX_PATH];
112
113         TRACE("(%p)->(path=%s flags=0x%08lx) \n",This,debugstr_a(lpszPath),dwFlags);
114
115         if(!lpszPath || !lpszPath[0]) return FALSE;
116
117         strcpy(szPath, lpszPath);
118         PathAddBackslashA(szPath);
119         strcat(szPath,"*.*");
120
121         /*enumerate the folders*/
122         if(dwFlags & SHCONTF_FOLDERS)
123         {
124           TRACE("-- (%p)-> enumerate SHCONTF_FOLDERS of %s\n",This,debugstr_a(szPath));
125           hFile = FindFirstFileA(szPath,&stffile);
126           if ( hFile != INVALID_HANDLE_VALUE )
127           {
128             do
129             {
130               if ( !(dwFlags & SHCONTF_INCLUDEHIDDEN) && (stffile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ) continue;
131               if ( (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && strcmp (stffile.cFileName, ".") && strcmp (stffile.cFileName, ".."))
132               {
133                 pidl = _ILCreateFolder (&stffile);
134                 if(pidl && AddToEnumList((IEnumIDList*)This, pidl))
135                 {
136                   continue;
137                 }
138                 return FALSE;
139               }
140             } while( FindNextFileA(hFile,&stffile));
141             FindClose (hFile);
142           }
143         }
144
145         /*enumerate the non-folder items (values) */
146         if(dwFlags & SHCONTF_NONFOLDERS)
147         {
148           TRACE("-- (%p)-> enumerate SHCONTF_NONFOLDERS of %s\n",This,debugstr_a(szPath));
149           hFile = FindFirstFileA(szPath,&stffile);
150           if ( hFile != INVALID_HANDLE_VALUE )
151           {
152             do
153             {
154               if ( !(dwFlags & SHCONTF_INCLUDEHIDDEN) && (stffile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ) continue;
155               if (! (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
156               {
157                 pidl = _ILCreateValue(&stffile);
158                 if(pidl && AddToEnumList((IEnumIDList*)This, pidl))
159                 {
160                   continue;
161                 }
162                 return FALSE;
163               }
164             } while( FindNextFileA(hFile,&stffile));
165             FindClose (hFile);
166           }
167         }
168         return TRUE;
169 }
170
171 /**************************************************************************
172  *  CreateDesktopEnumList()
173  */
174 static BOOL CreateDesktopEnumList(
175         IEnumIDList * iface,
176         DWORD dwFlags)
177 {
178         ICOM_THIS(IEnumIDListImpl,iface);
179
180         LPITEMIDLIST    pidl=NULL;
181         HKEY hkey;
182         char    szPath[MAX_PATH];
183
184         TRACE("(%p)->(flags=0x%08lx) \n",This,dwFlags);
185
186         /*enumerate the root folders */
187         if(dwFlags & SHCONTF_FOLDERS)
188         {
189           /*create the pidl for This item */
190           pidl = _ILCreateMyComputer();
191           if(pidl)
192           {
193             if(!AddToEnumList((IEnumIDList*)This, pidl))
194               return FALSE;
195           }
196
197           if (! RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\desktop\\NameSpace", 0, KEY_READ, &hkey))
198           {
199             char iid[50];
200             int i=0;
201
202             while (1)
203             {
204               DWORD size = sizeof (iid);
205
206               if (ERROR_SUCCESS!=RegEnumKeyExA(hkey, i, iid, &size, 0, NULL, NULL, NULL))
207                 break;
208
209               pidl = _ILCreateSpecial(iid);
210
211               if(pidl)
212                 AddToEnumList((IEnumIDList*)This, pidl);
213
214               i++;
215             }
216             RegCloseKey(hkey);
217           }
218         }
219
220         /*enumerate the elements in %windir%\desktop */
221         SHGetSpecialFolderPathA(0, szPath, CSIDL_DESKTOPDIRECTORY, FALSE);
222         CreateFolderEnumList( (IEnumIDList*)This, szPath, dwFlags);
223
224         return TRUE;
225 }
226
227 /**************************************************************************
228  *  CreateMyCompEnumList()
229  */
230 static BOOL CreateMyCompEnumList(
231         IEnumIDList * iface,
232         DWORD dwFlags)
233 {
234         ICOM_THIS(IEnumIDListImpl,iface);
235
236         LPITEMIDLIST    pidl=NULL;
237         DWORD           dwDrivemap;
238         CHAR            szDriveName[4];
239         HKEY            hkey;
240
241         TRACE("(%p)->(flags=0x%08lx) \n",This,dwFlags);
242
243         /*enumerate the folders*/
244         if(dwFlags & SHCONTF_FOLDERS)
245         {
246           dwDrivemap = GetLogicalDrives();
247           strcpy (szDriveName,"A:\\");
248           while (szDriveName[0]<='Z')
249           {
250             if(dwDrivemap & 0x00000001L)
251             {
252               pidl = _ILCreateDrive(szDriveName);
253               if(pidl)
254               {
255                 if(!AddToEnumList((IEnumIDList*)This, pidl))
256                   return FALSE;
257               }
258             }
259             szDriveName[0]++;
260             dwDrivemap = dwDrivemap >> 1;
261           }
262
263           TRACE("-- (%p)-> enumerate (mycomputer shell extensions)\n",This);
264           if (! RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\mycomputer\\NameSpace", 0, KEY_READ, &hkey))
265           {
266             char iid[50];
267             int i=0;
268
269             while (1)
270             {
271               DWORD size = sizeof (iid);
272
273               if (ERROR_SUCCESS!=RegEnumKeyExA(hkey, i, iid, &size, 0, NULL, NULL, NULL))
274                 break;
275
276               pidl = _ILCreateSpecial(iid);
277
278               if(pidl)
279                 AddToEnumList((IEnumIDList*)This, pidl);
280
281               i++;
282             }
283             RegCloseKey(hkey);
284           }
285         }
286         return TRUE;
287 }
288
289 /**************************************************************************
290 *   DeleteList()
291 */
292 static BOOL DeleteList(
293         IEnumIDList * iface)
294 {
295         ICOM_THIS(IEnumIDListImpl,iface);
296
297         LPENUMLIST  pDelete;
298
299         TRACE("(%p)->()\n",This);
300
301         while(This->mpFirst)
302         { pDelete = This->mpFirst;
303           This->mpFirst = pDelete->pNext;
304           SHFree(pDelete->pidl);
305           SHFree(pDelete);
306         }
307         This->mpFirst = This->mpLast = This->mpCurrent = NULL;
308         return TRUE;
309 }
310
311 /**************************************************************************
312  *  IEnumIDList_Folder_Constructor
313  *
314  */
315
316 IEnumIDList * IEnumIDList_Constructor(
317         LPCSTR lpszPath,
318         DWORD dwFlags,
319         DWORD dwKind)
320 {
321         IEnumIDListImpl*        lpeidl;
322         BOOL                    ret = FALSE;
323
324         TRACE("()->(%s flags=0x%08lx kind=0x%08lx)\n",debugstr_a(lpszPath),dwFlags, dwKind);
325
326         lpeidl = (IEnumIDListImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumIDListImpl));
327
328         if (lpeidl)
329         {
330           lpeidl->ref = 1;
331           lpeidl->lpVtbl = &eidlvt;
332
333           switch (dwKind)
334           {
335             case EIDL_DESK:
336               ret = CreateDesktopEnumList((IEnumIDList*)lpeidl, dwFlags);
337               break;
338
339             case EIDL_MYCOMP:
340               ret = CreateMyCompEnumList((IEnumIDList*)lpeidl, dwFlags);
341               break;
342
343             case EIDL_FILE:
344               ret = CreateFolderEnumList((IEnumIDList*)lpeidl, lpszPath, dwFlags);
345               break;
346           }
347
348             if(!ret) {
349                 HeapFree(GetProcessHeap(),0,lpeidl);
350                 lpeidl = NULL;
351             }
352         }
353
354         TRACE("-- (%p)->()\n",lpeidl);
355
356         return (IEnumIDList*)lpeidl;
357 }
358
359 /**************************************************************************
360  *  EnumIDList_QueryInterface
361  */
362 static HRESULT WINAPI IEnumIDList_fnQueryInterface(
363         IEnumIDList * iface,
364         REFIID riid,
365         LPVOID *ppvObj)
366 {
367         ICOM_THIS(IEnumIDListImpl,iface);
368
369         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);
370
371         *ppvObj = NULL;
372
373         if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
374         { *ppvObj = This;
375         }
376         else if(IsEqualIID(riid, &IID_IEnumIDList))  /*IEnumIDList*/
377         {    *ppvObj = (IEnumIDList*)This;
378         }
379
380         if(*ppvObj)
381         { IEnumIDList_AddRef((IEnumIDList*)*ppvObj);
382           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
383           return S_OK;
384         }
385
386         TRACE("-- Interface: E_NOINTERFACE\n");
387         return E_NOINTERFACE;
388 }
389
390 /******************************************************************************
391  * IEnumIDList_fnAddRef
392  */
393 static ULONG WINAPI IEnumIDList_fnAddRef(
394         IEnumIDList * iface)
395 {
396         ICOM_THIS(IEnumIDListImpl,iface);
397         TRACE("(%p)->(%lu)\n",This,This->ref);
398         return ++(This->ref);
399 }
400 /******************************************************************************
401  * IEnumIDList_fnRelease
402  */
403 static ULONG WINAPI IEnumIDList_fnRelease(
404         IEnumIDList * iface)
405 {
406         ICOM_THIS(IEnumIDListImpl,iface);
407
408         TRACE("(%p)->(%lu)\n",This,This->ref);
409
410         if (!--(This->ref)) {
411           TRACE(" destroying IEnumIDList(%p)\n",This);
412           DeleteList((IEnumIDList*)This);
413           HeapFree(GetProcessHeap(),0,This);
414           return 0;
415         }
416         return This->ref;
417 }
418
419 /**************************************************************************
420  *  IEnumIDList_fnNext
421  */
422
423 static HRESULT WINAPI IEnumIDList_fnNext(
424         IEnumIDList * iface,
425         ULONG celt,
426         LPITEMIDLIST * rgelt,
427         ULONG *pceltFetched)
428 {
429         ICOM_THIS(IEnumIDListImpl,iface);
430
431         ULONG    i;
432         HRESULT  hr = S_OK;
433         LPITEMIDLIST  temp;
434
435         TRACE("(%p)->(%ld,%p, %p)\n",This,celt,rgelt,pceltFetched);
436
437 /* It is valid to leave pceltFetched NULL when celt is 1. Some of explorer's
438  * subsystems actually use it (and so may a third party browser)
439  */
440         if(pceltFetched)
441           *pceltFetched = 0;
442
443         *rgelt=0;
444
445         if(celt > 1 && !pceltFetched)
446         { return E_INVALIDARG;
447         }
448
449         if(celt > 0 && !This->mpCurrent)
450         { return S_FALSE;
451         }
452
453         for(i = 0; i < celt; i++)
454         { if(!(This->mpCurrent))
455             break;
456
457           temp = ILClone(This->mpCurrent->pidl);
458           rgelt[i] = temp;
459           This->mpCurrent = This->mpCurrent->pNext;
460         }
461         if(pceltFetched)
462         {  *pceltFetched = i;
463         }
464
465         return hr;
466 }
467
468 /**************************************************************************
469 *  IEnumIDList_fnSkip
470 */
471 static HRESULT WINAPI IEnumIDList_fnSkip(
472         IEnumIDList * iface,ULONG celt)
473 {
474         ICOM_THIS(IEnumIDListImpl,iface);
475
476         DWORD    dwIndex;
477         HRESULT  hr = S_OK;
478
479         TRACE("(%p)->(%lu)\n",This,celt);
480
481         for(dwIndex = 0; dwIndex < celt; dwIndex++)
482         { if(!This->mpCurrent)
483           { hr = S_FALSE;
484             break;
485           }
486           This->mpCurrent = This->mpCurrent->pNext;
487         }
488         return hr;
489 }
490 /**************************************************************************
491 *  IEnumIDList_fnReset
492 */
493 static HRESULT WINAPI IEnumIDList_fnReset(
494         IEnumIDList * iface)
495 {
496         ICOM_THIS(IEnumIDListImpl,iface);
497
498         TRACE("(%p)\n",This);
499         This->mpCurrent = This->mpFirst;
500         return S_OK;
501 }
502 /**************************************************************************
503 *  IEnumIDList_fnClone
504 */
505 static HRESULT WINAPI IEnumIDList_fnClone(
506         IEnumIDList * iface,LPENUMIDLIST * ppenum)
507 {
508         ICOM_THIS(IEnumIDListImpl,iface);
509
510         TRACE("(%p)->() to (%p)->() E_NOTIMPL\n",This,ppenum);
511         return E_NOTIMPL;
512 }
513
514 /**************************************************************************
515  *  IEnumIDList_fnVTable
516  */
517 static ICOM_VTABLE (IEnumIDList) eidlvt =
518 {
519         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
520         IEnumIDList_fnQueryInterface,
521         IEnumIDList_fnAddRef,
522         IEnumIDList_fnRelease,
523         IEnumIDList_fnNext,
524         IEnumIDList_fnSkip,
525         IEnumIDList_fnReset,
526         IEnumIDList_fnClone,
527 };