00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "softCVS.h"
00016
00017 #include "pnotify.h"
00018 #include "multifile.h"
00019 #include "pystub.h"
00020
00021 #include <algorithm>
00022
00023
00024
00025
00026
00027
00028 SoftCVS::
00029 SoftCVS() {
00030 _cvs_binary = "cvs";
00031
00032 set_program_description
00033 ("softcvs is designed to prepare a directory hierarchy "
00034 "representing a SoftImage database for adding to CVS. "
00035 "First, it eliminates SoftImage's silly filename-based "
00036 "versioning system by renaming versioned filenames higher "
00037 "than 1-0 back to version 1-0. Then, it rolls up all the "
00038 "files for each scene except the texture images into a Panda "
00039 "multifile, which is added to CVS; the texture images are "
00040 "directly added to CVS where they are.\n\n"
00041
00042 "The reduction of hundreds of SoftImage files per scene down to one "
00043 "multifile and a handle of texture images should greatly improve "
00044 "the update and commit times of CVS.\n\n"
00045
00046 "You must run this from within the root of a SoftImage database "
00047 "directory; e.g. the directory that contains SCENES, PICTURES, MODELS, "
00048 "and so on.");
00049
00050 clear_runlines();
00051 add_runline("[opts]");
00052
00053 add_option
00054 ("nc", "", 80,
00055 "Do not attempt to add newly-created files to CVS. The default "
00056 "is to add them.",
00057 &SoftCVS::dispatch_none, &_no_cvs);
00058
00059 add_option
00060 ("cvs", "cvs_binary", 80,
00061 "Specify how to run the cvs program for adding newly-created files. "
00062 "The default is simply \"cvs\".",
00063 &SoftCVS::dispatch_string, NULL, &_cvs_binary);
00064 }
00065
00066
00067
00068
00069
00070
00071
00072 void SoftCVS::
00073 run() {
00074
00075
00076 Filename scenes = "SCENES/.";
00077 if (!scenes.exists()) {
00078 nout << "No SCENES directory found; you are not in the root of a "
00079 "SoftImage database.\n";
00080 exit(1);
00081 }
00082
00083
00084
00085 Filename cvs_entries = "CVS/Entries";
00086 if (!_no_cvs && !cvs_entries.exists()) {
00087 nout << "You do not appear to be within a CVS-controlled source "
00088 "directory.\n";
00089 exit(1);
00090 }
00091
00092
00093 traverse_root();
00094
00095
00096 collapse_scene_files();
00097
00098
00099
00100 if (!get_scenes()) {
00101 exit(1);
00102 }
00103
00104
00105
00106 remove_unused_elements();
00107
00108
00109 if (!_no_cvs) {
00110 cvs_add_or_remove("remove", _cvs_remove);
00111 cvs_add_or_remove("add -kb", _cvs_add);
00112 }
00113 }
00114
00115
00116
00117
00118
00119
00120
00121 void SoftCVS::
00122 traverse_root() {
00123 Filename root(".");
00124
00125
00126 vector_string subdirs;
00127 if (!root.scan_directory(subdirs)) {
00128 nout << "Unable to scan directory.\n";
00129 return;
00130 }
00131
00132 vector_string::const_iterator di;
00133 for (di = subdirs.begin(); di != subdirs.end(); ++di) {
00134 Filename subdir = (*di);
00135 if (subdir.is_directory() && subdir != "CVS") {
00136 traverse_subdir(subdir);
00137 }
00138 }
00139 }
00140
00141
00142
00143
00144
00145
00146
00147 void SoftCVS::
00148 traverse_subdir(const Filename &directory) {
00149
00150 vector_string files;
00151 if (!directory.scan_directory(files)) {
00152 nout << "Unable to scan directory " << directory << "\n";
00153 return;
00154 }
00155
00156
00157
00158 pset<string> cvs_elements;
00159 bool in_cvs = false;
00160 if (!_no_cvs) {
00161 in_cvs = scan_cvs(directory, cvs_elements);
00162 }
00163
00164 bool is_scenes = false;
00165 bool keep_all = false;
00166 bool wants_cvs = false;
00167
00168
00169
00170 string dirname = directory.get_basename();
00171 if (dirname == "SCENES") {
00172 is_scenes = true;
00173
00174 } else if (dirname == "CAMERAS") {
00175
00176
00177
00178 return;
00179
00180 } else if (dirname == "PICTURES") {
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194 keep_all = true;
00195 wants_cvs = !_no_cvs;
00196 }
00197
00198 vector_string::const_iterator fi;
00199 for (fi = files.begin(); fi != files.end(); ++fi) {
00200 const string &filename = (*fi);
00201 if (filename == "CVS") {
00202
00203
00204 } else if (filename == "Chapter.rsrc") {
00205
00206
00207 _global_files.push_back(Filename(directory, filename));
00208
00209 } else {
00210 SoftFilename soft(directory, filename);
00211
00212 if (cvs_elements.count(filename) != 0) {
00213
00214 soft.set_in_cvs(true);
00215 }
00216
00217 if (keep_all) {
00218 soft.increment_use_count();
00219 }
00220 if (wants_cvs && !in_cvs) {
00221
00222 cvs_add(directory);
00223 in_cvs = true;
00224 }
00225 soft.set_wants_cvs(wants_cvs);
00226
00227 if (is_scenes && soft.has_version() && soft.get_extension() == ".dsc") {
00228 _scene_files.push_back(soft);
00229 } else {
00230 _element_files.insert(soft);
00231 }
00232 }
00233 }
00234 }
00235
00236
00237
00238
00239
00240
00241
00242
00243 void SoftCVS::
00244 collapse_scene_files() {
00245
00246
00247
00248 SceneFiles versions;
00249 versions.swap(_scene_files);
00250
00251
00252
00253 sort(versions.begin(), versions.end());
00254
00255 SceneFiles::iterator vi;
00256 vi = versions.begin();
00257 while (vi != versions.end()) {
00258 SoftFilename &file = (*vi);
00259
00260 if (!file.is_1_0()) {
00261
00262
00263 SceneFiles::iterator start_vi;
00264 start_vi = vi;
00265 while (vi != versions.end() && (*vi).is_same_file(file)) {
00266 ++vi;
00267 }
00268
00269 rename_file(start_vi, vi);
00270
00271 } else {
00272 ++vi;
00273 }
00274
00275 file.make_1_0();
00276 _scene_files.push_back(file);
00277 }
00278 }
00279
00280
00281
00282
00283
00284
00285
00286
00287 bool SoftCVS::
00288 get_scenes() {
00289 bool okflag = true;
00290
00291
00292
00293 pset<string> cvs_elements;
00294 if (!_no_cvs) {
00295 scan_cvs(".", cvs_elements);
00296 }
00297
00298 SceneFiles::const_iterator vi;
00299 for (vi = _scene_files.begin(); vi != _scene_files.end(); ++vi) {
00300 const SoftFilename &sf = (*vi);
00301 Filename file(sf.get_dirname(), sf.get_filename());
00302
00303 file.set_text();
00304 ifstream in;
00305 if (!file.open_read(in)) {
00306 nout << "Unable to read " << file << "\n";
00307 } else {
00308 nout << "Scanning " << file << "\n";
00309
00310 Multifile multifile;
00311 Filename multifile_name = sf.get_base() + "mf";
00312
00313 if (!multifile.open_read_write(multifile_name)) {
00314 nout << "Unable to open " << multifile_name << " for updating.\n";
00315 okflag = false;
00316
00317 } else {
00318 if (!scan_scene_file(in, multifile)) {
00319 okflag = false;
00320 }
00321
00322
00323
00324
00325 vector_string::const_iterator gi;
00326 for (gi = _global_files.begin(); gi != _global_files.end(); ++gi) {
00327 if (multifile.update_subfile((*gi), (*gi), 0).empty()) {
00328 nout << "Unable to add " << (*gi) << "\n";
00329 okflag = false;
00330 }
00331 }
00332
00333
00334 if (multifile.update_subfile(file, file, 6).empty()) {
00335 nout << "Unable to add " << file << "\n";
00336 okflag = false;
00337 }
00338
00339 bool flushed = false;
00340 if (multifile.needs_repack()) {
00341 flushed = multifile.repack();
00342 } else {
00343 flushed = multifile.flush();
00344 }
00345 if (!flushed) {
00346 nout << "Failed to write " << multifile_name << ".\n";
00347 okflag = false;
00348 } else {
00349 nout << "Wrote " << multifile_name << ".\n";
00350
00351 if (!_no_cvs && cvs_elements.count(multifile_name) == 0) {
00352
00353 _cvs_add.push_back(multifile_name);
00354 }
00355 }
00356 }
00357 }
00358 }
00359
00360 return okflag;
00361 }
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371 void SoftCVS::
00372 remove_unused_elements() {
00373 ElementFiles::const_iterator fi;
00374 for (fi = _element_files.begin(); fi != _element_files.end(); ++fi) {
00375 const SoftFilename &sf = (*fi);
00376 Filename file(sf.get_dirname(), sf.get_filename());
00377
00378 if (sf.get_use_count() == 0) {
00379 nout << file << " is unused.\n";
00380
00381 if (!file.unlink()) {
00382 nout << "Unable to remove " << file << ".\n";
00383
00384 } else if (sf.get_in_cvs()) {
00385 _cvs_remove.push_back(file);
00386 }
00387
00388 } else if (sf.get_wants_cvs() && !sf.get_in_cvs()) {
00389 _cvs_add.push_back(file);
00390 }
00391 }
00392 }
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403 bool SoftCVS::
00404 rename_file(SoftCVS::SceneFiles::iterator begin,
00405 SoftCVS::SceneFiles::iterator end) {
00406 int length = end - begin;
00407 nassertr(length > 0, false);
00408
00409 SoftFilename &orig = (*begin);
00410
00411 string dirname = orig.get_dirname();
00412 string source_filename = orig.get_filename();
00413 string dest_filename = orig.get_1_0_filename();
00414
00415 if (length > 2) {
00416 nout << source_filename << " supercedes:\n";
00417 SceneFiles::const_iterator p;
00418 for (p = begin + 1; p != end; ++p) {
00419 nout << " " << (*p).get_filename() << "\n";
00420 }
00421
00422 } else if (length == 2) {
00423 nout << source_filename << " supercedes "
00424 << (*(begin + 1)).get_filename() << ".\n";
00425
00426 } else {
00427 nout << source_filename << " renamed.\n";
00428 }
00429
00430
00431
00432 SceneFiles::const_iterator p;
00433 for (p = begin + 1; p != end; ++p) {
00434 Filename file((*p).get_dirname(), (*p).get_filename());
00435 if (!file.unlink()) {
00436 nout << "Unable to remove " << file << ".\n";
00437 }
00438 }
00439
00440
00441 Filename source(dirname, source_filename);
00442 Filename dest(dirname, dest_filename);
00443
00444 if (!source.rename_to(dest)) {
00445 nout << "Unable to rename " << source << " to " << dest_filename << ".\n";
00446 exit(1);
00447 }
00448
00449 return true;
00450 }
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460 bool SoftCVS::
00461 scan_cvs(const string &dirname, pset<string> &cvs_elements) {
00462 Filename cvs_entries = dirname + "/CVS/Entries";
00463 if (!cvs_entries.exists()) {
00464 return false;
00465 }
00466
00467 ifstream in;
00468 cvs_entries.set_text();
00469 if (!cvs_entries.open_read(in)) {
00470 nout << "Unable to read CVS directory.\n";
00471 return true;
00472 }
00473
00474 string line;
00475 getline(in, line);
00476 while (!in.fail() && !in.eof()) {
00477 if (!line.empty() && line[0] == '/') {
00478 size_t slash = line.find('/', 1);
00479 if (slash != string::npos) {
00480 string filename = line.substr(1, slash - 1);
00481
00482 if (line.substr(slash + 1, 2) == "-1") {
00483
00484
00485
00486 } else {
00487 cvs_elements.insert(filename);
00488 }
00489 }
00490 }
00491
00492 getline(in, line);
00493 }
00494
00495 return true;
00496 }
00497
00498
00499
00500
00501
00502
00503
00504
00505 bool SoftCVS::
00506 scan_scene_file(istream &in, Multifile &multifile) {
00507 bool okflag = true;
00508
00509 int c = in.get();
00510 while (!in.eof() && !in.fail()) {
00511
00512 while (isspace(c) && !in.eof() && !in.fail()) {
00513 c = in.get();
00514 }
00515
00516
00517 string word;
00518 while (!isspace(c) && !in.eof() && !in.fail()) {
00519 word += c;
00520 c = in.get();
00521 }
00522
00523 if (!word.empty()) {
00524 SoftFilename v("", word);
00525
00526
00527 pair<ElementFiles::iterator, ElementFiles::iterator> range;
00528 range = _element_files.equal_range(v);
00529
00530 ElementFiles::iterator ei;
00531 for (ei = range.first; ei != range.second; ++ei) {
00532
00533
00534
00535 SoftFilename &sf = (SoftFilename &)(*ei);
00536 sf.increment_use_count();
00537
00538 Filename file(sf.get_dirname(), sf.get_filename());
00539 if (multifile.update_subfile(file, file, 6).empty()) {
00540 nout << "Unable to add " << file << "\n";
00541 okflag = false;
00542 }
00543 }
00544 }
00545 }
00546
00547 return okflag;
00548 }
00549
00550
00551
00552
00553
00554
00555
00556
00557 bool SoftCVS::
00558 cvs_add(const string &path) {
00559 string command = _cvs_binary + " add -kb " + path;
00560 nout << command << "\n";
00561 int result = system(command.c_str());
00562
00563 if (result != 0) {
00564 nout << "Failure invoking cvs.\n";
00565 return false;
00566 }
00567 return true;
00568 }
00569
00570
00571
00572
00573
00574
00575
00576
00577 bool SoftCVS::
00578 cvs_add_or_remove(const string &cvs_command, const vector_string &paths) {
00579 static const int max_command = 4096;
00580
00581 if (!paths.empty()) {
00582 string command = _cvs_binary + " " + cvs_command;
00583 vector_string::const_iterator pi;
00584 pi = paths.begin();
00585 while (pi != paths.end()) {
00586 const string &path = (*pi);
00587
00588 if ((int)command.length() + 1 + (int)path.length() >= max_command) {
00589
00590 nout << command << "\n";
00591 int result = system(command.c_str());
00592
00593 if (result != 0) {
00594 nout << "Failure invoking cvs.\n";
00595 return false;
00596 }
00597
00598 command = _cvs_binary + " " + cvs_command;
00599 }
00600
00601 command += ' ';
00602 command += path;
00603
00604 ++pi;
00605 }
00606 nout << command << "\n";
00607 int result = system(command.c_str());
00608
00609 if (result != 0) {
00610 nout << "Failure invoking cvs.\n";
00611 return false;
00612 }
00613 }
00614 return true;
00615 }
00616
00617
00618 int main(int argc, char *argv[]) {
00619
00620 pystub();
00621
00622 SoftCVS prog;
00623 prog.parse_command_line(argc, argv);
00624 prog.run();
00625 return 0;
00626 }