Home | History | Annotate | Download | only in src
      1 /*---------------------------------------------------------------------------*
      2  *  PFileSystem.c  *
      3  *                                                                           *
      4  *  Copyright 2007, 2008 Nuance Communciations, Inc.                               *
      5  *                                                                           *
      6  *  Licensed under the Apache License, Version 2.0 (the 'License');          *
      7  *  you may not use this file except in compliance with the License.         *
      8  *                                                                           *
      9  *  You may obtain a copy of the License at                                  *
     10  *      http://www.apache.org/licenses/LICENSE-2.0                           *
     11  *                                                                           *
     12  *  Unless required by applicable law or agreed to in writing, software      *
     13  *  distributed under the License is distributed on an 'AS IS' BASIS,        *
     14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
     15  *  See the License for the specific language governing permissions and      *
     16  *  limitations under the License.                                           *
     17  *                                                                           *
     18  *---------------------------------------------------------------------------*/
     19 
     20 #include "ArrayList.h"
     21 #include "LCHAR.h"
     22 #include "PFileSystem.h"
     23 #include "PFileSystemImpl.h"
     24 #include "phashtable.h"
     25 #include "plog.h"
     26 #include "pmemory.h"
     27 
     28 
     29 #define MTAG NULL
     30 
     31 /**
     32  * Indicates if PFileSystem is initialized.
     33  */
     34 extern ESR_BOOL PFileSystemCreated;
     35 
     36 /**
     37  * [file path, PFileSystem*] mapping.
     38  */
     39 extern PHashTable* PFileSystemPathMap;
     40 
     41 /**
     42  * Current working directory.
     43  */
     44 extern LCHAR PFileSystemCurrentDirectory[P_PATH_MAX];
     45 
     46 PORTABLE_API ESR_ReturnCode PFileSystemCanonicalSlashes(LCHAR* path)
     47 {
     48   ESR_ReturnCode rc;
     49 
     50   if (path == NULL)
     51   {
     52     rc = ESR_INVALID_ARGUMENT;
     53     PLogError(ESR_rc2str(rc));
     54     goto CLEANUP;
     55   }
     56 
     57   lstrtrim(path);
     58   CHKLOG(rc, lstrreplace(path, L('\\'), L('/')));
     59   return ESR_SUCCESS;
     60 CLEANUP:
     61   return rc;
     62 }
     63 
     64 ESR_ReturnCode PFileSystemLinearToPathTokens(const LCHAR* path, LCHAR*** tokenArray, size_t* count)
     65 {
     66   ESR_ReturnCode rc;
     67   const LCHAR* beginning;
     68   const LCHAR* ending;
     69   LCHAR linear[P_PATH_MAX];
     70   ArrayList* arrayList = NULL;
     71   LCHAR* value = NULL;
     72   size_t i;
     73 
     74   if (path == NULL || tokenArray == NULL || count == NULL)
     75   {
     76     rc = ESR_INVALID_ARGUMENT;
     77     PLogError(ESR_rc2str(rc));
     78     goto CLEANUP;
     79   }
     80   LSTRCPY(linear, path);
     81   CHKLOG(rc, PFileSystemCanonicalSlashes(linear));
     82   CHKLOG(rc, ArrayListCreate(&arrayList));
     83   beginning = linear;
     84   while (ESR_TRUE)
     85   {
     86     ending = LSTRCHR(beginning, L('/'));
     87     if (ending == NULL)
     88       ending = beginning + LSTRLEN(beginning);
     89     value = MALLOC(sizeof(LCHAR) * (ending - beginning + 1 + 1), MTAG);
     90     if (value == NULL)
     91     {
     92       rc = ESR_OUT_OF_MEMORY;
     93       PLogError(ESR_rc2str(rc));
     94       goto CLEANUP;
     95     }
     96     LSTRNCPY(value, beginning, ending - beginning + 1);
     97     value[ending-beginning+1] = L('\0');
     98     CHKLOG(rc, lstrtrim(value));
     99     if (LSTRLEN(value) == 0)
    100     {
    101       FREE(value);
    102       value = NULL;
    103     }
    104     else
    105     {
    106       CHKLOG(rc, arrayList->add(arrayList, value));
    107       value = NULL;
    108     }
    109     if (*ending == 0)
    110       break;
    111     beginning = ending + 1;
    112   }
    113 
    114   /* Build static token array */
    115   CHKLOG(rc, arrayList->getSize(arrayList, count));
    116   *tokenArray = MALLOC(*count * sizeof(LCHAR*), MTAG);
    117   if (*tokenArray == NULL)
    118   {
    119     rc = ESR_OUT_OF_MEMORY;
    120     goto CLEANUP;
    121   }
    122   for (i = 0; i < *count; ++i)
    123   {
    124     rc = arrayList->get(arrayList, i, (void**)(&(*tokenArray)[i]));
    125     if (rc != ESR_SUCCESS)
    126       goto CLEANUP;
    127   }
    128   rc = arrayList->destroy(arrayList);
    129   if (rc != ESR_SUCCESS)
    130     goto CLEANUP;
    131   return ESR_SUCCESS;
    132 CLEANUP:
    133   FREE(value);
    134   if (arrayList != NULL)
    135   {
    136     ESR_ReturnCode cleanRC;
    137 
    138     cleanRC = arrayList->getSize(arrayList, count);
    139     if (cleanRC != ESR_SUCCESS)
    140       return rc;
    141     for (i = 0; i < *count; ++i)
    142     {
    143       cleanRC = arrayList->get(arrayList, 0, (void**)&value);
    144       if (cleanRC != ESR_SUCCESS)
    145         return rc;
    146       FREE(value);
    147       cleanRC = arrayList->remove(arrayList, 0);
    148       if (cleanRC != ESR_SUCCESS)
    149         return rc;
    150     }
    151     arrayList->destroy(arrayList);
    152   }
    153   return rc;
    154 }
    155 
    156 ESR_ReturnCode PFileSystemIsAbsolutePath(const LCHAR* path, ESR_BOOL* isAbsolute)
    157 {
    158   LCHAR canonical[P_PATH_MAX];
    159   ESR_ReturnCode rc;
    160 
    161   if (isAbsolute == NULL)
    162   {
    163     rc = ESR_INVALID_ARGUMENT;
    164     PLogError(ESR_rc2str(rc));
    165     goto CLEANUP;
    166   }
    167   LSTRCPY(canonical, path);
    168   CHKLOG(rc, PFileSystemCanonicalSlashes(canonical));
    169 
    170   *isAbsolute = (canonical[0] == '/' ||
    171                  (LISALPHA(canonical[0]) && canonical[1] == ':' && canonical[2] == '/'));
    172   return ESR_SUCCESS;
    173 CLEANUP:
    174   return rc;
    175 }
    176 
    177 ESR_ReturnCode PFileSystemGetAbsolutePath(LCHAR* path, size_t* len)
    178 {
    179   ESR_ReturnCode rc;
    180 #define MAX_PATH_TOKENS 20
    181   LCHAR** tokens = NULL;
    182   size_t tokenLen = 0, i;
    183   ESR_BOOL isAbsolute;
    184 
    185   if (path == NULL || len == NULL)
    186   {
    187     rc = ESR_INVALID_ARGUMENT;
    188     PLogError(ESR_rc2str(rc));
    189     goto CLEANUP;
    190   }
    191   CHKLOG(rc, PFileSystemIsAbsolutePath(path, &isAbsolute));
    192 
    193   /* Prefix relative paths with the current working directory */
    194   if (!isAbsolute)
    195   {
    196     LCHAR cwd[P_PATH_MAX];
    197     size_t len2;
    198 
    199     len2 = P_PATH_MAX;
    200     CHKLOG(rc, PFileSystemGetcwd(cwd, &len2));
    201     len2 = *len;
    202     CHKLOG(rc, lstrinsert(cwd, path, 0, &len2));
    203   }
    204 
    205   CHKLOG(rc, PFileSystemCanonicalSlashes(path));
    206   tokenLen = MAX_PATH_TOKENS;
    207   CHKLOG(rc, PFileSystemLinearToPathTokens(path, &tokens, &tokenLen));
    208 
    209   LSTRCPY(path, L(""));
    210   for (i = 0; i < tokenLen; ++i)
    211   {
    212     if (LSTRCMP(tokens[i], L("../")) == 0)
    213     {
    214       size_t len2;
    215 
    216       len2 = *len;
    217       passert(path[LSTRLEN(path)-1] == L('/'));
    218       CHKLOG(rc, PFileSystemGetParentDirectory(path, &len2));
    219     }
    220     else if (LSTRCMP(tokens[i], L("./")) == 0)
    221     {
    222       if (i > 0)
    223       {
    224         /* do nothing */
    225       }
    226       else
    227       {
    228         LSTRCPY(path, L("./"));
    229       }
    230     }
    231     else
    232       LSTRCAT(path, tokens[i]);
    233     FREE(tokens[i]);
    234     tokens[i] = NULL;
    235   }
    236   FREE(tokens);
    237   return ESR_SUCCESS;
    238 CLEANUP:
    239   if (tokens != NULL)
    240   {
    241     for (i = 0; i < tokenLen; ++i)
    242     {
    243       FREE(tokens[i]);
    244       tokens[i] = NULL;
    245     }
    246   }
    247   return rc;
    248 }
    249 
    250 ESR_ReturnCode PFileSystemIsCreated(ESR_BOOL* isCreated)
    251 {
    252   ESR_ReturnCode rc;
    253 
    254   if (isCreated == NULL)
    255   {
    256     rc = ESR_INVALID_ARGUMENT;
    257     PLogError(ESR_rc2str(rc));
    258     goto CLEANUP;
    259   }
    260   *isCreated = PFileSystemCreated;
    261   return ESR_SUCCESS;
    262 CLEANUP:
    263   return rc;
    264 }
    265 
    266 /**
    267  * Given a path, returns the associated file-system and relative path.
    268  *
    269  * @param path Path to look up
    270  * @param fs [out] File-system which matches the path
    271  * @param relativePath [out] Relative path associated with match (should have length P_PATH_MAX)
    272  */
    273 ESR_ReturnCode PFileSystemGetFS(const LCHAR* path, PFileSystem** fileSystem, LCHAR* relativePath)
    274 {
    275   ESR_ReturnCode rc;
    276   PHashTableEntry* entry;
    277   LCHAR* key;
    278   PFileSystem* value;
    279   LCHAR* bestKey = NULL;
    280   PFileSystem* bestValue = NULL;
    281 
    282   CHKLOG(rc, PHashTableEntryGetFirst(PFileSystemPathMap, &entry));
    283   while (entry != NULL)
    284   {
    285     CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
    286     if (LSTRSTR(path, key) == path)
    287     {
    288       /* File-system handles file path */
    289 
    290       if (bestKey == NULL || LSTRLEN(key) > LSTRLEN(bestKey))
    291       {
    292         /* Found a better match -- the new key is a subdirectory of the previous bestKey */
    293         bestKey = key;
    294         bestValue = value;
    295       }
    296     }
    297     CHKLOG(rc, PHashTableEntryAdvance(&entry));
    298   }
    299   if (bestKey == NULL)
    300   {
    301     rc = ESR_INVALID_STATE;
    302     PLogError(L("No file-system handles the specified path (%s)"), path);
    303     goto CLEANUP;
    304   }
    305   *fileSystem = bestValue;
    306   if (relativePath != NULL)
    307   {
    308     ESR_BOOL isAbsolute;
    309 
    310     CHKLOG(rc, PFileSystemIsAbsolutePath(path + LSTRLEN(bestKey), &isAbsolute));
    311     LSTRCPY(relativePath, L(""));
    312     if (!isAbsolute)
    313     {
    314       /* Make sure that the relative path is relative to the root of the file-system */
    315       LSTRCAT(relativePath, L("/"));
    316     }
    317     LSTRCAT(relativePath, path + LSTRLEN(bestKey));
    318   }
    319   return ESR_SUCCESS;
    320 CLEANUP:
    321   return rc;
    322 }
    323 
    324 ESR_ReturnCode PFileSystemCreatePFile(const LCHAR* filename, ESR_BOOL littleEndian, PFile** self)
    325 {
    326   ESR_ReturnCode rc;
    327   LCHAR absolutePath[P_PATH_MAX];
    328   PFileSystem* fileSystem;
    329   size_t len;
    330 
    331   if (filename == NULL || self == NULL)
    332   {
    333     rc = ESR_INVALID_ARGUMENT;
    334     PLogError(ESR_rc2str(rc));
    335     goto CLEANUP;
    336   }
    337   LSTRCPY(absolutePath, filename);
    338   lstrtrim(absolutePath);
    339   len = P_PATH_MAX;
    340   CHKLOG(rc, PFileSystemGetAbsolutePath(absolutePath, &len));
    341   CHKLOG(rc, PFileSystemGetFS(absolutePath, &fileSystem, NULL));
    342   rc = fileSystem->createPFile(fileSystem, absolutePath, littleEndian, self);
    343   if (rc == ESR_NO_MATCH_ERROR)
    344     rc = ESR_OPEN_ERROR;
    345   if (rc != ESR_SUCCESS)
    346   {
    347     PLogError("%s, %s, %s", ESR_rc2str(rc), filename, absolutePath);
    348     goto CLEANUP;
    349   }
    350   return ESR_SUCCESS;
    351 CLEANUP:
    352   return rc;
    353 }
    354 
    355 ESR_ReturnCode PFileSystemMkdir(const LCHAR* path)
    356 {
    357   ESR_ReturnCode rc;
    358   LCHAR absolutePath[P_PATH_MAX];
    359   PFileSystem* fileSystem;
    360   size_t len;
    361 
    362   if (path == NULL)
    363   {
    364     rc = ESR_INVALID_ARGUMENT;
    365     PLogError(ESR_rc2str(rc));
    366     goto CLEANUP;
    367   }
    368   LSTRCPY(absolutePath, path);
    369   lstrtrim(absolutePath);
    370   len = P_PATH_MAX;
    371   CHKLOG(rc, PFileSystemGetAbsolutePath(absolutePath, &len));
    372   CHKLOG(rc, PFileSystemGetFS(absolutePath, &fileSystem, NULL));
    373   CHK(rc, fileSystem->mkdir(fileSystem, absolutePath));
    374   return ESR_SUCCESS;
    375 CLEANUP:
    376   return rc;
    377 }
    378 
    379 ESR_ReturnCode PFileSystemGetcwd(LCHAR* path, size_t* len)
    380 {
    381   ESR_ReturnCode rc;
    382 
    383   if (path == NULL || len == NULL)
    384   {
    385     rc = ESR_INVALID_ARGUMENT;
    386     PLogError(ESR_rc2str(rc));
    387     goto CLEANUP;
    388   }
    389   if (LSTRLEN(PFileSystemCurrentDirectory) + 1 > *len)
    390   {
    391     rc = ESR_BUFFER_OVERFLOW;
    392     *len = LSTRLEN(PFileSystemCurrentDirectory) + 1;
    393     PLogError(ESR_rc2str(rc));
    394     goto CLEANUP;
    395   }
    396   LSTRCPY(path, PFileSystemCurrentDirectory);
    397   /* Check function postcondition */
    398   passert(path[LSTRLEN(path)-1] == L('/'));
    399   return ESR_SUCCESS;
    400 CLEANUP:
    401   return rc;
    402 }
    403 
    404 ESR_ReturnCode PFileSystemChdir(const LCHAR* path)
    405 {
    406   ESR_ReturnCode rc;
    407   LCHAR absolutePath[P_PATH_MAX];
    408   PFileSystem* fileSystem;
    409   size_t len;
    410 
    411   if (path == NULL)
    412   {
    413     rc = ESR_INVALID_ARGUMENT;
    414     PLogError(ESR_rc2str(rc));
    415     goto CLEANUP;
    416   }
    417   LSTRCPY(absolutePath, path);
    418   /* Ensure path ends with '/' */
    419   if (absolutePath[LSTRLEN(absolutePath)-1] != L('/'))
    420     LSTRCAT(absolutePath, L("/"));
    421   lstrtrim(absolutePath);
    422   len = P_PATH_MAX;
    423   CHKLOG(rc, PFileSystemGetAbsolutePath(absolutePath, &len));
    424   CHKLOG(rc, PFileSystemGetFS(absolutePath, &fileSystem, NULL));
    425   rc = fileSystem->chdir(fileSystem, absolutePath);
    426   if (rc != ESR_SUCCESS)
    427   {
    428     PLogError(ESR_rc2str(rc));
    429     goto CLEANUP;
    430   }
    431   if (absolutePath[LSTRLEN(absolutePath)-1] != L('/'))
    432     LSTRCAT(absolutePath, L("/"));
    433   LSTRCPY(PFileSystemCurrentDirectory, absolutePath);
    434   return ESR_SUCCESS;
    435 CLEANUP:
    436   return rc;
    437 }
    438 
    439 /**
    440  * PRECONDITION: Directory names must end with '/'
    441  */
    442 ESR_ReturnCode PFileSystemGetParentDirectory(LCHAR* path, size_t* len)
    443 {
    444   LCHAR* lastSlash;
    445   LCHAR clone[P_PATH_MAX];
    446   ESR_ReturnCode rc;
    447   size_t len2;
    448 
    449   if (path == NULL || len == NULL)
    450   {
    451     rc = ESR_INVALID_ARGUMENT;
    452     PLogError(ESR_rc2str(rc));
    453     goto CLEANUP;
    454   }
    455   LSTRCPY(clone, path);
    456   lstrtrim(clone);
    457   len2 = P_PATH_MAX;
    458   CHKLOG(rc, PFileSystemGetAbsolutePath(clone, &len2));
    459 
    460   /* 1.0 - Strip filename */
    461   lastSlash = LSTRRCHR(clone, L('/'));
    462   if (lastSlash == NULL)
    463   {
    464     /* path contains only a filename */
    465     LSTRCPY(path, L("../"));
    466     return ESR_SUCCESS;
    467   }
    468   else if (lastSlash < clone + LSTRLEN(clone) - 1)
    469   {
    470 
    471     *(lastSlash + 1) = L('\0');
    472     if (LSTRLEN(clone) > *len)
    473     {
    474       *len = LSTRLEN(clone);
    475       rc = ESR_BUFFER_OVERFLOW;
    476       goto CLEANUP;
    477     }
    478     LSTRCPY(path, clone);
    479     *len = LSTRLEN(path);
    480     return ESR_SUCCESS;
    481   }
    482 
    483   /* Get parent directory */
    484   if (lastSlash -clone + 2 == 3 && LSTRNCMP(clone, L("../"), 3) == 0)
    485   {
    486     LSTRCAT(clone, L("../"));
    487     if (LSTRLEN(clone) > *len)
    488     {
    489       *len = LSTRLEN(clone);
    490       rc = ESR_BUFFER_OVERFLOW;
    491       goto CLEANUP;
    492     }
    493     LSTRCPY(path, clone);
    494     *len = LSTRLEN(path);
    495     return ESR_SUCCESS;
    496   }
    497   if (lastSlash -clone + 1 == 2 && LSTRNCMP(clone, L("./"), 2) == 0)
    498   {
    499     if (LSTRLEN(L("../")) > *len)
    500     {
    501       *len = LSTRLEN(L("../"));
    502       rc = ESR_BUFFER_OVERFLOW;
    503       goto CLEANUP;
    504     }
    505     LSTRCPY(path, L("../"));
    506     *len = LSTRLEN(path);
    507     return ESR_SUCCESS;
    508   }
    509   else if (lastSlash == clone && LSTRNCMP(clone, L("/"), 1) == 0)
    510   {
    511     rc = ESR_INVALID_ARGUMENT;
    512     PLogError(ESR_rc2str(rc));
    513     goto CLEANUP;
    514   }
    515   *lastSlash = 0;
    516   lastSlash = LSTRRCHR(clone, L('/'));
    517   if (lastSlash != NULL)
    518   {
    519     *(lastSlash + 1) = 0;
    520     if (LSTRLEN(clone) > *len)
    521     {
    522       *len = LSTRLEN(clone);
    523       rc = ESR_BUFFER_OVERFLOW;
    524       goto CLEANUP;
    525     }
    526     LSTRCPY(path, clone);
    527     *len = LSTRLEN(path);
    528   }
    529   else
    530   {
    531     LSTRCPY(path, L(""));
    532     *len = 0;
    533   }
    534   return ESR_SUCCESS;
    535 CLEANUP:
    536   return rc;
    537 }
    538 
    539 ESR_ReturnCode PFileSystemIsDirectoryPath(const LCHAR* path, ESR_BOOL* isDirectory)
    540 {
    541   LCHAR temp[P_PATH_MAX];
    542   ESR_ReturnCode rc;
    543 
    544   passert(isDirectory != NULL);
    545   LSTRCPY(temp, path);
    546   lstrtrim(temp);
    547   CHKLOG(rc, PFileSystemCanonicalSlashes(temp));
    548   *isDirectory = temp[LSTRLEN(temp)-1] == '/';
    549   return ESR_SUCCESS;
    550 CLEANUP:
    551   return rc;
    552 }
    553