cmake build system: visiblity support for clang
[supercollider.git] / common / SC_DirUtils.cpp
blob6e2346191fd9ecbc3c642d40e75769bab3e2f6c1
1 /*
2 * Copyright (c) 2005 Tim Walters. All rights reserved.
3 * Created by Tim Walters on 10/19/05.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18 * USA
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdio.h>
26 #ifdef _WIN32
27 # include <direct.h>
28 # include "SC_Win32Utils.h"
29 #else
30 # include <unistd.h>
31 # include <dirent.h>
32 # include <glob.h>
33 # include <sys/param.h>
34 # include <sys/stat.h>
35 # include <sys/types.h>
36 #endif
38 #include "SC_DirUtils.h"
40 #if defined(__APPLE__) || defined(SC_IPHONE)
41 #ifndef _SC_StandAloneInfo_
42 # include "SC_StandAloneInfo_Darwin.h"
43 #endif
44 # include <CoreFoundation/CFString.h>
45 # include <CoreFoundation/CFBundle.h>
46 #ifndef SC_IPHONE
47 # include <CoreServices/CoreServices.h>
48 #endif
49 #endif
51 const char * gIdeName = "none";
53 // Add a component to a path.
55 void sc_AppendToPath(char *path, size_t max_size, const char *component)
57 size_t currentLength = strlen(path);
58 if (currentLength >= max_size-1)
59 return;
60 path[currentLength] = SC_PATH_DELIMITER[0];
61 path[currentLength+1] = 0;
62 ++currentLength;
64 char * tail = path + currentLength;
65 size_t remain = max_size - currentLength;
67 strncat(tail, component, remain);
71 char *sc_StandardizePath(const char *path, char *newpath2)
73 char newpath1[MAXPATHLEN];
75 newpath1[0] = '\0';
76 newpath2[0] = '\0';
78 size_t pathLen = strlen(path);
80 if ((pathLen >= 2) && (path[0] == '~') && ((path[1] == '/') || (path[1] == '\\'))) {
81 char home[PATH_MAX];
82 sc_GetUserHomeDirectory(home, PATH_MAX);
84 if (home != 0) {
85 if ((pathLen - 1 + strlen(home)) >= MAXPATHLEN) {
86 return 0;
88 strcpy(newpath1, home);
89 strcat(newpath1, path + 1);
90 } else {
91 if (pathLen >= MAXPATHLEN) {
92 return 0;
94 strcpy(newpath1, path);
95 newpath1[0] = '.';
97 } else {
98 if (pathLen >= MAXPATHLEN) {
99 return 0;
101 strcpy(newpath1, path);
104 bool isAlias = false;
105 if(sc_ResolveIfAlias(newpath1, newpath2, isAlias, PATH_MAX)!=0) {
106 strcpy(newpath2, newpath1);
109 return newpath2;
113 // Returns TRUE iff dirname is an existing directory.
115 bool sc_DirectoryExists(const char *dirname)
117 #if defined(_WIN32)
118 DWORD attr = GetFileAttributes(dirname);
119 return ((attr != INVALID_FILE_ATTRIBUTES) &&
120 (attr & FILE_ATTRIBUTE_DIRECTORY));
121 #else
122 struct stat buf;
123 return ((stat(dirname, &buf) == 0) &&
124 S_ISDIR(buf.st_mode));
125 #endif
128 bool sc_IsSymlink(const char* path)
130 #if defined(_WIN32)
131 // FIXME
132 return false;
133 #else
134 struct stat buf;
136 return ((stat(path, &buf) == 0) &&
137 S_ISLNK(buf.st_mode));
138 #endif
141 bool sc_IsNonHostPlatformDir(const char *name)
143 #if defined(SC_IPHONE)
144 const char a[] = "linux", b[] = "windows", c[]="osx";
145 #elif defined(__APPLE__)
146 const char a[] = "linux", b[] = "windows", c[]="iphone";
147 #elif defined(__linux__)
148 const char a[] = "osx", b[] = "windows", c[]="iphone";
149 #elif defined(__FreeBSD__)
150 const char a[] = "osx", b[] = "windows", c[]="iphone";
151 #elif defined(_WIN32)
152 const char a[] = "osx", b[] = "linux", c[]="iphone";
153 #endif
154 return ((strcmp(name, a) == 0) ||
155 (strcmp(name, b) == 0) ||
156 (strcmp(name, c) == 0));
160 // Returns TRUE iff 'name' is special directory '.' or '..'
162 inline static bool sc_IsSpecialDirectory(const char* name)
164 return (strcmp(name, ".") == 0) || (strcmp(name, "..") == 0);
167 // Returns TRUE iff 'name' is to be ignored during compilation.
169 bool sc_SkipDirectory(const char *name)
171 return ((strcasecmp(name, "help") == 0) ||
172 (strcasecmp(name, "ignore") == 0) ||
173 (strcmp(name, ".svn") == 0) ||
174 (strcmp(name, ".git") == 0) ||
175 (strcmp(name, "_darcs") == 0) ||
176 ((strncmp(name, "scide_", 6) == 0) && (strcmp(name+6, gIdeName) != 0)) ||
177 sc_IsNonHostPlatformDir(name));
181 int sc_ResolveIfAlias(const char *path, char *returnPath, bool &isAlias, int length)
183 isAlias = false;
184 #if defined(__APPLE__) && !defined(SC_IPHONE)
185 FSRef dirRef;
186 OSStatus osStatusErr = FSPathMakeRef ((const UInt8 *) path, &dirRef, NULL);
187 if ( !osStatusErr ) {
188 Boolean isFolder;
189 Boolean wasAliased;
190 OSErr err = FSResolveAliasFile (&dirRef, true, &isFolder, &wasAliased);
191 if (err)
193 return -1;
195 isAlias = wasAliased;
196 if (wasAliased)
198 UInt8 resolvedPath[PATH_MAX];
199 osStatusErr = FSRefMakePath (&dirRef, resolvedPath, length);
200 if (osStatusErr)
202 return -1;
204 strncpy(returnPath, (char *) resolvedPath, length);
205 return 0;
208 #endif
209 strcpy(returnPath, path);
210 return 0;
213 // Support for Bundles
215 #if defined(__APPLE__) && !defined(SC_IPHONE) // running on OS X
217 // Support for stand-alone applications
219 bool sc_IsStandAlone()
221 return SC_StandAloneInfo::IsStandAlone();
224 void sc_GetResourceDirectory(char* pathBuf, int length)
226 SC_StandAloneInfo::GetResourceDir(pathBuf, length);
229 #elif defined(SC_IPHONE)
231 bool sc_IsStandAlone()
233 return false;
236 void sc_GetResourceDirectory(char* pathBuf, int length)
238 sc_GetUserAppSupportDirectory(pathBuf, length);
241 #elif defined(__unix__)
243 bool sc_IsStandAlone()
245 return false;
248 void sc_GetResourceDirectory(char* pathBuf, int length)
250 #ifdef SC_DATA_DIR
251 strncpy(pathBuf, SC_DATA_DIR, length);
252 #else
253 strncpy(pathBuf, "/usr/share/SuperCollider", length);
254 #endif
257 #else
259 bool sc_IsStandAlone()
261 return false;
264 static void sc_GetResourceDirectoryFromAppDirectory(char* pathBuf, int length)
266 char * result = getcwd(pathBuf, length);
267 if (result != pathBuf)
268 throw std::runtime_error("cannot get current working directory");
272 void sc_GetResourceDirectory(char* pathBuf, int length)
274 return sc_GetResourceDirectoryFromAppDirectory(pathBuf, length);
277 #endif
281 // Support for Extensions
283 // Get the user home directory.
285 void sc_GetUserHomeDirectory(char *str, int size)
287 #ifndef _WIN32
288 const char *home = getenv("HOME");
289 if(home!=NULL){
290 strncpy(str, home, size);
291 }else{
292 // cwd is not the user home directory; but this is better than leaving mem uninitialised.
293 strcpy(str, "./");
295 #else
296 win32_GetHomeFolder(str,size);
297 #endif
301 // Get the System level data directory.
303 void sc_GetSystemAppSupportDirectory(char *str, int size)
305 strncpy(str,
306 #if defined(SC_DATA_DIR)
307 SC_DATA_DIR,
308 #elif defined(SC_IPHONE)
309 "/",
310 #elif defined(__APPLE__)
311 "/Library/Application Support/SuperCollider",
312 #elif defined(_WIN32)
313 ( getenv("SC_SYSAPPSUP_PATH")==NULL ) ? "C:\\SuperCollider" : getenv("SC_SYSAPPSUP_PATH"),
314 #else
315 "/usr/local/share/SuperCollider",
316 #endif
317 size);
321 // Get the User level data directory.
323 void sc_GetUserAppSupportDirectory(char *str, int size)
325 // XDG support according to http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
326 const char * xdg_data_home = getenv("XDG_DATA_HOME");
327 if (xdg_data_home) {
328 strncpy(str, xdg_data_home, size);
329 sc_AppendToPath(str, size, "SuperCollider");
330 return;
333 sc_GetUserHomeDirectory(str, size);
335 #if defined(SC_IPHONE)
336 sc_AppendToPath(str, size, "Documents");
337 #elif defined(__APPLE__)
338 sc_AppendToPath(str, size, "Library/Application Support/SuperCollider");
339 #elif defined(_WIN32)
340 sc_AppendToPath(str, size, "SuperCollider");
341 #else
342 sc_AppendToPath(str, size, ".local/share/SuperCollider");
343 #endif
347 // Get the System level 'Extensions' directory.
349 void sc_GetSystemExtensionDirectory(char *str, int size)
351 sc_GetSystemAppSupportDirectory(str, size);
352 sc_AppendToPath(str, size, "Extensions");
356 // Get the System level 'Extensions' directory.
358 void sc_GetUserExtensionDirectory(char *str, int size)
360 sc_GetUserAppSupportDirectory(str, size);
361 sc_AppendToPath(str, size, "Extensions");
364 // Get the directory for the configuration files.
365 void sc_GetUserConfigDirectory(char *str, int size)
367 // XDG support according to http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
368 const char * xdg_config_home = getenv("XDG_CONFIG_HOME");
369 if (xdg_config_home) {
370 strncpy(str, xdg_config_home, size);
371 sc_AppendToPath(str, size, "SuperCollider");
374 #if defined(__linux__) || defined(__freebsd__)
375 char * home = str;
377 sc_GetUserHomeDirectory(str, size);
378 sc_AppendToPath(str, size, ".config/SuperCollider");
379 #else
380 sc_GetUserAppSupportDirectory(str, size);
381 #endif
385 // Directory access
387 struct SC_DirHandle
389 #ifdef _WIN32
390 HANDLE mHandle;
391 WIN32_FIND_DATA mEntry;
392 bool mAtEnd;
393 #else
394 DIR* mHandle;
395 struct dirent* mEntry;
396 #endif
399 SC_DirHandle* sc_OpenDir(const char* dirname)
401 SC_DirHandle* dir = new SC_DirHandle;
402 memset(dir, 0, sizeof(SC_DirHandle));
404 #ifdef _WIN32
405 char allInDir[PATH_MAX];
406 snprintf(allInDir, PATH_MAX, "%s\\*.*", dirname);
408 dir->mHandle = ::FindFirstFile(allInDir, &dir->mEntry);
409 if (dir->mHandle == INVALID_HANDLE_VALUE) {
410 delete dir;
411 return 0;
414 dir->mAtEnd = false;
415 #else
416 dir->mHandle = opendir(dirname);
417 if (!dir->mHandle) {
418 delete dir;
419 return 0;
421 #endif
423 return dir;
426 void sc_CloseDir(SC_DirHandle* dir)
428 #ifdef _WIN32
429 ::FindClose(dir->mHandle);
430 #else
431 closedir(dir->mHandle);
432 #endif
433 delete dir;
436 bool sc_ReadDir(SC_DirHandle* dir, const char* dirname, char* path, bool& skipEntry)
438 #ifdef _WIN32
439 bool success = true;
441 if (dir->mAtEnd)
442 return false;
444 const char* entry = dir->mEntry.cFileName;
446 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
447 skipEntry = true;
448 success = true;
449 } else {
450 skipEntry = false;
451 success = true;
454 char entrypathname[PATH_MAX];
455 strncpy(entrypathname, dirname, PATH_MAX);
456 sc_AppendToPath(entrypathname, PATH_MAX, dir->mEntry.cFileName);
458 bool isAlias = false;
459 sc_ResolveIfAlias(entrypathname, path, isAlias, PATH_MAX);
461 if (!::FindNextFile(dir->mHandle, &dir->mEntry)) {
462 dir->mAtEnd = true;
465 return true;
466 #else
467 if (!dir->mHandle)
468 return false;
470 dir->mEntry = readdir(dir->mHandle);
471 if (!dir->mEntry)
472 return false;
474 const char* entry = dir->mEntry->d_name;
476 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
477 skipEntry = true;
478 return true;
479 } else {
480 skipEntry = false;
483 // construct path from dir entry
484 char entrypathname[PATH_MAX];
485 strncpy(entrypathname, dirname, PATH_MAX);
486 sc_AppendToPath(entrypathname, PATH_MAX, dir->mEntry->d_name);
488 // resolve path
489 bool isAlias = false;
490 if (sc_ResolveIfAlias(entrypathname, path, isAlias, strlen(entrypathname))<0)
492 skipEntry = true;
495 return true;
496 #endif
500 // Globbing
502 struct SC_GlobHandle
504 #ifdef _WIN32
505 HANDLE mHandle;
506 char mFolder[PATH_MAX];
507 WIN32_FIND_DATA mEntry;
508 char mEntryPath[PATH_MAX];
509 bool mAtEnd;
510 #else
511 glob_t mHandle;
512 size_t mEntry;
513 #endif
516 SC_GlobHandle* sc_Glob(const char* pattern)
518 SC_GlobHandle* glob = new SC_GlobHandle;
520 #ifdef _WIN32
521 char patternWin[1024];
523 strncpy(patternWin, pattern, 1024);
524 patternWin[1023] = 0;
525 win32_ReplaceCharInString(patternWin, 1024, '/', '\\');
527 win32_ExtractContainingFolder(glob->mFolder, patternWin, PATH_MAX);
529 glob->mHandle = ::FindFirstFile(patternWin, &glob->mEntry);
530 if (glob->mHandle == INVALID_HANDLE_VALUE) {
531 delete glob;
532 return 0;
535 glob->mAtEnd = false;
536 #else
537 int flags = GLOB_MARK | GLOB_TILDE;
538 #ifdef __APPLE__
539 flags |= GLOB_QUOTE;
540 #endif
542 int err = ::glob(pattern, flags, NULL, &glob->mHandle);
543 if (err < 0) {
544 delete glob;
545 return 0;
548 glob->mEntry = 0;
549 #endif
551 return glob;
554 void sc_GlobFree(SC_GlobHandle* glob)
556 #ifdef _WIN32
557 ::FindClose(glob->mHandle);
558 #else
559 globfree(&glob->mHandle);
560 #endif
561 delete glob;
564 const char* sc_GlobNext(SC_GlobHandle* glob)
566 #ifdef _WIN32
567 if (glob->mAtEnd)
568 return 0;
569 strncpy(glob->mEntryPath, glob->mFolder, PATH_MAX);
570 sc_AppendToPath(glob->mEntryPath, PATH_MAX, glob->mEntry.cFileName);
571 if (!::FindNextFile(glob->mHandle, &glob->mEntry))
572 glob->mAtEnd = true;
573 return glob->mEntryPath;
574 #else
575 if (glob->mEntry >= glob->mHandle.gl_pathc)
576 return 0;
577 return glob->mHandle.gl_pathv[glob->mEntry++];
578 #endif