From 46821fc8f11b4b39c4ebb8f09eaea5cf8ffb1cff Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 22 May 2002 04:53:36 +0000 Subject: [PATCH] more preparations for VC++ building --- ppremake/Makefile.am | 7 +- ppremake/acconfig.h | 4 +- ppremake/config_msvc.h | 12 +- ppremake/configure.in | 15 +- ppremake/dSearchPath.I | 34 + ppremake/dSearchPath.cxx | 334 +++++++ ppremake/dSearchPath.h | 97 ++ ppremake/executionEnvironment.cxx | 62 ++ ppremake/executionEnvironment.h | 36 + ppremake/filename.I | 452 +++++++++ ppremake/filename.cxx | 1542 ++++++++++++++++++++++++++++- ppremake/filename.h | 191 +++- ppremake/ppScope.cxx | 74 +- ppremake/ppremake.h | 17 + ppremake/vector_string.h | 35 + 15 files changed, 2812 insertions(+), 100 deletions(-) create mode 100644 ppremake/dSearchPath.I create mode 100644 ppremake/dSearchPath.cxx create mode 100644 ppremake/dSearchPath.h create mode 100644 ppremake/executionEnvironment.cxx create mode 100644 ppremake/executionEnvironment.h create mode 100644 ppremake/filename.I create mode 100644 ppremake/vector_string.h diff --git a/ppremake/Makefile.am b/ppremake/Makefile.am index b14e93c156..18b9a2160f 100644 --- a/ppremake/Makefile.am +++ b/ppremake/Makefile.am @@ -1,7 +1,10 @@ bin_PROGRAMS = ppremake 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 \ gnu_getopt.c gnu_getopt.h gnu_regex.c gnu_regex.h \ ppCommandFile.cxx ppCommandFile.h ppDependableFile.cxx \ @@ -14,4 +17,4 @@ ppremake_SOURCES = \ ppremake.cxx ppremake.h sedAddress.cxx sedAddress.h sedCommand.cxx \ sedCommand.h sedContext.cxx sedContext.h sedProcess.cxx \ sedProcess.h sedScript.cxx sedScript.h tokenize.cxx \ - tokenize.h + tokenize.h vector_string.h diff --git a/ppremake/acconfig.h b/ppremake/acconfig.h index 635a1b0441..22fa6b37e6 100644 --- a/ppremake/acconfig.h +++ b/ppremake/acconfig.h @@ -20,8 +20,8 @@ /* Define if the C++ iostream library supports 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++. */ #undef WIN32_VC diff --git a/ppremake/config_msvc.h b/ppremake/config_msvc.h index 239e73263e..ed64a4e8a9 100644 --- a/ppremake/config_msvc.h +++ b/ppremake/config_msvc.h @@ -12,8 +12,8 @@ /* Define if the C++ iostream library supports 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 WIN32_VC 1 @@ -79,5 +79,9 @@ /* Define if you have the ANSI C header files. */ #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 ****************/ diff --git a/ppremake/configure.in b/ppremake/configure.in index f621350b9a..cc5a468306 100644 --- a/ppremake/configure.in +++ b/ppremake/configure.in @@ -1,6 +1,13 @@ dnl Process this file with autoconf to produce a configure script. 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) AC_PREFIX_DEFAULT(/usr/local/panda) @@ -60,14 +67,14 @@ else case "$host_os" in irix*) PLATFORM=Irix;; linux*) PLATFORM=Linux;; - cygwin*) PLATFORM=Win32;; + cygwin*) PLATFORM=Cygwin;; *) echo "Cannot determine platform; use --with-platform=name." exit 1;; esac fi -if test "$PLATFORM" = "Win32"; then - AC_DEFINE(PLATFORM_WIN32) +if test "$PLATFORM" = "Cygwin"; then + AC_DEFINE(PLATFORM_CYGWIN) fi AC_DEFINE_UNQUOTED(PLATFORM, "$PLATFORM") diff --git a/ppremake/dSearchPath.I b/ppremake/dSearchPath.I new file mode 100644 index 0000000000..a5735fd0f8 --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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); +} diff --git a/ppremake/dSearchPath.cxx b/ppremake/dSearchPath.cxx new file mode 100644 index 0000000000..b5278d1261 --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "dSearchPath.h" +#include "filename.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Results::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DSearchPath::Results:: +Results() { +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Results::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DSearchPath::Results:: +Results(const DSearchPath::Results ©) : + _files(copy._files) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Results::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DSearchPath::Results:: +operator = (const DSearchPath::Results ©) { + _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 ©) : + _directories(copy._directories) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: DSearchPath::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void DSearchPath:: +operator = (const DSearchPath ©) { + _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"; + } +} + + diff --git a/ppremake/dSearchPath.h b/ppremake/dSearchPath.h new file mode 100644 index 0000000000..fb9ee7feb8 --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PANDASEARCHPATH_H +#define PANDASEARCHPATH_H + +#include "ppremake.h" + +#include "filename.h" + +#include + +/////////////////////////////////////////////////////////////////// +// 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 ©); + void operator = (const Results ©); + ~Results(); + + void clear(); + int get_num_files() const; + Filename get_file(int n) const; + + private: + typedef vector Files; + Files _files; + friend class DSearchPath; + }; + +PUBLISHED: + DSearchPath(); + DSearchPath(const string &path, const string &delimiters = ": \n\t"); + DSearchPath(const DSearchPath ©); + void operator = (const DSearchPath ©); + ~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 Directories; + Directories _directories; +}; + +INLINE ostream &operator << (ostream &out, const DSearchPath &sp) { + sp.output(out); + return out; +} + +#include "dSearchPath.I" + +#endif diff --git a/ppremake/executionEnvironment.cxx b/ppremake/executionEnvironment.cxx new file mode 100644 index 0000000000..689980b296 --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "executionEnvironment.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include // for perror + +#ifdef WIN32_VC +// Windows requires this for getcwd(). +#include +#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); +} diff --git a/ppremake/executionEnvironment.h b/ppremake/executionEnvironment.h new file mode 100644 index 0000000000..fc88da7d54 --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/ppremake/filename.I b/ppremake/filename.I new file mode 100644 index 0000000000..e0c01bc439 --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// 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 ©) + : _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 ©) { + _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; +} diff --git a/ppremake/filename.cxx b/ppremake/filename.cxx index a46dce3861..8a9dd88f31 100644 --- a/ppremake/filename.cxx +++ b/ppremake/filename.cxx @@ -1,78 +1,1524 @@ // 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 +// panda3d@yahoogroups.com . +// //////////////////////////////////////////////////////////////////// #include "filename.h" -#include +#include "dSearchPath.h" +#include "executionEnvironment.h" + +#include // For rename() and tempnam() +#include // for clock() and time() +#include +#include + +#ifdef HAVE_UTIME_H +#include + +// We assume we have these too. +#include +#include +#endif + +#ifdef HAVE_DIRENT_H +#include +#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 +#endif + + +#if defined(WIN32) +/* begin Win32-specific code */ + +#include +#include + +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: 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::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. //////////////////////////////////////////////////////////////////// -bool -is_fullpath(const string &pathname) { - if (pathname.empty()) { +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 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: 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. +//////////////////////////////////////////////////////////////////// +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; } - if (pathname[0] == '/') { - return true; + // Temporarily save the current working directory. + Filename cwd = ExecutionEnvironment::get_cwd(); + + if (is_directory()) { + // If the filename itself represents a directory and not a + // filename, cd to the named directory, not the one above it. + string dirname = to_os_specific(); + + if (chdir(dirname.c_str()) < 0) { + return false; + } + (*this) = ExecutionEnvironment::get_cwd(); + + } else { + // Otherwise, if the filename represents a regular file (or + // doesn't even exist), cd to the directory above. + Filename dir(get_dirname()); + + if (dir.empty()) { + // No dirname means the file is in this directory. + set_dirname(cwd); + return true; + } + + string dirname = dir.to_os_specific(); + if (chdir(dirname.c_str()) < 0) { + return false; + } + set_dirname(ExecutionEnvironment::get_cwd().get_fullpath()); } - if (windows_platform) { - if (pathname.length() > 2 && - isalpha(pathname[0]) && pathname[1] == ':') { - // A drive-letter prefix. - return (pathname[2] == '/' || pathname[2] == '\\'); + // 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; + } +#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; } - // No drive-letter prefix. - return (pathname[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 (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; + } + } + } + } + + if (!found.empty()) { + (*this) = found; + return true; } 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; } - return pathname; + 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: PPScope::to_unix_filename -// Description: Changes backslashes to forward slashes, but only if -// windows_platform is set. Otherwise returns the -// string unchanged. +// 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. //////////////////////////////////////////////////////////////////// -string -to_unix_filename(string pathname) { - if (windows_platform) { - string::iterator si; - for (si = pathname.begin(); si != pathname.end(); ++si) { - if ((*si) == '\\') { - (*si) = '/'; - } +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 pathname; + 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 +} + +//////////////////////////////////////////////////////////////////// +// 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: 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. +//////////////////////////////////////////////////////////////////// +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; + } + } + + // 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; +} + diff --git a/ppremake/filename.h b/ppremake/filename.h index f6fc2358c8..a17aa59468 100644 --- a/ppremake/filename.h +++ b/ppremake/filename.h @@ -1,6 +1,19 @@ // 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 +// panda3d@yahoogroups.com . +// //////////////////////////////////////////////////////////////////// #ifndef FILENAME_H @@ -8,12 +21,176 @@ #include "ppremake.h" -// This header file defines a few functions handy for dealing with -// filenames in a cross-platform world. +#include "vector_string.h" -bool is_fullpath(const string &pathname); -string to_os_filename(string pathname); -string to_unix_filename(string pathname); +#include + +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 ©); + 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 ©); + + // 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. + + 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 + + diff --git a/ppremake/ppScope.cxx b/ppremake/ppScope.cxx index 15e87aa66f..fe7870cc82 100644 --- a/ppremake/ppScope.cxx +++ b/ppremake/ppScope.cxx @@ -1145,10 +1145,10 @@ expand_variable_nested(const string &varname, //////////////////////////////////////////////////////////////////// string PPScope:: expand_isfullpath(const string ¶ms) { - string filename = trim_blanks(expand_string(params)); + Filename filename = trim_blanks(expand_string(params)); string result; - if (is_fullpath(filename)) { + if (filename.is_fully_qualified()) { result = filename; } return result; @@ -1160,15 +1160,12 @@ expand_isfullpath(const string ¶ms) { // Description: Expands the "osfilename" function variable. This // converts the filename from a Unix-style filename // (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:: expand_osfilename(const string ¶ms) { @@ -1178,7 +1175,8 @@ expand_osfilename(const string ¶ms) { vector::iterator 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, " "); @@ -1191,15 +1189,9 @@ expand_osfilename(const string ¶ms) { // Description: Expands the "unixfilename" function variable. This // converts the filename from a platform-specific // 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:: expand_unixfilename(const string ¶ms) { @@ -1209,7 +1201,8 @@ expand_unixfilename(const string ¶ms) { vector::iterator 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, " "); @@ -1219,10 +1212,15 @@ expand_unixfilename(const string ¶ms) { //////////////////////////////////////////////////////////////////// // Function: PPScope::expand_cygpath_w // 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:: expand_cygpath_w(const string ¶ms) { @@ -1233,6 +1231,9 @@ expand_cygpath_w(const string ¶ms) { cygwin_conv_to_win32_path(filename.c_str(), result); filename = result; +#else + Filename fn(filename); + filename = fn.to_os_specific(); #endif return filename; @@ -1241,10 +1242,15 @@ expand_cygpath_w(const string ¶ms) { //////////////////////////////////////////////////////////////////// // Function: PPScope::expand_cygpath_p // 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:: expand_cygpath_p(const string ¶ms) { @@ -1255,6 +1261,8 @@ expand_cygpath_p(const string ¶ms) { cygwin_conv_to_posix_path(filename.c_str(), result); filename = result; +#else + filename = Filename::from_os_specific(filename); #endif return filename; @@ -1389,7 +1397,7 @@ expand_libtest(const string ¶ms) { // Also add the system directories to the list, whatever we think // those should be. Here we have to make a few assumptions. -#ifdef PLATFORM_WIN32 +#ifdef WIN32 const char *windir = getenv("WINDIR"); if (windir != (const char *)NULL) { directories.push_back(string(windir) + "\\System"); @@ -1427,7 +1435,7 @@ expand_libtest(const string ¶ms) { string found; -#ifdef PLATFORM_WIN32 +#ifdef WIN32 if (libname.length() > 4 && libname.substr(libname.length() - 4) == ".lib") { found = find_searchpath(directories, libname); if (found.empty()) { @@ -1472,7 +1480,7 @@ expand_bintest(const string ¶ms) { } // An explicit path from the root does not require a search. -#ifdef PLATFORM_WIN32 +#ifdef WIN32 if ((binname.length() > 2 && binname[1] == ':') || binname[0] == '/') #else if (binname[0] == '/') @@ -1494,7 +1502,7 @@ expand_bintest(const string ¶ms) { vector directories; -#ifdef PLATFORM_WIN32 +#ifdef WIN32 if (pathvar.find(';') != string::npos) { // If the path contains semicolons, it's a native Windows-style // path: split it up based on semicolons. @@ -1511,7 +1519,7 @@ expand_bintest(const string ¶ms) { string found; -#ifdef PLATFORM_WIN32 +#ifdef WIN32 found = find_searchpath(directories, binname + ".exe"); if (found.empty()) { found = find_searchpath(directories, binname); diff --git a/ppremake/ppremake.h b/ppremake/ppremake.h index 571b9e5bd7..f5f5d01e26 100644 --- a/ppremake/ppremake.h +++ b/ppremake/ppremake.h @@ -27,6 +27,11 @@ #include #endif +#if defined(PLATFORM_CYGWIN) || defined(WIN32_VC) +// Either Cygwin or Visual C++ is a Win32 environment. +#define WIN32 +#endif + #include #ifdef HAVE_NAMESPACE @@ -67,4 +72,16 @@ extern bool unix_platform; extern bool windows_platform; #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 diff --git a/ppremake/vector_string.h b/ppremake/vector_string.h new file mode 100644 index 0000000000..4aa5399dac --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_STRING_H +#define VECTOR_STRING_H + +#include "ppremake.h" + +#include + +//////////////////////////////////////////////////////////////////// +// 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 vector_string; + +#endif