#!/usr/bin/python import os, re, sys, curses from optparse import OptionParser from debian import deb822 class issue(deb822.Deb822): nonReleaseFields = ['Candidate', 'References', 'Description', 'Notes', 'Bugs'] reservedPrefixes = ['Ubuntu-'] def __init__(self, path): self.file = open(path, 'r') self.d = deb822.Deb822(self.file) self.name = os.path.basename(path) def status(self, release): if self.d.has_key(release): return self.d[release] else: return "" def fieldIsRelease(self, field): if field in self.nonReleaseFields: return False for p in self.reservedPrefixes: if field[:len(p)] == p: return False return True def get_releases(self): releases = set() for field in self.d.keys(): if self.fieldIsRelease(field): releases.add(field) return releases ignorePatterns = [re.compile('.*~$'), re.compile('^#.*'), re.compile('^00.*')] def ignore_issue_name(issue): for p in ignorePatterns: if p.match(issue): return True return False # Pad last part of CVE ID to 8 digits so string comparison keeps working def pad_cve_id(cve_id): return re.sub(r'-(\d+)$', lambda m: '-%08d' % int(m.group(1)), cve_id) def get_issues(dir): issues = [] L = [f for f in os.listdir(dir) if not os.path.isdir(os.path.join(dir, f)) and not ignore_issue_name(f)] L.sort(key=pad_cve_id) return [ issue(os.path.join(dir, f)) for f in L ] def parse_status(s): ws = '\s*' versions = '(?P\((\S*,\s*)*\S*\s*\))' changerefs = '(?P\[(\S*,\s*)*\S*\s*\])' state = '(?P\S*)' statusre = re.compile(ws + state + ws + '(' + versions + '?)' + '(' + changerefs + '?)') m = statusre.match(s) if not m: raise SyntaxError else: return m def filter_out_states(issues, releases, states, notstates): filteredissues = [] for i in issues: for release in releases or i.get_releases(): # Current state must be within 'states' (if specified), and # must not be within 'notstates' (if specified). m = parse_status(i.status(release)) if ((m.group('state') in states if states else True) and (m.group('state') not in notstates if notstates else True)): filteredissues.append(i) break return filteredissues if __name__ == '__main__': parser = OptionParser() parser.add_option("-d", "--dirs", action="append") parser.add_option("-r", "--release", action="append") parser.add_option("-s", "--states", action="append") parser.add_option("-n", "--notstates", action="append") parser.add_option("-c", "--color", "--colour", action="store_true") parser.add_option("--no-color", "--no-colour", action="store_false", dest="color") (options, args) = parser.parse_args() if not options.dirs: print('I: Listing issues in active directory') options.dirs = ['active'] if not options.states and not options.notstates: print('I: Excluding N/A, ignored and released issues') options.notstates = ['N/A', 'ignored', 'released'] inc_releases = options.release and set(options.release) if options.color is None: options.color = sys.stdout.isatty() if options.color: curses.setupterm() status_color = {"needed": curses.tparm(curses.tigetstr("setaf"), curses.COLOR_RED), "ignored": curses.tparm(curses.tigetstr("setaf"), curses.COLOR_YELLOW), "pending": curses.tparm(curses.tigetstr("setaf"), curses.COLOR_MAGENTA), "released": curses.tparm(curses.tigetstr("setaf"), curses.COLOR_GREEN), "N/A": curses.tparm(curses.tigetstr("setaf"), curses.COLOR_GREEN)} color_off = curses.tparm(curses.tigetstr("op")) else: color_off = '' issues = [] for d in options.dirs: issues = issues + get_issues(d) if options.states or options.notstates: issues = filter_out_states(issues, inc_releases, options.states, options.notstates) if options.release: list_releases = options.release else: all_releases = set() for i in issues: all_releases |= i.get_releases() list_releases = sorted(list(all_releases)) if len(list_releases) == 1: min_width = 0 max_width = 1000 else: min_width = 20 max_width = 20 sys.stdout.write(" ") for release in list_releases: sys.stdout.write(" %-20.20s " % release) sys.stdout.write("\n") for i in issues: sys.stdout.write("%17s:" % i.name) for release in list_releases: status = i.status(release) or "unknown" status_short = status.split(' ')[0] if options.color and status_color.has_key(status_short): color_on = status_color[status_short] else: color_on = '' sys.stdout.write(" %s%-*.*s%s " % (color_on, min_width, max_width, status, color_off)) sys.stdout.write("\n")