common: win32utils - compile fix
[supercollider.git] / common / SC_DirUtils.cpp
blobf59f7351335a14924d671cc41808d92b8c968155
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);
230 #elif defined(SC_IPHONE)
232 bool sc_IsStandAlone()
234 return false;
237 void sc_GetResourceDirectory(char* pathBuf, int length)
239 sc_GetUserAppSupportDirectory(pathBuf, length);
242 #elif defined(__unix__)
244 bool sc_IsStandAlone()
246 return false;
249 void sc_GetResourceDirectory(char* pathBuf, int length)
251 #ifdef SC_DATA_DIR
252 strncpy(pathBuf, SC_DATA_DIR, length);
253 #else
254 strncpy(pathBuf, "/usr/share/SuperCollider", length);
255 #endif
258 #else
260 bool sc_IsStandAlone()
262 return false;
265 static void sc_GetResourceDirectoryFromAppDirectory(char* pathBuf, int length)
267 char * result = getcwd(pathBuf, length);
268 if (result != pathBuf)
269 throw std::runtime_error("cannot get current working directory");
273 void sc_GetResourceDirectory(char* pathBuf, int length)
275 return sc_GetResourceDirectoryFromAppDirectory(pathBuf, length);
278 #endif
282 // Support for Extensions
284 // Get the user home directory.
286 void sc_GetUserHomeDirectory(char *str, int size)
288 #ifndef _WIN32
289 const char *home = getenv("HOME");
290 if(home!=NULL){
291 strncpy(str, home, size);
292 }else{
293 // cwd is not the user home directory; but this is better than leaving mem uninitialised.
294 strcpy(str, "./");
296 #else
297 win32_GetHomeFolder(str,size);
298 #endif
302 // Get the System level data directory.
304 void sc_GetSystemAppSupportDirectory(char *str, int size)
306 strncpy(str,
307 #if defined(SC_DATA_DIR)
308 SC_DATA_DIR,
309 #elif defined(SC_IPHONE)
310 "/",
311 #elif defined(__APPLE__)
312 "/Library/Application Support/SuperCollider",
313 #elif defined(_WIN32)
314 ( getenv("SC_SYSAPPSUP_PATH")==NULL ) ? "C:\\SuperCollider" : getenv("SC_SYSAPPSUP_PATH"),
315 #else
316 "/usr/local/share/SuperCollider",
317 #endif
318 size);
322 // Get the User level data directory.
324 void sc_GetUserAppSupportDirectory(char *str, int size)
326 // XDG support according to http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
327 const char * xdg_data_home = getenv("XDG_DATA_HOME");
328 if (xdg_data_home) {
329 strncpy(str, xdg_data_home, size);
330 sc_AppendToPath(str, size, "SuperCollider");
331 return;
334 sc_GetUserHomeDirectory(str, size);
336 #if defined(SC_IPHONE)
337 sc_AppendToPath(str, size, "Documents");
338 #elif defined(__APPLE__)
339 sc_AppendToPath(str, size, "Library/Application Support/SuperCollider");
340 #elif defined(_WIN32)
341 sc_AppendToPath(str, size, "SuperCollider");
342 #else
343 sc_AppendToPath(str, size, ".local/share/SuperCollider");
344 #endif
348 // Get the System level 'Extensions' directory.
350 void sc_GetSystemExtensionDirectory(char *str, int size)
352 sc_GetSystemAppSupportDirectory(str, size);
353 sc_AppendToPath(str, size, "Extensions");
357 // Get the System level 'Extensions' directory.
359 void sc_GetUserExtensionDirectory(char *str, int size)
361 sc_GetUserAppSupportDirectory(str, size);
362 sc_AppendToPath(str, size, "Extensions");
365 // Get the directory for the configuration files.
366 void sc_GetUserConfigDirectory(char *str, int size)
368 // XDG support according to http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
369 const char * xdg_config_home = getenv("XDG_CONFIG_HOME");
370 if (xdg_config_home) {
371 strncpy(str, xdg_config_home, size);
372 sc_AppendToPath(str, size, "SuperCollider");
373 return;
376 #if defined(__linux__) || defined(__freebsd__)
377 char * home = str;
379 sc_GetUserHomeDirectory(str, size);
380 sc_AppendToPath(str, size, ".config/SuperCollider");
381 #else
382 sc_GetUserAppSupportDirectory(str, size);
383 #endif
387 // Directory access
389 struct SC_DirHandle
391 #ifdef _WIN32
392 HANDLE mHandle;
393 WIN32_FIND_DATA mEntry;
394 bool mAtEnd;
395 #else
396 DIR* mHandle;
397 struct dirent* mEntry;
398 #endif
401 SC_DirHandle* sc_OpenDir(const char* dirname)
403 SC_DirHandle* dir = new SC_DirHandle;
404 memset(dir, 0, sizeof(SC_DirHandle));
406 #ifdef _WIN32
407 char allInDir[PATH_MAX];
408 snprintf(allInDir, PATH_MAX, "%s\\*.*", dirname);
410 dir->mHandle = ::FindFirstFile(allInDir, &dir->mEntry);
411 if (dir->mHandle == INVALID_HANDLE_VALUE) {
412 delete dir;
413 return 0;
416 dir->mAtEnd = false;
417 #else
418 dir->mHandle = opendir(dirname);
419 if (!dir->mHandle) {
420 delete dir;
421 return 0;
423 #endif
425 return dir;
428 void sc_CloseDir(SC_DirHandle* dir)
430 #ifdef _WIN32
431 ::FindClose(dir->mHandle);
432 #else
433 closedir(dir->mHandle);
434 #endif
435 delete dir;
438 bool sc_ReadDir(SC_DirHandle* dir, const char* dirname, char* path, bool& skipEntry)
440 #ifdef _WIN32
441 bool success = true;
443 if (dir->mAtEnd)
444 return false;
446 const char* entry = dir->mEntry.cFileName;
448 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
449 skipEntry = true;
450 success = true;
451 } else {
452 skipEntry = false;
453 success = true;
456 char entrypathname[PATH_MAX];
457 strncpy(entrypathname, dirname, PATH_MAX);
458 sc_AppendToPath(entrypathname, PATH_MAX, dir->mEntry.cFileName);
460 bool isAlias = false;
461 sc_ResolveIfAlias(entrypathname, path, isAlias, PATH_MAX);
463 if (!::FindNextFile(dir->mHandle, &dir->mEntry)) {
464 dir->mAtEnd = true;
467 return true;
468 #else
469 if (!dir->mHandle)
470 return false;
472 dir->mEntry = readdir(dir->mHandle);
473 if (!dir->mEntry)
474 return false;
476 const char* entry = dir->mEntry->d_name;
478 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
479 skipEntry = true;
480 return true;
481 } else {
482 skipEntry = false;
485 // construct path from dir entry
486 char entrypathname[PATH_MAX];
487 strncpy(entrypathname, dirname, PATH_MAX);
488 sc_AppendToPath(entrypathname, PATH_MAX, dir->mEntry->d_name);
490 // resolve path
491 bool isAlias = false;
492 if (sc_ResolveIfAlias(entrypathname, path, isAlias, strlen(entrypathname))<0)
494 skipEntry = true;
497 return true;
498 #endif
502 // Globbing
504 struct SC_GlobHandle
506 #ifdef _WIN32
507 HANDLE mHandle;
508 char mFolder[PATH_MAX];
509 WIN32_FIND_DATA mEntry;
510 char mEntryPath[PATH_MAX];
511 bool mAtEnd;
512 #else
513 glob_t mHandle;
514 size_t mEntry;
515 #endif
518 SC_GlobHandle* sc_Glob(const char* pattern)
520 SC_GlobHandle* glob = new SC_GlobHandle;
522 #ifdef _WIN32
523 char patternWin[1024];
525 strncpy(patternWin, pattern, 1024);
526 patternWin[1023] = 0;
527 win32_ReplaceCharInString(patternWin, 1024, '/', '\\');
529 win32_ExtractContainingFolder(glob->mFolder, patternWin, PATH_MAX);
531 glob->mHandle = ::FindFirstFile(patternWin, &glob->mEntry);
532 if (glob->mHandle == INVALID_HANDLE_VALUE) {
533 delete glob;
534 return 0;
537 glob->mAtEnd = false;
538 #else
539 int flags = GLOB_MARK | GLOB_TILDE;
540 #ifdef __APPLE__
541 flags |= GLOB_QUOTE;
542 #endif
544 int err = ::glob(pattern, flags, NULL, &glob->mHandle);
545 if (err < 0) {
546 delete glob;
547 return 0;
550 glob->mEntry = 0;
551 #endif
553 return glob;
556 void sc_GlobFree(SC_GlobHandle* glob)
558 #ifdef _WIN32
559 ::FindClose(glob->mHandle);
560 #else
561 globfree(&glob->mHandle);
562 #endif
563 delete glob;
566 const char* sc_GlobNext(SC_GlobHandle* glob)
568 #ifdef _WIN32
569 if (glob->mAtEnd)
570 return 0;
571 strncpy(glob->mEntryPath, glob->mFolder, PATH_MAX);
572 sc_AppendToPath(glob->mEntryPath, PATH_MAX, glob->mEntry.cFileName);
573 if (!::FindNextFile(glob->mHandle, &glob->mEntry))
574 glob->mAtEnd = true;
575 return glob->mEntryPath;
576 #else
577 if (glob->mEntry >= glob->mHandle.gl_pathc)
578 return 0;
579 return glob->mHandle.gl_pathv[glob->mEntry++];
580 #endif