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