00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "softCVS.h"
00020
00021 #include <notify.h>
00022
00023 #include <algorithm>
00024
00025
00026
00027
00028
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
00067
00068
00069
00070 void SoftCVS::
00071 run() {
00072
00073
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
00082
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
00091 traverse_root();
00092
00093
00094 collapse_scene_files();
00095
00096
00097
00098 count_references();
00099
00100
00101
00102 remove_unused_elements();
00103
00104
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
00113
00114
00115
00116
00117 void SoftCVS::
00118 traverse_root() {
00119 Filename root(".");
00120
00121
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
00139
00140
00141
00142
00143 void SoftCVS::
00144 traverse_subdir(const Filename &directory) {
00145
00146 vector_string files;
00147 if (!directory.scan_directory(files)) {
00148 nout << "Unable to scan directory " << directory << "\n";
00149 return;
00150 }
00151
00152
00153
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
00164
00165 string dirname = directory.get_basename();
00166 if (dirname == "SCENES") {
00167 is_scenes = true;
00168
00169 } else if (dirname == "PICTURES") {
00170
00171
00172
00173
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
00182
00183 } else if (filename == "Chapter.rsrc") {
00184
00185
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
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
00213
00214
00215
00216
00217
00218 void SoftCVS::
00219 collapse_scene_files() {
00220
00221
00222
00223 SceneFiles versions;
00224 versions.swap(_scene_files);
00225
00226
00227
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
00237
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
00257
00258
00259
00260
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
00286
00287
00288
00289
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
00317
00318
00319
00320
00321
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
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
00363 cvs_has_1_0 = true;
00364 } else {
00365 _cvs_remove.push_back(file);
00366 }
00367 }
00368 }
00369 }
00370
00371
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
00382 _cvs_remove.push_back(source);
00383 }
00384
00385 if (!cvs_has_1_0) {
00386
00387 _cvs_add.push_back(dest);
00388 }
00389
00390 orig.set_in_cvs(true);
00391
00392 return true;
00393 }
00394
00395
00396
00397
00398
00399
00400
00401
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
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
00430
00431
00432 } else {
00433 cvs_elements.insert(filename);
00434 }
00435 }
00436 }
00437
00438 getline(in, line);
00439 }
00440
00441 return true;
00442 }
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452 void SoftCVS::
00453 scan_scene_file(istream &in) {
00454 int c = in.get();
00455 while (!in.eof() && !in.fail()) {
00456
00457 while (isspace(c) && !in.eof() && !in.fail()) {
00458 c = in.get();
00459 }
00460
00461
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
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
00478
00479
00480 SoftFilename &file = (SoftFilename &)(*ei);
00481 file.increment_use_count();
00482 }
00483 }
00484 }
00485 }
00486
00487
00488
00489
00490
00491
00492
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
00509
00510
00511
00512
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
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 }