2 * UNIXFS - Shell namespace extension for the unix filesystem
4 * Copyright (C) 2005 Michael Jung
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.
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.
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
29 #ifdef HAVE_SYS_STAT_H
30 # include <sys/stat.h>
39 #define NONAMELESSUNION
40 #define NONAMELESSSTRUCT
49 #include "wine/debug.h"
51 #include "shell32_main.h"
52 #include "shellfolder.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(shell);
59 const GUID CLSID_UnixFolder = {0xcc702eb2, 0x7dc5, 0x11d9, {0xc6, 0x87, 0x00, 0x04, 0x23, 0x8a, 0x01, 0xcd}};
60 const GUID CLSID_UnixDosFolder = {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};
62 #define ADJUST_THIS(c,m,p) ((c*)(((long)p)-(long)&(((c*)0)->lp##m##Vtbl)))
63 #define STATIC_CAST(i,p) ((i*)&p->lp##i##Vtbl)
65 /* FileStruct reserves one byte for szNames, thus we don't have to
66 * alloc a byte for the terminating '\0' of 'name'. Two of the
67 * additional bytes are for SHITEMID's cb field. One is for IDLDATA's
68 * type field. One is for FileStruct's szNames field, to terminate
69 * the alternate DOS name, which we don't use here.
71 #define SHITEMID_LEN_FROM_NAME_LEN(n) \
72 (sizeof(USHORT)+sizeof(PIDLTYPE)+sizeof(FileStruct)+(n)+sizeof(char))
73 #define NAME_LEN_FROM_LPSHITEMID(s) \
74 (((LPSHITEMID)s)->cb-sizeof(USHORT)-sizeof(PIDLTYPE)-sizeof(FileStruct)-sizeof(char))
76 #define PATHMODE_UNIX 0
77 #define PATHMODE_DOS 1
79 /* UnixFolder object layout and typedef.
81 typedef struct _UnixFolder {
82 const IShellFolder2Vtbl *lpIShellFolder2Vtbl;
83 const IPersistFolder2Vtbl *lpIPersistFolder2Vtbl;
84 const ISFHelperVtbl *lpISFHelperVtbl;
87 LPITEMIDLIST m_pidlLocation;
88 LPITEMIDLIST *m_apidlSubDirs;
94 /******************************************************************************
95 * UNIXFS_is_rooted_at_desktop [Internal]
97 * Checks if the unixfs namespace extension is rooted at desktop level.
100 * TRUE, if unixfs is rooted at desktop level
103 BOOL UNIXFS_is_rooted_at_desktop(void) {
105 WCHAR *pwszCLSID_UnixDosFolder, wszRootedAtDesktop[69 + CHARS_IN_GUID] = {
106 'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
107 'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
108 'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
109 'N','a','m','e','S','p','a','c','e','\\',0 };
111 if (FAILED(StringFromCLSID(&CLSID_UnixDosFolder, &pwszCLSID_UnixDosFolder)))
114 lstrcatW(wszRootedAtDesktop, pwszCLSID_UnixDosFolder);
115 CoTaskMemFree(pwszCLSID_UnixDosFolder);
117 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRootedAtDesktop, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
124 /******************************************************************************
125 * UNIXFS_is_pidl_of_type [INTERNAL]
127 * Checks for the first SHITEMID of an ITEMIDLIST if it passes a filter.
130 * pIDL [I] The ITEMIDLIST to be checked.
131 * fFilter [I] Shell condition flags, which specify the filter.
134 * TRUE, if pIDL is accepted by fFilter
137 static inline BOOL UNIXFS_is_pidl_of_type(LPITEMIDLIST pIDL, SHCONTF fFilter) {
138 LPPIDLDATA pIDLData = _ILGetDataPointer(pIDL);
139 if (!(fFilter & SHCONTF_INCLUDEHIDDEN) && pIDLData &&
140 (pIDLData->u.file.uFileAttribs & FILE_ATTRIBUTE_HIDDEN))
144 if (_ILIsFolder(pIDL) && (fFilter & SHCONTF_FOLDERS)) return TRUE;
145 if (_ILIsValue(pIDL) && (fFilter & SHCONTF_NONFOLDERS)) return TRUE;
149 /******************************************************************************
150 * UNIXFS_is_dos_device [Internal]
152 * Determines if a unix directory corresponds to any dos device.
155 * statPath [I] The stat struct of the directory, as returned by stat(2).
158 * TRUE, if statPath corresponds to any dos drive letter
161 static BOOL UNIXFS_is_dos_device(const struct stat *statPath) {
162 struct stat statDrive;
165 WCHAR wszDosDevice[4] = { 'A', ':', '\\', 0 };
167 for (dwDriveMap = GetLogicalDrives(); dwDriveMap; dwDriveMap >>= 1, wszDosDevice[0]++) {
168 if (!(dwDriveMap & 0x1)) continue;
169 pszDrivePath = wine_get_unix_file_name(wszDosDevice);
170 if (pszDrivePath && !stat(pszDrivePath, &statDrive)) {
171 HeapFree(GetProcessHeap(), 0, pszDrivePath);
172 if ((statPath->st_dev == statDrive.st_dev) && (statPath->st_ino == statDrive.st_ino))
179 /******************************************************************************
180 * UNIXFS_get_unix_path [Internal]
182 * Convert an absolute dos path to an absolute canonicalized unix path.
183 * Evaluate "/.", "/.." and symbolic links.
186 * pszDosPath [I] An absolute dos path
187 * pszCanonicalPath [O] Buffer of length FILENAME_MAX. Will receive the canonical path.
191 * Failure, FALSE - Path not existent, too long, insufficient rights, to many symlinks
193 static BOOL UNIXFS_get_unix_path(LPCWSTR pszDosPath, char *pszCanonicalPath)
195 char *pPathTail, *pElement, *pCanonicalTail, szPath[FILENAME_MAX], *pszUnixPath;
196 struct stat fileStat;
198 TRACE("(pszDosPath=%s, pszCanonicalPath=%p)\n", debugstr_w(pszDosPath), pszCanonicalPath);
200 if (!pszDosPath || pszDosPath[1] != ':')
203 pszUnixPath = wine_get_unix_file_name(pszDosPath);
204 if (!pszUnixPath) return FALSE;
205 strcpy(szPath, pszUnixPath);
206 HeapFree(GetProcessHeap(), 0, pszUnixPath);
208 /* pCanonicalTail always points to the end of the canonical path constructed
209 * thus far. pPathTail points to the still to be processed part of the input
210 * path. pElement points to the path element currently investigated.
212 *pszCanonicalPath = '\0';
213 pCanonicalTail = pszCanonicalPath;
220 pElement = pPathTail;
221 pPathTail = strchr(pPathTail+1, '/');
222 if (!pPathTail) /* Last path element may not be terminated by '/'. */
223 pPathTail = pElement + strlen(pElement);
224 /* Temporarily terminate the current path element. Will be restored later. */
228 /* Skip "/." path elements */
229 if (!strcmp("/.", pElement)) {
234 /* Remove last element in canonical path for "/.." elements, then skip. */
235 if (!strcmp("/..", pElement)) {
236 char *pTemp = strrchr(pszCanonicalPath, '/');
238 pCanonicalTail = pTemp;
239 *pCanonicalTail = '\0';
244 /* lstat returns zero on success. */
245 if (lstat(szPath, &fileStat))
248 if (S_ISLNK(fileStat.st_mode)) {
249 char szSymlink[FILENAME_MAX];
250 int cLinkLen, cTailLen;
252 /* Avoid infinite loop for recursive links. */
256 cLinkLen = readlink(szPath, szSymlink, FILENAME_MAX);
261 cTailLen = strlen(pPathTail);
263 if (szSymlink[0] == '/') {
264 /* Absolute link. Copy to szPath, concat remaining path and start all over. */
265 if (cLinkLen + cTailLen + 1 > FILENAME_MAX)
268 /* Avoid double slashes. */
269 if (szSymlink[cLinkLen-1] == '/' && pPathTail[0] == '/') {
270 szSymlink[cLinkLen-1] = '\0';
274 memcpy(szSymlink + cLinkLen, pPathTail, cTailLen + 1);
275 memcpy(szPath, szSymlink, cLinkLen + cTailLen + 1);
276 *pszCanonicalPath = '\0';
277 pCanonicalTail = pszCanonicalPath;
280 /* Relative link. Expand into szPath and continue. */
281 char szTemp[FILENAME_MAX];
282 int cTailLen = strlen(pPathTail);
284 if (pElement - szPath + 1 + cLinkLen + cTailLen + 1 > FILENAME_MAX)
287 memcpy(szTemp, pPathTail, cTailLen + 1);
288 memcpy(pElement + 1, szSymlink, cLinkLen);
289 memcpy(pElement + 1 + cLinkLen, szTemp, cTailLen + 1);
290 pPathTail = pElement;
293 /* Regular directory or file. Copy to canonical path */
294 if (pCanonicalTail - pszCanonicalPath + pPathTail - pElement + 1 > FILENAME_MAX)
297 memcpy(pCanonicalTail, pElement, pPathTail - pElement + 1);
298 pCanonicalTail += pPathTail - pElement;
301 } while (pPathTail[0] == '/');
303 TRACE("--> %s\n", debugstr_a(pszCanonicalPath));
308 /******************************************************************************
309 * UNIXFS_build_shitemid [Internal]
311 * Constructs a new SHITEMID for the last component of path 'pszUnixPath' into
315 * pszUnixPath [I] An absolute path. The SHITEMID will be build for the last component.
316 * pIDL [O] SHITEMID will be constructed here.
319 * Success: A pointer to the terminating '\0' character of path.
323 * Minimum size of pIDL is SHITEMID_LEN_FROM_NAME_LEN(strlen(last_component_of_path)).
324 * If what you need is a PIDLLIST with a single SHITEMID, don't forget to append
327 static char* UNIXFS_build_shitemid(char *pszUnixPath, void *pIDL) {
331 struct stat fileStat;
335 TRACE("(pszUnixPath=%s, pIDL=%p)\n", debugstr_a(pszUnixPath), pIDL);
337 /* Compute the SHITEMID's length and wipe it. */
338 pszComponent = strrchr(pszUnixPath, '/') + 1;
339 cComponentLen = strlen(pszComponent);
340 memset(pIDL, 0, SHITEMID_LEN_FROM_NAME_LEN(cComponentLen));
341 ((LPSHITEMID)pIDL)->cb = SHITEMID_LEN_FROM_NAME_LEN(cComponentLen) ;
343 /* We are only interested in regular files and directories. */
344 if (stat(pszUnixPath, &fileStat)) return NULL;
345 if (!S_ISDIR(fileStat.st_mode) && !S_ISREG(fileStat.st_mode)) return NULL;
347 /* Set shell32's standard SHITEMID data fields. */
348 pIDLData = _ILGetDataPointer((LPCITEMIDLIST)pIDL);
349 pIDLData->type = S_ISDIR(fileStat.st_mode) ? PT_FOLDER : PT_VALUE;
350 pIDLData->u.file.dwFileSize = (DWORD)fileStat.st_size;
351 RtlSecondsSince1970ToTime( fileStat.st_mtime, &time );
352 fileTime.dwLowDateTime = time.u.LowPart;
353 fileTime.dwHighDateTime = time.u.HighPart;
354 FileTimeToDosDateTime(&fileTime, &pIDLData->u.file.uFileDate, &pIDLData->u.file.uFileTime);
355 pIDLData->u.file.uFileAttribs = 0;
356 if (S_ISDIR(fileStat.st_mode)) pIDLData->u.file.uFileAttribs |= FILE_ATTRIBUTE_DIRECTORY;
357 if (pszComponent[0] == '.') pIDLData->u.file.uFileAttribs |= FILE_ATTRIBUTE_HIDDEN;
358 memcpy(pIDLData->u.file.szNames, pszComponent, cComponentLen);
360 return pszComponent + cComponentLen;
363 /******************************************************************************
364 * UNIXFS_path_to_pidl [Internal]
367 * pUnixFolder [I] If path is relative, pUnixFolder represents the base path
368 * path [I] An absolute unix or dos path or a path relativ to pUnixFolder
369 * ppidl [O] The corresponding ITEMIDLIST. Release with SHFree/ILFree
373 * Failure: FALSE, invalid params or out of memory
376 * pUnixFolder also carries the information if the path is expected to be unix or dos.
378 static BOOL UNIXFS_path_to_pidl(UnixFolder *pUnixFolder, const WCHAR *path, LPITEMIDLIST *ppidl) {
380 int cSubDirs, cPidlLen, cPathLen;
381 char *pSlash, szCompletePath[FILENAME_MAX], *pNextPathElement;
383 TRACE("pUnixFolder=%p, path=%s, ppidl=%p\n", pUnixFolder, debugstr_w(path), ppidl);
388 /* Build an absolute path and let pNextPathElement point to the interesting
389 * relative sub-path. We need the absolute path to call 'stat', but the pidl
390 * will only contain the relative part.
392 if ((pUnixFolder->m_dwPathMode == PATHMODE_DOS) && (path[1] == ':'))
394 /* Absolute dos path. Convert to unix */
395 if (!UNIXFS_get_unix_path(path, szCompletePath))
397 pNextPathElement = szCompletePath;
399 else if ((pUnixFolder->m_dwPathMode == PATHMODE_UNIX) && (path[0] == '/'))
401 /* Absolute unix path. Just convert to ANSI. */
402 WideCharToMultiByte(CP_ACP, 0, path, -1, szCompletePath, FILENAME_MAX, NULL, NULL);
403 pNextPathElement = szCompletePath;
407 /* Relative dos or unix path. Concat with this folder's path */
408 int cBasePathLen = strlen(pUnixFolder->m_pszPath);
409 memcpy(szCompletePath, pUnixFolder->m_pszPath, cBasePathLen);
410 WideCharToMultiByte(CP_ACP, 0, path, -1, szCompletePath + cBasePathLen,
411 FILENAME_MAX - cBasePathLen, NULL, NULL);
412 pNextPathElement = szCompletePath + cBasePathLen - 1;
414 /* If in dos mode, replace '\' with '/' */
415 if (pUnixFolder->m_dwPathMode == PATHMODE_DOS) {
416 char *pBackslash = strchr(pNextPathElement, '\\');
419 pBackslash = strchr(pBackslash, '\\');
424 /* Special case for the root folder. */
425 if (!strcmp(szCompletePath, "/")) {
426 *ppidl = pidl = (LPITEMIDLIST)SHAlloc(sizeof(USHORT));
427 if (!pidl) return FALSE;
428 pidl->mkid.cb = 0; /* Terminate the ITEMIDLIST */
432 /* Remove trailing slash, if present */
433 cPathLen = strlen(szCompletePath);
434 if (szCompletePath[cPathLen-1] == '/')
435 szCompletePath[cPathLen-1] = '\0';
437 if ((szCompletePath[0] != '/') || (pNextPathElement[0] != '/')) {
438 ERR("szCompletePath: %s, pNextPathElment: %s\n", szCompletePath, pNextPathElement);
442 /* At this point, we have an absolute unix path in szCompletePath
443 * and the relative portion of it in pNextPathElement. Both starting with '/'
444 * and _not_ terminated by a '/'. */
445 TRACE("complete path: %s, relative path: %s\n", szCompletePath, pNextPathElement);
447 /* Count the number of sub-directories in the path */
449 pSlash = pNextPathElement;
452 pSlash = strchr(pSlash+1, '/');
455 /* Allocate enough memory to hold the path. The -cSubDirs is for the '/'
456 * characters, which are not stored in the ITEMIDLIST. */
457 cPidlLen = strlen(pNextPathElement) - cSubDirs + cSubDirs * SHITEMID_LEN_FROM_NAME_LEN(0) + sizeof(USHORT);
458 *ppidl = pidl = (LPITEMIDLIST)SHAlloc(cPidlLen);
459 if (!pidl) return FALSE;
461 /* Concatenate the SHITEMIDs of the sub-directories. */
462 while (*pNextPathElement) {
463 pSlash = strchr(pNextPathElement+1, '/');
464 if (pSlash) *pSlash = '\0';
465 pNextPathElement = UNIXFS_build_shitemid(szCompletePath, pidl);
466 if (pSlash) *pSlash = '/';
468 if (!pNextPathElement) {
472 pidl = ILGetNext(pidl);
474 pidl->mkid.cb = 0; /* Terminate the ITEMIDLIST */
476 if ((int)pidl-(int)*ppidl+sizeof(USHORT) != cPidlLen) /* We've corrupted the heap :( */
477 ERR("Computed length of pidl incorrect. Please report.\n");
482 /******************************************************************************
483 * UNIXFS_build_subfolder_pidls [Internal]
485 * Builds an array of subfolder PIDLs relative to a unix directory
488 * path [I] Name of a unix directory as a zero terminated ascii string
489 * apidl [O] The array of PIDLs
490 * pCount [O] Size of apidl
494 * Failure: FALSE, path is not a valid unix directory or out of memory
497 * The array of PIDLs and each PIDL are allocated with SHAlloc. You'll have
498 * to release each PIDL as well as the array itself with SHFree.
500 static BOOL UNIXFS_build_subfolder_pidls(UnixFolder *pUnixFolder)
502 struct dirent *pDirEntry;
503 struct stat fileStat;
505 DWORD cDirEntries, i;
509 TRACE("(pUnixFolder=%p)\n", pUnixFolder);
511 /* For updates: If there already is an initialized list, release it */
512 if (pUnixFolder->m_cSubDirs > 0) {
513 for (i=0; i < pUnixFolder->m_cSubDirs; i++)
514 SHFree(pUnixFolder->m_apidlSubDirs[i]);
515 SHFree(pUnixFolder->m_apidlSubDirs);
518 pUnixFolder->m_apidlSubDirs = NULL;
519 pUnixFolder->m_cSubDirs = 0;
521 dir = opendir(pUnixFolder->m_pszPath);
523 WARN("Failed to open directory '%s'.\n", pUnixFolder->m_pszPath);
527 /* Allocate space for fully qualified paths */
528 pszFQPath = SHAlloc(strlen(pUnixFolder->m_pszPath) + PATH_MAX);
530 WARN("SHAlloc failed!\n");
534 /* Count number of directory entries. */
535 for (cDirEntries = 0, pDirEntry = readdir(dir); pDirEntry; pDirEntry = readdir(dir)) {
536 if (!strcmp(pDirEntry->d_name, ".") || !strcmp(pDirEntry->d_name, "..")) continue;
537 sprintf(pszFQPath, "%s%s", pUnixFolder->m_pszPath, pDirEntry->d_name);
538 if (!stat(pszFQPath, &fileStat) && (S_ISDIR(fileStat.st_mode) || S_ISREG(fileStat.st_mode))) cDirEntries++;
541 /* If there are no entries, we are done. */
542 if (cDirEntries == 0) {
548 /* Allocate the array of PIDLs */
549 pUnixFolder->m_apidlSubDirs = SHAlloc(cDirEntries * sizeof(LPITEMIDLIST));
550 if (!pUnixFolder->m_apidlSubDirs) {
551 WARN("SHAlloc failed!\n");
555 /* Allocate and initialize one SHITEMID per sub-directory. */
556 for (rewinddir(dir), pDirEntry = readdir(dir), i = 0; pDirEntry; pDirEntry = readdir(dir)) {
559 if (!strcmp(pDirEntry->d_name, ".") || !strcmp(pDirEntry->d_name, "..")) continue;
561 sprintf(pszFQPath, "%s%s", pUnixFolder->m_pszPath, pDirEntry->d_name);
563 sLen = strlen(pDirEntry->d_name);
564 pid = (LPSHITEMID)SHAlloc(SHITEMID_LEN_FROM_NAME_LEN(sLen)+sizeof(USHORT));
566 WARN("SHAlloc failed!\n");
570 if (!UNIXFS_build_shitemid(pszFQPath, pid)) {
574 memset(((PBYTE)pid)+pid->cb, 0, sizeof(USHORT));
576 pUnixFolder->m_apidlSubDirs[i++] = (LPITEMIDLIST)pid;
579 pUnixFolder->m_cSubDirs = i;
586 /******************************************************************************
589 * Class whose heap based instances represent unix filesystem directories.
592 static void UnixFolder_Destroy(UnixFolder *pUnixFolder) {
595 TRACE("(pUnixFolder=%p)\n", pUnixFolder);
597 if (pUnixFolder->m_apidlSubDirs)
598 for (i=0; i < pUnixFolder->m_cSubDirs; i++)
599 SHFree(pUnixFolder->m_apidlSubDirs[i]);
600 SHFree(pUnixFolder->m_apidlSubDirs);
601 SHFree(pUnixFolder->m_pszPath);
602 ILFree(pUnixFolder->m_pidlLocation);
606 static HRESULT WINAPI UnixFolder_IShellFolder2_QueryInterface(IShellFolder2 *iface, REFIID riid,
609 UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
611 TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
613 if (!ppv) return E_INVALIDARG;
615 if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IShellFolder, riid) ||
616 IsEqualIID(&IID_IShellFolder2, riid))
618 *ppv = &This->lpIShellFolder2Vtbl;
619 } else if (IsEqualIID(&IID_IPersistFolder2, riid) || IsEqualIID(&IID_IPersistFolder, riid) ||
620 IsEqualIID(&IID_IPersist, riid))
622 *ppv = &This->lpIPersistFolder2Vtbl;
623 } else if (IsEqualIID(&IID_ISFHelper, riid)) {
624 *ppv = &This->lpISFHelperVtbl;
627 return E_NOINTERFACE;
630 IUnknown_AddRef((IUnknown*)*ppv);
634 static ULONG WINAPI UnixFolder_IShellFolder2_AddRef(IShellFolder2 *iface) {
635 UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
637 TRACE("(iface=%p)\n", iface);
639 return InterlockedIncrement(&This->m_cRef);
642 static ULONG WINAPI UnixFolder_IShellFolder2_Release(IShellFolder2 *iface) {
643 UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
646 TRACE("(iface=%p)\n", iface);
648 cRef = InterlockedDecrement(&This->m_cRef);
651 UnixFolder_Destroy(This);
656 static HRESULT WINAPI UnixFolder_IShellFolder2_ParseDisplayName(IShellFolder2* iface, HWND hwndOwner,
657 LPBC pbcReserved, LPOLESTR lpszDisplayName, ULONG* pchEaten, LPITEMIDLIST* ppidl,
658 ULONG* pdwAttributes)
660 UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
663 TRACE("(iface=%p, hwndOwner=%p, pbcReserved=%p, lpszDisplayName=%s, pchEaten=%p, ppidl=%p, "
664 "pdwAttributes=%p) stub\n", iface, hwndOwner, pbcReserved, debugstr_w(lpszDisplayName),
665 pchEaten, ppidl, pdwAttributes);
667 result = UNIXFS_path_to_pidl(This, lpszDisplayName, ppidl);
668 if (result && pdwAttributes && *pdwAttributes)
670 IShellFolder *pParentSF;
671 LPCITEMIDLIST pidlLast;
674 hr = SHBindToParent(This->m_pidlLocation, &IID_IShellFolder, (LPVOID*)&pParentSF, &pidlLast);
675 if (FAILED(hr)) return E_FAIL;
676 IShellFolder_GetAttributesOf(pParentSF, 1, &pidlLast, pdwAttributes);
677 IShellFolder_Release(pParentSF);
680 if (!result) TRACE("FAILED!\n");
681 return result ? S_OK : E_FAIL;
684 static IUnknown *UnixSubFolderIterator_Construct(UnixFolder *pUnixFolder, SHCONTF fFilter);
686 static HRESULT WINAPI UnixFolder_IShellFolder2_EnumObjects(IShellFolder2* iface, HWND hwndOwner,
687 SHCONTF grfFlags, IEnumIDList** ppEnumIDList)
689 UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
690 IUnknown *newIterator;
693 TRACE("(iface=%p, hwndOwner=%p, grfFlags=%08lx, ppEnumIDList=%p)\n",
694 iface, hwndOwner, grfFlags, ppEnumIDList);
696 if (This->m_cSubDirs == -1)
697 UNIXFS_build_subfolder_pidls(This);
699 newIterator = UnixSubFolderIterator_Construct(This, grfFlags);
700 hr = IUnknown_QueryInterface(newIterator, &IID_IEnumIDList, (void**)ppEnumIDList);
701 IUnknown_Release(newIterator);
706 static HRESULT WINAPI UnixFolder_IShellFolder2_BindToObject(IShellFolder2* iface, LPCITEMIDLIST pidl,
707 LPBC pbcReserved, REFIID riid, void** ppvOut)
709 UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
710 IPersistFolder2 *persistFolder;
711 LPITEMIDLIST pidlSubFolder;
714 TRACE("(iface=%p, pidl=%p, pbcReserver=%p, riid=%p, ppvOut=%p)\n",
715 iface, pidl, pbcReserved, riid, ppvOut);
717 if (!pidl || !pidl->mkid.cb)
720 if (This->m_dwPathMode == PATHMODE_DOS)
721 hr = UnixDosFolder_Constructor(NULL, &IID_IPersistFolder2, (void**)&persistFolder);
723 hr = UnixFolder_Constructor(NULL, &IID_IPersistFolder2, (void**)&persistFolder);
725 if (!SUCCEEDED(hr)) return hr;
726 hr = IPersistFolder_QueryInterface(persistFolder, riid, (void**)ppvOut);
728 pidlSubFolder = ILCombine(This->m_pidlLocation, pidl);
729 IPersistFolder2_Initialize(persistFolder, pidlSubFolder);
730 IPersistFolder2_Release(persistFolder);
731 ILFree(pidlSubFolder);
736 static HRESULT WINAPI UnixFolder_IShellFolder2_BindToStorage(IShellFolder2* This, LPCITEMIDLIST pidl,
737 LPBC pbcReserved, REFIID riid, void** ppvObj)
743 static HRESULT WINAPI UnixFolder_IShellFolder2_CompareIDs(IShellFolder2* iface, LPARAM lParam,
744 LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
746 BOOL isEmpty1, isEmpty2;
748 LPITEMIDLIST firstpidl;
752 TRACE("(iface=%p, lParam=%ld, pidl1=%p, pidl2=%p)\n", iface, lParam, pidl1, pidl2);
754 isEmpty1 = !pidl1 || !pidl1->mkid.cb;
755 isEmpty2 = !pidl2 || !pidl2->mkid.cb;
757 if (isEmpty1 && isEmpty2)
758 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
760 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
762 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
764 if (_ILIsFolder(pidl1) && !_ILIsFolder(pidl2))
765 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
766 if (!_ILIsFolder(pidl1) && _ILIsFolder(pidl2))
767 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
769 compare = CompareStringA(LOCALE_USER_DEFAULT, NORM_IGNORECASE,
770 _ILGetTextPointer(pidl1), NAME_LEN_FROM_LPSHITEMID(pidl1),
771 _ILGetTextPointer(pidl2), NAME_LEN_FROM_LPSHITEMID(pidl2));
773 if ((compare == CSTR_LESS_THAN) || (compare == CSTR_GREATER_THAN))
774 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)((compare == CSTR_LESS_THAN)?-1:1));
776 if (pidl1->mkid.cb < pidl2->mkid.cb)
777 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)-1);
778 else if (pidl1->mkid.cb > pidl2->mkid.cb)
779 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (WORD)1);
781 firstpidl = ILCloneFirst(pidl1);
782 pidl1 = ILGetNext(pidl1);
783 pidl2 = ILGetNext(pidl2);
785 hr = IShellFolder2_BindToObject(iface, firstpidl, NULL, &IID_IShellFolder, (LPVOID*)&psf);
787 hr = IShellFolder_CompareIDs(psf, lParam, pidl1, pidl2);
788 IShellFolder2_Release(psf);
795 static HRESULT WINAPI UnixFolder_IShellFolder2_CreateViewObject(IShellFolder2* iface, HWND hwndOwner,
796 REFIID riid, void** ppv)
798 HRESULT hr = E_INVALIDARG;
800 TRACE("(iface=%p, hwndOwner=%p, riid=%p, ppv=%p) stub\n", iface, hwndOwner, riid, ppv);
802 if (!ppv) return E_INVALIDARG;
805 if (IsEqualIID(&IID_IShellView, riid)) {
806 LPSHELLVIEW pShellView;
808 pShellView = IShellView_Constructor((IShellFolder*)iface);
810 hr = IShellView_QueryInterface(pShellView, riid, ppv);
811 IShellView_Release(pShellView);
818 static HRESULT WINAPI UnixFolder_IShellFolder2_GetAttributesOf(IShellFolder2* iface, UINT cidl,
819 LPCITEMIDLIST* apidl, SFGAOF* rgfInOut)
821 UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
824 TRACE("(iface=%p, cidl=%u, apidl=%p, rgfInOut=%p)\n", iface, cidl, apidl, rgfInOut);
826 if (!rgfInOut || (cidl && !apidl))
830 *rgfInOut &= This->m_dwAttributes;
832 char szAbsolutePath[FILENAME_MAX], *pszRelativePath;
835 *rgfInOut &= SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|SFGAO_CANRENAME|
837 lstrcpyA(szAbsolutePath, This->m_pszPath);
838 pszRelativePath = szAbsolutePath + lstrlenA(szAbsolutePath);
839 for (i=0; i<cidl; i++) {
840 if ((*rgfInOut & SFGAO_FILESYSTEM) && !(This->m_dwAttributes & SFGAO_FILESYSTEM)) {
841 struct stat fileStat;
842 char *pszName = _ILGetTextPointer(apidl[i]);
843 if (!pszName) return E_INVALIDARG;
844 lstrcpyA(pszRelativePath, pszName);
845 if (stat(szAbsolutePath, &fileStat) || !UNIXFS_is_dos_device(&fileStat))
846 *rgfInOut &= ~SFGAO_FILESYSTEM;
848 if (!_ILIsFolder(apidl[i]))
849 *rgfInOut &= ~(SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR);
856 static HRESULT WINAPI UnixFolder_IShellFolder2_GetUIObjectOf(IShellFolder2* iface, HWND hwndOwner,
857 UINT cidl, LPCITEMIDLIST* apidl, REFIID riid, UINT* prgfInOut, void** ppvOut)
859 UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
861 TRACE("(iface=%p, hwndOwner=%p, cidl=%d, apidl=%p, riid=%s, prgfInOut=%p, ppv=%p)\n",
862 iface, hwndOwner, cidl, apidl, debugstr_guid(riid), prgfInOut, ppvOut);
864 if (IsEqualIID(&IID_IContextMenu, riid)) {
865 *ppvOut = ISvItemCm_Constructor((IShellFolder*)iface, This->m_pidlLocation, apidl, cidl);
867 } else if (IsEqualIID(&IID_IDataObject, riid)) {
868 *ppvOut = IDataObject_Constructor(hwndOwner, This->m_pidlLocation, apidl, cidl);
870 } else if (IsEqualIID(&IID_IExtractIconA, riid)) {
872 if (cidl != 1) return E_FAIL;
873 pidl = ILCombine(This->m_pidlLocation, apidl[0]);
874 *ppvOut = (LPVOID)IExtractIconA_Constructor(pidl);
877 } else if (IsEqualIID(&IID_IExtractIconW, riid)) {
879 if (cidl != 1) return E_FAIL;
880 pidl = ILCombine(This->m_pidlLocation, apidl[0]);
881 *ppvOut = (LPVOID)IExtractIconW_Constructor(pidl);
884 } else if (IsEqualIID(&IID_IDropTarget, riid)) {
885 FIXME("IDropTarget\n");
887 } else if (IsEqualIID(&IID_IShellLinkW, riid)) {
888 FIXME("IShellLinkW\n");
890 } else if (IsEqualIID(&IID_IShellLinkA, riid)) {
891 FIXME("IShellLinkA\n");
894 FIXME("Unknown interface %s in GetUIObjectOf\n", debugstr_guid(riid));
895 return E_NOINTERFACE;
899 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDisplayNameOf(IShellFolder2* iface,
900 LPCITEMIDLIST pidl, SHGDNF uFlags, STRRET* lpName)
902 UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
905 TRACE("(iface=%p, pidl=%p, uFlags=%lx, lpName=%p)\n", iface, pidl, uFlags, lpName);
907 if ((GET_SHGDN_FOR(uFlags) & SHGDN_FORPARSING) &&
908 (GET_SHGDN_RELATION(uFlags) != SHGDN_INFOLDER))
910 if (!pidl->mkid.cb) {
911 lpName->uType = STRRET_CSTR;
912 strcpy(lpName->u.cStr, This->m_pszPath);
913 if (This->m_dwPathMode == PATHMODE_DOS) {
915 GetFullPathNameA(lpName->u.cStr, MAX_PATH, path, NULL);
916 PathRemoveBackslashA(path);
917 strcpy(lpName->u.cStr, path);
920 IShellFolder *pSubFolder;
923 hr = IShellFolder_BindToObject(iface, pidl, NULL, &IID_IShellFolder, (void**)&pSubFolder);
924 if (!SUCCEEDED(hr)) return hr;
926 hr = IShellFolder_GetDisplayNameOf(pSubFolder, (LPITEMIDLIST)&emptyIDL, uFlags, lpName);
927 IShellFolder_Release(pSubFolder);
930 char *pszFileName = _ILGetTextPointer(pidl);
931 lpName->uType = STRRET_CSTR;
932 strcpy(lpName->u.cStr, pszFileName ? pszFileName : "");
935 TRACE("--> %s\n", lpName->u.cStr);
940 static HRESULT WINAPI UnixFolder_IShellFolder2_SetNameOf(IShellFolder2* iface, HWND hwnd,
941 LPCITEMIDLIST pidl, LPCOLESTR lpszName, SHGDNF uFlags, LPITEMIDLIST* ppidlOut)
943 UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
945 char szSrc[FILENAME_MAX], szDest[FILENAME_MAX];
946 int cBasePathLen = lstrlenA(This->m_pszPath);
947 struct stat statDest;
948 LPITEMIDLIST pidlNew;
950 TRACE("(iface=%p, hwnd=%p, pidl=%p, lpszName=%s, uFlags=0x%08lx, ppidlOut=%p)\n",
951 iface, hwnd, pidl, debugstr_w(lpszName), uFlags, ppidlOut);
953 /* pidl has to contain a single non-empty SHITEMID */
954 if (_ILIsDesktop(pidl) || !_ILIsPidlSimple(pidl) || !_ILGetTextPointer(pidl))
960 /* build source path */
961 memcpy(szSrc, This->m_pszPath, cBasePathLen);
962 lstrcpyA(szSrc+cBasePathLen, _ILGetTextPointer(pidl));
964 /* build destination path */
965 if (uFlags & SHGDN_FORPARSING) { /* absolute path in lpszName */
966 WideCharToMultiByte(CP_ACP, 0, lpszName, -1, szDest, FILENAME_MAX, NULL, NULL);
968 memcpy(szDest, This->m_pszPath, cBasePathLen);
969 WideCharToMultiByte(CP_ACP, 0, lpszName, -1, szDest+cBasePathLen,
970 FILENAME_MAX-cBasePathLen, NULL, NULL);
973 TRACE("src=%s dest=%s\n", szSrc, szDest);
975 /* Fail, if destination does already exist */
976 if (!stat(szDest, &statDest))
979 /* Rename the file and inform the shell */
980 if (!rename(szSrc, szDest) && UNIXFS_path_to_pidl(This, lpszName, &pidlNew)) {
981 LPITEMIDLIST pidlSrc = ILCombine(This->m_pidlLocation, pidl);
982 LPITEMIDLIST pidlDest = ILCombine(This->m_pidlLocation, pidlNew);
983 UNIXFS_build_subfolder_pidls(This); /* Update list of children */
984 if (_ILIsFolder(pidlNew))
985 SHChangeNotify(SHCNE_RENAMEFOLDER, SHCNF_IDLIST, pidlSrc, pidlDest);
987 SHChangeNotify(SHCNE_RENAMEITEM, SHCNF_IDLIST, pidlSrc, pidlDest);
999 static HRESULT WINAPI UnixFolder_IShellFolder2_EnumSearches(IShellFolder2* iface,
1000 IEnumExtraSearch **ppEnum)
1006 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumn(IShellFolder2* iface,
1007 DWORD dwReserved, ULONG *pSort, ULONG *pDisplay)
1013 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultColumnState(IShellFolder2* iface,
1014 UINT iColumn, SHCOLSTATEF *pcsFlags)
1020 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDefaultSearchGUID(IShellFolder2* iface,
1027 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsEx(IShellFolder2* iface,
1028 LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
1034 #define SHELLVIEWCOLUMNS 6
1036 static HRESULT WINAPI UnixFolder_IShellFolder2_GetDetailsOf(IShellFolder2* iface,
1037 LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *psd)
1039 UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface);
1040 HRESULT hr = E_FAIL;
1041 struct passwd *pPasswd;
1042 struct group *pGroup;
1043 static const shvheader SFHeader[SHELLVIEWCOLUMNS] = {
1044 {IDS_SHV_COLUMN1, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 15},
1045 {IDS_SHV_COLUMN5, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
1046 {IDS_SHV_COLUMN10, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7},
1047 {IDS_SHV_COLUMN11, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 7},
1048 {IDS_SHV_COLUMN2, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 8},
1049 {IDS_SHV_COLUMN4, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10}
1052 TRACE("(iface=%p, pidl=%p, iColumn=%d, psd=%p) stub\n", iface, pidl, iColumn, psd);
1054 if (!psd || iColumn >= SHELLVIEWCOLUMNS)
1055 return E_INVALIDARG;
1058 psd->fmt = SFHeader[iColumn].fmt;
1059 psd->cxChar = SFHeader[iColumn].cxChar;
1060 psd->str.uType = STRRET_CSTR;
1061 LoadStringA(shell32_hInstance, SFHeader[iColumn].colnameid, psd->str.u.cStr, MAX_PATH);
1064 struct stat statItem;
1065 if (iColumn == 1 || iColumn == 2 || iColumn == 3) {
1066 char szPath[FILENAME_MAX], *pszFile = _ILGetTextPointer(pidl);
1068 return E_INVALIDARG;
1069 lstrcpyA(szPath, This->m_pszPath);
1070 lstrcatA(szPath, pszFile);
1071 if (stat(szPath, &statItem))
1072 return E_INVALIDARG;
1074 psd->str.u.cStr[0] = '\0';
1075 psd->str.uType = STRRET_CSTR;
1078 hr = IShellFolder2_GetDisplayNameOf(iface, pidl, SHGDN_NORMAL|SHGDN_INFOLDER, &psd->str);
1081 psd->str.u.cStr[0] = S_ISDIR(statItem.st_mode) ? 'd' : '-';
1082 psd->str.u.cStr[1] = (statItem.st_mode & S_IRUSR) ? 'r' : '-';
1083 psd->str.u.cStr[2] = (statItem.st_mode & S_IWUSR) ? 'w' : '-';
1084 psd->str.u.cStr[3] = (statItem.st_mode & S_IXUSR) ? 'x' : '-';
1085 psd->str.u.cStr[4] = (statItem.st_mode & S_IRGRP) ? 'r' : '-';
1086 psd->str.u.cStr[5] = (statItem.st_mode & S_IWGRP) ? 'w' : '-';
1087 psd->str.u.cStr[6] = (statItem.st_mode & S_IXGRP) ? 'x' : '-';
1088 psd->str.u.cStr[7] = (statItem.st_mode & S_IROTH) ? 'r' : '-';
1089 psd->str.u.cStr[8] = (statItem.st_mode & S_IWOTH) ? 'w' : '-';
1090 psd->str.u.cStr[9] = (statItem.st_mode & S_IXOTH) ? 'x' : '-';
1091 psd->str.u.cStr[10] = '\0';
1094 pPasswd = getpwuid(statItem.st_uid);
1095 if (pPasswd) strcpy(psd->str.u.cStr, pPasswd->pw_name);
1098 pGroup = getgrgid(statItem.st_gid);
1099 if (pGroup) strcpy(psd->str.u.cStr, pGroup->gr_name);
1102 _ILGetFileSize(pidl, psd->str.u.cStr, MAX_PATH);
1105 _ILGetFileDate(pidl, psd->str.u.cStr, MAX_PATH);
1113 static HRESULT WINAPI UnixFolder_IShellFolder2_MapColumnToSCID(IShellFolder2* iface, UINT iColumn,
1120 /* VTable for UnixFolder's IShellFolder2 interface.
1122 static const IShellFolder2Vtbl UnixFolder_IShellFolder2_Vtbl = {
1123 UnixFolder_IShellFolder2_QueryInterface,
1124 UnixFolder_IShellFolder2_AddRef,
1125 UnixFolder_IShellFolder2_Release,
1126 UnixFolder_IShellFolder2_ParseDisplayName,
1127 UnixFolder_IShellFolder2_EnumObjects,
1128 UnixFolder_IShellFolder2_BindToObject,
1129 UnixFolder_IShellFolder2_BindToStorage,
1130 UnixFolder_IShellFolder2_CompareIDs,
1131 UnixFolder_IShellFolder2_CreateViewObject,
1132 UnixFolder_IShellFolder2_GetAttributesOf,
1133 UnixFolder_IShellFolder2_GetUIObjectOf,
1134 UnixFolder_IShellFolder2_GetDisplayNameOf,
1135 UnixFolder_IShellFolder2_SetNameOf,
1136 UnixFolder_IShellFolder2_GetDefaultSearchGUID,
1137 UnixFolder_IShellFolder2_EnumSearches,
1138 UnixFolder_IShellFolder2_GetDefaultColumn,
1139 UnixFolder_IShellFolder2_GetDefaultColumnState,
1140 UnixFolder_IShellFolder2_GetDetailsEx,
1141 UnixFolder_IShellFolder2_GetDetailsOf,
1142 UnixFolder_IShellFolder2_MapColumnToSCID
1145 static HRESULT WINAPI UnixFolder_IPersistFolder2_QueryInterface(IPersistFolder2* This, REFIID riid,
1148 return UnixFolder_IShellFolder2_QueryInterface(
1149 (IShellFolder2*)ADJUST_THIS(UnixFolder, IPersistFolder2, This), riid, ppvObject);
1152 static ULONG WINAPI UnixFolder_IPersistFolder2_AddRef(IPersistFolder2* This)
1154 return UnixFolder_IShellFolder2_AddRef(
1155 (IShellFolder2*)ADJUST_THIS(UnixFolder, IPersistFolder2, This));
1158 static ULONG WINAPI UnixFolder_IPersistFolder2_Release(IPersistFolder2* This)
1160 return UnixFolder_IShellFolder2_Release(
1161 (IShellFolder2*)ADJUST_THIS(UnixFolder, IPersistFolder2, This));
1164 static HRESULT WINAPI UnixFolder_IPersistFolder2_GetClassID(IPersistFolder2* This, CLSID* pClassID)
1170 static HRESULT WINAPI UnixFolder_IPersistFolder2_Initialize(IPersistFolder2* iface, LPCITEMIDLIST pidl)
1172 UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder2, iface);
1173 struct stat statPrefix;
1174 LPCITEMIDLIST current = pidl, root;
1176 char *pNextDir, szBasePath[FILENAME_MAX] = "/";
1178 TRACE("(iface=%p, pidl=%p)\n", iface, pidl);
1180 /* Find the UnixFolderClass root */
1181 while (current->mkid.cb) {
1182 if (_ILIsSpecialFolder(current) &&
1183 (IsEqualIID(&CLSID_UnixFolder, _ILGetGUIDPointer(current)) ||
1184 IsEqualIID(&CLSID_UnixDosFolder, _ILGetGUIDPointer(current))))
1188 current = ILGetNext(current);
1191 if (current && current->mkid.cb) {
1192 root = current = ILGetNext(current);
1193 dwPathLen = 2; /* For the '/' prefix and the terminating '\0' */
1194 } else if (_ILIsDesktop(pidl) || _ILIsValue(pidl) || _ILIsFolder(pidl)) {
1195 /* Path rooted at Desktop */
1196 WCHAR wszDesktopPath[MAX_PATH];
1197 if (FAILED(SHGetSpecialFolderPathW(0, wszDesktopPath, CSIDL_DESKTOP, FALSE)))
1199 PathAddBackslashW(wszDesktopPath);
1200 if (!UNIXFS_get_unix_path(wszDesktopPath, szBasePath))
1202 dwPathLen = strlen(szBasePath) + 1;
1203 root = current = pidl;
1205 ERR("Unknown pidl type!\n");
1207 return E_INVALIDARG;
1210 /* Determine the path's length bytes */
1211 while (current && current->mkid.cb) {
1212 dwPathLen += NAME_LEN_FROM_LPSHITEMID(current) + 1; /* For the '/' */
1213 current = ILGetNext(current);
1216 /* Build the path */
1217 This->m_dwAttributes = SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|SFGAO_CANRENAME;
1218 This->m_pidlLocation = ILClone(pidl);
1219 This->m_pszPath = pNextDir = SHAlloc(dwPathLen);
1220 if (!This->m_pszPath || !This->m_pidlLocation) {
1221 WARN("SHAlloc failed!\n");
1225 strcpy(pNextDir, szBasePath);
1226 pNextDir += strlen(szBasePath);
1227 if (!(This->m_dwAttributes & SFGAO_FILESYSTEM)) {
1229 if (!stat(This->m_pszPath, &statPrefix) && UNIXFS_is_dos_device(&statPrefix))
1230 This->m_dwAttributes |= SFGAO_FILESYSTEM;
1232 while (current && current->mkid.cb) {
1233 memcpy(pNextDir, _ILGetTextPointer(current), NAME_LEN_FROM_LPSHITEMID(current));
1234 pNextDir += NAME_LEN_FROM_LPSHITEMID(current);
1235 if (!(This->m_dwAttributes & SFGAO_FILESYSTEM)) {
1237 if (!stat(This->m_pszPath, &statPrefix) && UNIXFS_is_dos_device(&statPrefix))
1238 This->m_dwAttributes |= SFGAO_FILESYSTEM;
1241 current = ILGetNext(current);
1248 static HRESULT WINAPI UnixFolder_IPersistFolder2_GetCurFolder(IPersistFolder2* iface, LPITEMIDLIST* ppidl)
1250 UnixFolder *This = ADJUST_THIS(UnixFolder, IPersistFolder2, iface);
1252 TRACE ("(iface=%p, ppidl=%p)\n", iface, ppidl);
1256 *ppidl = ILClone (This->m_pidlLocation);
1260 /* VTable for UnixFolder's IPersistFolder interface.
1262 static const IPersistFolder2Vtbl UnixFolder_IPersistFolder2_Vtbl = {
1263 UnixFolder_IPersistFolder2_QueryInterface,
1264 UnixFolder_IPersistFolder2_AddRef,
1265 UnixFolder_IPersistFolder2_Release,
1266 UnixFolder_IPersistFolder2_GetClassID,
1267 UnixFolder_IPersistFolder2_Initialize,
1268 UnixFolder_IPersistFolder2_GetCurFolder
1271 static HRESULT WINAPI UnixFolder_ISFHelper_QueryInterface(ISFHelper* iface, REFIID riid,
1274 return UnixFolder_IShellFolder2_QueryInterface(
1275 (IShellFolder2*)ADJUST_THIS(UnixFolder, ISFHelper, iface), riid, ppvObject);
1278 static ULONG WINAPI UnixFolder_ISFHelper_AddRef(ISFHelper* iface)
1280 return UnixFolder_IShellFolder2_AddRef(
1281 (IShellFolder2*)ADJUST_THIS(UnixFolder, ISFHelper, iface));
1284 static ULONG WINAPI UnixFolder_ISFHelper_Release(ISFHelper* iface)
1286 return UnixFolder_IShellFolder2_Release(
1287 (IShellFolder2*)ADJUST_THIS(UnixFolder, ISFHelper, iface));
1290 static HRESULT WINAPI UnixFolder_ISFHelper_GetUniqueName(ISFHelper* iface, LPSTR lpName, UINT uLen)
1292 UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
1295 LPITEMIDLIST pidlElem;
1298 static const char szNewFolder[] = "New Folder";
1300 TRACE("(iface=%p, lpName=%p, uLen=%u)\n", iface, lpName, uLen);
1302 if (uLen < sizeof(szNewFolder)+3)
1303 return E_INVALIDARG;
1305 hr = IShellFolder2_EnumObjects(STATIC_CAST(IShellFolder2, This), 0,
1306 SHCONTF_FOLDERS|SHCONTF_NONFOLDERS|SHCONTF_INCLUDEHIDDEN, &pEnum);
1307 if (SUCCEEDED(hr)) {
1308 lstrcpyA(lpName, szNewFolder);
1309 IEnumIDList_Reset(pEnum);
1311 while ((IEnumIDList_Next(pEnum, 1, &pidlElem, &dwFetched) == S_OK) && (dwFetched == 1)) {
1312 if (!strcasecmp(_ILGetTextPointer(pidlElem), lpName)) {
1313 IEnumIDList_Reset(pEnum);
1314 sprintf(lpName, "%s %d", szNewFolder, i++);
1321 IEnumIDList_Release(pEnum);
1326 static HRESULT WINAPI UnixFolder_ISFHelper_AddFolder(ISFHelper* iface, HWND hwnd, LPCSTR pszName,
1327 LPITEMIDLIST* ppidlOut)
1329 UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
1330 char szNewDir[FILENAME_MAX];
1332 TRACE("(iface=%p, hwnd=%p, pszName=%s, ppidlOut=%p)\n", iface, hwnd, pszName, ppidlOut);
1337 lstrcpyA(szNewDir, This->m_pszPath);
1338 lstrcatA(szNewDir, pszName);
1340 if (mkdir(szNewDir, 0755)) {
1341 char szMessage[256 + FILENAME_MAX];
1342 char szCaption[256];
1344 LoadStringA(shell32_hInstance, IDS_CREATEFOLDER_DENIED, szCaption, sizeof(szCaption));
1345 sprintf(szMessage, szCaption, szNewDir);
1346 LoadStringA(shell32_hInstance, IDS_CREATEFOLDER_CAPTION, szCaption, sizeof(szCaption));
1347 MessageBoxA(hwnd, szMessage, szCaption, MB_OK | MB_ICONEXCLAMATION);
1351 LPITEMIDLIST pidlRelative;
1352 WCHAR wszName[MAX_PATH];
1354 /* Update the folder's children */
1355 UNIXFS_build_subfolder_pidls(This);
1357 /* Inform the shell */
1358 MultiByteToWideChar(CP_ACP, 0, pszName, -1, wszName, MAX_PATH);
1359 if (UNIXFS_path_to_pidl(This, wszName, &pidlRelative)) {
1360 LPITEMIDLIST pidlAbsolute = ILCombine(This->m_pidlLocation, pidlRelative);
1362 *ppidlOut = pidlRelative;
1364 ILFree(pidlRelative);
1365 SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST, pidlAbsolute, NULL);
1366 ILFree(pidlAbsolute);
1372 static HRESULT WINAPI UnixFolder_ISFHelper_DeleteItems(ISFHelper* iface, UINT cidl,
1373 LPCITEMIDLIST* apidl)
1375 UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
1376 char szAbsolute[FILENAME_MAX], *pszRelative;
1377 LPITEMIDLIST pidlAbsolute;
1381 TRACE("(iface=%p, cidl=%d, apidl=%p)\n", iface, cidl, apidl);
1383 lstrcpyA(szAbsolute, This->m_pszPath);
1384 pszRelative = szAbsolute + lstrlenA(szAbsolute);
1386 for (i=0; i<cidl && SUCCEEDED(hr); i++) {
1387 lstrcpyA(pszRelative, _ILGetTextPointer(apidl[i]));
1388 pidlAbsolute = ILCombine(This->m_pidlLocation, apidl[i]);
1389 if (_ILIsFolder(apidl[i])) {
1390 if (rmdir(szAbsolute)) {
1393 SHChangeNotify(SHCNE_RMDIR, SHCNF_IDLIST, pidlAbsolute, NULL);
1395 } else if (_ILIsValue(apidl[i])) {
1396 if (unlink(szAbsolute)) {
1399 SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlAbsolute, NULL);
1402 ILFree(pidlAbsolute);
1404 UNIXFS_build_subfolder_pidls(This);
1409 static HRESULT WINAPI UnixFolder_ISFHelper_CopyItems(ISFHelper* iface, IShellFolder *psfFrom,
1410 UINT cidl, LPCITEMIDLIST *apidl)
1412 UnixFolder *This = ADJUST_THIS(UnixFolder, ISFHelper, iface);
1416 char szAbsoluteDst[FILENAME_MAX], *pszRelativeDst;
1418 TRACE("(iface=%p, psfFrom=%p, cidl=%d, apidl=%p): semi-stub\n", iface, psfFrom, cidl, apidl);
1420 if (!psfFrom || !cidl || !apidl)
1421 return E_INVALIDARG;
1423 /* All source items have to be filesystem items. */
1424 dwAttributes = SFGAO_FILESYSTEM;
1425 hr = IShellFolder_GetAttributesOf(psfFrom, cidl, apidl, &dwAttributes);
1426 if (FAILED(hr) || !(dwAttributes & SFGAO_FILESYSTEM))
1427 return E_INVALIDARG;
1429 lstrcpyA(szAbsoluteDst, This->m_pszPath);
1430 pszRelativeDst = szAbsoluteDst + strlen(szAbsoluteDst);
1432 for (i=0; i<cidl; i++) {
1433 WCHAR wszSrc[MAX_PATH];
1434 char szSrc[FILENAME_MAX];
1437 /* Build the unix path of the current source item. */
1438 if (FAILED(IShellFolder_GetDisplayNameOf(psfFrom, apidl[i], SHGDN_FORPARSING, &strret)))
1440 if (FAILED(StrRetToBufW(&strret, apidl[i], wszSrc, MAX_PATH)))
1442 if (!UNIXFS_get_unix_path(wszSrc, szSrc))
1445 /* Build the unix path of the current destination item */
1446 lstrcpyA(pszRelativeDst, _ILGetTextPointer(apidl[i]));
1448 FIXME("Would copy %s to %s. Not yet implemented.\n", szSrc, szAbsoluteDst);
1453 /* VTable for UnixFolder's ISFHelper interface
1455 static const ISFHelperVtbl UnixFolder_ISFHelper_Vtbl = {
1456 UnixFolder_ISFHelper_QueryInterface,
1457 UnixFolder_ISFHelper_AddRef,
1458 UnixFolder_ISFHelper_Release,
1459 UnixFolder_ISFHelper_GetUniqueName,
1460 UnixFolder_ISFHelper_AddFolder,
1461 UnixFolder_ISFHelper_DeleteItems,
1462 UnixFolder_ISFHelper_CopyItems
1465 /******************************************************************************
1466 * Unix[Dos]Folder_Constructor [Internal]
1469 * pUnkOuter [I] Outer class for aggregation. Currently ignored.
1470 * riid [I] Interface asked for by the client.
1471 * ppv [O] Pointer to an riid interface to the UnixFolder object.
1474 * Those are the only functions exported from shfldr_unixfs.c. They are called from
1475 * shellole.c's default class factory and thus have to exhibit a LPFNCREATEINSTANCE
1476 * compatible signature.
1478 * The UnixDosFolder_Constructor sets the dwPathMode member to PATHMODE_DOS. This
1479 * means that paths are converted from dos to unix and back at the interfaces.
1481 static HRESULT CreateUnixFolder(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv, DWORD dwPathMode) {
1482 HRESULT hr = E_FAIL;
1483 UnixFolder *pUnixFolder = SHAlloc((ULONG)sizeof(UnixFolder));
1486 pUnixFolder->lpIShellFolder2Vtbl = &UnixFolder_IShellFolder2_Vtbl;
1487 pUnixFolder->lpIPersistFolder2Vtbl = &UnixFolder_IPersistFolder2_Vtbl;
1488 pUnixFolder->lpISFHelperVtbl = &UnixFolder_ISFHelper_Vtbl;
1489 pUnixFolder->m_cRef = 0;
1490 pUnixFolder->m_pszPath = NULL;
1491 pUnixFolder->m_apidlSubDirs = NULL;
1492 pUnixFolder->m_cSubDirs = -1;
1493 pUnixFolder->m_dwPathMode = dwPathMode;
1495 UnixFolder_IShellFolder2_AddRef(STATIC_CAST(IShellFolder2, pUnixFolder));
1496 hr = UnixFolder_IShellFolder2_QueryInterface(STATIC_CAST(IShellFolder2, pUnixFolder), riid, ppv);
1497 UnixFolder_IShellFolder2_Release(STATIC_CAST(IShellFolder2, pUnixFolder));
1502 HRESULT WINAPI UnixFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
1503 TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
1504 return CreateUnixFolder(pUnkOuter, riid, ppv, PATHMODE_UNIX);
1507 HRESULT WINAPI UnixDosFolder_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID *ppv) {
1508 TRACE("(pUnkOuter=%p, riid=%p, ppv=%p)\n", pUnkOuter, riid, ppv);
1509 return CreateUnixFolder(pUnkOuter, riid, ppv, PATHMODE_DOS);
1512 /******************************************************************************
1513 * UnixSubFolderIterator
1515 * Class whose heap based objects represent iterators over the sub-directories
1516 * of a given UnixFolder object.
1519 /* UnixSubFolderIterator object layout and typedef.
1521 typedef struct _UnixSubFolderIterator {
1522 const IEnumIDListVtbl *lpIEnumIDListVtbl;
1524 UnixFolder *m_pUnixFolder;
1527 } UnixSubFolderIterator;
1529 static void UnixSubFolderIterator_Destroy(UnixSubFolderIterator *iterator) {
1530 TRACE("(iterator=%p)\n", iterator);
1532 UnixFolder_IShellFolder2_Release((IShellFolder2*)iterator->m_pUnixFolder);
1536 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_QueryInterface(IEnumIDList* iface,
1537 REFIID riid, void** ppv)
1539 TRACE("(iface=%p, riid=%p, ppv=%p)\n", iface, riid, ppv);
1541 if (!ppv) return E_INVALIDARG;
1543 if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IEnumIDList, riid)) {
1547 return E_NOINTERFACE;
1550 IEnumIDList_AddRef(iface);
1554 static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_AddRef(IEnumIDList* iface)
1556 UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1558 TRACE("(iface=%p)\n", iface);
1560 return InterlockedIncrement(&This->m_cRef);
1563 static ULONG WINAPI UnixSubFolderIterator_IEnumIDList_Release(IEnumIDList* iface)
1565 UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1568 TRACE("(iface=%p)\n", iface);
1570 cRef = InterlockedDecrement(&This->m_cRef);
1573 UnixSubFolderIterator_Destroy(This);
1578 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Next(IEnumIDList* iface, ULONG celt,
1579 LPITEMIDLIST* rgelt, ULONG* pceltFetched)
1581 UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1584 TRACE("(iface=%p, celt=%ld, rgelt=%p, pceltFetched=%p)\n", iface, celt, rgelt, pceltFetched);
1586 for (i=0; (i < celt) && (This->m_cIdx < This->m_pUnixFolder->m_cSubDirs); This->m_cIdx++) {
1587 LPITEMIDLIST pCurrent = This->m_pUnixFolder->m_apidlSubDirs[This->m_cIdx];
1588 if (UNIXFS_is_pidl_of_type(pCurrent, This->m_fFilter)) {
1589 rgelt[i] = ILClone(pCurrent);
1597 return (i == 0) ? S_FALSE : S_OK;
1600 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Skip(IEnumIDList* iface, ULONG celt)
1602 UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1605 TRACE("(iface=%p, celt=%ld)\n", iface, celt);
1607 for (i=0; i < celt; i++) {
1608 while (This->m_cIdx < This->m_pUnixFolder->m_cSubDirs &&
1609 !UNIXFS_is_pidl_of_type(This->m_pUnixFolder->m_apidlSubDirs[This->m_cIdx], This->m_fFilter))
1616 if (This->m_cIdx > This->m_pUnixFolder->m_cSubDirs) {
1617 This->m_cIdx = This->m_pUnixFolder->m_cSubDirs;
1624 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Reset(IEnumIDList* iface)
1626 UnixSubFolderIterator *This = ADJUST_THIS(UnixSubFolderIterator, IEnumIDList, iface);
1628 TRACE("(iface=%p)\n", iface);
1635 static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Clone(IEnumIDList* This,
1636 IEnumIDList** ppenum)
1642 /* VTable for UnixSubFolderIterator's IEnumIDList interface.
1644 static const IEnumIDListVtbl UnixSubFolderIterator_IEnumIDList_Vtbl = {
1645 UnixSubFolderIterator_IEnumIDList_QueryInterface,
1646 UnixSubFolderIterator_IEnumIDList_AddRef,
1647 UnixSubFolderIterator_IEnumIDList_Release,
1648 UnixSubFolderIterator_IEnumIDList_Next,
1649 UnixSubFolderIterator_IEnumIDList_Skip,
1650 UnixSubFolderIterator_IEnumIDList_Reset,
1651 UnixSubFolderIterator_IEnumIDList_Clone
1654 static IUnknown *UnixSubFolderIterator_Construct(UnixFolder *pUnixFolder, SHCONTF fFilter) {
1655 UnixSubFolderIterator *iterator;
1657 TRACE("(pUnixFolder=%p)\n", pUnixFolder);
1659 iterator = SHAlloc((ULONG)sizeof(UnixSubFolderIterator));
1660 iterator->lpIEnumIDListVtbl = &UnixSubFolderIterator_IEnumIDList_Vtbl;
1661 iterator->m_cRef = 0;
1662 iterator->m_cIdx = 0;
1663 iterator->m_pUnixFolder = pUnixFolder;
1664 iterator->m_fFilter = fFilter;
1666 UnixSubFolderIterator_IEnumIDList_AddRef((IEnumIDList*)iterator);
1667 UnixFolder_IShellFolder2_AddRef((IShellFolder2*)pUnixFolder);
1669 return (IUnknown*)iterator;