/** @file io.c * MAT File I/O Utility Functions */ /* * Copyright (c) 2005-2019, Christopher C. Hulbert * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include "matio_private.h" #if !defined(HAVE_VA_COPY) && defined(HAVE___VA_COPY) # define va_copy(d,s) __va_copy(d,s) #elif !defined(HAVE_VA_COPY) # define va_copy(d,s) memcpy(&(d),&(s),sizeof(va_list)) #endif static void (*logfunc)(int log_level, char *message ) = NULL; static const char *progname = NULL; /** @brief Allocates and prints to a new string * * @ingroup mat_util * @param format format string * @param ap variable argument list * @return Newly allocated string with format printed to it */ char * strdup_vprintf(const char* format, va_list ap) { va_list ap2; int size; char* buffer; va_copy(ap2, ap); size = mat_vsnprintf(NULL, 0, format, ap2)+1; va_end(ap2); buffer = (char*)malloc(size+1); if ( !buffer ) return NULL; mat_vsnprintf(buffer, size, format, ap); return buffer; } /** @brief Allocates and prints to a new string using printf format * * @ingroup mat_util * @param format format string * @return Pointer to resulting string, or NULL if there was an error */ char * strdup_printf(const char* format, ...) { char* buffer; va_list ap; va_start(ap, format); buffer = strdup_vprintf(format, ap); va_end(ap); return buffer; } /** @brief Default logging function * * Prints the message to stderr/stdout and calls abort() if the * log_level equals MATIO_LOG_LEVEL_ERROR. * @ingroup mat_util * @param log_level logging level * @param message logging message */ static void mat_logfunc( int log_level, char *message ) { if ( progname ) { if ( log_level & MATIO_LOG_LEVEL_CRITICAL) { fprintf(stderr,"-E- %s: %s\n", progname, message); fflush(stderr); } else if ( log_level & MATIO_LOG_LEVEL_ERROR ) { fprintf(stderr,"-E- %s: %s\n", progname, message); fflush(stderr); abort(); } else if ( log_level & MATIO_LOG_LEVEL_WARNING ) { fprintf(stderr,"-W- %s: %s\n", progname, message); fflush(stderr); } else if ( log_level & MATIO_LOG_LEVEL_DEBUG ) { fprintf(stderr,"-D- %s: %s\n", progname, message); fflush(stderr); } else if ( log_level & MATIO_LOG_LEVEL_MESSAGE ) { fprintf(stdout,"%s\n", message); fflush(stdout); } } else { if ( log_level & MATIO_LOG_LEVEL_CRITICAL) { fprintf(stderr,"-E- : %s\n", message); fflush(stderr); } else if ( log_level & MATIO_LOG_LEVEL_ERROR ) { fprintf(stderr,"-E- : %s\n", message); fflush(stderr); abort(); } else if ( log_level & MATIO_LOG_LEVEL_WARNING ) { fprintf(stderr,"-W- : %s\n", message); fflush(stderr); } else if ( log_level & MATIO_LOG_LEVEL_DEBUG ) { fprintf(stderr,"-D- : %s\n", message); fflush(stderr); } else if ( log_level & MATIO_LOG_LEVEL_MESSAGE ) { fprintf(stdout,"%s\n", message); fflush(stdout); } } } /** @brief Logging function handler * * Calls either the default logging function @ref mat_logfunc * set by @ref Mat_LogInit or a custom logging function set by * @ref Mat_LogInitFunc. * @ingroup mat_util * @param loglevel log level * @param format format string * @param ap variable argument list */ static void mat_log(int loglevel, const char *format, va_list ap) { char* buffer; if ( !logfunc ) return; buffer = strdup_vprintf(format, ap); (*logfunc)(loglevel,buffer); free(buffer); return; } #if defined(MAT73) && MAT73 #define MSG_SIZE 1024 /** @brief HDF5 Error logging function * * @ingroup mat_util * @param n indexed position of the error in the stack * @param err_desc pointer to a data structure describing the error * @param client_data pointer to client data */ static herr_t mat_h5_log_func(unsigned n, const H5E_error_t *err_desc, void *client_data) { char maj[MSG_SIZE]; char min[MSG_SIZE]; char cls[MSG_SIZE]; if ( H5Eget_class_name(err_desc->cls_id, cls, MSG_SIZE) < 0 ) return -1; if ( H5Eget_msg(err_desc->maj_num, NULL, maj, MSG_SIZE) < 0 ) return -1; if ( H5Eget_msg(err_desc->min_num, NULL, min, MSG_SIZE) < 0 ) return -1; Mat_Critical("%s error #%03u in %s()\n" " file : %s:%u\n" " major: %s\n" " minor: %s", cls, n, err_desc->func_name, err_desc->file_name, err_desc->line, maj, min); return 0; } /** @brief HDF5 Error logging function callback * * @ingroup mat_util * @param estack error stack identifier * @param client_data pointer to client data */ static herr_t mat_h5_log_cb(hid_t estack, void *client_data) { hid_t estack_id = H5Eget_current_stack(); H5Ewalk(estack_id, H5E_WALK_DOWNWARD, mat_h5_log_func, client_data); return H5Eclose_stack(estack_id); } #endif /** @var debug * @brief holds the debug level set in @ref Mat_SetDebug * This variable is used to determine if information should be printed to * the screen * @ingroup mat_util */ static int debug = 0; /** @var verbose * @brief holds the verbose level set in @ref Mat_SetVerbose * This variable is used to determine if information should be printed to * the screen * @ingroup mat_util */ static int verbose = 0; /** @var silent * @brief holds the silent level set in @ref Mat_SetVerbose * If set, all output which is not an error is not displayed regardless * of verbose level * @ingroup mat_util */ static int silent = 0; /** @brief Sets verbose parameters * * Sets the verbose level and silent level. These values are used by * programs to determine what information should be printed to the screen * @ingroup mat_util * @param verb sets logging verbosity level * @param s sets logging silent level */ int Mat_SetVerbose( int verb, int s ) { verbose = verb; silent = s; return 0; } /** @brief Set debug parameter * * Sets the debug level. This value is used by * program to determine what information should be printed to the screen * @ingroup mat_util * @param d sets logging debug level */ int Mat_SetDebug( int d ) { debug = d; return 0; } /** @brief Log a message unless silent * * Logs the message unless the silent option is set (See @ref Mat_SetVerbose). * To log a message based on the verbose level, use @ref Mat_VerbMessage * @ingroup mat_util * @param format message format */ int Mat_Message( const char *format, ... ) { va_list ap; if ( silent ) return 0; if ( !logfunc ) return 0; va_start(ap, format ); mat_log(MATIO_LOG_LEVEL_MESSAGE, format, ap ); va_end(ap); return 0; } /** @brief Log a message based on debug level * * If @e level is less than or equal to the set debug level, the message * is printed. If the level is higher than the set debug level nothing * is displayed. * @ingroup mat_util * @param level debug level * @param format message format */ int Mat_DebugMessage( int level, const char *format, ... ) { va_list ap; if ( silent ) return 0; if ( level > debug ) return 0; va_start(ap, format ); mat_log(MATIO_LOG_LEVEL_DEBUG, format, ap ); va_end(ap); return 0; } /** @brief Log a message based on verbose level * * If @e level is less than or equal to the set verbose level, the message * is printed. If the level is higher than the set verbose level nothing * is displayed. * @ingroup mat_util * @param level verbose level * @param format message format */ int Mat_VerbMessage( int level, const char *format, ... ) { va_list ap; if ( silent ) return 0; if ( level > verbose ) return 0; va_start(ap, format ); mat_log(MATIO_LOG_LEVEL_MESSAGE, format, ap ); va_end(ap); return 0; } /** @brief Logs a Critical message and returns to the user * * Logs a Critical message and returns to the user. If the program should * stop running, use @ref Mat_Error * @ingroup mat_util * @param format format string identical to printf format * @param ... arguments to the format string */ void Mat_Critical( const char *format, ... ) { va_list ap; va_start(ap, format ); mat_log(MATIO_LOG_LEVEL_CRITICAL, format, ap ); va_end(ap); } /** @brief Logs a Critical message and aborts the program * * Logs an Error message and aborts * @ingroup mat_util * @param format format string identical to printf format * @param ... arguments to the format string */ void Mat_Error( const char *format, ... ) { va_list ap; va_start(ap, format ); mat_log( MATIO_LOG_LEVEL_ERROR, format, ap ); /* Shall never return to the calling function */ va_end(ap); abort(); /* Always abort */ } /** @brief Prints a helpstring to stdout and exits with status EXIT_SUCCESS (typically 0) * * Prints the array of strings to stdout and exits with status EXIT_SUCCESS. The array * of strings should have NULL as its last element * @code * char *helpstr[] = {"My Help string line1","My help string line 2",NULL}; * Mat_Help(helpstr); * @endcode * @ingroup mat_util * @param helpstr array of strings with NULL as its last element */ void Mat_Help( const char *helpstr[] ) { int i; for (i = 0; helpstr[i] != NULL; i++) printf("%s\n",helpstr[i]); exit(EXIT_SUCCESS); } /** @brief Closes the logging system * * @ingroup mat_util * @retval 1 */ int Mat_LogClose( void ) { logfunc = NULL; #if defined(MAT73) && MAT73 H5Eset_auto(H5E_DEFAULT, NULL, NULL); #endif return 1; } /** @brief Initializes the logging system * * @ingroup mat_util * @param prog_name Name of the program initializing the logging functions * @return 0 on success */ int Mat_LogInit( const char *prog_name ) { logfunc = &mat_logfunc; #if defined(MAT73) && MAT73 H5Eset_auto(H5E_DEFAULT, mat_h5_log_cb, NULL); #endif verbose = 0; silent = 0; return 0; } /** @brief Initializes the logging system * * @ingroup mat_util * @param prog_name Name of the program initializing the logging functions * @param log_func pointer to the function to do the logging * @return 0 on success */ int Mat_LogInitFunc(const char *prog_name, void (*log_func)(int log_level,char *message)) { logfunc = log_func; progname = prog_name; #if defined(MAT73) && MAT73 H5Eset_auto(H5E_DEFAULT, mat_h5_log_cb, NULL); #endif verbose = 0; silent = 0; return 0; } /** @brief Prints a warning message to stdout * * Logs a warning message then returns * @ingroup mat_util * @param format format string identical to printf format * @param ... arguments to the format string */ void Mat_Warning( const char *format, ... ) { va_list ap; va_start(ap, format ); mat_log(MATIO_LOG_LEVEL_WARNING, format, ap ); va_end(ap); } /** @brief Calculate the size of MAT data types * * @ingroup mat_util * @param data_type Data type enumeration * @return size of the data type in bytes */ size_t Mat_SizeOf(enum matio_types data_type) { switch (data_type) { case MAT_T_DOUBLE: return sizeof(double); case MAT_T_SINGLE: return sizeof(float); #ifdef HAVE_MAT_INT64_T case MAT_T_INT64: return sizeof(mat_int64_t); #endif #ifdef HAVE_MAT_UINT64_T case MAT_T_UINT64: return sizeof(mat_uint64_t); #endif case MAT_T_INT32: return sizeof(mat_int32_t); case MAT_T_UINT32: return sizeof(mat_uint32_t); case MAT_T_INT16: return sizeof(mat_int16_t); case MAT_T_UINT16: return sizeof(mat_uint16_t); case MAT_T_INT8: return sizeof(mat_int8_t); case MAT_T_UINT8: return sizeof(mat_uint8_t); case MAT_T_UTF8: return 1; case MAT_T_UTF16: return 2; case MAT_T_UTF32: return 4; default: return 0; } }