00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "cvsCopy.h"
00016 #include "cvsSourceDirectory.h"
00017
00018 #include "pnotify.h"
00019 #include <algorithm>
00020
00021
00022
00023
00024
00025
00026 CVSCopy::
00027 CVSCopy() {
00028 _model_dirname = ".";
00029 _key_filename = "Sources.pp";
00030 _cvs_binary = "cvs";
00031 _user_aborted = false;
00032 _model_dir = (CVSSourceDirectory *)NULL;
00033 _map_dir = (CVSSourceDirectory *)NULL;
00034
00035 clear_runlines();
00036 add_runline("[opts] file [file ... ]");
00037
00038 add_option
00039 ("f", "", 80,
00040 "Force the copy to happen without any input from the user. If a file "
00041 "with the same name exists anywhere in the source hierarchy, it will "
00042 "be overwritten without prompting; if a file does not yet exist, it "
00043 "will be created in the directory named by -d or by -m, as appropriate.",
00044 &CVSCopy::dispatch_none, &_force);
00045
00046 add_option
00047 ("i", "", 80,
00048 "The opposite of -f, this will prompt the user before each action. "
00049 "The default is only to prompt the user when an action is ambiguous "
00050 "or unusual.",
00051 &CVSCopy::dispatch_none, &_interactive);
00052
00053 add_option
00054 ("d", "dirname", 80,
00055 "Copy model files that are not already present somewhere in the tree "
00056 "to the indicated directory. The default is the current directory.",
00057 &CVSCopy::dispatch_filename, &_got_model_dirname, &_model_dirname);
00058
00059 add_option
00060 ("m", "dirname", 80,
00061 "Copy texture map files to the indicated directory. The default "
00062 "is src/maps from the root directory.",
00063 &CVSCopy::dispatch_filename, &_got_map_dirname, &_map_dirname);
00064
00065 add_option
00066 ("root", "dirname", 80,
00067 "Specify the root of the CVS source hierarchy. The default is to "
00068 "use the ppremake convention of locating the directory above the -d "
00069 "directory that contains a file called Package.pp.",
00070 &CVSCopy::dispatch_filename, &_got_root_dirname, &_root_dirname);
00071
00072 add_option
00073 ("key", "filename", 80,
00074 "Specify the name of the file that must exist in each directory for "
00075 "it to be considered part of the CVS source hierarchy. The default "
00076 "is the ppremake convention, \"Sources.pp\". Other likely candidates "
00077 "are \"CVS\" to search a CVS hierarchy, or \".\" to include "
00078 "all subdirectories indiscriminately.",
00079 &CVSCopy::dispatch_filename, NULL, &_key_filename);
00080
00081 add_option
00082 ("nc", "", 80,
00083 "Do not attempt to add newly-created files to CVS. The default "
00084 "is to add them.",
00085 &CVSCopy::dispatch_none, &_no_cvs);
00086
00087 add_option
00088 ("cvs", "cvs_binary", 80,
00089 "Specify how to run the cvs program for adding newly-created files. "
00090 "The default is simply \"cvs\".",
00091 &CVSCopy::dispatch_string, NULL, &_cvs_binary);
00092 }
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112 CVSSourceTree::FilePath CVSCopy::
00113 import(const Filename &source, void *extra_data,
00114 CVSSourceDirectory *suggested_dir) {
00115 CopiedFiles::const_iterator ci;
00116 ci = _copied_files.find(source);
00117 if (ci != _copied_files.end()) {
00118
00119 return (*ci).second;
00120 }
00121
00122 if (!source.exists()) {
00123 nout << "Source filename " << source << " does not exist!\n";
00124 return CVSSourceTree::FilePath();
00125 }
00126
00127 string basename = filter_filename(source.get_basename());
00128
00129 CVSSourceTree::FilePath path =
00130 _tree.choose_directory(basename, suggested_dir, _force, _interactive);
00131 nassertr(path.is_valid(), path);
00132
00133 _copied_files[source] = path;
00134 Filename dest = path.get_fullpath();
00135
00136 bool new_file = !dest.exists();
00137 if (!new_file && verify_file(source, dest, path._dir, extra_data)) {
00138
00139 nout << path.get_path() << " is unchanged.\n";
00140
00141 } else {
00142
00143 nout << "Copying " << basename << " to " << path.get_path() << "\n";
00144
00145 if (!copy_file(source, dest, path._dir, extra_data, new_file)) {
00146 if (!continue_after_error()) {
00147 return CVSSourceTree::FilePath();
00148 }
00149 } else {
00150 if (new_file) {
00151 cvs_add(dest);
00152 }
00153 }
00154 }
00155
00156 return path;
00157 }
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167 bool CVSCopy::
00168 continue_after_error() {
00169 if (_force) {
00170 return true;
00171 }
00172 if (_user_aborted) {
00173 return false;
00174 }
00175
00176 while (true) {
00177 string result = prompt("Error occurred during copy! Continue (y/n)? ");
00178 nassertr(!result.empty(), false);
00179 if (result.size() == 1) {
00180 if (tolower(result[0]) == 'y') {
00181 return true;
00182 } else if (tolower(result[0]) == 'n') {
00183 _user_aborted = true;
00184 return false;
00185 }
00186 }
00187
00188 nout << "*** Invalid response: " << result << "\n\n";
00189 }
00190 }
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201 bool CVSCopy::
00202 handle_args(Args &args) {
00203 if (args.empty()) {
00204 nout << "You must specify the file(s) to copy from on the command line.\n";
00205 return false;
00206 }
00207
00208 for (Args::const_iterator ai = args.begin();
00209 ai != args.end();
00210 ++ai) {
00211 _source_files.push_back(Filename::from_os_specific(*ai));
00212 }
00213 return true;
00214 }
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226 bool CVSCopy::
00227 post_command_line() {
00228 if (!scan_hierarchy()) {
00229 return false;
00230 }
00231
00232 _model_dir = _tree.find_directory(_model_dirname);
00233 if (_model_dir == (CVSSourceDirectory *)NULL) {
00234 if (_got_model_dirname) {
00235 nout << "Warning: model directory " << _model_dirname
00236 << " is not within the source hierarchy.\n";
00237 }
00238 }
00239
00240 if (_got_map_dirname) {
00241 _map_dir = _tree.find_directory(_map_dirname);
00242
00243 if (_map_dir == (CVSSourceDirectory *)NULL) {
00244 nout << "Warning: map directory " << _map_dirname
00245 << " is not within the source hierarchy.\n";
00246 }
00247
00248 } else {
00249 _map_dir = _tree.find_relpath("src/maps");
00250
00251 if (_map_dir == (CVSSourceDirectory *)NULL) {
00252 nout << "Warning: no directory " << _tree.get_root_dirname()
00253 << "/src/maps.\n";
00254 _map_dir = _model_dir;
00255 }
00256 }
00257
00258 return true;
00259 }
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269 bool CVSCopy::
00270 verify_file(const Filename &, const Filename &,
00271 CVSSourceDirectory *, void *) {
00272 return false;
00273 }
00274
00275
00276
00277
00278
00279
00280
00281
00282 bool CVSCopy::
00283 verify_binary_file(Filename source, Filename dest) {
00284 if (source == dest) {
00285 return true;
00286 }
00287
00288 source.set_binary();
00289 dest.set_binary();
00290
00291 ifstream s, d;
00292 if (!source.open_read(s)) {
00293 return false;
00294 }
00295 if (!dest.open_read(d)) {
00296 return false;
00297 }
00298
00299 int cs, cd;
00300 cs = s.get();
00301 cd = d.get();
00302 while (!s.eof() && !s.fail() && !d.eof() && !d.fail()) {
00303 if (cs != cd) {
00304 return false;
00305 }
00306 cs = s.get();
00307 cd = d.get();
00308 }
00309
00310 if (s.fail() || d.fail()) {
00311
00312 return false;
00313 }
00314
00315
00316
00317
00318 if (!s.eof() || !d.eof()) {
00319 return false;
00320 }
00321
00322
00323 return true;
00324 }
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336 bool CVSCopy::
00337 copy_binary_file(Filename source, Filename dest) {
00338 if (source == dest) {
00339 return true;
00340 }
00341
00342 source.set_binary();
00343 dest.set_binary();
00344
00345 ifstream in;
00346 ofstream out;
00347 if (!source.open_read(in)) {
00348 nout << "Cannot read " << source << "\n";
00349 return false;
00350 }
00351
00352 dest.unlink();
00353 if (!dest.open_write(out)) {
00354 nout << "Cannot write " << dest << "\n";
00355 return false;
00356 }
00357
00358 int c;
00359 c = in.get();
00360 while (!in.eof() && !in.fail() && !out.fail()) {
00361 out.put(c);
00362 c = in.get();
00363 }
00364
00365 if (!in.eof() && in.fail()) {
00366 nout << "Error reading " << source << "\n";
00367 return false;
00368 }
00369 if (out.fail()) {
00370 nout << "Error writing " << dest << "\n";
00371 return false;
00372 }
00373
00374 return true;
00375 }
00376
00377
00378
00379
00380
00381
00382
00383
00384 bool CVSCopy::
00385 cvs_add(const Filename &filename) {
00386 if (_no_cvs) {
00387 return true;
00388 }
00389
00390 if (!CVSSourceTree::temp_chdir(filename.get_dirname())) {
00391 nout << "Invalid directory: " << filename.get_dirname() << "\n";
00392 return false;
00393 }
00394
00395 string command = _cvs_binary + " add -kb " +
00396 protect_from_shell(filename.get_basename());
00397 nout << command << "\n";
00398 int result = system(command.c_str());
00399
00400 CVSSourceTree::restore_cwd();
00401
00402 if (result != 0) {
00403 nout << "Failure invoking cvs.\n";
00404 return false;
00405 }
00406 return true;
00407 }
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417 string CVSCopy::
00418 protect_from_shell(const string &source) {
00419 string result;
00420
00421 for (string::const_iterator pi = source.begin(); pi != source.end(); ++pi) {
00422 switch (*pi) {
00423 case '\\':
00424 case ' ':
00425 case '\'':
00426 case '"':
00427 case '(':
00428 case ')':
00429 case '<':
00430 case '>':
00431 case '|':
00432 case '&':
00433 case '!':
00434 case '$':
00435 case '~':
00436 case '*':
00437 case '?':
00438 case '[':
00439 case ']':
00440 case ';':
00441 result += '\\';
00442
00443
00444 default:
00445 result += *pi;
00446 }
00447 }
00448
00449 return result;
00450 }
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461 string CVSCopy::
00462 filter_filename(const string &source) {
00463 return source;
00464 }
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475 bool CVSCopy::
00476 scan_hierarchy() {
00477 if (!_got_root_dirname) {
00478
00479
00480 if (!scan_for_root(_model_dirname)) {
00481 return false;
00482 }
00483 }
00484
00485 _tree.set_root(_root_dirname);
00486 nout << "Root is " << _tree.get_root_fullpath() << "\n";
00487
00488 return _tree.scan(_key_filename);
00489 }
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499 bool CVSCopy::
00500 scan_for_root(const string &dirname) {
00501 Filename sources = dirname + "/Sources.pp";
00502 if (!sources.exists()) {
00503 nout << "Couldn't find " << sources << " in source directory.\n";
00504 return false;
00505 }
00506 Filename package = dirname + "/Package.pp";
00507 if (package.exists()) {
00508
00509 _root_dirname = dirname;
00510 return true;
00511 }
00512
00513 return scan_for_root(dirname + "/..");
00514 }
00515
00516
00517
00518
00519
00520
00521
00522
00523 string CVSCopy::
00524 prompt(const string &message) {
00525 nout << flush;
00526 while (true) {
00527 cerr << message << flush;
00528 string response;
00529 getline(cin, response);
00530
00531
00532 size_t p = 0;
00533 while (p < response.length() && isspace(response[p])) {
00534 p++;
00535 }
00536
00537 size_t q = response.length();
00538 while (q > p && isspace(response[q - 1])) {
00539 q--;
00540 }
00541
00542 if (q > p) {
00543 return response.substr(p, q - p);
00544 }
00545 }
00546 }