Atrinik Server  4.0
license_check.py
1 #!/usr/bin/python
2 
3 import sys, os, getopt
4 
5 # Common escape sequences.
6 class colors:
7  bold = "\033[1m"
8  underscore = "\033[4m"
9  end = "\033[0m"
10 
11 # Print usage.
12 def usage():
13  print("\n" + colors.bold + colors.underscore + "Use:" + colors.end + colors.end)
14  print("\nAtrinik resources license checker application.\n")
15  print(colors.bold + colors.underscore + "Options:" + colors.end + colors.end)
16  print("\n\t-h, --help:\n\t\tDisplay this help.")
17  print("\n\t-v, --verbose:\n\t\tPrint all resources without known license.")
18  print("\n\t-d " + colors.underscore + "directory" + colors.end + ", --directory=" + colors.underscore + "directory" + colors.end + ":\n\t\tSpecify directory where to start checking licenses (recursively). Default is '.'.")
19  print("\n\t-c " + colors.underscore + "contributor" + colors.end + ", --contributor=" + colors.underscore + "GPL" + colors.end + ":\n\t\tOptional contributor to list all resources of. This can be a part of the contributor's name, the license it is using, etc.")
20  print("\n\t--text-only:\n\t\tDo not use escape sequences for coloring the output.")
21 
22 # Try to parse our command line options.
23 try:
24  opts, args = getopt.getopt(sys.argv[1:], "hvd:c:", ["help", "verbose", "directory=", "contributor=", "text-only"])
25 except getopt.GetoptError as err:
26  # Invalid option, show the error, print usage, and exit.
27  print(err)
28  usage()
29  sys.exit(2)
30 
31 # The default values.
32 path = "."
33 verbose = False
34 contributor = None
35 
36 # Parse options.
37 for o, a in opts:
38  if o in ("-h", "--help"):
39  usage()
40  sys.exit()
41  elif o in ("-v", "--verbose"):
42  verbose = True
43  elif o in ("-d", "--directory"):
44  path = a
45  elif o in ("-c", "--contributor"):
46  contributor = a.lower()
47  elif o == "--text-only":
48  colors.bold = ""
49  colors.underscore = ""
50  colors.end = ""
51 
52 if not os.path.exists(path) or not os.path.isdir(path):
53  print(colors.bold + "ERROR" + colors.end + ": The path '{0}' doesn't exist or is not a directory.".format(path))
54  sys.exit(2)
55 
56 licensed = {}
57 unlicensed = []
58 resource_info = {}
59 
60 def is_resource(s):
61  for ext in (".png", ".s3m", ".ogg", ".mid", ".xm", ".mod"):
62  if s.endswith(ext):
63  if os.path.basename(os.path.dirname(s)) == "durations":
64  return False
65 
66  return True
67 
68  return False
69 
70 # Recursively scans directory for resources under the same license.
71 def dir_recurse(path, license, extra):
72  for entry in os.listdir(path):
73  entry = os.path.join(dirpath, to_remove, entry)
74 
75  # Resource, add it to the list.
76  if is_resource(entry):
77  licensed[license].append(entry)
78 
79  if extra:
80  resource_info[entry] = extra
81  # Go on recursively.
82  elif os.path.isdir(entry):
83  dir_recurse(entry, license, extra)
84 
85 # Scan for resources and license files.
86 for (dirpath, dirnames, filenames) in os.walk(path):
87  # Parse LICENSE file in this directory.
88  if "LICENSE" in filenames:
89  f = os.path.join(dirpath, "LICENSE")
90  fh = open(f, "r")
91 
92  for linenum, line in enumerate(fh, 1):
93  # License name.
94  if line.endswith(":\n"):
95  license = line.strip()[:-1]
96 
97  if not license in licensed:
98  licensed[license] = []
99  # Resource or directory name that is covered by the license.
100  elif line[:1].isspace() and line[1:]:
101  try:
102  parts = line.strip().split()
103  to_remove = parts[0]
104  except IndexError:
105  print("Invalid line in file: {0}, line: {1}".format(f, linenum))
106  continue
107 
108  extra = None
109 
110  # There is more data about the resources(s).
111  if len(parts) > 1:
112  extra = " ".join(parts[1:])
113 
114  to_remove_path = os.path.join(dirpath, to_remove)
115 
116  # Remove it from dirnames to walk through recursively in the outermost loop
117  # but scan the directory to add the resource names.
118  if to_remove in dirnames:
119  dirnames.remove(to_remove)
120  dir_recurse(to_remove_path, license, extra)
121  # Simple resource name.
122  elif to_remove in filenames:
123  licensed[license].append(to_remove_path)
124 
125  if extra:
126  resource_info[to_remove_path] = extra
127 
128  filenames.remove(to_remove)
129  # Invalid entry, drop a warning.
130  else:
131  print(colors.bold + "ERROR" + colors.end + ": Invalid file/directory definition: {0} ({1}), line: {2}".format(to_remove, f, linenum))
132  # Invalid line.
133  elif line != "\n":
134  print(colors.bold + "ERROR" + colors.end + ": Invalid line in file: {0}, line: {1}".format(f, linenum))
135 
136  # Close the license file.
137  fh.close()
138 
139  # Anything else not removed by license parsing above is marked as unlicensed.
140  for entry in filenames:
141  entry = os.path.join(dirpath, entry)
142 
143  if is_resource(entry):
144  unlicensed.append(entry)
145 
146 # Show list of unlicensed resources in verbose mode.
147 if verbose:
148  print(colors.underscore + "List of unlicensed resources:" + colors.end)
149  unlicensed.sort()
150 
151  for entry in unlicensed:
152  print("\t" + entry)
153 
154 if not contributor:
155  print("\n" + colors.underscore + "Resource contributors:" + colors.end)
156  # Store length of licensed resources.
157  licensed_len = 0
158 
159  # Sort the licenses and output their staits.
160  for license in sorted(licensed, key = lambda entry: entry):
161  print("\t" + license + ": " + colors.bold + str(len(licensed[license])) + colors.end)
162  licensed_len += len(licensed[license])
163 
164  # Report total statistics.
165  print("\n" + colors.underscore + "Statistics:" + colors.end)
166  print("\tUnlicensed: {0}{1}{2}".format(colors.bold, len(unlicensed), colors.end))
167  print("\tLicensed: {0}{1}{2}".format(colors.bold, licensed_len, colors.end))
168 
169  total = licensed_len + len(unlicensed)
170 
171  # Show percentage.
172  if total:
173  print("\tPercent: {0}{1}%{2}".format(colors.bold, licensed_len * 100 / total, colors.end))
174 # Look for resources by specific contributor only.
175 else:
176  license = None
177 
178  # Try to find the contributor.
179  for entry in licensed:
180  if entry.lower().find(contributor) != -1:
181  license = entry
182  break
183 
184  # No such contributor.
185  if not license:
186  print(colors.bold + contributor + colors.end + " is not a known contributor.")
187  sys.exit(2)
188 
189  print("\n" + colors.underscore + "Resources by " + license + colors.end + ":")
190 
191  # Dump out the resources.
192  for resource in licensed[license]:
193  print("\t" + resource + (" " + resource_info[resource] if resource in resource_info else ""))
static PyObject * append(Atrinik_AttrList *al, PyObject *value)
Definition: attr_list.c:547