00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "cvsSourceTree.h"
00016 #include "cvsSourceDirectory.h"
00017
00018 #include "filename.h"
00019 #include "executionEnvironment.h"
00020 #include "pnotify.h"
00021 #include "string_utils.h"
00022
00023 #include <algorithm>
00024 #include <ctype.h>
00025 #include <stdio.h>
00026 #include <errno.h>
00027
00028 #ifdef WIN32_VC
00029 #include <direct.h>
00030 #endif
00031
00032 bool CVSSourceTree::_got_start_fullpath = false;
00033 Filename CVSSourceTree::_start_fullpath;
00034
00035
00036
00037
00038
00039
00040 CVSSourceTree::
00041 CVSSourceTree() {
00042 _root = (CVSSourceDirectory *)NULL;
00043 _got_root_fullpath = false;
00044 }
00045
00046
00047
00048
00049
00050
00051 CVSSourceTree::
00052 ~CVSSourceTree() {
00053 if (_root != (CVSSourceDirectory *)NULL) {
00054 delete _root;
00055 }
00056 }
00057
00058
00059
00060
00061
00062
00063
00064
00065 void CVSSourceTree::
00066 set_root(const Filename &root_path) {
00067 nassertv(_path.empty());
00068 _path = root_path;
00069 }
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079 bool CVSSourceTree::
00080 scan(const Filename &key_filename) {
00081 nassertr(_root == (CVSSourceDirectory *)NULL, false);
00082 Filename root_fullpath = get_root_fullpath();
00083 _root = new CVSSourceDirectory(this, NULL, root_fullpath.get_basename());
00084 return _root->scan(_path, key_filename);
00085 }
00086
00087
00088
00089
00090
00091
00092 CVSSourceDirectory *CVSSourceTree::
00093 get_root() const {
00094 return _root;
00095 }
00096
00097
00098
00099
00100
00101
00102
00103
00104 CVSSourceDirectory *CVSSourceTree::
00105 find_directory(const Filename &path) {
00106 string root_fullpath = get_root_fullpath();
00107 string fullpath = get_actual_fullpath(path);
00108
00109
00110
00111 if (root_fullpath.length() > fullpath.length() ||
00112 cmp_nocase(fullpath.substr(0, root_fullpath.length()), root_fullpath) != 0) {
00113
00114 return (CVSSourceDirectory *)NULL;
00115 }
00116
00117
00118 Filename relpath = fullpath.substr(root_fullpath.length());
00119
00120 return _root->find_relpath(relpath);
00121 }
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131 CVSSourceDirectory *CVSSourceTree::
00132 find_relpath(const string &relpath) {
00133 CVSSourceDirectory *result = _root->find_relpath(relpath);
00134 if (result != (CVSSourceDirectory *)NULL) {
00135 return result;
00136 }
00137
00138
00139
00140 size_t slash = relpath.find('/');
00141 Filename first = relpath.substr(0, slash);
00142 Filename rest;
00143 if (slash != string::npos) {
00144 rest = relpath.substr(slash + 1);
00145 }
00146
00147 if (cmp_nocase(first, _root->get_dirname()) == 0) {
00148 return _root->find_relpath(rest);
00149 }
00150
00151 return (CVSSourceDirectory *)NULL;
00152 }
00153
00154
00155
00156
00157
00158
00159
00160
00161 CVSSourceDirectory *CVSSourceTree::
00162 find_dirname(const string &dirname) {
00163 return _root->find_dirname(dirname);
00164 }
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176 CVSSourceTree::FilePath CVSSourceTree::
00177 choose_directory(const string &basename, CVSSourceDirectory *suggested_dir,
00178 bool force, bool interactive) {
00179 static FilePaths empty_paths;
00180
00181 Basenames::const_iterator bi;
00182 bi = _basenames.find(downcase(basename));
00183 if (bi != _basenames.end()) {
00184
00185 const FilePaths &paths = (*bi).second;
00186
00187 return prompt_user(basename, suggested_dir, paths,
00188 force, interactive);
00189 }
00190
00191
00192 return prompt_user(basename, suggested_dir, empty_paths,
00193 force, interactive);
00194 }
00195
00196
00197
00198
00199
00200
00201
00202 Filename CVSSourceTree::
00203 get_root_fullpath() {
00204 nassertr(!_path.empty(), Filename());
00205 if (!_got_root_fullpath) {
00206 _root_fullpath = get_actual_fullpath(_path);
00207 _got_root_fullpath = true;
00208 }
00209 return _root_fullpath;
00210 }
00211
00212
00213
00214
00215
00216
00217
00218 Filename CVSSourceTree::
00219 get_root_dirname() const {
00220 nassertr(_root != (CVSSourceDirectory *)NULL, Filename());
00221 return _root->get_dirname();
00222 }
00223
00224
00225
00226
00227
00228
00229
00230
00231 void CVSSourceTree::
00232 add_file(const string &basename, CVSSourceDirectory *dir) {
00233 FilePath file_path(dir, basename);
00234 _basenames[downcase(basename)].push_back(file_path);
00235 }
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245 bool CVSSourceTree::
00246 temp_chdir(const Filename &path) {
00247
00248
00249 get_start_fullpath();
00250
00251 string os_path = path.to_os_specific();
00252 if (chdir(os_path.c_str()) < 0) {
00253 return false;
00254 }
00255 return true;
00256 }
00257
00258
00259
00260
00261
00262
00263
00264 void CVSSourceTree::
00265 restore_cwd() {
00266 Filename start_fullpath = get_start_fullpath();
00267 string os_path = start_fullpath.to_os_specific();
00268
00269 if (chdir(os_path.c_str()) < 0) {
00270
00271 perror(os_path.c_str());
00272 nout << "Can't continue, aborting.\n";
00273 exit(1);
00274 }
00275 }
00276
00277
00278
00279
00280
00281
00282
00283
00284 CVSSourceTree::FilePath CVSSourceTree::
00285 prompt_user(const string &basename, CVSSourceDirectory *suggested_dir,
00286 const CVSSourceTree::FilePaths &paths,
00287 bool force, bool interactive) {
00288 if (paths.size() == 1) {
00289
00290 if (!interactive) {
00291 return paths[0];
00292 }
00293 FilePath result = ask_existing(basename, paths[0]);
00294 if (result.is_valid()) {
00295 return result;
00296 }
00297
00298 } else if (paths.size() > 1) {
00299
00300 if (force && !interactive) {
00301 return paths[0];
00302 }
00303 FilePath result = ask_existing(basename, paths, suggested_dir);
00304 if (result.is_valid()) {
00305 return result;
00306 }
00307 }
00308
00309
00310
00311 if (force && !interactive) {
00312 return FilePath(suggested_dir, basename);
00313 }
00314
00315
00316
00317 bool found_dir = false;
00318 FilePaths::const_iterator pi;
00319 for (pi = paths.begin(); pi != paths.end(); ++pi) {
00320 if ((*pi)._dir == suggested_dir) {
00321 found_dir = true;
00322 break;
00323 }
00324 }
00325
00326 if (!found_dir) {
00327 FilePath result = ask_new(basename, suggested_dir);
00328 if (result.is_valid()) {
00329 return result;
00330 }
00331 }
00332
00333
00334 return ask_any(basename, paths);
00335 }
00336
00337
00338
00339
00340
00341
00342
00343 CVSSourceTree::FilePath CVSSourceTree::
00344 ask_existing(const string &basename, const CVSSourceTree::FilePath &path) {
00345 while (true) {
00346 nout << basename << " found in tree at "
00347 << path.get_path() << ".\n";
00348 string result = prompt("Overwrite this file (y/n)? ");
00349 nassertr(!result.empty(), FilePath());
00350 if (result.size() == 1) {
00351 if (tolower(result[0]) == 'y') {
00352 return path;
00353 } else if (tolower(result[0]) == 'n') {
00354 return FilePath();
00355 }
00356 }
00357
00358 nout << "*** Invalid response: " << result << "\n\n";
00359 }
00360 }
00361
00362
00363
00364
00365
00366
00367
00368 CVSSourceTree::FilePath CVSSourceTree::
00369 ask_existing(const string &basename, const CVSSourceTree::FilePaths &paths,
00370 CVSSourceDirectory *suggested_dir) {
00371 while (true) {
00372 nout << basename << " found in tree at more than one place:\n";
00373
00374 bool any_suggested = false;
00375 for (int i = 0; i < (int)paths.size(); i++) {
00376 nout << " " << (i + 1) << ". "
00377 << paths[i].get_path() << "\n";
00378 if (paths[i]._dir == suggested_dir) {
00379 any_suggested = true;
00380 }
00381 }
00382
00383 int next_option = paths.size() + 1;
00384 int suggested_option = -1;
00385
00386 if (!any_suggested) {
00387
00388
00389 suggested_option = next_option;
00390 next_option++;
00391 nout << "\n" << suggested_option
00392 << ". create "
00393 << Filename(suggested_dir->get_path(), basename)
00394 << "\n";
00395 }
00396
00397 int other_option = next_option;
00398 nout << other_option << ". Other\n";
00399
00400 string result = prompt("Choose an option: ");
00401 nassertr(!result.empty(), FilePath());
00402 const char *nptr = result.c_str();
00403 char *endptr;
00404 int option = strtol(nptr, &endptr, 10);
00405 if (*endptr == '\0') {
00406 if (option >= 1 && option <= (int)paths.size()) {
00407 return paths[option - 1];
00408
00409 } else if (option == suggested_option) {
00410 return FilePath(suggested_dir, basename);
00411
00412 } else if (option == other_option) {
00413 return FilePath();
00414 }
00415 }
00416
00417 nout << "*** Invalid response: " << result << "\n\n";
00418 }
00419 }
00420
00421
00422
00423
00424
00425
00426 CVSSourceTree::FilePath CVSSourceTree::
00427 ask_new(const string &basename, CVSSourceDirectory *dir) {
00428 while (true) {
00429 nout << basename << " will be created in "
00430 << dir->get_path() << ".\n";
00431 string result = prompt("Create this file (y/n)? ");
00432 nassertr(!result.empty(), FilePath());
00433 if (result.size() == 1) {
00434 if (tolower(result[0]) == 'y') {
00435 return FilePath(dir, basename);
00436 } else if (tolower(result[0]) == 'n') {
00437 return FilePath();
00438 }
00439 }
00440
00441 nout << "*** Invalid response: " << result << "\n\n";
00442 }
00443 }
00444
00445
00446
00447
00448
00449
00450
00451 CVSSourceTree::FilePath CVSSourceTree::
00452 ask_any(const string &basename,
00453 const CVSSourceTree::FilePaths &paths) {
00454 while (true) {
00455 string result =
00456 prompt("Enter the name of the directory to copy " + basename + " to: ");
00457 nassertr(!result.empty(), FilePath());
00458
00459
00460
00461
00462 CVSSourceDirectory *dir = find_directory(result);
00463 if (dir == (CVSSourceDirectory *)NULL) {
00464 dir = find_relpath(result);
00465 }
00466 if (dir == (CVSSourceDirectory *)NULL) {
00467 dir = find_dirname(result);
00468 }
00469
00470 if (dir != (CVSSourceDirectory *)NULL) {
00471
00472
00473 FilePaths::const_iterator pi;
00474 for (pi = paths.begin(); pi != paths.end(); ++pi) {
00475 if ((*pi)._dir == dir) {
00476 return (*pi);
00477 }
00478 }
00479
00480
00481
00482 return FilePath(dir, basename);
00483 }
00484
00485 nout << "*** Not a valid directory name: " << result << "\n\n";
00486 }
00487 }
00488
00489
00490
00491
00492
00493
00494
00495
00496 string CVSSourceTree::
00497 prompt(const string &message) {
00498 nout << flush;
00499 while (true) {
00500 cerr << message << flush;
00501 string response;
00502 getline(cin, response);
00503
00504
00505 size_t p = 0;
00506 while (p < response.length() && isspace(response[p])) {
00507 p++;
00508 }
00509
00510 size_t q = response.length();
00511 while (q > p && isspace(response[q - 1])) {
00512 q--;
00513 }
00514
00515 if (q > p) {
00516 return response.substr(p, q - p);
00517 }
00518 }
00519 }
00520
00521
00522
00523
00524
00525
00526
00527 Filename CVSSourceTree::
00528 get_actual_fullpath(const Filename &path) {
00529 Filename canon = path;
00530 canon.make_canonical();
00531 return canon;
00532 }
00533
00534
00535
00536
00537
00538
00539
00540
00541 Filename CVSSourceTree::
00542 get_start_fullpath() {
00543 if (!_got_start_fullpath) {
00544 Filename cwd = ExecutionEnvironment::get_cwd();
00545 _start_fullpath = cwd.to_os_specific();
00546 }
00547 return _start_fullpath;
00548 }
00549
00550
00551
00552
00553
00554
00555
00556 CVSSourceTree::FilePath::
00557 FilePath() :
00558 _dir(NULL)
00559 {
00560 }
00561
00562
00563
00564
00565
00566
00567
00568 CVSSourceTree::FilePath::
00569 FilePath(CVSSourceDirectory *dir, const string &basename) :
00570 _dir(dir),
00571 _basename(basename)
00572 {
00573 }
00574
00575
00576
00577
00578
00579
00580
00581 bool CVSSourceTree::FilePath::
00582 is_valid() const {
00583 return (_dir != (CVSSourceDirectory *)NULL);
00584 }
00585
00586
00587
00588
00589
00590
00591
00592 Filename CVSSourceTree::FilePath::
00593 get_path() const {
00594 nassertr(_dir != (CVSSourceDirectory *)NULL, Filename());
00595 return Filename(_dir->get_path(), _basename);
00596 }
00597
00598
00599
00600
00601
00602
00603 Filename CVSSourceTree::FilePath::
00604 get_fullpath() const {
00605 nassertr(_dir != (CVSSourceDirectory *)NULL, Filename());
00606 return Filename(_dir->get_fullpath(), _basename);
00607 }
00608
00609
00610
00611
00612
00613
00614
00615 Filename CVSSourceTree::FilePath::
00616 get_rel_from(const CVSSourceDirectory *other) const {
00617 nassertr(_dir != (CVSSourceDirectory *)NULL, Filename());
00618 return Filename(other->get_rel_to(_dir), _basename);
00619 }
00620