/*
 * error.h: define classes used to throw exceptions out of spigot
 * functions.
 */

#include <string.h>
#include <stdarg.h>

struct spigot_error_base {
    char *errmsg;
    spigot_error_base() { errmsg = NULL; }
    spigot_error_base(const spigot_error_base &pe) {
        errmsg = (char *)malloc(1+strlen(pe.errmsg));
        strcpy(errmsg, pe.errmsg);
    }
    ~spigot_error_base() {
        if (errmsg)
            free(errmsg);
        errmsg = NULL;
    }
};

/*
 * Derive distinguishable classes of error from the above base class.
 */
struct spigot_error : spigot_error_base {
    /*
     * spigot_error is used for any fatal error (no matter at parse
     * time, spigot setup time, or during main evaluation). Its usual
     * constructor takes printf-style parameters.
     */
    spigot_error(const char *fmt, ...) {
        int len;
        va_list ap;

#if defined _MSC_VER
        /*
         * Sigh, VS2010 does not provide an ANSI-compliant vsnprintf;
         * it just returns -1 if the output didn't fit, which is no
         * help in finding out how much bigger you need to make the
         * buffer.
         */
        for (len = 512 ;; len *= 2) {
            int ret;
            errmsg = (char *)malloc(len+1);
            va_start(ap, fmt);
            ret = _vsnprintf(errmsg, len+1, fmt, ap);
            va_end(ap);
            if (ret >= 0 && ret < len)
                break;
        }
#else
        /*
         * With sensible vsnprintf, we can try once without any buffer
         * at all to find out the required length, then malloc exactly
         * that.
         */
        va_start(ap, fmt);
        len = vsnprintf(NULL, 0, fmt, ap);
        va_end(ap);
        errmsg = (char *)malloc(len+1);
        va_start(ap, fmt);
        vsnprintf(errmsg, len+1, fmt, ap);
        va_end(ap);
#endif
    }
};
struct spigot_eof : spigot_error_base {
    /*
     * spigot_eof is used to indicate that a file we were reading a
     * real number from has ended, and hence we can't generate any
     * further output. Its constructor takes a plain filename string,
     * and handlers for it will cope accordingly.
     */
    spigot_eof(const char *filename) {
        errmsg = (char *)malloc(1+strlen(filename));
        strcpy(errmsg, filename);
    }
};

/*
 * This utility spigot function is defined in enforce.cpp, and
 * declared in this header file because it's dependent on having
 * spigot_error defined.
 */
#define ENFORCE_RELATIONS(X) \
    X(ENFORCE_GT, ">"), X(ENFORCE_GE, ">="), \
    X(ENFORCE_LT, "<"), X(ENFORCE_LE, "<=")
#define ENUMERATE(a,b) a
enum EnforceRelation { ENFORCE_RELATIONS(ENUMERATE) };
#undef ENUMERATE
Spigot *spigot_enforce(Spigot *x, EnforceRelation rel, Spigot *bound,
                       spigot_error err);
