SCDoc: Use proper static string constants instead of comparing string literals.
[supercollider.git] / common / SC_DirUtils.cpp
blob529d95f4feea506e6309b23c247b8bb877d21cb4
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 ((strcasecmp(name, "help") == 0) ||
173 (strcasecmp(name, "ignore") == 0) ||
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 char * home = str;
404 sc_GetUserHomeDirectory(str, size);
405 sc_AppendToPath(str, size, ".config/SuperCollider");
406 #else
407 sc_GetUserAppSupportDirectory(str, size);
408 #endif
412 // Directory access
414 struct SC_DirHandle
416 #ifdef _WIN32
417 HANDLE mHandle;
418 WIN32_FIND_DATA mEntry;
419 bool mAtEnd;
420 #else
421 DIR* mHandle;
422 struct dirent* mEntry;
423 #endif
426 SC_DirHandle* sc_OpenDir(const char* dirname)
428 SC_DirHandle* dir = new SC_DirHandle;
429 memset(dir, 0, sizeof(SC_DirHandle));
431 #ifdef _WIN32
432 char allInDir[PATH_MAX];
433 snprintf(allInDir, PATH_MAX, "%s\\*.*", dirname);
435 dir->mHandle = ::FindFirstFile(allInDir, &dir->mEntry);
436 if (dir->mHandle == INVALID_HANDLE_VALUE) {
437 delete dir;
438 return 0;
441 dir->mAtEnd = false;
442 #else
443 dir->mHandle = opendir(dirname);
444 if (!dir->mHandle) {
445 delete dir;
446 return 0;
448 #endif
450 return dir;
453 void sc_CloseDir(SC_DirHandle* dir)
455 #ifdef _WIN32
456 ::FindClose(dir->mHandle);
457 #else
458 closedir(dir->mHandle);
459 #endif
460 delete dir;
463 bool sc_ReadDir(SC_DirHandle* dir, const char* dirname, char* path, bool& skipEntry)
465 #ifdef _WIN32
466 bool success = true;
468 if (dir->mAtEnd)
469 return false;
471 const char* entry = dir->mEntry.cFileName;
473 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
474 skipEntry = true;
475 success = true;
476 } else {
477 skipEntry = false;
478 success = true;
481 char entrypathname[PATH_MAX];
482 strncpy(entrypathname, dirname, PATH_MAX);
483 sc_AppendToPath(entrypathname, PATH_MAX, dir->mEntry.cFileName);
485 bool isAlias = false;
486 sc_ResolveIfAlias(entrypathname, path, isAlias, PATH_MAX);
488 if (!::FindNextFile(dir->mHandle, &dir->mEntry)) {
489 dir->mAtEnd = true;
492 return true;
493 #else
494 if (!dir->mHandle)
495 return false;
497 dir->mEntry = readdir(dir->mHandle);
498 if (!dir->mEntry)
499 return false;
501 const char* entry = dir->mEntry->d_name;
503 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
504 skipEntry = true;
505 return true;
506 } else {
507 skipEntry = false;
510 // construct path from dir entry
511 char entrypathname[PATH_MAX];
512 strncpy(entrypathname, dirname, PATH_MAX);
513 sc_AppendToPath(entrypathname, PATH_MAX, dir->mEntry->d_name);
515 // resolve path
516 bool isAlias = false;
517 if (sc_ResolveIfAlias(entrypathname, path, isAlias, strlen(entrypathname))<0)
519 skipEntry = true;
522 return true;
523 #endif
527 // Globbing
529 struct SC_GlobHandle
531 #ifdef _WIN32
532 HANDLE mHandle;
533 char mFolder[PATH_MAX];
534 WIN32_FIND_DATA mEntry;
535 char mEntryPath[PATH_MAX];
536 bool mAtEnd;
537 #else
538 glob_t mHandle;
539 size_t mEntry;
540 #endif
543 SC_GlobHandle* sc_Glob(const char* pattern)
545 SC_GlobHandle* glob = new SC_GlobHandle;
547 #ifdef _WIN32
548 char patternWin[1024];
550 strncpy(patternWin, pattern, 1024);
551 patternWin[1023] = 0;
552 win32_ReplaceCharInString(patternWin, 1024, '/', '\\');
554 win32_ExtractContainingFolder(glob->mFolder, patternWin, PATH_MAX);
556 glob->mHandle = ::FindFirstFile(patternWin, &glob->mEntry);
557 if (glob->mHandle == INVALID_HANDLE_VALUE) {
558 delete glob;
559 return 0;
562 glob->mAtEnd = false;
563 #else
564 int flags = GLOB_MARK | GLOB_TILDE;
565 #ifdef __APPLE__
566 flags |= GLOB_QUOTE;
567 #endif
569 int err = ::glob(pattern, flags, NULL, &glob->mHandle);
570 if (err < 0) {
571 delete glob;
572 return 0;
575 glob->mEntry = 0;
576 #endif
578 return glob;
581 void sc_GlobFree(SC_GlobHandle* glob)
583 #ifdef _WIN32
584 ::FindClose(glob->mHandle);
585 #else
586 globfree(&glob->mHandle);
587 #endif
588 delete glob;
591 const char* sc_GlobNext(SC_GlobHandle* glob)
593 #ifdef _WIN32
594 if (glob->mAtEnd)
595 return 0;
596 strncpy(glob->mEntryPath, glob->mFolder, PATH_MAX);
597 sc_AppendToPath(glob->mEntryPath, PATH_MAX, glob->mEntry.cFileName);
598 if (!::FindNextFile(glob->mHandle, &glob->mEntry))
599 glob->mAtEnd = true;
600 return glob->mEntryPath;
601 #else
602 if (glob->mEntry >= glob->mHandle.gl_pathc)
603 return 0;
604 return glob->mHandle.gl_pathv[glob->mEntry++];
605 #endif