From b839c8bbf3c99eaa4cc61dd9d6c1e598a6da26cf Mon Sep 17 00:00:00 2001 From: Attila Kovacs Date: Sat, 7 Sep 2024 18:03:09 +0200 Subject: [PATCH] Error handling improvements --- README.md | 2 +- include/xchange.h | 50 +++++++- src/xchange.c | 297 +++++++++++++++++++++++++++++++++++++--------- src/xlookup.c | 74 ++++++++---- src/xstruct.c | 295 ++++++++++++++++++++++++++++++--------------- 5 files changed, 543 insertions(+), 175 deletions(-) diff --git a/README.md b/README.md index 93be8c4..0daf644 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ prior to invoking `make`. The following build variables can be configured: - `LDFLAGS`: Linker flags (default is `-lm`). - `BUILD_MODE`: You can set it to `debug` to enable debugging features: it will initialize the global `xDebug` - variable to `TRUE`) and add `-g` to `CFLAGS`. + variable to `TRUE` and add `-g` to `CFLAGS`. - `CHECKEXTRA`: Extra options to pass to `cppcheck` for the `make check` target diff --git a/include/xchange.h b/include/xchange.h index c076dff..b1c0761 100644 --- a/include/xchange.h +++ b/include/xchange.h @@ -185,7 +185,8 @@ extern boolean xDebug; ///< Switch to enable debugging (very verbose) o // In xutil.c ------------------------------------------------> boolean xIsVerbose(); void xSetVerbose(boolean value); -int xError(const char *func, int errorCode); // TODO... +void xSetDebug(boolean value); +int xError(int code, const char *fn); const char *xErrorDescription(int code); // Structure access methods -----------------------------------> @@ -258,4 +259,51 @@ void xZero(void *buf, XType type, int count); char *xStringCopyOf(const char *str); +// <================= xchange internals ======================> + +/// \cond PRIVATE +#ifdef __XCHANGE_INTERNAL_API__ + +# include +# include + +// On some older platform NAN may not be defined, so define it here if need be +#ifndef NAN +# define NAN (0.0/0.0) +#endif + +# ifndef THREAD_LOCAL +# if __STDC_VERSION__ >= 201112L +# define THREAD_LOCAL _Thread_local ///< C11 standard for thread-local variables +# elif __GNUC__ >= 3 && __GNUC_MINOR__ >= 3 +# define THREAD_LOCAL __thread ///< pre C11 gcc >= 3.3 standard for thread-local variables +# else +# define THREAD_LOCAL ///< no thread-local variables +# endif +# endif + + +int x_trace(const char *loc, const char *op, int n); +void *x_trace_null(const char *loc, const char *op); +void x_set_errno(int en, const char *from, const char *desc, ...); +int x_error(int ret, int en, const char *from, const char *desc, ...); +int x_warn(const char *from, const char *desc, ...); + +/** + * Propagates an error (if any) with an offset. If the error is non-zero, it returns with the offset + * error value. Otherwise it keeps going as if it weren't even there... + * + * @param n {int} error code or the call that produces the error code + * + * @sa error_return + */ +# define prop_error(loc, n) { \ + int __ret = x_trace(loc, NULL, n); \ + if (__ret != 0) \ + return __ret; \ +} + +#endif /* __XCHANGE_INTERNAL_API__ */ +/// \endcond + #endif /* XCHANGE_H_ */ diff --git a/src/xchange.c b/src/xchange.c index 68eda6e..ccdbc73 100644 --- a/src/xchange.c +++ b/src/xchange.c @@ -16,10 +16,12 @@ #include // isspace() #include // sleep() #include +#include #include #include #include +#define __XCHANGE_INTERNAL_API__ ///< Use internal definitions #include "xchange.h" #ifndef INFINITY @@ -40,12 +42,124 @@ boolean xDebug = TRUE; boolean xDebug = FALSE; #endif +/** + * (for internal use) Propagates an error (if any). If the error is + * non-zero, it returns with the offset error value. Otherwise it keeps going as if it weren't + * even there... + * + * @param loc Function [:location] where error was produced. + * @param op (optional) further info or NULL. + * @param n error code that was received. + * + * @return n + */ +int x_trace(const char *loc, const char *op, int n) { + if(n != 0 && xDebug) { + fprintf(stderr, " @ %s", loc); + if(op) fprintf(stderr, " [%s]", op); + fprintf(stderr, " [=> %d]\n", n); + } + return n; +} + +/** + * (for internal use) Traces an error before returning NULL. + * + * @param loc Function [:location] where error was produced. + * @param op (optional) further info or NULL. + * @return NULL + */ +void *x_trace_null(const char *loc, const char *op) { + if(xDebug) { + fprintf(stderr, " @ %s", loc); + if(op) fprintf(stderr, " [%s]", op); + fprintf(stderr, " [=> NULL]\n"); + } + return NULL; +} + +/** + * (for internal use) Sets an errno and report errors to the standard error, depending + * on the current debug mode. + * + * @param en {int} UNIX error number (see errno.h) + * @param from {string} Function (:location) where error originated + * @param desc {string} Description of error, with information to convey to user. + * + * @sa x_error() + * @sa x_debug() + */ +void x_set_errno(int en, const char *from, const char *desc, ...) { + va_list varg; + + va_start(varg, desc); + if(xDebug) { + fprintf(stderr, "\n ERROR! %s: ", from); + vfprintf(stderr, desc, varg); + fprintf(stderr, "\n"); + } + va_end(varg); + + errno = en; +} + +/** + * (for internal use) Sets errno and reports errors to the standard error, depending + * on the current debug mode, before returning the supplied return code. + * + * @param ret return value + * @param en UNIX error code (see errno.h) + * @param from function (:location) where error originated + * @param desc description of error, with information to convey to user. + * + * @sa x_set_errno() + * @sa x_trace() + */ +int x_error(int ret, int en, const char *from, const char *desc, ...) { + va_list varg; + + va_start(varg, desc); + if(xDebug) { + fprintf(stderr, "\n ERROR! %s: ", from); + vfprintf(stderr, desc, varg); + if(ret) fprintf(stderr, " [=> %d]\n", ret); + } + va_end(varg); + + errno = en; + return ret; +} + +/** + * (for internal use) Prints a warning message. + * + * @param from function (:location) where error originated + * @param desc description of error, with information to convey to user. + * + * @sa x_set_errno() + * @sa x_trace() + */ +int x_warn(const char *from, const char *desc, ...) { + va_list varg; + + va_start(varg, desc); + if(xDebug) { + fprintf(stderr, "\n WARNING! %s: ", from); + vfprintf(stderr, desc, varg); + } + va_end(varg); + + return 0; +} + + /** * Checks if verbosity is enabled for the xchange library. * * @return TRUE (1) if verbosity is enabled, or else FALSE (0). * - * @sa setVerbose() + * @sa xSetVerbose() + * @sa xSetDebug() */ boolean xIsVerbose() { return xVerbose; @@ -62,6 +176,17 @@ void xSetVerbose(boolean value) { xVerbose = value ? TRUE : FALSE; } +/** + * Enables or disables debugging output. + * + * @param value TRUE (non-zero) to enable verbose output, or else FALSE (0). + * + * @sa xSetVerbose() + */ +void xSetDebug(boolean value) { + xDebug = value ? TRUE : FALSE; +} + /** * Checks if the type represents a fixed-size character / binary sequence. * @@ -99,7 +224,7 @@ int xStringElementSizeOf(XType type) { case X_LONG_HEX : l = 18; break; case X_FLOAT : l = 16; break; // 1 leading + 8 significant figs + 2 signs + 1 dot + 1 E + 3 exponent case X_DOUBLE : l = 25; break; // 1 leading + 16 significant figs + (4) + 4 exponent - default : return -1; + default : return x_error(-1, EINVAL, "xStringElementSizeOf", "invalid type: %d", type); } return (l+1); // with terminating '\0' } @@ -131,7 +256,7 @@ int xElementSizeOf(XType type) { case X_STRUCT: return sizeof(XStructure); case X_STRING: return sizeof(char *); } - return 0; + return x_warn("xElementSizeOf", "invalid type: %d", type); } /** @@ -144,8 +269,10 @@ int xElementSizeOf(XType type) { */ char xTypeChar(XType type) { if(type < 0) return 'C'; - if(type < 0x20) return '?'; - if(type > 0x7E) return '?'; + if(type < 0x20 || type > 0x7E) { + x_warn("xTypeChar", "invaid type: %d", type); + return '?'; + } return (char) (type & 0xff); } @@ -160,6 +287,10 @@ char xTypeChar(XType type) { */ int xGetElementCount(int ndim, const int *sizes) { int i, N = 1; + + if(ndim > 0 && !sizes) + x_warn("xGetElementCount", "input 'sizes' is NULL (ndim = %d)", ndim); + if(ndim > X_MAX_DIMS) ndim = X_MAX_DIMS; for(i = 0; i < ndim; i++) N *= sizes[i]; return N; @@ -173,13 +304,21 @@ int xGetElementCount(int ndim, const int *sizes) { * \param[in] sizes Sizes along each dimension. * * \return Number of characters written into the destination buffer, not counting the string - * termination. + * termination, or -1 if an the essential pointer arguments is NULL. * */ int xPrintDims(char *dst, int ndim, const int *sizes) { + static const char *fn = "xPrintDims"; + int i, N=0; char *next = dst; + if(!dst) + return x_error(-1, EINVAL, fn, "'dst' is NULL"); + + if(ndim > 0 && !sizes) + return x_error(-1, EINVAL, fn, "input 'sizes' is NULL (ndim = %d)", ndim); + if(ndim <= 0) return sprintf(dst, "1"); // default, will be overwritten with actual sizes, if any. else if(ndim > X_MAX_DIMS) ndim = X_MAX_DIMS; @@ -208,7 +347,7 @@ int xParseDims(const char *src, int *sizes) { int ndim, N = 1; char *next = (char *) src; - if(!src) return 0; + if(!src) return x_error(0, EINVAL, "xParseDims", "'src' is NULL"); if(!sizes) return 0; for(ndim = 0; ndim <= X_MAX_DIMS; ) { @@ -241,12 +380,23 @@ int xParseDims(const char *src, int *sizes) { * be set accordingly). */ void *xAlloc(XType type, int count) { - int eSize = xElementSizeOf(type); - if(eSize < 1 || count < 1) { - errno = EINVAL; + static const char *fn = "xAlloc"; + int eSize; + void *ptr; + + if(count < 1) { + x_error(0, EINVAL, fn, "invalid count=%d", count); return NULL; } - return calloc(eSize, count); + + eSize = xElementSizeOf(type); + if(eSize < 1) + return x_trace_null(fn, NULL); + + ptr = calloc(eSize, count); + if(!ptr) x_error(0, errno, fn, "calloc() error"); + + return ptr; } /** @@ -298,6 +448,8 @@ static int TokenMatch(char *a, char *b) { * @return TRUE (1) or FALSE (0). */ boolean xParseBoolean(char *str, char **end) { + static const char *fn = "xParseBoolean"; + static char *true[] = { "true", "t", "on", "yes", "y", "enabled", "active", NULL }; static char *false[] = { "false", "f", "off", "no", "n", "disabled", "invactive", NULL }; @@ -305,8 +457,8 @@ boolean xParseBoolean(char *str, char **end) { long l; if(!str) { - *end = NULL; - return FALSE; + if(end) *end = NULL; + return x_error(FALSE, EINVAL, fn, "input string is NULL"); } while(isspace(*str)) str++; @@ -328,7 +480,7 @@ boolean xParseBoolean(char *str, char **end) { if(errno != ERANGE) return (l != 0); - return FALSE; + return x_error(FALSE, errno, fn, "invalid argument: %s", str); } @@ -361,29 +513,36 @@ static int CompareToken(const char *a, const char *b) { * @param str String to parse floating-point value from * @param tail (optional) reference to pointed in which to return the parse position after successfully * parsing a floating-point value. - * @return the floating-point value at the head of the string. + * @return the floating-point value at the head of the string, or NAN if the input string is NULL. */ double xParseDouble(const char *str, char **tail) { -#if EXPLCIT_PARSE_SPECIAL_DOUBLES - char *next = (char *) str; - - while(isspace(*next)) next++; - - if(*next == '+' || *next == '-') next++; // Skip sign (if any) + if(!str) { + x_error(0, EINVAL, "xParseDouble", "input string is NULL"); + return NAN; + } - // If leading character is not a number or a decimal point, then check for special values. - if(*next) if((*next < '0' || *next > '9') && *next != '.') { - if(!CompareToken("nan", next)) { - if(tail) *tail = next + sizeof("nan") - 1; - return NAN; - } - if(!CompareToken("inf", next)) { - if(tail) *tail = next + sizeof("inf") - 1; - return NAN; - } - if(!CompareToken("infinity", next)) { - if(tail) *tail = next + sizeof("infinity") - 1; - return NAN; +#if EXPLCIT_PARSE_SPECIAL_DOUBLES + { + char *next = (char *) str; + + while(isspace(*next)) next++; + + if(*next == '+' || *next == '-') next++; // Skip sign (if any) + + // If leading character is not a number or a decimal point, then check for special values. + if(*next) if((*next < '0' || *next > '9') && *next != '.') { + if(!CompareToken("nan", next)) { + if(tail) *tail = next + sizeof("nan") - 1; + return NAN; + } + if(!CompareToken("inf", next)) { + if(tail) *tail = next + sizeof("inf") - 1; + return NAN; + } + if(!CompareToken("infinity", next)) { + if(tail) *tail = next + sizeof("infinity") - 1; + return NAN; + } } } #endif @@ -399,9 +558,12 @@ double xParseDouble(const char *str, char **tail) { * * @param str Pointer to buffer for printed value. * @param value Value to print. - * @return Number of characters printed into the buffer. + * @return Number of characters printed into the buffer, or -1 if there was an error. */ int xPrintDouble(char *str, double value) { + if(!str) + return x_error(-1, EINVAL, "xPrintDouble", "input string is NULL"); + // For double-precision restrict range to IEEE range if(value > DBL_MAX) return sprintf(str, "nan"); else if(value < -DBL_MAX) return sprintf(str, "nan"); @@ -422,6 +584,9 @@ int xPrintDouble(char *str, double value) { * @return Number of characters printed into the buffer. */ int xPrintFloat(char *str, float value) { + if(!str) + return x_error(-1, EINVAL, "xPrintFloat", "input string is NULL"); + // For single-precision restrict range to IEEE range if(value > FLT_MAX) return sprintf(str, "nan"); else if(value < -FLT_MAX) return sprintf(str, "nan"); @@ -434,14 +599,32 @@ int xPrintFloat(char *str, float value) { /** * Prints a descriptive error message to stderr, and returns the error code. * - * \param func String that describes the function or location where the error occurred. - * \param errorCode Error code that describes the failure. + * \param code The xchange error code that describes the failure (see xchange.h). + * \param fn String that describes the function or location where the error occurred. * * \return Same error code as specified on input. */ -int xError(const char *func, int errorCode) { - if(xDebug) fprintf(stderr, "DEBUG-X> %4d (%s) in %s.\n", errorCode, xErrorDescription(errorCode), func); - return errorCode; +int xError(int code, const char *fn) { + switch(code) { + case X_SUCCESS: return 0; + case X_FAILURE: return x_error(code, ECANCELED, fn, "internal failure"); + case X_ALREADY_OPEN: return x_error(code, EISCONN, fn, "already opened"); + case X_NO_INIT: return x_error(code, ENXIO, fn, "not initialized"); + case X_NO_SERVICE: return x_error(code, EIO, fn, "connection lost?"); + case X_NO_BLOCKED_READ: return x_error(code, 0, fn, "no blocked calls"); + case X_NO_PIPELINE: return x_error(code, EPIPE, fn, "pipeline mode disabled"); + case X_TIMEDOUT: return x_error(code, ETIMEDOUT, fn, "timed out while waiting to complete"); + case X_INTERRUPTED: return x_error(code, EINTR, fn, "wait released without read"); + case X_INCOMPLETE: return x_error(code, EAGAIN, fn, "incomplete data transfer"); + case X_NULL: return x_error(code, EFAULT, fn, "null value"); + case X_PARSE_ERROR: return x_error(code, ECANCELED, fn, "parse error"); + case X_NOT_ENOUGH_TOKENS: return x_error(code, EINVAL, fn, "not enough tokens"); + case X_GROUP_INVALID: return x_error(code, EINVAL, fn, "invalid group"); + case X_NAME_INVALID: return x_error(code, EINVAL, fn, "invalid variable name"); + case X_TYPE_INVALID: return x_error(code, EINVAL, fn, "invalid variable type"); + case X_SIZE_INVALID: return x_error(code, EDOM, fn, "invalid variable dimensions"); + } + return x_error(code, errno, fn, "unknown error"); } /** @@ -458,23 +641,23 @@ int xError(const char *func, int errorCode) { */ const char *xErrorDescription(int code) { switch(code) { - case X_FAILURE: errno = ECANCELED; return "internal failure"; - case X_SUCCESS: errno = 0; return "success!"; - case X_ALREADY_OPEN: errno = EISCONN; return "already opened"; - case X_NO_INIT: errno = ENXIO; return "not initialized"; - case X_NO_SERVICE: errno = EIO; return "connection lost?"; - case X_NO_BLOCKED_READ: errno = 0; return "no blocked calls"; - case X_NO_PIPELINE: errno = EPIPE; return "pipeline mode disabled"; - case X_TIMEDOUT: errno = ETIMEDOUT; return "timed out while waiting to complete"; - case X_INTERRUPTED: errno = EINTR; return "wait released without read"; - case X_INCOMPLETE: errno = EAGAIN; return "incomplete data transfer"; - case X_NULL: errno = EFAULT; return "null value"; - case X_PARSE_ERROR: errno = EINVAL; return "parse error"; - case X_NOT_ENOUGH_TOKENS: errno = EINVAL; return "not enough tokens"; - case X_GROUP_INVALID: errno = EINVAL; return "invalid group"; - case X_NAME_INVALID: errno = EINVAL; return "invalid variable name"; - case X_TYPE_INVALID: errno = EINVAL; return "invalid variable type"; - case X_SIZE_INVALID: errno = EDOM; return "invalid variable dimensions"; + case X_FAILURE: return "internal failure"; + case X_SUCCESS: return "success!"; + case X_ALREADY_OPEN: return "already opened"; + case X_NO_INIT: return "not initialized"; + case X_NO_SERVICE: return "connection lost?"; + case X_NO_BLOCKED_READ: return "no blocked calls"; + case X_NO_PIPELINE: return "pipeline mode disabled"; + case X_TIMEDOUT: return "timed out while waiting to complete"; + case X_INTERRUPTED: return "wait released without read"; + case X_INCOMPLETE: return "incomplete data transfer"; + case X_NULL: return "null value"; + case X_PARSE_ERROR: return "parse error"; + case X_NOT_ENOUGH_TOKENS: return "not enough tokens"; + case X_GROUP_INVALID: return "invalid group"; + case X_NAME_INVALID: return "invalid variable name"; + case X_TYPE_INVALID: return "invalid variable type"; + case X_SIZE_INVALID: return "invalid variable dimensions"; } return "unknown error"; } diff --git a/src/xlookup.c b/src/xlookup.c index 48808ee..4e66e34 100644 --- a/src/xlookup.c +++ b/src/xlookup.c @@ -13,6 +13,7 @@ #include #include +#define __XCHANGE_INTERNAL_API__ ///< Use internal definitions #include "xchange.h" /// \cond PRIVATE @@ -105,6 +106,7 @@ static XField *xLookupRemoveAsync(XLookupTable *tab, const char *id) { * @return the number of fields stored. */ long xLookupCount(const XLookupTable *tab) { + if(!tab) return 0; const XLookupPrivate *p = (XLookupPrivate *) tab->priv; return p->nEntries; } @@ -126,13 +128,16 @@ long xLookupCount(const XLookupTable *tab) { * @sa xLookupRemove() */ int xLookupPut(XLookupTable *tab, const char *prefix, const XField *field, XField **oldValue) { + static const char *fn = "xLookupPutField()"; + XLookupPrivate *p; int res; - if(!tab || !field) return xError("xLookupPutField()", X_NULL); + if(!tab) return x_error(X_NULL, EINVAL, fn, "input table is NULL"); + if(!field) return x_error(X_NULL, EINVAL, fn, "input field is NULL"); p = (XLookupPrivate *) tab->priv; - if(sem_wait(&p->sem) != 0) return X_FAILURE; + if(sem_wait(&p->sem) != 0) return x_error(X_FAILURE, errno, fn, "sem_wait() error"); res = xLookupPutAsync(tab, prefix, field, oldValue); sem_post(&p->sem); @@ -151,17 +156,21 @@ int xLookupPut(XLookupTable *tab, const char *prefix, const XField *field, XFiel * @sa xLookupPut() */ XField *xLookupRemove(XLookupTable *tab, const char *id) { + static const char *fn = "xLookupRemoveField()"; + XLookupPrivate *p; XField *f; if(!tab || !id) { - xError("xLookupRemoveField()", X_NULL); - errno = EINVAL; + x_error(X_NULL, EINVAL, fn, "NULL input: tab = %p, id = %p", tab, id); return NULL; } p = (XLookupPrivate *) tab->priv; - if(sem_wait(&p->sem) != 0) return NULL; + if(sem_wait(&p->sem) != 0) { + x_error(0, errno, fn, "sem_wait() error"); + return NULL; + } f = xLookupRemoveAsync(tab, id); sem_post(&p->sem); @@ -171,7 +180,7 @@ XField *xLookupRemove(XLookupTable *tab, const char *id) { -static int xLookupPullAllAsync(XLookupTable *tab, const char *prefix, const XStructure *s, boolean recursive) { +static int xLookupPutAllAsync(XLookupTable *tab, const char *prefix, const XStructure *s, boolean recursive) { XField *f; int lp = prefix ? strlen(prefix) : 0; int N = 0; @@ -192,7 +201,7 @@ static int xLookupPullAllAsync(XLookupTable *tab, const char *prefix, const XStr if(f->ndim == 0) sprintf(&p1[n], "%s", f->name); else sprintf(&p1[n], "%s" X_SEP "%d", f->name, (count + 1)); - N += xLookupPullAllAsync(tab, p1, &sub[count], TRUE); + N += xLookupPutAllAsync(tab, p1, &sub[count], TRUE); } } } @@ -252,13 +261,13 @@ int xLookupPutAll(XLookupTable *tab, const char *prefix, const XStructure *s, bo XLookupPrivate *p; int n; - if(!tab) return xError(fn, X_NULL); - if(!s) return xError(fn, X_NULL); + if(!tab) return x_error(X_NULL, EINVAL, fn, "input table is NULL"); + if(!s) return x_error(X_NULL, EINVAL, fn, "input structure is NULL"); p = (XLookupPrivate *) tab->priv; - if(sem_wait(&p->sem) != 0) return -1; + if(sem_wait(&p->sem) != 0) return x_error(X_FAILURE, errno, fn, "sem_wait() error"); - n = xLookupPullAllAsync(tab, prefix, s, recursive); + n = xLookupPutAllAsync(tab, prefix, s, recursive); sem_post(&p->sem); return n; @@ -284,11 +293,11 @@ int xLookupRemoveAll(XLookupTable *tab, const char *prefix, const XStructure *s, XLookupPrivate *p; int n; - if(!tab) return xError(fn, X_NULL); - if(!s) return xError(fn, X_STRUCT_INVALID); + if(!tab) return x_error(X_NULL, EINVAL, fn, "input table is NULL"); + if(!s) return x_error(X_NULL, EINVAL, fn, "input structure is NULL"); p = (XLookupPrivate *) tab->priv; - if(sem_wait(&p->sem) != 0) return -1; + if(sem_wait(&p->sem) != 0) return x_error(X_FAILURE, errno, fn, "sem_wait() error"); n = xLookupRemoveAllAsync(tab, prefix, s, recursive); sem_post(&p->sem); @@ -306,19 +315,32 @@ int xLookupRemoveAll(XLookupTable *tab, const char *prefix, const XStructure *s, * @return The new lookup table, or else NULL if there was an error. * * @sa xCreateLookup() - * @sa xDEstroyLookup() + * @sa xDestroyLookup() */ XLookupTable *xAllocLookup(unsigned int size) { + static const char *fn = "xAllocLookup"; + XLookupTable *tab; XLookupPrivate *p; - unsigned int n = 1; + unsigned int n = 2; + + if(size < 1) { + x_error(0, errno, fn, "invalid size: %d", size); + return NULL; + } + while(n < size) n <<= 1; p = (XLookupPrivate *) calloc(1, sizeof(XLookupPrivate)); + if(!p) { + x_error(0, errno, fn, "calloc() error"); + return NULL; + } + p->table = (XLookupEntry **) calloc(n, sizeof(XLookupEntry *)); if(!p->table) { - perror("ERROR! xAllocLookup"); + x_error(0, errno, fn, "calloc() error (n = %d)", n); free(p); return NULL; } @@ -327,6 +349,11 @@ XLookupTable *xAllocLookup(unsigned int size) { sem_init(&p->sem, FALSE, 1); tab = (XLookupTable *) calloc(1, sizeof(XLookupTable)); + if(!tab) { + x_error(0, errno, fn, "calloc() error"); + return NULL; + } + tab->priv = p; return tab; @@ -361,9 +388,9 @@ XLookupTable *xCreateLookup(const XStructure *s, boolean recursive) { } l = xAllocLookup(recursive ? xDeepCountFields(s) : xCountFields(s)); - if(!l) return NULL; + if(!l) return x_trace_null("xCreateLookup", NULL); - xLookupPullAllAsync(l, NULL, s, recursive); + xLookupPutAllAsync(l, NULL, s, recursive); return l; } @@ -386,16 +413,21 @@ XLookupTable *xCreateLookup(const XStructure *s, boolean recursive) { * @sa xGetField() */ XField *xLookupField(const XLookupTable *tab, const char *id) { + static const char *fn = "xLookupField"; + XLookupEntry *e; XLookupPrivate *p; if(!tab || !id) { - errno = EINVAL; + x_error(0, EINVAL, fn, "null parameter: tab=%p, id=%p", tab, id); return NULL; } p = (XLookupPrivate *) tab->priv; - if(sem_wait(&p->sem) != 0) return NULL; + if(sem_wait(&p->sem) != 0) { + x_error(0, errno, fn, "sem_wait() error"); + return NULL; + } e = xGetLookupEntryAsync(tab, id, xGetHash(id)); sem_post(&p->sem); diff --git a/src/xstruct.c b/src/xstruct.c index 0b9dfa0..c91d3d6 100644 --- a/src/xstruct.c +++ b/src/xstruct.c @@ -15,6 +15,7 @@ #include #include +#define __XCHANGE_INTERNAL_API__ ///< Use internal definitions #include "xchange.h" @@ -24,8 +25,10 @@ * \sa smaxDestroyStruct() * */ -__inline__ XStructure *xCreateStruct() { - return (XStructure *) calloc(1, sizeof(XStructure)); +XStructure *xCreateStruct() { + XStructure *s = (XStructure *) calloc(1, sizeof(XStructure)); + if(!s) x_error(0, errno, "xCreateStruct", "calloc() error"); + return s; } /** @@ -39,19 +42,26 @@ __inline__ XStructure *xCreateStruct() { * @sa xCopyOfField() */ XStructure *xCopyOfStruct(const XStructure *s) { + static const char *fn = "xCopyOfStruct"; + XStructure *copy; XField *f, *last = NULL; - if(!s) return NULL; - - copy = xCreateStruct(); - if(!copy) { - fprintf(stderr, "xCopyOfStruct(): %s\n", strerror(errno)); + if(!s) { + x_error(0, EINVAL, fn, "input structure is NULL"); return NULL; } - for(f=s->firstField; f != NULL; f=f->next) { + copy = xCreateStruct(); + if(!copy) + return x_trace_null(fn, NULL); + + for(f = s->firstField; f != NULL; f = f->next) { XField *cf = xCopyOfField(f); + if(!cf) { + xDestroyStruct(copy); + return x_trace_null(fn, NULL); + } if(f->type == X_STRUCT) if(f->value) { // Set the copy as the parent structure for any copied substructures. @@ -82,14 +92,19 @@ XStructure *xCopyOfStruct(const XStructure *s) { * @sa xCopyOfStruct() */ XField *xCopyOfField(const XField *f) { + static const char *fn = "xCopyOfField"; + XField *copy; int k, n, eCount; - if(!f) return NULL; + if(!f) { + x_error(0, EINVAL, fn, "input field is NULL"); + return NULL; + } copy = (XField *) malloc(sizeof(XField)); if(!copy) { - perror("xCopyOfField(): malloc(XField)"); + x_error(0, errno, fn, "malloc() error"); return NULL; } @@ -101,9 +116,8 @@ XField *xCopyOfField(const XField *f) { if(f->name) copy->name = xStringCopyOf(f->name); if(!f->name) { - perror("xCopyOfField(): xStringCopyOf(f->name)"); free(copy); - return NULL; + return x_trace_null(fn, f->name); } if(!f->value) return copy; @@ -113,15 +127,23 @@ XField *xCopyOfField(const XField *f) { // ----------------------------------------------------------------------------------- if(f->type == X_STRUCT) { - XStructure *s; + XStructure *s = (XStructure *) f->value, *c; eCount = xGetFieldCount(f); - s = calloc(eCount, sizeof(XStructure)); - copy->value = (char *) s; + c = calloc(eCount, sizeof(XStructure)); + if(!c) { + x_error(0, errno, fn, "calloc() error (eCount=%d)", eCount); + xDestroyField(copy); + return NULL; + } + copy->value = (char *) c; for(k = 0; k < eCount; k++) { - XStructure *e = xCopyOfStruct((XStructure *) f->value); - if(!e) continue; + XStructure *e = xCopyOfStruct(&s[k]); + if(!e) { + xDestroyField(copy); + return x_trace_null(fn, f->name); + } s[k].firstField = e->firstField; free(e); } @@ -147,8 +169,8 @@ XField *xCopyOfField(const XField *f) { // Allocate the copy value storage. copy->value = malloc(n); if(!copy->value) { - fprintf(stderr, "xCopyOfField(%c[%d]): %s\n", xTypeChar(f->type), eCount, strerror(errno)); - return copy; + x_error(0, errno, fn, "field %s value alloc: n=%d", f->name, n); + return NULL; } if(f->type == X_STRING || f->type == X_RAW) { @@ -176,10 +198,18 @@ XField *xCopyOfField(const XField *f) { * \sa xGetSubstruct() */ XField *xGetField(const XStructure *s, const char *id) { + static const char *fn = "xGetField"; + XField *e; - if(s == NULL) return NULL; - if(id == NULL) return NULL; + if(s == NULL) { + x_error(0, EINVAL, fn, "input structure is NULL"); + return NULL; + } + if(id == NULL) { + x_error(0, EINVAL, fn, "input id is NULL"); + return NULL; + } for(e = s->firstField; e != NULL; e = e->next) if(e->name) if(xMatchNextID(e->name, id) == X_SUCCESS) { const char *next = xNextIDToken(id); @@ -209,7 +239,6 @@ XStructure *xGetSubstruct(const XStructure *s, const char *id) { if(f && f->type == X_STRUCT) return (XStructure *) f->value; - errno = ENOENT; return NULL; } @@ -227,28 +256,38 @@ XStructure *xGetSubstruct(const XStructure *s, const char *id) { * \return A newly created field with the copy of the supplied data, or NULL if there was an error. */ XField *xCreateField(const char *name, XType type, int ndim, const int *sizes, const void *value) { - static const char *funcName = "xCreateField()"; + static const char *fn = "xCreateField"; XField *f; int n; if(!name) { - xError(funcName, X_NAME_INVALID); + x_error(0, EINVAL, fn, "name is NULL"); return NULL; } if(xLastSeparator(name)) { - // name contains a separator - xError(funcName, X_NAME_INVALID); + x_error(0, EINVAL, fn, "name contains separator: %s", name); return NULL; } if(ndim > X_MAX_DIMS) { - xError(funcName, X_SIZE_INVALID); + x_error(0, EINVAL, fn, "too many dimensions: %d", ndim); return NULL; } f = calloc(1, sizeof(XField)); + if(!f) { + x_error(0, errno, fn, "calloc error"); + return NULL; + } + f->name = xStringCopyOf(name); + if(!f->name) { + free(f); + x_error(0, errno, fn, "copy of name"); + return NULL; + } + f->type = type; if(ndim < 1) { @@ -267,7 +306,10 @@ XField *xCreateField(const char *name, XType type, int ndim, const int *sizes, c n = xGetElementCount(ndim, sizes) * xElementSizeOf(type); - if(type == X_STRUCT) { + if(n <= 0) { + x_trace(fn, "count", n); + } + else if(type == X_STRUCT) { f->value = (char *) value; } else { @@ -275,7 +317,7 @@ XField *xCreateField(const char *name, XType type, int ndim, const int *sizes, c if(!f->value) { xDestroyField(f); - xError(funcName, X_NULL); + x_error(0, errno, fn, "field %s value alloc: count=%d", f->name, n); return NULL; } @@ -297,7 +339,9 @@ XField *xCreateField(const char *name, XType type, int ndim, const int *sizes, c * \return A newly created field with the supplied data, or NULL if there was an error. */ XField *xCreateScalarField(const char *name, XType type, const void *value) { - return xCreateField(name, type, 0, NULL, value); + XField *f = xCreateField(name, type, 0, NULL, value); + if(!f) return x_trace_null("xCreateScalarField", NULL); + return f; } /** @@ -313,7 +357,9 @@ XField *xCreateScalarField(const char *name, XType type, const void *value) { */ XField *xCreate1DField(const char *name, XType type, int count, const void *values) { const int sizes[] = { count }; - return xCreateField(name, type, 1, sizes, values); + XField *f = xCreateField(name, type, 1, sizes, values); + if(!f) return x_trace_null("xCreate1DField", NULL); + return f; } /** @@ -326,7 +372,9 @@ XField *xCreate1DField(const char *name, XType type, int count, const void *valu * */ XField *xCreateDoubleField(const char *name, double value) { - return xCreateScalarField(name, X_DOUBLE, &value); + XField *f = xCreateScalarField(name, X_DOUBLE, &value); + if(!f) return x_trace_null("xCreateDoubleField", NULL); + return f; } /** @@ -340,7 +388,9 @@ XField *xCreateDoubleField(const char *name, double value) { * @sa xCreateLongField() */ XField *xCreateIntField(const char *name, int value) { - return xCreateScalarField(name, X_INT, &value); + XField *f = xCreateScalarField(name, X_INT, &value); + if(!f) return x_trace_null("xCreateIntField", NULL); + return f; } /** @@ -354,7 +404,9 @@ XField *xCreateIntField(const char *name, int value) { * @sa xCreateIntField() */ XField *xCreateLongField(const char *name, long long value) { - return xCreateScalarField(name, X_LONG, &value); + XField *f = xCreateScalarField(name, X_LONG, &value); + if(!f) return x_trace_null("xCreateLongField", NULL); + return f; } @@ -367,7 +419,9 @@ XField *xCreateLongField(const char *name, long long value) { * \return A newly created field with the supplied data, or NULL if there was an error. */ XField *xCreateBooleanField(const char *name, boolean value) { - return xCreateScalarField(name, X_BOOLEAN, &value); + XField *f = xCreateScalarField(name, X_BOOLEAN, &value); + if(!f) return x_trace_null("xCreateBooleanField", NULL); + return f; } @@ -381,7 +435,9 @@ XField *xCreateBooleanField(const char *name, boolean value) { */ XField *xCreateStringField(const char *name, const char *value) { const char *empty = ""; - return xCreateScalarField(name, X_STRING, value ? &value : &empty); + XField *f = xCreateScalarField(name, X_STRING, value ? &value : &empty); + if(!f) return x_trace_null("xCreateStringField", NULL); + return f; } @@ -433,36 +489,36 @@ int xGetFieldCount(const XField *f) { * \sa xGetSubstruct() */ XField *xSetSubstruct(XStructure *s, const char *name, XStructure *substruct) { - static const char *funcName = "xSetSubstruct()"; + static const char *fn = "xSetSubstruct"; XField *f; if(!s) { - xError(funcName, X_STRUCT_INVALID); + x_error(0, EINVAL, fn, "input structure is NULL"); return NULL; } if(!name) { - xError(funcName, X_NAME_INVALID); + x_error(0, EINVAL, fn, "input name is NULL"); return NULL; } if(!name[0]) { - xError(funcName, X_NAME_INVALID); + x_error(0, EINVAL, fn, "input name is empty"); return NULL; } if(!substruct) { - xError(funcName, X_NULL); + x_error(0, EINVAL, fn, "input sub-structure is NULL"); return NULL; } if(substruct->parent) { - xError(funcName, X_INCOMPLETE); + x_error(0, EALREADY, fn, "input substructure is already assigned"); return NULL; } f = xCreateScalarField(name, X_STRUCT, substruct); - if(!f) return NULL; + if(!f) return x_trace_null(fn, name); substruct->parent = s; @@ -480,22 +536,22 @@ XField *xSetSubstruct(XStructure *s, const char *name, XStructure *substruct) { * */ XField *xRemoveField(XStructure *s, const char *name) { - static const char *funcName = "xRemoveField()"; + static const char *fn = "xRemoveField"; XField *e, *last = NULL; if(!s) { - xError(funcName, X_STRUCT_INVALID); + x_error(0, EINVAL, fn, "input structure is NULL"); return NULL; } if(!name) { - xError(funcName, X_NAME_INVALID); + x_error(0, EINVAL, fn, "input name is NULL"); return NULL; } if(!name[0]) { - xError(funcName, X_NAME_INVALID); + x_error(0, EINVAL, fn, "input name is empty"); return NULL; } @@ -537,14 +593,15 @@ XField *xRemoveField(XStructure *s, const char *name) { * \sa xReverseFieldOrder() */ int xInsertField(XStructure *s, XField *f) { - static const char *funcName = "xInsertField()"; + static const char *fn = "xInsertField"; - if(!s) return xError(funcName, X_STRUCT_INVALID); - if(!f) return xError(funcName, X_NULL); - if(!f->name) return xError(funcName, X_NAME_INVALID); + if(!s) return x_error(X_STRUCT_INVALID, EINVAL, fn, "input structure is NULL"); + if(!f) return x_error(X_NULL, EINVAL, fn, "input field is NULL"); + if(!f->name) return x_error(X_NAME_INVALID, EINVAL, fn, "field->name is NULL"); + if(!f->name[0]) return x_error(X_NAME_INVALID, EINVAL, fn, "field->name is empty"); // The field name contains a separator? - if(xLastSeparator(f->name)) return xError(funcName, X_NAME_INVALID); + if(xLastSeparator(f->name)) return x_error(X_NAME_INVALID, EINVAL, fn, "field->name contains separator"); // Add the new field at the head of the list... f->next = s->firstField; @@ -577,25 +634,27 @@ int xInsertField(XStructure *s, XField *f) { * \sa xGetSubstruct() */ XField *xSetField(XStructure *s, XField *f) { - static const char *funcName = "xSetField()"; + static const char *fn = "xSetField"; XField *e, *last = NULL; if(!s) { - xError(funcName, X_STRUCT_INVALID); - errno = EINVAL; + x_error(0, EINVAL, fn, "input structure is NULL"); return NULL; } if(!f) { - xError(funcName, X_NULL); - errno = EINVAL; + x_error(0, EINVAL, fn, "input field is NULL"); return NULL; } if(!f->name) { - xError(funcName, X_NAME_INVALID); - errno = EINVAL; + x_error(0, EINVAL, fn, "input field->name is NULL"); + return NULL; + } + + if(!f->name[0]) { + x_error(0, EINVAL, fn, "input field->name is empty"); return NULL; } @@ -635,7 +694,10 @@ int xCountFields(const XStructure *s) { XField *f; int n = 0; - if(!s) return 0; + if(!s) { + x_warn("xCountFields", "input structure is NULL"); + return 0; + } for(f = s->firstField; f != NULL; f = f->next) n++; @@ -693,7 +755,10 @@ void xDestroyField(XField *f) { void xClearStruct(XStructure *s) { XField *f; - if(s == NULL) return; + if(s == NULL) { + x_warn("xClearStruct", "input structure is NULL"); + return; + } for(f = s->firstField; f != NULL; ) { XField *next = f->next; @@ -718,19 +783,15 @@ void xClearStruct(XStructure *s) { * @see xReduceAllDims() */ int xReduceDims(int *ndim, int *sizes) { + static const char *fn = "xReduceDims"; + int i; - if(!ndim) { - errno = EINVAL; - return X_SIZE_INVALID; - } + if(!ndim) return x_error(X_SIZE_INVALID, EINVAL, fn, "ndim pointer is NULL"); if(*ndim <= 0) return X_SUCCESS; - if(sizes == NULL) { - errno = EINVAL; - return -1; - } + if(sizes == NULL) return x_error(X_SIZE_INVALID, EINVAL, fn, "sizes is NULL (ndim = %d)", ndim); for(i = *ndim; --i >= 0; ) if (sizes[i] == 0) { *ndim = 1; @@ -758,17 +819,17 @@ int xReduceDims(int *ndim, int *sizes) { * @see xReduceDims() */ int xReduceAllDims(XStructure *s) { + static const char *fn; + XField *f; - if(!s) { - errno = EINVAL; - return X_STRUCT_INVALID; - } + if(!s) return x_error(X_STRUCT_INVALID, EINVAL, fn, "input structure is NULL"); f = s->firstField; if(f->next == NULL && f->type == X_STRUCT) { XStructure *sub = (XStructure *) f; XField *sf; + int status; s->firstField = sub->firstField; for(sf = s->firstField; sf; sf = sf->next) if(sf->type == X_STRUCT) { @@ -777,8 +838,11 @@ int xReduceAllDims(XStructure *s) { while(--i >= 0) ss[i].parent = s; } + status = xReduceAllDims(s); + if(status < 0) x_trace(fn, f->name, status); + free(f); - return xReduceAllDims(s); + return status; } for(f = s->firstField; f; f = f->next) { @@ -787,7 +851,15 @@ int xReduceAllDims(XStructure *s) { if(f->type == X_STRUCT) { XStructure *sub = (XStructure *) f; int i = xGetFieldCount(f); - while(--i >= 0) xReduceAllDims(&sub[i]); + while(--i >= 0) { + int status = xReduceAllDims(&sub[i]); + if(status < 0) { + char *id = malloc(strlen(f->name + 20)); + sprintf(id, "%s[%d]", f->name, i); + x_trace(fn, id, status); + free(id); + } + } } } @@ -805,7 +877,10 @@ int xReduceAllDims(XStructure *s) { char *xNextIDToken(const char *id) { char *next; - if(!id) return NULL; + if(!id) { + x_warn("xNextIDToken", "input is NULL"); + return NULL; + } // Ignore leading separator. if(!strncmp(id, X_SEP, X_SEP_LENGTH)) id += X_SEP_LENGTH; @@ -821,11 +896,16 @@ char *xNextIDToken(const char *id) { * @return Pointer to the start of the next compound ID token, or NULL if there is no more components in the ID. */ char *xCopyIDToken(const char *id) { + static const char *fn = "xCopyIDToken"; + const char *next; char *token; int l; - if(!id) return NULL; + if(!id) { + x_warn(fn, "input is NULL"); + return NULL; + } // Ignore leading separator. if(!strncmp(id, X_SEP, X_SEP_LENGTH)) id += X_SEP_LENGTH; @@ -835,7 +915,10 @@ char *xCopyIDToken(const char *id) { else l = strlen(id); token = malloc(l+1); - if(!token) return NULL; + if(!token) { + x_error(0, errno, fn, "malloc error"); + return NULL; + } memcpy(token, id, l); token[l] = '\0'; @@ -852,16 +935,17 @@ char *xCopyIDToken(const char *id) { * arguments are invalid. */ int xMatchNextID(const char *token, const char *id) { - int L; + static const char *fn = "xMatchNextID"; - if(!token) return X_NULL; - if(!id) return X_NULL; - if(*token == '\0') return X_NAME_INVALID; - if(*id == '\0') return X_GROUP_INVALID; + int L; + if(!token) return x_error(X_NULL, EINVAL, fn, "input token is NULL"); + if(!id) return x_error(X_NULL, EINVAL, fn, "input id is NULL"); + if(*token == '\0') return x_error(X_NAME_INVALID, EINVAL, fn, "input token is empty"); + if(*id == '\0') return x_error(X_GROUP_INVALID, EINVAL, fn, "input id is empty"); L = strlen(token); - if(strncmp(id, token, L)) return X_FAILURE; + if(strncmp(id, token, L) != 0) return X_FAILURE; if(id[L] == '\0') return X_SUCCESS; @@ -884,15 +968,23 @@ int xMatchNextID(const char *token, const char *id) { * \sa xSplitID() */ char *xGetAggregateID(const char *table, const char *key) { + static const char *fn = "xGetAggregateID"; + char *id; - if(table == NULL && key == NULL) return NULL; + if(table == NULL && key == NULL) { + x_error(0, EINVAL, fn, "both inputs are NULL"); + return NULL; + } if(table == NULL) return xStringCopyOf(key); if(key == NULL) return xStringCopyOf(table); id = (char *) malloc(strlen(table) + strlen(key) + X_SEP_LENGTH + 1); // : - if(!id) return NULL; + if(!id) { + x_error(0, errno, fn, "malloc error"); + return NULL; + } sprintf(id, "%s" X_SEP "%s", table, key); return id; @@ -907,7 +999,14 @@ char *xGetAggregateID(const char *table, const char *key) { * @sa xSplitID() */ char *xLastSeparator(const char *id) { - int l = strlen(id); + int l; + + if(!id) { + x_error(0, EINVAL, "xLastSeparator", "input id is NULL"); + return NULL; + } + + l = strlen(id); while(--l >= 0) if(id[l] == X_SEP[0]) if(!strncmp(&id[l], X_SEP, X_SEP_LENGTH)) return (char *) &id[l]; return NULL; @@ -933,7 +1032,7 @@ char *xLastSeparator(const char *id) { * \sa xLastSeparator() */ int xSplitID(char *id, char **pKey) { - if(id == NULL) return X_NULL; + if(id == NULL) return x_error(X_NULL, EINVAL, "xSplitID", "input id is NULL"); // Default NULL return for the second component. if(pKey) *pKey = NULL; @@ -957,10 +1056,12 @@ int xSplitID(char *id, char **pKey) { * @sa xCountFields() */ long xDeepCountFields(const XStructure *s) { + static const char *fn = "xDeepCountFields"; + XField *f; long n = 0; - if(!s) return 0; + if(!s) return x_error(0, EINVAL, fn, "input structure is NULL"); for(f = s->firstField; f != NULL; f = f->next) { n++; @@ -970,7 +1071,10 @@ long xDeepCountFields(const XStructure *s) { int i = xGetFieldCount(f); while(--i >= 0) { long m = xDeepCountFields(&sub[i]); - if(m < 0) return -1; + if(m < 0) { + x_trace(fn, f->name, m); + return -1; + } n += m; } } @@ -995,10 +1099,10 @@ int xSortFields(XStructure *s, int (*cmp)(const XField **f1, const XField **f2), XField **array, *f; int i, n; - if(s == NULL || cmp == NULL) return xError("xSortFields()", X_NULL); + if(s == NULL || cmp == NULL) return x_error(X_NULL, EINVAL, "xSortFields", "NULL argument: s=%p, cmp=%p", s, cmp); for(n = 0, f = s->firstField; f != NULL; f = f->next, n++) - if(f->type == X_STRUCT && recursive) { + if(f->type == X_STRUCT && recursive && f->value) { XStructure *sub = (XStructure *) f->value; int k = xGetFieldCount(f); while (--k >= 0) xSortFields(&sub[k], cmp, TRUE); @@ -1038,7 +1142,8 @@ static int XFieldNameCmp(const XField **f1, const XField **f2) { * @sa xReverseFieldOrder() */ int xSortFieldsByName(XStructure *s, boolean recursive) { - return xSortFields(s, XFieldNameCmp, recursive); + prop_error("xSortFieldsByName", xSortFields(s, XFieldNameCmp, recursive)); + return X_SUCCESS; } /** @@ -1055,7 +1160,7 @@ int xSortFieldsByName(XStructure *s, boolean recursive) { int xReverseFieldOrder(XStructure *s, boolean recursive) { XField *f, *rev = NULL; - if(s == NULL) return xError("xReverseFieldOrder()", X_NULL); + if(s == NULL) return x_error(X_NULL, EINVAL, "xReverseFieldOrder", "input structure is NULL"); f = s->firstField; s->firstField = NULL; @@ -1065,7 +1170,7 @@ int xReverseFieldOrder(XStructure *s, boolean recursive) { f->next = rev; rev = f; - if(f->type == X_STRUCT && recursive) { + if(f->type == X_STRUCT && recursive && f->value) { XStructure *sub = (XStructure *) f->value; int i = xGetFieldCount(f); while(--i >= 0) xReverseFieldOrder(&sub[i], TRUE);