comparison pack.c @ 56:770737295987

Added pack.c pack.h libpack-like for opening binary file endianness safe.
author David Demelier <markand@malikania.fr>
date Tue, 08 Nov 2011 18:26:05 +0100
parents
children 311eaa9d004c
comparison
equal deleted inserted replaced
55:cda80ba48029 56:770737295987
1 /*
2 * Copyright (c) 2011, David Demelier <markand@malikania.fr>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <sys/queue.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdint.h>
21 #include <stdarg.h>
22 #include <ctype.h>
23
24 #include "pack.h"
25
26 /* --------------------------------------------------------
27 * structure definitions
28 * -------------------------------------------------------- */
29
30 /*
31 * Conversion function pointer.
32 */
33 typedef void (*convert_fn)(void *);
34
35 /*
36 * Item structure store the integer into the largest data type
37 * uint64_t.
38 */
39
40 struct item {
41 size_t size; /* 8, 16, 32 or 64 bits? */
42 uint64_t i; /* the data */
43 convert_fn conv; /* conversion function */
44 STAILQ_ENTRY(item) next;
45 };
46
47 /*
48 * List of item structure.
49 */
50
51 STAILQ_HEAD(item_list, item);
52
53 /* --------------------------------------------------------
54 * prototypes
55 * -------------------------------------------------------- */
56
57 static int pack_item_add(struct item_list *, const struct item *);
58 static int pack_parse(struct item_list *, const char *, va_list);
59 static size_t pack_getsize(char);
60 static convert_fn pack_getconvert(char);
61 static void pack_convert16(void *);
62 static void pack_convert32(void *);
63 static void pack_convert64(void *);
64 static int pack_fatal(struct item_list *);
65
66 /* --------------------------------------------------------
67 * private functions
68 * -------------------------------------------------------- */
69
70 #define LENGTH(x) (sizeof (x) / sizeof (x[0]))
71
72 /*
73 * Macros that converts byte order or keep them if the host matches
74 * the byte order requested.
75 */
76
77 #define CONVERT_UINT16(i) do { \
78 if (ptype != PACK_HOST_BYTEORDER) \
79 i = pack_swap16(i); \
80 } while (/* CONSTCOND */ 0)
81
82 #define CONVERT_UINT32(i) do { \
83 if (ptype != PACK_HOST_BYTEORDER) \
84 i = pack_swap32(i); \
85 } while (/* CONSTCOND */ 0)
86
87 #define CONVERT_UINT64(i) do { \
88 if (ptype != PACK_HOST_BYTEORDER) \
89 i = pack_swap64(i); \
90 } while (/* CONSTCOND */ 0)
91
92 /*
93 * Associative structure, the `tok' member is used for the format
94 * and define the integer size. The convert function reverse the
95 * bytes if it is needed.
96 */
97
98 static struct integer {
99 char tok; /* format char */
100 size_t tocopy; /* size */
101 convert_fn convert; /* conversion function */
102 } sizes[] = {
103 { 'c', sizeof (uint8_t), NULL },
104 { 's', sizeof (uint16_t), &pack_convert16 },
105 { 'i', sizeof (uint32_t), &pack_convert32 },
106 { 'l', sizeof (uint64_t), &pack_convert64 }
107 };
108
109 /*
110 * Try to append a new item to the list. The function create a new item
111 * object since the pack_parse() use a stack'ed item and not an
112 * allocated object. Returns 0 or -1 on failure.
113 */
114
115 static int
116 pack_item_add(struct item_list *list, const struct item *item)
117 {
118 struct item *res;
119
120 if ((res = malloc(sizeof (struct item))) == NULL)
121 return -1;
122
123 res->size = item->size;
124 res->i = item->i;
125 res->conv = item->conv;
126
127 STAILQ_INSERT_TAIL(list, res, next);
128
129 return 0;
130 }
131
132 /*
133 * Parse the format, return 0 on success or -1 on failure.
134 */
135
136 #define PACK_GETARG(i, ap, tok) \
137 do { \
138 switch ((tok)) { \
139 case 'c': \
140 case 's': \
141 case 'i': \
142 (i) = va_arg((ap), unsigned); \
143 break; \
144 case 'l': \
145 (i) = va_arg((ap), uint64_t); \
146 default: \
147 break; \
148 } \
149 } while (/* CONSTCOND */ 0)
150
151 /*
152 * Little helper to get number of element when the user wants a array of
153 * objects. Sets the nelem to 0 on array failure or 1 on normal count.
154 */
155
156 #define PACK_GETNELEM(nelem, p) \
157 do { \
158 if (p[1] == '[') { \
159 if (p[2] != ']') \
160 nelem = (int) strtol(p + 2, NULL, 10); \
161 else if (p[2] == ']') \
162 nelem = va_arg(ap, int); \
163 \
164 /* Go to the end of fmt and find ']' */ \
165 while (*p != ']' && *p != '\0') \
166 ++p; \
167 } else \
168 nelem = 1; \
169 } while (/* CONSTCOND */ 0)
170
171 static int
172 pack_parse(struct item_list *list, const char *fmt, va_list ap)
173 {
174 const char *p;
175 char tok;
176 struct item item;
177
178 STAILQ_INIT(list);
179
180 for (p = fmt; *p != '\0'; ++p) {
181 int i, nelem = 1;
182
183 if (isspace(*p))
184 continue;
185
186 tok = *p;
187 item.size = pack_getsize(tok);
188
189 /* Bad character */
190 if (item.size == 0)
191 continue;
192
193 PACK_GETNELEM(nelem, p);
194 if (nelem == 0)
195 continue;
196
197 for (i = 0; i < nelem; ++i) {
198 PACK_GETARG(item.i, ap, tok);
199 item.conv = pack_getconvert(tok);
200
201 if (pack_item_add(list, &item) < 0)
202 return pack_fatal(list);
203 }
204 }
205
206 return 0;
207 }
208
209 /*
210 * Get the appropriate size associated with the `tok' character. If
211 * the token is not found the result is 0.
212 */
213
214 static size_t
215 pack_getsize(char tok)
216 {
217 struct integer *s;
218 unsigned int i;
219
220 for (s = sizes, i = 0; i < LENGTH(sizes); ++s, ++i)
221 if (s->tok == tok)
222 return s->tocopy;
223
224 return 0;
225 }
226
227 /*
228 * Return the conversion function.
229 */
230
231 static convert_fn
232 pack_getconvert(char tok)
233 {
234 struct integer *s;
235 unsigned int i;
236
237 for (s = sizes, i = 0; i < LENGTH(sizes); ++s, ++i)
238 if (s->tok == tok)
239 return s->convert;
240
241 return NULL;
242 }
243
244 /*
245 * Conversion functions. They reverse the bytes without any
246 * check.
247 */
248
249 static void
250 pack_convert16(void *obj)
251 {
252 uint16_t *x = obj;
253
254 *x = pack_swap16(*x);
255 }
256
257 static void
258 pack_convert32(void *obj)
259 {
260 uint32_t *x = obj;
261
262 *x = pack_swap32(*x);
263 }
264
265 static void
266 pack_convert64(void *obj)
267 {
268 uint64_t *x = obj;
269
270 *x = pack_swap64(*x);
271 }
272
273 static int
274 pack_fatal(struct item_list *list)
275 {
276 struct item *item, *tmp;
277
278 STAILQ_FOREACH_SAFE(item, list, next, tmp)
279 free(item);
280
281 return -1;
282 }
283
284 /* --------------------------------------------------------
285 * public functions
286 * -------------------------------------------------------- */
287
288 /*
289 * Function that writes everything to the file `path'. These functions
290 * does not append to the file, so if you want successive call to append
291 * variables use fpack instead.
292 * Returns 0 on success or -1 on failure.
293 */
294
295 int
296 pack(int ptype, const char *path, const char *fmt, ...)
297 {
298 va_list ap;
299 int status;
300
301 va_start(ap, fmt);
302 status = vpack(ptype, path, fmt, ap);
303 va_end(ap);
304
305 return status;
306 }
307
308 int
309 vpack(int ptype, const char *path, const char *fmt, va_list ap)
310 {
311 FILE *fp;
312 int status;
313
314 if ((fp = fopen(path, "w+b")) == NULL)
315 return -1;
316
317 status = vfpack(ptype, fp, fmt, ap);
318 fclose(fp);
319
320 return status;
321 }
322
323 /*
324 * Write to a file that is already open. The function does not call
325 * fclose() so you need to do it yourself later.
326 */
327
328 int
329 fpack(int ptype, FILE *fp, const char *fmt, ...)
330 {
331 va_list ap;
332 int status;
333
334 va_start(ap, fmt);
335 status = vfpack(ptype, fp, fmt, ap);
336 va_end(ap);
337
338 return status;
339 }
340
341 int
342 vfpack(int ptype, FILE *fp, const char *fmt, va_list ap)
343 {
344 struct item_list list;
345 int status;
346
347 if ((status = pack_parse(&list, fmt, ap)) == 0) {
348 struct item *item, *tmp;
349
350 STAILQ_FOREACH_SAFE(item, &list, next, tmp) {
351 /* 8 bits does not need to be converted */
352 if (ptype != PACK_HOST_BYTEORDER && item->conv != NULL)
353 item->conv(&item->i);
354
355 fwrite(&item->i, item->size, 1, fp);
356
357 free(item);
358 }
359 }
360
361 return status;
362 }
363
364 /*
365 * Function that read the binary file and restore values to the same format
366 * as pack functions. Arguments must be pointer of enough space to store
367 * the values.
368 */
369
370 int
371 unpack(int ptype, const char *path, const char *fmt, ...)
372 {
373 va_list ap;
374 int status;
375
376 va_start(ap, fmt);
377 status = vunpack(ptype, path, fmt, ap);
378 va_end(ap);
379
380 return status;
381 }
382
383 int
384 vunpack(int ptype, const char *path, const char *fmt, va_list ap)
385 {
386 FILE *fp;
387 int status;
388
389 if ((fp = fopen(path, "rb")) == NULL)
390 return -1;
391
392 status = vfunpack(ptype, fp, fmt, ap);
393 fclose(fp);
394
395 return status;
396
397 }
398
399 int
400 funpack(int ptype, FILE *fp, const char *fmt, ...)
401 {
402 va_list ap;
403 int status;
404
405 va_start(ap, fmt);
406 status = vfunpack(ptype, fp, fmt, ap);
407 va_end(ap);
408
409 return status;
410 }
411
412 int
413 vfunpack(int ptype, FILE *fp, const char *fmt, va_list ap)
414 {
415 const char *p;
416 void *ptr;
417 size_t tocopy;
418 convert_fn convert;
419
420 for (p = fmt; *p != '\0'; ++p) {
421 char tok;
422 int nelem, i;
423
424 if (isspace(*p))
425 continue;
426
427 tok = *p;
428 tocopy = pack_getsize(tok);
429
430 /* Bad character */
431 if (tocopy == 0)
432 continue;
433
434 /* Determine how many element to read and copy */
435 PACK_GETNELEM(nelem, p);
436 if (nelem == 0)
437 continue;
438
439 if ((ptr = va_arg(ap, void *)) == NULL)
440 continue;
441
442 for (i = 0; i < nelem; ++i) {
443 fread((char *) ptr + (tocopy * i), tocopy, 1, fp);
444
445 /* Convert if needed */
446 convert = pack_getconvert(tok);
447 if (ptype != PACK_HOST_BYTEORDER && convert != NULL)
448 convert((char *) ptr + (tocopy * i));
449 }
450 }
451
452 return 0;
453 }