#!/usr/bin/env python

# Cruft checker for overrides
# Copyright (C) 2000, 2001, 2002, 2004  James Troup <james@nocrew.org>
# $Id: cindy,v 1.13 2004/11/27 19:23:40 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
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

################################################################################

######################################################################
# NB: cindy is not a good idea with New Incoming as she doesn't take #
# into account accepted.  You can minimize the impact of this by     #
# running her immediately after kelly but that's still racy because  #
# lisa doesn't lock with kelly.  A better long term fix is the evil  #
# plan for accepted to be in the DB.                                 #
######################################################################

# NB[2]: cindy entirely breaks for suites that share overrides,
# e.g. experimental in Debian.

# NB[3]: cindy ENTIRELY breaks for 'source-only'-upload based distros
# like Ubuntu.  Go Cindy.

################################################################################

import pg, sys;
import utils, db_access;
import apt_pkg;

################################################################################

Cnf = None;
Options = None;
projectB = None;
override = {}

################################################################################

def usage (exit_code=0):
    print """Usage: cindy
Check for cruft in overrides.

  -h, --help                 show this help and exit"""

    sys.exit(exit_code)

################################################################################

def process(suite, component, type):
    global override;

    suite_id = db_access.get_suite_id(suite);
    if suite_id == -1:
        utils.fubar("Suite '%s' not recognised." % (suite));

    component_id = db_access.get_component_id(component);
    if component_id == -1:
        utils.fubar("Component '%s' not recognised." % (component));

    type_id = db_access.get_override_type_id(type);
    if type_id == -1:
        utils.fubar("Type '%s' not recognised. (Valid types are deb, udeb and dsc)" % (type));
    dsc_type_id = db_access.get_override_type_id("dsc");

    if type == "deb" or type == "udeb":
        packages = {};
        q = projectB.query("""
SELECT b.package FROM binaries b, bin_associations ba, files f,
                              location l, component c
 WHERE b.id = ba.bin AND f.id = b.file AND l.id = f.location
   AND c.id = l.component AND ba.suite = %s AND c.id = %s
""" % (suite_id, component_id));
        for i in q.getresult():
            packages[i[0]] = "";

    src_packages = {};
    q = projectB.query("""
SELECT s.source FROM source s, src_associations sa, files f, location l,
                     component c
 WHERE s.id = sa.source AND f.id = s.file AND l.id = f.location
   AND c.id = l.component AND sa.suite = %s AND c.id = %s
""" % (suite_id, component_id));
    for i in q.getresult():
        src_packages[i[0]] = "";

    q = projectB.query("SELECT package, priority, section, maintainer FROM override WHERE suite = %s AND component = %s AND type = %s" % (suite_id, component_id, type_id));
    projectB.query("BEGIN WORK");
    for i in q.getresult():
        package = i[0];
        if type == "deb" or type == "udeb":
            if not packages.has_key(package):
                if not src_packages.has_key(package):
                    print "DELETING: %s" % (package);
                    if not Options["No-Action"]:
                        projectB.query("DELETE FROM override WHERE package = '%s' AND suite = %s AND component = %s AND type = %s" % (package, suite_id, component_id, type_id));
                else:
                    print "MAKING SOURCE: %s" % (package);
                    if not Options["No-Action"]:
                        projectB.query("DELETE FROM override WHERE package = '%s' AND suite = %s AND component = %s AND type = %s" % (package, suite_id, component_id, type_id));
                    # Then if source doesn't already have a copy, insert it into source
                    q = projectB.query("SELECT package FROM override WHERE package = '%s' AND suite = %s AND component = %s AND type = %s" % (package, suite_id, component_id, dsc_type_id));
                    if not q.getresult() and not Options["No-Action"]:
                        projectB.query("INSERT INTO override (package, suite, component, priority, section, type, maintainer) VALUES ('%s', %s, %s, %s, %s, %s, '%s')" % (package, suite_id, component_id, i[1], i[2], dsc_type_id, i[3]));
        else: # dsc
            if not src_packages.has_key(package):
                print "DELETING: %s" % (package);
                if not Options["No-Action"]:
                    projectB.query("DELETE FROM override WHERE package = '%s' AND suite = %s AND component = %s AND type = %s" % (package, suite_id, component_id, type_id));
    projectB.query("COMMIT WORK");


################################################################################

def main ():
    global Cnf, Options, projectB, override;

    Cnf = utils.get_conf()

    Arguments = [('h',"help","Cindy::Options::Help"),
                 ('n',"no-action", "Cindy::Options::No-Action")];
    for i in [ "help", "no-action" ]:
	if not Cnf.has_key("Cindy::Options::%s" % (i)):
	    Cnf["Cindy::Options::%s" % (i)] = "";
    apt_pkg.ParseCommandLine(Cnf, Arguments, sys.argv);
    Options = Cnf.SubTree("Cindy::Options")

    if Options["Help"]:
	usage();

    projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"], int(Cnf["DB::Port"]));
    db_access.init(Cnf, projectB);

    suite = "unstable"
    print "Processing %s..." % (suite);
    for component in Cnf.SubTree("Component").List():
        if component == "mixed":
            continue; # Ick
        for otype in Cnf.ValueList("OverrideType"):
            print "Processing %s [%s - %s]..." % (suite, component, otype);
            process(suite, component, otype);

################################################################################

if __name__ == '__main__':
    main()

