irplib_error.c

00001 /* $Id: irplib_error.c,v 1.13 2007/03/14 08:19:25 llundin Exp $
00002  *
00003  * This file is part of the irplib package
00004  * Copyright (C) 2002,2003 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
00019  */
00020 
00021 /*
00022  * $Author: llundin $
00023  * $Date: 2007/03/14 08:19:25 $
00024  * $Revision: 1.13 $
00025  * $Name: uves-3_3_1 $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 /*-----------------------------------------------------------------------------
00033                                    Includes
00034  -----------------------------------------------------------------------------*/
00035 
00036 #include "irplib_error.h"
00037 
00038 #include <cpl.h>
00039 #include <cxutils.h>
00040 #include <stdarg.h>
00041 #include <string.h>
00042 /*----------------------------------------------------------------------------*/
00046 /*----------------------------------------------------------------------------*/
00047 
00048 /*-----------------------------------------------------------------------------
00049                                    Define
00050  -----------------------------------------------------------------------------*/
00051 /*
00052  *  As the error handler itself should never fail, it cannot rely on
00053  *  allocating memory dynamically. Therefore, define max limits for
00054  *  string lengths, size of error queue.
00055  */
00056 
00057 #ifndef IRPLIB_ERROR_MAX_STRING_LENGTH
00058 #define IRPLIB_ERROR_MAX_STRING_LENGTH 256
00059 #endif
00060 #ifndef IRPLIB_ERROR_MAX_ERRORS
00061 #define IRPLIB_ERROR_MAX_ERRORS 20
00062 #endif
00063 /* IRPLIB_ERROR_MAX_ERRORS is the maximum call-tree depth
00064    for error tracking. If the call-tree exceeds this limit,
00065    the deepest errors are removed (as the user is probably more interested
00066    in knowing the position of the errors that occurred nearest the
00067    root of the call-tree).
00068 */
00069 /*-----------------------------------------------------------------------------
00070                                    Typedefs
00071  -----------------------------------------------------------------------------*/
00072 
00073 /* @cond */
00074 typedef struct {
00075     char             filename    [IRPLIB_ERROR_MAX_STRING_LENGTH];
00076     char             function    [IRPLIB_ERROR_MAX_STRING_LENGTH];
00077     unsigned int     linenumber                     ;
00078     cpl_error_code   errorcode                      ;
00079     char             errormessage[IRPLIB_ERROR_MAX_STRING_LENGTH];
00080     char             cplmessage  [IRPLIB_ERROR_MAX_STRING_LENGTH];
00081 } irplib_error;
00082 /* @endcond */
00083 
00084 /*-----------------------------------------------------------------------------
00085                                    Local variables
00086  -----------------------------------------------------------------------------*/
00087 
00088 /* @cond The error queue */
00089 static struct {
00090     irplib_error errors[IRPLIB_ERROR_MAX_ERRORS];
00091     cpl_boolean         is_empty;
00092     unsigned int        first;    /* Index of first error (inclusive) */
00093     unsigned int        last;     /* Index of last  error (inclusive) */
00094     
00095     /* Invariant: 
00096        The last error in the queue matches
00097        the current CPL error state.
00098     
00099        This will be violated if the user calls
00100        cpl_error_set() or cpl_error_reset() or friends
00101        (directly).
00102 
00103        To (re-)establish this invariant
00104            irplib_error_validate_state()
00105        is called at the entry of every function
00106     */
00107 
00108 } queue;
00109 /* @endcond */
00110 
00111 /* Used only to avoid variadic macros which might not be supported
00112    on some platforms */
00113 static char error_msg[IRPLIB_ERROR_MAX_STRING_LENGTH];
00114 
00115 /*
00116  *  This module uses static memory; therefore it must be initialized
00117  *  (by calling irplib_error_reset()) before use.
00118  *  Complain loudly if the caller forgot.
00119  */
00120 static cpl_boolean is_initialized = CPL_FALSE;
00121 
00122 /*-----------------------------------------------------------------------------
00123                                    Functions code
00124  -----------------------------------------------------------------------------*/
00127 /*----------------------------------------------------------------------------*/
00143 /*----------------------------------------------------------------------------*/
00144 static void
00145 irplib_error_validate_state(const char *func, const char *file, unsigned int line)
00146 {
00147     if (!is_initialized)
00148     {
00149         cpl_msg_error(cpl_func, "Error handling system was not initialized "
00150               "when called from %s:%s:%d!", file, func, line);
00151         return;
00152     }
00153 
00154     if (cpl_error_get_code() == CPL_ERROR_NONE)
00155     {
00156         if (queue.is_empty)
00157         {
00158             /* Fine */
00159         }
00160         else
00161         {
00162             /* The cpl_error_code has been reset without resetting
00163                the the queue.
00164             */
00165             irplib_error_reset();
00166         }
00167     }
00168     else
00169     {
00170         /* An error code is set. Verify that it matches the
00171            latest error in the queue (which might not be the
00172            case if the error was set without using irplib_error_push(). 
00173         */
00174         
00175         irplib_error er;
00176         
00177         if (!queue.is_empty) 
00178         {
00179             er = queue.errors[queue.last];
00180         }
00181         
00182         /* if mismatch... */
00183         if (queue.is_empty ||
00184         !(
00185             strcmp(er.filename  ,cpl_error_get_file())     == 0 &&
00186             strcmp(er.function  ,cpl_error_get_function()) == 0 &&
00187             strcmp(er.cplmessage,cpl_error_get_message())  == 0 &&
00188             /* Don't match er.errormessage */
00189             er.errorcode == cpl_error_get_code() &&
00190             er.linenumber== cpl_error_get_line()
00191             )
00192         )
00193         {
00194             /* Insert this error into the queue */
00195             
00196             /* But to avoid infinite recursion (as we might be
00197                called from irplib_error_push_macro):
00198                1. Store the current CPL error state in a safe place.
00199                2. Set/reset CPL's error state to match the queue.
00200                3. Insert the previously stored state.
00201                4. Also, be careful to leave 
00202                   error_msg[IRPLIB_ERROR_MAX_STRING_LENGTH] unchanged.
00203             */
00204             
00205             char file_cpl[IRPLIB_ERROR_MAX_STRING_LENGTH];
00206             char func_cpl[IRPLIB_ERROR_MAX_STRING_LENGTH];
00207             cpl_error_code ec_cpl;
00208             unsigned int line_cpl;
00209 
00210             char message_local[IRPLIB_ERROR_MAX_STRING_LENGTH];
00211             
00212             /* 1 */
00213             strncpy(file_cpl, cpl_error_get_file(), 
00214                 IRPLIB_ERROR_MAX_STRING_LENGTH - 1);
00215             file_cpl[IRPLIB_ERROR_MAX_STRING_LENGTH-1] = '\0';
00216 
00217             strncpy(func_cpl, cpl_error_get_function(),
00218                 IRPLIB_ERROR_MAX_STRING_LENGTH - 1);
00219             func_cpl[IRPLIB_ERROR_MAX_STRING_LENGTH-1] = '\0';
00220 
00221             ec_cpl = cpl_error_get_code();
00222             line_cpl = cpl_error_get_line();
00223             
00224             /* 2 */
00225             if (queue.is_empty)
00226             {
00227                 cpl_error_reset();
00228             }
00229             else
00230             {
00231                 cpl_error_set_macro(er.function,
00232                         er.errorcode,
00233                         er.filename,
00234                         er.linenumber);
00235             }
00236             
00237             /* 3 + 4 */
00238             strncpy(message_local, error_msg,
00239                 IRPLIB_ERROR_MAX_STRING_LENGTH-1);
00240             message_local[IRPLIB_ERROR_MAX_STRING_LENGTH-1] = '\0';
00241             
00242 
00243             irplib_error_set_msg(" ");
00244             irplib_error_push_macro(func_cpl,
00245                         ec_cpl,
00246                         file_cpl,
00247                         line_cpl);
00248             irplib_error_set_msg("%s", message_local);
00249 
00250         } /* mismatch with CPL's error state */
00251 
00252     } /* cpl_error_code is set */
00253 
00254     return;
00255 }
00256 
00257 /*----------------------------------------------------------------------------*/
00277 /*----------------------------------------------------------------------------*/
00278 
00279 void irplib_error_push_macro(const char *func,
00280                  cpl_error_code ec,
00281                  const char *file, 
00282                  unsigned int line)
00283 {
00284     irplib_error_validate_state(func, file, line);
00285 
00286     if (ec == CPL_ERROR_NONE)
00287     {
00288         cpl_msg_error(cpl_func, "The error code CPL_ERROR_NONE was set from "
00289               "%s:%s:%d! Code changed to CPL_ERROR_UNSPECIFIED",
00290               file, func, line);
00291 
00292         ec = CPL_ERROR_UNSPECIFIED;
00293     }
00294 
00295     /* Compute new first/last indices */
00296     if (queue.is_empty)
00297     {
00298         /* First error */
00299         queue.first = 0;
00300         queue.last  = 0;
00301     }
00302     else
00303     {
00304         /* If queue is full */
00305         if ((queue.last + 1) % IRPLIB_ERROR_MAX_ERRORS
00306                 == (queue.first % IRPLIB_ERROR_MAX_ERRORS))
00307         {
00308             /* Delete oldest error */
00309             queue.first = (queue.first + 1) % IRPLIB_ERROR_MAX_ERRORS;
00310         }
00311         queue.last = (queue.last + 1) % IRPLIB_ERROR_MAX_ERRORS;
00312     }
00313     queue.is_empty = CPL_FALSE;
00314 
00315     cpl_error_set_macro(func, ec, file, line);
00316     
00317     /* Insert current error into the queue
00318      *
00319      * Make sure that the target string is always 0-terminated.
00320      * This is not guaranteed by strncpy.
00321      */
00322     strncpy(queue.errors[queue.last].filename    , file,
00323         IRPLIB_ERROR_MAX_STRING_LENGTH - 1);
00324     strncpy(queue.errors[queue.last].function    , func,
00325         IRPLIB_ERROR_MAX_STRING_LENGTH - 1);
00326     strncpy(queue.errors[queue.last].cplmessage  , cpl_error_get_message(), 
00327         IRPLIB_ERROR_MAX_STRING_LENGTH - 1);
00328     strncpy(queue.errors[queue.last].errormessage, error_msg,
00329         IRPLIB_ERROR_MAX_STRING_LENGTH - 1);
00330     queue.errors[queue.last].filename    [IRPLIB_ERROR_MAX_STRING_LENGTH - 1]
00331         = '\0';
00332     queue.errors[queue.last].function    [IRPLIB_ERROR_MAX_STRING_LENGTH - 1]
00333         = '\0';
00334     queue.errors[queue.last].cplmessage  [IRPLIB_ERROR_MAX_STRING_LENGTH - 1]
00335         = '\0';
00336     queue.errors[queue.last].errormessage[IRPLIB_ERROR_MAX_STRING_LENGTH - 1]
00337         = '\0';
00338 
00339     queue.errors[queue.last].linenumber = line;
00340     queue.errors[queue.last].errorcode  = ec;
00341 
00342     return;
00343 }
00344 
00345 /*----------------------------------------------------------------------------*/
00356 /*----------------------------------------------------------------------------*/
00357 void irplib_error_set_msg(const char *format, ...)
00358 {
00359     va_list al;
00360 
00361     va_start(al, format);
00362     /* vsnprintf is C99, so use cx_vsnprintf
00363        vsnprintf(error_msg, IRPLIB_ERROR_MAX_STRING_LENGTH, format, al);
00364     */
00365     cx_vsnprintf(error_msg, IRPLIB_ERROR_MAX_STRING_LENGTH, format, al);
00366     va_end(al);
00367 }
00368 
00369 /*----------------------------------------------------------------------------*/
00377 /*----------------------------------------------------------------------------*/
00378 void irplib_error_reset(void)
00379 {
00380     cpl_error_reset();
00381     queue.is_empty = CPL_TRUE;
00382     error_msg[0] = '\0';
00383 
00384     is_initialized = CPL_TRUE;
00385 }
00386 
00387 /*----------------------------------------------------------------------------*/
00404 /*----------------------------------------------------------------------------*/
00405 void irplib_error_dump_macro(const char *func, 
00406                  const char *file,
00407                  unsigned int line,
00408                  cpl_msg_severity severity,
00409                  cpl_msg_severity trace_severity)
00410 {
00411     /* Pointing to the CPL messaging functions
00412        that correspond to the specified severities: 
00413     */
00414     void (*error_msg_func)(const char *, const char *, ...)
00415 #if defined __GNUC__ &&  __GNUC__ >= 3
00416     __attribute__((format (printf, 2, 3)))
00417 #endif
00418     ;
00419 
00420     void (*trace_msg_func)(const char *, const char *, ...)
00421 #if defined __GNUC__ &&  __GNUC__ >= 3
00422     __attribute__((format (printf, 2, 3)))
00423 #endif
00424     ;
00425 
00426     irplib_error_validate_state(func, file, line);
00427 
00428     switch (severity)
00429     {
00430     case CPL_MSG_DEBUG:
00431         error_msg_func = &cpl_msg_debug;
00432         break;
00433     case CPL_MSG_INFO:
00434         error_msg_func = &cpl_msg_info;
00435         break;
00436     case CPL_MSG_WARNING:
00437         error_msg_func = &cpl_msg_warning;
00438         break;
00439     case CPL_MSG_ERROR:
00440         error_msg_func = &cpl_msg_error;
00441         break;
00442     case CPL_MSG_OFF:
00443         error_msg_func = NULL;
00444         break;
00445     default:
00446         cpl_msg_error(func, "Unknown message level: %d !",
00447               severity);
00448         error_msg_func = &cpl_msg_error;
00449         break;
00450     }
00451 
00452     switch (trace_severity)
00453     {
00454     case CPL_MSG_DEBUG:
00455         trace_msg_func = &cpl_msg_debug;
00456         break;
00457     case CPL_MSG_INFO:
00458         trace_msg_func = &cpl_msg_info;
00459         break;
00460     case CPL_MSG_WARNING:
00461         trace_msg_func = &cpl_msg_warning;
00462         break;
00463     case CPL_MSG_ERROR:
00464         trace_msg_func = &cpl_msg_error;
00465         break;
00466     case CPL_MSG_OFF:
00467         trace_msg_func = NULL;
00468         break;
00469     default:
00470         cpl_msg_error(func, "Unknown message level: %d !",
00471               severity);
00472         trace_msg_func = &cpl_msg_error;
00473         break;
00474     }
00475 
00476     if (cpl_error_get_code() == CPL_ERROR_NONE) 
00477     {
00478         if (error_msg_func != NULL)
00479         {
00480             error_msg_func(func, "No error has occurred");
00481         }
00482     }
00483     else
00484     {
00485         int i;
00486         cpl_error_code current_ec;
00487         
00488         /* Don't reset indentation:  cpl_msg_indent(0); 
00489            This will interfere with error-recovery. It is anyway
00490            the responsibility of the client never to change the net
00491            indentation, also when an error occurs (so don't try to
00492            hide such bug).
00493         */
00494         
00495         if (trace_msg_func != NULL)
00496         {
00497             trace_msg_func(func, "An error occurred, "
00498                    "dumping error trace:");
00499             trace_msg_func(func, " ");
00500         }
00501         
00502         /* 
00503          * Print errors from first to last
00504          * (both inclusive, and maybe wrap around) 
00505          */
00506         current_ec = CPL_ERROR_NONE;
00507         i = queue.first - 1;
00508         do {
00509         const char *c;
00510         cpl_boolean empty_message;
00511         
00512         i = (i+1) % IRPLIB_ERROR_MAX_ERRORS;
00513         
00514         c = queue.errors[i].errormessage;
00515         empty_message = CPL_TRUE;
00516         while (*c != '\0')
00517             {
00518             empty_message = empty_message && (*c == ' ');
00519             c++;
00520             }
00521 
00522         /* There are 2x2 cases to consider
00523            Was a non-empty error message was provided?
00524            Did the cpl_error_code change from the previous
00525            (lower) level? */
00526         if (empty_message)
00527             {
00528             /* No error message provided, print the standard
00529                CPL error message */
00530             if (error_msg_func != NULL)
00531                 {
00532                 error_msg_func(func, "%s",
00533                            queue.errors[i].cplmessage);
00534                 }
00535             }
00536         else if (queue.errors[i].errorcode == current_ec)
00537             {
00538             /* Error message provided, don't repeat error code */
00539             if (error_msg_func != NULL)
00540                 {
00541                 error_msg_func(func, "%s",
00542                            queue.errors[i].errormessage);
00543                 }
00544             }
00545         else
00546             {
00547             /* Error message provided, error code different
00548                from previous (lower) level */
00549             if (error_msg_func != NULL)
00550                 {
00551                 error_msg_func(func, "%s (%s)",
00552                            queue.errors[i].errormessage,
00553                            queue.errors[i].cplmessage);
00554                 }
00555             }
00556         
00557         if (trace_msg_func != NULL)
00558             {
00559             trace_msg_func(func,
00560                        " in [%d]%s() at %s:%-3d",
00561                        /* Here errors are numbered
00562                       according to depth: from N to 1 */
00563                        ((queue.last - i
00564                                          + IRPLIB_ERROR_MAX_ERRORS) %
00565                     IRPLIB_ERROR_MAX_ERRORS) + 1,
00566                        queue.errors[i].function,
00567                        queue.errors[i].filename,
00568                        queue.errors[i].linenumber);
00569             trace_msg_func(func, " ");
00570             }
00571 
00572         current_ec = queue.errors[i].errorcode;
00573         
00574         } while ((unsigned int) i != queue.last);
00575         
00576     } /* If an error occurred */
00577 }
00578 

Generated on Tue Jun 19 14:39:14 2007 for UVES Pipeline Reference Manual by  doxygen 1.4.6