diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2017-12-08 18:59:21 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2017-12-08 18:59:21 (GMT) |
commit | 4432c8d7e1ccb371db03e13cdb5378fceaa5ad04 (patch) | |
tree | 52449d211eba99b24d2e4f6e66193f9511c60b59 /ast/object.c | |
parent | d8a2dbd825159e4a57ec0c6f4465f62be9e05111 (diff) | |
download | blt-4432c8d7e1ccb371db03e13cdb5378fceaa5ad04.zip blt-4432c8d7e1ccb371db03e13cdb5378fceaa5ad04.tar.gz blt-4432c8d7e1ccb371db03e13cdb5378fceaa5ad04.tar.bz2 |
upgrade AST
Diffstat (limited to 'ast/object.c')
-rw-r--r-- | ast/object.c | 8657 |
1 files changed, 8657 insertions, 0 deletions
diff --git a/ast/object.c b/ast/object.c new file mode 100644 index 0000000..f1faaa2 --- /dev/null +++ b/ast/object.c @@ -0,0 +1,8657 @@ +/* +*class++ + +* Name: +* Object + +* Purpose: +* Base class for all AST Objects. + +* Constructor Function: +* None. + +* Description: +* This class is the base class from which all other classes in the +* AST library are derived. It provides all the basic Object +* behaviour and Object manipulation facilities required throughout +* the library. There is no Object constructor, however, as Objects +* on their own are not useful. + +* Inheritance: +* The Object base class does not inherit from any other class. + +* Attributes: +* All Objects have the following attributes: +* +* - Class: Object class name +* - ID: Object identification string +* - Ident: Permanent Object identification string +* - Nobject: Number of Objects in class +* - ObjSize: The in-memory size of the Object in bytes +* - RefCount: Count of active Object pointers +* - UseDefs: Allow use of default values for Object attributes? + +* Functions: +c The following functions may be applied to all Objects: +f The following routines may be applied to all Objects: +* +c - astAnnul: Annul a pointer to an Object +c - astBegin: Begin a new AST context +c - astClear: Clear attribute values for an Object +c - astClone: Clone a pointer to an Object +c - astCopy: Copy an Object +c - astDelete: Delete an Object +c - astEnd: End an AST context +c - astEscapes: Control whether graphical escape sequences are removed +c - astExempt: Exempt an Object pointer from AST context handling +c - astExport: Export an Object pointer to an outer context +c - astGet<X>: Get an attribute value for an Object +c - astHasAttribute: Test if an Object has a named attribute +c - astImport: Import an Object pointer to the current context +c - astIsA<Class>: Test class membership +c - astLock: Lock an Object for use by the calling thread +c - astToString: Create an in-memory serialisation of an Object +c - astSame: Do two AST pointers refer to the same Object? +c - astSet: Set attribute values for an Object +c - astSet<X>: Set an attribute value for an Object +c - astShow: Display a textual representation of an Object on standard +c output +c - astTest: Test if an attribute value is set for an Object +c - astTune: Set or get an integer AST tuning parameter +c - astTuneC: Set or get a character AST tuning parameter +c - astUnlock: Unlock an Object for use by other threads +c - astFromString: Re-create an Object from an in-memory serialisation +c - astVersion: Return the verson of the AST library being used. +f - AST_ANNUL: Annul a pointer to an Object +f - AST_BEGIN: Begin a new AST context +f - AST_CLEAR: Clear attribute values for an Object +f - AST_CLONE: Clone a pointer to an Object +f - AST_COPY: Copy an Object +f - AST_DELETE: Delete an Object +f - AST_END: End an AST context +f - AST_ESCAPES: Control whether graphical escape sequences are removed +f - AST_EXEMPT: Exempt an Object pointer from AST context handling +f - AST_EXPORT: Export an Object pointer to an outer context +f - AST_GET<X>: Get an attribute value for an Object +f - AST_HASATTRIBUTE: Test if an Object has a named attribute +f - AST_IMPORT: Import an Object pointer to the current context +f - AST_ISA<CLASS>: Test class membership +f - AST_SAME: Do two AST pointers refer to the same Object? +f - AST_SET: Set attribute values for an Object +f - AST_SET<X>: Set an attribute value for an Object +f - AST_SHOW: Display a textual representation of an Object on standard +f output +f - AST_TEST: Test if an attribute value is set for an Object +f - AST_TUNE: Set or get an integer AST tuning parameter +f - AST_TUNEC: Set or get a character AST tuning parameter +f - AST_VERSION: Return the verson of the AST library being used. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils +* Copyright (C) 2010 Science & Technology Facilities Council. +* All Rights Reserved. + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* <http://www.gnu.org/licenses/>. + +* Authors: +* RFWS: R.F. Warren-Smith (Starlink) +* DSB: David S. Berry (Starlink) + +* History: +* 1-FEB-1996 (RFWS): +* Original version. +* 22-APR-1996 (RFWS): +* Added attribute setting functions. +* 2-JUL-1996 (RFWS): +* Added functions to support the external interface (using +* identfiers). +* 10-SEP-1996 (RFWS): +* Added I/O facilities. +* 30-MAY-1997 (RFWS): +* Add ID attribute. +* 14-JUL-1997 (RFWS): +* Add astExempt function. +* 14-OCT-1997 (RFWS): +* Fixed uninitialised use of "dynamic" in astCopy_. +* 14-NOV-1997 (RFWS): +* Remove the subversive C "strtok" function. +* 20-JAN-1998 (RFWS): +* Make the astClear and astRVSet methods virtual. +* 29-APR-1998 (RFWS): +* Fixed bug in algorithm for encoding Object IDs. +* 15-SEP-1999 (RFWS) +* Made astAnnulId accessible from protected code. +* 12-APR-2000 (DSB): +* Zero all memory allocated for a new Object in InitObject before +* storing any new values in the memory. +* 3-APR-2001 (DSB): +* Added the Ident attribute. +* 28-SEP-2001 (DSB): +* Modified VSet to ensure a non-null string always follows the equal +* sign in the attribute setting passed to SetAttrib. +* 27-NOV-2002 (DSB): +* Modified astShow to use astWrite instead of astDump, so that +* invocations of astShow will be included in the count of the +* number of invocations of astWrite returned by astWriteInvocations. +* 8-JAN-2003 (DSB): +* Changed private InitVtab method to protected astInitObjectVtab +* method. +* 8-FEB-2004 (DSB): +* Added astEscapes. +* 10-FEB-2004 (DSB): +* Added debug conditional code to keep track of memory leaks. +* 22-AUG-2004 (DSB): +* Added astEqual +* 27-JAN-2005 (DSB): +* Correct use of ->ident pointers, and added further DEBUG blocks. +* 11-MAR-2005 (DSB): +* Added attribute UseDefs. +* 14-FEB-2006 (DSB): +* Added attribute ObjSize. +* 23-FEB-2006 (DSB): +* Added MemoryCaching tuning parameter. +* 27-FEB-2006 (DSB): +* Include Objects returned by astCopy in the ObjectCaching system. +* 28-FEB-2006 (DSB): +* Use astOK to protect against errors within astGrow. +* 1-MAR-2006 (DSB): +* Replace astSetPermMap within DEBUG blocks by astBeginPM/astEndPM. +* 26-MAY-2006 (DSB): +* Correct handling of commas within the attribute value supplied +* to astSetC. +* 30-MAY-2006 (DSB): +* Correct the correction made to handle commas within attribute +* 6-JUN-2007 (DSB): +* Fix harmless compiler warnings. +* 21-JUN-2007 (DSB): +* In astSet<X>, ignore trailing spaces in the attribute name. +* 22-JUN-2007 (DSB): +* - Make astVSet return a pointer to dynamic memory holding the +* expanded setting string. +* - Add astSetVtab, and astCast. +* 27-JUN-2007 (DSB): +* Modify astInitObject so that it ignores the supplied "size" value +* if some memory is supplied. +* 2-JULY-2007 (DSB): +* Fix memory access problem in VSet. +* 20-SEP-2007 (DSB): +* In astDelete, ensure the error status is reset before calling +* astGrow to extend the vtab free list. +* 22-APR-2008 (DSB): +* Added astSame. +* 24-OCT-2008 (DSB): +* Prevent a mutex deadlock that could occur when annulling an +* Object ID. +* 28-JAN-2008 (DSB): +* Allow unlocked objects to be annulled using astAnnul. +* 14-OCT-2009 (DSB): +* Modify astCast to make it a virtual function and add type +* checking. +* 7-APR-2010 (DSB): +* Added method astHasAttribute. +* 24-AUG-2010 (DSB): +* Allow commas to be included in attribute values supplied to +* astSet or astVSet by putting quotes around the attribute value. +* 16-JUN-2011 (DSB): +* Added component "iref" to the Object structure. This is an +* integer identifier for each object that is unique within the +* class of the object. Useful for debugging. +* 22-JUL-2011 (DSB): +* Add methods astSetProxy and astGetProxy. +* 2-SEP-2011 (DSB): +* Add functions astToString and astFromString. +* 13-SEP-2013 (DSB): +* Report an error in astAnnul if the supplied object handle is owned by +* a different thread. Note, the Object itself does not need to be owned +* by the current thread, since it should be possible for a thread to +* relinquish a pointer to an object (i.e. a handle) without actually +* owning the object itself. +* 6-JAN-2014 (DSB): +* Added method astEnvSet. +* 9-APR-2015 (DSB): +* Modify VSet to handle "%s" setting strings (i.e. where the whole +* list of settings is provided as a single variable argument). +* This is needed because supplying the while settings string in +* place of "%s" is considered a security issue by many compilers. +*class-- +*/ + +/* Module Macros. */ +/* ============== */ +/* Set the name of the class we are implementing. This indicates to + the header files that define class interfaces that they should make + "protected" symbols available. */ +#define astCLASS Object + +#define INVALID_CONTEXT -1 /* Context value for handles that have no + associated Object */ +#define UNOWNED_CONTEXT -2 /* Context value for handles for objects + that are not locked by any thread */ + + +/* Include files. */ +/* ============== */ + +/* Configuration information */ +/* ------------------------ */ +#include "version.h" /* Version numbers */ +#if HAVE_CONFIG_H +#include <config.h> +#endif + +/* Interface definitions. */ +/* ---------------------- */ +#include "error.h" /* Error reporting facilities */ +#include "memory.h" /* Memory allocation facilities */ +#include "channel.h" /* I/O channels */ +#include "object.h" /* Interface definition for this class */ +#include "plot.h" /* Plot class (for astStripEscapes) */ +#include "globals.h" /* Thread-safe global data access */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include <ctype.h> +#include <errno.h> +#include <float.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +/* Type Definitions */ +/* ================ */ +/* Structure used to pass data between astToString/FromString and the + corresponding Channel source and sink functions. */ +typedef struct StringData { + char *ptr; /* Pointer to serialisation text */ + char *buff; /* Pointer to a buffer for a single line of text */ + int len; /* Current length of serialisation text */ +} StringData; + +/* Module Variables. */ +/* ================= */ + +/* The following globals have the same values in all threads and so do + not need to be in thread-specific data. */ +/* ------------------------------------------------------------------ */ + +/* Character-valued tuning parameters. */ +#define MAXLEN_TUNEC 200 +static char hrdel[ MAXLEN_TUNEC ] = "%-%^50+%s70+h%+"; +static char mndel[ MAXLEN_TUNEC ] = "%-%^50+%s70+m%+"; +static char scdel[ MAXLEN_TUNEC ] = "%-%^50+%s70+s%+"; +static char dgdel[ MAXLEN_TUNEC ] = "%-%^53+%s60+o%+"; +static char amdel[ MAXLEN_TUNEC ] = "%-%^20+%s85+'%+"; +static char asdel[ MAXLEN_TUNEC ] = "%-%^20+%s85+\"%+"; +static char exdel[ MAXLEN_TUNEC ] = "10%-%^50+%s70+"; + +/* A pointer full of zeros. */ +static AstObject *zero_ptr; + +/* A flag which indicates what should happen when an AST Object is + deleted. If this flag is non-zero, the memory used by the Object is + not freed, but a pointer to it is placed on the end of a list of free + memory chunk pointers so that the memory can be re-used if necessary + avoiding the need to re-allocate memory with malloc (which is slow). + A separate list of free memory chunks is kept for each class because + each class object will require chunks of a different size. Pointers + to these lists are stored in the virtual function table associated + with each class. All memory on these lists is freed when object + caching is switched off via the astTune function. */ +static int object_caching = 0; + +/* Set up global data access, mutexes, etc, needed for thread safety. */ +#ifdef THREAD_SAFE + +/* Define the initial values for the global data for this module. */ +#define GLOBAL_inits \ + globals->Retain_Esc = 0; \ + globals->Context_Level = 0; \ + globals->GetAttrib_Buff[ 0 ] = 0; \ + globals->AstGetC_Init = 0; \ + globals->AstGetC_Istr = 0; \ + globals->Active_Handles = NULL; \ + globals->Class_Init = 0; \ + globals->Nvtab = 0; \ + globals->Known_Vtabs = NULL; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(Object) + +/* Define macros for accessing each item of thread specific global data. */ +#define retain_esc astGLOBAL(Object,Retain_Esc) +#define context_level astGLOBAL(Object,Context_Level) +#define active_handles astGLOBAL(Object,Active_Handles) +#define getattrib_buff astGLOBAL(Object,GetAttrib_Buff) +#define astgetc_strings astGLOBAL(Object,AstGetC_Strings) +#define astgetc_istr astGLOBAL(Object,AstGetC_Istr) +#define astgetc_init astGLOBAL(Object,AstGetC_Init) +#define class_init astGLOBAL(Object,Class_Init) +#define class_vtab astGLOBAL(Object,Class_Vtab) +#define nvtab astGLOBAL(Object,Nvtab) +#define known_vtabs astGLOBAL(Object,Known_Vtabs) + +/* mutex1 is used to prevent tuning parameters being accessed by more + than one thread at any one time. */ +static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX1 pthread_mutex_lock( &mutex1 ); +#define UNLOCK_MUTEX1 pthread_mutex_unlock( &mutex1 ); + +/* mutex2 is used to prevent the global lists of object handles being + accessed by more than one thread at any one time. */ +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +/* Each Object contains two mutexes. The primary mutex (mutex1) is used + to guard access to all aspects of the Object except for the "locker" + and "ref_count" items. The secondary mutex (mutex2) is used to guard + access to these two remaining items. We need this secondary mutex + since the "locker" and "ref_count" items need to be accessable within + a thread even if that thread has not locked the Object using astLock. + Define macros for accessing these two mutexes. */ +#define LOCK_PMUTEX(this) (pthread_mutex_lock(&((this)->mutex1))) +#define UNLOCK_PMUTEX(this) (pthread_mutex_unlock(&((this)->mutex1))) +#define LOCK_SMUTEX(this) (pthread_mutex_lock(&((this)->mutex2))) +#define UNLOCK_SMUTEX(this) (pthread_mutex_unlock(&((this)->mutex2))) + + + + + +/* If thread safety is not needed, declare and initialise globals at static + variables. */ +#else + +/* Define the class virtual function table and its initialisation flag as + static variables. */ +static int class_init = 0; /* Virtual function table initialised? */ +static AstObjectVtab class_vtab; /* Virtual function table */ + +/* A list of pointers to all the known class virtual function tables. */ +static int nvtab = 0; +static AstObjectVtab **known_vtabs = NULL; + +/* A flag which indicates if AST functions which return text strings + should retain any graphical escape sequences (as interpreted by the + Plot class). */ +static int retain_esc = 0; + +/* Context level (Begin/End/Exempt/Export) */ +static int context_level = 0; + +/* Array of list heads for each context (each list is a list of Handle + structures). */ +static int *active_handles = NULL; + +/* String returned by GetAttrib. */ +static char getattrib_buff[ AST__GETATTRIB_BUFF_LEN + 1 ] = ""; + +/* Pointers to string buffers returned by astGetC. */ +static char *astgetc_strings[ AST__ASTGETC_MAX_STRINGS ]; + +/* Offset of next string in "AstGetC_Strings" */ +static int astgetc_istr = 0; + +/* "AstGetC_Strings" array initialised? */ +static int astgetc_init = 0; + +/* Null macros for mutex locking and unlocking */ +#define LOCK_MUTEX1 +#define UNLOCK_MUTEX1 +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 +#define LOCK_PMUTEX(this) +#define LOCK_SMUTEX(this) +#define UNLOCK_PMUTEX(this) +#define UNLOCK_SMUTEX(this) + +#endif + + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstObject *Cast( AstObject *, AstObject *, int * ); +static const char *GetID( AstObject *, int * ); +static const char *GetAttrib( AstObject *, const char *, int * ); +static const char *GetIdent( AstObject *, int * ); +static const char *Get( AstObject *, const char *, int * ); +static const char *FromStringSource( void ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetObjSize( AstObject *, int * ); +static int HasAttribute( AstObject *, const char *, int * ); +static int Same( AstObject *, AstObject *, int * ); +static int TestAttrib( AstObject *, const char *, int * ); +static int TestID( AstObject *, int * ); +static int TestIdent( AstObject *, int * ); +static unsigned long Magic( const AstObject *, size_t, int * ); +static void CleanAttribs( AstObject *, int * ); +static void Clear( AstObject *, const char *, int * ); +static void ClearAttrib( AstObject *, const char *, int * ); +static void ClearIdent( AstObject *, int * ); +static void ClearID( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void EmptyObjectCache( int * ); +static void ToStringSink( const char * ); +static void SetAttrib( AstObject *, const char *, int * ); +static void SetID( AstObject *, const char *, int * ); +static void SetIdent( AstObject *, const char *, int * ); +static void Show( AstObject *, int * ); +static void VSet( AstObject *, const char *, char **, va_list, int * ); +static void EnvSet( AstObject *, int * ); + +static int GetUseDefs( AstObject *, int * ); +static int TestUseDefs( AstObject *, int * ); +static void ClearUseDefs( AstObject *, int * ); +static void SetUseDefs( AstObject *, int, int * ); + +#if defined(THREAD_SAFE) +static void ChangeThreadVtab( AstObject *, int * ); +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + +/* Member functions. */ +/* ================= */ +AstObject *astAnnul_( AstObject *this, int *status ) { +/* +*++ +* Name: +c astAnnul +f AST_ANNUL + +* Purpose: +* Annul a pointer to an Object. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c AstObject *astAnnul( AstObject *this ) +f CALL AST_ANNUL( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function annuls a pointer to an Object so that it is no +f This routine annuls a pointer to an Object so that it is no +* longer recognised as a valid pointer by the AST library. Any +* resources associated with the pointer are released and made +* available for re-use. +* +c This function also decrements the Object's RefCount attribute by +f This routine also decrements the Object's RefCount attribute by +* one. If this attribute reaches zero (which happens when the last +* pointer to the Object is annulled), then the Object is deleted. + +* Parameters: +c this +c The Object pointer to be annulled. +f THIS = INTEGER (Given and Returned) +f The Object pointer to be annulled. A null pointer value (AST__NULL) +f is always returned. +f STATUS = INTEGER (Given and Returned) +f The global status. + +c Returned Value: +c astAnnul() +c A null Object pointer (AST__NULL) is always returned. +c +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +c - This function will attempt to annul the pointer even if the +c Object is not currently locked by the calling thread (see astLock). +c - This function attempts to execute even if the AST error +c status is set +f - This routine attempts to execute even if STATUS is set to an +f error value +* on entry, although no further error report will be +* made if it subsequently fails under these circumstances. In +* particular, it will fail if the pointer suppled is not valid, +* but this will only be reported if the error status is clear on +* entry. +*-- +*/ + +/* Check the pointer to ensure it identifies a valid Object (this + generates an error if it doesn't). */ + if ( !astIsAObject( this ) ) return NULL; + +/* Get a lock on the object's secondary mutex. This mutex guards access + to the "ref_count" and "locker" components of the AstObject structure. */ + LOCK_SMUTEX(this); + +#ifdef MEM_DEBUG + { int rc; + char buf[100]; + rc = this->ref_count; + sprintf(buf,"annulled (refcnt: %d -> %d)", rc, rc-1 ); + astMemoryUse( this, buf ); + } +#endif + +/* Decrement the Object's reference count. */ + --(this->ref_count); + +/* Unlock the object's secondary mutex. */ + UNLOCK_SMUTEX(this); + +/* Decrement the Object's reference count and delete the Object if + necessary. */ + if ( !this->ref_count ) (void) astDelete( this ); + +/* Always return NULL. */ + return NULL; +} + +static AstObject *Cast( AstObject *this, AstObject *obj, int *status ) { +/* +*+ +* Name: +* astCast + +* Purpose: +* Cast an Object into an instance of a sub-class. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* AstObject *astCast( AstObject *this, AstObject *obj ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a deep copy of an ancestral component of the +* supplied object. The required class of the ancestral component is +* specified by another object. Specifically, if "this" and "new" are +* of the same class, a copy of "this" is returned. If "this" is an +* instance of a subclass of "obj", then a copy of the component +* of "this" that matches the class of "obj" is returned. Otherwise, +* a NULL pointer is returned without error. + +* Parameters: +* this +* Pointer to the Object to be cast. +* obj +* Pointer to an Object that defines the class of the returned Object. +* The returned Object will be of the same class as "obj". + +* Returned Value: +* A pointer to the new Object. NULL if "this" is not a sub-class of +* "obj", or if an error occurs. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstObject *new; + int generation_gap; + +/* Initialise */ + new = NULL; + +/* Check inherited status */ + if( !astOK ) return new; + +/* Check pointer have been supplied. */ + if( this && obj ) { + +/* See how many steps up the class inheritance ladder it is from "this" to + "obj". A positive value is returned if "this" is a sub-class of "obj". + A negative value is returned if "obj" is a sub-class of "this". Zero + is returned if they are of the same class. AST__COUSIN is returned if + they share a common ancestor but are not on the same line of descent. */ + generation_gap = astClassCompare( astVTAB( this ), astVTAB( obj ) ); + +/* If the two objects are of the same class, just return a copy of + "this". */ + if( generation_gap == 0 ) { + new = astCopy( this ); + +/* If "this" is a subclass of "obj", return a deep copy of "this" cast + into the class of "obj". */ + } else if( generation_gap != AST__COUSIN && generation_gap > 0 ) { + new = astCastCopy( this, obj ); + + } + } + +/* Return the new pointer. */ + return new; +} + +AstObject *astCastCopy_( AstObject *this, AstObject *obj, int *status ) { +/* +*+ +* Name: +* astCastCopy + +* Purpose: +* Cast an Object into an instance of a sub-class, without type-checking. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astCastCopy( AstObject *this, AstObject *obj ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a deep copy of an ancestral component of the +* supplied object. The required class of the ancestral component is +* specified by another object. No checks are performed that "this" is +* a sub-class of "obj". +* +* It works by temporarily changing the vtab in "this" to be the same +* as in "obj", and then doing a deep copy, and then re-instating the +* original vtab. + +* Parameters: +* this +* Pointer to the Object to be cast. +* obj +* Pointer to an Object that defines the class of the returned Object. +* The returned Object will be of the same class as "obj". + +* Returned Value: +* A pointer to the new Object. + +* Notes: +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + AstObject *new; + AstObjectVtab *this_vtab; + size_t this_size; + +/* Initialise */ + new = NULL; + +/* Check inherited status */ + if( !astOK ) return new; + +/* Check pointer have been supplied. */ + if( this && obj ) { + +/* Save a pointer to the original virtual function tables for "this". */ + this_vtab = astVTAB( this ); + +/* Temporarily change the vtab of "this" to that of "obJ". */ + this->vtab = astVTAB( obj ); + +/* Temporarily change the size of "this" to be the size of "obj". */ + this_size = this->size; + this->size = obj->size; + +/* Now take a copy of the object (now considered to be an instance of the + class specified by "obj"). */ + new = astCopy( this ); + +/* Re-instate the original Object vtab and size. */ + this->vtab = this_vtab; + this->size = this_size; + +/* The sub-clas to which "this" originally belonged may have extended the + range of values allowed for one or more of the attributes inherited from + the "obj" class. This means that the current attribute values stored + in the returned object may be inappropriate for the class of "obj". An + example is the System attribute defined by the Frame class, and extended + by sub-classes of Frame. So we now call astCleanAttribs to ensure that + any inappropriate attribute values are cleared in the returned object. */ + astCleanAttribs( new ); + } + +/* Return the new pointer. */ + return new; +} + +#if defined(THREAD_SAFE) +static void ChangeThreadVtab( AstObject *this, int *status ){ +/* +* Name: +* ChangeThreadVtab + +* Purpose: +* Modify an Object structure so that it refers to a vtab created by +* the currently executing thread. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* void ChangeThreadVtab( AstObject *this, int *status ) + +* Class Membership: +* Object member function. + +* Description: +* Each Object structure contains a pointer to a virtual function +* table (vtab) that identifies information about the class to +* which the Object belongs (function pointers, Object caches, +* etc). In order to avoid use of mutexes (which can slow down AST +* applications enormously), each thread has its own set of vtab +* structures (one for each AST class) stored in thread-specific +* data. Each time an Object is locked by the currently executing +* thread, this function should be called to change the vtab pointer +* in the Object to refer to the vtab relevant to the currently +* executing thread. + +* Parameters: +* this +* Pointer to the Object. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + const char *class; + int i; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to Thread-specific data for the currently executing thread. */ + astGET_GLOBALS(this); + +/* Get the class name for the supplied Object. This uses the existing + vtab pointer in the Object structure to locate the required GetClass + method and the class name. This vtab pointer may be for a vtab created + by a different thread to the one currently executing, but this shouldn't + matter since we are not modifying the vtab contents. */ + class = astGetClass( this ); + +/* Check a class name was obtained */ + if( class ) { + +/* Loop round the vtab structures created by the currently executing thread. */ + for( i = 0; i < nvtab; i++ ) { + +/* If the current vtab is for a class that matches the class of the + supplied Object, then store a pointer to the vtab in the Object + structure, and exit. */ + if( !strcmp( class, known_vtabs[ i ]->class ) ) { + this->vtab = known_vtabs[ i ]; + break; + } + } + } +} +#endif + +AstObject *astCheckLock_( AstObject *this, int *status ) { +/* +*+ +* Name: +* astCheckLock + +* Purpose: +* Check that supplied Object is locked by the calling thread. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astCheckLock( AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function reports an error if the supplied object has not +* previously been locked (using astLock) by the calling thread. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* A copy of the supplied pointer ("this") is returned. The Object +* reference count is not changed. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. + +*- +*/ + +/* This function does nothing in the non-threads version of libast. */ +#if defined(THREAD_SAFE) + +/* Local Variables; */ + AstObject *fail; + +/* Check the supplied pointer. */ + if( this ) { + +/* First use the private ManageLock function rather than the virtual + astManageLock method to check the top level Object is locked for use + by the current thread. This saves time and allows a more appropriate + error message to be issued. */ + if( ManageLock( this, AST__CHECKLOCK, 0, NULL, status ) ) { + if( astOK ) { + astError( AST__LCKERR, "astCheckLock(%s): The supplied %s cannot " + "be used since it is not locked for use by the current " + "thread (programming error).", status, astGetClass( this ), + astGetClass( this ) ); + } + +/* If the top level Object is locked, now use the virtual astManageLock + method to check any objects contained within the top level Object. */ + } else if( astManageLock( this, AST__CHECKLOCK, 0, &fail ) ) { + if( astOK ) { + astError( AST__LCKERR, "astCheckLock(%s): The supplied %s cannot " + "be used since a %s contained within the %s is not " + "locked for use by the current thread (programming " + "error).", status, astGetClass( this ), + astGetClass( this ), astGetClass( fail ), + astGetClass( this ) ); + } + } + } +#endif + +/* Return the supploed pointer. */ + return this; + +} + +int astClassCompare_( AstObjectVtab *class1, AstObjectVtab *class2, + int *status ) { +/* +*+ +* Name: +* astClassCompare + +* Purpose: +* Determine the relationship between two AST classes. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* int astClassCompare( AstObjectVtab *class1, AstObjectVtab *class2 ) + +* Class Membership: +* Object method. + +* Description: +* This function returns the number of steps up the class inheritance +* ladder from the class specified by "class1" to the class specified +* by "class2". + +* Parameters: +* class1 +* Pointer to a virtual function table describing the first AST class. +* class2 +* Pointer to a virtual function table describing the second AST class. + +* Returned Value: +* The generation gap between "class1" and "class2". The result will be +* positive if "class1" is a subclass of "class2", negative if "class2" +* is a subclass of "class1", zero if they are of the same class (or +* an error occurs), or AST__COUSIN if they are not on the same line +* of descent. + +*- +*/ + +/* Local Variables: */ + AstClassIdentifier *class1_id; + AstClassIdentifier *class2_id; + AstClassIdentifier *id; + int *class1_check; + int *class2_check; + int result; + +/* Initialise */ + result = 0; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Check pointer have been supplied. */ + if( class1 && class2 ) { + +/* Get pointers to the AstClassIdentifier that identifies the top-level + class of each vtab. */ + class1_id = class1->top_id; + class2_id = class2->top_id; + +/* Class membership is specified by the "check" value in each class + identifier. Get the check values for both vtabs. */ + class1_check = class1_id->check; + class2_check = class2_id->check; + +/* Try walking up the class heirarchy of "class1" until the class of + "class2" is reached. The top-level AstObject class has a NULL "parent" + pointer in its class identifier structure. */ + id = class1_id; + while( id && ( id->check != class2_check ) ) { + id = id->parent; + result++; + } + +/* If "class1" is not a subclass of "class2", try walking up the class + heirarchy of "class2" until the class of "class1" is reached. */ + if( !id ) { + result = 0; + id = class2_id; + while( id && ( id->check != class1_check ) ) { + id = id->parent; + result--; + } + +/* If "class2" is not a subclass of "class1", return AST__COUSIN. */ + if( !id ) result = AST__COUSIN; + } + } + +/* Return the generation gap. */ + return result; +} + +static void CleanAttribs( AstObject *this_object, int *status ) { +/* +*+ +* Name: +* astCleanAttribs + +* Purpose: +* Clear any invalid set attribute values. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* void astCleanAttribs( AstObject *this, int *status ) + +* Class Membership: +* Object method. + +* Description: +* This function clears any attributes that are currently set to +* invalid values in the supplied object. This can happen for instance +* when an object is cast into an instance of a parent class using +* astCast, since sub-classes can extend the range of valid values +* an attribute can take. + +* Parameters: +* this +* Pointer to the Object to be cleaned. +*- +*/ + +/* The base Object class has no attributes that need cleaning. */ + +} + +static void Clear( AstObject *this, const char *attrib, int *status ) { +/* +*++ +* Name: +c astClear +f AST_CLEAR + +* Purpose: +* Clear attribute values for an Object. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "object.h" +c void astClear( AstObject *this, const char *attrib ) +f CALL AST_CLEAR( THIS, ATTRIB, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function clears the values of a specified set of attributes +f This routine clears the values of a specified set of attributes +* for an Object. Clearing an attribute cancels any value that has +* previously been explicitly set for it, so that the standard +* default attribute value will subsequently be used instead. This +c also causes the astTest function to return the value zero for +f also causes the AST_TEST function to return the value .FALSE. for +* the attribute, indicating that no value has been set. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object. +c attrib +f ATTRIB = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string containing a +c comma-separated list of the names of the attributes to be cleared. +f A character string containing a comma-separated list of the +f names of the attributes to be cleared. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +* - Attribute names are not case sensitive and may be surrounded +* by white space. +* - It does no harm to clear an attribute whose value has not been +* set. +* - An error will result if an attempt is made to clear the value +* of a read-only attribute. +*-- +*/ + +/* Local Variables: */ + char *buff; /* Pointer to character buffer */ + char *name; /* Pointer to individual attribute name */ + char *name_end; /* Pointer to null at end of name */ + int i; /* Loop counter for characters */ + int j; /* Non-blank character count */ + int len; /* Length of attrib string */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the length of the attrib string. */ + len = (int) strlen( attrib ); + if ( len != 0 ) { + +/* Allocate memory and store a copy of the string. */ + buff = astStore( NULL, attrib, (size_t) ( len + 1 ) ); + if ( astOK ) { + +/* Loop to process each element in the comma-separated list. */ + name = buff; + while ( name ) { + +/* Change the comma at the end of each element to a null to terminate + the name. */ + if ( ( name_end = strchr( name, ',' ) ) ) *name_end = '\0'; + +/* Remove white space and upper case characters from the attribute + name. */ + for ( i = j = 0; name[ i ]; i++ ) { + if ( !isspace( name[ i ] ) ) name[ j++ ] = tolower( name[ i ] ); + } + +/* Terminate the attribute name and pass it to astClearAttrib to clear + the attribute (unless it is all blank, in which case we ignore + it). */ + name[ j ] = '\0'; + if ( j ) astClearAttrib( this, name ); + +/* Check for errors and abort if any clear operation fails. Otherwise, + process the next attribute. */ + if ( !astOK ) break; + name = name_end ? name_end + 1 : NULL; + } + } + +/* Free the memory allocated for the string buffer. */ + buff = astFree( buff ); + } +} + +static void ClearAttrib( AstObject *this, const char *attrib, int *status ) { +/* +*+ +* Name: +* astClearAttrib + +* Purpose: +* Clear an attribute value for an Object. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* void astClearAttrib( AstObject *this, const char *attrib ) + +* Class Membership: +* Object method. + +* Description: +* This function clears the value of a specified attribute for an +* Object, so that the default value will subsequently be used. + +* Parameters: +* this +* Pointer to the Object. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. + +* Notes: +* - The Object class does not have any writable attributes, so +* this function merely reports an error. It is intended to be +* extended by other class definitions. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Check the attribute name and clear the appropriate attribute. */ + +/* ID. */ +/* --- */ + if ( !strcmp( attrib, "id" ) ) { + astClearID( this ); + +/* Ident. */ +/* ------ */ + } else if ( !strcmp( attrib, "ident" ) ) { + astClearIdent( this ); + +/* UseDefs. */ +/* -------- */ + } else if ( !strcmp( attrib, "usedefs" ) ) { + astClearUseDefs( this ); + +/* Read-only attributes. */ +/* --------------------- */ +/* Test if the attribute string matches any of the read-only + attributes of this class. If it does, then report an error. */ + } else if ( !strcmp( attrib, "class" ) || + !strcmp( attrib, "nobject" ) || + !strcmp( attrib, "objsize" ) || + !strcmp( attrib, "refcount" ) ) { + astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " + "value for a %s.", status, attrib, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Since no writable attributes are defined for the Object class, any + attempt to clear a value for anything else is also an error. */ + } else { + astError( AST__BADAT, "astClear: The attribute name \"%s\" is invalid " + "for a %s.", status, attrib, astGetClass( this ) ); + } +} + +AstObject *astClone_( AstObject *this, int *status ) { +/* +*++ +* Name: +c astClone +f AST_CLONE + +* Purpose: +* Clone (duplicate) an Object pointer. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c AstObject *astClone( AstObject *this ) +f RESULT = AST_CLONE( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a duplicate pointer to an existing +* Object. It also increments the Object's RefCount attribute to +* keep track of how many pointers have been issued. +* +* Note that this function is NOT equivalent to an assignment +* statement, as in general the two pointers will not have the same +* value. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Original pointer to the Object. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astClone() +f AST_CLONE = INTEGER +* A duplicate pointer to the same Object. + +* Applicability: +* Object +* This function applies to all Objects. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Get a lock on the object's secondary mutex. This mutex guards access + to the "ref_count" and "locker" components of the AstObject structure. */ + LOCK_SMUTEX(this); + +#ifdef MEM_DEBUG + { int rc; + char buf[100]; + rc = this->ref_count; + sprintf(buf,"cloned (refcnt: %d -> %d)", rc, rc+1 ); + astMemoryUse( this, buf ); + } +#endif + +/* Increment the Object's reference count. */ + this->ref_count++; + +/* Unlock the object's secondary mutex. */ + UNLOCK_SMUTEX(this); + +/* Return a new pointer to the Object. */ + return this; +} + +AstObject *astCopy_( const AstObject *this, int *status ) { +/* +*++ +* Name: +c astCopy +f AST_COPY + +* Purpose: +* Copy an Object. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c AstObject *astCopy( const AstObject *this ) +f RESULT = AST_COPY( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +* This function creates a copy of an Object and returns a pointer +* to the resulting new Object. It makes a "deep" copy, which +* contains no references to any other Object (i.e. if the original +* Object contains references to other Objects, then the actual +* data are copied, not simply the references). This means that +* modifications may safely be made to the copy without indirectly +* affecting any other Object. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object to be copied. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astCopy() +f AST_COPY = INTEGER +* Pointer to the new Object. + +* Applicability: +* Object +* This function applies to all Objects. + +* Notes: +* - A null Object pointer (AST__NULL) will be returned if this +c function is invoked with the AST error status set, or if it +f function is invoked with STATUS set to an error value, or if it +* should fail for any reason. +*-- +*/ + +/* Local Variables: */ + AstObject *new; /* Pointer to new object */ + AstObjectVtab *vtab; /* Pointer to object vtab */ + int i; /* Loop counter for copy constructors */ + +/* Initiallise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Re-use cached memory, or allocate new memory using the size of the input + object, to store the output Object. */ + + vtab = this->vtab; + if( object_caching ){ + + if( vtab->nfree > 0 ) { + new = vtab->free_list[ --(vtab->nfree) ]; + vtab->free_list[ vtab->nfree ] = NULL; + } else { + new = astMalloc( this->size ); + } + + } else { + new = astMalloc( this->size ); + } + + if ( astOK ) { + +/* Perform an initial byte-by-byte copy of the entire object + structure. */ + (void) memcpy( (void *) new, (const void *) this, this->size ); + +/* Initialise any components of the new Object structure that need to + differ from the input. */ + new->check = Magic( new, new->size, status ); + new->dynamic = 1; + new->ref_count = 1; + new->id = NULL; /* ID attribute is not copied (but Ident is copied) */ + new->proxy = NULL; + +/* Copy the persistent identifier string. */ + if( this->ident ) { + new->ident = astStore( NULL, this->ident, strlen( this->ident ) + 1 ); + } + +/* Create a new mutex for the new Object, and lock it for use by the + current thread. */ +#ifdef THREAD_SAFE + if( pthread_mutex_init( &(new->mutex1), NULL ) != 0 && astOK ) { + astError( AST__INTER, "astInitObject(%s): Failed to " + "initialise POSIX mutex1 for the new Object.", status, + vtab->class ); + } + if( pthread_mutex_init( &(new->mutex2), NULL ) != 0 && astOK ) { + astError( AST__INTER, "astInitObject(%s): Failed to " + "initialise POSIX mutex2 for the new Object.", status, + vtab->class ); + } + new->locker = -1; + new->globals = NULL; + (void) ManageLock( new, AST__LOCK, 0, NULL, status ); +#endif + +/* Loop to execute any copy constructors declared by derived classes. */ + for ( i = 0; i < vtab->ncopy; i++ ) { + +/* Invoke each copy constructor in turn. */ + (*vtab->copy[ i ])( this, new, status ); + +/* If any copy constructor fails, work backwards through the + corresponding destructor functions, invoking each in turn to undo + the copy operations that have been completed so far. */ + if ( !astOK ) { + for ( ; i >= 0; i-- ) { + (*vtab->delete[ i ])( new, status ); + } + +/* Zero the entire new Object structure (to prevent accidental re-use + of any of its values after deletion). */ + (void) memset( new, 0, new->size ); + +/* Free the Object's memory and ensure that a NULL pointer will be + returned. */ + new = astFree( new ); + +/* Quit trying to copy the Object. */ + break; + } + } + } + +/* If OK, increment the count of active objects. */ + if ( astOK ) vtab->nobject++; + +/* Return a pointer to the new Object. */ + return new; +} + +AstObject *astDelete_( AstObject *this, int *status ) { +/* +*++ +* Name: +c astDelete +f AST_DELETE + +* Purpose: +* Delete an Object. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c AstObject *astDelete( AstObject *this ) +f CALL AST_DELETE( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function deletes an Object, freeing all resources +f This routine deletes an Object, freeing all resources +* associated with it and rendering any remaining pointers to the +* Object invalid. +* +* Note that deletion is unconditional, regardless of whether other +* pointers to the Object are still in use (possibly within other +* Objects). A safer approach is to defer deletion, until all +c references to an Object have expired, by using astBegin/astEnd +c (together with astClone and astAnnul if necessary). +f references to an Object have expired, by using AST_BEGIN/AST_END +f (together with AST_CLONE and AST_ANNUL if necessary). + +* Parameters: +c this +c Pointer to the Object to be deleted. +f THIS = INTEGER (Given and Returned) +f Pointer to the Object to be deleted. A null pointer value +f (AST__NULL) is always returned. +f STATUS = INTEGER (Given and Returned) +f The global status. + +c Returned Value: +c astDelete() +c A null Object pointer (AST__NULL) is always returned. +c +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +c - This function attempts to execute even if the AST error status +c is set +f - This routine attempts to execute even if STATUS is set to an error +f value +* on entry, although no further error report will be +* made if it subsequently fails under these circumstances. +*-- +*/ + +/* Local Variables: */ + AstObjectVtab *vtab; /* Pointer to virtual function table */ + int dynamic; /* Was memory allocated dynamically? */ + int i; /* Loop counter for destructors */ + int ifree; /* Index of next slot on free list */ + int status_value; /* AST error status value */ + size_t size; /* Object size */ + +/* Check the pointer to ensure it identifies a valid Object (this + generates an error if it doesn't). */ + if ( !astIsAObject( this ) ) return NULL; + +/* Loop through all the destructors associated with the Object by derived + classes (working up the class hierarchy). */ + for ( i = this->vtab->ndelete - 1; i >= 0; i-- ) { + +/* Invoke each destructor in turn. Attempt to continue even if destructors + fail. */ + ( *this->vtab->delete[ i ] )( this, status ); + } + +/* Free the ID strings. */ + this->id = astFree( this->id ); + this->ident = astFree( this->ident ); + +/* Attempt to unlock the Object and destroy its mutexes. */ +#if defined(THREAD_SAFE) + (void) ManageLock( this, AST__UNLOCK, 0, NULL, status ); + pthread_mutex_destroy( &(this->mutex1) ); + pthread_mutex_destroy( &(this->mutex2) ); +#endif + +/* Save the virtual function table address and note if the Object's + memory was allocated dynamically. Also note its size. */ + vtab = this->vtab; + dynamic = this->dynamic; + size = this->size; + +/* Zero the entire Object structure (to prevent accidental re-use of + any of its values after deletion). */ + (void) memset( this, 0, size ); + +/* If necessary, free the Object's memory. If object caching is switched + on, the memory is not in fact freed; it is merely placed onto the end + of the list of free memory blocks included in the virtual function table + of the AST class concerned. astGrow returns immediately if an error + has already occurred, so we need to reset the error status explicitly + before calling astGrow. */ + if ( dynamic ) { + if( object_caching ) { + ifree = (vtab->nfree)++; + + status_value = astStatus; + astClearStatus; + vtab->free_list = astGrow( vtab->free_list, vtab->nfree, + sizeof(AstObject *) ); + astSetStatus( status_value ); + + if( vtab->free_list ) vtab->free_list[ ifree ] = this; + } else { + (void) astFree( this ); + } + } + +/* Decrement the count of active Objects. */ + vtab->nobject--; + +/* Always return NULL. */ + return NULL; +} + +static void Dump( AstObject *this, AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astDump + +* Purpose: +* Write an Object to a Channel. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* void astDump( AstObject *this, AstChannel *channel ) + +* Class Membership: +* Object method. + +* Description: +* This function writes an Object to a Channel, appending it to any +* previous Objects written to that Channel. + +* Parameters: +* this +* Pointer to the Object to be written. +* channel +* Pointer to the output Channel. +*- +*/ + +/* Local Variables: */ + AstObjectVtab *vtab; /* Pointer to virtual function table */ + const char *sval; /* Pointer to string value */ + int helpful; /* Helpful to show value even if not set? */ + int idump; /* Loop counter for dump functions */ + int ival; /* Attribute value */ + int set; /* Attribute value set? */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Write an initial "Begin" item, giving the class name of the Object + being written. Also supply a pointer to the comment associated with + the most recently-declared dump function in the Object's virtual + function table. This should describe the class to which the Object + belongs (assuming it has correctly declared its dump function). */ + astWriteBegin( channel, astGetClass( this ), + this->vtab->dump_comment[ this->vtab->ndump - 1 ] ); + +/* Write out instance variable information for the base Object + class. Accompany these with appropriate comment strings, possibly + depending on the values being written.*/ + +/* In the case of attributes, we first use the appropriate (private) + Test... member function to see if they are set. If so, we then use + the (private) Get... function to obtain the value to be written + out. + + For attributes which are not set, we use the astGet... method to + obtain the value instead. This will supply a default value + (possibly provided by a derived class which over-rides this method) + which is more useful to a human reader as it corresponds to the + actual default attribute value. Since "set" will be zero, these + values are for information only and will not be read back. */ + +/* ID. */ +/* --- */ + set = TestID( this, status ); + sval = set ? GetID( this, status ) : astGetID( this ); + +/* Don't show an un-set ID value if it is blank. */ + helpful = ( sval && *sval ); + astWriteString( channel, "ID", set, helpful, sval, + "Object identification string" ); + +/* Ident. */ +/* --- */ + set = TestIdent( this, status ); + sval = set ? GetIdent( this, status ) : astGetIdent( this ); + +/* Don't show an un-set Ident value if it is blank. */ + helpful = ( sval && *sval ); + astWriteString( channel, "Ident", set, helpful, sval, + "Permanent Object identification string" ); + +/* UseDefs */ +/* ------- */ + set = TestUseDefs( this, status ); + ival = set ? GetUseDefs( this, status ) : astGetUseDefs( this ); + astWriteInt( channel, "UseDfs", set, 0, ival, + ival ? "Default attribute values can be used" : + "Default values cannot be used" ); + +/* RefCnt. */ +/* ------- */ + LOCK_SMUTEX(this); + ival = this->ref_count; + UNLOCK_SMUTEX(this); + + astWriteInt( channel, "RefCnt", 0, 0, ival, + "Count of active Object pointers" ); + + +/* Nobj. */ +/* ----- */ + vtab = this->vtab; + astWriteInt( channel, "Nobj", 0, 0, vtab->nobject, + "Count of active Objects in same class" ); + +/* Terminate the information above with an "IsA" item for the base + Object class. */ + astWriteIsA( channel, "Object", "AST Object" ); + +/* Now loop to perform the same operation for each additional class + from which the Object inherits (the Object class itself does not + declare a dump function). Invoke the dump function for each class + in turn, working down the class hierarchy, to write out instance + variable information for that class. */ + for ( idump = 0; idump < this->vtab->ndump; idump++ ) { + ( *this->vtab->dump[ idump ] )( this, channel, status ); + +/* Terminate the output from all except the final dump function with + an appropriate "IsA" item describing the class whose data have just + been written. */ + if ( idump != ( this->vtab->ndump - 1 ) ) { + astWriteIsA( channel, this->vtab->dump_class[ idump ], + this->vtab->dump_comment[ idump ] ); + } + +/* Quit looping if an error occurs. */ + if ( !astOK ) break; + } + +/* Terminate the output from the final dump function with an "End" + item to match the initial "Begin" item. */ + astWriteEnd( channel, astGetClass( this ) ); +} + +static void EmptyObjectCache( int *status ){ +/* +* Name: +* EmptyObjectCache + +* Purpose: +* Free all memory blocks currently on the free list of any class. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* EmptyObjectCache( int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function empties the cache of Object memory by freeing all +* memory blocks on the free_list of all classes. + +* Parameters: +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function attempts to execute even if an error has occurred. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int iblock; /* Index of next entry in free list */ + int itab; /* Index of next virtual function table */ + AstObjectVtab *vtab; /* Pointer to next virtual function table */ + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Loop round all the virtual function tables which are known about. */ + for( itab = 0; itab < nvtab; itab++ ) { + vtab = known_vtabs[ itab ]; + +/* Free all memory blocks stored on the free list for this class. */ + for( iblock = 0; iblock < vtab->nfree; iblock++ ) { + (vtab->free_list)[ iblock ] = astFree( (vtab->free_list)[ iblock ] ); + } + +/* Free the memory used to hold the free list, and indicate it has zero + length. */ + vtab->free_list = astFree( vtab->free_list ); + vtab->nfree = 0; + } +} + +static void EnvSet( AstObject *this, int *status ) { +/* +*+ +* Name: +* astEnvSet + +* Purpose: +* Set default values for an Object's attributes. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* void astEnvSet( AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function assigns a set of attribute values for an Object, +* the attributes and their values being specified by means of an +* environment variable of the form "<CLASSNAME>_OPTIONS" that has +* a value of the form: +* +* "attribute1 = value1, attribute2 = value2, ... " +* +* Here, "attribute" specifies an attribute name and the value to +* the right of each "=" sign should be a suitable textual +* representation of the value to be assigned to that +* attribute. This will be interpreted according to the attribute's +* data type. + +* Parameters: +* this +* Pointer to the Object. + +* Notes: +* - See astVSet for details of how the setting strings are +* interpreted. +*- +*/ + +/* Local Variables: */ + char varname[ 100 ]; + const char *attrs = NULL; + const char *class = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get the string holding default attribute values for the class of the + supplied object. This string is held in the class virtual function + table. */ + attrs = this->vtab->defaults; + +/* If this is the first time the defaults have been requested, get the + list of defaults from the environment variable "<CLASSNAME>_OPTIONS" + and store in the virtual function table. */ + if( !attrs ) { + +/* Get the class name. */ + class = astGetClass( this ); + +/* Form the upper-case name of the environment variable. */ + if( class ) { + sprintf( varname, "%s_OPTIONS", class ); + astChrCase( NULL, varname, 1, sizeof( varname ) ); + +/* Get the value of the environment variable. */ + attrs = getenv( varname ); + +/* If no defaults were specified store the string "None". */ + if( ! attrs ) attrs = "None"; + +/* Store a copy in the virtual function table. */ + astBeginPM; + this->vtab->defaults = astStore( NULL, attrs, strlen( attrs ) + 1 ); + astEndPM; + } + } + +/* If any defaults were specified, set the corresponding attributes. */ + if( attrs && strcmp( attrs, "None" ) ) astSet( this, attrs, status ); + +} + +static int Equal( AstObject *this, AstObject *that, int *status ){ +/* +*+ +* Name: +* astEqual + +* Purpose: +* Check equality of two AST Objects. + +* Type: +* Public (but undocumented) function. + +* Synopsis: +* #include "object.h" +* int astEqual( AstObject *this, AstObject *this ) + +* Class Membership: +* Object virtual function. + +* Description: +* This function returns non-zero if the two pointers identify +* equivalent objects. + +* Parameters: +* this +* Pointer to the first Object. +* that +* Pointer to the second Object. + +* Returned Value: +* Non-zero if the objects are equivalent. + +* Notes: +* - This function is available in the public interface even though it is +* documented as protected. This is because it is difficult to document +* precisely which aspects of two Objects must be equal in order for this +* function to return a non-zero value. Each class of Object supplies +* its own Equal method that tests which-ever attributes the class +* considers to be significiant. +* - The implementation of this function provided by the base Object +* class simply compares the class names and the structure size. +* Sub-classes should override this method to provide more appropriate tests. +* - Zero is returned if an error has already occurred, or if +* this function should fail for any reason. + +*- +*/ + +/* Local Variables: */ + int result; + +/* Check inherited status */ + if( !astOK ) return 0; + +/* Objects are equivalent if they are the same object. */ + if( this == that ) { + result = 1; + +/* Otherwise, check the structure size and class names */ + } else { + result = ( this->size == that->size && + !strcmp( astGetClass( this ), astGetClass( that ) ) ); + } + + return result; +} + +static const char *Get( AstObject *this, const char *attrib, int *status ) { +/* +* Name: +* Get + +* Purpose: +* Get the value of a specified attribute for an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* const char *Get( AstObject *this, const char *attrib, int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function returns a pointer to the value of a specified +* attribute for an Object, formatted as a character string. It is +* mainly a wrap-up used internally for invoking the astGetAttrib +* method. It converts the attribute name to lower case and removes +* white space before invoking the method. This saves derived +* classes that over-ride the astGetAttrib method from having to do +* this themselves. + +* Parameters: +* this +* Pointer to the Object. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This may contain mixed +* case and white space, but should not be composed entirely of +* white space. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Object, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Object. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + char *buff; /* Pointer to local string buffer */ + const char *result; /* Pointer value to return */ + int i; /* Loop counter for characters */ + int j; /* Non-blank character count */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Allocate a local buffer long enough to hold the attribute name + string. */ + buff = astMalloc( strlen( attrib ) + (size_t) 1 ); + if ( astOK ) { + +/* Copy the attribute name characters into the buffer, omitting all + white space and converting to lower case. */ + for ( i = j = 0; attrib[ i ]; i++ ) { + if ( !isspace( attrib[ i ] ) ) buff[ j++ ] = tolower( attrib[ i ] ); + } + +/* Terminate the copied string. */ + buff[ j ] = '\0'; + +/* If no characters were copied, the attribute name was blank, so + report an error. */ + if ( !j ) { + if( astOK ) astError( AST__BADAT, "astGet(%s): A blank attribute " + "name was given.", status, astGetClass( this ) ); + +/* Of OK, invoke astGetAttrib to obtain a pointer to the attribute + value formatted as a character string. */ + } else { + result = astGetAttrib( this, buff ); + +/* If required, strip out graphical escape sequences. */ + if( !astEscapes( -1 ) ) result = astStripEscapes( result ); + } + } + +/* Free the local string buffer. */ + buff = astFree( buff ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = NULL; + +/* Return the result. */ + return result; +} + +static const char *GetAttrib( AstObject *this, const char *attrib, int *status ) { +/* +*+ +* Name: +* astGetAttrib + +* Purpose: +* Get the value of a specified attribute for an Object. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* const char *astGetAttrib( AstObject *this, const char *attrib ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a pointer to the value of a specified +* attribute for an Object, formatted as a character string. + +* Parameters: +* this +* Pointer to the Object. +* attrib +* Pointer to a null-terminated string containing the name of +* the attribute whose value is required. This name should be in +* lower case, with all white space removed. + +* Returned Value: +* - Pointer to a null-terminated string containing the attribute +* value. + +* Notes: +* - The returned string pointer may point at memory allocated +* within the Object, or at static memory. The contents of the +* string may be over-written or the pointer may become invalid +* following a further invocation of the same function or any +* modification of the Object. A copy of the string should +* therefore be made if necessary. +* - A NULL pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + const char *result; /* Pointer value to return */ + int nobject; /* Nobject attribute value */ + int objsize; /* ObjSize attribute value */ + int ref_count; /* RefCount attribute value */ + int usedefs; /* UseDefs attribute value */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(this); + +/* Compare "attrib" with each recognised attribute name in turn, + obtaining the value of the required attribute. If necessary, write + the value into "getattrib_buff" as a null-terminated string in an + appropriate format. Set "result" to point at the result string. */ + +/* Class. */ +/* ------ */ + if ( !strcmp( attrib, "class" ) ) { + result = astGetClass( this ); + +/* ID. */ +/* --- */ + } else if ( !strcmp( attrib, "id" ) ) { + result = astGetID( this ); + +/* Ident. */ +/* ------ */ + } else if ( !strcmp( attrib, "ident" ) ) { + result = astGetIdent( this ); + +/* UseDefs */ +/* ------- */ + } else if ( !strcmp( attrib, "usedefs" ) ) { + usedefs = astGetUseDefs( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", usedefs ); + result = getattrib_buff; + } + +/* Nobject. */ +/* -------- */ + } else if ( !strcmp( attrib, "nobject" ) ) { + nobject = astGetNobject( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", nobject ); + result = getattrib_buff; + } + +/* ObjSize */ +/* ------- */ + } else if ( !strcmp( attrib, "objsize" ) ) { + objsize = astGetObjSize( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", objsize ); + result = getattrib_buff; + } + +/* RefCount. */ +/* --------- */ + } else if ( !strcmp( attrib, "refcount" ) ) { + ref_count = astGetRefCount( this ); + if ( astOK ) { + (void) sprintf( getattrib_buff, "%d", ref_count ); + result = getattrib_buff; + } + +/* If the attribute name was not recognised, then report an error. */ + } else if( astOK ){ + astError( AST__BADAT, "astGet: The %s given does not have an attribute " + "called \"%s\".", status, astGetClass( this ), attrib ); + } + +/* Return the result. */ + return result; +} + +const char *astGetClass_( const AstObject *this, int *status ) { +/* +*+ +* Name: +* astGetClass + +* Purpose: +* Obtain the value of the Class attribute for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* const char *astGetClass( const AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a pointer to the Class string for an +* Object. This contains the name of the class which created the +* Object. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* Pointer to a string containing the class name. + +* Notes: +* - This function does not check the global error status before +* executing. This is to allow it to be used to obtain class names +* for inclusion in error messages. +* - A pointer to an explanatory string will be returned if this +* function is given a pointer which does not identify an Object. +*- +*/ + +/* Local Variables: */ + const char *name; /* Pointer to returned string */ + +/* First check if the Object pointer supplied is NULL, and set the + returned pointer accordingly. */ + if ( !this ) { + name = "<NULL>"; + +/* Also check if the supposed Object has the correct "magic number" in + its check field. If not, it is not an Object. */ + } else if ( this->check != Magic( this, this->size, status ) ) { + name = "<unknown>"; + +/* If OK, obtain a pointer to the class name from the Object's virtual + function table. */ + } else { + name = this->vtab->class; + } + +/* Return the result. */ + return name; +} + +int astGetNobject_( const AstObject *this, int *status ) { +/* +*+ +* Name: +* astGetNobject + +* Purpose: +* Obtain the value of the Nobject attribute for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* int astGetNobject( const AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function returns the value of the Nobject attribute for an +* Object. This is a count of the number of active Objects in the +* same class as the Object supplied. This count does not include +* Objects in derived classes. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* The number of active Objects. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Return the active object count. */ + return this->vtab->nobject; +} + +static int GetObjSize( AstObject *this, int *status ) { +/* +*+ +* Name: +* astGetObjSize + +* Purpose: +* Determine the in-memory size of the Object. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* int astGetObjSize( AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function returns the in-memory size of an Object. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* The Object size, in bytes. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Return the object size. */ + return this->size; +} + +void *astGetProxy_( AstObject *this, int *status ) { +/* +*+ +* Name: +* astGetProxy + +* Purpose: +* Get a pointer to the foreign language proxy used to represent a +* given AST Object. + +* Type: +* Undocumented public function. + +* Synopsis: +* #include "object.h" +* void *astGetProxy( AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function returns any pointer stored previously in the AST +* Object using astSetProxy. If no such pointer has been stored, a +* NULL pointer is returned. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* Pointer to the proxy object, or NULL. + +* Notes: +* - This function is public, but is currently undocumented since it +* is only of interest to people writing AST interfaces for other +* languages. +* - This function attempts to execute even if the AST error status +* is set on entry, although no further error report will be made +* if it subsequently fails under these circumstances. +*- +*/ + return this ? this->proxy : NULL; +} + +int astGetRefCount_( AstObject *this, int *status ) { +/* +*+ +* Name: +* astGetRefCount + +* Purpose: +* Obtain the value of the RefCount attribute for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* int astGetRefCount( const AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function returns the value of the read-only RefCount +* attribute for an Object. This is a "reference count" of the +* number of active pointers to it, as accounted for by astClone +* and astAnnul (plus the pointer issued when it was created). If +* the reference count for an Object falls to zero when astAnnul is +* invoked, the object will be deleted. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* The reference count. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables; */ + int result; /* Returned value */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Get a lock on the object's secondary mutex. This mutex guards access + to the "ref_count" and "locker" components of the AstObject structure. */ + LOCK_SMUTEX(this); + +/* Get the reference count. */ + result = this->ref_count; + +/* Unlock the object's secondary mutex. */ + UNLOCK_SMUTEX(this); + +/* Return the result. */ + return result; +} + +/* +*++ +* Name: +c astGet<X> +f AST_GET<X> + +* Purpose: +* Get an attribute value for an Object. + +* Type: +* Public functions. + +* Synopsis: +c #include "object.h" +c <X>type astGet<X>( AstObject *this, const char *attrib ) +f RESULT = AST_GET<X>( THIS, ATTRIB, STATUS ) + +* Class Membership: +* Object methods. + +* Description: +* This is a family of functions which return a specified attribute +* value for an Object using one of several different data +* types. The type is selected by replacing <X> in the function name +c by C, D, F, I or L, to obtain a result in const char* (i.e. string), +c double, float, int, or long format, respectively. +f by C, D, I, L or R, to obtain a result in Character, Double +f precision, Integer, Logical or Real format, respectively. +* +* If possible, the attribute value is converted to the type you +* request. If conversion is not possible, an error will result. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object. +c attrib +f ATTRIB = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated string containing the name of +c the attribute whose value is required. +f A character string containing the name of the attribute whose +f value is required. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astGet<X>() +f AST_GET<X> = <X>type +c The attribute value, in the data type corresponding to <X> (or, +c in the case of astGetC, a pointer to a constant null-terminated +c character string containing this value). +f The attribute value, in the data type corresponding to <X>. + +* Applicability: +* Object +* These functions apply to all Objects. + +* Examples: +c printf( "RefCount = %d\n", astGetI( z, "RefCount" ) ); +c Prints the RefCount attribute value for Object "z" as an int. +c title = astGetC( axis, "Title" ); +c Obtains a pointer to a null-terminated character string containing +c the Title attribute of Object "axis". +f WRITE( *, '('' RefCount = '', A10 )' ) AST_GETC( Z, 'RefCount', STATUS ) +f Prints the RefCount attribute value for Object Z as a character +f string. +f NAXES = AST_GETI( FRAME, 'Naxes', STATUS ) +f Obtains the value of the Naxes attribute for Object FRAME as an +f integer. + +* Notes: +* - Attribute names are not case sensitive and may be surrounded +* by white space. +* - An appropriate "null" value will be returned if this function +c is invoked with the AST error status set, or if it should +f is invoked with STATUS set to an error value, or if it should +* fail for any reason. This null value is zero for numeric +c values and NULL for pointer values. +f values, .FALSE. for logical values, and blank for character values. +f - Numerical attribute values of zero translate to logical value +f .FALSE. and all other numerical values translate to .TRUE.. +c - The pointer returned by astGetC is guaranteed to remain valid +c and the string to which it points will not be over-written for a +c total of 50 successive invocations of this function. After this, +c the memory containing the string may be re-used, so a copy of +c the string should be made if it is needed for longer than this. +*-- +*/ + +/* Define a macro that expands to implement the astGetX_ member + functions required. The arguments to this macro are: + + code + The character that appears at the end of the function name. + type + The C type of the function return value. + format + A quoted string containing a astSscanf format specifier that + will read the attribute value into a variable of the required + data type. This format should transfer 1 astSscanf value. +*/ +#define MAKE_GETX(code,type,format) \ +type astGet##code##_( AstObject *this, const char *attrib, int *status ) { \ +\ +/* Local Variables: */ \ + const char *str; /* Pointer to string attribute value */ \ + int nc; /* Number of characters read from string */ \ + int nval; /* Number of values read from string */ \ + type result; /* Value to return */ \ + type value; /* Converted value */ \ +\ +/* Initialise. */ \ + result = (type) 0; \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return result; \ +\ +/* Obtain the attribute value as a string. */ \ + str = Get( this, attrib, status ); \ + if ( astOK ) { \ +\ +/* Read the value from the string, ignoring surrounding white \ + space. */ \ + nc = 0; \ + nval = astSscanf( str, " " format " %n", &value, &nc ); \ +\ +/* Check that the number of values read was 1 and that all the \ + string's characters were consumed. If so, use the result. */ \ + if ( ( nval == 1 ) && ( nc >= (int) strlen( str ) ) ) { \ + result = value; \ +\ +/* If the read was unsuccessful, report an error. */ \ + } else if( astOK ) { \ + astError( AST__ATGER, "astGet" #code "(%s): The attribute " \ + "value \"%s=%s\" cannot be read using the requested data " \ + "type.", status,astGetClass( this ), attrib, str ); \ + } \ + } \ +\ +/* Return the result. */ \ + return result; \ +} + +/* Use this macro to create all the GetX_ private member functions, + except SetC (which is handled separately). */ +MAKE_GETX(D,double,"%lf") +MAKE_GETX(F,float,"%f") +MAKE_GETX(I,int,"%d") +MAKE_GETX(L,long,"%ld") + +/* Handle GetC separately because memory must be allocated to hold the + returned character values. */ +const char *astGetC_( AstObject *this, const char *attrib, int *status ) { + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + const char *result; /* Pointer value to return */ + const char *value; /* Pointer to attribute value */ + int i; /* Loop count */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(this); + +/* If the "strings" array has not been initialised, fill it with + NULL pointers. */ + if ( !astgetc_init ) { + astgetc_init = 1; + for ( i = 0; i < AST__ASTGETC_MAX_STRINGS; i++ ) astgetc_strings[ i ] = NULL; + } + +/* Obtain a pointer to the required attribute value, formatted as a + character string. */ + value = Get( this, attrib, status ); + +/* If OK, store a copy of the resulting string in dynamically + allocated memory, putting a pointer to the copy into the next + element of the "astgetc_strings" array. (This process also de-allocates + any previously allocated memory pointed at by this "strings" + element, so the earlier string is effectively replaced by the new + one.) */ + if ( astOK ) { + + astBeginPM; + astgetc_strings[ astgetc_istr ] = astStore( astgetc_strings[ astgetc_istr ], + value, strlen( value ) + (size_t) 1 ); + astEndPM; + +/* If OK, return a pointer to the copy and increment "astgetc_istr" to use the + next element of "astgetc_strings" on the next invocation. Recycle + "astgetc_istr" to zero when all elements have been used. */ + if ( astOK ) { + result = astgetc_strings[ astgetc_istr++ ]; + if ( astgetc_istr == ( AST__ASTGETC_MAX_STRINGS - 1 ) ) astgetc_istr = 0; + } + } + +/* Return the result. */ + return result; + +} + +static int HasAttribute( AstObject *this, const char *attrib, int *status ) { +/* +*++ +* Name: +c astHasAttribute +f AST_HASATTRIBUTE + +* Purpose: +* Test if an Object has a named attribute. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c int astHasAttribute( AstObject *this, const char *attrib ) +f RESULT = AST_HASATTRIBUTE( THIS, ATTRIB, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function returns a boolean result (0 or 1) to indicate +f This function returns a logical result to indicate +* whether the supplied Object has an attribute with the supplied name. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the first Object. +c attrib +f ATTRIB = INTEGER (Given) +c Pointer to a string holding the +f The +* name of the attribute to be tested. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astHasAttribute() +c One if the Object has the named attribute, otherwise zero. +f AST_SAME = LOGICAL +f .TRUE. if the Object has the named attribute, otherwise +f .FALSE. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +c - A value of zero will be returned if this function is invoked +c with the AST error status set, or if it should fail for any reason. +f - A value of .FALSE. will be returned if this function is invoked +f with STATUS set to an error value, or if it should fail for any reason. +*-- +*/ + +/* Local Variables: */ + int oldrep; /* Original AST error reporting flag */ + int result; /* Returned value */ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Temporarily switch off error reporting. */ + oldrep = astReporting( 0 ); + +/* Attempt to get a value for the specified attribute. */ + (void) Get( this, attrib, status ); + +/* An error will have been reported if the object does not have the + requested attribute. Set the result and clear the error status. */ + if( !astOK ) { + result = 0; + astClearStatus; + } else { + result = 1; + } + +/* Re-instate the original error reporting flag. */ + (void) astReporting( oldrep ); + +/* Return the result. */ + return result; +} + +static unsigned long Magic( const AstObject *this, size_t size, int *status ) { +/* +* Name: +* Magic + +* Purpose: +* Generate a "magic number" for an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* unsigned long Magic( const AstObject *this, size_t size, int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function generates a "magic number" which is a function of an Object +* pointer (address) and an Object size. This number may be stored in an +* Object to allow it to be recognised as a valid Object by other routines +* and to provide security against argument passing errors, etc. + +* Parameters: +* this +* Pointer to an Object. +* size +* The Object size. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The magic number. + +* Notes: +* - This function does not perform any error checking. +*/ + +/* Form the bit-wise exclusive OR between the Object address and the Object + size, then add 2 and invert the bits. Return the result as an unsigned + long integer. */ + return ~( ( ( (unsigned long) this ) ^ ( (unsigned long) size ) ) + + ( (unsigned long) 2 ) ); +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this, int mode, int extra, + AstObject **fail, int *status ) { +/* +*+ +* Name: +* astManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* int astManageLock( AstObject *this, int mode, int extra, +* AstObject **fail ) + +* Class Membership: +* Object method. + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread. +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. + +* Returned Value: +* A status value: +* 0 - Success. +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. +* 5 - Check failed - object is locked by a different thread +* 6 - Check failed - object is unlocked +* + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. + +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int result; /* Returned value */ + +/* Initialise */ + result = 0; + if( fail ) *fail = NULL; + +/* Check the supplied point is not NULL. */ + if( ! this ) return result; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Get a lock on the object's secondary mutex. This gives us exclusive + access to the "locker" (and "ref_count") component in the AstObject + structure. All other components in the structure are guarded by the + primary mutex (this->mutex1). */ + if( LOCK_SMUTEX(this) ) { + result = 2; + +/* If the secondary mutex was locked succesfully, first deal with cases + where the caller wants to lock the Object for exclusive use by the + calling thread. */ + } else if( mode == AST__LOCK ) { + +/* If the Object is not currently locked, lock the Object primary mutex + and record the identity of the calling thread in the Object. */ + if( this->locker == -1 ) { + if( LOCK_PMUTEX(this) ) result = 2; + this->locker = AST__THREAD_ID; + this->globals = AST__GLOBALS; + ChangeThreadVtab( this, status ); + +/* If the Object is already locked by the calling thread, do nothing. */ + } else if( this->locker == AST__THREAD_ID ) { + +/* If the object is locked by a different thread, and the caller is + willing to wait, attempt to lock the Object primary mutex. This will + cause the calling thread to block until the Object is release by the + thread that currently has it locked. Then store the identity of the + calling thread (the new lock owner). We first need to release the + secondary mutex so that the other thread can modify the "locker" + component in the AstObject structure when it releases the Object + (using this function). We then re-lock the secondary mutex so this + thread can change the "locker" component safely. */ + } else if( extra ) { + if( UNLOCK_SMUTEX(this) ) { + result = 3; + } else if( LOCK_PMUTEX(this) ) { + result = 2; + } else if( LOCK_SMUTEX(this) ) { + result = 2; + } + this->locker = AST__THREAD_ID; + this->globals = AST__GLOBALS; + ChangeThreadVtab( this, status ); + +/* If the caller does not want to wait until the Object is available, + return a status of 1. */ + } else { + result = 1; + } + +/* Unlock the Object for use by other threads. */ + } else if( mode == AST__UNLOCK ) { + +/* Do nothing if the Object is currently unlocked. */ + if( this->locker == -1 ) { + +/* If the object is currently locked by the calling thread, clear the + identity of the thread that owns the lock and unlock the primary + mutex. */ + } else if( this->locker == AST__THREAD_ID ) { + this->locker = -1; + this->globals = NULL; + if( UNLOCK_PMUTEX(this) ) result = 3; + +/* Return an error status value if the Object is locked by another + thread. */ + } else { + result = 1; + } + +/* Check the Object is locked by the calling thread. Return a status of 1 if + not. */ + } else if( mode == AST__CHECKLOCK ) { + if( this->locker == -1 ) { + result = 6; + } else if( this->locker != AST__THREAD_ID ) { + result = 5; + } + +/* Return a status of 4 for any other modes. */ + } else { + result = 4; + } + +/* Unlock the secondary mutex so that other threads can access the "locker" + component in the Object to see if it is locked. */ + if( UNLOCK_SMUTEX(this) ) result = 3; + +/* If the operation failed, return a pointer to the failed object. */ + if( result && fail ) *fail = this; + +/* Return the status value */ + return result; +} +#endif + +char *astToString_( AstObject *this, int *status ) { +/* +c++ +* Name: +* astToString + +* Purpose: +* Create an in-memory serialisation of an Object + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* char *astToString( AstObject *this ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a string holding a minimal textual +* serialisation of the supplied AST Object. The Object can re +* re-created from the serialisation using astFromString. + +* Parameters: +* this +* Pointer to the Object to be serialised. + +* Returned Value: +* astToString() +* Pointer to dynamically allocated memory holding the +* serialisation, or NULL if an error occurs. The pointer +* should be freed when no longer needed using astFree. + +c-- +*/ + +/* Local Variables: */ + StringData data; /* Data passed to the sink function */ + AstChannel *channel; /* Pointer to output Channel */ + +/* Check the global error status. */ + if ( !astOK ) return NULL; + +/* Create a Channel which will write to an expanding dynamically + allocated memory buffer. Set Channel attributes to exclude all + non-essential characters. */ + channel = astChannel( NULL, ToStringSink, "Comment=0,Full=-1,Indent=0", + status ); + +/* Initialise the data structure used to communicate with the sink + function, and store a pointer to it in the Channel. */ + data.ptr = NULL; + data.buff = NULL; + data.len = 0; + astPutChannelData( channel, &data ); + +/* Write the Object to the Channel. */ + astWrite( channel, this ); + +/* Annul the Channel pointer. */ + channel = astAnnul( channel ); + +/* Free the returned string if an error has occurred. */ + if( !astOK ) data.ptr = astFree( data.ptr ); + +/* Return the pointer. */ + return data.ptr; +} + +static void ToStringSink( const char *text ){ +/* +* Name: +* ToStringSink + +* Purpose: +* A Channel sink function for use by the astToString method. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* ToStringSink( const char *text ) + +* Class Membership: +* Object member function. + +* Description: +* This function appends the supplied line of text to the end of a +* dynamically growing memory block. + +* Parameters: +* text +* Pointer to the null-terminated line of text to be stored. + +*/ + +/* Local Variables: */ + StringData *data; /* Data passed to the sink function */ + int *status; /* Pointer to local status value */ + int status_value; /* Local status value */ + +/* Set up the local status */ + status_value = 0; + status = &status_value; + +/* Get a pointer to the structure holding the current memory pointer and + the length of the currently allocated memory. */ + data = astChannelData; + +/* Append the supplied text to the end of the string, and update the + string length. */ + data->ptr = astAppendString( data->ptr, &(data->len), text ); + +/* Append a newline character to the end of the string, and update the + string length. */ + data->ptr = astAppendString( data->ptr, &(data->len), "\n" ); +} + +void astSet_( void *this_void, const char *settings, int *status, ... ) { +/* +*++ +* Name: +c astSet +f AST_SET + +* Purpose: +* Set attribute values for an Object. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astSet( AstObject *this, const char *settings, ... ) +f CALL AST_SET( THIS, SETTINGS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function assigns a set of attribute values to an Object, +f This routine assigns a set of attribute values to an Object, +* over-riding any previous values. The attributes and their new +* values are specified via a character string, which should +* contain a comma-separated list of the form: +* +* "attribute_1 = value_1, attribute_2 = value_2, ... " +* +* where "attribute_n" specifies an attribute name, and the value +* to the right of each "=" sign should be a suitable textual +* representation of the value to be assigned. This value will be +* interpreted according to the attribute's data type. +c +c The string supplied may also contain "printf"-style format +c specifiers, identified by "%" signs in the usual way. If +c present, these will be substituted by values supplied as +c additional optional arguments (using the normal "printf" rules) +c before the string is used. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object. +c settings +f SETTINGS = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string containing a +c comma-separated list of attribute settings in the form described +c above. +f A character string containing a comma-separated list of +f attribute settings in the form described above. +c ... +c Optional additional arguments which supply values to be +c substituted for any "printf"-style format specifiers that +c appear in the "settings" string. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Examples: +c astSet( map, "Report = 1, Zoom = 25.0" ); +c Sets the Report attribute for Object "map" to the value 1 and +c the Zoom attribute to 25.0. +c astSet( frame, "Label( %d ) =Offset along axis %d", axis, axis ); +c Sets the Label(axis) attribute for Object "frame" to a +c suitable string, where the axis number is obtained from +c "axis", a variable of type int. +c astSet( frame, "Title =%s", mystring ); +c Sets the Title attribute for Object "frame" to the contents of +c the string "mystring". +f CALL AST_SET( MAP, 'Report = 1, Zoom = 25.0', STATUS ) +f Sets the Report attribute for Object MAP to the value 1 and +f the Zoom attribute to 25.0. +f CALL AST_SET( FRAME, 'Label( 1 ) =Offset from cluster axis', STATUS ) +f Sets the Label(1) attribute for Object FRAME to a suitable +f string. + +* Notes: +* - Attribute names are not case sensitive and may be surrounded +* by white space. +* - White space may also surround attribute values, where it will +* generally be ignored (except for string-valued attributes where +* it is significant and forms part of the value to be assigned). +* - To include a literal comma in the value assigned to an attribute, +* the whole attribute value should be enclosed in quotation markes. +c Alternatively, you can use "%s" format and supply the value as a +c separate additional argument to astSet (or use the astSetC +c function instead). +c - The same procedure may be adopted if "%" signs are to be included +c and are not to be interpreted as format specifiers (alternatively, +c the "printf" convention of writing "%%" may be used). +* - An error will result if an attempt is made to set a value for +* a read-only attribute. +*-- + +* Implementation Notes: +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* Object identifier is of type (void *) and is converted and +* validated within the function itself. +* - This implementation of astSet is designed to be used within AST, +* and has an explicit status parameter. From outside AST, the astSet +* macro will invoke the astSetId_ function which does not have an +* status parameter. + +*-- +*/ + +/* Local Variables: */ + AstObject *this; /* Pointer to the Object structure */ + va_list args; /* Variable argument list */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain and validate a pointer to the Object structure. */ + this = astCheckObject( this_void ); + if ( astOK ) { + +/* Obtain the variable argument list and pass all arguments to the + astVSet method for interpretation. */ + va_start( args, status ); + astVSet( this, settings, NULL, args ); + va_end( args ); + } +} + +static void SetAttrib( AstObject *this, const char *setting, int *status ) { +/* +*+ +* Name: +* astSetAttrib + +* Purpose: +* Set an attribute value for an Object. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* void astSetAttrib( AstObject *this, const char *setting ) + +* Class Membership: +* Object method. + +* Description: +* This function assigns an attribute value for an Object, the attribute and +* its value being specified by means of a string of the form: +* +* "attribute= value " +* +* Here, "attribute" specifies the attribute name and should be in lower +* case with no white space present. The value to the right of the "=" +* should be a suitable textual representation of the value to be assigned +* and this will be interpreted according to the attribute's data type. +* White space surrounding the value is only significant for string +* attributes. + +* Parameters: +* this +* Pointer to the Object. +* setting +* Pointer to a null-terminated string specifying the new attribute +* value. + +* Notes: +* - The Object class does not have any writable attributes, so +* this function merely reports an error. It is intended to be +* extended by other class definitions. +*- +*/ + +/* Local Variables: */ + int id; /* Offset of ID string */ + int ival; /* Integer attribute value */ + int len; /* Length of setting string */ + int nc; /* Number of characters read by astSscanf */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the length of the setting string. */ + len = (int) strlen( setting ); + +/* Test for each recognised attribute in turn, using "astSscanf" to parse + the setting string and extract the attribute value (or an offset to + it in the case of string values). In each case, use the value set + in "nc" to check that the entire string was matched. Once a value + has been obtained, use the appropriate method to set it. */ + +/* ID. */ +/* --- */ + if ( nc = 0, ( 0 == astSscanf( setting, "id=%n%*[^\n]%n", &id, &nc ) ) + && ( nc >= len ) ) { + astSetID( this, setting + id ); + +/* Ident. */ +/* ------ */ + } else if ( nc = 0, ( 0 == astSscanf( setting, "ident=%n%*[^\n]%n", &id, &nc ) ) + && ( nc >= len ) ) { + astSetIdent( this, setting + id ); + +/* UseDefs */ +/* ------- */ + } else if ( nc = 0, + ( 1 == astSscanf( setting, "usedefs= %d %n", &ival, &nc ) ) + && ( nc >= len ) ) { + astSetUseDefs( this, ival ); + +/* Define a macro to see if the setting string matches any of the + read-only attributes of this class and use this to report an error + if it does. */ +#define MATCH(attrib) \ + ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ + ( nc >= len ) ) + + } else if ( MATCH( "class" ) || + MATCH( "nobject" ) || + MATCH( "objsize" ) || + MATCH( "refcount" ) ) { + astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, + setting, astGetClass( this ) ); + astError( AST__NOWRT, "This is a read-only attribute." , status); + +/* Since no writable attributes are defined for the Object class, any + attempt to set a value for anything else is also an error. */ + } else { + astError( AST__BADAT, "astSet: The attribute setting \"%s\" is invalid " + "for a %s.", status, setting, astGetClass( this ) ); + } + +/* Undefine macros local to this function. */ +#undef MATCH +} + +void astSetCopy_( AstObjectVtab *vtab, + void (* copy)( const AstObject *, AstObject *, int * ), int *status ) { +/* +*+ +* Name: +* astSetCopy + +* Purpose: +* Declare a copy constructor for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* void astSetCopy( AstObjectVtab *vtab, +* void (* copy)( const AstObject *, AstObject * ) ) + +* Class Membership: +* Object method. + +* Description: +* This function is provided so that class definitions can declare a copy +* constructor to be associated with an Object that is being constructed. +* When a copy is later performed on the Object, the copy constructor of +* each class to which the Object belongs will be invoked in turn (working +* down the class hierarchy). The copy constructor is passed pointers to the +* source and destination Objects. It should implement the copy and return +* void. + +* Parameters: +* vtab +* Pointer to the Object's virtual function table, in which the copy +* constructor's pointer is to be stored for future use. +* copy +* Pointer to the copy constructor function. + +* Notes: +* - When an Object is copied, a byte-by-byte copy of its structure is +* automatically made before any copy constructors are invoked. A copy +* constructor need only be provided if this does not suffice (e.g. if the +* structure contains pointers to other data). +* - If a copy constructor is declared for a class, then a +* destructor for that class must also be declared (using +* astSetDelete) so that there is a one-to-one correspondence +* between copy constructors and their associated destructors. +* - Copy constructors should check the global error status in the normal +* way and should set it (and report an error) if they fail. +*- +*/ + + +/* Check the global status. */ + if ( !astOK ) return; + +/* Indicate that subsequent memory allocations may never be freed (other + than by any AST exit handler). */ + astBeginPM; + +/* Expand the array of copy constructor pointers in the virtual function table + (if necessary) to accommodate the new one. */ + vtab->copy = astGrow( vtab->copy, vtab->ncopy + 1, + sizeof( void (*)( const AstObject *, AstObject * ) ) ); + +/* If OK, store the new function pointer and increment the count of copy + constructors. */ + if ( astOK ) { + vtab->copy[ vtab->ncopy++ ] = copy; + } + +/* Mark the end of the section in which memory allocations may never be freed + (other than by any AST exit handler). */ + astEndPM; + +} + +void astSetDelete_( AstObjectVtab *vtab, void (* delete)( AstObject *, int * ), int *status ) { +/* +*+ +* Name: +* astSetDelete + +* Purpose: +* Declare a destructor for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* void astSetDelete( AstObjectVtab *vtab, void (* delete)( AstObject * ) ) + +* Class Membership: +* Object method. + +* Description: +* This function is provided so that class definitions can declare a +* destructor to be associated with an Object. When the Object is later +* deleted, the destructor declared by each class to which the Object +* belongs will be invoked in turn (working up the class hierarchy). The +* destructor is passed a pointer to the Object. It should free any +* resources (e.g. memory) associated with it and return void. It should +* not free the memory containing the Object itself. + +* Parameters: +* vtab +* Pointer to the Object's virtual function table, in which the +* destructor's pointer is to be stored for future use. +* delete +* Pointer to the destructor function. + +* Notes: +* - A destructor need not be declared for a class if there are no +* resources to free. +* - If a destructor is declared for a class, then a copy +* constructor for that class must also be declared (using +* astSetCopy) so that there is a one-to-one correspondence between +* copy constructors and their associated destructors. +* - A destructor function should generally attempt to execute even +* if the global error status is set on entry, but should not +* report further errors in that case (errors should be reported +* normally if status is not set on entry). +*- +*/ + + +/* Check the global status. */ + if ( !astOK ) return; + +/* Indicate that subsequent memory allocations may never be freed (other + than by any AST exit handler). */ + astBeginPM; + +/* Expand the array of destructor pointers in the virtual function table (if + necessary) to accommodate the new one. */ + vtab->delete = astGrow( vtab->delete, vtab->ndelete + 1, + sizeof( void (*)( AstObject * ) ) ); + +/* If OK, store the new function pointer and increment the count of + destructors. */ + if ( astOK ) { + vtab->delete[ vtab->ndelete++ ] = delete; + } + +/* Mark the end of the section in which memory allocations may never be freed + (other than by any AST exit handler). */ + astEndPM; + +} + +void astSetDump_( AstObjectVtab *vtab, + void (* dump)( AstObject *, AstChannel *, int * ), + const char *class, const char *comment, int *status ) { +/* +*+ +* Name: +* astSetDump + +* Purpose: +* Declare a dump function for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* void astSetDump( AstObjectVtab *vtab, +* void (* dump)( AstObject *, AstChannel * ), +* const char *class, const char *comment ) + +* Class Membership: +* Object method. + +* Description: +* This function is provided so that class definitions can declare +* a dump function to be associated with an Object that is being +* constructed. When the astWrite (or astShow or astToString) method +* is later used to write the Object to a Channel, the dump function +* of each class to which the Object belongs will be invoked in turn +* (working down the class hierarchy). The dump function is passed +* pointers to the Object and the output Channel. It should write +* out any internal values (e.g. instance variables) for its class +* that are to be kept (using the protected astWrite... methods of +* the Channel) and return void. + +* Parameters: +* vtab +* Pointer to the Object's virtual function table, in which the +* dump function's pointer is to be stored for future use. +* dump +* Pointer to the dump function. +* class +* Pointer to a constant null-terminated string (residing in +* static memory) containing the name of the class that is +* declaring the dump function. +* comment +* Pointer to a constant null-terminated string (residing in +* static memory) containing a comment to associate with the +* dump function. This should normally describe the purpose of +* the class that is declaring the dump function. + +* Notes: +* - Dump functions should check the global error status in the +* normal way and should set it (and report an error) if they fail. +*- +*/ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Indicate that subsequent memory allocations may never be freed (other + than by any AST exit handler). */ + astBeginPM; + +/* Expand the arrays of pointers to dump functions and related data in + the virtual function table (if necessary) to accommodate the new + one. */ + vtab->dump = astGrow( vtab->dump, vtab->ndump + 1, + sizeof( void (*)( AstObject *, AstChannel * ) ) ); + vtab->dump_class = astGrow( vtab->dump_class, vtab->ndump + 1, + sizeof( char * ) ); + vtab->dump_comment = astGrow( vtab->dump_comment, vtab->ndump + 1, + sizeof( char * ) ); + +/* If OK, store the new pointers (to the dump function, class name and + comment) and increment the count of dump functions. */ + if ( astOK ) { + vtab->dump[ vtab->ndump ] = dump; + vtab->dump_class[ vtab->ndump ] = class; + vtab->dump_comment[ vtab->ndump ] = comment; + vtab->ndump++; + } + +/* Mark the end of the section in which memory allocations may never be + freed (other than by any AST exit handler). */ + astEndPM; +} + +void astSetProxy_( AstObject *this, void *proxy, int *status ) { +/* +*+ +* Name: +* astSetProxy + +* Purpose: +* Store a pointer to the foreign language proxy used to represent a +* given AST Object. + +* Type: +* Undocumented public function. + +* Synopsis: +* #include "object.h" +* void astSetProxy( AstObject *this, void *proxy ) + +* Class Membership: +* Object method. + +* Description: +* This function stores the supplied pointer in the AST Object so that +* it can be retrieved later using astGetProxy. +* +* The supplied pointer should point to a structure that is used +* to represent the AST Object within some external system. It is +* expected that the external system will check each object reference +* returned by AST to see if it has an associated proxy object. If not +* (i.e. if astGetProxy returns NULL), a new external object will be +* created to represent the AST Object, and a pointer to it will be +* stored in the AST Object using astSetProxy. If the AST Object +* already has a proxy, the AST reference is annulled and the existing +* proxy object is used by the external system. + +* Parameters: +* this +* Pointer to the Object. +* proxy +* Pointer to the proxy object, or NULL. + +* Notes: +* - The suppied pointer is not used within AST itself, other than to +* be returned by the astGetProxy method. +* - This function is public, but is currently undocumented since it +* is only of interest to people writing AST interfaces for other +* languages. +*- +*/ + if( !astOK ) return; + this->proxy = proxy; +} + +void astSetVtab_( AstObject *this, AstObjectVtab *vtab, int *status ) { +/* +*+ +* Name: +* astSetVtab + +* Purpose: +* Change the virtual function table associated with an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* void astSetVtab( AstObject *this, AstObjectVtab *vtab ) + +* Class Membership: +* Object method. + +* Description: +* This function changes the virtual function table associated with an +* Object. This may be needed, for instance, if a super-class +* initialises a parent class structure with a NULL vtab, causing the +* vtab of the parent class to be used instead of the super-class. +* Whilst the super-class object is being constructed its inherited methods +* will be determined by the parent class. Once the super-class object +* has been constructed, it can invoke this fuction in order to +* set the vtab to the super-class vtab, thus causing the method +* implementations provided by the super-cvlass to be used. + +* Parameters: +* this +* Pointer to the Object to be modified. +* vtab +* Pointer to the virtual function table to store in the Object. +*- +*/ + if( this ) this->vtab = vtab; +} + +static int Same( AstObject *this, AstObject *that, int *status ) { +/* +*++ +* Name: +c astSame +f AST_SAME + +* Purpose: +* Test if two AST pointers refer to the same Object. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c int astSame( AstObject *this, AstObject *that ) +f RESULT = AST_SAME( THIS, THAT, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function returns a boolean result (0 or 1) to indicate +f This function returns a logical result to indicate +* whether two pointers refer to the same Object. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the first Object. +c that +f THAT = INTEGER (Given) +* Pointer to the second Object. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astSame() +c One if the two pointers refer to the same Object, otherwise zero. +f AST_SAME = LOGICAL +f .TRUE. if the two pointers refer to the same Object, otherwise +f .FALSE. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +* - Two independent Objects that happen to be identical are not +* considered to be the same Object by this function. +c - A value of zero will be returned if this function is invoked +c with the AST error status set, or if it should fail for any reason. +f - A value of .FALSE. will be returned if this function is invoked +f with STATUS set to an error value, or if it should fail for any reason. +*-- +*/ + +/* Check the global error status. */ + if ( !astOK ) return 0; + +/* Return the result. */ + return ( this == that ) ? 1 : 0; +} + +/* +*++ +* Name: +c astSet<X> +f AST_SET<X> + +* Purpose: +* Set an attribute value for an Object. + +* Type: +* Public functions. + +* Synopsis: +c #include "object.h" +c void astSet<X>( AstObject *this, const char *attrib, <X>type value ) +f CALL AST_SET<X>( THIS, ATTRIB, VALUE, STATUS ) + +* Class Membership: +* Object methods. + +* Description: +c This is a family of functions which set a specified attribute +f This is a family of routines which set a specified attribute +* value for an Object using one of several different data +c types. The type is selected by replacing <X> in the function name +f types. The type is selected by replacing <X> in the routine name +c by C, D, F, I or L, to supply a value in const char* (i.e. string), +c double, float, int, or long format, respectively. +f by C, D, I, L or R, to supply a value in Character, Double +f precision, Integer, Logical or Real format, respectively. +* +* If possible, the value you supply is converted to the type of +* the attribute. If conversion is not possible, an error will +* result. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object. +c attrib +f ATTRIB = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string containing the +c name of the attribute whose value is to be set. +f A character string containing the name of the attribute whose +f value is to be set. +c value +f VALUE = <X>type (Given) +c The value to be set for the attribute, in the data type corresponding +c to <X> (or, in the case of astSetC, a pointer to a null-terminated +c character string containing this value). +f The value to be set for the attribute, in the data type corresponding +f to <X>. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c These functions apply to all Objects. +f These routines apply to all Objects. + +* Examples: +c astSetI( frame, "Preserve", 1 ); +c Sets the Preserve attribute value for Object "frame" to 1. +c astSetC( plot, "Format(1)", "%.2g" ); +c Sets the Format(1) attribute value for Object "plot" to the +c character string "%.2g". +f CALL AST_SETC( PLOT, 'Title', CVALUE, STATUS ) +f Sets the Title attribute value for Object PLOT to the contents +f of the character variable CVALUE. +f CALL AST_SETL( FRAME, 'Preserve', .TRUE., STATUS ); +f Sets the Preserve attribute value for Object FRAME to 1 (true). + +* Notes: +* - Attribute names are not case sensitive and may be surrounded +* by white space. +f - The logical value .FALSE. will translate to a numerical attribute +f value of zero and logical .TRUE. will translate to one. +* - An error will result if an attempt is made to set a value for +* a read-only attribute. +*-- +*/ + +/* Define a macro that expands to implement the astSetX_ member + functions required. The arguments to this macro are: + + code + The character that appears at the end of the function name. + type + The C type of the function "value" parameter. + format + A quoted string containing a sprintf format specifier that will + format the supplied value as a character string. This format should + consume 2 sprintf arguments: a field width and the value to be + formatted. + fmtlen + The number of characters in the format specifier (above). + fieldsz + The value of the field width to be used by the format specifier. +*/ +#define MAKE_SETX(code,type,format,fmtlen,fieldsz) \ +void astSet##code##_( AstObject *this, const char *attrib, type value, int *status ) { \ +\ +/* Local Variables: */ \ + char *setting; /* Pointer to attribute setting string */ \ + int len; /* Length of attribute name */ \ +\ +/* Check the global status. */ \ + if ( !astOK ) return; \ +\ +/* Obtain the length of the attribute name and allocate memory to hold \ + this name plus the format specifier to be appended to it. */ \ + len = (int) astChrLen( attrib ); \ + setting = astMalloc( (size_t) ( len + fmtlen + 2 ) ); \ +\ +/* Make a copy of the attribute name in the allocated memory. */ \ + if ( astOK ) { \ + (void) memcpy( setting, attrib, (size_t) len ); \ + setting[ len ] = 0; \ +\ +/* Append "=", followed by the format specifier, to construct a \ + suitable "setting" string for use by astSet. */ \ + (void) strcat( setting, "=" format ); \ +\ +/* Invoke astSet to set the attribute value. */ \ + astSet( this, setting, status, fieldsz, value ); \ + } \ +\ +/* Free the allocated memory. */ \ + setting = astFree( setting ); \ +} + +/* Use this macro to create all the SetX_ private member functions. */ +MAKE_SETX(D,double,"%.*g",4,DBL_DIG) +MAKE_SETX(F,float,"%.*g",4,FLT_DIG) +MAKE_SETX(I,int,"%.*d",4,1) +MAKE_SETX(L,long,"%.*ld",5,1) + + +/* The astSetC_ function is implemented separately so that commas can be + handled. Since astSetC can only be used to set a single attribute + value, we know that any commas in the supplied value are included + within the attribuite value, rather than being used as delimiters + between adjacent attribute settings. To avoid VSet using them as + delimiters, they are replaced here by '\r' before calling astSet, and + VSet then converts them back to commas. */ + +void astSetC_( AstObject *this, const char *attrib, const char *value, int *status ) { + +/* Local Variables: */ + char *d; /* Pointer to next setting character */ + char *newv; /* Pointer to new attribute value string */ + char *setting; /* Pointer to attribute setting string */ + const char *c; /* Pointer to next value character */ + int len; /* Length of attribute name */ + +/* Check the global status. */ + if ( !astOK ) return; + +/* Produce a copy of the supplied attribute value in which any commas + are replaced by carriage returns ("\r"). */ + newv = astMalloc( (size_t)( strlen( value ) + 1 ) ); + if( newv ) { + d = newv; + c = value; + while( *c ) { + if( *c == ',' ) { + *d = '\r'; + } else { + *d = *c; + } + c++; + d++; + } + *d = 0; + +/* Obtain the length of the attribute name and allocate memory to hold + this name plus the format specifier to be appended to it. */ + len = (int) astChrLen( attrib ); + setting = astMalloc( (size_t) ( len + 5 ) ); + +/* Make a copy of the attribute name in the allocated memory. */ + if ( astOK ) { + (void) memcpy( setting, attrib, (size_t) len ); + setting[ len ] = 0; + +/* Append "=", followed by the format specifier, to construct a + suitable "setting" string for use by astSet. */ + (void) strcat( setting, "=%*s" ); + +/* Invoke astSet to set the attribute value. */ + astSet( this, setting, status, 0, newv ); + } + +/* Free the allocated memory. */ + setting = astFree( setting ); + } + newv = astFree( newv ); +} + +static void Show( AstObject *this, int *status ) { +/* +*++ +* Name: +c astShow +f AST_SHOW + +* Purpose: +* Display a textual representation of an Object on standard output. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astShow( AstObject *this ) +f CALL AST_SHOW( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function displays a textual description of any AST Object +f This routine displays a textual description of any AST Object +* on standard output. It is provided primarily as an aid to +* debugging. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object to be displayed. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. +*-- +*/ + +/* Local Variables: */ + AstChannel *channel; /* Pointer to output Channel */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Create a Channel which will write to standard output. */ + channel = astChannel( NULL, NULL, "", status ); + +/* Write the Object to the Channel. */ + astWrite( channel, this ); + +/* Annul the Channel pointer. */ + channel = astAnnul( channel ); +} + +int astTest_( AstObject *this, const char *attrib, int *status ) { +/* +*++ +* Name: +c astTest +f AST_TEST + +* Purpose: +* Test if an Object attribute value is set. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c int astTest( AstObject *this, const char *attrib ) +f RESULT = AST_TEST( THIS, ATTRIB, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function returns a boolean result (0 or 1) to indicate +f This function returns a logical result to indicate +* whether a value has been explicitly set for one of an Object's +* attributes. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object. +c attrib +f ATTRIB = CHARACTER * ( * ) (Given) +c Pointer to a null-terminated character string containing +c the name of the attribute to be tested. +f A character string containing the name of the attribute to be +f tested. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astTest() +c One if a value has previously been explicitly set for the attribute +c (and hasn't been cleared), otherwise zero. +f AST_TEST = LOGICAL +f .TRUE. if a value has previously been explicitly set for the +f attribute (and hasn't been cleared), otherwise .FALSE.. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +* - Attribute names are not case sensitive and may be surrounded +* by white space. +c - A value of zero will be returned if this function is invoked +f - A value of .FALSE. will be returned if this function is invoked +c with the AST error status set, or if it should fail for any reason. +f with STATUS set to an error value, or if it should fail for any reason. +c - A value of zero will also be returned if this function is used +f - A value of .FALSE. will also be returned if this function is used +* to test a read-only attribute, although no error will result. +*-- +*/ + +/* Local Variables: */ + char *buff; /* Pointer to character buffer */ + int i; /* Loop counter for characters */ + int j; /* Non-blank character count */ + int len; /* Length of attrib string */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain the length of the attrib string. */ + len = (int) strlen( attrib ); + +/* Allocate memory and store a copy of the string. */ + buff = astStore( NULL, attrib, (size_t) ( len + 1 ) ); + if ( astOK ) { + +/* Remove white space and upper case characters. */ + for ( i = j = 0; buff[ i ]; i++ ) { + if ( !isspace( buff[ i ] ) ) buff[ j++ ] = tolower( buff[ i ] ); + } + +/* Terminate the attribute name and pass it to astTestAttrib to test + the attribute. */ + buff[ j ] = '\0'; + result = astTestAttrib( this, buff ); + } + +/* Free the memory allocated for the string buffer. */ + buff = astFree( buff ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result. */ + return result; +} + +static int TestAttrib( AstObject *this, const char *attrib, int *status ) { +/* +*+ +* Name: +* astTestAttrib + +* Purpose: +* Test if a specified attribute value is set for an Object. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* int astTestAttrib( AstObject *this, const char *attrib ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* a value has been set for one of an Object's attributes. + +* Parameters: +* this +* Pointer to the Object. +* attrib +* Pointer to a null-terminated string specifying the attribute +* name. This should be in lower case with no surrounding white +* space. + +* Returned Value: +* One if a value has been set, otherwise zero. + +* Notes: +* - A value of zero will be returned if this function is invoked +* with the global status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Check the attribute name and test the appropriate attribute. */ + +/* ID. */ +/* --- */ + if ( !strcmp( attrib, "id" ) ) { + result = astTestID( this ); + +/* Ident. */ +/* ------ */ + } else if ( !strcmp( attrib, "ident" ) ) { + result = astTestIdent( this ); + +/* UseDefs */ +/* ------- */ + } else if ( !strcmp( attrib, "usedefs" ) ) { + result = astTestUseDefs( this ); + +/* Test if the attribute string matches any of the read-only + attributes of this class. If it does, then return zero. */ + } else if ( !strcmp( attrib, "class" ) || + !strcmp( attrib, "nobject" ) || + !strcmp( attrib, "objsize" ) || + !strcmp( attrib, "refcount" ) ) { + result = 0; + +/* Any attempt to test any other attribute is an error. */ + } else if( astOK ){ + astError( AST__BADAT, "astTest: The attribute name \"%s\" is invalid " + "for a %s.", status, attrib, astGetClass( this ) ); + } + +/* Return the result, */ + return result; +} + +int astTune_( const char *name, int value, int *status ) { +/* +*++ +* Name: +c astTune +f AST_TUNE + +* Purpose: +* Set or get an integer-valued AST global tuning parameter. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c int astTune( const char *name, int value ) +f RESULT = AST_TUNE( NAME, VALUE, STATUS ) + +* Class Membership: +* Object function. + +* Description: +* This function returns the current value of an integer-valued AST +* global tuning parameter, optionally storing a new value for the +* parameter. For character-valued tuning parameters, see +c astTuneC. +f AST_TUNEC. + +* Parameters: +c name +f NAME = CHARACTER * ( * ) (Given) +* The name of the tuning parameter (case-insensitive). +c value +f VALUE = INTEGER (Given) +* The new value for the tuning parameter. If this is AST__TUNULL, +* the existing current value will be retained. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astTune() +f AST_TUNE = INTEGER +c The original value of the tuning parameter. A default value will +* be returned if no value has been set for the parameter. + +* Tuning Parameters: +* ObjectCaching +* A boolean flag which indicates what should happen +* to the memory occupied by an AST Object when the Object is deleted +* (i.e. when its reference count falls to zero or it is deleted using +c astDelete). +f AST_DELETE). +* If this is zero, the memory is simply freed using the systems "free" +* function. If it is non-zero, the memory is not freed. Instead a +* pointer to it is stored in a pool of such pointers, all of which +* refer to allocated but currently unused blocks of memory. This allows +* AST to speed up subsequent Object creation by re-using previously +* allocated memory blocks rather than allocating new memory using the +* systems malloc function. The default value for this parameter is +* zero. Setting it to a non-zero value will result in Object memory +* being cached in future. Setting it back to zero causes any memory +* blocks currently in the pool to be freed. Note, this tuning parameter +* only controls the caching of memory used to store AST Objects. To +* cache other memory blocks allocated by AST, use MemoryCaching. +* MemoryCaching +* A boolean flag similar to ObjectCaching except +* that it controls caching of all memory blocks of less than 300 bytes +* allocated by AST (whether for internal or external use), not just +* memory used to store AST Objects. + +* Notes: +c - This function attempts to execute even if the AST error +c status is set +f - This routine attempts to execute even if STATUS is set to an +f error value +* on entry, although no further error report will be +* made if it subsequently fails under these circumstances. +* - All threads in a process share the same AST tuning parameters +* values. +*-- +*/ + + int result = AST__TUNULL; + + if( name ) { + + LOCK_MUTEX1; + + if( astChrMatch( name, "ObjectCaching" ) ) { + result = object_caching; + if( value != AST__TUNULL ) { + object_caching = value; + if( !object_caching ) EmptyObjectCache( status ); + } + + } else if( astChrMatch( name, "MemoryCaching" ) ) { + result = astMemCaching( value ); + + } else if( astOK ) { + astError( AST__TUNAM, "astTune: Unknown AST tuning parameter " + "specified \"%s\".", status, name ); + } + + UNLOCK_MUTEX1; + + } + + return result; +} + +void astTuneC_( const char *name, const char *value, char *buff, + int bufflen, int *status ) { +/* +*++ +* Name: +c astTuneC +f AST_TUNEC + +* Purpose: +* Set or get a character-valued AST global tuning parameter. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astTuneC( const char *name, const char *value, char *buff, +c int bufflen ) +f CALL AST_TUNEC( NAME, VALUE, BUFF, STATUS ) + +* Class Membership: +* Object function. + +* Description: +* This function returns the current value of a character-valued +* AST global tuning parameter, optionally storing a new value +* for the parameter. For integer-valued tuning parameters, see +c astTune. +f AST_TUNE. + +* Parameters: +c name +f NAME = CHARACTER * ( * ) (Given) +* The name of the tuning parameter (case-insensitive). +c value +f VALUE = CHARACTER * ( ) (Given) +* The new value for the tuning parameter. If this is +f AST__TUNULLC, +c NULL, +* the existing current value will be retained. +c buff +f BUFF = CHARACTER * ( ) (Given) +* A character string in which to return the original value of +* the tuning parameter. An error will be reported if the buffer +* is too small to hold the value. +c NULL may be supplied if the old value is not required. +c bufflen +c The size of the supplied "buff" array. Ignored if "buff" is NULL. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Tuning Parameters: +* HRDel +* A string to be drawn following the hours field in a formatted +* sky axis value when "g" format is in use (see the Format +* attribute). This string may include escape sequences to produce +* super-scripts, etc. (see the Escapes attribute for details +* of the escape sequences allowed). The default value is +* "%-%^50+%s70+h%+" which produces a super-script "h". +* MNDel +* A string to be drawn following the minutes field in a formatted +* sky axis value when "g" format is in use. The default value is +* "%-%^50+%s70+m%+" which produces a super-script "m". +* SCDel +* A string to be drawn following the seconds field in a formatted +* sky axis value when "g" format is in use. The default value is +* "%-%^50+%s70+s%+" which produces a super-script "s". +* DGDel +* A string to be drawn following the degrees field in a formatted +* sky axis value when "g" format is in use. The default value is +* "%-%^53+%s60+o%+" which produces a super-script "o". +* AMDel +* A string to be drawn following the arc-minutes field in a formatted +* sky axis value when "g" format is in use. The default value is +* "%-%^20+%s85+'%+" which produces a super-script "'" (single quote). +* ASDel +* A string to be drawn following the arc-seconds field in a formatted +* sky axis value when "g" format is in use. The default value is +* "%-%^20+%s85+\"%+" which produces a super-script """ (double quote). +* EXDel +* A string to be drawn to introduce the exponent in a value when "g" +* format is in use. The default value is "10%-%^50+%s70+" which +* produces "10" followed by the exponent as a super-script. + +* Notes: +c - This function attempts to execute even if the AST error +c status is set +f - This routine attempts to execute even if STATUS is set to an +f error value +* on entry, although no further error report will be +* made if it subsequently fails under these circumstances. +* - All threads in a process share the same AST tuning parameters +* values. +*-- +*/ + +/* Local Variables: */ + char *p = NULL; + int len; + +/* Check the name of a tuning parameter was supplied. */ + if( name ) { + +/* Serialise access to the tuning parameters since they are common to all + threads. */ + LOCK_MUTEX1; + +/* Get a pointer to the buffer that holds the value of the requested + tuning parameter. */ + if( astChrMatch( name, "hrdel" ) ) { + p = hrdel; + } else if( astChrMatch( name, "mndel" ) ) { + p = mndel; + } else if( astChrMatch( name, "scdel" ) ) { + p = scdel; + } else if( astChrMatch( name, "dgdel" ) ) { + p = dgdel; + } else if( astChrMatch( name, "amdel" ) ) { + p = amdel; + } else if( astChrMatch( name, "asdel" ) ) { + p = asdel; + } else if( astChrMatch( name, "exdel" ) ) { + p = exdel; + +/* Report an error if an the tuning parameter name is unknown. */ + } else if( astOK ) { + p = NULL; + astError( AST__TUNAM, "astTuneC: Unknown AST tuning parameter " + "specified \"%s\".", status, name ); + } + +/* If the existing value was found. */ + if( p ) { + +/* And is to be returned in the supplied buffer... */ + if( buff ) { + +/* Check that the buffer is long enough. If so, copy the current value + into the buffer, otherwise report an error. */ + len = strlen( p ) ; + if( len < bufflen ) { + strcpy( buff, p ); + } else { + astError( AST__TUNAM, "astTuneC: Supplied string variable " + "is too small - the current '%s' value (%s) has " + "%d characters.", status, name, p, len ); + } + } + +/* If a new value is to be stored.... */ + if( value ) { + +/* Report an error if it is too long to fit in the static buffer. */ + len = strlen( value ) ; + if( len >= MAXLEN_TUNEC ) { + astError( AST__TUNAM, "astTuneC: Supplied value for '%s' " + "(%s) is too long - must not be longer than %d " + "characters.", status, name, value, MAXLEN_TUNEC ); + +/* Otherwise, copy the new value into the static buffer. */ + } else { + strcpy( p, value ); + } + } + } + + UNLOCK_MUTEX1; + } +} + +AstObject *astFromString_( const char *string, int *status ) { +/* +c++ +* Name: +* astFromString + +* Purpose: +* Re-create an Object from an in-memory serialisation + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* AstObject *astFromString( const char *string ) + +* Class Membership: +* Object method. + +* Description: +* This function returns a pointer to a new Object created from the +* supplied text string, which should have been created by astToString. + +* Parameters: +* string +* Pointer to a text string holding an Object serialisation created +* previously by astToString. + +* Returned Value: +* astFromString() +* Pointer to a new Object created from the supplied serialisation, +* or NULL if the serialisation was invalid, or an error occurred. + +c-- +*/ + +/* Local Variables: */ + StringData data; /* Data passed to the source function */ + AstChannel *channel; /* Pointer to output Channel */ + AstObject *result; /* Pointer to returned Object */ + +/* Check the global error status and supplied serialisation. */ + if ( !astOK || !string ) return NULL; + +/* Create a Channel which will read from the supplied serialisation. */ + channel = astChannel( FromStringSource, NULL, "", status ); + +/* Initialise the data structure used to communicate with the source + function, and store a pointer to it in the Channel. */ + data.ptr = (char *) string; + data.buff = NULL; + data.len = 0; + astPutChannelData( channel, &data ); + +/* Read an Object from the Channel. */ + result = astRead( channel ); + +/* Annul the Channel pointer. */ + channel = astAnnul( channel ); + +/* Free the line buffer. */ + data.buff = astFree( data.buff ); + +/* Annul the returned Object if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the Object pointer. */ + return result; +} + +static const char *FromStringSource( void ){ +/* +* Name: +* FromStringSource + +* Purpose: +* A Channel source function for use by the astFromString method. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* result = FromStringSource( void ) + +* Class Membership: +* Object member function. + +* Description: +* This function reads the next line of text from a serialisation and +* returns a pointer to it, or NULL if no lines remain. + +* Returned Value: +* Pointer to the null terminated line of text or NULL if no lines +* remain. +*/ + +/* Local Variables: */ + StringData *data; /* Data passed to the sink function */ + char *nl; /* Pointer to next newline character */ + int *status; /* Pointer to local status value */ + int nc; /* Number of characters to read from serialisation */ + int status_value; /* Local status value */ + +/* Set up the local status */ + status_value = 0; + status = &status_value; + +/* Get a pointer to the structure holding a pointer to the next line, and + to the buffer to return. */ + data = astChannelData; + +/* Return NULL if no text remains to be read. */ + if( !data->ptr || (data->ptr)[0] == 0 ) return NULL; + +/* Find the next newline (if any) in the serialisation. */ + nl = strchr( data->ptr, '\n' ); + +/* Get the number of characters to copy. */ + nc = nl ? nl - data->ptr : strlen( data->ptr ); + +/* Copy them into the returned buffer, including an extra character for + the terminating null. */ + data->buff = astStore( data->buff, data->ptr, nc + 1 ); + +/* Store the terminating null. */ + (data->buff)[ nc ] = 0; + +/* Update the pointer to the next character to read from the + serialisation. */ + data->ptr = nl ? nl + 1 : NULL; + +/* Return the buffer. */ + return data->buff; +} + +static void VSet( AstObject *this, const char *settings, char **text, + va_list args, int *status ) { +/* +*+ +* Name: +* astVSet + +* Purpose: +* Set values for an Object's attributes. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "object.h" +* void astVSet( AstObject *this, const char *settings, char **text, +* va_list args ) + +* Class Membership: +* Object method. + +* Description: +* This function assigns a set of attribute values for an Object, +* the attributes and their values being specified by means of a +* string containing a comma-separated list of the form: +* +* "attribute1 = value1, attribute2 = value2, ... " +* +* Here, "attribute" specifies an attribute name and the value to +* the right of each "=" sign should be a suitable textual +* representation of the value to be assigned to that +* attribute. This will be interpreted according to the attribute's +* data type. +* +* The string supplied may also contain "printf"-style format +* specifiers identified by a "%" sign in the usual way. If +* present, these will be substituted by values supplied as +* optional arguments (as a va_list variable argument list), using +* the normal "printf" rules, before the string is used. + +* Parameters: +* this +* Pointer to the Object. +* settings +* Pointer to a null-terminated string containing a +* comma-separated list of attribute settings. +* text +* Pointer to a location at which to return a pointer to dynamic +* memory holding a copy of the expanded setting string. This memory +* should be freed using astFree when no longer needed. If a NULL +* pointer is supplied, no string is created. +* args +* The variable argument list which contains values to be +* substituted for any "printf"-style format specifiers that +* appear in the "settings" string. + +* Notes: +* - Attribute names are not case sensitive. +* - White space may surround attribute names and will be ignored. +* - White space may also surround attribute values where it will +* be ignored (except for string-valued attributes where it is +* significant and forms part of the value to be assigned). +* - After this function has substituted values for "printf"-style +* format specifiers it splits the "settings" string into +* individual attribute settings which it passes one at a time to +* the protected astSetAttrib method (after removal of white space +* and conversion of attribute names to lower case). The +* astSetAttrib method should therefore be extended by derived +* classes which define new attributes, and this will allow the +* astVSet (and astSet) methods to have access to those attributes. +* - This function provides the same functionality as the astSet +* public method but accepts a va_list variable argument list +* instead of a variable number of arguments. It is provided for +* use by functions in other class implementations which accept a +* variable number of arguments and must therefore pass their +* argument list to this method in va_list form. +*- +*/ + +#define MIN_BUFF_LEN 1024 +#define ERRBUF_LEN 80 + +/* Local Variables: */ + char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */ + char setting_buf[ MIN_BUFF_LEN ]; /* Expanded "%s" settting string */ + char *dyn_buf; /* Pointer to dynamic buffer for expanded setting */ + char *errstat; /* Pointer to error message */ + char *assign; /* Pointer to assigment substring */ + char *assign_end; /* Pointer to null at end of assignment */ + char *buff1; /* Pointer to temporary string buffer */ + char *buff2; /* Pointer to temporary string buffer */ + char *buff3; /* Pointer to temporary string buffer */ + char *eq1; /* Pointer to 1st equals sign */ + int buff_len; /* Length of temporary buffer */ + int expanded; /* Has the Settings string been expanded yet? */ + int i; /* Loop counter for characters */ + int j; /* Offset for revised assignment character */ + int len; /* Length of settings string */ + int lo; /* Convert next character to lower case? */ + int nc; /* Number of vsprintf output characters */ + int quoted; /* Are we in a quoted string? */ + int stat; /* Value of errno after an error */ + int tq; /* Test if the next non-space is a quote? */ + +/* Initialise */ + if( text ) *text = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the length of the "settings" string and test it is not + zero. If it is, there is nothing more to do. */ + len = (int) strlen( settings ); + if ( len != 0 ) { + +/* If the setting string is just "%s" (with optional trailing and leading + white space) then the variable argument potentially contains more than + one attribute setting, in which case we expand the setting string now + and use the expanded string in place of the supplied string in the rest + of this function. */ + nc = 0; + sscanf( settings, " %%s %n", &nc ); + if( nc == len ) { + +/* Expand the supplied string using a fixed-length buffer. This writes at + most MIN_BUFF_LEN characters to "buf", but returns the number of + characters that would have been needed to write the whole string. */ + len = vsnprintf( setting_buf, sizeof(setting_buf), settings, args ); + +/* If the fixed-length buffer is too short, use a dynamically allocated + buffer instead. */ + if( len + 1 > MIN_BUFF_LEN ) { + dyn_buf = astMalloc( len + 1 ); + if( astOK ) { + len = vsnprintf( dyn_buf, len + 1, settings, args ); + settings = dyn_buf; + } + } else { + dyn_buf = NULL; + settings = setting_buf; + } + +/* Indicate that "settings" has been expanded. */ + expanded = 1; + + } else { + expanded = 0; + dyn_buf = NULL; + } + +/* Allocate memory and store a copy of the string. */ + buff1 = astStore( NULL, settings, (size_t) ( len + 1 ) ); + if ( astOK ) { + +/* Convert each comma in the string into '\n'. This is to distinguish + commas initially present from those introduced by the formatting to + be performed below. We only do this if there is more than one equals + sign in the setting string, since otherwise any commas are probably + characters contained within a string attribute value. Ignore commas + that occur within quoted strings. */ + eq1 = strchr( buff1, '=' ); + if( eq1 && strchr( eq1 + 1, '=' ) ) { + quoted = 0; + for ( i = 0; i < len; i++ ) { + if( !quoted ) { + if ( buff1[ i ] == ',' ) { + buff1[ i ] = '\n'; + } else if( buff1[ i ] == '"' ) { + quoted = 1; + } + } else if( buff1[ i ] == '"' ){ + quoted = 0; + } + } + } + +/* Calculate a size for a further buffer twice the size of the first + one. Ensure it is not less than a minimum size and then allocate + this buffer. */ + buff_len = 2 * len; + if ( buff_len < MIN_BUFF_LEN ) buff_len = MIN_BUFF_LEN; + buff2 = astMalloc( (size_t) ( buff_len + 1 ) ); + if ( astOK ) { + +/* Use "vsprintf" to substitute values for any format specifiers in + the "settings" string, writing the resulting string into the second + buffer. If the "settings" string has already been expanded, then just + copy it. */ + errno = 0; + if( !expanded ) { + nc = vsprintf( buff2, buff1, args ); + } else { + strcpy( buff2, buff1 ); + nc = strlen( buff1 ); + } + +/* Get a copy of the expanded string to return as the function value and + convert newlines back to commas. */ + if( text ) { + *text = astStore( NULL, buff2, nc + 1 ); + if( *text ) { + for ( i = 0; i <= nc; i++ ) { + if ( (*text)[ i ] == '\n' ) (*text)[ i ] = ','; + } + } + } + +/* The possibilities for error detection are limited here, but check + if an error value was returned and report an error. Include + information from errno if it was set. */ + if ( nc < 0 ) { + if( astOK ) { + stat = errno; + + if( stat ) { +#if HAVE_STRERROR_R + strerror_r( stat, errbuf, ERRBUF_LEN ); + errstat = errbuf; +#else + errstat = strerror( stat ); +#endif + } else { + *errbuf = 0; + errstat = errbuf; + } + + astError( AST__ATSER, "astVSet(%s): Error formatting an " + "attribute setting%s%s.", status, astGetClass( this ), + stat? " - " : "", errstat ); + astError( AST__ATSER, "The setting string was \"%s\".", status, + settings ); + } + +/* Also check that the result buffer did not overflow. If it did, + memory will probably have been corrupted but this cannot be + prevented with "vsprintf" (although we try and make the buffer + large enough). Report the error and abort. */ + } else if ( nc > buff_len ) { + if( astOK ) { + astError( AST__ATSER, "astVSet(%s): Internal buffer overflow " + "while formatting an attribute setting - the result " + "exceeds %d characters.", status, astGetClass( this ), + buff_len ); + astError( AST__ATSER, "The setting string was \"%s\".", status, + settings ); + } + +/* If all is OK, loop to process each formatted attribute assignment + (these are now separated by '\n' characters). */ + } else { + assign = buff2; + while ( assign ) { + +/* Change the '\n' at the end of each assignment to a null to + terminate it. */ + if ( ( assign_end = strchr( assign, '\n' ) ) ) { + *assign_end = '\0'; + } + +/* Before making the assignment, loop to remove white space and upper + case characters from the attribute name. */ + lo = 1; + tq = -1; + quoted = 0; + for ( i = j = 0; assign[ i ]; i++ ) { + +/* Note when an '=' sign is encountered (this signals the end of the + attribute name). */ + if ( assign[ i ] == '=' ) lo = 0; + +/* Before the '=' sign, convert all characters to lower case and move + everything to the left to eliminate white space. Afer the '=' sign, + copy all characters to their new location unchanged, except for any + delimiting quotes, which are removed. astSetC replaces commas in the + attribute value by '\r' characters. Reverse this now. */ + if ( !lo || !isspace( assign[ i ] ) ) { + if( assign[ i ] == '\r' ) { + assign[ j++ ] = ','; + + } else if( lo ) { + assign[ j++ ] = tolower( assign[ i ] ); + + } else { + assign[ j++ ] = assign[ i ]; + + if( tq > 0 && !isspace( assign[ i ] ) ) { + if( assign[ i ] == '"' ) { + quoted = 1; + j--; + } + tq = 0; + } + + } + } + +/* If the current character is the initial '=' sign, set "tq" positive, + meaning "check if the next non-space character is a quote". */ + if ( assign[ i ] == '=' && tq == -1 ) tq = 1; + } + +/* if the value was quoted. remove the trailing quote. */ + if( quoted ) { + j--; + while( isspace( assign[ j ] ) ) j--; + if( assign[ j ] == '"' ) j--; + j++; + } + +/* Terminate the revised assignment string and pass it to astSetAttrib + to make the assignment (unless the string was all blank, in which + case we ignore it). */ + assign[ j ] = '\0'; + if ( j ) { + +/* If there are no characters to the right of the equals sign append a + space after the equals sign. Without this, a string such as "Title=" + would not be succesfully matched against the attribute name "Title" + within SetAttrib. */ + if( assign[ j - 1 ] == '=' ) { + buff3 = astStore( NULL, assign, + (size_t) j + 2 ); + if ( astOK ) { + buff3[ j ] = ' '; + buff3[ j + 1 ] = '\0'; + astSetAttrib( this, buff3 ); + } + buff3 = astFree( buff3 ); + + } else { + astSetAttrib( this, assign ); + } + } + +/* Check for errors and abort if any assignment fails. Otherwise, + process the next assignment substring. */ + if ( !astOK ) break; + assign = assign_end ? assign_end + 1 : NULL; + } + } + } + +/* Free the memory allocated for string buffers. */ + buff2 = astFree( buff2 ); + dyn_buf = astFree( dyn_buf ); + } + buff1 = astFree( buff1 ); + } +} +#undef ERRBUF_LEN +#undef MIN_BUFF_LEN + +/* Attribute access functions. */ +/* --------------------------- */ +/* +*att++ +* Name: +* Class + +* Purpose: +* Object class name. + +* Type: +* Public attribute. + +* Synopsis: +* Character string, read-only. + +* Description: +* This attribute gives the name of the class to which an Object +* belongs. + +* Applicability: +* Object +* All Objects have this attribute. +*att-- +*/ + +/* +*att++ +* Name: +* ID + +* Purpose: +* Object identification string. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute contains a string which may be used to identify +* the Object to which it is attached. There is no restriction on +* the contents of this string, which is not used internally by the +* AST library, and is simply returned without change when +* required. The default value is an empty string. +* +* An identification string can be valuable when, for example, +c several Objects have been stored in a file (using astWrite) and +f several Objects have been stored in a file (using AST_WRITE) and +c are later retrieved (using astRead). Consistent use of the ID +f are later retrieved (using AST_READ). Consistent use of the ID +* attribute allows the retrieved Objects to be identified without +* depending simply on the order in which they were stored. +* +* This attribute may also be useful during debugging, to +c distinguish similar Objects when using astShow to display them. +f distinguish similar Objects when using AST_SHOW to display them. + +* Applicability: +* Object +* All Objects have this attribute. + +* Notes: +* - Unlike most other attributes, the value of the ID attribute is +* not transferred when an Object is copied. Instead, its value is +* undefined (and therefore defaults to an empty string) in any +* copy. However, it is retained in any external representation of +c an Object produced by the astWrite function. +f an Object produced by the AST_WRITE routine. +*att-- +*/ +/* Clear the ID value by freeing the allocated memory and assigning a + NULL pointer. */ +astMAKE_CLEAR(Object,ID,id,astFree( this->id )) + +/* If the ID value is not set, supply a default in the form of a + pointer to the constant string "". */ +astMAKE_GET(Object,ID,const char *,NULL,( this->id ? this->id : "" )) + +/* Set an ID value by freeing any previously allocated memory, + allocating new memory and storing the string. */ +astMAKE_SET(Object,ID,const char *,id,astStore( this->id, value, + strlen( value ) + (size_t) 1 )) + +/* The ID value is set if the pointer to it is not NULL. */ +astMAKE_TEST(Object,ID,( this->id != NULL )) + +/* +*att++ +* Name: +* Ident + +* Purpose: +* Permanent Object identification string. + +* Type: +* Public attribute. + +* Synopsis: +* String. + +* Description: +* This attribute is like the ID attribute, in that it contains a +* string which may be used to identify the Object to which it is +* attached. The only difference between ID and Ident is that Ident +* is transferred when an Object is copied, but ID is not. + +* Applicability: +* Object +* All Objects have this attribute. + +*att-- +*/ +/* Clear the Ident value by freeing the allocated memory and assigning a + NULL pointer. */ +astMAKE_CLEAR(Object,Ident,ident,astFree( this->ident )) + +/* If the Ident value is not set, supply a default in the form of a + pointer to the constant string "". */ +astMAKE_GET(Object,Ident,const char *,NULL,( this->ident ? this->ident : "" )) + +/* Set an Ident value by freeing any previously allocated memory, + allocating new memory and storing the string. */ +astMAKE_SET(Object,Ident,const char *,ident,astStore( this->ident, value, + strlen( value ) + (size_t) 1 )) + +/* The Ident value is set if the pointer to it is not NULL. */ +astMAKE_TEST(Object,Ident,( this->ident != NULL )) + +/* +*att++ +* Name: +* UseDefs + +* Purpose: +* Use default values for unspecified attributes? + +* Type: +* Public attribute. + +* Synopsis: +* Integer (boolean). + +* Description: +* This attribute specifies whether default values should be used +* internally for object attributes which have not been assigned a +* value explicitly. If a non-zero value (the default) is supplied for +* UseDefs, then default values will be used for attributes which have +* not explicitly been assigned a value. If zero is supplied for UseDefs, +* then an error will be reported if an attribute for which no explicit +* value has been supplied is needed internally within AST. +* +* Many attributes (including the UseDefs attribute itself) are unaffected +* by the setting of the UseDefs attribute, and default values will always +* be used without error for such attributes. The "Applicability:" section +* below lists the attributes which are affected by the setting of UseDefs. + +* Note, UseDefs only affects access to attributes internally within +* AST. The public accessor functions such as +c astGetC +f AST_GETC +* is unaffected by the UseDefs attribute - default values will always +* be returned if no value has been set. Application code should use the +c astTest +f AST_TEST +* function if required to determine if a value has been set for an +* attribute. + +* Applicability: +* Object +* All Objects have this attribute, but ignore its setting except +* as described below for individual classes. +* FrameSet +* The default value of UseDefs for a FrameSet is redefined to be +* the UseDefs value of its current Frame. +* CmpFrame +* The default value of UseDefs for a CmpFrame is redefined to be +* the UseDefs value of its first component Frame. +* Region +* The default value of UseDefs for a Region is redefined to be +* the UseDefs value of its encapsulated Frame. +* Frame +* If UseDefs is zero, an error is reported when aligning Frames if the +* Epoch, ObsLat or ObsLon attribute is required but has not been +* assigned a value explicitly. +* SkyFrame +* If UseDefs is zero, an error is reported when aligning SkyFrames +* if any of the following attributes are required but have not been +* assigned a value explicitly: Epoch, Equinox. +* SpecFrame +* If UseDefs is zero, an error is reported when aligning SpecFrames +* if any of the following attributes are required but have not been +* assigned a value explicitly: Epoch, RefRA, RefDec, RestFreq, +* SourceVel, StdOfRest. +* DSBSpecFrame +* If UseDefs is zero, an error is reported when aligning DSBSpecFrames +* or when accessing the ImagFreq attribute if any of the following +* attributes are required but have not been assigned a value explicitly: +* Epoch, DSBCentre, IF. +*att-- +*/ +astMAKE_CLEAR(Object,UseDefs,usedefs,CHAR_MAX) +astMAKE_GET(Object,UseDefs,int,1,((this->usedefs!=CHAR_MAX)?this->usedefs:1)) +astMAKE_SET(Object,UseDefs,int,usedefs,((value)?1:0)) +astMAKE_TEST(Object,UseDefs,(this->usedefs!=CHAR_MAX)) + +/* +*att++ +* Name: +* Nobject + +* Purpose: +* Number of Objects in class. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute gives the total number of Objects currently in +* existence in the same class as the Object whose attribute value +* is requested. This count does not include Objects which belong +* to derived (more specialised) classes. +* +* This attribute is mainly intended for debugging. It can be used +* to detect whether Objects which should have been deleted have, +* in fact, been deleted. + +* Applicability: +* Object +* All Objects have this attribute. +*att-- +*/ + +/* +*att++ +* Name: +* ObjSize + +* Purpose: +* The in-memory size of the Object. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute gives the total number of bytes of memory used by +* the Object. This includes any Objects which are encapsulated within +* the supplied Object. + +* Applicability: +* Object +* All Objects have this attribute. +*att-- +*/ + +/* +*att++ +* Name: +* RefCount + +* Purpose: +* Count of active Object pointers. + +* Type: +* Public attribute. + +* Synopsis: +* Integer, read-only. + +* Description: +* This attribute gives the number of active pointers associated +* with an Object. It is modified whenever pointers are created or +c annulled (by astClone, astAnnul or astEnd for example). The count +f annulled (by AST_CLONE, AST_ANNUL or AST_END for example). The count +* includes the initial pointer issued when the Object was created. +* +* If the reference count for an Object falls to zero as the result +* of annulling a pointer to it, then the Object will be deleted. + +* Applicability: +* Object +* All Objects have this attribute. +*att-- +*/ + +/* Standard class functions. */ +/* ========================= */ +/* +*+ +* Name: +* astCheck<Class> + +* Purpose: +* Validate class membership. + +* Type: +* Protected function. + +* Synopsis: +* #include "class.h" +* Ast<Class> *astCheck<Class>( Ast<Class> *this ) + +* Class Membership: +* <Class> class function. + +* Description: +* This function validates membership of the class called <Class>, +* or of any class derived from it. If the Object is not a member, +* or the pointer supplied does not identify a valid Object, an +* error is reported and the global error status is set to +* AST__OBJIN. + +* Parameters: +* this +* Pointer to the Object. + +* Returned Value: +* The function always returns a copy of the "this" pointer +* (whether it finds it valid or not). + +* Notes: +* - Each class provides a function (astCheck<Class>) of this form, +* where <Class> and <class> are replaced by the class name (with +* appropriate capitalisation). +* - Normal error status checking is performed, so this function +* will not execute if the global error status is set on entry (the +* usual function value will be returned, however). +* - This function is primarily intended for validating Object +* pointers passed to member functions as part of a class +* interface. +*- +*/ + +/* Implement the astCheckObject function using the macro defined for this + purpose in the "object.h" header file. */ +astMAKE_CHECK(Object) + +int astIsAObject_( const AstObject *this, int *status ) { +/* +*++ +* Name: +c astIsA<Class> +f AST_ISA<CLASS> + +* Purpose: +* Test membership of a class by an Object. + +* Type: +* Public function. + +* Synopsis: +c #include "class.h" +c int astIsA<Class>( const Ast<Class> *this ) +f RESULT = AST_ISA<CLASS>( THIS, STATUS ) + +* Class Membership: +* Class function. + +* Description: +* This is a family of functions which test whether an Object is a +c member of the class called <Class>, or of any class derived from +f member of the class called <CLASS>, or of any class derived from +* it. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Pointer to the Object. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Returned Value: +c astIsA<Class>() +c One if the Object belongs to the class called <Class> (or to a +c class derived from it), otherwise zero. +f AST_ISA<CLASS> = LOGICAL +f .TRUE. if the Object belongs to the class called <CLASS> (or to +f a class derived from it), otherwise .FALSE.. + +* Applicability: +* Object +* These functions apply to all Objects. + +* Examples: +c member = astIsAFrame( obj ); +c Tests whether Object "obj" is a member of the Frame class, or +c of any class derived from a Frame. +f MEMBER = AST_ISAFRAME( OBJ, STATUS ); +f Tests whether Object OBJ is a member of the Frame class, or +f of any class derived from a Frame. + +* Notes: +c - Every AST class provides a function (astIsA<Class>) of this +c form, where <Class> should be replaced by the class name. +f - Every AST class provides a function (AST_ISA<CLASS>) of this +f form, where <CLASS> should be replaced by the class name. +c - This function attempts to execute even if the AST error status +c is set +f - This function attempts to execute even if STATUS is set to an +f error value +* on entry, although no further error report will be made +* if it subsequently fails under these circumstances. +c - A value of zero will be returned if this function should fail +f - A value of .FALSE. will be returned if this function should fail +* for any reason. In particular, it will fail if the pointer +* supplied does not identify an Object of any sort. +*-- +*/ + +/* Local Variables: */ + int valid; /* Valid object? */ + +/* Since this is the base class, the implementation of this function + differs from that in derived classes (in that it fails and + potentially reports an error if the returned result is zero). */ + +/* Initialise. */ + valid = 0; + +/* Check if a NULL pointer was supplied (this can never be valid). If + OK, check if the Object contains the correct "magic number" in its + check field. */ + if ( !this || ( this->check != Magic( this, this->size, status ) ) ) { + +/* If it is not a valid Object, then report an error (but only if the + global error status has not already been set). */ + if ( astOK ) { + astError( AST__OBJIN, "astIsAObject(%s): Invalid Object pointer " + "given (points at address %p).", status, astGetClass( this ), + (void *) this ); + } + +/* Otherwise, note that the Object is valid. */ + } else { + valid = 1; + } + +/* Return the result. */ + return valid; +} + +void astInitObjectVtab_( AstObjectVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitObjectVtab + +* Purpose: +* Initialise a virtual function table for a Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* void astInitObjectVtab( AstObjectVtab *vtab, const char *name ) + +* Class Membership: +* Object vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the Object class. + +* Parameters: +* vtab +* Pointer to the virtual function table. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the virtual function table belongs (it +* is this pointer value that will subsequently be returned by the Object +* astClass function). +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int ivtab; /* Index of next entry in known_vtabs */ + +/* Check the local error status. */ + if ( !astOK ) return; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Initialise the contents of the class identifier. Since this is the + base class, we assign null values to the fields. */ + vtab->id.check = NULL; + vtab->id.parent = NULL; + +/* Store pointers to the member functions (implemented here) that provide + virtual methods for this class. */ + vtab->Clear = Clear; + vtab->ClearAttrib = ClearAttrib; + vtab->ClearID = ClearID; + vtab->ClearIdent = ClearIdent; + vtab->Dump = Dump; + vtab->Equal = Equal; + vtab->GetAttrib = GetAttrib; + vtab->GetID = GetID; + vtab->GetIdent = GetIdent; + vtab->HasAttribute = HasAttribute; + vtab->Same = Same; + vtab->SetAttrib = SetAttrib; + vtab->SetID = SetID; + vtab->SetIdent = SetIdent; + vtab->Show = Show; + vtab->TestAttrib = TestAttrib; + vtab->TestID = TestID; + vtab->TestIdent = TestIdent; + vtab->EnvSet = EnvSet; + vtab->VSet = VSet; + vtab->Cast = Cast; + vtab->GetObjSize = GetObjSize; + vtab->CleanAttribs = CleanAttribs; + + vtab->TestUseDefs = TestUseDefs; + vtab->SetUseDefs = SetUseDefs; + vtab->ClearUseDefs = ClearUseDefs; + vtab->GetUseDefs = GetUseDefs; + +#if defined(THREAD_SAFE) + vtab->ManageLock = ManageLock; +#endif + +/* Store the pointer to the class name. */ + vtab->class = name; + +/* Initialise the count of active objects and the number of destructors, + copy constructors and dump functions. */ + vtab->nobject = 0; + vtab->ndelete = 0; + vtab->ncopy = 0; + vtab->ndump = 0; + +/* Initialise the arrays of destructor, copy constructor and dump + function pointers. */ + vtab->delete = NULL; + vtab->copy = NULL; + vtab->dump = NULL; + vtab->dump_class = NULL; + vtab->dump_comment = NULL; + +/* Initialise the default attributes to use when creating objects. */ + vtab->defaults = NULL; + +/* The virtual function table for each class contains a list of pointers + to memory blocks which have previously been used to store an Object of + the same class, but which have since been deleted using astDelete. + These memory blocks are free to be re-used when a new Object of the + same class is initialised. This saves on the overheads associated with + continuously allocating small blocks of memory using malloc. */ + vtab->nfree = 0; + vtab->free_list = NULL; + +/* Add the supplied virtual function table pointer to the end of the list + of known vtabs. */ + ivtab = nvtab++; + + astBeginPM; + known_vtabs = astGrow( known_vtabs, nvtab, sizeof( AstObjectVtab *) ); + astEndPM; + + if( astOK && known_vtabs ) known_vtabs[ ivtab ] = vtab; + +/* Fill a pointer value with zeros (not necessarily the same thing as a + NULL pointer) for subsequent use. */ + (void) memset( &zero_ptr, 0, sizeof( AstObject * ) ); + +/* If we have just initialised the vtab for the current class, indicate + that the vtab is now initialised. */ + if( vtab == &class_vtab ) class_init = 1; +} + +AstObject *astInitObject_( void *mem, size_t size, int init, + AstObjectVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitObject + +* Purpose: +* Initialise an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astInitObject( void *mem, size_t size, int init, +* AstObjectVtab *vtab, const char *name ) + +* Class Membership: +* Object initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new Object. It allocates memory (if necessary) to accommodate the +* Object plus any additional data associated with the derived class. It +* then initialises an Object structure at the start of this memory. If the +* "init" flag is set, it also initialises the contents of a virtual +* function table for an Object at the start of the memory passed via the +* "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the Object is to be initialised. +* This must be of sufficient size to accommodate the Object data +* (sizeof(Object)) plus any data used by the derived class. If a value +* of NULL is given, this function will allocate the memory itself using +* the "size" parameter to determine its size. +* size +* The amount of memory used by the Object (plus derived class data). +* This will be used to allocate memory if a value of NULL is given for +* the "mem" parameter. This value is also stored in the Object +* structure, so a valid value must be supplied even if not required for +* allocating memory. +* init +* A logical flag indicating if the Object's virtual function table is +* to be initialised. If this value is non-zero, the virtual function +* table will be initialised by this function. +* vtab +* Pointer to the start of the virtual function table to be associated +* with the new Object. +* name +* Pointer to a constant null-terminated character string which contains +* the name of the class to which the new object belongs (it is this +* pointer value that will subsequently be returned by the Object +* astClass function). + +* Returned Value: +* A pointer to the new Object. + +* Notes: +* - A null pointer will be returned if this function is invoked with the +* global error status set, or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstObject *new; /* Pointer to new Object */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Determine if memory must be allocated dynamically. If so, use the last + block of memory in the list of previously allocated but currently + unused blocks identified by the vtab "free_list" array, reducing the + length of the free list by one, and nullifying the entry in the list + for safety. If the list is originally empty, allocate memory for a new + object using astMalloc. */ + if( !mem ) { + if( object_caching ) { + if( vtab->nfree > 0 ) { + mem = vtab->free_list[ --(vtab->nfree) ]; + vtab->free_list[ vtab->nfree ] = NULL; + if( astSizeOf( mem ) != size && astOK ) { + astError( AST__INTER, "astInitObject(%s): Free block has size " + "%d but the %s requires %d bytes (internal AST " + "programming error).", status, vtab->class, + (int) astSizeOf( mem ), vtab->class, (int) size ); + } + } else { + mem = astMalloc( size ); + } + + } else { + mem = astMalloc( size ); + } + +/* If memory had already been allocated, adjust the "size" value to match + the size of the allocated memory. */ + } else { + size = astSizeOf( mem ); + } + +/* Obtain a pointer to the new Object. */ + if ( astOK ) { + new = (AstObject *) mem; + +/* Zero the entire new Object structure (to prevent accidental re-use + of any of its values after deletion). */ + (void) memset( new, 0, size ); + +/* If necessary, initialise the virtual function table. */ +/* ---------------------------------------------------- */ + if ( init ) astInitObjectVtab( vtab, name ); + if( astOK ) { + +/* Initialise the Object data. */ +/* --------------------------- */ +/* Store a unique "magic" value in the Object structure. This will be + used (e.g. by astIsAObject) to determine if a pointer identifies a + valid Object. Note that this differs from the practice in derived + classes, where this number is stored in the virtual function + table. We take a different approach here so that we need not follow + a pointer to the virtual function table obtained from a structure + that hasn't yet been validated as an Object. This minimises the + risk of a memory access violation. */ + new->check = Magic( new, size, status ); + +/* Associate the Object with its virtual function table. */ + new->vtab = vtab; + +/* Store the Object size and note if its memory was dynamically allocated. */ + new->size = size; + new->dynamic = astIsDynamic( new ); + +/* Initialise the reference count (of Object pointers in use). */ + new->ref_count = 1; + +/* Initialise the ID strings. */ + new->id = NULL; + new->ident = NULL; + +/* Use default values for unspecified attributes. */ + new->usedefs = CHAR_MAX; + +/* Increment the count of active Objects in the virtual function table. + Use the count as a unique identifier (unique within the class) for + the Object. */ + new->iref = vtab->nobject++; + +/* Initialise the pointer to an external object that acts as a proxy for + the AST Object within foreign language interfaces. */ + new->proxy = NULL; + } + +/* If an error occurred, clean up by deleting the new Object. Otherwise + lock the object for use by the currently executing thread. */ + if ( !astOK ) { + new = astDelete( new ); + +#ifdef THREAD_SAFE + } else { + if( pthread_mutex_init( &(new->mutex1), NULL ) != 0 && astOK ) { + astError( AST__INTER, "astInitObject(%s): Failed to " + "initialise POSIX mutex1 for the new Object.", status, + vtab->class ); + } + if( pthread_mutex_init( &(new->mutex2), NULL ) != 0 && astOK ) { + astError( AST__INTER, "astInitObject(%s): Failed to " + "initialise POSIX mutex2 for the new Object.", status, + vtab->class ); + } + new->locker = -1; + new->globals = NULL; + (void) ManageLock( new, AST__LOCK, 0, NULL, status ); + if( !astOK ) new = astDelete( new ); +#endif + } + } + +/* Return a pointer to the new Object. */ + return new; +} + +AstObject *astLoadObject_( void *mem, size_t size, + AstObjectVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadObject + +* Purpose: +* Load an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astLoadObject( void *mem, size_t size, +* AstObjectVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* Object loader. + +* Description: +* This function is provided to load a new Object using data read +* from a Channel, and to allocate memory for it if necessary. +* +* If the "init" flag is set, it also initialises the contents of a +* virtual function table for an Object at the start of the memory +* passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory into which the Object is to be +* loaded. This must be of sufficient size to accommodate the +* Object data (sizeof(Object)) plus any data used by derived +* classes. If a value of NULL is given, this function will +* allocate the memory itself using the "size" parameter to +* determine its size. +* size +* The amount of memory used by the Object (plus derived class +* data). This will be used to allocate memory if a value of +* NULL is given for the "mem" parameter. This value is also +* stored in the Object structure, so a valid value must be +* supplied even if not required for allocating memory. +* +* If the "vtab" parameter is NULL, the "size" value is ignored +* and sizeof(AstObject) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new Object. If this is NULL, a pointer to +* the (static) virtual function table for the Object class is +* used instead. +* name +* Pointer to a constant null-terminated character string which +* contains the name of the class to which the new Object +* belongs (it is this pointer value that will subsequently be +* returned by the astGetClass method). +* +* If the "vtab" parameter is NULL, the "name" value is ignored +* and a pointer to the string "Object" is used instead. + +* Returned Value: +* A pointer to the new Object. + +* Notes: +* - A null pointer will be returned if this function is invoked +* with the global error status set, or if it should fail for any +* reason. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + AstObject *new; /* Pointer to the new Object */ + +/* Initialise. */ + new = NULL; + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(channel); + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this Object. In this case the + Object belongs to this class, so supply appropriate values for + initialising it and its virtual function table. */ + if ( !vtab ) { + size = sizeof( AstObject ); + vtab = &class_vtab; + name = "Object"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitObjectVtab( vtab, name ); + class_init = 1; + } + } + +/* There is no parent class to load, so simply initialise a new Object + structure as if a new Object were being created from scratch. */ + new = astInitObject( mem, size, 0, vtab, name ); + if ( astOK ) { + +/* Read input data. */ +/* ================ */ +/* Request the input Channel to read all the input data appropriate to + this class into the internal "values list". */ + astReadClassData( channel, "Object" ); + +/* Now read each individual data item from this list and use it to + initialise the appropriate instance variable(s) for this class. */ + new->id = astReadString( channel, "id", NULL ); + new->ident = astReadString( channel, "ident", NULL ); + new->usedefs = astReadInt( channel, "usedfs", CHAR_MAX ); + +/* We simply read the values for the read-only attributes (just in + case they've been un-commented in the external representation) and + discard them. This prevents any possibility of error due to un-read + input. */ + (void) astReadInt( channel, "refcnt", 0 ); + (void) astReadInt( channel, "nobj", 0 ); + +/* Initialise the pointer to an external object that acts as a proxy for + the AST Object within foreign language interfaces. */ + new->proxy = NULL; + +/* If an error occurred, clean up by deleting the new Object. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new Object pointer. */ + return new; +} + +/* Virtual function interfaces. */ +/* ============================ */ +/* These provide the external interface to the virtual functions defined by + this class. Each simply checks the global error status and then locates and + executes the appropriate member function, using the function pointer stored + in the object's virtual function table (this pointer is located using the + astMEMBER macro defined in "object.h"). + + Note that the member function may not be the one defined here, as it may + have been over-ridden by a derived class. However, it should still have the + same interface. */ +void astClear_( AstObject *this, const char *attrib, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,Clear))( this, attrib, status ); +} +void astClearAttrib_( AstObject *this, const char *attrib, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,ClearAttrib))( this, attrib, status ); +} +void astDump_( AstObject *this, AstChannel *channel, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,Dump))( this, channel, status ); +} + +#if defined(THREAD_SAFE) +int astManageLock_( AstObject *this, int mode, int extra, AstObject **fail, + int *status ) { + if( !this ) return 0; + return (**astMEMBER(this,Object,ManageLock))( this, mode, extra, fail, status ); +} +#endif + +int astEqual_( AstObject *this, AstObject *that, int *status ) { + if ( !astOK ) return 0; + if( this == that ) return 1; + return (**astMEMBER(this,Object,Equal))( this, that, status ); +} +const char *astGetAttrib_( AstObject *this, const char *attrib, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Object,GetAttrib))( this, attrib, status ); +} +void astSetAttrib_( AstObject *this, const char *setting, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,SetAttrib))( this, setting, status ); +} +void astShow_( AstObject *this, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,Show))( this, status ); +} +int astTestAttrib_( AstObject *this, const char *attrib, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Object,TestAttrib))( this, attrib, status ); +} +void astEnvSet_( AstObject *this, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,EnvSet))( this, status ); +} +void astVSet_( AstObject *this, const char *settings, char **text, va_list args, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,VSet))( this, settings, text, args, status ); +} +int astGetObjSize_( AstObject *this, int *status ) { + if ( !astOK || !this ) return 0; + return (**astMEMBER(this,Object,GetObjSize))( this, status ); +} +void astCleanAttribs_( AstObject *this, int *status ) { + if ( !astOK ) return; + (**astMEMBER(this,Object,CleanAttribs))( this, status ); +} +AstObject *astCast_( AstObject *this, AstObject *obj, int *status ) { + if ( !astOK ) return NULL; + return (**astMEMBER(this,Object,Cast))( this, obj, status ); +} +int astSame_( AstObject *this, AstObject *that, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Object,Same))( this, that, status ); +} +int astHasAttribute_( AstObject *this, const char *attrib, int *status ) { + if ( !astOK ) return 0; + return (**astMEMBER(this,Object,HasAttribute))( this, attrib, status ); +} + +/* External interface. */ +/* =================== */ +/* The following relates to the external interface to Objects and not + specifically to the implementation of the Object class itself + (although it contains external functions which replace the internal + versions defined earlier). */ + + +/* Type Definitions. */ +/* ----------------- */ +/* Define the Handle structure. This is attached to Objects when they + are accessed via the public (external, user-callable) interface. + Handles provide a buffer between the Object identifiers issued to + external users and the naked C pointers used to handle Objects + internally. They also implement the context levels used by + astBegin, astEnd, astExempt and astExport (which are only available + to external users). */ +typedef struct Handle { + AstObject *ptr; /* C Pointer to the associated Object */ + int context; /* Context level for this Object */ + int check; /* Check value to ensure validity */ + +#if defined(THREAD_SAFE) + int thread; /* Identifier for owning thread */ +#endif + +#if defined(MEM_DEBUG) + int id; /* The id associated with the memory block + holding the Object last associated with + this handle. */ + AstObjectVtab *vtab; /* Pointer to the firtual function table of + the Object last associated with this + handle. */ +#endif + +/* Links between Handles are implemented using integer offsets rather + than through pointers. */ + int flink; /* Backward link to previous Handle */ + int blink; /* Forward link to next Handle */ +} Handle; + +/* Define a union with an overlapping int and AstObject*. This is used + to transfer an integer bit pattern into and out of a pointer + variable used to store an Object identifier. This avoids any + implementation dependent aspects of integer-to-pointer + conversions. */ +typedef union IdUnion { + AstObject *pointer; + int integer; +} IdUnion; + +/* Define a union which allows a bit pattern to be accessed as a + signed or unsigned int. */ +typedef union MixedInts { + int i; + unsigned u; +} MixedInts; + +/* Static Variables. */ +/* ----------------- */ +/* The array of Handle structures is a pool of resources available to all + threads. Each thread has its own conext level and its own "active_handles" + array to identify the first Handle at each context level. */ +static Handle *handles = NULL; /* Pointer to allocated array of Handles */ +static int free_handles = -1; /* Offset to head of free Handle list */ +static int nhandles = 0; /* Number of Handles in "handles" array */ +static unsigned nids = 0; /* Number of IDs issued to external users */ + +#if defined(THREAD_SAFE) +static int unowned_handles = -1; /* Offset to head of unowned Handle + list. In a single threaded environment, + all handles must be owned by a thread. */ +#endif + +#ifdef MEM_DEBUG +static int Watched_Handle = -1; /* A handle index to be watched. Activity + on this handle is reported using + astHandleUse and astHandleAlarm. */ +#endif + + +/* External Interface Function Prototypes. */ +/* --------------------------------------- */ +/* MYSTATIC should normally be set to "static" to make the following + function have local symbols. But it may be set to blank for debugging + purposes in order to enable these functions to appear in a backtrace + such as produced by the astBacktrace function. */ +#define MYSTATIC + +/* Private functions associated with the external interface. */ +MYSTATIC AstObject *AssocId( int, int * ); +MYSTATIC int CheckId( AstObject *, int, int * ); +MYSTATIC void AnnulHandle( int, int * ); +MYSTATIC void InitContext( int * ); +MYSTATIC void InsertHandle( int, int *, int * ); +MYSTATIC void RemoveHandle( int, int *, int * ); + +#if defined(MEM_DEBUG) +MYSTATIC void CheckList( int *, int * ); +MYSTATIC void CheckInList( int, int *, int, int * ); +MYSTATIC int CheckThread( int, int *, int * ); +MYSTATIC const char *HandleString( int, char * ); +MYSTATIC const char *HeadString( int *, char * ); +#endif + + +/* The following functions have public prototypes only (i.e. no + protected prototypes), so we must provide local prototypes for use + within this module. */ +AstObject *astDeleteId_( AstObject *, int * ); +void astBegin_( void ); +void astEnd_( int * ); +void astExemptId_( AstObject *, int * ); +void astImportId_( AstObject *, int * ); +void astSetId_( void *, const char *, ... ); +void astLockId_( AstObject *, int, int * ); +void astUnlockId_( AstObject *, int, int * ); + + +/* External Interface Functions. */ +/* ----------------------------- */ +MYSTATIC void AnnulHandle( int ihandle, int *status ) { +/* +* Name: +* AnnulHandle + +* Purpose: +* Annul a Handle and its associated Object pointer. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* void AnnulHandle( int ihandle, int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function annuls an active Handle by annulling the Object +* pointer it is associated with, deactivating the Handle and +* returning it to the free Handle list for re-use. +* +* The reference count for the associated Object is decremented by +* this function and the Object will be deleted if this reference +* count falls to zero. + +* Parameters: +* ihandle +* Array offset that identifies the Handle to be annulled in the +* "handles" array. This is fully validated by this function. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The Handle supplied should be active, otherwise an error will +* result and the Handle will not be touched (but no error report +* will be made if the global error status has already been set). +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + AstObject *ptr; /* Object pointer */ + int context; /* Context level where Handle was issued */ + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Check that the handle offset supplied is valid and report an error + if it is not (but only if the global error status has not already + been set). */ + if ( ( ihandle < 0 ) || ( ihandle >= nhandles ) ) { + if ( astOK ) { + astError( AST__INHAN, "astAnnulHandle: Invalid attempt to annul an " + "Object Handle (no. %u).", status, ihandle ); + astError( AST__INHAN, "This Handle number is not valid (possible " + "internal programming error)." , status); + } + +/* If OK, obtain the Handle's context level. */ + } else { + context = handles[ ihandle ].context; + +/* If this indicates that the Handle isn't active, then report an + error (but only if the global error status has not already been + set). We allow handles that are currently not owned by any thread to + be annulled. */ + if ( context < 0 && context != UNOWNED_CONTEXT ) { + if ( astOK ) { + astError( AST__INHAN, "astAnnulHandle: Invalid attempt to annul " + "an Object Handle (no. %u).", status, ihandle ); + astError( AST__INHAN, "This Handle is not active (possible " + "internal programming error)." , status); + } + +/* If the Handle is active, annul its Object pointer. The astAnnul + function may call Delete functions supplied by any class, and these + Delete functions may involve annulling external Object IDs, which in + turn requires access to the handles array. For this reason, we release + the mutex that protects access to the handles arrays so that it can + potentially be re-aquired within astAnnul without causing deadlock. */ + } else { + +#ifdef MEM_DEBUG + astHandleUse( ihandle, "annulled using check value %d ", + handles[ ihandle ].check ); +#endif + + ptr = handles[ ihandle ].ptr; + UNLOCK_MUTEX2; + ptr = astAnnul( ptr ); + LOCK_MUTEX2; + +/* Remove the Handle from the active list for its context level. */ + if( context == UNOWNED_CONTEXT ) { + +#if defined(THREAD_SAFE) + RemoveHandle( ihandle, &unowned_handles, status ); +#else + if( astOK ) astError( AST__INTER, "AnnulHandle: reference to " + "'unowned_handles' in a non-thread-safe context " + "(internal AST programming error).", status ); +#endif + + } else if( active_handles ) { + RemoveHandle( ihandle, &active_handles[ context ], status ); + + } else if( astOK ){ + astError( AST__INTER, "AnnulHandle: active_handles array has " + "not been initialised (internal AST programming error).", + status ); + } + +/* Reset the Handle's "context" value (making it inactive) and its "check" + value (so it is no longer associated with an identifier value). */ + handles[ ihandle ].ptr = NULL; + handles[ ihandle ].context = INVALID_CONTEXT; + handles[ ihandle ].check = 0; +#if defined(THREAD_SAFE) + handles[ ihandle ].thread = -1; +#endif + +/* Place the modified Handle on the free Handles list ready for re-use. */ + InsertHandle( ihandle, &free_handles, status ); + + } + } +} + +AstObject *astAnnulId_( AstObject *this_id, int *status ) { +/* +*+ +* Name: +* AnnulId + +* Purpose: +* Annul an external Object identifier. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astAnnulId( AstObject *this ) + +* Class Membership: +* Object member function. + +* Description: +* This function implements the external (public) interface to the +* astAnnul method. It accepts an active Object identifier, which +* it annuls, causing the associated Handle to be deactivated and +* returned to the free Handles list for re-use. It also causes the +* reference count of the associated Object to be decremented and +* the Object to be deleted if this reference count falls to zero. + +* Parameters: +* this +* The Object identifier to be annulled. + +* Returned Value: +* A NULL C pointer is always returned (this should be translated +* into an identifier value of zero for external use). + +* Notes: +* - This function attempts to execute even if the global error +* status is set. +* - The identifier supplied should be associated with an active +* Object, otherwise an error will result (but no error report will +* be made if the global error status has already been set). +* - This function is invoked via the astAnnul macro for external use. +* For internal use (from protected code which needs to handle external +* IDs) it should be invoked via the astAnnulId macro (since astAnnul +* expects a true C pointer as its argument when used internally). +*- +*/ + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object (this generates an + error if it doesn't). Note, we use "astMakePointer_NoLockCheck", + rather than the usual "astMakePointer" since a thread should be able + to renounce interest in an object without needing to own the object. + If we used "astMakePointer" then a thread could not annul a pointer + unless it owned the object. But having annulled the pointer it could + then not unlock the object for use by another thread (since the + pointer it would need to do this has just been annulled). */ + if ( !astIsAObject( astMakePointer_NoLockCheck( this_id ) ) ) return NULL; + +/* Obtain the Handle offset for this Object and annul the Handle and + its associated Object pointer. Report an error if the handle is + currently owned by a different thread. That is, the *Object* need + not be locked by the current thread (as indicated by the use of + astMakePointer above), but the *handle* must be owned by the current + thread. */ + LOCK_MUTEX2; + AnnulHandle( CheckId( this_id, 1, status ), status ); + UNLOCK_MUTEX2; + +/* Always return a NULL pointer value. */ + return NULL; +} + +MYSTATIC AstObject *AssocId( int ihandle, int *status ) { +/* +* Name: +* AssocId + +* Purpose: +* Associate an identifier value with a Handle. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *AssocId( int ihandle ) + +* Class Membership: +* Object member function. + +* Description: +* This function takes a zero-based offset into the "handles" array +* that identifies a Handle associated with an active Object. It +* encodes this into an identifier value to be issued to an +* external user to identify that Handle and its Object, and then +* associates this identifier value with the Handle. + +* Parameters: +* ihandle +* Offset in the "handles" array that identifies the Handle, +* which should be active (i.e. associated with an active +* Object). This function will modify the "check" field in this +* Handle to associate it with the identifier value it returns. + +* Returned Value: +* The resulting identifier value. + +* Notes: +* - The returned identifier value is, in fact, an int. This +* allows the value to be passed easily to other languages +* (e.g. Fortran) and stored as an integer without loss of +* information. +* - The value is stored within C code as type AstObject*. This +* means that C code (e.g. function prototypes, etc.) need not be +* aware that an identifier (as opposed to an Object pointer) is +* being used, even though the actual bit patterns will be +* different. The same C code can then be used for both internal +* and external purposes so long as care is taken to convert +* between the two representations at appropriate points. +* - The reverse operation (conversion of an ID back to a handle +* offset) is performed by CheckId. +* - A zero identifier value will be returned if this function is +* invoked with the global status set or if it should fail for any +* reason. +*/ + +/* Local Variables: */ + AstObject *result; /* Pointer value to return */ + MixedInts test; /* Union for testing encoding */ + MixedInts work; /* Union for encoding ID value */ + +/* Initialise. */ + result = astI2P( 0 ); + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Copy the Handle offset and clear the lowest 8 bits by shifting + left. */ + work.i = ihandle; + work.u = work.u << 8U; + +/* Make a copy of the result shifted right again. Test if any bits + have been lost in this process. If so, there are too many Handles + in use at once to encode them into IDs, so report an error. */ + test.u = work.u >> 8U; + if ( test.i != ihandle ) { + astError( AST__XSOBJ, "AssocId(%s): There are too many AST Objects in " + "use at once.", status, astGetClass( handles[ ihandle ].ptr ) ); + +/* If OK, scramble the value by exclusive-ORing with the bit pattern + in AST__FAC (a value unique to this library), also shifted left by + 8 bits. This makes it even less likely that numbers from other + sources will be accepted in error as valid IDs. */ + } else { + work.u ^= ( ( (unsigned) AST__FAC ) << 8U ); + +/* Fill the lowest 8 bits with a count of the total number of IDs + issued so far (which we increment here). This makes each ID unique, + so that an old one that identifies a Handle that has been annulled + and re-used (i.e. associated with a new ID) can be spotted. We + only use the lowest 8 bits of this count because this provides + adequate error detection to reveal programming errors and we do not + need higher security than this. We also prevent a count of zero + being used, as this could result in a zero identifier value (this + being reserved as the "null" value). */ + if ( ++nids > 255U ) nids = 1U; + work.u |= nids; + +/* Store the value as a check count in the Handle. This will be used + to validate the ID in future. */ + handles[ ihandle ].check = work.i; + +/* Pack the value into the pointer to be returned. */ + result = astI2P( work.i ); + } + +/* Return the result. */ + return result; +} + +void astBegin_( void ) { +/* +*++ +* Name: +c astBegin +f AST_BEGIN + +* Purpose: +* Begin a new AST context. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astBegin +f CALL AST_BEGIN( STATUS ) + +* Class Membership: +* Object class function. + +* Description: +c This macro invokes a function to begin a new AST context. +c Any Object pointers +f This routine begins a new AST context. Any Object pointers +* created within this context will be annulled when it is later +c ended using astEnd (just as if astAnnul had been invoked), +f ended using AST_END (just as if AST_ANNUL had been invoked), +c unless they have first been exported using astExport or rendered +c exempt using astExempt. If +f unless they have first been exported using AST_EXPORT or rendered +f exempt using AST_EXEMPT. If +* annulling a pointer causes an Object's RefCount attribute to +* fall to zero (which happens when the last pointer to it is +* annulled), then the Object will be deleted. + +f Parameters: +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This macro applies to all Objects. +f This routine applies to all Objects. + +* Notes: +c - astBegin attempts to execute even if the AST error status +c is set on entry. +f - This routine attempts to execute even if STATUS is set to an +f error value. +c - Contexts delimited by astBegin and astEnd may be nested to any +c depth. +f - Contexts delimited by AST_BEGIN and AST_END may be nested to any +f depth. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int stat; /* Copy of global status */ + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Now that the thread-specific data has been initialised, we can get a + pointer to the threads inherited status value. */ + status = astGetStatusPtr; + +/* Save and clear the global status so that memory allocation can be + performed within this function even under error conditions. */ + stat = astStatus; + astClearStatus; + +/* Ensure that the active_handles array has been initialised. */ + if ( !active_handles ) InitContext( status ); + +/* Extend the "active handles" array to accommodate a new context + level. This array contains integer offsets into the "handles" array + to identify the handle which is at the head of the list of active + handles for each context level. */ + astBeginPM; + active_handles = astGrow( active_handles, context_level + 2, + sizeof( int ) ); + astEndPM; + +/* Initialise the array element for the new context level to indicate + an empty Handle list. */ + if ( astOK ) active_handles[ ++context_level ] = -1; + +/* Restore the original global status value. */ + astSetStatus( stat ); +} + +MYSTATIC int CheckId( AstObject *this_id, int lock_check, int *status ) { +/* +* Name: +* CheckId + +* Purpose: +* Check an identifier value and convert it into a Handle offset. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* int CheckId( AstObject *this, int lock_check, int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function takes an identifier value encoded by AssocId and +* validates it to ensure it is associated with an active +* Handle. If valid, it converts it back into a zero-based offset +* which may be used to access the Handle in the "handles" +* array. Otherwise, an error is reported. + +* Parameters: +* this +* The identifier value to be decoded. +* lock_check +* Should an error be reported if the handle is in an Object +* context for a different thread? +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The resulting Handle offset, or -1 if the identifier is not +* valid or any other error occurs. + +* Notes: +* - This function attempts to execute even if the global error +* status is set, but no further error report will be made if it +* fails under these circumstances. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + MixedInts work; /* Union for decoding ID value */ + int id; /* ID value as an int */ + int ihandle; /* Result to return */ + +#ifdef MEM_DEBUG + int oldok = astOK; +#endif + +/* Initialise. */ + ihandle = -1; + +/* Get a pointer to thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Retrieve the integer Object identifier value from the pointer + supplied. */ + id = astP2I( this_id ); + +/* Check if a value of zero has been supplied and report an error if + it has. */ + if ( !id ) { + if ( astOK ) { + astError( AST__OBJIN, "Invalid Object pointer given (value is " + "zero)." , status); + } + +/* If OK, reverse the encoding process performed by AssocId to + retrieve the Handle offset. */ + } else { + work.i = id; + work.u = ( work.u ^ ( ( (unsigned) AST__FAC ) << 8U ) ) >> 8U; + +/* Check that the offset obtained doesn't extend beyond the limits of + the "handles" array. Report an error if it does. */ + if ( ( work.i < 0 ) || ( work.i >= nhandles ) ) { + if ( astOK ) { + astError( AST__OBJIN, "Invalid Object pointer given (value is " + "%d).", status, id ); + } + +/* See if the "check" field matches the ID value and the Handle is + valid (i.e. is associated with an active Object). If not, the + Handle has been annulled and possibly re-used, so report an + error. */ + } else if ( ( handles[ work.i ].check != id ) || + ( handles[ work.i ].context == INVALID_CONTEXT ) ) { + if ( astOK ) { + astError( AST__OBJIN, "Invalid Object pointer given (value is " + "%d).", status, id ); + astError( AST__OBJIN, "This pointer has been annulled, or the " + "associated Object deleted." , status); + } +#if defined(THREAD_SAFE) + } else if( lock_check && handles[ work.i ].context != UNOWNED_CONTEXT && + handles[ work.i ].thread != AST__THREAD_ID ) { + if ( astOK ) { + astError( AST__OBJIN, "Invalid Object pointer given (value is " + "%d).", status, id ); + astError( AST__OBJIN, "This pointer is currently owned by " + "another thread (possible programming error)." , status); + } +#endif + +/* If OK, set the Handle offset to be returned. */ + } else { + ihandle = work.i; + } + +#ifdef MEM_DEBUG + if ( oldok && !astOK && ( work.i >= 0 ) && ( work.i < nhandles ) ) { + char buf[200]; + astError( astStatus, "Handle properties: %s ", status, + HandleString( work.i, buf ) ); + } +#endif + + } + +/* Return the result. */ + return ihandle; +} + +AstObject *astDeleteId_( AstObject *this_id, int *status ) { +/* +*+ +* Name: +* astDeleteId + +* Purpose: +* Delete an Object via an identifier. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astDeleteId_( AstObject *this ) + +* Class Membership: +* Object member function. + +* Description: +* This function implements the external (public) interface to the +* astDelete method. It accepts an active Object identifier and +* deletes the associated Object. Before doing so, it annuls all +* active identifiers associated with the object, deactivates their +* Handles and returns these Handles to the free Handles list for +* re-use. + +* Parameters: +* this +* An identifier for the Object to be deleted. + +* Returned Value: +* A NULL C pointer is always returned (this should be translated +* into an identifier value of zero for external use). + +* Notes: +* - This function attempts to execute even if the global error +* status is set. +* - The identifier supplied should be associated with an active +* Object, otherwise an error will result (but no error report will +* be made if the global error status has already been set). +* - Although this function is documented as "private" and should +* not be invoked directly from outside this class, it is not a +* static function and has a public prototype. This is because it +* must be invoked via the astDelete macro (defined in the +* "object.h" include file) as part of the public interface. +*- +*/ + +/* Local Variables: */ + AstObject *this; /* Pointer to Object */ + int i; /* Loop counter for Handles */ + int ihandle; /* Object Handle offset */ + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object (this generates an + error if it doesn't). */ + if ( !astIsAObject( this = astMakePointer( this_id ) ) ) return NULL; + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* Obtain the Handle offset for this Object. */ + ihandle = CheckId( this_id, 1, status ); + if ( ihandle != -1 ) { + +/* Since the Object is to be deleted, we must annul all identifiers + that refer to it. Loop to inspect each currently allocated Handle + in the "handles" array. */ + for ( i = 0; i < nhandles; i++ ) { + +/* Select active handles and test if their Object pointer refers to + the Object to be deleted. */ + if ( ( handles[ i ].context != INVALID_CONTEXT ) && + ( handles[ i ].ptr == this ) ) { + +/* If so, explicitly set the reference count for the Object to 2 so + that it will not be deleted (yet) when we annul the pointer + associated with the Handle. */ + handles[ i ].ptr->ref_count = 2; + +/* Annul the Handle, which frees its resources, decrements the Object + reference count and makes any ID associated with the Handle become + invalid. */ + AnnulHandle( i, status ); + } + } + +/* If required, tell the user that the handle's object has been deleted. */ +#ifdef MEM_DEBUG + astHandleUse( ihandle, "object-deleted" ); +#endif + } + + UNLOCK_MUTEX2; + +/* When all Handles associated with the Object have been annulled, + delete the object itself. This over-rides the reference count and + causes any remaining pointers to the Object (e.g. in internal code + or within other Objects) to become invalid. */ + this = astDelete( this ); + +/* Always return a NULL pointer value. */ + return NULL; +} + +void astEnd_( int *status ) { +/* +*++ +* Name: +c astEnd +f AST_END + +* Purpose: +* End an AST context. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astEnd +f CALL AST_END( STATUS ) + +* Class Membership: +* Object class function. + +* Description: +c This macro invokes a function to end an AST context which was +f This routine ends an AST context which was +c begun with a matching invocation of astBegin. Any Object +f begun with a matching invocation of AST_BEGIN. Any Object +* pointers created within this context will be annulled (just as +c if astAnnul had been invoked) and will cease to be valid +f if AST_ANNUL had been invoked) and will cease to be valid +* afterwards, unless they have previously been exported using +c astExport or rendered exempt using astExempt. +f AST_EXPORT or rendered exempt using AST_EXEMPT. +* If annulling a pointer causes an Object's RefCount attribute to +* fall to zero (which happens when the last pointer to it is +* annulled), then the Object will be deleted. + +* Parameters: +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This macro applies to all Objects. +f This routine applies to all Objects. + +* Notes: +c - astEnd attempts to execute even if the AST error status is set. +f - This routine attempts to execute even if STATUS is set to an +f error value. +c - Contexts delimited by astBegin and astEnd may be nested to any +c depth. +f - Contexts delimited by AST_BEGIN and AST_END may be nested to any +f depth. +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int ihandle; /* Offset of Handle to be annulled */ + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Check that the current context level is at least 1, otherwise there + has been no matching use of astBegin, so report an error (unless + the global status has already been set). */ + if ( context_level < 1 ) { + if ( astOK ) { + astError( AST__ENDIN, "astEnd: Invalid use of astEnd without a " + "matching astBegin." , status); + } + +/* If OK, loop while there are still active Handles associated with + the current context level. First gain exclusive access to the handles + array. */ + } else if ( active_handles ) { + LOCK_MUTEX2; + while ( ( ihandle = active_handles[ context_level ] ) != -1 ) { + +/* Annul the Handle at the head of the active Handles list. */ + AnnulHandle( ihandle, status ); + +/* It is just posible that under error conditions inactive Handles + might get left in the active_handles list and AnnulHandle would + then fail. Since this would result in an infinite loop, we check to + see if the handle we have just annulled is still in the list. If + so, transfer it to the free Handles list for re-use. */ + if ( ihandle == active_handles[ context_level ] ) { + RemoveHandle( ihandle, &active_handles[ context_level ], status ); + InsertHandle( ihandle, &free_handles, status ); + } + } + +/* Ensure the context level is decremented unless it was zero to start + with. */ + context_level--; + +/* Relinquish access to the handles array. */ + UNLOCK_MUTEX2; + } + +} + +void astExemptId_( AstObject *this_id, int *status ) { +/* +*++ +* Name: +c astExempt +f AST_EXEMPT + +* Purpose: +* Exempt an Object pointer from AST context handling. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astExempt( AstObject *this ) +f CALL AST_EXEMPT( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function exempts an Object pointer from AST context +f This routine exempts an Object pointer from AST context +c handling, as implemented by astBegin and astEnd. This means that +f handling, as implemented by AST_BEGIN and AST_END. This means that +c the pointer will not be affected when astEnd is invoked and will +f the pointer will not be affected when AST_END is called and will +* remain active until the end of the program, or until explicitly +c annulled using astAnnul. +f annulled using AST_ANNUL. +* +c If possible, you should avoid using this function when writing +f If possible, you should avoid using this routine when writing +* applications. It is provided mainly for developers of other +* libraries, who may wish to retain references to AST Objects in +* internal data structures, and who therefore need to avoid the +c effects of astBegin and astEnd. +f effects of AST_BEGIN and AST_END. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Object pointer to be exempted from context handling. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. +*-- + +* Implementation Notes: +* - This function does not exist in the "protected" interface to +* the Object class and is not available to other class +* implementations. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int context; /* Handle context level */ + int ihandle; /* Offset of Handle in "handles" array */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object. */ + (void) astCheckObject( astMakePointer( this_id ) ); + if ( astOK ) { + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* Obtain the Handle offset for this Object. */ + ihandle = CheckId( this_id, 1, status ); + if ( ihandle != -1 ) { + +/* Extract the context level at which the Object was created. */ + context = handles[ ihandle ].context; + +/* Set the new context level to zero, where it cannot be affected by + ending any context. */ + handles[ ihandle ].context = 0; + +/* Remove the object's Handle from its original active Handles list + and insert it into the list appropriate to its new context + level. */ + +#if defined(THREAD_SAFE) + if( context == UNOWNED_CONTEXT ) { + RemoveHandle( ihandle, &unowned_handles, status ); + } else { + RemoveHandle( ihandle, &active_handles[ context ], status ); + } +#else + RemoveHandle( ihandle, &active_handles[ context ], status ); +#endif + + InsertHandle( ihandle, &active_handles[ 0 ], status ); + +/* If required, tell the user that the handle has been exempted. */ +#ifdef MEM_DEBUG + astHandleUse( ihandle, "exempted" ); +#endif + } + + UNLOCK_MUTEX2; + } +} + +void astExportId_( AstObject *this_id, int *status ) { +/* +*++ +* Name: +c astExport +f AST_EXPORT + +* Purpose: +* Export an Object pointer to an outer context. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astExport( AstObject *this ) +f CALL AST_EXPORT( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function exports an Object pointer from the current AST context +f This routine exports an Object pointer from the current AST context +* into the context that encloses the current one. This means that +* the pointer will no longer be annulled when the current context +c is ended (with astEnd), but only when the next outer context (if +f is ended (with AST_END), but only when the next outer context (if +* any) ends. + +* Parameters: +c this +f THIS = INTEGER (Given) +* Object pointer to be exported. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +* Notes: +c - It is only sensible to apply this function to pointers that +f - It is only sensible to apply this routine to pointers that +* have been created within (or exported to) the current context +c and have not been rendered exempt using astExempt. +f and have not been rendered exempt using AST_EXEMPT. +* Applying it to an unsuitable Object pointer has no effect. +*-- + +* Implementation Notes: +* - This function does not exist in the "protected" interface to +* the Object class and is not available to other class +* implementations. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int context; /* Handle context level */ + int ihandle; /* Offset of Handle in "handles" array */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object. */ + (void) astCheckObject( astMakePointer( this_id ) ); + if ( astOK ) { + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* Obtain the Handle offset for this Object. */ + ihandle = CheckId( this_id, 1, status ); + if ( ihandle != -1 ) { + +/* Check that the current context level is at least 1 and report an + error if it is not. */ + if ( context_level < 1 ) { + if( astOK ) astError( AST__EXPIN, "astExport(%s): Attempt to export an Object " + "from context level zero.", status, + astGetClass( handles[ ihandle ].ptr ) ); + +/* Extract the context level at which the Object was created. */ + } else { + context = handles[ ihandle ].context; + +/* Check that the Object's existing context level is high enough to be + affected by being exported to the next outer context level. If not, + do nothing. */ + if ( context > ( context_level - 1 ) ) { + +/* Set the new context level. */ + handles[ ihandle ].context = context_level - 1; + +/* Remove the object's Handle from its original active Handles list + and insert it into the list appropriate to its new context + level. */ + RemoveHandle( ihandle, &active_handles[ context ], status ); + InsertHandle( ihandle, &active_handles[ context_level - 1 ], + status ); + +/* If required, tell the user that the handle has been exempted. */ +#ifdef MEM_DEBUG + astHandleUse( ihandle, "exported from context level %d", + context_level ); +#endif + } + } + } + UNLOCK_MUTEX2; + } +} + +void astImportId_( AstObject *this_id, int *status ) { +/* +*++ +* Name: +c astImport +f AST_IMPORT + +* Purpose: +* Import an Object pointer to the current context. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c void astImport( AstObject *this ) +f CALL AST_IMPORT( THIS, STATUS ) + +* Class Membership: +* Object method. + +* Description: +c This function +f This routine +* imports an Object pointer that was created in a higher or lower +* level context, into the current AST context. +* This means that the pointer will be annulled when the current context +c is ended (with astEnd). +f is ended (with AST_END). + +* Parameters: +c this +f THIS = INTEGER (Given) +* Object pointer to be imported. +f STATUS = INTEGER (Given and Returned) +f The global status. + +* Applicability: +* Object +c This function applies to all Objects. +f This routine applies to all Objects. + +*-- + +* Implementation Notes: +* - This function does not exist in the "protected" interface to +* the Object class and is not available to other class +* implementations. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int context; /* Handle context level */ + int ihandle; /* Offset of Handle in "handles" array */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object. */ + (void) astCheckObject( astMakePointer( this_id ) ); + if ( astOK ) { + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* Obtain the Handle offset for this Object. */ + ihandle = CheckId( this_id, 1, status ); + if ( ihandle != -1 ) { + +/* Extract the context level at which the Object was created. */ + context = handles[ ihandle ].context; + +/* Do nothing if the Identifier already belongs to the current context. */ + if( context != context_level ) { + +/* Set the new context level. */ + handles[ ihandle ].context = context_level; + +/* Remove the object's Handle from its original active Handles list + and insert it into the list appropriate to its new context + level. */ + RemoveHandle( ihandle, &active_handles[ context ], status ); + InsertHandle( ihandle, &active_handles[ context_level ], status ); + +/* If required, tell the user that the handle has been imported. */ +#ifdef MEM_DEBUG + astHandleUse( ihandle, "imported into context level %d", + context_level ); +#endif + } + } + UNLOCK_MUTEX2; + } +} + +void astLockId_( AstObject *this_id, int wait, int *status ) { +/* +c++ +* Name: +* astLock + +* Purpose: +* Lock an Object for exclusive use by the calling thread. + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* void astLock( AstObject *this, int wait ) + +* Class Membership: +* Object method. + +* Description: +* The thread-safe public interface to AST is designed so that an +* error is reported if any thread attempts to use an Object that it +* has not previously locked for its own exclusive use using this +* function. When an Object is created, it is initially locked by the +* thread that creates it, so newly created objects do not need to be +* explicitly locked. However, if an Object pointer is passed to +* another thread, the original thread must first unlock it (using +* astUnlock) and the new thread must then lock it (using astLock) +* before the new thread can use the Object. +* +* The "wait" parameter determines what happens if the supplied Object +* is curently locked by another thread when this function is invoked. + +* Parameters: +* this +* Pointer to the Object to be locked. +* wait +* If the Object is curently locked by another thread then this +* function will either report an error or block. If a non-zero value +* is supplied for "wait", the calling thread waits until the object +* is available for it to use. Otherwise, an error is reported and +* the function returns immediately without locking the Object. + +* Applicability: +* Object +* This function applies to all Objects. + +* Notes: +* - The astAnnul function is exceptional in that it can be used on +* pointers for Objects that are not currently locked by the calling +* thread. All other AST functions will report an error. +* - The Locked object will belong to the current AST context. +* - This function returns without action if the Object is already +* locked by the calling thread. +* - If simultaneous use of the same object is required by two or more +* threads, astCopy should be used to to produce a deep copy of +* the Object for each thread. Each copy should then be unlocked by +* the parent thread (i.e. the thread that created the copy), and then +* locked by the child thread (i.e. the thread that wants to use the +* copy). +* - This function is only available in the C interface. +* - This function returns without action if the AST library has +* been built without POSIX thread support (i.e. the "-with-pthreads" +* option was not specified when running the "configure" script). +c-- +*/ + +/* This function odes nothing if thread support is not enabvled. */ +#if defined(THREAD_SAFE) + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + AstObject *fail; /* Pointer to Object that failed to lock */ + AstObject *this; /* Pointer to Object */ + int ihandle; /* Index of supplied objetc handle */ + int lstat; /* Local status value */ + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object (this generates an + error if it doesn't). Note, we use the "astMakePointer_NoLockCheck", + since the usual "astMakePointer" macro invokes astCheckLock to report + an error if the Object is not currently locked by the calling thread. */ + if ( !astIsAObject( this = astMakePointer_NoLockCheck( this_id ) ) ) return; + +/* Ensure the global data for this class is accessable. Do not use the + globals pointer stored in "*this" because "*this" may be locked by + another thread and so we would pick up the wrong globals. */ + astGET_GLOBALS(NULL); + +/* Ensure the running thread has sole access to the static handles arrays. */ + LOCK_MUTEX2; + +/* Ensure the Handles arrays have been initialised. */ + if ( !active_handles ) InitContext( status ); + +/* Get the Handle index for the supplied object identifier. No error is + reported if the handle is not curently associated with a thread. + However, an error is reported if the Handle is associated with any + thread other than the running thread. */ + ihandle = CheckId( this_id, 0, status ); + +/* We've finished with the handles arrays, for the moment. */ + UNLOCK_MUTEX2; + +/* Check the object pointer was valid. */ + if( ihandle != -1 ){ + +/* The protected astManageLock function implements the public functions, + astLock and astUnlock. */ + lstat = astManageLock( this, AST__LOCK, wait, &fail ); + if( astOK ) { + if( lstat == 1 ) { + if( fail == this ) { + astError( AST__LCKERR, "astLock(%s): Failed to lock the %s because" + " it is already locked by another thread (programming " + "error).", status, astGetClass( this ), + astGetClass( this ) ); + + } else { + astError( AST__LCKERR, "astLock(%s): Failed to lock the %s because" + " a %s contained within it is already locked by another " + "thread (programming error).", status, + astGetClass( this ), astGetClass( this ), + astGetClass( fail ) ); + } + + } else if( lstat == 2 ) { + astError( AST__LCKERR, "astLock(%s): Failed to lock a POSIX mutex.", status, + astGetClass( this ) ); + +/* If the Object is now locked for the running thread... */ + } else { + +/* We need access to the handles arrays again. */ + LOCK_MUTEX2; + +/* If the supplied handle is not currently assigned to any thread, assign + it to the running thread. */ + if( handles[ ihandle ].context == UNOWNED_CONTEXT ) { + RemoveHandle( ihandle, &unowned_handles, status ); + +#if defined(MEM_DEBUG) + astHandleUse( ihandle, "locked by thread %d at context level %d", + handles[ ihandle ].thread, context_level ); +#endif + + handles[ ihandle ].thread = AST__THREAD_ID; + handles[ ihandle ].context = context_level; + InsertHandle( ihandle, &active_handles[ context_level ], + status ); + } + +/* Finished with the handles arrays again. */ + UNLOCK_MUTEX2; + } + } + } +#endif +} + +void astUnlockId_( AstObject *this_id, int report, int *status ) { +/* +c++ +* Name: +* astUnlock + +* Purpose: +* Unlock an Object for use by other threads. + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* void astUnlock( AstObject *this, int report ) + +* Class Membership: +* Object method. + +* Description: +* Unlocks an Object previously locked using astLock, so that other +* threads can use the Object. See astLock for further details. + +* Parameters: +* this +* Pointer to the Object to be unlocked. +* report +* If non-zero, an error will be reported if the supplied Object, +* or any Object contained within the supplied Object, is not +* currently locked by the running thread. If zero, such Objects +* will be left unchanged, and no error will be reported. + +* Applicability: +* Object +* This function applies to all Objects. + +* Notes: +* - This function attempts to execute even if the global error +* status is set, but no further error report will be made if it +* subsequently fails under these circumstances. +* - All unlocked Objects are excluded from AST context handling until +* they are re-locked using astLock. +* - This function is only available in the C interface. +* - This function returns without action if the Object is not currently +* locked by any thread. If it is locked by the running thread, it is +* unlocked. If it is locked by another thread, an error will be reported +* if "error" is non-zero. +* - This function returns without action if the AST library has +* been built without POSIX thread support (i.e. the "-with-pthreads" +* option was not specified when running the "configure" script). +c-- +*/ + +/* This function odes nothing if thread support is not enabvled. */ +#if defined(THREAD_SAFE) + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + AstErrorContext error_context;/* Info about the current error context */ + AstObject *fail; /* Pointer to Object that failed */ + AstObject *this; /* Pointer to Object */ + int ihandle; /* Index of supplied objetc handle */ + int lstat; /* Local status value */ + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object (this generates an + error if it doesn't). Note, we use the "astMakePointer_NoLockCheck", + since the usual "astMakePointer" macro invokes astCheckLock to report + an error if the Object is not currently locked by the calling thread. */ + if ( !astIsAObject( this = astMakePointer_NoLockCheck( this_id ) ) ) return; + +/* Ensure the global data for this class is accessable. */ + astGET_GLOBALS(this); + +/* Start a new error reporting context. This saves any existing error status + and then clear the status value. It also defer further error reporting. */ + astErrorBegin( &error_context ); + +/* Ensure the running thread has sole access to the static handles arrays. */ + LOCK_MUTEX2; + +/* Ensure the Handles arrays have been initialised. */ + if ( !active_handles ) InitContext( status ); + +/* Get the Handle index for the supplied object identifier. Report an error + if the handle is not curently associated with the running thread. */ + ihandle = CheckId( this_id, 1, status ); + +/* Break the associated of the handle with the current thread so that the + handle is not assigned to any thread. We do this before unlocking the + Object structure (using astManageLock) since as soon as astManageLock + returns, another thread that is waiting for the object to be unlocked + may start up and modify the handle properties. */ + if( ihandle >= 0 && handles[ ihandle ].context >= 0 ) { + RemoveHandle( ihandle, &active_handles[ handles[ ihandle ].context ], + status ); +#if defined(MEM_DEBUG) + astHandleUse( ihandle, "unlocked from thread %d at context " + "level %d", handles[ ihandle ].thread, + handles[ ihandle ].context ); +#endif + handles[ ihandle ].thread = -1; + handles[ ihandle ].context = UNOWNED_CONTEXT; + InsertHandle( ihandle, &unowned_handles, status ); + } + +/* We've finished with the handles arrays, for the moment. */ + UNLOCK_MUTEX2; + +/* Check the supplied object pointer was valid. */ + if( ihandle != -1 ){ + +/* The protected astManageLock function implements the public functions, + astLock and astUnlock. */ + lstat = astManageLock( this, AST__UNLOCK, 0, &fail ); + if( astOK ) { + if( lstat == 1 ) { + if( report ) { + if( fail == this ) { + astError( AST__LCKERR, "astUnlock(%s): Failed to unlock the %s " + "because it is locked by another thread (programming " + "error).", status, astGetClass( this ), + astGetClass( this ) ); + + } else { + astError( AST__LCKERR, "astUnlock(%s): Failed to unlock the %s " + "because a %s contained within it is locked by another " + "thread (programming error).", status, + astGetClass( this ), astGetClass( this ), + astGetClass( fail ) ); + } + } + + } else if( lstat == 3 ) { + astError( AST__LCKERR, "astUnlock(%s): Failed to unlock a POSIX mutex.", status, + astGetClass( this ) ); + + } + } + } + +/* End the error reporting context. If an error has occurred within this + function, then this will display the deferred error messages so long + as there was no error condition on entry to this function. If there + was an error condition on entry, then the original status value will be + re-instated. */ + astErrorEnd( &error_context ); + +#endif +} + +AstObject *astI2P_( int integer, int *status ) { +/* +*+ +* Name: +* astI2P + +* Purpose: +* Pack an integer Object ID into a pointer. + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* AstObject *astI2P( int integer ) + +* Class Membership: +* Object class function. + +* Description: +* This function accepts an integer (int) value representing an +* Object identifier and packs its bit pattern into a pointer value +* (from which it may subsequently be retrieved using astP2I). +* +* These functions should be used to avoid any dependency on +* integer-to-pointer conversions (given that the values are not +* true pointers) which might affect the exchange of Object +* identifier values with other languages, such as Fortran 77, +* where they are stored as integers. + +* Parameters: +* integer +* The integer value to be stored. + +* Returned Value: +* The resulting pointer value (which is not usually a valid C pointer). + +* Notes: +* - This function does not perform error checking and does not +* generate errors. +* - This is a protected function in the sense that it is not +* intended that external users should invoke it directly. It is +* accessible from external code, however, in order that public +* macros invoked from that code can make use of it. +*- +*/ + +/* Local Variables: */ + IdUnion temp; /* Overlapping int and pointer */ + +/* Clear the pointer value in the "temp" IdUnion and then set the + integer part of it to the value to be stored. */ + temp.pointer = zero_ptr; + temp.integer = integer; + +/* Return the resulting pointer value. */ + return temp.pointer; +} + +MYSTATIC void InitContext( int *status ) { +/* +* Name: +* InitContext + +* Purpose: +* Initialise the first AST context level. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* void InitContext( int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function initialises the first AST context level (the level +* before any call to astBegin has been made). It should be invoked +* once, before any use is made of context levels. + +* Parameters: +* status +* Pointer to the inherited status variable. + +* Parameters: +* None. + +* Notes: +* - This function does nothing after the first successful invocation. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Check that the active_handles array hasn't already been allocated. */ + if ( !active_handles ) { + +/* Allocate and initialise the "active_handles" array. */ + + astBeginPM; + active_handles = astMalloc( sizeof( int ) ); + astEndPM; + + if ( astOK ) active_handles[ 0 ] = -1; + } +} + +MYSTATIC void InsertHandle( int ihandle, int *head, int *status ) { +/* +* Name: +* InsertHandle + +* Purpose: +* Insert a Handle into a list. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* void InsertHandle( int ihandle, int *head, int *status ) + +* Class Membership: +* Object member function. + +* Description: +* This function inserts a Handle structure into a doubly linked +* list of such structures composed of elements drawn from the +* "handles" array. The new list member is inserted in front of the +* member previously occupying the "head of list" position. + +* Parameters: +* ihandle +* Offset in the "handles" array that identifies the handle to +* be added to the list. +* head +* Address of an int which holds the offset in the "handles" +* array of the element at the head of the list (or -1 if the +* list is empty). This value will be updated to identify the +* new list member. +* status +* Pointer to the inherited status variable. + +* Notes: +* - This function does not perform error chacking and does not +* generate errors. +* - The lists generated by this function use integer offsets into +* the "handles" array for their links, rather than pointers. This +* is because the array may be re-located in memory when it needs +* to be extended, so pointers to its element would not remain +* valid. +* - The list elements are drawn from the "handles" array in the +* first place so that they can be addressed by small integers (the +* offset in the array). This allows references to Handles to be +* encoded along with security information into an integer that is +* sufficiently short to be exported to other languages +* (e.g. Fortran) which might not be able to accommodate +* full-length C pointers. +*/ + + +#if defined(MEM_DEBUG) + char buf[80]; + astHandleUse( ihandle, "about to be inserted into %s (%d)", + HeadString( head, buf ), head ); + CheckList( head, status ); + CheckInList( ihandle, head, 0, status ); +#endif + +/* Check a head pointer was supplied (may not be if an error has + occurred). */ + if( ! head ) return; + +/* If the list is empty, the sole new element points at itself. */ + if ( *head == -1 ) { + handles[ ihandle ].flink = ihandle; + handles[ ihandle ].blink = ihandle; + +/* Otherwise, insert the new element in front of the element at the + head of the list. */ + } else { + handles[ ihandle ].flink = *head; + handles[ ihandle ].blink = handles[ *head ].blink; + handles[ ( handles[ *head ].blink) ].flink = ihandle; + handles[ *head ].blink = ihandle; + } + +/* Update the list head to identify the new element. */ + *head = ihandle; + +#if defined(MEM_DEBUG) + CheckList( head, status ); + astHandleUse( ihandle, "has been inserted into %s", buf ); +#endif +} + +AstObject *astMakeId_( AstObject *this, int *status ) { +/* +*+ +* Name: +* astMakeId + +* Purpose: +* Issue an identifier for an Object. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astMakeId( AstObject *this ) + +* Class Membership: +* Object member function. + +* Description: +* This function takes a normal C pointer to an Object and +* associates an identifier with it. + +* Parameters: +* this +* Pointer to an Object. Note that this function copies this +* value (it is not cloned), so the caller should not annul the +* pointer afterwards. + +* Returned Value: +* The resulting identifier value. + +* Notes: +* - An identifier value of zero will be returned and the supplied +* Object pointer will be annulled if this function is invoked with +* the global error status set or if it should fail for any reason. +* - A zero identifier value will also be returned if a NULL object +* pointer is supplied, but this will not provoke an error. +* - This is a protected function in the sense that it is not +* intended that external users should invoke it directly. It is +* accessible from external code, however, in order that public +* macros invoked from that code can make use of it. +*- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + AstObject *id; /* ID value to return */ + int ihandle; /* Handle offset */ + +/* Initialise. */ + id = astI2P( 0 ); + ihandle = 0; + +/* Check the global error status. */ + if ( astOK ) { + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(this); + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* If a non-NULL Object pointer was given, we must obtain a Handle + structure to associate with it (otherwise a zero identifier value + is returned without error). */ + if ( this ) { + +/* If the free Handles list is not empty, obtain a Handle from it. */ + if ( free_handles != -1 ) { + ihandle = free_handles; + RemoveHandle( ihandle, &free_handles, status ); + +/* Otherwise, allocate a new Handle by extending the "handles" array + and using the offset of the new element. */ + } else { + + astBeginPM; + handles = astGrow( handles, nhandles + 1, sizeof( Handle ) ); + astEndPM; + + if ( astOK ) { + ihandle = nhandles++; + + handles[ ihandle ].ptr = NULL; + handles[ ihandle ].context = INVALID_CONTEXT; + handles[ ihandle ].check = 0; + handles[ ihandle ].flink = -1; + handles[ ihandle ].blink = -1; +#if defined(THREAD_SAFE) + handles[ ihandle ].thread = 0; +#endif + +#if defined(MEM_DEBUG) + handles[ ihandle ].id = 0; + handles[ ihandle ].vtab = NULL; +#endif + } + } + +/* If the first AST context level has not yet been initialised, invoke + InitContext to initialise it and allocate memory for the + "active_handles" array which stores context information. */ + if ( astOK ) { + if ( !active_handles ) InitContext( status ); + +/* Store the Object pointer and current context level in the Handle. */ + if ( astOK ) { + handles[ ihandle ].ptr = this; + handles[ ihandle ].context = context_level; +#if defined(THREAD_SAFE) + handles[ ihandle ].thread = AST__THREAD_ID; +#endif + +/* Store extra debugging information in the handle if enabled */ +#if defined(MEM_DEBUG) + handles[ ihandle ].id = astMemoryId( this ); + handles[ ihandle ].vtab = this->vtab; + astHandleUse( ihandle, "associated with a %s (id %d)", + astGetClass( this ), astMemoryId( this )); +#endif + +/* Insert the Handle into the active Handles list for the current + context level. */ + InsertHandle( ihandle, &active_handles[ context_level ], status ); + +/* Associate an identifier value with the Handle. */ + id = AssocId( ihandle, status ); + +/* If an error occurred, clean up by annulling the Handle. This + ensures that the Object pointer is annulled and returns the unused + Handle to the Free Handle list. */ + if ( !astOK ) { + AnnulHandle( ihandle, status ); + this = NULL; + } + +/* If the Handle wasn't used (because of an earlier error), return it + to the free Handles list. */ + } else { + InsertHandle( ihandle, &free_handles, status ); + } + } + } + UNLOCK_MUTEX2; + } + +/* If a bad status value was either supplied or generated within this + function, then annul the supplied pointer (unless it is NULL). */ + if ( !astOK && this ) (void) astAnnul( this ); + +/* Return the identifier value. */ + return id; +} + +AstObject *astMakePointer_( AstObject *this_id, int *status ) { +/* +*+ +* Name: +* astMakePointer + +* Purpose: +* Obtain a true C pointer from an Object identifier. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astMakePointer( AstObject *this ) + +* Class Membership: +* Object class function. + +* Description: +* This function accepts an external Object identifier and returns +* a true C Object pointer that may be de-referenced to access the +* Object's data and methods. + +* Parameters: +* this +* The identifier value. + +* Returned Value: +* The true C pointer value. + +* Notes: +* - In a thread-safe context, an error is reported if the supplied +* Object has not been locked by the calling thread (using astLock) +* prior to invoking this function. +* - The object reference count is not modified by this function, +* so the returned pointer should not be annulled by the caller. +* - Typically, this function should be used whenever a public +* function must accept identifier values directly (rather than +* having them translated automatically into pointers during +* argument validation by the astCheck<Class> range of functions). +* - Note that this function will NOT accept a true C pointer as an +* argument, even when invoked from internal code (with astCLASS +* defined). An identifier value MUST be supplied, and an error +* will result if it is not associated with an active Object. +* - This function attempts to execute even if the global error +* status is set, but no further error report will be made if it +* subsequently fails under these circumstances. +* - This is a protected function in the sense that it is not +* intended that external users should invoke it directly. It is +* accessible from external code, however, in order that public +* macros invoked from that code can make use of it. +*- +*/ + +/* Local Variables: */ + AstObject *ptr; /* Pointer value to return */ + int ihandle; /* Handle offset in "handles" array */ + +/* Initialise. */ + ptr = NULL; + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* Validate the identifier supplied and derive the Handle offset. */ + ihandle = CheckId( this_id, 1, status ); + +/* If the identifier was valid, extract the Object pointer from the + Handle. */ + if ( ihandle != -1 ) ptr = handles[ ihandle ].ptr; + + UNLOCK_MUTEX2; + +/* Return the result. */ + return ptr; +} + +AstObject *astMakePointer_NoLockCheck_( AstObject *this_id, int *status ) { +/* +*+ +* Name: +* astMakePointer_NoLockCheck + +* Purpose: +* Obtain a true C pointer from an Object identifier. + +* Type: +* Protected function. + +* Synopsis: +* #include "object.h" +* AstObject *astMakePointer_NoLockCheck( AstObject *this ) + +* Class Membership: +* Object class function. + +* Description: +* This function accepts an external Object identifier and returns +* a true C Object pointer that may be de-referenced to access the +* Object's data and methods. +* +* It is identicial to astMakePointer except that it does not check +* that the supplied Object is locked by the running thread. + +* Parameters: +* this +* The identifier value. + +* Returned Value: +* The true C pointer value. + +* Notes: +* - The object reference count is not modified by this function, +* so the returned pointer should not be annulled by the caller. +* - Typically, this function should be used whenever a public +* function must accept identifier values directly (rather than +* having them translated automatically into pointers during +* argument validation by the astCheck<Class> range of functions). +* - Note that this function will NOT accept a true C pointer as an +* argument, even when invoked from internal code (with astCLASS +* defined). An identifier value MUST be supplied, and an error +* will result if it is not associated with an active Object. +* - This function attempts to execute even if the global error +* status is set, but no further error report will be made if it +* subsequently fails under these circumstances. +* - This is a protected function in the sense that it is not +* intended that external users should invoke it directly. It is +* accessible from external code, however, in order that public +* macros invoked from that code can make use of it. +*- +*/ + +/* Local Variables: */ + AstObject *ptr; /* Pointer value to return */ + int ihandle; /* Handle offset in "handles" array */ + +/* Initialise. */ + ptr = NULL; + +/* Gain exclusive access to the handles array. */ + LOCK_MUTEX2; + +/* Validate the identifier supplied and derive the Handle offset. */ + ihandle = CheckId( this_id, 0, status ); + +/* If the identifier was valid, extract the Object pointer from the + Handle. */ + if ( ihandle != -1 ) ptr = handles[ ihandle ].ptr; + + UNLOCK_MUTEX2; + +/* Return the result. */ + return ptr; +} + +int astP2I_( AstObject *pointer, int *status ) { +/* +*+ +* Name: +* astP2I + +* Purpose: +* Extract an integer Object ID from a pointer. + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* int astP2I( AstObject *pointer ) + +* Class Membership: +* Object class function. + +* Description: +* This function retrieves an integer (int) value representing an +* Object identifier from a pointer value (into which it has +* previously been packed using astI2P). +* +* These functions should be used to avoid any dependency on +* integer-to-pointer conversions (given that the values are not +* true pointers) which might affect the exchange of Object +* identifier values with other languages, such as Fortran 77, +* where they are stored as integers. + +* Parameters: +* pointer +* The pointer value into which the ID has previously been packed. + +* Returned Value: +* The extracted Object identifier value as an integer (int). + +* Notes: +* - This function does not perform error checking and does not +* generate errors. +* - This is a protected function in the sense that it is not +* intended that external users should invoke it directly. It is +* accessible from external code, however, in order that public +* macros invoked from that code can make use of it. +*- +*/ + +/* Local Variables: */ + IdUnion temp; /* Overlapping int and pointer */ + +/* Store the pointer value supplied. */ + temp.pointer = pointer; + +/* Return the integer part of it. */ + return temp.integer; +} + +MYSTATIC void RemoveHandle( int ihandle, int *head, int *status ) { +/* +* Name: +* RemoveHandle + +* Purpose: +* Remove a Handle from a list. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* void RemoveHandle( int ihandle, int *head ) + +* Class Membership: +* Object member function. + +* Description: +* This function removes a Handle structure from a doubly linked +* list of such structures composed of elements drawn from the +* "handles" array. The "head of list" position is updated if +* necessary. + +* Parameters: +* ihandle +* Offset in the "handles" array that identifies the Handle to +* be removed from the list (note that the Handle must actually +* be in the list, although this function does not check this). +* head +* Address of an int which holds the offset in the "handles" +* array of the element at the head of the list. This value will +* be updated if necessary to identify the new list head (or set +* to -1 if removing the Handle causes the list to become +* empty). + +* Notes: +* - This function does not perform error chacking and does not +* generate errors. +* - See the InsertHandle function for details of how and why lists +* of Handles are constructed. +*/ + + +#if defined(MEM_DEBUG) + char buf[80]; + astHandleUse( ihandle, "about to be removed from %s", HeadString( head, buf ) ); + CheckList( head, status ); + CheckInList( ihandle, head, 1, status ); +#endif + +/* Check a head pointer was supplied (may not be if an error has + occurred). */ + if( ! head ) return; + +/* Remove the Handle from the list by re-establishing links between + the elements on either side of it. */ + handles[ ( handles[ ihandle ].blink ) ].flink = handles[ ihandle ].flink; + handles[ ( handles[ ihandle ].flink ) ].blink = handles[ ihandle ].blink; + +/* If the element removed was at the head of the list, update the head + of list offset to identify the following element. */ + if ( ihandle == *head ) { + *head = handles[ ihandle ].flink; + +/* If the head of list still identifies the removed element, then note + that the list is now empty. */ + if ( *head == ihandle ) *head = -1; + } + +/* Make the removed element point at itself. */ + handles[ ihandle ].flink = ihandle; + handles[ ihandle ].blink = ihandle; + +#if defined(MEM_DEBUG) + astHandleUse( ihandle, "has been removed from %s", buf ); + CheckList( head, status ); +#endif +} + +void astSetId_( void *this_id_void, const char *settings, ... ) { +/* +* Name: +* astSetId_ + +* Purpose: +* Set values for an Object's attributes via an identifier. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* void astSetId_( AstObject *this, const char *settings, ... ) + +* Class Membership: +* Object member function. + +* Description: +* This function implements the axternal interface to the astSet +* method (q.v.). It accepts an Object identifier, but otherwise +* behaves identically to astSet. + +* Parameters: +* this +* Object identifier. +* settings +* Pointer to a null-terminated string containing a +* comma-separated list of attribute settings. +* ... +* Optional arguments which supply values to be substituted for +* any "printf"-style format specifiers that appear in the +* "settings" string. + +* Notes: +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* Object identifier is of type (void *) and is converted and +* validated within the function itself. +*/ + +/* Local Variables: */ + AstObject *this; /* Pointer to the Object structure */ + int *status; /* Pointer to inherited status variable */ + va_list args; /* Variable argument list */ + +/* Get a pointer to the integer holding the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain the Object pointer from the ID supplied and validate the + pointer to ensure it identifies a valid Object. */ + this = astCheckObject( astMakePointer( this_id_void ) ); + if ( astOK ) { + +/* Obtain the variable argument list and pass all arguments to the + astVSet method for interpretation. */ + va_start( args, settings ); + astVSet( this, settings, NULL, args ); + va_end( args ); + } +} + +int astThreadId_( AstObject *this_id, int ptr, int *status ) { +/* +c++ +* Name: +* astThread + +* Purpose: +* Determine the thread that owns an Object. + +* Type: +* Public function. + +* Synopsis: +* #include "object.h" +* int astThread( AstObject *this, int ptr ) + +* Class Membership: +* Object method. + +* Description: +* Returns an integer that indicates whether the supplied Object (or +* Object pointer) is currently unlocked, or is currently locked by +* the running thread, or another thread. + +* Parameters: +* this +* Pointer to the Object to be checked. +* ptr +* If non-zero, returns information about the supplied Object +* pointer, rather than the Object structure itself. See "Object +* Pointers and Structures" below. + +* Returned Value: +* astThread() +* A value of AST__UNLOCKED is returned if the Object (or pointer) +* is currently unlocked (i.e. has been unlocked using astUnlock +* but has not yet been locked using astLock). A value of +* AST__RUNNING is returned if the Object (or pointer) is currently +* locked by the running thread. A value of AST__OTHER is returned +* if the Object (or pointer) is currently locked by the another +* thread. + +* Object Pointers and Structures: +* At any one time, an AST Object can have several distinct pointers, +* any one of which can be used to access the Object structure. For +* instance, the astClone function will produce a new distinct pointer +* for a given Object. In fact, an AST "pointer" is not a real pointer +* at all - it is an identifier for a "handle" structure, encoded to +* make it look like a pointer. Each handle contains (amongst othere +* things) a "real" pointer to the Object structure. This allows more +* than one handle to refer to the same Object structure. So when you +* call astClone (for instance) you get back an identifier for a new +* handle that refers to the same Object as the supplied handle. +* +* In order to use an Object for anything useful, it must be locked +* for use by the running thread (either implicitly at creation or +* explicitly using astLock). The identity of the thread is stored in +* both the Object structure, and in the handle that was passed to +* astLock (or returned by the constructor function). Thus it is +* possible for a thread to have active pointers for Objects that are +* currently locked by another thread. In general, if such a pointer is +* passed to an AST function an error will be reported indicating that +* the Object is currently locked by another thread. The two exceptions +* to this is that astAnnul can be used to annull such a pointer, and +* this function can be used to return information about the pointer. +* +* The other practical consequence of this is that when astEnd is +* called, all active pointers currently owned by the running thread +* (at the current context level) are annulled. This includes pointers +* for Objects that are currently locked by other threads. +* +* If the "ptr" parameter is zero, then the returned value describes +* the Object structure itself. If "ptr" is non-zero, then the returned +* value describes the supplied Object pointer (i.e. handle), rather +* than the Object structure. + +* Notes: +* - This function attempts to execute even if the global error +* status is set, but no further error report will be made if it +* subsequently fails under these circumstances. +* - This function is only available in the C interface. +* - This function always returns AST__RUNNING if the AST library has +* been built without POSIX thread support (i.e. the "-with-pthreads" +* option was not specified when running the "configure" script). +c-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Thread-specific global data */ + int result; /* The returned value */ + +#if defined(THREAD_SAFE) + +/* More local Variables: */ + AstObject *this; + int ihandle; + int check; + +/* Ensure global variables are accessable. */ + astGET_GLOBALS(NULL); +#endif + +/* Initialise the returned value */ + result = AST__RUNNING; + +/* Nothing more to do if AST was not build with thread support. */ +#if defined(THREAD_SAFE) + +/* If the ownership of the handle is being queried... */ + if( ptr ) { + +/* Lock the mutex that guards access to the handles array */ + LOCK_MUTEX2; + +/* Check the supplied object identifier is valid and get the + corresponding index into the handles array. */ + ihandle = CheckId( this_id, 1, status ); + if( ihandle != -1 ) { + +/* Set the returned value on the basis of the threa didentifier stored in + the handle structure. */ + if( handles[ ihandle ].thread == -1 ) { + result = AST__UNLOCKED; + } else if( handles[ ihandle ].thread != AST__THREAD_ID ) { + result = AST__OTHER; + } + } + +/* Unlock the mutex that guards access to the handles array */ + UNLOCK_MUTEX2; + +/* Otherwise, the ownership of the Object is being queried. Obtain the + Object pointer from the ID supplied and validate the pointer to ensure + it identifies a valid Object (this generates an error if it doesn't). + Note, we use the "astMakePointer_NoLockCheck", since the usual + "astMakePointer" macro invokes astCheckLock to report an error if the + Object is not currently locked by the calling thread. */ + } else if( astIsAObject( this = astMakePointer_NoLockCheck( this_id ) ) ) { + +/* Determine which thread (if any) has the object locked, and set an + appropriate return value. */ + check = astManageLock( this, AST__CHECKLOCK, 0, NULL ); + + if( check == 5 ) { + result = AST__OTHER; + } else if( check == 6 ) { + result = AST__UNLOCKED; + } + } + +#endif + +/* Return the result. */ + return result; +} + +int astVersion_( int *status ) { +/* +*++ +* Name: +c astVersion +f AST_VERSION + +* Purpose: +* Return the version of the AST library being used. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c int astVersion +f RESULT = AST_VERSION() + +* Class Membership: +* Object class function. + +* Description: +c This macro invokes a function which +f This function +* returns an integer representing the version of the AST library +* being used. The library version is formatted as a string such as +* "2.0-7" which contains integers representing the "major version" (2), +* the "minor version" (0) and the "release" (7). The integer returned +* by this function combines all three integers together into a single +* integer using the expresion: +* +* (major version)*1E6 + (minor version)*1E3 + (release) + +* Returned Value: +c astVersion +f AST_VERSION = INTEGER +* The major version, minor version and release numbers for the AST +* library, encoded as a single integer. + +* Applicability: +* Object +c This macro applies to all Objects. +f This routine applies to all Objects. + +*-- +*/ + + return (int) AST__VMAJOR*1E6 + AST__VMINOR*1E3 + AST__RELEASE; +} + +int astEscapes_( int new_value, int *status ) { +/* +*++ +* Name: +c astEscapes +f AST_ESCAPES + +* Purpose: +* Control whether graphical escape sequences are included in strings. + +* Type: +* Public function. + +* Synopsis: +c #include "object.h" +c int astEscapes( int new_value ) +f RESULT = AST_ESCAPES( NEWVAL, STATUS ) + +* Class Membership: +* Object class function. + +* Description: +* The Plot class defines a set of escape sequences which can be +* included within a text string in order to control the appearance of +* sub-strings within the text. See the Escape attribute for a +* description of these escape sequences. It is usually inappropriate +* for AST to return strings containing such escape sequences when +* called by application code. For instance, an application which +* displays the value of the Title attribute of a Frame usually does +* not want the displayed string to include potentially long escape +* sequences which a human read would have difficuly interpreting. +* Therefore the default behaviour is for AST to strip out such escape +* sequences when called by application code. This default behaviour +* can be changed using this function. + +* Parameters: +c new_value +f NEWVAL = INTEGER (Given) +* A flag which indicates if escapes sequences should be included +* in returned strings. If zero is supplied, escape sequences will +* be stripped out of all strings returned by any AST function. If +* a positive value is supplied, then any escape sequences will be +* retained in the value returned to the caller. If a negative +* value is supplied, the current value of the flag will be left +* unchanged. + +* Returned Value: +c astEscapes +f AST_ESCAPES = INTEGER +* The value of the flag on entry to this function. + +* Applicability: +* Object +c This macro applies to all Objects. +f This routine applies to all Objects. + +* Notes: +* - This function also controls whether the +c astStripEscapes +f AST_STRIPESCAPES +* function removes escape sequences from the supplied string, or +* returns the supplied string without change. +* - This function attempts to execute even if an error has already +* occurred. + +*-- +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS + int old_val; + +/* Get a pointer to Thread-specific global data. */ + astGET_GLOBALS(NULL); + +/* Save the old value. */ + old_val = retain_esc; + +/* Set the new value. */ + if( new_value > 0 ) { + retain_esc = 1; + + } else if( new_value == 0 ) { + retain_esc = 0; + } + +/* Return the old value. */ + return old_val; +} + + +#if defined(MEM_DEBUG) + +/* Check each handle in a list is uniquely connected to one other handle + in both the forward and backward directions. */ + +void CheckList( int *head, int *status ) { + int ok; + int ihandle; + char buf[200]; + astDECLARE_GLOBALS + if( !astOK ) return; + + astGET_GLOBALS(NULL); + + ok = 1; + if ( *head != -1 ) { + ihandle = *head; + if( handles[ handles[ ihandle ].blink ].flink != ihandle || + handles[ handles[ ihandle ].flink ].blink != ihandle ) { + ok = 0; + + } else { + if( CheckThread( ihandle, head, status ) ) { + ihandle= handles[ *head ].blink; + while( ihandle != *head ) { + if( handles[ handles[ ihandle ].blink ].flink != ihandle || + handles[ handles[ ihandle ].flink ].blink != ihandle || + CheckThread( ihandle, head, status ) == 0 ) { + ok = 0; + break; + } + ihandle= handles[ ihandle ].blink; + } + } + } + } + + if( !ok ) { + printf("CheckList error in %s\n", HeadString( head, buf ) ); + printf(" Central handle: %s\n", HandleString( ihandle, buf ) ); + + if( handles[ handles[ ihandle ].blink ].flink != ihandle ) { + printf(" Central handle->blink: %s\n", + HandleString( handles[ ihandle ].blink, buf ) ); + printf(" Central handle->blink->flink: %s\n", + HandleString( handles[ handles[ ihandle ].blink ].flink, buf ) ); + } + + if( handles[ handles[ ihandle ].flink ].blink != ihandle ) { + printf(" Central handle->flink: %s\n", + HandleString( handles[ ihandle ].flink, buf ) ); + printf(" Central handle->flink->blink: %s\n", + HandleString( handles[ handles[ ihandle ].flink ].blink, buf ) ); + } + } + +} + + +/* Check if a specified handle is, or is not, in a given list of handles. */ + +void CheckInList( int ihandle, int *head, int in, int *status ){ + char list[80], buf[200]; + int found = 0; + if( !astOK ) return; + + if ( *head != -1 ) { + if( ihandle == *head ) { + found = 1; + } else { + if( CheckThread( ihandle, head, status ) ) { + int jhandle= handles[ *head ].blink; + while( jhandle != *head ) { + if( ihandle == jhandle ) { + found = 1; + break; + } + jhandle= handles[ jhandle ].blink; + } + } + } + } + + if( in && !found ) { + printf("Error: Handle %s not in %s\n", HandleString( ihandle, buf ), + HeadString( head, list ) ); + } else if( !in && found ) { + printf("Error: Handle %s is in %s\n", HandleString( ihandle, buf ), + HeadString( head, list ) ); + } + +} + +/* Check that a handle is owned by the currently executing thread. */ + +int CheckThread( int ihandle, int *head, int *status ) { + int result = 1; + +#if defined(THREAD_SAFE) + + char buf[200]; + astDECLARE_GLOBALS + if( !astOK ) return result; + + astGET_GLOBALS(NULL); + + if( *head == unowned_handles ) { + if( handles[ ihandle ].thread != -1 ) { + printf("Handle %s has wrong thread: is %d, should " + "be -1 (i.e. unowned)\n", HandleString( ihandle, buf ), + handles[ ihandle ].thread ); + + result = 0; + } + + } else if( *head == free_handles ) { + if( handles[ ihandle ].thread != -1 ) { + printf("Handle %s has wrong thread: is %d, should " + "be -1 (i.e. free)\n", HandleString( ihandle, buf ), + handles[ ihandle ].thread ); + result = 0; + } + + } else if( handles[ ihandle ].thread != AST__THREAD_ID ) { + printf("Handle %s has wrong thread: is %d, should " + "be %d\n", HandleString( ihandle, buf ), + handles[ ihandle ].thread, AST__THREAD_ID ); + result = 0; + } + +#endif + + return result; +} + +void astWatchHandle_( int handle ){ + Watched_Handle = handle; +} + +void astHandleUse_( int handle, const char *verb, ... ){ + va_list args; + if( handle == Watched_Handle ) { + va_start( args, verb ); + astHandleAlarm( verb, args ); + va_end( args ); + } +} + +void astHandleAlarm_( const char *verb, va_list args ){ + char buff[200], hbuf[200]; + astDECLARE_GLOBALS + astGET_GLOBALS(NULL); + + vsprintf( buff, verb, args ); + +#if defined(THREAD_SAFE) + printf( "astHandleAlarm: Handle %s %s (current thread is %d).\n\n", + HandleString( Watched_Handle, hbuf ), buff, AST__THREAD_ID ); +#else + printf( "astHandleAlarm: Handle %s %s.\n\n", + HandleString( Watched_Handle, hbuf ), buff ); +#endif +} + +MYSTATIC const char *HandleString( int ihandle, char *buf ){ +#if defined(THREAD_SAFE) + astDECLARE_GLOBALS + astGET_GLOBALS(NULL); + + if( ihandle >= 0 ) { + sprintf( buf, "(index:%d v:%d c:%d t:%d i:%d cl:%s) [cur. thread: %d]", + ihandle, + handles[ ihandle ].check, + handles[ ihandle ].context, handles[ ihandle ].thread, + handles[ ihandle ].id, + handles[ ihandle ].vtab ? handles[ ihandle ].vtab->class : "<none>", + AST__THREAD_ID ); + } else { + sprintf( buf, "(index:%d <invalid>) [cur. thread: %d]", ihandle, + AST__THREAD_ID ); + } + +#else + if( ihandle >= 0 ) { + sprintf( buf, "(index:%d v:%d c:%d i:%d cl:%s)", ihandle, + handles[ ihandle ].check, + handles[ ihandle ].context, handles[ ihandle ].id, + handles[ ihandle ].vtab ? handles[ ihandle ].vtab->class : "<none>" ); + } else { + sprintf( buf, "(index:%d <invalid>)", ihandle ); + } +#endif + return buf; +} + +MYSTATIC const char *HeadString( int *head, char *list ){ + int i; + astDECLARE_GLOBALS + astGET_GLOBALS(NULL); + + if( head == &free_handles ) { + strcpy( list, "free_handles" ); + +#if defined(THREAD_SAFE) + } else if( head == &unowned_handles ) { + strcpy( list, "unowned_handles" ); +#endif + + } else { + *list = 0; + for( i = 0; i <= context_level; i++ ) { + if( *head == active_handles[ i ] ) { + sprintf( list, "active_handles[%d]", i ); + break; + } + } + if( *list == 0 ) sprintf( list, "unknown handles list with head %d", + *head ); + } + return list; +} + +#endif |