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

pandatool/src/softprogs/softCVS.cxx

Go to the documentation of this file.
00001 // Filename: softCVS.cxx
00002 // Created by:  drose (10Nov00)
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 "softCVS.h"
00020 
00021 #include <notify.h>
00022 
00023 #include <algorithm>
00024 
00025 ////////////////////////////////////////////////////////////////////
00026 //     Function: SoftCVS::Constructor
00027 //       Access: Public
00028 //  Description:
00029 ////////////////////////////////////////////////////////////////////
00030 SoftCVS::
00031 SoftCVS() {
00032   _cvs_binary = "cvs";
00033 
00034   set_program_description
00035     ("softcvs scrubs over a SoftImage database that was recently copied "
00036      "into a CVS-controlled directory and prepares it for cvs updating.  "
00037      "It eliminates SoftImage's silly filename-based versioning system by "
00038      "renaming versioned filenames higher than 1-0 back to version 1-0 "
00039      "(thus overwriting the previous file version 1-0).  This allows CVS "
00040      "to manage the versioning rather than having to change the filename "
00041      "with each new version.  This program also automatically adds each "
00042      "new file to the CVS repository.\n\n"
00043 
00044      "You must run this from within the root of a SoftImage database "
00045      "directory; e.g. the directory that contains SCENES, PICTURES, MODELS, "
00046      "and so on.");
00047 
00048   clear_runlines();
00049   add_runline("[opts]");
00050 
00051   add_option
00052     ("nc", "", 80,
00053      "Do not attempt to add newly-created files to CVS.  The default "
00054      "is to add them.",
00055      &SoftCVS::dispatch_none, &_no_cvs);
00056 
00057   add_option
00058     ("cvs", "cvs_binary", 80,
00059      "Specify how to run the cvs program for adding newly-created files.  "
00060      "The default is simply \"cvs\".",
00061      &SoftCVS::dispatch_string, NULL, &_cvs_binary);
00062 }
00063 
00064 
00065 ////////////////////////////////////////////////////////////////////
00066 //     Function: SoftCVS::run
00067 //       Access: Public
00068 //  Description:
00069 ////////////////////////////////////////////////////////////////////
00070 void SoftCVS::
00071 run() {
00072   // First, check for the scenes directory.  If it doesn't exist, we
00073   // must not be in the root of a soft database.
00074   Filename scenes = "SCENES/.";
00075   if (!scenes.exists()) {
00076     nout << "No SCENES directory found; you are not in the root of a "
00077       "SoftImage database.\n";
00078     exit(1);
00079   }
00080 
00081   // Also, if we're expecting to use CVS, make sure the CVS directory
00082   // exists.
00083   Filename cvs_entries = "CVS/Entries";
00084   if (!_no_cvs && !cvs_entries.exists()) {
00085     nout << "You do not appear to be within a CVS-controlled source "
00086       "directory.\n";
00087     exit(1);
00088   }
00089 
00090   // Scan all the files in the database.
00091   traverse_root();
00092 
00093   // Collapse out the higher-versioned scene files.
00094   collapse_scene_files();
00095 
00096   // Now determine which element files are actually referenced by at
00097   // least one of the scene files.
00098   count_references();
00099 
00100   // Finally, remove all the element files that are no longer
00101   // referenced by any scenes.
00102   remove_unused_elements();
00103 
00104   // Now do all the cvs adding and removing we need.
00105   if (!_no_cvs) {
00106     cvs_add_or_remove("remove", _cvs_remove);
00107     cvs_add_or_remove("add -kb", _cvs_add);
00108   }
00109 }
00110 
00111 ////////////////////////////////////////////////////////////////////
00112 //     Function: SoftCVS::traverse_root
00113 //       Access: Private
00114 //  Description: Reads all of the toplevel directory names,
00115 //               e.g. SCENES, MATERIALS, etc., and traverses them.
00116 ////////////////////////////////////////////////////////////////////
00117 void SoftCVS::
00118 traverse_root() {
00119   Filename root(".");
00120 
00121   // Get the list of subdirectories.
00122   vector_string subdirs;
00123   if (!root.scan_directory(subdirs)) {
00124     nout << "Unable to scan directory.\n";
00125     return;
00126   }
00127 
00128   vector_string::const_iterator di;
00129   for (di = subdirs.begin(); di != subdirs.end(); ++di) {
00130     Filename subdir = (*di);
00131     if (subdir.is_directory() && subdir != "CVS") {
00132       traverse_subdir(subdir);
00133     }
00134   }
00135 }
00136 
00137 ////////////////////////////////////////////////////////////////////
00138 //     Function: SoftCVS::traverse_subdir
00139 //       Access: Private
00140 //  Description: Reads the directory indicated by prefix and
00141 //               identifies all of the SoftImage files stored there.
00142 ////////////////////////////////////////////////////////////////////
00143 void SoftCVS::
00144 traverse_subdir(const Filename &directory) {
00145   // Get the list of files in the directory.
00146   vector_string files;
00147   if (!directory.scan_directory(files)) {
00148     nout << "Unable to scan directory " << directory << "\n";
00149     return;
00150   }
00151 
00152   // We need to know the set of files in this directory that are CVS
00153   // elements.
00154   pset<string> cvs_elements;
00155   bool in_cvs = false;
00156   if (!_no_cvs) {
00157     in_cvs = scan_cvs(directory, cvs_elements);
00158   }
00159 
00160   bool is_scenes = false;
00161   bool keep_all = false;
00162 
00163   // Now make some special-case behavior based on the particular
00164   // SoftImage subdirectory we're in.
00165   string dirname = directory.get_basename();
00166   if (dirname == "SCENES") {
00167     is_scenes = true;
00168 
00169   } else if (dirname == "PICTURES") {
00170     // In the pictures directory, we must keep everything, since the
00171     // scene files don't explicitly reference these but they're still
00172     // important.  Textures that are no longer used will pile up; we
00173     // leave this is as the user's problem.
00174     keep_all = true;
00175   }
00176 
00177   vector_string::const_iterator fi;
00178   for (fi = files.begin(); fi != files.end(); ++fi) {
00179     const string &filename = (*fi);
00180     if (filename == "CVS") {
00181       // This special filename is not to be considered.
00182 
00183     } else if (filename == "Chapter.rsrc") {
00184       // This special filename should not be considered, except to add
00185       // it to CVS.
00186       if (in_cvs && cvs_elements.count(filename) == 0) {
00187         _cvs_add.push_back(Filename(directory, filename));
00188       }
00189 
00190     } else {
00191       SoftFilename soft(directory, filename);
00192 
00193       if (in_cvs && cvs_elements.count(filename) != 0) {
00194         // This file is known to be in CVS.
00195         soft.set_in_cvs(true);
00196       }
00197 
00198       if (keep_all) {
00199         soft.increment_use_count();
00200       }
00201 
00202       if (is_scenes && soft.has_version() && soft.get_extension() == ".dsc") {
00203         _scene_files.push_back(soft);
00204       } else {
00205         _element_files.insert(soft);
00206       }
00207     }
00208   }
00209 }
00210 
00211 ////////////////////////////////////////////////////////////////////
00212 //     Function: SoftCVS::collapse_scene_files
00213 //       Access: Private
00214 //  Description: Walks through the list of scene files found, and
00215 //               renames the higher-versioned ones to version 1-0,
00216 //               removing the intervening versions.
00217 ////////////////////////////////////////////////////////////////////
00218 void SoftCVS::
00219 collapse_scene_files() {
00220   // Get a copy of the scene files vector so we can modify it.  Also
00221   // empty out the _scene_files at the same time so we can fill it up
00222   // again.
00223   SceneFiles versions;
00224   versions.swap(_scene_files);
00225 
00226   // And sort them into order so we can easily compare higher and
00227   // lower versions.
00228   sort(versions.begin(), versions.end());
00229 
00230   SceneFiles::iterator vi;
00231   vi = versions.begin();
00232   while (vi != versions.end()) {
00233     SoftFilename &file = (*vi);
00234 
00235     if (!file.is_1_0()) {
00236       // Here's a file that needs to be renamed.  But first, identify
00237       // all the other versions of the same file.
00238       SceneFiles::iterator start_vi;
00239       start_vi = vi;
00240       while (vi != versions.end() && (*vi).is_same_file(file)) {
00241         ++vi;
00242       }
00243 
00244       rename_file(start_vi, vi);
00245 
00246     } else {
00247       ++vi;
00248     }
00249 
00250     file.make_1_0();
00251     _scene_files.push_back(file);
00252   }
00253 }
00254 
00255 ////////////////////////////////////////////////////////////////////
00256 //     Function: SoftCVS::count_references
00257 //       Access: Private
00258 //  Description: Walks through the list of scene files and looks for
00259 //               the set of element files referenced by the scene
00260 //               file.  Also add the scene files to CVS if warranted.
00261 ////////////////////////////////////////////////////////////////////
00262 void SoftCVS::
00263 count_references() {
00264   SceneFiles::const_iterator vi;
00265   for (vi = _scene_files.begin(); vi != _scene_files.end(); ++vi) {
00266     const SoftFilename &sf = (*vi);
00267     Filename file(sf.get_dirname(), sf.get_filename());
00268     if (!sf.get_in_cvs()) {
00269       _cvs_add.push_back(file);
00270     }
00271 
00272     file.set_text();
00273     ifstream in;
00274     if (!file.open_read(in)) {
00275       nout << "Unable to read " << file << "\n";
00276     } else {
00277       nout << "Scanning " << file << "\n";
00278       scan_scene_file(in);
00279     }
00280   }
00281 }
00282 
00283 
00284 ////////////////////////////////////////////////////////////////////
00285 //     Function: SoftCVS::remove_unused_elements
00286 //       Access: Private
00287 //  Description: Remove all the element files that weren't referenced
00288 //               by any scene file.  Also plan to cvs add all those
00289 //               that were referenced.
00290 ////////////////////////////////////////////////////////////////////
00291 void SoftCVS::
00292 remove_unused_elements() {
00293   ElementFiles::const_iterator fi;
00294   for (fi = _element_files.begin(); fi != _element_files.end(); ++fi) {
00295     const SoftFilename &sf = (*fi);
00296     Filename file(sf.get_dirname(), sf.get_filename());
00297 
00298     if (sf.get_use_count() == 0) {
00299       nout << file << " is unused.\n";
00300 
00301       if (!file.unlink()) {
00302         nout << "Unable to remove " << file << ".\n";
00303 
00304       } else if (sf.get_in_cvs()) {
00305         _cvs_remove.push_back(file);
00306       }
00307 
00308     } else if (!sf.get_in_cvs()) {
00309       _cvs_add.push_back(file);
00310     }
00311   }
00312 }
00313 
00314 
00315 ////////////////////////////////////////////////////////////////////
00316 //     Function: SoftCVS::rename_file
00317 //       Access: Private
00318 //  Description: Renames the first file in the indicated list to a
00319 //               version 1-0 filename, superceding all the other files
00320 //               in the list.  Returns true if the file is renamed,
00321 //               false otherwise.
00322 ////////////////////////////////////////////////////////////////////
00323 bool SoftCVS::
00324 rename_file(SoftCVS::SceneFiles::iterator begin,
00325             SoftCVS::SceneFiles::iterator end) {
00326   int length = end - begin;
00327   nassertr(length > 0, false);
00328 
00329   SoftFilename &orig = (*begin);
00330 
00331   string dirname = orig.get_dirname();
00332   string source_filename = orig.get_filename();
00333   string dest_filename = orig.get_1_0_filename();
00334 
00335   if (length > 2) {
00336     nout << source_filename << " supercedes:\n";
00337     SceneFiles::const_iterator p;
00338     for (p = begin + 1; p != end; ++p) {
00339       nout << "  " << (*p).get_filename() << "\n";
00340     }
00341 
00342   } else if (length == 2) {
00343     nout << source_filename << " supercedes "
00344          << (*(begin + 1)).get_filename() << ".\n";
00345 
00346   } else {
00347     nout << source_filename << " renamed.\n";
00348   }
00349 
00350   // Now remove all of the "wrong" files.
00351 
00352   bool cvs_has_1_0 = false;
00353 
00354   SceneFiles::const_iterator p;
00355   for (p = begin + 1; p != end; ++p) {
00356     Filename file((*p).get_dirname(), (*p).get_filename());
00357     if (!file.unlink()) {
00358       nout << "Unable to remove " << file << ".\n";
00359     } else {
00360       if ((*p).get_in_cvs()) {
00361         if ((*p).is_1_0()) {
00362           // We don't cvs remove the 1.0 version.
00363           cvs_has_1_0 = true;
00364         } else {
00365           _cvs_remove.push_back(file);
00366         }
00367       }
00368     }
00369   }
00370 
00371   // And rename the good one.
00372   Filename source(dirname, source_filename);
00373   Filename dest(dirname, dest_filename);
00374 
00375   if (!source.rename_to(dest)) {
00376     nout << "Unable to rename " << source << " to " << dest_filename << ".\n";
00377     exit(1);
00378   }
00379 
00380   if (orig.get_in_cvs()) {
00381     // We do have to cvs remove the old one.
00382     _cvs_remove.push_back(source);
00383   }
00384 
00385   if (!cvs_has_1_0) {
00386     // And we have to cvs add the new one.
00387     _cvs_add.push_back(dest);
00388   }
00389 
00390   orig.set_in_cvs(true);
00391 
00392   return true;
00393 }
00394 
00395 ////////////////////////////////////////////////////////////////////
00396 //     Function: SoftCVS::scan_cvs
00397 //       Access: Private
00398 //  Description: Scans the CVS repository in the indicated directory
00399 //               to determine which files are already versioned
00400 //               elements.  Returns true if the directory is
00401 //               CVS-controlled, false otherwise.
00402 ////////////////////////////////////////////////////////////////////
00403 bool SoftCVS::
00404 scan_cvs(const string &dirname, pset<string> &cvs_elements) {
00405   Filename cvs_entries = dirname + "/CVS/Entries";
00406   if (!cvs_entries.exists()) {
00407     // Try to CVSify the directory.
00408     if (!cvs_add(dirname)) {
00409       return false;
00410     }
00411   }
00412 
00413   ifstream in;
00414   cvs_entries.set_text();
00415   if (!cvs_entries.open_read(in)) {
00416     nout << "Unable to read CVS directory.\n";
00417     return true;
00418   }
00419 
00420   string line;
00421   getline(in, line);
00422   while (!in.fail() && !in.eof()) {
00423     if (!line.empty() && line[0] == '/') {
00424       size_t slash = line.find('/', 1);
00425       if (slash != string::npos) {
00426         string filename = line.substr(1, slash - 1);
00427 
00428         if (line.substr(slash + 1, 2) == "-1") {
00429           // If the first number after the slash is -1, the file used
00430           // to be here but was recently cvs removed.  It counts as no
00431           // longer being an element.
00432         } else {
00433           cvs_elements.insert(filename);
00434         }
00435       }
00436     }
00437 
00438     getline(in, line);
00439   }
00440 
00441   return true;
00442 }
00443 
00444 ////////////////////////////////////////////////////////////////////
00445 //     Function: SoftCVS::scan_scene_file
00446 //       Access: Private
00447 //  Description: Copies a scene file from the input stream to the
00448 //               output stream, looking for references to element
00449 //               files.  For each reference found, increments the
00450 //               appropriate element file's reference count.
00451 ////////////////////////////////////////////////////////////////////
00452 void SoftCVS::
00453 scan_scene_file(istream &in) {
00454   int c = in.get();
00455   while (!in.eof() && !in.fail()) {
00456     // Skip whitespace.
00457     while (isspace(c) && !in.eof() && !in.fail()) {
00458       c = in.get();
00459     }
00460 
00461     // Now begin a word.
00462     string word;
00463     while (!isspace(c) && !in.eof() && !in.fail()) {
00464       word += c;
00465       c = in.get();
00466     }
00467 
00468     if (!word.empty()) {
00469       SoftFilename v("", word);
00470 
00471       // Increment the use count on all matching elements of the multiset.
00472       pair<ElementFiles::iterator, ElementFiles::iterator> range;
00473       range = _element_files.equal_range(v);
00474 
00475       ElementFiles::iterator ei;
00476       for (ei = range.first; ei != range.second; ++ei) {
00477         // We cheat and get a non-const reference to the filename out
00478         // of the set.  We can safely do this because incrementing the
00479         // use count won't change its position in the set.
00480         SoftFilename &file = (SoftFilename &)(*ei);
00481         file.increment_use_count();
00482       }
00483     }
00484   }
00485 }
00486 
00487 ////////////////////////////////////////////////////////////////////
00488 //     Function: SoftCVS::cvs_add
00489 //       Access: Private
00490 //  Description: Invokes CVS to add just the named file to the
00491 //               repository.  Returns true on success, false on
00492 //               failure.
00493 ////////////////////////////////////////////////////////////////////
00494 bool SoftCVS::
00495 cvs_add(const string &path) {
00496   string command = _cvs_binary + " add -kb " + path;
00497   nout << command << "\n";
00498   int result = system(command.c_str());
00499 
00500   if (result != 0) {
00501     nout << "Failure invoking cvs.\n";
00502     return false;
00503   }
00504   return true;
00505 }
00506 
00507 ////////////////////////////////////////////////////////////////////
00508 //     Function: SoftCVS::cvs_add_or_remove
00509 //       Access: Private
00510 //  Description: Invokes CVS to add (or remove) all of the files in
00511 //               the indicated vector.  Returns true on success, false
00512 //               on failure.
00513 ////////////////////////////////////////////////////////////////////
00514 bool SoftCVS::
00515 cvs_add_or_remove(const string &cvs_command, const vector_string &paths) {
00516   static const int max_command = 4096;
00517 
00518   if (!paths.empty()) {
00519     string command = _cvs_binary + " " + cvs_command;
00520     vector_string::const_iterator pi;
00521     pi = paths.begin();
00522     while (pi != paths.end()) {
00523       const string &path = (*pi);
00524 
00525       if ((int)command.length() + 1 + (int)path.length() >= max_command) {
00526         // Fire off the command now.
00527         nout << command << "\n";
00528         int result = system(command.c_str());
00529 
00530         if (result != 0) {
00531           nout << "Failure invoking cvs.\n";
00532           return false;
00533         }
00534 
00535         command = _cvs_binary + " " + cvs_command;
00536       }
00537 
00538       command += ' ';
00539       command += path;
00540 
00541       ++pi;
00542     }
00543     nout << command << "\n";
00544     int result = system(command.c_str());
00545 
00546     if (result != 0) {
00547       nout << "Failure invoking cvs.\n";
00548       return false;
00549     }
00550   }
00551   return true;
00552 }
00553 
00554 int main(int argc, char *argv[]) {
00555   SoftCVS prog;
00556   prog.parse_command_line(argc, argv);
00557   prog.run();
00558   return 0;
00559 }

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