diff options
| author | jvoisin | 2011-06-10 01:29:29 +0200 |
|---|---|---|
| committer | jvoisin | 2011-06-10 01:29:29 +0200 |
| commit | c308cf7daaa4fa46377e2df0f2e9a397981e19b2 (patch) | |
| tree | f016ce17cd6747acc068a7d2fc5093d1bd96fa9e /lib | |
| parent | f7082a21d6511c5069fbb9ff186ce22f3e22fed7 (diff) | |
The current version is (mostly) working
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/check/images/jpg.py | 17 | ||||
| -rw-r--r-- | lib/hachoir_editor/__init__.py | 8 | ||||
| -rw-r--r-- | lib/hachoir_editor/field.py | 69 | ||||
| -rw-r--r-- | lib/hachoir_editor/fieldset.py | 346 | ||||
| -rw-r--r-- | lib/hachoir_editor/typed_field.py | 253 | ||||
| -rw-r--r-- | lib/mat.py | 104 | ||||
| -rw-r--r-- | lib/strippers.py | 3 | ||||
| -rw-r--r-- | lib/test.py | 70 |
8 files changed, 0 insertions, 870 deletions
diff --git a/lib/check/images/jpg.py b/lib/check/images/jpg.py deleted file mode 100644 index 7f29587..0000000 --- a/lib/check/images/jpg.py +++ /dev/null | |||
| @@ -1,17 +0,0 @@ | |||
| 1 | import hachoir_core.error | ||
| 2 | import hachoir_core.cmd_line | ||
| 3 | import hachoir_parser | ||
| 4 | import hachoir_metadata | ||
| 5 | import sys | ||
| 6 | import mat | ||
| 7 | |||
| 8 | |||
| 9 | class JpegStripper(file): | ||
| 10 | def checkField(self, field): | ||
| 11 | print(field.description) | ||
| 12 | if field.name.startswith("comment"): | ||
| 13 | return True | ||
| 14 | return field.name in ("photoshop", "exif", "adobe") | ||
| 15 | return False | ||
| 16 | |||
| 17 | |||
diff --git a/lib/hachoir_editor/__init__.py b/lib/hachoir_editor/__init__.py deleted file mode 100644 index b106278..0000000 --- a/lib/hachoir_editor/__init__.py +++ /dev/null | |||
| @@ -1,8 +0,0 @@ | |||
| 1 | from hachoir_editor.field import ( | ||
| 2 | EditorError, FakeField) | ||
| 3 | from hachoir_editor.typed_field import ( | ||
| 4 | EditableField, EditableBits, EditableBytes, | ||
| 5 | EditableInteger, EditableString, | ||
| 6 | createEditableField) | ||
| 7 | from hachoir_editor.fieldset import EditableFieldSet, NewFieldSet, createEditor | ||
| 8 | |||
diff --git a/lib/hachoir_editor/field.py b/lib/hachoir_editor/field.py deleted file mode 100644 index 6b1efe3..0000000 --- a/lib/hachoir_editor/field.py +++ /dev/null | |||
| @@ -1,69 +0,0 @@ | |||
| 1 | from hachoir_core.error import HachoirError | ||
| 2 | from hachoir_core.field import joinPath, MissingField | ||
| 3 | |||
| 4 | class EditorError(HachoirError): | ||
| 5 | pass | ||
| 6 | |||
| 7 | class FakeField(object): | ||
| 8 | """ | ||
| 9 | This class have API looks similar to Field API, but objects don't contain | ||
| 10 | any value: all values are _computed_ by parent methods. | ||
| 11 | |||
| 12 | Example: FakeField(editor, "abc").size calls editor._getFieldSize("abc"). | ||
| 13 | """ | ||
| 14 | is_field_set = False | ||
| 15 | |||
| 16 | def __init__(self, parent, name): | ||
| 17 | self._parent = parent | ||
| 18 | self._name = name | ||
| 19 | |||
| 20 | def _getPath(self): | ||
| 21 | return joinPath(self._parent.path, self._name) | ||
| 22 | path = property(_getPath) | ||
| 23 | |||
| 24 | def _getName(self): | ||
| 25 | return self._name | ||
| 26 | name = property(_getName) | ||
| 27 | |||
| 28 | def _getAddress(self): | ||
| 29 | return self._parent._getFieldAddress(self._name) | ||
| 30 | address = property(_getAddress) | ||
| 31 | |||
| 32 | def _getSize(self): | ||
| 33 | return self._parent.input[self._name].size | ||
| 34 | size = property(_getSize) | ||
| 35 | |||
| 36 | def _getValue(self): | ||
| 37 | return self._parent.input[self._name].value | ||
| 38 | value = property(_getValue) | ||
| 39 | |||
| 40 | def createDisplay(self): | ||
| 41 | # TODO: Returns new value if field is altered | ||
| 42 | return self._parent.input[self._name].display | ||
| 43 | display = property(createDisplay) | ||
| 44 | |||
| 45 | def _getParent(self): | ||
| 46 | return self._parent | ||
| 47 | parent = property(_getParent) | ||
| 48 | |||
| 49 | def hasValue(self): | ||
| 50 | return self._parent.input[self._name].hasValue() | ||
| 51 | |||
| 52 | def __getitem__(self, key): | ||
| 53 | # TODO: Implement this function! | ||
| 54 | raise MissingField(self, key) | ||
| 55 | |||
| 56 | def _isAltered(self): | ||
| 57 | return False | ||
| 58 | is_altered = property(_isAltered) | ||
| 59 | |||
| 60 | def writeInto(self, output): | ||
| 61 | size = self.size | ||
| 62 | addr = self._parent._getFieldInputAddress(self._name) | ||
| 63 | input = self._parent.input | ||
| 64 | stream = input.stream | ||
| 65 | if size % 8: | ||
| 66 | output.copyBitsFrom(stream, addr, size, input.endian) | ||
| 67 | else: | ||
| 68 | output.copyBytesFrom(stream, addr, size//8) | ||
| 69 | |||
diff --git a/lib/hachoir_editor/fieldset.py b/lib/hachoir_editor/fieldset.py deleted file mode 100644 index 1669b5a..0000000 --- a/lib/hachoir_editor/fieldset.py +++ /dev/null | |||
| @@ -1,346 +0,0 @@ | |||
| 1 | from hachoir_core.dict import UniqKeyError | ||
| 2 | from hachoir_core.field import MissingField, Float32, Float64, FakeArray | ||
| 3 | from hachoir_core.compatibility import any | ||
| 4 | from hachoir_core.i18n import _ | ||
| 5 | from hachoir_editor import createEditableField, EditorError | ||
| 6 | from collections import deque # Python 2.4 | ||
| 7 | import weakref # Python 2.1 | ||
| 8 | import struct | ||
| 9 | |||
| 10 | class EditableFieldSet(object): | ||
| 11 | MAX_SIZE = (1 << 40) # Arbitrary limit to catch errors | ||
| 12 | is_field_set = True | ||
| 13 | |||
| 14 | def __init__(self, parent, fieldset): | ||
| 15 | self._parent = parent | ||
| 16 | self.input = fieldset # original FieldSet | ||
| 17 | self._fields = {} # cache of editable fields | ||
| 18 | self._deleted = set() # Names of deleted fields | ||
| 19 | self._inserted = {} # Inserted field (name => list of field, | ||
| 20 | # where name is the name after) | ||
| 21 | |||
| 22 | def array(self, key): | ||
| 23 | # FIXME: Use cache? | ||
| 24 | return FakeArray(self, key) | ||
| 25 | |||
| 26 | def _getParent(self): | ||
| 27 | return self._parent | ||
| 28 | parent = property(_getParent) | ||
| 29 | |||
| 30 | def _isAltered(self): | ||
| 31 | if self._inserted: | ||
| 32 | return True | ||
| 33 | if self._deleted: | ||
| 34 | return True | ||
| 35 | return any(field.is_altered for field in self._fields.itervalues()) | ||
| 36 | is_altered = property(_isAltered) | ||
| 37 | |||
| 38 | def reset(self): | ||
| 39 | """ | ||
| 40 | Reset the field set and the input field set. | ||
| 41 | """ | ||
| 42 | for key, field in self._fields.iteritems(): | ||
| 43 | if not field.is_altered: | ||
| 44 | del self._fields[key] | ||
| 45 | self.input.reset() | ||
| 46 | |||
| 47 | def __len__(self): | ||
| 48 | return len(self.input) \ | ||
| 49 | - len(self._deleted) \ | ||
| 50 | + sum( len(new) for new in self._inserted.itervalues() ) | ||
| 51 | |||
| 52 | def __iter__(self): | ||
| 53 | for field in self.input: | ||
| 54 | name = field.name | ||
| 55 | if name in self._inserted: | ||
| 56 | for newfield in self._inserted[name]: | ||
| 57 | yield weakref.proxy(newfield) | ||
| 58 | if name not in self._deleted: | ||
| 59 | yield self[name] | ||
| 60 | if None in self._inserted: | ||
| 61 | for newfield in self._inserted[None]: | ||
| 62 | yield weakref.proxy(newfield) | ||
| 63 | |||
| 64 | def insertBefore(self, name, *new_fields): | ||
| 65 | self._insert(name, new_fields, False) | ||
| 66 | |||
| 67 | def insertAfter(self, name, *new_fields): | ||
| 68 | self._insert(name, new_fields, True) | ||
| 69 | |||
| 70 | def insert(self, *new_fields): | ||
| 71 | self._insert(None, new_fields, True) | ||
| 72 | |||
| 73 | def _insert(self, key, new_fields, next): | ||
| 74 | """ | ||
| 75 | key is the name of the field before which new_fields | ||
| 76 | will be inserted. If next is True, the fields will be inserted | ||
| 77 | _after_ this field. | ||
| 78 | """ | ||
| 79 | # Set unique field name | ||
| 80 | for field in new_fields: | ||
| 81 | if field._name.endswith("[]"): | ||
| 82 | self.input.setUniqueFieldName(field) | ||
| 83 | |||
| 84 | # Check that there is no duplicate in inserted fields | ||
| 85 | new_names = list(field.name for field in new_fields) | ||
| 86 | names_set = set(new_names) | ||
| 87 | if len(names_set) != len(new_fields): | ||
| 88 | duplicates = (name for name in names_set if 1 < new_names.count(name)) | ||
| 89 | raise UniqKeyError(_("Duplicates in inserted fields: %s") % ", ".join(duplicates)) | ||
| 90 | |||
| 91 | # Check that field names are not in input | ||
| 92 | if self.input: # Write special version for NewFieldSet? | ||
| 93 | for name in new_names: | ||
| 94 | if name in self.input and name not in self._deleted: | ||
| 95 | raise UniqKeyError(_("Field name '%s' already exists") % name) | ||
| 96 | |||
| 97 | # Check that field names are not in inserted fields | ||
| 98 | for fields in self._inserted.itervalues(): | ||
| 99 | for field in fields: | ||
| 100 | if field.name in new_names: | ||
| 101 | raise UniqKeyError(_("Field name '%s' already exists") % field.name) | ||
| 102 | |||
| 103 | # Input have already inserted field? | ||
| 104 | if key in self._inserted: | ||
| 105 | if next: | ||
| 106 | self._inserted[key].extend( reversed(new_fields) ) | ||
| 107 | else: | ||
| 108 | self._inserted[key].extendleft( reversed(new_fields) ) | ||
| 109 | return | ||
| 110 | |||
| 111 | # Whould like to insert in inserted fields? | ||
| 112 | if key: | ||
| 113 | for fields in self._inserted.itervalues(): | ||
| 114 | names = [item.name for item in fields] | ||
| 115 | try: | ||
| 116 | pos = names.index(key) | ||
| 117 | except ValueError: | ||
| 118 | continue | ||
| 119 | if 0 <= pos: | ||
| 120 | if next: | ||
| 121 | pos += 1 | ||
| 122 | fields.rotate(-pos) | ||
| 123 | fields.extendleft( reversed(new_fields) ) | ||
| 124 | fields.rotate(pos) | ||
| 125 | return | ||
| 126 | |||
| 127 | # Get next field. Use None if we are at the end. | ||
| 128 | if next: | ||
| 129 | index = self.input[key].index + 1 | ||
| 130 | try: | ||
| 131 | key = self.input[index].name | ||
| 132 | except IndexError: | ||
| 133 | key = None | ||
| 134 | |||
| 135 | # Check that field names are not in input | ||
| 136 | if key not in self.input: | ||
| 137 | raise MissingField(self, key) | ||
| 138 | |||
| 139 | # Insert in original input | ||
| 140 | self._inserted[key]= deque(new_fields) | ||
| 141 | |||
| 142 | def _getDescription(self): | ||
| 143 | return self.input.description | ||
| 144 | description = property(_getDescription) | ||
| 145 | |||
| 146 | def _getStream(self): | ||
| 147 | # FIXME: This property is maybe a bad idea since address may be differents | ||
| 148 | return self.input.stream | ||
| 149 | stream = property(_getStream) | ||
| 150 | |||
| 151 | def _getName(self): | ||
| 152 | return self.input.name | ||
| 153 | name = property(_getName) | ||
| 154 | |||
| 155 | def _getEndian(self): | ||
| 156 | return self.input.endian | ||
| 157 | endian = property(_getEndian) | ||
| 158 | |||
| 159 | def _getAddress(self): | ||
| 160 | if self._parent: | ||
| 161 | return self._parent._getFieldAddress(self.name) | ||
| 162 | else: | ||
| 163 | return 0 | ||
| 164 | address = property(_getAddress) | ||
| 165 | |||
| 166 | def _getAbsoluteAddress(self): | ||
| 167 | address = self.address | ||
| 168 | current = self._parent | ||
| 169 | while current: | ||
| 170 | address += current.address | ||
| 171 | current = current._parent | ||
| 172 | return address | ||
| 173 | absolute_address = property(_getAbsoluteAddress) | ||
| 174 | |||
| 175 | def hasValue(self): | ||
| 176 | return False | ||
| 177 | # return self._parent.input[self.name].hasValue() | ||
| 178 | |||
| 179 | def _getSize(self): | ||
| 180 | if self.is_altered: | ||
| 181 | return sum(field.size for field in self) | ||
| 182 | else: | ||
| 183 | return self.input.size | ||
| 184 | size = property(_getSize) | ||
| 185 | |||
| 186 | def _getPath(self): | ||
| 187 | return self.input.path | ||
| 188 | path = property(_getPath) | ||
| 189 | |||
| 190 | def _getOriginalField(self, name): | ||
| 191 | assert name in self.input | ||
| 192 | return self.input[name] | ||
| 193 | |||
| 194 | def _getFieldInputAddress(self, name): | ||
| 195 | """ | ||
| 196 | Absolute address of a field from the input field set. | ||
| 197 | """ | ||
| 198 | assert name in self.input | ||
| 199 | return self.input[name].absolute_address | ||
| 200 | |||
| 201 | def _getFieldAddress(self, name): | ||
| 202 | """ | ||
| 203 | Compute relative address of a field. The operation takes care of | ||
| 204 | deleted and resized fields. | ||
| 205 | """ | ||
| 206 | #assert name not in self._deleted | ||
| 207 | addr = 0 | ||
| 208 | for field in self: | ||
| 209 | if field.name == name: | ||
| 210 | return addr | ||
| 211 | addr += field.size | ||
| 212 | raise MissingField(self, name) | ||
| 213 | |||
| 214 | def _getItemByPath(self, path): | ||
| 215 | if not path[0]: | ||
| 216 | path = path[1:] | ||
| 217 | field = self | ||
| 218 | for name in path: | ||
| 219 | field = field[name] | ||
| 220 | return field | ||
| 221 | |||
| 222 | def __contains__(self, name): | ||
| 223 | try: | ||
| 224 | field = self[name] | ||
| 225 | return (field is not None) | ||
| 226 | except MissingField: | ||
| 227 | return False | ||
| 228 | |||
| 229 | def __getitem__(self, key): | ||
| 230 | """ | ||
| 231 | Create a weak reference to an editable field (EditableField) for the | ||
| 232 | field with specified name. If the field is removed later, using the | ||
| 233 | editable field will raise a weakref.ReferenceError exception. | ||
| 234 | |||
| 235 | May raise a MissingField error if the field doesn't exist in original | ||
| 236 | field set or it has been deleted. | ||
| 237 | """ | ||
| 238 | if "/" in key: | ||
| 239 | return self._getItemByPath(key.split("/")) | ||
| 240 | if isinstance(key, (int, long)): | ||
| 241 | raise EditorError("Integer index are not supported") | ||
| 242 | |||
| 243 | if (key in self._deleted) or (key not in self.input): | ||
| 244 | raise MissingField(self, key) | ||
| 245 | if key not in self._fields: | ||
| 246 | field = self.input[key] | ||
| 247 | if field.is_field_set: | ||
| 248 | self._fields[key] = createEditableFieldSet(self, field) | ||
| 249 | else: | ||
| 250 | self._fields[key] = createEditableField(self, field) | ||
| 251 | return weakref.proxy(self._fields[key]) | ||
| 252 | |||
| 253 | def __delitem__(self, name): | ||
| 254 | """ | ||
| 255 | Remove a field from the field set. May raise an MissingField exception | ||
| 256 | if the field has already been deleted. | ||
| 257 | """ | ||
| 258 | if name in self._deleted: | ||
| 259 | raise MissingField(self, name) | ||
| 260 | self._deleted.add(name) | ||
| 261 | if name in self._fields: | ||
| 262 | del self._fields[name] | ||
| 263 | |||
| 264 | def writeInto(self, output): | ||
| 265 | """ | ||
| 266 | Write the content if this field set into the output stream | ||
| 267 | (OutputStream). | ||
| 268 | """ | ||
| 269 | if not self.is_altered: | ||
| 270 | # Not altered: just copy bits/bytes | ||
| 271 | input = self.input | ||
| 272 | if input.size % 8: | ||
| 273 | output.copyBitsFrom(input.stream, | ||
| 274 | input.absolute_address, input.size, input.endian) | ||
| 275 | else: | ||
| 276 | output.copyBytesFrom(input.stream, | ||
| 277 | input.absolute_address, input.size//8) | ||
| 278 | else: | ||
| 279 | # Altered: call writeInto() method of each field | ||
| 280 | realaddr = 0 | ||
| 281 | for field in self: | ||
| 282 | field.writeInto(output) | ||
| 283 | realaddr += field.size | ||
| 284 | |||
| 285 | def _getValue(self): | ||
| 286 | raise EditorError('Field set "%s" has no value' % self.path) | ||
| 287 | def _setValue(self, value): | ||
| 288 | raise EditorError('Field set "%s" value is read only' % self.path) | ||
| 289 | value = property(_getValue, _setValue, "Value of field") | ||
| 290 | |||
| 291 | class EditableFloat(EditableFieldSet): | ||
| 292 | _value = None | ||
| 293 | |||
| 294 | def _isAltered(self): | ||
| 295 | return (self._value is not None) | ||
| 296 | is_altered = property(_isAltered) | ||
| 297 | |||
| 298 | def writeInto(self, output): | ||
| 299 | if self._value is not None: | ||
| 300 | self._write(output) | ||
| 301 | else: | ||
| 302 | EditableFieldSet.writeInto(self, output) | ||
| 303 | |||
| 304 | def _write(self, output): | ||
| 305 | format = self.input.struct_format | ||
| 306 | raw = struct.pack(format, self._value) | ||
| 307 | output.writeBytes(raw) | ||
| 308 | |||
| 309 | def _setValue(self, value): | ||
| 310 | self.parent._is_altered = True | ||
| 311 | self._value = value | ||
| 312 | value = property(EditableFieldSet._getValue, _setValue) | ||
| 313 | |||
| 314 | def createEditableFieldSet(parent, field): | ||
| 315 | cls = field.__class__ | ||
| 316 | # FIXME: Support Float80 | ||
| 317 | if cls in (Float32, Float64): | ||
| 318 | return EditableFloat(parent, field) | ||
| 319 | else: | ||
| 320 | return EditableFieldSet(parent, field) | ||
| 321 | |||
| 322 | class NewFieldSet(EditableFieldSet): | ||
| 323 | def __init__(self, parent, name): | ||
| 324 | EditableFieldSet.__init__(self, parent, None) | ||
| 325 | self._name = name | ||
| 326 | self._endian = parent.endian | ||
| 327 | |||
| 328 | def __iter__(self): | ||
| 329 | if None in self._inserted: | ||
| 330 | return iter(self._inserted[None]) | ||
| 331 | else: | ||
| 332 | raise StopIteration() | ||
| 333 | |||
| 334 | def _getName(self): | ||
| 335 | return self._name | ||
| 336 | name = property(_getName) | ||
| 337 | |||
| 338 | def _getEndian(self): | ||
| 339 | return self._endian | ||
| 340 | endian = property(_getEndian) | ||
| 341 | |||
| 342 | is_altered = property(lambda self: True) | ||
| 343 | |||
| 344 | def createEditor(fieldset): | ||
| 345 | return EditableFieldSet(None, fieldset) | ||
| 346 | |||
diff --git a/lib/hachoir_editor/typed_field.py b/lib/hachoir_editor/typed_field.py deleted file mode 100644 index 4abc989..0000000 --- a/lib/hachoir_editor/typed_field.py +++ /dev/null | |||
| @@ -1,253 +0,0 @@ | |||
| 1 | from hachoir_core.field import ( | ||
| 2 | RawBits, Bit, Bits, PaddingBits, | ||
| 3 | RawBytes, Bytes, PaddingBytes, | ||
| 4 | GenericString, Character, | ||
| 5 | isInteger, isString) | ||
| 6 | from hachoir_editor import FakeField | ||
| 7 | |||
| 8 | class EditableField(FakeField): | ||
| 9 | """ | ||
| 10 | Pure virtual class used to write editable field class. | ||
| 11 | """ | ||
| 12 | |||
| 13 | _is_altered = False | ||
| 14 | def __init__(self, parent, name, value=None): | ||
| 15 | FakeField.__init__(self, parent, name) | ||
| 16 | self._value = value | ||
| 17 | |||
| 18 | def _isAltered(self): | ||
| 19 | return self._is_altered | ||
| 20 | is_altered = property(_isAltered) | ||
| 21 | |||
| 22 | def hasValue(self): | ||
| 23 | return True | ||
| 24 | |||
| 25 | def _computeSize(self): | ||
| 26 | raise NotImplementedError() | ||
| 27 | def _getValue(self): | ||
| 28 | return self._value | ||
| 29 | def _setValue(self, value): | ||
| 30 | self._value = value | ||
| 31 | |||
| 32 | def _propGetValue(self): | ||
| 33 | if self._value is not None: | ||
| 34 | return self._getValue() | ||
| 35 | else: | ||
| 36 | return FakeField._getValue(self) | ||
| 37 | def _propSetValue(self, value): | ||
| 38 | self._setValue(value) | ||
| 39 | self._is_altered = True | ||
| 40 | value = property(_propGetValue, _propSetValue) | ||
| 41 | |||
| 42 | def _getSize(self): | ||
| 43 | if self._value is not None: | ||
| 44 | return self._computeSize() | ||
| 45 | else: | ||
| 46 | return FakeField._getSize(self) | ||
| 47 | size = property(_getSize) | ||
| 48 | |||
| 49 | def _write(self, output): | ||
| 50 | raise NotImplementedError() | ||
| 51 | |||
| 52 | def writeInto(self, output): | ||
| 53 | if self._is_altered: | ||
| 54 | self._write(output) | ||
| 55 | else: | ||
| 56 | return FakeField.writeInto(self, output) | ||
| 57 | |||
| 58 | class EditableFixedField(EditableField): | ||
| 59 | """ | ||
| 60 | Editable field with fixed size. | ||
| 61 | """ | ||
| 62 | |||
| 63 | def __init__(self, parent, name, value=None, size=None): | ||
| 64 | EditableField.__init__(self, parent, name, value) | ||
| 65 | if size is not None: | ||
| 66 | self._size = size | ||
| 67 | else: | ||
| 68 | self._size = self._parent._getOriginalField(self._name).size | ||
| 69 | |||
| 70 | def _getSize(self): | ||
| 71 | return self._size | ||
| 72 | size = property(_getSize) | ||
| 73 | |||
| 74 | class EditableBits(EditableFixedField): | ||
| 75 | def __init__(self, parent, name, *args): | ||
| 76 | if args: | ||
| 77 | if len(args) != 2: | ||
| 78 | raise TypeError( | ||
| 79 | "Wrong argument count, EditableBits constructor prototype is: " | ||
| 80 | "(parent, name, [size, value])") | ||
| 81 | size = args[0] | ||
| 82 | value = args[1] | ||
| 83 | assert isinstance(value, (int, long)) | ||
| 84 | else: | ||
| 85 | size = None | ||
| 86 | value = None | ||
| 87 | EditableFixedField.__init__(self, parent, name, value, size) | ||
| 88 | if args: | ||
| 89 | self._setValue(args[1]) | ||
| 90 | self._is_altered = True | ||
| 91 | |||
| 92 | def _setValue(self, value): | ||
| 93 | if not(0 <= value < (1 << self._size)): | ||
| 94 | raise ValueError("Invalid value, must be in range %s..%s" | ||
| 95 | % (0, (1 << self._size) - 1)) | ||
| 96 | self._value = value | ||
| 97 | |||
| 98 | def _write(self, output): | ||
| 99 | output.writeBits(self._size, self._value, self._parent.endian) | ||
| 100 | |||
| 101 | class EditableBytes(EditableField): | ||
| 102 | def _setValue(self, value): | ||
| 103 | if not value: raise ValueError( | ||
| 104 | "Unable to set empty string to a EditableBytes field") | ||
| 105 | self._value = value | ||
| 106 | |||
| 107 | def _computeSize(self): | ||
| 108 | return len(self._value) * 8 | ||
| 109 | |||
| 110 | def _write(self, output): | ||
| 111 | output.writeBytes(self._value) | ||
| 112 | |||
| 113 | class EditableString(EditableField): | ||
| 114 | MAX_SIZE = { | ||
| 115 | "Pascal8": (1 << 8)-1, | ||
| 116 | "Pascal16": (1 << 16)-1, | ||
| 117 | "Pascal32": (1 << 32)-1, | ||
| 118 | } | ||
| 119 | |||
| 120 | def __init__(self, parent, name, *args, **kw): | ||
| 121 | if len(args) == 2: | ||
| 122 | value = args[1] | ||
| 123 | assert isinstance(value, str) # TODO: support Unicode | ||
| 124 | elif not args: | ||
| 125 | value = None | ||
| 126 | else: | ||
| 127 | raise TypeError( | ||
| 128 | "Wrong argument count, EditableString constructor prototype is:" | ||
| 129 | "(parent, name, [format, value])") | ||
| 130 | EditableField.__init__(self, parent, name, value) | ||
| 131 | if len(args) == 2: | ||
| 132 | self._charset = kw.get('charset', None) | ||
| 133 | self._format = args[0] | ||
| 134 | if self._format in GenericString.PASCAL_FORMATS: | ||
| 135 | self._prefix_size = GenericString.PASCAL_FORMATS[self._format] | ||
| 136 | else: | ||
| 137 | self._prefix_size = 0 | ||
| 138 | self._suffix_str = GenericString.staticSuffixStr( | ||
| 139 | self._format, self._charset, self._parent.endian) | ||
| 140 | self._is_altered = True | ||
| 141 | else: | ||
| 142 | orig = self._parent._getOriginalField(name) | ||
| 143 | self._charset = orig.charset | ||
| 144 | self._format = orig.format | ||
| 145 | self._prefix_size = orig.content_offset | ||
| 146 | self._suffix_str = orig.suffix_str | ||
| 147 | |||
| 148 | def _setValue(self, value): | ||
| 149 | size = len(value) | ||
| 150 | if self._format in self.MAX_SIZE and self.MAX_SIZE[self._format] < size: | ||
| 151 | raise ValueError("String is too big") | ||
| 152 | self._value = value | ||
| 153 | |||
| 154 | def _computeSize(self): | ||
| 155 | return (self._prefix_size + len(self._value) + len(self._suffix_str))*8 | ||
| 156 | |||
| 157 | def _write(self, output): | ||
| 158 | if self._format in GenericString.SUFFIX_FORMAT: | ||
| 159 | output.writeBytes(self._value) | ||
| 160 | output.writeBytes(self._suffix_str) | ||
| 161 | elif self._format == "fixed": | ||
| 162 | output.writeBytes(self._value) | ||
| 163 | else: | ||
| 164 | assert self._format in GenericString.PASCAL_FORMATS | ||
| 165 | size = GenericString.PASCAL_FORMATS[self._format] | ||
| 166 | output.writeInteger(len(self._value), False, size, self._parent.endian) | ||
| 167 | output.writeBytes(self._value) | ||
| 168 | |||
| 169 | class EditableCharacter(EditableFixedField): | ||
| 170 | def __init__(self, parent, name, *args): | ||
| 171 | if args: | ||
| 172 | if len(args) != 3: | ||
| 173 | raise TypeError( | ||
| 174 | "Wrong argument count, EditableCharacter " | ||
| 175 | "constructor prototype is: (parent, name, [value])") | ||
| 176 | value = args[0] | ||
| 177 | if not isinstance(value, str) or len(value) != 1: | ||
| 178 | raise TypeError("EditableCharacter needs a character") | ||
| 179 | else: | ||
| 180 | value = None | ||
| 181 | EditableFixedField.__init__(self, parent, name, value, 8) | ||
| 182 | if args: | ||
| 183 | self._is_altered = True | ||
| 184 | |||
| 185 | def _setValue(self, value): | ||
| 186 | if not isinstance(value, str) or len(value) != 1: | ||
| 187 | raise TypeError("EditableCharacter needs a character") | ||
| 188 | self._value = value | ||
| 189 | |||
| 190 | def _write(self, output): | ||
| 191 | output.writeBytes(self._value) | ||
| 192 | |||
| 193 | class EditableInteger(EditableFixedField): | ||
| 194 | VALID_VALUE_SIGNED = { | ||
| 195 | 8: (-(1 << 8), (1 << 8)-1), | ||
| 196 | 16: (-(1 << 15), (1 << 15)-1), | ||
| 197 | 32: (-(1 << 31), (1 << 31)-1), | ||
| 198 | } | ||
| 199 | VALID_VALUE_UNSIGNED = { | ||
| 200 | 8: (0, (1 << 8)-1), | ||
| 201 | 16: (0, (1 << 16)-1), | ||
| 202 | 32: (0, (1 << 32)-1) | ||
| 203 | } | ||
| 204 | |||
| 205 | def __init__(self, parent, name, *args): | ||
| 206 | if args: | ||
| 207 | if len(args) != 3: | ||
| 208 | raise TypeError( | ||
| 209 | "Wrong argument count, EditableInteger constructor prototype is: " | ||
| 210 | "(parent, name, [signed, size, value])") | ||
| 211 | size = args[1] | ||
| 212 | value = args[2] | ||
| 213 | assert isinstance(value, (int, long)) | ||
| 214 | else: | ||
| 215 | size = None | ||
| 216 | value = None | ||
| 217 | EditableFixedField.__init__(self, parent, name, value, size) | ||
| 218 | if args: | ||
| 219 | self._signed = args[0] | ||
| 220 | self._is_altered = True | ||
| 221 | else: | ||
| 222 | self._signed = self._parent._getOriginalField(self._name).signed | ||
| 223 | |||
| 224 | def _setValue(self, value): | ||
| 225 | if self._signed: | ||
| 226 | valid = self.VALID_VALUE_SIGNED | ||
| 227 | else: | ||
| 228 | valid = self.VALID_VALUE_UNSIGNED | ||
| 229 | minval, maxval = valid[self._size] | ||
| 230 | if not(minval <= value <= maxval): | ||
| 231 | raise ValueError("Invalid value, must be in range %s..%s" | ||
| 232 | % (minval, maxval)) | ||
| 233 | self._value = value | ||
| 234 | |||
| 235 | def _write(self, output): | ||
| 236 | output.writeInteger( | ||
| 237 | self.value, self._signed, self._size//8, self._parent.endian) | ||
| 238 | |||
| 239 | def createEditableField(fieldset, field): | ||
| 240 | if isInteger(field): | ||
| 241 | cls = EditableInteger | ||
| 242 | elif isString(field): | ||
| 243 | cls = EditableString | ||
| 244 | elif field.__class__ in (RawBytes, Bytes, PaddingBytes): | ||
| 245 | cls = EditableBytes | ||
| 246 | elif field.__class__ in (RawBits, Bits, Bit, PaddingBits): | ||
| 247 | cls = EditableBits | ||
| 248 | elif field.__class__ == Character: | ||
| 249 | cls = EditableCharacter | ||
| 250 | else: | ||
| 251 | cls = FakeField | ||
| 252 | return cls(fieldset, field.name) | ||
| 253 | |||
diff --git a/lib/mat.py b/lib/mat.py deleted file mode 100644 index d22c9ab..0000000 --- a/lib/mat.py +++ /dev/null | |||
| @@ -1,104 +0,0 @@ | |||
| 1 | import hachoir_core.error | ||
| 2 | import hachoir_core.cmd_line | ||
| 3 | import hachoir_parser | ||
| 4 | import hachoir_metadata | ||
| 5 | |||
| 6 | from strippers import * | ||
| 7 | |||
| 8 | from hachoir_editor import (createEditor, | ||
| 9 | NewFieldSet, EditableInteger, EditableBytes) | ||
| 10 | |||
| 11 | import hachoir_editor | ||
| 12 | |||
| 13 | import sys | ||
| 14 | |||
| 15 | __version__ = "0.1" | ||
| 16 | __author__ = "jvoisin" | ||
| 17 | |||
| 18 | |||
| 19 | class file(): | ||
| 20 | def __init__(self, filename): | ||
| 21 | self.metadata = {} | ||
| 22 | self.clean = False | ||
| 23 | self.editor = createEditor(self.parser) | ||
| 24 | self.filename = filename | ||
| 25 | self.filename, self.realname = hachoir_core.cmd_line.unicodeFilename( | ||
| 26 | self.filename), self.filename | ||
| 27 | self.parser = hachoir_parser.createParser(self.filename, self.realname) | ||
| 28 | |||
| 29 | if not self.parser: | ||
| 30 | print("Unable to parse file : sorry") | ||
| 31 | sys.exit(1) | ||
| 32 | |||
| 33 | try: | ||
| 34 | self.meta = hachoir_metadata.extractMetadata(self.parser) | ||
| 35 | except hachoir_core.error.HachoirError, err: | ||
| 36 | print "Metadata extraction error: %s" % unicode(err) | ||
| 37 | self.data = None | ||
| 38 | |||
| 39 | if not self.meta: | ||
| 40 | print "Unable to extract metadata" | ||
| 41 | sys.exit(1) | ||
| 42 | |||
| 43 | def is_clean(self): | ||
| 44 | ''' | ||
| 45 | Return true if the file is clean from any compromizing meta | ||
| 46 | ''' | ||
| 47 | return self.clean | ||
| 48 | |||
| 49 | def remove_all(self): | ||
| 50 | ''' | ||
| 51 | Remove all the files that are compromizing | ||
| 52 | ''' | ||
| 53 | stripEditor(self.editor, self.realname, level, not(values.quiet)) | ||
| 54 | for key, field in metadata: | ||
| 55 | if should_remove(key): | ||
| 56 | remove(self, key) | ||
| 57 | |||
| 58 | def remove(self, field): | ||
| 59 | ''' | ||
| 60 | Remove the given file | ||
| 61 | ''' | ||
| 62 | del editor[field] | ||
| 63 | return True | ||
| 64 | |||
| 65 | |||
| 66 | def get_meta(self): | ||
| 67 | '''return a dict with all the meta of the file''' | ||
| 68 | #FIXME : sooooooooooo dirty ! | ||
| 69 | for title in self.meta: | ||
| 70 | if title.values != []: #if the field is not empty | ||
| 71 | value = "" | ||
| 72 | for item in title.values: | ||
| 73 | value = item.text | ||
| 74 | self.metadata[title.key] = value | ||
| 75 | return self.metadata | ||
| 76 | |||
| 77 | def should_remove(self, field): | ||
| 78 | ''' | ||
| 79 | return True if the field is compromizing | ||
| 80 | abstract method | ||
| 81 | ''' | ||
| 82 | raise NotImplementedError() | ||
| 83 | |||
| 84 | def stripEditor(editor, filename, realname, level, verbose): | ||
| 85 | ''' | ||
| 86 | Assign a stripper to an editor | ||
| 87 | ''' | ||
| 88 | cls = editor.input.__class__ | ||
| 89 | try: | ||
| 90 | stripper_cls = strippers[cls] | ||
| 91 | except KeyError: | ||
| 92 | print "Don't have stripper for file type: %s" % editor.description | ||
| 93 | return False | ||
| 94 | stripper = stripper_cls(editor, level, verbose) | ||
| 95 | |||
| 96 | if stripper(): | ||
| 97 | output = FileOutputStream(filename, realname) | ||
| 98 | editor.writeInto(output) | ||
| 99 | |||
| 100 | else: | ||
| 101 | print _("Stripper doesn't touch the file") | ||
| 102 | return True | ||
| 103 | |||
| 104 | file(sys.argv[1]).get_meta() | ||
diff --git a/lib/strippers.py b/lib/strippers.py deleted file mode 100644 index 70d0fc7..0000000 --- a/lib/strippers.py +++ /dev/null | |||
| @@ -1,3 +0,0 @@ | |||
| 1 | strippers = { | ||
| 2 | JpegFile: JpegStripper, | ||
| 3 | } | ||
diff --git a/lib/test.py b/lib/test.py deleted file mode 100644 index b1ff2a3..0000000 --- a/lib/test.py +++ /dev/null | |||
| @@ -1,70 +0,0 @@ | |||
| 1 | import mat | ||
| 2 | import unittest | ||
| 3 | import shutil | ||
| 4 | import glob | ||
| 5 | import tempfile | ||
| 6 | |||
| 7 | FILE_LIST = zip(glob.glob('clean*'), glob.glob('dirty*')) | ||
| 8 | |||
| 9 | class MATTest(unittest.TestCase): | ||
| 10 | def setUp(self): | ||
| 11 | '''create working copy of the clean and the dirty file in the TMP dir''' | ||
| 12 | self.file_list = [] | ||
| 13 | self.tmpdir = tempfile.mkdtemp() | ||
| 14 | |||
| 15 | for clean, dirty in FILE_LIST: | ||
| 16 | shutil.copy2(clean, self.tmpdir + clean) | ||
| 17 | shutil.copy2(dirty, self.tmpdir + dirty) | ||
| 18 | self.file_list.append((self.tmpdir + clean, self.tmpdir + dirty)) | ||
| 19 | |||
| 20 | def tearDown(self): | ||
| 21 | '''Remove the tmp folder''' | ||
| 22 | shutil.rmtree(self.tmpdir) | ||
| 23 | |||
| 24 | class Test_Remove(MATTest): | ||
| 25 | def test_remove(self): | ||
| 26 | '''make sure that the lib remove all compromizing meta''' | ||
| 27 | for clean, dirty in self.file_list: | ||
| 28 | mat.file(dirty).remove_all() | ||
| 29 | self.assertTrue(mat.file(dirty).is_clean()) | ||
| 30 | |||
| 31 | def test_remove_empty(self): | ||
| 32 | '''Test removal with clean files''' | ||
| 33 | for clean, dirty in self.file_list: | ||
| 34 | mat.file(clean).remove_all() | ||
| 35 | self.assertTrue(mat.file(clean).is_clean()) | ||
| 36 | |||
| 37 | |||
| 38 | class Test_List(MATTest): | ||
| 39 | def test_list(self): | ||
| 40 | '''check if get_meta returns all the expected meta''' | ||
| 41 | for clean, dirty in self.file_list: | ||
| 42 | meta_list = dict() #FIXME | ||
| 43 | self.assertDictEqual(mat.file(dirty).get_meta(), meta_list) | ||
| 44 | |||
| 45 | def testlist_list_empty(self): | ||
| 46 | '''check that a listing of a clean file return an empty dict''' | ||
| 47 | for clean, dirty in self.file_list: | ||
| 48 | self.assertEqual(mat.file(clean).get_meta(), None) | ||
| 49 | |||
| 50 | |||
| 51 | class Test_isClean(MATTest): | ||
| 52 | def test_clean(self): | ||
| 53 | '''test is_clean on clean files''' | ||
| 54 | for clean, dirty in self.file_list: | ||
| 55 | print "e" | ||
| 56 | self.assertTrue(mat.file(clean).is_clean()) | ||
| 57 | |||
| 58 | def test_clean(self): | ||
| 59 | '''test is_clean on dirty files''' | ||
| 60 | for clean, dirty in self.file_list: | ||
| 61 | self.assertFalse(mat.file(dirty).is_clean()) | ||
| 62 | |||
| 63 | |||
| 64 | if __name__ == '__main__': | ||
| 65 | suite = unittest.TestSuite() | ||
| 66 | suite.addTest(unittest.makeSuite(Test_Remove)) | ||
| 67 | suite.addTest(unittest.makeSuite(Test_List)) | ||
| 68 | suite.addTest(unittest.makeSuite(Test_isClean)) | ||
| 69 | unittest.TextTestRunner(verbosity=2).run(suite) | ||
| 70 | |||
