/*---------------------------------------------------------------------------*
* PANSIFileSystemImpl.c *
* *
* Copyright 2007, 2008 Nuance Communciations, Inc. *
* *
* Licensed under the Apache License, Version 2.0 (the 'License'); *
* you may not use this file except in compliance with the License. *
* *
* You may obtain a copy of the License at *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an 'AS IS' BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* *
*---------------------------------------------------------------------------*/
#include "LCHAR.h"
#include "PFileSystemImpl.h"
#include "PANSIFileSystemImpl.h"
#include "PANSIFileImpl.h"
#include "plog.h"
#include "pmemory.h"
//extern PFileSystem* PANSIFileSystemSingleton;
PFileSystem* PANSIFileSystemSingleton = (PFileSystem*)NULL;
#define MTAG NULL
#ifdef USE_THREAD
/* Prototype of private function */
PORTABLE_API ESR_ReturnCode PtrdFlush();
#endif
/**
* [file path, PFileSystem*] mapping.
*/
extern PHashTable* PFileSystemPathMap;
ESR_ReturnCode PANSIFileSystemCreate(void)
{
PANSIFileSystemImpl* impl;
ESR_ReturnCode rc;
if (PANSIFileSystemSingleton != NULL)
return ESR_SUCCESS;
impl = NEW(PANSIFileSystemImpl, MTAG);
if (impl == NULL)
return ESR_OUT_OF_MEMORY;
impl->super.super.destroy = &PANSIFileSystemDestroyImpl;
impl->super.super.createPFile = &PANSIFileSystemCreatePFileImpl;
impl->super.addPath = &PANSIFileSystemAddPathImpl;
impl->super.removePath = &PANSIFileSystemRemovePathImpl;
impl->super.getcwd = &PANSIFileSystemGetcwdImpl;
impl->super.super.mkdir = &PANSIFileSystemMkdirImpl;
impl->super.super.chdir = &PANSIFileSystemChdirImpl;
CHKLOG(rc, PHashTableCreate(NULL, MTAG, &impl->directoryMap));
PANSIFileSystemSingleton = &impl->super.super;
return ESR_SUCCESS;
CLEANUP:
return rc;
}
ESR_ReturnCode PANSIFileSystemDestroyImpl(PFileSystem* self)
{
PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) self;
PHashTableEntry* entry;
PHashTableEntry* oldEntry;
LCHAR* key;
LCHAR* value;
ESR_ReturnCode rc;
if (impl->directoryMap != NULL)
{
CHKLOG(rc, PHashTableEntryGetFirst(impl->directoryMap, &entry));
while (entry != NULL)
{
CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
oldEntry = entry;
CHKLOG(rc, PHashTableEntryAdvance(&entry));
CHKLOG(rc, PHashTableEntryRemove(oldEntry));
CHKLOG(rc, PHashTableRemoveValue(PFileSystemPathMap, key, NULL));
FREE(key);
FREE(value);
}
CHKLOG(rc, PHashTableDestroy(impl->directoryMap));
impl->directoryMap = NULL;
}
FREE(self);
return ESR_SUCCESS;
CLEANUP:
return rc;
}
ESR_ReturnCode PANSIFileSystemAddPathImpl(PFileSystem* self, const LCHAR* virtualPath, const LCHAR* realPath)
{
PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) self;
ESR_BOOL exists;
LCHAR* key = NULL;
LCHAR* value = NULL;
ESR_ReturnCode rc;
size_t len;
if (virtualPath == NULL || realPath == NULL)
{
rc = ESR_INVALID_ARGUMENT;
PLogError(ESR_rc2str(rc));
goto CLEANUP;
}
len = LSTRLEN(virtualPath) + 1;
if (virtualPath[LSTRLEN(virtualPath)-1] != L('/'))
++len;
key = MALLOC(sizeof(LCHAR) * len, MTAG);
if (key == NULL)
{
rc = ESR_OUT_OF_MEMORY;
PLogError(ESR_rc2str(rc));
goto CLEANUP;
}
LSTRCPY(key, virtualPath);
/* Make sure paths end with '/' */
CHKLOG(rc, PFileSystemCanonicalSlashes(key));
if (key[LSTRLEN(key)-1] != L('/'))
LSTRCAT(key, L("/"));
value = MALLOC(sizeof(LCHAR) * (LSTRLEN(realPath) + 1), MTAG);
if (value == NULL)
{
rc = ESR_OUT_OF_MEMORY;
PLogError(ESR_rc2str(rc));
goto CLEANUP;
}
LSTRCPY(value, realPath);
/* Make sure realPath is not an empty string */
lstrtrim(value);
if (LSTRLEN(value) == 0)
{
FREE(value);
value = NULL;
rc = ESR_INVALID_ARGUMENT;
PLogError(L("%s: realPath cannot be empty"), ESR_rc2str(rc));
goto CLEANUP;
}
/* Make sure paths end with '/' */
CHKLOG(rc, PFileSystemCanonicalSlashes(value));
if (value[LSTRLEN(value)-1] != L('/'))
LSTRCAT(value, L("/"));
CHKLOG(rc, PHashTableContainsKey(impl->directoryMap, key, &exists));
if (exists)
{
LCHAR* oldValue;
CHKLOG(rc, PHashTableGetValue(impl->directoryMap, key, (void **)&oldValue));
if (LSTRCMP(oldValue, value) != 0)
{
rc = ESR_IDENTIFIER_COLLISION;
PLogError(ESR_rc2str(rc));
goto CLEANUP;
}
}
CHKLOG(rc, PHashTablePutValue(impl->directoryMap, key, value, NULL));
CHKLOG(rc, PHashTablePutValue(PFileSystemPathMap, key, self, NULL));
return ESR_SUCCESS;
CLEANUP:
FREE(key);
FREE(value);
return rc;
}
ESR_ReturnCode PANSIFileSystemRemovePathImpl(PFileSystem* self, const LCHAR* virtualPath)
{
PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) self;
LCHAR path[P_PATH_MAX];
LCHAR* key;
LCHAR* value;
PHashTableEntry* entry;
ESR_ReturnCode rc;
if (virtualPath == NULL)
{
rc = ESR_INVALID_ARGUMENT;
PLogError(ESR_rc2str(rc));
goto CLEANUP;
}
/* Make sure paths end with '/' */
LSTRCPY(path, virtualPath);
CHKLOG(rc, PFileSystemCanonicalSlashes(path));
if (path[LSTRLEN(path)-1] != L('/'))
LSTRCAT(path, L("/"));
CHKLOG(rc, PHashTableGetEntry(impl->directoryMap, path, &entry));
CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
CHKLOG(rc, PHashTableEntryRemove(entry));
CHKLOG(rc, PHashTableRemoveValue(PFileSystemPathMap, key, NULL));
FREE(key);
FREE(value);
return ESR_SUCCESS;
CLEANUP:
return rc;
}
ESR_ReturnCode PANSIFileSystemGetRealPathImpl(PFileSystem* self, LCHAR* path, size_t* len)
{
PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) self;
PHashTableEntry* entry;
LCHAR* key;
LCHAR* value;
LCHAR* bestKey = NULL;
LCHAR* bestValue = NULL;
ESR_BOOL isAbsolute;
ESR_ReturnCode rc;
CHKLOG(rc, PFileSystemGetAbsolutePath(path, len));
CHKLOG(rc, PHashTableEntryGetFirst(impl->directoryMap, &entry));
while (entry != NULL)
{
CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void**)&key, (void**)&value));
if (LSTRNCMP(path, key, LSTRLEN(key)) == 0)
{
/* File-system handles file path */
if (bestKey == NULL || LSTRLEN(key) > LSTRLEN(bestKey))
{
/* Found a better match -- the new key is a subdirectory of the previous bestKey */
bestKey = key;
bestValue = value;
}
}
CHKLOG(rc, PHashTableEntryAdvance(&entry));
}
if (bestKey == NULL)
{
rc = ESR_INVALID_STATE;
PLogError(L("PANSIFileSystem does not handle the specified path: %s"), path);
goto CLEANUP;
}
if (LSTRLEN(bestValue) + 1 > *len)
{
*len = LSTRLEN(bestValue) + 1;
rc = ESR_BUFFER_OVERFLOW;
PLogError(ESR_rc2str(rc));
goto CLEANUP;
}
/* Delete the virtual-path */
LSTRCPY(path, path + LSTRLEN(bestKey));
CHKLOG(rc, PFileSystemIsAbsolutePath(path, &isAbsolute));
if (LSTRCMP(bestValue, L("/")) == 0 && isAbsolute)
{
/* do nothing */
}
else
{
/* Insert the key-path */
CHKLOG(rc, lstrinsert(bestValue, path, 0, len));
}
return ESR_SUCCESS;
CLEANUP:
return rc;
}
ESR_ReturnCode PANSIFileSystemCreatePFileImpl(PFileSystem* self, const LCHAR* path, ESR_BOOL littleEndian, PFile** file)
{
LCHAR realPath[P_PATH_MAX];
size_t len;
ESR_ReturnCode rc;
LSTRCPY(realPath, path);
len = P_PATH_MAX;
CHKLOG(rc, PANSIFileSystemGetRealPathImpl(self, realPath, &len));
return PANSIFileCreateImpl(realPath, littleEndian, file);
CLEANUP:
return rc;
}
ESR_ReturnCode PANSIFileSystemSetDefault(ESR_BOOL isDefault)
{
PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) PANSIFileSystemSingleton;
ESR_BOOL exists;
LCHAR* key = NULL;
LCHAR* value = NULL;
PHashTableEntry* entry;
ESR_ReturnCode rc;
if (isDefault)
{
key = MALLOC(sizeof(LCHAR), MTAG);
if (key == NULL)
{
rc = ESR_OUT_OF_MEMORY;
PLogError(ESR_rc2str(rc));
goto CLEANUP;
}
LSTRCPY(key, L(""));
value = MALLOC(sizeof(LCHAR), MTAG);
if (value == NULL)
{
rc = ESR_OUT_OF_MEMORY;
PLogError(ESR_rc2str(rc));
goto CLEANUP;
}
LSTRCPY(value, L(""));
CHKLOG(rc, PHashTableContainsKey(impl->directoryMap, key, &exists));
if (exists)
{
LCHAR* key;
LCHAR* value;
CHKLOG(rc, PHashTableGetEntry(impl->directoryMap, L(""), &entry));
CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
CHKLOG(rc, PHashTableEntryRemove(entry));
CHKLOG(rc, PHashTableRemoveValue(PFileSystemPathMap, key, NULL));
FREE(key);
FREE(value);
}
CHKLOG(rc, PHashTablePutValue(impl->directoryMap, key, value, NULL));
CHKLOG(rc, PHashTablePutValue(PFileSystemPathMap, key, PANSIFileSystemSingleton, NULL));
/* Set virtual current working directory to native current working directory */
}
else
{
CHKLOG(rc, PHashTableContainsKey(impl->directoryMap, L(""), &exists));
if (exists)
{
LCHAR* key;
LCHAR* value;
CHKLOG(rc, PHashTableGetEntry(impl->directoryMap, L(""), &entry));
CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
CHKLOG(rc, PHashTableContainsKey(PFileSystemPathMap, L(""), &exists));
if (exists)
{
LCHAR* key;
PFileSystem* value;
PHashTableEntry* entry;
CHKLOG(rc, PHashTableGetEntry(PFileSystemPathMap, L(""), &entry));
CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
if (value == PANSIFileSystemSingleton)
CHKLOG(rc, PHashTableEntryRemove(entry));
}
CHKLOG(rc, PHashTableEntryRemove(entry));
FREE(key);
FREE(value);
}
}
return ESR_SUCCESS;
CLEANUP:
FREE(key);
FREE(value);
return rc;
}