/* ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 1, or (at your option) ** any later version. ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include "CvsLog.h" #include "common.h" #ifndef NEW #define NEW new #endif std::vector gRcsFiles; CRcsFile *gRcsFile = 0L; CRevFile *gRevFile = 0L; #ifdef TARGET_OS_MAC std::vector gMacAlloca; #endif // // CRevNumber // void CRevNumber::reset(void) { allDigits.erase(allDigits.begin(), allDigits.end()); tagName = ""; } CRevNumber & CRevNumber::operator+=(int adigit) { allDigits.push_back(adigit); return *this; } CRevNumber & CRevNumber::operator=(const CRevNumber & arev) { reset(); for(int i = 0; i < arev.size(); i++) { *this += arev[i]; } tagName = arev.Tag(); return *this; } int CRevNumber::operator[](int index) const { if(index < 0 || index >= size()) return -1; return allDigits[index]; } const char *CRevNumber::c_str(void) const { static CLogStr str; str = ""; if(allDigits.empty()) return str; std::vector::const_iterator i = allDigits.begin(); str << *i; ++i; for(; i != allDigits.end(); ++i) { str << '.' << *i; } return str; } int CRevNumber::cmp(const CRevNumber & arev) const { const std::vector & rev1 = IntList(); const std::vector & rev2 = arev.IntList(); std::vector::const_iterator i = rev1.begin(); std::vector::const_iterator j = rev2.begin(); while(i != rev1.end() && j != rev2.end()) { if((*i) != (*j)) return (*i) < (*j) ? -1 : 1; ++i; ++j; } if(i == rev1.end() && j != rev2.end()) return -1; if(i != rev1.end() && j == rev2.end()) return 1; return 0; } bool CRevNumber::operator==(const CRevNumber & arev) const { if(size() != arev.size() || size() == 0) return false; const std::vector & rev1 = IntList(); const std::vector & rev2 = arev.IntList(); std::vector::const_iterator i = rev1.begin(); std::vector::const_iterator j = rev2.begin(); return memcmp(&*i, &*j, size() * sizeof(int)) == 0; } bool CRevNumber::ischildof(const CRevNumber & arev) const { if((arev.size() + 2) != size()) return false; if(size() == 0 || arev.size() == 0) return false; const std::vector & rev1 = IntList(); const std::vector & rev2 = arev.IntList(); std::vector::const_iterator i = rev1.begin(); std::vector::const_iterator j = rev2.begin(); return memcmp(&*i, &*j, arev.size() * sizeof(int)) == 0; } bool CRevNumber::issamebranch(const CRevNumber & arev) const { if(size() != arev.size() || size() == 0) return false; const std::vector & rev1 = IntList(); const std::vector & rev2 = arev.IntList(); std::vector::const_iterator i = rev1.begin(); std::vector::const_iterator j = rev2.begin(); return memcmp(&*i, &*j, (size() - 1) * sizeof(int)) == 0; } bool CRevNumber::ispartof(const CRevNumber & arev) const { const std::vector & rev1 = IntList(); const std::vector & rev2 = arev.IntList(); std::vector::const_iterator i = rev1.begin(); std::vector::const_iterator j = rev2.begin(); if(size() == 0 || arev.size() == 0) return false; if((arev.size() & 1) != 0) { // special case for "1.1.1" if((arev.size() + 1) != size()) return false; return memcmp(&*i, &*j, arev.size() * sizeof(int)) == 0; } // case for 1.1.1.1.2.4 is part of 1.1.1.1.0.2 if(arev.size() != size() || size() < 2) return false; if(memcmp(&*i, &*j, (size() - 2) * sizeof(int)) != 0) return false; return arev.IntList()[size() - 2] == 0 && IntList()[size() - 2] == arev.IntList()[size() - 1]; } bool CRevNumber::issubbranchof(const CRevNumber & arev) const { const std::vector & rev1 = IntList(); const std::vector & rev2 = arev.IntList(); std::vector::const_iterator i = rev1.begin(); std::vector::const_iterator j = rev2.begin(); if(size() == 0 || arev.size() == 0) return false; if((size() & 1) != 0) { // special case for "1.1.1" if((arev.size() + 1) != size()) return false; return memcmp(&*i, &*j, arev.size() * sizeof(int)) == 0; } // case for 1.4.0.2 is subbranch of 1.4 if((arev.size() + 2) != size() || IntList()[arev.size()] != 0) return false; return memcmp(&*i, &*j, arev.size() * sizeof(int)) == 0; } // // CLogStr // void CLogStr::flush(void) { if(str == 0L) return; free(str); str = 0L; } const char *CLogStr::operator=(const char *newstr) { flush(); if(newstr == 0L) return 0L; int l = strlen(newstr); str = (char *)malloc((l + 1) * sizeof(char)); if(str == 0L) throw std::bad_alloc(); strcpy(str, newstr); return *this; } const CLogStr & CLogStr::operator=(const CLogStr & newstr) { flush(); *this = (const char *)newstr; return *this; } const CLogStr & CLogStr::set(const char *buf, unsigned int len) { flush(); if(len == 0) return *this; str = (char *)malloc((len + 1) * sizeof(char)); if(str == 0L) throw std::bad_alloc(); memcpy(str, buf, len * sizeof(char)); str[len] = '\0'; return *this; } const CLogStr & CLogStr::replace(char which, char bywhich) { if(str == 0L) return *this; char *buf = str; while(*buf) { if(*buf == which) *buf++ = bywhich; else buf++; } return *this; } CLogStr & CLogStr::operator<<(const char *addToStr) { if(addToStr == 0L) return *this; if(str == 0L) { *this = addToStr; return *this; } unsigned int len = strlen(addToStr); if(len == 0) return *this; unsigned curlen = length(); str = (char *)realloc(str, (len + curlen + 1) * sizeof(char)); if(str == 0L) throw std::bad_alloc(); memcpy(str + curlen, addToStr, len * sizeof(char)); str[len + curlen] = '\0'; return *this; } CLogStr & CLogStr::operator<<(char addToStr) { char astr[2] = {'\0', '\0'}; astr[0] = addToStr; return *this << astr; } CLogStr & CLogStr::operator<<(int addToStr) { char astr[50]; sprintf(astr, "%d", addToStr); return *this << astr; } // // CRcsFile // CRcsFile::CRcsFile() { selRevisions = 0; totRevisions = 0; lockStrict = false; } CRcsFile::CRcsFile(const CRcsFile & afile) { *this = afile; } CRcsFile::~CRcsFile() { } CRcsFile & CRcsFile::operator=(const CRcsFile & afile) { rcsFile = afile.rcsFile; workingFile = afile.workingFile; headRev = afile.headRev; branchRev = afile.branchRev; keywordSubst = afile.keywordSubst; accessList = afile.accessList; symbolicList = afile.symbolicList; locksList = afile.locksList; selRevisions = afile.selRevisions; totRevisions = afile.totRevisions; lockStrict = afile.lockStrict; allRevs = afile.allRevs; descLog = afile.descLog; return *this; } #ifdef qUnix void CRcsFile::print(OSTREAM & out) const { out << "Rcs file : '" << RcsFile() << "'\n"; out << "Working file : '" << WorkingFile() << "'\n"; out << "Head revision : " << HeadRev().c_str() << '\n'; out << "Branch revision : " << BranchRev().c_str() << '\n'; out << "Locks :" << (LockStrict() ? " strict" : "") << '\n'; std::vector::const_iterator s; for(s = LocksList().begin(); s != LocksList().end(); ++s) { const CRevNumber & lock = *s; out << '\t' << lock.c_str() << " : '" << lock.Tag() << "'\n"; } out << "Access :\n"; std::vector::const_iterator a; for(a = AccessList().begin(); a != AccessList().end(); ++a) { out << "\t'" << *a << "'\n"; } out << "Symbolic names :\n"; std::vector::const_iterator n; for(n = SymbolicList().begin(); n != SymbolicList().end(); ++n) { const CRevNumber & symb = *n; out << '\t' << symb.c_str() << " : '" << symb.Tag() << "'\n"; } out << "Keyword substitution : '" << KeywordSubst() << "'\n"; out << "Total revisions : " << TotRevisions() << "\n"; out << "Selected revisions : " << SelRevisions() << "\n"; out << "Description :\n" << DescLog() << '\n'; std::vector::const_iterator i; for(i = AllRevs().begin(); i != AllRevs().end(); ++i) { i->print(out); } } #endif extern "C" { static int sortRevs(const void *r1, const void *r2); } #ifdef WIN32 extern "C" #endif /* WIN32 */ static int sortRevs(const void *r1, const void *r2) { CRevNumber & rev1 = ((CRevFile *)r1)->RevNum(); CRevNumber & rev2 = ((CRevFile *)r2)->RevNum(); return rev1.cmp(rev2); } void CRcsFile::sort() { #if !defined(__MWERKS__) && (__GNUC__ < 3) && _MSC_VER < 0x514 qsort(AllRevs().begin(), AllRevs().size(), sizeof(CRevFile), sortRevs); #else /* __MWERKS__ || __GNUC__ > 2 */ qsort(&*AllRevs().begin(), AllRevs().size(), sizeof(CRevFile), sortRevs); #endif /* __MWERKS__ || __GNUCC__ > 2 */ } // // CRevFile // CRevFile::CRevFile() { chgPos = 0; chgNeg = 0; memset(&revTime, 0, sizeof(revTime)); } CRevFile::CRevFile(const CRevFile & afile) { *this = afile; } CRevFile::~CRevFile() { } CRevFile & CRevFile::operator=(const CRevFile & afile) { revNum = afile.revNum; revTime = afile.revTime; locker = afile.locker; branchesList = afile.branchesList; author = afile.author; state = afile.state; chgPos = afile.chgPos; chgNeg = afile.chgNeg; descLog = afile.descLog; return *this; } #ifdef qUnix void CRevFile::print(OSTREAM & out) const { out << "----------------------------\n"; out << "Revision : " << RevNum().c_str() << '\n'; if(!Locker().empty()) out << "Locked by : '" << Locker() << "'\n"; out << "Date : " << (RevTime().tm_year + 1900) << '/' << RevTime().tm_mon << '/' << RevTime().tm_mday << ' ' << RevTime().tm_hour << ':' << RevTime().tm_min << ':' << RevTime().tm_sec << '\n'; out << "Author : '" << Author() << "'\n"; out << "State : '" << State() << "'\n"; out << "Lines : +" << ChgPos() << ' ' << ChgNeg() << '\n'; if(!BranchesList().empty()) { out << "Branches :\n"; std::vector::const_iterator s; for(s = BranchesList().begin(); s != BranchesList().end(); ++s) { const CRevNumber & branch = *s; out << '\t' << branch.c_str() << '\n'; } } out << "Description :\n" << DescLog() << '\n'; } #endif int yy_flex_debug; // remove that as soon a possible std::vector & CvsLogParse(FILE * file) { #if qCvsDebug yydebug = 0; #endif yy_flex_debug = 0; yyin = file; yyreset(); yyrestart(yyin); yyparse(); #ifdef TARGET_OS_MAC std::vector::iterator i; for(i = gMacAlloca.begin(); i != gMacAlloca.end(); i++) { if(*i != 0L) free(*i); *i = 0L; } gMacAlloca.erase(gMacAlloca.begin(), gMacAlloca.end()); #endif /* TARGET_OS_MAC */ return gRcsFiles; } void CvsLogReset(void) { yyreset(); } // // tree nodes // CLogNode::CLogNode(CLogNode * r) { root = r; next = 0L; user = 0L; } CLogNode::~CLogNode() { if(Next() != 0L) delete Next(); std::vector::iterator i; for(i = Childs().begin(); i != Childs().end(); ++i) { delete *i; *i = 0L; } if(user != 0L) delete user; } static bool insertSymbName(CLogNode *node, const CRevNumber & symb) { if(node->GetType() == kNodeRev) { CLogNodeRev & rev = *(CLogNodeRev *)node; if((*rev).RevNum() == symb) { // insert the tag as achild of the node CLogNodeTag *tag = NEW CLogNodeTag(symb.Tag(), node); node->Childs().push_back(tag); return true; } } std::vector::iterator i; for(i = node->Childs().begin(); i != node->Childs().end(); ++i) { if(insertSymbName(*i, symb)) return true; } if(node->Next() != 0L && insertSymbName(node->Next(), symb)) return true; return false; } static bool insertBranchName(CLogNode *node, const CRevNumber & symb) { std::vector::iterator i; for(i = node->Childs().begin(); i != node->Childs().end(); ++i) { CLogNode *subnode = *i; if(subnode->GetType() == kNodeRev) { CLogNodeRev & rev = *(CLogNodeRev *)subnode; if((*rev).RevNum().ispartof(symb)) { // insert the branch name as previous node of the // first node of that branch CLogNodeBranch *branch = NEW CLogNodeBranch(symb.Tag(), node); branch->Next() = subnode; *i = branch; return true; } } if(insertBranchName(subnode, symb)) return true; } if(node->Next() != 0L && insertBranchName(node->Next(), symb)) return true; // we didn't find to connect this branch because there is no // revision in this branch (empty branch) : so add it as a child of this node. if(node->GetType() == kNodeRev) { CLogNodeRev & rev = *(CLogNodeRev *)node; if(symb.issubbranchof((*rev).RevNum())) { CLogNodeBranch *branch = NEW CLogNodeBranch(symb.Tag(), node); node->Childs().push_back(branch); } } return false; } CLogNode *CvsLogGraph(CRcsFile & rcsfile) { // we sort the revisions in order to build the tree // using a stack-algorithm rcsfile.sort(); CLogNodeHeader * header = NEW CLogNodeHeader(rcsfile); CLogNodeRev * curNode = 0L; // append the revisions to the tree std::vector::const_iterator i; for(i = rcsfile.AllRevs().begin(); i != rcsfile.AllRevs().end(); ++i) { const CRevFile & rev = *i; if(curNode == 0L) { CLogNodeRev *nrev = NEW CLogNodeRev(rev, header); header->Childs().push_back(nrev); curNode = nrev; continue; } do { const CRevNumber & curRev = (**curNode).RevNum(); const CRevNumber & thisRev = rev.RevNum(); if(thisRev.ischildof(curRev)) { CLogNodeRev *nrev = NEW CLogNodeRev(rev, curNode); curNode->Childs().push_back(nrev); curNode = nrev; break; } else if(thisRev.issamebranch(curRev)) { CLogNodeRev *nrev = NEW CLogNodeRev(rev, curNode); curNode->Next() = nrev; curNode = nrev; break; } if(curNode->Root() == header) curNode = 0L; else curNode = (CLogNodeRev *)curNode->Root(); } while(curNode != 0L); if(curNode == 0L) { CLogNodeRev *nrev = NEW CLogNodeRev(rev, header); header->Childs().push_back(nrev); curNode = nrev; } } // append the tags std::vector::const_iterator s; for(s = rcsfile.SymbolicList().begin(); s != rcsfile.SymbolicList().end(); ++s) { insertSymbName(header, *s); } // append the branch names for(s = rcsfile.SymbolicList().begin(); s != rcsfile.SymbolicList().end(); ++s) { insertBranchName(header, *s); } return header; } #ifdef TARGET_OS_MAC void *cvstree_alloca(unsigned size) { void *res = malloc(size); if(res == 0L) return 0L; gMacAlloca.push_back(res); return res; } char *cvstree_strdup(const char *string) { int size = strlen(string); char *result = (char *)malloc((size + 1) * sizeof(char)); if(result == 0L) return 0L; strcpy(result, string); return result; } #endif /* TARGET_OS_MAC */