00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <Basics.H>
00028 #include <Text.H>
00029 #include <VestaConfig.H>
00030 #include <VestaSource.H>
00031 #include <VDirSurrogate.H>
00032 #include <VestaSourceAtomic.H>
00033 #include <Replicator.H>
00034 #include "ReposUI.H"
00035
00036 #if !defined(__sun__)
00037 extern "C" {
00038 #include <getopt.h>
00039 }
00040 #endif
00041
00042 using std::cout;
00043 using std::cin;
00044 using std::cerr;
00045 using std::endl;
00046
00047 Text program_name;
00048
00049 void
00050 usage()
00051 {
00052 cerr << "Usage: " << program_name << endl <<
00053 " [-q] [-Q] [-f]" << endl <<
00054 " [-m message | -M]" << endl <<
00055 " [-s session-dir | -S]" << endl <<
00056 " [-c content | -C]" << endl <<
00057 " [-n new-version]" << endl <<
00058 " [-R repos]" << endl <<
00059 " [-d dst] | -D]" << endl <<
00060 " [[-w] work-dir | -W]" << endl << endl;
00061 exit(1);
00062 }
00063
00064
00065 int
00066 main(int argc, char* argv[])
00067 {
00068 program_name = argv[0];
00069 try {
00070
00071
00072
00073 Text defpkgpar, defworkpar, timefmt;
00074 defpkgpar = VestaConfig::get_Text("UserInterface",
00075 "DefaultPackageParent");
00076 defworkpar = VestaConfig::get_Text("UserInterface",
00077 "DefaultWorkParent");
00078 timefmt = VestaConfig::get_Text("UserInterface", "TimeFormat");
00079
00080 bool prompt_msg = true, omit_msg = false;
00081 if(VestaConfig::is_set("UserInterface", "vcheckin_message")) {
00082 if(!VestaConfig::get_bool("UserInterface", "vcheckin_message")) {
00083 prompt_msg = false;
00084 omit_msg = true;
00085 }
00086 }
00087
00088 time_t now = time(NULL);
00089 char timebuf[256];
00090 strftime(timebuf, sizeof(timebuf), timefmt.cchars(), localtime(&now));
00091 Text cuser(AccessControl::self()->user());
00092 Text user(cuser.Sub(0, cuser.FindChar('@')));
00093 Text lhost(VDirSurrogate::defaultHost());
00094 Text lport(VDirSurrogate::defaultPort());
00095 const int vlen = 7;
00096 const Replicator::Flags rflags = (Replicator::Flags)
00097 (Replicator::attrNew | Replicator::attrOld | Replicator::attrAccess |
00098 Replicator::revive | Replicator::inclStubs | Replicator::latest);
00099
00100
00101
00102
00103 Text sessdir, content, newver, workdir, message, dstrepos, repos;
00104 bool no_workdir = false, no_sessdir = false;
00105 bool default_sessdir = true, default_content = true;
00106 bool default_newver = true, default_workdir = true;
00107 bool quiet = false, query = false, force = false;
00108 bool omit_dstrepos = false;
00109 bool default_repos = true;
00110 opterr = 0;
00111 for (;;) {
00112 int c = getopt(argc, argv, "qQfm:Ms:Sc:Cn:w:Wd:DR:");
00113 if (c == EOF) break;
00114 switch (c) {
00115 case 'q':
00116 quiet = true;
00117 break;
00118 case 'Q':
00119 query = true;
00120 break;
00121 case 'f':
00122 force = true;
00123 break;
00124 case 'm':
00125 message = optarg;
00126 omit_msg = false;
00127 if(message == "-") {
00128 message = "";
00129 prompt_msg = true;
00130 }
00131 else
00132 prompt_msg = false;
00133 break;
00134 case 'M':
00135 omit_msg = true;
00136 prompt_msg = false;
00137 break;
00138 case 's':
00139 sessdir = optarg;
00140 default_sessdir = false;
00141 no_sessdir = false;
00142 break;
00143 case 'S':
00144 sessdir = "NONE";
00145 default_sessdir = false;
00146 no_sessdir = true;
00147 break;
00148 case 'c':
00149 content = optarg;
00150 default_content = false;
00151 break;
00152 case 'C':
00153 content = "-1";
00154 default_content = false;
00155 break;
00156 case 'n':
00157 newver = optarg;
00158 default_newver = false;
00159 break;
00160 case 'w':
00161 workdir = optarg;
00162 default_workdir = false;
00163 no_workdir = false;
00164 break;
00165 case 'W':
00166 workdir = "NONE";
00167 default_workdir = false;
00168 no_workdir = true;
00169 break;
00170 case 'd':
00171 dstrepos = optarg;
00172 omit_dstrepos = false;
00173 break;
00174 case 'D':
00175 omit_dstrepos = true;
00176 break;
00177 case 'R':
00178 repos = optarg;
00179 default_repos = false;
00180 break;
00181 case '?':
00182 default:
00183 usage();
00184 }
00185 }
00186
00187 switch (argc - optind) {
00188 case 1:
00189 if (default_workdir) {
00190 workdir = argv[optind];
00191 default_workdir = false;
00192 }
00193 break;
00194 case 0:
00195 break;
00196 default:
00197 usage();
00198
00199 }
00200
00201
00202
00203
00204 if (!default_repos) {
00205 int colon = repos.FindCharR(':');
00206 if (colon == -1) {
00207 lhost = repos;
00208 repos = repos + ":" + lport;
00209 } else {
00210 lhost = repos.Sub(0, colon);
00211 lport = repos.Sub(colon+1);
00212 }
00213 }
00214
00215
00216
00217
00218 if (default_workdir) {
00219 workdir = ".";
00220 }
00221 VestaSource* vs_workdir = NULL;
00222 VestaSource* vs_workpar = NULL;
00223 Text cworkdir, cworkpar, workarc;
00224 if (!no_workdir) {
00225 cworkdir = ReposUI::canonicalize(workdir, defworkpar + "/" + user);
00226 ReposUI::split(cworkdir, cworkpar, workarc, "work-dir");
00227 vs_workdir = ReposUI::filenameToVS(cworkdir, lhost, lport);
00228 vs_workpar = ReposUI::filenameToVS(cworkpar, lhost, lport);
00229 }
00230
00231 if (default_sessdir) {
00232 if (no_workdir) {
00233 cerr << program_name <<
00234 ": can't default session-dir; " <<
00235 "no working directory" << endl;
00236 exit(2);
00237 }
00238
00239 char* attribval = vs_workdir->getAttrib("session-dir");
00240 if (attribval == NULL) {
00241 cerr << program_name <<
00242 ": can't default session-dir; " <<
00243 "work-dir has no session-dir attribute" << endl;
00244 exit(2);
00245 }
00246 sessdir = attribval;
00247 delete attribval;
00248 }
00249 VestaSource* vs_sessdir = NULL;
00250 Text csessdir;
00251 if (!no_sessdir) {
00252 csessdir = ReposUI::canonicalize(sessdir, defpkgpar);
00253 vs_sessdir = ReposUI::filenameToVS(csessdir, lhost, lport);
00254 }
00255
00256 if (default_content) {
00257 if (no_sessdir) {
00258 cerr << program_name <<
00259 ": can't default content; " <<
00260 "no session directory to search in" << endl;
00261 exit(2);
00262 }
00263 long high = ReposUI::highver(vs_sessdir);
00264 char buf[64];
00265 sprintf(buf, "%ld", high);
00266 content = buf;
00267 }
00268 VestaSource* vs_content = NULL;
00269 Text ccontent;
00270 if (content != "-1") {
00271 ccontent = ReposUI::canonicalize(content, csessdir);
00272 vs_content = ReposUI::filenameToVS(ccontent, lhost, lport);
00273 }
00274
00275 if (default_newver) {
00276
00277
00278 char* attribval;
00279 if (!no_sessdir) {
00280 attribval = vs_sessdir->getAttrib("new-version");
00281 if (attribval == NULL) {
00282 cerr << program_name <<
00283 ": can't default new-version; " <<
00284 "session-dir has no new-version attribute" << endl;
00285 exit(2);
00286 }
00287 } else if (vs_workdir != NULL) {
00288 attribval = vs_workdir->getAttrib("new-version");
00289 if (attribval == NULL) {
00290 cerr << program_name <<
00291 ": can't default new-version; " <<
00292 "work-dir has no new-version attribute" << endl;
00293 exit(2);
00294 }
00295 } else {
00296 cerr << program_name <<
00297 ": can't default new-version; " <<
00298 "no session-dir or work-dir to examine" << endl;
00299 exit(2);
00300 }
00301 newver = attribval;
00302 delete attribval;
00303 }
00304 Text cnewver = ReposUI::canonicalize(newver, defpkgpar);
00305 Text cpkg, newverarc;
00306 ReposUI::split(cnewver, cpkg, newverarc, "new-version");
00307 VestaSource* vs_pkg = ReposUI::filenameToVS(cpkg, lhost, lport);
00308
00309
00310 VestaSource *vs_newverstub;
00311 VestaSource::errorCode err =
00312 vs_pkg->lookup(newverarc.cchars(), vs_newverstub);
00313 if (err != VestaSource::ok) {
00314 cerr << program_name << ": " << cnewver << ": "
00315 << ReposUI::errorCodeText(err) << endl;
00316 exit(2);
00317 }
00318
00319
00320 const char* stub_dstrepos = vs_newverstub->getAttrib("checkout-from");
00321 char* stub_msg = vs_newverstub->getAttrib("message");
00322
00323
00324
00325
00326
00327 const char* stub_oldver = vs_newverstub->getAttrib("old-version");
00328 const char* stub_mstrepos = vs_newverstub->getAttrib("master-repository");
00329
00330
00331 if (dstrepos == "" && stub_dstrepos != NULL) {
00332 dstrepos = stub_dstrepos;
00333 }
00334
00335
00336 if(!query && prompt_msg) {
00337 message = ReposUI::getMessage("change history message",
00338 "Checkout description",
00339 stub_msg);
00340 }
00341
00342
00343 if (!force) {
00344
00345 if (!no_sessdir && !vs_sessdir->inAttribs("type", "session")) {
00346 cerr << program_name << ": " << csessdir
00347 << " is not a session directory" << endl;
00348 exit(2);
00349 }
00350 if (!no_workdir &&
00351 vs_workdir->getAttrib("checkout-time") == NULL) {
00352 cerr << program_name << ": " << cworkdir
00353 << " is not a working directory" << endl;
00354 exit(2);
00355 }
00356
00357
00358
00359
00360
00361 if (default_content && vs_workdir != NULL
00362 && ReposUI::changed(vs_workdir, vs_sessdir->timestamp())) {
00363 cerr << program_name << ": " << cworkdir
00364 << " has been modified since last advance" << endl;
00365 exit(2);
00366 }
00367
00368 if (vs_newverstub->type != VestaSource::stub ||
00369 !vs_newverstub->master ||
00370 vs_newverstub->getAttrib("checkout-time") == NULL) {
00371 cerr << program_name << ": " << cnewver
00372 << " is not a reservation stub" << endl;
00373 exit(2);
00374 }
00375 delete vs_newverstub;
00376 }
00377
00378 if (!quiet) {
00379 cout << "Checking in " << cnewver << endl;
00380 if (!no_workdir) {
00381 cout << "Deleting " << cworkdir << endl;
00382 }
00383 }
00384
00385 if (query) {
00386 cerr << program_name << ": nothing done (-Q flag given)" << endl;
00387 exit(0);
00388 }
00389
00390
00391
00392
00393 VestaSourceAtomic vsa(lhost, lport);
00394 VestaSourceAtomic::VSIndex vsi_content = -1,
00395 vsi_pkg = -1, vsi_workpar = -1, vsi_newverstub = -1,
00396 vsi_sessdir = -1, vsi_workdir = -1;
00397
00398
00399
00400
00401
00402 vsi_pkg = vsa.decl(cpkg, vs_pkg);
00403 vsa.typeCheck(cpkg, vsi_pkg, VestaSourceAtomic::
00404 typebit(VestaSource::appendableDirectory),
00405 VestaSource::inappropriateOp);
00406 vsa.accessCheck(cpkg, vsi_pkg, AccessControl::write,
00407 true, VestaSource::noPermission);
00408
00409 vsi_newverstub = vsa.lookup(cnewver, vsi_pkg, newverarc);
00410 vsa.typeCheck(cnewver, vsi_newverstub, VestaSourceAtomic::
00411 typebit(VestaSource::stub), VestaSource::nameInUse);
00412 vsa.testMaster(cnewver, vsi_newverstub, true,
00413 VestaSource::notMaster);
00414 vsa.accessCheck(cnewver, vsi_newverstub, AccessControl::write,
00415 true, VestaSource::noPermission);
00416
00417 if (vs_content) {
00418 vsi_content = vsa.decl(ccontent, vs_content);
00419 vsa.typeCheck(ccontent, vsi_content, VestaSourceAtomic::
00420 typebit(VestaSource::immutableDirectory),
00421 VestaSource::inappropriateOp);
00422 vsa.accessCheck(ccontent, vsi_content, AccessControl::read,
00423 true, VestaSource::noPermission);
00424 }
00425
00426 if (vs_workpar) {
00427 vsi_workpar = vsa.decl(cworkpar, vs_workpar);
00428 vsa.typeCheck(cworkpar, vsi_workpar, VestaSourceAtomic::
00429 typebit(VestaSource::mutableDirectory),
00430 VestaSource::inappropriateOp);
00431 vsa.accessCheck(cworkpar, vsi_workpar, AccessControl::write,
00432 true, VestaSource::noPermission);
00433 vsi_workdir = vsa.lookup(cworkdir, vsi_workpar, workarc);
00434 }
00435
00436 if (!no_sessdir) {
00437 vsi_sessdir = vsa.decl(csessdir, vs_sessdir);
00438 vsa.accessCheck(csessdir, vsi_sessdir, AccessControl::write,
00439 true, VestaSource::noPermission);
00440 }
00441
00442
00443
00444
00445 VestaSourceAtomic::VSIndex vsi_newver =
00446 vsa.insertImmutableDirectory(cnewver, vsi_pkg, newverarc, vsi_content,
00447 true, VestaSource::replaceDiff);
00448
00449 if (!no_workdir) {
00450 vsa.reallyDelete(cworkdir, vsi_workpar, workarc, true);
00451 }
00452
00453
00454
00455
00456
00457
00458
00459 if (!no_sessdir) {
00460 vsa.setAttrib(csessdir, vsi_sessdir, "checkin-by", cuser);
00461 vsa.setAttrib(csessdir, vsi_sessdir, "checkin-time", timebuf);
00462
00463 vsa.setAttrib(cnewver, vsi_newver, "session-dir", csessdir);
00464 }
00465
00466
00467 vsa.setAttrib(cnewver, vsi_newver, "checkin-by", cuser);
00468 vsa.setAttrib(cnewver, vsi_newver, "checkin-time", timebuf);
00469 if(!omit_msg && !message.Empty()) {
00470 vsa.setAttrib(cnewver, vsi_newver, "message", message);
00471 }
00472 if (vs_content) {
00473 vsa.setAttrib(cnewver, vsi_newver, "content", ccontent);
00474 }
00475
00476 vsa.clearAttrib(cnewver, vsi_newver, "work-dir");
00477 vsa.clearAttrib(cnewver, vsi_newver, "#mode");
00478
00479 if (vs_content) {
00480 vsa.setAttrib(cnewver, vsi_newver, "old-version", ReposUI::prevVersion(vs_content,
00481 omit_dstrepos ? "" : dstrepos));
00482 } else if (!no_sessdir) {
00483 vsa.mergeAttrib(cnewver, vsi_sessdir, vsi_newver, "old-version");
00484 } else if (stub_oldver) {
00485 vsa.setAttrib(cnewver, vsi_newver, "old-version", stub_oldver);
00486 }
00487 if (stub_mstrepos) {
00488 vsa.setAttrib(cnewver, vsi_newver, "master-repository", stub_mstrepos);
00489 }
00490
00491
00492
00493 if (!vsa.run()) {
00494 VestaSource::errorCode err;
00495 if (vsa.lasterr == VestaSource::ok) {
00496 err = vsa.okreplace;
00497 } else {
00498 err = vsa.lasterr;
00499 }
00500 cerr << program_name << ": " << vsa.name(vsa.ndone) << ": "
00501 << ReposUI::errorCodeText(err) << endl;
00502 exit(2);
00503 }
00504
00505
00506 if (!omit_dstrepos && dstrepos != "") {
00507 Text dhost, dport;
00508 int colon = dstrepos.FindCharR(':');
00509 if (colon == -1) {
00510 dhost = dstrepos;
00511 dport = lport;
00512 } else {
00513 dhost = dstrepos.Sub(0, colon);
00514 dport = dstrepos.Sub(colon+1);
00515 }
00516 if (lhost != dhost || lport != dport) {
00517 if (!quiet) {
00518 cout << "Replicating " << cnewver
00519 << " to " << dhost << ":" << dport << endl;
00520 }
00521 Replicator repl(lhost, lport, dhost, dport);
00522 Replicator::DirectiveSeq direcs;
00523 Replicator::Directive d('+', cnewver.Sub(vlen));
00524 direcs.addhi(d);
00525 repl.replicate(&direcs, rflags);
00526 }
00527 }
00528
00529 } catch (VestaConfig::failure f) {
00530 cerr << program_name << ": " << f.msg << endl;
00531 exit(2);
00532 } catch (SRPC::failure f) {
00533 cerr << program_name
00534 << ": SRPC failure; " << f.msg << " (" << f.r << ")" << endl;
00535 exit(2);
00536 } catch (ReposUI::failure f) {
00537 cerr << program_name << ": " << f.msg << endl;
00538 exit(2);
00539 } catch (Replicator::Failure f) {
00540 if (f.code == (VestaSource::errorCode) -1) {
00541 cerr << program_name << ": " << f.msg << endl;
00542 } else {
00543 cerr << program_name << ": " << ReposUI::errorCodeText(f.code)
00544 << ", " << f.msg << endl;
00545 }
00546 exit(2);
00547 } catch(FS::Failure f) {
00548 cerr << program_name << ": " << f << endl;
00549 exit(2);
00550 }
00551
00552 return 0;
00553 }