# cvstree.py -- cvs tree utilities # Copyright 1998-2004 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function import codecs import re import stat import sys import time from portage import os from portage import _encodings from portage import _unicode_encode if sys.hexversion >= 0x3000000: long = int # [D]/Name/Version/Date/Flags/Tags def pathdata(entries, path): """(entries,path) Returns the data(dict) for a specific file/dir at the path specified.""" mysplit=path.split("/") myentries=entries mytarget=mysplit[-1] mysplit=mysplit[:-1] for mys in mysplit: if mys in myentries["dirs"]: myentries=myentries["dirs"][mys] else: return None if mytarget in myentries["dirs"]: return myentries["dirs"][mytarget] elif mytarget in myentries["files"]: return myentries["files"][mytarget] else: return None def fileat(entries, path): return pathdata(entries,path) def isadded(entries, path): """(entries,path) Returns true if the path exists and is added to the cvs tree.""" mytarget=pathdata(entries, path) if mytarget: if "cvs" in mytarget["status"]: return 1 basedir=os.path.dirname(path) filename=os.path.basename(path) try: myfile = codecs.open( _unicode_encode(os.path.join(basedir, 'CVS', 'Entries'), encoding=_encodings['fs'], errors='strict'), mode='r', encoding=_encodings['content'], errors='strict') except IOError: return 0 mylines=myfile.readlines() myfile.close() rep=re.compile("^\/"+re.escape(filename)+"\/"); for x in mylines: if rep.search(x): return 1 return 0 def findnew(entries,recursive=0,basedir=""): """(entries,recursive=0,basedir="") Recurses the entries tree to find all elements that have been added but have not yet been committed. Returns a list of paths, optionally prepended with a basedir.""" if basedir and basedir[-1]!="/": basedir=basedir+"/" mylist=[] for myfile in entries["files"]: if "cvs" in entries["files"][myfile]["status"]: if "0" == entries["files"][myfile]["revision"]: mylist.append(basedir+myfile) if recursive: for mydir in entries["dirs"]: mylist+=findnew(entries["dirs"][mydir],recursive,basedir+mydir) return mylist def findoption(entries, pattern, recursive=0, basedir=""): """(entries, pattern, recursive=0, basedir="") Iterate over paths of cvs entries for which the pattern.search() method finds a match. Returns a list of paths, optionally prepended with a basedir.""" if not basedir.endswith("/"): basedir += "/" for myfile, mydata in entries["files"].items(): if "cvs" in mydata["status"]: if pattern.search(mydata["flags"]): yield basedir+myfile if recursive: for mydir, mydata in entries["dirs"].items(): for x in findoption(mydata, pattern, recursive, basedir+mydir): yield x def findchanged(entries,recursive=0,basedir=""): """(entries,recursive=0,basedir="") Recurses the entries tree to find all elements that exist in the cvs tree and differ from the committed version. Returns a list of paths, optionally prepended with a basedir.""" if basedir and basedir[-1]!="/": basedir=basedir+"/" mylist=[] for myfile in entries["files"]: if "cvs" in entries["files"][myfile]["status"]: if "current" not in entries["files"][myfile]["status"]: if "exists" in entries["files"][myfile]["status"]: if entries["files"][myfile]["revision"]!="0": mylist.append(basedir+myfile) if recursive: for mydir in entries["dirs"]: mylist+=findchanged(entries["dirs"][mydir],recursive,basedir+mydir) return mylist def findmissing(entries,recursive=0,basedir=""): """(entries,recursive=0,basedir="") Recurses the entries tree to find all elements that are listed in the cvs tree but do not exist on the filesystem. Returns a list of paths, optionally prepended with a basedir.""" if basedir and basedir[-1]!="/": basedir=basedir+"/" mylist=[] for myfile in entries["files"]: if "cvs" in entries["files"][myfile]["status"]: if "exists" not in entries["files"][myfile]["status"]: if "removed" not in entries["files"][myfile]["status"]: mylist.append(basedir+myfile) if recursive: for mydir in entries["dirs"]: mylist+=findmissing(entries["dirs"][mydir],recursive,basedir+mydir) return mylist def findunadded(entries,recursive=0,basedir=""): """(entries,recursive=0,basedir="") Recurses the entries tree to find all elements that are in valid cvs directories but are not part of the cvs tree. Returns a list of paths, optionally prepended with a basedir.""" if basedir and basedir[-1]!="/": basedir=basedir+"/" mylist=[] #ignore what cvs ignores. for myfile in entries["files"]: if "cvs" not in entries["files"][myfile]["status"]: mylist.append(basedir+myfile) if recursive: for mydir in entries["dirs"]: mylist+=findunadded(entries["dirs"][mydir],recursive,basedir+mydir) return mylist def findremoved(entries,recursive=0,basedir=""): """(entries,recursive=0,basedir="") Recurses the entries tree to find all elements that are in flagged for cvs deletions. Returns a list of paths, optionally prepended with a basedir.""" if basedir and basedir[-1]!="/": basedir=basedir+"/" mylist=[] for myfile in entries["files"]: if "removed" in entries["files"][myfile]["status"]: mylist.append(basedir+myfile) if recursive: for mydir in entries["dirs"]: mylist+=findremoved(entries["dirs"][mydir],recursive,basedir+mydir) return mylist def findall(entries, recursive=0, basedir=""): """(entries,recursive=0,basedir="") Recurses the entries tree to find all new, changed, missing, and unadded entities. Returns a 4 element list of lists as returned from each find*().""" if basedir and basedir[-1]!="/": basedir=basedir+"/" mynew = findnew(entries,recursive,basedir) mychanged = findchanged(entries,recursive,basedir) mymissing = findmissing(entries,recursive,basedir) myunadded = findunadded(entries,recursive,basedir) myremoved = findremoved(entries,recursive,basedir) return [mynew, mychanged, mymissing, myunadded, myremoved] ignore_list = re.compile("(^|/)(RCS(|LOG)|SCCS|CVS(|\.adm)|cvslog\..*|tags|TAGS|\.(make\.state|nse_depinfo)|.*~|(\.|)#.*|,.*|_$.*|.*\$|\.del-.*|.*\.(old|BAK|bak|orig|rej|a|olb|o|obj|so|exe|Z|elc|ln)|core)$") def apply_cvsignore_filter(list): x=0 while x < len(list): if ignore_list.match(list[x].split("/")[-1]): list.pop(x) else: x+=1 return list def getentries(mydir,recursive=0): """(basedir,recursive=0) Scans the given directory and returns an datadict of all the entries in the directory seperated as a dirs dict and a files dict.""" myfn=mydir+"/CVS/Entries" # entries=[dirs, files] entries={"dirs":{},"files":{}} if not os.path.exists(mydir): return entries try: myfile = codecs.open(_unicode_encode(myfn, encoding=_encodings['fs'], errors='strict'), mode='r', encoding=_encodings['content'], errors='strict') mylines=myfile.readlines() myfile.close() except SystemExit as e: raise except: mylines=[] for line in mylines: if line and line[-1]=="\n": line=line[:-1] if not line: continue if line=="D": # End of entries file break mysplit=line.split("/") if len(mysplit)!=6: print("Confused:",mysplit) continue if mysplit[0]=="D": entries["dirs"][mysplit[1]]={"dirs":{},"files":{},"status":[]} entries["dirs"][mysplit[1]]["status"]=["cvs"] if os.path.isdir(mydir+"/"+mysplit[1]): entries["dirs"][mysplit[1]]["status"]+=["exists"] entries["dirs"][mysplit[1]]["flags"]=mysplit[2:] if recursive: rentries=getentries(mydir+"/"+mysplit[1],recursive) entries["dirs"][mysplit[1]]["dirs"]=rentries["dirs"] entries["dirs"][mysplit[1]]["files"]=rentries["files"] else: # [D]/Name/revision/Date/Flags/Tags entries["files"][mysplit[1]]={} entries["files"][mysplit[1]]["revision"]=mysplit[2] entries["files"][mysplit[1]]["date"]=mysplit[3] entries["files"][mysplit[1]]["flags"]=mysplit[4] entries["files"][mysplit[1]]["tags"]=mysplit[5] entries["files"][mysplit[1]]["status"]=["cvs"] if entries["files"][mysplit[1]]["revision"][0]=="-": entries["files"][mysplit[1]]["status"]+=["removed"] for file in apply_cvsignore_filter(os.listdir(mydir)): if file=="CVS": continue if os.path.isdir(mydir+"/"+file): if file not in entries["dirs"]: entries["dirs"][file]={"dirs":{},"files":{}} # It's normal for a directory to be unlisted in Entries # when checked out without -P (see bug #257660). rentries=getentries(mydir+"/"+file,recursive) entries["dirs"][file]["dirs"]=rentries["dirs"] entries["dirs"][file]["files"]=rentries["files"] if "status" in entries["dirs"][file]: if "exists" not in entries["dirs"][file]["status"]: entries["dirs"][file]["status"]+=["exists"] else: entries["dirs"][file]["status"]=["exists"] elif os.path.isfile(mydir+"/"+file): if file not in entries["files"]: entries["files"][file]={"revision":"","date":"","flags":"","tags":""} if "status" in entries["files"][file]: if "exists" not in entries["files"][file]["status"]: entries["files"][file]["status"]+=["exists"] else: entries["files"][file]["status"]=["exists"] try: mystat=os.stat(mydir+"/"+file) mytime = time.asctime(time.gmtime(mystat[stat.ST_MTIME])) if "status" not in entries["files"][file]: entries["files"][file]["status"]=[] if mytime==entries["files"][file]["date"]: entries["files"][file]["status"]+=["current"] except SystemExit as e: raise except Exception as e: print("failed to stat",file) print(e) return else: print() print("File of unknown type:",mydir+"/"+file) print() return entries