#!/usr/bin/python

# lsb_release command for Debian
# (C) 2005 Chris Lawrence <lawrencc@debian.org>

#    This package 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; version 2 dated June, 1991.

#    This package 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 package; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
#    02111-1307, USA.

from optparse import OptionParser
import sys
import commands
import os
import re

# XXX: Update as needed
RELEASE_CODENAME_LOOKUP = {
    '1.1' : 'buzz',
    '1.2' : 'rex',
    '1.3' : 'bo',
    '2.0' : 'hamm',
    '2.1' : 'slink',
    '2.2' : 'potato',
    '3.0' : 'woody',
    '3.1' : 'sarge',
    '3.2' : 'etch', # XXX - may not end up as 3.2
    }

TESTING_CODENAME = 'etch'

# LSB compliance packages... may grow eventually
PACKAGES = 'lsb-core lsb-cxx lsb-graphics lsb-desktop lsb-qt4'

modnamere = re.compile(r'lsb-(?P<module>[a-z]+)-(?P<arch>[^ ]+)(?: \(= (?P<version>[0-9.]+)\))?')

def valid_lsb_versions(version, module):
    # If a module is ever released that only appears in >= version, deal
    # with that here
    if version == '3.0':
        return ['2.0', '3.0']
    elif version == '3.1':
        if module in ('desktop', 'qt4'):
            return ['3.1']
        else:
            return ['2.0', '3.0', '3.1']

    return [version]

def check_modules_installed():
    # Find which LSB modules are installed on this system
    output = commands.getoutput("dpkg-query -f '${Version} ${Provides}\n' -W %s 2>/dev/null" % PACKAGES)
    if not output:
        return []

    modules = []
    for line in output.split(os.linesep):
        version, provides = line.split(' ', 1)
        version = version.split('-', 1)[0]
        for pkg in provides.split(','):
            mob = modnamere.search(pkg)
            if not mob:
                continue

            mgroups = mob.groupdict()
            # If no versioned provides...
            if mgroups.get('version'):
                module = '%(module)s-%(version)s-%(arch)s' % mgroups
                modules += [module]
            else:
                module = mgroups['module']
                for v in valid_lsb_versions(version, module):
                    mgroups['version'] = v
                    module = '%(module)s-%(version)s-%(arch)s' % mgroups
                    modules += [module]
                    
    return modules

priorityre = re.compile(r'\s+(\d+)\s+')
versionre = re.compile(r'release v=([\d.+]+),o=Debian,a=([^,]+).*c=main')
releasere = re.compile(r'release o=Debian,a=([^,]+).*c=main')

def guess_release_from_apt():
    releases = []

    priority = None
    policy = commands.getoutput('apt-cache policy 2>/dev/null')
    for line in policy.split('\n'):
        m = priorityre.match(line)
        if m:
            priority = int(m.group(1))
        
        m = versionre.search(line)
        if m and priority is not None:
            releases.append((priority, m.group(1)))

        m = releasere.search(line)
        if m and priority is not None:
            releases.append((priority, m.group(1)))

    if not releases:
        return None

    releases.sort()
    releases.reverse()
    for (pri, release) in releases:
        if release != 'experimental':
            return release

    return None

def guess_debian_release():
    distinfo = {'ID' : 'Debian'}

    kern = os.uname()[0]
    if kern in ('Linux', 'Hurd', 'NetBSD'):
        distinfo['OS'] = 'GNU/'+kern
    elif kern == 'FreeBSD':
        distinfo['OS'] = 'GNU/k'+kern
    else:
        distinfo['OS'] = 'GNU'

    distinfo['DESCRIPTION'] = '%(ID)s %(OS)s' % distinfo

    if os.path.exists('/etc/debian_version'):
        release = open('/etc/debian_version').read().strip()
        if not release[0:1].isalpha():
            # /etc/debian_version should be numeric
            codename = RELEASE_CODENAME_LOOKUP.get(release, 'n/a')
            distinfo.update({ 'RELEASE' : release, 'CODENAME' : codename })
        else:
            release = guess_release_from_apt()
            if release:
                codename = RELEASE_CODENAME_LOOKUP.get(release)
                if not codename:
                    if release == 'testing':
                        codename = TESTING_CODENAME
                    else:
                        codename = 'sid'
                distinfo.update({ 'RELEASE' : release, 'CODENAME' : codename})
            else:
                distinfo.update({ 'RELEASE' : release, 'CODENAME' : 'sid'})
        distinfo['DESCRIPTION'] += ' %(RELEASE)s (%(CODENAME)s)' % distinfo

    distinfo['DESCRIPTION'] = '%(ID)s %(OS)s %(RELEASE)s (%(CODENAME)s)' % distinfo
    return distinfo

def get_distro_information():
    distinfo = guess_debian_release()

    # Replace any info in /etc/lsb-release
    if os.path.exists('/etc/lsb-release'):
        for line in open('/etc/lsb-release'):
            line = line.strip()
            if not line:
                continue
            var, arg = line.split('=', 1)
            if var.startswith('DISTRIB_'):
                var = var[8:]
                if arg.startswith('"') and arg.endswith('"'):
                    arg = arg[1:-1]
                distinfo[var] = arg

    return distinfo
    
def main():
    parser = OptionParser()
    parser.add_option('-v', '--version', dest='version', action='store_true',
                      default=False,
                      help="show LSB modules this system supports")
    parser.add_option('-i', '--id', dest='id', action='store_true',
                      default=False,
                      help="show distributor ID")
    parser.add_option('-d', '--description', dest='description',
                      default=False, action='store_true',
                      help="show description of this distribution")
    parser.add_option('-r', '--release', dest='release',
                      default=False, action='store_true',
                      help="show release number of this distribution")
    parser.add_option('-c', '--codename', dest='codename',
                      default=False, action='store_true',
                      help="show code name of this distribution")
    parser.add_option('-a', '--all', dest='all',
                      default=False, action='store_true',
                      help="show all of the above information")
    parser.add_option('-s', '--short', dest='short',
                      action='store_true', default=False,
                      help="show all of the above information in short format")
    
    (options, args) = parser.parse_args()
    if args:
        parser.error("No arguments are permitted")

    short = (options.short)
    all = (options.all)
    none = not (options.all or options.version or options.id or
                options.description or options.codename or options.release)

    distinfo = get_distro_information()

    if none or all or options.version:
        verinfo = check_modules_installed()
        if not verinfo:
            print >> sys.stderr, "No LSB modules are available."
        elif short:
            print ':'.join(verinfo)
        else:
            print 'LSB Version:\t' + ':'.join(verinfo)

    if options.id or all:
        if short:
            print distinfo.get('ID', 'n/a')
        else:
            print 'Distributor ID:\t%s' % distinfo.get('ID', 'n/a')

    if options.description or all:
        if short:
            print distinfo.get('DESCRIPTION', 'n/a')
        else:
            print 'Description:\t%s' % distinfo.get('DESCRIPTION', 'n/a')

    if options.release or all:
        if short:
            print distinfo.get('RELEASE', 'n/a')
        else:
            print 'Release:\t%s' % distinfo.get('RELEASE', 'n/a')

    if options.codename or all:
        if short:
            print distinfo.get('CODENAME', 'n/a')
        else:
            print 'Codename:\t%s' % distinfo.get('CODENAME', 'n/a')

if __name__ == '__main__':
    main()
