X-shooter Pipeline Reference Manual 3.8.15
irplib_error.c
Go to the documentation of this file.
1/* $Id: irplib_error.c,v 1.10 2008-02-27 09:43:52 rhaigron Exp $
2 *
3 * This file is part of the irplib package
4 * Copyright (C) 2002,2003,2004,2005,2006 European Southern Observatory
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA
19 */
20
21/*
22 * $Author: rhaigron $
23 * $Date: 2008-02-27 09:43:52 $
24 * $Revision: 1.10 $
25 * $Name: not supported by cvs2svn $
26 */
27
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31
32/*-----------------------------------------------------------------------------
33 Includes
34 -----------------------------------------------------------------------------*/
35
36#include "irplib_error.h"
37#include <string.h>
38#include <cpl.h>
39#include <cxutils.h>
40#include <stdarg.h>
41/*----------------------------------------------------------------------------*/
46/*----------------------------------------------------------------------------*/
49/*-----------------------------------------------------------------------------
50 Define
51 -----------------------------------------------------------------------------*/
52/*
53 * As the error handler itself should never fail, it cannot not rely on
54 * allocating memory dynamically. Therefore, define max limits for
55 * string lengths, size of error queue.
56 */
57
58#define MAX_STRING_LENGTH 200
59#define MAX_ERRORS 20
60/* MAX_ERRORS is the maximum recursion depth
61 for error tracking. If the call-tree exeeds this limit,
62 the deepest errors are removed (as the user is probably more interested
63 in the higher level errors)
64*/
65/*-----------------------------------------------------------------------------
66 Typedefs
67 -----------------------------------------------------------------------------*/
68
69typedef struct
70{
71 char filename[MAX_STRING_LENGTH];
72 char function[MAX_STRING_LENGTH];
73 unsigned int linenumber;
74 cpl_error_code errorcode;
75 char errormessage[MAX_STRING_LENGTH];
76 char cplmessage[MAX_STRING_LENGTH];
78
79/*-----------------------------------------------------------------------------
80 Local variables
81 -----------------------------------------------------------------------------*/
82
83/* The error queue */
84static struct
85{
87 cpl_boolean is_empty;
88 unsigned int first; /* Index of first error (inclusive) */
89 unsigned int last; /* Index of last error (inclusive) */
90
91 /* Invariant:
92 The last error in the queue matches
93 the current CPL error state.
94
95 This will be violated if the user calls
96 cpl_error_set() or cpl_error_reset() or friends
97 (directly).
98
99 To (re-)establish this invariant
100 irplib_error_validate_state()
101 is called at the entry of every function
102 */
103
105
106/* Used only to avoid variadic macros which might not be supported
107 on some platforms */
109
110/*
111 * This module uses static memory; therefore it must be initialized
112 * (by calling irplib_error_reset()) before use.
113 * Complain loudly if the caller forgot.
114 */
115static cpl_boolean is_initialized = CPL_FALSE;
116
117/*-----------------------------------------------------------------------------
118 Functions code
119 -----------------------------------------------------------------------------*/
120
121/*----------------------------------------------------------------------------*/
137/*----------------------------------------------------------------------------*/
138static void
139irplib_error_validate_state (const char *func, const char *file,
140 unsigned int line)
141{
142 if (!is_initialized) {
143 cpl_msg_error (__func__, "Error handling system was not initialized "
144 "when called from %s:%s:%d!", file, func, line);
145 return;
146 }
147 if (cpl_error_get_code () == CPL_ERROR_NONE) {
148 if (queue.is_empty) {
149 /* Fine */
150 }
151 else {
152 /* The cpl_error_code has been reset without resetting
153 the the queue.
154 */
156 }
157 }
158 else {
159 /* An error code is set. Verify that it matches the
160 latest error in the queue (which might not be the
161 case if the error was set without using irplib_error_push().
162 */
163
164 irplib_error er;
165 int is_mismatch = 0;
166
167
168 if (!queue.is_empty) {
169 er = queue.errors[queue.last];
170 is_mismatch = !(strcmp (er.filename, cpl_error_get_file ()) == 0 &&
171 strcmp (er.function, cpl_error_get_function ()) == 0 &&
172 strcmp (er.cplmessage, cpl_error_get_message ()) == 0 &&
173 /* Don't match er.errormessage */
174 er.errorcode == cpl_error_get_code () &&
175 er.linenumber == cpl_error_get_line ()
176 );
177 }
178
179 /* if mismatch... */
180 if (queue.is_empty || is_mismatch) {
181 /* Insert this error into the queue */
182
183 /* But to avoid infinite recursion (as we might be
184 called from irplib_error_push_macro):
185 1. Store the current CPL error state in a safe place.
186 2. Set/reset CPL's error state to match the queue.
187 3. Insert the previously stored state.
188 4. Also, be careful to leave
189 error_msg[MAX_STRING_LENGTH] unchanged.
190 */
191
192 char file_cpl[MAX_STRING_LENGTH];
193 char func_cpl[MAX_STRING_LENGTH];
194 cpl_error_code ec_cpl;
195 unsigned int line_cpl;
196
197 char message_local[MAX_STRING_LENGTH];
198
199 /* 1 */
200
201 strncpy (file_cpl, cpl_error_get_file (), MAX_STRING_LENGTH - 1);
202 file_cpl[MAX_STRING_LENGTH - 1] = '\0';
203
204 strncpy (func_cpl, cpl_error_get_function (), MAX_STRING_LENGTH - 1);
205 func_cpl[MAX_STRING_LENGTH - 1] = '\0';
206
207 ec_cpl = cpl_error_get_code ();
208 line_cpl = cpl_error_get_line ();
209
210 /* 2 */
211 if (queue.is_empty) {
212 cpl_error_reset ();
213 }
214 else {
215 cpl_error_set_message_macro (er.function,
216 er.errorcode, er.filename, er.linenumber," ");
217 }
218
219 /* 3 + 4 */
220 strncpy (message_local, error_msg, MAX_STRING_LENGTH - 1);
221 message_local[MAX_STRING_LENGTH - 1] = '\0';
222
223
225 xsh_irplib_error_push_macro (func_cpl, ec_cpl, file_cpl, line_cpl);
226 xsh_irplib_error_set_msg ("%s", message_local);
227
228 } /* mismatch with CPL's error state */
229
230 } /* cpl_error_code is set */
231
232 return;
233}
234
235/*----------------------------------------------------------------------------*/
256/*----------------------------------------------------------------------------*/
257
258cpl_error_code
260 cpl_error_code ec,
261 const char *file, unsigned int line)
262{
263 irplib_error_validate_state (func, file, line);
264
265 if (ec == CPL_ERROR_NONE) {
266 cpl_msg_error (__func__, "The error code CPL_ERROR_NONE was set from "
267 "%s:%s:%d! Code changed to CPL_ERROR_UNSPECIFIED",
268 file, func, line);
269
270 ec = CPL_ERROR_UNSPECIFIED;
271 }
272
273 /* Compute new first/last indices */
274 if (queue.is_empty) {
275 /* First error */
276 queue.first = 0;
277 queue.last = 0;
278 }
279 else {
280 /* If queue is full */
281 if ((queue.last + 1) % MAX_ERRORS == (queue.first % MAX_ERRORS)) {
282 /* Delete oldest error */
283 queue.first = (queue.first + 1) % MAX_ERRORS;
284 }
285 queue.last = (queue.last + 1) % MAX_ERRORS;
286 }
287 queue.is_empty = CPL_FALSE;
288 cpl_error_set_message_macro (func, ec, file, line," ");
289
290 /* Insert current error into the queue
291 *
292 * Make sure that the target string is always 0-terminated.
293 * This is not guaranteed by strncpy.
294 */
295 strncpy (queue.errors[queue.last].filename, file, MAX_STRING_LENGTH - 1);
296 strncpy (queue.errors[queue.last].function, func, MAX_STRING_LENGTH - 1);
297 strncpy (queue.errors[queue.last].cplmessage, cpl_error_get_message (),
299 strncpy (queue.errors[queue.last].errormessage, error_msg,
301 queue.errors[queue.last].filename[MAX_STRING_LENGTH - 1] = '\0';
302 queue.errors[queue.last].function[MAX_STRING_LENGTH - 1] = '\0';
303 queue.errors[queue.last].cplmessage[MAX_STRING_LENGTH - 1] = '\0';
304 queue.errors[queue.last].errormessage[MAX_STRING_LENGTH - 1] = '\0';
305
306 queue.errors[queue.last].linenumber = line;
307 queue.errors[queue.last].errorcode = ec;
308
309 return ec;
310}
311
312/*----------------------------------------------------------------------------*/
323/*----------------------------------------------------------------------------*/
324void
325xsh_irplib_error_set_msg (const char *format, ...)
326{
327 va_list al;
328
329 va_start (al, format);
330 /* vsnprintf is C99, so use cx_vsnprintf
331 vsnprintf(error_msg, MAX_STRING_LENGTH, format, al);
332 */
333 cx_vsnprintf (error_msg, MAX_STRING_LENGTH, format, al);
334 va_end (al);
335}
336
337/*----------------------------------------------------------------------------*/
345/*----------------------------------------------------------------------------*/
346void
348{
349 cpl_error_reset ();
350 queue.is_empty = CPL_TRUE;
351 error_msg[0] = '\0';
352
353 is_initialized = CPL_TRUE;
354}
355
356/*----------------------------------------------------------------------------*/
373/*----------------------------------------------------------------------------*/
374void
376 const char *file,
377 unsigned int line,
378 cpl_msg_severity severity,
379 cpl_msg_severity trace_severity)
380{
381 /* Pointing to the CPL messaging functions
382 that correspond to the specified severities:
383 */
384 void (*error_msg_func) (const char *, const char *, ...);
385 void (*trace_msg_func) (const char *, const char *, ...);
386
387 irplib_error_validate_state (func, file, line);
388
389 switch (severity) {
390 case CPL_MSG_DEBUG:
391 error_msg_func = (void(*)(const char*,const char*,...))&cpl_msg_debug;
392 break;
393 case CPL_MSG_INFO:
394 error_msg_func = (void(*)(const char*,const char*,...))&cpl_msg_info;
395 break;
396 case CPL_MSG_WARNING:
397 error_msg_func = (void(*)(const char*,const char*,...))&cpl_msg_warning;
398 break;
399 case CPL_MSG_ERROR:
400 error_msg_func = (void(*)(const char*,const char*,...))&cpl_msg_error;
401 break;
402 case CPL_MSG_OFF:
403 error_msg_func = NULL;
404 break;
405 default:
406 cpl_msg_error (func, "Unknown message level: %d !", severity);
407 error_msg_func = (void(*)(const char*,const char*,...))&cpl_msg_error;
408 break;
409 }
410
411 switch (trace_severity) {
412 case CPL_MSG_DEBUG:
413 trace_msg_func = (void(*)(const char*,const char*,...))&cpl_msg_debug;
414 break;
415 case CPL_MSG_INFO:
416 trace_msg_func = (void(*)(const char*,const char*,...))&cpl_msg_info;
417 break;
418 case CPL_MSG_WARNING:
419 trace_msg_func = (void(*)(const char*,const char*,...))&cpl_msg_warning;
420 break;
421 case CPL_MSG_ERROR:
422 trace_msg_func = (void(*)(const char*,const char*,...))&cpl_msg_error;
423 break;
424 case CPL_MSG_OFF:
425 trace_msg_func = NULL;
426 break;
427 default:
428 cpl_msg_error (func, "Unknown message level: %d !", severity);
429 trace_msg_func = (void(*)(const char*,const char*,...))&cpl_msg_error;
430 break;
431 }
432
433 if (cpl_error_get_code () == CPL_ERROR_NONE) {
434 if (error_msg_func != NULL) {
435 error_msg_func (func, "No error has occurred");
436 }
437 }
438 else {
439 int i;
440 cpl_error_code current_ec;
441
442 /* Don't reset indentation: cpl_msg_indent(0);
443 This will interfere with error-recovery. It is anyway
444 the responsibility of the client never to change the net
445 indentation, also when an error occurs (so don't try to
446 hide such bug).
447 */
448
449 if (trace_msg_func != NULL) {
450 trace_msg_func (func, "An error occurred, " "dumping error trace:");
451 trace_msg_func (func, " ");
452 }
453
454 /*
455 * Print errors from first to last
456 * (both inclusive, and maybe wrap around)
457 */
458 current_ec = CPL_ERROR_NONE;
459 i = queue.first - 1;
460 do {
461 const char *c;
462 cpl_boolean empty_message;
463
464 i = (i + 1) % MAX_ERRORS;
465
466 c = queue.errors[i].errormessage;
467 empty_message = CPL_TRUE;
468 while (*c != '\0') {
469 empty_message = empty_message && (*c == ' ');
470 c++;
471 }
472
473 /* There are 2x2 cases to consider
474 Was a non-empty error message was provided?
475 Did the cpl_error_code change from the previous
476 (lower) level? */
477 if (empty_message) {
478 /* No error message provided, print the standard
479 CPL error message */
480 if (error_msg_func != NULL) {
481 error_msg_func (func, "%s", queue.errors[i].cplmessage);
482 }
483 }
484 else if (queue.errors[i].errorcode == current_ec) {
485 /* Error message provided, don't repeat error code */
486 if (error_msg_func != NULL) {
487 error_msg_func (func, "%s", queue.errors[i].errormessage);
488 }
489 }
490 else {
491 /* Error message provided, error code different
492 from previous (lower) level */
493 if (error_msg_func != NULL) {
494 error_msg_func (func, "%s (%s)",
495 queue.errors[i].errormessage,
496 queue.errors[i].cplmessage);
497 }
498 }
499
500 if (trace_msg_func != NULL) {
501 trace_msg_func (func, " in [%d]%s() at %s:%-3d",
502 /* Here errors are numbered
503 according to depth: from N to 1 */
504 ((queue.last - i + MAX_ERRORS) %
505 MAX_ERRORS) + 1,
506 queue.errors[i].function,
507 queue.errors[i].filename, queue.errors[i].linenumber);
508 trace_msg_func (func, " ");
509 }
510
511 current_ec = queue.errors[i].errorcode;
512
513 } while ((unsigned int) i != queue.last);
514
515 } /* If an error occurred */
516}
517
cpl_boolean is_empty
Definition: irplib_error.c:87
static cpl_boolean is_initialized
Definition: irplib_error.c:115
unsigned int first
Definition: irplib_error.c:88
unsigned int last
Definition: irplib_error.c:89
#define MAX_STRING_LENGTH
Definition: irplib_error.c:58
cpl_error_code xsh_irplib_error_push_macro(const char *func, cpl_error_code ec, const char *file, unsigned int line)
Set or propagate an error.
Definition: irplib_error.c:259
void xsh_irplib_error_reset(void)
Reset the error state.
Definition: irplib_error.c:347
static void irplib_error_validate_state(const char *func, const char *file, unsigned int line)
Synchronize IRPLIB error state with CPL's error state.
Definition: irplib_error.c:139
void xsh_irplib_error_dump_macro(const char *func, const char *file, unsigned int line, cpl_msg_severity severity, cpl_msg_severity trace_severity)
Print the error queue.
Definition: irplib_error.c:375
static struct @0 queue
static char error_msg[MAX_STRING_LENGTH]
Definition: irplib_error.c:108
void xsh_irplib_error_set_msg(const char *format,...)
Temporarily store an error message.
Definition: irplib_error.c:325
#define MAX_ERRORS
Definition: irplib_error.c:59
irplib_error errors[MAX_ERRORS]
Definition: irplib_error.c:86
char filename[MAX_STRING_LENGTH]
Definition: irplib_error.c:71
cpl_error_code errorcode
Definition: irplib_error.c:74
char cplmessage[MAX_STRING_LENGTH]
Definition: irplib_error.c:76
char function[MAX_STRING_LENGTH]
Definition: irplib_error.c:72
unsigned int linenumber
Definition: irplib_error.c:73