Mercurial > code
view pack.c @ 137:97c9c20535e2
Just fix a -Wextra warning
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 26 Mar 2012 10:46:32 +0200 |
parents | 1d0e5580d402 |
children | 09fb5267c906 |
line wrap: on
line source
/* * pack.c -- endian dependant binary file reader * * Copyright (c) 2011, 2012, David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdarg.h> #include <string.h> #include <ctype.h> #include "pack.h" /* -------------------------------------------------------- * structure definitions * -------------------------------------------------------- */ typedef void (*ConvertFn)(void *); /* -------------------------------------------------------- * prototypes * -------------------------------------------------------- */ static size_t pack_getsize(char); static ConvertFn pack_getconvert_by_tok(char); static ConvertFn pack_getconvert_by_size(size_t); static void pack_convert16(uint16_t *); static void pack_convert32(uint32_t *); static void pack_convert64(uint64_t *); static void pack_write_one(int, FILE *, uint64_t, size_t); static void pack_write_multiple(int, FILE *, uint8_t *, int, size_t); /* -------------------------------------------------------- * private functions * -------------------------------------------------------- */ #define LENGTH(x) (sizeof (x) / sizeof (x[0])) /* * Associative structure, the `tok' member is used for the format * and define the integer size. The convert function reverse the * bytes if it is needed. */ static struct integer { char tok; /* format char */ size_t tocopy; /* size */ ConvertFn convert; /* conversion function */ } sizes[] = { { 'c', sizeof (uint8_t), NULL }, { 's', sizeof (uint16_t), (ConvertFn)pack_convert16 }, { 'i', sizeof (uint32_t), (ConvertFn)pack_convert32 }, { 'l', sizeof (uint64_t), (ConvertFn)pack_convert64 } }; /* * Parse the format, return 0 on success or -1 on failure. */ #define PACK_GETARG(i, ap, tok) \ do { \ switch ((tok)) { \ case 'c': \ case 's': \ case 'i': \ (i) = va_arg((ap), unsigned); \ break; \ case 'l': \ (i) = va_arg((ap), uint64_t); \ default: \ break; \ } \ } while (/* CONSTCOND */ 0) /* * Little helper to get number of element when the user wants a array of * objects. Sets the nelem to 0 on array failure or 1 on normal count. */ #define PACK_GETNELEM(nelem, p) \ do { \ if (p[1] == '[') { \ if (p[2] != ']') \ nelem = (int)strtol(p + 2, NULL, 10); \ else if (p[2] == ']') \ nelem = va_arg(ap, int); \ \ /* Go to the end of fmt and find ']' */ \ while (*p != ']' && *p != '\0') \ ++p; \ } else \ nelem = 1; \ } while (/* CONSTCOND */ 0) /* * Get the appropriate size associated with the `tok' character. If * the token is not found the result is 0. */ static size_t pack_getsize(char tok) { struct integer *s; unsigned int i; for (s = sizes, i = 0; i < LENGTH(sizes); ++s, ++i) if (s->tok == tok) return s->tocopy; return 0; } /* * Return the conversion function. */ static ConvertFn pack_getconvert_by_tok(char tok) { struct integer *s; unsigned int i; for (s = sizes, i = 0; i < LENGTH(sizes); ++s, ++i) if (s->tok == tok) return s->convert; return NULL; } /* * Same but by size. */ static ConvertFn pack_getconvert_by_size(size_t size) { struct integer *s; unsigned int i; for (s = sizes, i = 0; i < LENGTH(sizes); ++s, ++i) if (s->tocopy == size) return s->convert; return NULL; } /* * Conversion functions. They reverse the bytes without any * check. */ static void pack_convert16(uint16_t *x) { *x = pack_swap16(*x); } static void pack_convert32(uint32_t *x) { *x = pack_swap32(*x); } static void pack_convert64(uint64_t *x) { *x = pack_swap64(*x); } static void pack_write_one(int ptype, FILE *fp, uint64_t it, size_t size) { uint64_t cv = it; if (ptype != PACK_HOST_BYTEORDER) { ConvertFn conv = pack_getconvert_by_size(size); if (conv != NULL) conv(&cv); } fwrite(&cv, size, 1, fp); } static void pack_write_multiple(int ptype, FILE *fp, uint8_t *arr, int length, size_t size) { uint64_t cv; int i; ConvertFn conv = NULL; if (ptype != PACK_HOST_BYTEORDER) conv = pack_getconvert_by_size(size); for (i = 0; i < length; ++i) { cv = arr[i * size]; if (conv != NULL) conv(&cv); fwrite(&cv, size, 1, fp); } } /* -------------------------------------------------------- * public functions * -------------------------------------------------------- */ /* * Function that writes everything to the file `path'. These functions * does not append to the file, so if you want successive call to append * variables use fpack instead. * Returns 0 on success or -1 on failure. */ int pack_write(int ptype, const char *path, const char *fmt, ...) { va_list ap; int status; va_start(ap, fmt); status = pack_vwrite(ptype, path, fmt, ap); va_end(ap); return status; } int pack_vwrite(int ptype, const char *path, const char *fmt, va_list ap) { FILE *fp; int status; if ((fp = fopen(path, "w+b")) == NULL) return -1; status = pack_vfwrite(ptype, fp, fmt, ap); fclose(fp); return status; } /* * Write to a file that is already open. The function does not call * fclose() so you need to do it yourself later. */ int pack_fwrite(int ptype, FILE *fp, const char *fmt, ...) { va_list ap; int status; va_start(ap, fmt); status = pack_vfwrite(ptype, fp, fmt, ap); va_end(ap); return status; } int pack_vfwrite(int ptype, FILE *fp, const char *fmt, va_list ap) { int nelem; size_t size; char tok; const char *p; for (p = fmt; *p != '\0'; ++p) { if (isspace(*p)) continue; tok = *p; size = pack_getsize(tok); /* Bad character */ if (size == 0) continue; PACK_GETNELEM(nelem, p); if (nelem == 0) continue; /* * If i is 1, then we only have one integer, if it's more * than one, user may have given an array of something else. */ if (nelem == 1) { uint64_t item; PACK_GETARG(item, ap, tok); pack_write_one(ptype, fp, item, size); } else { uint8_t *arr; arr = va_arg(ap, uint8_t *); pack_write_multiple(ptype, fp, arr, nelem, size); } } return 0; } /* * Function that read the binary file and restore values to the same format * as pack functions. Arguments must be pointer of enough space to store * the values. */ int pack_read(int ptype, const char *path, const char *fmt, ...) { va_list ap; int status; va_start(ap, fmt); status = pack_vread(ptype, path, fmt, ap); va_end(ap); return status; } int pack_vread(int ptype, const char *path, const char *fmt, va_list ap) { FILE *fp; int status; if ((fp = fopen(path, "rb")) == NULL) return -1; status = pack_vfread(ptype, fp, fmt, ap); fclose(fp); return status; } int pack_fread(int ptype, FILE *fp, const char *fmt, ...) { va_list ap; int status; va_start(ap, fmt); status = pack_vfread(ptype, fp, fmt, ap); va_end(ap); return status; } int pack_vfread(int ptype, FILE *fp, const char *fmt, va_list ap) { const char *p; uint8_t *ptr; size_t tocopy; ConvertFn convert; for (p = fmt; *p != '\0'; ++p) { char tok; int nelem, i; if (isspace(*p)) continue; tok = *p; tocopy = pack_getsize(tok); /* Bad character */ if (tocopy == 0) continue; /* Determine how many element to read and copy */ PACK_GETNELEM(nelem, p); if (nelem == 0) continue; if ((ptr = va_arg(ap, uint8_t *)) == NULL) continue; for (i = 0; i < nelem; ++i) { fread(&ptr[tocopy * i], tocopy, 1, fp); /* Convert if needed */ convert = pack_getconvert_by_tok(tok); if (ptype != PACK_HOST_BYTEORDER && convert != NULL) convert((char *)ptr + (tocopy * i)); } } return 0; }