home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / lib / hplip / base / utils.pyc (.txt) < prev   
Encoding:
Python Compiled Bytecode  |  2007-04-29  |  41.1 KB  |  1,426 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.5)
  3.  
  4. from __future__ import generators
  5. import sys
  6. import os
  7. import fnmatch
  8. import tempfile
  9. import socket
  10. import struct
  11. import select
  12. import time
  13. import fcntl
  14. import errno
  15. import stat
  16. import string
  17. import commands
  18. import cStringIO
  19. import re
  20. import xml.parsers.expat as expat
  21. import getpass
  22. from g import *
  23. from codes import *
  24. import pexpect
  25. xml_basename_pat = re.compile('HPLIP-(\\d*)_(\\d*)_(\\d*).xml', re.IGNORECASE)
  26.  
  27. def Translator(frm = '', to = '', delete = '', keep = None):
  28.     allchars = string.maketrans('', '')
  29.     if len(to) == 1:
  30.         to = to * len(frm)
  31.     
  32.     trans = string.maketrans(frm, to)
  33.     if keep is not None:
  34.         delete = allchars.translate(allchars, keep.translate(allchars, delete))
  35.     
  36.     
  37.     def callable(s):
  38.         return s.translate(trans, delete)
  39.  
  40.     return callable
  41.  
  42. prv_pidfile = None
  43. prv_pidfile_name = ''
  44.  
  45. def get_pidfile_lock(a_pidfile_name = ''):
  46.     ''' Call this to either lock the pidfile, or to update it after a fork()
  47.         Credit: Henrique M. Holschuh <hmh@debian.org>
  48.     '''
  49.     global prv_pidfile_name, prv_pidfile
  50.     if prv_pidfile_name == '':
  51.         
  52.         try:
  53.             prv_pidfile_name = a_pidfile_name
  54.             prv_pidfile = os.fdopen(os.open(prv_pidfile_name, os.O_RDWR | os.O_CREAT, 420), 'r+')
  55.             fcntl.fcntl(prv_pidfile.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
  56.             while None:
  57.                 
  58.                 try:
  59.                     fcntl.flock(prv_pidfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
  60.                 except (OSError, IOError):
  61.                     e = None
  62.                     if e.errno == errno.EINTR:
  63.                         continue
  64.                     elif e.errno == errno.EWOULDBLOCK:
  65.                         
  66.                         try:
  67.                             prv_pidfile.seek(0)
  68.                             otherpid = int(prv_pidfile.readline(), 10)
  69.                             sys.stderr.write("can't lock %s, running daemon's pid may be %d\n" % (prv_pidfile_name, otherpid))
  70.                         except (OSError, IOError):
  71.                             e = None
  72.                             sys.stderr.write('error reading pidfile %s: (%d) %s\n' % (prv_pidfile_name, e.errno, e.strerror))
  73.  
  74.                         sys.exit(1)
  75.                     
  76.                     sys.stderr.write("can't lock %s: (%d) %s\n" % (prv_pidfile_name, e.errno, e.strerror))
  77.                     sys.exit(1)
  78.  
  79.                 break
  80.         except (OSError, IOError):
  81.             e = None
  82.             sys.stderr.write("can't open pidfile %s: (%d) %s\n" % (prv_pidfile_name, e.errno, e.strerror))
  83.             sys.exit(1)
  84.         except:
  85.             None<EXCEPTION MATCH>(OSError, IOError)
  86.         
  87.  
  88.     None<EXCEPTION MATCH>(OSError, IOError)
  89.     
  90.     try:
  91.         prv_pidfile.seek(0)
  92.         prv_pidfile.write('%d\n' % os.getpid())
  93.         prv_pidfile.flush()
  94.         prv_pidfile.truncate()
  95.     except (OSError, IOError):
  96.         e = None
  97.         log.error("can't update pidfile %s: (%d) %s\n" % (prv_pidfile_name, e.errno, e.strerror))
  98.  
  99.  
  100.  
  101. def daemonize(stdin = '/dev/null', stdout = '/dev/null', stderr = '/dev/null'):
  102.     '''
  103.     Credit: J\xc3\xbcrgen Hermann, Andy Gimblett, and Noah Spurrier
  104.             http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66012
  105.  
  106.     Proper pidfile support: Henrique M. Holschuh <hmh@debian.org>
  107.     '''
  108.     if prv_pidfile_name != '' or prv_pidfile_name != '':
  109.         get_pidfile_lock(prv_pidfile_name)
  110.     
  111.     
  112.     try:
  113.         pid = os.fork()
  114.         if pid > 0:
  115.             sys.exit(0)
  116.     except OSError:
  117.         e = None
  118.         sys.stderr.write('fork #1 failed: (%d) %s\n' % (e.errno, e.strerror))
  119.         sys.exit(1)
  120.  
  121.     os.chdir('/')
  122.     os.umask(0)
  123.     os.setsid()
  124.     
  125.     try:
  126.         pid = os.fork()
  127.         if pid > 0:
  128.             sys.exit(0)
  129.     except OSError:
  130.         e = None
  131.         sys.stderr.write('fork #2 failed: (%d) %s\n' % (e.errno, e.strerror))
  132.         sys.exit(1)
  133.  
  134.     if prv_pidfile_name != '':
  135.         get_pidfile_lock()
  136.     
  137.     si = file(stdin, 'r')
  138.     so = file(stdout, 'a+')
  139.     se = file(stderr, 'a+', 0)
  140.     os.dup2(si.fileno(), sys.stdin.fileno())
  141.     os.dup2(so.fileno(), sys.stdout.fileno())
  142.     os.dup2(se.fileno(), sys.stderr.fileno())
  143.  
  144.  
  145. def ifelse(cond, t, f):
  146.     if cond:
  147.         return t
  148.     else:
  149.         return f
  150.  
  151.  
  152. def to_bool_str(s, default = '0'):
  153.     ''' Convert an arbitrary 0/1/T/F/Y/N string to a normalized string 0/1.'''
  154.     if isinstance(s, str) and s:
  155.         if s[0].lower() in ('1', 't', 'y'):
  156.             return '1'
  157.         elif s[0].lower() in ('0', 'f', 'n'):
  158.             return '0'
  159.         
  160.     
  161.     return default
  162.  
  163.  
  164. def to_bool(s, default = False):
  165.     ''' Convert an arbitrary 0/1/T/F/Y/N string to a boolean True/False value.'''
  166.     if isinstance(s, str) and s:
  167.         if s[0].lower() in ('1', 't', 'y'):
  168.             return True
  169.         elif s[0].lower() in ('0', 'f', 'n'):
  170.             return False
  171.         
  172.     elif isinstance(s, bool):
  173.         return s
  174.     
  175.     return default
  176.  
  177.  
  178. def path_exists_safely(path):
  179.     ''' Returns True if path exists, and points to a file with permissions at least as strict as 0755.
  180.         Credit: Contributed by Henrique M. Holschuh <hmh@debian.org>'''
  181.     
  182.     try:
  183.         pathmode = os.stat(path)[stat.ST_MODE]
  184.         if pathmode & 18 != 0:
  185.             return False
  186.     except (IOError, OSError):
  187.         return False
  188.  
  189.     return True
  190.  
  191.  
  192. def walkFiles(root, recurse = True, abs_paths = False, return_folders = False, pattern = '*', path = None):
  193.     if path is None:
  194.         path = root
  195.     
  196.     
  197.     try:
  198.         names = os.listdir(root)
  199.     except os.error:
  200.         raise StopIteration
  201.  
  202.     if not pattern:
  203.         pass
  204.     pattern = '*'
  205.     pat_list = pattern.split(';')
  206.     for name in names:
  207.         fullname = os.path.normpath(os.path.join(root, name))
  208.         for pat in pat_list:
  209.             if fnmatch.fnmatch(name, pat):
  210.                 if return_folders or not os.path.isdir(fullname):
  211.                     pass
  212.                 None if abs_paths else None<EXCEPTION MATCH>ValueError
  213.                 continue
  214.         
  215.         if os.path.islink(fullname):
  216.             fullname = os.path.realpath(os.readlink(fullname))
  217.         
  218.         if recurse or os.path.isdir(fullname) or os.path.islink(fullname):
  219.             for f in walkFiles(fullname, recurse, abs_paths, return_folders, pattern, path):
  220.                 yield f
  221.             
  222.     
  223.  
  224.  
  225. def is_path_writable(path):
  226.     if os.path.exists(path):
  227.         s = os.stat(path)
  228.         mode = s[stat.ST_MODE] & 511
  229.         if mode & 2:
  230.             return True
  231.         elif s[stat.ST_GID] == os.getgid() and mode & 16:
  232.             return True
  233.         elif s[stat.ST_UID] == os.getuid() and mode & 128:
  234.             return True
  235.         
  236.     
  237.     return False
  238.  
  239.  
  240. class TextFormatter:
  241.     LEFT = 0
  242.     CENTER = 1
  243.     RIGHT = 2
  244.     
  245.     def __init__(self, colspeclist):
  246.         self.columns = []
  247.         for colspec in colspeclist:
  248.             self.columns.append(Column(**colspec))
  249.         
  250.  
  251.     
  252.     def compose(self, textlist, add_newline = False):
  253.         numlines = 0
  254.         textlist = list(textlist)
  255.         if len(textlist) != len(self.columns):
  256.             log.error('Formatter: Number of text items does not match columns')
  257.             return None
  258.         
  259.         for text, column in map(None, textlist, self.columns):
  260.             column.wrap(text)
  261.             numlines = max(numlines, len(column.lines))
  262.         
  263.         complines = [
  264.             ''] * numlines
  265.         for ln in range(numlines):
  266.             for column in self.columns:
  267.                 complines[ln] = complines[ln] + column.getline(ln)
  268.             
  269.         
  270.         if add_newline:
  271.             return '\n'.join(complines) + '\n'
  272.         else:
  273.             return '\n'.join(complines)
  274.  
  275.     
  276.     def bold(text):
  277.         return ''.join([
  278.             '\x1b[1m',
  279.             text,
  280.             '\x1b[0m'])
  281.  
  282.     bold = staticmethod(bold)
  283.  
  284.  
  285. class Column:
  286.     
  287.     def __init__(self, width = 78, alignment = TextFormatter.LEFT, margin = 0):
  288.         self.width = width
  289.         self.alignment = alignment
  290.         self.margin = margin
  291.         self.lines = []
  292.  
  293.     
  294.     def align(self, line):
  295.         if self.alignment == TextFormatter.CENTER:
  296.             return line.center(self.width)
  297.         elif self.alignment == TextFormatter.RIGHT:
  298.             return line.rjust(self.width)
  299.         else:
  300.             return line.ljust(self.width)
  301.  
  302.     
  303.     def wrap(self, text):
  304.         self.lines = []
  305.         words = []
  306.         for word in text.split():
  307.             if word <= self.width:
  308.                 words.append(word)
  309.                 continue
  310.             for i in range(0, len(word), self.width):
  311.                 words.append(word[i:i + self.width])
  312.             
  313.         
  314.         if not len(words):
  315.             return None
  316.         
  317.         current = words.pop(0)
  318.         for word in words:
  319.             increment = 1 + len(word)
  320.             if len(current) + increment > self.width:
  321.                 self.lines.append(self.align(current))
  322.                 current = word
  323.                 continue
  324.             current = current + ' ' + word
  325.         
  326.         self.lines.append(self.align(current))
  327.  
  328.     
  329.     def getline(self, index):
  330.         if index < len(self.lines):
  331.             return ' ' * self.margin + self.lines[index]
  332.         else:
  333.             return ' ' * (self.margin + self.width)
  334.  
  335.  
  336.  
  337. class Stack:
  338.     
  339.     def __init__(self):
  340.         self.stack = []
  341.  
  342.     
  343.     def pop(self):
  344.         return self.stack.pop()
  345.  
  346.     
  347.     def push(self, value):
  348.         self.stack.append(value)
  349.  
  350.     
  351.     def as_list(self):
  352.         return self.stack
  353.  
  354.     
  355.     def clear(self):
  356.         self.stack = []
  357.  
  358.  
  359.  
  360. class RingBuffer:
  361.     
  362.     def __init__(self, size_max = 50):
  363.         self.max = size_max
  364.         self.data = []
  365.  
  366.     
  367.     def append(self, x):
  368.         '''append an element at the end of the buffer'''
  369.         self.data.append(x)
  370.         if len(self.data) == self.max:
  371.             self.cur = 0
  372.             self.__class__ = RingBufferFull
  373.         
  374.  
  375.     
  376.     def replace(self, x):
  377.         '''replace the last element instead off appending'''
  378.         self.data[-1] = x
  379.  
  380.     
  381.     def get(self):
  382.         ''' return a list of elements from the oldest to the newest'''
  383.         return self.data
  384.  
  385.  
  386.  
  387. class RingBufferFull:
  388.     
  389.     def __init__(self, n):
  390.         pass
  391.  
  392.     
  393.     def append(self, x):
  394.         self.data[self.cur] = x
  395.         self.cur = (self.cur + 1) % self.max
  396.  
  397.     
  398.     def replace(self, x):
  399.         self.cur = (self.cur - 1) % self.max
  400.         self.data[self.cur] = x
  401.         self.cur = (self.cur + 1) % self.max
  402.  
  403.     
  404.     def get(self):
  405.         return self.data[self.cur:] + self.data[:self.cur]
  406.  
  407.  
  408.  
  409. def sort_dict_by_value(d):
  410.     ''' Returns the keys of dictionary d sorted by their values '''
  411.     items = d.items()
  412.     backitems = [ [
  413.         v[1],
  414.         v[0]] for v in items ]
  415.     backitems.sort()
  416.     return [ backitems[i][1] for i in range(0, len(backitems)) ]
  417.  
  418. codes = { }
  419. codes['reset'] = '\x1b[0m'
  420. codes['bold'] = '\x1b[01m'
  421. codes['teal'] = '\x1b[36;06m'
  422. codes['turquoise'] = '\x1b[36;01m'
  423. codes['fuscia'] = '\x1b[35;01m'
  424. codes['purple'] = '\x1b[35;06m'
  425. codes['blue'] = '\x1b[34;01m'
  426. codes['darkblue'] = '\x1b[34;06m'
  427. codes['green'] = '\x1b[32;01m'
  428. codes['darkgreen'] = '\x1b[32;06m'
  429. codes['yellow'] = '\x1b[33;01m'
  430. codes['brown'] = '\x1b[33;06m'
  431. codes['red'] = '\x1b[31;01m'
  432. codes['darkred'] = '\x1b[31;06m'
  433.  
  434. def bold(text):
  435.     return codes['bold'] + text + codes['reset']
  436.  
  437.  
  438. def white(text):
  439.     return bold(text)
  440.  
  441.  
  442. def teal(text):
  443.     return codes['teal'] + text + codes['reset']
  444.  
  445.  
  446. def turquoise(text):
  447.     return codes['turquoise'] + text + codes['reset']
  448.  
  449.  
  450. def darkteal(text):
  451.     return turquoise(text)
  452.  
  453.  
  454. def fuscia(text):
  455.     return codes['fuscia'] + text + codes['reset']
  456.  
  457.  
  458. def purple(text):
  459.     return codes['purple'] + text + codes['reset']
  460.  
  461.  
  462. def blue(text):
  463.     return codes['blue'] + text + codes['reset']
  464.  
  465.  
  466. def darkblue(text):
  467.     return codes['darkblue'] + text + codes['reset']
  468.  
  469.  
  470. def green(text):
  471.     return codes['green'] + text + codes['reset']
  472.  
  473.  
  474. def darkgreen(text):
  475.     return codes['darkgreen'] + text + codes['reset']
  476.  
  477.  
  478. def yellow(text):
  479.     return codes['yellow'] + text + codes['reset']
  480.  
  481.  
  482. def brown(text):
  483.     return codes['brown'] + text + codes['reset']
  484.  
  485.  
  486. def darkyellow(text):
  487.     return brown(text)
  488.  
  489.  
  490. def red(text):
  491.     return codes['red'] + text + codes['reset']
  492.  
  493.  
  494. def darkred(text):
  495.     return codes['darkred'] + text + codes['reset']
  496.  
  497.  
  498. def commafy(val):
  499.     if not val < 0 or '-' + commafy(abs(val)):
  500.         if not val < 1000 or str(val):
  501.             pass
  502.     return '%s,%03d' % (commafy(val / 1000), val % 1000)
  503.  
  504.  
  505. def format_bytes(s, show_bytes = False):
  506.     if s < 1024:
  507.         return ''.join([
  508.             commafy(s),
  509.             ' B'])
  510.     elif s < s:
  511.         pass
  512.     elif s < 1048576:
  513.         if show_bytes:
  514.             return ''.join([
  515.                 str(round(s / 1024, 1)),
  516.                 ' KB (',
  517.                 commafy(s),
  518.                 ')'])
  519.         else:
  520.             return ''.join([
  521.                 str(round(s / 1024, 1)),
  522.                 ' KB'])
  523.     elif show_bytes:
  524.         return ''.join([
  525.             str(round(s / 1.04858e+06, 1)),
  526.             ' MB (',
  527.             commafy(s),
  528.             ')'])
  529.     else:
  530.         return ''.join([
  531.             str(round(s / 1.04858e+06, 1)),
  532.             ' MB'])
  533.  
  534.  
  535. try:
  536.     make_temp_file = tempfile.mkstemp
  537. except AttributeError:
  538.     
  539.     def make_temp_file(suffix = '', prefix = '', dir = '', text = False):
  540.         path = tempfile.mktemp(suffix)
  541.         fd = os.open(path, os.O_RDWR | os.O_CREAT | os.O_EXCL, 448)
  542.         return (os.fdopen(fd, 'w+b'), path)
  543.  
  544.  
  545.  
  546. def log_title(program_name, version):
  547.     log.info('')
  548.     log.info(bold('HP Linux Imaging and Printing System (ver. %s)' % prop.version))
  549.     log.info(bold('%s ver. %s' % (program_name, version)))
  550.     log.info('')
  551.     log.info('Copyright (c) 2001-7 Hewlett-Packard Development Company, LP')
  552.     log.info('This software comes with ABSOLUTELY NO WARRANTY.')
  553.     log.info('This is free software, and you are welcome to distribute it')
  554.     log.info('under certain conditions. See COPYING file for more details.')
  555.     log.info('')
  556.  
  557.  
  558. def which(command, return_full_path = False):
  559.     path = os.getenv('PATH').split(':')
  560.     path.append('/sbin')
  561.     path.append('/usr/sbin')
  562.     path.append('/usr/local/sbin')
  563.     found_path = ''
  564.     for p in path:
  565.         
  566.         try:
  567.             files = os.listdir(p)
  568.         except:
  569.             continue
  570.             continue
  571.  
  572.         if command in files:
  573.             found_path = p
  574.             break
  575.             continue
  576.     
  577.     if return_full_path:
  578.         if found_path:
  579.             return os.path.join(found_path, command)
  580.         else:
  581.             return ''
  582.     else:
  583.         return found_path
  584.  
  585.  
  586. def deviceDefaultFunctions():
  587.     (cmd_print, cmd_copy, cmd_fax, cmd_pcard, cmd_scan, cmd_fab) = ('', '', '', '', '', '')
  588.     path = which('hp-print')
  589.     if len(path) > 0:
  590.         cmd_print = 'hp-print -p%PRINTER%'
  591.     else:
  592.         path = which('kprinter')
  593.         if len(path) > 0:
  594.             cmd_print = 'kprinter -P%PRINTER% --system cups'
  595.         else:
  596.             path = which('gtklp')
  597.             if len(path) > 0:
  598.                 cmd_print = 'gtklp -P%PRINTER%'
  599.             else:
  600.                 path = which('xpp')
  601.                 if len(path) > 0:
  602.                     cmd_print = 'xpp -P%PRINTER%'
  603.                 
  604.     path = which('xsane')
  605.     if len(path) > 0:
  606.         cmd_scan = 'xsane -V %SANE_URI%'
  607.     else:
  608.         path = which('kooka')
  609.         if len(path) > 0:
  610.             cmd_scan = 'kooka'
  611.         else:
  612.             path = which('xscanimage')
  613.             if len(path) > 0:
  614.                 cmd_scan = 'xscanimage'
  615.             
  616.     path = which('hp-unload')
  617.     if len(path):
  618.         cmd_pcard = 'hp-unload -d %DEVICE_URI%'
  619.     else:
  620.         cmd_pcard = 'python %HOME%/unload.py -d %DEVICE_URI%'
  621.     path = which('hp-makecopies')
  622.     if len(path):
  623.         cmd_copy = 'hp-makecopies -d %DEVICE_URI%'
  624.     else:
  625.         cmd_copy = 'python %HOME%/makecopies.py -d %DEVICE_URI%'
  626.     path = which('hp-sendfax')
  627.     if len(path):
  628.         cmd_fax = 'hp-sendfax -d %FAX_URI%'
  629.     else:
  630.         cmd_fax = 'python %HOME%/sendfax.py -d %FAX_URI%'
  631.     path = which('hp-fab')
  632.     if len(path):
  633.         cmd_fab = 'hp-fab'
  634.     else:
  635.         cmd_fab = 'python %HOME%/fab.py'
  636.     return (cmd_print, cmd_scan, cmd_pcard, cmd_copy, cmd_fax, cmd_fab)
  637.  
  638.  
  639. def no_qt_message_gtk():
  640.     
  641.     try:
  642.         import gtk
  643.         w = gtk.Window()
  644.         dialog = gtk.MessageDialog(w, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, 'PyQt not installed. GUI not available. Install "python-qt3" with the Synaptic Package Manager (Menu: System -> Administration -> Synaptic Package Manager) or run the command "sudo apt-get install python-qt3" in a terminal window.')
  645.         dialog.run()
  646.         dialog.destroy()
  647.     except ImportError:
  648.         pass
  649.  
  650.  
  651.  
  652. def checkPyQtImport():
  653.     
  654.     try:
  655.         import qt
  656.     except ImportError:
  657.         if os.getenv('DISPLAY') and os.getenv('STARTED_FROM_MENU'):
  658.             no_qt_message_gtk()
  659.         
  660.         log.error('PyQt not installed. GUI not available. Exiting.')
  661.         return False
  662.  
  663.     qtMajor = int(qt.qVersion().split('.')[0])
  664.     if qtMajor < MINIMUM_QT_MAJOR_VER:
  665.         log.error('Incorrect version of Qt installed. Ver. 3.0.0 or greater required.')
  666.         return False
  667.     
  668.     
  669.     try:
  670.         pyqtVersion = qt.PYQT_VERSION_STR
  671.     except:
  672.         pyqtVersion = qt.PYQT_VERSION
  673.  
  674.     while pyqtVersion.count('.') < 2:
  675.         pyqtVersion += '.0'
  676.     (maj_ver, min_ver, pat_ver) = pyqtVersion.split('.')
  677.     if pyqtVersion.find('snapshot') >= 0:
  678.         log.warning('A non-stable snapshot version of PyQt is installed.')
  679.     else:
  680.         
  681.         try:
  682.             maj_ver = int(maj_ver)
  683.             min_ver = int(min_ver)
  684.             pat_ver = int(pat_ver)
  685.         except ValueError:
  686.             (maj_ver, min_ver, pat_ver) = (0, 0, 0)
  687.  
  688.         if (maj_ver < MINIMUM_PYQT_MAJOR_VER or maj_ver == MINIMUM_PYQT_MAJOR_VER) and min_ver < MINIMUM_PYQT_MINOR_VER:
  689.             log.error('This program may not function properly with the version of PyQt that is installed (%d.%d.%d).' % (maj_ver, min_ver, pat_ver))
  690.             log.error('Incorrect version of pyQt installed. Ver. %d.%d or greater required.' % (MINIMUM_PYQT_MAJOR_VER, MINIMUM_PYQT_MINOR_VER))
  691.             log.error('This program will continue, but you may experience errors, crashes or other problems.')
  692.             return True
  693.         
  694.     return True
  695.  
  696.  
  697. def loadTranslators(app, user_config):
  698.     import qt
  699.     loc = None
  700.     if os.path.exists(user_config):
  701.         if not path_exists_safely(user_config):
  702.             log.warning('File %s has insecure permissions! File ignored.' % user_config)
  703.         else:
  704.             config = ConfigParser.ConfigParser()
  705.             config.read(user_config)
  706.             if config.has_section('ui'):
  707.                 loc = config.get('ui', 'loc')
  708.                 if not loc:
  709.                     loc = None
  710.                 
  711.             
  712.     
  713.     if loc is not None:
  714.         if loc.lower() == 'system':
  715.             loc = str(qt.QTextCodec.locale())
  716.         
  717.         if loc.lower() != 'c':
  718.             log.debug('Trying to load .qm file for %s locale.' % loc)
  719.             dirs = [
  720.                 prop.home_dir,
  721.                 prop.data_dir,
  722.                 prop.i18n_dir]
  723.             trans = qt.QTranslator(None)
  724.             for dir in dirs:
  725.                 qm_file = 'hplip_%s' % loc
  726.                 loaded = trans.load(qm_file, dir)
  727.                 if loaded:
  728.                     app.installTranslator(trans)
  729.                     break
  730.                     continue
  731.             
  732.         else:
  733.             loc = None
  734.     
  735.     if loc is None:
  736.         log.debug("Using default 'C' locale")
  737.     else:
  738.         log.debug('Using locale: %s' % loc)
  739.     return loc
  740.  
  741.  
  742. try:
  743.     from string import Template
  744. except ImportError:
  745.     
  746.     class _multimap:
  747.         '''Helper class for combining multiple mappings.
  748.  
  749.         Used by .{safe_,}substitute() to combine the mapping and keyword
  750.         arguments.
  751.         '''
  752.         
  753.         def __init__(self, primary, secondary):
  754.             self._primary = primary
  755.             self._secondary = secondary
  756.  
  757.         
  758.         def __getitem__(self, key):
  759.             
  760.             try:
  761.                 return self._primary[key]
  762.             except KeyError:
  763.                 return self._secondary[key]
  764.  
  765.  
  766.  
  767.     
  768.     class _TemplateMetaclass(type):
  769.         pattern = '\n        %(delim)s(?:\n          (?P<escaped>%(delim)s) |   # Escape sequence of two delimiters\n          (?P<named>%(id)s)      |   # delimiter and a Python identifier\n          {(?P<braced>%(id)s)}   |   # delimiter and a braced identifier\n          (?P<invalid>)              # Other ill-formed delimiter exprs\n        )\n        '
  770.         
  771.         def __init__(cls, name, bases, dct):
  772.             super(_TemplateMetaclass, cls).__init__(name, bases, dct)
  773.             if 'pattern' in dct:
  774.                 pattern = cls.pattern
  775.             else:
  776.                 pattern = _TemplateMetaclass.pattern % {
  777.                     'delim': re.escape(cls.delimiter),
  778.                     'id': cls.idpattern }
  779.             cls.pattern = re.compile(pattern, re.IGNORECASE | re.VERBOSE)
  780.  
  781.  
  782.     
  783.     class Template:
  784.         '''A string class for supporting $-substitutions.'''
  785.         __metaclass__ = _TemplateMetaclass
  786.         delimiter = '$'
  787.         idpattern = '[_a-z][_a-z0-9]*'
  788.         
  789.         def __init__(self, template):
  790.             self.template = template
  791.  
  792.         
  793.         def _invalid(self, mo):
  794.             i = mo.start('invalid')
  795.             lines = self.template[:i].splitlines(True)
  796.             if not lines:
  797.                 colno = 1
  798.                 lineno = 1
  799.             else:
  800.                 colno = i - len(''.join(lines[:-1]))
  801.                 lineno = len(lines)
  802.             raise ValueError('Invalid placeholder in string: line %d, col %d' % (lineno, colno))
  803.  
  804.         
  805.         def substitute(self, *args, **kws):
  806.             if len(args) > 1:
  807.                 raise TypeError('Too many positional arguments')
  808.             
  809.             if not args:
  810.                 mapping = kws
  811.             elif kws:
  812.                 mapping = _multimap(kws, args[0])
  813.             else:
  814.                 mapping = args[0]
  815.             
  816.             def convert(mo):
  817.                 if not mo.group('named'):
  818.                     pass
  819.                 named = mo.group('braced')
  820.                 if named is not None:
  821.                     val = mapping[named]
  822.                     return '%s' % val
  823.                 
  824.                 if mo.group('escaped') is not None:
  825.                     return self.delimiter
  826.                 
  827.                 if mo.group('invalid') is not None:
  828.                     self._invalid(mo)
  829.                 
  830.                 raise ValueError('Unrecognized named group in pattern', self.pattern)
  831.  
  832.             return self.pattern.sub(convert, self.template)
  833.  
  834.         
  835.         def safe_substitute(self, *args, **kws):
  836.             if len(args) > 1:
  837.                 raise TypeError('Too many positional arguments')
  838.             
  839.             if not args:
  840.                 mapping = kws
  841.             elif kws:
  842.                 mapping = _multimap(kws, args[0])
  843.             else:
  844.                 mapping = args[0]
  845.             
  846.             def convert(mo):
  847.                 named = mo.group('named')
  848.                 if named is not None:
  849.                     
  850.                     try:
  851.                         return '%s' % mapping[named]
  852.                     except KeyError:
  853.                         return self.delimiter + named
  854.                     except:
  855.                         None<EXCEPTION MATCH>KeyError
  856.                     
  857.  
  858.                 None<EXCEPTION MATCH>KeyError
  859.                 braced = mo.group('braced')
  860.                 if braced is not None:
  861.                     
  862.                     try:
  863.                         return '%s' % mapping[braced]
  864.                     except KeyError:
  865.                         return self.delimiter + '{' + braced + '}'
  866.                     except:
  867.                         None<EXCEPTION MATCH>KeyError
  868.                     
  869.  
  870.                 None<EXCEPTION MATCH>KeyError
  871.                 if mo.group('escaped') is not None:
  872.                     return self.delimiter
  873.                 
  874.                 if mo.group('invalid') is not None:
  875.                     return self.delimiter
  876.                 
  877.                 raise ValueError('Unrecognized named group in pattern', self.pattern)
  878.  
  879.             return self.pattern.sub(convert, self.template)
  880.  
  881.  
  882.  
  883.  
  884. cat = lambda _: Template(_).substitute(sys._getframe(1).f_globals, **sys._getframe(1).f_locals)
  885. identity = string.maketrans('', '')
  886. unprintable = identity.translate(identity, string.printable)
  887.  
  888. def printable(s):
  889.     return s.translate(identity, unprintable)
  890.  
  891.  
  892. def any(S, f = (lambda x: x)):
  893.     for x in S:
  894.         if f(x):
  895.             return True
  896.             continue
  897.     
  898.     return False
  899.  
  900.  
  901. def all(S, f = (lambda x: x)):
  902.     for x in S:
  903.         if not f(x):
  904.             return False
  905.             continue
  906.     
  907.     return True
  908.  
  909.  
  910. def openURL(url):
  911.     browsers = [
  912.         'firefox',
  913.         'mozilla',
  914.         'konqueror',
  915.         'galeon',
  916.         'skipstone']
  917.     browser_opt = {
  918.         'firefox': '-new-window',
  919.         'mozilla': '',
  920.         'konqueror': '',
  921.         'galeon': '-w',
  922.         'skipstone': '' }
  923.     for b in browsers:
  924.         if which(b):
  925.             cmd = '%s %s "%s" &' % (b, browser_opt[b], url)
  926.             log.debug(cmd)
  927.             os.system(cmd)
  928.             break
  929.             continue
  930.     
  931.  
  932.  
  933. def uniqueList(input):
  934.     temp = []
  935.     _[1]
  936.     return temp
  937.  
  938.  
  939. def list_move_up(l, m):
  940.     for i in range(1, len(l)):
  941.         if l[i] == m:
  942.             l[i - 1] = l[i]
  943.             l[i] = l[i - 1]
  944.             continue
  945.     
  946.  
  947.  
  948. def list_move_down(l, m):
  949.     for i in range(len(l) - 2, 0, -1):
  950.         if l[i] == m:
  951.             l[i] = l[i + 1]
  952.             l[i + 1] = l[i]
  953.             continue
  954.     
  955.  
  956.  
  957. class XMLToDictParser:
  958.     
  959.     def __init__(self):
  960.         self.stack = []
  961.         self.data = { }
  962.  
  963.     
  964.     def startElement(self, name, attrs):
  965.         self.stack.append(str(name).lower())
  966.         if len(attrs):
  967.             for a in attrs:
  968.                 self.stack.append(str(a).lower())
  969.                 self.addData(attrs[a])
  970.                 self.stack.pop()
  971.             
  972.         
  973.  
  974.     
  975.     def endElement(self, name):
  976.         self.stack.pop()
  977.  
  978.     
  979.     def charData(self, data):
  980.         data = str(data).strip()
  981.         if data and self.stack:
  982.             self.addData(data)
  983.         
  984.  
  985.     
  986.     def addData(self, data):
  987.         
  988.         try:
  989.             data = int(data)
  990.         except ValueError:
  991.             data = str(data)
  992.  
  993.         stack_str = '-'.join(self.stack)
  994.         stack_str_0 = '-'.join([
  995.             stack_str,
  996.             '0'])
  997.         
  998.         try:
  999.             self.data[stack_str]
  1000.         except KeyError:
  1001.             
  1002.             try:
  1003.                 self.data[stack_str_0]
  1004.             except KeyError:
  1005.                 self.data[stack_str] = data
  1006.  
  1007.             j = 2
  1008.             while True:
  1009.                 
  1010.                 try:
  1011.                     self.data['-'.join([
  1012.                         stack_str,
  1013.                         str(j)])]
  1014.                 except KeyError:
  1015.                     self.data['-'.join([
  1016.                         stack_str,
  1017.                         str(j)])] = data
  1018.                     break
  1019.  
  1020.                 j += 1
  1021.  
  1022.         self.data[stack_str_0] = self.data[stack_str]
  1023.         self.data['-'.join([
  1024.             stack_str,
  1025.             '1'])] = data
  1026.         del self.data[stack_str]
  1027.  
  1028.     
  1029.     def parseXML(self, text):
  1030.         parser = expat.ParserCreate()
  1031.         parser.StartElementHandler = self.startElement
  1032.         parser.EndElementHandler = self.endElement
  1033.         parser.CharacterDataHandler = self.charData
  1034.         parser.Parse(text, True)
  1035.         return self.data
  1036.  
  1037.  
  1038. USAGE_OPTIONS = ('[OPTIONS]', '', 'heading', False)
  1039. USAGE_LOGGING1 = ('Set the logging level:', '-l<level> or --logging=<level>', 'option', False)
  1040. USAGE_LOGGING2 = ('', '<level>: none, info\\*, error, warn, debug (\\*default)', 'option', False)
  1041. USAGE_LOGGING3 = ('Run in debug mode:', '-g (same as option: -ldebug)', 'option', False)
  1042. USAGE_ARGS = ('[PRINTER|DEVICE-URI] (See Notes)', '', 'heading', False)
  1043. USAGE_DEVICE = ('To specify a device-URI:', '-d<device-uri> or --device=<device-uri>', 'option', False)
  1044. USAGE_PRINTER = ('To specify a CUPS printer:', '-p<printer> or --printer=<printer>', 'option', False)
  1045. USAGE_BUS1 = ('Bus to probe (if device not specified):', '-b<bus> or --bus=<bus>', 'option', False)
  1046. USAGE_BUS2 = ('', '<bus>: cups\\*, usb\\*, net, bt, fw, par\\* (\\*defaults) (Note: bt and fw not supported in this release.)', 'option', False)
  1047. USAGE_HELP = ('This help information:', '-h or --help', 'option', True)
  1048. USAGE_SPACE = ('', '', 'space', False)
  1049. USAGE_EXAMPLES = ('Examples:', '', 'heading', False)
  1050. USAGE_NOTES = ('Notes:', '', 'heading', False)
  1051. USAGE_STD_NOTES1 = ('1. If device or printer is not specified, the local device bus is probed and the program enters interactive mode.', '', 'note', False)
  1052. USAGE_STD_NOTES2 = ('2. If -p\\* is specified, the default CUPS printer will be used.', '', 'note', False)
  1053. USAGE_SEEALSO = ('See Also:', '', 'heading', False)
  1054.  
  1055. def ttysize():
  1056.     ln1 = commands.getoutput('stty -a').splitlines()[0]
  1057.     vals = {
  1058.         'rows': None,
  1059.         'columns': None }
  1060.     for ph in ln1.split(';'):
  1061.         x = ph.split()
  1062.         if len(x) == 2:
  1063.             vals[x[0]] = x[1]
  1064.             vals[x[1]] = x[0]
  1065.             continue
  1066.     
  1067.     
  1068.     try:
  1069.         rows = int(vals['rows'])
  1070.         cols = int(vals['columns'])
  1071.     except TypeError:
  1072.         (rows, cols) = (25, 80)
  1073.  
  1074.     return (rows, cols)
  1075.  
  1076.  
  1077. def usage_formatter(override = 0):
  1078.     (rows, cols) = ttysize()
  1079.     if override:
  1080.         col1 = override
  1081.         col2 = cols - col1 - 8
  1082.     else:
  1083.         col1 = int(cols / 3) - 8
  1084.         col2 = cols - col1 - 8
  1085.     return TextFormatter(({
  1086.         'width': col1,
  1087.         'margin': 2 }, {
  1088.         'width': col2,
  1089.         'margin': 2 }))
  1090.  
  1091.  
  1092. def format_text(text_list, typ = 'text', title = '', crumb = '', version = ''):
  1093.     '''
  1094.     Format usage text in multiple formats:
  1095.         text: for --help in the console
  1096.         rest: for conversion with rst2web for the website
  1097.         man: for manpages
  1098.     '''
  1099.     if typ == 'text':
  1100.         formatter = usage_formatter()
  1101.         for line in text_list:
  1102.             (text1, text2, format, trailing_space) = line
  1103.             text1 = text1.replace('\\', '')
  1104.             text2 = text2.replace('\\', '')
  1105.             if format == 'summary':
  1106.                 log.info(bold(text1))
  1107.                 log.info('')
  1108.                 continue
  1109.             if format in ('para', 'name', 'seealso'):
  1110.                 log.info(text1)
  1111.                 if trailing_space:
  1112.                     log.info('')
  1113.                 
  1114.             trailing_space
  1115.             if format in ('heading', 'header'):
  1116.                 log.info(bold(text1))
  1117.                 continue
  1118.             if format in ('option', 'example'):
  1119.                 log.info(formatter.compose((text1, text2), trailing_space))
  1120.                 continue
  1121.             if format == 'note':
  1122.                 if text1.startswith(' '):
  1123.                     log.info('\t' + text1.lstrip())
  1124.                 else:
  1125.                     log.info(text1)
  1126.             text1.startswith(' ')
  1127.             if format == 'space':
  1128.                 log.info('')
  1129.                 continue
  1130.         
  1131.         log.info('')
  1132.     elif typ == 'rest':
  1133.         (colwidth1, colwidth2) = (0, 0)
  1134.         for line in text_list:
  1135.             (text1, text2, format, trailing_space) = line
  1136.             if format in ('option', 'example', 'note'):
  1137.                 colwidth1 = max(len(text1), colwidth1)
  1138.                 colwidth2 = max(len(text2), colwidth2)
  1139.                 continue
  1140.         
  1141.         colwidth1 += 3
  1142.         tablewidth = colwidth1 + colwidth2
  1143.         log.info('restindex\npage-title: %s\ncrumb: %s\nformat: rest\nfile-extension: html\nencoding: utf8\n/restindex\n' % (title, crumb))
  1144.         log.info('%s: %s (ver. %s)' % (crumb, title, version))
  1145.         log.info('=' * 80)
  1146.         log.info('')
  1147.         links = []
  1148.         for line in text_list:
  1149.             (text1, text2, format, trailing_space) = line
  1150.             if format == 'seealso':
  1151.                 links.append(text1)
  1152.                 text1 = '`%s`_' % text1
  1153.             
  1154.             len1 = len(text1)
  1155.             len2 = len(text2)
  1156.             if format == 'summary':
  1157.                 log.info(''.join([
  1158.                     '**',
  1159.                     text1,
  1160.                     '**']))
  1161.                 log.info('')
  1162.                 continue
  1163.             if format in ('para', 'name'):
  1164.                 log.info('')
  1165.                 log.info(text1)
  1166.                 log.info('')
  1167.                 continue
  1168.             if format in ('heading', 'header'):
  1169.                 log.info('')
  1170.                 log.info('**' + text1 + '**')
  1171.                 log.info('')
  1172.                 log.info('.. class:: borderless')
  1173.                 log.info('')
  1174.                 log.info(''.join([
  1175.                     '+',
  1176.                     '-' * colwidth1,
  1177.                     '+',
  1178.                     '-' * colwidth2,
  1179.                     '+']))
  1180.                 continue
  1181.             if format in ('option', 'example', 'seealso'):
  1182.                 if text1 and '`_' not in text1:
  1183.                     log.info(''.join([
  1184.                         '| *',
  1185.                         text1,
  1186.                         '*',
  1187.                         ' ' * (colwidth1 - len1 - 3),
  1188.                         '|',
  1189.                         text2,
  1190.                         ' ' * (colwidth2 - len2),
  1191.                         '|']))
  1192.                 elif text1:
  1193.                     log.info(''.join([
  1194.                         '|',
  1195.                         text1,
  1196.                         ' ' * (colwidth1 - len1),
  1197.                         '|',
  1198.                         text2,
  1199.                         ' ' * (colwidth2 - len2),
  1200.                         '|']))
  1201.                 else:
  1202.                     log.info(''.join([
  1203.                         '|',
  1204.                         ' ' * colwidth1,
  1205.                         '|',
  1206.                         text2,
  1207.                         ' ' * (colwidth2 - len2),
  1208.                         '|']))
  1209.                 log.info(''.join([
  1210.                     '+',
  1211.                     '-' * colwidth1,
  1212.                     '+',
  1213.                     '-' * colwidth2,
  1214.                     '+']))
  1215.                 continue
  1216.             if format == 'note':
  1217.                 if text1.startswith(' '):
  1218.                     log.info(''.join([
  1219.                         '|',
  1220.                         ' ' * (tablewidth + 1),
  1221.                         '|']))
  1222.                 
  1223.                 log.info(''.join([
  1224.                     '|',
  1225.                     text1,
  1226.                     ' ' * ((tablewidth - len1) + 1),
  1227.                     '|']))
  1228.                 log.info(''.join([
  1229.                     '+',
  1230.                     '-' * colwidth1,
  1231.                     '+',
  1232.                     '-' * colwidth2,
  1233.                     '+']))
  1234.                 continue
  1235.             if format == 'space':
  1236.                 log.info('')
  1237.                 continue
  1238.         
  1239.         for l in links:
  1240.             log.info('\n.. _`%s`: %s.html\n' % (l, l.replace('hp-', '')))
  1241.         
  1242.         log.info('')
  1243.     elif typ == 'man':
  1244.         log.info('.TH "%s" 1 "%s" Linux "User Manuals"' % (title, version))
  1245.         for line in text_list:
  1246.             (text1, text2, format, trailing_space) = line
  1247.             text1 = text1.replace('\\*', '*')
  1248.             text2 = text2.replace('\\*', '*')
  1249.             len1 = len(text1)
  1250.             len2 = len(text2)
  1251.             if format == 'summary':
  1252.                 log.info('.SH SYNOPSIS')
  1253.                 log.info('.B %s' % text1)
  1254.                 continue
  1255.             if format == 'name':
  1256.                 log.info('.SH NAME\n%s' % text1)
  1257.                 continue
  1258.             if format in ('option', 'example', 'note'):
  1259.                 if text1:
  1260.                     log.info('.IP "%s"\n%s' % (text1, text2))
  1261.                 else:
  1262.                     log.info(text2)
  1263.             text1
  1264.             if format in ('header', 'heading'):
  1265.                 log.info('.SH %s' % text1.upper().replace(':', '').replace('[', '').replace(']', ''))
  1266.                 continue
  1267.             if format in 'seealso, para':
  1268.                 log.info(text1)
  1269.                 continue
  1270.         
  1271.         log.info('')
  1272.     
  1273.  
  1274.  
  1275. def dquote(s):
  1276.     return ''.join([
  1277.         '"',
  1278.         s,
  1279.         '"'])
  1280.  
  1281.  
  1282. def xlstrip(s, chars = ' '):
  1283.     i = 0
  1284.     for c, i in zip(s, range(len(s))):
  1285.         if c not in chars:
  1286.             break
  1287.             continue
  1288.     
  1289.     return s[i:]
  1290.  
  1291.  
  1292. def xrstrip(s, chars = ' '):
  1293.     return xreverse(xlstrip(xreverse(s), chars))
  1294.  
  1295.  
  1296. def xreverse(s):
  1297.     l = list(s)
  1298.     l.reverse()
  1299.     return ''.join(l)
  1300.  
  1301.  
  1302. def xstrip(s, chars = ' '):
  1303.     return xreverse(xlstrip(xreverse(xlstrip(s, chars)), chars))
  1304.  
  1305.  
  1306. def getBitness():
  1307.     
  1308.     try:
  1309.         import platform
  1310.     except ImportError:
  1311.         return struct.calcsize('P') << 3
  1312.  
  1313.     return int(platform.architecture()[0][:-3])
  1314.  
  1315. BIG_ENDIAN = 0
  1316. LITTLE_ENDIAN = 1
  1317.  
  1318. def getEndian():
  1319.     if struct.pack('@I', 16909060)[0] == '\x01':
  1320.         return BIG_ENDIAN
  1321.     else:
  1322.         return LITTLE_ENDIAN
  1323.  
  1324.  
  1325. def get_password():
  1326.     return getpass.getpass('Enter password: ')
  1327.  
  1328.  
  1329. def run(cmd, log_output = True, password_func = get_password, timeout = 1):
  1330.     output = cStringIO.StringIO()
  1331.     
  1332.     try:
  1333.         child = pexpect.spawn(cmd, timeout = timeout)
  1334.     except pexpect.ExceptionPexpect:
  1335.         return (-1, '')
  1336.  
  1337.     
  1338.     try:
  1339.         while True:
  1340.             update_spinner()
  1341.             i = child.expect([
  1342.                 '[pP]assword:',
  1343.                 pexpect.EOF,
  1344.                 pexpect.TIMEOUT])
  1345.             if child.before:
  1346.                 log.debug(child.before)
  1347.                 output.write(child.before)
  1348.             
  1349.             if i == 0:
  1350.                 if password_func is not None:
  1351.                     child.sendline(password_func())
  1352.                 else:
  1353.                     child.sendline(get_password())
  1354.             password_func is not None
  1355.             if i == 1:
  1356.                 break
  1357.                 continue
  1358.             if i == 2:
  1359.                 continue
  1360.                 continue
  1361.     except Exception:
  1362.         e = None
  1363.         print 'Exception', e
  1364.  
  1365.     cleanup_spinner()
  1366.     child.close()
  1367.     return (child.exitstatus, output.getvalue())
  1368.  
  1369.  
  1370. def expand_range(ns):
  1371.     '''Credit: Jean Brouwers, comp.lang.python 16-7-2004
  1372.        Convert a string representation of a set of ranges into a 
  1373.        list of ints, e.g.
  1374.        "1-4, 7, 9-12" --> [1,2,3,4,7,9,10,11,12]
  1375.     '''
  1376.     fs = []
  1377.     for n in ns.split(','):
  1378.         n = n.strip()
  1379.         r = n.split('-')
  1380.         if len(r) == 2:
  1381.             h = r[0].rstrip('0123456789')
  1382.             r[0] = r[0][len(h):]
  1383.             if not r[0] and r[1]:
  1384.                 raise ValueError, 'empty range: ' + n
  1385.             
  1386.             r = [ int(i, 10) for i in r ]
  1387.             for i in range(r[0], r[1] + 1):
  1388.                 fs.append(h % i)
  1389.             
  1390.         None if r[0] == '0' or r[0][0] != '0' else None if w[1] > w[0] else [] if r[0] > r[1] else []
  1391.         fs.append(n)
  1392.     
  1393.     fs = []([ (n, i) for i, n in enumerate(fs) ]).keys()
  1394.     fs = _[4]
  1395.     fs.sort()
  1396.     return fs
  1397.  
  1398.  
  1399. def collapse_range(x):
  1400.     ''' Convert a list of integers into a string
  1401.         range representation: 
  1402.         [1,2,3,4,7,9,10,11,12] --> "1-4, 7, 9-12"
  1403.     '''
  1404.     if not x:
  1405.         return ''
  1406.     
  1407.     s = [
  1408.         str(x[0])]
  1409.     c = x[0]
  1410.     r = False
  1411.     for i in x[1:]:
  1412.         if i == c + 1:
  1413.             r = True
  1414.         elif r:
  1415.             s.append('-%s, %s' % (c, i))
  1416.             r = False
  1417.         else:
  1418.             s.append(', %s' % i)
  1419.         c = i
  1420.     
  1421.     if r:
  1422.         s.append('-%s' % i)
  1423.     
  1424.     return ''.join(s)
  1425.  
  1426.