Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

pandatool/src/cvscopy/cvsSourceTree.cxx

Go to the documentation of this file.
00001 // Filename: cvsSourceTree.cxx
00002 // Created by:  drose (31Oct00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "cvsSourceTree.h"
00020 #include "cvsSourceDirectory.h"
00021 
00022 #include "filename.h"
00023 #include "executionEnvironment.h"
00024 #include "notify.h"
00025 
00026 #include <algorithm>
00027 #include <ctype.h>
00028 #include <stdio.h> // for perror
00029 #include <errno.h>
00030 
00031 #ifdef WIN32_VC
00032 #include <direct.h>  // for chdir
00033 #endif
00034 
00035 bool CVSSourceTree::_got_start_fullpath = false;
00036 Filename CVSSourceTree::_start_fullpath;
00037 
00038 ////////////////////////////////////////////////////////////////////
00039 //     Function: CVSSourceTree::Constructor
00040 //       Access: Public
00041 //  Description:
00042 ////////////////////////////////////////////////////////////////////
00043 CVSSourceTree::
00044 CVSSourceTree() {
00045   _root = (CVSSourceDirectory *)NULL;
00046   _got_root_fullpath = false;
00047 }
00048 
00049 ////////////////////////////////////////////////////////////////////
00050 //     Function: CVSSourceTree::Destructor
00051 //       Access: Public
00052 //  Description:
00053 ////////////////////////////////////////////////////////////////////
00054 CVSSourceTree::
00055 ~CVSSourceTree() {
00056   if (_root != (CVSSourceDirectory *)NULL) {
00057     delete _root;
00058   }
00059 }
00060 
00061 ////////////////////////////////////////////////////////////////////
00062 //     Function: CVSSourceTree::set_root
00063 //       Access: Public
00064 //  Description: Sets the root of the source directory.  This must be
00065 //               called before scan(), and should not be called more
00066 //               than once.
00067 ////////////////////////////////////////////////////////////////////
00068 void CVSSourceTree::
00069 set_root(const Filename &root_path) {
00070   nassertv(_path.empty());
00071   _path = root_path;
00072 }
00073 
00074 ////////////////////////////////////////////////////////////////////
00075 //     Function: CVSSourceTree::scan
00076 //       Access: Public
00077 //  Description: Scans the complete source directory starting at the
00078 //               indicated pathname.  It is an error to call this more
00079 //               than once.  Returns true on success, false if there
00080 //               is an error.
00081 ////////////////////////////////////////////////////////////////////
00082 bool CVSSourceTree::
00083 scan(const Filename &key_filename) {
00084   nassertr(_root == (CVSSourceDirectory *)NULL, false);
00085   Filename root_fullpath = get_root_fullpath();
00086   _root = new CVSSourceDirectory(this, NULL, root_fullpath.get_basename());
00087   return _root->scan(_path, key_filename);
00088 }
00089 
00090 ////////////////////////////////////////////////////////////////////
00091 //     Function: CVSSourceTree::get_root
00092 //       Access: Public
00093 //  Description: Returns the root directory of the hierarchy.
00094 ////////////////////////////////////////////////////////////////////
00095 CVSSourceDirectory *CVSSourceTree::
00096 get_root() const {
00097   return _root;
00098 }
00099 
00100 ////////////////////////////////////////////////////////////////////
00101 //     Function: CVSSourceTree::find_directory
00102 //       Access: Public
00103 //  Description: Returns the source directory that corresponds to the
00104 //               given path, or NULL if there is no such directory in
00105 //               the source tree.
00106 ////////////////////////////////////////////////////////////////////
00107 CVSSourceDirectory *CVSSourceTree::
00108 find_directory(const Filename &path) {
00109   string root_fullpath = get_root_fullpath();
00110   string fullpath = get_actual_fullpath(path);
00111 
00112   // path is a subdirectory within the source hierarchy if and only if
00113   // root_fullpath is an initial prefix of fullpath.
00114   if (root_fullpath.length() > fullpath.length() ||
00115       fullpath.substr(0, root_fullpath.length()) != root_fullpath) {
00116     // Nope!
00117     return (CVSSourceDirectory *)NULL;
00118   }
00119 
00120   // The relative name is the part of fullpath not in root_fullpath.
00121   Filename relpath = fullpath.substr(root_fullpath.length());
00122 
00123   return _root->find_relpath(relpath);
00124 }
00125 
00126 ////////////////////////////////////////////////////////////////////
00127 //     Function: CVSSourceTree::find_relpath
00128 //       Access: Public
00129 //  Description: Returns the source directory that corresponds to the
00130 //               given relative path from the root, or NULL if there
00131 //               is no match.  The relative path may or may not
00132 //               include the name of the root directory itself.
00133 ////////////////////////////////////////////////////////////////////
00134 CVSSourceDirectory *CVSSourceTree::
00135 find_relpath(const string &relpath) {
00136   CVSSourceDirectory *result = _root->find_relpath(relpath);
00137   if (result != (CVSSourceDirectory *)NULL) {
00138     return result;
00139   }
00140 
00141   // Check for the root dirname at the front of the path, and remove
00142   // it if it's there.
00143   size_t slash = relpath.find('/');
00144   Filename first = relpath.substr(0, slash);
00145   Filename rest;
00146   if (slash != string::npos) {
00147     rest = relpath.substr(slash + 1);
00148   }
00149 
00150   if (first == _root->get_dirname()) {
00151     return _root->find_relpath(rest);
00152   }
00153 
00154   return (CVSSourceDirectory *)NULL;
00155 }
00156 
00157 ////////////////////////////////////////////////////////////////////
00158 //     Function: CVSSourceTree::find_dirname
00159 //       Access: Public
00160 //  Description: Returns the source directory that corresponds to the
00161 //               given local directory name, or NULL if there
00162 //               is no match.
00163 ////////////////////////////////////////////////////////////////////
00164 CVSSourceDirectory *CVSSourceTree::
00165 find_dirname(const string &dirname) {
00166   return _root->find_dirname(dirname);
00167 }
00168 
00169 ////////////////////////////////////////////////////////////////////
00170 //     Function: CVSSourceTree::choose_directory
00171 //       Access: Public
00172 //  Description: Determines where an externally referenced model file
00173 //               of the indicated name should go.  It does this by
00174 //               looking for an existing model file of the same name;
00175 //               if a matching model is not found, or if multiple
00176 //               matching files are found, prompts the user for the
00177 //               directory, or uses suggested_dir.
00178 ////////////////////////////////////////////////////////////////////
00179 CVSSourceDirectory *CVSSourceTree::
00180 choose_directory(const Filename &filename, CVSSourceDirectory *suggested_dir,
00181                  bool force, bool interactive) {
00182   static Directories empty_dirs;
00183 
00184   Filenames::const_iterator fi;
00185   fi = _filenames.find(filename);
00186   if (fi != _filenames.end()) {
00187     // The filename already exists somewhere.
00188     const Directories &dirs = (*fi).second;
00189 
00190     return prompt_user(filename, suggested_dir, dirs,
00191                        force, interactive);
00192   }
00193 
00194   // Now we have to prompt the user for a suitable place to put it.
00195   return prompt_user(filename, suggested_dir, empty_dirs,
00196                      force, interactive);
00197 }
00198 
00199 ////////////////////////////////////////////////////////////////////
00200 //     Function: CVSSourceTree::get_root_fullpath
00201 //       Access: Public
00202 //  Description: Returns the full path from the root to the top of
00203 //               the source hierarchy.
00204 ////////////////////////////////////////////////////////////////////
00205 Filename CVSSourceTree::
00206 get_root_fullpath() {
00207   nassertr(!_path.empty(), Filename());
00208   if (!_got_root_fullpath) {
00209     _root_fullpath = get_actual_fullpath(_path);
00210     _got_root_fullpath = true;
00211   }
00212   return _root_fullpath;
00213 }
00214 
00215 ////////////////////////////////////////////////////////////////////
00216 //     Function: CVSSourceTree::get_root_dirname
00217 //       Access: Public
00218 //  Description: Returns the local directory name of the root of the
00219 //               tree.
00220 ////////////////////////////////////////////////////////////////////
00221 Filename CVSSourceTree::
00222 get_root_dirname() const {
00223   nassertr(_root != (CVSSourceDirectory *)NULL, Filename());
00224   return _root->get_dirname();
00225 }
00226 
00227 ////////////////////////////////////////////////////////////////////
00228 //     Function: CVSSourceTree::add_file
00229 //       Access: Public
00230 //  Description: Adds a new file to the set of known files.  This is
00231 //               normally called from CVSSourceDirectory::scan() and
00232 //               should not be called directly by the user.
00233 ////////////////////////////////////////////////////////////////////
00234 void CVSSourceTree::
00235 add_file(const Filename &filename, CVSSourceDirectory *dir) {
00236   _filenames[filename].push_back(dir);
00237 }
00238 
00239 ////////////////////////////////////////////////////////////////////
00240 //     Function: CVSSourceTree::temp_chdir
00241 //       Access: Public, Static
00242 //  Description: Temporarily changes the current directory to the
00243 //               named path.  Returns true on success, false on
00244 //               failure.  Call restore_cwd() to restore to the
00245 //               original directory later.
00246 ////////////////////////////////////////////////////////////////////
00247 bool CVSSourceTree::
00248 temp_chdir(const Filename &path) {
00249   // We have to call this first to guarantee that we have already
00250   // determined our starting directory.
00251   get_start_fullpath();
00252 
00253   string os_path = path.to_os_specific();
00254   if (chdir(os_path.c_str()) < 0) {
00255     return false;
00256   }
00257   return true;
00258 }
00259 
00260 ////////////////////////////////////////////////////////////////////
00261 //     Function: CVSSourceTree::restore_cwd
00262 //       Access: Public, Static
00263 //  Description: Restores the current directory after changing it from
00264 //               temp_chdir().
00265 ////////////////////////////////////////////////////////////////////
00266 void CVSSourceTree::
00267 restore_cwd() {
00268   Filename start_fullpath = get_start_fullpath();
00269   string os_path = start_fullpath.to_os_specific();
00270 
00271   if (chdir(os_path.c_str()) < 0) {
00272     // Hey!  We can't get back to the directory we started from!
00273     perror(os_path.c_str());
00274     nout << "Can't continue, aborting.\n";
00275     exit(1);
00276   }
00277 }
00278 
00279 
00280 ////////////////////////////////////////////////////////////////////
00281 //     Function: CVSSourceTree::prompt_user
00282 //       Access: Private
00283 //  Description: Prompts the user, if necessary, to choose a directory
00284 //               to import the given file into.
00285 ////////////////////////////////////////////////////////////////////
00286 CVSSourceDirectory *CVSSourceTree::
00287 prompt_user(const string &filename, CVSSourceDirectory *suggested_dir,
00288             const CVSSourceTree::Directories &dirs,
00289             bool force, bool interactive) {
00290   if (dirs.size() == 1) {
00291     // The file already exists in exactly one place.
00292     if (!interactive) {
00293       return dirs[0];
00294     }
00295     CVSSourceDirectory *result = ask_existing(filename, dirs[0]);
00296     if (result != (CVSSourceDirectory *)NULL) {
00297       return result;
00298     }
00299 
00300   } else if (dirs.size() > 1) {
00301     // The file already exists in multiple places.
00302     if (force && !interactive) {
00303       return dirs[0];
00304     }
00305     CVSSourceDirectory *result = ask_existing(filename, dirs, suggested_dir);
00306     if (result != (CVSSourceDirectory *)NULL) {
00307       return result;
00308     }
00309   }
00310 
00311   // The file does not already exist, or the user declined to replace
00312   // an existing file.
00313   if (force && !interactive) {
00314     return suggested_dir;
00315   }
00316 
00317   if (find(dirs.begin(), dirs.end(), suggested_dir) == dirs.end()) {
00318     CVSSourceDirectory *result = ask_new(filename, suggested_dir);
00319     if (result != (CVSSourceDirectory *)NULL) {
00320       return result;
00321     }
00322   }
00323 
00324   // Ask the user where the damn thing should go.
00325   return ask_any(filename);
00326 }
00327 
00328 ////////////////////////////////////////////////////////////////////
00329 //     Function: CVSSourceTree::ask_existing
00330 //       Access: Private
00331 //  Description: Asks the user if he wants to replace an existing
00332 //               file.
00333 ////////////////////////////////////////////////////////////////////
00334 CVSSourceDirectory *CVSSourceTree::
00335 ask_existing(const string &filename, CVSSourceDirectory *dir) {
00336   while (true) {
00337     nout << filename << " found in tree at "
00338          << dir->get_path() + "/" + filename << ".\n";
00339     string result = prompt("Overwrite this file (y/n)? ");
00340     nassertr(!result.empty(), (CVSSourceDirectory *)NULL);
00341     if (result.size() == 1) {
00342       if (tolower(result[0]) == 'y') {
00343         return dir;
00344       } else if (tolower(result[0]) == 'n') {
00345         return NULL;
00346       }
00347     }
00348 
00349     nout << "*** Invalid response: " << result << "\n\n";
00350   }
00351 }
00352 
00353 ////////////////////////////////////////////////////////////////////
00354 //     Function: CVSSourceTree::ask_existing
00355 //       Access: Private
00356 //  Description: Asks the user which of several existing files he
00357 //               wants to replace.
00358 ////////////////////////////////////////////////////////////////////
00359 CVSSourceDirectory *CVSSourceTree::
00360 ask_existing(const string &filename, const CVSSourceTree::Directories &dirs,
00361              CVSSourceDirectory *suggested_dir) {
00362   while (true) {
00363     nout << filename << " found in tree at more than one place:\n";
00364 
00365     bool any_suggested = false;
00366     for (int i = 0; i < (int)dirs.size(); i++) {
00367       nout << "  " << (i + 1) << ". "
00368            << dirs[i]->get_path() + "/" + filename << "\n";
00369       if (dirs[i] == suggested_dir) {
00370         any_suggested = true;
00371       }
00372     }
00373 
00374     int next_option = dirs.size() + 1;
00375     int suggested_option = -1;
00376 
00377     if (!any_suggested) {
00378       suggested_option = next_option;
00379       next_option++;
00380       nout << "\n" << suggested_option
00381            << ". create "
00382            << suggested_dir->get_path() + "/" + filename
00383            << "\n";
00384     }
00385 
00386     int other_option = next_option;
00387     nout << other_option << ". Other\n";
00388 
00389     string result = prompt("Choose an option: ");
00390     nassertr(!result.empty(), (CVSSourceDirectory *)NULL);
00391     const char *nptr = result.c_str();
00392     char *endptr;
00393     int option = strtol(nptr, &endptr, 10);
00394     if (*endptr == '\0') {
00395       if (option >= 1 && option <= (int)dirs.size()) {
00396         return dirs[option - 1];
00397 
00398       } else if (option == suggested_option) {
00399         return suggested_dir;
00400 
00401       } else if (option == other_option) {
00402         return NULL;
00403       }
00404     }
00405 
00406     nout << "*** Invalid response: " << result << "\n\n";
00407   }
00408 }
00409 
00410 ////////////////////////////////////////////////////////////////////
00411 //     Function: CVSSourceTree::ask_new
00412 //       Access: Private
00413 //  Description: Asks the user if he wants to replace an existing
00414 //               file.
00415 ////////////////////////////////////////////////////////////////////
00416 CVSSourceDirectory *CVSSourceTree::
00417 ask_new(const string &filename, CVSSourceDirectory *dir) {
00418   while (true) {
00419     nout << filename << " will be created in "
00420          << dir->get_path() << ".\n";
00421     string result = prompt("Create this file (y/n)? ");
00422     nassertr(!result.empty(), (CVSSourceDirectory *)NULL);
00423     if (result.size() == 1) {
00424       if (tolower(result[0]) == 'y') {
00425         return dir;
00426       } else if (tolower(result[0]) == 'n') {
00427         return NULL;
00428       }
00429     }
00430 
00431     nout << "*** Invalid response: " << result << "\n\n";
00432   }
00433 }
00434 
00435 ////////////////////////////////////////////////////////////////////
00436 //     Function: CVSSourceTree::ask_any
00437 //       Access: Private
00438 //  Description: Asks the user to type in the name of the directory in
00439 //               which to store the file.
00440 ////////////////////////////////////////////////////////////////////
00441 CVSSourceDirectory *CVSSourceTree::
00442 ask_any(const string &filename) {
00443   while (true) {
00444     string result =
00445       prompt("Enter the name of the directory to copy " + filename + " to: ");
00446     nassertr(!result.empty(), (CVSSourceDirectory *)NULL);
00447 
00448     // The user might enter a fully-qualified path to the directory,
00449     // or a relative path from the root (with or without the root's
00450     // dirname), or the dirname of the particular directory.
00451     CVSSourceDirectory *dir = find_directory(result);
00452     if (dir == (CVSSourceDirectory *)NULL) {
00453       dir = find_relpath(result);
00454     }
00455     if (dir == (CVSSourceDirectory *)NULL) {
00456       dir = find_dirname(result);
00457     }
00458 
00459     if (dir != (CVSSourceDirectory *)NULL) {
00460       return dir;
00461     }
00462 
00463     nout << "*** Not a valid directory name: " << result << "\n\n";
00464   }
00465 }
00466 
00467 ////////////////////////////////////////////////////////////////////
00468 //     Function: CVSSourceTree::prompt
00469 //       Access: Private
00470 //  Description: Issues a prompt to the user and waits for a typed
00471 //               response.  Returns the response (which will not be
00472 //               empty).
00473 ////////////////////////////////////////////////////////////////////
00474 string CVSSourceTree::
00475 prompt(const string &message) {
00476   nout << flush;
00477   while (true) {
00478     cerr << message << flush;
00479     string response;
00480     getline(cin, response);
00481 
00482     // Remove leading and trailing whitespace.
00483     size_t p = 0;
00484     while (p < response.length() && isspace(response[p])) {
00485       p++;
00486     }
00487 
00488     size_t q = response.length();
00489     while (q > p && isspace(response[q - 1])) {
00490       q--;
00491     }
00492 
00493     if (q > p) {
00494       return response.substr(p, q - p);
00495     }
00496   }
00497 }
00498 
00499 ////////////////////////////////////////////////////////////////////
00500 //     Function: CVSSourceTree::get_actual_fullpath
00501 //       Access: Private, Static
00502 //  Description: Determines the actual full path from the root to the
00503 //               named directory.
00504 ////////////////////////////////////////////////////////////////////
00505 Filename CVSSourceTree::
00506 get_actual_fullpath(const Filename &path) {
00507   Filename canon = path;
00508   canon.make_canonical();
00509   return canon;
00510 }
00511 
00512 
00513 ////////////////////////////////////////////////////////////////////
00514 //     Function: CVSSourceTree::get_start_fullpath
00515 //       Access: Private, Static
00516 //  Description: Returns the full path from the root to the directory
00517 //               in which the user started the program.
00518 ////////////////////////////////////////////////////////////////////
00519 Filename CVSSourceTree::
00520 get_start_fullpath() {
00521   if (!_got_start_fullpath) {
00522     Filename cwd = ExecutionEnvironment::get_cwd();
00523     _start_fullpath = cwd.to_os_specific();
00524   }
00525   return _start_fullpath;
00526 }

Generated on Fri May 2 03:17:03 2003 for Panda-Tool by doxygen1.3