#!/usr/bin/env python ## system-config-printer ## Copyright (C) 2006, 2007, 2008 Red Hat, Inc. ## Copyright (C) 2006 Florian Festi ## Copyright (C) 2006, 2007, 2008 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 cups from cupshelpers import parseDeviceID import string import locale import os.path import re global debugging debugging = False def debugprint (s): if debugging: print s def ppdMakeModelSplit (ppd_make_and_model): """Convert the ppd-make-and-model field into a (make, model) pair.""" # If the string starts with a known model name (like "LaserJet") assume # that the manufacturer name is missing and add the manufacturer name # corresponding to the model name if re.search ("^(deskjet|laserjet|designjet|officejet|photosmart)", \ ppd_make_and_model, re.I): make = "HP" model = ppd_make_and_model elif re.search ("^(stylus|aculaser)", \ ppd_make_and_model, re.I): make = "Epson" model = ppd_make_and_model elif re.search ("^(stylewriter|imagewriter|deskwriter|laserwriter)", \ ppd_make_and_model, re.I): make = "Apple" model = ppd_make_and_model elif re.search ("^(pixus|pixma|selphy|imagerunner|\bbjc\b|\bbj\b|\blbp\b)",\ ppd_make_and_model, re.I): make = "Canon" model = ppd_make_and_model elif re.search ("^(\bhl\b|\bdcp\b|\bmfc\b)", \ ppd_make_and_model, re.I): make = "Brother" model = ppd_make_and_model elif re.search ("^(docuprint|docupage|phaser|workcentre|homecentre)", \ ppd_make_and_model, re.I): make = "Xerox" model = ppd_make_and_model elif re.search ("^(optra|(color\s*|)jetprinter)", \ ppd_make_and_model, re.I): make = "Lexmark" model = ppd_make_and_model elif re.search ("^(magicolor|pageworks|pagepro)", \ ppd_make_and_model, re.I): make = "KONICA MINOLTA" model = ppd_make_and_model elif re.search ("^(aficio)", \ ppd_make_and_model, re.I): make = "Ricoh" model = ppd_make_and_model elif re.search ("^(varioprint)", \ ppd_make_and_model, re.I): make = "Oce" model = ppd_make_and_model elif re.search ("^(okipage|microline)", \ ppd_make_and_model, re.I): make = "Okidata" model = ppd_make_and_model elif re.search ("^(konica[\s_-]*minolta)", \ ppd_make_and_model, re.I): make = "KONICA MINOLTA" model = ppd_make_and_model model = re.sub ("(?i)KONICA[\s_-]*MINOLTA\s*", "", model, 1) else: try: make, model = ppd_make_and_model.split(" ", 1) except: make = ppd_make_and_model model = '' def strip_suffix (model, suffix): if model.endswith (suffix): return model[:-len(suffix)] return model # Model names do not contain a comma, truncate all from the # comma on c = model.find (",") if c != -1: model = model[:c] # HP PPDs give NickNames like: # *NickName: "HP LaserJet 4 Plus v2013.111 Postscript (recommended)" # Find the version number. v = model.find (" v") if v != -1 and (model[v + 2].isdigit () or (model[v + 2] == '.' and model[v + 3].isdigit ())): # Truncate at that point. model = model[:v] f = model.find (" Foomatic/") if f != -1: model = model[:f] # Gutenprint PPDs have NickNames that end: # ... - CUPS+Gutenprint v5.0.0 gutenprint = model.find (" - CUPS+Gutenprint") if gutenprint != -1: model = model[:gutenprint] # Gimp-Print PPDs have NickNames that end: # ... - CUPS+Gimp-Print v4.2.7 gimpprint = model.find (" - CUPS+Gimp-Print") if gimpprint != -1: model = model[:gimpprint] wth = model.find (" w/") if wth != -1: model = model[:wth] make = re.sub ("(?i)KONICA[\s_-]*MINOLTA", "KONICA MINOLTA", make, 1) model = re.sub ("(?i)\s*\(recommended\)", "", model) model = re.sub ("(?i)\s*-\s*PostScript", "", model) model = re.sub ("(?i)\s*Postscript", "", model) model = re.sub ("(?i)\s*series", "", model) model = re.sub ("(?i)\s*PS", "", model) model = re.sub ("(?i)\s*PXL", "", model) model = re.sub ("(?i)[\s_-]*BT", "", model) model = re.sub ("(?i)\s*\(Bluetooth\)", "", model) for mfr in [ "Apple", "Canon", "Epson", "Lexmark", "Okidata" ]: if make == mfr.upper (): make = mfr model = model.strip () return (make, model) # Some drivers are just generally better than others. # Here is the preference list: DRIVER_TYPE_FOOMATIC_RECOMMENDED_NON_POSTSCRIPT = 8 DRIVER_TYPE_VENDOR = 10 DRIVER_TYPE_FOOMATIC_RECOMMENDED_POSTSCRIPT = 15 DRIVER_TYPE_FOOMATIC_HPIJS_ON_HP = 17 DRIVER_TYPE_GUTENPRINT_NATIVE_SIMPLIFIED = 20 DRIVER_TYPE_GUTENPRINT_NATIVE = 25 DRIVER_TYPE_SPLIX = 27 DRIVER_TYPE_FOOMATIC_PS = 30 DRIVER_TYPE_FOOMATIC_HPIJS = 40 DRIVER_TYPE_FOOMATIC_GUTENPRINT_SIMPLIFIED = 50 DRIVER_TYPE_FOOMATIC_GUTENPRINT = 60 DRIVER_TYPE_FOOMATIC = 70 DRIVER_TYPE_CUPS = 80 DRIVER_TYPE_FOOMATIC_GENERIC = 90 DRIVER_DOES_NOT_WORK = 999 def getDriverType (ppdname, ppds=None): """Decides which of the above types ppdname is.""" if ppdname.startswith ("gutenprint"): if ppdname.find ("/simple/") != -1: return DRIVER_TYPE_GUTENPRINT_NATIVE_SIMPLIFIED else: return DRIVER_TYPE_GUTENPRINT_NATIVE if ppdname.find ("SpliX")!= -1: return DRIVER_TYPE_SPLIX if (ppdname.find (":") == -1 and ppdname.find ("/cups-included/") != -1): return DRIVER_TYPE_CUPS if ppdname.startswith ("foomatic:"): # Foomatic (generated) -- but which driver? if ppdname.find ("Generic")!= -1: return DRIVER_TYPE_FOOMATIC_GENERIC if (ppds != None and ppds.getInfoFromPPDName (ppdname).\ get ('ppd-make-and-model', '').find ("(recommended)") != -1): if ppds.getInfoFromPPDName (ppdname).\ get ('ppd-make-and-model', '').find ("Postscript") != -1: return DRIVER_TYPE_FOOMATIC_RECOMMENDED_POSTSCRIPT else: return DRIVER_TYPE_FOOMATIC_RECOMMENDED_NON_POSTSCRIPT if ppdname.find ("-Postscript")!= -1: return DRIVER_TYPE_FOOMATIC_PS if ppdname.find ("-hpijs") != -1: if ppdname.find ("hpijs-rss") == -1: return DRIVER_TYPE_FOOMATIC_HPIJS if ppdname.find ("-gutenprint") != -1: if ppdname.find ("-simplified")!= -1: return DRIVER_TYPE_FOOMATIC_GUTENPRINT_SIMPLIFIED return DRIVER_TYPE_FOOMATIC_GUTENPRINT return DRIVER_TYPE_FOOMATIC if ppdname.find ("-hpijs") != -1: if ppdname.find ("hpijs-rss") == -1: return DRIVER_TYPE_FOOMATIC_HPIJS # Anything else should be a vendor's PPD. return DRIVER_TYPE_VENDOR # vendor's own class PPDs: # Status of match. STATUS_SUCCESS = 0 STATUS_MODEL_MISMATCH = 1 STATUS_GENERIC_DRIVER = 2 STATUS_NO_DRIVER = 3 def __init__ (self, ppds, language=None): """Takes a dict of PPDs, as returned by cups.Connection.getPPDs().""" self.ppds = ppds.copy () self.makes = None self.ids = None if (language == None or language == "C" or language == "POSIX"): language = "en_US" u = language.find ("_") if u != -1: short_language = language[:u] else: short_language = language to_remove = [] for ppdname, ppddict in self.ppds.iteritems (): try: natural_language = ppddict['ppd-natural-language'] except KeyError: continue if natural_language == "en": # Some manufacturer's PPDs are only available in this # language, so always let them though. continue if natural_language == language: continue if natural_language == short_language: continue to_remove.append (ppdname) for ppdname in to_remove: del self.ppds[ppdname] # CUPS sets the 'raw' model's ppd-make-and-model to 'Raw Queue' # which unfortunately then appears as manufacturer Raw and # model Queue. Use 'Generic' for this model. if self.ppds.has_key ('raw'): makemodel = self.ppds['raw']['ppd-make-and-model'] if not makemodel.startswith ("Generic "): self.ppds['raw']['ppd-make-and-model'] = "Generic " + makemodel def getMakes (self): """Returns a sorted list of strings.""" self._init_makes () makes_list = self.makes.keys () makes_list.sort (locale.strcoll) try: # "Generic" should be listed first. makes_list.remove ("Generic") makes_list.insert (0, "Generic") except ValueError: pass return makes_list def getModels (self, make): """Returns a sorted list of strings.""" self._init_makes () try: models_list = self.makes[make].keys () except KeyError: return [] models_list.sort (cups.modelSort) return models_list def getInfoFromModel (self, make, model): """Returns a dict of ppd-name:ppd-dict.""" self._init_makes () try: return self.makes[make][model] except KeyError: return {} def getInfoFromPPDName (self, ppdname): """Returns a ppd-dict.""" return self.ppds[ppdname] def orderPPDNamesByPreference (self, ppdnamelist=[]): """Returns a sorted list of ppd-names.""" if len (ppdnamelist) < 1: return ppdnamelist dict = self.getInfoFromPPDName (ppdnamelist[0]) make_model = dict['ppd-make-and-model'] mfg, mdl = ppdMakeModelSplit (make_model) def getDriverTypeWithBias (x, mfg): t = getDriverType (x, ppds=self) if mfg == "HP": if t == DRIVER_TYPE_FOOMATIC_HPIJS: # Prefer HPIJS for HP devices. t = DRIVER_TYPE_FOOMATIC_HPIJS_ON_HP return t def sort_ppdnames (a, b): ta = getDriverTypeWithBias (a, mfg) tb = getDriverTypeWithBias (b, mfg) if ta != tb: if tb < ta: return 1 else: return -1 # Prefer C locale localized PPDs to other languages, # just because we don't know the user's locale. def is_C_locale (x): while x: i = x.find ("C") if i == -1: return False lword = False if i == 0: lword = True elif x[i - 1] not in string.letters: lword = True if lword: rword = False if i == (len (x) - 1): rword = True elif x[i + 1] not in string.letters: rword = True if rword: return True x = x[i + 1:] ca = is_C_locale (a) cb = is_C_locale (b) if ca != cb: # If they compare equal stringwise up to "C", sort. if ca: l = a.find ("C") else: l = b.find ("C") if a[:l] == b[:l]: if cb: return 1 else: return -1 # String-wise compare. if a > b: return 1 elif a < b: return -1 return 0 ppdnamelist.sort (sort_ppdnames) return ppdnamelist def getPPDNameFromDeviceID (self, mfg, mdl, description="", commandsets=[], uri=None): """Returns a (status,ppd-name) integer,string pair.""" debugprint ("\n%s %s" % (mfg, mdl)) self._init_ids () id_matched = False try: ppdnamelist = self.ids[mfg.lower ()][mdl.lower ()] status = self.STATUS_SUCCESS id_matched = True except KeyError: if uri and (uri.startswith ("hp:") or uri.startswith ("hpfax:")): # The HPLIP backends make up incorrect IDs. if mfg == "HP": try: ppdnamelist = self.ids['hewlett-packard'][mdl.lower ()] status = self.STATUS_SUCCESS id_matched = True except KeyError: pass if not id_matched: ppdnamelist = None debugprint ("Trying make/model names") mfgl = mfg.lower () mdls = None self._init_makes () for attempt in range (2): for (make, models) in self.makes.iteritems (): if make.lower () == mfgl: mdls = models break # Try again with replacements. if mfgl == "hewlett-packard": mfgl = "hp" # Remove manufacturer name from model field ppdnamelist2 = None if mdl.startswith (mfg + ' '): mdl = mdl[len (mfg) + 1:] if mdl.startswith ('Hewlett-Packard '): mdl = mdl[16:] if mdl.startswith ('HP '): mdl = mdl[3:] if mdls and mdls.has_key (mdl): ppdnamelist2 = mdls[mdl].keys () status = self.STATUS_SUCCESS else: # Make use of the model name clean-up in the ppdMakeModelSplit () # function (mfg2, mdl2) = ppdMakeModelSplit (mfg + " " + mdl) if mdls and mdls.has_key (mdl2): ppdnamelist2 = mdls[mdl2].keys () status = self.STATUS_SUCCESS if ppdnamelist: if ppdnamelist2: ppdnamelist = ppdnamelist + ppdnamelist2 elif ppdnamelist2: ppdnamelist = ppdnamelist2 if not ppdnamelist and mdls: (s, ppds) = self._findBestMatchPPDs (mdls, mdl) if s != self.STATUS_NO_DRIVER: status = s ppdnamelist = ppds if not ppdnamelist and commandsets: if type (commandsets) != list: commandsets = commandsets.split (',') generic = self._getPPDNameFromCommandSet (commandsets) if generic: status = self.STATUS_GENERIC_DRIVER ppdnamelist = generic if not ppdnamelist: debugprint ("Text-only fallback") status = self.STATUS_NO_DRIVER ppdnamelist = ["textonly.ppd"] tppdfound = 0 for ppdpath in self.ppds.keys (): if ppdpath.endswith (ppdnamelist[0]): tppdfound = 1 ppdnamelist = [ppdpath] break if tppdfound == 0: debugprint ("No text-only driver?! Using postscript.ppd") ppdnamelist = ["postscript.ppd"] psppdfound = 0 for ppdpath in self.ppds.keys (): if ppdpath.endswith (ppdnamelist[0]): psppdfound = 1 ppdnamelist = [ppdpath] break if psppdfound == 0: debugprint ("No postscript.ppd; choosing any") ppdnamelist = [self.ppds.keys ()[0]] if id_matched: debugprint ("Checking DES field") inexact = set() if description: for ppdname in ppdnamelist: ppddict = self.ppds[ppdname] id = ppddict['ppd-device-id'] if not id: continue # Fetch description field. id_dict = parseDeviceID (id) if id_dict["DES"] != description: inexact.add (ppdname) exact = set (ppdnamelist).difference (inexact) debugprint ("discarding: %s" % inexact) if len (exact) >= 1: ppdnamelist = list (exact) # We've got a set of PPDs, any of which will drive the device. # Now we have to choose the "best" one. This is quite tricky # to decide, so let's sort them in order of preference and # take the first. ppdnamelist = self.orderPPDNamesByPreference (ppdnamelist) debugprint (str (ppdnamelist)) if not id_matched: print "No ID match for device %s:" % uri print " %s" % mfg print " %s" % mdl print " %s" % description try: cmd = reduce (lambda x, y: x + ","+ y, commandsets) except TypeError: cmd = "" print " %s" % cmd print "Using %s" % ppdnamelist[0] return (status, ppdnamelist[0]) def _findBestMatchPPDs (self, mdls, mdl): """ Find the best-matching PPDs based on the MDL Device ID. This function could be made a lot smarter. """ debugprint ("Trying best match") mdll = mdl.lower () if mdll.endswith (" series"): # Strip " series" from the end of the MDL field. mdll = mdll[:-7] mdl = mdl[:-7] best_mdl = None best_matchlen = 0 mdlnames = mdls.keys () # Perform a case-insensitive model sort on the names. mdlnamesl = map (lambda x: (x, x.lower()), mdlnames) mdlnamesl.append ((mdl, mdll)) mdlnamesl.sort (lambda x, y: cups.modelSort(x[1], y[1])) i = mdlnamesl.index ((mdl, mdll)) candidates = [mdlnamesl[i - 1]] if i + 1 < len (mdlnamesl): candidates.append (mdlnamesl[i + 1]) debugprint (candidates[0][0] + " <= " + mdl + " <= " + candidates[1][0]) else: debugprint (candidates[0][0] + " <= " + mdl) # Look at the models immediately before and after ours in the # sorted list, and pick the one with the longest initial match. for (candidate, candidatel) in candidates: prefix = os.path.commonprefix ([candidatel, mdll]) if len (prefix) > best_matchlen: best_mdl = mdls[candidate].keys () best_matchlen = len (prefix) debugprint ("%s: match length %d" % (candidate, best_matchlen)) # Did we match more than half of the model name? if best_mdl and best_matchlen > (len (mdll) / 2): ppdnamelist = best_mdl if best_matchlen == len (mdll): status = self.STATUS_SUCCESS else: status = self.STATUS_MODEL_MISMATCH else: status = self.STATUS_NO_DRIVER ppdnamelist = None # Last resort. Find the "most important" word in the MDL # field and look for a match based solely on that. If # there are digits, try lowering the number of # significant figures. mdlnames.sort (cups.modelSort) mdlitems = map (lambda x: (x.lower (), mdls[x]), mdlnames) modelid = None for word in mdll.split (' '): if modelid == None: modelid = word have_digits = False for i in range (len (word)): if word[i].isdigit (): have_digits = True break if have_digits: modelid = word break digits = 0 digits_start = -1 digits_end = -1 for i in range (len (modelid)): if modelid[i].isdigit (): if digits_start == -1: digits_start = i digits_end = i digits += 1 digits_end += 1 modelnumber = 0 if digits > 0: modelnumber = int (modelid[digits_start:digits_end]) modelpattern = (modelid[:digits_start] + "%d" + modelid[digits_end:]) debugprint ("Searching for model ID '%s', '%s' %% %d" % (modelid, modelpattern, modelnumber)) ignore_digits = 0 best_mdl = None found = False while ignore_digits < digits: div = pow (10, ignore_digits) modelid = modelpattern % ((modelnumber / div) * div) debugprint ("Ignoring %d of %d digits, trying %s" % (ignore_digits, digits, modelid)) for (name, ppds) in mdlitems: for word in name.split (' '): if word.lower () == modelid: found = True break if found: best_mdl = ppds.keys () break if found: break ignore_digits += 1 if digits < 2: break if found: ppdnamelist = best_mdl status = self.STATUS_MODEL_MISMATCH return (status, ppdnamelist) def _getPPDNameFromCommandSet (self, commandsets=[]): """Return ppd-name list or None, given a list of strings representing the command sets supported.""" try: self._init_makes () models = self.makes["Generic"] except KeyError: return None def get (*candidates): for model in candidates: (s, ppds) = self._findBestMatchPPDs (models, model) if s == self.STATUS_SUCCESS: return ppds return None cmdsets = map (lambda x: x.lower (), commandsets) if (("postscript" in cmdsets) or ("postscript2" in cmdsets) or ("postscript level 2 emulation" in cmdsets)): return get ("PostScript Printer") elif (("pclxl" in cmdsets) or ("pcl-xl" in cmdsets) or ("pcl6" in cmdsets) or ("pcl 6 emulation" in cmdsets)): return get ("PCL 6/PCL XL Printer") elif "pcl5e" in cmdsets: return get ("PCL 5e Printer") elif "pcl5c" in cmdsets: return get ("PCL 5c Printer") elif ("pcl5" in cmdsets) or ("pcl 5 emulation" in cmdsets): return get ("PCL 5 Printer") elif "pcl" in cmdsets: return get ("PCL 3 Printer") elif (("escpl2" in cmdsets) or ("esc/p2" in cmdsets) or ("escp2e" in cmdsets)): return get ("ESC/P Dot Matrix Printer") return None def _init_makes (self): if self.makes: return makes = {} lmakes = {} lmodels = {} for ppdname, ppddict in self.ppds.iteritems (): ppd_make_and_model = ppddict['ppd-make-and-model'] (make, model) = ppdMakeModelSplit (ppd_make_and_model) lmake = make.lower () lmodel = model.lower () if not lmakes.has_key (lmake): lmakes[lmake] = make lmodels[lmake] = {} makes[make] = {} else: make = lmakes[lmake] if not lmodels[lmake].has_key (lmodel): lmodels[lmake][lmodel] = model makes[make][model] = {} else: model = lmodels[lmake][lmodel] makes[make][model][ppdname] = ppddict self.makes = makes self.lmakes = lmakes self.lmodels = lmodels def _init_ids (self): if self.ids: return ids = {} for ppdname, ppddict in self.ppds.iteritems (): if not ppddict.has_key ('ppd-device-id'): continue id = ppddict['ppd-device-id'] if not id: continue # Fix up broken Kyocera IDs v = id.find (":Model") if v != -1: id = id[:v] + ';' + id[v + 1:] id_dict = parseDeviceID (id) lmfg = id_dict['MFG'].lower () lmdl = id_dict['MDL'].lower () bad = False if len (lmfg) == 0: debugprint ("Missing MFG field for %s" % ppdname) bad = True if len (lmdl) == 0: debugprint ("Missing MDL field for %s" % ppdname) bad = True if bad: continue if not ids.has_key (lmfg): ids[lmfg] = {} if not ids[lmfg].has_key (lmdl): ids[lmfg][lmdl] = [] ids[lmfg][lmdl].append (ppdname) self.ids = ids def show_help(): print "usage: ppds.py [--deviceid] [--list-models] [--list-ids] [--debug]" def main(): import sys, getopt try: opts, args = getopt.gnu_getopt (sys.argv[1:], '', ['help', 'deviceid', 'list-models', 'list-ids', 'debug']) except getopt.GetoptError: show_help() sys.exit (1) stdin_deviceid = False list_models = False list_ids = False for opt, optarg in opts: if opt == "--help": show_help () sys.exit (0) if opt == "--deviceid": stdin_deviceid = True elif opt == "--list-models": list_models = True elif opt == "--list-ids": list_ids = True elif opt == "--debug": debugging = True picklefile="pickled-ppds" import pickle try: f = open (picklefile, "r") cupsppds = pickle.load (f) except IOError: f = open (picklefile, "w") c = cups.Connection () cupsppds = c.getPPDs () pickle.dump (cupsppds, f) ppds = PPDs (cupsppds) makes = ppds.getMakes () models_count = 0 for make in makes: models = ppds.getModels (make) models_count += len (models) if list_models: print make for model in models: print " " + model print "%d makes, %d models" % (len (makes), models_count) ppds.getPPDNameFromDeviceID ("HP", "PSC 2200 Series") makes = ppds.ids.keys () models_count = 0 for make in makes: models = ppds.ids[make] models_count += len (models) if list_ids: print make for model in models: print " %s (%d)" % (model, len (ppds.ids[make][model])) for driver in ppds.ids[make][model]: print " " + driver print "%d ID makes, %d ID models" % (len (makes), models_count) print "\nID matching tests\n" idlist = [ "MFG:EPSON;CMD:ESCPL2,BDC,D4,D4PX;MDL:Stylus D78;CLS:PRINTER;DES:EPSON Stylus D78;", "MFG:Hewlett-Packard;MDL:LaserJet 1200 Series;CMD:MLC,PCL,POSTSCRIPT;CLS:PRINTER;", "MFG:Hewlett-Packard;MDL:LaserJet 3390 Series;CMD:MLC,PCL,POSTSCRIPT;CLS:PRINTER;", "MFG:Hewlett-Packard;MDL:PSC 2200 Series;CMD:MLC,PCL,PML,DW-PCL,DYN;CLS:PRINTER;1284.4DL:4d,4e,1;", "MFG:HP;MDL:PSC 2200 Series;CLS:PRINTER;DES:PSC 2200 Series;", # from HPLIP "MFG:HEWLETT-PACKARD;MDL:DESKJET 990C;CMD:MLC,PCL,PML;CLS:PRINTER;DES:Hewlett-Packard DeskJet 990C;", "CLASS:PRINTER;MODEL:HP LaserJet 6MP;MANUFACTURER:Hewlett-Packard;DESCRIPTION:Hewlett-Packard LaserJet 6MP Printer;COMMAND SET:PJL,MLC,PCLXL,PCL,POSTSCRIPT;", "MFG:Canon;CMD:BJL,BJRaster3,BSCCe;SOJ:TXT01;MDL:iP3000;CLS:PRINTER;DES:Canon iP3000;VER:1.09;STA:10;FSI:03;", "MFG:HP;MDL:Deskjet 5400 series;CMD:MLC,PCL,PML,DW-PCL,DESKJET,DYN;1284.4DL:4d,4e,1;CLS:PRINTER;DES:5440;", "MFG:New;MDL:Unknown PS Printer;CMD:POSTSCRIPT;", "MFG:New;MDL:Unknown PCL6 Printer;CMD:PCLXL;", "MFG:New;MDL:Unknown PCL5e Printer;CMD:PCL5e;", "MFG:New;MDL:Unknown PCL5c Printer;CMD:PCL5c;", "MFG:New;MDL:Unknown PCL5 Printer;CMD:PCL5;", "MFG:New;MDL:Unknown PCL3 Printer;CMD:PCL;", "MFG:New;MDL:Unknown ESC/P Printer;CMD:ESCP2E;", "MFG:New;MDL:Unknown Printer;", ] if stdin_deviceid: idlist = [raw_input ('Device ID: ')] for id in idlist: id_dict = parseDeviceID (id) print id_dict["MFG"], id_dict["MDL"] ppdname = ppds.getPPDNameFromDeviceID (id_dict["MFG"], id_dict["MDL"], id_dict["DES"], id_dict["CMD"]) print " ", ppdname if __name__ == "__main__": main()