Blob Blame History Raw
#!/usr/bin/python

## Copyright (C) 2009, 2010 Red Hat, Inc.
## Author: Tim Waugh <twaugh@redhat.com>

## 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., 675 Mass Ave, Cambridge, MA 02139, USA.

import sys

try:
    import cups
    CAN_EXAMINE_PPDS = True
except:
    CAN_EXAMINE_PPDS = False

from getopt import getopt
import errno
import os
import posix
import re
import shlex
import signal
import subprocess
import sys
import tempfile

if len (sys.argv) > 1:
    RPM_BUILD_ROOT = sys.argv[1]
else:
    RPM_BUILD_ROOT = None

class TimedOut(Exception):
    def __init__ (self):
        Exception.__init__ (self, "Timed out")

class DeviceIDs:
    def __init__ (self):
        self.ids = dict()

    def get_dict (self):
        return self.ids

    def get_tags (self):
        ret = []
        for mfg, mdlset in self.ids.iteritems ():
            mfgl = mfg.lower ().replace (" ", "_")
            for mdl in mdlset:
                mdll = mdl.lower ().replace (" ", "_")
                ret.append ("postscriptdriver(%s;%s;)" % (mfgl,
                                                          mdll))

        return ret

    def __add__ (self, other):
        if isinstance(other, DeviceIDs):
            for omfg, omdlset in other.ids.iteritems ():
                try:
                    mdlset = self.ids[omfg]
                except KeyError:
                    mdlset = set()
                    self.ids[omfg] = mdlset

                mdlset.update (omdlset)

            return self

        pieces = other.split (';')
        mfg = mdl = None
        for piece in pieces:
            s = piece.split (":")
            if len (s) != 2:
                continue
            key, value = s
            key = key.upper ().strip ()
            if key in ["MFG", "MANUFACTURER"]:
                mfg = value.strip ()
            elif key in ["MDL", "MODEL"]:
                mdl = value.strip ()

        if mfg and mdl:
            try:
                mdlset = self.ids[mfg]
            except KeyError:
                mdlset = set()
                self.ids[mfg] = mdlset

            mdlset.add (mdl)

        return self

class Driver:
    def __init__ (self):
        self.ids = DeviceIDs()

    def list (self):
        return self.ids

class PPDDriver(Driver):
    def __init__ (self, pathname=None):
        Driver.__init__ (self)
        self.pathname = pathname

    def list (self):
        if self.pathname != None:
            self.examine_file (self.pathname)

        return Driver.list (self)

    def examine_file (self, path):
        try:
            ppd = cups.PPD (path)
        except RuntimeError, e:
            # Not a PPD file.  Perhaps it's a drv file.
            drv = DrvDriver (path)
            self.ids += drv.list ()
            return

        attr = ppd.findAttr ('1284DeviceID')
        while attr:
            self.ids += attr.value
            attr = ppd.findNextAttr ('1284DeviceID')

class DynamicDriver(Driver):
    def __init__ (self, driver):
        Driver.__init__ (self)
        self.driver = driver
        signal.signal (signal.SIGALRM, self._alarm)

    def _alarm (self, sig, stack):
        raise TimedOut

    def list (self):
        signal.alarm (60)
        env = os.environ.copy ()
        if RPM_BUILD_ROOT:
            buildroot = RPM_BUILD_ROOT
            if not buildroot.endswith (os.path.sep):
                buildroot += os.path.sep

            env["DESTDIR"] = RPM_BUILD_ROOT
            env["LD_LIBRARY_PATH"] = "%susr/lib64:%susr/lib" % (buildroot,
                                                                buildroot)

        p = subprocess.Popen ([self.driver, "list"],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE,
                              env=env)
        try:
            (stdout, stderr) = p.communicate ()
            signal.alarm (0)
        except TimedOut:
            posix.kill (p.pid, signal.SIGKILL)
            raise

	if stderr:
		print >> sys.stderr, stderr

	ppds = []
	lines = stdout.split ('\n')
	for line in lines:
		l = shlex.split (line)
		if len (l) < 5:
                    continue
                self.ids += l[4]

        return Driver.list (self)

class DrvDriver(PPDDriver):
    def __init__ (self, pathname):
        PPDDriver.__init__ (self)
        self.drv = pathname

    def _alarm (self, sig, stack):
        raise TimedOut

    def list (self):
        tmpdir = os.environ.get ("TMPDIR", "/tmp") + os.path.sep
        outputdir = tempfile.mkdtemp (dir=tmpdir)
        
        argv = [ "ppdc",
                 "-d", outputdir,
                 "-I", "/usr/share/cups/ppdc",
                 self.drv ]

        signal.alarm (60)
        try:
            p = subprocess.Popen (argv,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)
        except OSError:
            # ppdc not available.
            os.rmdir (outputdir)
            return Driver.list (self)

        try:
            (stdout, stderr) = p.communicate ()
            signal.alarm (0)
        except TimedOut:
            posix.kill (p.pid, signal.SIGKILL)
            raise

        os.path.walk (outputdir, self.examine_directory, None)
        os.rmdir (outputdir)
        return Driver.list (self)

    def examine_directory (self, unused, dirname, fnames):
        for fname in fnames:
            path = dirname + os.path.sep + fname
            self.examine_file (path)
            os.unlink (path)

class TagBuilder:
    def __init__ (self, filelist=None):
        if filelist == None:
            filelist = sys.stdin

        paths = map (lambda x: x.rstrip (), filelist.readlines ())
        self.ids = DeviceIDs ()

        for path in paths:
            if path.find ("/usr/lib/cups/driver/") != -1:
                try:
                    self.ids += DynamicDriver (path).list ()
                except TimedOut:
                    pass
                except OSError, (e, s):
                    if e == errno.EACCES or e == errno.ENOENT:
                        # Not executable
                        pass
                    else:
                        raise

        if CAN_EXAMINE_PPDS:
            for path in paths:
                try:
                    self.ids += PPDDriver (path).list ()
                except TimedOut:
                    pass

    def get_tags (self):
        return self.ids.get_tags ()

if __name__ == "__main__":
    builder = TagBuilder ()
    tags = builder.get_tags ()
    for tag in tags:
        print tag