46122
|
1 ;;; bindat.el --- binary data structure packing and unpacking.
|
|
2
|
|
3 ;; Copyright (C) 2002 Free Software Foundation, Inc.
|
|
4
|
|
5 ;; Author: Kim F. Storm <storm@cua.dk>
|
|
6 ;; Assignment name: struct.el
|
|
7 ;; Keywords: comm data processes
|
|
8
|
|
9 ;; This file is part of GNU Emacs.
|
|
10
|
|
11 ;; GNU Emacs is free software; you can redistribute it and/or modify
|
|
12 ;; it under the terms of the GNU General Public License as published by
|
|
13 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
14 ;; any later version.
|
|
15
|
|
16 ;; GNU Emacs is distributed in the hope that it will be useful,
|
|
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19 ;; GNU General Public License for more details.
|
|
20
|
|
21 ;; You should have received a copy of the GNU General Public License
|
|
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
|
|
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
24 ;; Boston, MA 02111-1307, USA.
|
|
25
|
|
26 ;;; Commentary:
|
|
27
|
|
28 ;; Packing and unpacking of (binary) data structures.
|
|
29 ;;
|
|
30 ;; The data formats used in binary files and network protocols are
|
|
31 ;; often structed data which can be described by a C-style structure
|
|
32 ;; such as the one shown below. Using the bindat package, decoding
|
|
33 ;; and encoding binary data formats like these is made simple using a
|
|
34 ;; structure specification which closely resembles the C style
|
|
35 ;; structure declarations.
|
|
36 ;;
|
|
37 ;; Encoded (binary) data is stored in a unibyte string or vector,
|
|
38 ;; while the decoded data is stored in an alist with (FIELD . VALUE)
|
|
39 ;; pairs.
|
|
40
|
|
41 ;; Example:
|
|
42
|
|
43 ;; Consider the following C structures:
|
|
44 ;;
|
|
45 ;; struct header {
|
|
46 ;; unsigned long dest_ip;
|
|
47 ;; unsigned long src_ip;
|
|
48 ;; unsigned short dest_port;
|
|
49 ;; unsigned short src_port;
|
|
50 ;; };
|
|
51 ;;
|
|
52 ;; struct data {
|
|
53 ;; unsigned char type;
|
|
54 ;; unsigned char opcode;
|
|
55 ;; unsigned long length; /* In little endian order */
|
|
56 ;; unsigned char id[8]; /* nul-terminated string */
|
|
57 ;; unsigned char data[/* (length + 3) & ~3 */];
|
|
58 ;; };
|
|
59 ;;
|
|
60 ;; struct packet {
|
|
61 ;; struct header header;
|
|
62 ;; unsigned char items;
|
|
63 ;; unsigned char filler[3];
|
|
64 ;; struct data item[/* items */];
|
|
65 ;; };
|
|
66 ;;
|
|
67 ;; The corresponding Lisp bindat specification looks like this:
|
|
68 ;;
|
|
69 ;; (setq header-spec
|
|
70 ;; '((dest-ip ip)
|
|
71 ;; (src-ip ip)
|
|
72 ;; (dest-port u16)
|
|
73 ;; (src-port u16)))
|
|
74 ;;
|
|
75 ;; (setq data-spec
|
|
76 ;; '((type u8)
|
|
77 ;; (opcode u8)
|
|
78 ;; (length u16r) ;; little endian order
|
|
79 ;; (id strz 8)
|
|
80 ;; (data vec (length))
|
|
81 ;; (align 4)))
|
|
82 ;;
|
|
83 ;; (setq packet-spec
|
|
84 ;; '((header struct header-spec)
|
|
85 ;; (items u8)
|
|
86 ;; (fill 3)
|
|
87 ;; (item repeat (items)
|
|
88 ;; ((struct data-spec)))))
|
|
89 ;;
|
|
90 ;;
|
|
91 ;; A binary data representation may look like
|
|
92 ;; [ 192 168 1 100 192 168 1 101 01 28 21 32 2 0 0 0
|
|
93 ;; 2 3 5 0 ?A ?B ?C ?D ?E ?F 0 0 1 2 3 4 5 0 0 0
|
|
94 ;; 1 4 7 0 ?B ?C ?D ?E ?F ?G 0 0 6 7 8 9 10 11 12 0 ]
|
|
95 ;;
|
|
96 ;; The corresponding decoded structure looks like
|
|
97 ;;
|
|
98 ;; ((header
|
|
99 ;; (dest-ip . [192 168 1 100])
|
|
100 ;; (src-ip . [192 168 1 101])
|
|
101 ;; (dest-port . 284)
|
|
102 ;; (src-port . 5408))
|
|
103 ;; (items . 2)
|
|
104 ;; (item ((data . [1 2 3 4 5])
|
|
105 ;; (id . "ABCDEF")
|
|
106 ;; (length . 5)
|
|
107 ;; (opcode . 3)
|
|
108 ;; (type . 2))
|
|
109 ;; ((data . [6 7 8 9 10 11 12])
|
|
110 ;; (id . "BCDEFG")
|
|
111 ;; (length . 7)
|
|
112 ;; (opcode . 4)
|
|
113 ;; (type . 1))))
|
|
114 ;;
|
|
115 ;; To access a specific value in this structure, use the function
|
|
116 ;; bindat-get-field with the structure as first arg followed by a list
|
|
117 ;; of field names and array indexes, e.g. using the data above,
|
|
118 ;; (bindat-get-field decoded-structure 'item 1 'id)
|
|
119 ;; returns "BCDEFG".
|
|
120
|
|
121 ;; Binary Data Structure Specification Format
|
|
122 ;; ------------------------------------------
|
|
123
|
|
124 ;; The data specification is formatted as follows:
|
|
125
|
|
126 ;; SPEC ::= ( ITEM... )
|
|
127
|
|
128 ;; ITEM ::= ( [FIELD] TYPE )
|
|
129 ;; | ( [FIELD] eval FORM ) -- eval FORM for side-effect only
|
|
130 ;; | ( [FIELD] fill LEN ) -- skip LEN bytes
|
|
131 ;; | ( [FIELD] align LEN ) -- skip to next multiple of LEN bytes
|
|
132 ;; | ( [FIELD] struct SPEC_NAME )
|
|
133 ;; | ( [FIELD] union TAG_VAL (TAG SPEC)... [(t SPEC)] )
|
|
134 ;; | ( [FIELD] repeat COUNT SPEC )
|
|
135
|
|
136 ;; -- In (eval EXPR), the value of the last field is available in
|
|
137 ;; the dynamically bound variable `last'.
|
|
138
|
|
139 ;; TYPE ::= ( eval EXPR ) -- interpret result as TYPE
|
|
140 ;; | u8 | byte -- length 1
|
|
141 ;; | u16 | word | short -- length 2, network byte order
|
|
142 ;; | u24 -- 3-byte value
|
|
143 ;; | u32 | dword | long -- length 4, network byte order
|
|
144 ;; | u16r | u24r | u32r -- little endian byte order.
|
|
145 ;; | str LEN -- LEN byte string
|
|
146 ;; | strz LEN -- LEN byte (zero-terminated) string
|
|
147 ;; | vec LEN -- LEN byte vector
|
|
148 ;; | ip -- 4 byte vector
|
|
149 ;; | bits LEN -- List with bits set in LEN bytes.
|
|
150 ;;
|
|
151 ;; -- Note: 32 bit values may be limited by emacs' INTEGER
|
|
152 ;; implementation limits.
|
|
153 ;;
|
|
154 ;; -- Example: bits 2 will map bytes 0x1c 0x28 to list (2 3 7 11 13)
|
|
155
|
|
156 ;; FIELD ::= ( eval EXPR ) -- use result as NAME
|
|
157 ;; | NAME
|
|
158
|
|
159 ;; LEN ::= ARG
|
|
160 ;; | <omitted> | nil -- LEN = 1
|
|
161
|
|
162
|
|
163 ;; TAG_VAL ::= ARG
|
|
164
|
|
165 ;; TAG ::= LISP_CONSTANT
|
|
166 ;; | ( eval EXPR ) -- return non-nil if tag match;
|
|
167 ;; current TAG_VAL in `tag'.
|
|
168
|
|
169 ;; ARG ::= ( eval EXPR ) -- interpret result as ARG
|
|
170 ;; | INTEGER_CONSTANT
|
|
171 ;; | DEREF
|
|
172
|
|
173 ;; DEREF ::= ( [NAME | INTEGER]... ) -- Field NAME or Array index relative to
|
|
174 ;; current structure spec.
|
|
175 ;; -- see bindat-get-field
|
|
176
|
|
177 ;; A `union' specification
|
|
178 ;; ([FIELD] union TAG_VAL (TAG SPEC) ... [(t SPEC)])
|
|
179 ;; is interpreted by evalling TAG_VAL and then comparing that to
|
|
180 ;; each TAG using equal; if a match is found, the corresponding SPEC
|
|
181 ;; is used.
|
|
182 ;; If TAG is a form (eval EXPR), EXPR is evalled with `tag' bound to the
|
|
183 ;; value of TAG_VAL; the corresponding SPEC is used if the result is non-nil.
|
|
184 ;; Finally, if TAG is t, the corresponding SPEC is used unconditionally.
|
|
185 ;;
|
|
186 ;; An `eval' specification
|
|
187 ;; ([FIELD] eval FORM)
|
|
188 ;; is interpreted by evalling FORM for its side effects only.
|
|
189 ;; If FIELD is specified, the value is bound to that field.
|
|
190 ;; The FORM may access and update `raw-data' and `pos' (see `bindat-unpack'),
|
|
191 ;; as well as the lisp data structure in `struct'.
|
|
192
|
|
193 ;;; Code:
|
|
194
|
|
195 ;; Helper functions for structure unpacking.
|
|
196 ;; Relies on dynamic binding of RAW-DATA and POS
|
|
197
|
|
198 (defvar raw-data)
|
|
199 (defvar pos)
|
|
200
|
|
201 (defun bindat--unpack-u8 ()
|
|
202 (prog1
|
|
203 (if (stringp raw-data)
|
|
204 (string-to-char (substring raw-data pos (1+ pos)))
|
|
205 (aref raw-data pos))
|
|
206 (setq pos (1+ pos))))
|
|
207
|
|
208 (defun bindat--unpack-u16 ()
|
|
209 (let* ((a (bindat--unpack-u8)) (b (bindat--unpack-u8)))
|
|
210 (logior (lsh a 8) b)))
|
|
211
|
|
212 (defun bindat--unpack-u24 ()
|
|
213 (let* ((a (bindat--unpack-u16)) (b (bindat--unpack-u8)))
|
|
214 (logior (lsh a 8) b)))
|
|
215
|
|
216 (defun bindat--unpack-u32 ()
|
|
217 (let* ((a (bindat--unpack-u16)) (b (bindat--unpack-u16)))
|
|
218 (logior (lsh a 16) b)))
|
|
219
|
|
220 (defun bindat--unpack-u16r ()
|
|
221 (let* ((a (bindat--unpack-u8)) (b (bindat--unpack-u8)))
|
|
222 (logior a (lsh b 8))))
|
|
223
|
|
224 (defun bindat--unpack-u24r ()
|
|
225 (let* ((a (bindat--unpack-u16r)) (b (bindat--unpack-u8)))
|
|
226 (logior a (lsh b 16))))
|
|
227
|
|
228 (defun bindat--unpack-u32r ()
|
|
229 (let* ((a (bindat--unpack-u16r)) (b (bindat--unpack-u16r)))
|
|
230 (logior a (lsh b 16))))
|
|
231
|
|
232 (defun bindat--unpack-item (type len)
|
|
233 (if (eq type 'ip)
|
|
234 (setq type 'vec len 4))
|
|
235 (cond
|
|
236 ((memq type '(u8 byte))
|
|
237 (bindat--unpack-u8))
|
|
238 ((memq type '(u16 word short))
|
|
239 (bindat--unpack-u16))
|
|
240 ((eq type 'u24)
|
|
241 (bindat--unpack-u24))
|
|
242 ((memq type '(u32 dword long))
|
|
243 (bindat--unpack-u32))
|
|
244 ((eq type 'u16r)
|
|
245 (bindat--unpack-u16r))
|
|
246 ((eq type 'u24r)
|
|
247 (bindat--unpack-u24r))
|
|
248 ((eq type 'u32r)
|
|
249 (bindat--unpack-u32r))
|
|
250 ((eq type 'bits)
|
|
251 (let ((bits nil) (bnum (1- (* 8 len))) j m)
|
|
252 (while (>= bnum 0)
|
|
253 (if (= (setq m (bindat--unpack-u8)) 0)
|
|
254 (setq bnum (- bnum 8))
|
|
255 (setq j 128)
|
|
256 (while (> j 0)
|
|
257 (if (/= 0 (logand m j))
|
|
258 (setq bits (cons bnum bits)))
|
|
259 (setq bnum (1- bnum)
|
|
260 j (lsh j -1)))))
|
|
261 bits))
|
|
262 ((eq type 'str)
|
|
263 (let ((s (substring raw-data pos (+ pos len))))
|
|
264 (setq pos (+ pos len))
|
|
265 (if (stringp s) s
|
|
266 (string-make-unibyte (concat s)))))
|
|
267 ((eq type 'strz)
|
|
268 (let ((i 0) s)
|
|
269 (while (and (< i len) (/= (aref raw-data (+ pos i)) 0))
|
|
270 (setq i (1+ i)))
|
|
271 (setq s (substring raw-data pos (+ pos i)))
|
|
272 (setq pos (+ pos len))
|
|
273 (if (stringp s) s
|
|
274 (string-make-unibyte (concat s)))))
|
|
275 ((eq type 'vec)
|
|
276 (let ((v (make-vector len 0)) (i 0))
|
|
277 (while (< i len)
|
|
278 (aset v i (bindat--unpack-u8))
|
|
279 (setq i (1+ i)))
|
|
280 v))
|
|
281 (t nil)))
|
|
282
|
|
283 (defun bindat--unpack-group (spec)
|
|
284 (let (struct last)
|
|
285 (while spec
|
|
286 (let* ((item (car spec))
|
|
287 (field (car item))
|
|
288 (type (nth 1 item))
|
|
289 (len (nth 2 item))
|
|
290 (tail 3)
|
|
291 data)
|
|
292 (setq spec (cdr spec))
|
|
293 (if (and (consp field) (eq (car field) 'eval))
|
|
294 (setq field (eval (car (cdr field)))))
|
|
295 (if (and type (consp type) (eq (car type) 'eval))
|
|
296 (setq type (eval (car (cdr type)))))
|
|
297 (if (and len (consp len) (eq (car len) 'eval))
|
|
298 (setq len (eval (car (cdr len)))))
|
|
299 (if (memq field '(eval fill align struct union))
|
|
300 (setq tail 2
|
|
301 len type
|
|
302 type field
|
|
303 field nil))
|
|
304 (if (and (consp len) (not (eq type 'eval)))
|
|
305 (setq len (apply 'bindat-get-field struct len)))
|
|
306 (if (not len)
|
|
307 (setq len 1))
|
|
308 (cond
|
|
309 ((eq type 'eval)
|
|
310 (if field
|
|
311 (setq data (eval len))
|
|
312 (eval len)))
|
|
313 ((eq type 'fill)
|
|
314 (setq pos (+ pos len)))
|
|
315 ((eq type 'align)
|
|
316 (while (/= (% pos len) 0)
|
|
317 (setq pos (1+ pos))))
|
|
318 ((eq type 'struct)
|
|
319 (setq data (bindat--unpack-group (eval len))))
|
|
320 ((eq type 'repeat)
|
|
321 (let ((index 0))
|
|
322 (while (< index len)
|
|
323 (setq data (cons (bindat--unpack-group (nthcdr tail item)) data))
|
|
324 (setq index (1+ index)))
|
|
325 (setq data (nreverse data))))
|
|
326 ((eq type 'union)
|
|
327 (let ((tag len) (cases (nthcdr tail item)) case cc)
|
|
328 (while cases
|
|
329 (setq case (car cases)
|
|
330 cases (cdr cases)
|
|
331 cc (car case))
|
|
332 (if (or (equal cc tag) (equal cc t)
|
|
333 (and (consp cc) (eval cc)))
|
|
334 (setq data (bindat--unpack-group (cdr case))
|
|
335 cases nil)))))
|
|
336 (t
|
|
337 (setq data (bindat--unpack-item type len)
|
|
338 last data)))
|
|
339 (if data
|
|
340 (if field
|
|
341 (setq struct (cons (cons field data) struct))
|
|
342 (setq struct (append data struct))))))
|
|
343 struct))
|
|
344
|
|
345 (defun bindat-unpack (spec raw-data &optional pos)
|
|
346 "Return structured data according to SPEC for binary data in RAW-DATA.
|
|
347 RAW-DATA is a string or vector. Optional third arg POS specifies the
|
|
348 starting offset in RAW-DATA."
|
|
349 (unless pos (setq pos 0))
|
|
350 (bindat--unpack-group spec))
|
|
351
|
|
352 (defun bindat-get-field (struct &rest field)
|
|
353 "In structured data STRUCT, return value of field named FIELD.
|
|
354 If multiple field names are specified, use the field names to
|
|
355 lookup nested sub-structures in STRUCT, corresponding to the
|
|
356 C-language syntax STRUCT.FIELD1.FIELD2.FIELD3...
|
|
357 An integer value in the field list is taken as an array index,
|
|
358 e.g. corresponding to STRUCT.FIELD1[INDEX2].FIELD3..."
|
|
359 (while (and struct field)
|
|
360 (setq struct (if (integerp (car field))
|
|
361 (nth (car field) struct)
|
|
362 (let ((val (assq (car field) struct)))
|
|
363 (if (consp val) (cdr val)))))
|
|
364 (setq field (cdr field)))
|
|
365 struct)
|
|
366
|
|
367
|
|
368 ;; Calculate raw-data length of structured data
|
|
369
|
|
370 (defvar bindat--fixed-length-alist
|
|
371 '((u8 . 1) (byte . 1)
|
|
372 (u16 . 2) (u16r . 2) (word . 2) (short . 2)
|
|
373 (u24 . 3) (u24r . 3)
|
|
374 (u32 . 4) (u32r . 4) (dword . 4) (long . 4)
|
|
375 (ip . 4)))
|
|
376
|
|
377 (defun bindat--length-group (struct spec)
|
|
378 (let (last)
|
|
379 (while spec
|
|
380 (let* ((item (car spec))
|
|
381 (field (car item))
|
|
382 (type (nth 1 item))
|
|
383 (len (nth 2 item))
|
|
384 (tail 3))
|
|
385 (setq spec (cdr spec))
|
|
386 (if (and (consp field) (eq (car field) 'eval))
|
|
387 (setq field (eval (car (cdr field)))))
|
|
388 (if (and type (consp type) (eq (car type) 'eval))
|
|
389 (setq type (eval (car (cdr type)))))
|
|
390 (if (and len (consp len) (eq (car len) 'eval))
|
|
391 (setq len (eval (car (cdr len)))))
|
|
392 (if (memq field '(eval fill align struct union))
|
|
393 (setq tail 2
|
|
394 len type
|
|
395 type field
|
|
396 field nil))
|
|
397 (if (and (consp len) (not (eq type 'eval)))
|
|
398 (setq len (apply 'bindat-get-field struct len)))
|
|
399 (if (not len)
|
|
400 (setq len 1))
|
|
401 (cond
|
|
402 ((eq type 'eval)
|
|
403 (if field
|
|
404 (setq struct (cons (cons field (eval len)) struct))
|
|
405 (eval len)))
|
|
406 ((eq type 'fill)
|
|
407 (setq pos (+ pos len)))
|
|
408 ((eq type 'align)
|
|
409 (while (/= (% pos len) 0)
|
|
410 (setq pos (1+ pos))))
|
|
411 ((eq type 'struct)
|
|
412 (bindat--length-group
|
|
413 (if field (bindat-get-field struct field) struct) (eval len)))
|
|
414 ((eq type 'repeat)
|
|
415 (let ((index 0))
|
|
416 (while (< index len)
|
|
417 (bindat--length-group (nth index (bindat-get-field struct field)) (nthcdr tail item))
|
|
418 (setq index (1+ index)))))
|
|
419 ((eq type 'union)
|
|
420 (let ((tag len) (cases (nthcdr tail item)) case cc)
|
|
421 (while cases
|
|
422 (setq case (car cases)
|
|
423 cases (cdr cases)
|
|
424 cc (car case))
|
|
425 (if (or (equal cc tag) (equal cc t)
|
|
426 (and (consp cc) (eval cc)))
|
|
427 (progn
|
|
428 (bindat--length-group struct (cdr case))
|
|
429 (setq cases nil))))))
|
|
430 (t
|
|
431 (if (setq type (assq type bindat--fixed-length-alist))
|
|
432 (setq len (cdr type)))
|
|
433 (if field
|
|
434 (setq last (bindat-get-field struct field)))
|
|
435 (setq pos (+ pos len))))))))
|
|
436
|
|
437 (defun bindat-length (spec struct)
|
|
438 "Calculate raw-data length for STRUCT according to bindat specification SPEC."
|
|
439 (let ((pos 0))
|
|
440 (bindat--length-group struct spec)
|
|
441 pos))
|
|
442
|
|
443
|
|
444 ;; Pack structured data into raw-data
|
|
445
|
|
446 (defun bindat--pack-u8 (v)
|
|
447 (aset raw-data pos (logand v 255))
|
|
448 (setq pos (1+ pos)))
|
|
449
|
|
450 (defun bindat--pack-u16 (v)
|
|
451 (aset raw-data pos (logand (lsh v -8) 255))
|
|
452 (aset raw-data (1+ pos) (logand v 255))
|
|
453 (setq pos (+ pos 2)))
|
|
454
|
|
455 (defun bindat--pack-u24 (v)
|
|
456 (bindat--pack-u8 (lsh v -16))
|
|
457 (bindat--pack-u16 v))
|
|
458
|
|
459 (defun bindat--pack-u32 (v)
|
|
460 (bindat--pack-u16 (lsh v -16))
|
|
461 (bindat--pack-u16 v))
|
|
462
|
|
463 (defun bindat--pack-u16r (v)
|
|
464 (aset raw-data (1+ pos) (logand (lsh v -8) 255))
|
|
465 (aset raw-data pos (logand v 255))
|
|
466 (setq pos (+ pos 2)))
|
|
467
|
|
468 (defun bindat--pack-u24r (v)
|
|
469 (bindat--pack-u16r v)
|
|
470 (bindat--pack-u8 (lsh v -16)))
|
|
471
|
|
472 (defun bindat--pack-u32r (v)
|
|
473 (bindat--pack-u16r v)
|
|
474 (bindat--pack-u16r (lsh v -16)))
|
|
475
|
|
476 (defun bindat--pack-item (v type len)
|
|
477 (if (eq type 'ip)
|
|
478 (setq type 'vec len 4))
|
|
479 (cond
|
|
480 ((null v)
|
|
481 (setq pos (+ pos len)))
|
|
482 ((memq type '(u8 byte))
|
|
483 (bindat--pack-u8 v))
|
|
484 ((memq type '(u16 word short))
|
|
485 (bindat--pack-u16 v))
|
|
486 ((eq type 'u24)
|
|
487 (bindat--pack-u24 v))
|
|
488 ((memq type '(u32 dword long))
|
|
489 (bindat--pack-u32 v))
|
|
490 ((eq type 'u16r)
|
|
491 (bindat--pack-u16r v))
|
|
492 ((eq type 'u24r)
|
|
493 (bindat--pack-u24r v))
|
|
494 ((eq type 'u32r)
|
|
495 (bindat--pack-u32r v))
|
|
496 ((eq type 'bits)
|
|
497 (let ((bnum (1- (* 8 len))) j m)
|
|
498 (while (>= bnum 0)
|
|
499 (setq m 0)
|
|
500 (if (null v)
|
|
501 (setq bnum (- bnum 8))
|
|
502 (setq j 128)
|
|
503 (while (> j 0)
|
|
504 (if (memq bnum v)
|
|
505 (setq m (logior m j)))
|
|
506 (setq bnum (1- bnum)
|
|
507 j (lsh j -1))))
|
|
508 (bindat--pack-u8 m))))
|
|
509 ((memq type '(str strz vec))
|
|
510 (let ((l (length v)) (i 0))
|
|
511 (if (> l len) (setq l len))
|
|
512 (while (< i l)
|
|
513 (aset raw-data (+ pos i) (aref v i))
|
|
514 (setq i (1+ i)))
|
|
515 (setq pos (+ pos len))))
|
|
516 (t
|
|
517 (setq pos (+ pos len)))))
|
|
518
|
|
519 (defun bindat--pack-group (struct spec)
|
|
520 (let (last)
|
|
521 (while spec
|
|
522 (let* ((item (car spec))
|
|
523 (field (car item))
|
|
524 (type (nth 1 item))
|
|
525 (len (nth 2 item))
|
|
526 (tail 3))
|
|
527 (setq spec (cdr spec))
|
|
528 (if (and (consp field) (eq (car field) 'eval))
|
|
529 (setq field (eval (car (cdr field)))))
|
|
530 (if (and type (consp type) (eq (car type) 'eval))
|
|
531 (setq type (eval (car (cdr type)))))
|
|
532 (if (and len (consp len) (eq (car len) 'eval))
|
|
533 (setq len (eval (car (cdr len)))))
|
|
534 (if (memq field '(eval fill align struct union))
|
|
535 (setq tail 2
|
|
536 len type
|
|
537 type field
|
|
538 field nil))
|
|
539 (if (and (consp len) (not (eq type 'eval)))
|
|
540 (setq len (apply 'bindat-get-field struct len)))
|
|
541 (if (not len)
|
|
542 (setq len 1))
|
|
543 (cond
|
|
544 ((eq type 'eval)
|
|
545 (if field
|
|
546 (setq struct (cons (cons field (eval len)) struct))
|
|
547 (eval len)))
|
|
548 ((eq type 'fill)
|
|
549 (setq pos (+ pos len)))
|
|
550 ((eq type 'align)
|
|
551 (while (/= (% pos len) 0)
|
|
552 (setq pos (1+ pos))))
|
|
553 ((eq type 'struct)
|
|
554 (bindat--pack-group
|
|
555 (if field (bindat-get-field struct field) struct) (eval len)))
|
|
556 ((eq type 'repeat)
|
|
557 (let ((index 0))
|
|
558 (while (< index len)
|
|
559 (bindat--pack-group (nth index (bindat-get-field struct field)) (nthcdr tail item))
|
|
560 (setq index (1+ index)))))
|
|
561 ((eq type 'union)
|
|
562 (let ((tag len) (cases (nthcdr tail item)) case cc)
|
|
563 (while cases
|
|
564 (setq case (car cases)
|
|
565 cases (cdr cases)
|
|
566 cc (car case))
|
|
567 (if (or (equal cc tag) (equal cc t)
|
|
568 (and (consp cc) (eval cc)))
|
|
569 (progn
|
|
570 (bindat--pack-group struct (cdr case))
|
|
571 (setq cases nil))))))
|
|
572 (t
|
|
573 (setq last (bindat-get-field struct field))
|
|
574 (bindat--pack-item last type len)
|
|
575 ))))))
|
|
576
|
|
577 (defun bindat-pack (spec struct &optional raw-data pos)
|
48031
|
578 "Return binary data packed according to SPEC for structured data STRUCT.
|
46122
|
579 Optional third arg RAW-DATA is a pre-allocated string or vector to unpack into.
|
|
580 Optional fourth arg POS is the starting offset into RAW-DATA.
|
|
581 Note: The result is a multibyte string; use `string-make-unibyte' on it
|
|
582 to make it unibyte if necessary."
|
|
583 (let ((no-return raw-data))
|
|
584 (unless pos (setq pos 0))
|
|
585 (unless raw-data (setq raw-data (make-vector (+ pos (bindat-length spec struct)) 0)))
|
|
586 (bindat--pack-group struct spec)
|
|
587 (if no-return nil (concat raw-data))))
|
|
588
|
|
589
|
|
590 ;; Misc. format conversions
|
|
591
|
|
592 (defun bindat-format-vector (vect fmt sep &optional len)
|
|
593 "Format vector VECT using element format FMT and separator SEP.
|
|
594 Result is a string with each element of VECT formatted using FMT and
|
|
595 separated by the string SEP. If optional fourth arg LEN is given, use
|
|
596 only that many elements from VECT."
|
|
597 (unless len
|
|
598 (setq len (length vect)))
|
|
599 (let ((i len) (fmt2 (concat sep fmt)) (s nil))
|
|
600 (while (> i 0)
|
|
601 (setq i (1- i)
|
|
602 s (cons (format (if (= i 0) fmt fmt2) (aref vect i)) s)))
|
|
603 (apply 'concat s)))
|
|
604
|
|
605 (defun bindat-vector-to-dec (vect &optional sep)
|
|
606 "Format vector VECT in decimal format separated by dots.
|
|
607 If optional second arg SEP is a string, use that as separator."
|
|
608 (bindat-format-vector vect "%d" (if (stringp sep) sep ".")))
|
|
609
|
|
610 (defun bindat-vector-to-hex (vect &optional sep)
|
|
611 "Format vector VECT in hex format separated by dots.
|
|
612 If optional second arg SEP is a string, use that as separator."
|
|
613 (bindat-format-vector vect "%02x" (if (stringp sep) sep ":")))
|
|
614
|
|
615 (defun bindat-ip-to-string (ip)
|
|
616 "Format vector IP as an ip address in dotted notation."
|
|
617 (format "%d.%d.%d.%d"
|
|
618 (aref ip 0) (aref ip 1) (aref ip 2) (aref ip 3)))
|
|
619
|
|
620 (provide 'bindat)
|
|
621
|
|
622 ;;; bindat.el ends here
|