BASIS  version 1.2.3 (revision 2104)
path.cxx
Go to the documentation of this file.
00001 /**
00002  * @file  path.cxx
00003  * @brief Basic file path manipulation and related system functions.
00004  *
00005  * Copyright (c) 2011 University of Pennsylvania. All rights reserved.<br />
00006  * See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file.
00007  *
00008  * Contact: SBIA Group <sbia-software at uphs.upenn.edu>
00009  */
00010 
00011 
00012 #include <vector>
00013 
00014 #include <sbia/basis/config.h> // platform macros - must be first
00015 
00016 #include <stdlib.h>            // malloc() & free(), _splitpath_s() (WINDOWS)
00017 #include <string.h>            // strncmp()
00018 
00019 #if WINDOWS
00020 #  include <direct.h>          // _getcwd()
00021 #  include <windows.h>         // GetModuleFileName()
00022 #else
00023 #  include <unistd.h>          // getcwd(), rmdir()
00024 #  include <sys/stat.h>        // stat(), lstat()
00025 #  include <dirent.h>          // opendir()
00026 #endif
00027 #if MACOS
00028 #  include <mach-o/dyld.h>     // _NSGetExecutablePath()
00029 #endif
00030 
00031 #include <sbia/basis/assert.h> // debug assertions
00032 #include <sbia/basis/except.h> // to throw exceptions
00033 #include <sbia/basis/path.h>   // declarations and BASIS configuration
00034 
00035 
00036 // acceptable in .cxx file
00037 using namespace std;
00038 
00039 
00040 namespace sbia
00041 {
00042 
00043 namespace basis
00044 {
00045 
00046 
00047 // ===========================================================================
00048 // constants
00049 // ===========================================================================
00050 
00051 #if WINDOWS
00052 const char   cPathSeparator = '\\';
00053 const string cPathSeparatorStr ("\\");
00054 #else
00055 const char   cPathSeparator = '/';
00056 const string cPathSeparatorStr ("/");
00057 #endif
00058 
00059 // ===========================================================================
00060 // path representations
00061 // ===========================================================================
00062 
00063 // ---------------------------------------------------------------------------
00064 bool is_valid_path(const string& path, bool strict)
00065 {
00066     // an empty string is clearly no valid path
00067     if (path.empty()) return false;
00068     // we do not allow a path to start with a colon (:) (also not on Unix!)
00069     if (path[0] == ':') return false;
00070     // absolute Windows-style path with drive specification
00071     if (path.size() > 1 && path[1] == ':') {
00072         // first character must be a drive letter
00073         if ((path[0] < 'a' || 'z' < path[0]) &&
00074             (path[0] < 'A' || 'Z' < path[0])) return false;
00075         // absolute path must follow the drive specification
00076         if (path.size() == 2) return false;
00077         if (path[2] != '/' && path[2] != '\\') return false;
00078         if (strict) {
00079 #if !WINDOWS
00080             // on Unix systems, a drive specification is invalid
00081             // however, to be consistent, the drive C: is just ignored and
00082             // interpreted as root on Unix
00083             if (path[0] != 'C' && path[0] != 'c') return false;
00084 #endif
00085         }
00086     }
00087     // otherwise, the path is valid
00088     return true;
00089 }
00090 
00091 // ---------------------------------------------------------------------------
00092 string clean_path(const string& path)
00093 {
00094     if (!is_valid_path(path, false)) {
00095         BASIS_THROW(invalid_argument, "Invalid path: ''");
00096     }
00097 
00098     string cleaned_path;
00099 
00100     string::const_iterator prev;
00101     string::const_iterator curr;
00102     string::const_iterator next;
00103 
00104     // remove periods enclosed by slashes (or backslashes)
00105     prev = path.begin();
00106     if (prev == path.end()) return "";
00107 
00108     curr = prev + 1;
00109     next = ((curr == path.end()) ? curr : (curr + 1));
00110 
00111     cleaned_path.reserve(path.size());
00112     cleaned_path.push_back(*prev);
00113     while (curr != path.end()) {
00114         if (*curr == '.' && (*prev == '/' || *prev == '\\')) {
00115             if (next == path.end()) {
00116                 // remove the "/" that was added before b/c the
00117                 // final "/." should be removed together
00118                 cleaned_path.erase(cleaned_path.size() - 1);
00119             }
00120             else if (*next != '/' && *next != '\\') {
00121                 cleaned_path.push_back(*curr);
00122             }
00123         } else {
00124             cleaned_path.push_back(*curr);
00125         }
00126 
00127         prev++;
00128         curr++;
00129         if (next != path.end()) next++;
00130     }
00131 
00132     // remove duplicate slashes (and backslashes)
00133     string tmp;
00134     cleaned_path.swap(tmp);
00135 
00136     prev = tmp.begin();
00137     curr = prev + 1;
00138 
00139     cleaned_path.reserve(tmp.size());
00140     cleaned_path.push_back(*prev);
00141     while (curr != tmp.end()) {
00142         if ((*curr != '/' && *curr != '\\') || (*prev != '/' && *prev != '\\')) {
00143             cleaned_path.push_back(*curr);
00144         }
00145 
00146         prev++;
00147         curr++;
00148     }
00149 
00150     // remove references to parent directories
00151     size_t skip = 0;
00152 
00153     // in case of relative paths like "../../*" we need to skip the first
00154     // parent directory references
00155     while (skip + 3 <= cleaned_path.size()) {
00156         string sub = cleaned_path.substr(skip, 3);
00157         if (sub != "../" && sub != "..\\") break;
00158         skip += 3;
00159     }
00160     if (skip > 0) skip -= 1; // below, we look for "/.." instead of "../"
00161 
00162     string ref = "/.."; // will be set to "\.." on the "second" run
00163     size_t pos = skip;  // the starting position of the path "absolute" to
00164                         // to the root directory given by "../../" or "/",
00165                         // for example.
00166     for (;;) {
00167         pos = cleaned_path.find(ref, pos);
00168         if (pos == string::npos) {
00169             if (ref == "\\..") break;
00170             // no occurrences of "/.." found, now clean up the ones of "\.."
00171             ref = "\\..";
00172             pos = skip;
00173             pos = cleaned_path.find(ref, pos);
00174             if (pos == string::npos) break;
00175         }
00176         if (pos + 3 == cleaned_path.size()
00177                 || cleaned_path[pos + 3] == '/'
00178                 || cleaned_path[pos + 3] == '\\') {
00179             if (pos == 0) {
00180                 if (cleaned_path.size() == 3) cleaned_path.erase(1, 2);
00181                 else                          cleaned_path.erase(0, 3);
00182                 pos = skip;
00183             } else {
00184                 size_t start = cleaned_path.find_last_of("/\\", pos - 1);
00185                 if (start != string::npos) {
00186                     cleaned_path.erase(start, pos - start + 3);
00187                     pos = start + 1;
00188                 } else if (cleaned_path[0] == '/' || cleaned_path[0] == '\\') {
00189                     cleaned_path.erase(skip, pos + 3);
00190                     pos = skip;
00191                 } else {
00192                     pos += 3;
00193                 }
00194             }
00195         } else {
00196             pos += 3;
00197         }
00198     }
00199 
00200     return cleaned_path;
00201 }
00202 
00203 // ---------------------------------------------------------------------------
00204 string to_unix_path(const string& path, bool drive)
00205 {
00206     if (!is_valid_path(path)) {
00207         BASIS_THROW(invalid_argument, "Invalid path: '" << path << "'");
00208     }
00209 
00210     string unix_path;
00211     unix_path.reserve(path.size());
00212 
00213     string::const_iterator in = path.begin();
00214 
00215     // optional drive specification
00216     if (path.size() > 1 && path[1] == ':') {
00217         if (drive) {
00218             if ('a' <= path[0] && path[0] <= 'z') unix_path += 'A' + (path[0] - 'a');
00219             else                                  unix_path += path[0];
00220             unix_path += ":/";
00221         }
00222         in += 2;
00223     }
00224 
00225     // copy path while replacing backslashes by slashes
00226     while (in != path.end()) {
00227         if (*in == '\\') unix_path.push_back('/');
00228         else             unix_path.push_back(*in);
00229         in++;
00230     }
00231 
00232     return clean_path(unix_path);
00233 }
00234 
00235 // ---------------------------------------------------------------------------
00236 string to_windows_path(const string& path)
00237 {
00238     if (!is_valid_path(path, false)) {
00239         BASIS_THROW(invalid_argument, "Invalid path: '" << path << "'");
00240     }
00241 
00242     string windows_path(path.size(), '\0');
00243 
00244     // copy path while replacing slashes by backslashes
00245     string::const_iterator in  = path.begin();
00246     string::iterator       out = windows_path.begin();
00247 
00248     while (in != path.end()) {
00249         if (*in == '/') *out = '\\';
00250         else            *out = *in;
00251         in++;
00252         out++;
00253     }
00254 
00255     // prepend drive specification to absolute paths
00256     if (windows_path[0] == '\\') {
00257         windows_path.reserve(windows_path.size() + 2);
00258         windows_path.insert (0, "C:");
00259     }
00260 
00261     return clean_path(windows_path);
00262 }
00263 
00264 // ---------------------------------------------------------------------------
00265 string to_native_path(const string& path)
00266 {
00267 #if WINDOWS
00268     return to_windows_path(path);
00269 #else
00270     return to_unix_path(path);
00271 #endif
00272 }
00273 
00274 // ===========================================================================
00275 // working directory
00276 // ===========================================================================
00277 
00278 // ---------------------------------------------------------------------------
00279 string get_working_directory()
00280 {
00281     string wd;
00282 #if WINDOWS
00283     char* buffer = _getcwd(NULL, 0);
00284 #else
00285     char* buffer = getcwd(NULL, 0);
00286 #endif
00287     if (buffer) {
00288         wd = buffer;
00289         free(buffer);
00290     }
00291     if (!wd.empty()) wd = to_unix_path(wd, true);
00292     return wd;
00293 }
00294 
00295 // ===========================================================================
00296 // path components
00297 // ===========================================================================
00298 
00299 // ---------------------------------------------------------------------------
00300 void split_path(const string&path, string* root, string* dir, string* fname,
00301                 string* ext, const set<string>* exts)
00302 {
00303     // file root
00304     if (root) *root = get_file_root(path);
00305     // \note No need to validate path here if get_file_root() is executed.
00306     //       Otherwise, it is necessary as to_unix_path() is not as restrict.
00307     else if (!is_valid_path(path)) {
00308         BASIS_THROW(invalid_argument, "Invalid path: '" << path << "'");
00309     }
00310     // convert path to clean Unix-style path
00311     string unix_path = to_unix_path(path);
00312     // get position of last slash
00313     size_t last = unix_path.find_last_of('/');
00314     // file directory without root
00315     if (dir) {
00316         if (last == string::npos) {
00317             *dir = "";
00318         } else {
00319             size_t start = 0;
00320 
00321             if (unix_path[0] == '/') {
00322                 start = 1;
00323             } else if (unix_path.size() > 1 &&
00324                        unix_path[0] == '.'  &&
00325                        unix_path[1] == '/') {
00326                 start = 2;
00327             }
00328 
00329             *dir = unix_path.substr(start, last - start + 1);
00330         }
00331     }
00332     // file name and extension
00333     if (fname || ext) {
00334         string name;
00335 
00336         if (last == string::npos) {
00337             name = unix_path;
00338         } else {
00339             name = unix_path.substr(last + 1);
00340         }
00341 
00342         size_t pos = string::npos;
00343         
00344         // first test user supplied extension
00345         if (exts && exts->size() > 0) {
00346             for (set<string>::const_iterator i = exts->begin(); i != exts->end(); ++i) {
00347                 size_t start = name.size() - i->size();
00348                 if (start < pos && name.compare(start, i->size(), *i) == 0) {
00349                     pos = start;
00350                 }
00351             }
00352         }
00353         // otherwise, extract last extension component
00354         if (pos == string::npos) {
00355             pos = name.find_last_of('.');
00356         }
00357 
00358         if (pos == string::npos) {
00359             if (fname) {
00360                 *fname = name;
00361             }
00362             if (ext) {
00363                 *ext = "";
00364             }
00365         } else {
00366             if (fname) {
00367                 *fname = name.substr(0, pos);
00368             }
00369             if (ext) {
00370                 *ext = name.substr(pos);
00371             }
00372         }
00373     }
00374 }
00375 
00376 // ---------------------------------------------------------------------------
00377 string get_file_root(const string& path)
00378 {
00379     if (!is_valid_path(path)) {
00380         BASIS_THROW(invalid_argument, "Invalid path: '" << path << "'");
00381     }
00382 
00383     // absolute Unix-style input path
00384     if (path[0] == '/' || path[0] == '\\') {
00385 #if WINDOWS
00386         return "C:/";
00387 #else
00388         return "/";
00389 #endif
00390     }
00391     // absolute Windows-style input path
00392     if (path.size() > 1 && path[1] == ':') {
00393         char letter = path[0];
00394         if ('a' <= letter && letter <= 'z') {
00395             letter = 'A' + (letter - 'a');
00396         }
00397 #if WINDOWS
00398         string root; root += letter; root += ":/";
00399         return root;
00400 #else
00401         return "/";
00402 #endif
00403     }
00404     // otherwise, it's a relative path
00405     return "./";
00406 }
00407 
00408 // ---------------------------------------------------------------------------
00409 string get_file_directory(const string& path)
00410 {
00411     string root;
00412     string dir;
00413     split_path(path, &root, &dir, NULL, NULL);
00414     if (root == "./") {
00415         if (dir.empty()) return "./";
00416         else             return dir;
00417     } else {
00418         return root + dir.substr(0, dir.size() - 1);
00419     }
00420 }
00421 
00422 // ---------------------------------------------------------------------------
00423 string get_file_name(const string& path)
00424 {
00425     string fname;
00426     string ext;
00427     split_path(path, NULL, NULL, &fname, &ext);
00428     return fname + ext;
00429 }
00430 
00431 // ---------------------------------------------------------------------------
00432 string get_file_name_without_extension(const string& path, const set<string>* exts)
00433 {
00434     string fname;
00435     split_path(path, NULL, NULL, &fname, NULL, exts);
00436     return fname;
00437 }
00438 
00439 // ---------------------------------------------------------------------------
00440 string get_file_name_extension(const string& path, const set<string>* exts)
00441 {
00442     string ext;
00443     split_path(path, NULL, NULL, NULL, &ext, exts);
00444     return ext;
00445 }
00446 
00447 // ---------------------------------------------------------------------------
00448 bool has_extension(const string& path, const set<string>* exts)
00449 {
00450     string ext = get_file_name_extension(path, exts);
00451     return exts ? exts->find(ext) != exts->end() : !ext.empty();
00452 }
00453 
00454 // ===========================================================================
00455 // absolute / relative paths
00456 // ===========================================================================
00457 
00458 // ---------------------------------------------------------------------------
00459 bool is_absolute(const string& path)
00460 {
00461     return get_file_root(path) != "./";
00462 }
00463 
00464 // ---------------------------------------------------------------------------
00465 bool is_relative(const string& path)
00466 {
00467     return get_file_root(path) == "./";
00468 }
00469 
00470 // ---------------------------------------------------------------------------
00471 string to_absolute_path(const string& path)
00472 {
00473     return to_absolute_path(get_working_directory(), path);
00474 }
00475 
00476 // ---------------------------------------------------------------------------
00477 string to_absolute_path(const string& base, const string& path)
00478 {
00479     string abs_path(path);
00480     // make relative path absolute
00481     if (is_relative(abs_path)) {
00482         string abs_base(base);
00483         if (is_relative(abs_base)) {
00484             abs_base.insert(0, get_working_directory() + '/');
00485         }
00486         abs_path.insert(0, abs_base + '/');
00487     }
00488     // convert to (clean) Unix-style path with drive specification
00489     abs_path = to_unix_path(abs_path, true);
00490     // on Windows, prepend drive letter followed by a colon (:)
00491 #if WINDOWS
00492     if (abs_path[0] == '/') abs_path.insert(0, "C:");
00493 #endif
00494     return abs_path;
00495 }
00496 
00497 // ---------------------------------------------------------------------------
00498 string to_relative_path(const string& path)
00499 {
00500     string unix_path = to_unix_path(path, true);
00501     if (is_relative(unix_path)) return clean_path(unix_path);
00502     return to_relative_path(get_working_directory(), unix_path);
00503 }
00504 
00505 // ---------------------------------------------------------------------------
00506 string to_relative_path(const string& base, const string& path)
00507 {
00508     string unix_path = to_unix_path(path, true);
00509     // if relative path is given just return it cleaned
00510     if (is_relative(unix_path)) return clean_path(path);
00511     // make base path absolute
00512     string abs_base = to_unix_path(base, true);
00513     if (is_relative(abs_base)) {
00514         abs_base.insert(0, get_working_directory() + '/');
00515     }
00516     // path must have same root as base path; this check is intended for
00517     // Windows, where there is no relative path from one drive to another
00518     if (get_file_root(abs_base) != get_file_root(unix_path)) return "";
00519     // find start of first path component in which paths differ
00520     string::const_iterator b = abs_base .begin();
00521     string::const_iterator p = unix_path.begin();
00522     size_t pos = 0;
00523     size_t i = 0;
00524     while (b != abs_base.end() && p != unix_path.end() && *b == *p) {
00525         if (*p == '/') pos = i;
00526         b++; p++; i++;
00527     }
00528     // set pos to i (in this case, the size of one of the paths) if the end
00529     // of one path was reached, but the other path has a slash (or backslash)
00530     // at this position, this is required later below
00531     if ((b != abs_base .end() && (*b == '/' || *b == '\\')) ||
00532         (p != unix_path.end() && (*p == '/' || *p == '\\'))) pos = i;
00533     // skip trailing slash of other path if end of one path reached
00534     if (b == abs_base .end() && p != unix_path.end() && *p == '/') p++;
00535     if (p == unix_path.end() && b != abs_base .end() && *b == '/') b++;
00536     // if paths are the same, just return a period (.)
00537     //
00538     // Thanks to the previous skipping of trailing slashes, this condition
00539     // handles all of the following cases:
00540     //
00541     //    base := "/usr/bin"  path := "/usr/bin"
00542     //    base := "/usr/bin/" path := "/usr/bin/"
00543     //    base := "/usr/bin"  path := "/usr/bin/"
00544     //    base := "/usr/bin/" path := "/usr/bin"
00545     //
00546     // Note: The paths have been cleaned before by the to_unix_path() function.
00547     if (b == abs_base.end() && p == unix_path.end()) return ".";
00548     // otherwise, pos is the index of the last slash for which both paths
00549     // were identical; hence, everything that comes after in the original
00550     // path is preserved and for each following component in the base path
00551     // a "../" is prepended to the relative path
00552     string rel_path;
00553     // truncate base path with a slash (/) as for each "*/" path component,
00554     // a "../" will be prepended to the relative path
00555     if (b != abs_base.end() && abs_base[abs_base.size() - 1] != '/') {
00556         // \attention This operation may invalidate the iterator b!
00557         //            Therefore, remember position of iterator and get a new one.
00558         size_t pos = b - abs_base.begin();
00559         abs_base += '/';
00560         b = abs_base.begin() + pos;
00561     }
00562     while (b != abs_base.end()) {
00563         if (*b == '/') rel_path += "../";
00564         b++;
00565     }
00566     if (pos + 1 < unix_path.size()) rel_path += unix_path.substr(pos + 1);
00567     // remove trailing slash (/)
00568     if (rel_path[rel_path.size() - 1] == '/') {
00569         rel_path.erase (rel_path.size() - 1);
00570     }
00571     return rel_path;
00572 }
00573 
00574 // ---------------------------------------------------------------------------
00575 string join_paths(const string& base, const string& path)
00576 {
00577     if (is_absolute(path)) return clean_path(path);
00578     else return clean_path(base + '/' + path);
00579 }
00580 
00581 // ===========================================================================
00582 // file / directory checks
00583 // ===========================================================================
00584 
00585 // ---------------------------------------------------------------------------
00586 bool is_file(const std::string path)
00587 {
00588 #if WINDOWS 
00589     const DWORD info = ::GetFileAttributes(path.c_str());
00590     return (FILE_ATTRIBUTE_DIRECTORY & info) == 0;
00591 #else
00592     struct stat info;
00593     if (stat(path.c_str(), &info) != 0) return false;
00594     return S_ISREG(info.st_mode);
00595 #endif
00596     return false;
00597 }
00598 
00599 // ---------------------------------------------------------------------------
00600 bool is_dir(const std::string path)
00601 {
00602 #if WINDOWS 
00603     const DWORD info = ::GetFileAttributes(path.c_str());
00604     return (FILE_ATTRIBUTE_DIRECTORY & info) != 0;
00605 #else
00606     struct stat info;
00607     if (stat(path.c_str(), &info) != 0) return false;
00608     return S_ISDIR(info.st_mode);
00609 #endif
00610     return false;
00611 }
00612 
00613 // ---------------------------------------------------------------------------
00614 bool exists(const std::string path)
00615 {
00616 #if WINDOWS 
00617     const DWORD info = ::GetFileAttributes(path.c_str());
00618     return info != INVALID_FILE_ATTRIBUTES;
00619 #else
00620     struct stat info;
00621     if (stat(path.c_str(), &info) == 0) return true;
00622 #endif
00623     return false;
00624 }
00625 
00626 // ---------------------------------------------------------------------------
00627 bool is_symlink(const string& path)
00628 {
00629     if (!is_valid_path(path)) {
00630         BASIS_THROW(invalid_argument, "Invalid path: '" << path << "'");
00631     }
00632 
00633 #if WINDOWS
00634     return false;
00635 #else
00636     struct stat info;
00637     if (lstat(path.c_str(), &info) != 0) return false;
00638     return S_ISLNK(info.st_mode);
00639 #endif
00640 }
00641 
00642 // ===========================================================================
00643 // make/remove directory
00644 // ===========================================================================
00645 
00646 // ---------------------------------------------------------------------------
00647 bool make_directory(const string& path, bool parent)
00648 {
00649     if (path.empty() || is_file(path)) return false;
00650     vector<string> dirs;
00651     string         dir(path);
00652     if (parent) {
00653         while (!dir.empty() && !exists(dir)) {
00654             dirs.push_back(dir);
00655             dir = get_file_directory(dir);
00656         }
00657     } else if (!exists(dir)) {
00658         dirs.push_back(dir);
00659     }
00660     for (vector<string>::reverse_iterator it = dirs.rbegin(); it != dirs.rend(); ++it) {
00661 #if WINDOWS
00662         if (CreateDirectory(it->c_str(), NULL) == FALSE) return false;
00663 #else
00664         if (mkdir(it->c_str(), 0755) != 0) return false;
00665 #endif
00666     }
00667     return true;
00668 }
00669 
00670 // ---------------------------------------------------------------------------
00671 bool remove_directory(const string& path, bool recursive)
00672 {
00673     // remove files and subdirectories - recursive implementation
00674     if (recursive && !clear_directory(path)) return false;
00675     // remove this directory
00676 #if WINDOWS
00677     return (::SetFileAttributes(path.c_str(), FILE_ATTRIBUTE_NORMAL) == TRUE) &&
00678            (::RemoveDirectory(path.c_str()) == TRUE);
00679 #else
00680     return rmdir(path.c_str()) == 0;
00681 #endif
00682 }
00683 
00684 // ---------------------------------------------------------------------------
00685 bool clear_directory(const string& path)
00686 {
00687     bool ok = true;
00688     string subpath; // either subdirectory or file path
00689 
00690 #if WINDOWS
00691     WIN32_FIND_DATA info;
00692     HANDLE hFile = ::FindFirstFile(join_paths(path, "*.*").c_str(), &info);
00693     if (hFile != INVALID_HANDLE_VALUE) {
00694         do {
00695             // skip '.' and '..'
00696             if (strncmp(info.cFileName, ".", 2) == 0 || strncmp(info.cFileName, "..", 3) == 0) {
00697                 continue;
00698             }
00699             // remove subdirectory or file, respectively
00700             subpath = join_paths(path, info.cFileName);
00701             if(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
00702                 if (!remove_directory(subpath, true)) ok = false;
00703             } else {
00704                 if (::SetFileAttributes(subpath.c_str(), FILE_ATTRIBUTE_NORMAL) == FALSE ||
00705                     ::DeleteFile(subpath.c_str()) == FALSE) ok = false;
00706             }
00707         } while (::FindNextFile(hFile, &info) == TRUE);
00708         ::FindClose(hFile);
00709     }
00710 #else
00711     struct dirent *p = NULL;
00712     DIR *d = opendir(path.c_str());
00713     if (d != NULL) {
00714         while ((p = readdir(d)) != NULL) {
00715             // skip '.' and '..'
00716             if (strncmp(p->d_name, ".", 2) == 0 || strncmp(p->d_name, "..", 3) == 0) {
00717                 continue;
00718             }
00719             // remove subdirectory or file, respectively
00720             subpath = join_paths(path, p->d_name);
00721             if (is_dir(subpath)) {
00722                 if (!remove_directory(subpath, true)) ok = false;
00723             } else {
00724                 if (unlink(subpath.c_str()) != 0) ok = false;
00725             }
00726         }
00727         closedir(d);
00728     }
00729 #endif
00730     return ok;
00731 }
00732 
00733 // ===========================================================================
00734 // symbolic links
00735 // ===========================================================================
00736 
00737 // ---------------------------------------------------------------------------
00738 bool read_symlink(const string& link, string& value)
00739 {
00740     if (!is_valid_path(link)) {
00741         BASIS_THROW(invalid_argument, "Invalid path: '" << link << "'");
00742     }
00743 
00744     bool ok = true;
00745 #if WINDOWS
00746     value = link;
00747 #else
00748     char* buffer = NULL;
00749     char* newbuf = NULL;
00750     size_t buflen = 256;
00751 
00752     for (;;) {
00753         newbuf = reinterpret_cast<char*>(realloc(buffer, buflen * sizeof(char)));
00754         if (!newbuf) {
00755             ok = false;
00756             break;
00757         }
00758         buffer = newbuf;
00759 
00760         int n = readlink(link.c_str(), buffer, buflen);
00761 
00762         if (n < 0) {
00763             ok = false;
00764             break;
00765         } else if (static_cast<size_t>(n) < buflen) {
00766             buffer[n] = '\0';
00767             value = buffer;
00768             break;
00769         }
00770         buflen += 256;
00771     }
00772 
00773     free(buffer);
00774 #endif
00775     return ok;
00776 }
00777 
00778 // ---------------------------------------------------------------------------
00779 string get_real_path(const string& path)
00780 {
00781     string curr_path = to_absolute_path(path);
00782 #if UNIX
00783     // use stringstream and std::getline() to split absolute path at slashes (/)
00784     stringstream ss(curr_path);
00785     curr_path.clear();
00786     string fname;
00787     string prev_path;
00788     string next_path;
00789     char slash;
00790     ss >> slash; // root slash
00791     assert(slash == '/');
00792     while (getline(ss, fname, '/')) {
00793         // current absolute path
00794         curr_path += '/';
00795         curr_path += fname;
00796         // if current path is a symbolic link, follow it
00797         if (is_symlink(curr_path)) {
00798             // for safety reasons, restrict the depth of symbolic links followed
00799             for (unsigned int i = 0; i < 100; i++) {
00800                 if (read_symlink(curr_path, next_path)) {
00801                     curr_path = to_absolute_path(prev_path, next_path);
00802                     if (!is_symlink(next_path)) break;
00803                 } else {
00804                     // if real path could not be determined because of permissions
00805                     // or invalid path, return the original path
00806                     break;
00807                 }
00808             }
00809             // if real path could not be determined with the given maximum number
00810             // of loop iterations (endless cycle?) or one of the symbolic links
00811             // could not be read, just return original path as clean absolute path
00812             if (is_symlink(next_path)) {
00813                 return to_absolute_path(path);
00814             }
00815         }
00816         // memorize previous path used as base for to_absolute_path()
00817         prev_path = curr_path;
00818     }
00819 #endif
00820     return curr_path;
00821 }
00822 
00823 // ===========================================================================
00824 // executable file
00825 // ===========================================================================
00826 
00827 // ---------------------------------------------------------------------------
00828 string get_executable_path()
00829 {
00830     string path;
00831 #if LINUX
00832     path = get_real_path("/proc/self/exe");
00833 #elif WINDOWS
00834     LPTSTR buffer = NULL;
00835     LPTSTR newbuf = NULL;
00836     DWORD buflen = 256;
00837     DWORD retval = 0;
00838 
00839     for (;;) {
00840         newbuf = static_cast<LPTSTR>(realloc(buffer, buflen * sizeof(TCHAR)));
00841         if (!newbuf) break;
00842         buffer = newbuf;
00843         retval = GetModuleFileName(NULL, buffer, buflen);
00844         if (retval == 0 || retval < buflen) break;
00845         buflen += 256;
00846         retval = 0;
00847     }
00848 
00849     if (retval > 0) {
00850 #  ifdef UNICODE
00851         int n = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, NULL, 0, NULL, NULL);
00852         char* mbpath = static_cast<char*>(malloc(n));
00853         if (mbpath) {
00854             WideCharToMultiByte(CP_UTF8, 0, buffer, -1, mbpath, n, NULL, NULL);
00855             path = mbpath;
00856             free(mbpath);
00857         }
00858 #  else
00859         path = buffer;
00860 #  endif
00861     }
00862 
00863     free (buffer);
00864 #elif MACOS
00865     char* buffer = NULL;
00866     char* newbuf = NULL;
00867     uint32_t buflen = 256;
00868 
00869     buffer = reinterpret_cast<char*>(malloc(buflen * sizeof(char)));
00870     if (buffer) {
00871         if (_NSGetExecutablePath(buffer, &buflen) == 0) {
00872             path = buffer;
00873         } else {
00874             newbuf = reinterpret_cast<char*>(realloc(buffer, buflen * sizeof(char)));
00875             if (newbuf) {
00876                 buffer = newbuf;
00877                 if (_NSGetExecutablePath(buffer, &buflen) == 0) {
00878                     path = buffer;
00879                 }
00880             }
00881         }
00882     }
00883 
00884     free(buffer);
00885 #else
00886     // functionality not supported on this (unknown) platform
00887 #endif
00888     return clean_path(path);
00889 }
00890 
00891 // ---------------------------------------------------------------------------
00892 string get_executable_directory()
00893 {
00894     string path = get_executable_path();
00895     return path.empty() ? "" : get_file_directory(path);
00896 }
00897 
00898 // ---------------------------------------------------------------------------
00899 string get_executable_name()
00900 {
00901     string name = get_executable_path();
00902     if (name.empty()) return "";
00903 
00904 #if WINDOWS
00905     string ext = get_file_name_extension(name);
00906     if (ext == ".exe" || ext == ".com") {
00907         name = get_file_name_without_extension(name);
00908     } else {
00909         name = get_file_name(name);
00910     }
00911 #else
00912     name = get_file_name(name);
00913 #endif
00914 
00915     return name;
00916 }
00917 
00918 
00919 } // namespace basis
00920 
00921 } // namespace sbia