view pack.c @ 164:654f32079cdc

Use enum
author David Demelier <markand@malikania.fr>
date Wed, 05 Sep 2012 07:44:07 +0200
parents 81d64d4a5473
children 7f214f26a4c0
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 <string.h>
#include <ctype.h>

#include "pack.h"

/* Compilation tunables */
#if !defined(PACK_CHKSIZE)
#  define PACK_CHKSIZE	(256)		/* default chunk size */
#endif

/* Type */
enum pack_type {
	TYPE_FP,
	TYPE_FIXEDBUF,
	TYPE_AUTOBUF
};

struct pack_sc {
	enum pack_type	type;		/* which kind of work */
	short		endian;		/* endian */

	/* if TYPE_FP */
	FILE *		fp;		/* file to write */

	/* else */
	char *		buf;		/* bytes converted */
	size_t		size;		/* current length */
	size_t		allocated;	/* allocated size */

	/* for reading */
	unsigned int	position;	/* position where to read */
};

struct pack_value {
	/* for writing */
	int		nelem;		/* number of element */
	int		size;		/* size of elements */
	long		value;		/* direct value */
	char *		array;		/* array of values */

	/* for reading */
	char *		dst;		/* destination */
};

#define TOKNARG(tok, nelem) do {					\
	if (p[1] == '[') {						\
		char *end;						\
									\
		/* Can statically write the number, or pass it */	\
		if (p[2] != ']') {					\
			nelem = (int)strtol(&p[2], &end, 10);		\
			p = end;					\
		} else {						\
			nelem = va_arg(ap, int);			\
			while (!isspace(*p) && *p != ']' && *p != '\0')	\
				++p;					\
		}							\
	} else								\
		nelem = 1;						\
} while (/* CONSTCOND */ 0)

#define TOKSIZE(tok, size) do {						\
	switch (tok) {							\
	case 'c':							\
		size = sizeof (char);					\
		break;							\
	case 's':							\
		size = sizeof (short);					\
		break;							\
	case 'i':							\
		size = sizeof (int);					\
		break;							\
	case 'l':							\
		size = sizeof (long);					\
		break;							\
	default:							\
		size = 0;						\
		break;							\
	}								\
} while (/* CONSTCOND */ 0)

#define TOKGETARG(serv, tok, val, ap) do {				\
	if ((val)->nelem == 1 && serv == writeto) {			\
		long item;						\
		switch (tok) {						\
		case 'c': case 's': case 'i':				\
			item = va_arg(ap, int);				\
			break;						\
		case 'l':						\
			item = va_arg(ap, long);			\
			break;						\
		default:						\
			item = 0;					\
			break;						\
		}							\
									\
		memset(&(val)->value, 0, sizeof (long));		\
		memcpy(&(val)->value, &item, (val)->size);		\
		(val)->array = (char *)&(val)->value;			\
	} else { /* read is always a pointer */				\
		(val)->array = va_arg(ap, void *);			\
		(val)->dst = (val)->array;				\
	}								\
} while (/* CONSTCOND */ 0)

#define GETCONV(ed, size, conv) do {					\
	if (ed != PACK_HOST_BYTEORDER) {				\
		switch ((size)) {					\
		case (sizeof (short)):					\
			conv = conv16; break;				\
		case (sizeof (int)):					\
			conv = conv32; break;				\
		case (sizeof (long)):					\
			conv = conv64; break;				\
		default:						\
			conv = NULL;					\
		}							\
	} else								\
		conv = NULL;						\
} while (/* CONSTCOND */ 0)

static void
conv16(void *item)
{
	*(short *)item = pack_swap16(*(short *)item);
}

static void
conv32(void *item)
{
	*(int *)item = pack_swap32(*(int *)item);
}

static void
conv64(void *item)
{
	*(long *)item = pack_swap64(*(long *)item);
}

/*
 * Function that write content into the buffer or file depending
 * on the calling function. Returns the number of bytes written
 * or -1 on failure.
 */
static long
writeto(struct pack_sc *sc, struct pack_value *val)
{
	void (*conv)(void *);
	int i;
	long item, nbytes = 0;

	/* Get converter */
	GETCONV(sc->endian, val->size, conv);

	for (i = 0; i < val->nelem; ++i) {
		item = 0;
		memcpy(&item, &val->array[i * val->size], val->size);

		if (conv != NULL)
			conv(&item);

		switch (sc->type) {
		case TYPE_FP:
			fwrite(&item, val->size, 1, sc->fp);
			break;
		case TYPE_AUTOBUF:
			if (sc->size + val->size >= sc->allocated) {
				sc->allocated += PACK_CHKSIZE;
				sc->buf = realloc(sc->buf, sc->allocated);

				if (sc->buf == NULL)
					return -1;
			}
			/* FALLTHROUGH */
		case TYPE_FIXEDBUF:
			if (sc->size + val->size <= sc->allocated) {
				memcpy(&sc->buf[sc->size], &item, val->size);
				sc->size += val->size;
			} else
				continue;
		default:
			break;
		}

		nbytes += val->size;
	}

	return nbytes;
}

/*
 * Function that read the file or buffer and fill array.
 */
