home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / hplip / pcard / photocard.py < prev    next >
Encoding:
Python Source  |  2006-08-30  |  20.9 KB  |  735 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2003-2006 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.  
  20. # Std Lib
  21. import struct
  22. import time
  23. import fnmatch
  24. import mimetypes
  25. import array
  26.  
  27. # Local
  28. from base.g import *
  29. from base.codes import *
  30. from base import device, utils, exif
  31.  
  32. # Extensions
  33. import pcardext
  34.  
  35. # Photocard command codes
  36. ACK = 0x0100
  37. NAK = 0x0101
  38. READ_CMD = 0x0010
  39. WRITE_CMD = 0x0020
  40.  
  41. SECTOR_SIZE = 512 # don't change this (TODO: impl. in pcardext)
  42.  
  43. # Photocard sector cache
  44. MAX_CACHE = 512 # units = no. sectors 
  45.  
  46. # PhotoCardFile byte cache
  47. # Used for thumbnails
  48. INITIAL_PCARDFILE_BUFFER = 20*SECTOR_SIZE 
  49. INCREMENTAL_PCARDFILE_BUFFER = 2*SECTOR_SIZE 
  50.  
  51. class PhotoCardFile:    
  52.     # File-like interface
  53.  
  54.     def __init__(self, pc, name=None):
  55.         self.pos = 0
  56.         self.closed = True
  57.         self.file_size = 0
  58.         self.pc = pc
  59.         self.buffer = array.array('c') 
  60.  
  61.         if name is not None:
  62.             self.open(name)
  63.  
  64.         self.buffer_size = INITIAL_PCARDFILE_BUFFER
  65.         self.buffer.fromstring(pcardext.read(self.name, 0, self.buffer_size))
  66.  
  67.  
  68.     def open(self, name):
  69.         self.closed = False
  70.         self.name = name
  71.  
  72.     def seek(self, offset, whence=0):
  73.         if whence == 0:
  74.             self.pos = offset
  75.         elif whence == 1:
  76.             self.pos += offset
  77.         elif whence == 2:
  78.             self.pos = self.file_size - offset
  79.         else:
  80.             return
  81.  
  82.  
  83.     def tell(self):
  84.         return self.pos
  85.  
  86.  
  87.     def read(self, size): 
  88.         if size > 0:
  89.             if self.pos + size < self.buffer_size:
  90.                 data = self.buffer[self.pos : self.pos + size].tostring()
  91.                 self.pos += size
  92.                 return data
  93.             else:
  94.                 # Read some more in from the card to satisfy the request
  95.                 while self.pos + size >= self.buffer_size:
  96.                     self.buffer.fromstring(pcardext.read(self.name, self.buffer_size, INCREMENTAL_PCARDFILE_BUFFER))
  97.                     self.buffer_size += INCREMENTAL_PCARDFILE_BUFFER
  98.                 return self.read(size)
  99.  
  100.  
  101.     def close(self):
  102.         self.closed = True
  103.         self.pos = 0
  104.  
  105.  
  106. class PhotoCard:
  107.  
  108.     def __init__(self, dev_obj=None, device_uri=None, printer_name=None):
  109.  
  110.         if dev_obj is None:
  111.             self.device = device.Device(device_uri, printer_name)
  112.             self.device.open()
  113.             self.close_device = True
  114.         else:
  115.             self.device = dev_obj
  116.             self.close_device = False
  117.  
  118.         self.dir_stack = utils.Stack()
  119.         self.current_dir = []
  120.         self.device_uri = self.device.device_uri
  121.         self.pcard_mounted = False
  122.         self.saved_pwd = []
  123.         self.sector_buffer = {}
  124.         self.sector_buffer_counts = {}
  125.         self.cache_flag = True
  126.         self.write_protect = False
  127.  
  128.         self.callback = None
  129.  
  130.         self.channel_opened = False
  131.  
  132.  
  133.     def START_OPERATION(self, name=''):
  134.         pass
  135.  
  136.     def END_OPERATION(self, name='', flag=True):
  137.         if self.channel_opened and flag:
  138.             self.close_channel()
  139.  
  140.     def set_callback(self, callback):
  141.         self.callback = callback
  142.  
  143.     def _read(self, sector, nsector): 
  144.         log.debug("read pcard sector: sector=%d count=%d" % (sector, nsector))
  145.     
  146.         if self.cache_flag:
  147.             for s in range(sector, sector+nsector):
  148.                 if s not in self.sector_buffer:
  149.                     break
  150.             else:
  151.                 buffer = ''
  152.                 for s in range(sector, sector+nsector):
  153.                     buffer = ''.join([buffer, self.sector_buffer[s]])
  154.                     log.debug("Cached sector read sector=%d" % s)
  155.                     count = self.sector_buffer_counts[s]
  156.                     self.sector_buffer_counts[s] = count+1
  157.                     
  158.                     if self.callback is not None:
  159.                         self.callback()
  160.                 
  161.                 return buffer
  162.  
  163.         if self.callback is not None:
  164.             self.callback()
  165.         
  166.         if not self.channel_opened:
  167.             self.open_channel()
  168.         
  169.         log.debug("Normal sector read sector=%d count=%d" % (sector, nsector))
  170.         sectors_to_read = range(sector, sector+nsector)
  171.         request = struct.pack('!HH' + 'I'*nsector, READ_CMD, nsector, *sectors_to_read)
  172.  
  173.         if self.callback is not None:
  174.             self.callback()
  175.  
  176.         # send out request
  177.         bytes_written = self.device.writePCard(request)
  178.         log.debug("%d bytes written" % bytes_written)
  179.         
  180.         # read return code
  181.         data = self.device.readPCard(2)
  182.         code = struct.unpack('!H', data)[0]
  183.         
  184.         log.debug("Return code: %x" % code)
  185.         
  186.         if code == 0x0110:
  187.             
  188.             # read sector count and version
  189.             data = self.device.readPCard(6)
  190.             nsector_read, ver = struct.unpack('!IH', data)
  191.  
  192.             log.debug("code=0x%x, nsector=%d, ver=%d" % (code, nsector_read, ver))
  193.  
  194.             buffer, data_read, total_to_read = '', 0, nsector * SECTOR_SIZE
  195.  
  196.             while (data_read < total_to_read):
  197.                 #print data_read, total_to_read
  198.                 
  199.                 data = self.device.readPCard(total_to_read)
  200.  
  201.                 data_read += len(data)
  202.                 buffer = ''.join([buffer, data])
  203.  
  204.                 if self.callback is not None:
  205.                     self.callback()            
  206.  
  207.             if self.cache_flag:
  208.                 i = 0
  209.                 
  210.                 for s in range(sector, sector + nsector_read):
  211.                     self.sector_buffer[s] = buffer[i : i+SECTOR_SIZE]
  212.                     log.debug("Sector %d data=\n%s" % (s, repr(self.sector_buffer[s])))
  213.                     count = self.sector_buffer_counts.get(s, 0)
  214.                     self.sector_buffer_counts[s] = count+1
  215.                     i += SECTOR_SIZE
  216.  
  217.                     if self.callback is not None:
  218.                         self.callback()            
  219.  
  220.                 self._check_cache(nsector)
  221.  
  222.             return buffer
  223.         else:
  224.             log.error("Error code: %d" % code)
  225.             return ''
  226.  
  227.     def _write(self, sector, nsector, buffer):
  228.  
  229.         log.debug("write pcard sector: sector=%d count=%d len=%d data=\n%s" % (sector, nsector, len(buffer), repr(buffer)))
  230.  
  231.         if not self.channel_opened:
  232.             self.open_channel()
  233.  
  234.  
  235.         sectors_to_write = range(sector, sector+nsector)
  236.         request = struct.pack('!HHH' + 'I'*nsector, WRITE_CMD, nsector, 0, *sectors_to_write)
  237.         request = ''.join([request, buffer])
  238.  
  239.         if self.callback is not None:
  240.             self.callback()
  241.  
  242.         self.device.writePCard(request)
  243.         data = self.device.readPCard(2)
  244.  
  245.         if self.callback is not None:
  246.             self.callback()
  247.  
  248.         code = struct.unpack('!H', data)[0]
  249.  
  250.         if code != NAK:
  251.             if self.cache_flag:
  252.                 i = 0
  253.                 for s in range(sector, sector+nsector):
  254.                     log.debug("Caching sector %d" % sector)
  255.                     self.sector_buffer[s] = buffer[i:i+SECTOR_SIZE]
  256.                     self.sector_buffer_counts[s] = 1
  257.                     i += SECTOR_SIZE
  258.  
  259.                 if self.callback is not None:
  260.                     self.callback()    
  261.  
  262.                 self._check_cache(nsector)
  263.  
  264.             return 0
  265.  
  266.         else:    
  267.             if self.cache_flag:
  268.                 for s in range(sector, sector+nsector):
  269.                     try:
  270.                         del self.sector_buffer[s]
  271.                         del self.sector_buffer_counts[s]
  272.                     except KeyError:
  273.                         pass
  274.  
  275.             log.error("Photo card write failed (Card may be write protected)")
  276.             self.close_channel()
  277.             return 1
  278.  
  279.  
  280.     def _check_cache(self, nsector):
  281.         if len(self.sector_buffer) > MAX_CACHE:
  282.             # simple minded: scan for first nsector sectors that has count of 1 and throw it away
  283.             t, n = self.sector_buffer.keys()[:], 0
  284.             for s in t:
  285.                 if self.sector_buffer_counts[s] == 1:
  286.                     del self.sector_buffer[s]
  287.                     del self.sector_buffer_counts[s]
  288.                     n += 1
  289.                     if n >= nsector:
  290.                         break
  291.                     if self.callback is not None:
  292.                         self.callback()
  293.  
  294.  
  295.  
  296.     def cache_info(self):
  297.         return self.sector_buffer_counts
  298.  
  299.     def cache_check(self, sector):
  300.         return self.sector_buffer_counts.get(sector, 0)
  301.  
  302.     def cache_control(self, control):
  303.         self.cache_flag = control
  304.  
  305.         if not self.cache_flag:
  306.             self.cache_reset()
  307.  
  308.     def cache_state(self):
  309.         return self.cache_flag
  310.  
  311.     def cache_reset(self):
  312.         self.sector_buffer.clear()
  313.         self.sector_buffer_counts.clear()
  314.  
  315.     def df(self):
  316.         df = 0
  317.         self.START_OPERATION('df')
  318.         try:
  319.             df = pcardext.df()
  320.         finally:
  321.             self.END_OPERATION('df')
  322.             return df
  323.  
  324.     def ls(self, force_read=True, glob_list='*', openclose=True):
  325.         if not glob_list:
  326.             glob_list = '*'
  327.         if force_read:
  328.             self.START_OPERATION('ls')
  329.             try:
  330.                 self.current_dir = pcardext.ls()
  331.             finally:
  332.                 self.END_OPERATION('ls', openclose)
  333.  
  334.         self.current_dir = [(n.lower(),a,s) for (n,a,s) in self.current_dir]
  335.  
  336.         if glob_list == '*':
  337.             return self.current_dir
  338.  
  339.         return [fnmatch.filter(self.current_dir, x) for x in glob_list.strip().lower().split()][0]
  340.  
  341.     def size(self, name):
  342.         for f in self.current_dir:
  343.             if f == name:
  344.                 return self.current_dir[f][2]
  345.         return 0
  346.  
  347.     def current_files(self):
  348.         return [x for x in self.current_dir if x[1] != 'd']
  349.  
  350.     def current_directories(self):
  351.         return [x for x in self.current_dir if x[1] == 'd']
  352.  
  353.     def match_files(self, glob_list):
  354.         if len(glob_list) > 0:
  355.             current_files = [x[0] for x in self.current_files()]
  356.             return [fnmatch.filter(current_files, x) for x in glob_list.strip().lower().split()][0]
  357.         return []
  358.  
  359.     def match_dirs(self, glob_list):
  360.         if len(glob_list) > 0:
  361.             current_dirs = [x[0] for x in self.current_directories()]
  362.             return [fnmatch.filter(current_dirs, x) for x in glob_list.strip().lower().split()][0]
  363.         return []
  364.  
  365.     def classify_file(self, filename):
  366.         t = mimetypes.guess_type(filename)[0]
  367.         if t is None:
  368.             return 'unknown/unknown'
  369.         return t
  370.  
  371.     # copy a single file fom pwd to lpwd
  372.     def cp(self, name, local_file, openclose=True):
  373.         self.START_OPERATION('cp')
  374.         total = 0
  375.         try:
  376.             f = file(local_file, 'w');
  377.             total = pcardext.cp(name, f.fileno())
  378.             f.close()
  379.         finally:
  380.             self.END_OPERATION('cp', openclose)
  381.             return total
  382.  
  383.     # cp multiple files in the current working directory
  384.     def cp_multiple(self, filelist, remove_after_copy, cp_status_callback=None, rm_status_callback=None):
  385.         delta, total = 0, 0
  386.         self.START_OPERATION('cp_multiple')
  387.         t1 = time.time()
  388.         try:
  389.             for f in filelist:
  390.  
  391.                 size = self.cp(f, f, False)
  392.  
  393.                 if cp_status_callback:
  394.                     cp_status_callback(os.path.join(self.pwd(), f), os.path.join(os.getcwd(), f), size)
  395.  
  396.                 total += size
  397.  
  398.  
  399.                 if remove_after_copy:
  400.                     pcardext.rm(f)
  401.  
  402.             t2 = time.time()
  403.             delta = t2-t1
  404.         finally:
  405.             if remove_after_copy:
  406.                 self.ls(True, '*', False)
  407.             self.END_OPERATION('cp_multiple')
  408.             return (total, delta)
  409.  
  410.     # cp multiple files with paths
  411.     def cp_list(self, filelist, remove_after_copy, cp_status_callback=None, rm_status_callback=None):
  412.         self.save_wd()
  413.         delta, total = 0, 0
  414.         self.START_OPERATION('cp_list')
  415.         t1 = time.time()
  416.         try:
  417.             for f in filelist:
  418.  
  419.                 path_list = f.split('/')[:-1]
  420.                 filename = f.split('/')[-1]
  421.  
  422.                 for p in path_list:
  423.                     self.cd(p, False)
  424.  
  425.                 size = self.cp(filename, filename, False)
  426.  
  427.                 if cp_status_callback is not None:
  428.                     cp_status_callback(f, os.path.join(os.getcwd(), filename), size)
  429.  
  430.                 total += size    
  431.  
  432.                 if remove_after_copy:
  433.                     pcardext.rm(filename)
  434.  
  435.                     if rm_status_callback is not None:
  436.                         rm_status_callback(f)
  437.  
  438.                 self.cd('/', False)
  439.  
  440.             t2 = time.time()
  441.             delta = t2-t1
  442.         finally:
  443.             #if remove_after_copy:
  444.             #    self.ls( True, '*', False )
  445.             self.restore_wd()
  446.             self.END_OPERATION('cp_list')
  447.             return (total, delta)
  448.  
  449.  
  450.  
  451.     def cp_fd(self, name, fd):
  452.         total = 0
  453.         self.START_OPERATION('cp_fd')
  454.         try:
  455.             total = pcardext.cp(name, fd)
  456.         finally:
  457.             self.END_OPERATION('cp_fd')
  458.             return total
  459.  
  460.  
  461.     def unload(self, unload_list, cp_status_callback=None, rm_status_callback=None, dont_remove=False):
  462.         was_cancelled = False
  463.         self.save_wd()
  464.         self.START_OPERATION('unload')
  465.         total = 0
  466.         t1 = time.time()
  467.  
  468.         for f in unload_list:
  469.             if not was_cancelled:
  470.                 name, size, typ, subtyp = f
  471.  
  472.                 p = name.split('/')
  473.                 dirs = p[:-1]
  474.                 filename = p[-1]
  475.                 self.cd('/', False)
  476.  
  477.                 if cp_status_callback is not None:
  478.                     if cp_status_callback(os.path.join(self.pwd(), filename), 
  479.                                             os.path.join(os.getcwd(), filename), 1):
  480.                         was_cancelled = True
  481.                         break
  482.  
  483.                 if len(dirs) > 0:
  484.                     for d in dirs:
  485.                         self.cd(d, False)
  486.  
  487.                 if os.path.exists(os.path.join(os.getcwd(), filename)):
  488.                     i = 2
  489.  
  490.                     while True:
  491.                         if not os.path.exists(os.path.join(os.getcwd(), filename + " (%d)" % i)):
  492.                             break
  493.  
  494.                         i += 1
  495.  
  496.                     total += self.cp(filename, filename + " (%d)" % i, False)
  497.  
  498.                 else:    
  499.                     total += self.cp(filename, filename, False)
  500.  
  501.                 if cp_status_callback is not None:
  502.                     if cp_status_callback(os.path.join(self.pwd(), filename), 
  503.                                             os.path.join(os.getcwd(), filename), size):
  504.                         was_cancelled = True
  505.                         break
  506.  
  507.                 if not dont_remove:
  508.                     if rm_status_callback is not None:
  509.                         rm_status_callback(os.path.join(self.pwd(), filename))
  510.  
  511.                     self.rm(filename, False, False)
  512.  
  513.  
  514.         t2 = time.time()
  515.         self.restore_wd(False)
  516.         self.ls(True, '*', False)
  517.         self.END_OPERATION('unload')
  518.  
  519.         return total, (t2-t1), was_cancelled
  520.  
  521.  
  522.     def get_unload_list(self):
  523.         tree = self.tree()
  524.         return self.__build_unload_list(tree)
  525.  
  526.  
  527.     def __build_unload_list(self, tree, path=None, out=None): 
  528.         if path is None:
  529.             out = []
  530.             path = utils.Stack()
  531.         for d in tree:
  532.             if type(tree[d]) == type({}):
  533.                 path.push(d)
  534.                 self.__build_unload_list(tree[d], path, out) 
  535.                 path.pop()
  536.             else:
  537.                 typ, subtyp = self.classify_file(d).split('/')
  538.                 if typ in ['image', 'audio', 'video']:
  539.                     p = path.as_list()
  540.                     name = '/'.join(['/'.join(p), d])
  541.                     out.append((name, tree[d], typ, subtyp)) 
  542.  
  543.         return out
  544.  
  545.  
  546.     def info(self):
  547.         return pcardext.info()
  548.  
  549.  
  550.     def cd(self, dirs, openclose=True):
  551.         self.START_OPERATION('cd')
  552.         try:
  553.             stat = pcardext.cd(dirs)
  554.             if stat:
  555.                 if dirs == '/':
  556.                     self.dir_stack.clear()
  557.  
  558.                 else:
  559.                     dirs = dirs.split('/')
  560.                     for d in dirs:
  561.                         self.dir_stack.push(d)
  562.  
  563.                 self.ls(True, '*', False)
  564.  
  565.         finally:
  566.             self.END_OPERATION('cd', openclose)
  567.  
  568.  
  569.     def cdup(self, openclose=True):
  570.         if len(self.dir_stack.as_list()) == 0:
  571.             return self.cd('/', openclose)
  572.  
  573.         self.dir_stack.pop()
  574.         self.START_OPERATION('cdup')
  575.         try:
  576.             pcardext.cd('/')
  577.  
  578.             for d in self.dir_stack.as_list():
  579.                 pcardext.cd(d)
  580.  
  581.             self.ls(True, '*', False)
  582.         finally:
  583.             self.END_OPERATION('cdup', openclose)
  584.  
  585.     def rm(self, name, refresh_dir=True, openclose=True):
  586.         self.START_OPERATION()
  587.         try:
  588.             r = pcardext.rm(name)
  589.  
  590.             if refresh_dir:
  591.                 self.ls(True, '*', False)
  592.         finally:
  593.             self.END_OPERATION(openclose)
  594.             return r
  595.  
  596.     def mount(self):
  597.         log.debug("Mounting photocard...")
  598.         self.START_OPERATION('mount')
  599.         try:
  600.             stat = pcardext.mount(self._read, self._write)
  601.             disk_info = pcardext.info()
  602.             self.write_protect = disk_info[8]
  603.             log.debug("stat=%d" % stat)
  604.             
  605.             if stat == 0:
  606.                 if self.write_protect:
  607.                     # if write_protect is True,
  608.                     # card write NAK'd and channel was 
  609.                     # closed. We have to reopen here.
  610.                     self.open_channel()
  611.  
  612.                 self.pcard_mounted = True
  613.                 pcardext.cd('/')
  614.  
  615.                 self.ls(True, '*', False)
  616.  
  617.             else:
  618.                 self.pcard_mounted = False
  619.                 raise Error(ERROR_DEVICE_DOES_NOT_SUPPORT_OPERATION)
  620.         finally:
  621.             if self.pcard_mounted:
  622.                 self.END_OPERATION('mount')
  623.  
  624.  
  625.  
  626.     def pwd(self):
  627.         return '/' + '/'.join(self.dir_stack.as_list())
  628.  
  629.  
  630.     def save_wd(self):
  631.         self.saved_pwd = self.dir_stack.as_list()[:]
  632.  
  633.     def restore_wd(self, openclose=True):
  634.         self.cd('/', openclose)
  635.         for d in self.saved_pwd:
  636.             self.cd(d, openclose)
  637.  
  638.  
  639.     def tree(self):
  640.         self.START_OPERATION('tree')
  641.         dir_tree = {}
  642.         try:
  643.             self.save_wd()
  644.             dir_tree = self.__tree()
  645.             self.restore_wd(False)
  646.         finally:
  647.             self.END_OPERATION('tree')
  648.             return dir_tree
  649.  
  650.     def __tree(self, __d=None):
  651.         if __d is None:
  652.             __d = {}
  653.             pcardext.cd('/')
  654.  
  655.         for f in pcardext.ls(): # True, '*', False ):
  656.             fname = f[0].lower()
  657.  
  658.             if self.callback is not None:
  659.                 self.callback()
  660.  
  661.             if fname not in ('.', '..'):
  662.                 if f[1] == 'd':
  663.                     self.cd(fname, False)
  664.                     __d[fname] = {}
  665.                     __d[fname] = self.__tree(__d[fname])
  666.                     self.cdup(False)
  667.  
  668.                 else:
  669.                     __d[fname] = f[2]
  670.  
  671.         return __d
  672.  
  673.  
  674.     def get_exif(self, name):
  675.         exif_info = {}
  676.         self.START_OPERATION('get_exif')
  677.         pcf = None
  678.         try:
  679.             pcf = PhotoCardFile(self, name)
  680.             exif_info = exif.process_file(pcf)
  681.         finally:    
  682.             if pcf is not None:
  683.                 pcf.close()
  684.             self.END_OPERATION('get_exif')
  685.             return exif_info
  686.  
  687.  
  688.     def get_exif_path(self, name):
  689.         exif_info = {}
  690.         self.START_OPERATION('get_exif_path')
  691.         self.save_wd()
  692.         try:
  693.             path_list = name.split('/')[:-1]
  694.             filename = name.split('/')[-1]
  695.  
  696.             for p in path_list:
  697.                 self.cd(p, False)
  698.  
  699.             pcf = PhotoCardFile(self, filename)
  700.             exif_info = exif.process_file(pcf)
  701.  
  702.         finally:    
  703.             self.restore_wd(False)
  704.             pcf.close()
  705.             self.END_OPERATION('get_exif_path')
  706.             return exif_info
  707.  
  708.  
  709.  
  710.     def sector(self, sector):
  711.         self.START_OPERATION('sector')
  712.         try:
  713.             data = self._read(sector, 1)
  714.         finally:
  715.             self.END_OPERATION('sector')
  716.             return data
  717.  
  718.     def umount(self):
  719.         pcardext.umount()
  720.         self.pcard_mounted = False
  721.  
  722.     def open_channel(self):
  723.         self.channel_opened = True
  724.         self.device.openPCard()
  725.  
  726.     def close_channel(self):
  727.         self.channel_opened = False
  728.         self.device.closePCard()
  729.         
  730.  
  731.  
  732.  
  733.  
  734.  
  735.