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