diff --git a/postscriptdriver.prov b/postscriptdriver.prov new file mode 100755 index 0000000..1e6ccad --- /dev/null +++ b/postscriptdriver.prov @@ -0,0 +1,259 @@ +#!/usr/bin/python + +## Copyright (C) 2009, 2010 Red Hat, Inc. +## Author: Tim Waugh + +## 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 diff --git a/psdriver.attr b/psdriver.attr new file mode 100644 index 0000000..66e4037 --- /dev/null +++ b/psdriver.attr @@ -0,0 +1,3 @@ +%__psdriver_provides %{_rpmconfigdir}/postscriptdriver.prov +%__psdriver_path ^(/usr/lib/cups/driver/.*|%{_datadir}/cups/drv/.*\.drv)$ +%__psdriver_magic ^PPD File.*$ diff --git a/python-cups.spec b/python-cups.spec index 30ca6db..67df92d 100644 --- a/python-cups.spec +++ b/python-cups.spec @@ -6,15 +6,19 @@ Summary: Python bindings for CUPS Name: python-cups Version: 1.9.53 -Release: 1%{?dist} +Release: 2%{?dist} URL: http://cyberelk.net/tim/software/pycups/ Source: http://cyberelk.net/tim/data/pycups/pycups-%{version}.tar.bz2 +Source2: psdriver.attr +Source3: postscriptdriver.prov License: GPLv2+ Group: Development/Languages BuildRequires: cups-devel BuildRequires: python2-devel BuildRequires: epydoc +Conflicts: rpm-build < 4.9.0 + %description This package provides Python bindings for the CUPS API, known as pycups. It was written for use with @@ -40,17 +44,27 @@ epydoc -o html --html build/lib*/cups.so %{__python} setup.py install -O1 --skip-build --root %{buildroot} chmod 755 %{buildroot}%{python_sitearch}/cups.so +mkdir -p %{buildroot}%{_rpmconfigdir}/fileattrs +install -m644 %{SOURCE2} %{buildroot}%{_rpmconfigdir}/fileattrs/ +install -m755 %{SOURCE3} %{buildroot}%{_rpmconfigdir} + %files %defattr(-,root,root,-) %doc COPYING ChangeLog README NEWS TODO %{python_sitearch}/cups.so %{python_sitearch}/pycups*.egg-info +/usr/lib/rpm/fileattrs/psdriver.attr +%{_rpmconfigdir}/postscriptdriver.prov %files doc %defattr(-,root,root,-) %doc examples html %changelog +* Thu Jan 20 2011 Tim Waugh - 1.9.53-2 +- Moved postscriptdriver RPM tagging machinery here. Fixed + leading/trailing whitespace in tags as well. + * Wed Dec 15 2010 Tim Waugh - 1.9.53-1 - 1.9.53 fixing a thread-local storage issue (bug #662805).