| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- // Filename: cvsSourceTree.cxx
- // Created by: drose (31Oct00)
- //
- ////////////////////////////////////////////////////////////////////
- //
- // PANDA 3D SOFTWARE
- // Copyright (c) 2001 - 2004, 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://etc.cmu.edu/panda3d/docs/license/ .
- //
- // To contact the maintainers of this program write to
- // [email protected] .
- //
- ////////////////////////////////////////////////////////////////////
- #include "cvsSourceTree.h"
- #include "cvsSourceDirectory.h"
- #include "filename.h"
- #include "executionEnvironment.h"
- #include "pnotify.h"
- #include "string_utils.h"
- #include <algorithm>
- #include <ctype.h>
- #include <stdio.h> // for perror
- #include <errno.h>
- #ifdef WIN32_VC
- #include <direct.h> // for chdir
- #endif
- bool CVSSourceTree::_got_start_fullpath = false;
- Filename CVSSourceTree::_start_fullpath;
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::Constructor
- // Access: Public
- // Description:
- ////////////////////////////////////////////////////////////////////
- CVSSourceTree::
- CVSSourceTree() {
- _root = (CVSSourceDirectory *)NULL;
- _got_root_fullpath = false;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::Destructor
- // Access: Public
- // Description:
- ////////////////////////////////////////////////////////////////////
- CVSSourceTree::
- ~CVSSourceTree() {
- if (_root != (CVSSourceDirectory *)NULL) {
- delete _root;
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::set_root
- // Access: Public
- // Description: Sets the root of the source directory. This must be
- // called before scan(), and should not be called more
- // than once.
- ////////////////////////////////////////////////////////////////////
- void CVSSourceTree::
- set_root(const Filename &root_path) {
- nassertv(_path.empty());
- _path = root_path;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::scan
- // Access: Public
- // Description: Scans the complete source directory starting at the
- // indicated pathname. It is an error to call this more
- // than once. Returns true on success, false if there
- // is an error.
- ////////////////////////////////////////////////////////////////////
- bool CVSSourceTree::
- scan(const Filename &key_filename) {
- nassertr(_root == (CVSSourceDirectory *)NULL, false);
- Filename root_fullpath = get_root_fullpath();
- _root = new CVSSourceDirectory(this, NULL, root_fullpath.get_basename());
- return _root->scan(_path, key_filename);
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::get_root
- // Access: Public
- // Description: Returns the root directory of the hierarchy.
- ////////////////////////////////////////////////////////////////////
- CVSSourceDirectory *CVSSourceTree::
- get_root() const {
- return _root;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::find_directory
- // Access: Public
- // Description: Returns the source directory that corresponds to the
- // given path, or NULL if there is no such directory in
- // the source tree.
- ////////////////////////////////////////////////////////////////////
- CVSSourceDirectory *CVSSourceTree::
- find_directory(const Filename &path) {
- string root_fullpath = get_root_fullpath();
- string fullpath = get_actual_fullpath(path);
- // path is a subdirectory within the source hierarchy if and only if
- // root_fullpath is an initial prefix of fullpath.
- if (root_fullpath.length() > fullpath.length() ||
- cmp_nocase(fullpath.substr(0, root_fullpath.length()), root_fullpath) != 0) {
- // Nope!
- return (CVSSourceDirectory *)NULL;
- }
- // The relative name is the part of fullpath not in root_fullpath.
- Filename relpath = fullpath.substr(root_fullpath.length());
- return _root->find_relpath(relpath);
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::find_relpath
- // Access: Public
- // Description: Returns the source directory that corresponds to the
- // given relative path from the root, or NULL if there
- // is no match. The relative path may or may not
- // include the name of the root directory itself.
- ////////////////////////////////////////////////////////////////////
- CVSSourceDirectory *CVSSourceTree::
- find_relpath(const string &relpath) {
- CVSSourceDirectory *result = _root->find_relpath(relpath);
- if (result != (CVSSourceDirectory *)NULL) {
- return result;
- }
- // Check for the root dirname at the front of the path, and remove
- // it if it's there.
- size_t slash = relpath.find('/');
- Filename first = relpath.substr(0, slash);
- Filename rest;
- if (slash != string::npos) {
- rest = relpath.substr(slash + 1);
- }
- if (cmp_nocase(first, _root->get_dirname()) == 0) {
- return _root->find_relpath(rest);
- }
- return (CVSSourceDirectory *)NULL;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::find_dirname
- // Access: Public
- // Description: Returns the source directory that corresponds to the
- // given local directory name, or NULL if there
- // is no match.
- ////////////////////////////////////////////////////////////////////
- CVSSourceDirectory *CVSSourceTree::
- find_dirname(const string &dirname) {
- return _root->find_dirname(dirname);
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::choose_directory
- // Access: Public
- // Description: Determines where an externally referenced model file
- // of the indicated name should go. It does this by
- // looking for an existing model file of the same name;
- // if a matching model is not found, or if multiple
- // matching files are found, prompts the user for the
- // directory, or uses suggested_dir.
- ////////////////////////////////////////////////////////////////////
- CVSSourceTree::FilePath CVSSourceTree::
- choose_directory(const string &basename, CVSSourceDirectory *suggested_dir,
- bool force, bool interactive) {
- static FilePaths empty_paths;
- Basenames::const_iterator bi;
- bi = _basenames.find(downcase(basename));
- if (bi != _basenames.end()) {
- // The filename already exists somewhere.
- const FilePaths &paths = (*bi).second;
- return prompt_user(basename, suggested_dir, paths,
- force, interactive);
- }
- // Now we have to prompt the user for a suitable place to put it.
- return prompt_user(basename, suggested_dir, empty_paths,
- force, interactive);
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::get_root_fullpath
- // Access: Public
- // Description: Returns the full path from the root to the top of
- // the source hierarchy.
- ////////////////////////////////////////////////////////////////////
- Filename CVSSourceTree::
- get_root_fullpath() {
- nassertr(!_path.empty(), Filename());
- if (!_got_root_fullpath) {
- _root_fullpath = get_actual_fullpath(_path);
- _got_root_fullpath = true;
- }
- return _root_fullpath;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::get_root_dirname
- // Access: Public
- // Description: Returns the local directory name of the root of the
- // tree.
- ////////////////////////////////////////////////////////////////////
- Filename CVSSourceTree::
- get_root_dirname() const {
- nassertr(_root != (CVSSourceDirectory *)NULL, Filename());
- return _root->get_dirname();
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::add_file
- // Access: Public
- // Description: Adds a new file to the set of known files. This is
- // normally called from CVSSourceDirectory::scan() and
- // should not be called directly by the user.
- ////////////////////////////////////////////////////////////////////
- void CVSSourceTree::
- add_file(const string &basename, CVSSourceDirectory *dir) {
- FilePath file_path(dir, basename);
- _basenames[downcase(basename)].push_back(file_path);
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::temp_chdir
- // Access: Public, Static
- // Description: Temporarily changes the current directory to the
- // named path. Returns true on success, false on
- // failure. Call restore_cwd() to restore to the
- // original directory later.
- ////////////////////////////////////////////////////////////////////
- bool CVSSourceTree::
- temp_chdir(const Filename &path) {
- // We have to call this first to guarantee that we have already
- // determined our starting directory.
- get_start_fullpath();
- string os_path = path.to_os_specific();
- if (chdir(os_path.c_str()) < 0) {
- return false;
- }
- return true;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::restore_cwd
- // Access: Public, Static
- // Description: Restores the current directory after changing it from
- // temp_chdir().
- ////////////////////////////////////////////////////////////////////
- void CVSSourceTree::
- restore_cwd() {
- Filename start_fullpath = get_start_fullpath();
- string os_path = start_fullpath.to_os_specific();
- if (chdir(os_path.c_str()) < 0) {
- // Hey! We can't get back to the directory we started from!
- perror(os_path.c_str());
- nout << "Can't continue, aborting.\n";
- exit(1);
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::prompt_user
- // Access: Private
- // Description: Prompts the user, if necessary, to choose a directory
- // to import the given file into.
- ////////////////////////////////////////////////////////////////////
- CVSSourceTree::FilePath CVSSourceTree::
- prompt_user(const string &basename, CVSSourceDirectory *suggested_dir,
- const CVSSourceTree::FilePaths &paths,
- bool force, bool interactive) {
- if (paths.size() == 1) {
- // The file already exists in exactly one place.
- if (!interactive) {
- return paths[0];
- }
- FilePath result = ask_existing(basename, paths[0]);
- if (result.is_valid()) {
- return result;
- }
- } else if (paths.size() > 1) {
- // The file already exists in multiple places.
- if (force && !interactive) {
- return paths[0];
- }
- FilePath result = ask_existing(basename, paths, suggested_dir);
- if (result.is_valid()) {
- return result;
- }
- }
- // The file does not already exist, or the user declined to replace
- // an existing file.
- if (force && !interactive) {
- return FilePath(suggested_dir, basename);
- }
- // Is the file already in the suggested directory? If not, prompt
- // the user to put it there.
- bool found_dir = false;
- FilePaths::const_iterator pi;
- for (pi = paths.begin(); pi != paths.end(); ++pi) {
- if ((*pi)._dir == suggested_dir) {
- found_dir = true;
- break;
- }
- }
- if (!found_dir) {
- FilePath result = ask_new(basename, suggested_dir);
- if (result.is_valid()) {
- return result;
- }
- }
- // Ask the user where the damn thing should go.
- return ask_any(basename, paths);
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::ask_existing
- // Access: Private
- // Description: Asks the user if he wants to replace an existing
- // file.
- ////////////////////////////////////////////////////////////////////
- CVSSourceTree::FilePath CVSSourceTree::
- ask_existing(const string &basename, const CVSSourceTree::FilePath &path) {
- while (true) {
- nout << basename << " found in tree at "
- << path.get_path() << ".\n";
- string result = prompt("Overwrite this file (y/n)? ");
- nassertr(!result.empty(), FilePath());
- if (result.size() == 1) {
- if (tolower(result[0]) == 'y') {
- return path;
- } else if (tolower(result[0]) == 'n') {
- return FilePath();
- }
- }
- nout << "*** Invalid response: " << result << "\n\n";
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::ask_existing
- // Access: Private
- // Description: Asks the user which of several existing files he
- // wants to replace.
- ////////////////////////////////////////////////////////////////////
- CVSSourceTree::FilePath CVSSourceTree::
- ask_existing(const string &basename, const CVSSourceTree::FilePaths &paths,
- CVSSourceDirectory *suggested_dir) {
- while (true) {
- nout << basename << " found in tree at more than one place:\n";
- bool any_suggested = false;
- for (int i = 0; i < (int)paths.size(); i++) {
- nout << " " << (i + 1) << ". "
- << paths[i].get_path() << "\n";
- if (paths[i]._dir == suggested_dir) {
- any_suggested = true;
- }
- }
- int next_option = paths.size() + 1;
- int suggested_option = -1;
- if (!any_suggested) {
- // If it wasn't already in the suggested directory, offer to put
- // it there.
- suggested_option = next_option;
- next_option++;
- nout << "\n" << suggested_option
- << ". create "
- << Filename(suggested_dir->get_path(), basename)
- << "\n";
- }
- int other_option = next_option;
- nout << other_option << ". Other\n";
- string result = prompt("Choose an option: ");
- nassertr(!result.empty(), FilePath());
- const char *nptr = result.c_str();
- char *endptr;
- int option = strtol(nptr, &endptr, 10);
- if (*endptr == '\0') {
- if (option >= 1 && option <= (int)paths.size()) {
- return paths[option - 1];
- } else if (option == suggested_option) {
- return FilePath(suggested_dir, basename);
- } else if (option == other_option) {
- return FilePath();
- }
- }
- nout << "*** Invalid response: " << result << "\n\n";
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::ask_new
- // Access: Private
- // Description: Asks the user if he wants to create a new file.
- ////////////////////////////////////////////////////////////////////
- CVSSourceTree::FilePath CVSSourceTree::
- ask_new(const string &basename, CVSSourceDirectory *dir) {
- while (true) {
- nout << basename << " will be created in "
- << dir->get_path() << ".\n";
- string result = prompt("Create this file (y/n)? ");
- nassertr(!result.empty(), FilePath());
- if (result.size() == 1) {
- if (tolower(result[0]) == 'y') {
- return FilePath(dir, basename);
- } else if (tolower(result[0]) == 'n') {
- return FilePath();
- }
- }
- nout << "*** Invalid response: " << result << "\n\n";
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::ask_any
- // Access: Private
- // Description: Asks the user to type in the name of the directory in
- // which to store the file.
- ////////////////////////////////////////////////////////////////////
- CVSSourceTree::FilePath CVSSourceTree::
- ask_any(const string &basename,
- const CVSSourceTree::FilePaths &paths) {
- while (true) {
- string result =
- prompt("Enter the name of the directory to copy " + basename + " to: ");
- nassertr(!result.empty(), FilePath());
- // The user might enter a fully-qualified path to the directory,
- // or a relative path from the root (with or without the root's
- // dirname), or the dirname of the particular directory.
- CVSSourceDirectory *dir = find_directory(result);
- if (dir == (CVSSourceDirectory *)NULL) {
- dir = find_relpath(result);
- }
- if (dir == (CVSSourceDirectory *)NULL) {
- dir = find_dirname(result);
- }
- if (dir != (CVSSourceDirectory *)NULL) {
- // If the file is already in this directory, we must preserve
- // its existing case.
- FilePaths::const_iterator pi;
- for (pi = paths.begin(); pi != paths.end(); ++pi) {
- if ((*pi)._dir == dir) {
- return (*pi);
- }
- }
- // Otherwise, since we're creating a new file, keep the original
- // case.
- return FilePath(dir, basename);
- }
- nout << "*** Not a valid directory name: " << result << "\n\n";
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::prompt
- // Access: Private
- // Description: Issues a prompt to the user and waits for a typed
- // response. Returns the response (which will not be
- // empty).
- ////////////////////////////////////////////////////////////////////
- string CVSSourceTree::
- prompt(const string &message) {
- nout << flush;
- while (true) {
- cerr << message << flush;
- string response;
- getline(cin, response);
- // Remove leading and trailing whitespace.
- size_t p = 0;
- while (p < response.length() && isspace(response[p])) {
- p++;
- }
- size_t q = response.length();
- while (q > p && isspace(response[q - 1])) {
- q--;
- }
- if (q > p) {
- return response.substr(p, q - p);
- }
- }
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::get_actual_fullpath
- // Access: Private, Static
- // Description: Determines the actual full path from the root to the
- // named directory.
- ////////////////////////////////////////////////////////////////////
- Filename CVSSourceTree::
- get_actual_fullpath(const Filename &path) {
- Filename canon = path;
- canon.make_canonical();
- return canon;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::get_start_fullpath
- // Access: Private, Static
- // Description: Returns the full path from the root to the directory
- // in which the user started the program.
- ////////////////////////////////////////////////////////////////////
- Filename CVSSourceTree::
- get_start_fullpath() {
- if (!_got_start_fullpath) {
- Filename cwd = ExecutionEnvironment::get_cwd();
- _start_fullpath = cwd.to_os_specific();
- }
- return _start_fullpath;
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::FilePath::Constructor
- // Access: Public
- // Description: Creates an invalid FilePath specification.
- ////////////////////////////////////////////////////////////////////
- CVSSourceTree::FilePath::
- FilePath() :
- _dir(NULL)
- {
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::FilePath::Constructor
- // Access: Public
- // Description: Creates a valid FilePath specification with the
- // indicated directory and basename.
- ////////////////////////////////////////////////////////////////////
- CVSSourceTree::FilePath::
- FilePath(CVSSourceDirectory *dir, const string &basename) :
- _dir(dir),
- _basename(basename)
- {
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::FilePath::is_valid
- // Access: Public
- // Description: Returns true if this FilePath represents a valid
- // file, or false if it represents an error return.
- ////////////////////////////////////////////////////////////////////
- bool CVSSourceTree::FilePath::
- is_valid() const {
- return (_dir != (CVSSourceDirectory *)NULL);
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::FilePath::get_path
- // Access: Public
- // Description: Returns the relative path to this file from the root
- // of the source tree.
- ////////////////////////////////////////////////////////////////////
- Filename CVSSourceTree::FilePath::
- get_path() const {
- nassertr(_dir != (CVSSourceDirectory *)NULL, Filename());
- return Filename(_dir->get_path(), _basename);
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::FilePath::get_fullpath
- // Access: Public
- // Description: Returns the full path to this file.
- ////////////////////////////////////////////////////////////////////
- Filename CVSSourceTree::FilePath::
- get_fullpath() const {
- nassertr(_dir != (CVSSourceDirectory *)NULL, Filename());
- return Filename(_dir->get_fullpath(), _basename);
- }
- ////////////////////////////////////////////////////////////////////
- // Function: CVSSourceTree::FilePath::get_rel_from
- // Access: Public
- // Description: Returns the relative path to this file as seen from
- // the indicated source directory.
- ////////////////////////////////////////////////////////////////////
- Filename CVSSourceTree::FilePath::
- get_rel_from(const CVSSourceDirectory *other) const {
- nassertr(_dir != (CVSSourceDirectory *)NULL, Filename());
- return Filename(other->get_rel_to(_dir), _basename);
- }
|