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"
29 #include "OS/FileUtil.hpp"
32 #include "Android/Environment.hpp"
36 #include "OS/PathName.hpp"
42 #include <windef.h> // for MAX_PATH
52 #include "OS/FlashCardEnumerator.hpp"
56 #include <android/log.h>
61 #define XCSDATADIR "XCSoarData"
64 * The default mount point of the SD card on Android.
66 #define ANDROID_SDCARD "/sdcard"
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"
76 * The absolute location of the XCSoarData directory.
78 static TCHAR
*gcc_restrict data_path
;
79 static size_t data_path_length
;
84 assert(data_path
!= NULL
);
90 SetPrimaryDataPath(const TCHAR
*path
)
93 assert(!StringIsEmpty(path
));
96 data_path
= _tcsdup(path
);
97 data_path_length
= _tcslen(data_path
);
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
);
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
);
130 RelativePath(const TCHAR
*path
)
132 assert(data_path
!= NULL
);
134 const TCHAR
*p
= StringAfterPrefix(path
, data_path
);
135 return p
!= NULL
&& IsDirSeparator(*p
)
141 * Convert backslashes to slashes on platforms where it matters.
142 * @param p Pointer to the string to normalize
145 NormalizeBackslashes(TCHAR
*p
)
147 #if !defined(_WIN32) || defined(__WINE__)
148 while ((p
= _tcschr(p
, '\\')) != NULL
)
153 static constexpr TCHAR local_path_code
[] = _T("%LOCAL_PATH%\\");
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
);
165 while (*ptr
== _T('/') || *ptr
== _T('\\'))
168 if (StringIsEmpty(ptr
))
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
);
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
)
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
);
197 * Find a XCSoarData folder in the same location as the executable.
200 FindDataPathAtModule(HMODULE hModule
, TCHAR
*buffer
)
202 if (GetModuleFileName(hModule
, buffer
, MAX_PATH
) <= 0)
205 ReplaceBaseName(buffer
, _T(XCSDATADIR
));
206 return Directory::Exists(buffer
)
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.
230 InFlash(const TCHAR
*path
, TCHAR
*buffer
)
232 assert(path
!= NULL
);
233 assert(buffer
!= NULL
);
235 FlashCardEnumerator enumerator
;
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
);
249 ModuleInFlash(HMODULE hModule
, TCHAR
*buffer
)
251 if (GetModuleFileName(hModule
, buffer
, MAX_PATH
) <= 0)
254 return InFlash(buffer
, buffer
);
258 * Looks for a directory called "XCSoarData" on all flash disks.
261 ExistingDataOnFlash(TCHAR
*buffer
)
263 assert(buffer
!= NULL
);
265 FlashCardEnumerator enumerator
;
267 while ((name
= enumerator
.Next()) != NULL
) {
268 _stprintf(buffer
, _T(DIR_SEPARATOR_S
"%s" DIR_SEPARATOR_S XCSDATADIR
), name
);
269 if (Directory::Exists(buffer
))
279 ModuleInFlash(HMODULE module
, TCHAR
*buffer
)
281 if (GetModuleFileName(module
, buffer
, MAX_PATH
) <= 0)
285 if (_tcslen(buffer
) < 3 ||
286 buffer
[1] != _T(':') ||
287 buffer
[2] != _T('\\'))
290 // Trim the module path to the drive letter plus colon
291 buffer
[2] = _T('\0');
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).
306 fgrep(const char *fname
, const char *string
, const char *string2
= NULL
)
311 if ((fp
= fopen(fname
, "r")) == NULL
)
313 while (fgets(line
, sizeof(line
), fp
) != NULL
)
314 if (strstr(line
, string
) != NULL
&&
315 (string2
== NULL
|| strstr(line
, string2
) == NULL
)) {
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
332 GetHomeDataPath(TCHAR
*gcc_restrict buffer
, bool create
=false)
335 /* hard-coded path for Android */
339 /* on Unix or WINE, use ~/.xcsoar */
340 const TCHAR
*home
= getenv("HOME");
342 _tcscpy(buffer
, home
);
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
347 _tcscat(buffer
, _T("/XCSoarData"));
349 _tcscat(buffer
, _T("/.xcsoar"));
353 return _T("/etc/xcsoar");
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
371 _tcscat(buffer
, _T(DIR_SEPARATOR_S
));
372 _tcscat(buffer
, _T(XCSDATADIR
));
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
))
387 /* hard-coded path for Altair */
388 return _tcsdup(_T("\\NOR Flash"));
393 TCHAR buffer
[MAX_PATH
];
394 const TCHAR
*path
= FindDataPathAtModule(NULL
, buffer
);
396 return _tcsdup(path
);
402 /* hack for Samsung Galaxy S and Samsung Galaxy Tab (which has a
403 build-in and an external SD card) */
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'",
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'",
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
);
440 return _tcsdup(_T(ANDROID_SDCARD
"/" XCSDATADIR
));
444 /* if XCSoar was started from a flash disk, put the XCSoarData onto
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
);
456 /* if a flash disk with XCSoarData exists, use it */
457 if (ExistingDataOnFlash(buffer
) != NULL
)
458 return _tcsdup(buffer
);
464 TCHAR buffer
[MAX_PATH
];
465 const TCHAR
*path
= GetHomeDataPath(buffer
, true);
467 return _tcsdup(path
);
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
),
493 if (_tcscmp(data_path
, flash_path
) == 0)
494 /* don't scan primary data path twice */
497 Directory::VisitSpecificFiles(flash_path
, filter
, visitor
, true);
499 #endif /* _WIN32_WCE && !GNAV*/
504 * Resolve all symlinks in the specified (allocated) string, and
505 * returns a newly allocated string. The specified string is freed by
512 char *result
= realpath(path
, buffer
);
517 return strdup(result
);
524 data_path
= FindDataPath();
525 if (data_path
== NULL
)
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
);
537 data_path_length
= _tcslen(data_path
);
542 DeinitialiseDataPath()
550 Directory::Create(GetPrimaryDataPath());