/*---------------------------------------------------------------------------* * PFileSystem.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 "ArrayList.h" #include "LCHAR.h" #include "PFileSystem.h" #include "PFileSystemImpl.h" #include "phashtable.h" #include "plog.h" #include "pmemory.h" #define MTAG NULL /** * Indicates if PFileSystem is initialized. */ extern ESR_BOOL PFileSystemCreated; /** * [file path, PFileSystem*] mapping. */ extern PHashTable* PFileSystemPathMap; /** * Current working directory. */ extern LCHAR PFileSystemCurrentDirectory[P_PATH_MAX]; PORTABLE_API ESR_ReturnCode PFileSystemCanonicalSlashes(LCHAR* path) { ESR_ReturnCode rc; if (path == NULL) { rc = ESR_INVALID_ARGUMENT; PLogError(ESR_rc2str(rc)); goto CLEANUP; } lstrtrim(path); CHKLOG(rc, lstrreplace(path, L('\\'), L('/'))); return ESR_SUCCESS; CLEANUP: return rc; } ESR_ReturnCode PFileSystemLinearToPathTokens(const LCHAR* path, LCHAR*** tokenArray, size_t* count) { ESR_ReturnCode rc; const LCHAR* beginning; const LCHAR* ending; LCHAR linear[P_PATH_MAX]; ArrayList* arrayList = NULL; LCHAR* value = NULL; size_t i; if (path == NULL || tokenArray == NULL || count == NULL) { rc = ESR_INVALID_ARGUMENT; PLogError(ESR_rc2str(rc)); goto CLEANUP; } LSTRCPY(linear, path); CHKLOG(rc, PFileSystemCanonicalSlashes(linear)); CHKLOG(rc, ArrayListCreate(&arrayList)); beginning = linear; while (ESR_TRUE) { ending = LSTRCHR(beginning, L('/')); if (ending == NULL) ending = beginning + LSTRLEN(beginning); value = MALLOC(sizeof(LCHAR) * (ending - beginning + 1 + 1), MTAG); if (value == NULL) { rc = ESR_OUT_OF_MEMORY; PLogError(ESR_rc2str(rc)); goto CLEANUP; } LSTRNCPY(value, beginning, ending - beginning + 1); value[ending-beginning+1] = L('\0'); CHKLOG(rc, lstrtrim(value)); if (LSTRLEN(value) == 0) { FREE(value); value = NULL; } else { CHKLOG(rc, arrayList->add(arrayList, value)); value = NULL; } if (*ending == 0) break; beginning = ending + 1; } /* Build static token array */ CHKLOG(rc, arrayList->getSize(arrayList, count)); *tokenArray = MALLOC(*count * sizeof(LCHAR*), MTAG); if (*tokenArray == NULL) { rc = ESR_OUT_OF_MEMORY; goto CLEANUP; } for (i = 0; i < *count; ++i) { rc = arrayList->get(arrayList, i, (void**)(&(*tokenArray)[i])); if (rc != ESR_SUCCESS) goto CLEANUP; } rc = arrayList->destroy(arrayList); if (rc != ESR_SUCCESS) goto CLEANUP; return ESR_SUCCESS; CLEANUP: FREE(value); if (arrayList != NULL) { ESR_ReturnCode cleanRC; cleanRC = arrayList->getSize(arrayList, count); if (cleanRC != ESR_SUCCESS) return rc; for (i = 0; i < *count; ++i) { cleanRC = arrayList->get(arrayList, 0, (void**)&value); if (cleanRC != ESR_SUCCESS) return rc; FREE(value); cleanRC = arrayList->remove(arrayList, 0); if (cleanRC != ESR_SUCCESS) return rc; } arrayList->destroy(arrayList); } return rc; } ESR_ReturnCode PFileSystemIsAbsolutePath(const LCHAR* path, ESR_BOOL* isAbsolute) { LCHAR canonical[P_PATH_MAX]; ESR_ReturnCode rc; if (isAbsolute == NULL) { rc = ESR_INVALID_ARGUMENT; PLogError(ESR_rc2str(rc)); goto CLEANUP; } LSTRCPY(canonical, path); CHKLOG(rc, PFileSystemCanonicalSlashes(canonical)); *isAbsolute = (canonical[0] == '/' || (LISALPHA(canonical[0]) && canonical[1] == ':' && canonical[2] == '/')); return ESR_SUCCESS; CLEANUP: return rc; } ESR_ReturnCode PFileSystemGetAbsolutePath(LCHAR* path, size_t* len) { ESR_ReturnCode rc; #define MAX_PATH_TOKENS 20 LCHAR** tokens = NULL; size_t tokenLen = 0, i; ESR_BOOL isAbsolute; if (path == NULL || len == NULL) { rc = ESR_INVALID_ARGUMENT; PLogError(ESR_rc2str(rc)); goto CLEANUP; } CHKLOG(rc, PFileSystemIsAbsolutePath(path, &isAbsolute)); /* Prefix relative paths with the current working directory */ if (!isAbsolute) { LCHAR cwd[P_PATH_MAX]; size_t len2; len2 = P_PATH_MAX; CHKLOG(rc, PFileSystemGetcwd(cwd, &len2)); len2 = *len; CHKLOG(rc, lstrinsert(cwd, path, 0, &len2)); } CHKLOG(rc, PFileSystemCanonicalSlashes(path)); tokenLen = MAX_PATH_TOKENS; CHKLOG(rc, PFileSystemLinearToPathTokens(path, &tokens, &tokenLen)); LSTRCPY(path, L("")); for (i = 0; i < tokenLen; ++i) { if (LSTRCMP(tokens[i], L("../")) == 0) { size_t len2; len2 = *len; passert(path[LSTRLEN(path)-1] == L('/')); CHKLOG(rc, PFileSystemGetParentDirectory(path, &len2)); } else if (LSTRCMP(tokens[i], L("./")) == 0) { if (i > 0) { /* do nothing */ } else { LSTRCPY(path, L("./")); } } else LSTRCAT(path, tokens[i]); FREE(tokens[i]); tokens[i] = NULL; } FREE(tokens); return ESR_SUCCESS; CLEANUP: if (tokens != NULL) { for (i = 0; i < tokenLen; ++i) { FREE(tokens[i]); tokens[i] = NULL; } } return rc; } ESR_ReturnCode PFileSystemIsCreated(ESR_BOOL* isCreated) { ESR_ReturnCode rc; if (isCreated == NULL) { rc = ESR_INVALID_ARGUMENT; PLogError(ESR_rc2str(rc)); goto CLEANUP; } *isCreated = PFileSystemCreated; return ESR_SUCCESS; CLEANUP: return rc; } /** * Given a path, returns the associated file-system and relative path. * * @param path Path to look up * @param fs [out] File-system which matches the path * @param relativePath [out] Relative path associated with match (should have length P_PATH_MAX) */ ESR_ReturnCode PFileSystemGetFS(const LCHAR* path, PFileSystem** fileSystem, LCHAR* relativePath) { ESR_ReturnCode rc; PHashTableEntry* entry; LCHAR* key; PFileSystem* value; LCHAR* bestKey = NULL; PFileSystem* bestValue = NULL; CHKLOG(rc, PHashTableEntryGetFirst(PFileSystemPathMap, &entry)); while (entry != NULL) { CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value)); if (LSTRSTR(path, key) == path) { /* 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("No file-system handles the specified path (%s)"), path); goto CLEANUP; } *fileSystem = bestValue; if (relativePath != NULL) { ESR_BOOL isAbsolute; CHKLOG(rc, PFileSystemIsAbsolutePath(path + LSTRLEN(bestKey), &isAbsolute)); LSTRCPY(relativePath, L("")); if (!isAbsolute) { /* Make sure that the relative path is relative to the root of the file-system */ LSTRCAT(relativePath, L("/")); } LSTRCAT(relativePath, path + LSTRLEN(bestKey)); } return ESR_SUCCESS; CLEANUP: return rc; } ESR_ReturnCode PFileSystemCreatePFile(const LCHAR* filename, ESR_BOOL littleEndian, PFile** self) { ESR_ReturnCode rc; LCHAR absolutePath[P_PATH_MAX]; PFileSystem* fileSystem; size_t len; if (filename == NULL || self == NULL) { rc = ESR_INVALID_ARGUMENT; PLogError(ESR_rc2str(rc)); goto CLEANUP; } LSTRCPY(absolutePath, filename); lstrtrim(absolutePath); len = P_PATH_MAX; CHKLOG(rc, PFileSystemGetAbsolutePath(absolutePath, &len)); CHKLOG(rc, PFileSystemGetFS(absolutePath, &fileSystem, NULL)); rc = fileSystem->createPFile(fileSystem, absolutePath, littleEndian, self); if (rc == ESR_NO_MATCH_ERROR) rc = ESR_OPEN_ERROR; if (rc != ESR_SUCCESS) { PLogError("%s, %s, %s", ESR_rc2str(rc), filename, absolutePath); goto CLEANUP; } return ESR_SUCCESS; CLEANUP: return rc; } ESR_ReturnCode PFileSystemMkdir(const LCHAR* path) { ESR_ReturnCode rc; LCHAR absolutePath[P_PATH_MAX]; PFileSystem* fileSystem; size_t len; if (path == NULL) { rc = ESR_INVALID_ARGUMENT; PLogError(ESR_rc2str(rc)); goto CLEANUP; } LSTRCPY(absolutePath, path); lstrtrim(absolutePath); len = P_PATH_MAX; CHKLOG(rc, PFileSystemGetAbsolutePath(absolutePath, &len)); CHKLOG(rc, PFileSystemGetFS(absolutePath, &fileSystem, NULL)); CHK(rc, fileSystem->mkdir(fileSystem, absolutePath)); return ESR_SUCCESS; CLEANUP: return rc; } ESR_ReturnCode PFileSystemGetcwd(LCHAR* path, size_t* len) { ESR_ReturnCode rc; if (path == NULL || len == NULL) { rc = ESR_INVALID_ARGUMENT; PLogError(ESR_rc2str(rc)); goto CLEANUP; } if (LSTRLEN(PFileSystemCurrentDirectory) + 1 > *len) { rc = ESR_BUFFER_OVERFLOW; *len = LSTRLEN(PFileSystemCurrentDirectory) + 1; PLogError(ESR_rc2str(rc)); goto CLEANUP; } LSTRCPY(path, PFileSystemCurrentDirectory); /* Check function postcondition */ passert(path[LSTRLEN(path)-1] == L('/')); return ESR_SUCCESS; CLEANUP: return rc; } ESR_ReturnCode PFileSystemChdir(const LCHAR* path) { ESR_ReturnCode rc; LCHAR absolutePath[P_PATH_MAX]; PFileSystem* fileSystem; size_t len; if (path == NULL) { rc = ESR_INVALID_ARGUMENT; PLogError(ESR_rc2str(rc)); goto CLEANUP; } LSTRCPY(absolutePath, path); /* Ensure path ends with '/' */ if (absolutePath[LSTRLEN(absolutePath)-1] != L('/')) LSTRCAT(absolutePath, L("/")); lstrtrim(absolutePath); len = P_PATH_MAX; CHKLOG(rc, PFileSystemGetAbsolutePath(absolutePath, &len)); CHKLOG(rc, PFileSystemGetFS(absolutePath, &fileSystem, NULL)); rc = fileSystem->chdir(fileSystem, absolutePath); if (rc != ESR_SUCCESS) { PLogError(ESR_rc2str(rc)); goto CLEANUP; } if (absolutePath[LSTRLEN(absolutePath)-1] != L('/')) LSTRCAT(absolutePath, L("/")); LSTRCPY(PFileSystemCurrentDirectory, absolutePath); return ESR_SUCCESS; CLEANUP: return rc; } /** * PRECONDITION: Directory names must end with '/' */ ESR_ReturnCode PFileSystemGetParentDirectory(LCHAR* path, size_t* len) { LCHAR* lastSlash; LCHAR clone[P_PATH_MAX]; ESR_ReturnCode rc; size_t len2; if (path == NULL || len == NULL) { rc = ESR_INVALID_ARGUMENT; PLogError(ESR_rc2str(rc)); goto CLEANUP; } LSTRCPY(clone, path); lstrtrim(clone); len2 = P_PATH_MAX; CHKLOG(rc, PFileSystemGetAbsolutePath(clone, &len2)); /* 1.0 - Strip filename */ lastSlash = LSTRRCHR(clone, L('/')); if (lastSlash == NULL) { /* path contains only a filename */ LSTRCPY(path, L("../")); return ESR_SUCCESS; } else if (lastSlash < clone + LSTRLEN(clone) - 1) { *(lastSlash + 1) = L('\0'); if (LSTRLEN(clone) > *len) { *len = LSTRLEN(clone); rc = ESR_BUFFER_OVERFLOW; goto CLEANUP; } LSTRCPY(path, clone); *len = LSTRLEN(path); return ESR_SUCCESS; } /* Get parent directory */ if (lastSlash -clone + 2 == 3 && LSTRNCMP(clone, L("../"), 3) == 0) { LSTRCAT(clone, L("../")); if (LSTRLEN(clone) > *len) { *len = LSTRLEN(clone); rc = ESR_BUFFER_OVERFLOW; goto CLEANUP; } LSTRCPY(path, clone); *len = LSTRLEN(path); return ESR_SUCCESS; } if (lastSlash -clone + 1 == 2 && LSTRNCMP(clone, L("./"), 2) == 0) { if (LSTRLEN(L("../")) > *len) { *len = LSTRLEN(L("../")); rc = ESR_BUFFER_OVERFLOW; goto CLEANUP; } LSTRCPY(path, L("../")); *len = LSTRLEN(path); return ESR_SUCCESS; } else if (lastSlash == clone && LSTRNCMP(clone, L("/"), 1) == 0) { rc = ESR_INVALID_ARGUMENT; PLogError(ESR_rc2str(rc)); goto CLEANUP; } *lastSlash = 0; lastSlash = LSTRRCHR(clone, L('/')); if (lastSlash != NULL) { *(lastSlash + 1) = 0; if (LSTRLEN(clone) > *len) { *len = LSTRLEN(clone); rc = ESR_BUFFER_OVERFLOW; goto CLEANUP; } LSTRCPY(path, clone); *len = LSTRLEN(path); } else { LSTRCPY(path, L("")); *len = 0; } return ESR_SUCCESS; CLEANUP: return rc; } ESR_ReturnCode PFileSystemIsDirectoryPath(const LCHAR* path, ESR_BOOL* isDirectory) { LCHAR temp[P_PATH_MAX]; ESR_ReturnCode rc; passert(isDirectory != NULL); LSTRCPY(temp, path); lstrtrim(temp); CHKLOG(rc, PFileSystemCanonicalSlashes(temp)); *isDirectory = temp[LSTRLEN(temp)-1] == '/'; return ESR_SUCCESS; CLEANUP: return rc; }