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
00028 #include <unistd.h>
00029 #include <stdio.h>
00030 #include <ctype.h>
00031 #include <errno.h>
00032
00033 #include <Basics.H>
00034 #include <sys/types.h>
00035 #include <dirent.h>
00036
00037 #include <FS.H>
00038 #include <Generics.H>
00039 #include <VestaConfig.H>
00040 #include <VestaSource.H>
00041 #include <UniqueId.H>
00042 #include <ParseImports.H>
00043 #include <ReposUI.H>
00044
00045 using std::ostream;
00046 using std::ofstream;
00047 using std::ifstream;
00048 using std::cout;
00049 using std::cin;
00050 using std::cerr;
00051 using std::endl;
00052
00053
00054 static const int Silent = 0;
00055 static const int ChangesOnly = 1;
00056 static const int Verbose = 2;
00057
00058
00059 static bool doWork = true;
00060 static bool query = false;
00061 static int verbose = ChangesOnly;
00062 static bool update_local = false;
00063 static bool update_co = false;
00064 static bool advance_co = false;
00065 static bool onlyMine = false;
00066 static bool toCheckout = false;
00067 static bool parse_errors_stop = false;
00068 static TextSeq limiters;
00069 static char *attrName = NULL;
00070 static TextSeq *attrVals = NULL;
00071 static bool attrMatchSense;
00072
00073 static void Option(char *flag, char *meaning) throw ()
00074 {
00075 cerr << " " << flag;
00076 int pad = max(0, 23 - strlen(flag));
00077 for (int i = 0; i < pad; i++) cerr << ' ';
00078 cerr << ' ' << meaning << endl;
00079 }
00080
00081 static void Syntax(char *msg, char *arg = (char *)NULL) throw ()
00082 {
00083 cerr << "Error: " << msg;
00084 if (arg != (char *)NULL) cerr << ": '" << arg << "'";
00085 cerr << endl;
00086 cerr << "Syntax: vupdate [ options ] [ model ]" << endl;
00087 cerr << "Possible options are:" << endl;
00088 Option("-n or -no-action", "do not change any models");
00089 Option("-q or -query", "ask user about each update");
00090 Option("-s or -silent", "do not report updates");
00091 Option("-r or -update-local",
00092 "recurse on all models imported locally");
00093 Option("-c or -update-checkout",
00094 "recurse on all models imported from checkout sessions");
00095 Option("-u or -update-all", "set both -update-local and -update-checkout");
00096 Option("-a or -advance-checkout", "advance imported checkout sessions");
00097 Option("-v or -verbose", "report on all processed models");
00098 Option("-l or -limit substring",
00099 "only update imports containing 'substring'");
00100 Option("-L or -limit-checkout",
00101 "only update imports from checkout sessions");
00102 Option("-t or -to-checkout", "update to a more recent checkout session");
00103 Option("-m or -only-mine", "update a checkout import only if owned by me");
00104 Option("-A or -attr attr-spec",
00105 "only update to versions with given attributes");
00106 Option("-e or -parse-errors-stop",
00107 "stop if on import paths we can't understand (non-numeric arcs)");
00108 exit(1);
00109 }
00110
00111 static void Indent(ostream &os, int n) throw ()
00112 {
00113 for (; n > 0; n--) os << ' ';
00114 }
00115
00116 static void CopyFromTo(ifstream &ifs, ofstream &ofs, int loc = -1)
00117 throw (FS::Failure)
00118
00119
00120
00121 {
00122 const int BuffSz = 1024;
00123 char buff[BuffSz];
00124 int toCopy = (loc >= 0) ? (loc - (int)FS::Posn(ifs)) : (-1);
00125 assert(loc < 0 || toCopy >= 0);
00126 try {
00127 while (loc < 0 || toCopy > 0) {
00128 int cnt = (loc < 0) ? BuffSz : min(BuffSz, toCopy);
00129 FS::Read(ifs, buff, cnt);
00130 FS::Write(ofs, buff, cnt);
00131 if (loc >= 0) toCopy -= cnt;
00132 }
00133 }
00134 catch (FS::EndOfFile) {
00135
00136
00137 assert(loc < 0);
00138
00139
00140 FS::Write(ofs, buff, ifs.gcount());
00141 }
00142 }
00143
00144
00145 static const Text AppendableRootName = "/vesta/";
00146 static VestaSource *RepositoryRoot;
00147
00148
00149 class VUpdateInit {
00150 public:
00151 VUpdateInit() throw () {
00152 RepositoryRoot = VestaSource::repositoryRoot();
00153 }
00154 };
00155 static VUpdateInit vupdateInit;
00156
00157 static bool ImmutableDir(const Text &path) throw (SRPC::failure)
00158
00159
00160 {
00161 if (path.FindText(AppendableRootName) != 0) return false;
00162 Text relPath(path.Sub(AppendableRootName.Length()));
00163 VestaSource *res = 0;
00164 VestaSource::errorCode err =
00165 RepositoryRoot->lookupPathname(relPath.cchars(), res);
00166 bool result = ((err == VestaSource::ok) &&
00167 (res->type == VestaSource::immutableDirectory));
00168 if(res != 0)
00169 delete res;
00170 return result;
00171 }
00172
00173 static void VersionDir(const Text &path, int &prefixLen,
00174 int &suffixStart, int &verNum,
00175 bool &checkedIn, bool doStat)
00176 throw (ParseImports::Error, SRPC::failure)
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211 {
00212 Text verTxt;
00213
00214
00215 int len = path.Length();
00216 Text pattern("/checkout/");
00217 int ckout = path.FindText(pattern);
00218 if (ckout >= 0) {
00219
00220 int ckoutEnd = ckout + pattern.Length();
00221 prefixLen = path.FindChar('/', ckoutEnd);
00222 if (prefixLen < 0) {
00223 Text msg("malformed checkout session name: '");
00224 msg += path; msg += "'";
00225 throw ParseImports::Error(msg);
00226 }
00227 Text ckoutVer(path.Sub(ckoutEnd, prefixLen - ckoutEnd));
00228 prefixLen++;
00229
00230
00231
00232 suffixStart = path.FindChar('/', prefixLen);
00233 if (suffixStart < 0) suffixStart = len;
00234 verTxt = path.Sub(prefixLen, suffixStart - prefixLen);
00235
00236
00237 if (doStat) {
00238 Text dirName(path.Sub(0, ckout + 1) + ckoutVer);
00239 checkedIn = ImmutableDir(dirName);
00240 }
00241 if (checkedIn) {
00242 prefixLen = ckout + 1;
00243 verTxt = ckoutVer;
00244 }
00245 } else {
00246
00247 prefixLen = 0;
00248 do {
00249 if (path[prefixLen] == '/') prefixLen++;
00250 int curr = prefixLen;
00251 while (curr < len && isdigit((int)(path[curr]))) curr++;
00252 if (curr >= len || path[curr] == '/') {
00253 suffixStart = curr;
00254 break;
00255 }
00256 prefixLen = path.FindChar('/', prefixLen);
00257 } while (prefixLen >= 0);
00258 if (prefixLen < 0 || prefixLen >= len) {
00259 Text msg("no numeric arc: '"); msg += path; msg += "'";
00260 throw ParseImports::Error(msg);
00261 }
00262 verTxt = path.Sub(prefixLen, suffixStart - prefixLen);
00263 }
00264
00265 if (sscanf(verTxt.chars(), "%d", &verNum) != 1) {
00266
00267 Text msg("version not numeric: '"); msg += verTxt; msg += "'";
00268 throw ParseImports::Error(msg);
00269 }
00270 }
00271
00272 static bool AllDigits(const char *str) throw ()
00273
00274 {
00275 for (; *str != '\0'; str++) {
00276 if (!isdigit((int)(*str))) return false;
00277 }
00278 return true;
00279 }
00280
00281 static bool AttribMatch(VestaSource *child) throw (SRPC::failure)
00282
00283
00284
00285 {
00286
00287 const char *attrVal = child->getAttrib(attrName);
00288 if (attrVal != (char *)NULL) {
00289 delete [] attrVal;
00290
00291 if (attrVals != (TextSeq *)NULL) {
00292 for (int i = 0; i < attrVals->size(); i++) {
00293 Text val = attrVals->get(i);
00294 if (child->inAttribs(attrName, val.cchars())) {
00295 return attrMatchSense;
00296 }
00297 }
00298
00299 return !attrMatchSense;
00300 } else {
00301
00302 return true;
00303 }
00304 } else {
00305
00306 return (attrVals != NULL && !attrMatchSense);
00307 }
00308 }
00309
00310
00311 typedef struct {
00312 int max;
00313 VestaSource *parent;
00314 } CallbackClosure;
00315
00316 static bool ListCallback(void *closure, VestaSource::typeTag type,
00317 Arc arc, unsigned int index, Bit32 pseudoInode, ShortId filesid, bool master)
00318 throw (SRPC::failure)
00319
00320
00321
00322
00323 {
00324 CallbackClosure *cc = (CallbackClosure *)closure;
00325 if (type == VestaSource::immutableDirectory && AllDigits(arc)) {
00326 if (attrName != (char *)NULL) {
00327
00328 VestaSource *child = 0;
00329 VestaSource::errorCode err =
00330 cc->parent->lookupIndex(index, child);
00331 bool no_good = (err != VestaSource::ok || !AttribMatch(child));
00332 if (child != NULL) delete child;
00333 if(no_good)
00334 return true;
00335 }
00336 int val;
00337 int scanRes = sscanf(arc, "%d", &val);
00338 assert(scanRes == 1);
00339 if (val > cc->max) cc->max = val;
00340 }
00341 return true;
00342 }
00343
00344 static int HighestVersion(const Text &path)
00345 throw (ParseImports::Error, SRPC::failure)
00346
00347
00348
00349 {
00350
00351 if (path.FindText(AppendableRootName) != 0) {
00352 Text errMsg("imported path not in appendable root: ");
00353 errMsg += path;
00354 throw ParseImports::Error(errMsg);
00355 }
00356 Text relPath(path.Sub(AppendableRootName.Length()));
00357 VestaSource *dir = 0;
00358 VestaSource::errorCode err =
00359 RepositoryRoot->lookupPathname(relPath.cchars(), dir);
00360 if (err != VestaSource::ok) {
00361 Text errMsg("unable to open directory in import: ");
00362 errMsg += path;
00363 throw ParseImports::Error(errMsg);
00364 } else if (dir->type != VestaSource::appendableDirectory) {
00365 delete dir;
00366 Text errMsg("package import is not an appendable directory: ");
00367 errMsg += path;
00368 throw ParseImports::Error(errMsg);
00369 }
00370
00371 assert(dir != 0);
00372
00373
00374 CallbackClosure cc;
00375 cc.max = -1;
00376 cc.parent = dir;
00377 err = dir->list( 0, ListCallback, (void *)(&cc));
00378
00379
00380 delete dir;
00381 return cc.max;
00382 }
00383
00384 static bool OthersCheckout(const Text &p_path)
00385
00386
00387 {
00388
00389 Text pattern("/checkout/");
00390
00391 int ckout = p_path.FindText(pattern);
00392 int ckoutEnd = ckout + pattern.Length();
00393 int l_session_len;
00394
00395
00396 bool result = false;
00397
00398 if ((ckout >= 0) &&
00399
00400 ((l_session_len = p_path.FindChar('/', ckoutEnd)) >= 0))
00401 {
00402 Text l_session_path = p_path.Sub(0, l_session_len);
00403
00404 VestaSource *l_session = NULL;
00405 try {
00406 l_session = ReposUI::filenameToVS(l_session_path);
00407 } catch (...) {
00408
00409
00410
00411 }
00412
00413 if ((l_session != NULL) && l_session->inAttribs("type", "session")) {
00414
00415 Text l_uspec(AccessControl::self()->user());
00416
00417
00418
00419
00420 char *l_checkout_user = l_session->getAttrib("checkout-by");
00421 if((l_checkout_user == 0) ||
00422 (strcmp(l_uspec.cchars(), l_checkout_user) != 0))
00423 result = true;
00424 if(l_checkout_user != 0)
00425 delete [] l_checkout_user;
00426 }
00427 if(l_session != 0)
00428 delete l_session;
00429 }
00430
00431 return result;
00432 }
00433
00434 static void Update(const Text &model, TextSeq &localModels)
00435 throw (FS::DoesNotExist, FS::Failure, ParseImports::Error,
00436 VestaConfig::failure, SRPC::failure);
00437
00438 static void RecurseOnCheckout(const Text &p_path, const Text& suffix)
00439 throw (FS::DoesNotExist, FS::Failure, ParseImports::Error,
00440 VestaConfig::failure, SRPC::failure)
00441
00442
00443
00444 {
00445 if (!advance_co && !update_co) return;
00446
00447
00448 Text pattern("/checkout/");
00449
00450 int ckout = p_path.FindText(pattern);
00451 int ckoutEnd = ckout + pattern.Length();
00452 int l_session_len;
00453
00454
00455 if (ckout < 0 || (l_session_len = p_path.FindChar('/', ckoutEnd)) < 0) {
00456
00457 return;
00458 }
00459
00460 Text l_session_path = p_path.Sub(0, l_session_len);
00461
00462 VestaSource *l_session = NULL;
00463 try {
00464 l_session = ReposUI::filenameToVS(l_session_path);
00465 } catch (...) {
00466
00467
00468
00469 }
00470 if (l_session == NULL || !l_session->inAttribs("type", "session")) {
00471
00472 return;
00473 }
00474
00475
00476 Text l_uspec(AccessControl::self()->user());
00477
00478
00479
00480 char *l_checkout_user = l_session->getAttrib("checkout-by");
00481 bool not_my_checkout = ((l_checkout_user == 0) ||
00482 (strcmp(l_uspec.cchars(), l_checkout_user) != 0));
00483 if(l_checkout_user != 0)
00484 delete [] l_checkout_user;
00485 if(not_my_checkout)
00486 {
00487 delete l_session;
00488 return;
00489 }
00490
00491
00492 char *l_workdir = l_session->getAttrib("work-dir");
00493 delete l_session;
00494 if (l_workdir == NULL) return;
00495
00496
00497
00498
00499 if (!FS::IsDirectory(l_workdir))
00500 {
00501 if (verbose != Silent)
00502 cerr << "vupdate: the working directory of checkout session "
00503 << p_path << " (" << l_workdir
00504 << ("), no longer exists! "
00505 "Skipping recursive updates for this import.")
00506 << endl;
00507 delete [] l_workdir;
00508 return;
00509 }
00510
00511
00512 Text w_path = Text(l_workdir) + (suffix.Empty() ? "/build.ves" : suffix);
00513
00514
00515 if (update_co) {
00516 if (query) {
00517 cout << " Update \"" << w_path << "\" (y/n)? ";
00518 cout.flush();
00519 const int BuffLen = 20; char buff[BuffLen];
00520 cin.getline(buff, BuffLen);
00521 if (buff[0] != 'y')
00522 {
00523 delete [] l_workdir;
00524 return;
00525 }
00526 }
00527
00528
00529 TextSeq localModels( 10);
00530 Update(w_path, localModels);
00531 while (localModels.size() > 0) {
00532
00533 Text model = localModels.remlo();
00534 Update(model, localModels);
00535 }
00536 }
00537
00538
00539
00540 if (doWork) {
00541 Text cmd = Text("vadvance ") + l_workdir;
00542 system(cmd.cchars());
00543 }
00544 delete [] l_workdir;
00545 }
00546
00547 static bool NewVersion(const Import *imp, Text &newPath,
00548 Text &toWrite)
00549 throw (FS::DoesNotExist, FS::Failure, ParseImports::Error,
00550 VestaConfig::failure, SRPC::failure)
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561 {
00562 int prefixLen, suffixPos, verNum;
00563 bool checkedIn = false;
00564 char buff[20];
00565
00566
00567 int pathLen = imp->path.Length();
00568 VersionDir(imp->path, prefixLen, suffixPos,
00569 verNum, checkedIn, true);
00570 Text verDir(imp->path.Sub(0, prefixLen));
00571 Text suffix(imp->path.Sub(suffixPos));
00572
00573
00574 if (!checkedIn) RecurseOnCheckout(imp->path, suffix);
00575
00576
00577 int newVerNum = HighestVersion(verDir);
00578
00579
00580 if (verNum > newVerNum) {
00581
00582
00583
00584
00585 if (attrName != (char *)NULL) return false;
00586
00587
00588
00589 Text msg("version number exceeds largest existing version:\n ");
00590 msg += imp->path;
00591 throw ParseImports::Error(msg);
00592 }
00593
00594
00595
00596
00597
00598
00599 bool newest_is_checkout = false;
00600 Text l_session_path;
00601 int l_session_ver_num;
00602 if (toCheckout) {
00603 int printRes = sprintf(buff, "%d", newVerNum + 1);
00604 assert(printRes > 0);
00605 Text newVerTxt(buff);
00606
00607 Text l_stub_path = verDir + newVerTxt;
00608
00609 VestaSource *l_stub = 0;
00610
00611
00612
00613 try
00614 {
00615 l_stub = ReposUI::filenameToVS(l_stub_path);
00616 }
00617 catch (...) { }
00618 if (l_stub && l_stub->type == VestaSource::stub) {
00619 const char *l_session_dir = l_stub->getAttrib("session-dir");
00620 if(l_session_dir != NULL)
00621 {
00622 l_session_path = l_session_dir + Text('/');
00623
00624
00625
00626
00627
00628 if ((!onlyMine || !OthersCheckout(l_session_path)) &&
00629 (l_session_path.Sub(0, prefixLen) == verDir))
00630 {
00631 newest_is_checkout = true;
00632
00633
00634 RecurseOnCheckout(l_session_path, suffix);
00635 l_session_ver_num = HighestVersion(l_session_path);
00636 }
00637 delete [] l_session_dir;
00638 }
00639 }
00640 if(l_stub != 0)
00641 delete l_stub;
00642 }
00643
00644
00645 if (newVerNum == verNum && !checkedIn && !newest_is_checkout) {
00646 return false;
00647 }
00648 assert((verNum < newVerNum && !checkedIn)
00649 || (verNum <= newVerNum && checkedIn)
00650 || newest_is_checkout);
00651
00652 Text newVerTxt;
00653 if (newest_is_checkout) {
00654
00655 int printRes = sprintf(buff, "%d", l_session_ver_num);
00656 assert(printRes > 0);
00657
00658
00659
00660
00661 newVerTxt = l_session_path.Sub(prefixLen) + buff;
00662
00663
00664 newPath = verDir + newVerTxt + imp->path.Sub(suffixPos);
00665 } else {
00666
00667 int printRes = sprintf(buff, "%d", newVerNum); assert(printRes > 0);
00668 newVerTxt = buff;
00669
00670
00671 newPath = verDir + newVerTxt + imp->path.Sub(suffixPos);
00672
00673
00674
00675
00676 if (onlyMine && OthersCheckout(newPath)) return false;
00677 }
00678
00679
00680 VersionDir(imp->orig, prefixLen, suffixPos,
00681 verNum, checkedIn, false);
00682 toWrite = imp->orig.Sub(0, prefixLen) +newVerTxt+ imp->orig.Sub(suffixPos);
00683 return true;
00684 }
00685
00686 static void RenameTempFile(const Text &temp, const Text &orig)
00687 throw (FS::DoesNotExist, FS::Failure)
00688 {
00689
00690 if (rename(temp.cchars(), orig.cchars()) != 0) {
00691 if (errno == EXDEV) {
00692
00693 ifstream ifs;
00694 ofstream ofs;
00695 FS::OpenReadOnly(temp, ifs);
00696 FS::OpenForWriting(orig, ofs);
00697 CopyFromTo(ifs, ofs);
00698 FS::Close(ifs);
00699 FS::Close(ofs);
00700
00701
00702 (void)unlink(temp.cchars());
00703 } else {
00704 throw FS::Failure("rename(2)", temp + " " + orig);
00705 }
00706 }
00707 }
00708
00709 static Text TempSuffix()
00710 {
00711 char pidBuff[40];
00712 FP::Tag uid = UniqueId();
00713 int sres = sprintf(pidBuff, "-%016" FORMAT_LENGTH_INT_64 "x-%016" FORMAT_LENGTH_INT_64 "x~",
00714 uid.Word0(), uid.Word1());
00715 assert(sres > 0);
00716 assert(sres < sizeof(pidBuff));
00717
00718 return pidBuff;
00719 }
00720
00721
00722
00723
00724 static Text OpenTempFile( ofstream &ofs) throw (FS::Failure)
00725 {
00726 char pidBuff[40];
00727 Text prefix = (VestaConfig::get_Text("UserInterface", "TempDir") +
00728 "/vup");
00729
00730 Text res = FS::TempFname(prefix, TempSuffix);
00731
00732 FS::OpenForWriting(res, ofs);
00733
00734 return res;
00735 }
00736
00737 static bool LimiterMatch(const Text &p_path) throw ()
00738
00739
00740 {
00741 for (int i = 0; i < limiters.size(); i++) {
00742 if (p_path.FindText(limiters.get(i)) < 0) return false;
00743 }
00744 return true;
00745 }
00746
00747 static void Update(const Text &model, TextSeq &localModels)
00748 throw (FS::DoesNotExist, FS::Failure, ParseImports::Error,
00749 VestaConfig::failure, SRPC::failure)
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764 {
00765 ImportSeq imports( 10);
00766 ParseImports::P(model, imports);
00767
00768
00769 ifstream ifs;
00770 ofstream ofs;
00771 Text newModel;
00772 if (doWork) {
00773 FS::OpenReadOnly(model, ifs);
00774 newModel = OpenTempFile( ofs);
00775 }
00776
00777 try {
00778
00779 bool modelPrinted = false;
00780 if (verbose == Verbose) {
00781 cout << model << endl;
00782 modelPrinted = true;
00783 }
00784 bool modelChanged = false;
00785 while (imports.size() > 0) {
00786
00787 Import imp(*(imports.getlo()));
00788 delete imports.remlo();
00789 if (imp.local) {
00790 localModels.addhi(imp.path);
00791 continue;
00792 }
00793
00794
00795 if (imp.noUpdate) continue;
00796
00797
00798 if (!LimiterMatch(imp.path)) continue;
00799
00800
00801 if (doWork) {
00802 CopyFromTo(ifs, ofs, imp.start);
00803 FS::Seek(ifs, imp.end);
00804 }
00805
00806 try
00807 {
00808
00809 Text newPath, pathToWrite;
00810 if (NewVersion(&imp, newPath, pathToWrite)) {
00811
00812 if (verbose != Silent) {
00813 if (!modelPrinted) {
00814 cout << model << endl;
00815 modelPrinted = true;
00816 }
00817 cout << " " << imp.path << endl;
00818 cout << " -> " << newPath << endl;
00819 }
00820
00821
00822 if (doWork) {
00823 bool useNewVersion = true;
00824 if (query) {
00825 cout << " Update this import (y/n)? "; cout.flush();
00826 const int BuffLen = 20; char buff[BuffLen];
00827 cin.getline(buff, BuffLen);
00828 useNewVersion = (buff[0] == 'y');
00829 }
00830 ofs << (useNewVersion ? pathToWrite : imp.orig);
00831
00832
00833 if (useNewVersion) modelChanged = true;
00834 }
00835
00836 } else {
00837
00838 if (doWork) ofs << imp.orig;
00839 }
00840 }
00841
00842
00843 catch (const ParseImports::Error &err)
00844 {
00845
00846 if(parse_errors_stop)
00847 {
00848 throw;
00849 }
00850 else
00851 {
00852
00853 cerr << "vupdate: " << err << endl;
00854
00855
00856 if (doWork) ofs << imp.orig;
00857 }
00858 }
00859
00860 catch(...)
00861 {
00862 throw;
00863 }
00864 }
00865 if (doWork) {
00866
00867 if (modelChanged) CopyFromTo(ifs, ofs);
00868
00869
00870 FS::Close(ifs);
00871 FS::Close(ofs);
00872 if (modelChanged) {
00873 RenameTempFile(newModel, model);
00874 } else {
00875
00876 (void)unlink(newModel.cchars());
00877 }
00878 }
00879 }
00880 catch (...) {
00881
00882 (void)unlink(newModel.cchars());
00883
00884 while (imports.size() > 0)
00885 delete imports.remlo();
00886 throw;
00887 }
00888 }
00889
00890 void CheckExclusive( bool &exclusive) throw ()
00891
00892
00893 {
00894 if (exclusive) {
00895 Syntax("more than one mutually exclusive argument specified");
00896 }
00897 exclusive = true;
00898 }
00899
00900
00901 int main(int argc, char *argv[])
00902 {
00903
00904 bool nqsExclusive = false, svExclusive = false;
00905 int arg;
00906 for (arg = 1; arg < argc && *argv[arg] == '-'; arg++) {
00907 char *argp = argv[arg];
00908 if (strncmp(argp, "--", 2) == 0) argp++;
00909 if (strcmp(argp, "-no-action") == 0 ||
00910 strcmp(argp, "-n") == 0) {
00911 CheckExclusive( nqsExclusive);
00912 doWork = false;
00913 } else if (strcmp(argp, "-query") == 0 ||
00914 strcmp(argp, "-q") == 0) {
00915 CheckExclusive( nqsExclusive);
00916 query = true;
00917 } else if (strcmp(argp, "-silent") == 0 ||
00918 strcmp(argp, "-s") == 0) {
00919 CheckExclusive( nqsExclusive);
00920 CheckExclusive( svExclusive);
00921 verbose = Silent;
00922 } else if (strcmp(argp, "-update-local") == 0 ||
00923 strcmp(argp, "-r") == 0) {
00924 update_local = true;
00925 } else if (strcmp(argp, "-update-checkout") == 0 ||
00926 strcmp(argp, "-c") == 0) {
00927 update_co = true;
00928 advance_co = true;
00929 } else if (strcmp(argp, "-update-all") == 0 ||
00930 strcmp(argp, "-u") == 0) {
00931 update_local = true;
00932 update_co = true;
00933 advance_co = true;
00934 } else if (strcmp(argp, "-verbose") == 0 ||
00935 strcmp(argp, "-v") == 0) {
00936 CheckExclusive( svExclusive);
00937 verbose = Verbose;
00938 } else if (strcmp(argp, "-advance-checkout") == 0 ||
00939 strcmp(argp, "-a") == 0) {
00940 advance_co = true;
00941 } else if (strcmp(argp, "-limit") == 0 ||
00942 strcmp(argp, "-l") == 0) {
00943
00944 arg++;
00945
00946 if (arg < argc) {
00947
00948 limiters.addhi(Text(argv[arg]));
00949 } else {
00950
00951 Syntax("-limit requires a string argument");
00952 }
00953 } else if (strcmp(argp, "-limit-checkout") == 0 ||
00954 strcmp(argp, "-L") == 0) {
00955
00956
00957
00958 limiters.addhi("/checkout/");
00959 } else if (strcmp(argp, "-only-mine") == 0 ||
00960 strcmp(argp, "-m") == 0) {
00961 onlyMine = true;
00962 } else if (strcmp(argp, "-to-checkout") == 0 ||
00963 strcmp(argp, "-t") == 0) {
00964 toCheckout = true;
00965 } else if (strcmp(argp, "-attr") == 0 ||
00966 strcmp(argp, "-A") == 0) {
00967
00968 arg++;
00969
00970 if (arg < argc) {
00971
00972 char *colon = index(argv[arg], ':');
00973 char *circum = index(argv[arg], '^');
00974 if (colon != (char *)NULL && circum != (char *)NULL) {
00975 Syntax("illegal argument to '-attr' switch");
00976 }
00977 char *nextVal = (char *)NULL;
00978 if (colon != (char *)NULL) {
00979 *colon = '\0';
00980 attrMatchSense = true;
00981 nextVal = colon + 1;
00982 } else if (circum != (char *)NULL) {
00983 *circum = '\0';
00984 attrMatchSense = false;
00985 nextVal = circum + 1;
00986 }
00987 attrName = argv[arg];
00988
00989
00990 if (nextVal != NULL) {
00991 attrVals = NEW(TextSeq);
00992 char *lastChar = nextVal + strlen(nextVal);
00993 *lastChar = ',';
00994 while (nextVal <= lastChar) {
00995 char *end = index(nextVal, ',');
00996 *end++ = '\0';
00997 attrVals->addhi(Text(nextVal));
00998 nextVal = end;
00999 }
01000 }
01001 } else {
01002
01003 Syntax("-attr requires an attr-spec argument");
01004 }
01005 } else if (strcmp(argp, "-parse-errors-stop") == 0 ||
01006 strcmp(argp, "-e") == 0) {
01007 parse_errors_stop = true;
01008 } else {
01009 Syntax("illegal argument", argv[arg]);
01010 }
01011 }
01012 if (toCheckout && attrName != (char *)NULL) {
01013 Syntax("-to-checkout and -attr cannot both be specified");
01014 }
01015 if (arg < argc - 1) Syntax("too many arguments");
01016 Text model((arg < argc) ? argv[arg] : ".main.ves");
01017
01018
01019 char wd_buff[PATH_MAX+1];
01020 char *res = getcwd(wd_buff, PATH_MAX+1); assert(res != (char *)NULL);
01021 res = strcat(wd_buff, "/"); assert(res != (char *)NULL);
01022
01023
01024 try {
01025
01026 TextSeq localModels( 10);
01027 Update(ParseImports::ResolvePath(model, Text(wd_buff)),
01028 localModels);
01029
01030
01031 if (update_local) {
01032 while (localModels.size() > 0) {
01033 model = localModels.remlo();
01034 Update(model, localModels);
01035 }
01036 }
01037 } catch (FS::DoesNotExist) {
01038 cerr << "vupdate: model file does not exist" << endl;
01039 exit(2);
01040 } catch (const FS::Failure &f) {
01041 cerr << "vupdate: " << f << endl;
01042 exit(2);
01043 } catch (const ParseImports::Error &err) {
01044 cerr << "vupdate: " << err << endl;
01045 exit(2);
01046 } catch (const VestaConfig::failure &f) {
01047 cerr << "vupdate: " << f.msg << endl;
01048 exit(2);
01049 } catch (const SRPC::failure &f) {
01050 cerr << "vupdate: error contacting Vesta repository: " << f.msg <<endl;
01051 exit(2);
01052 }
01053
01054
01055 if (!doWork && verbose) {
01056 cout << "WARNING: At your request, nothing was updated!" << endl;
01057 }
01058 exit(0);
01059 }