Browse Source

more preparations for VC++ building

David Rose 23 years ago
parent
commit
46821fc8f1

+ 5 - 2
ppremake/Makefile.am

@@ -1,7 +1,10 @@
 bin_PROGRAMS = ppremake
 bin_PROGRAMS = ppremake
 
 
 ppremake_SOURCES =							\
 ppremake_SOURCES =							\
-    check_include.cxx check_include.h filename.cxx filename.h		\
+    check_include.cxx check_include.h					\
+    dSearchPath.I dSearchPath.cxx dSearchPath.h				\
+    executionEnvironment.cxx executionEnvironment.h			\
+    filename.I filename.cxx filename.h					\
     find_searchpath.cxx find_searchpath.h				\
     find_searchpath.cxx find_searchpath.h				\
     gnu_getopt.c gnu_getopt.h gnu_regex.c gnu_regex.h			\
     gnu_getopt.c gnu_getopt.h gnu_regex.c gnu_regex.h			\
     ppCommandFile.cxx ppCommandFile.h ppDependableFile.cxx		\
     ppCommandFile.cxx ppCommandFile.h ppDependableFile.cxx		\
@@ -14,4 +17,4 @@ ppremake_SOURCES =							\
     ppremake.cxx ppremake.h sedAddress.cxx sedAddress.h sedCommand.cxx	\
     ppremake.cxx ppremake.h sedAddress.cxx sedAddress.h sedCommand.cxx	\
     sedCommand.h sedContext.cxx sedContext.h sedProcess.cxx		\
     sedCommand.h sedContext.cxx sedContext.h sedProcess.cxx		\
     sedProcess.h sedScript.cxx sedScript.h tokenize.cxx			\
     sedProcess.h sedScript.cxx sedScript.h tokenize.cxx			\
-    tokenize.h
+    tokenize.h vector_string.h

+ 2 - 2
ppremake/acconfig.h

@@ -20,8 +20,8 @@
 /* Define if the C++ iostream library supports ios::binary.  */
 /* Define if the C++ iostream library supports ios::binary.  */
 #undef HAVE_IOS_BINARY
 #undef HAVE_IOS_BINARY
 
 
-/* Define if we're compiling for a Windows platform.  */
-#undef PLATFORM_WIN32
+/* Define if we're compiling for Cygwin. */
+#undef PLATFORM_CYGWIN
 
 
 /* Define if we're compiling using Windows Microsoft Visual C++. */
 /* Define if we're compiling using Windows Microsoft Visual C++. */
 #undef WIN32_VC
 #undef WIN32_VC

+ 8 - 4
ppremake/config_msvc.h

@@ -12,8 +12,8 @@
 /* Define if the C++ iostream library supports ios::binary.  */
 /* Define if the C++ iostream library supports ios::binary.  */
 /* #undef HAVE_IOS_BINARY */
 /* #undef HAVE_IOS_BINARY */
 
 
-/* Define if we're compiling for a Windows platform.  */
-#define PLATFORM_WIN32 1
+/* Define if we're compiling for Cygwin. */
+/* #undef PLATFORM_CYGWIN */
 
 
 /* Define if we're compiling using Windows Microsoft Visual C++. */
 /* Define if we're compiling using Windows Microsoft Visual C++. */
 #define WIN32_VC 1
 #define WIN32_VC 1
@@ -79,5 +79,9 @@
 /* Define if you have the ANSI C header files. */
 /* Define if you have the ANSI C header files. */
 #define STDC_HEADERS 1
 #define STDC_HEADERS 1
 
 
-/* Version number of package */
-#define VERSION "1.03"
+/****************  UPDATE VERSION NUMBER HERE  ****************
+ **         Also be sure to change the version number        **
+ **             at the beginning of configure.in.            **
+ ****************                              ****************/
+#define VERSION "1.04"
+/****************  UPDATE VERSION NUMBER HERE  ****************/

+ 11 - 4
ppremake/configure.in

@@ -1,6 +1,13 @@
 dnl Process this file with autoconf to produce a configure script.
 dnl Process this file with autoconf to produce a configure script.
 AC_INIT(ppremake.cxx)
 AC_INIT(ppremake.cxx)
-AM_INIT_AUTOMAKE(ppremake, 1.03)
+
+dnl ****************  UPDATE VERSION NUMBER HERE  ****************
+dnl **         Also be sure to change the version number        **
+dnl **                at the end of config_msvc.h.              **
+dnl ****************                              ****************
+AM_INIT_AUTOMAKE(ppremake, 1.04)
+dnl ****************  UPDATE VERSION NUMBER HERE  ****************
+
 AM_CONFIG_HEADER(config.h)
 AM_CONFIG_HEADER(config.h)
 
 
 AC_PREFIX_DEFAULT(/usr/local/panda)
 AC_PREFIX_DEFAULT(/usr/local/panda)
@@ -60,14 +67,14 @@ else
   case "$host_os" in
   case "$host_os" in
     irix*) PLATFORM=Irix;;
     irix*) PLATFORM=Irix;;
     linux*) PLATFORM=Linux;;
     linux*) PLATFORM=Linux;;
-    cygwin*) PLATFORM=Win32;;
+    cygwin*) PLATFORM=Cygwin;;
     *) echo "Cannot determine platform; use --with-platform=name."
     *) echo "Cannot determine platform; use --with-platform=name."
        exit 1;;
        exit 1;;
   esac
   esac
 fi
 fi
 
 
-if test "$PLATFORM" = "Win32"; then
-  AC_DEFINE(PLATFORM_WIN32)
+if test "$PLATFORM" = "Cygwin"; then
+  AC_DEFINE(PLATFORM_CYGWIN)
 fi
 fi
 
 
 AC_DEFINE_UNQUOTED(PLATFORM, "$PLATFORM")
 AC_DEFINE_UNQUOTED(PLATFORM, "$PLATFORM")

+ 34 - 0
ppremake/dSearchPath.I

@@ -0,0 +1,34 @@
+// Filename: dSearchPath.I
+// Created by:  drose (01Jul00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::search_path
+//       Access: Public, Static
+//  Description: A quick-and-easy way to search a searchpath for a
+//               file when you don't feel like building or keeping
+//               around a DSearchPath object.  This simply
+//               constructs a temporary DSearchPath based on the
+//               indicated path string, and searches that.
+////////////////////////////////////////////////////////////////////
+INLINE Filename DSearchPath::
+search_path(const Filename &filename, const string &path,
+            const string &delimiters) {
+  DSearchPath search(path, delimiters);
+  return search.find_file(filename);
+}

+ 334 - 0
ppremake/dSearchPath.cxx

