blob: 87bbed8bbd7de924a4daaa3472943143107a416b [file] [log] [blame]
# cvstree.py -- cvs tree utilities
# Copyright 1998-2017 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(r"^\/%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(r"(^|/)(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