00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <Basics.H>
00024 #include <Text.H>
00025 #include <VestaSource.H>
00026 #include <VDirSurrogate.H>
00027 #include <sys/mount.h>
00028 #include <dirent.h>
00029 #include <fnmatch.h>
00030 #include "ReposUI.H"
00031 #include <VestaConfig.H>
00032 #include <Units.H>
00033
00034 using std::cin;
00035 using std::cout;
00036 using std::endl;
00037 using std::flush;
00038 using std::ofstream;
00039 using std::ifstream;
00040
00041 static void
00042 throw_errno(const Text& msg, int saved_errno)
00043 {
00044 Text etxt = Basics::errno_Text(saved_errno);
00045 throw ReposUI::failure(msg + ": " + etxt, saved_errno);
00046 }
00047
00048 static void
00049 throw_errno(const Text& msg)
00050 {
00051 throw_errno(msg, errno);
00052 }
00053
00054
00055 bool
00056 ReposUI::qualified(const Text& name) throw ()
00057 {
00058 return (name[0] == '/') ||
00059 (name[0] == '.' && (name[1] == '\0' || name[1] == '/')) ||
00060 (name[0] == '.' && name[1] == '.' && (name[2]=='\0' || name[2]=='/'));
00061 }
00062
00063
00064 Text
00065 ReposUI::qualify(const Text& name, const Text& root) throw ()
00066 {
00067 if (qualified(name)) {
00068 return name;
00069 } else if (name == "") {
00070 return root;
00071 } else {
00072 return root + "/" + name;
00073 }
00074 }
00075
00076
00077
00078 void
00079 ReposUI::split(const Text& name, Text& par, Text& arc, const Text& errtext)
00080 throw (ReposUI::failure)
00081 {
00082 int lastslash = name.FindCharR('/');
00083 if (lastslash == -1) {
00084 arc = name;
00085 par = "";
00086 } else {
00087 arc = name.Sub(lastslash + 1);
00088 par = name.Sub(0, lastslash);
00089 if (par == "") par = "/";
00090 }
00091 if (arc.Length() == 0) {
00092 throw ReposUI::failure(errtext +
00093 " must not have empty final arc");
00094 }
00095 if (arc == "." || arc == "..") {
00096 throw ReposUI::failure(errtext +
00097 " must not have . or .. as final arc");
00098 }
00099 }
00100
00101
00102
00103
00104 int
00105 stripPrefix(Text name, Text prefix, int& tail)
00106 {
00107 int plen = prefix.Length();
00108 if (prefix == name.Sub(0, plen)) {
00109 if (name.Length() == plen) {
00110 tail = plen;
00111 return true;
00112 }
00113 if (name[plen] == '/') {
00114 tail = plen + 1;
00115 return true;
00116 }
00117 }
00118 return false;
00119 }
00120
00121 static Text
00122 precanonicalize(const Text& name, ReposUI::RootLoc& rootloc)
00123 throw (VestaConfig::failure, ReposUI::failure)
00124 {
00125
00126
00127
00128 Text aname;
00129 try {
00130 aname = FS::GetAbsolutePath(name);
00131 }
00132 catch (FS::Failure f) {
00133 throw_errno(f.get_op() + " failed", f.get_errno());
00134 }
00135
00136
00137 Text cname = FS::RemoveSpecialArcs(aname);
00138
00139
00140
00141
00142
00143
00144
00145 cname = ReposUI::stripRoot(cname, rootloc);
00146 return cname;
00147 }
00148
00149 Text
00150 ReposUI::canonicalize(const Text& name)
00151 throw (VestaConfig::failure, ReposUI::failure)
00152 {
00153 ReposUI::RootLoc rootloc;
00154 Text cname = precanonicalize(name, rootloc);
00155 Text root;
00156 if (rootloc == ReposUI::VESTA) {
00157 root = "/vesta";
00158 } else if (rootloc == ReposUI::VESTAWORK) {
00159 root = "/vesta-work";
00160 } else {
00161 throw ReposUI::failure(name + " is not in the Vesta repository");
00162 }
00163 return root + (cname.Empty() ? "" : "/") + cname;
00164 }
00165
00166 Text
00167 ReposUI::canonicalize(const Text& name, const Text& qual)
00168 throw (VestaConfig::failure, ReposUI::failure)
00169 {
00170 ReposUI::RootLoc rootloc;
00171 Text cname = precanonicalize(qualify(name, qual), rootloc);
00172 Text root;
00173 if (rootloc == ReposUI::VESTA) {
00174 root = "/vesta";
00175 } else if (rootloc == ReposUI::VESTAWORK) {
00176 root = "/vesta-work";
00177 } else {
00178 throw ReposUI::failure(name + " is not in the Vesta repository");
00179 }
00180 return root + (cname.Empty() ? "" : "/") + cname;
00181 }
00182
00183 Text ReposUI::stripRoot(const Text& name, RootLoc& rootloc)
00184 throw (VestaConfig::failure)
00185 {
00186 int tail = 0;
00187 if (stripPrefix(name, "/vesta", tail) ||
00188 stripPrefix(name, VestaConfig::get_Text("UserInterface",
00189 "AppendableRootName"),
00190 tail)) {
00191 rootloc = ReposUI::VESTA;
00192 }
00193 else if (stripPrefix(name, "/vesta-work", tail) ||
00194 stripPrefix(name, VestaConfig::get_Text("UserInterface",
00195 "MutableRootName"),
00196 tail)) {
00197 rootloc = ReposUI::VESTAWORK;
00198 }
00199 else {
00200 rootloc = ReposUI::NOWHERE;
00201 }
00202 return name.Sub(tail);
00203 }
00204
00205
00206 Text ReposUI::stripSpecificRoot(const Text& name, RootLoc which_root,
00207 bool require_canonical)
00208 throw (ReposUI::failure, VestaConfig::failure)
00209 {
00210 int tail = 0;
00211 bool success = false;
00212 switch(which_root) {
00213 case ReposUI::VESTA:
00214 success = stripPrefix(name, "/vesta", tail);
00215 if(!success && !require_canonical) {
00216 success = stripPrefix(name,
00217 VestaConfig::get_Text("UserInterface",
00218 "AppendableRootName"),
00219 tail);
00220 }
00221 if(!success)
00222 throw ReposUI::failure(name +
00223 " is not in the appendable portion of the Vesta repository");
00224 break;
00225 case ReposUI::VESTAWORK:
00226 success = stripPrefix(name, "/vesta-work", tail);
00227 if(!success && !require_canonical) {
00228 success = stripPrefix(name,
00229 VestaConfig::get_Text("UserInterface",
00230 "MutableRootName"),
00231 tail);
00232 }
00233 if(!success)
00234 throw ReposUI::failure(name +
00235 " is not in the mutable portion of the Vesta repository");
00236 break;
00237 default:
00238 throw ReposUI::failure("stripSpecificRoot: Invalid parameter");
00239 }
00240 return name.Sub(tail);
00241 }
00242
00243 VestaSource*
00244 ReposUI::lookupCreatePath(Text root_name,
00245 Text pathname, Text hints)
00246 throw(ReposUI::failure, VestaConfig::failure, SRPC::failure)
00247 {
00248 int len = pathname.Length();
00249 VestaSource* vs_next;
00250 Text current_path = root_name;
00251
00252 VestaSource* vs_current = ReposUI::filenameToMasterVS(root_name, hints);
00253
00254 while(!pathname.Empty()) {
00255 int separator = pathname.FindChar('/');
00256
00257
00258 Text dir_name;
00259 if(separator > 0 && separator < len) {
00260 dir_name = pathname.Sub(0, separator);
00261 pathname = pathname.Sub(separator + 1);
00262 }
00263 else if(separator < 0) {
00264 dir_name = pathname;
00265 pathname = "";
00266 }
00267 else {
00268 assert(separator == 0);
00269 pathname = pathname.Sub(separator + 1);
00270 continue;
00271 }
00272 Arc arc = dir_name.cchars();
00273
00274 VestaSource::errorCode lookup_err = vs_current->lookup(arc, vs_next);
00275 if(lookup_err != VestaSource::ok) {
00276 if(lookup_err == VestaSource::notFound) {
00277
00278 VestaSource::errorCode create_err =
00279 vs_current->insertAppendableDirectory(arc, true,
00280 NULL,
00281 VestaSource::dontReplace,
00282 &vs_next);
00283 if(create_err != VestaSource::ok) {
00284 throw ReposUI::failure(current_path + "/" + arc + ": " +
00285 ReposUI::errorCodeText(create_err),
00286 -1, create_err);
00287 }
00288 }
00289 else {
00290 throw ReposUI::failure(current_path + "/" + arc + ": " +
00291 ReposUI::errorCodeText(lookup_err),
00292 -1, lookup_err);
00293 }
00294 }
00295 else {
00296
00297
00298
00299 if(!vs_next->master) {
00300 delete vs_next;
00301 try {
00302 vs_next = ReposUI::filenameToMasterVS(current_path, hints);
00303 } catch(...) {
00304
00305
00306 delete vs_current;
00307 throw;
00308 }
00309 }
00310 }
00311
00312
00313 delete vs_current;
00314 vs_current = vs_next;
00315 current_path = current_path + "/" + arc;
00316 }
00317
00318 return vs_current;
00319 }
00320
00321
00322 VestaSource*
00323 ReposUI::filenameToVS(Text name, Text host, Text port)
00324 throw (ReposUI::failure, VestaConfig::failure, SRPC::failure)
00325 {
00326 ReposUI::RootLoc rootloc;
00327 Text cname = precanonicalize(name, rootloc);
00328 VestaSource* root = NULL;
00329 bool needFree = false;
00330
00331 try {
00332 if (rootloc == ReposUI::VESTA) {
00333 if (host.Empty()) {
00334 root = VestaSource::repositoryRoot();
00335 } else {
00336 root = VDirSurrogate::repositoryRoot(host, port);
00337 needFree = true;
00338 }
00339 } else if (rootloc == ReposUI::VESTAWORK) {
00340 if (host.Empty()) {
00341 root = VestaSource::mutableRoot();
00342 } else {
00343 root = VDirSurrogate::mutableRoot(host, port);
00344 needFree = true;
00345 }
00346 } else {
00347 throw ReposUI::failure(name + " is not in the Vesta repository");
00348 }
00349
00350 VestaSource *vs;
00351 VestaSource::errorCode err =
00352 root->lookupPathname(cname.cchars(), vs);
00353 if (needFree) delete root;
00354 if (err != VestaSource::ok) {
00355 throw ReposUI::failure(name + ": " + ReposUI::errorCodeText(err),
00356 -1, err);
00357 }
00358 return vs;
00359 } catch (SRPC::failure f) {
00360 if (needFree && root) delete root;
00361 throw ReposUI::failure(name + ": SRPC failure; " + f.msg);
00362 }
00363 }
00364
00365 VestaSource*
00366 ReposUI::filenameToVS(Text name)
00367 throw (ReposUI::failure, VestaConfig::failure, SRPC::failure)
00368 {
00369 return filenameToVS(name, "", "");
00370 }
00371
00372 VestaSource*
00373 ReposUI::filenameToVS(Text name, Text hostport)
00374 throw (ReposUI::failure, VestaConfig::failure, SRPC::failure)
00375 {
00376 Text host, port;
00377 if (hostport.Empty()) {
00378 host = port = "";
00379 } else {
00380 int colon = hostport.FindChar(':');
00381 if (colon == -1) {
00382 host = hostport;
00383 port = VestaConfig::get_Text("Repository", "VestaSourceSRPC_port");
00384 } else {
00385 host = hostport.Sub(0, colon);
00386 port = hostport.Sub(colon+1);
00387 }
00388 }
00389 return filenameToVS(name, host, port);
00390 }
00391
00392
00393
00394 bool getMasterHintsCallback(void* closure, const char* value)
00395 {
00396 if(closure != 0) {
00397 Text* hints = (Text*) closure;
00398 *hints = *hints + " " + Text(value);
00399 }
00400 return true;
00401 }
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417 static VestaSource*
00418 filenameToSpecialVS(Text name, Text hints, bool needMaster)
00419 throw (ReposUI::failure, VestaConfig::failure, SRPC::failure)
00420 {
00421 Table<Text, Text>::Default tried;
00422 Text reposhost, reposport, defaultport;
00423 enum { NOTHING, NAME, COPY } found = NOTHING;
00424
00425
00426 ReposUI::RootLoc rootloc;
00427 Text cname = precanonicalize(name, rootloc);
00428 if (rootloc != ReposUI::VESTA) {
00429 throw ReposUI::failure(name + " is not under /vesta");
00430 }
00431
00432
00433 reposhost = VDirSurrogate::defaultHost();
00434 reposport = defaultport = VDirSurrogate::defaultPort();
00435
00436
00437 for (;;) {
00438
00439 tried.Put(reposhost + ":" + reposport, "");
00440
00441 VestaSource* vs = NULL;
00442 VestaSource* root = NULL;
00443 VestaSource::errorCode err = VestaSource::rpcFailure;
00444
00445 try {
00446 root = VDirSurrogate::repositoryRoot(reposhost, reposport);
00447 err = root->lookupPathname(cname.cchars(), vs);
00448 } catch (SRPC::failure f) {
00449
00450 tried.Put(reposhost + ":" + reposport, f.msg);
00451 }
00452
00453
00454 if (err == VestaSource::ok) {
00455 if (vs->master) {
00456 delete root;
00457 return vs;
00458 }
00459 tried.Put(reposhost + ":" + reposport, "Not master");
00460 if (vs->type != VestaSource::stub &&
00461 vs->type != VestaSource::ghost) {
00462 if (!needMaster) {
00463 delete root;
00464 return vs;
00465 }
00466 found = COPY;
00467 } else {
00468 if (found < NAME) found = NAME;
00469 }
00470 }
00471 else {
00472 Text reason;
00473 bool inTable = tried.Get(reposhost + ":" + reposport, reason);
00474 if(!inTable || (inTable && reason == ""))
00475 tried.Put(reposhost + ":" + reposport, ReposUI::errorCodeText(err));
00476 }
00477
00478
00479
00480 Text head = cname;
00481 reposhost = reposport = "";
00482 if (root) {
00483 for (;;) {
00484 if (vs) {
00485 vs->getAttrib("master-repository",
00486 getMasterHintsCallback, (void*) &hints);
00487 delete vs;
00488 vs = NULL;
00489 }
00490 int slash = head.FindCharR('/');
00491 if (slash == -1) break;
00492 head = head.Sub(0, slash);
00493 err = root->lookupPathname(head.cchars(), vs);
00494 }
00495 delete root;
00496 }
00497
00498
00499 for (;;) {
00500 int i = 0;
00501 while (hints[i] == ',' || hints[i] == ' ') i++;
00502 if (hints[i] == '\000') break;
00503 int j = i;
00504 while (hints[j] && hints[j] != ',' && hints[j] != ' ') j++;
00505 Text mht = hints.Sub(i, j - i);
00506 hints = hints.Sub(j);
00507 Text dummy;
00508 if (!tried.Get(mht, dummy)) {
00509
00510 int colon = mht.FindCharR(':');
00511 if(colon > 0) {
00512 reposhost = mht.Sub(0, colon);
00513 reposport = mht.Sub(colon+1);
00514 }
00515 else {
00516 reposhost = mht;
00517 reposport = defaultport;
00518 }
00519 break;
00520 }
00521 }
00522
00523
00524 if (reposhost.Empty()) {
00525 Text msg;
00526 switch (found) {
00527 case NOTHING:
00528 msg = "can't find ";
00529 break;
00530 case NAME:
00531 msg = "can't find a replica of ";
00532 break;
00533 case COPY:
00534 msg = "can't find master replica of ";
00535 break;
00536 }
00537 msg = msg + name + " (tried:";
00538 Table<Text, Text>::Iterator iter(&tried);
00539 Text repname;
00540 Text repreason;
00541 while (iter.Next(repname, repreason)) {
00542 msg = msg + " " + repname;
00543 if(needMaster && repreason != "") {
00544 msg = msg + " [" + repreason + "]";
00545 }
00546 }
00547 throw ReposUI::failure(msg + ")");
00548 }
00549
00550
00551 }
00552 }
00553
00554 VestaSource*
00555 ReposUI::filenameToMasterVS(Text name, Text hints)
00556 throw (ReposUI::failure, VestaConfig::failure, SRPC::failure)
00557 {
00558 return filenameToSpecialVS(name, hints, true);
00559 }
00560
00561 VestaSource*
00562 ReposUI::filenameToRealVS(Text name, Text hints)
00563 throw (ReposUI::failure, VestaConfig::failure, SRPC::failure)
00564 {
00565 return filenameToSpecialVS(name, hints, false);
00566 }
00567
00568 Text
00569 ReposUI::vsToFilename(VestaSource* vs)
00570 throw (ReposUI::failure, VestaConfig::failure, SRPC::failure)
00571 {
00572 Text ret = "";
00573 LongId longid_par;
00574 unsigned int index;
00575 VestaSource* vs_cur = vs;
00576 VestaSource* vs_par;
00577 VestaSource* vs_junk;
00578 VestaSource::errorCode err;
00579 char arcbuf[MAX_ARC_LEN+1];
00580 for (;;) {
00581 if (vs_cur->longid == RootLongId) {
00582 ret = "/vesta/" + ret;
00583 break;
00584 } else if (vs_cur->longid == MutableRootLongId) {
00585 ret = "/vesta-work/" + ret;
00586 break;
00587 }
00588 longid_par = vs_cur->longid.getParent(&index);
00589 if (longid_par == NullLongId) {
00590 throw ReposUI::failure("cannot find filename for given longid");
00591 }
00592 vs_par = VDirSurrogate::
00593 LongIdLookup(longid_par, vs_cur->host(), vs_cur->port());
00594 assert(vs_par != NULL);
00595 err = vs_par->lookupIndex(index, vs_junk, arcbuf);
00596 assert(err == VestaSource::ok);
00597 delete vs_junk;
00598 ret = Text(arcbuf) + "/" + ret;
00599 if (vs_cur != vs) delete vs_cur;
00600 vs_cur = vs_par;
00601 }
00602
00603 ret = ret.Sub(0, ret.Length() - 1);
00604 if (vs_cur != vs) delete vs_cur;
00605 return ret;
00606 }
00607
00608 Text
00609 ReposUI::getMasterHintDir(VestaSource* vs, const Text& cname)
00610 throw (ReposUI::failure, SRPC::failure)
00611 {
00612 if(!vs->master)
00613 throw ReposUI::failure("Not master", -1, VestaSource::notMaster);
00614
00615 VestaSource* vs_cur = vs;
00616 VestaSource* vs_parent = NULL;
00617 Text hint_dir = cname;
00618 while(vs_cur) {
00619
00620 vs_parent = vs_cur->getParent();
00621 if(vs_parent) {
00622
00623 if(!vs_parent->master)
00624 break;
00625
00626 if(vs_cur != vs)
00627 delete vs_cur;
00628 vs_cur = vs_parent;
00629
00630 int slash = hint_dir.FindCharR('/');
00631 assert(slash != -1);
00632 hint_dir = hint_dir.Sub(0, slash);
00633 }
00634 else
00635 break;
00636 }
00637
00638 if(vs_parent)
00639 delete vs_parent;
00640 if(vs_cur && vs_cur != vs)
00641 delete vs_cur;
00642
00643 return hint_dir;
00644 }
00645
00646 struct verclosure {
00647 long high;
00648 VestaSource* parent;
00649 Text hints;
00650 Text path;
00651 };
00652
00653 static bool
00654 vercallback(void* closure, VestaSource::typeTag type, Arc arc,
00655 unsigned int index, Bit32 pseudoInode, ShortId filesid,bool master)
00656 {
00657 verclosure* cl = (verclosure*) closure;
00658 char* endptr;
00659 long val = strtol(arc, &endptr, 10);
00660 if (*endptr == '\0' && val > cl->high) {
00661 if(type != VestaSource::stub) {
00662 cl->high = val;
00663 } else if(!master) {
00664 try {
00665 if(cl->path == "")
00666 cl->path = ReposUI::vsToFilename(cl->parent);
00667 Text name = cl->path + "/" + arc;
00668 VestaSource* vs_real = ReposUI::filenameToRealVS(name, cl->hints);
00669 if(vs_real->type != VestaSource::stub)
00670 cl->high = val;
00671 delete vs_real;
00672 } catch(ReposUI::failure) {
00673
00674 }
00675 }
00676 }
00677 return true;
00678 }
00679
00680 long
00681 ReposUI::highver(VestaSource* vs, Text hints, Text path)
00682 throw (ReposUI::failure, SRPC::failure)
00683 {
00684
00685
00686
00687
00688
00689 verclosure cl;
00690 cl.high = -1;
00691 cl.parent = vs;
00692 cl.hints = hints;
00693 cl.path = path;
00694
00695 VestaSource::errorCode err = vs->list(0, vercallback, &cl);
00696 if (err != VestaSource::ok) {
00697 throw ReposUI::failure("error listing directory of versions: " +
00698 ReposUI::errorCodeText(err), -1, err);
00699 }
00700 return cl.high;
00701 }
00702
00703 Text
00704 ReposUI::uniquify(VestaSource* vs, const Text& prefix)
00705 throw (ReposUI::failure, SRPC::failure)
00706 {
00707 char buf[64];
00708 Text result;
00709 unsigned int suffix = 1;
00710 while(1)
00711 {
00712
00713 sprintf(buf, "%d", suffix);
00714 result = prefix + "." + buf;
00715
00716 VestaSource *kid = 0;
00717 VestaSource::errorCode err = vs->lookup(result.cchars(), kid);
00718
00719 if(err == VestaSource::notFound)
00720 {
00721 break;
00722 }
00723
00724 else if(err == VestaSource::ok)
00725 {
00726 assert(kid != 0);
00727 delete kid;
00728 }
00729
00730 else
00731 {
00732 throw ReposUI::failure("error on lookup for generating unique name: " +
00733 ReposUI::errorCodeText(err), -1, err);
00734 }
00735
00736 suffix++;
00737 }
00738
00739 return result;
00740 }
00741
00742 struct changed_closure {
00743 bool changed;
00744 VestaSource* vs;
00745 time_t since;
00746 };
00747
00748 static bool
00749 changed_callback(void* closure, VestaSource::typeTag type,
00750 Arc arc, unsigned int index, Bit32 pseudoInode,
00751 ShortId filesid, bool master)
00752 {
00753 changed_closure* cl = (changed_closure*) closure;
00754 assert(!cl->changed);
00755 switch (type) {
00756 case VestaSource::mutableFile:
00757 cl->changed = true;
00758 break;
00759 case VestaSource::mutableDirectory:
00760 {
00761 VestaSource* vs;
00762 VestaSource::errorCode err = cl->vs->lookupIndex(index, vs);
00763 if (err != VestaSource::ok) {
00764 throw ReposUI::failure("error on lookupIndex: " +
00765 ReposUI::errorCodeText(err), -1 , err);
00766 }
00767 cl->changed = ReposUI::changed(vs, cl->since);
00768 }
00769 break;
00770 default:
00771 break;
00772 }
00773 return !cl->changed;
00774 }
00775
00776 bool
00777 ReposUI::changed(VestaSource* vs, time_t since)
00778 throw (ReposUI::failure, SRPC::failure)
00779 {
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789 if (vs->timestamp() > since) return true;
00790 changed_closure cl;
00791 cl.changed = false;
00792 cl.vs = vs;
00793 cl.since = since;
00794 VestaSource::errorCode err = vs->list(0, changed_callback, &cl);
00795 if (err != VestaSource::ok) {
00796 throw ReposUI::failure("error listing directory: " +
00797 ReposUI::errorCodeText(err), -1 , err);
00798 }
00799 return cl.changed;
00800 }
00801
00802 struct cleanup_closure {
00803 VestaSource* vs;
00804 Sequence<Text>* pattern;
00805 size_t maxsize;
00806 Text prefix;
00807 };
00808
00809 static bool
00810 cleanup_callback(void* closure, VestaSource::typeTag type,
00811 Arc arc, unsigned int index, Bit32 pseudoInode,
00812 ShortId filesid, bool master)
00813 {
00814 cleanup_closure* cl = (cleanup_closure*) closure;
00815 VestaSource* vs;
00816 VestaSource::errorCode err = cl->vs->lookupIndex(index, vs);
00817 if (err != VestaSource::ok) {
00818 throw ReposUI::failure("error on lookupIndex: " +
00819 ReposUI::errorCodeText(err), -1 , err);
00820 }
00821
00822 switch (type) {
00823 case VestaSource::mutableFile:
00824 case VestaSource::immutableFile: {
00825 int i;
00826 for (i=0; i<cl->pattern->size(); i++) {
00827 if (fnmatch(cl->pattern->get(i).cchars(), arc, 0) == 0) {
00828 err = cl->vs->reallyDelete(arc);
00829 if (err != VestaSource::ok) {
00830 throw ReposUI::failure("error deleting " + cl->prefix + arc + ": " +
00831 ReposUI::errorCodeText(err), -1 , err);
00832 }
00833 return true;
00834 }
00835 }
00836 if (type == VestaSource::mutableFile && vs->size() > cl->maxsize) {
00837 throw(ReposUI::failure(cl->prefix + arc + " is " +
00838 Basics::FormatUnitVal(vs->size()) +
00839 " which is over the max file size safety limit (" +
00840 Basics::FormatUnitVal(cl->maxsize) + ")"));
00841 }
00842 break; }
00843
00844 case VestaSource::mutableDirectory: {
00845 cleanup_closure cl2 = *cl;
00846 cl2.vs = vs;
00847 cl2.prefix = cl->prefix + arc + "/";
00848 err = vs->list(0, cleanup_callback, &cl2);
00849 if (err != VestaSource::ok) {
00850 throw ReposUI::failure("error listing directory " + cl->prefix +
00851 ReposUI::errorCodeText(err), -1 , err);
00852 }
00853 break; }
00854
00855 default:
00856 break;
00857 }
00858 return true;
00859 }
00860
00861 void
00862 ReposUI::cleanup(VestaSource* vs, const Text& pattern,
00863 unsigned long maxsize, const Text& prefix)
00864 throw (ReposUI::failure, SRPC::failure)
00865 {
00866 cleanup_closure cl;
00867 cl.vs = vs;
00868 int i = 0;
00869 cl.pattern = NEW(Sequence<Text>);
00870 for (;;) {
00871 while (pattern[i] && pattern[i] == ' ') i++;
00872 if (!pattern[i]) break;
00873 int j = i;
00874 while (pattern[j] && pattern[j] != ' ') j++;
00875 cl.pattern->addhi(pattern.Sub(i, j-i));
00876 i = j;
00877 }
00878 cl.maxsize = maxsize;
00879 cl.prefix = prefix;
00880 int len = prefix.Length();
00881 if (len == 0 || prefix[len - 1] != '/') {
00882 cl.prefix = cl.prefix + "/";
00883 }
00884 VestaSource::errorCode err = vs->list(0, cleanup_callback, &cl);
00885 if (err != VestaSource::ok) {
00886 throw ReposUI::failure("error listing directory " + prefix +
00887 ReposUI::errorCodeText(err), -1 , err);
00888 }
00889 }
00890
00891 const Text
00892 ReposUI::errorCodeText(VestaSource::errorCode err) throw (ReposUI::failure)
00893 {
00894 switch (err) {
00895 case VestaSource::ok:
00896 return "No error";
00897 case VestaSource::notFound:
00898 return "Not found";
00899 case VestaSource::noPermission:
00900 return "No permission";
00901 case VestaSource::nameInUse:
00902 return "Name in use";
00903 case VestaSource::inappropriateOp:
00904 return "Operation not available on given source type";
00905 case VestaSource::nameTooLong:
00906 return "Name too long";
00907 case VestaSource::rpcFailure:
00908 return "Remote call from repository to another server failed";
00909 case VestaSource::notADirectory:
00910 return "Not a directory";
00911 case VestaSource::isADirectory:
00912 return "Is a directory";
00913 case VestaSource::invalidArgs:
00914 return "Invalid argument";
00915 case VestaSource::outOfSpace:
00916 return "Out of disk space";
00917 case VestaSource::notMaster:
00918 return "Not master replica";
00919 case VestaSource::longIdOverflow:
00920 return "LongId overflow (directories nested too deeply)";
00921 default: {
00922 const char *str = VestaSource::errorCodeString(err);
00923 if (str) {
00924 return Text("VestaSource::") + str;
00925 } else {
00926 throw ReposUI::failure("unknown VestaSource error code",
00927 -1, err);
00928 }
00929 }
00930 }
00931
00932 }
00933
00934 static Text promptMessage(bool interactive,
00935 const Text &description) throw (ReposUI::failure)
00936 {
00937 Text message;
00938
00939 char buf[1024];
00940 int i = 0;
00941 int c;
00942 bool start_of_line = true;
00943 bool dot_starts_line = false;
00944 bool broke_with_dot = false;
00945 if (interactive) {
00946 cout << "Enter " << description
00947 << ", terminated with ^D or . on a line by itself\n"
00948 << ": " << flush;
00949 }
00950 while ((c = cin.get()) != EOF) {
00951 if (c == '\000') {
00952 throw ReposUI::failure(description + " contains a NUL character",
00953 -1, VestaSource::invalidArgs);
00954 }
00955 if (c == '.' && start_of_line == true) {
00956 dot_starts_line = true;
00957 }
00958 else if(c != '\n') {
00959 dot_starts_line = false;
00960 }
00961 start_of_line = false;
00962 buf[i++] = c;
00963 if (interactive && c == '\n') {
00964 if ( dot_starts_line == true ) {
00965 broke_with_dot = true;
00966 break;
00967 }
00968 cout << ": " << flush;
00969 start_of_line = true;
00970 }
00971 if (i == sizeof(buf) - 1) {
00972 buf[i] = '\000';
00973 message = message + buf;
00974 i = 0;
00975 }
00976 }
00977
00978 buf[i] = '\000';
00979 message = message + buf;
00980
00981 if(broke_with_dot) message = message.Sub(0, message.Length()-3);
00982 if (interactive) cout << endl;
00983
00984 return message;
00985 }
00986
00987 Text ReposUI::getMessage(const Text &description,
00988 const Text &in_description,
00989 const char* in_message)
00990 throw (ReposUI::failure, FS::Failure)
00991 {
00992 Text message;
00993 char* editor = NULL;
00994 bool interactive = isatty(0);
00995 editor = getenv("EDITOR");
00996 if(!editor)
00997 {
00998 if(interactive && in_message)
00999 cout << in_description << ":" << endl
01000 << in_message << endl;
01001 message = promptMessage(interactive, description);
01002 }
01003 else
01004 {
01005 char temp_name[15] = "/tmp/msgXXXXXX";
01006 int temp_fd = mkstemp(temp_name);
01007 if(temp_fd == -1)
01008 throw ReposUI::failure("error creating a temp file for editing "+
01009 description);
01010
01011 {
01012 int close_res;
01013 do
01014 close_res = ::close(temp_fd);
01015 while((close_res != 0) && (errno == EINTR));
01016 }
01017 Text fname = (Text)temp_name;
01018
01019 Text default_msg = ("<enter " + description + " here>");
01020 ofstream ofs;
01021 FS::OpenForWriting(fname, ofs);
01022 if(in_message)
01023 FS::Write(ofs, (char *) in_message, strlen(in_message));
01024 else
01025 FS::Write(ofs, default_msg.chars(), default_msg.Length());
01026 FS::Close(ofs);
01027
01028 cout << "Running " << editor << " to edit " << description << endl;
01029 Text cmd = (Text)editor + " " + fname;
01030 if(system(cmd.chars()) != 0) {
01031 throw ReposUI::failure("error running editor " + (Text)editor);
01032 }
01033
01034 ifstream ifs;
01035 FS::OpenReadOnly(fname, ifs);
01036 char buff[1024];
01037 try {
01038 while(!FS::AtEOF(ifs)) {
01039 memset(buff,0, 1024);
01040 FS::Read(ifs, buff, 1023);
01041 message = message + (Text)buff;
01042 }
01043 }
01044 catch(FS::EndOfFile) {
01045 message = message + (Text)buff;
01046 }
01047 FS::Close(ifs);
01048 FS::Delete(fname);
01049
01050
01051 if(message == default_msg)
01052 message = "";
01053 else
01054 {
01055
01056 int i = 0;
01057 while((i < message.Length()) && isspace(message[i]))
01058 i++;
01059 if(i == message.Length())
01060 message = "";
01061 }
01062
01063
01064
01065
01066 if(message.Empty() ||
01067 (in_message && message == in_message))
01068 {
01069 if(message.Empty())
01070 cout << "Empty " << description;
01071 else
01072 cout << in_description << " unedited for " << description;
01073 cout << ". Continue (y/n)? " << flush;
01074
01075 char buff[20];
01076 cin.getline(buff, sizeof(buff));
01077 if((buff[0] != 'y') && (buff[0] != 'Y'))
01078 {
01079 throw ReposUI::failure("user aborted after editing " +
01080 description);
01081 }
01082 }
01083 }
01084 return message;
01085 }
01086
01087 Text ReposUI::prevVersion(VestaSource* vs, Text hints)
01088 throw (failure, SRPC::failure)
01089 {
01090 VestaSource* parent = NULL;
01091 VestaSource* version = vs;
01092 VestaSource *cvs;
01093
01094
01095 if(hints == "" && VestaConfig::is_set("UserInterface", "DefaultHints")) {
01096 hints = VestaConfig::get_Text("UserInterface", "DefaultHints");
01097 }
01098
01099
01100 while(1) {
01101 char *content = version->getAttrib("content");
01102 if(!content) break;
01103
01104
01105 try {
01106 cvs = filenameToRealVS(content, hints);
01107 } catch(...) {
01108 cvs = NULL;
01109 }
01110
01111
01112 if(cvs) {
01113 assert(version != cvs);
01114 if(version != vs) delete version;
01115 version = cvs;
01116 delete [] content;
01117 continue;
01118 }
01119
01120
01121
01122 Text parent_t, arc;
01123 split(content, parent_t, arc, "malformed content attribute");
01124 try {
01125 parent = filenameToRealVS(parent_t, hints);
01126 } catch(...) {
01127
01128 }
01129 delete [] content;
01130 break;
01131 }
01132
01133
01134
01135 if(parent == NULL || !parent->inAttribs("type", "session")) {
01136 parent = version->getParent();
01137 }
01138 char *ov = NULL;
01139 Text r("");
01140 if(parent->inAttribs("type", "session")) {
01141 ov = parent->getAttrib("old-version");
01142 }
01143 if(ov) {
01144 r = ov;
01145 } else if(ov = version->getAttrib("old-version")) {
01146
01147 r = ov;
01148 } else if(parent->inAttribs("type", "package") || parent->inAttribs("type", "branch")) {
01149
01150
01151
01152 ov = parent->getAttrib("old-version");
01153 if(ov) r = ov;
01154 } else {
01155
01156 }
01157
01158
01159 delete parent;
01160 if(ov) delete [] ov;
01161 if(version != vs) delete version;
01162 return r;
01163 }