dissect.cstruct 4.6.dev6__tar.gz → 4.6.dev7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/PKG-INFO +1 -1
  2. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/parser.py +67 -3
  3. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect.cstruct.egg-info/PKG-INFO +1 -1
  4. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_parser.py +121 -4
  5. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/.git-blame-ignore-revs +0 -0
  6. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/.gitattributes +0 -0
  7. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/CHANGELOG.md +0 -0
  8. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/COPYRIGHT +0 -0
  9. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/LICENSE +0 -0
  10. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/MANIFEST.in +0 -0
  11. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/README.md +0 -0
  12. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/__init__.py +0 -0
  13. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/bitbuffer.py +0 -0
  14. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/compiler.py +0 -0
  15. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/cstruct.py +0 -0
  16. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/exceptions.py +0 -0
  17. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/expression.py +0 -0
  18. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/tools/__init__.py +0 -0
  19. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/tools/stubgen.py +0 -0
  20. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/__init__.py +0 -0
  21. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/base.py +0 -0
  22. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/char.py +0 -0
  23. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/enum.py +0 -0
  24. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/flag.py +0 -0
  25. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/int.py +0 -0
  26. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/leb128.py +0 -0
  27. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/packed.py +0 -0
  28. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/pointer.py +0 -0
  29. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/structure.py +0 -0
  30. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/void.py +0 -0
  31. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/types/wchar.py +0 -0
  32. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect/cstruct/utils.py +0 -0
  33. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect.cstruct.egg-info/SOURCES.txt +0 -0
  34. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect.cstruct.egg-info/dependency_links.txt +0 -0
  35. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect.cstruct.egg-info/entry_points.txt +0 -0
  36. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect.cstruct.egg-info/requires.txt +0 -0
  37. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/dissect.cstruct.egg-info/top_level.txt +0 -0
  38. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/examples/disk.py +0 -0
  39. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/examples/mirai.py +0 -0
  40. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/examples/pe.py +0 -0
  41. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/examples/protobuf.py +0 -0
  42. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/examples/secdesc.py +0 -0
  43. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/pyproject.toml +0 -0
  44. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/setup.cfg +0 -0
  45. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/__init__.py +0 -0
  46. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/_data/testdef.txt +0 -0
  47. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/_docs/Makefile +0 -0
  48. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/_docs/__init__.py +0 -0
  49. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/_docs/conf.py +0 -0
  50. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/_docs/index.rst +0 -0
  51. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/conftest.py +0 -0
  52. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_align.py +0 -0
  53. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_annotations.py +0 -0
  54. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_basic.py +0 -0
  55. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_bitbuffer.py +0 -0
  56. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_bitfield.py +0 -0
  57. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_compiler.py +0 -0
  58. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_ctypes.py +0 -0
  59. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_expression.py +0 -0
  60. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_tools_stubgen.py +0 -0
  61. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_types_base.py +0 -0
  62. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_types_char.py +0 -0
  63. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_types_custom.py +0 -0
  64. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_types_enum.py +0 -0
  65. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_types_flag.py +0 -0
  66. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_types_int.py +0 -0
  67. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_types_leb128.py +0 -0
  68. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_types_packed.py +0 -0
  69. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_types_pointer.py +0 -0
  70. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_types_structure.py +0 -0
  71. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_types_union.py +0 -0
  72. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_types_void.py +0 -0
  73. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_types_wchar.py +0 -0
  74. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/test_utils.py +0 -0
  75. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tests/utils.py +0 -0
  76. {dissect_cstruct-4.6.dev6 → dissect_cstruct-4.6.dev7}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.cstruct
3
- Version: 4.6.dev6
3
+ Version: 4.6.dev7
4
4
  Summary: A Dissect module implementing a parser for C-like structures: structure parsing in Python made easy
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Apache License 2.0
@@ -49,12 +49,18 @@ class TokenParser(Parser):
49
49
  self.compiled = compiled
50
50
  self.align = align
51
51
  self.TOK = self._tokencollection()
52
+ self._conditionals = []
53
+ self._conditionals_depth = 0
52
54
 
53
55
  @staticmethod
54
56
  def _tokencollection() -> TokenCollection:
55
57
  TOK = TokenCollection()
56
58
  TOK.add(r"#\[(?P<values>[^\]]+)\](?=\s*)", "CONFIG_FLAG")
57
- TOK.add(r"#define\s+(?P<name>[^\s]+)\s+(?P<value>[^\r\n]+)\s*", "DEFINE")
59
+ TOK.add(r"#define\s+(?P<name>[^\s]+)(?P<value>[^\r\n]*)", "DEFINE")
60
+ TOK.add(r"#ifdef\s+(?P<name>[^\s]+)\s*", "IFDEF")
61
+ TOK.add(r"#ifndef\s+(?P<name>[^\s]+)\s*", "IFNDEF")
62
+ TOK.add(r"#else\s*", "ELSE")
63
+ TOK.add(r"#endif\s*", "ENDIF")
58
64
  TOK.add(r"typedef(?=\s)", "TYPEDEF")
