Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

GatherWeedRoots.C

Go to the documentation of this file.
00001 // Copyright (C) 2001, Compaq Computer Corporation
00002 // 
00003 // This file is part of Vesta.
00004 // 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 // 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 // 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 // Created on Fri Feb 28 15:27:07 PST 1997 by heydon
00020 
00021 // Last modified on Sat Apr 30 16:58:17 EDT 2005 by ken@xorian.net         
00022 //      modified on Mon Aug 12 16:33:42 EDT 2002 by kcschalk@shr.intel.com 
00023 //      modified on Wed Jul 12 16:26:23 PDT 2000 by mann  
00024 //      modified on Fri Jul 10 17:14:12 PDT 1998 by heydon
00025 
00026 // Unix includes
00027 #include <sys/types.h>
00028 #if defined(__linux__) && !defined(__GNUC__)
00029 // For some reason, the Linux sys/types.h only defines int64_t under
00030 // the GNU compilers.  We need it for subsequent system includes.
00031 typedef long int int64_t;
00032 #endif
00033 #include <sys/param.h> // for MAXPATHLEN
00034 #include <fnmatch.h>
00035 #include <string.h>
00036 
00037 // Vesta includes
00038 #include <Basics.H>
00039 #include <Table.H>
00040 #include <FS.H>
00041 #include <FP.H>
00042 #include <SourceOrDerived.H> // for NullShortId
00043 #include <VestaSource.H>
00044 #include <ReadConfig.H>
00045 #include <Debug.H>
00046 #include "PkgBuild.H"
00047 #include "CommonErrors.H"
00048 #include "RootTbl.H"
00049 #include "GatherWeedRoots.H"
00050 
00051 using std::ifstream;
00052 using std::cout;
00053 using std::cerr;
00054 using std::endl;
00055 
00056 // FPPkgTbl
00057 typedef Table<FP::Tag,const PkgBuild*>::Default FPPkgTbl;
00058 typedef Table<FP::Tag,const PkgBuild*>::Iterator FPPkgIter;
00059 
00060 // maximum input line length
00061 static const int MaxPatternLen = MAXPATHLEN;
00062 static const int MaxPathLen = MAXPATHLEN;
00063 
00064 // constants
00065 static const char *FirstStr = "FIRST";
00066 static const char *LastStr = "LAST";
00067 static const char Pound = '#';        // comment character
00068 static const char Plus = '+';         // char for adding to set
00069 static const char Minus = '-';        // char for removing from set
00070 static const char OpenBracket = '[';  // start of interval pattern
00071 static const char CloseBracket = ']'; // end of interval pattern
00072 static const char Slash = '/';        // pathname separator
00073 static const char Null = '\0';        // end-of-string character
00074 static const char OpenCurly = '{';    // start of alternative set
00075 static const char CloseCurly = '}';   // end of alternative set
00076 static const char Comma = ',';        // separator in interval/alternative set
00077 static const FP::Tag EmptyFP("");     // dummy fingerprint
00078 
00079 enum Op { Add, Remove };
00080 
00081 // Global variables -----------------------------------------------------------
00082 
00083 // true iff "echoModels" argument specified
00084 static bool GatherWeedRoots_printModelNames;
00085 
00086 // number of models in weeder instructions printed
00087 static int GatherWeedRoots_instructionModelCnt;
00088 
00089 // table mapping FP(pathname) -> PkgBuild
00090 static FPPkgTbl *GatherWeedRoots_pkgTbl = (FPPkgTbl *)NULL;
00091 
00092 // repository root (appendable)
00093 static VestaSource *GatherWeedRoots_reposRoot = (VestaSource *)NULL;
00094 
00095 // name of appendable root
00096 static Text GatherWeedRoots_rootName;
00097 static int GatherWeedRoots_rootNameLen; // length of "rootName"
00098 
00099 static inline bool IsEmpty(const char *str) throw ()
00100 /* Return true iff "str" is the empty string. */
00101 { return *str == Null; }
00102 
00103 // Error message printing -----------------------------------------------------
00104 
00105 static void GatherWeedRoots_MaxPathLenErr() throw ()
00106 {
00107     cerr << "Fatal error: built-in constant 'MaxPathLen' too small; ";
00108     cerr << "exiting..." << endl;
00109     exit(1);
00110 }
00111 
00112 // Model file processing ------------------------------------------------------
00113 
00114 static PkgBuild* GatherWeedRoots_GetPkgBuild(VestaSource *vs,
00115   const FP::Tag &parentFP, VestaSource::typeTag parentType)
00116   throw (ReposError, SRPC::failure)
00117 /* Allocate and return a new "PkgBuild" structure corresponding to the
00118    source "vs" whose parent directory has fingerprint "parentFP" and type
00119    "parentType". Returns NULL if "vs" is not an immutable file or "parentType"
00120    is not an immutable directory. */
00121 {
00122     if (vs->type != VestaSource::immutableFile
00123         || parentType != VestaSource::immutableDirectory) {
00124         return (PkgBuild *)NULL;
00125     }
00126     ShortId shortId = vs->shortId();
00127     assert(shortId != NullShortId);
00128     return NEW_PTRFREE_CONSTR(PkgBuild, (parentFP, shortId));
00129 } // GatherWeedRoots_GetPkgBuild
00130 
00131 static void GatherWeedRoots_HandleFile(Op op, VestaSource *vs,
00132   const char *filename, const FP::Tag &parentFP,
00133   VestaSource::typeTag parentType)
00134   throw (ReposError, SRPC::failure)
00135 /* Add or remove the file "vs" named by "filename" to/from "pkgTbl" according
00136    to "op", where the parent directory of "vs" has fingerprint "parentFP" and
00137    type "parentType". */
00138 {
00139     if (GatherWeedRoots_printModelNames) {
00140         cout << "  ";
00141         cout << ((op == Add) ? Plus : Minus) << ' ' << filename << endl;
00142         GatherWeedRoots_instructionModelCnt++;
00143     }
00144     FP::Tag fp(filename);
00145     switch (op) {
00146       case Add:
00147         // only add an association if none has already been established
00148         const PkgBuild *pkg;
00149         if (!(GatherWeedRoots_pkgTbl->Get(fp, /*OUT*/ pkg))) {
00150             pkg = GatherWeedRoots_GetPkgBuild(vs, parentFP, parentType);
00151             if (pkg != (PkgBuild *)NULL) {
00152                 bool inTbl = GatherWeedRoots_pkgTbl->Put(fp, pkg);
00153                 assert(!inTbl);
00154             } else {
00155                 cerr << "Warning: ignoring '" << filename << "'" << endl;
00156             }
00157         }
00158         break;
00159       case Remove:
00160         const PkgBuild *dummy;
00161         (void) GatherWeedRoots_pkgTbl->Delete(fp, /*OUT*/ dummy);
00162         break;
00163       default:
00164         assert(false);
00165     }
00166 } // GatherWeedRoots_HandleFile
00167 
00168 // Pattern matching -----------------------------------------------------------
00169 
00170 static int GatherWeedRoots_DecimalValue(/*INOUT*/ const char* &str,
00171   bool allowSuffix = false) throw ()
00172 /* If "str" is a string consisting entirely of decimal digits that does not
00173    begin with any superfluous leading zeroes, return the value of the digits
00174    (interpreted as a base-10 number). Otherwise, return -1. If "allowSuffix"
00175    is "true", then "str" must just have a string of decimal digits as its
00176    prefix. In any case "str" will be set to point to the first character after
00177    the prefix of decimal digits. */
00178 {
00179     const char *init = str;
00180     int res = 0;
00181     while (isdigit(*str)) {
00182         res = (10 * res) + (*str++ - '0');
00183     }
00184     if (init == str) return -1; // empty sequence of digits
00185     if (*init == '0' && (str - init) > 1) return -1; // extra leading zero
00186     return ((IsEmpty(str) || allowSuffix) ? res : -1);
00187 } // GatherWeedRoots_DecimalValue
00188 
00189 struct FirstLastState {
00190     VestaSource *parentVS;
00191     char *path;
00192     int first;
00193     int last;
00194 };
00195 
00196 static bool GatherWeedRoots_FirstLastCallback(void *arg,
00197   VestaSource::typeTag type, Arc arc, unsigned int index,
00198   Bit32 pseudoInode, ShortId filesid, bool master) 
00199     throw (ReposError, SRPC::failure)
00200 /* This is the callback function past to the "list" method in
00201    "GetFirstLast" below. For each directory entry it is passed,
00202    it updates its "FirstLastState" argument's "first" and "last"
00203    fields if "arc" is a decimal value and if the corresponding
00204    "VestaSource" is an immutable or appendable directory. */
00205 {
00206     FirstLastState *s = (FirstLastState *)arg;
00207 
00208     const char *dummy_name = arc;
00209     int value = GatherWeedRoots_DecimalValue(/*INOUT*/ dummy_name);
00210     if (value >= 0) {
00211         // only update values if it this entry is a directory
00212         VestaSource *childVS;
00213         VestaSource::errorCode errCode;
00214         errCode = s->parentVS->lookupIndex(index, /*OUT*/ childVS);
00215         if (errCode != VestaSource::ok) {
00216             throw ReposError(errCode, "VestaSource::lookupIndex", s->path);
00217         }
00218         if (childVS->type == VestaSource::immutableDirectory
00219             || childVS->type == VestaSource::appendableDirectory) {
00220             s->first = (s->first == -1) ? value : min(s->first, value);
00221             s->last = (s->last == -1) ? value : max(s->last, value);
00222         }
00223     }
00224     return true;
00225 } // GatherWeedRoots_FirstLastCallback
00226 
00227 static void GatherWeedRoots_GetFirstLast(VestaSource *vs, char *path,
00228   /*OUT*/ int &first, /*OUT*/ int &last) throw (ReposError, SRPC::failure)
00229 /* Sets "first" and "last" to the minimum and maximum values, respectively,
00230    of entries in the directory "vs" (whose corresponding pathname is "path")
00231    whose names consist entirely of decimal digits and that are directories.
00232    If there are no such child entries, "first" and "last" are set to "-1". */
00233 {
00234     // initialize closure for callback
00235     FirstLastState s;
00236     s.parentVS = vs;
00237     s.path = path;
00238     s.first = s.last = -1;
00239 
00240     // invoke callback on each directory entry
00241     VestaSource::errorCode errCode;
00242     errCode = vs->list(/*firstIndex=*/ 0,
00243       GatherWeedRoots_FirstLastCallback, (void*)(&s));
00244     if (errCode != VestaSource::ok) {
00245         throw ReposError(errCode, "VestaSource::list", path);
00246     }
00247 
00248     // return results from callback state
00249     first = s.first;
00250     last = s.last;
00251 } // GatherWeedRoots_GetFirstLast
00252 
00253 static char* GatherWeedRoots_ReplaceFirstLast(VestaSource *vs, char *path,
00254   char *pattern) throw (ReposError, SRPC::failure)
00255 /* Return a version of "pattern" in which all occurrences of the special
00256    strings "FIRST" and "LAST" in "pattern" have been replaced by the
00257    names of the entries in the directory "dirPath" consisting entirely of
00258    decimal digits (with no leading zeroes) whose values are the smallest and
00259    largest, respectively. Assumes that "dirPath" does in fact name a
00260    directory. "end" points to the end of "dirPath", and "rem" is the number of
00261    characters remaining in the buffer pointed to by "dirPath". "dirPath" is
00262    assumed not to end in a slash unless it is the root directory. On exit,
00263    the string pointed to by "dirPath" must be unchanged. */
00264 {
00265     // return if neither FIRST nor LAST occurs in "pattern"
00266     if (strstr(pattern, FirstStr) == (char *)NULL &&
00267         strstr(pattern, LastStr) == (char *)NULL)
00268         return pattern;
00269 
00270     // determine "first" and "last" numerical values
00271     int first, last;
00272     GatherWeedRoots_GetFirstLast(vs, path, /*OUT*/ first, /*OUT*/ last);
00273 
00274     // replace all occurrences of FIRST and LAST in "pattern"
00275     Text res;
00276     while (!IsEmpty(pattern)) {
00277         // find occurrence of each string
00278         char *firstLoc = strstr(pattern, FirstStr);
00279         char *lastLoc = strstr(pattern, LastStr);
00280 
00281         // set "minLoc" to the first occurrence of either
00282         char *minLoc;
00283         if (firstLoc == (char *)NULL) minLoc = lastLoc;
00284         else if (lastLoc == (char *)NULL) minLoc = firstLoc;
00285         else minLoc = (firstLoc < lastLoc) ? firstLoc : lastLoc;
00286 
00287         // set "value", "skipLen" to the matched value & its length
00288         int value = -1, skipLen = 0;
00289         if (minLoc == (char *)NULL) {
00290             minLoc = index(pattern, Null); // set "minLoc" to end of string
00291         } else {
00292             if (minLoc == firstLoc) {
00293                 value = first; skipLen = strlen(FirstStr);
00294             } else {
00295                 value = last; skipLen = strlen(LastStr);
00296             }
00297             assert(skipLen > 0);
00298         }
00299 
00300         // copy characters up to the match
00301         if (minLoc > pattern) {
00302             res += Text(pattern, minLoc - pattern);
00303         }
00304         pattern = (minLoc + skipLen);
00305 
00306         // copy the digits for "value";
00307         if (value >= 0) {
00308             char buff[20];
00309             int spres = sprintf(buff, "%d", value);
00310             assert(spres >= 0);
00311             res += buff;
00312         } else if (skipLen > 0) {
00313             // no numbered directories; fill in string that
00314             // won't match anything
00315             res += "_NO_MATCH_";
00316         }
00317     }
00318     return res.chars();
00319 } // GatherWeedRoots_ReplaceFirstLast
00320 
00321 static int GatherWeedRoots_ParseExpr(/*INOUT*/ const char* &curr)
00322   throw (InputError)
00323 {
00324     int res, sign = 0;
00325     while (isdigit(*curr)) {
00326         // read next number
00327         int num =
00328           GatherWeedRoots_DecimalValue(/*INOUT*/ curr, /*allowSuffix=*/ true);
00329         if (num < 0) {
00330             throw InputError("expected number in interval expression");
00331         }
00332 
00333         // incorporate it into the result
00334         if (sign == 0) res = num;
00335         else res += (sign * num);
00336 
00337         // look for '+' or '-' to continue
00338         if (*curr == Plus) { sign = 1; curr++; }
00339         else if (*curr == Minus) { sign = -1; curr++; }
00340     }
00341     return res;
00342 } // GatherWeedRoots_ParseExpr
00343 
00344 static char* GatherWeedRoots_ReplaceInterval(const char *pattern)
00345   throw (SysError, InputError)
00346 /* Return a version of "pattern" in which all interval expressions have been
00347    replaced by a set expression representing the integer values of the
00348    interval. For example, the pattern ``A[8,12]B'' is converted to the
00349    expression ``A{8,9,10,11,12}B''. */
00350 {
00351     Text res;
00352     while (!IsEmpty(pattern)) {
00353         char *intvStart = index(pattern, OpenBracket);
00354         char *intvEnd, *comma;
00355         int low, high;
00356         if (intvStart != (char *)NULL) {
00357             comma = index(intvStart, Comma);
00358             intvEnd = index(intvStart, CloseBracket);
00359             // test for an interval expression
00360             if (comma != NULL && intvEnd != NULL &&
00361                 intvStart < comma && comma < intvEnd &&
00362                 intvStart+1 != comma && comma != intvEnd-1) {
00363                 // compute "low" and "high" values
00364                 const char *curr = intvStart + 1;
00365                 low = GatherWeedRoots_ParseExpr(/*INOUT*/ curr);
00366                 if (*curr++ != Comma) {
00367                     throw InputError("expected comma in interval");
00368                 }
00369                 high = GatherWeedRoots_ParseExpr(/*INOUT*/ curr);
00370                 if (*curr++ != CloseBracket) {
00371                     throw InputError("expected ']' at end of interval");
00372                 }
00373             } else {
00374                 // this is the case of a normal character set expression
00375                 if (intvEnd == NULL) {
00376                     throw InputError("no matching ']' in pattern");
00377                 }
00378             }
00379         } else {
00380             intvStart = index(pattern, Null); // set start to end of string
00381         }
00382 
00383         // copy the characters up to the match
00384         if (intvStart > pattern) {
00385             res += Text(pattern, intvStart - pattern);
00386         }
00387 
00388         // add expression for interval
00389         if (IsEmpty(intvStart)) {
00390             pattern = intvStart;
00391         } else {
00392             pattern = intvEnd + 1;
00393             if (low <= high) {
00394                 // non-empty interval
00395                 const int MaxNumLen = 20;
00396                 // Allocate temporary buffer:
00397                 //   2 chars for '{' and '}'
00398                 //   MaxNumLen chars for each number in [ low, high ]
00399                 //   1 char for each comma separating the numbers
00400                 char *buff = NEW_PTRFREE_ARRAY(char,
00401                                                2 + ((MaxNumLen+1) * (high-low+1)));
00402                 char *curr = buff;
00403                 *curr++ = OpenCurly;
00404                 for (int i = low; i <= high; i++) {
00405                     char numBuff[MaxNumLen];
00406                     int spres = sprintf(numBuff, "%d", i);
00407                     assert(spres >= 0);
00408                     if (strcpy(curr, numBuff) == NULL)
00409                         throw SysError("strcpy(3)");
00410                     curr += strlen(numBuff);
00411                     if (i < high) *curr++ = Comma;
00412                 }
00413                 *curr++ = CloseCurly;
00414                 res += Text(buff, curr - buff);
00415             } else {
00416                 // empty interval: fill in a string that won't match anything
00417                 res += "_NO_MATCH_";
00418             }
00419         }
00420     }
00421     return res.chars();
00422 } // GatherWeedRoots_ReplaceInterval
00423 
00424 static int GatherWeedRoots_CurlyMatchHelper(char *buff, char *end,
00425   const char *pattern, const char *name) throw (InputError, SysError)
00426 {
00427     // test for base case
00428     char *openCurly, *closeCurly; // both pointers into "pattern"
00429     if ((openCurly = index(pattern, OpenCurly)) == NULL) {
00430         if (strcpy(end, pattern) == NULL) throw SysError("strcpy(3)");
00431         return fnmatch(buff, name, FNM_PERIOD);
00432     }
00433 
00434     // check for closing curly
00435     if ((closeCurly = index(openCurly+1, CloseCurly)) == NULL) {
00436         throw InputError("opening '{' not matched by a closing '}'");
00437     }
00438 
00439     // copy prefix before curly into "buff"
00440     int len = openCurly - pattern;
00441     if (strncpy(end, pattern, len) == NULL) throw SysError("strncpy(3)");
00442     end += len;
00443 
00444     // iterate and recurse
00445     int res = FNM_NOMATCH;
00446     char *curr; // pointer into "pattern", between "openCurly" & "closeCurly"
00447     for (curr = openCurly + 1; (curr-1) != closeCurly; curr++) {
00448         char *wdEnd = index(curr, Comma);
00449         if (wdEnd == (char *)NULL || wdEnd > closeCurly) wdEnd = closeCurly;
00450         int len = wdEnd - curr;
00451         if (strncpy(end, curr, len) == NULL) throw SysError("strncpy(3)");
00452         res = GatherWeedRoots_CurlyMatchHelper(buff,
00453           end + len, closeCurly + 1, name);
00454         if (res != FNM_NOMATCH) return res;
00455         curr = wdEnd;
00456     }
00457     return res;
00458 } // GatherWeedRoots_CurlyMatchHelper
00459 
00460 static int GatherWeedRoots_CurlyMatch(const char *pattern, const char *name)
00461   throw (SysError, InputError)
00462 /* Return the result of calling "fnmatch(pattern', name)" for all patterns
00463    "pattern'" obtained by replacing instances of "{...}" in "pattern" by the
00464    listed strings. If any replacement produces a match, return 0. If all
00465    replacements return FNM_MATCH, return FNM_MATCH. If any other value is
00466    returned, return it immediately. */
00467 {
00468     // handle no-work case
00469     if (index(pattern, OpenCurly) == NULL) {
00470         return fnmatch(pattern, name, FNM_PERIOD);
00471     }
00472 
00473     // allocate a buffer into which the instantiated pattern will be copied
00474     /* To be conservative, we allocate a buffer as large as the pattern. This
00475        is safe because it is impossible for an instantiation of "{...}" to be
00476        longer than the pattern itself. */
00477     char *buff = NEW_PTRFREE_ARRAY(char, strlen(pattern));
00478     return GatherWeedRoots_CurlyMatchHelper(buff, buff, pattern, name);
00479 } // GatherWeedRoots_CurlyMatch
00480 
00481 // Search ---------------------------------------------------------------------
00482 
00483 // forward declaration
00484 static void GatherWeedRoots_SearchPath(Op op, VestaSource *vs, char *path,
00485   char *end, int rem, char *rest, const FP::Tag &parentFP,
00486   VestaSource::typeTag parentType)
00487   throw (SysError, ReposError, InputError, SRPC::failure);
00488 
00489 struct SearchDirState {
00490     Op op;
00491     VestaSource *parentVS;
00492     char *path;
00493     char *end;
00494     int rem;
00495     char *dirPattern;
00496     char *rest;
00497 };
00498 
00499 static bool GatherWeedRoots_SearchDirCallback(void *closure,
00500   VestaSource::typeTag type, Arc arc, unsigned int index,
00501   Bit32 pseudoInode, ShortId filesid, bool master)
00502   throw (SysError, ReposError, InputError, SRPC::failure)
00503 /* This is the callback function passed to VestaSource::list by the
00504    "SearchDir" function below. It processes a single directory entry. */
00505 {
00506     // "closure" is actually of type "SearchDirState *"
00507     SearchDirState *s = (SearchDirState *)closure;
00508 
00509     // compare name of directory entry to "dirPattern"
00510     int match = GatherWeedRoots_CurlyMatch(s->dirPattern, arc);
00511     switch (match) {
00512       case 0: // match
00513         { // check that there's enough space in the buffer
00514           int namlen = strlen(arc);
00515           if (namlen >= s->rem) GatherWeedRoots_MaxPathLenErr();
00516 
00517           // append entry name to "s->path"
00518           if (strcpy(s->end, arc) == (char *)NULL) {
00519               throw SysError("strcpy(3)");
00520           }
00521 
00522           // get the "VestaSource" object for "arc" in "thisVS"
00523           VestaSource *childVS;
00524           VestaSource::errorCode errCode;
00525           errCode = s->parentVS->lookupIndex(index, /*OUT*/ childVS);
00526           if (errCode != VestaSource::ok) {
00527               throw ReposError(errCode, "VestaSource::lookupIndex", s->path);
00528           }
00529 
00530           // search recursively
00531           GatherWeedRoots_SearchPath(s->op, childVS, s->path,
00532             s->end + namlen, s->rem - namlen, s->rest, s->parentVS->fptag,
00533             s->parentVS->type);
00534         }
00535         break;
00536       case FNM_NOMATCH:
00537         break;
00538       default:
00539         throw SysError("fnmatch(3)");
00540     }
00541     return true;
00542 } // GatherWeedRoots_SearchDirCallback
00543 
00544 static void GatherWeedRoots_SearchDir(Op op, VestaSource *vs, char *path,
00545   char *end, int rem, char *rest, const FP::Tag &parentFP)
00546   throw (SysError, ReposError, InputError, SRPC::failure)
00547 /* Like "SearchPath" below, but "vs" is known to be an immutable or
00548    appendable directory. */
00549 {
00550     // isolate pattern for this directory
00551     char *restStart = rest;
00552     char *dirPattern;
00553     if ((rest = index(restStart, Slash)) != NULL) {
00554         int dirPatternLen = rest - restStart;
00555         dirPattern = NEW_PTRFREE_ARRAY(char, dirPatternLen+1);
00556         if (strncpy(dirPattern, restStart, dirPatternLen) == NULL) {
00557             throw SysError("strncpy(3)");
00558         }
00559         dirPattern[dirPatternLen] = Null; // terminate pattern
00560         rest++;       // advance past Slash to start of next pattern
00561     } else {
00562         dirPattern = restStart;
00563         rest = restStart + strlen(restStart); // set "rest" to end of pattern
00564     }
00565 
00566     // handle "FIRST", "LAST", "[expr,expr]" in "dirPattern"
00567     dirPattern = GatherWeedRoots_ReplaceFirstLast(vs, path, dirPattern);
00568     dirPattern = GatherWeedRoots_ReplaceInterval(dirPattern);
00569 
00570     // append slash to "path" if necessary
00571     assert(*(end-1) != Slash);
00572     if (rem == 0) GatherWeedRoots_MaxPathLenErr();
00573     *end++ = Slash; *end = Null;
00574     rem--;
00575 
00576     // initialize state for callback
00577     SearchDirState s;
00578     s.op = op;
00579     s.parentVS = vs;
00580     s.path = path;
00581     s.end = end;
00582     s.rem = rem;
00583     s.dirPattern = dirPattern;
00584     s.rest = rest;
00585 
00586     // enumerate directory, matching each entry against "dirPattern"
00587     VestaSource::errorCode errCode;
00588     errCode = vs->list(/*firstIndex=*/ 0,
00589       GatherWeedRoots_SearchDirCallback, (void *)(&s));
00590     if (errCode != VestaSource::ok) {
00591         throw ReposError(errCode, "VestaSource::list", path);
00592     }
00593 } // GatherWeedRoots_SearchDir
00594 
00595 static void GatherWeedRoots_SearchPath(Op op, VestaSource *vs, char *path,
00596   char *end, int rem, char *rest, const FP::Tag &parentFP,
00597   VestaSource::typeTag parentType)
00598   throw (SysError, ReposError, InputError, SRPC::failure)
00599 /* Recursively search the file or directory "vs" named by "path", where
00600    "end" is a pointer to the null character that terminates "path", and "rem"
00601    is the number of characters remaining in the buffer starting at "end".
00602    "rest" is the remainder of the search pattern. "op" indicates whether the
00603    entries matched by the path "path" combined with the pattern "rest" should
00604    be added or removed from the set of models. "parentFP" is the fingerprint
00605    of the parent directory of "vs", which has type "parentType"; "parentFP" is
00606    defined if and only if "parentType" represents an immutable directory.
00607 
00608    The last (non-null) character of "path" is assumed *not* to be a slash.
00609    "rest" should start with the full arc of the search pattern path; hence,
00610    its first character is assumed *not* to be a slash. */
00611 {
00612     assert(IsEmpty(end) && *(end-1) != Slash);
00613 
00614     switch (vs->type) {
00615       case VestaSource::immutableFile:
00616         // handle the base case of the recursion
00617         if (IsEmpty(rest)) {
00618             GatherWeedRoots_HandleFile(op, vs, path, parentFP, parentType);
00619         }
00620         break;
00621       case VestaSource::immutableDirectory:
00622       case VestaSource::appendableDirectory:
00623         GatherWeedRoots_SearchDir(op, vs, path, end, rem, rest, parentFP);
00624         break;
00625       case VestaSource::ghost:
00626       case VestaSource::stub:
00627         // silently ignore ghosts and stubs
00628         break;
00629       default:
00630         // print a warning message for everything else
00631         cerr << "Warning: ignoring '" << path << "'" << endl;
00632         break;
00633     }
00634 } // GatherWeedRoots_SearchPath
00635 
00636 static void GatherWeedRoots_ProcessPattern(Op op, char *pattern)
00637   throw (SysError, ReposError, InputError, SRPC::failure)
00638 /* Process the single model pattern specified by "op" and "pattern". Throws
00639    "InputError" if "pattern" is an invalid path pattern. */
00640 {
00641     // verify that repository root name is a '/' followed by 1 or more chars
00642     if (GatherWeedRoots_rootNameLen == 0) {
00643         throw InputError("repository root name is empty");
00644     } else if (GatherWeedRoots_rootName[0] != Slash) {
00645         throw InputError("repository root name is not an absolute path");
00646     } else if (GatherWeedRoots_rootNameLen == 1) {
00647         throw InputError("repository root name must be different from '/'");
00648     }
00649 
00650     // determine ``root'' directory of the search
00651     char dir[MaxPathLen];
00652     int rem = MaxPathLen; // number of characters remaining in "dir"
00653     char *end = dir;
00654     if (strlen(pattern) > GatherWeedRoots_rootNameLen
00655         && strncmp(pattern, GatherWeedRoots_rootName.cchars(),
00656                    GatherWeedRoots_rootNameLen) == 0
00657         && pattern[GatherWeedRoots_rootNameLen] == Slash)
00658     {
00659         strncpy(dir, pattern, GatherWeedRoots_rootNameLen);
00660         end += GatherWeedRoots_rootNameLen;
00661         rem -= GatherWeedRoots_rootNameLen;
00662         pattern += GatherWeedRoots_rootNameLen + 1; // skip '/'
00663     } else {
00664         throw InputError("weeder pattern does not start with repository root");
00665     }
00666     *end = Null;
00667 
00668     // invoke recursive routine to explore it
00669     try {
00670         GatherWeedRoots_SearchPath(op, GatherWeedRoots_reposRoot,
00671           dir, end, rem, pattern, EmptyFP, VestaSource::unused);
00672     }
00673     catch (InputError &err) {
00674         // fill in an "arg" if none was present
00675         if (err.arg.Empty()) err.arg = pattern;
00676         // re-throw the exception
00677         throw;
00678     }
00679 } // GatherWeedRoots_ProcessPattern
00680 
00681 // Read input file ------------------------------------------------------------
00682 
00683 static void GatherWeedRoots_SkipWhite(/*INOUT*/ char* &ptr) throw ()
00684 /* Advance "ptr" past spaces and tabs. */
00685 {
00686     while (*ptr == ' ' || *ptr == '\t') ptr++;
00687 }
00688 
00689 static void GatherWeedRoots_GetLine(ifstream &ifs, char *buff, int rem)
00690   throw (FS::Failure)
00691 {
00692     while (rem > 0) {
00693         int c = ifs.get();
00694         if (c == EOF || c == '\n') break;
00695         *buff++ = (char)c;
00696         rem--;
00697     }
00698     if (rem == 0) GatherWeedRoots_MaxPathLenErr();
00699     *buff = Null;
00700 } // GatherWeedRoots_GetLine
00701 
00702 static void GatherWeedRoots_ProcessFile(ifstream &ifs)
00703   throw (FS::Failure, SysError, ReposError, InputError, SRPC::failure)
00704 /* Process the input file "ifs". Throws "FS::Failure" if there is an error
00705    reading from this stream. Throws "InputError" if the input is not of the
00706    correct format. */
00707 {
00708     while (!FS::AtEOF(ifs)) {
00709         // read next line
00710         char buff[MaxPatternLen];
00711         GatherWeedRoots_GetLine(ifs, buff, MaxPatternLen);
00712 
00713         // skip blank lines and comments
00714         if (strlen(buff) == 0) continue;
00715         if (strlen(buff) > 0 && buff[0] == Pound) continue;
00716 
00717         // handle real input line
00718         Op op;
00719         char *curr = buff;
00720         GatherWeedRoots_SkipWhite(/*INOUT*/ curr);
00721         switch (*curr++) {
00722           case Plus:
00723             op = Add;
00724             break;
00725           case Minus:
00726             op = Remove;
00727             break;
00728           default:
00729             throw InputError("model pattern does not start with '+' or '-'");
00730         }
00731         GatherWeedRoots_SkipWhite(/*INOUT*/ curr);
00732         GatherWeedRoots_ProcessPattern(op, curr);
00733     }
00734 } // GatherWeedRoots_ProcessFile
00735 
00736 // Main method ----------------------------------------------------------------
00737 
00738 void GatherWeedRoots::P(char *file, bool echoModels, /*OUT*/ RootTbl *rootTbl)
00739   throw (SRPC::failure, FS::Failure, FS::DoesNotExist,
00740          InputError, SysError, ReposError)
00741 {
00742     // initialize globals
00743     GatherWeedRoots_printModelNames = echoModels;
00744     GatherWeedRoots_pkgTbl = NEW_CONSTR(FPPkgTbl,
00745                                         (/*sizeHint=*/ 500, /*useGC=*/ true));
00746     GatherWeedRoots_reposRoot = VestaSource::repositoryRoot();
00747     assert(GatherWeedRoots_reposRoot->type ==VestaSource::appendableDirectory);
00748     GatherWeedRoots_rootName =
00749       ReadConfig::TextVal("UserInterface", "AppendableRootName");
00750     GatherWeedRoots_rootNameLen = GatherWeedRoots_rootName.Length();
00751 
00752     // try opening the file
00753     ifstream ifs;
00754     FS::OpenReadOnly(file, /*OUT*/ ifs);
00755     try {
00756         try {
00757             if (GatherWeedRoots_printModelNames) {
00758                 cout << "Models in weeder instructions:" << endl;
00759                 GatherWeedRoots_instructionModelCnt = 0;
00760             }
00761             GatherWeedRoots_ProcessFile(ifs);
00762             if (GatherWeedRoots_printModelNames) {
00763                 if (GatherWeedRoots_instructionModelCnt == 0) {
00764                     cout << "  NONE" << endl;
00765                 }
00766                 cout << endl;
00767             }
00768         } catch (...) {
00769             if (GatherWeedRoots_printModelNames) {
00770                 if (GatherWeedRoots_instructionModelCnt == 0) {
00771                     cout << "  NONE SO FAR" << endl;
00772                 }
00773                 cout << endl;
00774             }
00775             FS::Close(ifs);
00776             throw;
00777         }
00778         FS::Close(ifs);
00779 
00780         if (GatherWeedRoots_pkgTbl != (FPPkgTbl *)NULL) {
00781             // copy range of "pkgTbl" to domain of "rootTbl"
00782             FPPkgIter it(GatherWeedRoots_pkgTbl);
00783             FP::Tag fp;
00784             const PkgBuild *pkg;
00785             while (it.Next(/*OUT*/ fp, /*OUT*/ pkg)) {
00786                 (void) rootTbl->Put(*pkg, false);
00787             }
00788         }
00789 
00790     } catch (...) {
00791         // drop pointers on floor for GC
00792         GatherWeedRoots_pkgTbl = (FPPkgTbl *)NULL;
00793         GatherWeedRoots_reposRoot = (VestaSource *)NULL;
00794         throw;
00795     }
00796     // drop global pointers on floor for GC
00797     GatherWeedRoots_pkgTbl = (FPPkgTbl *)NULL;
00798     GatherWeedRoots_reposRoot = (VestaSource *)NULL;
00799     GatherWeedRoots_rootName = "";
00800 } // GatherWeedRoots::P

Generated on Mon May 8 00:48:59 2006 for Vesta by  doxygen 1.4.2