cvsSourceTree.cxx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. // Filename: cvsSourceTree.cxx
  2. // Created by: drose (31Oct00)
  3. //
  4. ////////////////////////////////////////////////////////////////////
  5. //
  6. // PANDA 3D SOFTWARE
  7. // Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
  8. //
  9. // All use of this software is subject to the terms of the Panda 3d
  10. // Software license. You should have received a copy of this license
  11. // along with this source code; you will also find a current copy of
  12. // the license at http://etc.cmu.edu/panda3d/docs/license/ .
  13. //
  14. // To contact the maintainers of this program write to
  15. // [email protected] .
  16. //
  17. ////////////////////////////////////////////////////////////////////
  18. #include "cvsSourceTree.h"
  19. #include "cvsSourceDirectory.h"
  20. #include "filename.h"
  21. #include "executionEnvironment.h"
  22. #include "pnotify.h"
  23. #include "string_utils.h"
  24. #include <algorithm>
  25. #include <ctype.h>
  26. #include <stdio.h> // for perror
  27. #include <errno.h>
  28. #ifdef WIN32_VC
  29. #include <direct.h> // for chdir
  30. #endif
  31. bool CVSSourceTree::_got_start_fullpath = false;
  32. Filename CVSSourceTree::_start_fullpath;
  33. ////////////////////////////////////////////////////////////////////
  34. // Function: CVSSourceTree::Constructor
  35. // Access: Public
  36. // Description:
  37. ////////////////////////////////////////////////////////////////////
  38. CVSSourceTree::
  39. CVSSourceTree() {
  40. _root = (CVSSourceDirectory *)NULL;
  41. _got_root_fullpath = false;
  42. }
  43. ////////////////////////////////////////////////////////////////////
  44. // Function: CVSSourceTree::Destructor
  45. // Access: Public
  46. // Description:
  47. ////////////////////////////////////////////////////////////////////
  48. CVSSourceTree::
  49. ~CVSSourceTree() {
  50. if (_root != (CVSSourceDirectory *)NULL) {
  51. delete _root;
  52. }
  53. }
  54. ////////////////////////////////////////////////////////////////////
  55. // Function: CVSSourceTree::set_root
  56. // Access: Public
  57. // Description: Sets the root of the source directory. This must be
  58. // called before scan(), and should not be called more
  59. // than once.
  60. ////////////////////////////////////////////////////////////////////
  61. void CVSSourceTree::
  62. set_root(const Filename &root_path) {
  63. nassertv(_path.empty());
  64. _path = root_path;
  65. }
  66. ////////////////////////////////////////////////////////////////////
  67. // Function: CVSSourceTree::scan
  68. // Access: Public
  69. // Description: Scans the complete source directory starting at the
  70. // indicated pathname. It is an error to call this more
  71. // than once. Returns true on success, false if there
  72. // is an error.
  73. ////////////////////////////////////////////////////////////////////
  74. bool CVSSourceTree::
  75. scan(const Filename &key_filename) {
  76. nassertr(_root == (CVSSourceDirectory *)NULL, false);
  77. Filename root_fullpath = get_root_fullpath();
  78. _root = new CVSSourceDirectory(this, NULL, root_fullpath.get_basename());
  79. return _root->scan(_path, key_filename);
  80. }
  81. ////////////////////////////////////////////////////////////////////
  82. // Function: CVSSourceTree::get_root
  83. // Access: Public
  84. // Description: Returns the root directory of the hierarchy.
  85. ////////////////////////////////////////////////////////////////////
  86. CVSSourceDirectory *CVSSourceTree::
  87. get_root() const {
  88. return _root;
  89. }
  90. ////////////////////////////////////////////////////////////////////
  91. // Function: CVSSourceTree::find_directory
  92. // Access: Public
  93. // Description: Returns the source directory that corresponds to the
  94. // given path, or NULL if there is no such directory in
  95. // the source tree.
  96. ////////////////////////////////////////////////////////////////////
  97. CVSSourceDirectory *CVSSourceTree::
  98. find_directory(const Filename &path) {
  99. string root_fullpath = get_root_fullpath();
  100. string fullpath = get_actual_fullpath(path);
  101. // path is a subdirectory within the source hierarchy if and only if
  102. // root_fullpath is an initial prefix of fullpath.
  103. if (root_fullpath.length() > fullpath.length() ||
  104. cmp_nocase(fullpath.substr(0, root_fullpath.length()), root_fullpath) != 0) {
  105. // Nope!
  106. return (CVSSourceDirectory *)NULL;
  107. }
  108. // The relative name is the part of fullpath not in root_fullpath.
  109. Filename relpath = fullpath.substr(root_fullpath.length());
  110. return _root->find_relpath(relpath);
  111. }
  112. ////////////////////////////////////////////////////////////////////
  113. // Function: CVSSourceTree::find_relpath
  114. // Access: Public
  115. // Description: Returns the source directory that corresponds to the
  116. // given relative path from the root, or NULL if there
  117. // is no match. The relative path may or may not
  118. // include the name of the root directory itself.
  119. ////////////////////////////////////////////////////////////////////
  120. CVSSourceDirectory *CVSSourceTree::
  121. find_relpath(const string &relpath) {
  122. CVSSourceDirectory *result = _root->find_relpath(relpath);
  123. if (result != (CVSSourceDirectory *)NULL) {
  124. return result;
  125. }
  126. // Check for the root dirname at the front of the path, and remove
  127. // it if it's there.
  128. size_t slash = relpath.find('/');
  129. Filename first = relpath.substr(0, slash);
  130. Filename rest;
  131. if (slash != string::npos) {
  132. rest = relpath.substr(slash + 1);
  133. }
  134. if (cmp_nocase(first, _root->get_dirname()) == 0) {
  135. return _root->find_relpath(rest);
  136. }
  137. return (CVSSourceDirectory *)NULL;
  138. }
  139. ////////////////////////////////////////////////////////////////////
  140. // Function: CVSSourceTree::find_dirname
  141. // Access: Public
  142. // Description: Returns the source directory that corresponds to the
  143. // given local directory name, or NULL if there
  144. // is no match.
  145. ////////////////////////////////////////////////////////////////////
  146. CVSSourceDirectory *CVSSourceTree::
  147. find_dirname(const string &dirname) {
  148. return _root->find_dirname(dirname);
  149. }
  150. ////////////////////////////////////////////////////////////////////
  151. // Function: CVSSourceTree::choose_directory
  152. // Access: Public
  153. // Description: Determines where an externally referenced model file
  154. // of the indicated name should go. It does this by
  155. // looking for an existing model file of the same name;
  156. // if a matching model is not found, or if multiple
  157. // matching files are found, prompts the user for the
  158. // directory, or uses suggested_dir.
  159. ////////////////////////////////////////////////////////////////////
  160. CVSSourceTree::FilePath CVSSourceTree::
  161. choose_directory(const string &basename, CVSSourceDirectory *suggested_dir,
  162. bool force, bool interactive) {
  163. static FilePaths empty_paths;
  164. Basenames::const_iterator bi;
  165. bi = _basenames.find(downcase(basename));
  166. if (bi != _basenames.end()) {
  167. // The filename already exists somewhere.
  168. const FilePaths &paths = (*bi).second;
  169. return prompt_user(basename, suggested_dir, paths,
  170. force, interactive);
  171. }
  172. // Now we have to prompt the user for a suitable place to put it.
  173. return prompt_user(basename, suggested_dir, empty_paths,
  174. force, interactive);
  175. }
  176. ////////////////////////////////////////////////////////////////////
  177. // Function: CVSSourceTree::get_root_fullpath
  178. // Access: Public
  179. // Description: Returns the full path from the root to the top of
  180. // the source hierarchy.
  181. ////////////////////////////////////////////////////////////////////
  182. Filename CVSSourceTree::
  183. get_root_fullpath() {
  184. nassertr(!_path.empty(), Filename());
  185. if (!_got_root_fullpath) {
  186. _root_fullpath = get_actual_fullpath(_path);
  187. _got_root_fullpath = true;
  188. }
  189. return _root_fullpath;
  190. }
  191. ////////////////////////////////////////////////////////////////////
  192. // Function: CVSSourceTree::get_root_dirname
  193. // Access: Public
  194. // Description: Returns the local directory name of the root of the
  195. // tree.
  196. ////////////////////////////////////////////////////////////////////
  197. Filename CVSSourceTree::
  198. get_root_dirname() const {
  199. nassertr(_root != (CVSSourceDirectory *)NULL, Filename());
  200. return _root->get_dirname();
  201. }
  202. ////////////////////////////////////////////////////////////////////
  203. // Function: CVSSourceTree::add_file
  204. // Access: Public
  205. // Description: Adds a new file to the set of known files. This is
  206. // normally called from CVSSourceDirectory::scan() and
  207. // should not be called directly by the user.
  208. ////////////////////////////////////////////////////////////////////
  209. void CVSSourceTree::
  210. add_file(const string &basename, CVSSourceDirectory *dir) {
  211. FilePath file_path(dir, basename);
  212. _basenames[downcase(basename)].push_back(file_path);
  213. }
  214. ////////////////////////////////////////////////////////////////////
  215. // Function: CVSSourceTree::temp_chdir
  216. // Access: Public, Static
  217. // Description: Temporarily changes the current directory to the
  218. // named path. Returns true on success, false on
  219. // failure. Call restore_cwd() to restore to the
  220. // original directory later.
  221. ////////////////////////////////////////////////////////////////////
  222. bool CVSSourceTree::
  223. temp_chdir(const Filename &path) {
  224. // We have to call this first to guarantee that we have already
  225. // determined our starting directory.
  226. get_start_fullpath();
  227. string os_path = path.to_os_specific();
  228. if (chdir(os_path.c_str()) < 0) {
  229. return false;
  230. }
  231. return true;
  232. }
  233. ////////////////////////////////////////////////////////////////////
  234. // Function: CVSSourceTree::restore_cwd
  235. // Access: Public, Static
  236. // Description: Restores the current directory after changing it from
  237. // temp_chdir().
  238. ////////////////////////////////////////////////////////////////////
  239. void CVSSourceTree::
  240. restore_cwd() {
  241. Filename start_fullpath = get_start_fullpath();
  242. string os_path = start_fullpath.to_os_specific();
  243. if (chdir(os_path.c_str()) < 0) {
  244. // Hey! We can't get back to the directory we started from!
  245. perror(os_path.c_str());
  246. nout << "Can't continue, aborting.\n";
  247. exit(1);
  248. }
  249. }
  250. ////////////////////////////////////////////////////////////////////
  251. // Function: CVSSourceTree::prompt_user
  252. // Access: Private
  253. // Description: Prompts the user, if necessary, to choose a directory
  254. // to import the given file into.
  255. ////////////////////////////////////////////////////////////////////
  256. CVSSourceTree::FilePath CVSSourceTree::
  257. prompt_user(const string &basename, CVSSourceDirectory *suggested_dir,
  258. const CVSSourceTree::FilePaths &paths,
  259. bool force, bool interactive) {
  260. if (paths.size() == 1) {
  261. // The file already exists in exactly one place.
  262. if (!interactive) {
  263. return paths[0];
  264. }
  265. FilePath result = ask_existing(basename, paths[0]);
  266. if (result.is_valid()) {
  267. return result;
  268. }
  269. } else if (paths.size() > 1) {
  270. // The file already exists in multiple places.
  271. if (force && !interactive) {
  272. return paths[0];
  273. }
  274. FilePath result = ask_existing(basename, paths, suggested_dir);
  275. if (result.is_valid()) {
  276. return result;
  277. }
  278. }
  279. // The file does not already exist, or the user declined to replace
  280. // an existing file.
  281. if (force && !interactive) {
  282. return FilePath(suggested_dir, basename);
  283. }
  284. // Is the file already in the suggested directory? If not, prompt
  285. // the user to put it there.
  286. bool found_dir = false;
  287. FilePaths::const_iterator pi;
  288. for (pi = paths.begin(); pi != paths.end(); ++pi) {
  289. if ((*pi)._dir == suggested_dir) {
  290. found_dir = true;
  291. break;
  292. }
  293. }
  294. if (!found_dir) {
  295. FilePath result = ask_new(basename, suggested_dir);
  296. if (result.is_valid()) {
  297. return result;
  298. }
  299. }
  300. // Ask the user where the damn thing should go.
  301. return ask_any(basename, paths);
  302. }
  303. ////////////////////////////////////////////////////////////////////
  304. // Function: CVSSourceTree::ask_existing
  305. // Access: Private
  306. // Description: Asks the user if he wants to replace an existing
  307. // file.
  308. ////////////////////////////////////////////////////////////////////
  309. CVSSourceTree::FilePath CVSSourceTree::
  310. ask_existing(const string &basename, const CVSSourceTree::FilePath &path) {
  311. while (true) {
  312. nout << basename << " found in tree at "
  313. << path.get_path() << ".\n";
  314. string result = prompt("Overwrite this file (y/n)? ");
  315. nassertr(!result.empty(), FilePath());
  316. if (result.size() == 1) {
  317. if (tolower(result[0]) == 'y') {
  318. return path;
  319. } else if (tolower(result[0]) == 'n') {
  320. return FilePath();
  321. }
  322. }
  323. nout << "*** Invalid response: " << result << "\n\n";
  324. }
  325. }
  326. ////////////////////////////////////////////////////////////////////
  327. // Function: CVSSourceTree::ask_existing
  328. // Access: Private
  329. // Description: Asks the user which of several existing files he
  330. // wants to replace.
  331. ////////////////////////////////////////////////////////////////////
  332. CVSSourceTree::FilePath CVSSourceTree::
  333. ask_existing(const string &basename, const CVSSourceTree::FilePaths &paths,
  334. CVSSourceDirectory *suggested_dir) {
  335. while (true) {
  336. nout << basename << " found in tree at more than one place:\n";
  337. bool any_suggested = false;
  338. for (int i = 0; i < (int)paths.size(); i++) {
  339. nout << " " << (i + 1) << ". "
  340. << paths[i].get_path() << "\n";
  341. if (paths[i]._dir == suggested_dir) {
  342. any_suggested = true;
  343. }
  344. }
  345. int next_option = paths.size() + 1;
  346. int suggested_option = -1;
  347. if (!any_suggested) {
  348. // If it wasn't already in the suggested directory, offer to put
  349. // it there.
  350. suggested_option = next_option;
  351. next_option++;
  352. nout << "\n" << suggested_option
  353. << ". create "
  354. << Filename(suggested_dir->get_path(), basename)
  355. << "\n";
  356. }
  357. int other_option = next_option;
  358. nout << other_option << ". Other\n";
  359. string result = prompt("Choose an option: ");
  360. nassertr(!result.empty(), FilePath());
  361. const char *nptr = result.c_str();
  362. char *endptr;
  363. int option = strtol(nptr, &endptr, 10);
  364. if (*endptr == '\0') {
  365. if (option >= 1 && option <= (int)paths.size()) {
  366. return paths[option - 1];
  367. } else if (option == suggested_option) {
  368. return FilePath(suggested_dir, basename);
  369. } else if (option == other_option) {
  370. return FilePath();
  371. }
  372. }
  373. nout << "*** Invalid response: " << result << "\n\n";
  374. }
  375. }
  376. ////////////////////////////////////////////////////////////////////
  377. // Function: CVSSourceTree::ask_new
  378. // Access: Private
  379. // Description: Asks the user if he wants to create a new file.
  380. ////////////////////////////////////////////////////////////////////
  381. CVSSourceTree::FilePath CVSSourceTree::
  382. ask_new(const string &basename, CVSSourceDirectory *dir) {
  383. while (true) {
  384. nout << basename << " will be created in "
  385. << dir->get_path() << ".\n";
  386. string result = prompt("Create this file (y/n)? ");
  387. nassertr(!result.empty(), FilePath());
  388. if (result.size() == 1) {
  389. if (tolower(result[0]) == 'y') {
  390. return FilePath(dir, basename);
  391. } else if (tolower(result[0]) == 'n') {
  392. return FilePath();
  393. }
  394. }
  395. nout << "*** Invalid response: " << result << "\n\n";
  396. }
  397. }
  398. ////////////////////////////////////////////////////////////////////
  399. // Function: CVSSourceTree::ask_any
  400. // Access: Private
  401. // Description: Asks the user to type in the name of the directory in
  402. // which to store the file.
  403. ////////////////////////////////////////////////////////////////////
  404. CVSSourceTree::FilePath CVSSourceTree::
  405. ask_any(const string &basename,
  406. const CVSSourceTree::FilePaths &paths) {
  407. while (true) {
  408. string result =
  409. prompt("Enter the name of the directory to copy " + basename + " to: ");
  410. nassertr(!result.empty(), FilePath());
  411. // The user might enter a fully-qualified path to the directory,
  412. // or a relative path from the root (with or without the root's
  413. // dirname), or the dirname of the particular directory.
  414. CVSSourceDirectory *dir = find_directory(result);
  415. if (dir == (CVSSourceDirectory *)NULL) {
  416. dir = find_relpath(result);
  417. }
  418. if (dir == (CVSSourceDirectory *)NULL) {
  419. dir = find_dirname(result);
  420. }
  421. if (dir != (CVSSourceDirectory *)NULL) {
  422. // If the file is already in this directory, we must preserve
  423. // its existing case.
  424. FilePaths::const_iterator pi;
  425. for (pi = paths.begin(); pi != paths.end(); ++pi) {
  426. if ((*pi)._dir == dir) {
  427. return (*pi);
  428. }
  429. }
  430. // Otherwise, since we're creating a new file, keep the original
  431. // case.
  432. return FilePath(dir, basename);
  433. }
  434. nout << "*** Not a valid directory name: " << result << "\n\n";
  435. }
  436. }
  437. ////////////////////////////////////////////////////////////////////
  438. // Function: CVSSourceTree::prompt
  439. // Access: Private
  440. // Description: Issues a prompt to the user and waits for a typed
  441. // response. Returns the response (which will not be
  442. // empty).
  443. ////////////////////////////////////////////////////////////////////
  444. string CVSSourceTree::
  445. prompt(const string &message) {
  446. nout << flush;
  447. while (true) {
  448. cerr << message << flush;
  449. string response;
  450. getline(cin, response);
  451. // Remove leading and trailing whitespace.
  452. size_t p = 0;
  453. while (p < response.length() && isspace(response[p])) {
  454. p++;
  455. }
  456. size_t q = response.length();
  457. while (q > p && isspace(response[q - 1])) {
  458. q--;
  459. }
  460. if (q > p) {
  461. return response.substr(p, q - p);
  462. }
  463. }
  464. }
  465. ////////////////////////////////////////////////////////////////////
  466. // Function: CVSSourceTree::get_actual_fullpath
  467. // Access: Private, Static
  468. // Description: Determines the actual full path from the root to the
  469. // named directory.
  470. ////////////////////////////////////////////////////////////////////
  471. Filename CVSSourceTree::
  472. get_actual_fullpath(const Filename &path) {
  473. Filename canon = path;
  474. canon.make_canonical();
  475. return canon;
  476. }
  477. ////////////////////////////////////////////////////////////////////
  478. // Function: CVSSourceTree::get_start_fullpath
  479. // Access: Private, Static
  480. // Description: Returns the full path from the root to the directory
  481. // in which the user started the program.
  482. ////////////////////////////////////////////////////////////////////
  483. Filename CVSSourceTree::
  484. get_start_fullpath() {
  485. if (!_got_start_fullpath) {
  486. Filename cwd = ExecutionEnvironment::get_cwd();
  487. _start_fullpath = cwd.to_os_specific();
  488. }
  489. return _start_fullpath;
  490. }
  491. ////////////////////////////////////////////////////////////////////
  492. // Function: CVSSourceTree::FilePath::Constructor
  493. // Access: Public
  494. // Description: Creates an invalid FilePath specification.
  495. ////////////////////////////////////////////////////////////////////
  496. CVSSourceTree::FilePath::
  497. FilePath() :
  498. _dir(NULL)
  499. {
  500. }
  501. ////////////////////////////////////////////////////////////////////
  502. // Function: CVSSourceTree::FilePath::Constructor
  503. // Access: Public
  504. // Description: Creates a valid FilePath specification with the
  505. // indicated directory and basename.
  506. ////////////////////////////////////////////////////////////////////
  507. CVSSourceTree::FilePath::
  508. FilePath(CVSSourceDirectory *dir, const string &basename) :
  509. _dir(dir),
  510. _basename(basename)
  511. {
  512. }
  513. ////////////////////////////////////////////////////////////////////
  514. // Function: CVSSourceTree::FilePath::is_valid
  515. // Access: Public
  516. // Description: Returns true if this FilePath represents a valid
  517. // file, or false if it represents an error return.
  518. ////////////////////////////////////////////////////////////////////
  519. bool CVSSourceTree::FilePath::
  520. is_valid() const {
  521. return (_dir != (CVSSourceDirectory *)NULL);
  522. }
  523. ////////////////////////////////////////////////////////////////////
  524. // Function: CVSSourceTree::FilePath::get_path
  525. // Access: Public
  526. // Description: Returns the relative path to this file from the root
  527. // of the source tree.
  528. ////////////////////////////////////////////////////////////////////
  529. Filename CVSSourceTree::FilePath::
  530. get_path() const {
  531. nassertr(_dir != (CVSSourceDirectory *)NULL, Filename());
  532. return Filename(_dir->get_path(), _basename);
  533. }
  534. ////////////////////////////////////////////////////////////////////
  535. // Function: CVSSourceTree::FilePath::get_fullpath
  536. // Access: Public
  537. // Description: Returns the full path to this file.
  538. ////////////////////////////////////////////////////////////////////
  539. Filename CVSSourceTree::FilePath::
  540. get_fullpath() const {
  541. nassertr(_dir != (CVSSourceDirectory *)NULL, Filename());
  542. return Filename(_dir->get_fullpath(), _basename);
  543. }
  544. ////////////////////////////////////////////////////////////////////
  545. // Function: CVSSourceTree::FilePath::get_rel_from
  546. // Access: Public
  547. // Description: Returns the relative path to this file as seen from
  548. // the indicated source directory.
  549. ////////////////////////////////////////////////////////////////////
  550. Filename CVSSourceTree::FilePath::
  551. get_rel_from(const CVSSourceDirectory *other) const {
  552. nassertr(_dir != (CVSSourceDirectory *)NULL, Filename());
  553. return Filename(other->get_rel_to(_dir), _basename);
  554. }