+/*
+ * 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;
+}