common: prevent buffer overflow
[supercollider.git] / common / SC_DirUtils.cpp
blob7b428d446fea8cf26c0deb4e4079ccd1eceff582
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, size_t max_size, const char *component)
62 size_t currentLength = strlen(path);
63 if (currentLength >= max_size-1)
64 return;
65 path[currentLength] = SC_PATH_DELIMITER[0];
66 path[currentLength+1] = 0;
67 ++currentLength;
69 char * tail = path + currentLength;
70 size_t remain = max_size - currentLength;
72 strncat(tail, component, remain);
76 char *sc_StandardizePath(const char *path, char *newpath2)
78 char newpath1[MAXPATHLEN];
80 newpath1[0] = '\0';
81 newpath2[0] = '\0';
83 size_t pathLen = strlen(path);
85 if ((pathLen >= 2) && (path[0] == '~') && ((path[1] == '/') || (path[1] == '\\'))) {
86 char home[PATH_MAX];
87 sc_GetUserHomeDirectory(home, PATH_MAX);
89 if (home != 0) {
90 if ((pathLen - 1 + strlen(home)) >= MAXPATHLEN) {
91 return 0;
93 strcpy(newpath1, home);
94 strcat(newpath1, path + 1);
95 } else {
96 if (pathLen >= MAXPATHLEN) {
97 return 0;
99 strcpy(newpath1, path);
100 newpath1[0] = '.';
102 } else {
103 if (pathLen >= MAXPATHLEN) {
104 return 0;
106 strcpy(newpath1, path);
109 bool isAlias = false;
110 if(sc_ResolveIfAlias(newpath1, newpath2, isAlias, PATH_MAX)!=0) {
111 strcpy(newpath2, newpath1);
114 return newpath2;
118 // Returns TRUE iff dirname is an existing directory.
120 bool sc_DirectoryExists(const char *dirname)
122 #if defined(_WIN32)
123 DWORD attr = GetFileAttributes(dirname);
124 return ((attr != INVALID_FILE_ATTRIBUTES) &&
125 (attr & FILE_ATTRIBUTE_DIRECTORY));
126 #else
127 struct stat buf;
128 return ((stat(dirname, &buf) == 0) &&
129 S_ISDIR(buf.st_mode));
130 #endif
133 bool sc_IsSymlink(const char* path)
135 #if defined(_WIN32)
136 // FIXME
137 return false;
138 #else
139 struct stat buf;
141 return ((stat(path, &buf) == 0) &&
142 S_ISLNK(buf.st_mode));
143 #endif
146 bool sc_IsNonHostPlatformDir(const char *name)
148 #if defined(SC_IPHONE)
149 const char a[] = "linux", b[] = "windows", c[]="osx";
150 #elif defined(__APPLE__)
151 const char a[] = "linux", b[] = "windows", c[]="iphone";
152 #elif defined(__linux__)
153 const char a[] = "osx", b[] = "windows", c[]="iphone";
154 #elif defined(__FreeBSD__)
155 const char a[] = "osx", b[] = "windows", c[]="iphone";
156 #elif defined(_WIN32)
157 const char a[] = "osx", b[] = "linux", c[]="iphone";
158 #endif
159 return ((strcmp(name, a) == 0) ||
160 (strcmp(name, b) == 0) ||
161 (strcmp(name, c) == 0));
165 // Returns TRUE iff 'name' is special directory '.' or '..'
167 inline static bool sc_IsSpecialDirectory(const char* name)
169 return (strcmp(name, ".") == 0) || (strcmp(name, "..") == 0);
172 // Returns TRUE iff 'name' is to be ignored during compilation.
174 bool sc_SkipDirectory(const char *name)
176 return ((strcasecmp(name, "help") == 0) ||
177 (strcasecmp(name, "ignore") == 0) ||
178 (strcmp(name, ".svn") == 0) ||
179 (strcmp(name, ".git") == 0) ||
180 (strcmp(name, "_darcs") == 0) ||
181 ((strncmp(name, "scide_", 6) == 0) && (strcmp(name+6, gIdeName) != 0)) ||
182 sc_IsNonHostPlatformDir(name));
186 int sc_ResolveIfAlias(const char *path, char *returnPath, bool &isAlias, int length)
188 isAlias = false;
189 #if defined(__APPLE__) && !defined(SC_IPHONE)
190 FSRef dirRef;
191 OSStatus osStatusErr = FSPathMakeRef ((const UInt8 *) path, &dirRef, NULL);
192 if ( !osStatusErr ) {
193 Boolean isFolder;
194 Boolean wasAliased;
195 OSErr err = FSResolveAliasFile (&dirRef, true, &isFolder, &wasAliased);
196 if (err)
198 return -1;
200 isAlias = wasAliased;
201 if (wasAliased)
203 UInt8 resolvedPath[PATH_MAX];
204 osStatusErr = FSRefMakePath (&dirRef, resolvedPath, length);
205 if (osStatusErr)
207 return -1;
209 strncpy(returnPath, (char *) resolvedPath, length);
210 return 0;
213 #endif
214 strcpy(returnPath, path);
215 return 0;
218 // Support for Bundles
220 #if defined(__APPLE__) && !defined(SC_IPHONE) // running on OS X
222 // Support for stand-alone applications
224 bool sc_IsStandAlone()
226 return SC_StandAloneInfo::IsStandAlone();
229 void sc_GetResourceDirectory(char* pathBuf, int length)
231 SC_StandAloneInfo::GetResourceDir(pathBuf, length);
234 #elif defined(SC_IPHONE)
236 bool sc_IsStandAlone()
238 return false;
241 void sc_GetResourceDirectory(char* pathBuf, int length)
243 sc_GetUserAppSupportDirectory(pathBuf, length);
246 #elif defined(__unix__)
248 bool sc_IsStandAlone()
250 return false;
253 void sc_GetResourceDirectory(char* pathBuf, int length)
255 #ifdef SC_DATA_DIR
256 strncpy(pathBuf, SC_DATA_DIR, length);
257 #else
258 strncpy(pathBuf, "/usr/share/SuperCollider", length);
259 #endif
262 #else
264 bool sc_IsStandAlone()
266 return false;
269 static void sc_GetResourceDirectoryFromAppDirectory(char* pathBuf, int length)
271 char * result = getcwd(pathBuf, length);
272 if (result != pathBuf)
273 throw std::runtime_error("cannot get current working directory");
277 void sc_GetResourceDirectory(char* pathBuf, int length)
279 return sc_GetResourceDirectoryFromAppDirectory(pathBuf, length);
282 #endif
286 // Support for Extensions
288 // Get the user home directory.
290 void sc_GetUserHomeDirectory(char *str, int size)
292 #ifndef _WIN32
293 char *home = getenv("HOME");
294 if(home!=NULL){
295 strncpy(str, home, size);
296 }else{
297 // cwd is not the user home directory; but this is better than leaving mem uninitialised.
298 strcpy(str, "./");
300 #else
301 win32_GetHomeFolder(str,size);
302 #endif
306 // Get the System level data directory.
308 void sc_GetSystemAppSupportDirectory(char *str, int size)
310 strncpy(str,
311 #if defined(SC_DATA_DIR)
312 SC_DATA_DIR,
313 #elif defined(SC_IPHONE)
314 "/",
315 #elif defined(__APPLE__)
316 "/Library/Application Support/SuperCollider",
317 #elif defined(_WIN32)
318 ( getenv("SC_SYSAPPSUP_PATH")==NULL ) ? "C:\\SuperCollider" : getenv("SC_SYSAPPSUP_PATH"),
319 #else
320 "/usr/local/share/SuperCollider",
321 #endif
322 size);
326 // Get the User level data directory.
328 void sc_GetUserAppSupportDirectory(char *str, int size)
330 // XDG support according to http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
331 char * xdg_data_home = getenv("XDG_DATA_HOME");
332 if (xdg_data_home) {
333 string config_folder = string(xdg_data_home) + SC_PATH_DELIMITER + "SuperCollider";
334 strncpy(str, config_folder.c_str(), size);
335 return;
338 char home[PATH_MAX];
339 sc_GetUserHomeDirectory(home, PATH_MAX);
341 snprintf(str,
342 size,
343 #if defined(SC_IPHONE)
344 "%s/Documents",
345 #elif defined(__APPLE__)
346 "%s/Library/Application Support/SuperCollider",
347 #elif defined(_WIN32)
348 "%s\\SuperCollider",
349 #else
350 "%s/.local/share/SuperCollider",
351 #endif
352 home);
356 // Get the System level 'Extensions' directory.
358 void sc_GetSystemExtensionDirectory(char *str, int size)
360 char path[PATH_MAX];
361 sc_GetSystemAppSupportDirectory(path, sizeof(path));
362 sc_AppendToPath(path, PATH_MAX, "Extensions");
363 strncpy(str, path, size);
367 // Get the System level 'Extensions' directory.
369 void sc_GetUserExtensionDirectory(char *str, int size)
371 char path[PATH_MAX];
372 sc_GetUserAppSupportDirectory(path, sizeof(path));
373 sc_AppendToPath(path, PATH_MAX, "Extensions");
374 strncpy(str, path, size);
377 // Get the directory for the configuration files.
378 void sc_GetUserConfigDirectory(char *str, int size)
380 // XDG support according to http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
381 char * xdg_config_home = getenv("XDG_CONFIG_HOME");
382 if (xdg_config_home) {
383 string config_folder = string(xdg_config_home) + SC_PATH_DELIMITER + "SuperCollider";
384 strncpy(str, config_folder.c_str(), size);
385 return;
388 #if defined(__linux__) || defined(__freebsd__)
389 char home[PATH_MAX];
391 sc_GetUserHomeDirectory(home, PATH_MAX);
392 sc_AppendToPath(home, PATH_MAX, ".config/SuperCollider");
393 strncpy(str, home, size);
394 #else
395 sc_GetUserAppSupportDirectory(str, size);
396 #endif
400 // Directory access
402 struct SC_DirHandle
404 #ifdef _WIN32
405 HANDLE mHandle;
406 WIN32_FIND_DATA mEntry;
407 bool mAtEnd;
408 #else
409 DIR* mHandle;
410 struct dirent* mEntry;
411 #endif
414 SC_DirHandle* sc_OpenDir(const char* dirname)
416 SC_DirHandle* dir = new SC_DirHandle;
417 memset(dir, 0, sizeof(SC_DirHandle));
419 #ifdef _WIN32
420 char allInDir[PATH_MAX];
421 snprintf(allInDir, PATH_MAX, "%s\\*.*", dirname);
423 dir->mHandle = ::FindFirstFile(allInDir, &dir->mEntry);
424 if (dir->mHandle == INVALID_HANDLE_VALUE) {
425 delete dir;
426 return 0;
429 dir->mAtEnd = false;
430 #else
431 dir->mHandle = opendir(dirname);
432 if (!dir->mHandle) {
433 delete dir;
434 return 0;
436 #endif
438 return dir;
441 void sc_CloseDir(SC_DirHandle* dir)
443 #ifdef _WIN32
444 ::FindClose(dir->mHandle);
445 #else
446 closedir(dir->mHandle);
447 #endif
448 delete dir;
451 bool sc_ReadDir(SC_DirHandle* dir, const char* dirname, char* path, bool& skipEntry)
453 #ifdef _WIN32
454 bool success = true;
456 if (dir->mAtEnd)
457 return false;
459 const char* entry = dir->mEntry.cFileName;
461 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
462 skipEntry = true;
463 success = true;
464 } else {
465 skipEntry = false;
466 success = true;
469 char entrypathname[PATH_MAX];
470 strncpy(entrypathname, dirname, PATH_MAX);
471 sc_AppendToPath(entrypathname, dir->mEntry.cFileName);
473 bool isAlias = false;
474 sc_ResolveIfAlias(entrypathname, path, isAlias, PATH_MAX);
476 if (!::FindNextFile(dir->mHandle, &dir->mEntry)) {
477 dir->mAtEnd = true;
480 return true;
481 #else
482 if (!dir->mHandle)
483 return false;
485 dir->mEntry = readdir(dir->mHandle);
486 if (!dir->mEntry)
487 return false;
489 const char* entry = dir->mEntry->d_name;
491 if (sc_IsSpecialDirectory(entry) || (skipEntry && sc_SkipDirectory(entry))) {
492 skipEntry = true;
493 return true;
494 } else {
495 skipEntry = false;
498 // construct path from dir entry
499 char entrypathname[PATH_MAX];
500 strncpy(entrypathname, dirname, PATH_MAX);
501 sc_AppendToPath(entrypathname, PATH_MAX, dir->mEntry->d_name);
503 // resolve path
504 bool isAlias = false;
505 if (sc_ResolveIfAlias(entrypathname, path, isAlias, strlen(entrypathname))<0)
507 skipEntry = true;
510 return true;
511 #endif
515 // Globbing
517 struct SC_GlobHandle
519 #ifdef _WIN32
520 HANDLE mHandle;
521 char mFolder[PATH_MAX];
522 WIN32_FIND_DATA mEntry;
523 char mEntryPath[PATH_MAX];
524 bool mAtEnd;
525 #else
526 glob_t mHandle;
527 size_t mEntry;
528 #endif
531 SC_GlobHandle* sc_Glob(const char* pattern)
533 SC_GlobHandle* glob = new SC_GlobHandle;
535 #ifdef _WIN32
536 char patternWin[1024];
538 strncpy(patternWin, pattern, 1024);
539 patternWin[1023] = 0;
540 win32_ReplaceCharInString(patternWin, 1024, '/', '\\');
542 win32_ExtractContainingFolder(glob->mFolder, patternWin, PATH_MAX);
544 glob->mHandle = ::FindFirstFile(patternWin, &glob->mEntry);
545 if (glob->mHandle == INVALID_HANDLE_VALUE) {
546 delete glob;
547 return 0;
550 glob->mAtEnd = false;
551 #else
552 int flags = GLOB_MARK | GLOB_TILDE;
553 #ifdef __APPLE__
554 flags |= GLOB_QUOTE;
555 #endif
557 int err = ::glob(pattern, flags, NULL, &glob->mHandle);
558 if (err < 0) {
559 delete glob;
560 return 0;
563 glob->mEntry = 0;
564 #endif
566 return glob;
569 void sc_GlobFree(SC_GlobHandle* glob)
571 #ifdef _WIN32
572 ::FindClose(glob->mHandle);
573 #else
574 globfree(&glob->mHandle);
575 #endif
576 delete glob;
579 const char* sc_GlobNext(SC_GlobHandle* glob)
581 #ifdef _WIN32
582 if (glob->mAtEnd)
583 return 0;
584 strncpy(glob->mEntryPath, glob->mFolder, PATH_MAX);
585 sc_AppendToPath(glob->mEntryPath, glob->mEntry.cFileName);
586 if (!::FindNextFile(glob->mHandle, &glob->mEntry))
587 glob->mAtEnd = true;
588 return glob->mEntryPath;
589 #else
590 if (glob->mEntry >= glob->mHandle.gl_pathc)
591 return 0;
592 return glob->mHandle.gl_pathv[glob->mEntry++];
593 #endif