1 # -*- coding: utf-8 -*-
3 # (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
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.
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.
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
29 from base.codes import *
30 from base import device, utils, exif
35 if not os.getenv("HPLIP_BUILD"):
36 log.error("PCARDEXT could not be loaded. Please check HPLIP installation.")
39 # Photocard command codes
45 SECTOR_SIZE = 512 # don't change this (TODO: impl. in pcardext)
47 # Photocard sector cache
48 MAX_CACHE = 512 # units = no. sectors
50 # PhotoCardFile byte cache
52 INITIAL_PCARDFILE_BUFFER = 20*SECTOR_SIZE
53 INCREMENTAL_PCARDFILE_BUFFER = 2*SECTOR_SIZE
58 def __init__(self, pc, name=None):
63 self.buffer = array.array('c')
68 self.buffer_size = INITIAL_PCARDFILE_BUFFER
69 self.buffer.fromstring(pcardext.read(self.name, 0, self.buffer_size))
76 def seek(self, offset, whence=0):
82 self.pos = self.file_size - offset
93 if self.pos + size < self.buffer_size:
94 data = self.buffer[self.pos : self.pos + size].tostring()
98 # Read some more in from the card to satisfy the request
99 while self.pos + size >= self.buffer_size:
100 self.buffer.fromstring(pcardext.read(self.name, self.buffer_size, INCREMENTAL_PCARDFILE_BUFFER))
101 self.buffer_size += INCREMENTAL_PCARDFILE_BUFFER
102 return self.read(size)
112 def __init__(self, dev_obj=None, device_uri=None, printer_name=None):
115 self.device = device.Device(device_uri, printer_name)
117 self.close_device = True
119 self.device = dev_obj
120 self.close_device = False
122 self.dir_stack = utils.Stack()
123 self.current_dir = []
124 self.device_uri = self.device.device_uri
125 self.pcard_mounted = False
127 self.sector_buffer = {}
128 self.sector_buffer_counts = {}
129 self.cache_flag = True
130 self.write_protect = False
134 self.channel_opened = False
137 def START_OPERATION(self, name=''):
140 def END_OPERATION(self, name='', flag=True):
141 if self.channel_opened and flag:
144 def set_callback(self, callback):
145 self.callback = callback
147 def _read(self, sector, nsector):
148 log.debug("read pcard sector: sector=%d count=%d" % (sector, nsector))
151 for s in range(sector, sector+nsector):
152 if s not in self.sector_buffer:
156 for s in range(sector, sector+nsector):
157 buffer = ''.join([buffer, self.sector_buffer[s]])
158 log.debug("Cached sector read sector=%d" % s)
159 count = self.sector_buffer_counts[s]
160 self.sector_buffer_counts[s] = count+1
162 if self.callback is not None:
165 #log.log_data(buffer)
168 if self.callback is not None:
171 if not self.channel_opened:
174 log.debug("Normal sector read sector=%d count=%d" % (sector, nsector))
175 sectors_to_read = range(sector, sector+nsector)
176 request = struct.pack('!HH' + 'I'*nsector, READ_CMD, nsector, *sectors_to_read)
177 #log.log_data(request)
179 if self.callback is not None:
183 bytes_written = self.device.writePCard(request)
184 log.debug("%d bytes written" % bytes_written)
187 data = self.device.readPCard(2)
189 code = struct.unpack('!H', data)[0]
191 log.debug("Return code: %x" % code)
195 # read sector count and version
196 data = self.device.readPCard(6)
197 nsector_read, ver = struct.unpack('!IH', data)
199 log.debug("code=0x%x, nsector=%d, ver=%d" % (code, nsector_read, ver))
201 buffer, data_read, total_to_read = '', 0, nsector * SECTOR_SIZE
203 while (data_read < total_to_read):
204 data = self.device.readPCard(total_to_read)
206 data_read += len(data)
207 buffer = ''.join([buffer, data])
209 if self.callback is not None:
215 for s in range(sector, sector + nsector_read):
216 self.sector_buffer[s] = buffer[i : i+SECTOR_SIZE]
217 #log.debug("Sector %d data=\n%s" % (s, repr(self.sector_buffer[s])))
218 count = self.sector_buffer_counts.get(s, 0)
219 self.sector_buffer_counts[s] = count+1
222 if self.callback is not None:
225 self._check_cache(nsector)
227 #log.log_data(buffer)
230 log.error("Error code: %d" % code)
233 def _write(self, sector, nsector, buffer):
235 #log.debug("write pcard sector: sector=%d count=%d len=%d data=\n%s" % (sector, nsector, len(buffer), repr(buffer)))
236 log.debug("write pcard sector: sector=%d count=%d len=%d" % (sector, nsector, len(buffer)))
238 if not self.channel_opened:
242 sectors_to_write = range(sector, sector+nsector)
243 request = struct.pack('!HHH' + 'I'*nsector, WRITE_CMD, nsector, 0, *sectors_to_write)
244 request = ''.join([request, buffer])
246 if self.callback is not None:
249 self.device.writePCard(request)
250 data = self.device.readPCard(2)
252 if self.callback is not None:
255 code = struct.unpack('!H', data)[0]
260 for s in range(sector, sector+nsector):
261 log.debug("Caching sector %d" % sector)
262 self.sector_buffer[s] = buffer[i:i+SECTOR_SIZE]
263 self.sector_buffer_counts[s] = 1
266 if self.callback is not None:
269 self._check_cache(nsector)
275 for s in range(sector, sector+nsector):
277 del self.sector_buffer[s]
278 del self.sector_buffer_counts[s]
282 log.error("Photo card write failed (Card may be write protected)")
287 def _check_cache(self, nsector):
288 if len(self.sector_buffer) > MAX_CACHE:
289 # simple minded: scan for first nsector sectors that has count of 1 and throw it away
290 t, n = self.sector_buffer.keys()[:], 0
292 if self.sector_buffer_counts[s] == 1:
293 del self.sector_buffer[s]
294 del self.sector_buffer_counts[s]
298 if self.callback is not None:
303 def cache_info(self):
304 return self.sector_buffer_counts
306 def cache_check(self, sector):
307 return self.sector_buffer_counts.get(sector, 0)
309 def cache_control(self, control):
310 self.cache_flag = control
312 if not self.cache_flag:
315 def cache_state(self):
316 return self.cache_flag
318 def cache_reset(self):
319 self.sector_buffer.clear()
320 self.sector_buffer_counts.clear()
324 self.START_OPERATION('df')
328 self.END_OPERATION('df')
331 def ls(self, force_read=True, glob_list='*', openclose=True):
335 self.START_OPERATION('ls')
337 self.current_dir = pcardext.ls()
339 self.END_OPERATION('ls', openclose)
341 self.current_dir = [(n.lower(),a,s) for (n,a,s) in self.current_dir]
344 return self.current_dir
346 return [fnmatch.filter(self.current_dir, x) for x in glob_list.strip().lower().split()][0]
348 def size(self, name):
349 for f in self.current_dir:
351 return self.current_dir[f][2]
354 def current_files(self):
355 return [x for x in self.current_dir if x[1] != 'd']
357 def current_directories(self):
358 return [x for x in self.current_dir if x[1] == 'd']
360 def match_files(self, glob_list):
361 if len(glob_list) > 0:
362 current_files = [x[0] for x in self.current_files()]
363 return [fnmatch.filter(current_files, x) for x in glob_list.strip().lower().split()][0]
366 def match_dirs(self, glob_list):
367 if len(glob_list) > 0:
368 current_dirs = [x[0] for x in self.current_directories()]
369 return [fnmatch.filter(current_dirs, x) for x in glob_list.strip().lower().split()][0]
372 def classify_file(self, filename):
373 t = mimetypes.guess_type(filename)[0]
375 return 'unknown/unknown'
378 # copy a single file fom pwd to lpwd
379 def cp(self, name, local_file, openclose=True):
380 self.START_OPERATION('cp')
383 f = file(local_file, 'w');
384 total = pcardext.cp(name, f.fileno())
387 self.END_OPERATION('cp', openclose)
390 # cp multiple files in the current working directory
391 def cp_multiple(self, filelist, remove_after_copy, cp_status_callback=None, rm_status_callback=None):
393 self.START_OPERATION('cp_multiple')
398 size = self.cp(f, f, False)
400 if cp_status_callback:
401 cp_status_callback(os.path.join(self.pwd(), f), os.path.join(os.getcwd(), f), size)
406 if remove_after_copy:
412 if remove_after_copy:
413 self.ls(True, '*', False)
414 self.END_OPERATION('cp_multiple')
415 return (total, delta)
417 # cp multiple files with paths
418 def cp_list(self, filelist, remove_after_copy, cp_status_callback=None, rm_status_callback=None):
421 self.START_OPERATION('cp_list')
426 path_list = f.split('/')[:-1]
427 filename = f.split('/')[-1]
432 size = self.cp(filename, filename, False)
434 if cp_status_callback is not None:
435 cp_status_callback(f, os.path.join(os.getcwd(), filename), size)
439 if remove_after_copy:
440 pcardext.rm(filename)
442 if rm_status_callback is not None:
443 rm_status_callback(f)
450 #if remove_after_copy:
451 # self.ls( True, '*', False )
453 self.END_OPERATION('cp_list')
454 return (total, delta)
458 def cp_fd(self, name, fd):
460 self.START_OPERATION('cp_fd')
462 total = pcardext.cp(name, fd)
464 self.END_OPERATION('cp_fd')
468 def unload(self, unload_list, cp_status_callback=None, rm_status_callback=None, dont_remove=False):
469 was_cancelled = False
471 self.START_OPERATION('unload')
475 for f in unload_list:
476 if not was_cancelled:
477 name, size, typ, subtyp = f
484 if cp_status_callback is not None:
485 if cp_status_callback(os.path.join(self.pwd(), filename),
486 os.path.join(os.getcwd(), filename), 0):
494 if os.path.exists(os.path.join(os.getcwd(), filename)):
498 if not os.path.exists(os.path.join(os.getcwd(), filename + " (%d)" % i)):
503 total += self.cp(filename, filename + " (%d)" % i, False)
506 total += self.cp(filename, filename, False)
508 if cp_status_callback is not None:
509 if cp_status_callback(os.path.join(self.pwd(), filename),
510 os.path.join(os.getcwd(), filename), size):
515 if rm_status_callback is not None:
516 rm_status_callback(os.path.join(self.pwd(), filename))
518 self.rm(filename, False, False)
522 self.restore_wd(False)
523 self.ls(True, '*', False)
524 self.END_OPERATION('unload')
526 return total, (t2-t1), was_cancelled
529 def get_unload_list(self):
531 return self.__build_unload_list(tree)
534 def __build_unload_list(self, tree, path=None, out=None):
539 if type(tree[d]) == type({}):
541 self.__build_unload_list(tree[d], path, out)
544 typ, subtyp = self.classify_file(d).split('/')
545 if typ in ['image', 'audio', 'video']:
547 name = '/'.join(['/'.join(p), d])
548 out.append((name, tree[d], typ, subtyp))
554 return pcardext.info()
557 def cd(self, dirs, openclose=True):
558 self.START_OPERATION('cd')
560 stat = pcardext.cd(dirs)
563 self.dir_stack.clear()
566 dirs = dirs.split('/')
568 self.dir_stack.push(d)
570 self.ls(True, '*', False)
573 self.END_OPERATION('cd', openclose)
576 def cdup(self, openclose=True):
577 if len(self.dir_stack.as_list()) == 0:
578 return self.cd('/', openclose)
581 self.START_OPERATION('cdup')
585 for d in self.dir_stack.as_list():
588 self.ls(True, '*', False)
590 self.END_OPERATION('cdup', openclose)
592 def rm(self, name, refresh_dir=True, openclose=True):
593 self.START_OPERATION()
595 r = pcardext.rm(name)
598 self.ls(True, '*', False)
600 self.END_OPERATION(openclose)
604 log.debug("Mounting photocard...")
605 self.START_OPERATION('mount')
607 stat = pcardext.mount(self._read, self._write)
608 disk_info = pcardext.info()
609 self.write_protect = disk_info[8]
610 log.debug("stat=%d" % stat)
613 if self.write_protect:
614 # if write_protect is True,
615 # card write NAK'd and channel was
616 # closed. We have to reopen here.
619 self.pcard_mounted = True
622 self.ls(True, '*', False)
625 self.pcard_mounted = False
626 raise Error(ERROR_DEVICE_DOES_NOT_SUPPORT_OPERATION)
628 if self.pcard_mounted:
629 self.END_OPERATION('mount')
634 return '/' + '/'.join(self.dir_stack.as_list())
638 self.saved_pwd = self.dir_stack.as_list()[:]
640 def restore_wd(self, openclose=True):
641 self.cd('/', openclose)
642 for d in self.saved_pwd:
643 self.cd(d, openclose)
647 self.START_OPERATION('tree')
651 dir_tree = self.__tree()
652 self.restore_wd(False)
654 self.END_OPERATION('tree')
657 def __tree(self, __d=None):
662 for f in pcardext.ls(): # True, '*', False ):
665 if self.callback is not None:
668 if fname not in ('.', '..'):
670 self.cd(fname, False)
672 __d[fname] = self.__tree(__d[fname])
681 def get_exif(self, name):
683 self.START_OPERATION('get_exif')
686 pcf = PhotoCardFile(self, name)
687 exif_info = exif.process_file(pcf)
691 self.END_OPERATION('get_exif')
695 def get_exif_path(self, name):
697 self.START_OPERATION('get_exif_path')
700 path_list = name.split('/')[:-1]
701 filename = name.split('/')[-1]
706 pcf = PhotoCardFile(self, filename)
707 exif_info = exif.process_file(pcf)
710 self.restore_wd(False)
712 self.END_OPERATION('get_exif_path')
717 def sector(self, sector):
718 self.START_OPERATION('sector')
720 data = self._read(sector, 1)
722 self.END_OPERATION('sector')
727 self.pcard_mounted = False
729 def open_channel(self):
730 self.channel_opened = True
731 self.device.openPCard()
733 def close_channel(self):
734 self.channel_opened = False
735 self.device.closePCard()