# without any warranty.
lbxdir = $(includedir)/lbx
-lbx_HEADERS = src/lbx.h src/image.h
+lbx_HEADERS = src/lbx.h src/image.h src/error.h
noinst_HEADERS += src/misc.h src/tools.h src/pack.h
lib_LTLIBRARIES += liblbx.la
-liblbx_la_SOURCES = src/lbx.c src/fops.c src/image.c src/pack.c
+liblbx_la_SOURCES = src/lbx.c src/fops.c src/image.c src/pack.c src/error.c
bin_PROGRAMS += lbxtool
lbxtool_SOURCES = src/lbxtool.c
--- /dev/null
+/*
+ * 2ooM: The Master of Orion II Reverse Engineering Project
+ * Utilities for out-of-band error propagation.
+ * Copyright (C) 2010 Nick Bowler
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <libintl.h>
+
+#include "error.h"
+
+#define _(s) dgettext(PACKAGE, s)
+
+#if !defined(LBX_ERROR_LIMIT)
+# define LBX_ERROR_LIMIT 256
+#endif
+
+static unsigned error_base, error_tip;
+static int error_ring[LBX_ERROR_LIMIT];
+
+static const char **user_errors;
+static unsigned user_error_count;
+static unsigned user_error_max = 2;
+
+int lbx_error_new(const char *str)
+{
+ assert(user_error_count <= user_error_max);
+ if (!user_errors || user_error_count == user_error_max) {
+ size_t size = sizeof *user_errors * user_error_max * 2;
+ const char **new;
+
+ new = realloc(user_errors, size);
+ if (!new)
+ return -1;
+ user_error_max *= 2;
+ user_errors = new;
+ }
+
+ user_errors[user_error_count] = str;
+ return LBX_EUBASE + user_error_count++;
+}
+
+int lbx_error_raise(int code)
+{
+ if (code == LBX_EOK) {
+ return -1;
+ } else if (code >= LBX_EMAX && code < LBX_EUBASE
+ || code >= LBX_EUBASE + user_error_count) {
+ fprintf(stderr, "%s: invalid error code %d\n", __func__, code);
+ return -1;
+ }
+
+ error_ring[error_tip++] = code;
+ error_tip %= LBX_ERROR_LIMIT;
+
+ if (error_tip == error_base) {
+ error_base = (error_base + 1) % LBX_ERROR_LIMIT;
+ return -1;
+ }
+
+ return 0;
+}
+
+static void getmsg(int error, const char **msg)
+{
+ if (error < 0) {
+ *msg = strerror(-error);
+ return;
+ }
+
+ switch (error) {
+ case LBX_EOK:
+ *msg = _("Success");
+ break;
+ case LBX_EMAGIC:
+ *msg = _("Bad magic number");
+ break;
+ case LBX_EFORMAT:
+ *msg = _("Invalid file format");
+ break;
+ case LBX_ENOENT:
+ *msg = _("Specified item does not exist");
+ break;
+ case LBX_ENOMEM:
+ *msg = _("Memory allocation failed");
+ break;
+ case LBX_EEOF:
+ *msg = _("Unexpected end of file");
+ break;
+ default:
+ *msg = user_errors[error - LBX_EUBASE];
+ }
+}
+
+int lbx_error_peek(const char **msg)
+{
+ int error = error_tip == error_base ? LBX_EOK : error_ring[error_base];
+
+ assert(error < LBX_EMAX || error >= LBX_EUBASE);
+ assert(error < LBX_EUBASE + user_error_count);
+
+ if (msg)
+ getmsg(error, msg);
+ return error;
+}
+
+
+int lbx_error_get(const char **msg)
+{
+ int error = lbx_error_peek(msg);
+
+ if (error != LBX_EOK)
+ error_base = (error_base + 1) % LBX_ERROR_LIMIT;
+ return error;
+}
--- /dev/null
+#ifndef LBX_ERROR_H_
+#define LBX_ERROR_H_
+
+enum {
+ LBX_EOK,
+ LBX_EMAGIC,
+ LBX_EFORMAT,
+ LBX_ENOENT,
+ LBX_ENOMEM,
+ LBX_EEOF,
+ LBX_EMAX,
+
+ LBX_EUBASE = 9000
+};
+
+/*
+ * Create a new user error. User errors are primarily intended to be used by
+ * lbx_file_ops callback functions, but can be used for any purpose.
+ *
+ * Returns a positive code on success (which can be subsequently passed to
+ * lbx_error_raise), or a negative value on failure.
+ */
+int lbx_error_new(const char *str);
+
+/*
+ * Signal an error. The given code is recorded for later retreival by
+ * lbx_error_get. Errors are reported on a first-in, first-out basis.
+ * Errors are stored in a ring buffer which can overflow, at which point
+ * the oldest unretrieved error will be deleted.
+ *
+ * Negative codes represent system errors. That is, the negation of some
+ * (positive) errno value as returned by a library function. If a positive
+ * code does not correspond to any error, nothing is recorded.
+ *
+ * Returns -1 if the error code was not valid, or if an unretreived code
+ * was deleted. Otherwise, this function returns 0.
+ */
+int lbx_error_raise(int code);
+
+/*
+ * Retrieves an error. The oldest reported error is removed from the buffer.
+ * If msg is not NULL, a pointer to a human-readable description of the error
+ * is stored in *msg.
+ *
+ * Returns the retrieved error code, or 0 if there were no errors.
+ */
+int lbx_error_get(const char **msg);
+
+/*
+ * Retrieves an error. This function is the same as lbx_error_get, except that
+ * the error code is not removed from the buffer and will be reported by a
+ * subsequent call to lbx_error_get or lbx_error_peek.
+ */
+int lbx_error_peek(const char **msg);
+
+/*
+ * Helper function for when you only care about the message.
+ */
+static inline const char *lbx_errmsg(void)
+{
+ const char *msg;
+
+ lbx_error_get(&msg);
+ return msg;
+}
+
+#endif
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
+#include <errno.h>
#include "misc.h"
+#include "error.h"
#include "lbx.h"
/* Default I/O operations for ordinary files. */
static size_t file_read(void *buf, size_t size, void *handle)
{
- return fread(buf, 1, size, (FILE *)handle);
+ size_t rc = fread(buf, 1, size, (FILE *)handle);
+
+ if (rc < size && ferror((FILE *)handle))
+ lbx_error_raise(-errno);
+ return rc;
}
static int file_seek(void *handle, long offset, int whence)
{
- return fseek((FILE *)handle, offset, whence);
+ if (fseek((FILE *)handle, offset, whence) == -1) {
+ lbx_error_raise(-errno);
+ return -1;
+ }
}
static long file_tell(void *handle)
struct lbx_pipe_state *state = handle;
size_t rc;
- rc = fread(buf, 1, size, state->f);
+ rc = file_read(buf, size, state->f);
state->offset += rc;
return rc;
}
#include "pack.h"
#include "misc.h"
#include "lbx.h"
+#include "error.h"
#include "image.h"
#define FLAG_RAW 0x0100 /* Image is stored as a flat array of bytes. */
img = malloc(sizeof *img + sizeof img->offsets[0] * (nframes+1));
if (!img) {
- lbx_errno = -errno;
+ lbx_error_raise(LBX_ENOMEM);
return NULL;
}
struct lbx_image *img;
if (fops->read(hdr_buf, sizeof hdr_buf, f) != sizeof hdr_buf) {
- lbx_errno = -errno;
if (fops->eof(f))
- lbx_errno = LBX_EEOF;
+ lbx_error_raise(LBX_EEOF);
return NULL;
}
unsigned char buf[4];
if (fops->read(buf, sizeof buf, f) != sizeof buf) {
- lbx_errno = -errno;
if (fops->eof(f))
- lbx_errno = LBX_EEOF;
+ lbx_error_raise(LBX_EEOF);
free(img);
return NULL;
}
unsigned char buf[4];
if (fops->read(buf, sizeof buf, f) != sizeof buf) {
- lbx_errno = -errno;
if (fops->eof(f))
- lbx_errno = LBX_EEOF;
+ lbx_error_raise(LBX_EEOF);
free(img);
return NULL;
}
img->paloff = fops->tell(f);
if (img->palstart + img->palcount > 256) {
- lbx_errno = LBX_EFORMAT;
+ lbx_error_raise(LBX_EFORMAT);
free(img);
return NULL;
}
/* Ensure that the row fits in the image. */
if (img->height - img->currenty <= yval || xval >= img->width) {
- lbx_errno = LBX_EFORMAT;
+ lbx_error_raise(LBX_EFORMAT);
return -1;
}
xval = unpack_16_le(buf+2);
if (img->width - img->currentx <= xval) {
- lbx_errno = LBX_EFORMAT;
+ lbx_error_raise(LBX_EFORMAT);
return -1;
}
img->currentx += xval;
}
if (count > img->width - img->currentx) {
- lbx_errno = LBX_EFORMAT;
+ lbx_error_raise(LBX_EFORMAT);
return -1;
}
return 0;
readerr:
- lbx_errno = -errno;
if (img->fops->eof(img->f))
- lbx_errno = LBX_EEOF;
+ lbx_error_raise(LBX_EEOF);
return -1;
}
tmp = calloc(height, width);
if (!tmp) {
- lbx_errno = -errno;
+ lbx_error_raise(LBX_ENOMEM);
return NULL;
}
new = malloc(height * sizeof *new);
if (!new) {
- lbx_errno = -errno;
+ lbx_error_raise(LBX_ENOMEM);
free(tmp);
return NULL;
}
assert(img->flags & FLAG_RAW);
if (img->fops->seek(img->f, img->offsets[frame], SEEK_SET)) {
- lbx_errno = -errno;
return NULL;
}
if (img->fops->read(img->framedata[0], size, img->f) != size) {
- lbx_errno = -errno;
if (img->fops->eof(img->f))
- lbx_errno = LBX_EEOF;
+ lbx_error_raise(LBX_EEOF);
return NULL;
}
memset(img->mask[0], 1, size);
if (img->fops->tell(img->f) > img->offsets[frame+1]) {
- lbx_errno = LBX_EFORMAT;
+ lbx_error_raise(LBX_EFORMAT);
return NULL;
}
unsigned char **lbximg_getframe(struct lbx_image *img, int frame)
{
if (frame >= img->frames || frame < 0) {
- lbx_errno = LBX_ERANGE;
+ lbx_error_raise(LBX_ENOENT);
return NULL;
}
int rc, first = 1;
if (img->fops->seek(img->f, img->offsets[frame], SEEK_SET)) {
- lbx_errno = -errno;
return NULL;
}
first = 0;
if (img->fops->tell(img->f) > img->offsets[frame+1]) {
- lbx_errno = LBX_EFORMAT;
+ lbx_error_raise(LBX_EFORMAT);
return NULL;
}
} while (!rc);
for (i = 0; i < 256; i++) {
if (fops->read(entry, sizeof entry, f) != sizeof entry) {
- lbx_errno = (fops->eof(f)) ? LBX_EEOF : -errno;
+ if (fops->eof(f))
+ lbx_error_raise(LBX_EEOF);
return -1;
}
if (entry[0] != 1) {
- lbx_errno = LBX_EFORMAT;
+ lbx_error_raise(LBX_EFORMAT);
return -1;
}
return 0;
if (img->fops->seek(img->f, img->paloff, SEEK_SET)) {
- lbx_errno = -errno;
return -1;
}
}
if (entry[0] != 0) {
- lbx_errno = LBX_EFORMAT;
+ lbx_error_raise(LBX_EFORMAT);
return -1;
}
return 0;
readerr:
- lbx_errno = img->fops->eof(img->f) ? LBX_EEOF : -errno;
+ if (img->fops->eof(img->f))
+ lbx_error_raise(LBX_EEOF);
return -1;
}
#include "pack.h"
#include "misc.h"
+#include "error.h"
#include "lbx.h"
#define LBX_MAGIC 0x0000fead
#define LBX_HDR_SIZE 8
-int lbx_errno = 0;
-
struct lbx_state {
char *name;
struct lbx_state *lbx;
if (magic != LBX_MAGIC) {
- lbx_errno = -LBX_EMAGIC;
+ lbx_error_raise(LBX_EMAGIC);
return NULL;
}
lbx = malloc(sizeof *lbx + sizeof lbx->offsets[0] * (nfiles+1));
if (!lbx) {
- lbx_errno = -errno;
+ lbx_error_raise(LBX_ENOMEM);
return NULL;
}
dupname = str_dup(name);
if (!dupname) {
- lbx_errno = -errno;
+ lbx_error_raise(LBX_ENOMEM);
goto err;
}
if (fops->read(hdr_buf, sizeof hdr_buf, f) != sizeof hdr_buf) {
- lbx_errno = -errno;
+ if (fops->eof(f))
+ lbx_error_raise(LBX_EEOF);
goto err;
}
unsigned char buf[4];
if (fops->read(buf, sizeof buf, f) != sizeof buf) {
- lbx_errno = -errno;
if (fops->eof(f))
- lbx_errno = LBX_EEOF;
+ lbx_error_raise(LBX_EEOF);
goto err;
}
static char str[256]; /* FIXME */
if (fileno >= lbx->nfiles) {
+ lbx_error_raise(LBX_ENOENT);
buf->name = NULL;
- lbx_errno = LBX_ERANGE;
return -1;
}
struct lbx_file_state *state;
if (fileno >= lbx->nfiles) {
- lbx_errno = LBX_ERANGE;
+ lbx_error_raise(LBX_ENOENT);
return NULL;
}
lbx->last_file = NULL;
if (lbx->fops->seek(lbx->f, lbx->offsets[fileno], SEEK_SET) != 0) {
- lbx_errno = -errno;
return NULL;
}
state = malloc(sizeof *state);
if (!state) {
- lbx_errno = -errno;
+ lbx_error_raise(LBX_ENOMEM);
return NULL;
}
f->lbx->last_file = NULL;
free(f);
}
-
-const char *lbx_strerror(void)
-{
- if (lbx_errno < 0)
- return strerror(-lbx_errno);
-
- switch (lbx_errno) {
- case LBX_ESUCCESS: return "Success";
- case LBX_EMAGIC: return "Bad magic number";
- case LBX_EEOF: return "Unexpected end-of-file";
- case LBX_ERANGE: return "Index out of range";
- case LBX_EFORMAT: return "Invalid file format";
- }
-
- return "Unknown error";
-}
#include <stdio.h>
-/* Errors */
-enum {
- LBX_ESUCCESS,
- LBX_EMAGIC,
- LBX_EEOF,
- LBX_ERANGE,
- LBX_EFORMAT,
-};
-extern int lbx_errno;
-
struct lbx_file_ops {
size_t (*read)(void *buf, size_t size, void *handle);
int (*seek)(void *handle, long offset, int whence);
int lbx_file_eof(LBXfile *f);
void lbx_file_close(LBXfile *f);
-/* Misc operations */
-const char *lbx_strerror(void);
-
#endif
#include "tools.h"
#include "image.h"
+#include "error.h"
#include "lbx.h"
/* Global flags */
struct lbx_imginfo info;
if (!overimg) {
- errmsg("failed to open override image: %s\n", lbx_strerror());
+ errmsg("failed to open override image: %s\n", lbx_errmsg());
return -1;
}
lbximg_getinfo(overimg, &info);
}
if (lbximg_getpalette(overimg, palette) == -1) {
- errmsg("error reading override palette: %s\n", lbx_strerror());
+ errmsg("error reading override palette: %s\n", lbx_errmsg());
lbximg_close(overimg);
return -1;
}
/* Read the external palette, if any. */
if (palf && lbximg_loadpalette(palf, &lbx_default_fops, palette) != 0) {
- errmsg("error reading external palette: %s\n", lbx_strerror());
+ errmsg("error reading external palette: %s\n", lbx_errmsg());
return -1;
}
/* Read the embedded palette, if any. */
if (info->palettesz && lbximg_getpalette(img, palette) == -1) {
- errmsg("error reading embedded palette: %s\n", lbx_strerror());
+ errmsg("error reading embedded palette: %s\n", lbx_errmsg());
return -1;
}
data = lbximg_getframe(img, i);
if (!data) {
- errmsg("error in frame %u: %s\n", i, lbx_strerror());
+ errmsg("error in frame %u: %s\n", i, lbx_errmsg());
continue;
}
img = lbximg_open(&stdin_handle, &lbx_pipe_fops, NULL);
if (!img) {
- errmsg("failed to open image: %s.\n", lbx_strerror());
+ errmsg("failed to open image: %s.\n", lbx_errmsg());
return EXIT_FAILURE;
}
#include "tools.h"
#include "lbx.h"
-
+#include "error.h"
static void printusage(void)
{
file = lbx_file_open(lbx, i);
if (!file) {
- errmsg("failed to open archive member %s: %s.\n",
- stat.name, lbx_strerror());
+ errmsg("%s: %s.\n", stat.name, lbx_errmsg());
continue;
}
lbx = lbx_open(&stdin_handle, &lbx_pipe_fops, NULL, "stdin");
if (!lbx) {
- errmsg("failed to open archive: %s.\n", lbx_strerror());
+ errmsg("%s: %s.\n", file ? file : "stdin", lbx_errmsg());
return EXIT_FAILURE;
}