Fix inconsistency in categories: "Internal" (1 file) vs "Internals" (2 files)
[supercollider.git] / common / SC_DirUtils.cpp
blobf5ba229a9d58648890c5e60670c8a3635b9bd1ba
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 #include <string>
28 #ifdef _WIN32
29 # include <direct.h>
30 # include "SC_Win32Utils.h"
31 #else
32 # include <unistd.h>
33 # include <dirent.h>
34 # include <glob.h>
35 # include <sys/param.h>
36 # include <sys/stat.h>
37 # include <sys/types.h>
38 #endif
40 #include <stdexcept>
41 #include "SC_DirUtils.h"
43 #if defined(__APPLE__) || defined(SC_IPHONE)
44 #ifndef _SC_StandAloneInfo_
45 # include "SC_StandAloneInfo_Darwin.h"
46 #endif
47 # include <CoreFoundation/CFString.h>
48 # include <CoreFoundation/CFBundle.h>
49 #ifndef SC_IPHONE
50 # include <CoreServices/CoreServices.h>
51 #endif
52 #endif
54 using std::string;
56 const char * gIdeName = "none";
58 // Add a component to a path.
60 void sc_AppendToPath(char *path, const char *component)
62 #if defined(_WIN32)
63 strncat(path, "\\", PATH_MAX);
64 #else
65 strncat(path, "/", PATH_MAX);
66 #endif
67 strncat(path, component, PATH_MAX);
70 char *sc_StandardizePath(const char *path, char *newpath2) {
71 char newpath1[MAXPATHLEN];
73 newpath1[0] = '\0';
74 newpath2[0] = '\0';
76 size_t pathLen = strlen(path);
78 if ((pathLen >= 2) && (path[0] == '~') && ((path[1] == '/') || (path[1] == '\\'))) {
79 char home[PATH_MAX];
80 sc_GetUserHomeDirectory(home, PATH_MAX);
82 if (home != 0) {
83 if ((pathLen - 1 + strlen(home)) >= MAXPATHLEN) {
84 return 0;
86 strcpy(newpath1, home);
87 strcat(newpath1, path + 1);
88 } else {
89 if (pathLen >= MAXPATHLEN) {
90 return 0;
92 strcpy(newpath1, path);
93 newpath1[0] = '.';
95 } else {
96 if (pathLen >= MAXPATHLEN) {
97 return 0;
99 strcpy(newpath1, path);
102 bool isAlias = false;
103 sc_ResolveIfAlias(newpath1, newpath2, isAlias, PATH_MAX);
105 return newpath2;
109 // Returns TRUE iff dirname is an existing directory.
111 bool sc_DirectoryExists(const char *dirname)
113 #if defined(_WIN32)
114 DWORD attr = GetFileAttributes(dirname);
115 return ((attr != INVALID_FILE_ATTRIBUTES) &&
116 (attr & FILE_ATTRIBUTE_DIRECTORY));
117 #else
118 struct stat buf;
119 return ((stat(dirname, &buf) == 0) &&
120 S_ISDIR(buf.st_mode));
121 #endif
124 bool sc_IsSymlink(const char* path)
126 #if defined(_WIN32)
127 // FIXME
128 return false;
129 #else
130 struct stat buf;
132 return ((stat(path, &buf) == 0) &&
133 S_ISLNK(buf.st_mode));
134 #endif
137 bool sc_IsNonHostPlatformDir(const char *name)
139 #if defined(SC_IPHONE)
140 const char a[] = "linux", b[] = "windows", c[]="osx";
141 #elif defined(__APPLE__)
142 const char a[] = "linux", b[] = "windows", c[]="iphone";
143 #elif defined(__linux__)
144 const char a[] = "osx", b[] = "windows", c[]="iphone";
145 #elif defined(__FreeBSD__)
146 const char a[] = "osx", b[] = "windows", c[]="iphone";
147 #elif defined(_WIN32)
148 const char a[] = "osx", b[] = "linux", c[]="iphone";
149 #endif
150 return ((strcmp(name, a) == 0) ||
151 (strcmp(name, b) == 0) ||
152 (strcmp(name, c) == 0));
156 // Returns TRUE iff 'name' is special directory '.' or '..'
158 inline static bool sc_IsSpecialDirectory(const char* name)
160 return (strcmp(name, ".") == 0) || (strcmp(name, "..") == 0);
163 // Returns TRUE iff 'name' is to be ignored during compilation.
165 bool sc_SkipDirectory(const char *name)
167 return ((strcasecmp(name, "help") == 0) ||
168 (strcasecmp(name, "ignore") == 0) ||
169 (strcmp(name, ".svn") == 0) ||
170 (strcmp(name, ".git") == 0) ||
171 (strcmp(name, "_darcs") == 0) ||
172 ((strncmp(name, "scide_", 6) == 0) && (strcmp(name+6, gIdeName) != 0)) ||
173 sc_IsNonHostPlatformDir(name));
177 int sc_ResolveIfAlias(const char *path, char *returnPath, bool &isAlias, int length)
179 isAlias = false;
180 #if defined(__APPLE__) && !defined(SC_IPHONE)
181 FSRef dirRef;
182 OSStatus osStatusErr = FSPathMakeRef ((const UInt8 *) path, &dirRef, NULL);
183 if ( !osStatusErr ) {
184 Boolean isFolder;
185 Boolean wasAliased;
186 OSErr err = FSResolveAliasFile (&dirRef, true, &isFolder, &wasAliased);
187 if (err)
189 return -1;
191 isAlias = wasAliased;
192 if (wasAliased)
194 UInt8 resolvedPath[PATH_MAX];
195 osStatusErr = FSRefMakePath (&dirRef, resolvedPath, length);
196 if (osStatusErr)
198 return -1;
200 strncpy(returnPath, (char *) resolvedPath, length);
201 return 0;
204 #elif defined(__linux__) || defined(__FreeBSD__)
205 isAlias = sc_IsSymlink(path);
206 if (realpath(path, returnPath))
208 return 0;
211 return -1;
212 #endif
213 strcpy(returnPath, path);
214 return 0;
217 // Support for Bundles
219 #if defined(__APPLE__) && !defined(SC_IPHONE) // running on OS X
221 // Support for stand-alone applications
223 bool sc_IsStandAlone()
225 return SC_StandAloneInfo::IsStandAlone();
228 void sc_GetResourceDirectory(char* pathBuf, int length)
230 SC_StandAloneInfo::GetResourceDir(pathBuf, length);
233 #elif defined(SC_IPHONE)
235 bool sc_IsStandAlone()
237 return false;
240 void sc_GetResourceDirectory(char* pathBuf, int length)
242 sc_GetUserAppSupportDirectory(pathBuf, length);
245 #elif defined(__unix__)
247 bool sc_IsStandAlone()
249 return false;
252 void sc_GetResourceDirectory(char* pathBuf, int length)
254 #ifdef SC_DATA_DIR
255 strncpy(pathBuf, SC_DATA_DIR, length);
256 #else
257 strncpy(pathBuf, "/usr/share/SuperCollider", length);
258 #endif
261 #else
263 bool sc_IsStandAlone()
265 return false;
268 static void sc_GetResourceDirectoryFromAppDirectory(char* pathBuf, int length)
270 char * result = getcwd(pathBuf, length);
271 if (result != pathBuf)
272 throw std::runtime_error("cannot get current working directory");
276 void sc_GetResourceDirectory(char* pathBuf, int length)
278 return sc_GetResourceDirectoryFromAppDirectory(pathBuf, length);
281 #endif
285 // Support for Extensions
287 // Get the user home directory.
289 void sc_GetUserHomeDirectory(char *str, int size)
291 #ifndef _WIN32
292 char *home = getenv("HOME");
293 if(home!=NULL){
294 strncpy(str, home, size);
295 }else{
296 // cwd is not the user home directory; but this is better than leaving mem uninitialised.
297 strcpy(str, "./");
299 #else
300 win32_GetHomeFolder(str,size);
301 #endif
305 // Get the System level data directory.
307 void sc_GetSystemAppSupportDirectory(char *str, int size)
309 strncpy(str,
310 #if defined(SC_DATA_DIR)
311 SC_DATA_DIR,
312 #elif defined(SC_IPHONE)
313 "/",
314 #elif defined(__APPLE__)
315 "/Library/Application Support/SuperCollider",
316 #elif defined(_WIN32)
317 ( getenv("SC_SYSAPPSUP_PATH")==NULL ) ? "C:\\SuperCollider" : getenv("SC_SYSAPPSUP_PATH"),
318 #else
319 "/usr/local/share/SuperCollider",
320 #endif
321 size);
325 // Get the User level data directory.
327 void sc_GetUserAppSupportDirectory(char *str, int size)
329 // XDG support according to http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
330 char * xdg_data_home = getenv("XDG_DATA_HOME");
331 if (xdg_data_home) {
332 string config_folder = string(xdg_data_home) + SC_PATH_DELIMITER + "SuperCollider";
333 strncpy(str, config_folder.c_str(), size);
334 return;
337 char home[PATH_MAX];
338 sc_GetUserHomeDirectory(home, PATH_MAX);
340 snprintf(str,
341 size,
342 #if defined(SC_IPHONE)
343 "%s/Documents",
344 #elif defined(__APPLE__)
345 "%s/Library/Application Support/SuperCollider",
346 #elif defined(_WIN32)
347 "%s\\SuperCollider",
348 #else
349 "%s/.local/share/SuperCollider",
350 #endif
351 home);
355 // Get the System level 'Extensions' directory.
357 void sc_GetSystemExtensionDirectory(char *str, int size)
359 char path[PATH_MAX];
360 sc_GetSystemAppSupportDirectory(path, sizeof(path));
361 sc_AppendToPath(path, "Extensions");
362 strncpy(str, path, size);
366 // Get the System level 'Extensions' directory.
368 void sc_GetUserExtensionDirectory(char *str, int size)
370 char path[PATH_MAX];
371 sc_GetUserAppSupportDirectory(path, sizeof(path));
372 sc_AppendToPath(path, "Extensions");
373 strncpy(str, path, size);
376 // Get the directory for the configuration files.
377 void sc_GetUserConfigDirectory(char *str, int size)
379 // XDG support according to http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
380 char * xdg_config_home = getenv("XDG_CONFIG_HOME");
381 if (xdg_config_home) {
382 string config_folder = string(xdg_config_home) + SC_PATH_DELIMITER + "SuperCollider";
383 strncpy(str, config_folder.c_str(), size);
384 return;
387 #if defined(__linux__) || defined(__freebsd__)
388 char home[PATH_MAX];
390 sc_GetUserHomeDirectory(home, PATH_MAX);
391 sc_AppendToPath(home, ".config/SuperCollider");
392 strncpy(str, home, size);
393 #else
394 sc_GetUserAppSupportDirectory(str, size);
395 #endif
399 // Directory access
401 struct SC_DirHandle
403 #ifdef _WIN32
404 HANDLE mHandle;
405 WIN32_FIND_DATA mEntry;
406 bool mAtEnd;
407 #else
408 DIR* mHandle;
409 struct dirent* mEntry;
410 #endif
413 SC_DirHandle* sc_OpenDir(const char* dirname)
415 SC_DirHandle* dir = new SC_DirHandle;
416 memset(dir, 0, sizeof(SC_DirHandle));
418 #ifdef _WIN32
419 char allInDir[PATH_MAX];
420 snprintf(allInDir, PATH_MAX, "%s\\*.*", dirname);
422 dir->mHandle = ::FindFirstFile(allInDir, &dir->mEntry);
423 if (dir->mHandle == INVALID_HANDLE_VALUE) {
424 delete dir;
425 return 0;
428 dir->mAtEnd = false;
429 #else
430 dir->mHandle = opendir(dirname);
431 if (!dir->mHandle) {
432 delete dir;
433 return 0;
435 #endif
437 return dir;
440 void sc_CloseDir(SC_DirHandle* dir)
442 #ifdef _WIN32
443 ::FindClose(dir->mHandle);
444 #else
445 closedir(dir->mHandle);
446 #endif
447 delete dir;
450 bool sc_ReadDir(SC_DirHandle* dir, const char* dirname, char* path, bool& skipEntry)
452 #ifdef _WIN32
453 bool success = true;
455 if (dir->mAtEnd)
456 return false;
458 const char* entry = dir->mEntry.cFileName;
460 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
461 skipEntry = true;
462 success = true;
463 } else {
464 skipEntry = false;
465 success = true;
468 char entrypathname[PATH_MAX];
469 strncpy(entrypathname, dirname, PATH_MAX);
470 sc_AppendToPath(entrypathname, dir->mEntry.cFileName);
472 bool isAlias = false;
473 sc_ResolveIfAlias(entrypathname, path, isAlias, PATH_MAX);
475 if (!::FindNextFile(dir->mHandle, &dir->mEntry)) {
476 dir->mAtEnd = true;
479 return true;
480 #else
481 if (!dir->mHandle)
482 return false;
484 dir->mEntry = readdir(dir->mHandle);
485 if (!dir->mEntry)
486 return false;
488 const char* entry = dir->mEntry->d_name;
490 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
491 skipEntry = true;
492 return true;
493 } else {
494 skipEntry = false;
497 // construct path from dir entry
498 char entrypathname[PATH_MAX];
499 strncpy(entrypathname, dirname, PATH_MAX);
500 sc_AppendToPath(entrypathname, dir->mEntry->d_name);
502 // resolve path
503 bool isAlias = false;
504 if (sc_ResolveIfAlias(entrypathname, path, isAlias, strlen(entrypathname))<0)
506 skipEntry = true;
509 return true;
510 #endif
514 // Globbing
516 struct SC_GlobHandle
518 #ifdef _WIN32
519 HANDLE mHandle;
520 char mFolder[PATH_MAX];
521 WIN32_FIND_DATA mEntry;
522 char mEntryPath[PATH_MAX];
523 bool mAtEnd;
524 #else
525 glob_t mHandle;
526 size_t mEntry;
527 #endif
530 SC_GlobHandle* sc_Glob(const char* pattern)
532 SC_GlobHandle* glob = new SC_GlobHandle;
534 #ifdef _WIN32
535 char patternWin[1024];
537 strncpy(patternWin, pattern, 1024);
538 patternWin[1023] = 0;
539 win32_ReplaceCharInString(patternWin, 1024, '/', '\\');
541 win32_ExtractContainingFolder(glob->mFolder, patternWin, PATH_MAX);
543 glob->mHandle = ::FindFirstFile(patternWin, &glob->mEntry);
544 if (glob->mHandle == INVALID_HANDLE_VALUE) {
545 delete glob;
546 return 0;
549 glob->mAtEnd = false;
550 #else
551 int flags = GLOB_MARK | GLOB_TILDE;
552 #ifdef __APPLE__
553 flags |= GLOB_QUOTE;
554 #endif
556 int err = ::glob(pattern, flags, NULL, &glob->mHandle);
557 if (err < 0) {
558 delete glob;
559 return 0;
562 glob->mEntry = 0;
563 #endif
565 return glob;
568 void sc_GlobFree(SC_GlobHandle* glob)
570 #ifdef _WIN32
571 ::FindClose(glob->mHandle);
572 #else
573 globfree(&glob->mHandle);
574 #endif
575 delete glob;
578 const char* sc_GlobNext(SC_GlobHandle* glob)
580 #ifdef _WIN32
581 if (glob->mAtEnd)
582 return 0;
583 strncpy(glob->mEntryPath, glob->mFolder, PATH_MAX);
584 sc_AppendToPath(glob->mEntryPath, glob->mEntry.cFileName);
585 if (!::FindNextFile(glob->mHandle, &glob->mEntry))
586 glob->mAtEnd = true;
587 return glob->mEntryPath;
588 #else
589 if (glob->mEntry >= glob->mHandle.gl_pathc)
590 return 0;
591 return glob->mHandle.gl_pathv[glob->mEntry++];
592 #endif