SCDoc: improved regexps
[supercollider.git] / common / SC_DirUtils.cpp
blob62f65490d8c69ca1f842e0ce4af18f98199807d1
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)
72 char newpath1[MAXPATHLEN];
74 newpath1[0] = '\0';
75 newpath2[0] = '\0';
77 size_t pathLen = strlen(path);
79 if ((pathLen >= 2) && (path[0] == '~') && ((path[1] == '/') || (path[1] == '\\'))) {
80 char home[PATH_MAX];
81 sc_GetUserHomeDirectory(home, PATH_MAX);
83 if (home != 0) {
84 if ((pathLen - 1 + strlen(home)) >= MAXPATHLEN) {
85 return 0;
87 strcpy(newpath1, home);
88 strcat(newpath1, path + 1);
89 } else {
90 if (pathLen >= MAXPATHLEN) {
91 return 0;
93 strcpy(newpath1, path);
94 newpath1[0] = '.';
96 } else {
97 if (pathLen >= MAXPATHLEN) {
98 return 0;
100 strcpy(newpath1, path);
103 bool isAlias = false;
104 if(sc_ResolveIfAlias(newpath1, newpath2, isAlias, PATH_MAX)!=0) {
105 strcpy(newpath2, newpath1);
108 return newpath2;
112 // Returns TRUE iff dirname is an existing directory.
114 bool sc_DirectoryExists(const char *dirname)
116 #if defined(_WIN32)
117 DWORD attr = GetFileAttributes(dirname);
118 return ((attr != INVALID_FILE_ATTRIBUTES) &&
119 (attr & FILE_ATTRIBUTE_DIRECTORY));
120 #else
121 struct stat buf;
122 return ((stat(dirname, &buf) == 0) &&
123 S_ISDIR(buf.st_mode));
124 #endif
127 bool sc_IsSymlink(const char* path)
129 #if defined(_WIN32)
130 // FIXME
131 return false;
132 #else
133 struct stat buf;
135 return ((stat(path, &buf) == 0) &&
136 S_ISLNK(buf.st_mode));
137 #endif
140 bool sc_IsNonHostPlatformDir(const char *name)
142 #if defined(SC_IPHONE)
143 const char a[] = "linux", b[] = "windows", c[]="osx";
144 #elif defined(__APPLE__)
145 const char a[] = "linux", b[] = "windows", c[]="iphone";
146 #elif defined(__linux__)
147 const char a[] = "osx", b[] = "windows", c[]="iphone";
148 #elif defined(__FreeBSD__)
149 const char a[] = "osx", b[] = "windows", c[]="iphone";
150 #elif defined(_WIN32)
151 const char a[] = "osx", b[] = "linux", c[]="iphone";
152 #endif
153 return ((strcmp(name, a) == 0) ||
154 (strcmp(name, b) == 0) ||
155 (strcmp(name, c) == 0));
159 // Returns TRUE iff 'name' is special directory '.' or '..'
161 inline static bool sc_IsSpecialDirectory(const char* name)
163 return (strcmp(name, ".") == 0) || (strcmp(name, "..") == 0);
166 // Returns TRUE iff 'name' is to be ignored during compilation.
168 bool sc_SkipDirectory(const char *name)
170 return ((strcasecmp(name, "help") == 0) ||
171 (strcasecmp(name, "ignore") == 0) ||
172 (strcmp(name, ".svn") == 0) ||
173 (strcmp(name, ".git") == 0) ||
174 (strcmp(name, "_darcs") == 0) ||
175 ((strncmp(name, "scide_", 6) == 0) && (strcmp(name+6, gIdeName) != 0)) ||
176 sc_IsNonHostPlatformDir(name));
180 int sc_ResolveIfAlias(const char *path, char *returnPath, bool &isAlias, int length)
182 isAlias = false;
183 #if defined(__APPLE__) && !defined(SC_IPHONE)
184 FSRef dirRef;
185 OSStatus osStatusErr = FSPathMakeRef ((const UInt8 *) path, &dirRef, NULL);
186 if ( !osStatusErr ) {
187 Boolean isFolder;
188 Boolean wasAliased;
189 OSErr err = FSResolveAliasFile (&dirRef, true, &isFolder, &wasAliased);
190 if (err)
192 return -1;
194 isAlias = wasAliased;
195 if (wasAliased)
197 UInt8 resolvedPath[PATH_MAX];
198 osStatusErr = FSRefMakePath (&dirRef, resolvedPath, length);
199 if (osStatusErr)
201 return -1;
203 strncpy(returnPath, (char *) resolvedPath, length);
204 return 0;
207 #endif
208 strcpy(returnPath, path);
209 return 0;
212 // Support for Bundles
214 #if defined(__APPLE__) && !defined(SC_IPHONE) // running on OS X
216 // Support for stand-alone applications
218 bool sc_IsStandAlone()
220 return SC_StandAloneInfo::IsStandAlone();
223 void sc_GetResourceDirectory(char* pathBuf, int length)
225 SC_StandAloneInfo::GetResourceDir(pathBuf, length);
228 #elif defined(SC_IPHONE)
230 bool sc_IsStandAlone()
232 return false;
235 void sc_GetResourceDirectory(char* pathBuf, int length)
237 sc_GetUserAppSupportDirectory(pathBuf, length);
240 #elif defined(__unix__)
242 bool sc_IsStandAlone()
244 return false;
247 void sc_GetResourceDirectory(char* pathBuf, int length)
249 #ifdef SC_DATA_DIR
250 strncpy(pathBuf, SC_DATA_DIR, length);
251 #else
252 strncpy(pathBuf, "/usr/share/SuperCollider", length);
253 #endif
256 #else
258 bool sc_IsStandAlone()
260 return false;
263 static void sc_GetResourceDirectoryFromAppDirectory(char* pathBuf, int length)
265 char * result = getcwd(pathBuf, length);
266 if (result != pathBuf)
267 throw std::runtime_error("cannot get current working directory");
271 void sc_GetResourceDirectory(char* pathBuf, int length)
273 return sc_GetResourceDirectoryFromAppDirectory(pathBuf, length);
276 #endif
280 // Support for Extensions
282 // Get the user home directory.
284 void sc_GetUserHomeDirectory(char *str, int size)
286 #ifndef _WIN32
287 char *home = getenv("HOME");
288 if(home!=NULL){
289 strncpy(str, home, size);
290 }else{
291 // cwd is not the user home directory; but this is better than leaving mem uninitialised.
292 strcpy(str, "./");
294 #else
295 win32_GetHomeFolder(str,size);
296 #endif
300 // Get the System level data directory.
302 void sc_GetSystemAppSupportDirectory(char *str, int size)
304 strncpy(str,
305 #if defined(SC_DATA_DIR)
306 SC_DATA_DIR,
307 #elif defined(SC_IPHONE)
308 "/",
309 #elif defined(__APPLE__)
310 "/Library/Application Support/SuperCollider",
311 #elif defined(_WIN32)
312 ( getenv("SC_SYSAPPSUP_PATH")==NULL ) ? "C:\\SuperCollider" : getenv("SC_SYSAPPSUP_PATH"),
313 #else
314 "/usr/local/share/SuperCollider",
315 #endif
316 size);
320 // Get the User level data directory.
322 void sc_GetUserAppSupportDirectory(char *str, int size)
324 // XDG support according to http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
325 char * xdg_data_home = getenv("XDG_DATA_HOME");
326 if (xdg_data_home) {
327 string config_folder = string(xdg_data_home) + SC_PATH_DELIMITER + "SuperCollider";
328 strncpy(str, config_folder.c_str(), size);
329 return;
332 char home[PATH_MAX];
333 sc_GetUserHomeDirectory(home, PATH_MAX);
335 snprintf(str,
336 size,
337 #if defined(SC_IPHONE)
338 "%s/Documents",
339 #elif defined(__APPLE__)
340 "%s/Library/Application Support/SuperCollider",
341 #elif defined(_WIN32)
342 "%s\\SuperCollider",
343 #else
344 "%s/.local/share/SuperCollider",
345 #endif
346 home);
350 // Get the System level 'Extensions' directory.
352 void sc_GetSystemExtensionDirectory(char *str, int size)
354 char path[PATH_MAX];
355 sc_GetSystemAppSupportDirectory(path, sizeof(path));
356 sc_AppendToPath(path, "Extensions");
357 strncpy(str, path, size);
361 // Get the System level 'Extensions' directory.
363 void sc_GetUserExtensionDirectory(char *str, int size)
365 char path[PATH_MAX];
366 sc_GetUserAppSupportDirectory(path, sizeof(path));
367 sc_AppendToPath(path, "Extensions");
368 strncpy(str, path, size);
371 // Get the directory for the configuration files.
372 void sc_GetUserConfigDirectory(char *str, int size)
374 // XDG support according to http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
375 char * xdg_config_home = getenv("XDG_CONFIG_HOME");
376 if (xdg_config_home) {
377 string config_folder = string(xdg_config_home) + SC_PATH_DELIMITER + "SuperCollider";
378 strncpy(str, config_folder.c_str(), size);
379 return;
382 #if defined(__linux__) || defined(__freebsd__)
383 char home[PATH_MAX];
385 sc_GetUserHomeDirectory(home, PATH_MAX);
386 sc_AppendToPath(home, ".config/SuperCollider");
387 strncpy(str, home, size);
388 #else
389 sc_GetUserAppSupportDirectory(str, size);
390 #endif
394 // Directory access
396 struct SC_DirHandle
398 #ifdef _WIN32
399 HANDLE mHandle;
400 WIN32_FIND_DATA mEntry;
401 bool mAtEnd;
402 #else
403 DIR* mHandle;
404 struct dirent* mEntry;
405 #endif
408 SC_DirHandle* sc_OpenDir(const char* dirname)
410 SC_DirHandle* dir = new SC_DirHandle;
411 memset(dir, 0, sizeof(SC_DirHandle));
413 #ifdef _WIN32
414 char allInDir[PATH_MAX];
415 snprintf(allInDir, PATH_MAX, "%s\\*.*", dirname);
417 dir->mHandle = ::FindFirstFile(allInDir, &dir->mEntry);
418 if (dir->mHandle == INVALID_HANDLE_VALUE) {
419 delete dir;
420 return 0;
423 dir->mAtEnd = false;
424 #else
425 dir->mHandle = opendir(dirname);
426 if (!dir->mHandle) {
427 delete dir;
428 return 0;
430 #endif
432 return dir;
435 void sc_CloseDir(SC_DirHandle* dir)
437 #ifdef _WIN32
438 ::FindClose(dir->mHandle);
439 #else
440 closedir(dir->mHandle);
441 #endif
442 delete dir;
445 bool sc_ReadDir(SC_DirHandle* dir, const char* dirname, char* path, bool& skipEntry)
447 #ifdef _WIN32
448 bool success = true;
450 if (dir->mAtEnd)
451 return false;
453 const char* entry = dir->mEntry.cFileName;
455 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
456 skipEntry = true;
457 success = true;
458 } else {
459 skipEntry = false;
460 success = true;
463 char entrypathname[PATH_MAX];
464 strncpy(entrypathname, dirname, PATH_MAX);
465 sc_AppendToPath(entrypathname, dir->mEntry.cFileName);
467 bool isAlias = false;
468 sc_ResolveIfAlias(entrypathname, path, isAlias, PATH_MAX);
470 if (!::FindNextFile(dir->mHandle, &dir->mEntry)) {
471 dir->mAtEnd = true;
474 return true;
475 #else
476 if (!dir->mHandle)
477 return false;
479 dir->mEntry = readdir(dir->mHandle);
480 if (!dir->mEntry)
481 return false;
483 const char* entry = dir->mEntry->d_name;
485 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
486 skipEntry = true;
487 return true;
488 } else {
489 skipEntry = false;
492 // construct path from dir entry
493 char entrypathname[PATH_MAX];
494 strncpy(entrypathname, dirname, PATH_MAX);
495 sc_AppendToPath(entrypathname, dir->mEntry->d_name);
497 // resolve path
498 bool isAlias = false;
499 if (sc_ResolveIfAlias(entrypathname, path, isAlias, strlen(entrypathname))<0)
501 skipEntry = true;
504 return true;
505 #endif
509 // Globbing
511 struct SC_GlobHandle
513 #ifdef _WIN32
514 HANDLE mHandle;
515 char mFolder[PATH_MAX];
516 WIN32_FIND_DATA mEntry;
517 char mEntryPath[PATH_MAX];
518 bool mAtEnd;
519 #else
520 glob_t mHandle;
521 size_t mEntry;
522 #endif
525 SC_GlobHandle* sc_Glob(const char* pattern)
527 SC_GlobHandle* glob = new SC_GlobHandle;
529 #ifdef _WIN32
530 char patternWin[1024];
532 strncpy(patternWin, pattern, 1024);
533 patternWin[1023] = 0;
534 win32_ReplaceCharInString(patternWin, 1024, '/', '\\');
536 win32_ExtractContainingFolder(glob->mFolder, patternWin, PATH_MAX);
538 glob->mHandle = ::FindFirstFile(patternWin, &glob->mEntry);
539 if (glob->mHandle == INVALID_HANDLE_VALUE) {
540 delete glob;
541 return 0;
544 glob->mAtEnd = false;
545 #else
546 int flags = GLOB_MARK | GLOB_TILDE;
547 #ifdef __APPLE__
548 flags |= GLOB_QUOTE;
549 #endif
551 int err = ::glob(pattern, flags, NULL, &glob->mHandle);
552 if (err < 0) {
553 delete glob;
554 return 0;
557 glob->mEntry = 0;
558 #endif
560 return glob;
563 void sc_GlobFree(SC_GlobHandle* glob)
565 #ifdef _WIN32
566 ::FindClose(glob->mHandle);
567 #else
568 globfree(&glob->mHandle);
569 #endif
570 delete glob;
573 const char* sc_GlobNext(SC_GlobHandle* glob)
575 #ifdef _WIN32
576 if (glob->mAtEnd)
577 return 0;
578 strncpy(glob->mEntryPath, glob->mFolder, PATH_MAX);
579 sc_AppendToPath(glob->mEntryPath, glob->mEntry.cFileName);
580 if (!::FindNextFile(glob->mHandle, &glob->mEntry))
581 glob->mAtEnd = true;
582 return glob->mEntryPath;
583 #else
584 if (glob->mEntry >= glob->mHandle.gl_pathc)
585 return 0;
586 return glob->mHandle.gl_pathv[glob->mEntry++];
587 #endif