diff --git a/ppremake/configure.in b/ppremake/configure.in index 3ae4655c6e..13a74b92a6 100644 --- a/ppremake/configure.in +++ b/ppremake/configure.in @@ -42,7 +42,7 @@ AC_SUBST(libm) dnl Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS(malloc.h alloca.h unistd.h utime.h io.h minmax.h dirent.h sys/types.h sys/time.h sys/utime.h sys/wait.h string.h regex.h getopt.h) +AC_CHECK_HEADERS(malloc.h alloca.h unistd.h utime.h io.h minmax.h dirent.h glob.h sys/types.h sys/time.h sys/utime.h sys/wait.h string.h regex.h getopt.h) dnl Checks for typedefs, structures, and compiler characteristics. diff --git a/ppremake/dSearchPath.cxx b/ppremake/dSearchPath.cxx index b5278d1261..9d67557e7d 100644 --- a/ppremake/dSearchPath.cxx +++ b/ppremake/dSearchPath.cxx @@ -85,12 +85,22 @@ get_num_files() const { // Access: Public // Description: Returns the nth file on the result list. //////////////////////////////////////////////////////////////////// -Filename DSearchPath::Results:: +const Filename &DSearchPath::Results:: get_file(int n) const { assert(n >= 0 && n < (int)_files.size()); return _files[n]; } +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Results::add_file +// Access: Public +// Description: Adds a new file to the result list. +//////////////////////////////////////////////////////////////////// +void DSearchPath::Results:: +add_file(const Filename &file) { + _files.push_back(file); +} + //////////////////////////////////////////////////////////////////// // Function: DSearchPath::Default Constructor // Access: Public @@ -246,7 +256,7 @@ get_num_directories() const { // Access: Public // Description: Returns the nth directory on the search list. //////////////////////////////////////////////////////////////////// -Filename DSearchPath:: +const Filename &DSearchPath:: get_directory(int n) const { assert(n >= 0 && n < (int)_directories.size()); return _directories[n]; @@ -262,11 +272,22 @@ get_directory(int n) const { //////////////////////////////////////////////////////////////////// Filename DSearchPath:: find_file(const Filename &filename) const { - Directories::const_iterator di; - for (di = _directories.begin(); di != _directories.end(); ++di) { - Filename match((*di), filename); - if (match.exists()) { - return match; + if (filename.is_local()) { + Directories::const_iterator di; + for (di = _directories.begin(); di != _directories.end(); ++di) { + Filename match((*di), filename); + if (match.exists()) { + if ((*di) == "." && filename.is_fully_qualified()) { + // A special case for the "." directory: to avoid prefixing + // an endless stream of ./ in front of files, if the + // filename already has a ./ prefixed + // (i.e. is_fully_qualified() is true), we don't + // prefix another one. + return filename; + } else { + return match; + } + } } } @@ -280,21 +301,37 @@ find_file(const Filename &filename) const { // the indicated file, in order. Fills up the results // list with *all* of the matching filenames found, if // any. Returns the number of matches found. +// +// It is the responsibility of the the caller to clear +// the results list first; otherwise, the newly-found +// files will be appended to the list. //////////////////////////////////////////////////////////////////// int DSearchPath:: find_all_files(const Filename &filename, DSearchPath::Results &results) const { - results._files.clear(); + int num_added = 0; - Directories::const_iterator di; - for (di = _directories.begin(); di != _directories.end(); ++di) { - Filename match((*di), filename); - if (match.exists()) { - results._files.push_back(match); + if (filename.is_local()) { + Directories::const_iterator di; + for (di = _directories.begin(); di != _directories.end(); ++di) { + Filename match((*di), filename); + if (match.exists()) { + if ((*di) == "." && filename.is_fully_qualified()) { + // A special case for the "." directory: to avoid prefixing + // an endless stream of ./ in front of files, if the + // filename already has a ./ prefixed + // (i.e. is_fully_qualified() is true), we don't + // prefix another one. + results.add_file(filename); + } else { + results.add_file(match); + } + num_added++; + } } } - return results._files.size(); + return num_added; } //////////////////////////////////////////////////////////////////// diff --git a/ppremake/dSearchPath.h b/ppremake/dSearchPath.h index fb9ee7feb8..34c27adced 100644 --- a/ppremake/dSearchPath.h +++ b/ppremake/dSearchPath.h @@ -22,9 +22,10 @@ #include "ppremake.h" #include "filename.h" - #include +#define pvector std::vector + /////////////////////////////////////////////////////////////////// // Class : DSearchPath // Description : This class stores a list of directories that can be @@ -45,12 +46,14 @@ public: void clear(); int get_num_files() const; - Filename get_file(int n) const; + const Filename &get_file(int n) const; + + public: + void add_file(const Filename &file); private: - typedef vector Files; + typedef pvector Files; Files _files; - friend class DSearchPath; }; PUBLISHED: @@ -70,7 +73,7 @@ PUBLISHED: bool is_empty() const; int get_num_directories() const; - Filename get_directory(int n) const; + const Filename &get_directory(int n) const; Filename find_file(const Filename &filename) const; int find_all_files(const Filename &filename, Results &results) const; @@ -83,7 +86,7 @@ PUBLISHED: void write(ostream &out, int indent_level = 0) const; private: - typedef vector Directories; + typedef pvector Directories; Directories _directories; }; diff --git a/ppremake/filename.cxx b/ppremake/filename.cxx index 0f8d7d2658..35c653c5b2 100644 --- a/ppremake/filename.cxx +++ b/ppremake/filename.cxx @@ -33,6 +33,10 @@ #include #endif +#ifdef HAVE_GLOB_H +#include +#endif + #ifdef HAVE_DIRENT_H #include #endif @@ -705,44 +709,14 @@ make_canonical() { return false; } + if (get_fullpath() == "/") { + // The root directory is a special case. + return true; + } + // Temporarily save the current working directory. Filename cwd = ExecutionEnvironment::get_cwd(); - - if (is_directory()) { - // If the filename itself represents a directory and not a - // filename, cd to the named directory, not the one above it. - string dirname = to_os_specific(); - - if (chdir(dirname.c_str()) < 0) { - return false; - } - (*this) = ExecutionEnvironment::get_cwd(); - - } else { - // Otherwise, if the filename represents a regular file (or - // doesn't even exist), cd to the directory above. - Filename dir(get_dirname()); - - if (dir.empty()) { - // No dirname means the file is in this directory. - set_dirname(cwd); - return true; - } - - string dirname = dir.to_os_specific(); - if (chdir(dirname.c_str()) < 0) { - return false; - } - set_dirname(ExecutionEnvironment::get_cwd().get_fullpath()); - } - - // Now restore the current working directory. - string osdir = cwd.to_os_specific(); - if (chdir(osdir.c_str()) < 0) { - cerr << "Error! Cannot change back to " << cwd << "\n"; - } - - return true; + return r_make_canonical(cwd); } //////////////////////////////////////////////////////////////////// @@ -1182,7 +1156,32 @@ find_on_searchpath(const DSearchPath &searchpath) { //////////////////////////////////////////////////////////////////// bool Filename:: scan_directory(vector_string &contents) const { -#if defined(HAVE_DIRENT_H) +#if defined(HAVE_GLOB_H) + // In some cases, particularly with NFS, it seems that opendir() + // .. readdir() fails to properly read the entire directory ("Value + // too large for defined data type"), but glob() succeeds. + string dirname; + if (empty()) { + dirname = "*"; + } else if (_filename[_filename.length() - 1] == '/') { + dirname = _filename + "*"; + } else { + dirname = _filename + "/*"; + } + + glob_t globbuf; + + int r = glob(dirname.c_str(), GLOB_ERR, NULL, &globbuf); + size_t offset = dirname.size() - 1; + + for (int i = 0; globbuf.gl_pathv[i] != NULL; i++) { + contents.push_back(globbuf.gl_pathv[i] + offset); + } + globfree(&globbuf); + + return true; + +#elif defined(HAVE_DIRENT_H) size_t orig_size = contents.size(); string dirname; @@ -1193,6 +1192,7 @@ scan_directory(vector_string &contents) const { } DIR *root = opendir(dirname.c_str()); if (root == (DIR *)NULL) { + perror(dirname.c_str()); return false; } @@ -1204,6 +1204,12 @@ scan_directory(vector_string &contents) const { } d = readdir(root); } + if (errno != 0 && errno != ENOENT && errno != ENOTDIR) { + cerr << "Error occurred while scanning directory " << dirname << "\n"; + perror(dirname.c_str()); + closedir(root); + return false; + } closedir(root); sort(contents.begin() + orig_size, contents.end()); @@ -1379,14 +1385,20 @@ open_append(ofstream &stream) const { // false otherwise. This requires the setting of the // set_text()/set_binary() flags to open the file // appropriately as indicated; it is an error to call -// open_read() without first calling one of set_text() -// or set_binary(). +// open_read_write() without first calling one of +// set_text() or set_binary(). //////////////////////////////////////////////////////////////////// bool Filename:: open_read_write(fstream &stream) const { assert(is_text() || is_binary()); - ios_openmode open_mode = ios::in | ios::out; + ios_openmode open_mode = ios::out | ios::in; + + // Since ios::in also seems to imply ios::nocreate (!), we must + // guarantee the file already exists before we try to open it. + if (!exists()) { + touch(); + } #ifdef HAVE_IOS_BINARY // For some reason, some systems (like Irix) don't define @@ -1731,3 +1743,50 @@ count_slashes(const string &str) { return count; } + +//////////////////////////////////////////////////////////////////// +// Function: Filename::r_make_canonical +// Access: Private +// Description: The recursive implementation of make_canonical(). +//////////////////////////////////////////////////////////////////// +bool Filename:: +r_make_canonical(const Filename &cwd) { + if (get_fullpath() == "/") { + // If we reached the root, the whole path doesn't exist. Report + // failure. + return false; + } + + // First, try to cd to the filename directly. + string os_specific = to_os_specific(); + + if (chdir(os_specific.c_str()) >= 0) { + // That worked, save the full path string. + (*this) = ExecutionEnvironment::get_cwd(); + + // And restore the current working directory. + string osdir = cwd.to_os_specific(); + if (chdir(osdir.c_str()) < 0) { + cerr << "Error! Cannot change back to " << cwd << "\n"; + } + return true; + } + + // That didn't work; maybe it's not a directory. Recursively go to + // the directory above. + + Filename dir(get_dirname()); + + if (dir.empty()) { + // No dirname means the file is in this directory. + set_dirname(cwd); + return true; + } + + if (!dir.r_make_canonical(cwd)) { + return false; + } + set_dirname(dir); + return true; +} + diff --git a/ppremake/filename.h b/ppremake/filename.h index f246233903..a62f7dbbcb 100644 --- a/ppremake/filename.h +++ b/ppremake/filename.h @@ -175,6 +175,7 @@ private: void locate_extension(); size_t get_common_prefix(const string &other) const; static int count_slashes(const string &str); + bool r_make_canonical(const Filename &cwd); string _filename; // We'll make these size_t instead of string::size_type to help out