aseprite/src/file_system.cpp
2013-01-27 12:13:13 -03:00

1119 lines
28 KiB
C++

/* ASEPRITE
* Copyright (C) 2001-2013 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Some of the original code to handle PIDLs come from the
MiniExplorer example of the Vaca library:
http://vaca.sourceforge.net/
*/
#include "config.h"
#include <cstdio>
#include <vector>
#include <map>
#include <algorithm>
#include <utility>
#include <allegro.h>
// define this macro to solve the problem of for_each_file Allegro
// routine which does not support to wrap 64-bits pointers in its
// user-data parameter
#define WORKAROUND_64BITS_SUPPORT
// in Windows we can use PIDLS
#if defined ALLEGRO_WINDOWS
// uncomment this if you don't want to use PIDLs in windows
#define USE_PIDLS
#endif
#if defined ALLEGRO_UNIX || defined ALLEGRO_MACOSX || defined ALLEGRO_DJGPP || defined ALLEGRO_MINGW32
#include <sys/stat.h>
#endif
#if defined ALLEGRO_UNIX || defined ALLEGRO_MACOSX || defined ALLEGRO_MINGW32
#include <sys/unistd.h>
#endif
#if defined USE_PIDLS
#include <winalleg.h>
#include <shlobj.h>
#include <shlwapi.h>
#endif
#include "base/path.h"
#include "file_system.h"
//////////////////////////////////////////////////////////////////////
#ifdef USE_PIDLS
// ..using Win32 Shell (PIDLs)
#define IS_FOLDER(fi) \
(((fi)->attrib & SFGAO_FOLDER) == SFGAO_FOLDER)
#define MYPC_CSLID "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"
#else
// ..using Allegro (for_each_file)
#define IS_FOLDER(fi) \
(((fi)->attrib & FA_DIREC) == FA_DIREC)
#if (DEVICE_SEPARATOR != 0) && (DEVICE_SEPARATOR != '\0')
#define HAVE_DRIVES
#else
#define CASE_SENSITIVE
#endif
#ifndef FA_ALL
#define FA_ALL FA_RDONLY | FA_DIREC | FA_ARCH | FA_HIDDEN | FA_SYSTEM
#endif
#define FA_TO_SHOW FA_RDONLY | FA_DIREC | FA_ARCH | FA_SYSTEM
#endif
//////////////////////////////////////////////////////////////////////
#ifndef MAX_PATH
#define MAX_PATH 4096
#endif
#define NOTINITIALIZED "{__not_initialized_path__}"
// a position in the file-system
class FileItem : public IFileItem
{
public:
base::string keyname;
base::string filename;
base::string displayname;
FileItem* parent;
FileItemList children;
unsigned int version;
bool removed;
#ifdef USE_PIDLS
LPITEMIDLIST pidl; // relative to parent
LPITEMIDLIST fullpidl; // relative to the Desktop folder
// (like a full path-name, because the
// desktop is the root on Windows)
SFGAOF attrib;
#else
int attrib;
#endif
FileItem(FileItem* parent);
~FileItem();
void insertChildSorted(FileItem* child);
int compare(const FileItem& that) const;
bool operator<(const FileItem& that) const { return compare(that) < 0; }
bool operator>(const FileItem& that) const { return compare(that) > 0; }
bool operator==(const FileItem& that) const { return compare(that) == 0; }
bool operator!=(const FileItem& that) const { return compare(that) != 0; }
// IFileItem interface
bool isFolder() const;
bool isBrowsable() const;
base::string getKeyName() const;
base::string getFileName() const;
base::string getDisplayName() const;
IFileItem* getParent() const;
const FileItemList& getChildren();
bool hasExtension(const base::string& csv_extensions);
BITMAP* getThumbnail();
void setThumbnail(BITMAP* thumbnail);
};
typedef std::map<base::string, FileItem*> FileItemMap;
typedef std::map<base::string, BITMAP*> ThumbnailMap;
// the root of the file-system
static FileItem* rootitem = NULL;
static FileItemMap* fileitems_map;
static ThumbnailMap* thumbnail_map;
static unsigned int current_file_system_version = 0;
#ifdef USE_PIDLS
static IMalloc* shl_imalloc = NULL;
static IShellFolder* shl_idesktop = NULL;
#else
#ifdef WORKAROUND_64BITS_SUPPORT
static FileItem* for_each_child_callback_param;
#endif
#endif
/* a more easy PIDLs interface (without using the SH* & IL* routines of W2K) */
#ifdef USE_PIDLS
static void update_by_pidl(FileItem* fileitem);
static LPITEMIDLIST concat_pidl(LPITEMIDLIST pidlHead, LPITEMIDLIST pidlTail);
static UINT get_pidl_size(LPITEMIDLIST pidl);
static LPITEMIDLIST get_next_pidl(LPITEMIDLIST pidl);
static LPITEMIDLIST get_last_pidl(LPITEMIDLIST pidl);
static LPITEMIDLIST clone_pidl(LPITEMIDLIST pidl);
static LPITEMIDLIST remove_last_pidl(LPITEMIDLIST pidl);
static void free_pidl(LPITEMIDLIST pidl);
static base::string get_key_for_pidl(LPITEMIDLIST pidl);
static FileItem* get_fileitem_by_fullpidl(LPITEMIDLIST pidl, bool create_if_not);
static void put_fileitem(FileItem* fileitem);
#else
static FileItem* get_fileitem_by_path(const base::string& path, bool create_if_not);
static void for_each_child_callback(const char *filename, int attrib, int param);
static base::string remove_backslash_if_needed(const base::string& filename);
static base::string get_key_for_filename(const base::string& filename);
static void put_fileitem(FileItem* fileitem);
#endif
FileSystemModule* FileSystemModule::m_instance = NULL;
/**
* Initializes the file-system module to navigate the file-system.
*/
FileSystemModule::FileSystemModule()
{
ASSERT(m_instance == NULL);
m_instance = this;
fileitems_map = new FileItemMap;
thumbnail_map = new ThumbnailMap;
#ifdef USE_PIDLS
/* get the IMalloc interface */
SHGetMalloc(&shl_imalloc);
/* get desktop IShellFolder interface */
SHGetDesktopFolder(&shl_idesktop);
#endif
// first version of the file system
++current_file_system_version;
// get the root element of the file system (this will create
// the 'rootitem' FileItem)
getRootFileItem();
PRINTF("File system module installed\n");
}
/**
* Shutdowns the file-system module.
*/
FileSystemModule::~FileSystemModule()
{
PRINTF("File system module: uninstalling\n");
ASSERT(m_instance == this);
for (FileItemMap::iterator
it=fileitems_map->begin(); it!=fileitems_map->end(); ++it) {
delete it->second;
}
fileitems_map->clear();
for (ThumbnailMap::iterator
it=thumbnail_map->begin(); it!=thumbnail_map->end(); ++it) {
destroy_bitmap(it->second);
}
thumbnail_map->clear();
#ifdef USE_PIDLS
// relase desktop IShellFolder interface
shl_idesktop->Release();
// release IMalloc interface
shl_imalloc->Release();
shl_imalloc = NULL;
#endif
delete fileitems_map;
delete thumbnail_map;
PRINTF("File system module: uninstalled\n");
m_instance = NULL;
}
FileSystemModule* FileSystemModule::instance()
{
return m_instance;
}
/**
* Marks all FileItems as deprecated to be refresh the next time they
* are queried through @ref FileItem#getChildren.
*
* @see FileItem#getChildren
*/
void FileSystemModule::refresh()
{
++current_file_system_version;
}
IFileItem* FileSystemModule::getRootFileItem()
{
FileItem* fileitem;
if (rootitem)
return rootitem;
fileitem = new FileItem(NULL);
rootitem = fileitem;
//PRINTF("FS: Creating root fileitem %p\n", rootitem);
#ifdef USE_PIDLS
{
// get the desktop PIDL
LPITEMIDLIST pidl = NULL;
if (SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidl) != S_OK) {
// TODO do something better
ASSERT(false);
exit(1);
}
fileitem->pidl = pidl;
fileitem->fullpidl = pidl;
fileitem->attrib = SFGAO_FOLDER;
shl_idesktop->GetAttributesOf(1, (LPCITEMIDLIST *)&pidl, &fileitem->attrib);
update_by_pidl(fileitem);
}
#else
{
const char* root;
#if defined HAVE_DRIVES
root = "C:\\";
#else
root = "/";
#endif
fileitem->filename = root;
fileitem->displayname = root;
fileitem->attrib = FA_DIREC;
}
#endif
// insert the file-item in the hash-table
put_fileitem(fileitem);
return fileitem;
}
/**
* Returns the FileItem through the specified @a path.
*
* @warning You have to call path.fix_separators() before.
*/
IFileItem* FileSystemModule::getFileItemFromPath(const base::string& path)
{
IFileItem* fileitem = NULL;
//PRINTF("FS: get_fileitem_from_path(%s)\n", path.c_str());
#ifdef USE_PIDLS
{
ULONG cbEaten;
WCHAR wStr[MAX_PATH];
LPITEMIDLIST fullpidl = NULL;
SFGAOF attrib = SFGAO_FOLDER;
if (path.empty()) {
fileitem = getRootFileItem();
//PRINTF("FS: > %p (root)\n", fileitem);
return fileitem;
}
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
path.c_str(), path.size()+1, wStr, MAX_PATH);
if (shl_idesktop->ParseDisplayName(NULL, NULL,
wStr, &cbEaten,
&fullpidl,
&attrib) != S_OK) {
//PRINTF("FS: > (null)\n");
return NULL;
}
fileitem = get_fileitem_by_fullpidl(fullpidl, true);
free_pidl(fullpidl);
}
#else
{
base::string buf = remove_backslash_if_needed(path);
fileitem = get_fileitem_by_path(buf, true);
}
#endif
//PRINTF("FS: get_fileitem_from_path(%s) -> %p\n", path.c_str(), fileitem);
return fileitem;
}
bool FileSystemModule::dirExists(const base::string& path)
{
struct al_ffblk info;
int ret;
base::string path2 = base::join_path(path, "*.*");
ret = al_findfirst(path2.c_str(), &info, FA_ALL);
al_findclose(&info);
return (ret == 0);
}
// ======================================================================
// FileItem class (IFileItem implementation)
// ======================================================================
bool FileItem::isFolder() const
{
return IS_FOLDER(this);
}
bool FileItem::isBrowsable() const
{
ASSERT(this->filename != NOTINITIALIZED);
#ifdef USE_PIDLS
return IS_FOLDER(this)
&& (base::get_file_extension(this->filename) != "zip")
&& ((!this->filename.empty() && (*this->filename.begin()) != ':') ||
(this->filename == MYPC_CSLID));
#else
return IS_FOLDER(this);
#endif
}
base::string FileItem::getKeyName() const
{
ASSERT(this->keyname != NOTINITIALIZED);
return this->keyname;
}
base::string FileItem::getFileName() const
{
ASSERT(this->filename != NOTINITIALIZED);
return this->filename;
}
base::string FileItem::getDisplayName() const
{
ASSERT(this->displayname != NOTINITIALIZED);
return this->displayname;
}
IFileItem* FileItem::getParent() const
{
if (this == rootitem)
return NULL;
else {
ASSERT(this->parent);
return this->parent;
}
}
const FileItemList& FileItem::getChildren()
{
// Is the file-item a folder?
if (IS_FOLDER(this) &&
// if the children list is empty, or the file-system version
// change (it's like to say: the current this->children list
// is outdated)...
(this->children.empty() ||
current_file_system_version > this->version)) {
FileItemList::iterator it;
FileItem* child;
// we have to mark current items as deprecated
for (it=this->children.begin();
it!=this->children.end(); ++it) {
child = static_cast<FileItem*>(*it);
child->removed = true;
}
//PRINTF("FS: Loading files for %p (%s)\n", fileitem, fileitem->displayname);
#ifdef USE_PIDLS
{
IShellFolder* pFolder = NULL;
if (this == rootitem)
pFolder = shl_idesktop;
else
shl_idesktop->BindToObject(this->fullpidl,
NULL,
IID_IShellFolder,
(LPVOID *)&pFolder);
if (pFolder != NULL) {
IEnumIDList *pEnum = NULL;
ULONG c, fetched;
/* get the interface to enumerate subitems */
pFolder->EnumObjects(win_get_window(),
SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &pEnum);
if (pEnum != NULL) {
LPITEMIDLIST itempidl[256];
SFGAOF attribs[256];
/* enumerate the items in the folder */
while (pEnum->Next(256, itempidl, &fetched) == S_OK && fetched > 0) {
/* request the SFGAO_FOLDER attribute to know what of the
item is a folder */
for (c=0; c<fetched; ++c) {
attribs[c] = SFGAO_FOLDER;
pFolder->GetAttributesOf(1, (LPCITEMIDLIST *)itempidl, attribs+c);
}
/* generate the FileItems */
for (c=0; c<fetched; ++c) {
LPITEMIDLIST fullpidl = concat_pidl(this->fullpidl,
itempidl[c]);
child = get_fileitem_by_fullpidl(fullpidl, false);
if (!child) {
child = new FileItem(this);
child->pidl = itempidl[c];
child->fullpidl = fullpidl;
child->attrib = attribs[c];
update_by_pidl(child);
put_fileitem(child);
}
else {
ASSERT(child->parent == this);
free_pidl(fullpidl);
free_pidl(itempidl[c]);
}
this->insertChildSorted(child);
}
}
pEnum->Release();
}
if (pFolder != shl_idesktop)
pFolder->Release();
}
}
#else
{
char buf[MAX_PATH], path[MAX_PATH], tmp[32];
ustrcpy(path, this->filename.c_str());
put_backslash(path);
replace_filename(buf,
path,
uconvert_ascii("*.*", tmp),
sizeof(buf));
#ifdef WORKAROUND_64BITS_SUPPORT
// we cannot use the for_each_file's 'param' to wrap a 64-bits pointer
for_each_child_callback_param = this;
for_each_file(buf, FA_TO_SHOW, for_each_child_callback, 0);
#else
for_each_file(buf, FA_TO_SHOW,
for_each_child_callback,
(int)this);
#endif
}
#endif
// check old file-items (maybe removed directories or file-items)
for (it=this->children.begin();
it!=this->children.end(); ) {
child = static_cast<FileItem*>(*it);
if (child->removed) {
it = this->children.erase(it);
fileitems_map->erase(fileitems_map->find(child->keyname));
delete child;
}
else
++it;
}
// now this file-item is updated
this->version = current_file_system_version;
}
return this->children;
}
bool FileItem::hasExtension(const base::string& csv_extensions)
{
ASSERT(this->filename != NOTINITIALIZED);
return base::has_file_extension(this->filename, csv_extensions);
}
BITMAP* FileItem::getThumbnail()
{
ThumbnailMap::iterator it = thumbnail_map->find(this->filename);
if (it != thumbnail_map->end())
return it->second;
else
return NULL;
}
void FileItem::setThumbnail(BITMAP* thumbnail)
{
// destroy the current thumbnail of the file (if exists)
ThumbnailMap::iterator it = thumbnail_map->find(this->filename);
if (it != thumbnail_map->end()) {
destroy_bitmap(it->second);
thumbnail_map->erase(it);
}
// insert the new one in the map
thumbnail_map->insert(std::make_pair(this->filename, thumbnail));
}
FileItem::FileItem(FileItem* parent)
{
//PRINTF("FS: Creating %p fileitem with parent %p\n", this, parent);
this->keyname = NOTINITIALIZED;
this->filename = NOTINITIALIZED;
this->displayname = NOTINITIALIZED;
this->parent = parent;
this->version = current_file_system_version;
this->removed = false;
#ifdef USE_PIDLS
this->pidl = NULL;
this->fullpidl = NULL;
this->attrib = 0;
#else
this->attrib = 0;
#endif
}
FileItem::~FileItem()
{
PRINTF("FS: Destroying FileItem() with parent %p\n", parent);
#ifdef USE_PIDLS
if (this->fullpidl && this->fullpidl != this->pidl) {
free_pidl(this->fullpidl);
this->fullpidl = NULL;
}
if (this->pidl) {
free_pidl(this->pidl);
this->pidl = NULL;
}
#endif
}
void FileItem::insertChildSorted(FileItem* child)
{
// this file-item wasn't removed from the last lookup
child->removed = false;
// if the fileitem is already in the list we can go back
if (std::find(children.begin(), children.end(), child) != children.end())
return;
bool inserted = false;
for (FileItemList::iterator
it=children.begin(); it!=children.end(); ++it) {
if (*static_cast<FileItem*>(*it) > *child) {
children.insert(it, child);
inserted = true;
break;
}
}
if (!inserted)
children.push_back(child);
}
/**
* Compares two FileItems.
*
* Based on 'ustricmp' of Allegro. It makes sure that eg "foo.bar"
* comes before "foo-1.bar", and also that "foo9.bar" comes before
* "foo10.bar".
*/
int FileItem::compare(const FileItem& that) const
{
if (IS_FOLDER(this)) {
if (!IS_FOLDER(&that))
return -1;
}
else if (IS_FOLDER(&that))
return 1;
{
int c1, c2;
int x1, x2;
char *t1, *t2;
const char* s1 = this->displayname.c_str();
const char* s2 = that.displayname.c_str();
for (;;) {
c1 = utolower(ugetxc(&s1));
c2 = utolower(ugetxc(&s2));
if ((c1 >= '0') && (c1 <= '9') && (c2 >= '0') && (c2 <= '9')) {
x1 = ustrtol(s1 - ucwidth(c1), &t1, 10);
x2 = ustrtol(s2 - ucwidth(c2), &t2, 10);
if (x1 != x2)
return x1 - x2;
else if (t1 - s1 != t2 - s2)
return (t2 - s2) - (t1 - s1);
s1 = t1;
s2 = t2;
}
else if (c1 != c2) {
if (!c1)
return -1;
else if (!c2)
return 1;
else if (c1 == '.')
return -1;
else if (c2 == '.')
return 1;
return c1 - c2;
}
if (!c1)
return 0;
}
}
return -1;
}
//////////////////////////////////////////////////////////////////////
// PIDLS: Only for Win32
//////////////////////////////////////////////////////////////////////
#ifdef USE_PIDLS
/* updates the names of the file-item through its PIDL */
static void update_by_pidl(FileItem* fileitem)
{
STRRET strret;
TCHAR pszName[MAX_PATH];
IShellFolder *pFolder = NULL;
if (fileitem == rootitem)
pFolder = shl_idesktop;
else {
ASSERT(fileitem->parent);
shl_idesktop->BindToObject(fileitem->parent->fullpidl,
NULL,
IID_IShellFolder,
(LPVOID *)&pFolder);
}
/****************************************/
/* get the file name */
if (pFolder != NULL &&
pFolder->GetDisplayNameOf(fileitem->pidl,
SHGDN_NORMAL | SHGDN_FORPARSING,
&strret) == S_OK) {
StrRetToBuf(&strret, fileitem->pidl, pszName, MAX_PATH);
fileitem->filename = pszName;
}
else if (shl_idesktop->GetDisplayNameOf(fileitem->fullpidl,
SHGDN_NORMAL | SHGDN_FORPARSING,
&strret) == S_OK) {
StrRetToBuf(&strret, fileitem->fullpidl, pszName, MAX_PATH);
fileitem->filename = pszName;
}
else
fileitem->filename = "ERR";
/****************************************/
/* get the name to display */
if (fileitem->isFolder() &&
pFolder &&
pFolder->GetDisplayNameOf(fileitem->pidl,
SHGDN_INFOLDER,
&strret) == S_OK) {
StrRetToBuf(&strret, fileitem->pidl, pszName, MAX_PATH);
fileitem->displayname = pszName;
}
else if (fileitem->isFolder() &&
shl_idesktop->GetDisplayNameOf(fileitem->fullpidl,
SHGDN_INFOLDER,
&strret) == S_OK) {
StrRetToBuf(&strret, fileitem->fullpidl, pszName, MAX_PATH);
fileitem->displayname = pszName;
}
else {
fileitem->displayname = base::get_file_name(fileitem->filename);
}
if (pFolder != NULL && pFolder != shl_idesktop) {
pFolder->Release();
}
}
static LPITEMIDLIST concat_pidl(LPITEMIDLIST pidlHead, LPITEMIDLIST pidlTail)
{
LPITEMIDLIST pidlNew;
UINT cb1, cb2;
ASSERT(pidlHead);
ASSERT(pidlTail);
cb1 = get_pidl_size(pidlHead) - sizeof(pidlHead->mkid.cb);
cb2 = get_pidl_size(pidlTail);
pidlNew = (LPITEMIDLIST)shl_imalloc->Alloc(cb1 + cb2);
if (pidlNew) {
CopyMemory(pidlNew, pidlHead, cb1);
CopyMemory(((LPSTR)pidlNew) + cb1, pidlTail, cb2);
}
return pidlNew;
}
static UINT get_pidl_size(LPITEMIDLIST pidl)
{
UINT cbTotal = 0;
if (pidl) {
cbTotal += sizeof(pidl->mkid.cb); /* null terminator */
while (pidl) {
cbTotal += pidl->mkid.cb;
pidl = get_next_pidl(pidl);
}
}
return cbTotal;
}
static LPITEMIDLIST get_next_pidl(LPITEMIDLIST pidl)
{
if (pidl != NULL && pidl->mkid.cb > 0) {
pidl = (LPITEMIDLIST)(((LPBYTE)(pidl)) + pidl->mkid.cb);
if (pidl->mkid.cb > 0)
return pidl;
}
return NULL;
}
static LPITEMIDLIST get_last_pidl(LPITEMIDLIST pidl)
{
LPITEMIDLIST pidlLast = pidl;
LPITEMIDLIST pidlNew = NULL;
while (pidl) {
pidlLast = pidl;
pidl = get_next_pidl(pidl);
}
if (pidlLast) {
ULONG sz = get_pidl_size(pidlLast);
pidlNew = (LPITEMIDLIST)shl_imalloc->Alloc(sz);
CopyMemory(pidlNew, pidlLast, sz);
}
return pidlNew;
}
static LPITEMIDLIST clone_pidl(LPITEMIDLIST pidl)
{
ULONG sz = get_pidl_size(pidl);
LPITEMIDLIST pidlNew = (LPITEMIDLIST)shl_imalloc->Alloc(sz);
CopyMemory(pidlNew, pidl, sz);
return pidlNew;
}
static LPITEMIDLIST remove_last_pidl(LPITEMIDLIST pidl)
{
LPITEMIDLIST pidlFirst = pidl;
LPITEMIDLIST pidlLast = pidl;
while (pidl) {
pidlLast = pidl;
pidl = get_next_pidl(pidl);
}
if (pidlLast)
pidlLast->mkid.cb = 0;
return pidlFirst;
}
static void free_pidl(LPITEMIDLIST pidl)
{
shl_imalloc->Free(pidl);
}
static base::string get_key_for_pidl(LPITEMIDLIST pidl)
{
#if 0
char *key = base_malloc(get_pidl_size(pidl)+1);
UINT c, i = 0;
while (pidl) {
for (c=0; c<pidl->mkid.cb; ++c) {
if (pidl->mkid.abID[c])
key[i++] = pidl->mkid.abID[c];
else
key[i++] = 1;
}
pidl = get_next_pidl(pidl);
}
key[i] = 0;
return key;
#else
STRRET strret;
TCHAR pszName[MAX_PATH];
char key[4096];
int len;
ustrcpy(key, empty_string);
// Go pidl by pidl from the fullpidl to the root (desktop)
//PRINTF("FS: ***\n");
pidl = clone_pidl(pidl);
while (pidl->mkid.cb > 0) {
if (shl_idesktop->GetDisplayNameOf(pidl,
SHGDN_INFOLDER | SHGDN_FORPARSING,
&strret) == S_OK) {
StrRetToBuf(&strret, pidl, pszName, MAX_PATH);
//PRINTF("FS: + %s\n", pszName);
len = ustrlen(pszName);
if (len > 0) {
if (*key) {
if (pszName[len-1] != '\\') {
memmove(key+len+1, key, ustrlen(key)+1);
key[len] = '\\';
}
else
memmove(key+len, key, ustrlen(key)+1);
}
else
key[len] = 0;
memcpy(key, pszName, len);
}
}
remove_last_pidl(pidl);
}
free_pidl(pidl);
//PRINTF("FS: =%s\n***\n", key);
return key;
#endif
}
static FileItem* get_fileitem_by_fullpidl(LPITEMIDLIST fullpidl, bool create_if_not)
{
FileItemMap::iterator it = fileitems_map->find(get_key_for_pidl(fullpidl));
if (it != fileitems_map->end())
return it->second;
if (!create_if_not)
return NULL;
// new file-item
FileItem* fileitem = new FileItem(NULL);
fileitem->fullpidl = clone_pidl(fullpidl);
fileitem->attrib = SFGAO_FOLDER;
HRESULT hr = shl_idesktop->GetAttributesOf(1, (LPCITEMIDLIST *)&fileitem->fullpidl,
&fileitem->attrib);
if (hr == S_OK) {
LPITEMIDLIST parent_fullpidl = clone_pidl(fileitem->fullpidl);
remove_last_pidl(parent_fullpidl);
fileitem->pidl = get_last_pidl(fileitem->fullpidl);
fileitem->parent = get_fileitem_by_fullpidl(parent_fullpidl, true);
free_pidl(parent_fullpidl);
}
update_by_pidl(fileitem);
put_fileitem(fileitem);
//PRINTF("FS: fileitem %p created %s with parent %p\n", fileitem, fileitem->keyname.c_str(), fileitem->parent);
return fileitem;
}
/**
* Inserts the @a fileitem in the hash map of items.
*/
static void put_fileitem(FileItem* fileitem)
{
ASSERT(fileitem->filename != NOTINITIALIZED);
ASSERT(fileitem->keyname == NOTINITIALIZED);
fileitem->keyname = get_key_for_pidl(fileitem->fullpidl);
ASSERT(fileitem->keyname != NOTINITIALIZED);
#ifdef DEBUGMODE
FileItemMap::iterator it = fileitems_map->find(get_key_for_pidl(fileitem->fullpidl));
ASSERT(it == fileitems_map->end());
#endif
// insert this file-item in the hash-table
fileitems_map->insert(std::make_pair(fileitem->keyname, fileitem));
}
#else
//////////////////////////////////////////////////////////////////////
// Allegro for_each_file: Portable
//////////////////////////////////////////////////////////////////////
static FileItem* get_fileitem_by_path(const base::string& path, bool create_if_not)
{
if (path.empty())
return rootitem;
FileItemMap::iterator it = fileitems_map->find(get_key_for_filename(path));
if (it != fileitems_map->end())
return it->second;
if (!create_if_not)
return NULL;
// get the attributes of the file
int attrib = 0;
if (!file_exists(path.c_str(), FA_ALL, &attrib)) {
if (!FileSystemModule::instance()->dirExists(path))
return NULL;
attrib = FA_DIREC;
}
// new file-item
FileItem* fileitem = new FileItem(NULL);
fileitem->filename = path;
fileitem->displayname = base::get_file_name(path);
fileitem->attrib = attrib;
// get the parent
{
base::string parent_path = remove_backslash_if_needed(base::join_path(base::get_file_path(path), ""));
fileitem->parent = get_fileitem_by_path(parent_path, true);
}
put_fileitem(fileitem);
return fileitem;
}
static void for_each_child_callback(const char *filename, int attrib, int param)
{
#ifdef WORKAROUND_64BITS_SUPPORT
FileItem* fileitem = for_each_child_callback_param;
#else
FileItem* fileitem = (FileItem*)param;
#endif
FileItem* child;
const char *filename_without_path = get_filename(filename);
if (*filename_without_path == '.' &&
(ustrcmp(filename_without_path, ".") == 0 ||
ustrcmp(filename_without_path, "..") == 0))
return;
child = get_fileitem_by_path(filename, false);
if (!child) {
ASSERT(fileitem != NULL);
child = new FileItem(fileitem);
child->filename = filename;
child->displayname = filename_without_path;
child->attrib = attrib;
put_fileitem(child);
}
else {
ASSERT(child->parent == fileitem);
}
fileitem->insertChildSorted(child);
}
static base::string remove_backslash_if_needed(const base::string& filename)
{
if (!filename.empty() && base::is_path_separator(*(filename.end()-1))) {
int len = filename.size();
#ifdef HAVE_DRIVES
// if the name is C:\ or something like that, the backslash isn't
// removed
if (len == 3 && filename[1] == ':')
return filename;
#else
// this is just the root '/' slash
if (len == 1)
return filename;
#endif
return base::remove_path_separator(filename);
}
return filename;
}
static base::string get_key_for_filename(const base::string& filename)
{
base::string buf(filename);
#if !defined CASE_SENSITIVE
buf.tolower();
#endif
buf = base::fix_path_separators(buf);
return buf;
}
static void put_fileitem(FileItem* fileitem)
{
ASSERT(fileitem->filename != NOTINITIALIZED);
ASSERT(fileitem->keyname == NOTINITIALIZED);
fileitem->keyname = get_key_for_filename(fileitem->filename);
ASSERT(fileitem->keyname != NOTINITIALIZED);
// insert this file-item in the hash-table
fileitems_map->insert(std::make_pair(fileitem->keyname, fileitem));
}
#endif