from debian import deb822 import os.path import re import sys 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 release in self.d: 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 _ignore_patterns = [re.compile('.*~$'), re.compile('^#.*'), re.compile('^00.*')] def _ignore_issue_name(issue): for p in _ignore_patterns: 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 ] _status_re = re.compile( r'\s*(\S*)' r'(?:\s*\([^\s)]+\))?' # optional version r'(?:\s*\[[^]]+\])?' # optional changerefs r'(?:\s*"[^"]+")?' # optional comment ) _comma_re = re.compile(r'\s*,\s*') def _coalesce_state(old_state, new_state): if old_state is None: return new_state # "needed" overrides everything else if 'needed' in [old_state, new_state]: return 'needed' # "pendng" overrides everything but "needed" if 'pending' in [old_state, new_state]: return 'pending' # We don't expect to use more than one of "released", "ignored", # or "N/A" and it's not clear which should override which if old_state != new_state: print(f'W: Not sure how to coalesce status {old_state} and {new_state}', file=sys.stderr) return old_state def parse_status(s): state = None start = 0 while True: m = _status_re.match(s[start:]) if not m: raise SyntaxError(f'bad status {s[start:]}') start += m.end() # Coalesce with last status state = _coalesce_state(state, m.group(1)) # End of field? if start == len(s) or s[start:].isspace(): return {'state': state} # No, must be followed by a comma separator m = _comma_re.match(s[start:]) if not m: raise SyntaxError(f'missing separator in{s[start:]}') start += m.end()