| # cvstree.py -- cvs tree utilities |
| # Copyright 1998-2014 Gentoo Foundation |
| # Distributed under the terms of the GNU General Public License v2 |
| |
| from __future__ import print_function |
| |
| import io |
| 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: |
| # pylint: disable=W0622 |
| long = int |
| |
| # [D]/Name/Version/Date/Flags/Tags |
| |
| def pathdata(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): |
| """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 = io.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("^\/%s\/" % re.escape(filename)) |
| for x in mylines: |
| if rep.search(x): |
| return 1 |
| |
| return 0 |
| |
| def findnew(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 += "/" |
| |
| 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=""): |
| """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=""): |
| """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 += "/" |
| |
| 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=""): |
| """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 += "/" |
| |
| 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=""): |
| """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 += "/" |
| |
| # Ignore what cvs ignores. |
| mylist = [] |
| 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=""): |
| """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 += "/" |
| |
| 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=""): |
| """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 += "/" |
| 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): |
| """Scans the given directory and returns a datadict of all the entries in |
| the directory separated 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 = io.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 os.listdir(mydir): |
| if file == "CVS": |
| continue |
| if os.path.isdir(mydir + "/" + file): |
| if file not in entries["dirs"]: |
| if ignore_list.match(file) is not None: |
| continue |
| 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"]: |
| if ignore_list.match(file) is not None: |
| continue |
| 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 |
| |
| elif ignore_list.match(file) is not None: |
| pass |
| else: |
| print() |
| print("File of unknown type:", mydir + "/" + file) |
| print() |
| |
| return entries |