@@ -0,0 +1,334 @@
+// Filename: dSearchPath.cxx
+// Created by:  drose (01Jul00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "dSearchPath.h"
+#include "filename.h"
+
+#include <algorithm>
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::Results::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DSearchPath::Results::
+Results() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::Results::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DSearchPath::Results::
+Results(const DSearchPath::Results &copy) :
+  _files(copy._files)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::Results::Copy Assignment Operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DSearchPath::Results::
+operator = (const DSearchPath::Results &copy) {
+  _files = copy._files;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::Results::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DSearchPath::Results::
+~Results() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::Results::clear
+//       Access: Public
+//  Description: Removes all the files from the list.
+////////////////////////////////////////////////////////////////////
+void DSearchPath::Results::
+clear() {
+  _files.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::Results::get_num_files
+//       Access: Public
+//  Description: Returns the number of files on the result list.
+////////////////////////////////////////////////////////////////////
+int DSearchPath::Results::
+get_num_files() const {
+  return _files.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::Results::get_file
+//       Access: Public
+//  Description: Returns the nth file on the result list.
+////////////////////////////////////////////////////////////////////
+Filename DSearchPath::Results::
+get_file(int n) const {
+  assert(n >= 0 && n < (int)_files.size());
+  return _files[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::Default Constructor
+//       Access: Public
+//  Description: Creates an empty search path.
+////////////////////////////////////////////////////////////////////
+DSearchPath::
+DSearchPath() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DSearchPath::
+DSearchPath(const string &path, const string &delimiters) {
+  append_path(path, delimiters);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DSearchPath::
+DSearchPath(const DSearchPath &copy) :
+  _directories(copy._directories)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::Copy Assignment Operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DSearchPath::
+operator = (const DSearchPath &copy) {
+  _directories = copy._directories;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DSearchPath::
+~DSearchPath() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::clear
+//       Access: Public
+//  Description: Removes all the directories from the search list.
+////////////////////////////////////////////////////////////////////
+void DSearchPath::
+clear() {
+  _directories.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::append_directory
+//       Access: Public
+//  Description: Adds a new directory to the end of the search list.
+////////////////////////////////////////////////////////////////////
+void DSearchPath::
+append_directory(const Filename &directory) {
+  _directories.push_back(directory);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::prepend_directory
+//       Access: Public
+//  Description: Adds a new directory to the front of the search list.
+////////////////////////////////////////////////////////////////////
+void DSearchPath::
+prepend_directory(const Filename &directory) {
+  _directories.insert(_directories.begin(), directory);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::append_path
+//       Access: Public
+//  Description: Adds all of the directories listed in the search path
+//               to the end of the search list.
+////////////////////////////////////////////////////////////////////
+void DSearchPath::
+append_path(const string &path, const string &delimiters) {
+  size_t p = 0;
+  while (p < path.length()) {
+    size_t q = path.find_first_of(delimiters, p);
+    if (q == string::npos) {
+      _directories.push_back(path.substr(p));
+      return;
+    }
+    if (q != p) {
+      _directories.push_back(path.substr(p, q - p));
+    }
+    p = q + 1;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::append_path
+//       Access: Public
+//  Description: Adds all of the directories listed in the search path
+//               to the end of the search list.
+////////////////////////////////////////////////////////////////////
+void DSearchPath::
+append_path(const DSearchPath &path) {
+  copy(path._directories.begin(), path._directories.end(),
+       back_inserter(_directories));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::prepend_path
+//       Access: Public
+//  Description: Adds all of the directories listed in the search path
+//               to the beginning of the search list.
+////////////////////////////////////////////////////////////////////
+void DSearchPath::
+prepend_path(const DSearchPath &path) {
+  if (!path._directories.empty()) {
+    Directories new_directories = path._directories;
+    copy(_directories.begin(), _directories.end(),
+         back_inserter(new_directories));
+    _directories.swap(new_directories);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::is_empty
+//       Access: Public
+//  Description: Returns true if the search list is empty, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool DSearchPath::
+is_empty() const {
+  return _directories.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::get_num_directories
+//       Access: Public
+//  Description: Returns the number of directories on the search list.
+////////////////////////////////////////////////////////////////////
+int DSearchPath::
+get_num_directories() const {
+  return _directories.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::get_directory
+//       Access: Public
+//  Description: Returns the nth directory on the search list.
+////////////////////////////////////////////////////////////////////
+Filename DSearchPath::
+get_directory(int n) const {
+  assert(n >= 0 && n < (int)_directories.size());
+  return _directories[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::find_file
+//       Access: Public
+//  Description: Searches all the directories in the search list for
+//               the indicated file, in order.  Returns the full
+//               matching pathname of the first match if found, or the
+//               empty string if not found.
+////////////////////////////////////////////////////////////////////
+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;
+    }
+  }
+
+  return string();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::find_all_files
+//       Access: Public
+//  Description: Searches all the directories in the search list for
+//               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.
+////////////////////////////////////////////////////////////////////
+int DSearchPath::
+find_all_files(const Filename &filename,
+               DSearchPath::Results &results) const {
+  results._files.clear();
+
+  Directories::const_iterator di;
+  for (di = _directories.begin(); di != _directories.end(); ++di) {
+    Filename match((*di), filename);
+    if (match.exists()) {
+      results._files.push_back(match);
+    }
+  }
+
+  return results._files.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::output
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DSearchPath::
+output(ostream &out, const string &separator) const {
+  if (!_directories.empty()) {
+    Directories::const_iterator di = _directories.begin();
+    out << (*di);
+    ++di;
+    while (di != _directories.end()) {
+      out << separator << (*di);
+      ++di;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DSearchPath::write
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DSearchPath::
+write(ostream &out, int indent_level) const {
+  Directories::const_iterator di;
+  for (di = _directories.begin(); di != _directories.end(); ++di) {
+    for (int i = 0; i < indent_level; i++) {
+      out << ' ';
+    }
+    out << (*di) << "\n";
+  }
+}
+
+

+ 97 - 0
ppremake/dSearchPath.h

@@ -0,0 +1,97 @@
+// Filename: dSearchPath.h
+// Created by:  drose (01Jul00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef PANDASEARCHPATH_H
+#define PANDASEARCHPATH_H
+
+#include "ppremake.h"
+
+#include "filename.h"
+
+#include <vector>
+
+///////////////////////////////////////////////////////////////////
+//       Class : DSearchPath
+// Description : This class stores a list of directories that can be
+//               searched, in order, to locate a particular file.  It
+//               is normally constructed by passing it a traditional
+//               searchpath-style string, e.g. a list of directory
+//               names delimited by spaces or colons, but it can also
+//               be built up explicitly.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOL DSearchPath {
+public:
+  class EXPCL_DTOOL Results {
+  PUBLISHED:
+    Results();
+    Results(const Results &copy);
+    void operator = (const Results &copy);
+    ~Results();
+
+    void clear();
+    int get_num_files() const;
+    Filename get_file(int n) const;
+
+  private:
+    typedef vector<Filename> Files;
+    Files _files;
+    friend class DSearchPath;
+  };
+
+PUBLISHED:
+  DSearchPath();
+  DSearchPath(const string &path, const string &delimiters = ": \n\t");
+  DSearchPath(const DSearchPath &copy);
+  void operator = (const DSearchPath &copy);
+  ~DSearchPath();
+
+  void clear();
+  void append_directory(const Filename &directory);
+  void prepend_directory(const Filename &directory);
+  void append_path(const string &path,
+                   const string &delimiters = ": \n\t");
+  void append_path(const DSearchPath &path);
+  void prepend_path(const DSearchPath &path);
+
+  bool is_empty() const;
+  int get_num_directories() const;
+  Filename get_directory(int n) const;
+
+  Filename find_file(const Filename &filename) const;
+  int find_all_files(const Filename &filename, Results &results) const;
+
+  INLINE static Filename
+  search_path(const Filename &filename, const string &path,
+              const string &delimiters = ": \n\t");
+
+  void output(ostream &out, const string &separator = ":") const;
+  void write(ostream &out, int indent_level = 0) const;
+
+private:
+  typedef vector<Filename> Directories;
+  Directories _directories;
+};
+
+INLINE ostream &operator << (ostream &out, const DSearchPath &sp) {
+  sp.output(out);
+  return out;
+}
+
+#include "dSearchPath.I"
+
+#endif

+ 62 - 0
ppremake/executionEnvironment.cxx

@@ -0,0 +1,62 @@
+// Filename: executionEnvironment.cxx
+// Created by:  drose (15May00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "executionEnvironment.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>  // for perror
+
+#ifdef WIN32_VC
+// Windows requires this for getcwd().
+#include <direct.h>
+#define getcwd _getcwd
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: ExecutionEnviroment::get_cwd
+//       Access: Public, Static
+//  Description: Returns the name of the current working directory.
+////////////////////////////////////////////////////////////////////
+Filename ExecutionEnvironment::
+get_cwd() {
+  // getcwd() requires us to allocate a dynamic buffer and grow it on
+  // demand.
+  static size_t bufsize = 1024;
+  static char *buffer = NULL;
+
+  if (buffer == (char *)NULL) {
+    buffer = new char[bufsize];
+  }
+
+  while (getcwd(buffer, bufsize) == (char *)NULL) {
+    if (errno != ERANGE) {
+      perror("getcwd");
+      return string();
+    }
+    delete[] buffer;
+    bufsize = bufsize * 2;
+    buffer = new char[bufsize];
+    assert(buffer != (char *)NULL);
+  }
+
+  return Filename::from_os_specific(buffer);
+}

+ 36 - 0
ppremake/executionEnvironment.h

@@ -0,0 +1,36 @@
+// Filename: executionEnvironment.h
+// Created by:  drose (15May00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef EXECUTIONENVIRONMENT_H
+#define EXECUTIONENVIRONMENT_H
+
+#include "ppremake.h"
+#include "filename.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : ExecutionEnvironment
+// Description : This class is borrowed from dtool/src/dtoolutil, and
+//               stripped down to just the bare minimum that Filename
+//               needs; and also modified to build outside of Panda.
+////////////////////////////////////////////////////////////////////
+class ExecutionEnvironment {
+public:
+  static Filename get_cwd();
+};
+
+#endif

+ 452 - 0
ppremake/filename.I

@@ -0,0 +1,452 @@
+// Filename: filename.I
+// Created by:  drose (18Jan99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename::
+Filename(const string &filename) {
+  (*this) = filename;
+  _flags = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename::
+Filename(const char *filename) {
+  (*this) = filename;
+  _flags = 0;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename::
+Filename(const Filename &copy)
+  : _filename(copy._filename),
+    _dirname_end(copy._dirname_end),
+    _basename_start(copy._basename_start),
+    _basename_end(copy._basename_end),
+    _extension_start(copy._extension_start),
+    _flags(copy._flags)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::text_filename named constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename Filename::
+text_filename(const string &filename) {
+  Filename result(filename);
+  result.set_text();
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::binary_filename named constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename Filename::
+binary_filename(const string &filename) {
+  Filename result(filename);
+  result.set_binary();
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::dso_filename named constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename Filename::
+dso_filename(const string &filename) {
+  Filename result(filename);
+  result.set_type(T_dso);
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::executable_filename named constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename Filename::
+executable_filename(const string &filename) {
+  Filename result(filename);
+  result.set_type(T_executable);
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename::
+~Filename() {
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Assignment operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename &Filename::
+operator = (const string &filename) {
+  _filename = filename;
+
+  locate_basename();
+  locate_extension();
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Assignment operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename &Filename::
+operator = (const char *filename) {
+  assert(filename != NULL);
+  return (*this) = string(filename);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Copy assignment operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename &Filename::
+operator = (const Filename &copy) {
+  _filename = copy._filename;
+  _dirname_end = copy._dirname_end;
+  _basename_start = copy._basename_start;
+  _basename_end = copy._basename_end;
+  _extension_start = copy._extension_start;
+  _flags = copy._flags;
+  return *this;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::string typecast operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE Filename::
+operator const string & () const {
+  return _filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::c_str
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE const char *Filename::
+c_str() const {
+  return _filename.c_str();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::empty
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool Filename::
+empty() const {
+  return _filename.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::length
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE size_t Filename::
+length() const {
+  return _filename.length();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Indexing operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE char Filename::
+operator [] (int n) const {
+  assert(n >= 0 && n < (int)_filename.length());
+  return _filename[n];
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::get_fullpath
+//       Access: Public
+//  Description: Returns the entire filename: directory, basename,
+//               extension.  This is the same thing returned by the
+//               string typecast operator, so this function is a
+//               little redundant.
+////////////////////////////////////////////////////////////////////
+INLINE string Filename::
+get_fullpath() const {
+  return _filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::get_dirname
+//       Access: Public
+//  Description: Returns the directory part of the filename.  This is
+//               everything in the filename up to, but not including
+//               the rightmost slash.
+////////////////////////////////////////////////////////////////////
+INLINE string Filename::
+get_dirname() const {
+  return _filename.substr(0, _dirname_end);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::get_basename
+//       Access: Public
+//  Description: Returns the basename part of the filename.  This is
+//               everything in the filename after the rightmost slash,
+//               including any extensions.
+////////////////////////////////////////////////////////////////////
+INLINE string Filename::
+get_basename() const {
+  return _filename.substr(_basename_start);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::get_fullpath_wo_extension
+//       Access: Public
+//  Description: Returns the full filename--directory and basename
+//               parts--except for the extension.
+////////////////////////////////////////////////////////////////////
+INLINE string Filename::
+get_fullpath_wo_extension() const {
+  return _filename.substr(0, _basename_end);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::get_basename_wo_extension
+//       Access: Public
+//  Description: Returns the basename part of the filename, without
+//               the file extension.
+////////////////////////////////////////////////////////////////////
+INLINE string Filename::
+get_basename_wo_extension() const {
+  if (_basename_end == string::npos) {
+    return _filename.substr(_basename_start);
+  } else {
+    return _filename.substr(_basename_start, _basename_end - _basename_start);
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::get_extension
+//       Access: Public
+//  Description: Returns the file extension.  This is everything after
+//               the rightmost dot, if there is one, or the empty
+//               string if there is not.
+////////////////////////////////////////////////////////////////////
+INLINE string Filename::
+get_extension() const {
+  if (_extension_start == string::npos) {
+    return string();
+  } else {
+    return _filename.substr(_extension_start);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::set_binary
+//       Access: Public
+//  Description: Indicates that the filename represents a binary file.
+//               This is primarily relevant to the read_file() and
+//               write_file() methods, so they can set the appropriate
+//               flags to the OS.
+////////////////////////////////////////////////////////////////////
+INLINE void Filename::
+set_binary() {
+  _flags = (_flags & ~F_text) | F_binary;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::set_text
+//       Access: Public
+//  Description: Indicates that the filename represents a text file.
+//               This is primarily relevant to the read_file() and
+//               write_file() methods, so they can set the appropriate
+//               flags to the OS.
+////////////////////////////////////////////////////////////////////
+INLINE void Filename::
+set_text() {
+  _flags = (_flags & ~F_binary) | F_text;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::is_binary
+//       Access: Public
+//  Description: Returns true if the Filename has been indicated to
+//               represent a binary file via a previous call to
+//               set_binary().  It is possible that neither
+//               is_binary() nor is_text() will be true, if neither
+//               set_binary() nor set_text() was ever called.
+////////////////////////////////////////////////////////////////////
+INLINE bool Filename::
+is_binary() const {
+  return ((_flags & F_binary) != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::is_text
+//       Access: Public
+//  Description: Returns true if the Filename has been indicated to
+//               represent a text file via a previous call to
+//               set_text().  It is possible that neither is_binary()
+//               nor is_text() will be true, if neither set_binary()
+//               nor set_text() was ever called.
+////////////////////////////////////////////////////////////////////
+INLINE bool Filename::
+is_text() const {
+  return ((_flags & F_text) != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::set_type
+//       Access: Public
+//  Description: Sets the type of the file represented by the
+//               filename.  This is useful for to_os_specific(),
+//               resolve_filename(), test_existence(), and all such
+//               real-world access functions.  It helps the Filename
+//               know how to map the internal filename to the
+//               OS-specific filename (for instance, maybe executables
+//               should have an .exe extension).
+////////////////////////////////////////////////////////////////////
+INLINE void Filename::
+set_type(Filename::Type type) {
+  _flags = (_flags & ~F_type) | type;
+  switch (type) {
+  case T_dso:
+  case T_executable:
+    set_binary();
+
+  case T_general:
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::get_type
+//       Access: Public
+//  Description: Returns the type of the file represented by the
+//               filename, as previously set by set_type().
+////////////////////////////////////////////////////////////////////
+INLINE Filename::Type Filename::
+get_type() const {
+  return (Type)(_flags & (int)F_type);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::is_local
+//       Access: Public
+//  Description: Returns true if the filename is local, e.g. does not
+//               begin with a slash, or false if the filename is fully
+//               specified from the root.
+////////////////////////////////////////////////////////////////////
+INLINE bool Filename::
+is_local() const {
+  return _filename.empty() || _filename[0] != '/';
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::is_fully_qualified
+//       Access: Public
+//  Description: Returns true if the filename is fully qualified,
+//               e.g. begins with a slash.  This is almost, but not
+//               quite, the same thing as !is_local().  It's not
+//               exactly the same because a special case is made for
+//               filenames that begin with a single dot followed by a
+//               slash--these are considered to be fully qualified
+//               (they are explicitly relative to the current
+//               directory, and do not refer to a filename on a search
+//               path somewhere).
+////////////////////////////////////////////////////////////////////
+INLINE bool Filename::
+is_fully_qualified() const {
+  return
+    (_filename.size() > 2 && _filename[0] == '.' && _filename[1] == '/') ||
+    (!_filename.empty() && _filename[0] == '/');
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Equality operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool Filename::
+operator == (const string &other) const {
+  return (*(string *)this) == other;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Inequality operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool Filename::
+operator != (const string &other) const {
+  return (*(string *)this) != other;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Ordering operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool Filename::
+operator < (const string &other) const {
+  return (*(string *)this) < other;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::output
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void Filename::
+output(ostream &out) const {
+  out << _filename;
+}

+ 1490 - 44
ppremake/filename.cxx

@@ -1,78 +1,1524 @@
 // Filename: filename.cxx
 // Filename: filename.cxx
-// Created by:  drose (19Oct00)
-// 
+// Created by:  drose (18Jan99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "filename.h"
 #include "filename.h"
-#include <ctype.h>
+#include "dSearchPath.h"
+#include "executionEnvironment.h"
+
+#include <stdio.h>  // For rename() and tempnam()
+#include <time.h>   // for clock() and time()
+#include <sys/stat.h>
+#include <algorithm>
+
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+
+// We assume we have these too.
+#include <errno.h>
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+// It's true that dtoolbase.h includes this already, but we include
+// this again in case we are building this file within ppremake.
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+
+#if defined(WIN32)
+/* begin Win32-specific code */
+
+#include <direct.h>
+#include <windows.h>
+
+static string
+front_to_back_slash(const string &str) {
+  string result = str;
+  string::iterator si;
+  for (si = result.begin(); si != result.end(); ++si) {
+    if ((*si) == '/') {
+      (*si) = '\\';
+    }
+  }
+
+  return result;
+}
+
+static string
+back_to_front_slash(const string &str) {
+  string result = str;
+  string::iterator si;
+  for (si = result.begin(); si != result.end(); ++si) {
+    if ((*si) == '\\') {
+      (*si) = '/';
+    }
+  }
+
+  return result;
+}
+
+static const string &
+get_panda_root() {
+  static string panda_root;
+  static bool got_panda_root = false;
+
+  if (!got_panda_root) {
+    const char *envvar = getenv("PANDA_ROOT");
+    if (envvar == (const char *)NULL) {
+      envvar = getenv("CYGWIN_ROOT");
+    }
+
+    if (envvar != (const char *)NULL) {
+      panda_root = front_to_back_slash(envvar);
+    }
+
+    if (!panda_root.empty() && panda_root[panda_root.length() - 1] != '\\') {
+      panda_root += '\\';
+    }
+
+    got_panda_root = true;
+  }
+
+  return panda_root;
+}
+
+static string
+convert_pathname(const string &unix_style_pathname) {
+  if (unix_style_pathname.empty()) {
+    return string();
+  }
+
+  // To convert from a Unix-style pathname to a Windows-style
+  // pathname, we need to change all forward slashes to backslashes.
+  // We might need to add a prefix as well, since Windows pathnames
+  // typically begin with a drive letter.
+
+  // By convention, if the top directory name consists of just one
+  // letter, we treat that as a drive letter and map the rest of the
+  // filename accordingly.  On the other hand, if the top directory
+  // name consists of more than one letter, we assume this is a file
+  // within some predefined tree whose root is given by the
+  // environment variable "PANDA_ROOT", or if that is not defined,
+  // "CYGWIN_ROOT" (for backward compatibility).
+  string windows_pathname;
+
+  if (unix_style_pathname[0] != '/') {
+    // 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);
+
+  } else if (unix_style_pathname.length() > 3 &&
+             isalpha(unix_style_pathname[1]) &&
+             unix_style_pathname[2] == '/') {
+    // This pathname begins with a slash and a single letter.  That
+    // must be the drive letter.
+    windows_pathname =
+      string(1, toupper(unix_style_pathname[1])) + ":" +
+      front_to_back_slash(unix_style_pathname.substr(2));
+
+  } else {
+    // It starts with a slash, but the first part is not a single
+    // letter, so prefix $PANDA_ROOT.
+
+    windows_pathname =
+      get_panda_root() + front_to_back_slash(unix_style_pathname.substr(1));
+  }
+
+  return windows_pathname;
+}
+
+string
+convert_dso_pathname(const string &unix_style_pathname) {
+  // 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);
+  }
+  if (unix_style_pathname.substr(dot) != ".so") {
+    // Some other extension.
+    return convert_pathname(unix_style_pathname);
+  }
+
+  string dll_basename = unix_style_pathname.substr(0, dot);
+
+#ifdef _DEBUG
+  // If we're building a debug version, all the dso files we link in
+  // must be named file_d.dll.  This does prohibit us from linking in
+  // external dso files, generated outside of the Panda build system,
+  // that don't follow this _d convention.  Maybe we need a separate
+  // convert_system_dso_pathname() function.
+
+  // We can't simply check to see if the file exists, because this
+  // might not be a full path to the dso filename--it might be
+  // somewhere on the LD_LIBRARY_PATH, or on PATH, or any of a number
+  // of nutty places.
+
+  return convert_pathname(dll_basename + "_d.dll");
+#else
+  return convert_pathname(dll_basename + ".dll");
+#endif
+}
+
+string
+convert_executable_pathname(const string &unix_style_pathname) {
+  // 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");
+  }
+  if (unix_style_pathname.substr(dot) != ".exe") {
+    // Some other extension.
+    return convert_pathname(unix_style_pathname + ".exe");
+  }
+
+  return convert_pathname(unix_style_pathname);
+}
+#endif //WIN32
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::Constructor
+//       Access: Public
+//  Description: This constructor composes the filename out of a
+//               directory part and a basename part.  It will insert
+//               an intervening '/' if necessary.
+////////////////////////////////////////////////////////////////////
+Filename::
+Filename(const Filename &dirname, const Filename &basename) {
+  if (dirname.empty()) {
+    (*this) = basename;
+  } else {
+    string dirpath = dirname.get_fullpath();
+    if (dirpath[dirpath.length() - 1] == '/') {
+      (*this) = dirpath + basename.get_fullpath();
+    } else {
+      (*this) = dirpath + "/" + basename.get_fullpath();
+    }
+  }
+  _flags = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::from_os_specific
+//       Access: Public, Static
+//  Description: This named constructor returns a Panda-style filename
+//               (that is, using forward slashes, and no drive letter)
+//               based on the supplied filename string that describes
+//               a filename in the local system conventions (for
+//               instance, on Windows, it may use backslashes or begin
+//               with a drive letter and a colon).
+//
+//               Use this function to create a Filename from an
+//               externally-given filename string.  Use
+//               to_os_specific() again later to reconvert it back to
+//               the local operating system's conventions.
+//
+//               This function will do the right thing even if the
+//               filename is partially local conventions and partially
+//               Panda conventions; e.g. some backslashes and some
+//               forward slashes.
+////////////////////////////////////////////////////////////////////
+Filename Filename::
+from_os_specific(const string &os_specific, Filename::Type type) {
+#if defined(WIN32)
+  string result = back_to_front_slash(os_specific);
+  const string &panda_root = get_panda_root();
+
+  // If the initial prefix is the same as panda_root, remove it.
+  if (!panda_root.empty() && panda_root.length() < result.length()) {
+    bool matches = true;
+    size_t p;
+    for (p = 0; p < panda_root.length() && matches; p++) {
+      char c = tolower(panda_root[p]);
+      if (c == '\\') {
+        c = '/';
+      }
+      matches = (c == tolower(result[p]));
+    }
+
+    if (matches) {
+      // The initial prefix matches!  Replace the initial bit with a
+      // leading slash.
+      result = result.substr(panda_root.length());
+      assert(!result.empty());
+      if (result[0] != '/') {
+        result = '/' + result;
+      }
+      Filename filename(result);
+      filename.set_type(type);
+      return filename;
+    }
+  }
+
+  // All right, the initial prefix was not under panda_root.  But
+  // maybe it begins with a drive letter.
+  if (result.size() >= 3 && isalpha(result[0]) &&
+      result[1] == ':' && result[2] == '/') {
+    result[1] = tolower(result[0]);
+    result[0] = '/';
+  }
+
+  Filename filename(result);
+  filename.set_type(type);
+  return filename;
+
+#else
+  // Generic Unix-style filenames--no conversion necessary.
+  Filename filename(os_specific);
+  filename.set_type(type);
+  return filename;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::temporary
+//       Access: Public
+//  Description: Generates a temporary filename within the indicated
+//               directory, using the indicated prefix.  If the
+//               directory is empty, a system-defined directory is
+//               chosen instead.
+//
+//               The generated filename did not exist when the
+//               Filename checked, but since it does not specifically
+//               create the file, it is possible that another process
+//               could simultaneously create a file by the same name.
+////////////////////////////////////////////////////////////////////
+Filename Filename::
+temporary(const string &dirname, const string &prefix, Type type) {
+  if (dirname.empty()) {
+    // If we are not given a dirname, use the system tempnam()
+    // function to create a system-defined temporary filename.
+    char *name = tempnam(NULL, prefix.c_str());
+    Filename result(name);
+    free(name);
+    result.set_type(type);
+    return result;
+  }
+
+  // If we *are* given a dirname, then use our own algorithm to make
+  // up a filename within that dirname.  We do that because the system
+  // tempnam() (for instance, under Windows) may ignore the dirname.
+
+  Filename result(dirname, "");
+  result.set_type(type);
+  do {
+    // We take the time of day and multiply it by the process time.
+    // This will give us a very large number, of which we take the
+    // bottom 24 bits and generate a 6-character hex code.
+    int hash = (clock() * time(NULL)) & 0xffffff;
+    char hex_code[10];
+    sprintf(hex_code, "%06x", hash);
+    result.set_basename(prefix + hex_code);
+  } while (result.exists());
+
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::set_fullpath
+//       Access: Public
+//  Description: Replaces the entire filename: directory, basename,
+//               extension.  This can also be achieved with the
+//               assignment operator.
+////////////////////////////////////////////////////////////////////
+void Filename::
+set_fullpath(const string &s) {
+  (*this) = s;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::set_dirname
+//       Access: Public
+//  Description: Replaces the directory part of the filename.  This is
+//               everything in the filename up to, but not including
+//               the rightmost slash.
+////////////////////////////////////////////////////////////////////
+void Filename::
+set_dirname(const string &s) {
+  if (s.empty()) {
+    // Remove the directory prefix altogether.
+    _filename.replace(0, _basename_start, "");
+
+    int length_change = - ((int)_basename_start);
+
+    _dirname_end = 0;
+    _basename_start += length_change;
+    _basename_end += length_change;
+    _extension_start += length_change;
+
+  } else {
+    // Replace the existing directory prefix, or insert a new one.
+
+    // We build the string ss to include the terminal slash.
+    string ss;
+    if (s[s.length()-1] == '/') {
+      ss = s;
+    } else {
+      ss = s+'/';
+    }
+
+    int length_change = ss.length() - _basename_start;
+
+    _filename.replace(0, _basename_start, ss);
+
+    _dirname_end = ss.length() - 1;
+
+    // An exception: if the dirname string was the single slash, the
+    // dirname includes that slash.
+    if (ss.length() == 1) {
+      _dirname_end = 1;
+    }
+
+    _basename_start += length_change;
+
+    if (_basename_end != string::npos) {
+      _basename_end += length_change;
+      _extension_start += length_change;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::set_basename
+//       Access: Public
+//  Description: Replaces the basename part of the filename.  This is
+//               everything in the filename after the rightmost slash,
+//               including any extensions.
+////////////////////////////////////////////////////////////////////
+void Filename::
+set_basename(const string &s) {
+  _filename.replace(_basename_start, string::npos, s);
+  locate_extension();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::set_fullpath_wo_extension
+//       Access: Public
+//  Description: Replaces the full filename--directory and basename
+//               parts--except for the extension.
+////////////////////////////////////////////////////////////////////
+void Filename::
+set_fullpath_wo_extension(const string &s) {
+  int length_change = s.length() - _basename_end;
+
+  _filename.replace(0, _basename_end, s);
+
+  if (_basename_end != string::npos) {
+    _basename_end += length_change;
+    _extension_start += length_change;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::set_basename_wo_extension
+//       Access: Public
+//  Description: Replaces the basename part of the filename, without
+//               the file extension.
+////////////////////////////////////////////////////////////////////
+void Filename::
+set_basename_wo_extension(const string &s) {
+  int length_change = s.length() - (_basename_end - _basename_start);
+
+  if (_basename_end == string::npos) {
+    _filename.replace(_basename_start, string::npos, s);
+
+  } else {
+    _filename.replace(_basename_start, _basename_end - _basename_start, s);
+
+    _basename_end += length_change;
+    _extension_start += length_change;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::set_extension
+//       Access: Public
+//  Description: Replaces the file extension.  This is everything after
+//               the rightmost dot, if there is one, or the empty
+//               string if there is not.
+////////////////////////////////////////////////////////////////////
+void Filename::
+set_extension(const string &s) {
+  if (s.empty()) {
+    // Remove the extension altogether.
+    if (_basename_end != string::npos) {
+      _filename.replace(_basename_end, string::npos, "");
+      _basename_end = string::npos;
+      _extension_start = string::npos;
+    }
+
+  } else if (_basename_end == string::npos) {
+    // Insert an extension where there was none before.
+    _basename_end = _filename.length();
+    _extension_start = _filename.length() + 1;
+    _filename += '.' + s;
+
+  } else {
+    // Replace an existing extension.
+    _filename.replace(_extension_start, string::npos, s);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::standardize
+//       Access: Public
+//  Description: Converts the filename to standard form by replacing
+//               consecutive slashes with a single slash, removing a
+//               trailing slash if present, and backing up over ../
+//               sequences within the filename where possible.
+////////////////////////////////////////////////////////////////////
+void Filename::
+standardize() {
+  assert(!_filename.empty());
+  if (_filename == ".") {
+    // Don't change a single dot; this refers to the current directory.
+    return;
+  }
+
+  vector<string> components;
+
+  // Pull off the components of the filename one at a time.
+  bool global = (_filename[0] == '/');
+
+  size_t p = 0;
+  while (p < _filename.length() && _filename[p] == '/') {
+    p++;
+  }
+  while (p < _filename.length()) {
+    size_t slash = _filename.find('/', p);
+    string component = _filename.substr(p, slash - p);
+    if (component == ".") {
+      // Ignore /./.
+    } else if (component == ".." && !components.empty() &&
+               !(components.back() == "..")) {
+      // Back up.
+      components.pop_back();
+    } else {
+      components.push_back(component);
+    }
+
+    p = slash;
+    while (p < _filename.length() && _filename[p] == '/') {
+      p++;
+    }
+  }
+
+  // Now reassemble the filename.
+  string result;
+  if (global) {
+    result = "/";
+  }
+  if (!components.empty()) {
+    result += components[0];
+    for (int i = 1; i < (int)components.size(); i++) {
+      result += "/" + components[i];
+    }
+  }
+
+  (*this) = result;
+}
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PPScope::is_fullpath
-//  Description: Returns true if the given pathname appears to be a
-//               fully-specified pathname.  This means it begins with
-//               a slash for unix_platform, and it begins with a slash
-//               or backslash, with an optional drive leterr, for
-//               windows_platform.
+//     Function: Filename::make_absolute
+//       Access: Public
+//  Description: Converts the filename to a fully-qualified pathname
+//               from the root (if it is a relative pathname), and
+//               then standardizes it (see standardize()).
+//
+//               This is sometimes a little problematic, since it may
+//               convert the file to its 'true' absolute pathname,
+//               which could be an ugly NFS-named file, irrespective
+//               of symbolic links
+//               (e.g. /.automount/dimbo/root/usr2/fit/people/drose
+//               instead of /fit/people/drose); besides being ugly,
+//               filenames like this may not be consistent across
+//               multiple different platforms.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-bool
-is_fullpath(const string &pathname) {
-  if (pathname.empty()) {
+void Filename::
+make_absolute() {
+  make_absolute(ExecutionEnvironment::get_cwd());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::make_absolute
+//       Access: Public
+//  Description: Converts the filename to a fully-qualified filename
+//               from the root (if it is a relative filename), and
+//               then standardizes it (see standardize()).  This
+//               flavor accepts a specific starting directory that the
+//               filename is known to be relative to.
+////////////////////////////////////////////////////////////////////
+void Filename::
+make_absolute(const Filename &start_directory) {
+  if (is_local()) {
+    (*this) = Filename(start_directory, _filename);
+  }
+
+  standardize();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::make_canonical
+//       Access: Public
+//  Description: Converts this filename to a canonical name by
+//               replacing the directory part with the fully-qualified
+//               directory part.  This is done by changing to that
+//               directory and calling getcwd().
+//
+//               This has the effect of (a) converting relative paths
+//               to absolute paths (but see make_absolute() if this is
+//               the only effect you want), and (b) always resolving a
+//               given directory name to the same string, even if
+//               different symbolic links are traversed, and (c)
+//               changing nice symbolic-link paths like
+//               /fit/people/drose to ugly NFS automounter names like
+//               /hosts/dimbo/usr2/fit/people/drose.  This can be
+//               troubling, but sometimes this is exactly what you
+//               want, particularly if you're about to call
+//               make_relative_to() between two filenames.
+//
+//               The return value is true if successful, or false on
+//               failure (usually because the directory name does not
+//               exist or cannot be chdir'ed into).
+////////////////////////////////////////////////////////////////////
+bool Filename::
+make_canonical() {
+  if (empty()) {
+    // An empty filename is a special case.  This doesn't name
+    // anything.
     return false;
     return false;
   }
   }
 
 
-  if (pathname[0] == '/') {
+  // 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;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::to_os_specific
+//       Access: Public
+//  Description: Converts the filename from our generic Unix-like
+//               convention (forward slashes starting with the root at
+//               '/') to the corresponding filename in the local
+//               operating system (slashes in the appropriate
+//               direction, starting with the root at C:\, for
+//               instance).  Returns the string representing the
+//               converted filename, but does not change the Filename
+//               itself.
+//
+//               See also from_os_specific().
+////////////////////////////////////////////////////////////////////
+string Filename::
+to_os_specific() 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());
+  case T_executable:
+    return convert_executable_pathname(standard.get_fullpath());
+  default:
+    return convert_pathname(standard.get_fullpath());
+  }
+#else // WIN32
+  return standard;
+#endif // WIN32
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::exists
+//       Access: Public
+//  Description: Returns true if the filename exists on the disk,
+//               false otherwise.  If the type is indicated to be
+//               executable, this also tests that the file has execute
+//               permission.
+////////////////////////////////////////////////////////////////////
+bool Filename::
+exists() const {
+  string os_specific = to_os_specific();
+
+#ifdef WIN32_VC
+  bool exists = false;
+
+  DWORD results = GetFileAttributes(os_specific.c_str());
+  if (results != -1) {
+    exists = true;
+  }
+
+#else  // WIN32_VC
+  struct stat this_buf;
+  bool exists = false;
+
+  if (stat(os_specific.c_str(), &this_buf) == 0) {
+    exists = true;
+  }
+#endif
+
+  return exists;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::is_regular_file
+//       Access: Public
+//  Description: Returns true if the filename exists and is the
+//               name of a regular file (i.e. not a directory or
+//               device), false otherwise.
+////////////////////////////////////////////////////////////////////
+bool Filename::
+is_regular_file() const {
+  string os_specific = to_os_specific();
+
+#ifdef WIN32_VC
+  bool isreg = false;
+
+  DWORD results = GetFileAttributes(os_specific.c_str());
+  if (results != -1) {
+    isreg = (results == FILE_ATTRIBUTE_NORMAL);
+  }
+
+#else  // WIN32_VC
+  struct stat this_buf;
+  bool isreg = false;
+
+  if (stat(os_specific.c_str(), &this_buf) == 0) {
+    isreg = S_ISREG(this_buf.st_mode);
+  }
+#endif
+
+  return isreg;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::is_directory
+//       Access: Public
+//  Description: Returns true if the filename exists and is a
+//               directory name, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool Filename::
+is_directory() const {
+  string os_specific = to_os_specific();
+
+#ifdef WIN32_VC
+  bool isdir = false;
+
+  DWORD results = GetFileAttributes(os_specific.c_str());
+  if (results != -1) {
+    isdir = (results & FILE_ATTRIBUTE_DIRECTORY) != 0;
+  }
+#else  // WIN32_VC
+  struct stat this_buf;
+  bool isdir = false;
+
+  if (stat(os_specific.c_str(), &this_buf) == 0) {
+    isdir = S_ISDIR(this_buf.st_mode);
+  }
+#endif
+
+  return isdir;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::is_executable
+//       Access: Public
+//  Description: Returns true if the filename exists and is
+//               executable
+////////////////////////////////////////////////////////////////////
+bool Filename::
+is_executable() const {
+#ifdef WIN32_VC
+  // no access() in windows, but to our advantage executables can only
+  // end in .exe or .com
+  string extension = get_extension();
+  if (extension == "exe" || extension == "com") {
+    return exists();
+  }
+
+#else /* WIN32_VC */
+  string os_specific = to_os_specific();
+  if (access(os_specific.c_str(), X_OK) == 0) {
     return true;
     return true;
   }
   }
+#endif /* WIN32_VC */
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::compare_timestamps
+//       Access: Public
+//  Description: Returns a number less than zero if the file named by
+//               this object is older than the given file, zero if
+//               they have the same timestamp, or greater than zero if
+//               this one is newer.
+//
+//               If this_missing_is_old is true, it indicates that a
+//               missing file will be treated as if it were older than
+//               any other file; otherwise, a missing file will be
+//               treated as if it were newer than any other file.
+//               Similarly for other_missing_is_old.
+////////////////////////////////////////////////////////////////////
+int Filename::
+compare_timestamps(const Filename &other,
+                   bool this_missing_is_old,
+                   bool other_missing_is_old) const {
+  string os_specific = to_os_specific();
+  string other_os_specific = other.to_os_specific();
+
+#ifdef WIN32_VC
+  struct _stat this_buf;
+  bool this_exists = false;
+
+  if (_stat(os_specific.c_str(), &this_buf) == 0) {
+    this_exists = true;
+  }
+
+  struct _stat other_buf;
+  bool other_exists = false;
+
+  if (_stat(other_os_specific.c_str(), &other_buf) == 0) {
+    other_exists = true;
+  }
+#else  // WIN32_VC
+  struct stat this_buf;
+  bool this_exists = false;
+
+  if (stat(os_specific.c_str(), &this_buf) == 0) {
+    this_exists = true;
+  }
+
+  struct stat other_buf;
+  bool other_exists = false;
+
+  if (stat(other_os_specific.c_str(), &other_buf) == 0) {
+    other_exists = true;
+  }
+#endif
+
+  if (this_exists && other_exists) {
+    // Both files exist, return the honest time comparison.
+    return (int)this_buf.st_mtime - (int)other_buf.st_mtime;
+
+  } else if (!this_exists && !other_exists) {
+    // Neither file exists.
+    if (this_missing_is_old == other_missing_is_old) {
+      // Both files are either "very old" or "very new".
+      return 0;
+    }
+    if (this_missing_is_old) {
+      // This file is "very old", the other is "very new".
+      return -1;
+    } else {
+      // This file is "very new", the other is "very old".
+      return 1;
+    }
+
+  } else if (!this_exists) {
+    // This file doesn't, the other one does.
+    return this_missing_is_old ? -1 : 1;
+
+  } else { // !other_exists
+    assert(!other_exists);
+
+    // This file exists, the other one doesn't.
+    return other_missing_is_old ? 1 : -1;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::resolve_filename
+//       Access: Public
+//  Description: Searches the given search path for the filename.  If
+//               it is found, updates the filename to the full
+//               pathname found and returns true; otherwise, returns
+//               false.
+////////////////////////////////////////////////////////////////////
+bool Filename::
+resolve_filename(const DSearchPath &searchpath,
+                 const string &default_extension) {
+  string found;
+
+  if (is_local()) {
+    found = searchpath.find_file(get_fullpath());
 
 
-  if (windows_platform) {
-    if (pathname.length() > 2 && 
-        isalpha(pathname[0]) && pathname[1] == ':') {
-      // A drive-letter prefix.
-      return (pathname[2] == '/' || pathname[2] == '\\');
+    if (found.empty()) {
+      // We didn't find it with the given extension; can we try the
+      // default extension?
+      if (get_extension().empty() && !default_extension.empty()) {
+        Filename try_ext = *this;
+        try_ext.set_extension(default_extension);
+        found = searchpath.find_file(try_ext.get_fullpath());
+      }
+    }
+
+  } else {
+    if (exists()) {
+      // The full pathname exists.  Return true.
+      return true;
+
+    } else {
+      // The full pathname doesn't exist with the given extension;
+      // does it exist with the default extension?
+      if (get_extension().empty() && !default_extension.empty()) {
+        Filename try_ext = *this;
+        try_ext.set_extension(default_extension);
+        if (try_ext.exists()) {
+          found = try_ext;
+        }
+      }
     }
     }
-    // No drive-letter prefix.
-    return (pathname[0] == '\\');
+  }
+
+  if (!found.empty()) {
+    (*this) = found;
+    return true;
   }
   }
 
 
   return false;
   return false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PPScope::to_os_filename
-//  Description: Changes forward slashes to backslashes, but only if
-//               windows_platform is set.  Otherwise returns the
-//               string unchanged.
+//     Function: Filename::make_relative_to
+//       Access: Public
+//  Description: Adjusts this filename, which must be a
+//               fully-specified pathname beginning with a slash, to
+//               make it a relative filename, relative to the
+//               fully-specified directory indicated (which must also
+//               begin with, and may or may not end with, a slash--a
+//               terminating slash is ignored).
+//
+//               This only performs a string comparsion, so it may be
+//               wise to call make_canonical() on both filenames
+//               before calling make_relative_to().
+//
+//               If allow_backups is false, the filename will only be
+//               adjusted to be made relative if it is already
+//               somewhere within or below the indicated directory.
+//               If allow_backups is true, it will be adjusted in all
+//               cases, even if this requires putting a series of ../
+//               characters before the filename--unless it would have
+//               to back all the way up to the root.
+//
+//               Returns true if the file was adjusted, false if it
+//               was not.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-string
-to_os_filename(string pathname) {
-  if (windows_platform) {
-    string::iterator si;
-    for (si = pathname.begin(); si != pathname.end(); ++si) {
-      if ((*si) == '/') {
-        (*si) = '\\';
+bool Filename::
+make_relative_to(Filename directory, bool allow_backups) {
+  if (_filename.empty() || directory.empty() ||
+      _filename[0] != '/' || directory[0] != '/') {
+    return false;
+  }
+
+  standardize();
+  directory.standardize();
+
+  if (directory == "/") {
+    // Don't be silly.
+    return false;
+  }
+
+  string rel_to_file = directory.get_fullpath() + "/.";
+
+  size_t common = get_common_prefix(rel_to_file);
+  if (common < 2) {
+    // Oh, never mind.
+    return false;
+  }
+
+  string result;
+  int slashes = count_slashes(rel_to_file.substr(common));
+  if (slashes > 0 && !allow_backups) {
+    // Too bad; the file's not under the indicated directory.
+    return false;
+  }
+
+  for (int i = 0; i < slashes; i++) {
+    result += "../";
+  }
+  result += _filename.substr(common);
+  (*this) = result;
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::find_on_searchpath
+//       Access: Public
+//  Description: Performs the reverse of the resolve_filename()
+//               operation: assuming that the current filename is
+//               fully-specified pathname (i.e. beginning with '/'),
+//               look on the indicated search path for a directory
+//               under which the file can be found.  When found,
+//               adjust the Filename to be relative to the indicated
+//               directory name.
+//
+//               Returns the index of the directory on the searchpath
+//               at which the file was found, or -1 if it was not
+//               found.
+////////////////////////////////////////////////////////////////////
+int Filename::
+find_on_searchpath(const DSearchPath &searchpath) {
+  if (_filename.empty() || _filename[0] != '/') {
+    return -1;
+  }
+
+  int num_directories = searchpath.get_num_directories();
+  for (int i = 0; i < num_directories; i++) {
+    if (make_relative_to(searchpath.get_directory(i), false)) {
+      return i;
+    }
+  }
+
+  return -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::scan_directory
+//       Access: Public
+//  Description: Attempts to open the named filename as if it were a
+//               directory and looks for the non-hidden files within
+//               the directory.  Fills the given vector up with the
+//               sorted list of filenames that are local to this
+//               directory.
+//
+//               It is the user's responsibility to ensure that the
+//               contents vector is empty before making this call;
+//               otherwise, the new files will be appended to it.
+//
+//               Returns true on success, false if the directory could
+//               not be read for some reason.
+////////////////////////////////////////////////////////////////////
+bool Filename::
+scan_directory(vector_string &contents) const {
+#if defined(HAVE_DIRENT_H)
+  size_t orig_size = contents.size();
+
+  DIR *root = opendir(_filename.c_str());
+  if (root == (DIR *)NULL) {
+    return false;
+  }
+
+  struct dirent *d;
+  d = readdir(root);
+  while (d != (struct dirent *)NULL) {
+    if (d->d_name[0] != '.') {
+      contents.push_back(d->d_name);
+    }
+    d = readdir(root);
+  }
+  closedir(root);
+
+  sort(contents.begin() + orig_size, contents.end());
+  return true;
+
+#elif defined(WIN32_VC)
+  // Use FindFirstFile()/FindNextFile() to walk through the list of
+  // files in a directory.
+  size_t orig_size = contents.size();
+
+  string match;
+  if (empty()) {
+    match = "*.*";
+  } else {
+    match = to_os_specific() + "\\*.*";
+  }
+  WIN32_FIND_DATA find_data;
+
+  HANDLE handle = FindFirstFile(match.c_str(), &find_data);
+  if (handle == INVALID_HANDLE_VALUE) {
+    if (GetLastError() == ERROR_NO_MORE_FILES) {
+      // No matching files is not an error.
+      return true;
+    }
+    return false;
+  }
+
+  do {
+    string filename = find_data.cFileName;
+    if (filename != "." && filename != "..") {
+      contents.push_back(filename);
+    }
+  } while (FindNextFile(handle, &find_data));
+
+  bool scan_ok = (GetLastError() == ERROR_NO_MORE_FILES);
+  FindClose(handle);
+
+  sort(contents.begin() + orig_size, contents.end());
+  return scan_ok;
+  
+#else
+  // Don't know how to scan directories!
+  return false;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::open_read
+//       Access: Public
+//  Description: Opens the indicated ifstream for reading the file, if
+//               possible.  Returns true if successful, 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().
+////////////////////////////////////////////////////////////////////
+bool Filename::
+open_read(ifstream &stream) const {
+  assert(is_text() || is_binary());
+
+  int open_mode = ios::in;
+
+#ifdef HAVE_IOS_BINARY
+  // For some reason, some systems (like Irix) don't define
+  // ios::binary.
+  if (!is_text()) {
+    open_mode |= ios::binary;
+  }
+#endif
+
+  string os_specific = to_os_specific();
+  stream.clear();
+  stream.open(os_specific.c_str(), open_mode);
+  return (!stream.fail());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::open_write
+//       Access: Public
+//  Description: Opens the indicated ifstream for writing the file, if
+//               possible.  Returns true if successful, 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().
+////////////////////////////////////////////////////////////////////
+bool Filename::
+open_write(ofstream &stream) const {
+  assert(is_text() || is_binary());
+
+  int open_mode = ios::out;
+
+#ifdef HAVE_IOS_BINARY
+  // For some reason, some systems (like Irix) don't define
+  // ios::binary.
+  if (!is_text()) {
+    open_mode |= ios::binary;
+  }
+#endif
+
+  stream.clear();
+  string os_specific = to_os_specific();
+#ifdef WIN32_VC
+  stream.open(os_specific.c_str(), open_mode);
+#else
+  stream.open(os_specific.c_str(), open_mode, 0666);
+#endif
+
+  return (!stream.fail());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::open_append
+//       Access: Public
+//  Description: Opens the indicated ifstream for writing the file, if
+//               possible.  Returns true if successful, 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().
+////////////////////////////////////////////////////////////////////
+bool Filename::
+open_append(ofstream &stream) const {
+  assert(is_text() || is_binary());
+
+  int open_mode = ios::app;
+
+#ifdef HAVE_IOS_BINARY
+  // For some reason, some systems (like Irix) don't define
+  // ios::binary.
+  if (!is_text()) {
+    open_mode |= ios::binary;
+  }
+#endif
+
+  stream.clear();
+  string os_specific = to_os_specific();
+#ifdef WIN32_VC
+  stream.open(os_specific.c_str(), open_mode);
+#else
+  stream.open(os_specific.c_str(), open_mode, 0666);
+#endif
+
+  return (!stream.fail());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::open_read_write
+//       Access: Public
+//  Description: Opens the indicated fstream for read/write access to
+//               the file, if possible.  Returns true if successful,
+//               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().
+////////////////////////////////////////////////////////////////////
+bool Filename::
+open_read_write(fstream &stream) const {
+  assert(is_text() || is_binary());
+
+  int open_mode = ios::in | ios::out;
+
+#ifdef HAVE_IOS_BINARY
+  // For some reason, some systems (like Irix) don't define
+  // ios::binary.
+  if (!is_text()) {
+    open_mode |= ios::binary;
+  }
+#endif
+
+  stream.clear();
+  string os_specific = to_os_specific();
+#ifdef WIN32_VC
+  stream.open(os_specific.c_str(), open_mode);
+#else
+  stream.open(os_specific.c_str(), open_mode, 0666);
+#endif
+
+  return (!stream.fail());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::touch
+//       Access: Public
+//  Description: Updates the modification time of the file to the
+//               current time.  If the file does not already exist, it
+//               will be created.  Returns true if successful, false
+//               if there is an error.
+////////////////////////////////////////////////////////////////////
+bool Filename::
+touch() const {
+#ifdef HAVE_UTIME_H
+  // Most Unix systems can do this explicitly.
+  string os_specific = to_os_specific();
+  int result = utime(os_specific.c_str(), NULL);
+  if (result < 0) {
+    if (errno == ENOENT) {
+      // So the file doesn't already exist; create it.
+      int fd = creat(os_specific.c_str(), 0666);
+      if (fd < 0) {
+        perror(os_specific.c_str());
+        return false;
       }
       }
+      close(fd);
+      return true;
     }
     }
+    perror(os_specific.c_str());
+    return false;
   }
   }
+  return true;
+#else
+  // 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
+}
 
 
-  return pathname;
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::unlink
+//       Access: Public
+//  Description: Permanently deletes the file associated with the
+//               filename, if possible.  Returns true if successful,
+//               false if failure (for instance, because the file did
+//               not exist, or because permissions were inadequate).
+////////////////////////////////////////////////////////////////////
+bool Filename::
+unlink() const {
+  string os_specific = to_os_specific();
+  return (::unlink(os_specific.c_str()) == 0);
 }
 }
 
 
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PPScope::to_unix_filename
-//  Description: Changes backslashes to forward slashes, but only if
-//               windows_platform is set.  Otherwise returns the
-//               string unchanged.
+//     Function: Filename::rename_to
+//       Access: Public
+//  Description: Renames the file to the indicated new filename.  If
+//               the new filename is in a different directory, this
+//               will perform a move.  Returns true if successful,
+//               false if failure.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-string
-to_unix_filename(string pathname) {
-  if (windows_platform) {
-    string::iterator si;
-    for (si = pathname.begin(); si != pathname.end(); ++si) {
-      if ((*si) == '\\') {
-        (*si) = '/';
+bool Filename::
+rename_to(const Filename &other) const {
+  string os_specific = to_os_specific();
+  string other_os_specific = other.to_os_specific();
+  return (rename(os_specific.c_str(),
+                 other_os_specific.c_str()) == 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::make_dir
+//       Access: Public
+//  Description: Creates all the directories in the path to the file
+//               specified in the filename, except for the basename
+//               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.
+////////////////////////////////////////////////////////////////////
+bool Filename::
+make_dir() const {
+  Filename path = *this;
+  path.standardize();
+  string 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
+  // because the directory was already there.
+  size_t slash = dirname.find('/');
+  while (slash != string::npos) {
+    Filename component(dirname.substr(0, slash));
+    string os_specific = component.to_os_specific();
+#ifndef WIN32_VC
+    mkdir(os_specific.c_str(), 0777);
+#else
+    mkdir(os_specific.c_str());
+#endif
+    slash = dirname.find('/', slash + 1);
+  }
+
+  // Now make the last one, and check the return value.
+  Filename component(dirname);
+  string os_specific = component.to_os_specific();
+#ifndef WIN32_VC
+  int result = mkdir(os_specific.c_str(), 0777);
+#else
+  int result = mkdir(os_specific.c_str());
+#endif
+
+  return (result == 0);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::locate_basename
+//       Access: Private
+//  Description: After the string has been reassigned, search for the
+//               slash marking the beginning of the basename, and set
+//               _dirname_end and _basename_start correctly.
+////////////////////////////////////////////////////////////////////
+void Filename::
+locate_basename() {
+  // Scan for the last slash, which marks the end of the directory
+  // part.
+  if (_filename.empty()) {
+    _dirname_end = 0;
+    _basename_start = 0;
+
+  } else {
+
+    string::size_type slash = _filename.rfind('/');
+    if (slash != string::npos) {
+      _basename_start = slash + 1;
+      _dirname_end = _basename_start;
+
+      // One exception: in case there are multiple slashes in a row,
+      // we want to treat them as a single slash.  The directory
+      // therefore actually ends at the first of these; back up a bit.
+      while (_dirname_end > 0 && _filename[_dirname_end-1] == '/') {
+        _dirname_end--;
+      }
+
+      // Another exception: if the dirname was nothing but slashes, it
+      // was the root directory, or / itself.  In this case the dirname
+      // does include the terminal slash (of course).
+      if (_dirname_end == 0) {
+        _dirname_end = 1;
       }
       }
+
+    } else {
+      _dirname_end = 0;
+      _basename_start = 0;
     }
     }
   }
   }
 
 
-  return pathname;
+  // Now:
+
+  // _dirname_end is the last slash character, or 0 if there are no
+  // slash characters.
+
+  // _basename_start is the character after the last slash character,
+  // or 0 if there are no slash characters.
 }
 }
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::locate_extension
+//       Access: Private
+//  Description: Once the end of the directory prefix has been found,
+//               and _dirname_end and _basename_start are set
+//               correctly, search for the dot marking the beginning
+//               of the extension, and set _basename_end and
+//               _extension_start correctly.
+////////////////////////////////////////////////////////////////////
+void Filename::
+locate_extension() {
+  // Now scan for the last dot after that slash.
+  if (_filename.empty()) {
+    _basename_end = string::npos;
+    _extension_start = string::npos;
+
+  } else {
+    string::size_type dot = _filename.length() - 1;
+
+    while (dot+1 > _basename_start && _filename[dot] != '.') {
+      --dot;
+    }
+
+    if (dot+1 > _basename_start) {
+      _basename_end = dot;
+      _extension_start = dot + 1;
+    } else {
+      _basename_end = string::npos;
+      _extension_start = string::npos;
+    }
+  }
+
+  // Now:
+
+  // _basename_end is the last dot, or npos if there is no dot.
+
+  // _extension_start is the character after the last dot, or npos if
+  // there is no dot.
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::get_common_prefix
+//       Access: Private
+//  Description: Returns the length of the longest common initial
+//               substring of this string and the other one that ends
+//               in a slash.  This is the lowest directory common to
+//               both filenames.
+////////////////////////////////////////////////////////////////////
+size_t Filename::
+get_common_prefix(const string &other) const {
+  size_t len = 0;
+
+  // First, get the length of the common initial substring.
+  while (len < length() && len < other.length() &&
+         _filename[len] == other[len]) {
+    len++;
+  }
+
+  // Now insist that it ends in a slash.
+  while (len > 0 && _filename[len-1] != '/') {
+    len--;
+  }
+
+  return len;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::count_slashes
+//       Access: Private, Static
+//  Description: Returns the number of non-consecutive slashes in the
+//               indicated string, not counting a terminal slash.
+////////////////////////////////////////////////////////////////////
+int Filename::
+count_slashes(const string &str) {
+  int count = 0;
+  string::const_iterator si;
+  si = str.begin();
+
+  while (si != str.end()) {
+    if (*si == '/') {
+      count++;
+
+      // Skip consecutive slashes.
+      ++si;
+      while (*si == '/') {
+        ++si;
+      }
+      if (si == str.end()) {
+        // Oops, that was a terminal slash.  Don't count it.
+        count--;
+      }
+
+    } else {
+      ++si;
+    }
+  }
+
+  return count;
+}
+

+ 184 - 7
ppremake/filename.h

@@ -1,6 +1,19 @@
 // Filename: filename.h
 // Filename: filename.h
-// Created by:  drose (19Oct00)
-// 
+// Created by:  drose (18Jan99)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #ifndef FILENAME_H
 #ifndef FILENAME_H
@@ -8,12 +21,176 @@
 
 
 #include "ppremake.h"
 #include "ppremake.h"
 
 
-// This header file defines a few functions handy for dealing with
-// filenames in a cross-platform world.
+#include "vector_string.h"
+
+#include <assert.h>
+
+class DSearchPath;
+
+////////////////////////////////////////////////////////////////////
+//       Class : Filename
+// Description : The name of a file, such as a texture file or an Egg
+//               file.  Stores the full pathname, and includes
+//               functions for extracting out the directory prefix
+//               part and the file extension and stuff.
+//
+//               A Filename is also aware of the mapping between the
+//               Unix-like filename convention we use internally, and
+//               the local OS's specific filename convention, and it
+//               knows how to perform basic OS-specific I/O, like
+//               testing for file existence and searching a
+//               searchpath, as well as the best way to open an
+//               fstream for reading or writing.
+////////////////////////////////////////////////////////////////////
+class EXPCL_DTOOL Filename {
+PUBLISHED:
+  enum Type {
+    // These type values must fit within the bits allocated for
+    // F_type, below.
+    T_general    = 0x00,
+    T_dso        = 0x01,
+    T_executable = 0x02,
+    // Perhaps other types will be added later.
+  };
+
+public:
+  enum Flags {
+    F_type            = 0x0f,
+    F_binary          = 0x10,
+    F_text            = 0x20,
+  };
+
+PUBLISHED:
+  INLINE Filename(const string &filename = "");
+  INLINE Filename(const char *filename);
+  INLINE Filename(const Filename &copy);
+  Filename(const Filename &dirname, const Filename &basename);
+  INLINE ~Filename();
+
+  // Static constructors to explicitly create a filename that refers
+  // to a text or binary file.  This is in lieu of calling set_text()
+  // or set_binary() or set_type().
+  INLINE static Filename text_filename(const string &filename);
+  INLINE static Filename binary_filename(const string &filename);
+  INLINE static Filename dso_filename(const string &filename);
+  INLINE static Filename executable_filename(const string &filename);
+
+  static Filename from_os_specific(const string &os_specific,
+                                   Type type = T_general);
+  static Filename temporary(const string &dirname, const string &prefix,
+                            Type type = T_general);
+
+  // Assignment is via the = operator.
+  INLINE Filename &operator = (const string &filename);
+  INLINE Filename &operator = (const char *filename);
+  INLINE Filename &operator = (const Filename &copy);
+
+  // And retrieval is by any of the classic string operations.
+  INLINE operator const string & () const;
+  INLINE const char *c_str() const;
+  INLINE bool empty() const;
+  INLINE size_t length() const;
+  INLINE char operator [] (int n) const;
+
+  // Or, you can use any of these.
+  INLINE string get_fullpath() const;
+  INLINE string get_dirname() const;
+  INLINE string get_basename() const;
+  INLINE string get_fullpath_wo_extension() const;
+  INLINE string get_basename_wo_extension() const;
+  INLINE string get_extension() const;
+
+  // You can also use any of these to reassign pieces of the filename.
+  void set_fullpath(const string &s);
+  void set_dirname(const string &s);
+  void set_basename(const string &s);
+  void set_fullpath_wo_extension(const string &s);
+  void set_basename_wo_extension(const string &s);
+  void set_extension(const string &s);
+
+  // Setting these flags appropriately is helpful when opening or
+  // searching for a file; it helps the Filename resolve OS-specific
+  // conventions (for instance, that dynamic library names should
+  // perhaps be changed from .so to .dll).
+  INLINE void set_binary();
+  INLINE void set_text();
+  INLINE bool is_binary() const;
+  INLINE bool is_text() const;
+
+  INLINE void set_type(Type type);
+  INLINE Type get_type() const;
+
+  void standardize();
+
+  // The following functions deal with the outside world.
 
 
-bool is_fullpath(const string &pathname);
-string to_os_filename(string pathname);
-string to_unix_filename(string pathname);
+  INLINE bool is_local() const;
+  INLINE bool is_fully_qualified() const;
+  void make_absolute();
+  void make_absolute(const Filename &start_directory);
+
+  bool make_canonical();
+
+  string to_os_specific() const;
+
+  bool exists() const;
+  bool is_regular_file() const;
+  bool is_directory() const;
+  bool is_executable() const;
+  int compare_timestamps(const Filename &other,
+                         bool this_missing_is_old = true,
+                         bool other_missing_is_old = true) const;
+  bool resolve_filename(const DSearchPath &searchpath,
+                        const string &default_extension = string());
+  bool make_relative_to(Filename directory, bool allow_backups = true);
+  int find_on_searchpath(const DSearchPath &searchpath);
+
+  bool scan_directory(vector_string &contents) const;
+
+  bool open_read(ifstream &stream) const;
+  bool open_write(ofstream &stream) const;
+  bool open_append(ofstream &stream) const;
+  bool open_read_write(fstream &stream) const;
+
+  bool touch() const;
+
+  bool unlink() const;
+  bool rename_to(const Filename &other) const;
+
+  bool make_dir() const;
+
+  // Comparison operators are handy.
+  INLINE bool operator == (const string &other) const;
+  INLINE bool operator != (const string &other) const;
+  INLINE bool operator < (const string &other) const;
+
+  INLINE void output(ostream &out) const;
+
+private:
+  void locate_basename();
+  void locate_extension();
+  size_t get_common_prefix(const string &other) const;
+  static int count_slashes(const string &str);
+
+  string _filename;
+  // We'll make these size_t instead of string::size_type to help out
+  // cppParser.
+  size_t _dirname_end;
+  size_t _basename_start;
+  size_t _basename_end;
+  size_t _extension_start;
+
+  int _flags;
+};
+
+INLINE ostream &operator << (ostream &out, const Filename &n) {
+  n.output(out);
+  return out;
+}
+
+#include "filename.I"
 
 
 #endif
 #endif
 
 
+
+

+ 41 - 33
ppremake/ppScope.cxx

@@ -1145,10 +1145,10 @@ expand_variable_nested(const string &varname,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 string PPScope::
 string PPScope::
 expand_isfullpath(const string &params) {
 expand_isfullpath(const string &params) {
-  string filename = trim_blanks(expand_string(params));
+  Filename filename = trim_blanks(expand_string(params));
 
 
   string result;
   string result;
-  if (is_fullpath(filename)) {
+  if (filename.is_fully_qualified()) {
     result = filename;
     result = filename;
   }
   }
   return result;
   return result;
@@ -1160,15 +1160,12 @@ expand_isfullpath(const string &params) {
 //  Description: Expands the "osfilename" function variable.  This
 //  Description: Expands the "osfilename" function variable.  This
 //               converts the filename from a Unix-style filename
 //               converts the filename from a Unix-style filename
 //               (e.g. with slash separators) to a platform-specific
 //               (e.g. with slash separators) to a platform-specific
-//               filename.  Currently, this only has an effect when
-//               generating code for a Windows platform: it simply
-//               converts forward slashes to backslashes.  On other
-//               platforms it has no effect.
+//               filename.
 //
 //
-//               This is different from cygpath_w in that (a) it works
-//               regardless of whether we are actually running under
-//               Cygwin, and (b) it does nothing more intelligent than
-//               reverse slashes.
+//               This follows the same rules of Panda filename
+//               conversion; i.e. forward slashes become backslashes,
+//               and $PANDA_ROOT prefixes full pathnames, unless the
+//               topmost directory name is a single letter.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 string PPScope::
 string PPScope::
 expand_osfilename(const string &params) {
 expand_osfilename(const string &params) {
@@ -1178,7 +1175,8 @@ expand_osfilename(const string &params) {
 
 
   vector<string>::iterator wi;
   vector<string>::iterator wi;
   for (wi = words.begin(); wi != words.end(); ++wi) {
   for (wi = words.begin(); wi != words.end(); ++wi) {
-    (*wi) = to_os_filename(*wi);
+    Filename filename = (*wi);
+    (*wi) = filename.to_os_specific();
   }
   }
 
 
   string result = repaste(words, " ");
   string result = repaste(words, " ");
@@ -1191,15 +1189,9 @@ expand_osfilename(const string &params) {
 //  Description: Expands the "unixfilename" function variable.  This
 //  Description: Expands the "unixfilename" function variable.  This
 //               converts the filename from a platform-specific
 //               converts the filename from a platform-specific
 //               filename to a Unix-style filename (e.g. with slash
 //               filename to a Unix-style filename (e.g. with slash
-//               separators).  Currently, this only has an effect when
-//               generating code for a Windows platform: it simply
-//               converts backslashes to forward slashes.  On other
-//               platforms it has no effect.
+//               separators).
 //
 //
-//               This is different from cygpath_p in that (a) it works
-//               regardless of whether we are actually running under
-//               Cygwin, and (b) it does nothing more intelligent than
-//               reverse slashes.
+//               This follows the rules of Panda filename conversion.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 string PPScope::
 string PPScope::
 expand_unixfilename(const string &params) {
 expand_unixfilename(const string &params) {
@@ -1209,7 +1201,8 @@ expand_unixfilename(const string &params) {
 
 
   vector<string>::iterator wi;
   vector<string>::iterator wi;
   for (wi = words.begin(); wi != words.end(); ++wi) {
   for (wi = words.begin(); wi != words.end(); ++wi) {
-    (*wi) = to_unix_filename(*wi);
+    Filename filename = Filename::from_os_specific(*wi);
+    (*wi) = filename;
   }
   }
 
 
   string result = repaste(words, " ");
   string result = repaste(words, " ");
@@ -1219,10 +1212,15 @@ expand_unixfilename(const string &params) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PPScope::expand_cygpath_w
 //     Function: PPScope::expand_cygpath_w
 //       Access: Private
 //       Access: Private
-//  Description: Expands the "cygpath_w" function variable.  This is
-//               equivalent to $[shell cygpath -w ...] when running
-//               under Cygwin, and returns the parameter itself when
-//               not running under Cygwin.
+//  Description: Expands the "cygpath_w" function variable.  
+//
+//               This converts the Unix-style filename to a Windows
+//               filename using the Cygwin rules when ppremake has
+//               been compiled with Cygwin; it is thus equivalent to
+//               the result of the cygpath -w command.
+//
+//               When ppremake has not been compiled with Cygwin, this
+//               returns the same as the "osfilename" variable.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 string PPScope::
 string PPScope::
 expand_cygpath_w(const string &params) {
 expand_cygpath_w(const string &params) {
@@ -1233,6 +1231,9 @@ expand_cygpath_w(const string &params) {
 
 
   cygwin_conv_to_win32_path(filename.c_str(), result);
   cygwin_conv_to_win32_path(filename.c_str(), result);
   filename = result;
   filename = result;
+#else
+  Filename fn(filename);
+  filename = fn.to_os_specific();
 #endif
 #endif
 
 
   return filename;
   return filename;
@@ -1241,10 +1242,15 @@ expand_cygpath_w(const string &params) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PPScope::expand_cygpath_p
 //     Function: PPScope::expand_cygpath_p
 //       Access: Private
 //       Access: Private
-//  Description: Expands the "cygpath_p" function variable.  This is
-//               equivalent to $[shell cygpath -p ...] when running
-//               under Cygwin, and returns the parameter itself when
-//               not running under Cygwin.
+//  Description: Expands the "cygpath_p" function variable.
+//
+//               This converts the Windows filename to a Unix-style
+//               filename using the Cygwin rules when ppremake has
+//               been compiled with Cygwin; it is thus equivalent to
+//               the result of the cygpath -p command.
+//
+//               When ppremake has not been compiled with Cygwin, this
+//               returns the same as the "unixfilename" variable.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 string PPScope::
 string PPScope::
 expand_cygpath_p(const string &params) {
 expand_cygpath_p(const string &params) {
@@ -1255,6 +1261,8 @@ expand_cygpath_p(const string &params) {
 
 
   cygwin_conv_to_posix_path(filename.c_str(), result);
   cygwin_conv_to_posix_path(filename.c_str(), result);
   filename = result;
   filename = result;
+#else
+  filename = Filename::from_os_specific(filename);
 #endif
 #endif
 
 
   return filename;
   return filename;
@@ -1389,7 +1397,7 @@ expand_libtest(const string &params) {
 
 
   // Also add the system directories to the list, whatever we think
   // Also add the system directories to the list, whatever we think
   // those should be.  Here we have to make a few assumptions.
   // those should be.  Here we have to make a few assumptions.
-#ifdef PLATFORM_WIN32
+#ifdef WIN32
   const char *windir = getenv("WINDIR");
   const char *windir = getenv("WINDIR");
   if (windir != (const char *)NULL) {
   if (windir != (const char *)NULL) {
     directories.push_back(string(windir) + "\\System");
     directories.push_back(string(windir) + "\\System");
@@ -1427,7 +1435,7 @@ expand_libtest(const string &params) {
 
 
   string found;
   string found;
 
 
-#ifdef PLATFORM_WIN32
+#ifdef WIN32
   if (libname.length() > 4 && libname.substr(libname.length() - 4) == ".lib") {
   if (libname.length() > 4 && libname.substr(libname.length() - 4) == ".lib") {
     found = find_searchpath(directories, libname);    
     found = find_searchpath(directories, libname);    
     if (found.empty()) {
     if (found.empty()) {
@@ -1472,7 +1480,7 @@ expand_bintest(const string &params) {
   }
   }
 
 
   // An explicit path from the root does not require a search.
   // An explicit path from the root does not require a search.
-#ifdef PLATFORM_WIN32
+#ifdef WIN32
   if ((binname.length() > 2 && binname[1] == ':') || binname[0] == '/')
   if ((binname.length() > 2 && binname[1] == ':') || binname[0] == '/')
 #else
 #else
     if (binname[0] == '/')
     if (binname[0] == '/')
@@ -1494,7 +1502,7 @@ expand_bintest(const string &params) {
 
 
   vector<string> directories;
   vector<string> directories;
 
 
-#ifdef PLATFORM_WIN32
+#ifdef WIN32
   if (pathvar.find(';') != string::npos) {
   if (pathvar.find(';') != string::npos) {
     // If the path contains semicolons, it's a native Windows-style
     // If the path contains semicolons, it's a native Windows-style
     // path: split it up based on semicolons.
     // path: split it up based on semicolons.
@@ -1511,7 +1519,7 @@ expand_bintest(const string &params) {
 
 
   string found;
   string found;
 
 
-#ifdef PLATFORM_WIN32
+#ifdef WIN32
   found = find_searchpath(directories, binname + ".exe");
   found = find_searchpath(directories, binname + ".exe");
   if (found.empty()) {
   if (found.empty()) {
     found = find_searchpath(directories, binname);
     found = find_searchpath(directories, binname);

+ 17 - 0
ppremake/ppremake.h

@@ -27,6 +27,11 @@
 #include <strstream.h>
 #include <strstream.h>
 #endif
 #endif
 
 
+#if defined(PLATFORM_CYGWIN) || defined(WIN32_VC)
+// Either Cygwin or Visual C++ is a Win32 environment.
+#define WIN32
+#endif
+
 #include <string>
 #include <string>
 
 
 #ifdef HAVE_NAMESPACE
 #ifdef HAVE_NAMESPACE
@@ -67,4 +72,16 @@ extern bool unix_platform;
 extern bool windows_platform;
 extern bool windows_platform;
 #endif
 #endif
 
 
+/* These are defined so that we may build Filename and DSearchPath,
+   which are copied from dtool.  We have to copy these files from
+   dtool since ppremake must be built first, and stands outside of
+   Panda; but we want to minimize the changes we must make to these
+   files so that we can easily recopy them at need. 
+
+   These symbols just make the build environment a bit more
+   Panda-like. */
+#define PUBLISHED public
+#define INLINE inline
+#define EXPCL_DTOOL
+
 #endif
 #endif

+ 35 - 0
ppremake/vector_string.h

@@ -0,0 +1,35 @@
+// Filename: vector_string.h
+// Created by:  drose (15May00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef VECTOR_STRING_H
+#define VECTOR_STRING_H
+
+#include "ppremake.h"
+
+#include <vector>
+
+////////////////////////////////////////////////////////////////////
+//       Class : vector_string
+// Description : This typedef is borrowed from dtool/src/dtoolutil, and
+//               stripped down to just the bare minimum that Filename
+//               needs; and also modified to build outside of Panda.
+////////////////////////////////////////////////////////////////////
+
+typedef vector<string> vector_string;
+
+#endif