59
65
  TOK.add(r"(?:struct|union)(?=\s|{)", "STRUCT")
60
66
  TOK.add(
@@ -80,12 +86,61 @@ class TokenParser(Parser):
80
86
  idents.append(tokens.consume())
81
87
  return " ".join([i.value for i in idents])
82
88
 
89
+ def _conditional(self, tokens: TokenConsumer) -> None:
90
+ token = tokens.consume()
91
+ pattern = self.TOK.patterns[token.token]
92
+ match = pattern.match(token.value).groupdict()
93
+
94
+ value = match["name"]
95
+
96
+ if token.token == self.TOK.IFDEF:
97
+ self._conditionals.append(value in self.cstruct.consts)
98
+ elif token.token == self.TOK.IFNDEF:
99
+ self._conditionals.append(value not in self.cstruct.consts)
100
+
101
+ def _check_conditional(self, tokens: TokenConsumer) -> bool:
102
+ """Check and handle conditionals. Return a boolean indicating if we need to continue to the next token."""
103
+ if self._conditionals and self._conditionals_depth == len(self._conditionals):
104
+ # If we have a conditional and the depth matches, handle it accordingly
105
+ if tokens.next == self.TOK.ELSE:
106
+ # Flip the last conditional
107
+ tokens.consume()
108
+ self._conditionals[-1] = not self._conditionals[-1]
109
+ return True
110
+
111
+ if tokens.next == self.TOK.ENDIF:
112
+ # Pop the last conditional
113
+ tokens.consume()
114
+ self._conditionals.pop()
115
+ self._conditionals_depth -= 1
116
+ return True
117
+
118
+ if tokens.next in (self.TOK.IFDEF, self.TOK.IFNDEF):
119
+ # If we encounter a new conditional, increase the depth
120
+ self._conditionals_depth += 1
121
+
122
+ if tokens.next == self.TOK.ENDIF:
123
+ # Similarly, decrease the depth if needed
124
+ self._conditionals_depth -= 1
125
+
126
+ if self._conditionals and not self._conditionals[-1]:
127
+ # If the last conditional evaluated to False, skip the next token
128
+ tokens.consume()
129
+ return True
130
+
131
+ if tokens.next in (self.TOK.IFDEF, self.TOK.IFNDEF):
132
+ # If the next token is a conditional, process it
133
+ self._conditional(tokens)
134
+ return True
135
+
136
+ return False
137
+
83
138
  def _constant(self, tokens: TokenConsumer) -> None:
84
139
  const = tokens.consume()
85
140
  pattern = self.TOK.patterns[self.TOK.DEFINE]
86
141
  match = pattern.match(const.value).groupdict()
87
142
 
88
- value = match["value"]
143
+ value = match["value"].strip()
89
144
  try:
90
145
  value = ast.literal_eval(value)
91
146
  except (ValueError, SyntaxError):
@@ -208,6 +263,9 @@ class TokenParser(Parser):
208
263
  tokens.consume()
209
264
  break
210
265
 
266
+ if self._check_conditional(tokens):
267
+ continue
268
+
211
269
  field = self._parse_field(tokens)
212
270
  fields.append(field)
213
271
 
@@ -266,7 +324,7 @@ class TokenParser(Parser):
266
324
  return Field(None, type_, None)
267
325
 
268
326
  if tokens.next != self.TOK.NAME:
269
- raise ParserError(f"line {self._lineno(tokens.next)}: expected name")
327
+ raise ParserError(f"line {self._lineno(tokens.next)}: expected name, got {tokens.next!r}")
270
328
  nametok = tokens.consume()
271
329
 
272
330
  type_, name, bits = self._parse_field_type(type_, nametok.value)
@@ -378,6 +436,9 @@ class TokenParser(Parser):
378
436
  if token is None:
379
437
  break
380
438
 
439
+ if self._check_conditional(tokens):
440
+ continue
441
+
381
442
  if token == self.TOK.CONFIG_FLAG:
382
443
  self._config_flag(tokens)
383
444
  elif token == self.TOK.DEFINE:
@@ -395,6 +456,9 @@ class TokenParser(Parser):
395
456
  else:
396
457
  raise ParserError(f"line {self._lineno(token)}: unexpected token {token!r}")
397
458
 
459
+ if self._conditionals:
460
+ raise ParserError(f"line {self._lineno(tokens.previous)}: unclosed conditional statement")
461
+
398
462
 
399
463
  class CStyleParser(Parser):
400
464
  """Definition parser for C-like structure syntax.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.cstruct
3
- Version: 4.6.dev6
3
+ Version: 4.6.dev7
4
4
  Summary: A Dissect module implementing a parser for C-like structures: structure parsing in Python made easy
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Apache License 2.0
@@ -1,18 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING
4
3
  from unittest.mock import Mock
5
4
 
6
5
  import pytest
7
6
 
7
+ from dissect.cstruct import cstruct
8
8
  from dissect.cstruct.exceptions import ParserError
9
9
  from dissect.cstruct.parser import TokenParser
10
10
  from dissect.cstruct.types import BaseArray, Pointer, Structure
11
11
  from tests.utils import verify_compiled
12
12
 
13
- if TYPE_CHECKING:
14
- from dissect.cstruct import cstruct
15
-
16
13
 
17
14
  def test_nested_structs(cs: cstruct, compiled: bool) -> None:
18
15
  cdef = """
@@ -164,3 +161,123 @@ def test_typedef_pointer(cs: cstruct) -> None:
164
161
  assert cs.IMAGE_DATA_DIRECTORY is cs._IMAGE_DATA_DIRECTORY
165
162
  assert issubclass(cs.PIMAGE_DATA_DIRECTORY, Pointer)
166
163
  assert cs.PIMAGE_DATA_DIRECTORY.type == cs._IMAGE_DATA_DIRECTORY
164
+
165
+
166
+ def test_conditional_ifdef(cs: cstruct) -> None:
167
+ cdef = """
168
+ #define MY_CONST 42
169
+
170
+ #ifdef MY_CONST
171
+ struct test {
172
+ uint32 a;
173
+ };
174
+ #endif
175
+ """
176
+ cs.load(cdef)
177
+
178
+ assert "test" in cs.typedefs
179
+
180
+
181
+ def test_conditional_ifndef(cs: cstruct) -> None:
182
+ cdef = """
183
+ #ifndef MYVAR
184
+ #define MYVAR (1)
185
+ #endif
186
+ """
187
+ cs.load(cdef)
188
+
189
+ assert "MYVAR" in cs.consts
190
+ assert cs.consts["MYVAR"] == 1
191
+
192
+
193
+ def test_conditional_ifndef_guard(cs: cstruct) -> None:
194
+ cdef = """
195
+ /* Define Guard */
196
+ #ifndef __MYGUARD
197
+ #define __MYGUARD
198
+
199
+ typedef struct myStruct
200
+ {
201
+ char charVal[16];
202
+ }
203
+ #endif // __MYGUARD
204
+ """
205
+ cs.load(cdef)
206
+
207
+ assert "__MYGUARD" in cs.consts
208
+ assert "myStruct" in cs.typedefs
209
+
210
+
211
+ def test_conditional_nested() -> None:
212
+ cdef = """
213
+ #ifndef MYSWITCH1
214
+ #define MYVAR1 (1)
215
+ #else
216
+ #ifdef MYSWITCH2
217
+ #define MYVAR1 (2)
218
+ #else
219
+ #define MYVAR1 (3)
220
+ #endif
221
+ #endif
222
+ """
223
+ cs = cstruct().load(cdef)
224
+
225
+ assert "MYVAR1" in cs.consts
226
+ assert cs.consts["MYVAR1"] == 1
227
+
228
+ cs = cstruct().load("#define MYSWITCH1")
229
+
230
+ assert "MYSWITCH1" in cs.consts
231
+
232
+ cs.load(cdef)
233
+
234
+ assert "MYVAR1" in cs.consts
235
+ assert cs.consts["MYVAR1"] == 3
236
+
237
+
238
+ def test_conditional_in_struct(cs: cstruct) -> None:
239
+ cdef = """
240
+ struct t_bitfield {
241
+ union {
242
+ struct {
243
+ uint32_t bit0:1;
244
+ uint32_t bit1:1;
245
+ #ifdef MYSWT
246
+ uint32_t bit2:1;
247
+ #endif
248
+ } fval;
249
+ uint32_t bits;
250
+ };
251
+ };
252
+ """
253
+ cs.load(cdef)
254
+
255
+ assert "t_bitfield" in cs.typedefs
256
+ assert "fval" in cs.t_bitfield.fields
257
+ assert "bit0" in cs.t_bitfield.fields["fval"].type.fields
258
+ assert "bit1" in cs.t_bitfield.fields["fval"].type.fields
259
+ assert "bit2" not in cs.t_bitfield.fields["fval"].type.fields
260
+
261
+
262
+ def test_conditional_parsing_error(cs: cstruct) -> None:
263
+ cdef = """
264
+ #ifndef __HELP
265
+ #define __HELP
266
+ #endif
267
+ struct test {
268
+ uint32 a;
269
+ };
270
+ #endif
271
+ """
272
+ with pytest.raises(ParserError, match="line 8: unexpected token .+ENDIF"):
273
+ cs.load(cdef)
274
+
275
+ cdef = """
276
+ #ifndef __HELP
277
+ #define __HELP
278
+ struct test {
279
+ uint32 a;
280
+ };
281
+ """
282
+ with pytest.raises(ParserError, match="line 6: unclosed conditional statement"):
283
+ cs.load(cdef)