From 309967f6c55e9f7892431de85a98561677d093e9 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 19 Jul 2012 09:30:28 -0400 Subject: [PATCH] Implement the folder records and filesystem reconstruction. --- igor/packed.py | 61 ++++++++++++++++++++++++++++++++++++++++++- igor/record/base.py | 1 + igor/record/folder.py | 6 ++--- test/test.py | 34 ++++++++++++++++++------ 4 files changed, 90 insertions(+), 12 deletions(-) diff --git a/igor/packed.py b/igor/packed.py index 48f933e..10bf214 100644 --- a/igor/packed.py +++ b/igor/packed.py @@ -9,8 +9,13 @@ from .util import need_to_reorder_bytes as _need_to_reorder_bytes from .record import RECORD_TYPE as _RECORD_TYPE from .record.base import UnknownRecord as _UnknownRecord from .record.base import UnusedRecord as _UnusedRecord +from .record.folder import FolderStartRecord as _FolderStartRecord +from .record.folder import FolderEndRecord as _FolderEndRecord +from .record.variables import VariablesRecord as _VariablesRecord +from .record.wave import WaveRecord as _WaveRecord +# From PTN003: # Igor writes other kinds of records in a packed experiment file, for # storing things like pictures, page setup records, and miscellaneous # settings. The format for these records is quite complex and is not @@ -66,5 +71,59 @@ def load(filename, strict=True, ignore_unknown=True): if not hasattr(filename, 'read'): f.close() - return 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. + + When Igor Pro reads the data folder start record, it creates a new + data folder with the specified name. Any subsequent variable, wave + or data folder start records cause Igor to create data objects in + this new data folder, until Igor Pro reads a corresponding data + folder end record.""" + # From the Igor Manual, chapter 2, section 8, page II-123 + # http://www.wavemetrics.net/doc/igorman/II-08%20Data%20Folders.pdf + """Like the Macintosh file system, Igor Pro's data folders use the + colon character (:) to separate components of a path to an + object. This is analogous to Unix which uses / and Windows which + uses \. (Reminder: Igor's data folders exist wholly in memory + while an experiment is open. It is not a disk file system!) + + A data folder named "root" always exists and contains all other + data folders. + """ + # From the Igor Manual, chapter 4, page IV-2 + # http://www.wavemetrics.net/doc/igorman/IV-01%20Commands.pdf + """For waves and data folders only, you can also use "liberal" + names. Liberal names can include almost any character, including + spaces and dots (see Liberal Object Names on page III-415 for + details). + """ + # From the Igor Manual, chapter 3, section 16, page III-416 + # http://www.wavemetrics.net/doc/igorman/III-16%20Miscellany.pdf + """Liberal names have the same rules as standard names except you + may use any character except control characters and the following: + + " ' : ; + """ + filesystem = {'root': {}} + dir_stack = [('root', filesystem['root'])] + for record in records: + cwd = dir_stack[-1][-1] + if isinstance(record, _FolderStartRecord): + name = record.null_terminated_text + cwd[name] = {} + dir_stack.append((name, cwd[name])) + elif isinstance(record, _FolderEndRecord): + 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) diff --git a/igor/record/base.py b/igor/record/base.py index 454e739..53abe24 100644 --- a/igor/record/base.py +++ b/igor/record/base.py @@ -33,3 +33,4 @@ class TextRecord (Record): def __init__(self, *args, **kwargs): super(TextRecord, self).__init__(*args, **kwargs) self.text = str(self.data).replace('\r\n', '\n').replace('\r', '\n') + self.null_terminated_text = self.text.split('\x00', 1)[0] diff --git a/igor/record/folder.py b/igor/record/folder.py index b03e283..be98c58 100644 --- a/igor/record/folder.py +++ b/igor/record/folder.py @@ -1,11 +1,11 @@ # Copyright -from .base import Record +from .base import TextRecord -class FolderStartRecord (Record): +class FolderStartRecord (TextRecord): pass -class FolderEndRecord (Record): +class FolderEndRecord (TextRecord): pass diff --git a/test/test.py b/test/test.py index 25a44eb..bf8d6de 100644 --- a/test/test.py +++ b/test/test.py @@ -1248,9 +1248,9 @@ record 39: 'whpad3': 0, 'whpad4': 0}) record 40: - +'Packages' record 41: - +'WMDataBase' record 42: {'header': {'numSysVars': 21, 'numUserStrs': 6, @@ -1267,9 +1267,9 @@ record 42: 'u_str': '2'}, 'userVars': {}} record 43: - +'' record 44: - +'PolarGraphs' record 45: {'header': {'numSysVars': 21, 'numUserStrs': 10, @@ -1317,15 +1317,28 @@ record 45: 'u_y1': 42.732577139459856, 'u_y2': 45.081649278814126}} record 46: - +'' record 47: - +'' record 48: '| Platform=Windows95, IGORVersion=3.130\n\n\n\nMoveWindow/P 5.25,40.25,504.75,335\n...hook=PolarWindowHook\nEndMacro\n' record 49: '' record 50: '#include version >= 3.0\n' + +filesystem: +{'root': {':variables': [], + ':waves': [, + , + , + , + , + , + , + ], + 'Packages': {'PolarGraphs': {':variables': []}, + 'WMDataBase': {':variables': []}}}} """ import os.path @@ -1335,6 +1348,7 @@ import sys from igor.binarywave import load as loadibw from igor.packed import load as loadpxp from igor.record.base import TextRecord +from igor.record.folder import FolderStartRecord, FolderEndRecord from igor.record.variables import VariablesRecord from igor.record.wave import WaveRecord @@ -1353,10 +1367,12 @@ def dumpibw(filename, strict=True): def dumppxp(filename, strict=True): sys.stderr.write('Testing {}\n'.format(filename)) path = os.path.join(_data_dir, filename) - records = loadpxp(path, strict=strict) + records,filesystem = loadpxp(path, strict=strict) for i,record in enumerate(records): print('record {}:'.format(i)) - if isinstance(record, TextRecord): + if isinstance(record, (FolderStartRecord, FolderEndRecord)): + pprint(record.null_terminated_text) + elif isinstance(record, TextRecord): pprint(record.text) elif isinstance(record, VariablesRecord): pprint(record.variables) @@ -1364,6 +1380,8 @@ def dumppxp(filename, strict=True): pprint(record.wave) else: pprint(record) + print('\nfilesystem:') + pprint(filesystem) def pprint(data): lines = pformat(data).splitlines() -- 2.26.2