1 /*
2 *  Name:
3 *     error.c
4 
5 *  Purpose:
6 *     Implement error handling functions.
7 
8 *  Description:
9 *     This file implements the Error module which provides functions
10 *     for handling error conditions in the AST library.  For a
11 *     description of the module and its interface, see the .h file of
12 *     the same name.
13 *
14 *     Since its initial release, AST has used a global status variable
15 *     rather than adding an explicit status parameter to the argument
16 *     list of each AST function. This caused problems for the thread-safe
17 *     version of AST since each thread needs its own status value. Whilst
18 *     it would have been possible for each function to access a global
19 *     status value via the pthreads "thread speific data key" mechanism,
20 *     the huge number of status checks performed within AST caused this
21 *     option to be slow. Instead AST has been modified so that every
22 *     function has an explicit status pointer parameter. This though
23 *     causes problems in that we cannot change the public interface to
24 *     AST because doing so would break large amounts of external software.
25 *     To get round this, the macros that define the public interface to
26 *     AST have been modified so that they provide a status pointer
27 *     automatically to the function that is being invoked. This is how
28 *     the system works...
29 *
30 *     All AST functions have an integer inherited status pointer parameter
31 *     called "status". This parameter is "hidden" in AST functions that
32 *     are invoked via macros (typically public and protected functions).
33 *     This means that whilst "int *status" appears explicitly at the end
34 *     of the function argument list (in both prototype and definition), it
35 *     is not included in the prologue documentation, and is not included
36 *     explicitly in the argument list when invoking the function. Instead,
37 *     the macro that is used to invoke the function adds in the required
38 *     status parameter to the function invocation.
39 *
40 *     Macros which are invoked within AST (the protected interface) expand
41 *     to include ", status" at the end of the function parameter list. For
42 *     backward compatability with previous versions of AST, macros which
43 *     are invoked from outside AST (the public interface) expand to include
44 *     ", astGetStatusPtr" at the end of the function parameter list. The
45 *     astGetStatusPtr function returns a pointer to the interbal AST
46 *     status variable or to the external variable specified via astWatch.
47 *
48 *     Parameter lists for functions that have variable argument lists
49 *     (such as astError) cannot be handled in this way, since macros cannot
50 *     have variable numbers of arguments. Instead, separate public and
51 *     protected implementations of such functions are provided within AST.
52 *     Protected implementations include an explicit, documented status
53 *     pointer parameter that must be given explicitly when invoking the
54 *     function. Public implementations do not have a status pointer
55 *     parameter. Instead they obtain the status pointer internally using
56 *     astGetStatusPtr.
57 *
58 *     Private functions are called directly rather than via macros, and so
59 *     they have a documented status pointer parameter that should be
60 *     included explicitly in the parameter list when invoking the
61 *     function.
62 
63 *  Copyright:
64 *     Copyright (C) 1997-2006 Council for the Central Laboratory of the
65 *     Research Councils
66 *     Copyright (C) 2008-2009 Science & Technology Facilities Council.
67 *     All Rights Reserved.
68 
69 *  Licence:
70 *     This program is free software: you can redistribute it and/or
71 *     modify it under the terms of the GNU Lesser General Public
72 *     License as published by the Free Software Foundation, either
73 *     version 3 of the License, or (at your option) any later
74 *     version.
75 *
76 *     This program is distributed in the hope that it will be useful,
77 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
78 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
79 *     GNU Lesser General Public License for more details.
80 *
81 *     You should have received a copy of the GNU Lesser General
82 *     License along with this program.  If not, see
83 *     <http://www.gnu.org/licenses/>.
84 
85 *  Authors:
86 *     RFWS: R.F. Warren-Smith (Starlink)
87 *     DSB: David S. Berry (Starlink)
88 
89 *  History:
90 *     2-JAN-1996 (RFWS):
91 *        Original version.
92 *     8-JAN-1996 (RFWS):
93 *        Tidied up.
94 *     26-JAN-1996 (RFWS):
95 *        Incorporated changes to prologue style.
96 *     14-JUN-1996 (RFWS):
97 *        Added astAt.
98 *     20-JUN-1996 (RFWS):
99 *        Added astSetStatus.
100 *     15-JUL-1996 (RFWS):
101 *        Sorted out the public interface.
102 *     16-JUL-1996 (RFWS):
103 *        Added astWatch.
104 *     18-MAR-1998 (RFWS):
105 *        Added notes about functions being available for writing
106 *        foreign language and graphics interfaces, etc.
107 *     27-NOV-2002 (DSB):
108 *        Added suppression of error reporting using astReporting.
109 *     11-MAR-2004 (DSB):
110 *        Add facility to astAt to allow astAt to be called from public
111 *        interface without private interface settings over-riding the
112 *        public interface settings.
113 *     30-MAR-2005 (DSB):
114 *        Added facility to report deferred messages when reporting is
115 *        switched back on.
116 *     16-FEB-2006 (DSB):
117 *        Improve efficiency by replacing the astOK_ function with a macro
118 *        which tests the value of status variable. The pointer which points
119 *        to the AST status variable are now global rather than static.
120 *     19-SEP-2008 (DSB):
121 *        Big changes for the thread-safe version of AST.
122 *     3-FEB-2009 (DSB):
123 *        Added astBacktrace.
124 */
125 
126 /* Define the astCLASS macro (even although this is not a class
127    implementation) to obtain access to protected interfaces. */
128 #define astCLASS
129 
130 /* Include files. */
131 /* ============== */
132 /* Interface definitions. */
133 /* ---------------------- */
134 #include "err.h"                 /* Interface to the err module */
135 #include "error.h"               /* Interface to this module */
136 #include "globals.h"             /* Thread-safe global data access */
137 
138 /* C header files. */
139 /* --------------- */
140 #include <stdarg.h>
141 #include <stdio.h>
142 #include <stdlib.h>
143 #include <string.h>
144 
145 /* Configuration results. */
146 /* ---------------------- */
147 #if HAVE_CONFIG_H
148 #include <config.h>
149 #endif
150 
151 /* Select the appropriate memory management functions. These will be the
152    system's malloc, free and realloc unless AST was configured with the
153    "--with-starmem" option, in which case they will be the starmem
154    malloc, free and realloc. */
155 #ifdef HAVE_STAR_MEM_H
156 #  include <star/mem.h>
157 #  define MALLOC starMalloc
158 #  define FREE starFree
159 #  define REALLOC starRealloc
160 #else
161 #  define MALLOC malloc
162 #  define FREE free
163 #  define REALLOC realloc
164 #endif
165 
166 /* Include execinfo.h if the backtrace function is available */
167 #if HAVE_EXECINFO_H
168 #include <execinfo.h>
169 #endif
170 
171 
172 
173 /* Module Variables. */
174 /* ================= */
175 
176 /* Define macros for accessing all items of thread-safe global data
177    used by this module. */
178 #ifdef THREAD_SAFE
179 
180 #define reporting astGLOBAL(Error,Reporting)
181 #define current_file astGLOBAL(Error,Current_File)
182 #define current_routine astGLOBAL(Error,Current_Routine)
183 #define current_line astGLOBAL(Error,Current_Line)
184 #define foreign_set astGLOBAL(Error,Foreign_Set)
185 #define message_stack astGLOBAL(Error,Message_Stack)
186 #define mstack_size astGLOBAL(Error,Mstack_Size)
187 
188 /* Since the external astPutErr function may not be thread safe, we need
189    to ensure that it cannot be invoked simultaneously from two different
190    threads. So we lock a mutex before each call to astPutErr. */
191 static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
192 
193 #define INVOKE_ASTPUTERR( status, buff ) \
194    ( pthread_mutex_lock( &mutex1 ), \
195      astPutErr( (status), (buff) ), \
196      (void) pthread_mutex_unlock( &mutex1 ) )
197 
198 /* Define the initial values for the global data for this module. */
199 #define GLOBAL_inits \
200    globals->Reporting = 1; \
201    globals->Current_File = NULL;  \
202    globals->Current_Routine = NULL;  \
203    globals->Current_Line = 0; \
204    globals->Foreign_Set = 0; \
205    globals->Mstack_Size = 0; \
206 
207 /* Create the global initialisation function. */
208 astMAKE_INITGLOBALS(Error)
209 
210 
211 /* If thread safety is not needed, declare globals at static variables. */
212 /* -------------------------------------------------------------------- */
213 #else
214 
215 /* Status variable. */
216 static int internal_status = 0;  /* Internal error status */
217 int *starlink_ast_status_ptr = &internal_status; /* Pointer to status variable */
218 
219 /* Reporting flag: delivery of message is supressed if zero. */
220 static int reporting = 1;
221 
222 /* Error context. */
223 static const char *current_file = NULL; /* Current file name pointer */
224 static const char *current_routine = NULL; /* Current routine name pointer */
225 static int current_line = 0;     /* Current line number */
226 static int foreign_set = 0;      /* Have foreign values been set? */
227 
228 /* Un-reported message stack */
229 static char *message_stack[ AST__ERROR_MSTACK_SIZE ];
230 static int mstack_size = 0;
231 
232 /* If thread-safety is not needed, we can invoke the external astPutErr
233    function directly. */
234 #define INVOKE_ASTPUTERR( status, buff ) \
235    astPutErr( (status), (buff) );
236 
237 #endif
238 
239 
240 /* Function prototypes. */
241 /* ==================== */
242 static void EmptyStack( int, int * );
243 
244 /* Function implementations. */
245 /* ========================= */
astAt_(const char * routine,const char * file,int line,int forn,int * status)246 void astAt_( const char *routine, const char *file, int line, int forn,
247              int *status) {
248 /*
249 *+
250 *  Name:
251 *     astAt
252 
253 *  Purpose:
254 *     Store a routine, file and line number context in case of error.
255 
256 *  Type:
257 *     Protected function.
258 
259 *  Synopsis:
260 *     #include "error.h"
261 *     void astAt( const char *routine, const char *file, int line, int forn)
262 
263 *  Description:
264 *     This function stores a pointer to two strings containing the
265 *     names of a routine and a file, together with an integer line
266 *     number. These values are retained for subsequent use in
267 *     reporting the context of any error that may arise.
268 
269 *  Parameters:
270 *     routine
271 *        Pointer to a null terminated C string containing a routine
272 *        name (which should reside in static memory).
273 *     file
274 *        Pointer to a null terminated C string containing a file name
275 *        (which should reside in static memory).
276 *     line
277 *        The line number in the file.
278 *     for
279 *        Is this call being made from a foreign language interface?
280 *        If so any values supplied will take precedence of the values
281 *        set by the C interface.
282 
283 *  Notes:
284 *     - This function returns without action (i.e. without changing
285 *     the stored values) if the global error status is set. It
286 *     performs no other error checking.
287 *     - Any (or all) of the arguments may be omitted by supplying a
288 *     NULL or zero value (as appropriate) and will then not be included
289 *     in any error report.
290 *     - This function is documented as protected because it should not
291 *     be invoked by external code. However, it is available via the
292 *     external C interface so that it may be used when writing (e.g.)
293 *     foreign language or graphics interfaces.
294 *-
295 */
296 
297 /* Local Variables: */
298    astDECLARE_GLOBALS             /* Pointer to thread-specific global data */
299 
300 /* Check the global error status. */
301    if ( !astOK ) return;
302 
303 /* If needed, get a pointer to the thread specific global data structure. */
304    astGET_GLOBALS(NULL);
305 
306 /* If the values refer to a foreign interface, or if no foreign values
307    have yet been set, store the supplied values. */
308    if( forn|| !foreign_set ) {
309       current_routine = routine;
310       current_file = file;
311       current_line = line;
312    }
313 
314 /* If the values relate to a foreign interface, set a flag which prevents
315    local values set later replacing them. */
316    foreign_set = forn;
317 }
318 
astBacktrace_(int * status)319 void astBacktrace_( int *status ) {
320 /*
321 c+
322 *  Name:
323 *     astBacktrace
324 
325 *  Purpose:
326 *     Display a backtrace on standard output.
327 
328 *  Type:
329 *     Protected macro.
330 
331 *  Synopsis:
332 *     #include "error.h"
333 *     astBacktrace;
334 
335 *  Description:
336 *     This macro displays a set of messages on standard output that
337 *     give a backtrace of the caller. It can be useful for debugging AST
338 *     code in situations when it is not easy or possible to use a
339 *     debugger (for instance, when debugging JNIAST).
340 
341 *  Notes:
342 *     - Only non-static function names are included in the backtrace.
343 *     - This function requires the GNU C library. When called, it will
344 *     just issue a warning if the GNU 'backtrace' function was not
345 *     available when AST was configured.
346 c-
347 */
348 #if HAVE_BACKTRACE
349 
350 #define MAX_ADDR 100
351 
352 /* Local Variables: */
353    char **strings;           /* Pointer to array of formated strings */
354    char buf[ 120 ];          /* Output line buffer */
355    int j;                    /* String index */
356    int np;                   /* Number of used return addresses */
357    void *buffer[ MAX_ADDR ]; /* Array of return addresses */
358 
359 /* Get the array of return addresses. */
360    np = backtrace( buffer, MAX_ADDR );
361 
362 /* Convert them into strings. */
363    strings = backtrace_symbols( buffer, np );
364 
365 /* If succesful, display them and then free the array. Note we skip the
366    first one since that will refer to this function. */
367    if( strings ) {
368       INVOKE_ASTPUTERR( astStatus, " " );
369       for( j = 1; j < np; j++ ) {
370          sprintf( buf, "%d: %s", j, strings[j] );
371          INVOKE_ASTPUTERR( astStatus, buf );
372       }
373       free( strings );
374       INVOKE_ASTPUTERR( astStatus, " " );
375 
376 /* If not succesful, issue a warning. */
377    } else {
378       INVOKE_ASTPUTERR( astStatus, "Cannot convert backtrace addresses into formatted strings" );
379    }
380 
381 #else
382    INVOKE_ASTPUTERR( astStatus, "Backtrace functionality is not available "
383                      "on the current operating system." );
384 #endif
385 }
386 
astClearStatus_(int * status)387 void astClearStatus_( int *status ) {
388 /*
389 c++
390 *  Name:
391 *     astClearStatus
392 
393 *  Purpose:
394 *     Clear the AST error status.
395 
396 *  Type:
397 *     Public macro.
398 
399 *  Synopsis:
400 *     #include "error.h"
401 *     void astClearStatus
402 
403 *  Description:
404 *     This macro resets the AST error status to an OK value,
405 *     indicating that an error condition (if any) has been cleared.
406 
407 *  Notes:
408 *     - If the AST error status is set to an error value (after an
409 *     error), most AST functions will not execute and will simply
410 *     return without action. Using astClearStatus will restore normal
411 *     behaviour.
412 c--
413 */
414 
415 /* Empty the deferred error stack without displaying the messages on the
416    stack. */
417    EmptyStack( 0, status );
418 
419 /* Reset the error status value. */
420    *status = 0;
421 }
422 
EmptyStack(int display,int * status)423 static void EmptyStack( int display, int *status ) {
424 /*
425 *  Name:
426 *     EmptyStack
427 
428 *  Purpose:
429 *     Empty the stack of deferred error messages, optionally displaying
430 *     them.
431 
432 *  Type:
433 *     Private function.
434 
435 *  Synopsis:
436 *     #include "error.h"
437 *     void EmptyStack( int display, int *status )
438 
439 *  Description:
440 *     This function removes all messages from the stack of deferred error
441 *     messages. If "display" is non-zero it reports them using astPutErr
442 *     before deleting them.
443 
444 *  Parameters:
445 *     display
446 *        Report messages before deleting them?
447 *     status
448 *        Pointer to the integer holding the inherited status value.
449 
450 */
451 
452 /* Local variables; */
453    astDECLARE_GLOBALS         /* Pointer to thread-specific global data */
454    int i;
455 
456 /* If needed, get a pointer to the thread specific global data structure. */
457    astGET_GLOBALS(NULL);
458 
459 /* Loop round all messages on the stack. */
460    for( i = 0; i < mstack_size; i++ ) {
461 
462 /* Display the message if required. */
463       if( display ) INVOKE_ASTPUTERR( astStatus, message_stack[ i ] );
464 
465 /* Free the memory used to hold the message. */
466       FREE( message_stack[ i ] );
467       message_stack[ i ] = NULL;
468    }
469 
470 /* Reset the stack size to zero. */
471    mstack_size = 0;
472 
473 }
474 
astErrorPublic_(int status_value,const char * fmt,...)475 void astErrorPublic_( int status_value, const char *fmt, ... ) {
476 /*
477 *+
478 *  Name:
479 *     astError
480 
481 *  Purpose:
482 *     Set the AST error status and report an error message.
483 
484 *  Type:
485 *     Protected function.
486 
487 *  Synopsis:
488 *     #include "error.h"
489 *     void astError( int status_value, const char *fmt, ... )
490 
491 *  Description:
492 *     This function sets the AST error status to a specified value and
493 *     reports an associated error message.
494 
495 *  Parameters:
496 *     status_value
497 *        The new error status value to be set.
498 *     fmt
499 *        Pointer to a null-terminated character string containing the
500 *        format specification for the error message, in the same way
501 *        as for a call to the C "printf" family of functions.
502 *     ...
503 *        Additional optional arguments (as used by e.g. "printf")
504 *        which specify values which are to appear in the error
505 *        message.
506 
507 *  Notes:
508 *     This function operates just like "printf", except that:
509 *     - The first argument is an error status.
510 *     - The return value is void.
511 *     - A newline is automatically appended to the error message
512 *     (there is no need to add one).
513 *     - This function is documented as protected because it should not
514 *     be invoked by external code. However, it is available via the
515 *     external C interface so that it may be used when writing (e.g.)
516 *     foreign language or graphics interfaces.
517 *-
518 
519 *  This is the public implementation of astError. It does not have an
520    status pointer parameter, but instead obtains the status pointer
521    explicitly using the astGetStatusPtr function.  This is different to
522    other public functions, which typically have a status pointer parameter
523    that is supplied via a call to astGetStatusPtr within the associated
524    interface macro. The benefit of doing it the usual way is that the
525    public and protected implementations are the same, with the
526    differences between public and protecte dinterfaces wrapped up in the
527    associated interface macro. We cannot do this with this function
528    because of the variale argument list. The prologue for the astError_
529    function defines the interface for use internally within AST.
530 
531 */
532 
533 /* Local Constants: */
534 #define BUFF_LEN 1023            /* Max. length of an error message */
535 
536 /* Local Variables: */
537    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
538    char buff[ BUFF_LEN + 1 ];    /* Message buffer */
539    int *status;                  /* Pointer to inherited status value */
540    int imess;                    /* Index into deferred message stack */
541    int nc;                       /* Number of characters written */
542    va_list args;                 /* Variable argument list pointer */
543 
544 /* Initialise the variable argument list pointer. */
545    va_start( args, fmt );
546 
547 /* If needed, get a pointer to the thread specific global data structure. */
548    astGET_GLOBALS(NULL);
549 
550 /* Get a pointer to the integer holding the inherited status value. */
551    status = astGetStatusPtr;
552 
553 /* If this is the first report of an error (the global status has not
554    previously been set) and error context information is available,
555    then construct an error context message. */
556    if ( astOK &&
557         ( current_routine || current_file || current_line ) ) {
558       nc = sprintf( buff, "AST: Error" );
559       if ( current_routine ) {
560          nc += sprintf( buff + nc, " in routine %s", current_routine );
561       }
562       if ( current_line ) {
563          nc += sprintf( buff + nc, " at line %d", current_line );
564       }
565       if ( current_file ) {
566          nc += sprintf( buff + nc, " in file %s", current_file );
567       }
568       nc += sprintf( buff + nc, "." );
569 
570 /* Deliver the error message unless reporting has been switched off using
571    astReporting. In which case store them in a static array. */
572       if( reporting ) {
573          INVOKE_ASTPUTERR( status_value, buff );
574       } else if( mstack_size < AST__ERROR_MSTACK_SIZE ){
575          imess = mstack_size++;
576          message_stack[ imess ] = MALLOC( strlen( buff ) + 1 );
577          if( message_stack[ imess ] ) {
578             strcpy( message_stack[ imess ], buff );
579          }
580       }
581 
582 /* Set the global status. */
583       astSetStatus( status_value );
584    }
585 
586 /* Write the error message supplied to the formatting buffer. */
587    nc = vsprintf( buff, fmt, args );
588 
589 /* Tidy up the argument pointer. */
590    va_end( args );
591 
592 /* Deliver the error message unless reporting has been switched off using
593    astReporting. */
594    if( reporting ) {
595       INVOKE_ASTPUTERR( status_value, buff );
596    } else if( mstack_size < AST__ERROR_MSTACK_SIZE ){
597       imess = mstack_size++;
598       message_stack[ imess ] = MALLOC( strlen( buff ) + 1 );
599       if( message_stack[ imess ] ) {
600          strcpy( message_stack[ imess ], buff );
601       }
602    }
603 
604 /* Set the error status value. */
605    astSetStatus( status_value );
606 
607 /* Undefine macros local to this function. */
608 #undef BUFF_LEN
609 }
610 
astError_(int status_value,const char * fmt,int * status,...)611 void astError_( int status_value, const char *fmt, int *status, ... ) {
612 /*
613 *+
614 *  Name:
615 *     astError
616 
617 *  Purpose:
618 *     Set the AST error status and report an error message.
619 
620 *  Type:
621 *     Protected function.
622 
623 *  Synopsis:
624 *     #include "error.h"
625 *     void astError( int status_value, const char *fmt, int *status, ... )
626 
627 *  Description:
628 *     This function sets the AST error status to a specified value and
629 *     reports an associated error message.
630 
631 *  Parameters:
632 *     status_value
633 *        The error status value to be set.
634 *     fmt
635 *        Pointer to a null-terminated character string containing the
636 *        format specification for the error message, in the same way
637 *        as for a call to the C "printf" family of functions.
638 *     status
639 *        Pointer to the integer holding the inherited status value.
640 *     ...
641 *        Additional optional arguments (as used by e.g. "printf")
642 *        which specify values which are to appear in the error
643 *        message.
644 
645 *  Notes:
646 *     This function operates just like "printf", except that:
647 *     - The first argument is an error status.
648 *     - The return value is void.
649 *     - A newline is automatically appended to the error message
650 *     (there is no need to add one).
651 *     - This function is documented as protected because it should not
652 *     be invoked by external code. However, it is available via the
653 *     external C interface so that it may be used when writing (e.g.)
654 *     foreign language or graphics interfaces.
655 *-
656 
657 *  This is the protected implementation of astError. It has a status
658    pointer parameter that is not present in the public form. Different
659    implementations for protected and public interfaces are required
660    because of the variable argument list.
661 
662 */
663 
664 /* Local Constants: */
665 #define BUFF_LEN 1023            /* Max. length of an error message */
666 
667 /* Local Variables: */
668    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
669    char buff[ BUFF_LEN + 1 ];    /* Message buffer */
670    int imess;                    /* Index into deferred message stack */
671    int nc;                       /* Number of characters written */
672    va_list args;                 /* Variable argument list pointer */
673 
674 /* Initialise the variable argument list pointer. */
675    va_start( args, status );
676 
677 /* If needed, get a pointer to the thread specific global data structure. */
678    astGET_GLOBALS(NULL);
679 
680 /* If this is the first report of an error (the global status has not
681    previously been set) and error context information is available,
682    then construct an error context message. */
683    if ( astOK &&
684         ( current_routine || current_file || current_line ) ) {
685       nc = sprintf( buff, "AST: Error" );
686       if ( current_routine ) {
687          nc += sprintf( buff + nc, " in routine %s", current_routine );
688       }
689       if ( current_line ) {
690          nc += sprintf( buff + nc, " at line %d", current_line );
691       }
692       if ( current_file ) {
693          nc += sprintf( buff + nc, " in file %s", current_file );
694       }
695       nc += sprintf( buff + nc, "." );
696 
697 /* Deliver the error message unless reporting has been switched off using
698    astReporting. In which case store them in a static array. */
699       if( reporting ) {
700          INVOKE_ASTPUTERR( status_value, buff );
701       } else if( mstack_size < AST__ERROR_MSTACK_SIZE ){
702          imess = mstack_size++;
703          message_stack[ imess ] = MALLOC( strlen( buff ) + 1 );
704          if( message_stack[ imess ] ) {
705             strcpy( message_stack[ imess ], buff );
706          }
707       }
708 
709 /* Set the global status. */
710       astSetStatus( status_value );
711    }
712 
713 /* Write the error message supplied to the formatting buffer. */
714    nc = vsprintf( buff, fmt, args );
715 
716 /* Tidy up the argument pointer. */
717    va_end( args );
718 
719 /* Deliver the error message unless reporting has been switched off using
720    astReporting. */
721    if( reporting ) {
722       INVOKE_ASTPUTERR( status_value, buff );
723    } else if( mstack_size < AST__ERROR_MSTACK_SIZE ){
724       imess = mstack_size++;
725       message_stack[ imess ] = MALLOC( strlen( buff ) + 1 );
726       if( message_stack[ imess ] ) {
727          strcpy( message_stack[ imess ], buff );
728       }
729    }
730 
731 /* Set the error status value. */
732    astSetStatus( status_value );
733 
734 /* Undefine macros local to this function. */
735 #undef BUFF_LEN
736 }
737 
astGetStatusPtr_()738 int *astGetStatusPtr_(){
739 /*
740 *+
741 *  Name:
742 *     astGetStatusPtr
743 
744 *  Purpose:
745 *     Return a pointer to the integer holding the inherited status value.
746 
747 *  Type:
748 *     Protected function.
749 
750 *  Synopsis:
751 *     #include "error.h"
752 *     int *astGetStatusPtr;
753 
754 *  Description:
755 *     This macro returns a pointer to the integer holding the inherited
756 *     status pointer. This will either be an internal global integer
757 *     (possibly stored as thread specific data), or an ineger specified
758 *     via the astWatch function.
759 
760 *  Returned Value:
761 *      A pointer to the integer holding the inherited status value.
762 
763 *-
764 */
765 
766 /* The thread-safe version of AST stores the status pointer in thread
767    specific data, using the key stored in the global variable
768    "starlink_ast_status_key". */
769 #if defined(THREAD_SAFE)
770    astDECLARE_GLOBALS
771    AstStatusBlock *sb;
772 
773    astGET_GLOBALS(NULL);
774    sb = (AstStatusBlock *) pthread_getspecific(starlink_ast_status_key);
775    return sb->status_ptr;
776 
777 /* The non thread-safe version of AST stores the status pointer in the
778    global variable "starlink_ast_status_ptr". */
779 #else
780    return starlink_ast_status_ptr;
781 #endif
782 }
783 
784 /*
785 c++
786 *  Name:
787 *     astOK
788 
789 *  Purpose:
790 *     Test whether AST functions have been successful.
791 
792 *  Type:
793 *     Public macro.
794 
795 *  Synopsis:
796 *     #include "error.h"
797 *     int astOK
798 
799 *  Description:
800 *     This macro returns a boolean value (0 or 1) to indicate if
801 *     preceding AST functions have completed successfully
802 *     (i.e. without setting the AST error status). If the error status
803 *     is set to an error value, a value of zero is returned, otherwise
804 *     the result is one.
805 
806 *  Returned Value:
807 *     astOK
808 *        One if the AST error status is OK, otherwise zero.
809 
810 *  Notes:
811 *     - If the AST error status is set to an error value (after an
812 *     error), most AST functions will not execute and will simply
813 *     return without action. To clear the error status and restore
814 *     normal behaviour, use astClearStatus.
815 c--
816 */
817 
818 
astReporting_(int report,int * status)819 int astReporting_( int report, int *status ) {
820 /*
821 c+
822 *  Name:
823 *     astReporting
824 
825 *  Purpose:
826 *     Controls the reporting of error messages.
827 
828 *  Type:
829 *     Protected function.
830 
831 *  Synopsis:
832 *     #include "error.h"
833 *     int astReporting( int report )
834 
835 *  Description:
836 *     Error messages supplied to astError will only be delivered to the
837 *     underlying error system if the "Reporting" flag is set to a
838 *     non-zero value. Setting this flag to zero suppresses the reporting
839 *     of error messages (the value of the AST error status however is
840 *     unaffected). Instead, the reports are saved in an internal message
841 *     stack. When reporting is switched back on again, any messages on this
842 *     stack of deferred messages will be reported (and the stack emptied)
843 *     if the AST error status is not astOK. Also the stack is emptied each
844 *     time astClearStatus is called (the stacked messages are not displayed
845 *     in this case).
846 
847 *  Parameters:
848 *     report
849 *        The new value for the Reporting flag.
850 
851 *  Returned Value:
852 *     The original value of the Reporting flag.
853 
854 *  Notes:
855 *     - The Reporting flag is initially set to 1.
856 c-
857 */
858 
859 /* Local Variables: */
860    astDECLARE_GLOBALS         /* Pointer to thread-specific global data */
861    int oldval;                /* Original "reporting" value */
862 
863 /* If needed, get a pointer to the thread specific global data structure. */
864    astGET_GLOBALS(NULL);
865 
866 /* Save the original reporting value, and then store the new value. */
867    oldval = reporting;
868    reporting = report;
869 
870 /* If we are now reporting errors, flush any messages on the error stack.
871    This causes the messages to be displayed and the stack emptied. */
872    if( reporting ) EmptyStack( 1, status );
873 
874 /* Return the original reporting value. */
875    return oldval;
876 }
877 
878 /*
879 c++
880 *  Name:
881 *     astSetStatus
882 
883 *  Purpose:
884 *     Set the AST error status to an explicit value.
885 
886 *  Type:
887 *     Public function.
888 
889 *  Synopsis:
890 *     #include "error.h"
891 *     void astSetStatus( int status_value )
892 
893 *  Description:
894 *     This function sets the AST error status to the value supplied.
895 *     It does not cause any error message to be produced and should
896 *     not be used as part of normal error reporting. Its purpose is
897 *     simply to communicate to AST that an error has occurred in some
898 *     other item of software.
899 *
900 *     For example, a source or sink function supplied as an argument
901 *     to astChannel or astFitsChan might use this to signal that an
902 *     input/output error has occurred. AST could then respond by
903 *     terminating the current read or write operation.
904 
905 *  Parameters:
906 *     status_value
907 *        The new error status value to be set.
908 
909 *  Notes:
910 *     - If the AST error status is set to an error value, most AST
911 *     functions will not execute and will simply return without
912 *     action. To clear the error status and restore normal behaviour,
913 *     use astClearStatus.
914 c--
915 */
916 
917 /*
918 c++
919 *  Name:
920 *     astStatus
921 
922 *  Purpose:
923 *     Obtain the current AST error status value.
924 
925 *  Type:
926 *     Public function.
927 
928 *  Synopsis:
929 *     #include "error.h"
930 *     int astStatus
931 
932 *  Description:
933 *     This function returns the current value of the AST error status.
934 
935 *  Returned Value:
936 *     astStatus
937 *        The AST error status value.
938 
939 *  Notes:
940 *     - If the AST error status is set to an error value (after an
941 *     error), most AST functions will not execute and will simply
942 *     return without action. To clear the error status and restore
943 *     normal behaviour, use astClearStatus.
944 c--
945 */
946 
astWatch_(int * status_ptr)947 int *astWatch_( int *status_ptr ) {
948 /*
949 c++
950 *  Name:
951 *     astWatch
952 
953 *  Purpose:
954 *     Identify a new error status variable for the AST library.
955 
956 *  Type:
957 *     Public function.
958 
959 *  Synopsis:
960 *     #include "error.h"
961 *     int *astWatch( int *status_ptr )
962 
963 *  Description:
964 *     This function allows a new error status variable to be accessed
965 *     by the AST library when checking for and reporting error
966 *     conditions.
967 *
968 *     By default, the library uses an internal integer error status
969 *     which is set to an error value if an error occurs. Use of
970 *     astWatch allows the internal error status to be replaced by an
971 *     integer variable of your choosing, so that the AST library can
972 *     share its error status directly with other code which uses the
973 *     same error detection convention.
974 *
975 *     If an alternative error status variable is supplied, it is used
976 *     by all related AST functions and macros (e.g. astOK, astStatus
977 *     and astClearStatus).
978 
979 *  Parameters:
980 *     status_ptr
981 *        Pointer to an int whose value is to be used subsequently as
982 *        the AST inherited status value. If a NULL pointer is supplied,
983 *        the AST library will revert to using its own internal error status.
984 
985 *  Returned Value:
986 *     astWatch()
987 *        Address of the previous error status variable. This may later
988 *        be passed back to astWatch to restore the previous behaviour
989 *        of the library. (Note that on the first invocation of
990 *        astWatch the returned value will be the address of the
991 *        internal error status variable.)
992 
993 *  Notes:
994 *     - This function is not available in the FORTRAN 77 interface to
995 *     the AST library.
996 c--
997 */
998 
999 /* Local Variables: */
1000    int *result;               /* Value to be returned */
1001    astDECLARE_GLOBALS         /* Pointer to thread-specific global data */
1002 
1003 #if defined(THREAD_SAFE)
1004        AstStatusBlock *sb = NULL;
1005 #endif
1006 
1007 /* Ensure that the thread-specific status block has been created and
1008    ininitialised. */
1009    astGET_GLOBALS(NULL);
1010 
1011 #if defined(THREAD_SAFE)
1012    sb = (AstStatusBlock *) pthread_getspecific( starlink_ast_status_key );
1013    result = sb->status_ptr;
1014    sb->status_ptr = status_ptr ? status_ptr : &(sb->internal_status);
1015 #else
1016    result = starlink_ast_status_ptr;
1017    starlink_ast_status_ptr = status_ptr ? status_ptr : &internal_status;
1018 #endif
1019 
1020 /* Return the old address. */
1021    return result;
1022 }
1023 
1024 
1025