summaryrefslogtreecommitdiffstats
path: root/ast/channel.c
diff options
context:
space:
mode:
Diffstat (limited to 'ast/channel.c')
-rw-r--r--ast/channel.c6458
1 files changed, 6458 insertions, 0 deletions
diff --git a/ast/channel.c b/ast/channel.c
new file mode 100644
index 0000000..9219502
--- /dev/null
+++ b/ast/channel.c
@@ -0,0 +1,6458 @@
+/*
+*class++
+* Name:
+* Channel
+
+* Purpose:
+* Basic (textual) I/O channel.
+
+* Constructor Function:
+c astChannel
+f AST_CHANNEL
+
+* Description:
+* The Channel class implements low-level input/output for the AST
+* library. Writing an Object to a Channel will generate a textual
+* representation of that Object, and reading from a Channel will
+* create a new Object from its textual representation.
+*
+* Normally, when you use a Channel, you should provide "source"
+c and "sink" functions which connect it to an external data store
+f and "sink" routines which connect it to an external data store
+* by reading and writing the resulting text. By default, however,
+* a Channel will read from standard input and write to standard
+* output. Alternatively, a Channel can be told to read or write from
+* specific text files using the SinkFile and SourceFile attributes,
+* in which case no sink or source function need be supplied.
+
+* Inheritance:
+* The Channel class inherits from the Object class.
+
+* Attributes:
+* In addition to those attributes common to all Objects, every
+* Channel also has the following attributes:
+*
+* - Comment: Include textual comments in output?
+* - Full: Set level of output detail
+* - Indent: Indentation increment between objects
+* - ReportLevel: Selects the level of error reporting
+* - SinkFile: The path to a file to which the Channel should write
+* - Skip: Skip irrelevant data?
+* - SourceFile: The path to a file from which the Channel should read
+* - Strict: Generate errors instead of warnings?
+
+* Functions:
+c In addition to those functions applicable to all Objects, the
+c following functions may also be applied to all Channels:
+f In addition to those routines applicable to all Objects, the
+f following routines may also be applied to all Channels:
+*
+c - astWarnings: Return warnings from the previous read or write
+c - astPutChannelData: Store data to pass to source or sink functions
+c - astRead: Read an Object from a Channel
+c - astWrite: Write an Object to a Channel
+f - AST_WARNINGS: Return warnings from the previous read or write
+f - AST_READ: Read an Object from a Channel
+f - AST_WRITE: Write an Object to a Channel
+
+* Copyright:
+* Copyright (C) 1997-2006 Council for the Central Laboratory of the
+* Copyright (C) 2009 Science & Technology Facilities Council.
+* All Rights Reserved.
+* Research Councils
+
+* 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)
+
+* History:
+* 12-AUG-1996 (RFWS):
+* Original version.
+* 6-SEP-1996:
+* Finished initial implementation.
+* 11-DEC-1996 (RFWS):
+* Added support for foreign language source and sink functions.
+* 28-APR-1997 (RFWS):
+* Prevent "-0" being written (use "0" instead).
+* 27-NOV-2002 (DSB):
+* Added astWriteInvocations.
+* 8-JAN-2003 (DSB):
+* - Changed private InitVtab method to protected astInitChannelVtab
+* method.
+* - Modified to use protected Vtab initialisation methods when
+* loading an Object.
+* 1-NOV-2003 (DSB):
+* Change the initialiser so that it accepts source and sink
+* wrapper functions as arguments (for use by derived classes).
+* 16-AUG-2006 (DSB):
+* - Document non-destructive nature of unsuccessful astRead calls
+* on a FitsChan.
+* 3-OCT-2008 (DSB):
+* Added "Strict" attribute.
+* 11-DEC-2008 (DSB):
+* Added astPutChannelData and astChannelData functions.
+* 16-JAN-2009 (DSB):
+* Added astAddWarning and astWarnings.
+* 11-JUN-2009 (DSB):
+* Enable astChannelData to be used from within astRead.
+* 7-DEC-2009 (DSB):
+* Added Indent attribute.
+* 12-FEB-2010 (DSB):
+* Represent AST__BAD externally using the string "<bad>".
+* 23-JUN-2011 (DSB):
+* Added attributes SinkFile and SourceFile.
+* 2-OCT-2012 (DSB):
+* Report an error if an Inf or NaN value is read from the external
+* source.
+*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 Channel
+
+/* Define a string containing the maximum length of keywords used to
+ identify values in the external representation of data. This is
+ deliberately kept small so as to simplify integration with
+ standards such as FITS. */
+#define MAX_NAME "8"
+
+/* Max length of string returned by GetAttrib */
+#define GETATTRIB_BUFF_LEN 50
+
+/* String used to represent AST__BAD externally. */
+#define BAD_STRING "<bad>"
+
+/* Include files. */
+/* ============== */
+/* Interface definitions. */
+/* ---------------------- */
+
+#include "globals.h" /* Thread-safe global data access */
+#include "error.h" /* Error reporting facilities */
+#include "memory.h" /* Memory allocation facilities */
+#include "object.h" /* Base Object class */
+#include "channel.h" /* Interface definition for this class */
+#include "loader.h" /* Interface to the global loader */
+#include "keymap.h" /* Storing arbitrary data in an AST Object */
+#include "pointset.h" /* For AST__BAD */
+
+/* Error code definitions. */
+/* ----------------------- */
+#include "ast_err.h" /* AST error codes */
+
+/* C header files. */
+/* --------------- */
+#include <ctype.h>
+#include <errno.h>
+#include <float.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Module Variables. */
+/* ================= */
+
+/* Address of this static variable is used as a unique identifier for
+ member of this class. */
+static int class_check;
+
+/* Pointers to parent class methods which are extended by this class. */
+static const char *(* parent_getattrib)( AstObject *, const char *, int * );
+static int (* parent_testattrib)( AstObject *, const char *, int * );
+static void (* parent_clearattrib)( AstObject *, const char *, int * );
+static void (* parent_setattrib)( AstObject *, const char *, int * );
+
+/* Define macros for accessing each item of thread specific global data. */
+#ifdef THREAD_SAFE
+
+/* Define how to initialise thread-specific globals. */
+#define GLOBAL_inits \
+ globals->Class_Init = 0; \
+ globals->AstReadClassData_Msg = 0; \
+ globals->GetAttrib_Buff[ 0 ] = 0; \
+ globals->Items_Written = 0; \
+ globals->Current_Indent = 0; \
+ globals->Nest = -1; \
+ globals->Nwrite_Invoc = 0; \
+ globals->Object_Class = NULL; \
+ globals->Values_List = NULL; \
+ globals->Values_Class = NULL; \
+ globals->Values_OK = NULL; \
+ globals->End_Of_Object = NULL; \
+ globals->Channel_Data = NULL;
+
+/* Create the function that initialises global data for this module. */
+astMAKE_INITGLOBALS(Channel)
+
+/* Define macros for accessing each item of thread specific global data. */
+#define class_init astGLOBAL(Channel,Class_Init)
+#define class_vtab astGLOBAL(Channel,Class_Vtab)
+#define astreadclassdata_msg astGLOBAL(Channel,AstReadClassData_Msg)
+#define getattrib_buff astGLOBAL(Channel,GetAttrib_Buff)
+#define items_written astGLOBAL(Channel,Items_Written)
+#define current_indent astGLOBAL(Channel,Current_Indent)
+#define nest astGLOBAL(Channel,Nest)
+#define nwrite_invoc astGLOBAL(Channel,Nwrite_Invoc)
+#define object_class astGLOBAL(Channel,Object_Class)
+#define values_list astGLOBAL(Channel,Values_List)
+#define values_class astGLOBAL(Channel,Values_Class)
+#define values_ok astGLOBAL(Channel,Values_OK)
+#define end_of_object astGLOBAL(Channel,End_Of_Object)
+#define channel_data astGLOBAL(Channel,Channel_Data)
+
+
+
+static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
+#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 );
+#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 );
+
+static pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;
+#define LOCK_MUTEX3 pthread_mutex_lock( &mutex3 );
+#define UNLOCK_MUTEX3 pthread_mutex_unlock( &mutex3 );
+
+/* If thread safety is not needed, declare and initialise globals at static
+ variables. */
+#else
+
+/* Contextual error message reported in astReadClassData? */
+static int astreadclassdata_msg = 0;
+
+/* Buffer returned by GetAttrib. */
+static char getattrib_buff[ GETATTRIB_BUFF_LEN + 1 ];
+
+/* Count of the number of output items written since the last "Begin"
+ or "IsA" item. */
+static int items_written = 0;
+
+/* Amount of indentation to be applied to the next output item. */
+static int current_indent = 0;
+
+/* Nesting level, used to keep track of data associated with building
+ Objects when they contain other Objects. */
+static int nest = -1;
+
+/* The number of times astWrite has been invoked. */
+static int nwrite_invoc = 0;
+
+/* Pointer to a user-supplied block of memory to be made available to
+ source or sink functions via the astChannelData function. */
+static void *channel_data = NULL;
+
+/***
+ The following items are all pointers to dynamically allocated
+ arrays (stacks) that grow as necessary to accommodate one element
+ for each level of nesting (one more than the value of "nest").
+***/
+
+/* Stack of pointers to null-terminated character strings giving the
+ names of the classes of the Objects being built at each nesting
+ level. */
+static char **object_class = NULL;
+
+/* Stack of pointers to the elements designated as the "heads" of
+ circular, doubly linked lists of name-value associations. */
+static AstChannelValue **values_list = NULL;
+
+/* Stack of pointers to null-terminated character strings giving the
+ names of the classes for which the values held in the values lists
+ are intended. */
+static char **values_class = NULL;
+
+/* Stack of flags indicating whether the values held in the values
+ lists are intended for the class loaders currently executing to
+ build Objects at each nesting level. */
+static int *values_ok = NULL;
+
+/* Stack of flags indicating whether "End" items have been read for
+ the Objects being built at each nesting level. */
+static int *end_of_object = NULL;
+
+
+/* Define the class virtual function table and its initialisation flag
+ as static variables. */
+static AstChannelVtab class_vtab; /* Virtual function table */
+static int class_init = 0; /* Virtual function table initialised? */
+#define LOCK_MUTEX2
+#define UNLOCK_MUTEX2
+#define LOCK_MUTEX3
+#define UNLOCK_MUTEX3
+
+#endif
+
+/* External Interface Function Prototypes. */
+/* ======================================= */
+/* The following functions have public prototypes only (i.e. no
+ protected prototypes), so we must provide local prototypes for use
+ within this module. */
+AstChannel *astChannelForId_( const char *(*)( void ),
+ char *(*)( const char *(*)( void ), int * ),
+ void (*)( const char * ),
+ void (*)( void (*)( const char * ),
+ const char *, int * ),
+ const char *, ... );
+AstChannel *astChannelId_( const char *(*)( void ), void (*)( const char * ), const char *, ... );
+
+/* Prototypes for Private Member Functions. */
+/* ======================================== */
+static AstObject *Read( AstChannel *, int * );
+static AstObject *ReadObject( AstChannel *, const char *, AstObject *, int * );
+static AstChannelValue *FreeValue( AstChannelValue *, int * );
+static AstChannelValue *LookupValue( const char *, int * );
+static AstKeyMap *Warnings( AstChannel *, int * );
+static char *GetNextText( AstChannel *, int * );
+static char *InputTextItem( AstChannel *, int * );
+static char *ReadString( AstChannel *, const char *, const char *, int * );
+static char *SourceWrap( const char *(*)( void ), int * );
+static const char *GetAttrib( AstObject *, const char *, int * );
+static double ReadDouble( AstChannel *, const char *, double, int * );
+static int GetComment( AstChannel *, int * );
+static int GetFull( AstChannel *, int * );
+static int GetSkip( AstChannel *, int * );
+static int GetStrict( AstChannel *, int * );
+static int ReadInt( AstChannel *, const char *, int, int * );
+static int TestAttrib( AstObject *, const char *, int * );
+static int TestComment( AstChannel *, int * );
+static int TestFull( AstChannel *, int * );
+static int TestSkip( AstChannel *, int * );
+static int TestStrict( AstChannel *, int * );
+static int Use( AstChannel *, int, int, int * );
+static int Write( AstChannel *, AstObject *, int * );
+static void AddWarning( AstChannel *, int, const char *, const char *, int * );
+static void AppendValue( AstChannelValue *, AstChannelValue **, int * );
+static void ClearAttrib( AstObject *, const char *, int * );
+static void ClearComment( AstChannel *, int * );
+static void ClearFull( AstChannel *, int * );
+static void ClearSkip( AstChannel *, int * );
+static void ClearStrict( AstChannel *, int * );
+static void ClearValues( AstChannel *, int * );
+static void Copy( const AstObject *, AstObject *, int * );
+static void Delete( AstObject *, int * );
+static void Dump( AstObject *, AstChannel *, int * );
+static void GetNextData( AstChannel *, int, char **, char **, int * );
+static void OutputTextItem( AstChannel *, const char *, int * );
+static void PutChannelData( AstChannel *, void *, int * );
+static void PutNextText( AstChannel *, const char *, int * );
+static void ReadClassData( AstChannel *, const char *, int * );
+static void RemoveValue( AstChannelValue *, AstChannelValue **, int * );
+static void SetAttrib( AstObject *, const char *, int * );
+static void SetComment( AstChannel *, int, int * );
+static void SetFull( AstChannel *, int, int * );
+static void SetSkip( AstChannel *, int, int * );
+static void SetStrict( AstChannel *, int, int * );
+static void SinkWrap( void (*)( const char * ), const char *, int * );
+static void Unquote( AstChannel *, char *, int * );
+static void WriteBegin( AstChannel *, const char *, const char *, int * );
+static void WriteDouble( AstChannel *, const char *, int, int, double, const char *, int * );
+static void WriteEnd( AstChannel *, const char *, int * );
+static void WriteInt( AstChannel *, const char *, int, int, int, const char *, int * );
+static void WriteIsA( AstChannel *, const char *, const char *, int * );
+static void WriteObject( AstChannel *, const char *, int, int, AstObject *, const char *, int * );
+static void WriteString( AstChannel *, const char *, int, int, const char *, const char *, int * );
+
+static int GetReportLevel( AstChannel *, int * );
+static int TestReportLevel( AstChannel *, int * );
+static void ClearReportLevel( AstChannel *, int * );
+static void SetReportLevel( AstChannel *, int, int * );
+
+static int GetIndent( AstChannel *, int * );
+static int TestIndent( AstChannel *, int * );
+static void ClearIndent( AstChannel *, int * );
+static void SetIndent( AstChannel *, int, int * );
+
+static const char *GetSourceFile( AstChannel *, int * );
+static int TestSourceFile( AstChannel *, int * );
+static void ClearSourceFile( AstChannel *, int * );
+static void SetSourceFile( AstChannel *, const char *, int * );
+
+static const char *GetSinkFile( AstChannel *, int * );
+static int TestSinkFile( AstChannel *, int * );
+static void ClearSinkFile( AstChannel *, int * );
+static void SetSinkFile( AstChannel *, const char *, int * );
+
+/* Member functions. */
+/* ================= */
+static void AddWarning( AstChannel *this, int level, const char *msg,
+ const char *method, int *status ) {
+/*
+*+
+* Name:
+* astAddWarning
+
+* Purpose:
+* Add a warning to a Channel.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* void astAddWarning( AstChannel *this, int level, const char *msg,
+* const char *method, int status, ... )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function stores a warning message inside a Channel. These
+* messages can be retirieved using astWarnings.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* level
+* Ignore the warning if the ReportLevel attribute value is less
+* than "level".
+* msg
+* The wanting message to store. It may contain printf format
+* specifiers. If a NULL pointer is supplied, all warnings
+* currently stored in the Channel are removed.
+* method
+* The method name.
+* status
+* Inherited status value.
+* ...
+* Extra values to substitute into the message string as
+* replacements for the printf format specifiers.
+*-
+
+* Note: The expansion of the printf format specifiers is done in the
+* astAddWarning_ wrapper function. The AddWarning functions defined by
+* each class receives the fully expanded message and does not have a
+* variable argument list. The variable argument list is included in the
+* above prologue in order to document the wrapper function.
+
+*/
+
+/* Local Variables: */
+ int i; /* Message index */
+ char *a; /* Pointer to copy of message */
+
+/* If a NULL pointer was supplied, free all warnings currently in the
+ Channel. Do this before checking the inherited status so that it works
+ even if an error has occurred. */
+ if( !msg ) {
+ for( i = 0; i < this->nwarn; i++ ) {
+ (this->warnings)[ i ] = astFree( (this->warnings)[ i ] );
+ }
+ this->warnings = astFree( this->warnings );
+ this->nwarn = 0;
+ return;
+ }
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Only proceed if the message level is sufficiently important. */
+ if( astGetReportLevel( this ) >= level ) {
+
+/* If we are being strict, issue an error rather than a warning. */
+ if( astGetStrict( this ) ) {
+ if( astOK ) {
+ astError( AST__BADIN, "%s(%s): %s", status, method,
+ astGetClass( this ), msg );
+ }
+
+/* Otherwise, we store a copy of the message in the Channel. */
+ } else {
+
+/* Allocate memory and store a copy of th supplied string in it. */
+ a = astStore( NULL, msg, strlen( msg ) + 1 );
+
+/* Expand the array of warning pointers in ther Channel structure. */
+ this->warnings = astGrow( this->warnings, this->nwarn + 1,
+ sizeof( char * ) );
+
+/* If all is OK so far, store the new warning pointer, and increment the
+ number of warnings in the Channel. */
+ if( astOK ) {
+ (this->warnings)[ (this->nwarn)++ ] = a;
+
+/* Otherwise, attempt to free the memory holding the copy of the warning. */
+ } else {
+ a = astFree( a );
+ }
+ }
+ }
+}
+
+static void AppendValue( AstChannelValue *value, AstChannelValue **head, int *status ) {
+/*
+* Name:
+* AppendValue
+
+* Purpose:
+* Append a Value structure to a list.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* void AppendValue( AstChannelValue *value, AstChannelValue **head, int *status )
+
+* Class Membership:
+* Channel member function.
+
+* Description:
+* This function appends a Value structure to a doubly linked
+* circular list of such structures. The new list element is
+* inserted just in front of the element occupying the "head of
+* list" position (i.e. it becomes the new last element in the
+* list).
+
+* Parameters:
+* value
+* Pointer to the new element. This must not already be in the
+* list.
+* head
+* Address of a pointer to the element at the head of the list
+* (this pointer should be NULL if the list is initially
+* empty). This pointer will only be updated if a new element is
+* being added to an empty list.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - This function does not perform error chacking and does not
+* generate errors.
+*/
+
+/* If the list is initially empty, the sole new element points at
+ itself. */
+ if ( !*head ) {
+ value->flink = value;
+ value->blink = value;
+
+/* Update the list head to identify the new element. */
+ *head = value;
+
+/* Otherwise, insert the new element in front of the element at the
+ head of the list. */
+ } else {
+ value->flink = *head;
+ value->blink = ( *head )->blink;
+ ( *head )->blink = value;
+ value->blink->flink = value;
+ }
+}
+
+void *astChannelData_( void ) {
+/*
+c++
+* Name:
+* astChannelData
+
+* Purpose:
+* Return a pointer to user-supplied data stored with a Channel.
+
+* Type:
+* Public macro.
+
+* Synopsis:
+* #include "channel.h"
+* void *astChannelData
+
+* Class Membership:
+* Channel macro.
+
+* Description:
+* This macro is intended to be used within the source or sink
+* functions associated with a Channel. It returns any pointer
+* previously stored in the Channel (that is, the Channel that has
+* invoked the source or sink function) using astPutChannelData.
+*
+* This mechanism is a thread-safe alternative to passing file
+* descriptors, etc, via static global variables.
+
+* Returned Value:
+* astChannelData
+* The pointer previously stored with the Channel using
+* astPutChannelData. A NULL pointer will be returned if no such
+* pointer has been stored with the Channel.
+
+* Applicability:
+* Channel
+* This macro applies to all Channels.
+
+* Notes:
+* - This routine is not available in the Fortran 77 interface to
+* the AST library.
+c--
+*/
+ astDECLARE_GLOBALS
+ astGET_GLOBALS(NULL);
+ return channel_data;
+}
+
+static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* ClearAttrib
+
+* Purpose:
+* Clear an attribute value for a Channel.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* void ClearAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* Channel member function (over-rides the astClearAttrib protected
+* method inherited from the Object class).
+
+* Description:
+* This function clears the value of a specified attribute for a
+* Channel, so that the default value will subsequently be used.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* attrib
+* Pointer to a null terminated string specifying the attribute
+* name. This should be in lower case with no surrounding white
+* space.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ AstChannel *this; /* Pointer to the Channel structure */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Channel structure. */
+ this = (AstChannel *) this_object;
+
+/* Check the attribute name and clear the appropriate attribute. */
+
+/* Comment. */
+/* -------- */
+ if ( !strcmp( attrib, "comment" ) ) {
+ astClearComment( this );
+
+/* Full. */
+/* ----- */
+ } else if ( !strcmp( attrib, "full" ) ) {
+ astClearFull( this );
+
+/* Indent. */
+/* ------- */
+ } else if ( !strcmp( attrib, "indent" ) ) {
+ astClearIndent( this );
+
+/* ReportLevel. */
+/* ------------ */
+ } else if ( !strcmp( attrib, "reportlevel" ) ) {
+ astClearReportLevel( this );
+
+/* Skip. */
+/* ----- */
+ } else if ( !strcmp( attrib, "skip" ) ) {
+ astClearSkip( this );
+
+/* SourceFile. */
+/* ----------- */
+ } else if ( !strcmp( attrib, "sourcefile" ) ) {
+ astClearSourceFile( this );
+
+/* SinkFile. */
+/* --------- */
+ } else if ( !strcmp( attrib, "sinkfile" ) ) {
+ astClearSinkFile( this );
+
+/* Strict. */
+/* ------- */
+ } else if ( !strcmp( attrib, "strict" ) ) {
+ astClearStrict( this );
+
+/* If the attribute is still not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ (*parent_clearattrib)( this_object, attrib, status );
+ }
+}
+
+static void ClearValues( AstChannel *this, int *status ) {
+/*
+* Name:
+* ClearValues
+
+* Purpose:
+* Clear the current values list.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* void ClearValues( AstChannel *this, int *status )
+
+* Class Membership:
+* Channel member function.
+
+* Description:
+* This function clears any (un-read) Value structures remaining in
+* the current values list (i.e. at the current nesting level). It
+* should be invoked after all required values have been read.
+*
+* If the values list has not been read, or if any remaining values
+* are found (i.e. the list is not empty) then this indicates an
+* unrecognised input class or an input value that has not been
+* read by a class loader. This implies an error in the loader, or
+* bad input data, so an error is reported.
+*
+* All resources used by any remaining Value structures are freed
+* and the values list is left in an empty state.
+
+* Parameters:
+* this
+* Pointer to the Channel being read. This is only used for
+* constructing error messages. It must not be NULL.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - This function attempts to execute even if the global error
+* status is set on entry, although no further error report will be
+* made if it subsequently fails under these circumstances.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ AstChannelValue **head; /* Address of pointer to values list */
+ AstChannelValue *value; /* Pointer to value list element */
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this);
+
+/* If "values_class" is non-NULL, then the values list has previously
+ been filled with Values for a class. */
+ if ( values_class[ nest ] ) {
+
+/* If "values_ok" is zero, however, then these Values have not yet
+ been read by a class loader. This must be due to a bad class name
+ associated with them or because the class data are not available in
+ the correct order. If we are using strict error reporting, then report
+ an error (unless the error status is already set). */
+ if ( astGetStrict( this ) && !values_ok[ nest ] && astOK ) {
+ astError( AST__BADIN,
+ "astRead(%s): Invalid class structure in input data.", status,
+ astGetClass( this ) );
+ astError( AST__BADIN,
+ "Class \"%s\" is invalid or out of order within a %s.", status,
+ values_class[ nest ], object_class[ nest ] );
+ }
+
+/* Free the memory holding the class string. */
+ values_class[ nest ] = astFree( values_class[ nest ] );
+ }
+
+/* Reset the "values_ok" flag. */
+ values_ok[ nest ] = 0;
+
+/* Now clear any Values remaining in the values list. Obtain the
+ address of the pointer to the head of this list (at the current
+ nesting level) and loop to remove Values from the list while it is
+ not empty. */
+ head = values_list + nest;
+ while ( *head ) {
+
+/* Obtain a pointer to the first element. */
+ value = *head;
+
+/* Issue a warning. */
+ if ( value->is_object ) {
+ astAddWarning( this, 1, "The Object \"%s = <%s>\" was "
+ "not recognised as valid input.", "astRead", status,
+ value->name, astGetClass( value->ptr.object ) );
+ } else {
+ astAddWarning( this, 1, "The value \"%s = %s\" was not "
+ "recognised as valid input.", "astRead", status,
+ value->name, value->ptr.string );
+ }
+
+/* Remove the Value structure from the list (which updates the head of
+ list pointer) and free its resources. */
+ RemoveValue( value, head, status );
+ value = FreeValue( value, status );
+ }
+}
+
+static AstChannelValue *FreeValue( AstChannelValue *value, int *status ) {
+/*
+* Name:
+* FreeValue
+
+* Purpose:
+* Free a dynamically allocated Value structure.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* AstChannelValue *FreeValue( AstChannelValue *value, int *status )
+
+* Class Membership:
+* Channel member function.
+
+* Description:
+* This function frees a dynamically allocated Value structure,
+* releasing all resources used by it. The structure contents must
+* have been correctly initialised.
+
+* Parameters:
+* value
+* Pointer to the Value structure to be freed.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A NULL pointer is always returned.
+
+* Notes:
+* - This function attempts to execute even if the global error
+* status is set on entry, although no further error report will be
+* made if it subsequently fails under these circumstances.
+*/
+
+/* Check that a non-NULL pointer has been supplied. */
+ if ( value ) {
+
+/* If the "name" component has been allocated, then free it. */
+ if ( value->name ) value->name = astFree( value->name );
+
+/* If the "ptr" component identifies an Object, then annul the Object
+ pointer. */
+ if ( value->is_object ) {
+ if ( value->ptr.object ) {
+ value->ptr.object = astAnnul( value->ptr.object );
+ }
+
+/* Otherwise, if it identifies a string, then free the string. */
+ } else {
+ if ( value->ptr.string ) {
+ value->ptr.string = astFree( value->ptr.string );
+ }
+ }
+
+/* Free the Value structure itself. */
+ value = astFree( value );
+ }
+
+/* Return a NULL pointer. */
+ return NULL;
+}
+
+static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* GetAttrib
+
+* Purpose:
+* Get the value of a specified attribute for a Channel.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* const char *GetAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* Channel member function (over-rides the protected astGetAttrib
+* method inherited from the Object class).
+
+* Description:
+* This function returns a pointer to the value of a specified
+* attribute for a Channel, formatted as a character string.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* 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.
+* 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 Channel, 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 Channel. 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 /* Declare the thread specific global data */
+ AstChannel *this; /* Pointer to the Channel structure */
+ const char *result; /* Pointer value to return */
+ int comment; /* Comment attribute value */
+ int full; /* Full attribute value */
+ int indent; /* Indent attribute value */
+ int report_level; /* ReportLevel attribute value */
+ int skip; /* Skip attribute value */
+ int strict; /* Report errors insead of warnings? */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this_object);
+
+/* Obtain a pointer to the Channel structure. */
+ this = (AstChannel *) this_object;
+
+/* 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. */
+
+/* Comment. */
+/* -------- */
+ if ( !strcmp( attrib, "comment" ) ) {
+ comment = astGetComment( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", comment );
+ result = getattrib_buff;
+ }
+
+/* Full. */
+/* ----- */
+ } else if ( !strcmp( attrib, "full" ) ) {
+ full = astGetFull( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", full );
+ result = getattrib_buff;
+ }
+
+/* Indent. */
+/* ------- */
+ } else if ( !strcmp( attrib, "indent" ) ) {
+ indent = astGetIndent( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", indent );
+ result = getattrib_buff;
+ }
+
+/* ReportLevel. */
+/* ------------ */
+ } else if ( !strcmp( attrib, "reportlevel" ) ) {
+ report_level = astGetReportLevel( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", report_level );
+ result = getattrib_buff;
+ }
+
+/* Skip. */
+/* ----- */
+ } else if ( !strcmp( attrib, "skip" ) ) {
+ skip = astGetSkip( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", skip );
+ result = getattrib_buff;
+ }
+
+/* SourceFile. */
+/* ----------- */
+ } else if ( !strcmp( attrib, "sourcefile" ) ) {
+ result = astGetSourceFile( this );
+
+/* SinkFile. */
+/* --------- */
+ } else if ( !strcmp( attrib, "sinkfile" ) ) {
+ result = astGetSinkFile( this );
+
+/* Strict. */
+/* ------- */
+ } else if ( !strcmp( attrib, "strict" ) ) {
+ strict = astGetStrict( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", strict );
+ result = getattrib_buff;
+ }
+
+/* If the attribute name was not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ result = (*parent_getattrib)( this_object, attrib, status );
+ }
+
+/* Return the result. */
+ return result;
+
+}
+
+static void GetNextData( AstChannel *this, int skip, char **name,
+ char **val, int *status ) {
+/*
+*+
+* Name:
+* astGetNextData
+
+* Purpose:
+* Read the next item of data from a data source.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* void astGetNextData( AstChannel *this, int skip, char **name,
+* char **val )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function reads the next item of input data from a data
+* source associated with a Channel and returns the result.
+*
+* It is layered conceptually on the astGetNextText method, but
+* instead of returning the raw input text, it decodes it and
+* returns name/value pairs ready for use. Note that in some
+* derived classes, where the data are not stored as text, this
+* function may not actually use astGetNextText, but will access
+* the data directly.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* skip
+* A non-zero value indicates that a new Object is to be read,
+* and that all input data up to the next "Begin" item are to be
+* skipped in order to locate it. This is useful if the data
+* source contains AST objects interspersed with other data (but
+* note that these other data cannot appear inside AST Objects,
+* only between them).
+*
+* A zero value indicates that all input data are significant
+* and the next item will therefore be read and an attempt made
+* to interpret it whatever it contains. Any other data
+* inter-mixed with AST Objects will then result in an error.
+* name
+* An address at which to store a pointer to a null-terminated
+* dynamically allocated string containing the name of the next
+* item in the input data stream. This name will be in lower
+* case with no surrounding white space. It is the callers
+* responsibilty to free the memory holding this string (using
+* astFree) when it is no longer required.
+*
+* A NULL pointer value will be returned (without error) to
+* indicate when there are no further input data items to be
+* read.
+* val
+* An address at which to store a pointer to a null-terminated
+* dynamically allocated string containing the value associated
+* with the next item in the input data stream. No case
+* conversion is performed on this string and all white space is
+* potentially significant. It is the callers responsibilty to
+* free the memory holding this string (using astFree) when it
+* is no longer required.
+*
+* The returned pointer will be NULL if an Object data item is
+* read (see the "Data Representation" section).
+
+* Data Representation:
+* The returned data items fall into the following categories:
+*
+* - Begin: Identified by the name string "begin", this indicates
+* the start of an Object definition. The associated value string
+* gives the class name of the Object being defined.
+*
+* - IsA: Identified by the name string "isa", this indicates the
+* end of the data associated with a particular class structure
+* within the definiton of a larger Object. The associated value
+* string gives the name of the class whose data have just been
+* read.
+*
+* - End: Identified by the name string "end", this indicates the
+* end of the data associated with a complete Object
+* definition. The associated value string gives the class name of
+* the Object whose definition is being ended.
+*
+* - Non-Object: Identified by any other name string plus a
+* non-NULL "val" pointer, this gives the value of a non-Object
+* structure component (instance variable). The name identifies
+* which instance variable it is (within the context of the class
+* whose data are being read) and the value is encoded as a string.
+*
+* - Object: Identified by any other name string plus a NULL "val"
+* pointer, this identifies the value of an Object structure
+* component (instance variable). The name identifies which
+* instance variable it is (within the context of the class whose
+* data are being read) and the value is given by subsequent data
+* items (so the next item should be a "Begin" item).
+
+* Notes:
+* - NULL pointer values will be returned if this function is
+* invoked with the global error status set, or if it should fail
+* for any reason.
+* - This method is provided primarily so that derived classes may
+* over-ride it in order to read from alternative data sources. It
+* provides a higher-level interface than astGetNextText, so is
+* suitable for classes that either need to read textual data in a
+* different format, or to read from non-textual data sources.
+*-
+*/
+
+/* Local Variables: */
+ char *line; /* Pointer to input text line */
+ int done; /* Data item read? */
+ int i; /* Loop counter for string characters */
+ int len; /* Length of input text line */
+ int nc1; /* Offset to start of first field */
+ int nc2; /* Offset to end of first field */
+ int nc3; /* Offset to start of second field */
+ int nc; /* Number of charaters read by "astSscanf" */
+
+/* Initialise the returned values. */
+ *name = NULL;
+ *val = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Read the next input line as text (the loop is needed to allow
+ initial lines to be skipped if the "skip" flag is set). */
+ done = 0;
+ while ( !done && ( line = InputTextItem( this, status ) ) && astOK ) {
+
+/* If OK, determine the line length. */
+ len = strlen( line );
+
+/* Non-Object value. */
+/* ----------------- */
+/* Test for lines of the form " name = value" (or similar), where the
+ name is no more than MAX_NAME characters long (the presence of a
+ value on the right hand side indicates that this is a non-Object
+ value, encoded as a string). Ignore these lines if the "skip" flag
+ is set. */
+ if ( nc = 0,
+ ( !skip
+ && ( 0 == astSscanf( line,
+ " %n%*" MAX_NAME "[^ \t=]%n = %n%*[^\n]%n",
+ &nc1, &nc2, &nc3, &nc ) )
+ && ( nc >= len ) ) ) {
+
+/* Note we have found a data item. */
+ done = 1;
+
+/* Extract the name and value fields. */
+ *name = astString( line + nc1, nc2 - nc1 );
+ *val = astString( line + nc3, len - nc3 );
+
+/* If OK, truncate the value to remove any trailing white space. */
+ if ( astOK ) {
+ i = len - nc3 - 1;
+ while ( ( i >= 0 ) && isspace( ( *val )[ i ] ) ) i--;
+ ( *val )[ i + 1 ] = '\0';
+
+/* Also remove any quotes from the string. */
+ Unquote( this, *val, status );
+ }
+
+/* Object value. */
+/* ------------- */
+/* Test for lines of the form " name = " (or similar), where the name
+ is no more than MAX_NAME characters long (the absence of a value on
+ the right hand side indicates that this is an Object, whose
+ definition follows on subsequent input lines). Ignore these lines
+ if the "skip" flag is set. */
+ } else if ( nc = 0,
+ ( !skip
+ && ( 0 == astSscanf( line,
+ " %n%*" MAX_NAME "[^ \t=]%n = %n",
+ &nc1, &nc2, &nc ) )
+ && ( nc >= len ) ) ) {
+
+/* Note we have found a data item. */
+ done = 1;
+
+/* Extract the name field but leave the value pointer as NULL. */
+ *name = astString( line + nc1, nc2 - nc1 );
+
+/* Begin. */
+/* ------ */
+/* Test for lines of the form " Begin Class " (or similar). */
+ } else if ( nc = 0,
+ ( ( 0 == astSscanf( line,
+ " %*1[Bb]%*1[Ee]%*1[Gg]%*1[Ii]%*1[Nn] %n%*s%n %n",
+ &nc1, &nc2, &nc ) )
+ && ( nc >= len ) ) ) {
+
+/* Note we have found a data item. */
+ done = 1;
+
+/* Set the returned name to "begin" and extract the associated class
+ name for the value. Store both of these in dynamically allocated
+ strings. */
+ *name = astString( "begin", 5 );
+ *val = astString( line + nc1, nc2 - nc1 );
+
+/* IsA. */
+/* ---- */
+/* Test for lines of the form " IsA Class " (or similar). Ignore these
+ lies if the "skip" flag is set. */
+ } else if ( nc = 0,
+ ( !skip
+ && ( 0 == astSscanf( line,
+ " %*1[Ii]%*1[Ss]%*1[Aa] %n%*s%n %n",
+ &nc1, &nc2, &nc ) )
+ && ( nc >= len ) ) ) {
+
+/* Note we have found a data item. */
+ done = 1;
+
+/* Set the returned name to "isa" and extract the associated class
+ name for the value. */
+ *name = astString( "isa", 3 );
+ *val = astString( line + nc1, nc2 - nc1 );
+
+/* End. */
+/* ---- */
+/* Test for lines of the form " End Class " (or similar). Ignore these
+ lines if the "skip" flag is set. */
+ } else if ( nc = 0,
+ ( !skip
+ && ( 0 == astSscanf( line,
+ " %*1[Ee]%*1[Nn]%*1[Dd] %n%*s%n %n",
+ &nc1, &nc2, &nc ) )
+ && ( nc >= len ) ) ) {
+
+/* Note we have found a data item. */
+ done = 1;
+
+/* If found, set the returned name to "end" and extract the associated
+ class name for the value. */
+ *name = astString( "end", 3 );
+ *val = astString( line + nc1, nc2 - nc1 );
+
+/* If the input line didn't match any of the above and the "skip" flag
+ is not set, then report an error. */
+ } else if ( !skip ) {
+ astError( AST__BADIN,
+ "astRead(%s): Cannot interpret the input data: \"%s\".", status,
+ astGetClass( this ), line );
+ }
+
+/* Free the memory holding the input data as text. */
+ line = astFree( line );
+ }
+
+/* If successful, convert the name to lower case. */
+ if ( astOK && *name ) {
+ for ( i = 0; ( *name )[ i ]; i++ ) {
+ ( *name )[ i ] = tolower( ( *name )[ i ] );
+ }
+ }
+
+/* If an error occurred, ensure that any memory allocated is freed and
+ that NULL pointer values are returned. */
+ if ( !astOK ) {
+ *name = astFree( *name );
+ *val = astFree( *val );
+ }
+}
+
+static char *GetNextText( AstChannel *this, int *status ) {
+/*
+*+
+* Name:
+* GetNextText
+
+* Purpose:
+* Read the next line of input text from a data source.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* char *astGetNextText( AstChannel *this )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function reads the next "raw" input line of text from the
+* data source associated with a Channel.
+*
+* Each line is returned as a pointer to a null-terminated string
+* held in dynamic memory, and it is the caller's responsibility to
+* free this memory (using astFree) when it is no longer
+* required. A NULL pointer is returned if there are no more input
+* lines to be read.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+
+* Returned Value:
+* Pointer to a null-terminated string containing the input line
+* (held in dynamically allocated memory, which must be freed by
+* the caller when no longer required). A NULL pointer is returned
+* if there are no more input lines to be read.
+
+* 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.
+* - This method is provided primarily so that derived classes may
+* over-ride it in order to read from alternative (textual) data
+* sources.
+*-
+*/
+
+/* Local Constants: */
+#define MIN_CHARS 81 /* Initial size for allocating memory */
+#define ERRBUF_LEN 80
+
+/* Local Variables: */
+ FILE *fd; /* Input file descriptor */
+ char *errstat; /* Pointer for system error message */
+ char *line; /* Pointer to line data to be returned */
+ char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */
+ const char *sink_file; /* Path to output sink file */
+ const char *source_file; /* Path to source file */
+ int c; /* Input character */
+ int len; /* Length of input line */
+ int readstat; /* "errno" value set by "getchar" */
+ int size; /* Size of allocated memory */
+
+/* Initialise. */
+ line = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return line;
+
+/* If the SourceFile attribute of the Channel specifies an input file,
+ but no input file has yet been opened, open it now. Report an error if
+ it is the same as the sink file. */
+ if( astTestSourceFile( this ) && !this->fd_in ) {
+ source_file = astGetSourceFile( this );
+
+ if( this->fd_out ) {
+ sink_file = astGetSinkFile( this );
+ if( astOK && !strcmp( sink_file, source_file ) ) {
+ astError( AST__RDERR, "astRead(%s): Failed to open input "
+ "SourceFile '%s' - the file is currently being used "
+ "as the output SinkFile.", status, astGetClass( this ),
+ source_file );
+ }
+ }
+
+ if( astOK ) {
+ this->fd_in = fopen( source_file, "r" );
+ if( !this->fd_in ) {
+ if ( errno ) {
+#if HAVE_STRERROR_R
+ strerror_r( errno, errbuf, ERRBUF_LEN );
+ errstat = errbuf;
+#else
+ errstat = strerror( errno );
+#endif
+ astError( AST__RDERR, "astRead(%s): Failed to open input "
+ "SourceFile '%s' - %s.", status, astGetClass( this ),
+ source_file, errstat );
+ } else {
+ astError( AST__RDERR, "astRead(%s): Failed to open input "
+ "SourceFile '%s'.", status, astGetClass( this ),
+ source_file );
+ }
+ }
+
+ }
+ }
+
+/* Source function defined, but no input file. */
+/* ------------------------------------------- */
+/* If no active input file descriptor is stored in the Channel, but
+ a source function (and its wrapper function) is defined for the
+ Channel, use the wrapper function to invoke the source function to
+ read a line of input text. This is returned in a dynamically
+ allocated string. */
+ if ( !this->fd_in && this->source && this->source_wrap ) {
+
+/* About to call an externally supplied function which may not be
+ thread-safe, so lock a mutex first. Also store the channel data
+ pointer in a global variable so that it can be accessed in the source
+ function using macro astChannelData. */
+ astStoreChannelData( this );
+ LOCK_MUTEX3;
+ line = ( *this->source_wrap )( this->source, status );
+ UNLOCK_MUTEX3;
+
+/* Input file defined, or no source function. */
+/* ------------------------------------------ */
+/* Read the line from the input file or from standard input. */
+ } else if( astOK ) {
+ c = '\0';
+ len = 0;
+ size = 0;
+
+/* Choose the file descriptor to use. */
+ fd = this->fd_in ? this->fd_in : stdin;
+
+/* Loop to read input characters, saving any "errno" value that may be
+ set by "getchar" if an error occurs. Quit if an end of file (or
+ error) occurs or if a newline character is read. */
+ while ( errno = 0, c = getc( fd ), readstat = errno,
+ ( c != EOF ) && ( c != '\n' ) ) {
+
+/* If no memory has yet been allocated to hold the line, allocate some
+ now, using MIN_CHARS as the initial line length. */
+ if ( !line ) {
+ line = astMalloc( sizeof( char ) * (size_t) MIN_CHARS );
+ size = MIN_CHARS;
+
+/* If memory has previously been allocated, extend it when necessary
+ to hold the new input character (plus a terminating null) and note
+ the new size. */
+ } else if ( ( len + 2 ) > size ) {
+ line = astGrow( line, len + 2, sizeof( char ) );
+ if ( !astOK ) break;
+ size = (int) astSizeOf( line );
+ }
+
+/* Store the character just read. */
+ line[ len++ ] = c;
+ }
+
+/* If the above loop completed without setting the global error
+ status, check the last character read and use "ferror" to see if a
+ read error occurred. If so, report the error, using the saved
+ "errno" value (but only if one was set). */
+ if ( astOK && ( c == EOF ) && ferror( fd ) ) {
+ if ( readstat ) {
+#if HAVE_STRERROR_R
+ strerror_r( readstat, errbuf, ERRBUF_LEN );
+ errstat = errbuf;
+#else
+ errstat = strerror( readstat );
+#endif
+ astError( AST__RDERR,
+ "astRead(%s): Read error on standard input - %s.", status,
+ astGetClass( this ), errstat );
+ } else {
+ astError( AST__RDERR,
+ "astRead(%s): Read error on standard input.", status,
+ astGetClass( this ) );
+ }
+ }
+
+/* If an empty line has been read, allocate memory to hold an empty
+ string. */
+ if ( !line && ( c == '\n' ) ) {
+ line = astMalloc( sizeof( char ) );
+ }
+
+/* If memory has been allocated and there has been no error,
+ null-terminate the string of input characters. */
+ if ( line ) {
+ if ( astOK ) {
+ line[ len ] = '\0';
+
+/* If there has been an error, free the allocated memory. */
+ } else {
+ line = astFree( line );
+ }
+ }
+ }
+
+
+/* Return the result pointer. */
+ return line;
+
+/* Undefine macros local to this function. */
+#undef MIN_CHARS
+#undef ERRBUF_LEN
+}
+
+static AstKeyMap *Warnings( AstChannel *this, int *status ){
+/*
+*++
+* Name:
+c astWarnings
+f AST_WARNINGS
+
+* Purpose:
+* Returns any warnings issued by the previous read or write operation.
+
+* Type:
+* Public virtual function.
+
+* Synopsis:
+c #include "channel.h"
+c AstKeyMap *astWarnings( AstChannel *this )
+f RESULT = AST_WARNINGS( THIS, STATUS )
+
+* Class Membership:
+* Channel member function.
+
+* Description:
+* This function returns an AST KeyMap object holding the text of any
+* warnings issued as a result of the previous invocation of the
+c astRead or astWrite
+f AST_READ or AST_WRITE
+* function on the Channel. If no warnings were issued, a
+c a NULL value
+f AST__NULL
+* will be returned.
+*
+* Such warnings are non-fatal and will not prevent the
+* read or write operation succeeding. However, the converted object
+* may not be identical to the original object in all respects.
+* Differences which would usually be deemed as insignificant in most
+* usual cases will generate a warning, whereas more significant
+* differences will generate an error.
+*
+* The "Strict" attribute allows this warning facility to be switched
+* off, so that a fatal error is always reported for any conversion
+* error.
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Channel.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Returned Value:
+c astWarnings()
+f AST_WARNINGS = INTEGER
+* A pointer to the KeyMap holding the warning messages, or
+c NULL
+f AST__NULL
+* if no warnings were issued during the previous read operation.
+
+* Applicability:
+* Channel
+* The basic Channel class generates a warning when ever an
+* un-recognised item is encountered whilst reading an Object from
+* an external data source. If Strict is zero (the default), then
+* unexpected items in the Object description are simply ignored,
+* and any remaining items are used to construct the returned
+* Object. If Strict is non-zero, an error will be reported and a
+* NULL Object pointer returned if any unexpected items are
+* encountered.
+*
+* As AST continues to be developed, new attributes are added
+* occasionally to selected classes. If an older version of AST is
+* used to read external Object descriptions created by a more
+* recent version of AST, then the Channel class will, by default,
+* ignore the new attributes, using the remaining attributes to
+* construct the Object. This is usually a good thing. However,
+* since external Object descriptions are often stored in plain
+* text, it is possible to edit them using a text editor. This
+* gives rise to the possibility of genuine errors in the
+* description due to finger-slips, typos, or simple
+* mis-understanding. Such inappropriate attributes will be ignored
+* if Strict is left at its default zero value. This will cause the
+* mis-spelled attribute to revert to its default value,
+* potentially causing subtle changes in the behaviour of
+* application software. If such an effect is suspected, the Strict
+* attribute can be set non-zero, resulting in the erroneous
+* attribute being identified in an error message.
+* FitsChan
+* The returned KeyMap will contain warnings for all conditions
+* listed in the Warnings attribute.
+* XmlChan
+* Reports conversion errors that result in what are usally
+* insignificant changes.
+
+* Notes:
+* - The returned KeyMap uses keys of the form "Warning_1",
+* "Warning_2", etc.
+* - A value of
+c NULL will be returned if this function is invoked with the AST
+c error status set,
+f AST__NULL will be returned if this function is invoked with STATUS
+f set to an error value,
+* or if it should fail for any reason.
+*--
+*/
+
+/* Local Variables: */
+ AstKeyMap *result;
+ char key[ 20 ];
+ int i;
+
+/* Check the global status, and supplied keyword name. */
+ result = NULL;
+ if( !astOK ) return result;
+
+/* Check there are some warnings to return. */
+ if( this->nwarn && this->warnings ) {
+
+/* Create the KeyMap. */
+ result = astKeyMap( "", status );
+
+/* Loop round all warnings, adding them into the KeyMap. */
+ for( i = 0; i < this->nwarn; i++ ){
+ sprintf( key, "Warning_%d", i + 1 );
+ astMapPut0C( result, key, (this->warnings)[ i ], " " );
+ }
+ }
+
+/* Return the KeyMap. */
+ return result;
+}
+
+AstChannel *astInitChannel_( void *mem, size_t size, int init,
+ AstChannelVtab *vtab, const char *name,
+ const char *(* source)( void ),
+ char *(* source_wrap)( const char *(*)( void ), int * ),
+ void (* sink)( const char * ),
+ void (* sink_wrap)( void (*)( const char * ),
+ const char *, int * ), int *status ) {
+/*
+*+
+* Name:
+* astInitChannel
+
+* Purpose:
+* Initialise a Channel.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "channel.h"
+* AstChannel *astInitChannel( void *mem, size_t size, int init,
+* AstChannelVtab *vtab, const char *name,
+* const char *(* source)( void ),
+* char *(* source_wrap)( const char *(*)( void ), int * ),
+* void (* sink)( const char * ),
+* void (* sink_wrap)( void (*)( const char * ),
+* const char *, int * ) )
+
+* Class Membership:
+* Channel initialiser.
+
+* Description:
+* This function is provided for use by class implementations to
+* initialise a new Channel object. It allocates memory (if
+* necessary) to accommodate the Channel plus any additional data
+* associated with the derived class. It then initialises a
+* Channel structure at the start of this memory. If the "init"
+* flag is set, it also initialises the contents of a virtual
+* function table for a Channel at the start of the memory passed
+* via the "vtab" parameter.
+
+* Parameters:
+* mem
+* A pointer to the memory in which the Channel is to be
+* initialised. This must be of sufficient size to accommodate
+* the Channel data (sizeof(Channel)) 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 Channel (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 Channel structure, so a valid value must be
+* supplied even if not required for allocating memory.
+* init
+* A boolean flag indicating if the Channel'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 Channel.
+* 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).
+* source
+* Pointer to a "source" function which will be used to obtain
+* lines of input text. Generally, this will be obtained by
+* casting a pointer to a source function which is compatible
+* with the "source_wrap" wrapper function (below). The pointer
+* should later be cast back to its original type by the
+* "source_wrap" function before the function is invoked.
+*
+* If "source" is NULL, the Channel will read from standard
+* input instead.
+* source_wrap
+* Pointer to a function which can be used to invoke the
+* "source" function supplied (above). This wrapper function is
+* necessary in order to hide variations in the nature of the
+* source function, such as may arise when it is supplied by a
+* foreign (non-C) language interface.
+*
+* The single parameter of the "source_wrap" function is a
+* pointer to the "source" function, and it should cast this
+* function pointer (as necessary) and invoke the function with
+* appropriate arguments to obtain the next line of input
+* text. The "source_wrap" function should then return a pointer
+* to a dynamically allocated, null terminated string containing
+* the text that was read. The string will be freed (using
+* astFree) when no longer required and the "source_wrap"
+* function need not concern itself with this. A NULL pointer
+* should be returned if there is no more input to read.
+*
+* If "source_wrap" is NULL, the Channel will read from standard
+* input instead.
+* sink
+* Pointer to a "sink" function which will be used to deliver
+* lines of output text. Generally, this will be obtained by
+* casting a pointer to a sink function which is compatible with
+* the "sink_wrap" wrapper function (below). The pointer should
+* later be cast back to its original type by the "sink_wrap"
+* function before the function is invoked.
+*
+* If "sink" is NULL, the Channel will write to standard output
+* instead.
+* sink_wrap
+* Pointer to a function which can be used to invoke the "sink"
+* function supplied (above). This wrapper function is necessary
+* in order to hide variations in the nature of the sink
+* function, such as may arise when it is supplied by a foreign
+* (non-C) language interface.
+*
+* The first parameter of the "sink_wrap" function is a pointer
+* to the "sink" function, and the second parameter is a pointer
+* to a const, null-terminated character string containing the
+* text to be written. The "sink_wrap" function should cast the
+* "sink" function pointer (as necessary) and invoke the
+* function with appropriate arguments to deliver the line of
+* output text. The "sink_wrap" function then returns void.
+*
+* If "sink_wrap" is NULL, the Channel will write to standard
+* output instead.
+
+* Returned Value:
+* A pointer to the new Channel.
+
+* 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: */
+ AstChannel *new; /* Pointer to new Channel */
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* If necessary, initialise the virtual function table. */
+ if ( init ) astInitChannelVtab( vtab, name );
+
+/* Initialise an Object structure (the parent class) as the first
+ component within the Channel structure, allocating memory if
+ necessary. */
+ new = (AstChannel *) astInitObject( mem, size, 0,
+ (AstObjectVtab *) vtab, name );
+
+ if ( astOK ) {
+
+/* Initialise the Channel data. */
+/* ---------------------------- */
+/* Save the pointers to the source and sink functions and the wrapper
+ functions that invoke them. */
+ new->source = source;
+ new->source_wrap = source_wrap;
+ new->sink = sink;
+ new->sink_wrap = sink_wrap;
+
+/* Indicate no input or output files have been associated with the
+ Channel. */
+ new->fd_in = NULL;
+ new->fn_in = NULL;
+ new->fd_out = NULL;
+ new->fn_out = NULL;
+
+/* Set all attributes to their undefined values. */
+ new->comment = -INT_MAX;
+ new->full = -INT_MAX;
+ new->indent = -INT_MAX;
+ new->report_level = -INT_MAX;
+ new->skip = -INT_MAX;
+ new->strict = -INT_MAX;
+ new->data = NULL;
+ new->warnings = NULL;
+ new->nwarn = 0;
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return a pointer to the new object. */
+ return new;
+}
+
+void astInitChannelVtab_( AstChannelVtab *vtab, const char *name, int *status ) {
+/*
+*+
+* Name:
+* astInitChannelVtab
+
+* Purpose:
+* Initialise a virtual function table for a Channel.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "channel.h"
+* void astInitChannelVtab( AstChannelVtab *vtab, const char *name )
+
+* Class Membership:
+* Channel vtab initialiser.
+
+* Description:
+* This function initialises the component of a virtual function
+* table which is used by the Channel class.
+
+* Parameters:
+* vtab
+* Pointer to the virtual function table. The components used by
+* all ancestral classes will be initialised if they have not already
+* been initialised.
+* 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 /* Pointer to thread-specific global data */
+ AstObjectVtab *object; /* Pointer to Object component of Vtab */
+
+/* Check the local error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Initialize the component of the virtual function table used by the
+ parent class. */
+ astInitObjectVtab( (AstObjectVtab *) vtab, name );
+
+/* Store a unique "magic" value in the virtual function table. This
+ will be used (by astIsAChannel) to determine if an object belongs
+ to this class. We can conveniently use the address of the (static)
+ class_check variable to generate this unique value. */
+ vtab->id.check = &class_check;
+ vtab->id.parent = &(((AstObjectVtab *) vtab)->id);
+
+/* Initialise member function pointers. */
+/* ------------------------------------ */
+/* Store pointers to the member functions (implemented here) that
+ provide virtual methods for this class. */
+ vtab->AddWarning = AddWarning;
+ vtab->ClearComment = ClearComment;
+ vtab->ClearFull = ClearFull;
+ vtab->ClearSkip = ClearSkip;
+ vtab->ClearStrict = ClearStrict;
+ vtab->GetComment = GetComment;
+ vtab->GetFull = GetFull;
+ vtab->GetNextData = GetNextData;
+ vtab->GetNextText = GetNextText;
+ vtab->GetSkip = GetSkip;
+ vtab->GetStrict = GetStrict;
+ vtab->Warnings = Warnings;
+ vtab->PutNextText = PutNextText;
+ vtab->Read = Read;
+ vtab->ReadClassData = ReadClassData;
+ vtab->ReadDouble = ReadDouble;
+ vtab->ReadInt = ReadInt;
+ vtab->ReadObject = ReadObject;
+ vtab->ReadString = ReadString;
+ vtab->SetComment = SetComment;
+ vtab->SetFull = SetFull;
+ vtab->SetSkip = SetSkip;
+ vtab->SetStrict = SetStrict;
+ vtab->TestComment = TestComment;
+ vtab->TestFull = TestFull;
+ vtab->TestSkip = TestSkip;
+ vtab->TestStrict = TestStrict;
+ vtab->Write = Write;
+ vtab->WriteBegin = WriteBegin;
+ vtab->WriteDouble = WriteDouble;
+ vtab->WriteEnd = WriteEnd;
+ vtab->WriteInt = WriteInt;
+ vtab->WriteIsA = WriteIsA;
+ vtab->WriteObject = WriteObject;
+ vtab->WriteString = WriteString;
+ vtab->PutChannelData = PutChannelData;
+
+ vtab->ClearReportLevel = ClearReportLevel;
+ vtab->GetReportLevel = GetReportLevel;
+ vtab->SetReportLevel = SetReportLevel;
+ vtab->TestReportLevel = TestReportLevel;
+
+ vtab->ClearIndent = ClearIndent;
+ vtab->GetIndent = GetIndent;
+ vtab->SetIndent = SetIndent;
+ vtab->TestIndent = TestIndent;
+
+ vtab->ClearSourceFile = ClearSourceFile;
+ vtab->GetSourceFile = GetSourceFile;
+ vtab->SetSourceFile = SetSourceFile;
+ vtab->TestSourceFile = TestSourceFile;
+
+ vtab->ClearSinkFile = ClearSinkFile;
+ vtab->GetSinkFile = GetSinkFile;
+ vtab->SetSinkFile = SetSinkFile;
+ vtab->TestSinkFile = TestSinkFile;
+
+/* Save the inherited pointers to methods that will be extended, and
+ replace them with pointers to the new member functions. */
+ object = (AstObjectVtab *) vtab;
+
+ parent_clearattrib = object->ClearAttrib;
+ object->ClearAttrib = ClearAttrib;
+ parent_getattrib = object->GetAttrib;
+ object->GetAttrib = GetAttrib;
+ parent_setattrib = object->SetAttrib;
+ object->SetAttrib = SetAttrib;
+ parent_testattrib = object->TestAttrib;
+ object->TestAttrib = TestAttrib;
+
+/* Declare the destructor and copy constructor. */
+ astSetDelete( (AstObjectVtab *) vtab, Delete );
+ astSetCopy( (AstObjectVtab *) vtab, Copy );
+
+/* Declare the Dump function for this class. There is no destructor or
+ copy constructor. */
+ astSetDump( vtab, Dump, "Channel", "Basic I/O Channel" );
+
+/* If we have just initialised the vtab for the current class, indicate
+ that the vtab is now initialised, and store a pointer to the class
+ identifier in the base "object" level of the vtab. */
+ if( vtab == &class_vtab ) {
+ class_init = 1;
+ astSetVtabClassIdentifier( vtab, &(vtab->id) );
+ }
+}
+
+static char *InputTextItem( AstChannel *this, int *status ) {
+/*
+* Name:
+* InputTextItem
+
+* Purpose:
+* Read the next item from a data source as text.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* char *InputTextItem( AstChannel *this )
+
+* Class Membership:
+* Channel member function.
+
+* Description:
+* This function reads the next input data item as text from the
+* data source associated with a Channel. It is similar to the
+* astGetNextText method (which it invokes), except that it strips
+* off any comments along with leading and trailing white
+* space. Input lines which are empty or do not contain significant
+* characters (e.g. all comment) are skipped, so that only
+* significant lines are returned.
+*
+* Each line is returned as a pointer to a null-terminated string
+* held in dynamic memory, and it is the caller's responsibility to
+* free this memory (using astFree) when it is no longer
+* required. A NULL pointer is returned if there are no more input
+* lines to be read.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+
+* Returned Value:
+* Pointer to a null-terminated string containing the input line
+* (held in dynamically allocated memory, which must be freed by
+* the caller when no longer required). A NULL pointer is returned
+* if there are no more input lines to be read.
+
+* 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: */
+ char *line; /* Pointer to line data to be returned */
+ int i; /* Loop counter for line characters */
+ int j; /* Counter for characters */
+ int len; /* Length of result line */
+ int nonspace; /* Non-space character encountered? */
+ int quoted; /* Character is inside quotes? */
+
+/* Initialise. */
+ line = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return line;
+
+/* Loop to read input lines until one is found which contains useful
+ characters or end of file is reached (or a read error occurs). */
+ while ( !line && ( line = astGetNextText( this ) ) && astOK ) {
+
+/* Loop to remove comments and leading and trailing white space. */
+ len = 0;
+ nonspace = 0;
+ quoted = 0;
+ for ( i = j = 0; line[ i ]; i++ ) {
+
+/* Note quote characters and ignore all text after the first unquoted
+ comment character. */
+ if ( line[ i ] == '"' ) quoted = !quoted;
+ if ( ( line[ i ] == '#' ) && !quoted ) break;
+
+/* Note the first non-space character and ignore everything before
+ it. */
+ if ( ( nonspace = nonspace || !isspace( line[ i ] ) ) ) {
+
+/* Move each character to its new position in the string. */
+ line[ j++ ] = line[ i ];
+
+/* Note the final length of the string (ignoring trailing spaces). */
+ if ( !isspace( line[ i ] ) ) len = j;
+ }
+ }
+
+/* If the string is not empty, terminate it. */
+ if ( len ) {
+ line[ len ] = '\0';
+
+/* Otherwise, free the memory used for the string so that another
+ input line will be read. */
+ } else {
+ line = astFree( line );
+ }
+ }
+
+/* Return the result pointer. */
+ return line;
+
+/* Undefine macros local to this function. */
+#undef MIN_CHARS
+}
+
+static AstChannelValue *LookupValue( const char *name, int *status ) {
+/*
+* Name:
+* LookupValue
+
+* Purpose:
+* Look up a Value structure by name.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* AstChannelValue *LookupValue( const char *name )
+
+* Class Membership:
+* Channel member function.
+
+* Description:
+* This function searches the current values list (i.e. at the
+* current nesting level) to identify a Value structure with a
+* specified name. If one is found, it is removed from the list and
+* a pointer to it is returned. If no suitable Value can be found,
+* a NULL pointer is returned instead.
+
+* Parameters:
+* name
+* Pointer to a constant null-terminated character string
+* containing the name of the required Value. This must be in
+* lower case with no surrounding white space. Note that names
+* longer than NAME_MAX characters will not match any Value.
+
+* Returned value:
+* Pointer to the required Value structure, or NULL if no suitable
+* Value exists.
+
+* Notes:
+* - The returned pointer refers to a dynamically allocated
+* structure and it is the callers responsibility to free this when
+* no longer required. The FreeValue function must be used for this
+* purpose.
+* - 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 /* Declare the thread specific global data */
+ AstChannelValue **head; /* Address of head of list pointer */
+ AstChannelValue *result; /* Pointer value to return */
+ AstChannelValue *value; /* Pointer to list element */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(NULL);
+
+/* Check that the "values_ok" flag is set. If not, the Values in the
+ values list belong to a different class to that of the current
+ class loader, so we cannot return any Value. */
+ if ( values_ok[ nest ] ) {
+
+/* Obtain the address of the current "head of list" pointer for the
+ values list (at the current nesting level). */
+ head = values_list + nest;
+
+/* Obtain the head of list pointer itself and check the list is not
+ empty. */
+ if ( ( value = *head ) ) {
+
+/* Loop to inspect each list element. */
+ while ( 1 ) {
+
+/* If a name match is found, remove the element from the list, return
+ a pointer to it and quit searching. */
+ if ( !strcmp( name, value->name ) ) {
+ RemoveValue( value, head, status );
+ result = value;
+ break;
+ }
+
+/* Follow the list until we return to the head. */
+ value = value->flink;
+ if ( value == *head ) break;
+ }
+ }
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static void OutputTextItem( AstChannel *this, const char *line, int *status ) {
+/*
+* Name:
+* OutputTextItem
+
+* Purpose:
+* Output a data item formatted as text.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* void OutputTextItem( AstChannel *this, const char *line, int *status )
+
+* Class Membership:
+* Channel member function.
+
+* Description:
+* This function outputs a data item formatted as a text string to
+* a data sink associated with a Channel. It keeps track of the
+* number of items written.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* line
+* Pointer to a constant null-terminated string containing the
+* data item to be output (no newline character should be
+* appended).
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this);
+
+/* Write out the line of text using the astPutNextText method (which
+ may be over-ridden). */
+ astPutNextText( this, line );
+
+/* If successful, increment the count of items written. */
+ if ( astOK ) items_written++;
+}
+
+static void PutChannelData( AstChannel *this, void *data, int *status ) {
+/*
+c++
+* Name:
+* astPutChannelData
+
+* Purpose:
+* Store arbitrary data to be passed to a source or sink function.
+
+* Type:
+* Public function.
+
+* Synopsis:
+* #include "channel.h"
+* void astPutChannelData( AstChannel *this, void *data )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function stores a supplied arbitrary pointer in the Channel.
+* When a source or sink function is invoked by the Channel, the
+* invoked function can use the astChannelData macro to retrieve the
+* pointer. This provides a thread-safe alternative to passing file
+* descriptors, etc, via global static variables.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* data
+* A pointer to be made available to the source and sink functions
+* via the astChannelData macro. May be NULL.
+
+* Applicability:
+* Channel
+* All Channels have this function.
+
+* Notes:
+* - This routine is not available in the Fortran 77 interface to
+* the AST library.
+c--
+*/
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Store the pointer. */
+ this->data = data;
+}
+
+static void PutNextText( AstChannel *this, const char *line, int *status ) {
+/*
+*+
+* Name:
+* astPutNextText
+
+* Purpose:
+* Write a line of output text to a data sink.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* void astPutNextText( AstChannel *this, const char *line )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function writes an output line of text to a data sink
+* associated with a Channel.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* line
+* Pointer to a constant null-terminated string containing the
+* line of output text to be written (no newline character
+* should be appended).
+
+* Notes:
+* - This method is provided primarily so that derived classes may
+* over-ride it in order to write to alternative (textual) data
+* sinks.
+*-
+*/
+
+/* Local Constants: */
+#define ERRBUF_LEN 80
+
+/* Local Variables: */
+ char *errstat; /* Pointer for system error message */
+ char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */
+ const char *sink_file; /* Path to output sink file */
+ const char *source_file; /* Path to output source file */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* If the SinkFile attribute of the Channel specifies an output file,
+ but no output file has yet been opened, open it now. Report an error
+ if it is the same as the source file. */
+ if( astTestSinkFile( this ) && !this->fd_out ) {
+ sink_file = astGetSinkFile( this );
+
+ if( this->fd_out ) {
+ source_file = astGetSourceFile( this );
+ if( astOK && !strcmp( sink_file, source_file ) ) {
+ astError( AST__WRERR, "astWrite(%s): Failed to open output "
+ "SinkFile '%s' - the file is currently being used "
+ "as the input SourceFile.", status, astGetClass( this ),
+ sink_file );
+ }
+ }
+
+ if( astOK ) {
+ this->fd_out = fopen( sink_file, "w" );
+ if( !this->fd_out ) {
+ if ( errno ) {
+#if HAVE_STRERROR_R
+ strerror_r( errno, errbuf, ERRBUF_LEN );
+ errstat = errbuf;
+#else
+ errstat = strerror( errno );
+#endif
+ astError( AST__WRERR, "astWrite(%s): Failed to open output "
+ "SinkFile '%s' - %s.", status, astGetClass( this ),
+ sink_file, errstat );
+ } else {
+ astError( AST__WRERR, "astWrite(%s): Failed to open output "
+ "SinkFile '%s'.", status, astGetClass( this ),
+ sink_file );
+ }
+ }
+ }
+ }
+
+/* Check no error occurred above. */
+ if( astOK ) {
+
+/* If an active output file descriptor is stored in the channel, write
+ the text to it, with a newline appended. */
+ if( this->fd_out ) {
+ (void) fprintf( this->fd_out, "%s\n", line );
+
+/* Otherwise, if a sink function (and its wrapper function) is defined for
+ the Channel, use the wrapper function to invoke the sink function to
+ output the text line. Since we are about to call an externally supplied
+ function which may not be thread-safe, lock a mutex first. Also store
+ the channel data pointer in a global variable so that it can be accessed
+ in the source function using macro astChannelData. */
+ } else if ( this->sink && this->sink_wrap ) {
+ astStoreChannelData( this );
+ LOCK_MUTEX2;
+ ( *this->sink_wrap )( *this->sink, line, status );
+ UNLOCK_MUTEX2;
+
+/* Otherwise, simply write the text to standard output with a newline
+ appended. */
+ } else {
+ (void) printf( "%s\n", line );
+ }
+ }
+}
+
+static AstObject *Read( AstChannel *this, int *status ) {
+/*
+*++
+* Name:
+c astRead
+f AST_READ
+
+* Purpose:
+* Read an Object from a Channel.
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "channel.h"
+c AstObject *astRead( AstChannel *this )
+f RESULT = AST_READ( THIS, STATUS )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function reads the next Object from a Channel and returns a
+* pointer to the new Object.
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Channel.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Returned Value:
+c astRead()
+f AST_READ = INTEGER
+* A pointer to the new Object. The class to which this will
+* belong is determined by the input data, so is not known in
+* advance.
+
+* Applicability:
+* FitsChan
+c All successful use of astRead on a FitsChan is destructive, so that
+f All successful use of AST_READ on a FitsChan is destructive, so that
+* FITS header cards are consumed in the process of reading an Object,
+* and are removed from the FitsChan (this deletion can be prevented
+* for specific cards by calling the FitsChan
+c astRetainFits function).
+f AST_RETAINFITS routine).
+* An unsuccessful call of
+c astRead
+f AST_READ
+* (for instance, caused by the FitsChan not containing the necessary
+* FITS headers cards needed to create an Object) results in the
+* contents of the FitsChan being left unchanged.
+* StcsChan
+* The AST Object returned by a successful use of
+c astRead
+f AST_READ
+* on an StcsChan, will be either a Region or a KeyMap, depending
+* on the values of the StcsArea, StcsCoords and StcsProps
+* attributes. See the documentation for these attributes for further
+* information.
+
+* Notes:
+* - A null Object pointer (AST__NULL) will be returned, without
+* error, if the Channel contains no further Objects to be read.
+* - A null Object pointer will also be returned if this function
+c is invoked with the AST error status set, or if it should fail
+f is invoked with STATUS set to an error value, or if it should fail
+* for any reason.
+*--
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ AstLoaderType *loader; /* Pointer to loader for Object */
+ AstObject *new; /* Pointer to new Object */
+ char *class; /* Pointer to Object class name string */
+ char *name; /* Pointer to data item name */
+ int skip; /* Skip non-AST data? */
+ int top; /* Reading top-level Object definition? */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this);
+
+/* Determine if we are reading a top-level (i.e. user-level) Object
+ definition, as opposed to the definition of an Object contained
+ within another Object. This is indicated by the current nesting
+ level. */
+ top = ( nest == -1 );
+
+/* If reading a top-level object, determine if data lying in between
+ Object definitions in the input data stream are to be skipped. */
+ skip = ( top && astGetSkip( this ) );
+
+/* Read the next input data item. If we are reading a top-level Object
+ definition, skip any unrelated data beforehand. Otherwise read the
+ data strictly as it comes (there should be no unrelated data
+ embedded within Object definitions themselves). */
+ astGetNextData( this, skip, &name, &class );
+
+/* If no suitable data item was found (and no error occurred), we have
+ reached the end of data. For a top-level Object a NULL Object
+ pointer is simply returned, but for a nested Object this indicates
+ that part of the Object definition is missing, so report an
+ error. */
+ if ( astOK ) {
+ if ( !name ) {
+ if ( !top ) {
+ astError( AST__EOCHN,
+ "astRead(%s): End of input encountered while trying to "
+ "read an AST Object.", status, astGetClass( this ) );
+ }
+
+/* If a data item was found, check it is a "Begin" item. If not, there
+ is a data item missing, so report an error and free all memory. */
+ } else if ( strcmp( name, "begin" ) ) {
+ astError( AST__BADIN,
+ "astRead(%s): Missing \"Begin\" when expecting an Object.", status,
+ astGetClass( this ) );
+ name = astFree( name );
+ if ( class ) class = astFree( class );
+
+/* If the required "Begin" item was found, free the memory used for the
+ name string. */
+ } else {
+ name = astFree( name );
+
+/* Use the associated class name to locate the loader for that
+ class. This function will then be used to build the Object. */
+ loader = astGetLoader( class, status );
+
+/* Extend all necessary stack arrays to accommodate entries for the
+ next nesting level (this allocates space if none has yet been
+ allocated). */
+ end_of_object = astGrow( end_of_object, nest + 2, sizeof( int ) );
+ object_class = astGrow( object_class, nest + 2, sizeof( char * ) );
+ values_class = astGrow( values_class, nest + 2, sizeof( char * ) );
+ values_list = astGrow( values_list, nest + 2, sizeof( AstChannelValue * ) );
+ values_ok = astGrow( values_ok, nest + 2, sizeof( int ) );
+
+/* If an error occurred, free the memory used by the class string,
+ which will not now be used. */
+ if ( !astOK ) {
+ class = astFree( class );
+
+/* Otherwise, increment the nesting level and initialise the new stack
+ elements for this new level. This includes clearing the
+ "end_of_object" flag so that ReadClassData can read more data, and
+ storing the class name of the object we are about to read. */
+ } else {
+ nest++;
+ end_of_object[ nest ] = 0;
+ object_class[ nest ] = class;
+ values_class[ nest ] = NULL;
+ values_list[ nest ] = NULL;
+ values_ok[ nest ] = 0;
+
+/* Invoke the loader, which reads the Object definition from the input
+ data stream and builds the Object. Supply NULL/zero values to the
+ loader so that it will substitute values appropriate to its own
+ class. */
+ new = (*loader)( NULL, (size_t) 0, NULL, NULL, this, status );
+
+/* Clear the values list for the current nesting level. If the list
+ has not been read or any Values remain in it, an error will
+ result. */
+ ClearValues( this, status );
+
+/* If no error has yet occurred, check that the "end_of_object" flag
+ has been set. If not, the input data were not correctly terminated,
+ so report an error. */
+ if ( astOK && !end_of_object[ nest ] ) {
+ astError( AST__BADIN,
+ "astRead(%s): Unexpected end of input (missing end "
+ "of %s).", status,
+ astGetClass( this ), object_class[ nest ] );
+ }
+
+/* If an error occurred, report contextual information. Only do this
+ for top-level Objects to avoid multple messages. */
+ if ( !astOK && top ) {
+ astError( astStatus, "Error while reading a %s from a %s.", status,
+ class, astGetClass( this ) );
+ }
+
+/* Clear the Object's class string, freeing the associated memory
+ (note this is the memory allocated for the "class" string
+ earlier). */
+ object_class[ nest ] = astFree( object_class[ nest ] );
+
+/* Restore the previous nesting level. */
+ nest--;
+ }
+
+/* Once the top-level Object has been built, free the memory used by
+ the stack arrays. */
+ if ( top ) {
+ end_of_object = astFree( end_of_object );
+ object_class = astFree( object_class );
+ values_class = astFree( values_class );
+ values_list = astFree( values_list );
+ values_ok = astFree( values_ok );
+ }
+ }
+ }
+
+/* If an error occurred, clean up by deleting the new Object and
+ return a NULL pointer. */
+ if ( !astOK ) new = astDelete( new );
+
+/* Return the pointer to the new Object. */
+ return new;
+}
+
+static void ReadClassData( AstChannel *this, const char *class, int *status ) {
+/*
+*+
+* Name:
+* astReadClassData
+
+* Purpose:
+* Read values from a data source for a class loader.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* void astReadClassData( AstChannel *this, const char *class )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function reads the data for a class from the data source
+* associated with a Channel, so as to provide values for
+* initialising the instance variables of that class as part of
+* building a complete Object. This function should be invoked by
+* the loader for each class immediately before it attempts to read
+* these values.
+*
+* The values read are placed into the current values list by this
+* function. They may then be read from this list by the class
+* loader making calls to astReadDouble, astReadInt, astReadObject
+* and astReadString. The order in which values are read by the
+* loader is unimportant (although using the same order for reading
+* as for writing will usually be more efficient) and values are
+* removed from the list as they are read.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* class
+* A pointer to a constant null-terminated string containing the
+* name of the class whose loader is requesting the data (note
+* this is not usually the same as the class name of the Object
+* being built). This value allows the class structure of the
+* input data to be validated.
+*-
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ AstObject *object; /* Pointer to new Object */
+ AstChannelValue *value; /* Pointer to Value structure */
+ char *name; /* Pointer to data item name string */
+ char *val; /* Pointer to data item value string */
+ int done; /* All class data read? */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this);
+
+/* If the "values_ok" flag is set, this indicates that the values list
+ (at the current nesting level) has been filled by a previous
+ invocation of this function and has then been read by the
+ appropriate class loader. In this case, clear any entries which may
+ remain in the current values list. If any such entries are found,
+ they represent input data that were not read, so an error will
+ result. */
+ if ( values_ok[ nest ] ) ClearValues( this, status );
+
+/* If "values_class" is non-NULL, this indicates that the values list
+ (at the current nesting level) has been filled by a previous
+ invocation of this function, but that the values belong to a class
+ whose loader has not yet tried to read them. In this case, we must
+ continue to keep the values until they are needed, so we do not
+ read any more input data this time. */
+ if ( values_class[ nest ] ) {
+
+/* If the class to which the previously saved values belong matches
+ the class we now want values for, set the "values_ok" flag. This
+ then allows the values to be accessed (by LookupValue). */
+ values_ok[ nest ] = !strcmp( values_class[ nest ], class );
+
+/* If the current values list is empty, we must read in values for the
+ next class that appears in the input data. However, first check
+ that the "end_of_object" flag has not been set. If it has, we have
+ already reached the end of this Object's data, so there is some
+ kind of problem with the order in which class loaders have been
+ invoked. This will probably never happen, but report an error if
+ necessary. */
+ } else if ( end_of_object[ nest ] ) {
+ astError( AST__LDERR,
+ "astRead(%s): Invalid attempt to read further %s data "
+ "following an end of %s.", status,
+ astGetClass( this ), class, object_class[ nest ] );
+ astError( AST__LDERR,
+ "Perhaps the wrong class loader has been invoked?" , status);
+
+/* If we need new values, loop to read input data items until the end
+ of the data for a class is reached. */
+ } else {
+ done = 0;
+ while ( astOK && !done ) {
+
+/* Read the next input data item. */
+ astGetNextData( this, 0, &name, &val );
+ if ( astOK ) {
+
+/* Unexpected end of input. */
+/* ------------------------ */
+/* If no "name" value is returned, we have reached the end of the
+ input data stream without finding the required end of class
+ terminator, so report an error. */
+ if ( !name ) {
+ astError( AST__EOCHN,
+ "astRead(%s): Unexpected end of input (missing end "
+ "of %s).", status,
+ astGetClass( this ), object_class[ nest ] );
+
+/* "IsA" item. */
+/* ----------- */
+/* Otherwise, if an "IsA" item was read, it indicates the end of the
+ data for a class. Store the pointer to the name of this class in
+ "values_class" and note whether this is the class whose data we
+ wanted in "values_ok". If the data we have read do not belong to
+ the class we wanted, they will simply be kept until the right class
+ comes looking for them. */
+ } else if ( !strcmp( name, "isa" ) ) {
+ values_class[ nest ] = val;
+ values_ok[ nest ] = !strcmp( val, class );
+
+/* Free the memory holding the name string. */
+ name = astFree( name );
+
+/* Note we have finished reading class data. */
+ done = 1;
+
+/* "End" item. */
+/* ----------- */
+/* If an "End" item was read, it indicates the end of the data both
+ for a class and for the current Object definition as a whole. Set
+ the "end_of_object" flag (for the current nesting level) which
+ prevents any further data being read for this Object. This flag is
+ also used (by Read) to check that an "End" item was actually
+ read. */
+ } else if ( !strcmp( name, "end" ) ) {
+ end_of_object[ nest ] = 1;
+
+/* Check that the class name in the "End" item matches that of the
+ Object being built. If so, store the pointer to the name of this
+ class in "values_class" and note whether this is the class whose
+ data we wanted in "values_ok". If the data we have read do not
+ belong to the class we wanted, they will simply be kept until the
+ right class comes looking for them. */
+ if ( !strcmp( val, object_class[ nest ] ) ) {
+ values_class[ nest ] = val;
+ values_ok[ nest ] = !strcmp( class, val );
+
+/* If the "End" item contains the wrong class name (i.e. not matching
+ the corresponding "Begin" item), then report an error. */
+ } else {
+ astError( AST__BADIN,
+ "astRead(%s): Bad class structure in input data.", status,
+ astGetClass( this ) );
+ astError( AST__BADIN,
+ "End of %s read when expecting end of %s.", status,
+ val, object_class[ nest ] );
+
+/* Free the memory used by the class string, which will not now be
+ used. */
+ val = astFree( val );
+ }
+
+/* Free the memory holding the name string. */
+ name = astFree( name );
+
+/* Note we have finished reading class data. */
+ done = 1;
+
+/* String value. */
+/* ------------- */
+/* If any other name is obtained and "val" is not NULL, we have read a
+ non-Object value, encoded as a string. Allocate memory for a Value
+ structure to describe it. */
+ } else if ( val ) {
+ value = astMalloc( sizeof( AstChannelValue ) );
+ if ( astOK ) {
+
+/* Store pointers to the name and value string in the Value structure
+ and note this is not an Object value. */
+ value->name = name;
+ value->ptr.string = val;
+ value->is_object = 0;
+
+/* Append the Value structure to the values list for the current
+ nesting level. */
+ AppendValue( value, values_list + nest, status );
+
+/* If an error occurred, free the memory holding the "name" and "val"
+ strings. */
+ } else {
+ name = astFree( name );
+ val = astFree( val );
+ }
+
+/* Object value. */
+/* ------------- */
+/* If "val" is NULL, we have read an Object item, and the Object
+ definition should follow. Allocate memory for a Value structure to
+ describe it. */
+ } else {
+ value = astMalloc( sizeof( AstChannelValue ) );
+
+/* Invoke astRead to read the Object definition from subsequent data
+ items and to build the Object, returning a pointer to it. This will
+ result in recursive calls to the current function, but as these
+ will use higher nesting levels they will not interfere with the
+ current invocation. */
+ astreadclassdata_msg = 0;
+ object = astRead( this );
+ if ( astOK ) {
+
+/* Store pointers to the name and Object in the Value structure and
+ note this is an Object value. */
+ value->name = name;
+ value->ptr.object = object;
+ value->is_object = 1;
+
+/* Append the Value structure to the values list for the current
+ nesting level. */
+ AppendValue( value, values_list + nest, status );
+
+/* If an error occurred, report a contextual error maessage and set
+ the "astreadclassdata_msg" flag (this prevents multiple messages if this function is
+ invoked recursively to deal with nested Objects). */
+ } else {
+ if ( !astreadclassdata_msg ) {
+ astError( astStatus,
+ "Failed to read the \"%s\" Object value.", status,
+ name );
+ astreadclassdata_msg = 1;
+ }
+
+/* Free the memory holding the "name" string and any Value structure
+ that was allocated. */
+ name = astFree( name );
+ value = astFree( value );
+ }
+ }
+ }
+ }
+ }
+}
+
+static double ReadDouble( AstChannel *this, const char *name, double def, int *status ) {
+/*
+*+
+* Name:
+* astReadDouble
+
+* Purpose:
+* Read a double value as part of loading a class.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* double astReadDouble( AstChannel *this, const char *name, double def )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function searches the current values list of a Channel to
+* identify a double value with a specified name. If such a value
+* is found, it is returned, otherwise a default value is returned
+* instead.
+*
+* This function should only be invoked from within the loader
+* function associated with a class, in order to return a double
+* value to be assigned to an instance variable. It must be
+* preceded by a call to the astReadClassData function, which loads
+* the values associated with the class into the current values
+* list from the input data source.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated character string
+* containing the name of the required value. This must be in
+* lower case with no surrounding white space. Note that names
+* longer than 6 characters will not match any value.
+* def
+* If no suitable value can be found (e.g. it is absent from the
+* data stream being read), then this value will be returned
+* instead.
+
+* Returned Value:
+* The required value, or the default if the value was not found.
+
+* Notes:
+* - A value of 0.0 will be returned if this function is invoked
+* with the global error status set, or if it should fail for any
+* reason.
+*-
+*/
+
+/* Local Variables: */
+ AstChannelValue *value; /* Pointer to required Value structure */
+ double result; /* Value to be returned */
+ int nc; /* Number of characters read by astSscanf */
+
+/* Initialise. */
+ result = 0.0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Search for a Value structure with the required name in the current
+ values list.*/
+ value = LookupValue( name, status );
+ if ( astOK ) {
+
+/* If a Value was found, check that it describes a string (as opposed
+ to an Object). */
+ if ( value ) {
+ if ( !value->is_object ) {
+
+/* If so, then attempt to decode the string to give a double value,
+ checking that the entire string is read (and checking for the magic string
+ used to represent bad values). If this fails, then the wrong name has
+ probably been given, or the input data are corrupt, so report an error. */
+ nc = 0;
+ if ( ( 0 == astSscanf( value->ptr.string, " " BAD_STRING " %n",
+ &nc ) )
+ && ( nc >= (int) strlen( value->ptr.string ) ) ) {
+ result = AST__BAD;
+
+ } else if ( !( ( 1 == astSscanf( value->ptr.string, " %lf %n",
+ &result, &nc ) )
+ && ( nc >= (int) strlen( value->ptr.string ) ) ) ) {
+ astError( AST__BADIN,
+ "astRead(%s): The value \"%s = %s\" cannot "
+ "be read as a double precision floating point "
+ "number.", status, astGetClass( this ),
+ value->name, value->ptr.string );
+
+ } else if( !astISFINITE( result ) ) {
+ astError( AST__BADIN,
+ "astRead(%s): Illegal double precision floating "
+ "point value \"%s\" read for \"%s\".", status,
+ astGetClass( this ), value->ptr.string, value->name );
+
+ }
+
+/* Report a similar error if the Value does not describe a string. */
+ } else {
+ astError( AST__BADIN,
+ "astRead(%s): The Object \"%s = <%s>\" cannot "
+ "be read as a double precision floating point number.", status,
+ astGetClass( this ),
+ value->name, astGetClass( value->ptr.object ) );
+ }
+
+/* Free the Value structure and the resources it points at. */
+ value = FreeValue( value, status );
+
+/* If no suitable Value structure was found, then use the default
+ value instead. */
+ } else {
+ result = def;
+ }
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static int ReadInt( AstChannel *this, const char *name, int def, int *status ) {
+/*
+*+
+* Name:
+* astReadInt
+
+* Purpose:
+* Read an int value as part of loading a class.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* int astReadInt( AstChannel *this, const char *name, int def )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function searches the current values list of a Channel to
+* identify an int value with a specified name. If such a value is
+* found, it is returned, otherwise a default value is returned
+* instead.
+*
+* This function should only be invoked from within the loader
+* function associated with a class, in order to return an int
+* value to be assigned to an instance variable. It must be
+* preceded by a call to the astReadClassData function, which loads
+* the values associated with the class into the current values
+* list from the input data source.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated character string
+* containing the name of the required value. This must be in
+* lower case with no surrounding white space. Note that names
+* longer than 6 characters will not match any value.
+* def
+* If no suitable value can be found (e.g. it is absent from the
+* data stream being read), then this value will be returned
+* instead.
+
+* Returned Value:
+* The required value, or the default if the value was not found.
+
+* 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: */
+ AstChannelValue *value; /* Pointer to required Value structure */
+ int nc; /* Number of characters read by astSscanf */
+ int result; /* Value to be returned */
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Search for a Value structure with the required name in the current
+ values list.*/
+ value = LookupValue( name, status );
+ if ( astOK ) {
+
+/* If a Value was found, check that it describes a string (as opposed
+ to an Object). */
+ if ( value ) {
+ if ( !value->is_object ) {
+
+/* If so, then attempt to decode the string to give an int value,
+ checking that the entire string is read. If this fails, then the
+ wrong name has probably been given, or the input data are corrupt,
+ so report an error. */
+ nc = 0;
+ if ( !( ( 1 == astSscanf( value->ptr.string, " %d %n",
+ &result, &nc ) )
+ && ( nc >= (int) strlen( value->ptr.string ) ) ) ) {
+ astError( AST__BADIN,
+ "astRead(%s): The value \"%s = %s\" cannot "
+ "be read as an integer.", status, astGetClass( this ),
+ value->name, value->ptr.string );
+ }
+
+/* Report a similar error if the Value does not describe a string. */
+ } else {
+ astError( AST__BADIN,
+ "astRead(%s): The Object \"%s = <%s>\" cannot "
+ "be read as an integer.", status, astGetClass( this ),
+ value->name, astGetClass( value->ptr.object ) );
+ }
+
+/* Free the Value structure and the resources it points at. */
+ value = FreeValue( value, status );
+
+/* If no suitable Value structure was found, then use the default
+ value instead. */
+ } else {
+ result = def;
+ }
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static AstObject *ReadObject( AstChannel *this, const char *name,
+ AstObject *def, int *status ) {
+/*
+*+
+* Name:
+* astReadObject
+
+* Purpose:
+* Read a (sub)Object as part of loading a class.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* AstObject *astReadObject( AstChannel *this, const char *name,
+* AstObject *def )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function searches the current values list of a Channel to
+* identify an Object with a specified name. If such an Object is
+* found, a pointer to it is returned, otherwise a default pointer
+* is returned instead.
+*
+* This function should only be invoked from within the loader
+* function associated with a class, in order to return an Object
+* pointer value to be assigned to an instance variable. It must be
+* preceded by a call to the astReadClassData function, which loads
+* the values associated with the class into the current values
+* list from the input data source.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated character string
+* containing the name of the required Object. This must be in
+* lower case with no surrounding white space. Note that names
+* longer than 6 characters will not match any Object.
+* def
+* If no suitable Object can be found (e.g. the Object is absent
+* from the data stream being read), then a clone of this
+* default Object pointer will be returned instead (or NULL if
+* this default pointer is NULL).
+
+* Returned Value:
+* A pointer to the Object, or a clone of the default pointer if
+* the Object was not found.
+
+* 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 *result; /* Pointer value to return */
+ AstChannelValue *value; /* Pointer to required Value structure */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Search for a Value structure with the required name in the current
+ values list.*/
+ value = LookupValue( name, status );
+ if ( astOK ) {
+
+/* If a Value was found, check that it describes an Object (as opposed to
+ a string). */
+ if ( value ) {
+ if ( value->is_object ) {
+
+/* If so, then extract the Object pointer, replacing it with NULL. */
+ result = value->ptr.object;
+ value->ptr.object = NULL;
+
+/* If the Value does not describe an Object, then the wrong name has
+ probably been given, or the input data are corrupt, so report an
+ error. */
+ } else {
+ astError( AST__BADIN,
+ "astRead(%s): The value \"%s = %s\" cannot be "
+ "read as an Object.", status, astGetClass( this ),
+ value->name, value->ptr.string );
+ }
+
+/* Free the Value structure and the resources it points at. */
+ value = FreeValue( value, status );
+
+/* If no suitable Value structure was found, clone the default
+ pointer, if given. */
+ } else if ( def ) {
+ result = astClone( def );
+ }
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static char *ReadString( AstChannel *this, const char *name,
+ const char *def, int *status ) {
+/*
+*+
+* Name:
+* astReadString
+
+* Purpose:
+* Read a string value as part of loading a class.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* char *astReadString( AstChannel *this, const char *name,
+* const char *def )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function searches the current values list of a Channel to
+* identify a string value with a specified name. If such a value
+* is found, a pointer to the string is returned, otherwise a
+* pointer to a copy of a default string is returned instead.
+*
+* This function should only be invoked from within the loader
+* function associated with a class, in order to return a string
+* pointer value to be assigned to an instance variable. It must be
+* preceded by a call to the astReadClassData function, which loads
+* the values associated with the class into the current values
+* list from the input data source.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated character string
+* containing the name of the required value. This must be in
+* lower case with no surrounding white space. Note that names
+* longer than 6 characters will not match any value.
+* def
+* If no suitable string can be found (e.g. the value is absent
+* from the data stream being read), then a dynamically
+* allocated copy of the null-terminated string pointed at by
+* "def" will be made, and a pointer to this copy will be
+* returned instead (or NULL if this default pointer is NULL).
+
+* Returned Value:
+* A pointer to a dynamically allocated null-terminated string
+* containing the value required, or to a copy of the default
+* string if the value was not found (or NULL if the "def" pointer
+* was NULL).
+
+* Notes:
+* - It is the caller's responsibility to arrange for the memory
+* holding the returned string to be freed (using astFree) when it
+* is no longer required.
+* - 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: */
+ AstChannelValue *value; /* Pointer to required Value structure */
+ char *result; /* Pointer value to return */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Search for a Value structure with the required name in the current
+ values list.*/
+ value = LookupValue( name, status );
+ if ( astOK ) {
+
+/* If a Value was found, check that it describes a string (as opposed
+ to an Object). */
+ if ( value ) {
+ if ( !value->is_object ) {
+
+/* If so, then extract the string pointer, replacing it with NULL. */
+ result = value->ptr.string;
+ value->ptr.string = NULL;
+
+/* If the Value does not describe a string, then the wrong name has
+ probably been given, or the input data are corrupt, so report an
+ error. */
+ } else {
+ astError( AST__BADIN,
+ "astRead(%s): The Object \"%s = <%s>\" cannot "
+ "be read as a string.", status, astGetClass( this ),
+ value->name, astGetClass( value->ptr.object ) );
+ }
+
+/* Free the Value structure and the resources it points at. */
+ value = FreeValue( value, status );
+
+/* If no suitable Value structure was found, then make a dynamic copy
+ of the default string (if given) and return a pointer to this. */
+ } else if ( def ) {
+ result = astStore( NULL, def, strlen( def ) + (size_t) 1 );
+ }
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static void RemoveValue( AstChannelValue *value, AstChannelValue **head, int *status ) {
+/*
+* Name:
+* RemoveValue
+
+* Purpose:
+* Remove a Value structure from a circular linked list.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* void RemoveValue( AstChannelValue *value, AstChannelValue **head, int *status );
+
+* Class Membership:
+* Channel member function.
+
+* Description:
+* This function removes a Value structure from a doubly linked
+* circular list of such structures. The "head of list" pointer is
+* updated to point at the element following the one removed.
+
+* Parameters:
+* value
+* Pointer to the structure to be removed (note that this must
+* actually be in the list, although this function does not
+* check).
+* head
+* Address of a pointer to the element at the head of the
+* list. This pointer will be updated to point at the list
+* element that follows the one removed. If the list becomes
+* empty, the pointer will be set to NULL.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - This function does not perform error chacking and does not
+* generate errors.
+*/
+
+/* Remove the Value structure from the list by re-establishing links
+ between the elements on either side of it. */
+ value->blink->flink = value->flink;
+ value->flink->blink = value->blink;
+
+/* Update the head of list pointer to identify the following
+ element. */
+ *head = value->flink;
+
+/* If the head of list identifies the removed element, then note that
+ the list is now empty. */
+ if ( *head == value ) *head = NULL;
+
+/* Make the removed element point at itself. */
+ value->flink = value;
+ value->blink = value;
+}
+
+static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
+/*
+* Name:
+* SetAttrib
+
+* Purpose:
+* Set an attribute value for a Channel.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* void SetAttrib( AstObject *this, const char *setting )
+
+* Class Membership:
+* Channel member function (over-rides the astSetAttrib protected
+* method inherited from the Object class).
+
+* Description:
+* This function assigns an attribute value for a Channel, 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 Channel.
+* setting
+* Pointer to a null terminated string specifying the new attribute
+* value.
+*/
+
+/* Local Variables: */
+ AstChannel *this; /* Pointer to the Channel structure */
+ int comment; /* Comment attribute value */
+ int full; /* Full attribute value */
+ int indent; /* Indent attribute value */
+ int len; /* Length of setting string */
+ int nc; /* Number of characters read by "astSscanf" */
+ int report_level; /* Skip attribute value */
+ int skip; /* Skip attribute value */
+ int sourcefile; /* Offset of SourceFile string */
+ int sinkfile; /* Offset of SinkFile string */
+ int strict; /* Report errors instead of warnings? */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Channel structure. */
+ this = (AstChannel *) this_object;
+
+/* 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. */
+
+/* Comment. */
+/* ---------*/
+ if ( nc = 0,
+ ( 1 == astSscanf( setting, "comment= %d %n", &comment, &nc ) )
+ && ( nc >= len ) ) {
+ astSetComment( this, comment );
+
+/* Full. */
+/* ----- */
+ } else if ( nc = 0,
+ ( 1 == astSscanf( setting, "full= %d %n", &full, &nc ) )
+ && ( nc >= len ) ) {
+ astSetFull( this, full );
+
+/* Indent. */
+/* ------- */
+ } else if ( nc = 0,
+ ( 1 == astSscanf( setting, "indent= %d %n", &indent, &nc ) )
+ && ( nc >= len ) ) {
+ astSetIndent( this, indent );
+
+/* ReportLavel. */
+/* ------------ */
+ } else if ( nc = 0,
+ ( 1 == astSscanf( setting, "reportlevel= %d %n", &report_level, &nc ) )
+ && ( nc >= len ) ) {
+ astSetReportLevel( this, report_level );
+
+/* Skip. */
+/* ----- */
+ } else if ( nc = 0,
+ ( 1 == astSscanf( setting, "skip= %d %n", &skip, &nc ) )
+ && ( nc >= len ) ) {
+ astSetSkip( this, skip );
+
+/* SinkFile. */
+/* --------- */
+ } else if ( nc = 0,
+ ( 0 == astSscanf( setting, "sinkfile=%n%*[^\n]%n", &sinkfile, &nc ) )
+ && ( nc >= len ) ) {
+ astSetSinkFile( this, setting + sinkfile );
+
+/* SourceFile. */
+/* ----------- */
+ } else if ( nc = 0,
+ ( 0 == astSscanf( setting, "sourcefile=%n%*[^\n]%n", &sourcefile, &nc ) )
+ && ( nc >= len ) ) {
+ astSetSourceFile( this, setting + sourcefile );
+
+/* Strict. */
+/* ------- */
+ } else if ( nc = 0,
+ ( 1 == astSscanf( setting, "strict= %d %n", &strict, &nc ) )
+ && ( nc >= len ) ) {
+ astSetStrict( this, strict );
+
+/* If the attribute is still not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ (*parent_setattrib)( this_object, setting, status );
+ }
+}
+
+static void SinkWrap( void (* sink)( const char * ), const char *line, int *status ) {
+/*
+* Name:
+* SinkWrap
+
+* Purpose:
+* Wrapper function to invoke a C Channel sink function.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* void SinkWrap( void (* sink)( const char * ), const char *line, int *status )
+
+* Class Membership:
+* Channel member function.
+
+* Description:
+* This function invokes the sink function whose pointer is
+* supplied in order to write an output line to an external data
+* store.
+
+* Parameters:
+* sink
+* Pointer to a sink function, whose single parameter is a
+* pointer to a const, null-terminated string containing the
+* text to be written, and which returns void. This is the form
+* of Channel sink function employed by the C language interface
+* to the AST library.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Invoke the sink function. */
+ ( *sink )( line );
+}
+
+static char *SourceWrap( const char *(* source)( void ), int *status ) {
+/*
+* Name:
+* SourceWrap
+
+* Purpose:
+* Wrapper function to invoke a C Channel source function.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* char *SourceWrap( const char *, int *status(* source)( void ) )
+
+* Class Membership:
+* Channel member function.
+
+* Description:
+* This function invokes the source function whose pointer is
+* supplied in order to read the next input line from an external
+* data store. It then returns a pointer to a dynamic string
+* containing a copy of the text that was read.
+
+* Parameters:
+* source
+* Pointer to a source function, with no parameters, that
+* returns a pointer to a const, null-terminated string
+* containing the text that it read. This is the form of Channel
+* source function employed by the C language interface to the
+* AST library.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to a dynamically allocated, null terminated string
+* containing a copy of the text that was read. This string must be
+* freed by the caller (using astFree) when no longer required.
+*
+* A NULL pointer will be returned if there is no more input text
+* to read.
+
+* Notes:
+* - A NULL pointer value 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 *result; /* Pointer value to return */
+ const char *line; /* Pointer to input line */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Invoke the source function to read the next input line and return a
+ pointer to the resulting string. */
+ line = ( *source )();
+
+/* If a string was obtained, make a dynamic copy of it and save the
+ resulting pointer. */
+ if ( line ) result = astString( line, (int) strlen( line ) );
+
+/* Return the result. */
+ return result;
+}
+
+void astStoreChannelData_( AstChannel *this, int *status ) {
+/*
+*+
+* Name:
+* astStoreChannelData
+
+* Purpose:
+* Store the Channel's channel-data pointer in a thread-specific
+* global variable.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* astStoreChannelData( AstChannel *this )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function stores the Channel's channel-data pointer (if any)
+* established by the previous call to astPutChannelData, in a
+* thread-specific global variable from where the astChannelData macro
+* can access it.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+*-
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this);
+
+/* Store the pointer int he global variable. */
+ channel_data = this->data;
+}
+
+static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* TestAttrib
+
+* Purpose:
+* Test if a specified attribute value is set for a Channel.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* int TestAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* Channel member function (over-rides the astTestAttrib protected
+* method inherited from the Object class).
+
+* Description:
+* This function returns a boolean result (0 or 1) to indicate whether
+* a value has been set for one of a Channel's attributes.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* attrib
+* Pointer to a null terminated string specifying the attribute
+* name. This should be in lower case with no surrounding white
+* space.
+* status
+* Pointer to the inherited status variable.
+
+* 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: */
+ AstChannel *this; /* Pointer to the Channel structure */
+ int result; /* Result value to return */
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Obtain a pointer to the Channel structure. */
+ this = (AstChannel *) this_object;
+
+/* Check the attribute name and test the appropriate attribute. */
+
+/* Comment. */
+/* -------- */
+ if ( !strcmp( attrib, "comment" ) ) {
+ result = astTestComment( this );
+
+/* Full. */
+/* ----- */
+ } else if ( !strcmp( attrib, "full" ) ) {
+ result = astTestFull( this );
+
+/* Indent. */
+/* ------- */
+ } else if ( !strcmp( attrib, "indent" ) ) {
+ result = astTestIndent( this );
+
+/* ReportLevel. */
+/* ------------ */
+ } else if ( !strcmp( attrib, "reportlevel" ) ) {
+ result = astTestReportLevel( this );
+
+/* Skip. */
+/* ----- */
+ } else if ( !strcmp( attrib, "skip" ) ) {
+ result = astTestSkip( this );
+
+/* SourceFile. */
+/* ----------- */
+ } else if ( !strcmp( attrib, "sourcefile" ) ) {
+ result = astTestSourceFile( this );
+
+/* SinkFile. */
+/* ----------- */
+ } else if ( !strcmp( attrib, "sinkfile" ) ) {
+ result = astTestSinkFile( this );
+
+/* Strict. */
+/* ------- */
+ } else if ( !strcmp( attrib, "strict" ) ) {
+ result = astTestStrict( this );
+
+/* If the attribute is still not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ result = (*parent_testattrib)( this_object, attrib, status );
+ }
+
+/* Return the result, */
+ return result;
+}
+
+static void Unquote( AstChannel *this, char *str, int *status ) {
+/*
+* Name:
+* Unquote
+
+* Purpose:
+* Remove quotes from a (possibly) quoted string.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* void Unquote( AstChannel *this, char *str, int *status )
+
+* Class Membership:
+* Channel member function.
+
+* Description:
+* This function removes one layer of quote characters (") from a
+* string which is possibly quoted. Any quotes within quotes (which
+* should have been doubled when the string was originally quoted)
+* are also converted back to single quotes again.
+*
+* The quotes need not start or end at the ends of the string, and
+* there may be any number of quoted sections within the string. No
+* error results if the string does not contain any quotes at all
+* (it is simply returned unchanged), but an error results if any
+* unmatched quotes are found.
+
+* Parameters:
+* this
+* Pointer to a Channel. This is only used for constructing error
+* messages and has no influence on the string processing.
+* str
+* Pointer to the null-terminated string to be processed. This
+* is modified in place. The new string starts at the same
+* location as the original but has a new null character
+* appended if necessary (it will usually be shorter than the
+* original).
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ int i; /* Loop counter for "input" characters */
+ int j; /* Counter for "output" characters */
+ int quoted; /* Inside a quoted string? */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Loop to inspect each character in the string. */
+ quoted = 0;
+ for ( i = j = 0; str[ i ]; i++ ) {
+
+/* Non-quote characters are simply copied to their new location in the
+ string. */
+ if ( str[ i ] != '"' ) {
+ str[ j++ ] = str[ i ];
+
+/* If a quote character '"' is encountered and we are not already in a
+ quoted string, then note the start of a quoted string (and discard
+ the quote). */
+ } else if ( !quoted ) {
+ quoted = 1;
+
+/* If a quote character is encountered inside a quoted string, then
+ check if the next character is also a quote. If so, convert this
+ double quote to a single one. */
+ } else if ( str[ i + 1 ] == '"' ) {
+ str[ j++ ] = '"';
+ i++;
+
+/* If a single quote character is encountered inside a quoted string,
+ then note the end of the quoted string (and discard the quote). */
+ } else {
+ quoted = 0;
+ }
+ }
+
+/* Append a null to terminate the processed string. */
+ str[ j ] = '\0';
+
+/* If the "quoted" flag is still set, then there were unmatched
+ quotes, so report an error. */
+ if ( quoted ) {
+ astError( AST__UNMQT,
+ "astRead(%s): Unmatched quotes in input data: %s.", status,
+ astGetClass( this ), str );
+ }
+}
+
+static int Use( AstChannel *this, int set, int helpful, int *status ) {
+/*
+* Name:
+* Use
+
+* Purpose:
+* Decide whether to write a value to a data sink.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* int Use( AstChannel *this, int set, int helpful, int *status )
+
+* Class Membership:
+* Channel member function.
+
+* Description:
+* This function decides whether a value supplied by a class "Dump"
+* function, via a call to one of the astWrite... protected
+* methods, should actually be written to the data sink associated
+* with a Channel.
+*
+* This decision is based on the settings of the "set" and
+* "helpful" flags supplied to the astWrite... method, plus the
+* attribute settings of the Channel.
+
+* Parameters:
+* this
+* A pointer to the Channel.
+* set
+* The "set" flag supplied.
+* helpful
+* The "helpful" value supplied.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* One if the value should be written out, otherwise zero.
+
+* 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 full; /* Full attribute value */
+ int result; /* Result value to be returned */
+
+/* Check the global error status. */
+ if ( !astOK ) return 0;
+
+/* If "set" is non-zero, then so is the result ("set" values must
+ always be written out). */
+ result = ( set != 0 );
+
+/* Otherwise, obtain the value of the Channel's Full attribute. */
+ if ( !set ) {
+ full = astGetFull( this );
+
+/* If Full is positive, display all values, if zero, display only
+ "helpful" values, if negative, display no (un-"set") values. */
+ if ( astOK ) result = ( ( helpful && ( full > -1 ) ) || ( full > 0 ) );
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static int Write( AstChannel *this, AstObject *object, int *status ) {
+/*
+*++
+* Name:
+c astWrite
+f AST_WRITE
+
+* Purpose:
+* Write an Object to a Channel.
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "channel.h"
+c int astWrite( AstChannel *this, AstObject *object )
+f RESULT = AST_WRITE( THIS, OBJECT, STATUS )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function writes an Object to a Channel, appending it to any
+* previous Objects written to that Channel.
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Channel.
+c object
+f OBJECT = INTEGER (Given)
+* Pointer to the Object which is to be written.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Returned Value:
+c astWrite()
+f AST_WRITE = INTEGER
+* The number of Objects written to the Channel by this
+c invocation of astWrite (normally, this will be one).
+f invocation of AST_WRITE (normally, this will be one).
+
+* Applicability:
+* FitsChan
+* If the FitsChan uses a foreign encoding (e.g. FITS-WCS) rather
+* than the native AST encoding, then storing values in the
+* FitsChan for keywords NAXIS1, NAXIS2, etc., before invoking
+c astWrite
+f AST_WRITE
+* can help to produce a successful write.
+
+* Notes:
+* - 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
+f with STATUS set to an error value, or if it should fail for any
+* reason.
+* - Invoking this function will usually cause the sink function
+* associated with the channel to be called in order to transfer a
+* textual description of the supplied object to some external data
+* store. However, the FitsChan class behaves differently. Invoking
+* this function on a FitsChan causes new FITS header cards to be
+* added to an internal buffer (the sink function is not invoked).
+* This buffer is written out through the sink function only when the
+* FitsChan is deleted.
+*--
+*/
+
+/* Check the global error status. */
+ if ( !astOK ) return 0;
+
+/* The work of this function is actually performed by the protected
+ astDump method of the Object. The fact that this is further
+ encapsulated within the astWrite method (which belongs to the
+ Channel) is simply a trick to allow it to be over-ridden either by
+ a derived Channel, or a derived Object (or both), and hence to
+ adapt to the nature of either argument. */
+ astDump( object, this );
+
+/* Return the number of Objects written. */
+ return astOK ? 1 : 0;
+}
+
+static void WriteBegin( AstChannel *this, const char *class,
+ const char *comment, int *status ) {
+/*
+*+
+* Name:
+* astWriteBegin
+
+* Purpose:
+* Write a "Begin" data item to a data sink.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* void astWriteBegin( AstChannel *this, const char *class,
+* const char *comment )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function writes a "Begin" data item to the data sink
+* associated with a Channel, so as to begin the output of a new
+* Object definition.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* class
+* Pointer to a constant null-terminated string containing the
+* name of the class to which the Object belongs.
+* comment
+* Pointer to a constant null-terminated string containing a
+* textual comment to be associated with the "Begin"
+* item. Normally, this will describe the purpose of the Object.
+
+* Notes:
+* - The comment supplied may not actually be used, depending on
+* the nature of the Channel supplied.
+*-
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ char *line; /* Pointer to dynamic output string */
+ int i; /* Loop counter for indentation characters */
+ int nc; /* Number of output characters */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this);
+
+/* Start building a dynamic string with an initial space. Then add
+ further spaces to suit the current indentation level. */
+ line = astAppendString( NULL, &nc, " " );
+ for ( i = 0; i < current_indent; i++ ) {
+ line = astAppendString( line, &nc, " " );
+ }
+
+/* Append the "Begin" keyword followed by the class name. */
+ line = astAppendString( line, &nc, "Begin " );
+ line = astAppendString( line, &nc, class );
+
+/* If required, also append the comment. */
+ if ( astGetComment( this ) && *comment ) {
+ line = astAppendString( line, &nc, " \t# " );
+ line = astAppendString( line, &nc, comment );
+ }
+
+/* Write out the resulting line of text. */
+ OutputTextItem( this, line, status );
+
+/* Free the dynamic string. */
+ line = astFree( line );
+
+/* Increment the indentation level and clear the count of items written
+ for this Object. */
+ current_indent += astGetIndent( this );
+ items_written = 0;
+}
+
+static void WriteDouble( AstChannel *this, const char *name,
+ int set, int helpful,
+ double value, const char *comment, int *status ) {
+/*
+*+
+* Name:
+* astWriteDouble
+
+* Purpose:
+* Write a double value to a data sink.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* void astWriteDouble( AstChannel *this, const char *name,
+* int set, int helpful,
+* double value, const char *comment )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function writes a named double value, representing the
+* value of a class instance variable, to the data sink associated
+* with a Channel. It is intended for use by class "Dump" functions
+* when writing out class information which will subsequently be
+* re-read.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated string containing the
+* name to be used to identify the value in the external
+* representation. This will form the key for identifying it
+* again when it is re-read. The name supplied should be unique
+* within its class.
+*
+* Mixed case may be used and will be preserved in the external
+* representation (where possible) for cosmetic effect. However,
+* case is not significant when re-reading values.
+*
+* It is recommended that a maximum of 6 alphanumeric characters
+* (starting with an alphabetic character) be used. This permits
+* maximum flexibility in adapting to standard external data
+* representations (e.g. FITS).
+* set
+* If this is zero, it indicates that the value being written is
+* a default value (or can be re-generated from other values) so
+* need not necessarily be written out. Such values will
+* typically be included in the external representation with
+* (e.g.) a comment character so that they are available to
+* human readers but will be ignored when re-read. They may also
+* be completely omitted in some circumstances.
+*
+* If "set" is non-zero, the value will always be explicitly
+* included in the external representation so that it can be
+* re-read.
+* helpful
+* This flag provides a hint about whether a value whose "set"
+* flag is zero (above) should actually appear at all in the
+* external representaton.
+*
+* If the external representation allows values to be "commented
+* out" then, by default, values will be included in this form
+* only if their "helpful" flag is non-zero. Otherwise, they
+* will be omitted entirely. When possible, omitting the more
+* obscure values associated with a class is recommended in
+* order to improve readability.
+*
+* This default behaviour may be further modified if the
+* Channel's Full attribute is set - either to permit all values
+* to be shown, or to suppress non-essential information
+* entirely.
+* value
+* The value to be written.
+* comment
+* Pointer to a constant null-terminated string containing a
+* textual comment to be associated with the value.
+*
+* Note that this comment may not actually be used, depending on
+* the nature of the Channel supplied and the setting of its
+* Comment attribute.
+*-
+*/
+
+/* Local Constants: */
+#define BUFF_LEN 100 /* Size of local formatting buffer */
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ char *line; /* Pointer to dynamic output string */
+ char buff[ BUFF_LEN + 1 ]; /* Local formatting buffer */
+ int i; /* Loop counter for indentation characters */
+ int nc; /* Number of output characters */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this);
+
+/* Use the "set" and "helpful" flags, along with the Channel's
+ attributes to decide whether this value should actually be
+ written. */
+ if ( Use( this, set, helpful, status ) ) {
+
+/* Start building a dynamic string with an initial space, or a comment
+ character if "set" is zero. Then add further spaces to suit the
+ current indentation level. */
+ line = astAppendString( NULL, &nc, set ? " " : "#" );
+ for ( i = 0; i < current_indent; i++ ) {
+ line = astAppendString( line, &nc, " " );
+ }
+
+/* Append the name string followed by " = ". */
+ line = astAppendString( line, &nc, name );
+ line = astAppendString( line, &nc, " = " );
+
+/* Format the value as a string and append this. Make sure "-0" isn't
+ produced. Use a magic string to represent bad values. */
+ if( value != AST__BAD ) {
+ (void) sprintf( buff, "%.*g", AST__DBL_DIG, value );
+ if ( !strcmp( buff, "-0" ) ) {
+ buff[ 0 ] = '0';
+ buff[ 1 ] = '\0';
+ }
+ } else {
+ strcpy( buff, BAD_STRING );
+ }
+ line = astAppendString( line, &nc, buff );
+
+/* If required, also append the comment. */
+ if ( astGetComment( this ) && *comment ) {
+ line = astAppendString( line, &nc, " \t# " );
+ line = astAppendString( line, &nc, comment );
+ }
+
+/* Write out the resulting line of text. */
+ OutputTextItem( this, line, status );
+
+/* Free the dynamic string. */
+ line = astFree( line );
+ }
+
+/* Undefine macros local to this function. */
+#undef BUFF_LEN
+}
+
+static void WriteEnd( AstChannel *this, const char *class, int *status ) {
+/*
+*+
+* Name:
+* astWriteEnd
+
+* Purpose:
+* Write an "End" data item to a data sink.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* void astWriteEnd( AstChannel *this, const char *class )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function writes an "End" data item to the data sink
+* associated with a Channel. This item delimits the end of an
+* Object definition.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* class
+* Pointer to a constant null-terminated string containing the
+* class name of the Object whose definition is being terminated
+* by the "End" item.
+*-
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ char *line; /* Pointer to dynamic output string */
+ int i; /* Loop counter for indentation characters */
+ int nc; /* Number of output characters */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this);
+
+/* Decrement the indentation level so that the "End" item matches the
+ corresponding "Begin" item. */
+ current_indent -= astGetIndent( this );
+
+/* Start building a dynamic string with an initial space. Then add
+ further spaces to suit the current indentation level. */
+ line = astAppendString( NULL, &nc, " " );
+ for ( i = 0; i < current_indent; i++ ) {
+ line = astAppendString( line, &nc, " " );
+ }
+
+/* Append the "End" keyword followed by the class name. */
+ line = astAppendString( line, &nc, "End " );
+ line = astAppendString( line, &nc, class );
+
+/* Write out the resulting line of text. */
+ OutputTextItem( this, line, status );
+
+/* Free the dynamic string. */
+ line = astFree( line );
+}
+
+static void WriteInt( AstChannel *this, const char *name, int set, int helpful,
+ int value, const char *comment, int *status ) {
+/*
+*+
+* Name:
+* astWriteInt
+
+* Purpose:
+* Write an integer value to a data sink.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* void astWriteInt( AstChannel *this, const char *name,
+* int set, int helpful,
+* int value, const char *comment )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function writes a named integer value, representing the
+* value of a class instance variable, to the data sink associated
+* with a Channel. It is intended for use by class "Dump" functions
+* when writing out class information which will subsequently be
+* re-read.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated string containing the
+* name to be used to identify the value in the external
+* representation. This will form the key for identifying it
+* again when it is re-read. The name supplied should be unique
+* within its class.
+*
+* Mixed case may be used and will be preserved in the external
+* representation (where possible) for cosmetic effect. However,
+* case is not significant when re-reading values.
+*
+* It is recommended that a maximum of 6 alphanumeric characters
+* (starting with an alphabetic character) be used. This permits
+* maximum flexibility in adapting to standard external data
+* representations (e.g. FITS).
+* set
+* If this is zero, it indicates that the value being written is
+* a default value (or can be re-generated from other values) so
+* need not necessarily be written out. Such values will
+* typically be included in the external representation with
+* (e.g.) a comment character so that they are available to
+* human readers but will be ignored when re-read. They may also
+* be completely omitted in some circumstances.
+*
+* If "set" is non-zero, the value will always be explicitly
+* included in the external representation so that it can be
+* re-read.
+* helpful
+* This flag provides a hint about whether a value whose "set"
+* flag is zero (above) should actually appear at all in the
+* external representaton.
+*
+* If the external representation allows values to be "commented
+* out" then, by default, values will be included in this form
+* only if their "helpful" flag is non-zero. Otherwise, they
+* will be omitted entirely. When possible, omitting the more
+* obscure values associated with a class is recommended in
+* order to improve readability.
+*
+* This default behaviour may be further modified if the
+* Channel's Full attribute is set - either to permit all values
+* to be shown, or to suppress non-essential information
+* entirely.
+* value
+* The value to be written.
+* comment
+* Pointer to a constant null-terminated string containing a
+* textual comment to be associated with the value.
+*
+* Note that this comment may not actually be used, depending on
+* the nature of the Channel supplied and the setting of its
+* Comment attribute.
+*-
+*/
+
+/* Local Constants: */
+#define BUFF_LEN 50 /* Size of local formatting buffer */
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ char *line; /* Pointer to dynamic output string */
+ char buff[ BUFF_LEN + 1 ]; /* Local formatting buffer */
+ int i; /* Loop counter for indentation characters */
+ int nc; /* Number of output characters */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this);
+
+/* Use the "set" and "helpful" flags, along with the Channel's
+ attributes to decide whether this value should actually be
+ written. */
+ if ( Use( this, set, helpful, status ) ) {
+
+/* Start building a dynamic string with an initial space, or a comment
+ character if "set" is zero. Then add further spaces to suit the
+ current indentation level. */
+ line = astAppendString( NULL, &nc, set ? " " : "#" );
+ for ( i = 0; i < current_indent; i++ ) {
+ line = astAppendString( line, &nc, " " );
+ }
+
+/* Append the name string followed by " = ". */
+ line = astAppendString( line, &nc, name );
+ line = astAppendString( line, &nc, " = " );
+
+/* Format the value as a decimal string and append this. */
+ (void) sprintf( buff, "%d", value );
+ line = astAppendString( line, &nc, buff );
+
+/* If required, also append the comment. */
+ if ( astGetComment( this ) && *comment ) {
+ line = astAppendString( line, &nc, " \t# " );
+ line = astAppendString( line, &nc, comment );
+ }
+
+/* Write out the resulting line of text. */
+ OutputTextItem( this, line, status );
+
+/* Free the dynamic string. */
+ line = astFree( line );
+ }
+
+/* Undefine macros local to this function. */
+#undef BUFF_LEN
+}
+
+int astWriteInvocations_( int *status ){
+/*
+*+
+* Name:
+* astWriteInvocations
+
+* Purpose:
+* Returns the number of invocations of the astWrite method.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "channel.h"
+* int astWriteInvocations
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function returns the number of invocations of astWrite which
+* have been made so far, excluding those made from within the
+* astWriteObject method. An example of its use is to allow a Dump
+* function to determine if a sub-object has already been dumped
+* during the current invocation of astWrite. See the Dump method for
+* the AstUnit class as an example.
+*-
+*/
+ astDECLARE_GLOBALS
+ astGET_GLOBALS(NULL);
+ return nwrite_invoc;
+}
+
+static void WriteIsA( AstChannel *this, const char *class,
+ const char *comment, int *status ) {
+/*
+*+
+* Name:
+* astWriteIsA
+
+* Purpose:
+* Write an "IsA" data item to a data sink.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* void astWriteIsA( AstChannel *this, const char *class,
+* const char *comment )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function writes an "IsA" data item to the data sink
+* associated with a Channel. This item delimits the end of the
+* data associated with the instance variables of a class, as part
+* of an overall Object definition.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* class
+* Pointer to a constant null-terminated string containing the
+* name of the class whose data are terminated by the "IsA"
+* item.
+* comment
+* Pointer to a constant null-terminated string containing a
+* textual comment to be associated with the "IsA"
+* item. Normally, this will describe the purpose of the class
+* whose data are being terminated.
+
+* Notes:
+* - The comment supplied may not actually be used, depending on
+* the nature of the Channel supplied.
+*-
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ char *line; /* Pointer to dynamic output string */
+ int i; /* Loop counter for indentation characters */
+ int indent_inc; /* Indentation increment */
+ int nc; /* Number of output characters */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this);
+
+/* Output an "IsA" item only if there has been at least one item
+ written since the last "Begin" or "IsA" item, or if the Full
+ attribute for the Channel is greater than zero (requesting maximum
+ information). */
+ if ( items_written || astGetFull( this ) > 0 ) {
+
+/* Start building a dynamic string with an initial space. Then add
+ further spaces to suit the current indentation level, but reduced
+ by one to allow the "IsA" item to match the "Begin" and "End" items
+ which enclose it. */
+ indent_inc = astGetIndent( this );
+ line = astAppendString( NULL, &nc, " " );
+ for ( i = 0; i < ( current_indent - indent_inc ); i++ ) {
+ line = astAppendString( line, &nc, " " );
+ }
+
+/* Append the "IsA" keyword followed by the class name. */
+ line = astAppendString( line, &nc, "IsA " );
+ line = astAppendString( line, &nc, class );
+
+/* If required, also append the comment. */
+ if ( astGetComment( this ) && *comment ) {
+ line = astAppendString( line, &nc, " \t# " );
+ line = astAppendString( line, &nc, comment );
+ }
+
+/* Write out the resulting line of text. */
+ OutputTextItem( this, line, status );
+
+/* Free the dynamic string. */
+ line = astFree( line );
+
+/* Clear the count of items written for this class. */
+ items_written = 0;
+ }
+}
+
+static void WriteObject( AstChannel *this, const char *name,
+ int set, int helpful,
+ AstObject *value, const char *comment, int *status ) {
+/*
+*+
+* Name:
+* astWriteObject
+
+* Purpose:
+* Write an Object as a value to a data sink.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* void astWriteObject( AstChannel *this, const char *name,
+* int set, int helpful,
+* AstObject *value, const char *comment )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function writes an Object as a named value, representing
+* the value of a class instance variable, to the data sink
+* associated with a Channel. It is intended for use by class
+* "Dump" functions when writing out class information which will
+* subsequently be re-read.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated string containing the
+* name to be used to identify the value in the external
+* representation. This will form the key for identifying it
+* again when it is re-read. The name supplied should be unique
+* within its class.
+*
+* Mixed case may be used and will be preserved in the external
+* representation (where possible) for cosmetic effect. However,
+* case is not significant when re-reading values.
+*
+* It is recommended that a maximum of 6 alphanumeric characters
+* (starting with an alphabetic character) be used. This permits
+* maximum flexibility in adapting to standard external data
+* representations (e.g. FITS).
+* set
+* If this is zero, it indicates that the value being written is
+* a default value (or can be re-generated from other values) so
+* need not necessarily be written out. Such values will
+* typically be included in the external representation with
+* (e.g.) a comment character so that they are available to
+* human readers but will be ignored when re-read. They may also
+* be completely omitted in some circumstances.
+*
+* If "set" is non-zero, the value will always be explicitly
+* included in the external representation so that it can be
+* re-read.
+* helpful
+* This flag provides a hint about whether a value whose "set"
+* flag is zero (above) should actually appear at all in the
+* external representaton.
+*
+* If the external representation allows values to be "commented
+* out" then, by default, values will be included in this form
+* only if their "helpful" flag is non-zero. Otherwise, they
+* will be omitted entirely. When possible, omitting the more
+* obscure values associated with a class is recommended in
+* order to improve readability.
+*
+* This default behaviour may be further modified if the
+* Channel's Full attribute is set - either to permit all values
+* to be shown, or to suppress non-essential information
+* entirely.
+* value
+* A Pointer to the Object to be written.
+* comment
+* Pointer to a constant null-terminated string containing a
+* textual comment to be associated with the value.
+*
+* Note that this comment may not actually be used, depending on
+* the nature of the Channel supplied and the setting of its
+* Comment attribute.
+*-
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ char *line; /* Pointer to dynamic output string */
+ int i; /* Loop counter for indentation characters */
+ int indent_inc; /* Indentation increment */
+ int nc; /* Number of output characters */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this);
+
+/* Use the "set" and "helpful" flags, along with the Channel's
+ attributes to decide whether this value should actually be
+ written. */
+ if ( Use( this, set, helpful, status ) ) {
+
+/* Start building a dynamic string with an initial space, or a comment
+ character if "set" is zero. Then add further spaces to suit the
+ current indentation level. */
+ line = astAppendString( NULL, &nc, set ? " " : "#" );
+ for ( i = 0; i < current_indent; i++ ) {
+ line = astAppendString( line, &nc, " " );
+ }
+
+/* Append the name string followed by " =". The absence of a value on
+ the right hand side indicates an Object value, whose definition
+ follows. */
+ line = astAppendString( line, &nc, name );
+ line = astAppendString( line, &nc, " =" );
+
+/* If required, also append the comment. */
+ if ( astGetComment( this ) && *comment ) {
+ line = astAppendString( line, &nc, " \t# " );
+ line = astAppendString( line, &nc, comment );
+ }
+
+/* Write out the resulting line of text. */
+ OutputTextItem( this, line, status );
+
+/* Free the dynamic string. */
+ line = astFree( line );
+
+/* If the value is not a default, write the Object to the Channel as
+ well, suitably indented (this is omitted if the value is commented
+ out). */
+ if ( set ) {
+ indent_inc = astGetIndent( this );
+ current_indent += indent_inc;
+ (void) astWrite( this, value );
+ current_indent -= indent_inc;
+ }
+ }
+}
+
+static void WriteString( AstChannel *this, const char *name,
+ int set, int helpful,
+ const char *value, const char *comment, int *status ) {
+/*
+*+
+* Name:
+* astWriteString
+
+* Purpose:
+* Write a string value to a data sink.
+
+* Type:
+* Protected virtual function.
+
+* Synopsis:
+* #include "channel.h"
+* void astWriteString( AstChannel *this, const char *name,
+* int set, int helpful,
+* const char *value, const char *comment )
+
+* Class Membership:
+* Channel method.
+
+* Description:
+* This function writes a named string value, representing the
+* value of a class instance variable, to the data sink associated
+* with a Channel. It is intended for use by class "Dump" functions
+* when writing out class information which will subsequently be
+* re-read.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated string containing the
+* name to be used to identify the value in the external
+* representation. This will form the key for identifying it
+* again when it is re-read. The name supplied should be unique
+* within its class.
+*
+* Mixed case may be used and will be preserved in the external
+* representation (where possible) for cosmetic effect. However,
+* case is not significant when re-reading values.
+*
+* It is recommended that a maximum of 6 alphanumeric characters
+* (starting with an alphabetic character) be used. This permits
+* maximum flexibility in adapting to standard external data
+* representations (e.g. FITS).
+* set
+* If this is zero, it indicates that the value being written is
+* a default value (or can be re-generated from other values) so
+* need not necessarily be written out. Such values will
+* typically be included in the external representation with
+* (e.g.) a comment character so that they are available to
+* human readers but will be ignored when re-read. They may also
+* be completely omitted in some circumstances.
+*
+* If "set" is non-zero, the value will always be explicitly
+* included in the external representation so that it can be
+* re-read.
+* helpful
+* This flag provides a hint about whether a value whose "set"
+* flag is zero (above) should actually appear at all in the
+* external representaton.
+*
+* If the external representation allows values to be "commented
+* out" then, by default, values will be included in this form
+* only if their "helpful" flag is non-zero. Otherwise, they
+* will be omitted entirely. When possible, omitting the more
+* obscure values associated with a class is recommended in
+* order to improve readability.
+*
+* This default behaviour may be further modified if the
+* Channel's Full attribute is set - either to permit all values
+* to be shown, or to suppress non-essential information
+* entirely.
+* value
+* Pointer to a constant null-terminated string containing the
+* value to be written.
+* comment
+* Pointer to a constant null-terminated string containing a
+* textual comment to be associated with the value.
+*
+* Note that this comment may not actually be used, depending on
+* the nature of the Channel supplied and the setting of its
+* Comment attribute.
+*-
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ char *line; /* Pointer to dynamic output string */
+ int i; /* Loop counter for characters */
+ int nc; /* Number of output characters */
+ int quote; /* Quote character found? */
+ int size; /* Size of allocated memory */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this);
+
+/* Use the "set" and "helpful" flags, along with the Channel's
+ attributes to decide whether this value should actually be
+ written. */
+ if ( Use( this, set, helpful, status ) ) {
+
+/* Start building a dynamic string with an initial space, or a comment
+ character if "set" is zero. Then add further spaces to suit the
+ current indentation level. */
+ line = astAppendString( NULL, &nc, set ? " " : "#" );
+ for ( i = 0; i < current_indent; i++ ) {
+ line = astAppendString( line, &nc, " " );
+ }
+
+/* Append the name string followed by " = " and an opening quote
+ character (the string will be quoted to protect leading and
+ trailing spaces). */
+ line = astAppendString( line, &nc, name );
+ line = astAppendString( line, &nc, " = \"" );
+
+/* We now append the value string, but must inspect each character so
+ that quotes (appearing inside quotes) can be doubled. Determine the
+ current size of memory allocated for the dynamic string. */
+ size = (int) astSizeOf( line );
+
+/* Loop to inspect each character and see if it is a quote. */
+ for ( i = 0; value[ i ]; i++ ) {
+ quote = ( value[ i ] == '"' );
+
+/* If more memory is required, extend the dynamic string (allowing for
+ doubling of quotes and the final null) and save its new size. */
+ if ( nc + 2 + quote > size ) {
+ line = astGrow( line, nc + 2 + quote, sizeof( char ) );
+ if ( astOK ) {
+ size = (int) astSizeOf( line );
+
+/* Quit if an error occurs. */
+ } else {
+ break;
+ }
+ }
+
+/* Append the value character to the dynamic string, duplicating each
+ quote character. */
+ line[ nc++ ] = value[ i ];
+ if ( quote ) line[ nc++ ] = '"';
+ }
+
+/* Append the closing quote. */
+ line = astAppendString( line, &nc, "\"" );
+
+/* If required, also append the comment. */
+ if ( astGetComment( this ) && *comment ) {
+ line = astAppendString( line, &nc, " \t# " );
+ line = astAppendString( line, &nc, comment );
+ }
+
+/* Write out the resulting line of text. */
+ OutputTextItem( this, line, status );
+
+/* Free the dynamic string. */
+ line = astFree( line );
+ }
+}
+
+/* Functions which access class attributes. */
+/* ---------------------------------------- */
+/* Implement member functions to access the attributes associated with
+ this class using the macros defined for this purpose in the
+ "object.h" file. */
+
+/*
+*att++
+* Name:
+* SourceFile
+
+* Purpose:
+* Input file from which to read data.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* String.
+
+* Description:
+* This attribute specifies the name of a file from which the Channel
+* should read data. If specified it is used in preference to any source
+* function specified when the Channel was created.
+*
+* Assigning a new value to this attribute will cause any previously
+* opened SourceFile to be closed. The first subsequent call to
+c astRead
+f AST_READ
+* will attempt to open the new file (an error will be reported if the
+* file cannot be opened), and read data from it. All subsequent call to
+c astRead
+f AST_READ
+* will read data from the new file, until the SourceFile attribute is
+* cleared or changed.
+*
+* Clearing the attribute causes any open SourceFile to be closed. All
+* subsequent data reads will use the source function specified when the
+* Channel was created, or will read from standard input if no source
+* function was specified.
+*
+* If no value has been assigned to SourceFile, a null string will be
+* returned if an attempt is made to get the attribute value.
+
+* Notes:
+* - Any open SourceFile is closed when the Channel is deleted.
+* - If the Channel is copied or dumped
+c (using astCopy or astShow)
+f (using AST_COPY or AST_SHOW)
+* the SourceFile attribute is left in a cleared state in the output
+* Channel (i.e. the value of the SourceFile attribute is not copied).
+
+* Applicability:
+* FitsChan
+* In the case of a FitsChan, the specified SourceFile supplements
+* the source function specified when the FitsChan was created,
+* rather than replacing the source function. The source file
+* should be a text file (not a FITS file) containing one header per
+* line. When a value is assigned to SourceFile, the file is opened
+* and read immediately, and all headers read from the file are
+* appended to the end of any header already in the FitsChan. The file
+* is then closed. Clearing the SourceFile attribute has no further
+* effect, other than nullifying the string (i.e. the file name)
+* associated with the attribute.
+
+*att--
+*/
+
+/* Clear the SourceFile value by closing any open file, freeing the
+ allocated memory and assigning a NULL pointer. */
+astMAKE_CLEAR(Channel,SourceFile,fn_in,((this->fd_in=(this->fd_in?(fclose(this->fd_in),NULL):NULL)),astFree(this->fn_in)))
+
+/* If the SourceFile value is not set, supply a default in the form of a
+ pointer to the constant string "". */
+astMAKE_GET(Channel,SourceFile,const char *,NULL,( this->fn_in ? this->fn_in : "" ))
+
+/* Set a SourceFile value by closing any open file, freeing any previously
+ allocated memory, allocating new memory, storing the string and saving
+ the pointer to the copy. */
+astMAKE_SET(Channel,SourceFile,const char *,fn_in,((this->fd_in=(this->fd_in?(fclose(this->fd_in),NULL):NULL)),astStore( this->fn_in, value, strlen( value ) + (size_t) 1 )))
+
+/* The SourceFile value is set if the pointer to it is not NULL. */
+astMAKE_TEST(Channel,SourceFile,( this->fn_in != NULL ))
+
+/*
+*att++
+* Name:
+* SinkFile
+
+* Purpose:
+* Output file to which to data should be written.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* String.
+
+* Description:
+* This attribute specifies the name of a file to which the Channel
+* should write data. If specified it is used in preference to any sink
+* function specified when the Channel was created.
+*
+* Assigning a new value to this attribute will cause any previously
+* opened SinkFile to be closed. The first subsequent call to
+c astWrite
+f AST_WRITE
+* will attempt to open the new file (an error will be reported if the
+* file cannot be opened), and write data to it. All subsequent call to
+c astWrite
+f AST_WRITE
+* will write data to the new file, until the SinkFile attribute is
+* cleared or changed.
+*
+* Clearing the attribute causes any open SinkFile to be closed. All
+* subsequent data writes will use the sink function specified when the
+* Channel was created, or will write to standard output if no sink
+* function was specified.
+*
+* If no value has been assigned to SinkFile, a null string will be
+* returned if an attempt is made to get the attribute value.
+
+* Notes:
+* - A new SinkFile will over-write any existing file with the same
+* name unless the existing file is write protected, in which case an
+* error will be reported.
+* - Any open SinkFile is closed when the Channel is deleted.
+* - If the Channel is copied or dumped
+c (using astCopy or astShow)
+f (using AST_COPY or AST_SHOW)
+* the SinkFile attribute is left in a cleared state in the output
+* Channel (i.e. the value of the SinkFile attribute is not copied).
+
+* Applicability:
+* FitsChan
+* When the FitsChan is destroyed, any headers in the FitsChan will be
+* written out to the sink file, if one is specified (if not, the
+* sink function used when the FitsChan was created is used). The
+* sink file is a text file (not a FITS file) containing one header
+* per line.
+
+*att--
+*/
+
+/* Clear the SinkFile value by closing any open file, freeing the allocated
+ memory and assigning a NULL pointer. */
+astMAKE_CLEAR(Channel,SinkFile,fn_out,((this->fd_out=(this->fd_out?(fclose(this->fd_out),NULL):NULL)),astFree(this->fn_out)))
+
+/* If the SinkFile value is not set, supply a default in the form of a
+ pointer to the constant string "". */
+astMAKE_GET(Channel,SinkFile,const char *,NULL,( this->fn_out ? this->fn_out : "" ))
+
+/* Set a SinkFile value by closing any open file, freeing any previously
+ allocated memory, allocating new memory, storing the string and saving
+ the pointer to the copy. */
+astMAKE_SET(Channel,SinkFile,const char *,fn_out,((this->fd_out=(this->fd_out?(fclose(this->fd_out),NULL):NULL)),astStore( this->fn_out, value, strlen( value ) + (size_t) 1 )))
+
+/* The SinkFile value is set if the pointer to it is not NULL. */
+astMAKE_TEST(Channel,SinkFile,( this->fn_out != NULL ))
+
+
+/*
+*att++
+* Name:
+* Comment
+
+* Purpose:
+* Include textual comments in output?
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer (boolean).
+
+* Description:
+* This is a boolean attribute which controls whether textual
+* comments are to be included in the output generated by a
+* Channel. If included, they will describe what each item of
+* output represents.
+*
+* If Comment is non-zero, then comments will be included. If
+* it is zero, comments will be omitted.
+
+* Applicability:
+* Channel
+* The default value is non-zero for a normal Channel.
+* FitsChan
+* The default value is non-zero for a FitsChan.
+* XmlChan
+* The default value is zero for an XmlChan.
+
+*att--
+*/
+
+/* This is a boolean value (0 or 1) with a value of -INT_MAX when
+ undefined but yielding a default of one. */
+astMAKE_CLEAR(Channel,Comment,comment,-INT_MAX)
+astMAKE_GET(Channel,Comment,int,0,( this->comment != -INT_MAX ? this->comment : 1 ))
+astMAKE_SET(Channel,Comment,int,comment,( value != 0 ))
+astMAKE_TEST(Channel,Comment,( this->comment != -INT_MAX ))
+
+/*
+*att++
+* Name:
+* Full
+
+* Purpose:
+* Set level of output detail.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer.
+
+* Description:
+* This attribute is a three-state flag and takes values of -1, 0
+* or +1. It controls the amount of information included in the
+* output generated by a Channel.
+*
+* If Full is zero, then a modest amount of
+* non-essential but useful information will be included in the
+* output. If Full is negative, all non-essential information will
+* be suppressed to minimise the amount of output, while if it is
+* positive, the output will include the maximum amount of detailed
+* information about the Object being written.
+
+* Applicability:
+* Channel
+* The default value is zero for a normal Channel.
+* FitsChan
+* The default value is zero for a FitsChan.
+* XmlChan
+* The default value is -1 for an XmlChan.
+* StcsChan
+* The default value is zero for an StcsChan. Set a positive value
+* to cause default values to be included in STC-S descriptions.
+
+* Notes:
+* - All positive values supplied for this attribute are converted
+* to +1 and all negative values are converted to -1.
+*att--
+*/
+
+/* This ia a 3-state value (-1, 0 or +1) with a value of -INT_MAX when
+ undefined but yielding a default of zero. */
+astMAKE_CLEAR(Channel,Full,full,-INT_MAX)
+astMAKE_GET(Channel,Full,int,0,( this->full != -INT_MAX ? this->full : 0 ))
+astMAKE_SET(Channel,Full,int,full,( value > 0 ? 1 : ( value < 0 ? -1 : 0 ) ))
+astMAKE_TEST(Channel,Full,( this->full != -INT_MAX ))
+
+/*
+*att++
+* Name:
+* Indent
+
+* Purpose:
+* Specifies the indentation to use in text produced by a Channel.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer (boolean).
+
+* Description:
+* This attribute controls the indentation within the output text produced by
+f the AST_WRITE function.
+c the astWrite function.
+* It gives the increase in the indentation for each level in the object
+* heirarchy. If it is set to zero, no indentation will be used. [3]
+
+* Applicability:
+* Channel
+* The default value is zero for a basic Channel.
+* FitsChan
+* The FitsChan class ignores this attribute.
+* StcsChan
+* The default value for an StcsChan is zero, which causes the entire
+* STC-S description is written out by a single invocation of the sink
+* function. The text supplied to the sink function will not contain
+* any linefeed characters, and each pair of adjacent words will be
+* separated by a single space. The text may thus be arbitrarily large
+* and the StcsLength attribute is ignored.
+*
+* If Indent is non-zero, then the text is written out via multiple
+* calls to the sink function, each call corresponding to a single
+* "line" of text (although no line feed characters will be inserted
+* by AST). The complete STC-S description is broken into lines so that:
+*
+* - the line length specified by attribute StcsLength is not exceeded
+* - each sub-phrase (time, space, etc.) starts on a new line
+* - each argument in a compound spatial region starts on a new line
+*
+* If this causes a sub-phrase to extend to two or more lines, then the
+* second and subsequent lines will be indented by three spaces compared
+* to the first line. In addition, lines within a compound spatial region
+* will have extra indentation to highlight the nesting produced by the
+* parentheses. Each new level of nesting will be indented by a further
+* three spaces.
+f
+f Note, the default value of zero is unlikely to be appropriate when
+f an StcsChan is used within Fortran code. In this case, Indent
+f should usually be set non-zero, and the StcsLength attribute set to
+f the size of the CHARACTER variable used to
+f receive the text returned by AST_GETLINE within the sink function.
+f This avoids the possibility of long lines being truncated invisibly
+f within AST_GETLINE.
+* XmlChan
+* The default value for an XmlChan is zero, which results in no
+* linefeeds or indentation strings being added to output text.
+* If any non-zero value is assigned to Indent, then extra linefeed and
+* space characters will be inserted as necessary to ensure that each
+* XML tag starts on a new line, and each tag will be indented by
+* a further 3 spaces to show its depth in the containment hierarchy.
+*att--
+*/
+
+/* This is an integer value with a value of -INT_MAX when undefined,
+ yielding a default of 3. Sub-classes may over-ride theis default. */
+astMAKE_CLEAR(Channel,Indent,indent,-INT_MAX)
+astMAKE_GET(Channel,Indent,int,3,( this->indent != -INT_MAX ? this->indent : 3 ))
+astMAKE_SET(Channel,Indent,int,indent,value)
+astMAKE_TEST(Channel,Indent,( this->indent != -INT_MAX ))
+
+/*
+*att++
+* Name:
+* ReportLevel
+
+* Purpose:
+* Determines which read/write conditions are reported.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer.
+
+* Description:
+* This attribute determines which, if any, of the conditions that occur
+* whilst reading or writing an Object should be reported. These
+* conditions will generate either a fatal error or a warning, as
+* determined by attribute Strict. ReportLevel can take any of the
+* following values:
+*
+* 0 - Do not report any conditions.
+*
+* 1 - Report only conditions where significant information content has been
+* changed. For instance, an unsupported time-scale has been replaced by a
+* supported near-equivalent time-scale. Another example is if a basic
+* Channel unexpected encounters data items that may have been introduced
+* by later versions of AST.
+*
+* 2 - Report the above, and in addition report significant default
+* values. For instance, if no time-scale was specified when reading an
+* Object from an external data source, report the default time-scale
+* that is being used.
+*
+* 3 - Report the above, and in addition report any other potentially
+* interesting conditions that have no significant effect on the
+* conversion. For instance, report if a time-scale of "TT"
+* (terrestrial time) is used in place of "ET" (ephemeris time). This
+* change has no signficiant effect because ET is the predecessor of,
+* and is continuous with, TT. Synonyms such as "IAT" and "TAI" are
+* another example.
+*
+* The default value is 1. Note, there are many other conditions that
+* can occur whilst reading or writing an Object that completely
+* prevent the conversion taking place. Such conditions will always
+* generate errors, irrespective of the ReportLevel and Strict attributes.
+
+* Applicability:
+* Channel
+* All Channels have this attribute.
+* FitsChan
+* All the conditions selected by the FitsChan Warnings attribute are
+* reported at level 1.
+*att--
+*/
+
+/* This is an integer value with a value of -INT_MAX when undefined,
+ yielding a default of one. */
+astMAKE_CLEAR(Channel,ReportLevel,report_level,-INT_MAX)
+astMAKE_GET(Channel,ReportLevel,int,1,( this->report_level != -INT_MAX ? this->report_level : 1 ))
+astMAKE_SET(Channel,ReportLevel,int,report_level,value)
+astMAKE_TEST(Channel,ReportLevel,( this->report_level != -INT_MAX ))
+
+/*
+*att++
+* Name:
+* Skip
+
+* Purpose:
+* Skip irrelevant data?
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer (boolean).
+
+* Description:
+* This is a boolean attribute which indicates whether the Object
+* data being read through a Channel are inter-mixed with other,
+* irrelevant, external data.
+*
+* If Skip is zero (the default), then the source of input data is
+* expected to contain descriptions of AST Objects and comments and
+* nothing else (if anything else is read, an error will
+* result). If Skip is non-zero, then any non-Object data
+* encountered between Objects will be ignored and simply skipped
+* over in order to reach the next Object.
+
+* Applicability:
+* Channel
+* All Channels have this attribute.
+* FitsChan
+* The FitsChan class sets the default value of this attribute
+* to 1, so that all irrelevant FITS headers will normally be
+* ignored.
+*att--
+*/
+
+/* This ia a boolean value (0 or 1) with a value of -INT_MAX when
+ undefined but yielding a default of zero. */
+astMAKE_CLEAR(Channel,Skip,skip,-INT_MAX)
+astMAKE_GET(Channel,Skip,int,0,( this->skip != -INT_MAX ? this->skip : 0 ))
+astMAKE_SET(Channel,Skip,int,skip,( value != 0 ))
+astMAKE_TEST(Channel,Skip,( this->skip != -INT_MAX ))
+
+/*
+*att++
+* Name:
+* Strict
+
+* Purpose:
+* Report an error if any unexpeted data items are found?
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer (boolean).
+
+* Description:
+* This is a boolean attribute which indicates whether a warning
+* rather than an error should be issed for insignificant conversion
+* problems. If it is set non-zero, then fatal errors are issued
+* instead of warnings, resulting in the
+c AST error status being set.
+f inherited STATUS variable being set to an error value.
+* If Strict is zero (the default), then execution continues after minor
+* conversion problems, and a warning message is added to the Channel
+* structure. Such messages can be retrieved using the
+c astWarnings
+f AST_WARNINGS
+* function.
+
+* Notes:
+* - This attribute was introduced in AST version 5.0. Prior to this
+* version of AST unexpected data items read by a basic Channel always
+* caused an error to be reported. So applications linked against
+* versions of AST prior to version 5.0 may not be able to read Object
+* descriptions created by later versions of AST, if the Object's class
+* description has changed.
+
+* Applicability:
+* Channel
+* All Channels have this attribute.
+*att--
+*/
+
+/* This ia a boolean value (0 or 1) with a value of -INT_MAX when
+ undefined but yielding a default of zero. */
+astMAKE_CLEAR(Channel,Strict,strict,-INT_MAX)
+astMAKE_GET(Channel,Strict,int,0,( this->strict != -INT_MAX ? this->strict : 0 ))
+astMAKE_SET(Channel,Strict,int,strict,( value != 0 ))
+astMAKE_TEST(Channel,Strict,( this->strict != -INT_MAX ))
+
+/* Destructor. */
+/* ----------- */
+static void Delete( AstObject *obj, int *status ) {
+/*
+* Name:
+* Delete
+
+* Purpose:
+* Destructor for Channel objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Delete( AstObject *obj, int *status )
+
+* Description:
+* This function implements the destructor for Channel objects.
+
+* Parameters:
+* obj
+* Pointer to the object to be deleted.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* This function attempts to execute even if the global error status is
+* set.
+*/
+
+/* Local Variables: */
+ AstChannel *this; /* Pointer to Channel */
+
+/* Obtain a pointer to the Channel structure. */
+ this = (AstChannel *) obj;
+
+/* Free memory used to store warnings. */
+ astAddWarning( this, 0, NULL, NULL, status );
+
+/* Close any open input or output files. */
+ if( this->fd_in ) fclose( this->fd_in );
+ if( this->fd_out ) fclose( this->fd_out );
+
+/* Free file name memory. */
+ this->fn_in = astFree( this->fn_in );
+ this->fn_out = astFree( this->fn_out );
+}
+
+/* Copy constructor. */
+/* ----------------- */
+static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
+/*
+* Name:
+* Copy
+
+* Purpose:
+* Copy constructor for Channel objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Copy( const AstObject *objin, AstObject *objout, int *status )
+
+* Description:
+* This function implements the copy constructor for Channel objects.
+
+* Parameters:
+* objin
+* Pointer to the object to be copied.
+* objout
+* Pointer to the object being constructed.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - This constructor makes a deep copy.
+*/
+
+/* Local Variables: */
+ AstChannel *out; /* Pointer to output Channel */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain pointers to the input and output Channels. */
+ out = (AstChannel *) objout;
+
+/* Just clear any references to the input memory from the output Channel. */
+ out->warnings = NULL;
+ out->nwarn = 0;
+ out->fd_in = NULL;
+ out->fn_in = NULL;
+ out->fd_out = NULL;
+ out->fn_out = NULL;
+}
+
+/* Dump function. */
+/* -------------- */
+static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
+/*
+* Name:
+* Dump
+
+* Purpose:
+* Dump function for Channel objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Dump( AstObject *this, AstChannel *channel, int *status )
+
+* Description:
+* This function implements the Dump function which writes out data
+* for the Channel class to an output Channel.
+
+* Parameters:
+* this
+* Pointer to the Object whose data are being written.
+* channel
+* Pointer to the Channel to which the data are being written.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ AstChannel *this; /* Pointer to the Channel structure */
+ const char *comment; /* Pointer to comment string */
+ int ival; /* Integer value */
+ int set; /* Attribute value set? */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Channel structure. */
+ this = (AstChannel *) this_object;
+
+/* Write out values representing the instance variables for the
+ Channel 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. */
+
+/* Indent */
+/* ------------ */
+ set = TestIndent( this, status );
+ ival = set ? GetIndent( this, status ) : astGetIndent( this );
+ astWriteInt( channel, "Indnt", set, 0, ival, "Indentation increment" );
+
+/* ReportLevel. */
+/* ------------ */
+ set = TestReportLevel( this, status );
+ ival = set ? GetReportLevel( this, status ) : astGetReportLevel( this );
+ astWriteInt( channel, "RpLev", set, 0, ival, "Error reporting level" );
+
+/* Skip. */
+/* ----- */
+ set = TestSkip( this, status );
+ ival = set ? GetSkip( this, status ) : astGetSkip( this );
+ astWriteInt( channel, "Skip", set, 0, ival,
+ ival ? "Ignore data between Objects" :
+ "No data allowed between Objects" );
+
+/* Strict. */
+/* ------- */
+ set = TestStrict( this, status );
+ ival = set ? GetStrict( this, status ) : astGetStrict( this );
+ astWriteInt( channel, "Strict", set, 0, ival,
+ ival ? "Report errors insead of warnings" :
+ "Report warnings instead of errors" );
+
+/* Full. */
+/* ----- */
+ set = TestFull( this, status );
+ ival = set ? GetFull( this, status ) : astGetFull( this );
+ if ( ival < 0 ) {
+ comment = "Suppress non-essential output";
+ }else if ( ival == 0 ) {
+ comment = "Output standard information";
+ } else {
+ comment = "Output maximum information";
+ }
+ astWriteInt( channel, "Full", set, 0, ival, comment );
+
+/* Comment. */
+/* -------- */
+ set = TestComment( this, status );
+ ival = set ? GetComment( this, status ) : astGetComment( this );
+ astWriteInt( channel, "Comm", set, 0, ival,
+ ival ? "Display comments" :
+ "Omit comments" );
+}
+
+/* Standard class functions. */
+/* ========================= */
+/* Implement the astIsAChannel and astCheckChannel functions using the
+ macros defined for this purpose in the "object.h" header file. */
+astMAKE_ISA(Channel,Object)
+astMAKE_CHECK(Channel)
+
+AstChannel *astChannel_( const char *(* source)( void ),
+ void (* sink)( const char * ),
+ const char *options, int *status, ...) {
+/*
+*+
+* Name:
+* astChannel
+
+* Purpose:
+* Create a Channel.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "channel.h"
+* AstChannel *astChannel( const char *(* source)( void ),
+* void (* sink)( const char * ),
+* const char *options, ..., int *status )
+
+* Class Membership:
+* Channel constructor.
+
+* Description:
+* This function creates a new Channel and optionally initialises
+* its attributes.
+*
+* A Channel implements low-level input/output for the AST library.
+* Writing an Object to a Channel (using astWrite) will generate a
+* textual representation of that Object, and reading from a
+* Channel (using astRead) will create a new Object from its
+* textual representation.
+*
+* Normally, when you use a Channel, you should provide "source"
+* and "sink" functions which connect it to an external data store
+* by reading and writing the resulting text. By default, however,
+* a Channel will read from standard input and write to standard
+* output.
+
+* Parameters:
+* source
+* Pointer to a "source" function that takes no arguments and
+* returns a pointer to a null-terminated string.
+*
+* This function will be used by the Channel to obtain lines of
+* input text. On each invocation, it should return a pointer to
+* the next input line read from some external data store, and a
+* NULL pointer when there are no more lines to read.
+*
+* If "source" is NULL, the Channel will read from standard
+* input instead.
+* sink
+* Pointer to a "sink" function that takes a pointer to a
+* null-terminated string as an argument and returns void.
+*
+* This function will be used by the Channel to deliver lines of
+* output text. On each invocation, it should deliver the
+* contents of the string supplied to some external data store.
+*
+* If "sink" is NULL, the Channel will write to standard output
+* instead.
+* options
+* Pointer to a null-terminated string containing an optional
+* comma-separated list of attribute assignments to be used for
+* initialising the new Channel. The syntax used is identical to
+* that for the astSet function and may include "printf" format
+* specifiers identified by "%" symbols in the normal way.
+* status
+* Pointer to the inherited status variable.
+* ...
+* If the "options" string contains "%" format specifiers, then
+* an optional list of additional arguments may follow it in
+* order to supply values to be substituted for these
+* specifiers. The rules for supplying these are identical to
+* those for the astSet function (and for the C "printf"
+* function).
+
+* Returned Value:
+* astChannel()
+* A pointer to the new Channel.
+
+* Notes:
+* - A NULL pointer value will be returned if this function is
+* invoked with the global error status set, or if it should fail
+* for any reason.
+*-
+
+* Implementation Notes:
+* - This function implements the basic Channel constructor which
+* is available via the protected interface to the Channel class.
+* A public interface is provided by the astChannelId_ function.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstChannel *new; /* Pointer to new Channel */
+ va_list args; /* Variable argument list */
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Initialise the Channel, allocating memory and initialising the
+ virtual function table as well if necessary. Supply pointers to
+ (local) wrapper functions that can invoke the source and sink
+ functions with appropriate arguments for the C language. */
+ new = astInitChannel( NULL, sizeof( AstChannel ), !class_init, &class_vtab,
+ "Channel", source, SourceWrap, sink, SinkWrap );
+
+/* If successful, note that the virtual function table has been
+ initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable argument list and pass it along with the
+ options string to the astVSet method to initialise the new
+ Channel's attributes. */
+ va_start( args, status );
+ astVSet( new, options, NULL, args );
+ va_end( args );
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return a pointer to the new Channel. */
+ return new;
+}
+
+AstChannel *astChannelId_( const char *(* source)( void ),
+ void (* sink)( const char * ),
+ const char *options, ... ) {
+/*
+*++
+* Name:
+c astChannel
+f AST_CHANNEL
+
+* Purpose:
+* Create a Channel.
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "channel.h"
+c AstChannel *astChannel( const char *(* source)( void ),
+c void (* sink)( const char * ),
+c const char *options, ... )
+f RESULT = AST_CHANNEL( SOURCE, SINK, OPTIONS, STATUS )
+
+* Class Membership:
+* Channel constructor.
+
+* Description:
+* This function creates a new Channel and optionally initialises
+* its attributes.
+*
+* A Channel implements low-level input/output for the AST library.
+c Writing an Object to a Channel (using astWrite) will generate a
+f Writing an Object to a Channel (using AST_WRITE) will generate a
+* textual representation of that Object, and reading from a
+c Channel (using astRead) will create a new Object from its
+f Channel (using AST_READ) will create a new Object from its
+* textual representation.
+*
+* Normally, when you use a Channel, you should provide "source"
+c and "sink" functions which connect it to an external data store
+f and "sink" routines which connect it to an external data store
+* by reading and writing the resulting text. By default, however,
+* a Channel will read from standard input and write to standard
+* output. Alternatively, a Channel can be told to read or write from
+* specific text files using the SinkFile and SourceFile attributes,
+* in which case no sink or source function need be supplied.
+
+* Parameters:
+c source
+f SOURCE = SUBROUTINE (Given)
+c Pointer to a source function that takes no arguments and
+c returns a pointer to a null-terminated string. If no value
+c has been set for the SourceFile attribute, this function
+c will be used by the Channel to obtain lines of input text. On
+c each invocation, it should return a pointer to the next input
+c line read from some external data store, and a NULL pointer
+c when there are no more lines to read.
+c
+c If "source" is NULL and no value has been set for the SourceFile
+c attribute, the Channel will read from standard input instead.
+f A source routine, which is a subroutine which takes a single
+f integer error status argument. If no value has been set
+f for the SourceFile attribute, this routine will be used by
+f the Channel to obtain lines of input text. On each
+f invocation, it should read the next input line from some
+f external data store, and then return the resulting text to
+f the AST library by calling AST_PUTLINE. It should supply a
+f negative line length when there are no more lines to read.
+f If an error occurs, it should set its own error status
+f argument to an error value before returning.
+f
+f If the null routine AST_NULL is suppied as the SOURCE value,
+f and no value has been set for the SourceFile attribute,
+f the Channel will read from standard input instead.
+c sink
+f SINK = SUBROUTINE (Given)
+c Pointer to a sink function that takes a pointer to a
+c null-terminated string as an argument and returns void.
+c If no value has been set for the SinkFile attribute, this
+c function will be used by the Channel to deliver lines of
+c output text. On each invocation, it should deliver the
+c contents of the string supplied to some external data store.
+c
+c If "sink" is NULL, and no value has been set for the SinkFile
+c attribute, the Channel will write to standard output instead.
+f A sink routine, which is a subroutine which takes a single
+f integer error status argument. If no value has been set
+f for the SinkFile attribute, this routine will be used by
+f the Channel to deliver lines of output text. On each
+f invocation, it should obtain the next output line from the
+f AST library by calling AST_GETLINE, and then deliver the
+f resulting text to some external data store. If an error
+f occurs, it should set its own error status argument to an
+f error value before returning.
+f
+f If the null routine AST_NULL is suppied as the SINK value,
+f and no value has been set for the SinkFile attribute,
+f the Channel will write to standard output instead.
+c options
+f OPTIONS = CHARACTER * ( * ) (Given)
+c Pointer to a null-terminated string containing an optional
+c comma-separated list of attribute assignments to be used for
+c initialising the new Channel. The syntax used is identical to
+c that for the astSet function and may include "printf" format
+c specifiers identified by "%" symbols in the normal way.
+f A character string containing an optional comma-separated
+f list of attribute assignments to be used for initialising the
+f new Channel. The syntax used is identical to that for the
+f AST_SET routine.
+c ...
+c If the "options" string contains "%" format specifiers, then
+c an optional list of additional arguments may follow it in
+c order to supply values to be substituted for these
+c specifiers. The rules for supplying these are identical to
+c those for the astSet function (and for the C "printf"
+c function).
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Returned Value:
+c astChannel()
+f AST_CHANNEL = INTEGER
+* A pointer to the new Channel.
+
+* Notes:
+c - Application code can pass arbitrary data (such as file
+c descriptors, etc) to source and sink functions using the
+c astPutChannelData function. The source or sink function should use
+c the astChannelData macro to retrieve this data.
+f - The names of the routines supplied for the SOURCE and SINK
+f arguments should appear in EXTERNAL statements in the Fortran
+f routine which invokes AST_CHANNEL. However, this is not generally
+f necessary for the null routine AST_NULL (so long as the AST_PAR
+f include file has been used).
+* - 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.
+f - Note that the null routine AST_NULL (one underscore) is
+f different to AST__NULL (two underscores), which is the null Object
+f pointer.
+*--
+
+* Implementation Notes:
+* - This function implements the external (public) interface to
+* the astChannel constructor function. It returns an ID value
+* (instead of a true C pointer) to external users, and must be
+* provided because astChannel_ has a variable argument list which
+* cannot be encapsulated in a macro (where this conversion would
+* otherwise occur).
+* - The variable argument list also prevents this function from
+* invoking astChanel_ directly, so it must be a re-implementation
+* of it in all respects, except for the final conversion of the
+* result to an ID value.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstChannel *new; /* Pointer to new Channel */
+ va_list args; /* Variable argument list */
+
+ int *status; /* Pointer to inherited status value */
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Initialise the Channel, allocating memory and initialising the
+ virtual function table as well if necessary. Supply pointers to
+ (local) wrapper functions that can invoke the source and sink
+ functions with appropriate arguments for the C language. */
+ new = astInitChannel( NULL, sizeof( AstChannel ), !class_init, &class_vtab,
+ "Channel", source, SourceWrap, sink, SinkWrap );
+
+/* If successful, note that the virtual function table has been
+ initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable argument list and pass it along with the
+ options string to the astVSet method to initialise the new
+ Channel's attributes. */
+ va_start( args, options );
+ astVSet( new, options, NULL, args );
+ va_end( args );
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return an ID value for the new Channel. */
+ return astMakeId( new );
+}
+
+AstChannel *astChannelForId_( const char *(* source)( void ),
+ char *(* source_wrap)( const char *(*)( void ), int * ),
+ void (* sink)( const char * ),
+ void (* sink_wrap)( void (*)( const char * ),
+ const char *, int * ),
+ const char *options, ... ) {
+/*
+*+
+* Name:
+* astChannelFor
+
+* Purpose:
+* Initialise a Channel from a foreign language interface.
+
+* Type:
+* Public function.
+
+* Synopsis:
+* #include "channel.h"
+* AstChannel *astChannelFor( const char *(* source)( void ),
+* char *(* source_wrap)( const char *(*)
+* ( void ), int * ),
+* void (* sink)( const char * ),
+* void (* sink_wrap)( void (*)( const char * ),
+* const char *, int * ),
+* const char *options, ... )
+
+* Class Membership:
+* Channel constructor.
+
+* Description:
+* This function creates a new Channel from a foreign language
+* interface and optionally initialises its attributes.
+*
+* A Channel implements low-level input/output for the AST library.
+* Writing an Object to a Channel (using astWrite) will generate a
+* textual representation of that Object, and reading from a
+* Channel (using astRead) will create a new Object from its
+* textual representation.
+*
+* Normally, when you use a Channel, you should provide "source"
+* and "sink" functions which connect it to an external data store
+* by reading and writing the resulting text. This function also
+* requires you to provide "wrapper" functions which will invoke
+* the source and sink functions. By default, however, a Channel
+* will read from standard input and write to standard output.
+
+* Parameters:
+* source
+* Pointer to a "source" function which will be used to obtain
+* lines of input text. Generally, this will be obtained by
+* casting a pointer to a source function which is compatible
+* with the "source_wrap" wrapper function (below). The pointer
+* should later be cast back to its original type by the
+* "source_wrap" function before the function is invoked.
+*
+* If "source" is NULL, the Channel will read from standard
+* input instead.
+* source_wrap
+* Pointer to a function which can be used to invoke the
+* "source" function supplied (above). This wrapper function is
+* necessary in order to hide variations in the nature of the
+* source function, such as may arise when it is supplied by a
+* foreign (non-C) language interface.
+*
+* The single parameter of the "source_wrap" function is a
+* pointer to the "source" function, and it should cast this
+* function pointer (as necessary) and invoke the function with
+* appropriate arguments to obtain the next line of input
+* text. The "source_wrap" function should then return a pointer
+* to a dynamically allocated, null terminated string containing
+* the text that was read. The string will be freed (using
+* astFree) when no longer required and the "source_wrap"
+* function need not concern itself with this. A NULL pointer
+* should be returned if there is no more input to read.
+*
+* If "source_wrap" is NULL, the Channel will read from standard
+* input instead.
+* sink
+* Pointer to a "sink" function which will be used to deliver
+* lines of output text. Generally, this will be obtained by
+* casting a pointer to a sink function which is compatible with
+* the "sink_wrap" wrapper function (below). The pointer should
+* later be cast back to its original type by the "sink_wrap"
+* function before the function is invoked.
+*
+* If "sink" is NULL, the Channel will write to standard output
+* instead.
+* sink_wrap
+* Pointer to a function which can be used to invoke the "sink"
+* function supplied (above). This wrapper function is necessary
+* in order to hide variations in the nature of the sink
+* function, such as may arise when it is supplied by a foreign
+* (non-C) language interface.
+*
+* The first parameter of the "sink_wrap" function is a pointer
+* to the "sink" function, and the second parameter is a pointer
+* to a const, null-terminated character string containing the
+* text to be written. The "sink_wrap" function should cast the
+* "sink" function pointer (as necessary) and invoke the
+* function with appropriate arguments to deliver the line of
+* output text. The "sink_wrap" function then returns void.
+*
+* If "sink_wrap" is NULL, the Channel will write to standard
+* output instead.
+* options
+* Pointer to a null-terminated string containing an optional
+* comma-separated list of attribute assignments to be used for
+* initialising the new Channel. The syntax used is identical to
+* that for the astSet function and may include "printf" format
+* specifiers identified by "%" symbols in the normal way.
+* ...
+* If the "options" string contains "%" format specifiers, then
+* an optional list of additional arguments may follow it in
+* order to supply values to be substituted for these
+* specifiers. The rules for supplying these are identical to
+* those for the astSet function (and for the C "printf"
+* function).
+
+* Returned Value:
+* astChannelFor()
+* A pointer to the new Channel.
+
+* Notes:
+* - A null Object pointer (AST__NULL) will be returned if this
+* function is invoked with the global error status set, or if it
+* should fail for any reason.
+* - This function is only available through the public interface
+* to the Channel class (not the protected interface) and is
+* intended solely for use in implementing foreign language
+* interfaces to this class.
+*-
+
+* Implememtation Notes:
+* - This function behaves exactly like astChannelId_, in that it
+* returns ID values and not true C pointers, but it has two
+* additional arguments. These are pointers to the "wrapper
+* functions" which are needed to accommodate foreign language
+* interfaces.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ AstChannel *new; /* Pointer to new Channel */
+ va_list args; /* Variable argument list */
+ int *status; /* Pointer to inherited status value */
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Initialise the Channel, allocating memory and initialising the
+ virtual function table as well if necessary. */
+ new = astInitChannel( NULL, sizeof( AstChannel ), !class_init, &class_vtab,
+ "Channel", source, source_wrap, sink, sink_wrap );
+
+/* If successful, note that the virtual function table has been
+ initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable argument list and pass it along with the
+ options string to the astVSet method to initialise the new
+ Channel's attributes. */
+ va_start( args, options );
+ astVSet( new, options, NULL, args );
+ va_end( args );
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return an ID value for the new Channel. */
+ return astMakeId( new );
+}
+
+AstChannel *astLoadChannel_( void *mem, size_t size,
+ AstChannelVtab *vtab, const char *name,
+ AstChannel *channel, int *status ) {
+/*
+*+
+* Name:
+* astLoadChannel
+
+* Purpose:
+* Load a Channel.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "channel.h"
+* AstChannel *astLoadChannel( void *mem, size_t size,
+* AstChannelVtab *vtab, const char *name,
+* AstChannel *channel )
+
+* Class Membership:
+* Channel loader.
+
+* Description:
+* This function is provided to load a new Channel using data read
+* from a Channel. It first loads the data used by the parent class
+* (which allocates memory if necessary) and then initialises a
+* Channel structure in this memory, using data read from the input
+* Channel.
+*
+* If the "init" flag is set, it also initialises the contents of a
+* virtual function table for a Channel at the start of the memory
+* passed via the "vtab" parameter.
+
+
+* Parameters:
+* mem
+* A pointer to the memory into which the Channel is to be
+* loaded. This must be of sufficient size to accommodate the
+* Channel data (sizeof(Channel)) 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 Channel (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 Channel 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(AstChannel) is used instead.
+* vtab
+* Pointer to the start of the virtual function table to be
+* associated with the new Channel. If this is NULL, a pointer
+* to the (static) virtual function table for the Channel 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 "Channel" is used instead.
+
+* Returned Value:
+* A pointer to the new Channel.
+
+* 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 /* Pointer to thread-specific global data */
+ AstChannel *new; /* Pointer to the new Channel */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(channel);
+
+/* If a NULL virtual function table has been supplied, then this is
+ the first loader to be invoked for this Channel. In this case the
+ Channel belongs to this class, so supply appropriate values to be
+ passed to the parent class loader (and its parent, etc.). */
+ if ( !vtab ) {
+ size = sizeof( AstChannel );
+ vtab = &class_vtab;
+ name = "Channel";
+
+/* If required, initialise the virtual function table for this class. */
+ if ( !class_init ) {
+ astInitChannelVtab( vtab, name );
+ class_init = 1;
+ }
+ }
+
+/* Invoke the parent class loader to load data for all the ancestral
+ classes of the current one, returning a pointer to the resulting
+ partly-built Channel. */
+ new = astLoadObject( mem, size, (AstObjectVtab *) vtab, name,
+ channel );
+
+ 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, "Channel" );
+
+/* Set the pointers to the source and sink functions, and their
+ wrapper functions, to NULL (we cannot restore these since they
+ refer to process-specific addresses). */
+ new->source = NULL;
+ new->source_wrap = NULL;
+ new->sink = NULL;
+ new->sink_wrap = NULL;
+
+/* We do not have any data to pass to the source and sink functions. */
+ new->data = NULL;
+
+/* No warnings yet. */
+ new->warnings = NULL;
+ new->nwarn = 0;
+
+/* Indicate no input or output files have been associated with the
+ Channel. */
+ new->fd_in = NULL;
+ new->fn_in = NULL;
+ new->fd_out = NULL;
+ new->fn_out = NULL;
+
+/* Now read each individual data item from this list and use it to
+ initialise the appropriate instance variable(s) for this class. */
+
+/* In the case of attributes, we first read the "raw" input value,
+ supplying the "unset" value as the default. If a "set" value is
+ obtained, we then use the appropriate (private) Set... member
+ function to validate and set the value properly. */
+
+/* Indent. */
+/* ------- */
+ new->indent = astReadInt( channel, "indnt", -INT_MAX );
+ if ( TestIndent( new, status ) ) SetIndent( new, new->indent, status );
+
+/* ReportLevel. */
+/* ------------ */
+ new->report_level = astReadInt( channel, "rplev", -INT_MAX );
+ if ( TestReportLevel( new, status ) ) SetReportLevel( new,
+ new->report_level,
+ status );
+
+/* Skip. */
+/* ----- */
+ new->skip = astReadInt( channel, "skip", -INT_MAX );
+ if ( TestSkip( new, status ) ) SetSkip( new, new->skip, status );
+
+/* Strict. */
+/* ------- */
+ new->strict = astReadInt( channel, "strict", -INT_MAX );
+ if ( TestStrict( new, status ) ) SetStrict( new, new->strict, status );
+
+/* Full. */
+/* ----- */
+ new->full = astReadInt( channel, "full", -INT_MAX );
+ if ( TestFull( new, status ) ) SetFull( new, new->full, status );
+
+/* Comment. */
+/* -------- */
+ new->comment = astReadInt( channel, "comm", -INT_MAX );
+ if ( TestComment( new, status ) ) SetComment( new, new->comment, status );
+
+/* If an error occurred, clean up by deleting the new Channel. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return the new Channel 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 astGetNextData_( AstChannel *this, int begin, char **name, char **val, int *status ) {
+ *name = NULL;
+ *val = NULL;
+ if ( !astOK ) return;
+ (**astMEMBER(this,Channel,GetNextData))( this, begin, name, val, status );
+}
+char *astGetNextText_( AstChannel *this, int *status ) {
+ if ( !astOK ) return NULL;
+ return (**astMEMBER(this,Channel,GetNextText))( this, status );
+}
+void astPutNextText_( AstChannel *this, const char *line, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Channel,PutNextText))( this, line, status );
+}
+AstObject *astRead_( AstChannel *this, int *status ) {
+ if ( !astOK ) return NULL;
+ astAddWarning( this, 0, NULL, NULL, status );
+ return (**astMEMBER(this,Channel,Read))( this, status );
+}
+void astReadClassData_( AstChannel *this, const char *class, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Channel,ReadClassData))( this, class, status );
+}
+double astReadDouble_( AstChannel *this, const char *name, double def, int *status ) {
+ if ( !astOK ) return 0.0;
+ return (**astMEMBER(this,Channel,ReadDouble))( this, name, def, status );
+}
+int astReadInt_( AstChannel *this, const char *name, int def, int *status ) {
+ if ( !astOK ) return 0;
+ return (**astMEMBER(this,Channel,ReadInt))( this, name, def, status );
+}
+AstObject *astReadObject_( AstChannel *this, const char *name,
+ AstObject *def, int *status ) {
+ if ( !astOK ) return NULL;
+ return (**astMEMBER(this,Channel,ReadObject))( this, name, def, status );
+}
+char *astReadString_( AstChannel *this, const char *name, const char *def, int *status ) {
+ if ( !astOK ) return NULL;
+ return (**astMEMBER(this,Channel,ReadString))( this, name, def, status );
+}
+void astWriteBegin_( AstChannel *this, const char *class,
+ const char *comment, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Channel,WriteBegin))( this, class, comment, status );
+}
+void astWriteDouble_( AstChannel *this, const char *name, int set, int helpful,
+ double value, const char *comment, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Channel,WriteDouble))( this, name, set, helpful, value,
+ comment, status );
+}
+void astWriteEnd_( AstChannel *this, const char *class, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Channel,WriteEnd))( this, class, status );
+}
+void astWriteInt_( AstChannel *this, const char *name, int set, int helpful,
+ int value, const char *comment, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Channel,WriteInt))( this, name, set, helpful, value,
+ comment, status );
+}
+void astWriteIsA_( AstChannel *this, const char *class, const char *comment, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Channel,WriteIsA))( this, class, comment, status );
+}
+void astWriteString_( AstChannel *this, const char *name, int set, int helpful,
+ const char *value, const char *comment, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Channel,WriteString))( this, name, set, helpful, value,
+ comment, status );
+}
+void astPutChannelData_( AstChannel *this, void *data, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Channel,PutChannelData))( this, data, status );
+}
+
+AstKeyMap *astWarnings_( AstChannel *this, int *status ){
+ if( !astOK ) return NULL;
+ return (**astMEMBER(this,Channel,Warnings))( this, status );
+}
+
+/* Because of the variable argument list, we need to work a bit harder on
+ astAddWarning. Functions that provide implementations of the
+ astAddWarning method recieve the fully expanded message and so do not
+ need a variable argument list. */
+
+void astAddWarning_( void *this_void, int level, const char *fmt,
+ const char *method, int *status, ... ) {
+ AstChannel *this;
+ char buff[ 201 ];
+ va_list args;
+ int nc;
+
+ this = astCheckChannel( this_void );
+
+ if( fmt ) {
+ if( astOK ) {
+ va_start( args, status );
+ nc = vsprintf( buff, fmt, args );
+ va_end( args );
+ if( nc > 200 ) {
+ astError( AST__INTER, "astAddWarning(%s): Message buffer size "
+ "exceeded (internal AST programming error).",
+ status, astGetClass( this ) );
+ } else {
+ (**astMEMBER(this,Channel,AddWarning))( this, level, buff, method, status );
+ }
+ }
+ } else {
+ (**astMEMBER(this,Channel,AddWarning))( this, level, NULL, method, status );
+ }
+}
+
+/* Count the number of times astWrite is invoked (excluding invocations
+ made from within the astWriteObject method - see below). The count is
+ done here so that invocations of astWrite within a sub-class will be
+ included. */
+int astWrite_( AstChannel *this, AstObject *object, int *status ) {
+ astDECLARE_GLOBALS
+ if ( !astOK ) return 0;
+ astGET_GLOBALS(this);
+ nwrite_invoc++;
+ astAddWarning( this, 0, NULL, NULL, status );
+ return (**astMEMBER(this,Channel,Write))( this, object, status );
+}
+
+/* We do not want to count invocations of astWrite made from within the
+ astWriteObject method. So decrement the number of invocations first
+ (this assumes that each invocation of astWriteObject will only invoke
+ astWrite once). */
+void astWriteObject_( AstChannel *this, const char *name, int set,
+ int helpful, AstObject *value, const char *comment, int *status ) {
+ astDECLARE_GLOBALS
+ if ( !astOK ) return;
+ astGET_GLOBALS(this);
+ nwrite_invoc--;
+ (**astMEMBER(this,Channel,WriteObject))( this, name, set, helpful, value,
+ comment, status );
+}
+
+
+
+
+