1/*---------------------------------------------------------------------------*
2 *  PANSIFileSystemImpl.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
21#include "LCHAR.h"
22#include "PFileSystemImpl.h"
23#include "PANSIFileSystemImpl.h"
24#include "PANSIFileImpl.h"
25#include "plog.h"
26#include "pmemory.h"
27
28//extern PFileSystem* PANSIFileSystemSingleton;
29PFileSystem* PANSIFileSystemSingleton = (PFileSystem*)NULL;
30
31#define MTAG NULL
32
33
34#ifdef USE_THREAD
35/* Prototype of private function */
36PORTABLE_API ESR_ReturnCode PtrdFlush();
37#endif
38
39/**
40 * [file path, PFileSystem*] mapping.
41 */
42extern PHashTable* PFileSystemPathMap;
43
44
45ESR_ReturnCode PANSIFileSystemCreate(void)
46{
47  PANSIFileSystemImpl* impl;
48  ESR_ReturnCode rc;
49
50  if (PANSIFileSystemSingleton != NULL)
51    return ESR_SUCCESS;
52  impl = NEW(PANSIFileSystemImpl, MTAG);
53  if (impl == NULL)
54    return ESR_OUT_OF_MEMORY;
55  impl->super.super.destroy = &PANSIFileSystemDestroyImpl;
56  impl->super.super.createPFile = &PANSIFileSystemCreatePFileImpl;
57  impl->super.addPath = &PANSIFileSystemAddPathImpl;
58  impl->super.removePath = &PANSIFileSystemRemovePathImpl;
59  impl->super.getcwd = &PANSIFileSystemGetcwdImpl;
60  impl->super.super.mkdir = &PANSIFileSystemMkdirImpl;
61  impl->super.super.chdir = &PANSIFileSystemChdirImpl;
62
63  CHKLOG(rc, PHashTableCreate(NULL, MTAG, &impl->directoryMap));
64  PANSIFileSystemSingleton = &impl->super.super;
65  return ESR_SUCCESS;
66CLEANUP:
67  return rc;
68}
69
70ESR_ReturnCode PANSIFileSystemDestroyImpl(PFileSystem* self)
71{
72  PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) self;
73  PHashTableEntry* entry;
74  PHashTableEntry* oldEntry;
75  LCHAR* key;
76  LCHAR* value;
77  ESR_ReturnCode rc;
78
79  if (impl->directoryMap != NULL)
80  {
81    CHKLOG(rc, PHashTableEntryGetFirst(impl->directoryMap, &entry));
82    while (entry != NULL)
83    {
84      CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
85      oldEntry = entry;
86      CHKLOG(rc, PHashTableEntryAdvance(&entry));
87      CHKLOG(rc, PHashTableEntryRemove(oldEntry));
88      CHKLOG(rc, PHashTableRemoveValue(PFileSystemPathMap, key, NULL));
89      FREE(key);
90      FREE(value);
91    }
92    CHKLOG(rc, PHashTableDestroy(impl->directoryMap));
93    impl->directoryMap = NULL;
94  }
95  FREE(self);
96  return ESR_SUCCESS;
97CLEANUP:
98  return rc;
99}
100
101ESR_ReturnCode PANSIFileSystemAddPathImpl(PFileSystem* self, const LCHAR* virtualPath, const LCHAR* realPath)
102{
103  PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) self;
104  ESR_BOOL exists;
105  LCHAR* key = NULL;
106  LCHAR* value = NULL;
107  ESR_ReturnCode rc;
108  size_t len;
109
110  if (virtualPath == NULL || realPath == NULL)
111  {
112    rc = ESR_INVALID_ARGUMENT;
113    PLogError(ESR_rc2str(rc));
114    goto CLEANUP;
115  }
116
117  len = LSTRLEN(virtualPath) + 1;
118  if (virtualPath[LSTRLEN(virtualPath)-1] != L('/'))
119    ++len;
120  key = MALLOC(sizeof(LCHAR) * len, MTAG);
121  if (key == NULL)
122  {
123    rc = ESR_OUT_OF_MEMORY;
124    PLogError(ESR_rc2str(rc));
125    goto CLEANUP;
126  }
127  LSTRCPY(key, virtualPath);
128  /* Make sure paths end with '/' */
129  CHKLOG(rc, PFileSystemCanonicalSlashes(key));
130  if (key[LSTRLEN(key)-1] != L('/'))
131    LSTRCAT(key, L("/"));
132  value = MALLOC(sizeof(LCHAR) * (LSTRLEN(realPath) + 1), MTAG);
133  if (value == NULL)
134  {
135    rc = ESR_OUT_OF_MEMORY;
136    PLogError(ESR_rc2str(rc));
137    goto CLEANUP;
138  }
139  LSTRCPY(value, realPath);
140
141  /* Make sure realPath is not an empty string */
142  lstrtrim(value);
143  if (LSTRLEN(value) == 0)
144  {
145    FREE(value);
146    value = NULL;
147    rc = ESR_INVALID_ARGUMENT;
148    PLogError(L("%s: realPath cannot be empty"), ESR_rc2str(rc));
149    goto CLEANUP;
150  }
151
152  /* Make sure paths end with '/' */
153  CHKLOG(rc, PFileSystemCanonicalSlashes(value));
154  if (value[LSTRLEN(value)-1] != L('/'))
155    LSTRCAT(value, L("/"));
156
157  CHKLOG(rc, PHashTableContainsKey(impl->directoryMap, key, &exists));
158  if (exists)
159  {
160    LCHAR* oldValue;
161
162    CHKLOG(rc, PHashTableGetValue(impl->directoryMap, key, (void **)&oldValue));
163    if (LSTRCMP(oldValue, value) != 0)
164    {
165      rc = ESR_IDENTIFIER_COLLISION;
166      PLogError(ESR_rc2str(rc));
167      goto CLEANUP;
168    }
169  }
170  CHKLOG(rc, PHashTablePutValue(impl->directoryMap, key, value, NULL));
171  CHKLOG(rc, PHashTablePutValue(PFileSystemPathMap, key, self, NULL));
172  return ESR_SUCCESS;
173CLEANUP:
174  FREE(key);
175  FREE(value);
176  return rc;
177}
178
179ESR_ReturnCode PANSIFileSystemRemovePathImpl(PFileSystem* self, const LCHAR* virtualPath)
180{
181  PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) self;
182  LCHAR path[P_PATH_MAX];
183  LCHAR* key;
184  LCHAR* value;
185  PHashTableEntry* entry;
186  ESR_ReturnCode rc;
187
188  if (virtualPath == NULL)
189  {
190    rc = ESR_INVALID_ARGUMENT;
191    PLogError(ESR_rc2str(rc));
192    goto CLEANUP;
193  }
194  /* Make sure paths end with '/' */
195  LSTRCPY(path, virtualPath);
196  CHKLOG(rc, PFileSystemCanonicalSlashes(path));
197  if (path[LSTRLEN(path)-1] != L('/'))
198    LSTRCAT(path, L("/"));
199  CHKLOG(rc, PHashTableGetEntry(impl->directoryMap, path, &entry));
200  CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
201  CHKLOG(rc, PHashTableEntryRemove(entry));
202  CHKLOG(rc, PHashTableRemoveValue(PFileSystemPathMap, key, NULL));
203  FREE(key);
204  FREE(value);
205  return ESR_SUCCESS;
206CLEANUP:
207  return rc;
208}
209
210ESR_ReturnCode PANSIFileSystemGetRealPathImpl(PFileSystem* self, LCHAR* path, size_t* len)
211{
212  PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) self;
213  PHashTableEntry* entry;
214  LCHAR* key;
215  LCHAR* value;
216  LCHAR* bestKey = NULL;
217  LCHAR* bestValue = NULL;
218  ESR_BOOL isAbsolute;
219  ESR_ReturnCode rc;
220
221  CHKLOG(rc, PFileSystemGetAbsolutePath(path, len));
222  CHKLOG(rc, PHashTableEntryGetFirst(impl->directoryMap, &entry));
223  while (entry != NULL)
224  {
225    CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void**)&key, (void**)&value));
226    if (LSTRNCMP(path, key, LSTRLEN(key)) == 0)
227    {
228      /* File-system handles file path */
229      if (bestKey == NULL || LSTRLEN(key) > LSTRLEN(bestKey))
230      {
231        /* Found a better match -- the new key is a subdirectory of the previous bestKey */
232        bestKey = key;
233        bestValue = value;
234      }
235    }
236    CHKLOG(rc, PHashTableEntryAdvance(&entry));
237  }
238  if (bestKey == NULL)
239  {
240    rc = ESR_INVALID_STATE;
241    PLogError(L("PANSIFileSystem does not handle the specified path: %s"), path);
242    goto CLEANUP;
243  }
244
245  if (LSTRLEN(bestValue) + 1 > *len)
246  {
247    *len = LSTRLEN(bestValue) + 1;
248    rc = ESR_BUFFER_OVERFLOW;
249    PLogError(ESR_rc2str(rc));
250    goto CLEANUP;
251  }
252  /* Delete the virtual-path */
253  LSTRCPY(path, path + LSTRLEN(bestKey));
254
255  CHKLOG(rc, PFileSystemIsAbsolutePath(path, &isAbsolute));
256  if (LSTRCMP(bestValue, L("/")) == 0 && isAbsolute)
257  {
258    /* do nothing */
259  }
260  else
261  {
262    /* Insert the key-path */
263    CHKLOG(rc, lstrinsert(bestValue, path, 0, len));
264  }
265  return ESR_SUCCESS;
266CLEANUP:
267  return rc;
268}
269
270ESR_ReturnCode PANSIFileSystemCreatePFileImpl(PFileSystem* self, const LCHAR* path, ESR_BOOL littleEndian, PFile** file)
271{
272  LCHAR realPath[P_PATH_MAX];
273  size_t len;
274  ESR_ReturnCode rc;
275
276  LSTRCPY(realPath, path);
277  len = P_PATH_MAX;
278  CHKLOG(rc, PANSIFileSystemGetRealPathImpl(self, realPath, &len));
279  return PANSIFileCreateImpl(realPath, littleEndian, file);
280CLEANUP:
281  return rc;
282}
283
284ESR_ReturnCode PANSIFileSystemSetDefault(ESR_BOOL isDefault)
285{
286  PANSIFileSystemImpl* impl = (PANSIFileSystemImpl*) PANSIFileSystemSingleton;
287  ESR_BOOL exists;
288  LCHAR* key = NULL;
289  LCHAR* value = NULL;
290  PHashTableEntry* entry;
291  ESR_ReturnCode rc;
292
293  if (isDefault)
294  {
295
296		key = MALLOC(sizeof(LCHAR), MTAG);
297    if (key == NULL)
298    {
299      rc = ESR_OUT_OF_MEMORY;
300      PLogError(ESR_rc2str(rc));
301      goto CLEANUP;
302    }
303    LSTRCPY(key, L(""));
304    value = MALLOC(sizeof(LCHAR), MTAG);
305    if (value == NULL)
306    {
307      rc = ESR_OUT_OF_MEMORY;
308      PLogError(ESR_rc2str(rc));
309      goto CLEANUP;
310    }
311    LSTRCPY(value, L(""));
312
313    CHKLOG(rc, PHashTableContainsKey(impl->directoryMap, key, &exists));
314    if (exists)
315    {
316      LCHAR* key;
317      LCHAR* value;
318
319      CHKLOG(rc, PHashTableGetEntry(impl->directoryMap, L(""), &entry));
320      CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
321      CHKLOG(rc, PHashTableEntryRemove(entry));
322      CHKLOG(rc, PHashTableRemoveValue(PFileSystemPathMap, key, NULL));
323      FREE(key);
324      FREE(value);
325    }
326    CHKLOG(rc, PHashTablePutValue(impl->directoryMap, key, value, NULL));
327    CHKLOG(rc, PHashTablePutValue(PFileSystemPathMap, key, PANSIFileSystemSingleton, NULL));
328
329		/* Set virtual current working directory to native current working directory */
330  }
331  else
332  {
333    CHKLOG(rc, PHashTableContainsKey(impl->directoryMap, L(""), &exists));
334    if (exists)
335    {
336      LCHAR* key;
337      LCHAR* value;
338
339      CHKLOG(rc, PHashTableGetEntry(impl->directoryMap, L(""), &entry));
340      CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
341
342      CHKLOG(rc, PHashTableContainsKey(PFileSystemPathMap, L(""), &exists));
343      if (exists)
344      {
345        LCHAR* key;
346        PFileSystem* value;
347        PHashTableEntry* entry;
348
349        CHKLOG(rc, PHashTableGetEntry(PFileSystemPathMap, L(""), &entry));
350        CHKLOG(rc, PHashTableEntryGetKeyValue(entry, (void **)&key, (void **)&value));
351        if (value == PANSIFileSystemSingleton)
352          CHKLOG(rc, PHashTableEntryRemove(entry));
353      }
354
355      CHKLOG(rc, PHashTableEntryRemove(entry));
356      FREE(key);
357      FREE(value);
358    }
359  }
360  return ESR_SUCCESS;
361CLEANUP:
362  FREE(key);
363  FREE(value);
364  return rc;
365}
366