Overlay icons for .lnk files with a small arrow in the lower left
[wine] / dlls / shell32 / shfldr_unixfs.c
1 /*
2  * UNIXFS - Shell namespace extension for the unix filesystem
3  *
4  * Copyright (C) 2005 Michael Jung
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 #include <stdio.h>
23 #include <stdarg.h>
24 #include <limits.h>
25 #include <dirent.h>
26 #ifdef HAVE_SYS_STAT_H
27 # include <sys/stat.h>
28 #endif
29 #ifdef HAVE_PWD_H
30 # include <pwd.h>
31 #endif
32 #include <grp.h>
33 #include <limits.h>
34
35 #define COBJMACROS
36 #define NONAMELESSUNION
37 #define NONAMELESSSTRUCT
38
39 #include "windef.h"
40 #include "winbase.h"
41 #include "winuser.h"
42 #include "objbase.h"
43 #include "winreg.h"
44 #include "winternl.h"
45 #include "wine/debug.h"
46
47 #include "shell32_main.h"
48 #include "shfldr.h"
49 #include "shresdef.h"
50 #include "pidl.h"
51
52 WINE_DEFAULT_DEBUG_CHANNEL(shell);
53
54 const GUID CLSID_UnixFolder = {0xcc702eb2, 0x7dc5, 0x11d9, {0xc6, 0x87, 0x00, 0x04, 0x23, 0x8a, 0x01, 0xcd}};
55
56 #define ADJUST_THIS(c,m,p) ((c*)(((long)p)-(long)&(((c*)0)->lp##m##Vtbl)))
57 #define STATIC_CAST(i,p) ((i*)&p->lp##i##Vtbl)
58
59 /* FileStruct reserves one byte for szNames, thus we don't have to 
60  * alloc a byte for the terminating '\0' of 'name'. Two of the
61  * additional bytes are for SHITEMID's cb field. One is for IDLDATA's
62  * type field. One is for FileStruct's szNames field, to terminate
63  * the alternate DOS name, which we don't use here. And then there's
64  * the additional StatStruct.
65  */
66 #define SHITEMID_LEN_FROM_NAME_LEN(n) \
67     (sizeof(USHORT)+sizeof(PIDLTYPE)+sizeof(FileStruct)+(n)+sizeof(char)+sizeof(StatStruct))
68 #define NAME_LEN_FROM_LPSHITEMID(s) \
69     (((LPSHITEMID)s)->cb-sizeof(USHORT)-sizeof(PIDLTYPE)-sizeof(FileStruct)-sizeof(char)-sizeof(StatStruct))
70 #define LPSTATSTRUCT_FROM_LPSHITEMID(s) ((StatStruct*)(((LPBYTE)s)+((LPSHITEMID)s)->cb-sizeof(StatStruct)))
71
72 /* This structure is appended to shell32's FileStruct type in IDLs to store unix
73  * filesystem specific informationen extracted with the stat system call.
74  */
75 typedef struct tagStatStruct {
76     mode_t st_mode;
77     uid_t  st_uid;
78     gid_t  st_gid;
79 } StatStruct;
80
81 /******************************************************************************
82  * UNIXFS_is_pidl_of_type [INTERNAL]
83  *
84  * Checks for the first SHITEMID of an ITEMIDLIST if it passes a filter.
85  *
86  * PARAMS
87  *  pIDL    [I] The ITEMIDLIST to be checked.
88  *  fFilter [I] Shell condition flags, which specify the filter.
89  *
90  * RETURNS
91  *  TRUE, if pIDL is accepted by fFilter
92  *  FALSE, otherwise
93  */
94 static inline BOOL UNIXFS_is_pidl_of_type(LPITEMIDLIST pIDL, SHCONTF fFilter) {
95     LPSTR pszText = _ILGetTextPointer(pIDL);
96     if (!pszText) return FALSE;
97     if (pszText[0] == '.' && !(fFilter & SHCONTF_INCLUDEHIDDEN)) return FALSE;
98     if (_ILIsFolder(pIDL) && (fFilter & SHCONTF_FOLDERS)) return TRUE;
99     if (_ILIsValue(pIDL) && (fFilter & SHCONTF_NONFOLDERS)) return TRUE;
100     return FALSE;
101 }
102
103 /******************************************************************************
104  * UNIXFS_build_shitemid [Internal]
105  *
106  * Constructs a new SHITEMID for a single path item (up to the next '/' or 
107  * '\0') into a buffer. Decorates the SHITEMID with information from a stat 
108  * system call.
109  *
110  * PARAMS
111  *  name   [I] The name of the next path item. Terminated by either '\0' or '/'.
112  *  pStat  [I] A stat struct variable obtained by a stat system call on the file.
113  *  buffer [O] SHITEMID will be constructed here.
114  *
115  * RETURNS
116  *  A pointer to the next '/' or '\0' character in name.
117  *  
118  * NOTES
119  *  Minimum size of buffer is SHITEMID_LEN_FROM_NAME_LEN(strlen(name)).
120  *  If what you need is a PIDLLIST with a single SHITEMID, don't forget to append
121  *  a 0 USHORT value.
122  */
123 static char* UNIXFS_build_shitemid(char *name, struct stat *pStat, void *buffer) {
124     LARGE_INTEGER time;
125     FILETIME fileTime;
126     LPPIDLDATA pIDLData;
127     StatStruct *pStatStruct;
128     int cNameLen;
129     char *pSlash;
130
131     TRACE("(name=%s, buffer=%p)\n", debugstr_a(name), buffer);
132
133     pSlash = strchr(name, '/');
134     cNameLen = pSlash ? pSlash - name : strlen(name); 
135     
136     memset(buffer, 0, SHITEMID_LEN_FROM_NAME_LEN(cNameLen));
137     ((LPSHITEMID)buffer)->cb = SHITEMID_LEN_FROM_NAME_LEN(cNameLen) ;
138     
139     pIDLData = _ILGetDataPointer((LPCITEMIDLIST)buffer);
140     pIDLData->type = S_ISDIR(pStat->st_mode) ? PT_FOLDER : PT_VALUE;
141     pIDLData->u.file.dwFileSize = (DWORD)pStat->st_size;
142     RtlSecondsSince1970ToTime( pStat->st_mtime, &time );
143     fileTime.dwLowDateTime = time.u.LowPart;
144     fileTime.dwHighDateTime = time.u.HighPart;
145     FileTimeToDosDateTime(&fileTime, &pIDLData->u.file.uFileDate, &pIDLData->u.file.uFileTime);
146     pIDLData->u.file.uFileAttribs = 
147         (S_ISDIR(pStat->st_mode) ? FILE_ATTRIBUTE_DIRECTORY : 0) |
148         (name[0] == '.' ? FILE_ATTRIBUTE_HIDDEN : 0);
149     memcpy(pIDLData->u.file.szNames, name, cNameLen);
150
151     pStatStruct = LPSTATSTRUCT_FROM_LPSHITEMID(buffer);
152     pStatStruct->st_mode = pStat->st_mode;
153     pStatStruct->st_uid = pStat->st_uid;
154     pStatStruct->st_gid = pStat->st_gid;
155
156     return pSlash ? pSlash+1 : (name + cNameLen);
157 }
158
159 /******************************************************************************
160  * UNIXFS_path_to_pidl [Internal]
161  *
162  * PARAMS
163  *  base  [I] Path prefix. May be NULL if "path" parameter is absolute
164  *  path  [I] An absolute unix path or a path relativ to "base"
165  *  ppidl [O] The corresponding ITEMIDLIST. Release with SHFree/ILFree
166  *  
167  * RETURNS
168  *  Success: TRUE
169  *  Failure: FALSE, invalid params or out of memory
170  */
171 static BOOL UNIXFS_path_to_pidl(const char *base, const char *path, LPITEMIDLIST *ppidl) {
172     LPITEMIDLIST pidl;
173     struct stat fileStat;
174     int cSubDirs, cPidlLen, res;
175     char *pSlash, *pCompletePath, *pNextPathElement;
176
177     TRACE("path=%s, ppidl=%p\n", debugstr_a(path), ppidl);
178     
179     /* Fail, if base + path is not an absolute path */
180     if (!ppidl || !path || (path[0] != '/' && (!base || base[0] != '/'))) return FALSE;
181
182     /* Build an absolute path and let pNextPathElement point to the interesting relativ path. */
183     if (path[0] != '/') {
184         pCompletePath = SHAlloc(strlen(base)+strlen(path)+1);
185         if (!pCompletePath) return FALSE;
186         sprintf(pCompletePath, "%s%s", base, path);
187         pNextPathElement = pCompletePath + strlen(base);
188     } else {
189         pCompletePath = SHAlloc(strlen(path)+1);
190         if (!pCompletePath) return FALSE;
191         memcpy(pCompletePath, path, strlen(path)+1);
192         pNextPathElement = pCompletePath + 1;
193     }
194     
195     /* Count the number of sub-directories in the path */
196     cSubDirs = 1; /* Path may not be terminated with '/', thus start with 1 */
197     pSlash = strchr(pNextPathElement, '/');
198     while (pSlash) {
199         cSubDirs++;
200         pSlash = strchr(pSlash+1, '/');
201     }
202    
203     /* Allocate enough memory to hold the path. The -cSubDirs is for the '/' 
204      * characters, which are not stored in the ITEMIDLIST. */
205     cPidlLen = strlen(pCompletePath) - cSubDirs + cSubDirs * SHITEMID_LEN_FROM_NAME_LEN(0) + sizeof(USHORT);
206     *ppidl = pidl = (LPITEMIDLIST)SHAlloc(cPidlLen);
207     if (!pidl) {
208         SHFree(pCompletePath);
209         return FALSE;
210     }
211
212     /* Concatenate the SHITEMIDs of the sub-directories. */
213     while (*pNextPathElement) {
214         pSlash = strchr(pNextPathElement, '/');
215         if (pSlash) {
216             *pSlash = '\0';
217             res = stat(pCompletePath, &fileStat);
218             *pSlash = '/';
219             if (res) {
220                 SHFree(pCompletePath);
221                 SHFree(pidl);
222                 return FALSE;
223             }
224         } else {
225             if (stat(pCompletePath, &fileStat)) {
226                 SHFree(pCompletePath);
227                 SHFree(pidl);
228                 return FALSE;
229             }
230         }
231                 
232         pNextPathElement = UNIXFS_build_shitemid(pNextPathElement, &fileStat, pidl);
233         pidl = ILGetNext(pidl);
234     }
235     pidl->mkid.cb = 0; /* Terminate the ITEMIDLIST */
236  
237     SHFree(pCompletePath);
238     return TRUE;
239 }
240
241 /******************************************************************************
242  * UNIXFS_pidl_to_path [Internal]
243  *
244  * Construct the unix path that corresponds to a fully qualified ITEMIDLIST
245  *
246  * PARAMS
247  *  pidl [I] ITEMIDLIST that specifies the absolute location of the folder
248  *  path [O] The corresponding unix path as a zero terminated ascii string
249  *
250  * RETURNS
251  *  Success: TRUE
252  *  Failure: FALSE, pidl doesn't specify a unix path or out of memory
253  */
254 static BOOL UNIXFS_pidl_to_path(LPCITEMIDLIST pidl, PSZ *path) {
255     LPCITEMIDLIST current = pidl, root;
256     DWORD dwPathLen;
257     char *pNextDir;
258
259     TRACE("(pidl=%p, path=%p)\n", pidl, path);
260     
261     *path = NULL;
262
263     /* Find the UnixFolderClass root */
264     while (current->mkid.cb) {
265         LPPIDLDATA pData = _ILGetDataPointer(current);
266         if (!pData) return FALSE;
267         if (pData->type == PT_GUID && IsEqualIID(&CLSID_UnixFolder, &pData->u.guid.guid)) break;
268         current = ILGetNext(current);
269     }
270     root = current = ILGetNext(current);
271     
272     /* Determine the path's length bytes */
273     dwPathLen = 2; /* For the '/' prefix and the terminating '\0' */
274     while (current->mkid.cb) {
275         dwPathLen += NAME_LEN_FROM_LPSHITEMID(current) + 1; /* For the '/' */
276         current = ILGetNext(current);
277     };
278
279     /* Build the path */
280     *path = pNextDir = SHAlloc(dwPathLen);
281     if (!path) {
282         WARN("SHAlloc failed!\n");
283         return FALSE;
284     }
285     current = root;
286     *pNextDir++ = '/';
287     while (current->mkid.cb) {
288         memcpy(pNextDir, _ILGetTextPointer(current), NAME_LEN_FROM_LPSHITEMID(current));
289         pNextDir += NAME_LEN_FROM_LPSHITEMID(current);
290         *pNextDir++ = '/';
291         current = ILGetNext(current);
292     }
293     *pNextDir='\0';
294     
295     TRACE("resulting path: %s\n", *path);
296     return TRUE;
297 }
298
299 /******************************************************************************
300  * UNIXFS_build_subfolder_pidls [Internal]
301  *
302  * Builds an array of subfolder PIDLs relative to a unix directory
303  *
304  * PARAMS
305  *  path   [I] Name of a unix directory as a zero terminated ascii string
306  *  apidl  [O] The array of PIDLs
307  *  pCount [O] Size of apidl
308  *
309  * RETURNS
310  *  Success: TRUE
311  *  Failure: FALSE, path is not a valid unix directory or out of memory
312  *
313  * NOTES
314  *  The array of PIDLs and each PIDL are allocated with SHAlloc. You'll have
315  *  to release each PIDL as well as the array itself with SHFree.
316  */
317 static BOOL UNIXFS_build_subfolder_pidls(const char *path, LPITEMIDLIST **apidl, DWORD *pCount)
318 {
319     struct dirent *pDirEntry;
320     struct stat fileStat;
321     DIR *dir;
322     DWORD cDirEntries, i;
323     USHORT sLen;
324     char *pszFQPath;
325
326     TRACE("(path=%s, apidl=%p, pCount=%p)\n", debugstr_a(path), apidl, pCount);
327     
328     *apidl = NULL;
329     *pCount = 0;
330   
331     dir = opendir(path);
332     if (!dir) {
333         WARN("Failed to open directory '%s'.\n", path);
334         return FALSE;
335     }
336
337     /* Allocate space for fully qualified paths */
338     pszFQPath = SHAlloc(strlen(path) + PATH_MAX);
339     if (!pszFQPath) {
340         WARN("SHAlloc failed!\n");
341         return FALSE;
342     }
343  
344     /* Count number of directory entries. */
345     for (cDirEntries = 0, pDirEntry = readdir(dir); pDirEntry; pDirEntry = readdir(dir)) { 
346         if (!strcmp(pDirEntry->d_name, ".") || !strcmp(pDirEntry->d_name, "..")) continue;
347         sprintf(pszFQPath, "%s%s", path, pDirEntry->d_name);
348         if (!stat(pszFQPath, &fileStat) && (S_ISDIR(fileStat.st_mode) || S_ISREG(fileStat.st_mode))) cDirEntries++;
349     }
350
351     /* If there are no entries, we are done. */
352     if (cDirEntries == 0) {
353         closedir(dir);
354         SHFree(pszFQPath);
355         return TRUE;
356     }
357
358     /* Allocate the array of PIDLs */
359     *apidl = SHAlloc(cDirEntries * sizeof(LPITEMIDLIST));
360     if (!apidl) {
361         WARN("SHAlloc failed!\n");
362         return FALSE;
363     }
364   
365     /* Allocate and initialize one SHITEMID per sub-directory. */
366     for (rewinddir(dir), pDirEntry = readdir(dir), i = 0; pDirEntry; pDirEntry = readdir(dir)) {
367         LPSHITEMID pid;
368             
369         if (!strcmp(pDirEntry->d_name, ".") || !strcmp(pDirEntry->d_name, "..")) continue;
370
371         sprintf(pszFQPath, "%s%s", path, pDirEntry->d_name);
372         if (stat(pszFQPath, &fileStat)) continue;
373         if (!S_ISDIR(fileStat.st_mode) && !S_ISREG(fileStat.st_mode)) continue;
374    
375         sLen = strlen(pDirEntry->d_name);
376         pid = (LPSHITEMID)SHAlloc(SHITEMID_LEN_FROM_NAME_LEN(sLen)+sizeof(USHORT));
377         if (!pid) {
378             WARN("SHAlloc failed!\n");
379             return FALSE;
380         }
381         UNIXFS_build_shitemid(pDirEntry->d_name, &fileStat, pid);
382         memset(((PBYTE)pid)+pid->cb, 0, sizeof(USHORT));
383
384         (*apidl)[i++] = (LPITEMIDLIST)pid;
385     }
386     
387     *pCount = i;    
388     closedir(dir);
389     SHFree(pszFQPath);
390
391     return TRUE;
392 }
393
394 /******************************************************************************
395  * UnixFolderIcon 
396  *
397  * Singleton class, which is used by the shell to extract icons to represent
398  * folders in tree- and listviews. Currently, all this singleton does is to
399  * provide the shell with the absolute path to "shell32.dll" and with the 
400  * indices of the closed and opened folder icons in the resources of this dll.
401  */
402
403 /* UnixFolderIcon object layout and typedef.
404  */
405 typedef struct _UnixFolderIcon {
406     const IExtractIconWVtbl *lpIExtractIconWVtbl;
407     BOOL bFolder;
408 } UnixFolderIcon;
409
410 static HRESULT WINAPI UnixFolderIcon_IExtractIconW_QueryInterface(IExtractIconW *iface, REFIID riid, 
411     void **ppv) 
412 {
413     TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
414     
415     if (!ppv) return E_INVALIDARG;
416     
417     if (IsEqualIID(&IID_IUnknown, riid) ||
418         IsEqualIID(&IID_IExtractIconW, riid))
419     {
420         *ppv = iface;
421     } else {
422         *ppv = NULL;
423         return E_NOINTERFACE;
424     }
425
426     IExtractIconW_AddRef(iface);
427     return S_OK;
428 }
429
430 static ULONG WINAPI UnixFolderIcon_IExtractIconW_AddRef(IExtractIconW *iface) {
431     TRACE("(iface=%p)\n", iface);
432     return 2;
433 }
434
435 static ULONG WINAPI UnixFolderIcon_IExtractIconW_Release(IExtractIconW *iface) {
436     TRACE("(iface=%p)\n", iface);
437     return 1;
438 }
439
440 static HRESULT WINAPI UnixFolderIcon_IExtractIconW_GetIconLocation(IExtractIconW *iface, 
441     UINT uFlags, LPWSTR szIconFile, UINT cchMax, INT* piIndex, UINT* pwFlags)
442 {
443     UnixFolderIcon *This = ADJUST_THIS(UnixFolderIcon, IExtractIconW, iface);
444         
445     TRACE("(iface=%p, uFlags=%u, szIconFile=%s, cchMax=%u, piIndex=%p, pwFlags=%p)\n",
446             iface, uFlags, debugstr_w(szIconFile), cchMax, piIndex, pwFlags);
447     
448     lstrcpynW(szIconFile, swShell32Name, cchMax);
449     if (This->bFolder) {
450         *piIndex = (uFlags & GIL_OPENICON) ? -IDI_SHELL_FOLDER_OPEN : -IDI_SHELL_FOLDER;
451     } else {
452         *piIndex = -IDI_SHELL_DOCUMENT;
453     }
454     *pwFlags = 0;
455
456     return S_OK;
457 }
458
459 static HRESULT WINAPI UnixFolderIcon_IExtractIconW_Extract(
460     IExtractIconW *iface, LPCWSTR pszFile, UINT nIconIndex, HICON* phiconLarge, HICON* phiconSmall, 
461     UINT nIconSize)
462 {
463     TRACE("(iface=%p, pszFile=%s, nIconIndex=%u, phiconLarge=%p, phiconSmall=%p, nIconSize=%u)"
464           "stub\n", iface, debugstr_w(pszFile), nIconIndex, phiconLarge, phiconSmall, nIconSize);
465
466     return E_NOTIMPL;
467 }
468
469 /* VTable for the IExtractIconW interface of the UnixFolderIcon class. 
470  */
471 static const IExtractIconWVtbl UnixFolderIcon_IExtractIconW_Vtbl = {
472     UnixFolderIcon_IExtractIconW_QueryInterface,
473     UnixFolderIcon_IExtractIconW_AddRef,
474     UnixFolderIcon_IExtractIconW_Release,
475     UnixFolderIcon_IExtractIconW_GetIconLocation,
476     UnixFolderIcon_IExtractIconW_Extract
477 };
478
479 /* The singleton instance
480  */
481 UnixFolderIcon UnixFolderIconSingleton = { &UnixFolderIcon_IExtractIconW_Vtbl, TRUE };
482 UnixFolderIcon UnixDocumentIconSingleton = { &UnixFolderIcon_IExtractIconW_Vtbl, FALSE };
483
484 /******************************************************************************
485  * UnixFolder
486  *
487  * Class whose heap based instances represent unix filesystem directories.
488  */
489
490 /* UnixFolder object layout and typedef.
491  */
492 typedef struct _UnixFolder {
493     const IShellFolder2Vtbl  *lpIShellFolder2Vtbl;
494     const IPersistFolder2Vtbl *lpIPersistFolder2Vtbl;
495     ULONG m_cRef;
496     CHAR *m_pszPath;
497     LPITEMIDLIST m_pidlLocation;
498     LPITEMIDLIST *m_apidlSubDirs;
499     DWORD m_cSubDirs;
500 } UnixFolder;
501
502 static void UnixFolder_Destroy(UnixFolder *pUnixFolder) {
503     DWORD i;
504
505     TRACE("(pUnixFolder=%p)\n", pUnixFolder);
506     
507     if (pUnixFolder->m_apidlSubDirs) 
508         for (i=0; i < pUnixFolder->m_cSubDirs; i++) 
509             SHFree(pUnixFolder->m_apidlSubDirs[i]);
510     SHFree(pUnixFolder->m_apidlSubDirs);
511     SHFree(pUnixFolder->m_pszPath);
512     ILFree(pUnixFolder->m_pidlLocation);
513     SHFree(pUnixFolder);
514 }
515
516 static HRESULT WINAPI UnixFolder_IShellFolder2_QueryInterface(IShellFolder2 *iface, REFIID riid, 
517     void **ppv) 
518 {
519     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
520         
521     TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
522     
523     if (!ppv) return E_INVALIDARG;
524     
525     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IShellFolder, riid) || 
526         IsEqualIID(&IID_IShellFolder2, riid)) 
527     {
528         *ppv = &This->lpIShellFolder2Vtbl;
529     } else if (IsEqualIID(&IID_IPersistFolder2, riid) || IsEqualIID(&IID_IPersistFolder, riid) || 
530                IsEqualIID(&IID_IPersist, riid)) 
531     {
532         *ppv = &This->lpIPersistFolder2Vtbl;
533     } else {
534         *ppv = NULL;
535         return E_NOINTERFACE;
536     }
537
538     IUnknown_AddRef((IUnknown*)*ppv);
539     return S_OK;
540 }
541
542 static ULONG WINAPI UnixFolder_IShellFolder2_AddRef(IShellFolder2 *iface) {
543     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
544
545     TRACE("(iface=%p)\n", iface);
546
547     return InterlockedIncrement(&This->m_cRef);
548 }
549
550 static ULONG WINAPI UnixFolder_IShellFolder2_Release(IShellFolder2 *iface) {
551     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
552     ULONG cRef;
553     
554     TRACE("(iface=%p)\n", iface);
555
556     cRef = InterlockedDecrement(&This->m_cRef);
557     
558     if (!cRef) 
559         UnixFolder_Destroy(This);
560
561     return cRef;
562 }
563
564 static HRESULT WINAPI UnixFolder_IShellFolder2_ParseDisplayName(IShellFolder2* iface, HWND hwndOwner, 
565     LPBC pbcReserved, LPOLESTR lpszDisplayName, ULONG* pchEaten, LPITEMIDLIST* ppidl, 
566     ULONG* pdwAttributes)
567 {
568     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
569     int cPathLen;
570     char *pszAnsiPath, *pBackslash;
571     BOOL result;
572
573     TRACE("(iface=%p, hwndOwner=%p, pbcReserved=%p, lpszDisplayName=%s, pchEaten=%p, ppidl=%p, "
574           "pdwAttributes=%p) stub\n", iface, hwndOwner, pbcReserved, debugstr_w(lpszDisplayName), 
575           pchEaten, ppidl, pdwAttributes);
576     pchEaten = NULL;
577     cPathLen = lstrlenW(lpszDisplayName);
578     pszAnsiPath = (char*)SHAlloc(cPathLen+1);
579     WideCharToMultiByte(CP_ACP, 0, lpszDisplayName, -1, pszAnsiPath, cPathLen+1, NULL, NULL);
580
581     for (pBackslash = strchr(pszAnsiPath, '\\'); pBackslash; pBackslash = strchr(pBackslash, '\\'))
582         *pBackslash = '/';
583     
584     result = UNIXFS_path_to_pidl(This->m_pszPath, pszAnsiPath, ppidl);
585     if (result && pdwAttributes && *pdwAttributes)
586     {
587         /* need to traverse to the last element for the attribute */
588         LPCITEMIDLIST pidl, last_pidl;
589         pidl = last_pidl = *ppidl;
590         while(pidl && pidl->mkid.cb)
591         {
592             last_pidl = pidl;
593             pidl = ILGetNext(pidl);
594         }
595         SHELL32_GetItemAttributes((IShellFolder*)iface, last_pidl, pdwAttributes);
596     }
597
598     SHFree(pszAnsiPath);
599    
600     if (!result) TRACE("FAILED!\n");
601     
602     return result ? S_OK : E_FAIL;
603 }
604
605 static IUnknown *UnixSubFolderIterator_Construct(UnixFolder *pUnixFolder, SHCONTF fFilter);
606
607 static HRESULT WINAPI UnixFolder_IShellFolder2_EnumObjects(IShellFolder2* iface, HWND hwndOwner, 
608     SHCONTF grfFlags, IEnumIDList** ppEnumIDList)
609 {
610     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
611     IUnknown *newIterator;
612     HRESULT hr;
613     
614     TRACE("(iface=%p, hwndOwner=%p, grfFlags=%08lx, ppEnumIDList=%p)\n", 
615             iface, hwndOwner, grfFlags, ppEnumIDList);
616
617     newIterator = UnixSubFolderIterator_Construct(This, grfFlags);
618     hr = IUnknown_QueryInterface(newIterator, &IID_IEnumIDList, (void**)ppEnumIDList);
619     IUnknown_Release(newIterator);
620     
621     return hr;
622 }
623
624 static HRESULT WINAPI UnixFolder_IShellFolder2_BindToObject(IShellFolder2* iface, LPCITEMIDLIST pidl,
625     LPBC pbcReserved, REFIID riid, void** ppvOut)
626 {
627     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
628     IPersistFolder2 *persistFolder;
629     LPITEMIDLIST pidlSubFolder;
630     HRESULT hr;
631         
632     TRACE("(iface=%p, pidl=%p, pbcReserver=%p, riid=%p, ppvOut=%p)\n", 
633             iface, pidl, pbcReserved, riid, ppvOut);
634
635     hr = UnixFolder_Constructor(NULL, &IID_IPersistFolder2, (void**)&persistFolder);
636     if (!SUCCEEDED(hr)) return hr;
637     hr = IPersistFolder_QueryInterface(persistFolder, riid, (void**)ppvOut);
638     
639     pidlSubFolder = ILCombine(This->m_pidlLocation, pidl);
640     IPersistFolder2_Initialize(persistFolder, pidlSubFolder);
641     IPersistFolder2_Release(persistFolder);
642     ILFree(pidlSubFolder);
643
644     return hr;
645 }
646
647 static HRESULT WINAPI UnixFolder_IShellFolder2_BindToStorage(IShellFolder2* This, LPCITEMIDLIST pidl, 
648     LPBC pbcReserved, REFIID riid, void** ppvObj)
649 {
650     TRACE("stub\n");
651     return E_NOTIMPL;
652 }
653
654 static HRESULT WINAPI UnixFolder_IShellFolder2_CompareIDs(IShellFolder2* iface, LPARAM lParam, 
655     LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
656 {
657     BOOL isEmpty1, isEmpty2;
658     HRESULT hr = E_FAIL;
659     LPITEMIDLIST firstpidl;
660     IShellFolder2 *psf;
661     int compare;
662
663     TRACE("(iface=%p, lParam=%ld, pidl1=%p, pidl2=%p)\n", iface, lParam, pidl1, pidl2);
664     
665     isEmpty1 = !pidl1 || !pidl1->mkid.cb;
666     isEmpty2 = !pidl2 || !pidl2->mkid.cb;
667
668     if (isEmpty1 && isEmpty2) 
669         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
670     else if (isEmpty1)
671         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
672     else if (isEmpty2)
673         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
674
675     if (_ILIsFolder(pidl1) && !_ILIsFolder(pidl2)) 
676         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
677     if (!_ILIsFolder(pidl1) && _ILIsFolder(pidl2))
678         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
679
680     compare = CompareStringA(LOCALE_USER_DEFAULT, NORM_IGNORECASE, 
681                              _ILGetTextPointer(pidl1), NAME_LEN_FROM_LPSHITEMID(pidl1),
682                              _ILGetTextPointer(pidl2), NAME_LEN_FROM_LPSHITEMID(pidl2));
683     
684     if ((compare == CSTR_LESS_THAN) || (compare == CSTR_GREATER_THAN)) 
685         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)(compare == CSTR_LESS_THAN)?-1:1);
686
687     if (pidl1->mkid.cb < pidl2->mkid.cb)
688         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
689     else if (pidl1->mkid.cb > pidl2->mkid.cb)
690         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
691
692     firstpidl = ILCloneFirst(pidl1);
693     pidl1 = ILGetNext(pidl1);
694     pidl2 = ILGetNext(pidl2);
695     
696     hr = IShellFolder2_BindToObject(iface, firstpidl, NULL, &IID_IShellFolder, (LPVOID*)&psf);
697     if (SUCCEEDED(hr)) {
698         hr = IShellFolder_CompareIDs(psf, lParam, pidl1, pidl2);
699         IShellFolder2_Release(psf);
700     }
701
702     ILFree(firstpidl);
703     return hr;
704 }
705
706 static HRESULT WINAPI UnixFolder_IShellFolder2_CreateViewObject(IShellFolder2* iface, HWND hwndOwner,
707     REFIID riid, void** ppv)
708 {
709     HRESULT hr = E_INVALIDARG;
710         
711     TRACE("(iface=%p, hwndOwner=%p, riid=%p, ppv=%p) stub\n", iface, hwndOwner, riid, ppv);
712     
713     if (!ppv) return E_INVALIDARG;
714     *ppv = NULL;
715     
716     if (IsEqualIID(&IID_IShellView, riid)) {
717         LPSHELLVIEW pShellView;
718         
719         pShellView = IShellView_Constructor((IShellFolder*)iface);
720         if (pShellView) {
721             hr = IShellView_QueryInterface(pShellView, riid, ppv);
722             IShellView_Release(pShellView);
723         }
724     } 
725     
726     return hr;
727 }
728
729 static HRESULT WINAPI UnixFolder_IShellFolder2_GetAttributesOf(IShellFolder2* iface, UINT cidl, 
730     LPCITEMIDLIST* apidl, SFGAOF* rgfInOut)
731 {
732     UINT i;
733     SFGAOF flags= ~(SFGAOF)0;
734         
735     TRACE("(iface=%p, cidl=%u, apidl=%p, rgfInOut=%p) semi-stub\n", iface, cidl, apidl, rgfInOut);
736    
737     for (i=0; i<cidl; i++) {
738         LPPIDLDATA pData = _ILGetDataPointer(apidl[i]);
739         if (!pData) continue;
740         if (pData->type == PT_FOLDER) flags &= (SFGAO_FILESYSTEM|SFGAO_FOLDER|SFGAO_HASSUBFOLDER);
741         if (pData->type == PT_VALUE) flags &= SFGAO_FILESYSTEM;
742     }
743     
744     *rgfInOut = *rgfInOut & flags;
745             
746     return S_OK;
747 }
748
749 static HRESULT WINAPI UnixFolder_IShellFolder2_GetUIObjectOf(IShellFolder2* iface, HWND hwndOwner, 
750     UINT cidl, LPCITEMIDLIST* apidl, REFIID riid, UINT* prgfInOut, void** ppvOut)
751 {
752     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
753     
754     TRACE("(iface=%p, hwndOwner=%p, cidl=%d, apidl=%p, riid=%s, prgfInOut=%p, ppv=%p)\n",
755         iface, hwndOwner, cidl, apidl, debugstr_guid(riid), prgfInOut, ppvOut);
756
757     if (IsEqualIID(&IID_IContextMenu, riid)) {
758         *ppvOut = ISvItemCm_Constructor((IShellFolder*)iface, This->m_pidlLocation, apidl, cidl);
759         return S_OK;
760     } else if (IsEqualIID(&IID_IDataObject, riid)) {
761         *ppvOut = IDataObject_Constructor(hwndOwner, This->m_pidlLocation, apidl, cidl);
762         return S_OK;
763     } else if (IsEqualIID(&IID_IExtractIconA, riid)) {
764         FIXME("IExtractIconA\n");
765         return E_FAIL;
766     } else if (IsEqualIID(&IID_IExtractIconW, riid)) {
767         if (cidl != 1) return E_FAIL;
768         if (_ILIsFolder(apidl[0])) 
769             *ppvOut = &UnixFolderIconSingleton;
770         else
771             *ppvOut = &UnixDocumentIconSingleton;
772         return S_OK;
773     } else if (IsEqualIID(&IID_IDropTarget, riid)) {
774         FIXME("IDropTarget\n");
775         return E_FAIL;
776     } else if (IsEqualIID(&IID_IShellLinkW, riid)) {
777         FIXME("IShellLinkW\n");
778         return E_FAIL;
779     } else if (IsEqualIID(&IID_IShellLinkA, riid)) {
780         FIXME("IShellLinkA\n");
781         return E_FAIL;
782     } else {
783         FIXME("Unknown interface %s in GetUIObjectOf\n", debugstr_guid(riid));
784         return E_NOINTERFACE;
785     }
786 }
787
788 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDisplayNameOf(IShellFolder2* iface, 
789     LPCITEMIDLIST pidl, SHGDNF uFlags, STRRET* lpName)
790 {
791     UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
792     HRESULT hr = S_OK;    
793
794     TRACE("(iface=%p, pidl=%p, uFlags=%lx, lpName=%p)\n", iface, pidl, uFlags, lpName);
795     
796     if ((GET_SHGDN_FOR(uFlags) == SHGDN_FORPARSING) &&
797         (GET_SHGDN_RELATION(uFlags) != SHGDN_INFOLDER))
798     {
799         if (!pidl->mkid.cb) {
800             lpName->uType = STRRET_CSTR;
801             strcpy(lpName->u.cStr, This->m_pszPath);
802         } else {
803             IShellFolder *pSubFolder;
804             USHORT emptyIDL = 0;
805
806             hr = IShellFolder_BindToObject(iface, pidl, NULL, &IID_IShellFolder, (void**)&pSubFolder);
807             if (!SUCCEEDED(hr)) return hr;
808        
809             hr = IShellFolder_GetDisplayNameOf(pSubFolder, (LPITEMIDLIST)&emptyIDL, uFlags, lpName);
810             IShellFolder_Release(pSubFolder);
811         }
812     } else {
813         char *pszFileName = _ILGetTextPointer(pidl);
814         lpName->uType = STRRET_CSTR;
815         strcpy(lpName->u.cStr, pszFileName ? pszFileName : "");
816     }
817
818     return hr;
819 }
820
821 static HRESULT WINAPI UnixFolder_IShellFolder2_SetNameOf(IShellFolder2* This, HWND hwnd, 
822     LPCITEMIDLIST pidl, LPCOLESTR lpszName, SHGDNF uFlags, LPITEMIDLIST* ppidlOut)
823 {
824     TRACE("stub\n");
825     return E_NOTIMPL;
826 }
827
828 static HRESULT WINAPI UnixFolder_IShellFolder2_EnumSearches(IShellFolder2* iface, 
829     IEnumExtraSearch **ppEnum) 
830 {
831     TRACE("stub\n");
832     return E_NOTIMPL;
833 }
834
835 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumn(IShellFolder2* iface, 
836     DWORD dwReserved, ULONG *pSort, ULONG *pDisplay) 
837 {
838     TRACE("stub\n");
839     return E_NOTIMPL;
840 }
841
842 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumnState(IShellFolder2* iface, 
843     UINT iColumn, SHCOLSTATEF *pcsFlags)
844 {
845     TRACE("stub\n");
846     return E_NOTIMPL;
847 }
848
849 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultSearchGUID(IShellFolder2* iface, 
850     GUID *pguid)
851 {
852     TRACE("stub\n");
853     return E_NOTIMPL;
854 }
855
856 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsEx(IShellFolder2* iface, 
857     LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
858 {
859     TRACE("stub\n");
860     return E_NOTIMPL;
861 }
862
863 #define SHELLVIEWCOLUMNS 6 
864
865 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsOf(IShellFolder2* iface, 
866     LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *psd)
867 {
868     HRESULT hr = E_FAIL;
869     struct passwd *pPasswd;
870     struct group *pGroup;
871     static const shvheader SFHeader[SHELLVIEWCOLUMNS] = {
872         {IDS_SHV_COLUMN1,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
873         {IDS_SHV_COLUMN5,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
874         {IDS_SHV_COLUMN10, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7},
875         {IDS_SHV_COLUMN11, SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7},
876         {IDS_SHV_COLUMN2,  SHCOLSTATE_TYPE_STR  | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 8},
877         {IDS_SHV_COLUMN4,  SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10}
878     };
879
880     TRACE("(iface=%p, pidl=%p, iColumn=%d, psd=%p) stub\n", iface, pidl, iColumn, psd);
881     
882     if (!psd || iColumn >= SHELLVIEWCOLUMNS)
883         return E_INVALIDARG;
884
885     if (!pidl) {
886         psd->fmt = SFHeader[iColumn].fmt;
887         psd->cxChar = SFHeader[iColumn].cxChar;
888         psd->str.uType = STRRET_CSTR;
889         LoadStringA(shell32_hInstance, SFHeader[iColumn].colnameid, psd->str.u.cStr, MAX_PATH);
890         return S_OK;
891     } else {
892         StatStruct *pStatStruct = LPSTATSTRUCT_FROM_LPSHITEMID(pidl);
893         psd->str.u.cStr[0] = '\0';
894         psd->str.uType = STRRET_CSTR;
895         switch (iColumn) {
896             case 0:
897                 hr = IShellFolder2_GetDisplayNameOf(iface, pidl, SHGDN_NORMAL|SHGDN_INFOLDER, &psd->str);
898                 break;
899             case 1:
900                 psd->str.u.cStr[0] = S_ISDIR(pStatStruct->st_mode) ? 'd' : '-';
901                 psd->str.u.cStr[1] = (pStatStruct->st_mode & S_IRUSR) ? 'r' : '-';
902                 psd->str.u.cStr[2] = (pStatStruct->st_mode & S_IWUSR) ? 'w' : '-';
903                 psd->str.u.cStr[3] = (pStatStruct->st_mode & S_IXUSR) ? 'x' : '-';
904                 psd->str.u.cStr[4] = (pStatStruct->st_mode & S_IRGRP) ? 'r' : '-';
905                 psd->str.u.cStr[5] = (pStatStruct->st_mode & S_IWGRP) ? 'w' : '-';
906                 psd->str.u.cStr[6] = (pStatStruct->st_mode & S_IXGRP) ? 'x' : '-';
907                 psd->str.u.cStr[7] = (pStatStruct->st_mode & S_IROTH) ? 'r' : '-';
908                 psd->str.u.cStr[8] = (pStatStruct->st_mode & S_IWOTH) ? 'w' : '-';
909                 psd->str.u.cStr[9] = (pStatStruct->st_mode & S_IXOTH) ? 'x' : '-';
910                 psd->str.u.cStr[10] = '\0';
911                 break;
912             case 2:
913                 pPasswd = getpwuid(pStatStruct->st_uid);
914                 if (pPasswd) strcpy(psd->str.u.cStr, pPasswd->pw_name);
915                 break;
916             case 3:
917                 pGroup = getgrgid(pStatStruct->st_gid);
918                 if (pGroup) strcpy(psd->str.u.cStr, pGroup->gr_name);
919                 break;
920             case 4:
921                 _ILGetFileSize(pidl, psd->str.u.cStr, MAX_PATH);
922                 break;
923             case 5:
924                 _ILGetFileDate(pidl, psd->str.u.cStr, MAX_PATH);
925                 break;
926         }
927     }
928     
929     return hr;
930 }
931
932 static HRESULT WINAPI UnixFolder_IShellFolder2_MapColumnToSCID(IShellFolder2* iface, UINT iColumn,
933     SHCOLUMNID *pscid) 
934 {
935     TRACE("stub\n");
936     return E_NOTIMPL;
937 }
938
939 /* VTable for UnixFolder's IShellFolder2 interface.
940  */
941 static const IShellFolder2Vtbl UnixFolder_IShellFolder2_Vtbl = {
942     UnixFolder_IShellFolder2_QueryInterface,
943     UnixFolder_IShellFolder2_AddRef,
944     UnixFolder_IShellFolder2_Release,
945     UnixFolder_IShellFolder2_ParseDisplayName,
946     UnixFolder_IShellFolder2_EnumObjects,
947     UnixFolder_IShellFolder2_BindToObject,
948     UnixFolder_IShellFolder2_BindToStorage,
949     UnixFolder_IShellFolder2_CompareIDs,
950     UnixFolder_IShellFolder2_CreateViewObject,
951     UnixFolder_IShellFolder2_GetAttributesOf,
952     UnixFolder_IShellFolder2_GetUIObjectOf,
953     UnixFolder_IShellFolder2_GetDisplayNameOf,
954     UnixFolder_IShellFolder2_SetNameOf,
955     UnixFolder_IShellFolder2_GetDefaultSearchGUID,
956     UnixFolder_IShellFolder2_EnumSearches,
957     UnixFolder_IShellFolder2_GetDefaultColumn,
958     UnixFolder_IShellFolder2_GetDefaultColumnState,
959     UnixFolder_IShellFolder2_GetDetailsEx,
960     UnixFolder_IShellFolder2_GetDetailsOf,
961     UnixFolder_IShellFolder2_MapColumnToSCID
962 };
963
964 static HRESULT WINAPI UnixFolder_IPersistFolder2_QueryInterface(IPersistFolder2* This, REFIID riid, 
965     void** ppvObject)
966 {
967     return UnixFolder_IShellFolder2_QueryInterface(
968                 (IShellFolder2*)ADJUST_THIS(UnixFolder, IPersistFolder2, This), riid, ppvObject);
969 }
970
971 static ULONG WINAPI UnixFolder_IPersistFolder2_AddRef(IPersistFolder2* This)
972 {
973     return UnixFolder_IShellFolder2_AddRef(
974                 (IShellFolder2*)ADJUST_THIS(UnixFolder, IPersistFolder2, This));
975 }
976
977 static ULONG WINAPI UnixFolder_IPersistFolder2_Release(IPersistFolder2* This)
978 {
979     return UnixFolder_IShellFolder2_Release(
980                 (IShellFolder2*)ADJUST_THIS(UnixFolder, IPersistFolder2, This));
981 }
982
983 static HRESULT WINAPI UnixFolder_IPersistFolder2_GetClassID(IPersistFolder2* This, CLSID* pClassID)
984 {
985     TRACE("stub\n");
986     return E_NOTIMPL;
987 }
988
989 static HRESULT WINAPI UnixFolder_IPersistFolder2_Initialize(IPersistFolder2* iface, LPCITEMIDLIST pidl)
990 {
991     UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder2, iface);
992     
993     TRACE("(iface=%p, pidl=%p)\n", iface, pidl);
994
995     This->m_pidlLocation = ILClone(pidl);
996     UNIXFS_pidl_to_path(pidl, &This->m_pszPath);
997     UNIXFS_build_subfolder_pidls(This->m_pszPath, &This->m_apidlSubDirs, &This->m_cSubDirs);
998     
999     return S_OK;
1000 }
1001
1002 static HRESULT WINAPI UnixFolder_IPersistFolder2_GetCurFolder(IPersistFolder2* iface, LPITEMIDLIST* ppidl)
1003 {
1004     UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder2, iface);
1005     TRACE ("(%p)->(%p)\n", This, ppidl);
1006
1007     if (!ppidl)
1008         return E_POINTER;
1009     *ppidl = ILClone (This->m_pidlLocation);
1010     return S_OK;
1011 }
1012         
1013 /* VTable for UnixFolder's IPersistFolder interface.
1014  */
1015 static const IPersistFolder2Vtbl UnixFolder_IPersistFolder2_Vtbl = {
1016     UnixFolder_IPersistFolder2_QueryInterface,
1017     UnixFolder_IPersistFolder2_AddRef,
1018     UnixFolder_IPersistFolder2_Release,
1019     UnixFolder_IPersistFolder2_GetClassID,
1020     UnixFolder_IPersistFolder2_Initialize,
1021     UnixFolder_IPersistFolder2_GetCurFolder
1022 };
1023
1024 /******************************************************************************
1025  * UnixFolder_Constructor [Internal]
1026  *
1027  * PARAMS
1028  *  pUnkOuter [I] Outer class for aggregation. Currently ignored.
1029  *  riid      [I] Interface asked for by the client.
1030  *  ppv       [O] Pointer to an riid interface to the UnixFolder object.
1031  *
1032  * NOTES
1033  *  This is the only function exported from shfldr_unixfs.c. It's called from
1034  *  shellole.c's default class factory and thus has to exhibit a LPFNCREATEINSTANCE
1035  *  compatible signature.
1036  */
1037 HRESULT WINAPI UnixFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
1038     HRESULT hr;
1039     UnixFolder *pUnixFolder;
1040     
1041     TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
1042
1043     pUnixFolder = SHAlloc((ULONG)sizeof(UnixFolder));
1044     pUnixFolder->lpIShellFolder2Vtbl = &UnixFolder_IShellFolder2_Vtbl;
1045     pUnixFolder->lpIPersistFolder2Vtbl = &UnixFolder_IPersistFolder2_Vtbl;
1046     pUnixFolder->m_cRef = 0;
1047     pUnixFolder->m_pszPath = NULL;
1048     pUnixFolder->m_apidlSubDirs = NULL;
1049     pUnixFolder->m_cSubDirs = 0;
1050
1051     UnixFolder_IShellFolder2_AddRef(STATIC_CAST(IShellFolder2, pUnixFolder));
1052     hr = UnixFolder_IShellFolder2_QueryInterface(STATIC_CAST(IShellFolder2, pUnixFolder), riid, ppv);
1053     UnixFolder_IShellFolder2_Release(STATIC_CAST(IShellFolder2, pUnixFolder));
1054     return hr;
1055 }
1056
1057 /******************************************************************************
1058  * UnixSubFolderIterator
1059  *
1060  * Class whose heap based objects represent iterators over the sub-directories
1061  * of a given UnixFolder object. 
1062  */
1063
1064 /* UnixSubFolderIterator object layout and typedef.
1065  */
1066 typedef struct _UnixSubFolderIterator {
1067     const IEnumIDListVtbl *lpIEnumIDListVtbl;
1068     ULONG m_cRef;
1069     UnixFolder *m_pUnixFolder;
1070     ULONG m_cIdx;
1071     SHCONTF m_fFilter;
1072 } UnixSubFolderIterator;
1073
1074 static void UnixSubFolderIterator_Destroy(UnixSubFolderIterator *iterator) {
1075     TRACE("(iterator=%p)\n", iterator);
1076         
1077     UnixFolder_IShellFolder2_Release((IShellFolder2*)iterator->m_pUnixFolder);
1078     SHFree(iterator);
1079 }
1080
1081 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_QueryInterface(IEnumIDList* iface, 
1082     REFIID riid, void** ppv)
1083 {
1084     TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
1085     
1086     if (!ppv) return E_INVALIDARG;
1087     
1088     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IEnumIDList, riid)) {
1089         *ppv = iface;
1090     } else {
1091         *ppv = NULL;
1092         return E_NOINTERFACE;
1093     }
1094
1095     IEnumIDList_AddRef(iface);
1096     return S_OK;
1097 }
1098                             
1099 static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_AddRef(IEnumIDList* iface)
1100 {
1101     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1102
1103     TRACE("(iface=%p)\n", iface);
1104    
1105     return InterlockedIncrement(&This->m_cRef);
1106 }
1107
1108 static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_Release(IEnumIDList* iface)
1109 {
1110     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1111     ULONG cRef;
1112     
1113     TRACE("(iface=%p)\n", iface);
1114
1115     cRef = InterlockedDecrement(&This->m_cRef);
1116     
1117     if (!cRef) 
1118         UnixSubFolderIterator_Destroy(This);
1119
1120     return cRef;
1121 }
1122
1123 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Next(IEnumIDList* iface, ULONG celt, 
1124     LPITEMIDLIST* rgelt, ULONG* pceltFetched)
1125 {
1126     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1127     ULONG i;
1128
1129     TRACE("(iface=%p, celt=%ld, rgelt=%p, pceltFetched=%p)\n", iface, celt, rgelt, pceltFetched);
1130    
1131     for (i=0; (i < celt) && (This->m_cIdx < This->m_pUnixFolder->m_cSubDirs); This->m_cIdx++) {
1132         LPITEMIDLIST pCurrent = This->m_pUnixFolder->m_apidlSubDirs[This->m_cIdx];
1133         if (UNIXFS_is_pidl_of_type(pCurrent, This->m_fFilter)) {
1134             rgelt[i] = ILClone(pCurrent);
1135             i++;
1136         }
1137     }
1138
1139     if (pceltFetched)
1140         *pceltFetched = i;
1141
1142     return i == celt ? S_OK : S_FALSE;
1143 }
1144
1145 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Skip(IEnumIDList* iface, ULONG celt)
1146 {
1147     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1148     ULONG i;
1149     
1150     TRACE("(iface=%p, celt=%ld)\n", iface, celt);
1151
1152     for (i=0; i < celt; i++) {
1153         while (This->m_cIdx < This->m_pUnixFolder->m_cSubDirs &&
1154                !UNIXFS_is_pidl_of_type(This->m_pUnixFolder->m_apidlSubDirs[This->m_cIdx], This->m_fFilter)) 
1155         {
1156             This->m_cIdx++;
1157         }
1158         This->m_cIdx++;
1159     }
1160     
1161     if (This->m_cIdx > This->m_pUnixFolder->m_cSubDirs) {
1162         This->m_cIdx = This->m_pUnixFolder->m_cSubDirs;
1163         return S_FALSE;
1164     } else {
1165         return S_OK;
1166     }
1167 }
1168
1169 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Reset(IEnumIDList* iface)
1170 {
1171     UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1172         
1173     TRACE("(iface=%p)\n", iface);
1174
1175     This->m_cIdx = 0;
1176     
1177     return S_OK;
1178 }
1179
1180 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Clone(IEnumIDList* This, 
1181     IEnumIDList** ppenum)
1182 {
1183     TRACE("stub\n");
1184     return E_NOTIMPL;
1185 }
1186
1187 /* VTable for UnixSubFolderIterator's IEnumIDList interface.
1188  */
1189 static const IEnumIDListVtbl UnixSubFolderIterator_IEnumIDList_Vtbl = {
1190     UnixSubFolderIterator_IEnumIDList_QueryInterface,
1191     UnixSubFolderIterator_IEnumIDList_AddRef,
1192     UnixSubFolderIterator_IEnumIDList_Release,
1193     UnixSubFolderIterator_IEnumIDList_Next,
1194     UnixSubFolderIterator_IEnumIDList_Skip,
1195     UnixSubFolderIterator_IEnumIDList_Reset,
1196     UnixSubFolderIterator_IEnumIDList_Clone
1197 };
1198
1199 static IUnknown *UnixSubFolderIterator_Construct(UnixFolder *pUnixFolder, SHCONTF fFilter) {
1200     UnixSubFolderIterator *iterator;
1201
1202     TRACE("(pUnixFolder=%p)\n", pUnixFolder);
1203     
1204     iterator = SHAlloc((ULONG)sizeof(UnixSubFolderIterator));
1205     iterator->lpIEnumIDListVtbl = &UnixSubFolderIterator_IEnumIDList_Vtbl;
1206     iterator->m_cRef = 0;
1207     iterator->m_cIdx = 0;
1208     iterator->m_pUnixFolder = pUnixFolder;
1209     iterator->m_fFilter = fFilter;
1210
1211     UnixSubFolderIterator_IEnumIDList_AddRef((IEnumIDList*)iterator);
1212     UnixFolder_IShellFolder2_AddRef((IShellFolder2*)pUnixFolder);
1213     
1214     return (IUnknown*)iterator;
1215 }