/* * Winefile * * Copyright 2000, 2003, 2004, 2005, 2008 Martin Fuchs * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "winefile.h" /* allocate and initialise a directory entry */ Entry* alloc_entry(void) { Entry* entry = (Entry*) heap_alloc(sizeof(Entry)); #ifdef _SHELL_FOLDERS entry->pidl = NULL; entry->folder = NULL; entry->hicon = 0; #endif return entry; } /* free a directory entry */ static void free_entry(Entry* entry) { #ifdef _SHELL_FOLDERS if (entry->hicon && entry->hicon!=(HICON)-1) DestroyIcon(entry->hicon); if (entry->folder && entry->folder!=Globals.iDesktop) IShellFolder_Release(entry->folder); if (entry->pidl) IMalloc_Free(Globals.iMalloc, entry->pidl); #endif heap_free(entry); } /* recursively free all child entries */ void free_entries(Entry* dir) { Entry *entry, *next=dir->down; if (next) { dir->down = 0; do { entry = next; next = entry->next; free_entries(entry); free_entry(entry); } while(next); } } /* sort order for different directory/file types */ enum TYPE_ORDER { TO_DIR = 0, TO_DOT = 1, TO_DOTDOT = 2, TO_OTHER_DIR = 3, TO_FILE = 4 }; /* distinguish between ".", ".." and any other directory names */ static int TypeOrderFromDirname(LPCTSTR name) { if (name[0] == '.') { if (name[1] == '\0') return TO_DOT; /* "." */ if (name[1]=='.' && name[2]=='\0') return TO_DOTDOT; /* ".." */ } return TO_OTHER_DIR; /* anything else */ } /* directories first... */ static int compareType(const WIN32_FIND_DATA* fd1, const WIN32_FIND_DATA* fd2) { int order1 = fd1->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE; int order2 = fd2->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY? TO_DIR: TO_FILE; /* Handle "." and ".." as special case and move them at the very first beginning. */ if (order1==TO_DIR && order2==TO_DIR) { order1 = TypeOrderFromDirname(fd1->cFileName); order2 = TypeOrderFromDirname(fd2->cFileName); } return order2==order1? 0: order1data; const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data; int cmp = compareType(fd1, fd2); if (cmp) return cmp; return lstrcmpi(fd1->cFileName, fd2->cFileName); } /* compare two entries by extension string */ static int compareExt(const void* arg1, const void* arg2) { const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data; const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data; const TCHAR *name1, *name2, *ext1, *ext2; int cmp = compareType(fd1, fd2); if (cmp) return cmp; name1 = fd1->cFileName; name2 = fd2->cFileName; ext1 = _tcsrchr(name1, '.'); ext2 = _tcsrchr(name2, '.'); if (ext1) ext1++; else ext1 = sEmpty; if (ext2) ext2++; else ext2 = sEmpty; cmp = lstrcmpi(ext1, ext2); if (cmp) return cmp; return lstrcmpi(name1, name2); } /* compare two entries by size */ static int compareSize(const void* arg1, const void* arg2) { const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data; const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data; int cmp = compareType(fd1, fd2); if (cmp) return cmp; cmp = fd2->nFileSizeHigh - fd1->nFileSizeHigh; if (cmp < 0) return -1; else if (cmp > 0) return 1; cmp = fd2->nFileSizeLow - fd1->nFileSizeLow; return cmp<0? -1: cmp>0? 1: 0; } /* compare two entries by date */ static int compareDate(const void* arg1, const void* arg2) { const WIN32_FIND_DATA* fd1 = &(*(const Entry* const*)arg1)->data; const WIN32_FIND_DATA* fd2 = &(*(const Entry* const*)arg2)->data; int cmp = compareType(fd1, fd2); if (cmp) return cmp; return CompareFileTime(&fd2->ftLastWriteTime, &fd1->ftLastWriteTime); } /* array of entry comparison functions */ static int (*sortFunctions[])(const void* arg1, const void* arg2) = { compareName, /* SORT_NAME */ compareExt, /* SORT_EXT */ compareSize, /* SORT_SIZE */ compareDate /* SORT_DATE */ }; /* sort a list of directory entries by using the given sort order */ static void SortDirectory(Entry* dir, enum SORT_ORDER sortOrder) { Entry* entry = dir->down; Entry** array, **p; int len; len = 0; for(entry=dir->down; entry; entry=entry->next) len++; if (len) { array = heap_alloc(len*sizeof(Entry*)); p = array; for(entry=dir->down; entry; entry=entry->next) *p++ = entry; /* call qsort() with the appropriate compare function */ qsort(array, len, sizeof(array[0]), sortFunctions[sortOrder]); dir->down = array[0]; for(p=array; --len; p++) p[0]->next = p[1]; (*p)->next = 0; heap_free(array); } } /* read a directory in one of the file system namespaces using the given sorting order */ void read_directory(Entry* dir, LPCTSTR path, enum SORT_ORDER sortOrder, HWND hwnd) { TCHAR buffer[MAX_PATH]; Entry* entry; LPCTSTR s; PTSTR d; #ifdef _SHELL_FOLDERS if (dir->etype == ET_SHELL) { read_directory_shell(dir, hwnd); if (Globals.prescan_node) { s = path; d = buffer; while(*s) *d++ = *s++; *d++ = '\\'; for(entry=dir->down; entry; entry=entry->next) if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { read_directory_shell(entry, hwnd); SortDirectory(entry, sortOrder); } } } else #endif #if !defined(_NO_EXTENSIONS) && defined(__WINE__) if (dir->etype == ET_UNIX) { read_directory_unix(dir, path); if (Globals.prescan_node) { s = path; d = buffer; while(*s) *d++ = *s++; *d++ = '/'; for(entry=dir->down; entry; entry=entry->next) if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { lstrcpy(d, entry->data.cFileName); read_directory_unix(entry, buffer); SortDirectory(entry, sortOrder); } } } else #endif { read_directory_win(dir, path); if (Globals.prescan_node) { s = path; d = buffer; while(*s) *d++ = *s++; *d++ = '\\'; for(entry=dir->down; entry; entry=entry->next) if (entry->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { lstrcpy(d, entry->data.cFileName); read_directory_win(entry, buffer); SortDirectory(entry, sortOrder); } } } SortDirectory(dir, sortOrder); } /* read the directory tree of a drive in one of the file system namespaces using the given sorting order */ Entry* read_tree(Root* root, LPCTSTR path, LPITEMIDLIST pidl, LPTSTR drv, enum SORT_ORDER sortOrder, HWND hwnd) { #if !defined(_NO_EXTENSIONS) && defined(__WINE__) static const TCHAR sSlash[] = {'/', '\0'}; #endif static const TCHAR sBackslash[] = {'\\', '\0'}; #ifdef _SHELL_FOLDERS if (pidl) { /* read shell namespace tree */ root->drive_type = DRIVE_UNKNOWN; drv[0] = '\\'; drv[1] = '\0'; load_string(root->volname, IDS_DESKTOP); root->fs_flags = 0; load_string(root->fs, IDS_SHELL); return read_tree_shell(root, pidl, sortOrder, hwnd); } else #endif #if !defined(_NO_EXTENSIONS) && defined(__WINE__) if (*path == '/') { /* read unix file system tree */ root->drive_type = GetDriveType(path); lstrcat(drv, sSlash); load_string(root->volname, IDS_ROOT_FS); root->fs_flags = 0; load_string(root->fs, IDS_UNIXFS); lstrcpy(root->path, sSlash); return read_tree_unix(root, path, sortOrder, hwnd); } #endif /* read WIN32 file system tree */ root->drive_type = GetDriveType(path); lstrcat(drv, sBackslash); GetVolumeInformation(drv, root->volname, _MAX_FNAME, 0, 0, &root->fs_flags, root->fs, _MAX_DIR); lstrcpy(root->path, drv); return read_tree_win(root, path, sortOrder, hwnd); } /* get full path of specified directory entry */ void get_path(Entry* dir, PTSTR path) { Entry* entry; int len = 0; int level = 0; #ifdef _SHELL_FOLDERS if (dir->etype == ET_SHELL) { SFGAOF attribs; HRESULT hr = S_OK; path[0] = '\0'; attribs = 0; if (dir->folder) hr = IShellFolder_GetAttributesOf(dir->folder, 1, (LPCITEMIDLIST*)&dir->pidl, &attribs); if (SUCCEEDED(hr) && (attribs&SFGAO_FILESYSTEM)) { IShellFolder* parent = dir->up? dir->up->folder: Globals.iDesktop; hr = path_from_pidl(parent, dir->pidl, path, MAX_PATH); } } else #endif { for(entry=dir; entry; level++) { LPCTSTR name; int l; { LPCTSTR s; name = entry->data.cFileName; s = name; for(l=0; *s && *s!='/' && *s!='\\'; s++) l++; } if (entry->up) { if (l > 0) { memmove(path+l+1, path, len*sizeof(TCHAR)); memcpy(path+1, name, l*sizeof(TCHAR)); len += l+1; #ifndef _NO_EXTENSIONS if (entry->etype == ET_UNIX) path[0] = '/'; else #endif path[0] = '\\'; } entry = entry->up; } else { memmove(path+l, path, len*sizeof(TCHAR)); memcpy(path, name, l*sizeof(TCHAR)); len += l; break; } } if (!level) { #ifndef _NO_EXTENSIONS if (entry->etype == ET_UNIX) path[len++] = '/'; else #endif path[len++] = '\\'; } path[len] = '\0'; } }