Add update-copyright blurb to the README (+minor typo fixes).
[igor.git] / igor / packed.py
index 10bf2141e34f63e73a9cf4c6fc862e0210f8ec53..c644063feeef8a28eace33aebab136e4de6b6dfe 100644 (file)
@@ -1,7 +1,23 @@
-# Copyright
+# Copyright (C) 2012 W. Trevor King <wking@tremily.us>
+#
+# This file is part of igor.
+#
+# igor is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# igor is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with igor.  If not, see <http://www.gnu.org/licenses/>.
 
 "Read IGOR Packed Experiment files files into records."
 
+from . import LOG as _LOG
 from .struct import Structure as _Structure
 from .struct import Field as _Field
 from .util import byte_order as _byte_order
@@ -39,6 +55,7 @@ SUPERCEDED_MASK = 0x8000  # Bit is set if the record is superceded by
 
 
 def load(filename, strict=True, ignore_unknown=True):
+    _LOG.debug('loading a packed experiment file from {}'.format(filename))
     records = []
     if hasattr(filename, 'read'):
         f = filename  # filename is actually a stream object
@@ -48,29 +65,54 @@ def load(filename, strict=True, ignore_unknown=True):
     initial_byte_order = '='
     try:
         while True:
+            PackedFileRecordHeader.byte_order = initial_byte_order
+            PackedFileRecordHeader.setup()
             b = buffer(f.read(PackedFileRecordHeader.size))
             if not b:
                 break
-            PackedFileRecordHeader.set_byte_order(initial_byte_order)
+            if len(b) < PackedFileRecordHeader.size:
+                raise ValueError(
+                    ('not enough data for the next record header ({} < {})'
+                     ).format(len(b), PackedFileRecordHeader.size))
+            _LOG.debug('reading a new packed experiment file record')
             header = PackedFileRecordHeader.unpack_from(b)
             if header['version'] and not byte_order:
                 need_to_reorder = _need_to_reorder_bytes(header['version'])
                 byte_order = initial_byte_order = _byte_order(need_to_reorder)
+                _LOG.debug(
+                    'get byte order from version: {} (reorder? {})'.format(
+                        byte_order, need_to_reorder))
                 if need_to_reorder:
-                    PackedFileRecordHeader.set_byte_order(byte_order)
+                    PackedFileRecordHeader.byte_order = byte_order
+                    PackedFileRecordHeader.setup()
                     header = PackedFileRecordHeader.unpack_from(b)
+                    _LOG.debug(
+                        'reordered version: {}'.format(header['version']))
             data = buffer(f.read(header['numDataBytes']))
+            if len(data) < header['numDataBytes']:
+                raise ValueError(
+                    ('not enough data for the next record ({} < {})'
+                     ).format(len(b), header['numDataBytes']))
             record_type = _RECORD_TYPE.get(
                 header['recordType'] & PACKEDRECTYPE_MASK, _UnknownRecord)
+            _LOG.debug('the new record has type {} ({}).'.format(
+                    record_type, header['recordType']))
             if record_type in [_UnknownRecord, _UnusedRecord
                                ] and not ignore_unknown:
                 raise KeyError('unkown record type {}'.format(
                         header['recordType']))
             records.append(record_type(header, data, byte_order=byte_order))
     finally:
+        _LOG.debug('finished loading {} records from {}'.format(
+                len(records), filename))
         if not hasattr(filename, 'read'):
             f.close()
 
+    filesystem = _build_filesystem(records)
+
+    return (records, filesystem)
+
+def _build_filesystem(records):
     # From PTN003:
     """The name must be a valid Igor data folder name. See Object
     Names in the Igor Reference help file for name rules.
@@ -117,13 +159,25 @@ def load(filename, strict=True, ignore_unknown=True):
             dir_stack.pop()
         elif isinstance(record, (_VariablesRecord, _WaveRecord)):
             if isinstance(record, _VariablesRecord):
-                filename = ':variables'  # start with an invalid character
-            else:                        # to avoid collisions with folder
-                filename = ':waves'      # names
-            if filename in cwd:
-                cwd[filename].append(record)
-            else:
-                cwd[filename] = [record]
-
-    return (records, filesystem)
+                sys_vars = record.variables['variables']['sysVars'].keys()
+                for filename,value in record.namespace.items():
+                    if len(dir_stack) > 1 and filename in sys_vars:
+                        # From PTN003:
+                        """When reading a packed file, any system
+                        variables encountered while the current data
+                        folder is not the root should be ignored.
+                        """
+                        continue
+                    _check_filename(dir_stack, filename)
+                    cwd[filename] = value
+            else:  # WaveRecord
+                filename = record.wave['wave']['wave_header']['bname']
+                _check_filename(dir_stack, filename)
+                cwd[filename] = record
+    return filesystem
 
+def _check_filename(dir_stack, filename):
+    cwd = dir_stack[-1][-1]
+    if filename in cwd:
+        raise ValueError('collision on name {} in {}'.format(
+                filename, ':'.join(d for d,cwd in dir_stack)))