static long
readfrom(struct pack_sc *sc, struct pack_value *val)
{
	void (*conv)(void *);
	int i, oktoread;
	long item, nbytes = 0;

	/* Get converter */
	GETCONV(sc->endian, val->size, conv);

	for (i = 0; i < val->nelem; ++i) {
		item = oktoread = 0;

		switch (sc->type) {
		case TYPE_FP:
			fread(&item, val->size, 1, sc->fp);
			oktoread = 1;
			break;
		case TYPE_FIXEDBUF:
			if (sc->position + val->size <= sc->allocated) {
				memcpy(&item, &sc->buf[sc->position], val->size);
				sc->position += val->size;
				oktoread = 1;
			}
			break;
		default:
			break;
		}

		if (conv != NULL)
			conv(&item);

		if (oktoread) {
			memcpy(&val->dst[i * val->size], &item, val->size);
			nbytes += val->size;
		}
	}

	return nbytes;
}

/*
 * Function that analyses the format, write or read the values depending
 * on the calling functions.
 */
static long
packit(struct pack_sc *sc, long (*func)(struct pack_sc *, struct pack_value *),
       const char *fmt, va_list ap)
{
	struct pack_value val;
	const char *p;
	int tok;
	long nbytes = 0, rv;

	for (p = fmt; *p != '\0'; ++p) {
		tok = *p;

		if (isspace(*p))
			continue;

		TOKNARG(tok, val.nelem);
		TOKSIZE(tok, val.size);

		if (val.size == 0 || val.nelem == 0)
			continue;

		TOKGETARG(func, tok, &val, ap);
		if ((rv = func(sc, &val)) == -1)
			return -1;

		nbytes += rv;
	}

	return nbytes;
}

long
pack_write(short ed, const char *path, const char *fmt, ...)
{
	va_list ap;
	long rv;

	va_start(ap, fmt);
	rv = pack_vwrite(ed, path, fmt, ap);
	va_end(ap);

	return rv;
}

long
pack_vwrite(short ed, const char *path, const char *fmt, va_list ap)
{
	FILE *fp;
	long rv;

	if ((fp = fopen(path, "wb")) == NULL)
		return -1;

	rv = pack_vfwrite(ed, fp, fmt, ap);
	fclose(fp);

	return rv;
}

long
pack_fwrite(short ed, FILE *fp, const char *fmt, ...)
{
	va_list ap;
	long rv;

	va_start(ap, fmt);
	rv = pack_vfwrite(ed, fp, fmt, ap);
	va_end(ap);

	return rv;
}

long
pack_vfwrite(short ed, FILE *fp, const char *fmt, va_list ap)
{
	struct pack_sc sc = {
		TYPE_FP, ed, fp, NULL, 0, 0, 0
	};

	return packit(&sc, writeto, fmt, ap);
}

long
pack_swrite(short ed, char *buf, size_t max, const char *fmt, ...)
{
	va_list ap;	
	long rv;

	va_start(ap, fmt);
	rv = pack_vswrite(ed, buf, max, fmt, ap);
	va_end(ap);

	return rv;
}

long
pack_vswrite(short ed, char *buf, size_t max, const char *fmt, va_list ap)
{
	struct pack_sc sc = {
		TYPE_FIXEDBUF, ed, NULL, buf, 0, max, 0
	};

	return packit(&sc, writeto, fmt, ap);
}

long
pack_aswrite(short ed, char **bufp, const char *fmt, ...)
{
	va_list ap;	
	long rv;

	va_start(ap, fmt);
	rv = pack_vaswrite(ed, bufp, fmt, ap);
	va_end(ap);

	return rv;
}

long
pack_vaswrite(short ed, char **bufp, const char *fmt, va_list ap)
{
	struct pack_sc sc = {
		TYPE_AUTOBUF, ed, NULL, NULL, 0, PACK_CHKSIZE, 0
	};

	int rv;

	if ((sc.buf = malloc(PACK_CHKSIZE)) == NULL)
		return -1;

	rv = packit(&sc, writeto, fmt, ap);
	*bufp = sc.buf;

	return rv;
}

long
pack_read(short ed, const char *path, const char *fmt, ...)
{
	va_list ap;
	long rv;

	va_start(ap, fmt);
	rv = pack_vread(ed, path, fmt, ap);
	va_end(ap);

	return rv;
}

long
pack_vread(short ed, const char *path, const char *fmt, va_list ap)
{
	FILE *fp;
	long rv;

	if ((fp = fopen(path, "rb")) == NULL)
		return -1;

	rv = pack_vfread(ed, fp, fmt, ap);
	fclose(fp);

	return rv;
}

long
pack_fread(short ed, FILE *fp, const char *fmt, ...)
{
	va_list ap;
	long rv;

	va_start(ap, fmt);
	rv = pack_vfread(ed, fp, fmt, ap);
	va_end(ap);

	return rv;
}

long
pack_vfread(short ed, FILE *fp, const char *fmt, va_list ap)
{
	struct pack_sc sc = {
		TYPE_FP, ed, fp, NULL, 0, 0, 0
	};

	return packit(&sc, readfrom, fmt, ap);
}

long
pack_sread(short ed, const char *buf, size_t max, const char *fmt, ...)
{
	va_list ap;	
	long rv;

	va_start(ap, fmt);
	rv = pack_vsread(ed, buf, max, fmt, ap);
	va_end(ap);

	return rv;
}

long
pack_vsread(short ed, const char *buf, size_t max, const char *fmt, va_list ap)
{
	struct pack_sc sc = {
		TYPE_FIXEDBUF, ed, NULL, (char *)buf, 0, max, 0
	};

	return packit(&sc, readfrom, fmt, ap);
}