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