X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=utils.py;h=ce9f4f4a973af1b1559c93fadecc345b25cfb751;hb=f92156d5ec5ea4aa65035a6835ec1b9e8c466230;hp=97df03c06d818f32a878a187dfd8e8f1c3a2cb7e;hpb=9fa337542a3305a3e6efa863b92e8ff17677377b;p=dak.git diff --git a/utils.py b/utils.py index 97df03c0..ce9f4f4a 100644 --- a/utils.py +++ b/utils.py @@ -1,6 +1,8 @@ +#!/usr/bin/env python + # Utility functions -# Copyright (C) 2000, 2001 James Troup -# $Id: utils.py,v 1.38 2002-02-12 23:15:23 troup Exp $ +# Copyright (C) 2000, 2001, 2002 James Troup +# $Id: utils.py,v 1.52 2002-11-22 04:06:34 troup Exp $ # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,8 +18,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -import commands, os, pwd, re, socket, shutil, string, sys, tempfile -import apt_pkg +################################################################################ + +import commands, os, pwd, re, socket, shutil, string, sys, tempfile, traceback; +import apt_pkg; +import db_access; + +################################################################################ re_comments = re.compile(r"\#.*") re_no_epoch = re.compile(r"^\d*\:") @@ -29,7 +36,7 @@ re_issource = re.compile (r"(.+)_(.+?)\.(orig\.tar\.gz|diff\.gz|tar\.gz|dsc)$"); re_single_line_field = re.compile(r"^(\S*)\s*:\s*(.*)"); re_multi_line_field = re.compile(r"^\s(.*)"); -re_taint_free = re.compile(r"^[-+\.\w]+$"); +re_taint_free = re.compile(r"^[-+~\.\w]+$"); re_parse_maintainer = re.compile(r"^\s*(\S.*\S)\s*\<([^\> \t]+)\>"); @@ -57,21 +64,18 @@ def open_file(filename, mode='r'): raise cant_open_exc, filename return f -def touch_file(filename): - fd = os.open(filename, os.O_RDONLY | os.O_CREAT); - os.close(fd); - ###################################################################################### -# From reportbug -def our_raw_input(): - sys.stdout.flush() +def our_raw_input(prompt=""): + if prompt: + sys.stdout.write(prompt); + sys.stdout.flush(); try: - ret = raw_input() + ret = raw_input(); return ret except EOFError: - sys.stderr.write('\nUser interrupt (^D).\n') - raise SystemExit + sys.stderr.write('\nUser interrupt (^D).\n'); + raise SystemExit; ###################################################################################### @@ -86,18 +90,18 @@ def str_isnum (s): def extract_component_from_section(section): component = ""; - if string.find(section, '/') != -1: - component = string.split(section, '/')[0]; - if string.lower(component) == "non-us" and string.count(section, '/') > 0: - s = component + '/' + string.split(section, '/')[1]; + if section.find('/') != -1: + component = section.split('/')[0]; + if component.lower() == "non-us" and section.count('/') > 0: + s = component + '/' + section.split('/')[1]; if Cnf.has_key("Component::%s" % s): # Avoid e.g. non-US/libs component = s; - if string.lower(section) == "non-us": + if section.lower() == "non-us": component = "non-US/main"; # non-US prefix is case insensitive - if string.lower(component)[:6] == "non-us": + if component.lower()[:6] == "non-us": component = "non-US"+component[6:]; # Expand default component @@ -126,7 +130,7 @@ def extract_component_from_section(section): # o The data section must end with a blank line and must be followed by # "-----BEGIN PGP SIGNATURE-----". -def parse_changes(filename, dsc_whitespace_rules): +def parse_changes(filename, dsc_whitespace_rules=0): changes_in = open_file(filename); error = ""; changes = {}; @@ -140,58 +144,61 @@ def parse_changes(filename, dsc_whitespace_rules): index = 0; indexed_lines = {}; for line in lines: - index = index + 1; + index += 1; indexed_lines[index] = line[:-1]; inside_signature = 0; indices = indexed_lines.keys() index = 0; + first = -1; while index < max(indices): - index = index + 1; + index += 1; line = indexed_lines[index]; if line == "": if dsc_whitespace_rules: - index = index + 1; + index += 1; if index > max(indices): raise invalid_dsc_format_exc, index; line = indexed_lines[index]; - if string.find(line, "-----BEGIN PGP SIGNATURE") != 0: + if not line.startswith("-----BEGIN PGP SIGNATURE"): raise invalid_dsc_format_exc, index; inside_signature = 0; break; - if string.find(line, "-----BEGIN PGP SIGNATURE") == 0: + if line.startswith("-----BEGIN PGP SIGNATURE"): break; - if string.find(line, "-----BEGIN PGP SIGNED MESSAGE") == 0: + if line.startswith("-----BEGIN PGP SIGNED MESSAGE"): if dsc_whitespace_rules: inside_signature = 1; while index < max(indices) and line != "": - index = index + 1; + index += 1; line = indexed_lines[index]; continue; slf = re_single_line_field.match(line); if slf: - field = string.lower(slf.groups()[0]); + field = slf.groups()[0].lower(); changes[field] = slf.groups()[1]; first = 1; continue; if line == " .": - changes[field] = changes[field] + '\n'; + changes[field] += '\n'; continue; mlf = re_multi_line_field.match(line); if mlf: + if first == -1: + raise changes_parse_error_exc, "'%s'\n [Multi-line field continuing on from nothing?]" % (line); if first == 1 and changes[field] != "": - changes[field] = changes[field] + '\n'; + changes[field] += '\n'; first = 0; - changes[field] = changes[field] + mlf.groups()[0] + '\n'; + changes[field] += mlf.groups()[0] + '\n'; continue; - error = error + line; + error += line; if dsc_whitespace_rules and inside_signature: raise invalid_dsc_format_exc, index; changes_in.close(); - changes["filecontents"] = string.join (lines, ""); + changes["filecontents"] = "".join(lines); if error != "": raise changes_parse_error_exc, error; @@ -202,25 +209,25 @@ def parse_changes(filename, dsc_whitespace_rules): # Dropped support for 1.4 and ``buggy dchanges 3.4'' (?!) compared to di.pl -def build_file_list(changes, dsc): +def build_file_list(changes, is_a_dsc=0): files = {} format = changes.get("format", "") if format != "": format = float(format) - if dsc == "" and (format < 1.5 or format > 2.0): + if not is_a_dsc and (format < 1.5 or format > 2.0): raise nk_format_exc, format; # No really, this has happened. Think 0 length .dsc file. if not changes.has_key("files"): raise no_files_exc - for i in string.split(changes["files"], "\n"): + for i in changes["files"].split("\n"): if i == "": break - s = string.split(i) + s = i.split(); section = priority = ""; try: - if dsc != "": + if is_a_dsc: (md5, size, name) = s else: (md5, size, section, priority, name) = s @@ -257,7 +264,7 @@ def fix_maintainer (maintainer): if m != None and len(m.groups()) == 2: name = m.group(1) email = m.group(2) - if string.find(name, ',') != -1 or string.find(name, '.') != -1: + if name.find(',') != -1 or name.find('.') != -1: rfc822 = re_parse_maintainer.sub(r"\2 (\1)", maintainer) return (rfc822, name, email) @@ -289,10 +296,9 @@ def send_mail (message, filename): def poolify (source, component): if component != "": - component = component + '/'; + component += '/'; # FIXME: this is nasty - component = string.lower(component); - component = string.replace(component, 'non-us/', 'non-US/'); + component = component.lower().replace('non-us/', 'non-US/'); if source[:3] == "lib": return component + source[:4] + '/' + source + '/' else: @@ -300,7 +306,7 @@ def poolify (source, component): ###################################################################################### -def move (src, dest, overwrite = 0): +def move (src, dest, overwrite = 0, perms = 0664): if os.path.exists(dest) and os.path.isdir(dest): dest_dir = dest; else: @@ -311,7 +317,7 @@ def move (src, dest, overwrite = 0): os.umask(umask); #print "Moving %s to %s..." % (src, dest); if os.path.exists(dest) and os.path.isdir(dest): - dest = dest + '/' + os.path.basename(src); + dest += '/' + os.path.basename(src); # Don't overwrite unless forced to if os.path.exists(dest): if not overwrite: @@ -320,10 +326,10 @@ def move (src, dest, overwrite = 0): if not os.access(dest, os.W_OK): raise cant_overwrite_exc shutil.copy2(src, dest); - os.chmod(dest, 0664); + os.chmod(dest, perms); os.unlink(src); -def copy (src, dest, overwrite = 0): +def copy (src, dest, overwrite = 0, perms = 0664): if os.path.exists(dest) and os.path.isdir(dest): dest_dir = dest; else: @@ -334,7 +340,7 @@ def copy (src, dest, overwrite = 0): os.umask(umask); #print "Copying %s to %s..." % (src, dest); if os.path.exists(dest) and os.path.isdir(dest): - dest = dest + '/' + os.path.basename(src); + dest += '/' + os.path.basename(src); # Don't overwrite unless forced to if os.path.exists(dest): if not overwrite: @@ -343,7 +349,7 @@ def copy (src, dest, overwrite = 0): if not os.access(dest, os.W_OK): raise cant_overwrite_exc shutil.copy2(src, dest); - os.chmod(dest, 0664); + os.chmod(dest, perms); ###################################################################################### @@ -375,17 +381,20 @@ def which_apt_conf_file (): # (woefully incomplete) def regex_safe (s): - s = string.replace(s, '+', '\\\\+'); - s = string.replace(s, '.', '\\\\.'); + s = s.replace('+', '\\\\+'); + s = s.replace('.', '\\\\.'); return s ###################################################################################### # Perform a substition of template -def TemplateSubst(Map,Template): - for x in Map.keys(): - Template = string.replace(Template,x,Map[x]); - return Template; +def TemplateSubst(map, filename): + file = open_file(filename); + template = file.read(); + for x in map.keys(): + template = template.replace(x,map[x]); + file.close(); + return template; ###################################################################################### @@ -401,7 +410,7 @@ def warn(msg): # Returns the user name with a laughable attempt at rfc822 conformancy # (read: removing stray periods). def whoami (): - return string.replace(string.split(pwd.getpwuid(os.getuid())[4],',')[0], '.', ''); + return pwd.getpwuid(os.getuid())[4].split(',')[0].replace('.', ''); ###################################################################################### @@ -422,36 +431,25 @@ def cc_fix_changes (changes): if o != "": del changes["architecture"] changes["architecture"] = {} - for j in string.split(o): + for j in o.split(): changes["architecture"][j] = 1 -# Sort by 'have source', by source name, by source version number, by filename - +# Sort by source name, source version, 'have source', and then by filename def changes_compare (a, b): try: - a_changes = parse_changes(a, 0) + a_changes = parse_changes(a); except: return -1; try: - b_changes = parse_changes(b, 0) + b_changes = parse_changes(b); except: return 1; cc_fix_changes (a_changes); cc_fix_changes (b_changes); - # Sort by 'have source' - - a_has_source = a_changes["architecture"].get("source") - b_has_source = b_changes["architecture"].get("source") - if a_has_source and not b_has_source: - return -1; - elif b_has_source and not a_has_source: - return 1; - # Sort by source name - a_source = a_changes.get("source"); b_source = b_changes.get("source"); q = cmp (a_source, b_source); @@ -459,15 +457,21 @@ def changes_compare (a, b): return q; # Sort by source version - a_version = a_changes.get("version"); b_version = b_changes.get("version"); q = apt_pkg.VersionCompare(a_version, b_version); if q: - return q + return q; - # Fall back to sort by filename + # Sort by 'have source' + a_has_source = a_changes["architecture"].get("source"); + b_has_source = b_changes["architecture"].get("source"); + if a_has_source and not b_has_source: + return -1; + elif b_has_source and not a_has_source: + return 1; + # Fall back to sort by filename return cmp(a, b); ################################################################################ @@ -477,7 +481,7 @@ def find_next_free (dest, too_many=100): orig_dest = dest; while os.path.exists(dest) and extra < too_many: dest = orig_dest + '.' + repr(extra); - extra = extra + 1; + extra += 1; if extra >= too_many: raise tried_too_hard_exc; return dest; @@ -491,13 +495,59 @@ def result_join (original, sep = '\t'): list.append(""); else: list.append(original[i]); - return string.join(list, sep); + return sep.join(list); + +################################################################################ + +def prefix_multi_line_string(str, prefix): + out = ""; + for line in str.split('\n'): + line = line.strip(); + if line: + out += "%s%s\n" % (prefix, line); + # Strip trailing new line + if out: + out = out[:-1]; + return out; + +################################################################################ -def plural (x): - if x > 1: - return "s"; +def validate_changes_file_arg(file, fatal=1): + error = None; + + orig_filename = file + if file.endswith(".katie"): + file = file[:-6]+".changes"; + + if not file.endswith(".changes"): + error = "invalid file type; not a changes file"; else: - return ""; + if not os.access(file,os.R_OK): + if os.path.exists(file): + error = "permission denied"; + else: + error = "file not found"; + + if error: + if fatal: + fubar("%s: %s." % (orig_filename, error)); + else: + warn("Skipping %s - %s" % (orig_filename, error)); + return None; + else: + return file; + +################################################################################ + +def real_arch(arch): + return (arch != "source" and arch != "all"); + +################################################################################ + +def join_with_commas_and(list): + if len(list) == 0: return "nothing"; + if len(list) == 1: return list[0]; + return ", ".join(list[:-1]) + " and " + list[-1]; ################################################################################ @@ -506,6 +556,93 @@ def get_conf(): ################################################################################ +# Handle -a, -c and -s arguments; returns them as SQL constraints +def parse_args(Options): + # Process suite + if Options["Suite"]: + suite_ids_list = []; + for suite in Options["Suite"].split(): + suite_id = db_access.get_suite_id(suite); + if suite_id == -1: + warn("suite '%s' not recognised." % (suite)); + else: + suite_ids_list.append(suite_id); + if suite_ids_list: + con_suites = "AND su.id IN (%s)" % ", ".join(map(str, suite_ids_list)); + else: + fubar("No valid suite given."); + else: + con_suites = ""; + + # Process component + if Options["Component"]: + component_ids_list = []; + for component in Options["Component"].split(): + component_id = db_access.get_component_id(component); + if component_id == -1: + warn("component '%s' not recognised." % (component)); + else: + component_ids_list.append(component_id); + if component_ids_list: + con_components = "AND c.id IN (%s)" % ", ".join(map(str, component_ids_list)); + else: + fubar("No valid component given."); + else: + con_components = ""; + + # Process architecture + con_architectures = ""; + if Options["Architecture"]: + arch_ids_list = []; + check_source = 0; + for architecture in Options["Architecture"].split(): + if architecture == "source": + check_source = 1; + else: + architecture_id = db_access.get_architecture_id(architecture); + if architecture_id == -1: + warn("architecture '%s' not recognised." % (architecture)); + else: + arch_ids_list.append(architecture_id); + if arch_ids_list: + con_architectures = "AND a.id IN (%s)" % ", ".join(map(str, arch_ids_list)); + else: + if not check_source: + fubar("No valid architecture given."); + else: + check_source = 1; + + return (con_suites, con_architectures, con_components, check_source); + +################################################################################ + +# Inspired(tm) by Bryn Keller's print_exc_plus (See +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52215) + +def print_exc(): + tb = sys.exc_info()[2]; + while tb.tb_next: + tb = tb.tb_next; + stack = []; + frame = tb.tb_frame; + while frame: + stack.append(frame); + frame = frame.f_back; + stack.reverse(); + traceback.print_exc(); + for frame in stack: + print "\nFrame %s in %s at line %s" % (frame.f_code.co_name, + frame.f_code.co_filename, + frame.f_lineno); + for key, value in frame.f_locals.items(): + print "\t%20s = " % key,; + try: + print value; + except: + print ""; + +################################################################################ + apt_pkg.init() Cnf = apt_pkg.newConfiguration();