Tizen 2.1 base
[platform/upstream/hplip.git] / base / utils.py
1 # -*- coding: utf-8 -*-
2 #
3 # (c) Copyright 2001-2009 Hewlett-Packard Development Company, L.P.
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 #
19 # Author: Don Welch, Naga Samrat Chowdary Narla, Goutam Kodu, Amarnath Chitumalla
20 #
21 # Thanks to Henrique M. Holschuh <hmh@debian.org> for various security patches
22 #
23
24 from __future__ import generators
25
26 # Std Lib
27 import sys
28 import os
29 from subprocess import Popen, PIPE
30 import grp
31 import fnmatch
32 import tempfile
33 import socket
34 import struct
35 import select
36 import time
37 import fcntl
38 import errno
39 import stat
40 import string
41 import commands # TODO: Replace with subprocess (commands is deprecated in Python 3.0)
42 import cStringIO
43 import re
44 import xml.parsers.expat as expat
45 import getpass
46 import locale
47 import htmlentitydefs
48 import urllib
49
50 try:
51     import platform
52     platform_avail = True
53 except ImportError:
54     platform_avail = False
55
56 # Local
57 from g import *
58 from codes import *
59 import pexpect
60
61 BIG_ENDIAN = 0
62 LITTLE_ENDIAN = 1
63
64 def addgroup():
65     lis = []
66     try:
67         fp=open('/etc/cups/cupsd.conf')
68     except IOError:
69         try:
70             if "root" != grp.getgrgid(os.stat('/etc/cups/cupsd.conf').st_gid).gr_name:
71                 return [grp.getgrgid(os.stat('/etc/cups/cupsd.conf').st_gid).gr_name]
72         except OSError:
73             return lis
74
75     try:
76         lis = ((re.findall('SystemGroup [\w* ]*',fp.read()))[0].replace('SystemGroup ','')).split(' ')
77     except IndexError:
78         return lis
79
80     if 'root' in lis:
81         lis.remove('root')
82     fp.close()
83     return lis
84
85 def list_to_string(lis):
86     if len(lis) == 0:
87         return ""
88     if len(lis) == 1:
89         return str("\""+lis[0]+"\"")
90     if len(lis) >= 1:
91         return "\""+"\", \"".join(lis)+"\" and \""+str(lis.pop())+"\""
92         
93 def lock(f):
94     log.debug("Locking: %s" % f.name)
95     try:
96         fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
97         return True
98     except (IOError, OSError):
99         log.debug("Failed to unlock %s." % f.name)
100         return False
101
102
103 def unlock(f):
104     if f is not None:
105         log.debug("Unlocking: %s" % f.name)
106         try:
107             fcntl.flock(f.fileno(), fcntl.LOCK_UN)
108             os.remove(f.name)
109         except (IOError, OSError):
110             pass
111
112
113 def lock_app(application, suppress_error=False):
114     dir = prop.user_dir
115     if os.geteuid() == 0:
116         dir = '/var'
117
118     elif not os.path.exists(dir):
119         os.makedirs(dir)
120
121     lock_file = os.path.join(dir, '.'.join([application, 'lock']))
122     try:
123         lock_file_f = open(lock_file, "w")
124     except IOError:
125         if not suppress_error:
126             log.error("Unable to open %s lock file." % lock_file)
127         return False, None
128
129     #log.debug("Locking file: %s" % lock_file)
130
131     if not lock(lock_file_f):
132         if not suppress_error:
133             log.error("Unable to lock %s. Is %s already running?" % (lock_file, application))
134         return False, None
135
136     return True, lock_file_f
137
138
139 #xml_basename_pat = re.compile(r"""HPLIP-(\d*)_(\d*)_(\d*).xml""", re.IGNORECASE)
140
141
142 def Translator(frm='', to='', delete='', keep=None):
143     allchars = string.maketrans('','')
144
145     if len(to) == 1:
146         to = to * len(frm)
147     trans = string.maketrans(frm, to)
148
149     if keep is not None:
150         delete = allchars.translate(allchars, keep.translate(allchars, delete))
151
152     def callable(s):
153         return s.translate(trans, delete)
154
155     return callable
156
157
158 def to_bool_str(s, default='0'):
159     """ Convert an arbitrary 0/1/T/F/Y/N string to a normalized string 0/1."""
160     if isinstance(s, str) and s:
161         if s[0].lower() in ['1', 't', 'y']:
162             return u'1'
163         elif s[0].lower() in ['0', 'f', 'n']:
164             return u'0'
165
166     return default
167
168 def to_bool(s, default=False):
169     """ Convert an arbitrary 0/1/T/F/Y/N string to a boolean True/False value."""
170     if isinstance(s, str) and s:
171         if s[0].lower() in ['1', 't', 'y']:
172             return True
173         elif s[0].lower() in ['0', 'f', 'n']:
174             return False
175     elif isinstance(s, bool):
176         return s
177
178     return default
179
180
181 def walkFiles(root, recurse=True, abs_paths=False, return_folders=False, pattern='*', path=None):
182     if path is None:
183         path = root
184
185     try:
186         names = os.listdir(root)
187     except os.error:
188         raise StopIteration
189
190     pattern = pattern or '*'
191     pat_list = pattern.split(';')
192
193     for name in names:
194         fullname = os.path.normpath(os.path.join(root, name))
195
196         for pat in pat_list:
197             if fnmatch.fnmatch(name, pat):
198                 if return_folders or not os.path.isdir(fullname):
199                     if abs_paths:
200                         yield fullname
201                     else:
202                         try:
203                             yield os.path.basename(fullname)
204                         except ValueError:
205                             yield fullname
206
207         #if os.path.islink(fullname):
208         #    fullname = os.path.realpath(os.readlink(fullname))
209
210         if recurse and os.path.isdir(fullname): # or os.path.islink(fullname):
211             for f in walkFiles(fullname, recurse, abs_paths, return_folders, pattern, path):
212                 yield f
213
214
215 def is_path_writable(path):
216     if os.path.exists(path):
217         s = os.stat(path)
218         mode = s[stat.ST_MODE] & 0777
219
220         if mode & 02:
221             return True
222         elif s[stat.ST_GID] == os.getgid() and mode & 020:
223             return True
224         elif s[stat.ST_UID] == os.getuid() and mode & 0200:
225             return True
226
227     return False
228
229
230 # Provides the TextFormatter class for formatting text into columns.
231 # Original Author: Hamish B Lawson, 1999
232 # Modified by: Don Welch, 2003
233 class TextFormatter:
234
235     LEFT  = 0
236     CENTER = 1
237     RIGHT  = 2
238
239     def __init__(self, colspeclist):
240         self.columns = []
241         for colspec in colspeclist:
242             self.columns.append(Column(**colspec))
243
244     def compose(self, textlist, add_newline=False):
245         numlines = 0
246         textlist = list(textlist)
247         if len(textlist) != len(self.columns):
248             log.error("Formatter: Number of text items does not match columns")
249             return
250         for text, column in map(None, textlist, self.columns):
251             column.wrap(text)
252             numlines = max(numlines, len(column.lines))
253         complines = [''] * numlines
254         for ln in range(numlines):
255             for column in self.columns:
256                 complines[ln] = complines[ln] + column.getline(ln)
257         if add_newline:
258             return '\n'.join(complines) + '\n'
259         else:
260             return '\n'.join(complines)
261
262 class Column:
263
264     def __init__(self, width=78, alignment=TextFormatter.LEFT, margin=0):
265         self.width = width
266         self.alignment = alignment
267         self.margin = margin
268         self.lines = []
269
270     def align(self, line):
271         if self.alignment == TextFormatter.CENTER:
272             return line.center(self.width)
273         elif self.alignment == TextFormatter.RIGHT:
274             return line.rjust(self.width)
275         else:
276             return line.ljust(self.width)
277
278     def wrap(self, text):
279         self.lines = []
280         words = []
281         for word in text.split():
282             if word <= self.width:
283                 words.append(word)
284             else:
285                 for i in range(0, len(word), self.width):
286                     words.append(word[i:i+self.width])
287         if not len(words): return
288         current = words.pop(0)
289         for word in words:
290             increment = 1 + len(word)
291             if len(current) + increment > self.width:
292                 self.lines.append(self.align(current))
293                 current = word
294             else:
295                 current = current + ' ' + word
296         self.lines.append(self.align(current))
297
298     def getline(self, index):
299         if index < len(self.lines):
300             return ' '*self.margin + self.lines[index]
301         else:
302             return ' ' * (self.margin + self.width)
303
304
305
306 class Stack:
307     def __init__(self):
308         self.stack = []
309
310     def pop(self):
311         return self.stack.pop()
312
313     def push(self, value):
314         self.stack.append(value)
315
316     def as_list(self):
317         return self.stack
318
319     def clear(self):
320         self.stack = []
321
322     def __len__(self):
323         return len(self.stack)
324
325
326
327 class Queue(Stack):
328     def __init__(self):
329         Stack.__init__(self)
330
331     def get(self):
332         return self.stack.pop(0)
333
334     def put(self, value):
335         Stack.push(self, value)
336
337
338
339 # RingBuffer class
340 # Source: Python Cookbook 1st Ed., sec. 5.18, pg. 201
341 # Credit: Sebastien Keim
342 # License: Modified BSD
343 class RingBuffer:
344     def __init__(self, size_max=50):
345         self.max = size_max
346         self.data = []
347
348     def append(self,x):
349         """append an element at the end of the buffer"""
350         self.data.append(x)
351
352         if len(self.data) == self.max:
353             self.cur = 0
354             self.__class__ = RingBufferFull
355
356     def replace(self, x):
357         """replace the last element instead off appending"""
358         self.data[-1] = x
359
360     def get(self):
361         """ return a list of elements from the oldest to the newest"""
362         return self.data
363
364
365 class RingBufferFull:
366     def __init__(self, n):
367         #raise "you should use RingBuffer"
368         pass
369
370     def append(self, x):
371         self.data[self.cur] = x
372         self.cur = (self.cur+1) % self.max
373
374     def replace(self, x):
375         # back up 1 position to previous location
376         self.cur = (self.cur-1) % self.max
377         self.data[self.cur] = x
378         # setup for next item
379         self.cur = (self.cur+1) % self.max
380
381     def get(self):
382         return self.data[self.cur:] + self.data[:self.cur]
383
384
385
386 def sort_dict_by_value(d):
387     """ Returns the keys of dictionary d sorted by their values """
388     items=d.items()
389     backitems=[[v[1],v[0]] for v in items]
390     backitems.sort()
391     return [backitems[i][1] for i in range(0, len(backitems))]
392
393
394 def commafy(val):
395     return unicode(locale.format("%d", val, grouping=True))
396
397
398 def format_bytes(s, show_bytes=False):
399     if s < 1024:
400         return ''.join([commafy(s), ' B'])
401     elif 1024 < s < 1048576:
402         if show_bytes:
403             return ''.join([unicode(round(s/1024.0, 1)) , u' KB (',  commafy(s), ')'])
404         else:
405             return ''.join([unicode(round(s/1024.0, 1)) , u' KB'])
406     elif 1048576 < s < 1073741824:
407         if show_bytes:
408             return ''.join([unicode(round(s/1048576.0, 1)), u' MB (',  commafy(s), ')'])
409         else:
410             return ''.join([unicode(round(s/1048576.0, 1)), u' MB'])
411     else:
412         if show_bytes:
413             return ''.join([unicode(round(s/1073741824.0, 1)), u' GB (',  commafy(s), ')'])
414         else:
415             return ''.join([unicode(round(s/1073741824.0, 1)), u' GB'])
416
417
418
419 try:
420     make_temp_file = tempfile.mkstemp # 2.3+
421 except AttributeError:
422     def make_temp_file(suffix='', prefix='', dir='', text=False): # pre-2.3
423         path = tempfile.mktemp(suffix)
424         fd = os.open(path, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0700)
425         return ( os.fdopen( fd, 'w+b' ), path )
426
427
428
429 def which(command, return_full_path=False):
430     path = os.getenv('PATH').split(':')
431
432     # Add these paths for Fedora
433     path.append('/sbin')
434     path.append('/usr/sbin')
435     path.append('/usr/local/sbin')
436
437     found_path = ''
438     for p in path:
439         try:
440             files = os.listdir(p)
441         except OSError:
442             continue
443         else:
444             if command in files:
445                 found_path = p
446                 break
447
448     if return_full_path:
449         if found_path:
450             return os.path.join(found_path, command)
451         else:
452             return ''
453     else:
454         return found_path
455
456
457 class UserSettings(object): # Note: Deprecated after 2.8.8 in Qt4 (see ui4/ui_utils.py)
458     def __init__(self):
459         self.load()
460
461     def loadDefaults(self):
462         # Print
463         self.cmd_print = ''
464         path = which('hp-print')
465
466         if len(path) > 0:
467             self.cmd_print = 'hp-print -p%PRINTER%'
468         else:
469             path = which('kprinter')
470             if len(path) > 0:
471                 self.cmd_print = 'kprinter -P%PRINTER% --system cups'
472             else:
473                 path = which('gtklp')
474                 if len(path) > 0:
475                     self.cmd_print = 'gtklp -P%PRINTER%'
476                 else:
477                     path = which('xpp')
478                     if len(path) > 0:
479                         self.cmd_print = 'xpp -P%PRINTER%'
480
481         # Scan
482         self.cmd_scan = ''
483         path = which('xsane')
484
485         if len(path) > 0:
486             self.cmd_scan = 'xsane -V %SANE_URI%'
487         else:
488             path = which('kooka')
489             if len(path) > 0:
490                 self.cmd_scan = 'kooka'
491             else:
492                 path = which('xscanimage')
493                 if len(path) > 0:
494                     self.cmd_scan = 'xscanimage'
495
496         # Photo Card
497         path = which('hp-unload')
498
499         if len(path):
500             self.cmd_pcard = 'hp-unload -d %DEVICE_URI%'
501         else:
502             self.cmd_pcard = 'python %HOME%/unload.py -d %DEVICE_URI%'
503
504         # Copy
505         path = which('hp-makecopies')
506
507         if len(path):
508             self.cmd_copy = 'hp-makecopies -d %DEVICE_URI%'
509         else:
510             self.cmd_copy = 'python %HOME%/makecopies.py -d %DEVICE_URI%'
511
512         # Fax
513         path = which('hp-sendfax')
514
515         if len(path):
516             self.cmd_fax = 'hp-sendfax -d %FAX_URI%'
517         else:
518             self.cmd_fax = 'python %HOME%/sendfax.py -d %FAX_URI%'
519
520         # Fax Address Book
521         path = which('hp-fab')
522
523         if len(path):
524             self.cmd_fab = 'hp-fab'
525         else:
526             self.cmd_fab = 'python %HOME%/fab.py'
527
528     def load(self):
529         self.loadDefaults()
530         log.debug("Loading user settings...")
531         self.auto_refresh = to_bool(user_conf.get('refresh', 'enable', '0'))
532
533         try:
534             self.auto_refresh_rate = int(user_conf.get('refresh', 'rate', '30'))
535         except ValueError:
536             self.auto_refresh_rate = 30 # (secs)
537
538         try:
539             self.auto_refresh_type = int(user_conf.get('refresh', 'type', '0'))
540         except ValueError:
541             self.auto_refresh_type = 0 # refresh 1 (1=refresh all)
542
543         self.cmd_print = user_conf.get('commands', 'prnt', self.cmd_print)
544         self.cmd_scan = user_conf.get('commands', 'scan', self.cmd_scan)
545         self.cmd_pcard = user_conf.get('commands', 'pcard', self.cmd_pcard)
546         self.cmd_copy = user_conf.get('commands', 'cpy', self.cmd_copy)
547         self.cmd_fax = user_conf.get('commands', 'fax', self.cmd_fax)
548         self.cmd_fab = user_conf.get('commands', 'fab', self.cmd_fab)
549
550         self.upgrade_notify= to_bool(user_conf.get('upgrade', 'notify_upgrade', '0'))
551         self.upgrade_last_update_time = int(user_conf.get('upgrade','last_upgraded_time', '0'))
552         self.upgrade_pending_update_time =int(user_conf.get('upgrade', 'pending_upgrade_time', '0'))
553         self.latest_available_version=str(user_conf.get('upgrade', 'latest_available_version',''))
554         self.debug()
555
556     def debug(self):
557         log.debug("Print command: %s" % self.cmd_print)
558         log.debug("PCard command: %s" % self.cmd_pcard)
559         log.debug("Fax command: %s" % self.cmd_fax)
560         log.debug("FAB command: %s" % self.cmd_fab)
561         log.debug("Copy command: %s " % self.cmd_copy)
562         log.debug("Scan command: %s" % self.cmd_scan)
563         log.debug("Auto refresh: %s" % self.auto_refresh)
564         log.debug("Auto refresh rate: %s" % self.auto_refresh_rate)
565         log.debug("Auto refresh type: %s" % self.auto_refresh_type)
566         log.debug("Upgrade notification:%d"  %self.upgrade_notify)
567         log.debug("Last Installed time:%d" %self.upgrade_last_update_time)
568         log.debug("Next scheduled installation time:%d" % self.upgrade_pending_update_time)
569
570
571     def save(self):
572         log.debug("Saving user settings...")
573         user_conf.set('commands', 'prnt', self.cmd_print)
574         user_conf.set('commands', 'pcard', self.cmd_pcard)
575         user_conf.set('commands', 'fax', self.cmd_fax)
576         user_conf.set('commands', 'scan', self.cmd_scan)
577         user_conf.set('commands', 'cpy', self.cmd_copy)
578         user_conf.set('refresh', 'enable',self.auto_refresh)
579         user_conf.set('refresh', 'rate', self.auto_refresh_rate)
580         user_conf.set('refresh', 'type', self.auto_refresh_type)
581         user_conf.set('upgrade', 'notify_upgrade', self.upgrade_notify)
582         user_conf.set('upgrade','last_upgraded_time', self.upgrade_last_update_time)
583         user_conf.set('upgrade', 'pending_upgrade_time', self.upgrade_pending_update_time)
584         user_conf.set('upgrade', 'latest_available_version', self.latest_available_version)
585
586         self.debug()
587
588
589
590 def no_qt_message_gtk():
591     try:
592         import gtk
593         w = gtk.Window()
594         dialog = gtk.MessageDialog(w, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
595                                    gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
596                                    "PyQt not installed. GUI not available. Please check that the PyQt package is installed. Exiting.")
597         dialog.run()
598         dialog.destroy()
599
600     except ImportError:
601         log.error("PyQt not installed. GUI not available. Please check that the PyQt package is installed. Exiting.")
602
603
604 def canEnterGUIMode(): # qt3
605     if not prop.gui_build:
606         log.warn("GUI mode disabled in build.")
607         return False
608
609     elif not os.getenv('DISPLAY'):
610         log.warn("No display found.")
611         return False
612
613     elif not checkPyQtImport():
614         log.warn("Qt/PyQt 3 initialization failed.")
615         return False
616
617     return True
618
619
620 def canEnterGUIMode4(): # qt4
621     if not prop.gui_build:
622         log.warn("GUI mode disabled in build.")
623         return False
624
625     elif not os.getenv('DISPLAY'):
626         log.warn("No display found.")
627         return False
628
629     elif not checkPyQtImport4():
630         log.warn("Qt/PyQt 4 initialization failed.")
631         return False
632
633     return True
634
635
636 def checkPyQtImport(): # qt3
637     # PyQt
638     try:
639         import qt
640     except ImportError:
641         if os.getenv('DISPLAY') and os.getenv('STARTED_FROM_MENU'):
642             no_qt_message_gtk()
643
644         log.error("PyQt not installed. GUI not available. Exiting.")
645         return False
646
647     # check version of Qt
648     qtMajor = int(qt.qVersion().split('.')[0])
649
650     if qtMajor < MINIMUM_QT_MAJOR_VER:
651
652         log.error("Incorrect version of Qt installed. Ver. 3.0.0 or greater required.")
653         return False
654
655     #check version of PyQt
656     try:
657         pyqtVersion = qt.PYQT_VERSION_STR
658     except AttributeError:
659         pyqtVersion = qt.PYQT_VERSION
660
661     while pyqtVersion.count('.') < 2:
662         pyqtVersion += '.0'
663
664     (maj_ver, min_ver, pat_ver) = pyqtVersion.split('.')
665
666     if pyqtVersion.find('snapshot') >= 0:
667         log.warning("A non-stable snapshot version of PyQt is installed.")
668     else:
669         try:
670             maj_ver = int(maj_ver)
671             min_ver = int(min_ver)
672             pat_ver = int(pat_ver)
673         except ValueError:
674             maj_ver, min_ver, pat_ver = 0, 0, 0
675
676         if maj_ver < MINIMUM_PYQT_MAJOR_VER or \
677             (maj_ver == MINIMUM_PYQT_MAJOR_VER and min_ver < MINIMUM_PYQT_MINOR_VER):
678             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))
679             log.error("Incorrect version of pyQt installed. Ver. %d.%d or greater required." % (MINIMUM_PYQT_MAJOR_VER, MINIMUM_PYQT_MINOR_VER))
680             log.error("This program will continue, but you may experience errors, crashes or other problems.")
681             return True
682
683     return True
684
685
686 def checkPyQtImport4():
687     try:
688         import PyQt4
689     except ImportError:
690         return False
691     else:
692         return True
693
694
695 try:
696     from string import Template # will fail in Python <= 2.3
697 except ImportError:
698     # Code from Python 2.4 string.py
699     #import re as _re
700
701     class _multimap:
702         """Helper class for combining multiple mappings.
703
704         Used by .{safe_,}substitute() to combine the mapping and keyword
705         arguments.
706         """
707         def __init__(self, primary, secondary):
708             self._primary = primary
709             self._secondary = secondary
710
711         def __getitem__(self, key):
712             try:
713                 return self._primary[key]
714             except KeyError:
715                 return self._secondary[key]
716
717
718     class _TemplateMetaclass(type):
719         pattern = r"""
720         %(delim)s(?:
721           (?P<escaped>%(delim)s) |   # Escape sequence of two delimiters
722           (?P<named>%(id)s)      |   # delimiter and a Python identifier
723           {(?P<braced>%(id)s)}   |   # delimiter and a braced identifier
724           (?P<invalid>)              # Other ill-formed delimiter exprs
725         )
726         """
727
728         def __init__(cls, name, bases, dct):
729             super(_TemplateMetaclass, cls).__init__(name, bases, dct)
730             if 'pattern' in dct:
731                 pattern = cls.pattern
732             else:
733                 pattern = _TemplateMetaclass.pattern % {
734                     'delim' : re.escape(cls.delimiter),
735                     'id'    : cls.idpattern,
736                     }
737             cls.pattern = re.compile(pattern, re.IGNORECASE | re.VERBOSE)
738
739
740     class Template:
741         """A string class for supporting $-substitutions."""
742         __metaclass__ = _TemplateMetaclass
743
744         delimiter = '$'
745         idpattern = r'[_a-z][_a-z0-9]*'
746
747         def __init__(self, template):
748             self.template = template
749
750         # Search for $$, $identifier, ${identifier}, and any bare $'s
751         def _invalid(self, mo):
752             i = mo.start('invalid')
753             lines = self.template[:i].splitlines(True)
754             if not lines:
755                 colno = 1
756                 lineno = 1
757             else:
758                 colno = i - len(''.join(lines[:-1]))
759                 lineno = len(lines)
760             raise ValueError('Invalid placeholder in string: line %d, col %d' %
761                              (lineno, colno))
762
763         def substitute(self, *args, **kws):
764             if len(args) > 1:
765                 raise TypeError('Too many positional arguments')
766             if not args:
767                 mapping = kws
768             elif kws:
769                 mapping = _multimap(kws, args[0])
770             else:
771                 mapping = args[0]
772             # Helper function for .sub()
773             def convert(mo):
774                 # Check the most common path first.
775                 named = mo.group('named') or mo.group('braced')
776                 if named is not None:
777                     val = mapping[named]
778                     # We use this idiom instead of str() because the latter will
779                     # fail if val is a Unicode containing non-ASCII characters.
780                     return '%s' % val
781                 if mo.group('escaped') is not None:
782                     return self.delimiter
783                 if mo.group('invalid') is not None:
784                     self._invalid(mo)
785                 raise ValueError('Unrecognized named group in pattern',
786                                  self.pattern)
787             return self.pattern.sub(convert, self.template)
788
789
790         def safe_substitute(self, *args, **kws):
791             if len(args) > 1:
792                 raise TypeError('Too many positional arguments')
793             if not args:
794                 mapping = kws
795             elif kws:
796                 mapping = _multimap(kws, args[0])
797             else:
798                 mapping = args[0]
799             # Helper function for .sub()
800             def convert(mo):
801                 named = mo.group('named')
802                 if named is not None:
803                     try:
804                         # We use this idiom instead of str() because the latter
805                         # will fail if val is a Unicode containing non-ASCII
806                         return '%s' % mapping[named]
807                     except KeyError:
808                         return self.delimiter + named
809                 braced = mo.group('braced')
810                 if braced is not None:
811                     try:
812                         return '%s' % mapping[braced]
813                     except KeyError:
814                         return self.delimiter + '{' + braced + '}'
815                 if mo.group('escaped') is not None:
816                     return self.delimiter
817                 if mo.group('invalid') is not None:
818                     return self.delimiter
819                 raise ValueError('Unrecognized named group in pattern',
820                                  self.pattern)
821             return self.pattern.sub(convert, self.template)
822
823
824
825 #cat = lambda _ : Template(_).substitute(sys._getframe(1).f_globals, **sys._getframe(1).f_locals)
826
827 def cat(s):
828     globals = sys._getframe(1).f_globals.copy()
829     if 'self' in globals:
830         del globals['self']
831
832     locals = sys._getframe(1).f_locals.copy()
833     if 'self' in locals:
834         del locals['self']
835
836     return Template(s).substitute(sys._getframe(1).f_globals, **locals)
837
838
839 identity = string.maketrans('','')
840 unprintable = identity.translate(identity, string.printable)
841
842
843 def printable(s):
844     return s.translate(identity, unprintable)
845
846
847 def any(S,f=lambda x:x):
848     for x in S:
849         if f(x): return True
850     return False
851
852
853 def all(S,f=lambda x:x):
854     for x in S:
855         if not f(x): return False
856     return True
857
858
859 BROWSERS = ['firefox', 'mozilla', 'konqueror', 'galeon', 'skipstone'] # in preferred order
860 BROWSER_OPTS = {'firefox': '-new-window', 'mozilla' : '', 'konqueror': '', 'galeon': '-w', 'skipstone': ''}
861
862
863 def find_browser():
864     if platform_avail and platform.system() == 'Darwin':
865         return "open"
866     else:
867         for b in BROWSERS:
868             if which(b):
869                 return b
870         else:
871             return None
872
873
874 def openURL(url, use_browser_opts=True):
875     if platform_avail and platform.system() == 'Darwin':
876         cmd = 'open "%s"' % url
877         log.debug(cmd)
878         os.system(cmd)
879     else:
880         for b in BROWSERS:
881             bb = which(b)
882             if bb:
883                 bb = os.path.join(bb, b)
884                 if use_browser_opts:
885                     cmd = """%s %s "%s" &""" % (bb, BROWSER_OPTS[b], url)
886                 else:
887                     cmd = """%s "%s" &""" % (bb, url)
888                 log.debug(cmd)
889                 os.system(cmd)
890                 break
891         else:
892             log.warn("Unable to open URL: %s" % url)
893
894
895 def uniqueList(input):
896     temp = []
897     [temp.append(i) for i in input if not temp.count(i)]
898     return temp
899
900
901 def list_move_up(l, m, cmp=None):
902     if cmp is None:
903         f = lambda x: l[x] == m
904     else:
905         f = lambda x: cmp(l[x], m)
906
907     for i in range(1, len(l)):
908         if f(i):
909             l[i-1], l[i] = l[i], l[i-1]
910
911
912 def list_move_down(l, m, cmp=None):
913     if cmp is None:
914         f = lambda x: l[x] == m
915     else:
916         f = lambda x: cmp(l[x], m)
917
918     for i in range(len(l)-2, -1, -1):
919         if f(i):
920             l[i], l[i+1] = l[i+1], l[i]
921
922
923
924 class XMLToDictParser:
925     def __init__(self):
926         self.stack = []
927         self.data = {}
928         self.last_start = ''
929
930     def startElement(self, name, attrs):
931         #print "START:", name, attrs
932         self.stack.append(unicode(name).lower())
933         self.last_start = unicode(name).lower()
934
935         if len(attrs):
936             for a in attrs:
937                 self.stack.append(unicode(a).lower())
938                 self.addData(attrs[a])
939                 self.stack.pop()
940
941     def endElement(self, name):
942         if name.lower() == self.last_start:
943             self.addData('')
944
945         #print "END:", name
946         self.stack.pop()
947
948     def charData(self, data):
949         data = unicode(data).strip()
950
951         if data and self.stack:
952             self.addData(data)
953
954     def addData(self, data):
955         #print "DATA:", data
956         self.last_start = ''
957         try:
958             data = int(data)
959         except ValueError:
960             data = unicode(data)
961
962         stack_str = '-'.join(self.stack)
963         stack_str_0 = '-'.join([stack_str, '0'])
964
965         try:
966             self.data[stack_str]
967         except KeyError:
968             try:
969                 self.data[stack_str_0]
970             except KeyError:
971                 self.data[stack_str] = data
972             else:
973                 j = 2
974                 while True:
975                     try:
976                         self.data['-'.join([stack_str, unicode(j)])]
977                     except KeyError:
978                         self.data['-'.join([stack_str, unicode(j)])] = data
979                         break
980                     j += 1
981
982         else:
983             self.data[stack_str_0] = self.data[stack_str]
984             self.data['-'.join([stack_str, '1'])] = data
985             del self.data[stack_str]
986
987
988     def parseXML(self, text):
989         parser = expat.ParserCreate()
990         parser.StartElementHandler = self.startElement
991         parser.EndElementHandler = self.endElement
992         parser.CharacterDataHandler = self.charData
993         parser.Parse(text.encode('utf-8'), True)
994         return self.data
995
996
997 def dquote(s):
998     return ''.join(['"', s, '"'])
999
1000
1001 # Python 2.2.x compatibility functions (strip() family with char argument added in Python 2.2.3)
1002 if sys.hexversion < 0x020203f0:
1003     def xlstrip(s, chars=' '):
1004         i = 0
1005         for c, i in zip(s, range(len(s))):
1006             if c not in chars:
1007                 break
1008
1009         return s[i:]
1010
1011     def xrstrip(s, chars=' '):
1012         return xreverse(xlstrip(xreverse(s), chars))
1013
1014     def xreverse(s):
1015         l = list(s)
1016         l.reverse()
1017         return ''.join(l)
1018
1019     def xstrip(s, chars=' '):
1020         return xreverse(xlstrip(xreverse(xlstrip(s, chars)), chars))
1021
1022 else:
1023     xlstrip = string.lstrip
1024     xrstrip = string.rstrip
1025     xstrip = string.strip
1026
1027
1028 def getBitness():
1029     if platform_avail:
1030         return int(platform.architecture()[0][:-3])
1031     else:
1032         return struct.calcsize("P") << 3
1033
1034
1035 def getProcessor():
1036     if platform_avail:
1037         return platform.machine().replace(' ', '_').lower() # i386, i686, power_macintosh, etc.
1038     else:
1039         return "i686" # TODO: Need a fix here
1040
1041
1042 def getEndian():
1043     if sys.byteorder == 'big':
1044         return BIG_ENDIAN
1045     else:
1046         return LITTLE_ENDIAN
1047
1048
1049 def get_password():
1050     return getpass.getpass("Enter password: ")
1051
1052 def get_password_ui(pswd_msg=''):
1053     fp = open("/etc/hp/hplip.conf", "r")
1054     qt = "qt3"
1055     for line in fp:
1056         if string.find(line, "qt4") is not -1 and string.find(line, "yes") is not -1:
1057             qt = "qt4"
1058     fp.close()
1059     if qt is "qt4":
1060         from ui4.setupdialog import showPasswordUI
1061         if pswd_msg == '':
1062             username, password = showPasswordUI("Your HP Device requires to install HP proprietary plugin\nPlease enter root/superuser password to continue", "root", False)
1063         else:
1064             username, password = showPasswordUI(pswd_msg, "root", False)
1065     if qt is "qt3":
1066         from ui.setupform import showPasswordUI
1067         if pswd_msg == '':
1068             username, password = showPasswordUI("Your HP Device requires to install HP proprietary plugin\nPlease enter root/superuser password to continue", "root", False)
1069         else:
1070             username, password = showPasswordUI(pswd_msg, "root", False)
1071     return  password
1072
1073 def run(cmd, log_output=True, password_func=get_password, timeout=1, spinner=True, pswd_msg=''):
1074     output = cStringIO.StringIO()
1075
1076     try:
1077         child = pexpect.spawn(cmd, timeout=timeout)
1078     except pexpect.ExceptionPexpect:
1079         return -1, ''
1080
1081     try:
1082         while True:
1083             if spinner:
1084                 update_spinner()
1085
1086             i = child.expect(["[pP]assword:", pexpect.EOF, pexpect.TIMEOUT])
1087
1088             if child.before:
1089                 output.write(child.before)
1090                 if log_output:
1091                     log.debug(child.before)
1092
1093             if i == 0: # Password:
1094                 if password_func is not None:
1095                     if password_func == "get_password_ui":
1096                         child.sendline(get_password_ui(pswd_msg))
1097                     else:
1098                         child.sendline(password_func())
1099                 else:
1100                     child.sendline(get_password())
1101                 
1102  
1103             elif i == 1: # EOF
1104                 break
1105
1106             elif i == 2: # TIMEOUT
1107                 continue
1108
1109
1110     except Exception, e:
1111         log.error("Exception: %s" % e)
1112     if spinner:
1113         cleanup_spinner()
1114     child.close()
1115
1116     return child.exitstatus, output.getvalue()
1117
1118
1119 def expand_range(ns): # ns -> string repr. of numeric range, e.g. "1-4, 7, 9-12"
1120     """Credit: Jean Brouwers, comp.lang.python 16-7-2004
1121        Convert a string representation of a set of ranges into a
1122        list of ints, e.g.
1123        u"1-4, 7, 9-12" --> [1,2,3,4,7,9,10,11,12]
1124     """
1125     fs = []
1126     for n in ns.split(u','):
1127         n = n.strip()
1128         r = n.split('-')
1129         if len(r) == 2:  # expand name with range
1130             h = r[0].rstrip(u'0123456789')  # header
1131             r[0] = r[0][len(h):]
1132              # range can't be empty
1133             if not (r[0] and r[1]):
1134                 raise ValueError, 'empty range: ' + n
1135              # handle leading zeros
1136             if r[0] == u'0' or r[0][0] != u'0':
1137                 h += '%d'
1138             else:
1139                 w = [len(i) for i in r]
1140                 if w[1] > w[0]:
1141                    raise ValueError, 'wide range: ' + n
1142                 h += u'%%0%dd' % max(w)
1143              # check range
1144             r = [int(i, 10) for i in r]
1145             if r[0] > r[1]:
1146                raise ValueError, 'bad range: ' + n
1147             for i in range(r[0], r[1]+1):
1148                 fs.append(h % i)
1149         else:  # simple name
1150             fs.append(n)
1151
1152      # remove duplicates
1153     fs = dict([(n, i) for i, n in enumerate(fs)]).keys()
1154      # convert to ints and sort
1155     fs = [int(x) for x in fs if x]
1156     fs.sort()
1157
1158     return fs
1159
1160
1161 def collapse_range(x): # x --> sorted list of ints
1162     """ Convert a list of integers into a string
1163         range representation:
1164         [1,2,3,4,7,9,10,11,12] --> u"1-4,7,9-12"
1165     """
1166     if not x:
1167         return ''
1168
1169     s, c, r = [str(x[0])], x[0], False
1170
1171     for i in x[1:]:
1172         if i == (c+1):
1173             r = True
1174         else:
1175             if r:
1176                 s.append(u'-%s,%s' % (c,i))
1177                 r = False
1178             else:
1179                 s.append(u',%s' % i)
1180
1181         c = i
1182
1183     if r:
1184         s.append(u'-%s' % i)
1185
1186     return ''.join(s)
1187
1188
1189 def createSequencedFilename(basename, ext, dir=None, digits=3):
1190     if dir is None:
1191         dir = os.getcwd()
1192
1193     m = 0
1194     for f in walkFiles(dir, recurse=False, abs_paths=False, return_folders=False, pattern='*', path=None):
1195         r, e = os.path.splitext(f)
1196
1197         if r.startswith(basename) and ext == e:
1198             try:
1199                 i = int(r[len(basename):])
1200             except ValueError:
1201                 continue
1202             else:
1203                 m = max(m, i)
1204
1205     return os.path.join(dir, "%s%0*d%s" % (basename, digits, m+1, ext))
1206
1207
1208 def validate_language(lang, default='en_US'):
1209     if lang is None:
1210         loc, encoder = locale.getdefaultlocale()
1211     else:
1212         lang = lang.lower().strip()
1213         for loc, ll in supported_locales.items():
1214             if lang in ll:
1215                 break
1216         else:
1217             loc = 'en_US'
1218             log.warn("Unknown lang/locale. Using default of %s." % loc)
1219
1220     return loc
1221
1222
1223 def gen_random_uuid():
1224     try:
1225         import uuid # requires Python 2.5+
1226         return str(uuid.uuid4())
1227
1228     except ImportError:
1229         uuidgen = which("uuidgen")
1230         if uuidgen:
1231             uuidgen = os.path.join(uuidgen, "uuidgen")
1232             return commands.getoutput(uuidgen) # TODO: Replace with subprocess (commands is deprecated in Python 3.0)
1233         else:
1234             return ''
1235
1236
1237 class RestTableFormatter(object):
1238     def __init__(self, header=None):
1239         self.header = header # tuple of strings
1240         self.rows = [] # list of tuples
1241
1242     def add(self, row_data): # tuple of strings
1243         self.rows.append(row_data)
1244
1245     def output(self, w):
1246         if self.rows:
1247             num_cols = len(self.rows[0])
1248             for r in self.rows:
1249                 if len(r) != num_cols:
1250                     log.error("Invalid number of items in row: %s" % r)
1251                     return
1252
1253             if len(self.header) != num_cols:
1254                 log.error("Invalid number of items in header.")
1255
1256             col_widths = []
1257             for x, c in enumerate(self.header):
1258                 max_width = len(c)
1259                 for r in self.rows:
1260                     max_width = max(max_width, len(r[x]))
1261
1262                 col_widths.append(max_width+2)
1263
1264             x = '+'
1265             for c in col_widths:
1266                 x = ''.join([x, '-' * (c+2), '+'])
1267
1268             x = ''.join([x, '\n'])
1269             w.write(x)
1270
1271             # header
1272             if self.header:
1273                 x = '|'
1274                 for i, c in enumerate(col_widths):
1275                     x = ''.join([x, ' ', self.header[i], ' ' * (c+1-len(self.header[i])), '|'])
1276
1277                 x = ''.join([x, '\n'])
1278                 w.write(x)
1279
1280                 x = '+'
1281                 for c in col_widths:
1282                     x = ''.join([x, '=' * (c+2), '+'])
1283
1284                 x = ''.join([x, '\n'])
1285                 w.write(x)
1286
1287             # data rows
1288             for j, r in enumerate(self.rows):
1289                 x = '|'
1290                 for i, c in enumerate(col_widths):
1291                     x = ''.join([x, ' ', self.rows[j][i], ' ' * (c+1-len(self.rows[j][i])), '|'])
1292
1293                 x = ''.join([x, '\n'])
1294                 w.write(x)
1295
1296                 x = '+'
1297                 for c in col_widths:
1298                     x = ''.join([x, '-' * (c+2), '+'])
1299
1300                 x = ''.join([x, '\n'])
1301                 w.write(x)
1302
1303         else:
1304             log.error("No data rows")
1305
1306
1307 def mixin(cls):
1308     import inspect
1309
1310     locals = inspect.stack()[1][0].f_locals
1311     if "__module__" not in locals:
1312         raise TypeError("Must call mixin() from within class def.")
1313
1314     dict = cls.__dict__.copy()
1315     dict.pop("__doc__", None)
1316     dict.pop("__module__", None)
1317
1318     locals.update(dict)
1319
1320
1321
1322 # TODO: Move usage stuff to to base/module/Module class
1323
1324
1325  # ------------------------- Usage Help
1326 USAGE_OPTIONS = ("[OPTIONS]", "", "heading", False)
1327 USAGE_LOGGING1 = ("Set the logging level:", "-l<level> or --logging=<level>", 'option', False)
1328 USAGE_LOGGING2 = ("", "<level>: none, info\*, error, warn, debug (\*default)", "option", False)
1329 USAGE_LOGGING3 = ("Run in debug mode:", "-g (same as option: -ldebug)", "option", False)
1330 USAGE_LOGGING_PLAIN = ("Output plain text only:", "-t", "option", False)
1331 USAGE_ARGS = ("[PRINTER|DEVICE-URI]", "", "heading", False)
1332 USAGE_ARGS2 = ("[PRINTER]", "", "heading", False)
1333 USAGE_DEVICE = ("To specify a device-URI:", "-d<device-uri> or --device=<device-uri>", "option", False)
1334 USAGE_PRINTER = ("To specify a CUPS printer:", "-p<printer> or --printer=<printer>", "option", False)
1335 USAGE_BUS1 = ("Bus to probe (if device not specified):", "-b<bus> or --bus=<bus>", "option", False)
1336 USAGE_BUS2 = ("", "<bus>: cups\*, usb\*, net, bt, fw, par\* (\*defaults) (Note: bt and fw not supported in this release.)", 'option', False)
1337 USAGE_HELP = ("This help information:", "-h or --help", "option", True)
1338 USAGE_SPACE = ("", "", "space", False)
1339 USAGE_EXAMPLES = ("Examples:", "", "heading", False)
1340 USAGE_NOTES = ("Notes:", "", "heading", False)
1341 USAGE_STD_NOTES1 = ("If device or printer is not specified, the local device bus is probed and the program enters interactive mode.", "", "note", False)
1342 USAGE_STD_NOTES2 = ("If -p\* is specified, the default CUPS printer will be used.", "", "note", False)
1343 USAGE_SEEALSO = ("See Also:", "", "heading", False)
1344 USAGE_LANGUAGE = ("Set the language:", "-q <lang> or --lang=<lang>. Use -q? or --lang=? to see a list of available language codes.", "option", False)
1345 USAGE_LANGUAGE2 = ("Set the language:", "--lang=<lang>. Use --lang=? to see a list of available language codes.", "option", False)
1346 USAGE_MODE = ("[MODE]", "", "header", False)
1347 USAGE_NON_INTERACTIVE_MODE = ("Run in non-interactive mode:", "-n or --non-interactive", "option", False)
1348 USAGE_GUI_MODE = ("Run in graphical UI mode:", "-u or --gui (Default)", "option", False)
1349 USAGE_INTERACTIVE_MODE = ("Run in interactive mode:", "-i or --interactive", "option", False)
1350
1351 if sys_conf.get('configure', 'ui-toolkit', 'qt3') == 'qt3':
1352     USAGE_USE_QT3 = ("Use Qt3:",  "--qt3 (Default)",  "option",  False)
1353     USAGE_USE_QT4 = ("Use Qt4:",  "--qt4",  "option",  False)
1354 else:
1355     USAGE_USE_QT3 = ("Use Qt3:",  "--qt3",  "option",  False)
1356     USAGE_USE_QT4 = ("Use Qt4:",  "--qt4 (Default)",  "option",  False)
1357
1358
1359
1360
1361 def ttysize(): # TODO: Move to base/tui
1362     ln1 = commands.getoutput('stty -a').splitlines()[0]
1363     vals = {'rows':None, 'columns':None}
1364     for ph in ln1.split(';'):
1365         x = ph.split()
1366         if len(x) == 2:
1367             vals[x[0]] = x[1]
1368             vals[x[1]] = x[0]
1369     try:
1370         rows, cols = int(vals['rows']), int(vals['columns'])
1371     except TypeError:
1372         rows, cols = 25, 80
1373
1374     return rows, cols
1375
1376
1377 def usage_formatter(override=0): # TODO: Move to base/module/Module class
1378     rows, cols = ttysize()
1379
1380     if override:
1381         col1 = override
1382         col2 = cols - col1 - 8
1383     else:
1384         col1 = int(cols / 3) - 8
1385         col2 = cols - col1 - 8
1386
1387     return TextFormatter(({'width': col1, 'margin' : 2},
1388                             {'width': col2, 'margin' : 2},))
1389
1390
1391 def format_text(text_list, typ='text', title='', crumb='', version=''): # TODO: Move to base/module/Module class
1392     """
1393     Format usage text in multiple formats:
1394         text: for --help in the console
1395         rest: for conversion with rst2web for the website
1396         man: for manpages
1397     """
1398     if typ == 'text':
1399         formatter = usage_formatter()
1400
1401         for line in text_list:
1402             text1, text2, format, trailing_space = line
1403
1404             # remove any reST/man escapes
1405             text1 = text1.replace("\\", "")
1406             text2 = text2.replace("\\", "")
1407
1408             if format == 'summary':
1409                 log.info(log.bold(text1))
1410                 log.info("")
1411
1412             elif format in ('para', 'name', 'seealso'):
1413                 log.info(text1)
1414
1415                 if trailing_space:
1416                     log.info("")
1417
1418             elif format in ('heading', 'header'):
1419                 log.info(log.bold(text1))
1420
1421             elif format in ('option', 'example'):
1422                 log.info(formatter.compose((text1, text2), trailing_space))
1423
1424             elif format == 'note':
1425                 if text1.startswith(' '):
1426                     log.info('\t' + text1.lstrip())
1427                 else:
1428                     log.info(text1)
1429
1430             elif format == 'space':
1431                 log.info("")
1432
1433         log.info("")
1434
1435
1436     elif typ == 'rest':
1437         opt_colwidth1, opt_colwidth2 = 0, 0
1438         exmpl_colwidth1, exmpl_colwidth2 = 0, 0
1439         note_colwidth1, note_colwidth2 = 0, 0
1440
1441         for line in text_list:
1442             text1, text2, format, trailing_space = line
1443
1444             if format  == 'option':
1445                 opt_colwidth1 = max(len(text1), opt_colwidth1)
1446                 opt_colwidth2 = max(len(text2), opt_colwidth2)
1447
1448             elif format == 'example':
1449                 exmpl_colwidth1 = max(len(text1), exmpl_colwidth1)
1450                 exmpl_colwidth2 = max(len(text2), exmpl_colwidth2)
1451
1452             elif format == 'note':
1453                 note_colwidth1 = max(len(text1), note_colwidth1)
1454                 note_colwidth2 = max(len(text2), note_colwidth2)
1455
1456         opt_colwidth1 += 4
1457         opt_colwidth2 += 4
1458         exmpl_colwidth1 += 4
1459         exmpl_colwidth2 += 4
1460         note_colwidth1 += 4
1461         note_colwidth2 += 4
1462         opt_tablewidth = opt_colwidth1 + opt_colwidth2
1463         exmpl_tablewidth = exmpl_colwidth1 + exmpl_colwidth2
1464         note_tablewidth = note_colwidth1 + note_colwidth2
1465
1466         # write the rst2web header
1467         log.info("""restindex
1468 page-title: %s
1469 crumb: %s
1470 format: rest
1471 file-extension: html
1472 encoding: utf8
1473 /restindex\n""" % (title, crumb))
1474
1475         t = "%s: %s (ver. %s)" % (crumb, title, version)
1476         log.info(t)
1477         log.info("="*len(t))
1478         log.info("")
1479
1480         links = []
1481         needs_header = False
1482         for line in text_list:
1483             text1, text2, format, trailing_space = line
1484
1485             if format == 'seealso':
1486                 links.append(text1)
1487                 text1 = "`%s`_" % text1
1488
1489             len1, len2 = len(text1), len(text2)
1490
1491             if format == 'summary':
1492                 log.info(''.join(["**", text1, "**"]))
1493                 log.info("")
1494
1495             elif format in ('para', 'name'):
1496                 log.info("")
1497                 log.info(text1)
1498                 log.info("")
1499
1500             elif format in ('heading', 'header'):
1501
1502                 log.info("")
1503                 log.info("**" + text1 + "**")
1504                 log.info("")
1505                 needs_header = True
1506
1507             elif format == 'option':
1508                 if needs_header:
1509                     log.info(".. class:: borderless")
1510                     log.info("")
1511                     log.info(''.join(["+", "-"*opt_colwidth1, "+", "-"*opt_colwidth2, "+"]))
1512                     needs_header = False
1513
1514                 if text1 and '`_' not in text1:
1515                     log.info(''.join(["| *", text1, '*', " "*(opt_colwidth1-len1-3), "|", text2, " "*(opt_colwidth2-len2), "|"]))
1516                 elif text1:
1517                     log.info(''.join(["|", text1, " "*(opt_colwidth1-len1), "|", text2, " "*(opt_colwidth2-len2), "|"]))
1518                 else:
1519                     log.info(''.join(["|", " "*(opt_colwidth1), "|", text2, " "*(opt_colwidth2-len2), "|"]))
1520
1521                 log.info(''.join(["+", "-"*opt_colwidth1, "+", "-"*opt_colwidth2, "+"]))
1522
1523             elif format == 'example':
1524                 if needs_header:
1525                     log.info(".. class:: borderless")
1526                     log.info("")
1527                     log.info(''.join(["+", "-"*exmpl_colwidth1, "+", "-"*exmpl_colwidth2, "+"]))
1528                     needs_header = False
1529
1530                 if text1 and '`_' not in text1:
1531                     log.info(''.join(["| *", text1, '*', " "*(exmpl_colwidth1-len1-3), "|", text2, " "*(exmpl_colwidth2-len2), "|"]))
1532                 elif text1:
1533                     log.info(''.join(["|", text1, " "*(exmpl_colwidth1-len1), "|", text2, " "*(exmpl_colwidth2-len2), "|"]))
1534                 else:
1535                     log.info(''.join(["|", " "*(exmpl_colwidth1), "|", text2, " "*(exmpl_colwidth2-len2), "|"]))
1536
1537                 log.info(''.join(["+", "-"*exmpl_colwidth1, "+", "-"*exmpl_colwidth2, "+"]))
1538
1539             elif format == 'seealso':
1540                 if text1 and '`_' not in text1:
1541                     log.info(text1)
1542
1543
1544             elif format == 'note':
1545                 if needs_header:
1546                     log.info(".. class:: borderless")
1547                     log.info("")
1548                     log.info(''.join(["+", "-"*note_colwidth1, "+", "-"*note_colwidth2, "+"]))
1549                     needs_header = False
1550
1551                 if text1.startswith(' '):
1552                     log.info(''.join(["|", " "*(note_tablewidth+1), "|"]))
1553
1554                 log.info(''.join(["|", text1, " "*(note_tablewidth-len1+1), "|"]))
1555                 log.info(''.join(["+", "-"*note_colwidth1, "+", "-"*note_colwidth2, "+"]))
1556
1557             elif format == 'space':
1558                 log.info("")
1559
1560         for l in links:
1561             log.info("\n.. _`%s`: %s.html\n" % (l, l.replace('hp-', '')))
1562
1563         log.info("")
1564
1565     elif typ == 'man':
1566         log.info('.TH "%s" 1 "%s" Linux "User Manuals"' % (crumb, version))
1567         log.info(".SH NAME\n%s \- %s" % (crumb, title))
1568
1569         for line in text_list:
1570             text1, text2, format, trailing_space = line
1571
1572             text1 = text1.replace("\\*", "*")
1573             text2 = text2.replace("\\*", "*")
1574
1575             len1, len2 = len(text1), len(text2)
1576
1577             if format == 'summary':
1578                 log.info(".SH SYNOPSIS")
1579                 log.info(".B %s" % text1.replace('Usage:', ''))
1580
1581             elif format == 'name':
1582                 log.info(".SH DESCRIPTION\n%s" % text1)
1583
1584             elif format in ('option', 'example', 'note'):
1585                 if text1:
1586                     log.info('.IP "%s"\n%s' % (text1, text2))
1587                 else:
1588                     log.info(text2)
1589
1590             elif format in ('header', 'heading'):
1591                 log.info(".SH %s" % text1.upper().replace(':', '').replace('[', '').replace(']', ''))
1592
1593             elif format in ('seealso, para'):
1594                 log.info(text1)
1595
1596         log.info(".SH AUTHOR")
1597         log.info("HPLIP (Hewlett-Packard Linux Imaging and Printing) is an")
1598         log.info("HP developed solution for printing, scanning, and faxing with")
1599         log.info("HP inkjet and laser based printers in Linux.")
1600
1601         log.info(".SH REPORTING BUGS")
1602         log.info("The HPLIP Launchpad.net site")
1603         log.info(".B https://launchpad.net/hplip")
1604         log.info("is available to get help, report")
1605         log.info("bugs, make suggestions, discuss the HPLIP project or otherwise")
1606         log.info("contact the HPLIP Team.")
1607
1608         log.info(".SH COPYRIGHT")
1609         log.info("Copyright (c) 2001-14 Hewlett-Packard Development Company, L.P.")
1610         log.info(".LP")
1611         log.info("This software comes with ABSOLUTELY NO WARRANTY.")
1612         log.info("This is free software, and you are welcome to distribute it")
1613         log.info("under certain conditions. See COPYING file for more details.")
1614
1615         log.info("")
1616
1617
1618 def log_title(program_name, version, show_ver=True): # TODO: Move to base/module/Module class
1619     log.info("")
1620
1621     if show_ver:
1622         log.info(log.bold("HP Linux Imaging and Printing System (ver. %s)" % prop.version))
1623     else:
1624         log.info(log.bold("HP Linux Imaging and Printing System"))
1625
1626     log.info(log.bold("%s ver. %s" % (program_name, version)))
1627     log.info("")
1628     log.info("Copyright (c) 2001-14 Hewlett-Packard Development Company, LP")
1629     log.info("This software comes with ABSOLUTELY NO WARRANTY.")
1630     log.info("This is free software, and you are welcome to distribute it")
1631     log.info("under certain conditions. See COPYING file for more details.")
1632     log.info("")
1633
1634
1635 def ireplace(old, search, replace):
1636     regex = '(?i)' + re.escape(search)
1637     return re.sub(regex, replace, old)
1638
1639
1640 def su_sudo():
1641     su_sudo_str = None
1642
1643     if which('kdesu'):
1644         su_sudo_str = 'kdesu -- %s'
1645
1646     elif which('gnomesu'):
1647         su_sudo_str = 'gnomesu -c "%s"'
1648
1649     elif which('gksu'):
1650         su_sudo_str = 'gksu "%s"'
1651     
1652     elif which('su'):
1653         su_sudo_str = 'su'
1654
1655     return su_sudo_str
1656
1657 # This function returns the distro name and distro version. 
1658 #This is provided to check on Fedora 14 in pkit.py file for Plugin-installation. 
1659 #is_su variable is used to provide a check on Fedora 8 
1660 def os_release():
1661     os_name = None;
1662     os_version = None;
1663     is_su = None;
1664     if which('lsb_release'):
1665        name = os.popen('lsb_release -i | cut -f 2')
1666        os_name = name.read().strip()
1667        name.close()
1668        version = os.popen('lsb_release -r | cut -f 2')
1669        os_version=version.read().strip()
1670        version.close()
1671        is_su = True
1672     else:
1673        name = os.popen('cat /etc/issue | cut -c 1-6 | head -n 1')
1674        os_name = name.read().strip()
1675        name.close()
1676        version1=os.popen('cat /etc/issue | cut -c 16 | head -n 1')
1677        version2=version1.read().strip()
1678        version1.close()
1679        if (version2 == '1'):
1680            version=os.popen('cat /etc/issue | cut -c 16-17 | head -n 1')
1681            is_su = True
1682        else: 
1683            version=os.popen('cat /etc/issue | cut -c 16 | head -n 1')
1684            is_su = False
1685        os_version=version.read().strip()
1686        version.close()
1687
1688     return os_name,os_version,is_su 
1689     
1690
1691 #
1692 # Removes HTML or XML character references and entities from a text string.
1693 #
1694
1695 def unescape(text):
1696     def fixup(m):
1697         text = m.group(0)
1698         if text[:2] == "&#":
1699             # character reference
1700             try:
1701                 if text[:3] == "&#x":
1702                     #return unichr(int(text[3:-1], 16))
1703                     return chr(int(text[3:-1], 16))
1704                 else:
1705                     #return unichr(int(text[2:-1]))
1706                     return chr(int(text[2:-1]))
1707             except ValueError:
1708                 pass
1709         else:
1710             # named entity
1711             try:
1712                 #text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
1713                 text = chr(htmlentitydefs.name2codepoint[text[1:-1]])
1714             except KeyError:
1715                 pass
1716         return text # leave as is
1717     return re.sub("&#?\w+;", fixup, text)
1718
1719
1720 # Adds HTML or XML character references and entities from a text string
1721
1722 def escape(s):
1723     if not isinstance(s, unicode):
1724         s = unicode(s) # hmmm...
1725
1726     s = s.replace(u"&", u"&amp;")
1727
1728     for c in htmlentitydefs.codepoint2name:
1729         if c != 0x26: # exclude &
1730             s = s.replace(unichr(c), u"&%s;" % htmlentitydefs.codepoint2name[c])
1731
1732     for c in range(0x20) + range(0x7f, 0xa0):
1733         s = s.replace(unichr(c), u"&#%d;" % c)
1734
1735     return s
1736
1737 # checks if given process is running.
1738 #return value:
1739 #    True or False
1740 #    None - if process is not running
1741 #    grep output - if process is running
1742
1743 def Is_Process_Running(process_name):
1744     try:
1745         p1 = Popen(["ps", "aux"], stdout=PIPE)
1746         p2 = Popen(["grep", process_name], stdin=p1.stdout, stdout=PIPE)
1747         p3 = Popen(["grep", "-v", "grep"], stdin=p2.stdout, stdout=PIPE)
1748         output = p3.communicate()[0]
1749         log.debug("Is_Process_Running outpu = %s " %output)
1750
1751         if process_name in output:
1752             return True, output
1753         else:
1754             return False, None
1755
1756     except Exception, e:
1757         log.error("Execution failed: process Name[%s]" %process_name)
1758         print >>sys.stderr, "Execution failed:", e
1759         return False, None
1760
1761 #return tye: strings
1762 #Return values.
1763 #   None --> on error.
1764 #  "terminal name"-->success
1765 def get_terminal():
1766     terminal_list=['gnome-terminal', 'konsole','x-terminal-emulator', 'xterm', 'gtkterm']
1767     cnt = 0
1768     terminal_cmd = None
1769     while cnt < len(terminal_list):
1770         if which(terminal_list[cnt]):
1771             terminal_cmd = terminal_list[cnt]+" -e "
1772             log.debug("Available Terminal = %s " %terminal_cmd)
1773             break
1774             
1775     return terminal_cmd
1776
1777 #Return Type: bool
1778 # Return values:
1779 #      True --> if it is older version
1780 #      False  --> if it is same or later version.
1781
1782 def Is_HPLIP_older_version(installed_version, available_version):
1783     
1784     if available_version == "" or available_version == None or installed_version == "" or installed_version == None:
1785         log.debug("available_version is ''")
1786         return False
1787
1788     installed_array=installed_version.split('.')
1789     available_array=available_version.split('.')
1790
1791     log.debug("HPLIP Installed_version=%s  Available_version=%s"%(installed_version,available_version))
1792     cnt = 0
1793     Is_older = False
1794     while cnt <len(installed_array) and cnt <len(available_array):
1795         if(int(installed_array[cnt]) < int(available_array[cnt])):
1796             Is_older = True
1797             break
1798         elif(int(installed_array[cnt]) > int(available_array[cnt])):
1799             log.debug("Already new verison is installed")
1800             return False
1801         cnt += 1
1802
1803     # To check internal version is installed.
1804     if Is_older is False and len(installed_array) >len(available_array):
1805         Is_older = True
1806
1807     return Is_older
1808
1809
1810 def downLoad_status(count, blockSize, totalSize):
1811     percent = int(count*blockSize*100/totalSize)
1812     if count != 0:
1813         sys.stdout.write("\b\b\b")
1814     sys.stdout.write("%s" %(log.color("%2d%%"%percent, 'bold')))
1815     sys.stdout.flush()
1816
1817
1818 def download_from_network(weburl, outputFile = None, useURLLIB=False):
1819     result =False
1820
1821     if weburl is "" or weburl is None:
1822         log.error("URL is empty")
1823         return result, ""
1824
1825     if outputFile is None:
1826         fp, outputFile = make_temp_file()
1827
1828     try:
1829         if useURLLIB is False:
1830             wget = which("wget")
1831             if wget:
1832                 wget = os.path.join(wget, "wget")
1833                 status, output = run("%s --cache=off --timeout=60 --output-document=%s %s" %(wget, outputFile, weburl))
1834                 if status:
1835                     log.error("Failed to connect to HPLIP site. Error code = %d" %status)
1836                     return False, ""
1837             else:
1838                 useURLLIB = True
1839
1840         if useURLLIB:
1841             sys.stdout.write("Download in progress...")
1842             urllib.urlretrieve(weburl, outputFile, downLoad_status)
1843
1844     except IOError, e:
1845         log.error("I/O Error: %s" % e.strerror)
1846         return False, ""
1847
1848     if not os.path.exists(outputFile):
1849         log.error("Failed to get hplip version/ %s file not found."%hplip_version_file)
1850         return False, ""
1851
1852     return True, outputFile
1853
1854
1855
1856
1857     
1858 class Sync_Lock:
1859     def __init__(self, filename):
1860         self.Lock_filename = filename
1861         self.handler = open(self.Lock_filename, 'w')
1862
1863 # Wait for another process to release resource and acquires the resource.
1864     def acquire(self):
1865         fcntl.flock(self.handler, fcntl.LOCK_EX)
1866
1867     def release(self):
1868         fcntl.flock(self.handler, fcntl.LOCK_UN)
1869
1870     def __del__(self):
1871         self.handler.close()