/* $Id: cpl_error.c,v 1.51 2008/01/30 12:44:21 yjung Exp $ * * This file is part of the ESO Common Pipeline Library * Copyright (C) 2001-2005 European Southern Observatory * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * $Author: yjung $ * $Date: 2008/01/30 12:44:21 $ * $Revision: 1.51 $ * $Name: $ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "cpl_error_impl.h" /** * @defgroup cpl_error Error handling * * This module provides functions to maintain the @em cpl_error_code * set by any CPL function, similarly to what is done with the @em errno * variable of the standard C library. The following guidelines are * respected: * * - If no error occurs in a CPL function the @em cpl_error_code will * remain unchanged. * - If an error occurs in a CPL function, a new CPL error is set, causing * the @em cpl_error_code to be modified to the new error. * * A @em cpl_error_code equal to the enumeration constant * @c CPL_ERROR_NONE would indicate no error condition. Note, * however, that the @em cpl_error_code is only set when an error * occurs, and it is not reset by successful function calls. * For this reason it may be appropriate in some cases to reset * the @em cpl_error_code using the function @c cpl_error_reset(). * The @em cpl_error_code set by a CPL function can be obtained by * calling the function @c cpl_error_get_code(), but functions of * type @em cpl_error_code would not only return this code directly, * but would also return @c CPL_ERROR_NONE in case of success. Other * CPL functions return zero on success, or a non-zero value to indicate * a change of the @em cpl_error_code, while CPL functions returning * a pointer would flag an error by returning a @c NULL. * * To each @em cpl_error_code is associated a standard error message, * that can be obtained by calling the function @c cpl_error_get_message(). * Conventionally, no CPL function will ever display any error message, * leaving to the caller the decision of how to handle a given error * condition. A call to the function @c cpl_error_get_function() would * return the name of the function where the error occurred, and the * functions @c cpl_error_get_file() and @c cpl_error_get_line() would * also return the name of the source file containing the function * code, and the line number where the error occurred. The function * @c cpl_error_get_where() would gather all this items together, in * a colon-separated string. * * @par Synopsis: * @code * #include * @endcode */ /**@{*/ #ifndef CPL_LINESZ #ifdef FITS_LINESZ #define CPL_LINESZ FITS_LINESZ #else #define CPL_LINESZ 6 #endif #endif #define MAX_WHERE_LENGTH (2+(MAX_NAME_LENGTH)+(MAX_FILE_LENGTH)+(CPL_LINESZ)) /*----------------------------------------------------------------------------- Private functions -----------------------------------------------------------------------------*/ static cpl_boolean cpl_error_status = CPL_FALSE; static cpl_boolean cpl_error_read_only = CPL_FALSE; static cpl_error * cpl_error_fill(const char *, cpl_error_code, const char *, unsigned); /*----------------------------------------------------------------------------- Definitions of functions -----------------------------------------------------------------------------*/ /** * @brief * Reset the @em cpl_error_code. * * @return Nothing. * * This function initialises the @em cpl_error_code to @c CPL_ERROR_NONE. */ void cpl_error_reset(void) { if (!cpl_error_read_only) cpl_error_status = CPL_FALSE; } /* * @brief * Set CPL error code, function name, source file & line number where it * occurred along with a text message * * @param function Character string with function name, cpl_func * @param code Error code * @param file Character string with source file name (__FILE__) * @param line Integer with line number (__LINE__) * @param text Text to append to error message, may be printf-like * @param ... Optional, variable argument list for the printf-like text * @return The CPL error code * @note This function is only provided for cpl_error_set*() macros. * @see cpl_error_set_message() */ cpl_error_code cpl_error_set_message_macro(const char * function, cpl_error_code code, const char * file, unsigned line, const char * text, ...) { if (!cpl_error_read_only && code != CPL_ERROR_NONE) { cpl_error * error = cpl_error_fill(function, code, file, line); if (text != NULL && text[0] != '\0' && (text[0] != ' ' || text[1] != '\0')) { /* The user supplied a message */ /* Calling this function with text NULL or empty is supported, but causes a compiler warning on some systems. The support calls that do not set a message, call with a single space causes that user message to be ignored. */ static char message[CPL_ERROR_MAX_MESSAGE_LENGTH]; int nlen; va_list arglist; va_start(arglist, text); nlen = (int)cx_vsnprintf((cxchar *)message, (cxsize)CPL_ERROR_MAX_MESSAGE_LENGTH, (const cxchar *)text, arglist); va_end(arglist); if (nlen > 0) { /* Concatenate the standard message and the user supplied message */ cx_assert( error != NULL ); (void)cx_snprintf((cxchar *)error->msg, (cxsize)CPL_ERROR_MAX_MESSAGE_LENGTH, (const cxchar *)"%s: %s", cpl_error_get_message_default(code), message); } } } return code; } /** * @brief * Get the last @em cpl_error_code set. * * @return @em cpl_error_code of last occurred CPL error. * * Get @em cpl_error_code of last occurred error. */ cpl_error_code cpl_error_get_code(void) { cpl_error_code code = CPL_ERROR_NONE; if (cpl_error_status) { const cpl_error * error = cpl_errorstate_find(); code = error->code; } return code; } /** * @brief * Get standard message of the current CPL error. * * @return Standard message of the current CPL error. * * If the @em cpl_error_code is equal to @c CPL_ERROR_NONE, * an empty string is returned. */ const char * cpl_error_get_message(void) { const cpl_error * error; if (!cpl_error_status) return cpl_error_get_message_default(CPL_ERROR_NONE); error = cpl_errorstate_find(); cx_assert(error->code != CPL_ERROR_NONE); return strlen(error->msg) ? error->msg : cpl_error_get_message_default(error->code); } /** * @brief * Get the function name where the last CPL error occurred. * * @return Identifier string of the function name where the last CPL error * occurred. * * Get the function name where the last CPL error occurred. */ const char *cpl_error_get_function(void) { const char * function = ""; if (cpl_error_status) { const cpl_error * error = cpl_errorstate_find(); function = error->function; } return function; } /** * @brief * Get function name, source file and line number where the last * CPL error occurred. * * @return String containing function name, source file and line number * separated by colons (:). * * Get where the last CPL error occurred in the form * @c function_name:source_file:line_number */ const char *cpl_error_get_where(void) { static char cpl_error_where_string[MAX_WHERE_LENGTH]; (void)cx_snprintf((cxchar *)cpl_error_where_string, (cxsize)MAX_WHERE_LENGTH, (const cxchar *)"%s:%s:%u", cpl_error_get_function(), cpl_error_get_file(), cpl_error_get_line()); return cpl_error_where_string; } /** * @brief * Get the source code file name where the last CPL error occurred. * * @return Name of source file name where the last CPL error occurred. * * Get the source code file name where the last CPL error occurred. */ const char *cpl_error_get_file(void) { const char * file = ""; if (cpl_error_status) { const cpl_error * error = cpl_errorstate_find(); file = error->file; } return file; } /** * @brief * Get the line number where the last CPL error occurred. * * @return Line number of the source file where the last CPL error occurred. * * Get the line number of the source file where the last CPL error occurred. */ unsigned cpl_error_get_line(void) { unsigned line = 0; if (cpl_error_status) { const cpl_error * error = cpl_errorstate_find(); line = error->line; } return line; } /*----------------------------------------------------------------------------*/ /** @brief Return the standard CPL error message of the current CPL error @param code The error code of the current CPL error @return The standard CPL error message of the current CPL error */ /*----------------------------------------------------------------------------*/ const char * cpl_error_get_message_default(cpl_error_code code) { const char * message; switch (code) { case CPL_ERROR_NONE: message = ""; break; case CPL_ERROR_UNSPECIFIED: message = "An unspecified error"; break; case CPL_ERROR_DUPLICATING_STREAM: message = "Cannot duplicate output stream"; break; case CPL_ERROR_ASSIGNING_STREAM: message = "Cannot associate a stream with a file descriptor"; break; case CPL_ERROR_FILE_IO: message = "File access permission denied"; break; case CPL_ERROR_BAD_FILE_FORMAT: message = "Bad file format"; break; case CPL_ERROR_FILE_ALREADY_OPEN: message = "File already open"; break; case CPL_ERROR_FILE_NOT_CREATED: message = "File cannot be created"; break; case CPL_ERROR_FILE_NOT_FOUND: message = "File not found"; break; case CPL_ERROR_DATA_NOT_FOUND: message = "Data not found"; break; case CPL_ERROR_ACCESS_OUT_OF_RANGE: message = "Access beyond boundaries"; break; case CPL_ERROR_NULL_INPUT: message = "Null input data"; break; case CPL_ERROR_INCOMPATIBLE_INPUT: message = "Input data do not match"; break; case CPL_ERROR_ILLEGAL_INPUT: message = "Illegal input"; break; case CPL_ERROR_ILLEGAL_OUTPUT: message = "Illegal output"; break; case CPL_ERROR_UNSUPPORTED_MODE: message = "Unsupported mode"; break; case CPL_ERROR_SINGULAR_MATRIX: message = "Singular matrix"; break; case CPL_ERROR_DIVISION_BY_ZERO: message = "Division by zero"; break; case CPL_ERROR_TYPE_MISMATCH: message = "Type mismatch"; break; case CPL_ERROR_INVALID_TYPE: message = "Invalid type"; break; case CPL_ERROR_CONTINUE: message = "The iterative process did not converge"; break; case CPL_ERROR_NO_WCS: message = "The WCS functionalities are missing"; break; case CPL_ERROR_EOL: message = "A user-defined error"; break; default: message = "A user-defined error"; break; } return message; } /**@}*/ /*----------------------------------------------------------------------------*/ /** @internal @brief Get the status of the CPL error state @return True iff an error code has been set @note This function may only be used by the cpl_errorstate module. CPL_FALSE: The CPL error state is clear, no history may be read. CPL_TRUE: The CPL error state has been set, and contains at least one CPL error state (with a non-zero error code). */ /*----------------------------------------------------------------------------*/ cpl_boolean cpl_error_is_set(void) { return cpl_error_status; } /*----------------------------------------------------------------------------*/ /** @internal @brief Get the read-only status of the CPL error system @return True iff the CPL error system is read-only @note This function may only be used by the cpl_errorstate module. */ /*----------------------------------------------------------------------------*/ cpl_boolean cpl_error_is_readonly(void) { return cpl_error_read_only; } /*----------------------------------------------------------------------------*/ /** @internal @brief Set the status of the CPL error system to read-only @note This function may only be used by the cpl_errorstate module. */ /*----------------------------------------------------------------------------*/ void cpl_error_set_readonly(void) { cpl_error_read_only = CPL_TRUE; } /*----------------------------------------------------------------------------*/ /** @internal @brief Set the status of the CPL error system to read/write @note This function may only be used by the cpl_errorstate module. */ /*----------------------------------------------------------------------------*/ void cpl_error_reset_readonly(void) { cpl_error_read_only = CPL_FALSE; } /* * @internal * @brief Set the basics of a CPL error and return the struct * @param function Character string with function name, cpl_func * @param code Error code * @param file Character string with source file name (__FILE__) * @param line Integer with line number (__LINE__) * @return A pointer to the struct of the error. * @see cpl_error_set_message_macro() */ static cpl_error * cpl_error_fill(const char * function, cpl_error_code code, const char * file, unsigned line) { cpl_error * error = cpl_errorstate_append(); cx_assert(error != NULL); cx_assert(code != CPL_ERROR_NONE); cx_assert(!cpl_error_read_only); cpl_error_status = CPL_TRUE; error->code = code; error->line = line; if (function == NULL) { error->function[0] = '\0'; } else { (void)strncpy(error->function, function, MAX_NAME_LENGTH); error->function[MAX_NAME_LENGTH] = '\0'; } if (file == NULL) { error->file[0] = '\0'; } else { (void)strncpy(error->file, file, MAX_FILE_LENGTH); error->file[MAX_FILE_LENGTH] = '\0'; } error->msg[0] = '\0'; return error; }