Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / common / SC_DirUtils.cpp
bloba6256a28530ab48ea3c4da0f8348908e69c8fee0
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>
25 #include <stdexcept>
27 #ifdef _WIN32
28 # include <direct.h>
29 # include "SC_Win32Utils.h"
30 #else
31 # include <unistd.h>
32 # include <dirent.h>
33 # include <glob.h>
34 # include <sys/param.h>
35 # include <sys/stat.h>
36 # include <sys/types.h>
37 #endif
39 #include "SC_DirUtils.h"
41 #if defined(__APPLE__) || defined(SC_IPHONE)
42 #ifndef _SC_StandAloneInfo_
43 # include "SC_StandAloneInfo_Darwin.h"
44 #endif
45 # include <CoreFoundation/CFString.h>
46 # include <CoreFoundation/CFBundle.h>
47 #ifndef SC_IPHONE
48 # include <CoreServices/CoreServices.h>
49 #endif
50 #endif
52 const char * gIdeName = "none";
54 // Add a component to a path.
56 void sc_AppendToPath(char *path, size_t max_size, const char *component)
58 size_t currentLength = strlen(path);
59 if (currentLength >= max_size-1)
60 return;
61 path[currentLength] = SC_PATH_DELIMITER[0];
62 path[currentLength+1] = 0;
63 ++currentLength;
65 char * tail = path + currentLength;
66 size_t remain = max_size - currentLength;
68 strncat(tail, component, remain);
72 char *sc_StandardizePath(const char *path, char *newpath2)
74 char newpath1[MAXPATHLEN];
76 newpath1[0] = '\0';
77 newpath2[0] = '\0';
79 size_t pathLen = strlen(path);
81 if ((pathLen >= 2) && (path[0] == '~') && ((path[1] == '/') || (path[1] == '\\'))) {
82 char home[PATH_MAX];
83 sc_GetUserHomeDirectory(home, PATH_MAX);
85 if (home != 0) {
86 if ((pathLen - 1 + strlen(home)) >= MAXPATHLEN) {
87 return 0;
89 strcpy(newpath1, home);
90 strcat(newpath1, path + 1);
91 } else {
92 if (pathLen >= MAXPATHLEN) {
93 return 0;
95 strcpy(newpath1, path);
96 newpath1[0] = '.';
98 } else {
99 if (pathLen >= MAXPATHLEN) {
100 return 0;
102 strcpy(newpath1, path);
105 bool isAlias = false;
106 if(sc_ResolveIfAlias(newpath1, newpath2, isAlias, PATH_MAX)!=0) {
107 strcpy(newpath2, newpath1);
110 return newpath2;
114 // Returns TRUE iff dirname is an existing directory.
116 bool sc_DirectoryExists(const char *dirname)
118 #if defined(_WIN32)
119 DWORD attr = GetFileAttributes(dirname);
120 return ((attr != INVALID_FILE_ATTRIBUTES) &&
121 (attr & FILE_ATTRIBUTE_DIRECTORY));
122 #else
123 struct stat buf;
124 return ((stat(dirname, &buf) == 0) &&
125 S_ISDIR(buf.st_mode));
126 #endif
129 bool sc_IsSymlink(const char* path)
131 #if defined(_WIN32)
132 // FIXME
133 return false;
134 #else
135 struct stat buf;
137 return ((stat(path, &buf) == 0) &&
138 S_ISLNK(buf.st_mode));
139 #endif
142 bool sc_IsNonHostPlatformDir(const char *name)
144 #if defined(SC_IPHONE)
145 const char a[] = "linux", b[] = "windows", c[]="osx";
146 #elif defined(__APPLE__)
147 const char a[] = "linux", b[] = "windows", c[]="iphone";
148 #elif defined(__linux__)
149 const char a[] = "osx", b[] = "windows", c[]="iphone";
150 #elif defined(__FreeBSD__)
151 const char a[] = "osx", b[] = "windows", c[]="iphone";
152 #elif defined(_WIN32)
153 const char a[] = "osx", b[] = "linux", c[]="iphone";
154 #endif
155 return ((strcmp(name, a) == 0) ||
156 (strcmp(name, b) == 0) ||
157 (strcmp(name, c) == 0));
161 // Returns TRUE iff 'name' is special directory '.' or '..'
163 inline static bool sc_IsSpecialDirectory(const char* name)
165 return (strcmp(name, ".") == 0) || (strcmp(name, "..") == 0);
168 // Returns TRUE iff 'name' is to be ignored during compilation.
170 bool sc_SkipDirectory(const char *name)
172 return (stringCaseCompare(name, "help") ||
173 stringCaseCompare(name, "ignore") ||
174 (strcmp(name, ".svn") == 0) ||
175 (strcmp(name, ".git") == 0) ||
176 (strcmp(name, "_darcs") == 0) ||
177 ((strncmp(name, "scide_", 6) == 0) && (strcmp(name+6, gIdeName) != 0)) ||
178 sc_IsNonHostPlatformDir(name));
182 int sc_ResolveIfAlias(const char *path, char *returnPath, bool &isAlias, int length)
184 isAlias = false;
185 #if defined(__APPLE__) && !defined(SC_IPHONE)
186 FSRef dirRef;
187 OSStatus osStatusErr = FSPathMakeRef ((const UInt8 *) path, &dirRef, NULL);
188 if ( !osStatusErr ) {
189 Boolean isFolder;
190 Boolean wasAliased;
191 OSErr err = FSResolveAliasFile (&dirRef, true, &isFolder, &wasAliased);
192 if (err)
194 return -1;
196 isAlias = wasAliased;
197 if (wasAliased)
199 UInt8 resolvedPath[PATH_MAX];
200 osStatusErr = FSRefMakePath (&dirRef, resolvedPath, length);
201 if (osStatusErr)
203 return -1;
205 strncpy(returnPath, (char *) resolvedPath, length);
206 return 0;
209 #endif
210 strcpy(returnPath, path);
211 return 0;
214 // Support for Bundles
216 #if defined(__APPLE__) && !defined(SC_IPHONE) // running on OS X
218 // Support for stand-alone applications
220 bool sc_IsStandAlone()
222 return SC_StandAloneInfo::IsStandAlone();
225 void sc_GetResourceDirectory(char* pathBuf, int length)
227 SC_StandAloneInfo::GetResourceDir(pathBuf, length);
231 void sc_AppendBundleName(char *str, int size)
233 CFBundleRef mainBundle;
234 mainBundle = CFBundleGetMainBundle();
235 if(mainBundle){
236 CFDictionaryRef dictRef = CFBundleGetInfoDictionary(mainBundle);
237 CFStringRef strRef;
238 strRef = (CFStringRef)CFDictionaryGetValue(dictRef, CFSTR("CFBundleName"));
239 if(strRef){
240 const char *bundleName = CFStringGetCStringPtr(strRef, CFStringGetSystemEncoding());
241 if(bundleName) {
242 sc_AppendToPath(str, size, bundleName);
243 return;
247 sc_AppendToPath(str, size, "SuperCollider");
250 #elif defined(SC_IPHONE)
252 bool sc_IsStandAlone()
254 return false;
257 void sc_GetResourceDirectory(char* pathBuf, int length)
259 sc_GetUserAppSupportDirectory(pathBuf, length);
262 #elif defined(__unix__)
264 bool sc_IsStandAlone()
266 return false;
269 void sc_GetResourceDirectory(char* pathBuf, int length)
271 #ifdef SC_DATA_DIR
272 strncpy(pathBuf, SC_DATA_DIR, length);
273 #else
274 strncpy(pathBuf, "/usr/share/SuperCollider", length);
275 #endif
278 #else
280 bool sc_IsStandAlone()
282 return false;
285 static void sc_GetResourceDirectoryFromAppDirectory(char* pathBuf, int length)
287 char * result = getcwd(pathBuf, length);
288 if (result != pathBuf)
289 throw std::runtime_error("cannot get current working directory");
293 void sc_GetResourceDirectory(char* pathBuf, int length)
295 return sc_GetResourceDirectoryFromAppDirectory(pathBuf, length);
298 #endif
300 // Support for Extensions
302 // Get the user home directory.
304 void sc_GetUserHomeDirectory(char *str, int size)
306 #ifndef _WIN32
307 const char *home = getenv("HOME");
308 if(home!=NULL){
309 strncpy(str, home, size);
310 }else{
311 // cwd is not the user home directory; but this is better than leaving mem uninitialised.
312 strcpy(str, "./");
314 #else
315 win32_GetHomeFolder(str,size);
316 #endif
320 // Get the System level data directory.
322 void sc_GetSystemAppSupportDirectory(char *str, int size)
324 strncpy(str,
325 #if defined(SC_DATA_DIR)
326 SC_DATA_DIR,
327 #elif defined(SC_IPHONE)
328 "/",
329 #elif defined(__APPLE__)
330 "/Library/Application Support",
331 #elif defined(_WIN32)
332 ( getenv("SC_SYSAPPSUP_PATH")==NULL ) ? "C:\\SuperCollider" : getenv("SC_SYSAPPSUP_PATH"),
333 #else
334 "/usr/local/share/SuperCollider",
335 #endif
336 size);
338 #if defined(__APPLE__)
339 // Get the main bundle name for the app from the enclosed Info.plist
340 sc_AppendBundleName(str, size);
341 #endif
345 // Get the User level data directory.
347 void sc_GetUserAppSupportDirectory(char *str, int size)
349 // XDG support according to http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
350 const char * xdg_data_home = getenv("XDG_DATA_HOME");
351 if (xdg_data_home) {
352 strncpy(str, xdg_data_home, size);
353 sc_AppendToPath(str, size, "SuperCollider");
354 return;
357 sc_GetUserHomeDirectory(str, size);
359 #if defined(SC_IPHONE)
360 sc_AppendToPath(str, size, "Documents");
361 #elif defined(__APPLE__)
362 // Get the main bundle name for the app
363 sc_AppendToPath(str, size, "Library/Application Support");
364 sc_AppendBundleName(str, size);
365 #elif defined(_WIN32)
366 sc_AppendToPath(str, size, "SuperCollider");
367 #else
368 sc_AppendToPath(str, size, ".local/share/SuperCollider");
369 #endif
373 // Get the System level 'Extensions' directory.
375 void sc_GetSystemExtensionDirectory(char *str, int size)
377 sc_GetSystemAppSupportDirectory(str, size);
378 sc_AppendToPath(str, size, "Extensions");
382 // Get the System level 'Extensions' directory.
384 void sc_GetUserExtensionDirectory(char *str, int size)
386 sc_GetUserAppSupportDirectory(str, size);
387 sc_AppendToPath(str, size, "Extensions");
390 // Get the directory for the configuration files.
391 void sc_GetUserConfigDirectory(char *str, int size)
393 // XDG support according to http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
394 const char * xdg_config_home = getenv("XDG_CONFIG_HOME");
395 if (xdg_config_home) {
396 strncpy(str, xdg_config_home, size);
397 sc_AppendToPath(str, size, "SuperCollider");
398 return;
401 #if defined(__linux__) || defined(__freebsd__)
402 sc_GetUserHomeDirectory(str, size);
403 sc_AppendToPath(str, size, ".config/SuperCollider");
404 #else
405 sc_GetUserAppSupportDirectory(str, size);
406 #endif
410 // Directory access
412 struct SC_DirHandle
414 #ifdef _WIN32
415 HANDLE mHandle;
416 WIN32_FIND_DATA mEntry;
417 bool mAtEnd;
418 #else
419 DIR* mHandle;
420 struct dirent* mEntry;
421 #endif
424 SC_DirHandle* sc_OpenDir(const char* dirname)
426 SC_DirHandle* dir = new SC_DirHandle;
427 memset(dir, 0, sizeof(SC_DirHandle));
429 #ifdef _WIN32
430 char allInDir[PATH_MAX];
431 snprintf(allInDir, PATH_MAX, "%s\\*.*", dirname);
433 dir->mHandle = ::FindFirstFile(allInDir, &dir->mEntry);
434 if (dir->mHandle == INVALID_HANDLE_VALUE) {
435 delete dir;
436 return 0;
439 dir->mAtEnd = false;
440 #else
441 dir->mHandle = opendir(dirname);
442 if (!dir->mHandle) {
443 delete dir;
444 return 0;
446 #endif
448 return dir;
451 void sc_CloseDir(SC_DirHandle* dir)
453 #ifdef _WIN32
454 ::FindClose(dir->mHandle);
455 #else
456 closedir(dir->mHandle);
457 #endif
458 delete dir;
461 bool sc_ReadDir(SC_DirHandle* dir, const char* dirname, char* path, bool& skipEntry)
463 #ifdef _WIN32
464 bool success = true;
466 if (dir->mAtEnd)
467 return false;
469 const char* entry = dir->mEntry.cFileName;
471 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
472 skipEntry = true;
473 success = true;
474 } else {
475 skipEntry = false;
476 success = true;
479 char entrypathname[PATH_MAX];
480 strncpy(entrypathname, dirname, PATH_MAX);
481 sc_AppendToPath(entrypathname, PATH_MAX, dir->mEntry.cFileName);
483 bool isAlias = false;
484 sc_ResolveIfAlias(entrypathname, path, isAlias, PATH_MAX);
486 if (!::FindNextFile(dir->mHandle, &dir->mEntry)) {
487 dir->mAtEnd = true;
490 return true;
491 #else
492 if (!dir->mHandle)
493 return false;
495 dir->mEntry = readdir(dir->mHandle);
496 if (!dir->mEntry)
497 return false;
499 const char* entry = dir->mEntry->d_name;
501 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
502 skipEntry = true;
503 return true;
504 } else {
505 skipEntry = false;
508 // construct path from dir entry
509 char entrypathname[PATH_MAX];
510 strncpy(entrypathname, dirname, PATH_MAX);
511 sc_AppendToPath(entrypathname, PATH_MAX, dir->mEntry->d_name);
513 // resolve path
514 bool isAlias = false;
515 if (sc_ResolveIfAlias(entrypathname, path, isAlias, strlen(entrypathname))<0)
517 skipEntry = true;
520 return true;
521 #endif
525 // Globbing
527 struct SC_GlobHandle
529 #ifdef _WIN32
530 HANDLE mHandle;
531 char mFolder[PATH_MAX];
532 WIN32_FIND_DATA mEntry;
533 char mEntryPath[PATH_MAX];
534 bool mAtEnd;
535 #else
536 glob_t mHandle;
537 size_t mEntry;
538 #endif
541 SC_GlobHandle* sc_Glob(const char* pattern)
543 SC_GlobHandle* glob = new SC_GlobHandle;
545 #ifdef _WIN32
546 char patternWin[1024];
548 strncpy(patternWin, pattern, 1024);
549 patternWin[1023] = 0;
550 win32_ReplaceCharInString(patternWin, 1024, '/', '\\');
552 win32_ExtractContainingFolder(glob->mFolder, patternWin, PATH_MAX);
554 glob->mHandle = ::FindFirstFile(patternWin, &glob->mEntry);
555 if (glob->mHandle == INVALID_HANDLE_VALUE) {
556 delete glob;
557 return 0;
560 glob->mAtEnd = false;
561 #else
562 int flags = GLOB_MARK | GLOB_TILDE;
563 #ifdef __APPLE__
564 flags |= GLOB_QUOTE;
565 #endif
567 int err = ::glob(pattern, flags, NULL, &glob->mHandle);
568 if (err < 0) {
569 delete glob;
570 return 0;
573 glob->mEntry = 0;
574 #endif
576 return glob;
579 void sc_GlobFree(SC_GlobHandle* glob)
581 #ifdef _WIN32
582 ::FindClose(glob->mHandle);
583 #else
584 globfree(&glob->mHandle);
585 #endif
586 delete glob;
589 const char* sc_GlobNext(SC_GlobHandle* glob)
591 #ifdef _WIN32
592 if (glob->mAtEnd)
593 return 0;
594 strncpy(glob->mEntryPath, glob->mFolder, PATH_MAX);
595 sc_AppendToPath(glob->mEntryPath, PATH_MAX, glob->mEntry.cFileName);
596 if (!::FindNextFile(glob->mHandle, &glob->mEntry))
597 glob->mAtEnd = true;
598 return glob->mEntryPath;
599 #else
600 if (glob->mEntry >= glob->mHandle.gl_pathc)
601 return 0;
602 return glob->mHandle.gl_pathv[glob->mEntry++];
603 #endif