diff --git a/ppremake/executionEnvironment.cxx b/ppremake/executionEnvironment.cxx index 689980b296..8095818031 100644 --- a/ppremake/executionEnvironment.cxx +++ b/ppremake/executionEnvironment.cxx @@ -31,6 +31,81 @@ #define getcwd _getcwd #endif +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::get_environment_variable +// Access: Public, Static +// Description: Returns the definition of the indicated environment +// variable, or the empty string if the variable is +// undefined. The nonstatic implementation. +//////////////////////////////////////////////////////////////////// +string ExecutionEnvironment:: +get_environment_variable(const string &var) { + const char *def = getenv(var.c_str()); + if (def != (char *)NULL) { + return def; + } + return string(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnviroment::expand_string +// Access: Public, Static +// Description: Reads the string, looking for environment variable +// names marked by a $. Expands all such variable +// names. A repeated dollar sign ($$) is mapped to a +// single dollar sign. +// +// Returns the expanded string. +//////////////////////////////////////////////////////////////////// +string ExecutionEnvironment:: +expand_string(const string &str) { + string result; + + size_t last = 0; + size_t dollar = str.find('$'); + while (dollar != string::npos && dollar + 1 < str.length()) { + size_t start = dollar + 1; + + if (str[start] == '$') { + // A double dollar sign maps to a single dollar sign. + result += str.substr(last, start - last); + last = start + 1; + + } else { + string varname; + size_t end = start; + + if (str[start] == '{') { + // Curly braces delimit the variable name explicitly. + end = str.find('}', start + 1); + if (end != string::npos) { + varname = str.substr(start + 1, end - (start + 1)); + end++; + } + } + + if (end == start) { + // Scan for the end of the variable name. + while (end < str.length() && (isalnum(str[end]) || str[end] == '_')) { + end++; + } + varname = str.substr(start, end - start); + } + + string subst = + result += str.substr(last, dollar - last); + result += get_environment_variable(varname); + last = end; + } + + dollar = str.find('$', last); + } + + result += str.substr(last); + + return result; +} + //////////////////////////////////////////////////////////////////// // Function: ExecutionEnviroment::get_cwd // Access: Public, Static diff --git a/ppremake/executionEnvironment.h b/ppremake/executionEnvironment.h index fc88da7d54..2eca1fa667 100644 --- a/ppremake/executionEnvironment.h +++ b/ppremake/executionEnvironment.h @@ -30,6 +30,8 @@ //////////////////////////////////////////////////////////////////// class ExecutionEnvironment { public: + static string get_environment_variable(const string &var); + static string expand_string(const string &str); static Filename get_cwd(); }; diff --git a/ppremake/filename.cxx b/ppremake/filename.cxx index e6ed230ca7..0f8d7d2658 100644 --- a/ppremake/filename.cxx +++ b/ppremake/filename.cxx @@ -52,6 +52,12 @@ #include #endif +// The MSVC 6.0 Win32 SDK lacks the following definitions, so we define them +// here for compatibility. +#ifndef FILE_ATTRIBUTE_DEVICE +#define FILE_ATTRIBUTE_DEVICE 0x00000040 +#endif + // We might have been linked with the Cygwin dll. This is ideal if it // is available, because it allows Panda to access all the Cygwin // mount definitions if they are in use. If the Cygwin dll is not @@ -99,7 +105,7 @@ get_panda_root() { panda_root = front_to_back_slash(envvar); } - if (!panda_root.empty() && panda_root[panda_root.length() - 1] != '\\') { + if (panda_root.empty() || panda_root[panda_root.length() - 1] != '\\') { panda_root += '\\'; } @@ -110,7 +116,7 @@ get_panda_root() { } static string -convert_pathname(const string &unix_style_pathname) { +convert_pathname(const string &unix_style_pathname, bool use_backslash) { if (unix_style_pathname.empty()) { return string(); } @@ -133,7 +139,11 @@ convert_pathname(const string &unix_style_pathname) { // It doesn't even start from the root, so we don't have to do // anything fancy--relative pathnames are the same in Windows as // in Unix, except for the direction of the slashes. - windows_pathname = front_to_back_slash(unix_style_pathname); + if (use_backslash) { + windows_pathname = front_to_back_slash(unix_style_pathname); + } else { + windows_pathname = unix_style_pathname; + } } else if (unix_style_pathname.length() > 3 && isalpha(unix_style_pathname[1]) && @@ -145,9 +155,15 @@ convert_pathname(const string &unix_style_pathname) { // compilers (e.g. Cygwin's gcc 2.95.3) happy; so that they do not // confuse this string constructor with one that takes two // iterators. - windows_pathname = - string(1, (char)toupper(unix_style_pathname[1])) + ":" + - front_to_back_slash(unix_style_pathname.substr(2)); + if (use_backslash) { + windows_pathname = + string(1, (char)toupper(unix_style_pathname[1])) + ":" + + front_to_back_slash(unix_style_pathname.substr(2)); + } else { + windows_pathname = + string(1, (char)toupper(unix_style_pathname[1])) + ":" + + unix_style_pathname.substr(2); + } } else { // It starts with a slash, but the first part is not a single @@ -157,29 +173,37 @@ convert_pathname(const string &unix_style_pathname) { // Use Cygwin to convert it if possible. char result[4096] = ""; cygwin_conv_to_win32_path(unix_style_pathname.c_str(), result); - windows_pathname = result; + if (use_backslash) { + windows_pathname = result; + } else { + windows_pathname = back_to_front_slash(result); + } #else // HAVE_CYGWIN // Without Cygwin, just prefix $PANDA_ROOT. - windows_pathname = - get_panda_root() + front_to_back_slash(unix_style_pathname.substr(1)); + windows_pathname = get_panda_root(); + if (use_backslash) { + windows_pathname += front_to_back_slash(unix_style_pathname.substr(1)); + } else { + windows_pathname += unix_style_pathname.substr(1); + } #endif // HAVE_CYGWIN } return windows_pathname; } -string -convert_dso_pathname(const string &unix_style_pathname) { +static string +convert_dso_pathname(const string &unix_style_pathname, bool use_backslash) { // If the extension is .so, change it to .dll. size_t dot = unix_style_pathname.rfind('.'); if (dot == string::npos || unix_style_pathname.find('/', dot) != string::npos) { // No filename extension. - return convert_pathname(unix_style_pathname); + return convert_pathname(unix_style_pathname, use_backslash); } if (unix_style_pathname.substr(dot) != ".so") { // Some other extension. - return convert_pathname(unix_style_pathname); + return convert_pathname(unix_style_pathname, use_backslash); } string dll_basename = unix_style_pathname.substr(0, dot); @@ -196,27 +220,27 @@ convert_dso_pathname(const string &unix_style_pathname) { // somewhere on the LD_LIBRARY_PATH, or on PATH, or any of a number // of nutty places. - return convert_pathname(dll_basename + "_d.dll"); + return convert_pathname(dll_basename + "_d.dll", use_backslash); #else - return convert_pathname(dll_basename + ".dll"); + return convert_pathname(dll_basename + ".dll", use_backslash); #endif } -string -convert_executable_pathname(const string &unix_style_pathname) { +static string +convert_executable_pathname(const string &unix_style_pathname, bool use_backslash) { // If the extension is not .exe, append .exe. size_t dot = unix_style_pathname.rfind('.'); if (dot == string::npos || unix_style_pathname.find('/', dot) != string::npos) { // No filename extension. - return convert_pathname(unix_style_pathname + ".exe"); + return convert_pathname(unix_style_pathname + ".exe", use_backslash); } if (unix_style_pathname.substr(dot) != ".exe") { // Some other extension. - return convert_pathname(unix_style_pathname + ".exe"); + return convert_pathname(unix_style_pathname + ".exe", use_backslash); } - return convert_pathname(unix_style_pathname); + return convert_pathname(unix_style_pathname, use_backslash); } #endif //WIN32 @@ -313,6 +337,19 @@ from_os_specific(const string &os_specific, Filename::Type type) { #endif // WIN32 } +//////////////////////////////////////////////////////////////////// +// Function: Filename::expand_from +// Access: Public, Static +// Description: Returns the same thing as from_os_specific(), but +// embedded environment variable references +// (e.g. "$DMODELS/foo.txt") are expanded out. +//////////////////////////////////////////////////////////////////// +Filename Filename:: +expand_from(const string &os_specific, Filename::Type type) { + return from_os_specific(ExecutionEnvironment::expand_string(os_specific), + type); +} + //////////////////////////////////////////////////////////////////// // Function: Filename::temporary // Access: Public @@ -505,6 +542,39 @@ set_extension(const string &s) { } } +//////////////////////////////////////////////////////////////////// +// Function: Filename::extract_components +// Access: Public +// Description: Extracts out the individual directory components of +// the path into a series of strings. get_basename() +// will be the last component stored in the vector. +// Note that no distinction is made by this method +// between a leading slash and no leading slash, but you +// can call is_local() to differentiate the two cases. +//////////////////////////////////////////////////////////////////// +void Filename:: +extract_components(vector_string &components) const { + components.clear(); + + size_t p = 0; + if (!_filename.empty() && _filename[0] == '/') { + // Skip the leading slash. + p = 1; + } + while (p < _filename.length()) { + size_t q = _filename.find('/', p); + if (q == string::npos) { + components.push_back(_filename.substr(p)); + return; + } + components.push_back(_filename.substr(p, q - p)); + p = q + 1; + } + + // A trailing slash means we have an empty get_basename(). + components.push_back(string()); +} + //////////////////////////////////////////////////////////////////// // Function: Filename::standardize // Access: Public @@ -700,11 +770,48 @@ to_os_specific() const { #ifdef WIN32 switch (get_type()) { case T_dso: - return convert_dso_pathname(standard.get_fullpath()); + return convert_dso_pathname(standard.get_fullpath(), true); case T_executable: - return convert_executable_pathname(standard.get_fullpath()); + return convert_executable_pathname(standard.get_fullpath(), true); default: - return convert_pathname(standard.get_fullpath()); + return convert_pathname(standard.get_fullpath(), true); + } +#else // WIN32 + return standard; +#endif // WIN32 +} + +//////////////////////////////////////////////////////////////////// +// Function: Filename::to_os_generic +// Access: Public +// Description: This is similar to to_os_specific(), but it is +// designed to generate a filename that can be +// understood on as many platforms as possible. Since +// Windows can usually understand a +// forward-slash-delimited filename, this means it does +// the same thing as to_os_specific(), but it uses +// forward slashes instead of backslashes. +// +// This method has a pretty limited use; it should +// generally be used for writing file references to a +// file that might be read on any operating system. +//////////////////////////////////////////////////////////////////// +string Filename:: +to_os_generic() const { + if (empty()) { + return string(); + } + Filename standard(*this); + standard.standardize(); + +#ifdef WIN32 + switch (get_type()) { + case T_dso: + return convert_dso_pathname(standard.get_fullpath(), false); + case T_executable: + return convert_executable_pathname(standard.get_fullpath(), false); + default: + return convert_pathname(standard.get_fullpath(), false); } #else // WIN32 return standard; @@ -1158,7 +1265,7 @@ bool Filename:: open_read(ifstream &stream) const { assert(is_text() || is_binary()); - ios::openmode open_mode = ios::in; + ios_openmode open_mode = ios::in; #ifdef HAVE_IOS_BINARY // For some reason, some systems (like Irix) don't define @@ -1184,12 +1291,30 @@ open_read(ifstream &stream) const { // appropriately as indicated; it is an error to call // open_read() without first calling one of set_text() // or set_binary(). +// +// If truncate is true, the file is truncated to zero +// length upon opening it, if it already exists. +// Otherwise, the file is kept at its original length. //////////////////////////////////////////////////////////////////// bool Filename:: -open_write(ofstream &stream) const { +open_write(ofstream &stream, bool truncate) const { assert(is_text() || is_binary()); - ios::openmode open_mode = ios::out; + ios_openmode open_mode = ios::out; + + if (truncate) { + open_mode |= ios::trunc; + + } else { + // Some systems insist on having ios::in set to prevent the file + // from being truncated when we open it. Makes ios::trunc kind of + // pointless, doesn't it? On the other hand, setting ios::in also + // seems to imply ios::nocreate (!), so we should only set this if + // the file already exists. + if (exists()) { + open_mode |= ios::in; + } + } #ifdef HAVE_IOS_BINARY // For some reason, some systems (like Irix) don't define @@ -1225,7 +1350,7 @@ bool Filename:: open_append(ofstream &stream) const { assert(is_text() || is_binary()); - ios::openmode open_mode = ios::app; + ios_openmode open_mode = ios::app; #ifdef HAVE_IOS_BINARY // For some reason, some systems (like Irix) don't define @@ -1261,7 +1386,7 @@ 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::in | ios::out; #ifdef HAVE_IOS_BINARY // For some reason, some systems (like Irix) don't define @@ -1292,7 +1417,36 @@ open_read_write(fstream &stream) const { //////////////////////////////////////////////////////////////////// bool Filename:: touch() const { -#ifdef HAVE_UTIME_H +#ifdef WIN32_VC + // In Windows, we have to use the Windows API to do this reliably. + + // First, guarantee the file exists (and also get its handle). + string os_specific = to_os_specific(); + HANDLE fhandle; + fhandle = CreateFile(os_specific.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, + NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (fhandle == INVALID_HANDLE_VALUE) { + return false; + } + + // Now update the file time and date. + SYSTEMTIME sysnow; + FILETIME ftnow; + GetSystemTime(&sysnow); + if (!SystemTimeToFileTime(&sysnow, &ftnow)) { + CloseHandle(fhandle); + return false; + } + + if (!SetFileTime(fhandle, NULL, NULL, &ftnow)) { + CloseHandle(fhandle); + return false; + } + + CloseHandle(fhandle); + return true; + +#elif defined(HAVE_UTIME_H) // Most Unix systems can do this explicitly. string os_specific = to_os_specific(); @@ -1325,14 +1479,14 @@ touch() const { return false; } return true; -#else // HAVE_UTIME_H +#else // WIN32, HAVE_UTIME_H // Other systems may not have an explicit control over the // modification time. For these systems, we'll just temporarily // open the file in append mode, then close it again (it gets closed // when the ofstream goes out of scope). ofstream file; return open_append(file); -#endif // HAVE_UTIME_H +#endif // WIN32, HAVE_UTIME_H } //////////////////////////////////////////////////////////////////// @@ -1374,12 +1528,27 @@ rename_to(const Filename &other) const { // itself. This assumes that the Filename contains the // name of a file, not a directory name; it ensures that // the directory containing the file exists. +// +// However, if the filename ends in a slash, it assumes +// the Filename represents the name of a directory, and +// creates all the paths. //////////////////////////////////////////////////////////////////// bool Filename:: make_dir() const { + if (empty()) { + return false; + } Filename path = *this; path.standardize(); - string dirname = path.get_dirname(); + string dirname; + if (_filename[_filename.length() - 1] == '/') { + // The Filename ends in a slash; it represents a directory. + dirname = path.get_fullpath(); + + } else { + // The Filename does not end in a slash; it represents a file. + dirname = path.get_dirname(); + } // First, make sure everything up to the last path is known. We // don't care too much if any of these fail; maybe they failed diff --git a/ppremake/filename.h b/ppremake/filename.h index a17aa59468..f246233903 100644 --- a/ppremake/filename.h +++ b/ppremake/filename.h @@ -77,6 +77,8 @@ PUBLISHED: static Filename from_os_specific(const string &os_specific, Type type = T_general); + static Filename expand_from(const string &user_string, + Type type = T_general); static Filename temporary(const string &dirname, const string &prefix, Type type = T_general); @@ -120,6 +122,7 @@ PUBLISHED: INLINE void set_type(Type type); INLINE Type get_type() const; + void extract_components(vector_string &components) const; void standardize(); // The following functions deal with the outside world. @@ -132,6 +135,7 @@ PUBLISHED: bool make_canonical(); string to_os_specific() const; + string to_os_generic() const; bool exists() const; bool is_regular_file() const; @@ -148,7 +152,7 @@ PUBLISHED: bool scan_directory(vector_string &contents) const; bool open_read(ifstream &stream) const; - bool open_write(ofstream &stream) const; + bool open_write(ofstream &stream, bool truncate = true) const; bool open_append(ofstream &stream) const; bool open_read_write(fstream &stream) const; diff --git a/ppremake/ppScope.cxx b/ppremake/ppScope.cxx index 5b8107ef29..881f687fb7 100644 --- a/ppremake/ppScope.cxx +++ b/ppremake/ppScope.cxx @@ -1911,34 +1911,34 @@ expand_makeguid(const string ¶ms) { MD5Final(digest, &context); string guid; - int i = 0; char hex[2]; + int i; - for (int i = 0; i < 4; i++) { + for (i = 0; i < 4; i++) { sprintf(hex, "%02x", digest[i]); guid.append(hex); } guid += "-"; - for (int i = 4; i < 6; i++) { + for (i = 4; i < 6; i++) { sprintf(hex, "%02x", digest[i]); guid.append(hex); } guid += "-"; - for (int i = 6; i < 8; i++) { + for (i = 6; i < 8; i++) { sprintf(hex, "%02x", digest[i]); guid.append(hex); } guid += "-"; - for (int i = 8; i < 10; i++) { + for (i = 8; i < 10; i++) { sprintf(hex, "%02x", digest[i]); guid.append(hex); } guid += "-"; - for (int i = 10; i < 16; i++) { + for (i = 10; i < 16; i++) { sprintf(hex, "%02x", digest[i]); guid.append(hex); } diff --git a/ppremake/ppremake.h b/ppremake/ppremake.h index 2199bd8920..67738f353f 100644 --- a/ppremake/ppremake.h +++ b/ppremake/ppremake.h @@ -22,17 +22,30 @@ #include #ifdef HAVE_SSTREAM #include -#else // HAVE_SSTREAM +#else /* HAVE_SSTREAM */ #include -#endif // HAVE_SSTREAM -#else // HAVE_IOSTREAM +#endif /* HAVE_SSTREAM */ + +typedef std::ios::openmode ios_openmode; +typedef std::ios::fmtflags ios_fmtflags; +typedef std::ios::iostate ios_iostate; +typedef std::ios::seekdir ios_seekdir; + +#else /* HAVE_IOSTREAM */ #include #include #include -#endif // HAVE_IOSTREAM + +typedef int ios_openmode; +typedef int ios_fmtflags; +typedef int ios_iostate; +/* Old iostream libraries used ios::seek_dir instead of ios::seekdir. */ +typedef ios::seek_dir ios_seekdir; + +#endif /* HAVE_IOSTREAM */ #if defined(HAVE_CYGWIN) || defined(WIN32_VC) -// Either Cygwin or Visual C++ is a Win32 environment. +/* Either Cygwin or Visual C++ is a Win32 environment. */ #define WIN32 #endif