• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PFileSystemCanonicalSlashes(LCHAR * path)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 
PFileSystemLinearToPathTokens(const LCHAR * path,LCHAR *** tokenArray,size_t * count)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 
PFileSystemIsAbsolutePath(const LCHAR * path,ESR_BOOL * isAbsolute)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 
PFileSystemGetAbsolutePath(LCHAR * path,size_t * len)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 
PFileSystemIsCreated(ESR_BOOL * isCreated)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  */
PFileSystemGetFS(const LCHAR * path,PFileSystem ** fileSystem,LCHAR * relativePath)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 
PFileSystemCreatePFile(const LCHAR * filename,ESR_BOOL littleEndian,PFile ** self)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 
PFileSystemMkdir(const LCHAR * path)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 
PFileSystemGetcwd(LCHAR * path,size_t * len)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 
PFileSystemChdir(const LCHAR * path)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  */
PFileSystemGetParentDirectory(LCHAR * path,size_t * len)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 
PFileSystemIsDirectoryPath(const LCHAR * path,ESR_BOOL * isDirectory)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