CirclingWind: eliminate attributes min_vector, max_vector
[xcsoar.git] / src / LocalPath.cpp
blob22fe81cbe99af15f94c1e3e59741ffbee2c22089
1 /*
2 Copyright_License {
4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "LocalPath.hpp"
25 #include "Compatibility/path.h"
26 #include "Util/StringUtil.hpp"
27 #include "Asset.hpp"
29 #include "OS/FileUtil.hpp"
31 #ifdef ANDROID
32 #include "Android/Environment.hpp"
33 #endif
35 #ifdef WIN32
36 #include "OS/PathName.hpp"
37 #endif
39 #include <assert.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <windef.h> // for MAX_PATH
43 #ifdef WIN32
44 #ifdef HAVE_POSIX
45 #include <windows.h>
46 #else
47 #include <shlobj.h>
48 #endif
49 #endif
51 #ifdef _WIN32_WCE
52 #include "OS/FlashCardEnumerator.hpp"
53 #endif
55 #ifdef ANDROID
56 #include <android/log.h>
57 #include <sys/stat.h>
58 #include <unistd.h>
59 #endif
61 #define XCSDATADIR "XCSoarData"
63 /**
64 * The default mount point of the SD card on Android.
66 #define ANDROID_SDCARD "/sdcard"
68 /**
69 * On the Samsung Galaxy Tab, the "external" SD card is mounted here.
70 * Shame on the Samsung engineers, they didn't implement
71 * Environment.getExternalStorageDirectory() properly.
73 #define ANDROID_SAMSUNG_EXTERNAL_SD "/sdcard/external_sd"
75 /**
76 * The absolute location of the XCSoarData directory.
78 static TCHAR *gcc_restrict data_path;
79 static size_t data_path_length;
81 const TCHAR *
82 GetPrimaryDataPath()
84 assert(data_path != NULL);
86 return data_path;
89 void
90 SetPrimaryDataPath(const TCHAR *path)
92 assert(path != NULL);
93 assert(!StringIsEmpty(path));
95 free(data_path);
96 data_path = _tcsdup(path);
97 data_path_length = _tcslen(data_path);
100 void
101 LocalPath(TCHAR *gcc_restrict buffer, const TCHAR *gcc_restrict file)
103 assert(data_path != NULL);
105 memcpy(buffer, data_path, data_path_length * sizeof(data_path[0]));
106 buffer[data_path_length] = _T(DIR_SEPARATOR);
107 _tcscpy(buffer + data_path_length + 1, file);
110 TCHAR *
111 LocalPath(TCHAR *gcc_restrict buffer, const TCHAR *gcc_restrict subdir,
112 const TCHAR *gcc_restrict name)
114 assert(data_path != NULL);
115 assert(subdir != NULL);
116 assert(!StringIsEmpty(subdir));
117 assert(name != NULL);
118 assert(!StringIsEmpty(name));
120 memcpy(buffer, data_path, data_path_length * sizeof(data_path[0]));
121 buffer[data_path_length] = _T(DIR_SEPARATOR);
122 _tcscpy(buffer + data_path_length + 1, subdir);
123 _tcscat(buffer + data_path_length + 1, _T(DIR_SEPARATOR_S));
124 _tcscat(buffer + data_path_length + 1, name);
126 return buffer;
129 const TCHAR *
130 RelativePath(const TCHAR *path)
132 assert(data_path != NULL);
134 const TCHAR *p = StringAfterPrefix(path, data_path);
135 return p != NULL && IsDirSeparator(*p)
136 ? p + 1
137 : NULL;
141 * Convert backslashes to slashes on platforms where it matters.
142 * @param p Pointer to the string to normalize
144 static void
145 NormalizeBackslashes(TCHAR *p)
147 #if !defined(_WIN32) || defined(__WINE__)
148 while ((p = _tcschr(p, '\\')) != NULL)
149 *p++ = '/';
150 #endif
153 static constexpr TCHAR local_path_code[] = _T("%LOCAL_PATH%\\");
155 void
156 ExpandLocalPath(TCHAR *dest, const TCHAR *src)
158 // Get the relative file name and location (ptr)
159 const TCHAR *ptr = StringAfterPrefix(src, local_path_code);
160 if (ptr == NULL) {
161 _tcscpy(dest, src);
162 return;
165 while (*ptr == _T('/') || *ptr == _T('\\'))
166 ++ptr;
168 if (StringIsEmpty(ptr))
169 return;
171 // Replace the code "%LOCAL_PATH%\\" by the full local path (output)
172 LocalPath(dest, ptr);
174 // Normalize the backslashes (if necessary)
175 NormalizeBackslashes(dest);
178 void
179 ContractLocalPath(TCHAR* filein)
181 TCHAR output[MAX_PATH];
183 // Get the relative file name and location (ptr)
184 const TCHAR *relative = RelativePath(filein);
185 if (relative == NULL)
186 return;
188 // Replace the full local path by the code "%LOCAL_PATH%\\" (output)
189 _stprintf(output, _T("%s%s"), local_path_code, relative);
190 // ... and copy it to the buffer (filein)
191 _tcscpy(filein, output);
194 #ifdef WIN32
197 * Find a XCSoarData folder in the same location as the executable.
199 static const TCHAR *
200 FindDataPathAtModule(HMODULE hModule, TCHAR *buffer)
202 if (GetModuleFileName(hModule, buffer, MAX_PATH) <= 0)
203 return NULL;
205 ReplaceBaseName(buffer, _T(XCSDATADIR));
206 return Directory::Exists(buffer)
207 ? buffer
208 : NULL;
211 #endif
213 #ifdef _WIN32_WCE
215 static bool
216 InFlashNamed(const TCHAR *path, const TCHAR *name)
218 size_t name_length = _tcslen(name);
220 return IsDirSeparator(path[0]) &&
221 memcmp(path + 1, name, name_length * sizeof(name[0])) == 0 &&
222 IsDirSeparator(path[1 + name_length]);
226 * Determine whether the specified path is on a flash disk. If yes,
227 * it returns the absolute root path of the disk.
229 static const TCHAR *
230 InFlash(const TCHAR *path, TCHAR *buffer)
232 assert(path != NULL);
233 assert(buffer != NULL);
235 FlashCardEnumerator enumerator;
236 const TCHAR *name;
237 while ((name = enumerator.Next()) != NULL) {
238 if (InFlashNamed(path, name)) {
239 buffer[0] = DIR_SEPARATOR;
240 _stprintf(buffer, _T(DIR_SEPARATOR_S"%s"), name);
241 return buffer;
245 return NULL;
248 static const TCHAR *
249 ModuleInFlash(HMODULE hModule, TCHAR *buffer)
251 if (GetModuleFileName(hModule, buffer, MAX_PATH) <= 0)
252 return NULL;
254 return InFlash(buffer, buffer);
258 * Looks for a directory called "XCSoarData" on all flash disks.
260 static const TCHAR *
261 ExistingDataOnFlash(TCHAR *buffer)
263 assert(buffer != NULL);
265 FlashCardEnumerator enumerator;
266 const TCHAR *name;
267 while ((name = enumerator.Next()) != NULL) {
268 _stprintf(buffer, _T(DIR_SEPARATOR_S "%s" DIR_SEPARATOR_S XCSDATADIR), name);
269 if (Directory::Exists(buffer))
270 return buffer;
273 return NULL;
276 #elif defined(WIN32)
278 static const TCHAR *
279 ModuleInFlash(HMODULE module, TCHAR *buffer)
281 if (GetModuleFileName(module, buffer, MAX_PATH) <= 0)
282 return NULL;
284 // At least "C:\"
285 if (_tcslen(buffer) < 3 ||
286 buffer[1] != _T(':') ||
287 buffer[2] != _T('\\'))
288 return NULL;
290 // Trim the module path to the drive letter plus colon
291 buffer[2] = _T('\0');
292 return buffer;
295 #endif
297 #ifdef ANDROID
300 * Determine whether a text file contains a given string
302 * If two strings are given, the second string is considered
303 * as no-match for the given line (i.e. string1 AND !string2).
305 static bool
306 fgrep(const char *fname, const char *string, const char *string2 = NULL)
308 char line[100];
309 FILE *fp;
311 if ((fp = fopen(fname, "r")) == NULL)
312 return false;
313 while (fgets(line, sizeof(line), fp) != NULL)
314 if (strstr(line, string) != NULL &&
315 (string2 == NULL || strstr(line, string2) == NULL)) {
316 fclose(fp);
317 return true;
319 fclose(fp);
320 return false;
323 #endif /* ANDROID */
326 * Returns the location of XCSoarData in the user's home directory.
328 * @param create true creates the path if it does not exist
329 * @return a buffer which may be used to build the path
331 static const TCHAR *
332 GetHomeDataPath(TCHAR *gcc_restrict buffer, bool create=false)
334 if (IsAndroid())
335 /* hard-coded path for Android */
336 return NULL;
338 #ifdef HAVE_POSIX
339 /* on Unix or WINE, use ~/.xcsoar */
340 const TCHAR *home = getenv("HOME");
341 if (home != NULL) {
342 _tcscpy(buffer, home);
343 #ifdef __APPLE__
344 /* Mac OS X users are not used to dot-files in their home
345 directory - make it a little bit easier for them to find the
346 files */
347 _tcscat(buffer, _T("/XCSoarData"));
348 #else
349 _tcscat(buffer, _T("/.xcsoar"));
350 #endif
351 return buffer;
352 } else
353 return _T("/etc/xcsoar");
354 #else
355 if (IsWindowsCE())
356 /* clear the buffer, just in case we evaluate it after
357 SHGetSpecialFolderPath() failure, see below */
358 buffer[0] = _T('\0');
360 bool success = SHGetSpecialFolderPath(NULL, buffer, CSIDL_PERSONAL, create);
361 if (IsWindowsCE() && !success && !StringIsEmpty(buffer))
362 /* MSDN: "If you are using the AYGShell extensions, then this
363 function returns FALSE even if successful. If the folder
364 represented by the CSIDL does not exist and is not created, a
365 NULL string is returned indicating that the directory does not
366 exist." */
367 success = true;
368 if (!success)
369 return NULL;
371 _tcscat(buffer, _T(DIR_SEPARATOR_S));
372 _tcscat(buffer, _T(XCSDATADIR));
373 return buffer;
374 #endif
377 static TCHAR *
378 FindDataPath()
380 if (IsAltair() && IsEmbedded()) {
381 /* if XCSoarData exists on USB drive, use that, because the
382 internal memory is extremely small */
383 const TCHAR *usb = _T("\\USB HD\\" XCSDATADIR);
384 if (Directory::Exists(usb))
385 return _tcsdup(usb);
387 /* hard-coded path for Altair */
388 return _tcsdup(_T("\\NOR Flash"));
391 #ifdef WIN32
393 TCHAR buffer[MAX_PATH];
394 const TCHAR *path = FindDataPathAtModule(NULL, buffer);
395 if (path != NULL)
396 return _tcsdup(path);
398 #endif
400 if (IsAndroid()) {
401 #ifdef ANDROID
402 /* hack for Samsung Galaxy S and Samsung Galaxy Tab (which has a
403 build-in and an external SD card) */
404 struct stat st;
405 if (stat(ANDROID_SAMSUNG_EXTERNAL_SD, &st) == 0 &&
406 (st.st_mode & S_IFDIR) != 0 &&
407 fgrep("/proc/mounts", ANDROID_SAMSUNG_EXTERNAL_SD " ", "tmpfs ")) {
408 __android_log_print(ANDROID_LOG_DEBUG, "XCSoar",
409 "Enable Samsung hack, " XCSDATADIR " in "
410 ANDROID_SAMSUNG_EXTERNAL_SD);
411 return strdup(ANDROID_SAMSUNG_EXTERNAL_SD "/" XCSDATADIR);
414 /* try Context.getExternalStoragePublicDirectory() */
415 char buffer[MAX_PATH];
416 if (Environment::getExternalStoragePublicDirectory(buffer, sizeof(buffer),
417 "XCSoarData") != NULL) {
418 __android_log_print(ANDROID_LOG_DEBUG, "XCSoar",
419 "Environment.getExternalStoragePublicDirectory()='%s'",
420 buffer);
421 return strdup(buffer);
424 /* now try Context.getExternalStorageDirectory(), because
425 getExternalStoragePublicDirectory() needs API level 8 */
426 if (Environment::getExternalStorageDirectory(buffer,
427 sizeof(buffer) - 32) != NULL) {
428 __android_log_print(ANDROID_LOG_DEBUG, "XCSoar",
429 "Environment.getExternalStorageDirectory()='%s'",
430 buffer);
432 strcat(buffer, "/" XCSDATADIR);
433 return strdup(buffer);
436 /* hard-coded path for Android */
437 __android_log_print(ANDROID_LOG_DEBUG, "XCSoar",
438 "Fallback " XCSDATADIR " in " ANDROID_SDCARD);
439 #endif
440 return _tcsdup(_T(ANDROID_SDCARD "/" XCSDATADIR));
443 #ifdef WIN32
444 /* if XCSoar was started from a flash disk, put the XCSoarData onto
445 it, too */
447 TCHAR buffer[MAX_PATH];
448 if (ModuleInFlash(NULL, buffer) != NULL) {
449 _tcscat(buffer, _T(DIR_SEPARATOR_S));
450 _tcscat(buffer, _T(XCSDATADIR));
451 if (Directory::Exists(buffer))
452 return _tcsdup(buffer);
455 #ifdef _WIN32_WCE
456 /* if a flash disk with XCSoarData exists, use it */
457 if (ExistingDataOnFlash(buffer) != NULL)
458 return _tcsdup(buffer);
459 #endif
461 #endif
464 TCHAR buffer[MAX_PATH];
465 const TCHAR *path = GetHomeDataPath(buffer, true);
466 if (path != NULL)
467 return _tcsdup(path);
470 return NULL;
473 void
474 VisitDataFiles(const TCHAR* filter, File::Visitor &visitor)
476 const TCHAR *data_path = GetPrimaryDataPath();
477 Directory::VisitSpecificFiles(data_path, filter, visitor, true);
480 TCHAR buffer[MAX_PATH];
481 const TCHAR *home_path = GetHomeDataPath(buffer);
482 if (home_path != NULL && _tcscmp(data_path, home_path) != 0)
483 Directory::VisitSpecificFiles(home_path, filter, visitor, true);
486 #if defined(_WIN32_WCE) && !defined(GNAV)
487 TCHAR flash_path[MAX_PATH];
488 FlashCardEnumerator enumerator;
489 const TCHAR *flash_name;
490 while ((flash_name = enumerator.Next()) != NULL) {
491 _stprintf(flash_path, _T(DIR_SEPARATOR_S "%s" DIR_SEPARATOR_S XCSDATADIR),
492 flash_name);
493 if (_tcscmp(data_path, flash_path) == 0)
494 /* don't scan primary data path twice */
495 continue;
497 Directory::VisitSpecificFiles(flash_path, filter, visitor, true);
499 #endif /* _WIN32_WCE && !GNAV*/
502 #ifdef ANDROID
504 * Resolve all symlinks in the specified (allocated) string, and
505 * returns a newly allocated string. The specified string is freed by
506 * this function.
508 static char *
509 RealPath(char *path)
511 char buffer[4096];
512 char *result = realpath(path, buffer);
513 if (result == NULL)
514 return path;
516 free(path);
517 return strdup(result);
519 #endif
521 bool
522 InitialiseDataPath()
524 data_path = FindDataPath();
525 if (data_path == NULL)
526 return false;
528 #ifdef ANDROID
529 /* on some Android devices, /sdcard or /sdcard/external_sd are
530 symlinks, and on some devices (Samsung phones), the Android
531 DownloadManager does not allow destination paths pointing inside
532 these symlinks; to avoid problems with this restriction, all
533 symlinks on the way must be resolved by RealPath(): */
534 data_path = RealPath(data_path);
535 #endif
537 data_path_length = _tcslen(data_path);
538 return true;
541 void
542 DeinitialiseDataPath()
544 free(data_path);
547 void
548 CreateDataPath()
550 Directory::Create(GetPrimaryDataPath());