1 /*
2 *class++
3 *  Name:
4 *     Channel
5 
6 *  Purpose:
7 *     Basic (textual) I/O channel.
8 
9 *  Constructor Function:
10 c     astChannel
11 f     AST_CHANNEL
12 
13 *  Description:
14 *     The Channel class implements low-level input/output for the AST
15 *     library.  Writing an Object to a Channel will generate a textual
16 *     representation of that Object, and reading from a Channel will
17 *     create a new Object from its textual representation.
18 *
19 *     Normally, when you use a Channel, you should provide "source"
20 c     and "sink" functions which connect it to an external data store
21 f     and "sink" routines which connect it to an external data store
22 *     by reading and writing the resulting text. By default, however,
23 *     a Channel will read from standard input and write to standard
24 *     output. Alternatively, a Channel can be told to read or write from
25 *     specific text files using the SinkFile and SourceFile attributes,
26 *     in which case no sink or source function need be supplied.
27 
28 *  Inheritance:
29 *     The Channel class inherits from the Object class.
30 
31 *  Attributes:
32 *     In addition to those attributes common to all Objects, every
33 *     Channel also has the following attributes:
34 *
35 *     - Comment: Include textual comments in output?
36 *     - Full: Set level of output detail
37 *     - Indent: Indentation increment between objects
38 *     - ReportLevel: Selects the level of error reporting
39 *     - SinkFile: The path to a file to which the Channel should write
40 *     - Skip: Skip irrelevant data?
41 *     - SourceFile: The path to a file from which the Channel should read
42 *     - Strict: Generate errors instead of warnings?
43 
44 *  Functions:
45 c     In addition to those functions applicable to all Objects, the
46 c     following functions may also be applied to all Channels:
47 f     In addition to those routines applicable to all Objects, the
48 f     following routines may also be applied to all Channels:
49 *
50 c     - astWarnings: Return warnings from the previous read or write
51 c     - astPutChannelData: Store data to pass to source or sink functions
52 c     - astRead: Read an Object from a Channel
53 c     - astWrite: Write an Object to a Channel
54 f     - AST_WARNINGS: Return warnings from the previous read or write
55 f     - AST_READ: Read an Object from a Channel
56 f     - AST_WRITE: Write an Object to a Channel
57 
58 *  Copyright:
59 *     Copyright (C) 1997-2006 Council for the Central Laboratory of the
60 *     Copyright (C) 2009 Science & Technology Facilities Council.
61 *     All Rights Reserved.
62 *     Research Councils
63 
64 *  Licence:
65 *     This program is free software: you can redistribute it and/or
66 *     modify it under the terms of the GNU Lesser General Public
67 *     License as published by the Free Software Foundation, either
68 *     version 3 of the License, or (at your option) any later
69 *     version.
70 *
71 *     This program is distributed in the hope that it will be useful,
72 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
73 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
74 *     GNU Lesser General Public License for more details.
75 *
76 *     You should have received a copy of the GNU Lesser General
77 *     License along with this program.  If not, see
78 *     <http://www.gnu.org/licenses/>.
79 
80 *  Authors:
81 *     RFWS: R.F. Warren-Smith (Starlink)
82 
83 *  History:
84 *     12-AUG-1996 (RFWS):
85 *        Original version.
86 *     6-SEP-1996:
87 *        Finished initial implementation.
88 *     11-DEC-1996 (RFWS):
89 *        Added support for foreign language source and sink functions.
90 *     28-APR-1997 (RFWS):
91 *        Prevent "-0" being written (use "0" instead).
92 *     27-NOV-2002 (DSB):
93 *        Added astWriteInvocations.
94 *     8-JAN-2003 (DSB):
95 *        - Changed private InitVtab method to protected astInitChannelVtab
96 *        method.
97 *        - Modified to use protected Vtab initialisation methods when
98 *        loading an Object.
99 *     1-NOV-2003 (DSB):
100 *        Change the initialiser so that it accepts source and sink
101 *        wrapper functions as arguments (for use by derived classes).
102 *     16-AUG-2006 (DSB):
103 *        - Document non-destructive nature of unsuccessful astRead calls
104 *        on a FitsChan.
105 *     3-OCT-2008 (DSB):
106 *        Added "Strict" attribute.
107 *     11-DEC-2008 (DSB):
108 *        Added astPutChannelData and astChannelData functions.
109 *     16-JAN-2009 (DSB):
110 *        Added astAddWarning and astWarnings.
111 *     11-JUN-2009 (DSB):
112 *        Enable astChannelData to be used from within astRead.
113 *     7-DEC-2009 (DSB):
114 *        Added Indent attribute.
115 *     12-FEB-2010 (DSB):
116 *        Represent AST__BAD externally using the string "<bad>".
117 *     23-JUN-2011 (DSB):
118 *        Added attributes SinkFile and SourceFile.
119 *     2-OCT-2012 (DSB):
120 *        Report an error if an Inf or NaN value is read from the external
121 *        source.
122 *class--
123 */
124 
125 /* Module Macros. */
126 /* ============== */
127 /* Set the name of the class we are implementing. This indicates to
128    the header files that define class interfaces that they should make
129    "protected" symbols available. */
130 #define astCLASS Channel
131 
132 /* Define a string containing the maximum length of keywords used to
133    identify values in the external representation of data. This is
134    deliberately kept small so as to simplify integration with
135    standards such as FITS. */
136 #define MAX_NAME "8"
137 
138 /* Max length of string returned by GetAttrib */
139 #define GETATTRIB_BUFF_LEN 50
140 
141 /* String used to represent AST__BAD externally. */
142 #define BAD_STRING "<bad>"
143 
144 /* Include files. */
145 /* ============== */
146 /* Interface definitions. */
147 /* ---------------------- */
148 
149 #include "globals.h"             /* Thread-safe global data access */
150 #include "error.h"               /* Error reporting facilities */
151 #include "memory.h"              /* Memory allocation facilities */
152 #include "object.h"              /* Base Object class */
153 #include "channel.h"             /* Interface definition for this class */
154 #include "loader.h"              /* Interface to the global loader */
155 #include "keymap.h"              /* Storing arbitrary data in an AST Object */
156 #include "pointset.h"            /* For AST__BAD */
157 
158 /* Error code definitions. */
159 /* ----------------------- */
160 #include "ast_err.h"             /* AST error codes */
161 
162 /* C header files. */
163 /* --------------- */
164 #include <ctype.h>
165 #include <errno.h>
166 #include <float.h>
167 #include <limits.h>
168 #include <stdarg.h>
169 #include <stddef.h>
170 #include <stdio.h>
171 #include <string.h>
172 
173 /* Module Variables. */
174 /* ================= */
175 
176 /* Address of this static variable is used as a unique identifier for
177    member of this class. */
178 static int class_check;
179 
180 /* Pointers to parent class methods which are extended by this class. */
181 static const char *(* parent_getattrib)( AstObject *, const char *, int * );
182 static int (* parent_testattrib)( AstObject *, const char *, int * );
183 static void (* parent_clearattrib)( AstObject *, const char *, int * );
184 static void (* parent_setattrib)( AstObject *, const char *, int * );
185 
186 /* Define macros for accessing each item of thread specific global data. */
187 #ifdef THREAD_SAFE
188 
189 /* Define how to initialise thread-specific globals. */
190 #define GLOBAL_inits \
191    globals->Class_Init = 0; \
192    globals->AstReadClassData_Msg = 0; \
193    globals->GetAttrib_Buff[ 0 ] = 0; \
194    globals->Items_Written = 0; \
195    globals->Current_Indent = 0; \
196    globals->Nest = -1; \
197    globals->Nwrite_Invoc = 0; \
198    globals->Object_Class = NULL; \
199    globals->Values_List = NULL; \
200    globals->Values_Class = NULL; \
201    globals->Values_OK = NULL; \
202    globals->End_Of_Object = NULL; \
203    globals->Channel_Data  = NULL;
204 
205 /* Create the function that initialises global data for this module. */
206 astMAKE_INITGLOBALS(Channel)
207 
208 /* Define macros for accessing each item of thread specific global data. */
209 #define class_init astGLOBAL(Channel,Class_Init)
210 #define class_vtab astGLOBAL(Channel,Class_Vtab)
211 #define astreadclassdata_msg astGLOBAL(Channel,AstReadClassData_Msg)
212 #define getattrib_buff astGLOBAL(Channel,GetAttrib_Buff)
213 #define items_written  astGLOBAL(Channel,Items_Written)
214 #define current_indent astGLOBAL(Channel,Current_Indent)
215 #define nest           astGLOBAL(Channel,Nest)
216 #define nwrite_invoc   astGLOBAL(Channel,Nwrite_Invoc)
217 #define object_class   astGLOBAL(Channel,Object_Class)
218 #define values_list    astGLOBAL(Channel,Values_List)
219 #define values_class   astGLOBAL(Channel,Values_Class)
220 #define values_ok      astGLOBAL(Channel,Values_OK)
221 #define end_of_object  astGLOBAL(Channel,End_Of_Object)
222 #define channel_data   astGLOBAL(Channel,Channel_Data)
223 
224 
225 
226 static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
227 #define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 );
228 #define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 );
229 
230 static pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;
231 #define LOCK_MUTEX3 pthread_mutex_lock( &mutex3 );
232 #define UNLOCK_MUTEX3 pthread_mutex_unlock( &mutex3 );
233 
234 /* If thread safety is not needed, declare and initialise globals at static
235    variables. */
236 #else
237 
238 /* Contextual error message reported in astReadClassData? */
239 static int astreadclassdata_msg = 0;
240 
241 /* Buffer returned by GetAttrib. */
242 static char getattrib_buff[ GETATTRIB_BUFF_LEN + 1 ];
243 
244 /* Count of the number of output items written since the last "Begin"
245    or "IsA" item. */
246 static int items_written = 0;
247 
248 /* Amount of indentation to be applied to the next output item. */
249 static int current_indent = 0;
250 
251 /* Nesting level, used to keep track of data associated with building
252    Objects when they contain other Objects. */
253 static int nest = -1;
254 
255 /* The number of times astWrite has been invoked. */
256 static int nwrite_invoc = 0;
257 
258 /* Pointer to a user-supplied block of memory to be made available to
259    source or sink functions via the astChannelData function. */
260 static void *channel_data = NULL;
261 
262 /***
263    The following items are all pointers to dynamically allocated
264    arrays (stacks) that grow as necessary to accommodate one element
265    for each level of nesting (one more than the value of "nest").
266 ***/
267 
268 /* Stack of pointers to null-terminated character strings giving the
269    names of the classes of the Objects being built at each nesting
270    level. */
271 static char **object_class = NULL;
272 
273 /* Stack of pointers to the elements designated as the "heads" of
274    circular, doubly linked lists of name-value associations. */
275 static AstChannelValue **values_list = NULL;
276 
277 /* Stack of pointers to null-terminated character strings giving the
278    names of the classes for which the values held in the values lists
279    are intended. */
280 static char **values_class = NULL;
281 
282 /* Stack of flags indicating whether the values held in the values
283    lists are intended for the class loaders currently executing to
284    build Objects at each nesting level. */
285 static int *values_ok = NULL;
286 
287 /* Stack of flags indicating whether "End" items have been read for
288    the Objects being built at each nesting level. */
289 static int *end_of_object = NULL;
290 
291 
292 /* Define the class virtual function table and its initialisation flag
293    as static variables. */
294 static AstChannelVtab class_vtab;   /* Virtual function table */
295 static int class_init = 0;       /* Virtual function table initialised? */
296 #define LOCK_MUTEX2
297 #define UNLOCK_MUTEX2
298 #define LOCK_MUTEX3
299 #define UNLOCK_MUTEX3
300 
301 #endif
302 
303 /* External Interface Function Prototypes. */
304 /* ======================================= */
305 /* The following functions have public prototypes only (i.e. no
306    protected prototypes), so we must provide local prototypes for use
307    within this module. */
308 AstChannel *astChannelForId_( const char *(*)( void ),
309                               char *(*)( const char *(*)( void ), int * ),
310                               void (*)( const char * ),
311                               void (*)( void (*)( const char * ),
312                                         const char *, int * ),
313                               const char *, ... );
314 AstChannel *astChannelId_( const char *(*)( void ), void (*)( const char * ), const char *, ... );
315 
316 /* Prototypes for Private Member Functions. */
317 /* ======================================== */
318 static AstObject *Read( AstChannel *, int * );
319 static AstObject *ReadObject( AstChannel *, const char *, AstObject *, int * );
320 static AstChannelValue *FreeValue( AstChannelValue *, int * );
321 static AstChannelValue *LookupValue( const char *, int * );
322 static AstKeyMap *Warnings( AstChannel *, int * );
323 static char *GetNextText( AstChannel *, int * );
324 static char *InputTextItem( AstChannel *, int * );
325 static char *ReadString( AstChannel *, const char *, const char *, int * );
326 static char *SourceWrap( const char *(*)( void ), int * );
327 static const char *GetAttrib( AstObject *, const char *, int * );
328 static double ReadDouble( AstChannel *, const char *, double, int * );
329 static int GetComment( AstChannel *, int * );
330 static int GetFull( AstChannel *, int * );
331 static int GetSkip( AstChannel *, int * );
332 static int GetStrict( AstChannel *, int * );
333 static int ReadInt( AstChannel *, const char *, int, int * );
334 static int TestAttrib( AstObject *, const char *, int * );
335 static int TestComment( AstChannel *, int * );
336 static int TestFull( AstChannel *, int * );
337 static int TestSkip( AstChannel *, int * );
338 static int TestStrict( AstChannel *, int * );
339 static int Use( AstChannel *, int, int, int * );
340 static int Write( AstChannel *, AstObject *, int * );
341 static void AddWarning( AstChannel *, int, const char *, const char *, int * );
342 static void AppendValue( AstChannelValue *, AstChannelValue **, int * );
343 static void ClearAttrib( AstObject *, const char *, int * );
344 static void ClearComment( AstChannel *, int * );
345 static void ClearFull( AstChannel *, int * );
346 static void ClearSkip( AstChannel *, int * );
347 static void ClearStrict( AstChannel *, int * );
348 static void ClearValues( AstChannel *, int * );
349 static void Copy( const AstObject *, AstObject *, int * );
350 static void Delete( AstObject *, int * );
351 static void Dump( AstObject *, AstChannel *, int * );
352 static void GetNextData( AstChannel *, int, char **, char **, int * );
353 static void OutputTextItem( AstChannel *, const char *, int * );
354 static void PutChannelData( AstChannel *, void *, int * );
355 static void PutNextText( AstChannel *, const char *, int * );
356 static void ReadClassData( AstChannel *, const char *, int * );
357 static void RemoveValue( AstChannelValue *, AstChannelValue **, int * );
358 static void SetAttrib( AstObject *, const char *, int * );
359 static void SetComment( AstChannel *, int, int * );
360 static void SetFull( AstChannel *, int, int * );
361 static void SetSkip( AstChannel *, int, int * );
362 static void SetStrict( AstChannel *, int, int * );
363 static void SinkWrap( void (*)( const char * ), const char *, int * );
364 static void Unquote( AstChannel *, char *, int * );
365 static void WriteBegin( AstChannel *, const char *, const char *, int * );
366 static void WriteDouble( AstChannel *, const char *, int, int, double, const char *, int * );
367 static void WriteEnd( AstChannel *, const char *, int * );
368 static void WriteInt( AstChannel *, const char *, int, int, int, const char *, int * );
369 static void WriteIsA( AstChannel *, const char *, const char *, int * );
370 static void WriteObject( AstChannel *, const char *, int, int, AstObject *, const char *, int * );
371 static void WriteString( AstChannel *, const char *, int, int, const char *, const char *, int * );
372 
373 static int GetReportLevel( AstChannel *, int * );
374 static int TestReportLevel( AstChannel *, int * );
375 static void ClearReportLevel( AstChannel *, int * );
376 static void SetReportLevel( AstChannel *, int, int * );
377 
378 static int GetIndent( AstChannel *, int * );
379 static int TestIndent( AstChannel *, int * );
380 static void ClearIndent( AstChannel *, int * );
381 static void SetIndent( AstChannel *, int, int * );
382 
383 static const char *GetSourceFile( AstChannel *, int * );
384 static int TestSourceFile( AstChannel *, int * );
385 static void ClearSourceFile( AstChannel *, int * );
386 static void SetSourceFile( AstChannel *, const char *, int * );
387 
388 static const char *GetSinkFile( AstChannel *, int * );
389 static int TestSinkFile( AstChannel *, int * );
390 static void ClearSinkFile( AstChannel *, int * );
391 static void SetSinkFile( AstChannel *, const char *, int * );
392 
393 /* Member functions. */
394 /* ================= */
AddWarning(AstChannel * this,int level,const char * msg,const char * method,int * status)395 static void AddWarning( AstChannel *this, int level, const char *msg,
396                         const char *method, int *status ) {
397 /*
398 *+
399 *  Name:
400 *     astAddWarning
401 
402 *  Purpose:
403 *     Add a warning to a Channel.
404 
405 *  Type:
406 *     Protected virtual function.
407 
408 *  Synopsis:
409 *     #include "channel.h"
410 *     void astAddWarning( AstChannel *this, int level, const char *msg,
411 *                         const char *method, int status, ... )
412 
413 *  Class Membership:
414 *     Channel method.
415 
416 *  Description:
417 *     This function stores a warning message inside a Channel. These
418 *     messages can be retirieved using astWarnings.
419 
420 *  Parameters:
421 *     this
422 *        Pointer to the Channel.
423 *     level
424 *        Ignore the warning if the ReportLevel attribute value is less
425 *        than "level".
426 *     msg
427 *        The wanting message to store. It may contain printf format
428 *        specifiers. If a NULL pointer is supplied, all warnings
429 *        currently stored in the Channel are removed.
430 *     method
431 *        The method name.
432 *     status
433 *        Inherited status value.
434 *     ...
435 *        Extra values to substitute into the message string as
436 *        replacements for the printf format specifiers.
437 *-
438 
439 *  Note: The expansion of the printf format specifiers is done in the
440 *     astAddWarning_ wrapper function. The AddWarning functions defined by
441 *     each class receives the fully expanded message and does not have a
442 *     variable argument list. The variable argument list is included in the
443 *     above prologue in order to document the wrapper function.
444 
445 */
446 
447 /* Local Variables: */
448    int i;          /* Message index */
449    char *a;        /* Pointer to copy of message */
450 
451 /* If a NULL pointer was supplied, free all warnings currently in the
452    Channel. Do this before checking the inherited status so that it works
453    even if an error has occurred. */
454    if( !msg ) {
455       for( i = 0; i < this->nwarn; i++ ) {
456          (this->warnings)[ i ] = astFree( (this->warnings)[ i ] );
457       }
458       this->warnings = astFree( this->warnings );
459       this->nwarn = 0;
460       return;
461    }
462 
463 /* Check the global error status. */
464    if ( !astOK ) return;
465 
466 /* Only proceed if the message level is sufficiently important. */
467    if( astGetReportLevel( this ) >= level ) {
468 
469 /* If we are being strict, issue an error rather than a warning. */
470       if( astGetStrict( this ) ) {
471          if( astOK ) {
472             astError( AST__BADIN, "%s(%s): %s", status, method,
473                       astGetClass( this ), msg );
474          }
475 
476 /* Otherwise, we store a copy of the message in the Channel. */
477       } else {
478 
479 /* Allocate memory and store a copy of th supplied string in it. */
480          a = astStore( NULL, msg, strlen( msg ) + 1 );
481 
482 /* Expand the array of warning pointers in ther Channel structure. */
483          this->warnings = astGrow( this->warnings, this->nwarn + 1,
484                                    sizeof( char * ) );
485 
486 /* If all is OK so far, store the new warning pointer, and increment the
487    number of warnings in the Channel. */
488          if( astOK ) {
489             (this->warnings)[ (this->nwarn)++ ] = a;
490 
491 /* Otherwise, attempt to free the memory holding the copy of the warning. */
492          } else {
493             a = astFree( a );
494          }
495       }
496    }
497 }
498 
AppendValue(AstChannelValue * value,AstChannelValue ** head,int * status)499 static void AppendValue( AstChannelValue *value, AstChannelValue **head, int *status ) {
500 /*
501 *  Name:
502 *     AppendValue
503 
504 *  Purpose:
505 *     Append a Value structure to a list.
506 
507 *  Type:
508 *     Private function.
509 
510 *  Synopsis:
511 *     #include "channel.h"
512 *     void AppendValue( AstChannelValue *value, AstChannelValue **head, int *status )
513 
514 *  Class Membership:
515 *     Channel member function.
516 
517 *  Description:
518 *     This function appends a Value structure to a doubly linked
519 *     circular list of such structures. The new list element is
520 *     inserted just in front of the element occupying the "head of
521 *     list" position (i.e. it becomes the new last element in the
522 *     list).
523 
524 *  Parameters:
525 *     value
526 *        Pointer to the new element. This must not already be in the
527 *        list.
528 *     head
529 *        Address of a pointer to the element at the head of the list
530 *        (this pointer should be NULL if the list is initially
531 *        empty). This pointer will only be updated if a new element is
532 *        being added to an empty list.
533 *     status
534 *        Pointer to the inherited status variable.
535 
536 *  Notes:
537 *     - This function does not perform error chacking and does not
538 *     generate errors.
539 */
540 
541 /* If the list is initially empty, the sole new element points at
542    itself. */
543    if ( !*head ) {
544       value->flink = value;
545       value->blink = value;
546 
547 /* Update the list head to identify the new element. */
548       *head = value;
549 
550 /* Otherwise, insert the new element in front of the element at the
551    head of the list. */
552    } else {
553       value->flink = *head;
554       value->blink = ( *head )->blink;
555       ( *head )->blink = value;
556       value->blink->flink = value;
557    }
558 }
559 
astChannelData_(void)560 void *astChannelData_( void ) {
561 /*
562 c++
563 *  Name:
564 *     astChannelData
565 
566 *  Purpose:
567 *     Return a pointer to user-supplied data stored with a Channel.
568 
569 *  Type:
570 *     Public macro.
571 
572 *  Synopsis:
573 *     #include "channel.h"
574 *     void *astChannelData
575 
576 *  Class Membership:
577 *     Channel macro.
578 
579 *  Description:
580 *     This macro is intended to be used within the source or sink
581 *     functions associated with a Channel. It returns any pointer
582 *     previously stored in the Channel (that is, the Channel that has
583 *     invoked the source or sink function) using astPutChannelData.
584 *
585 *     This mechanism is a thread-safe alternative to passing file
586 *     descriptors, etc, via static global variables.
587 
588 *  Returned Value:
589 *     astChannelData
590 *        The pointer previously stored with the Channel using
591 *        astPutChannelData. A NULL pointer will be returned if no such
592 *        pointer has been stored with the Channel.
593 
594 *  Applicability:
595 *     Channel
596 *        This macro applies to all Channels.
597 
598 *  Notes:
599 *     - This routine is not available in the Fortran 77 interface to
600 *     the AST library.
601 c--
602 */
603    astDECLARE_GLOBALS
604    astGET_GLOBALS(NULL);
605    return channel_data;
606 }
607 
ClearAttrib(AstObject * this_object,const char * attrib,int * status)608 static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
609 /*
610 *  Name:
611 *     ClearAttrib
612 
613 *  Purpose:
614 *     Clear an attribute value for a Channel.
615 
616 *  Type:
617 *     Private function.
618 
619 *  Synopsis:
620 *     #include "channel.h"
621 *     void ClearAttrib( AstObject *this, const char *attrib, int *status )
622 
623 *  Class Membership:
624 *     Channel member function (over-rides the astClearAttrib protected
625 *     method inherited from the Object class).
626 
627 *  Description:
628 *     This function clears the value of a specified attribute for a
629 *     Channel, so that the default value will subsequently be used.
630 
631 *  Parameters:
632 *     this
633 *        Pointer to the Channel.
634 *     attrib
635 *        Pointer to a null terminated string specifying the attribute
636 *        name.  This should be in lower case with no surrounding white
637 *        space.
638 *     status
639 *        Pointer to the inherited status variable.
640 */
641 
642 /* Local Variables: */
643    AstChannel *this;              /* Pointer to the Channel structure */
644 
645 /* Check the global error status. */
646    if ( !astOK ) return;
647 
648 /* Obtain a pointer to the Channel structure. */
649    this = (AstChannel *) this_object;
650 
651 /* Check the attribute name and clear the appropriate attribute. */
652 
653 /* Comment. */
654 /* -------- */
655    if ( !strcmp( attrib, "comment" ) ) {
656       astClearComment( this );
657 
658 /* Full. */
659 /* ----- */
660    } else if ( !strcmp( attrib, "full" ) ) {
661       astClearFull( this );
662 
663 /* Indent. */
664 /* ------- */
665    } else if ( !strcmp( attrib, "indent" ) ) {
666       astClearIndent( this );
667 
668 /* ReportLevel. */
669 /* ------------ */
670    } else if ( !strcmp( attrib, "reportlevel" ) ) {
671       astClearReportLevel( this );
672 
673 /* Skip. */
674 /* ----- */
675    } else if ( !strcmp( attrib, "skip" ) ) {
676       astClearSkip( this );
677 
678 /* SourceFile. */
679 /* ----------- */
680    } else if ( !strcmp( attrib, "sourcefile" ) ) {
681       astClearSourceFile( this );
682 
683 /* SinkFile. */
684 /* --------- */
685    } else if ( !strcmp( attrib, "sinkfile" ) ) {
686       astClearSinkFile( this );
687 
688 /* Strict. */
689 /* ------- */
690    } else if ( !strcmp( attrib, "strict" ) ) {
691       astClearStrict( this );
692 
693 /* If the attribute is still not recognised, pass it on to the parent
694    method for further interpretation. */
695    } else {
696       (*parent_clearattrib)( this_object, attrib, status );
697    }
698 }
699 
ClearValues(AstChannel * this,int * status)700 static void ClearValues( AstChannel *this, int *status ) {
701 /*
702 *  Name:
703 *     ClearValues
704 
705 *  Purpose:
706 *     Clear the current values list.
707 
708 *  Type:
709 *     Private function.
710 
711 *  Synopsis:
712 *     #include "channel.h"
713 *     void ClearValues( AstChannel *this, int *status )
714 
715 *  Class Membership:
716 *     Channel member function.
717 
718 *  Description:
719 *     This function clears any (un-read) Value structures remaining in
720 *     the current values list (i.e. at the current nesting level). It
721 *     should be invoked after all required values have been read.
722 *
723 *     If the values list has not been read, or if any remaining values
724 *     are found (i.e. the list is not empty) then this indicates an
725 *     unrecognised input class or an input value that has not been
726 *     read by a class loader. This implies an error in the loader, or
727 *     bad input data, so an error is reported.
728 *
729 *     All resources used by any remaining Value structures are freed
730 *     and the values list is left in an empty state.
731 
732 *  Parameters:
733 *     this
734 *        Pointer to the Channel being read. This is only used for
735 *        constructing error messages. It must not be NULL.
736 *     status
737 *        Pointer to the inherited status variable.
738 
739 *  Notes:
740 *     - This function attempts to execute even if the global error
741 *     status is set on entry, although no further error report will be
742 *     made if it subsequently fails under these circumstances.
743 */
744 
745 /* Local Variables: */
746    astDECLARE_GLOBALS            /* Declare the thread specific global data */
747    AstChannelValue **head;       /* Address of pointer to values list */
748    AstChannelValue *value;       /* Pointer to value list element */
749 
750 /* Get a pointer to the structure holding thread-specific global data. */
751    astGET_GLOBALS(this);
752 
753 /* If "values_class" is non-NULL, then the values list has previously
754    been filled with Values for a class. */
755    if ( values_class[ nest ] ) {
756 
757 /* If "values_ok" is zero, however, then these Values have not yet
758    been read by a class loader. This must be due to a bad class name
759    associated with them or because the class data are not available in
760    the correct order. If we are using strict error reporting, then report
761    an error (unless the error status is already set). */
762       if ( astGetStrict( this ) && !values_ok[ nest ] && astOK ) {
763          astError( AST__BADIN,
764                    "astRead(%s): Invalid class structure in input data.", status,
765                    astGetClass( this ) );
766          astError( AST__BADIN,
767                    "Class \"%s\" is invalid or out of order within a %s.", status,
768                    values_class[ nest ], object_class[ nest ] );
769       }
770 
771 /* Free the memory holding the class string. */
772       values_class[ nest ] = astFree( values_class[ nest ] );
773    }
774 
775 /* Reset the "values_ok" flag. */
776    values_ok[ nest ] = 0;
777 
778 /* Now clear any Values remaining in the values list. Obtain the
779    address of the pointer to the head of this list (at the current
780    nesting level) and loop to remove Values from the list while it is
781    not empty. */
782    head = values_list + nest;
783    while ( *head ) {
784 
785 /* Obtain a pointer to the first element. */
786       value = *head;
787 
788 /* Issue a warning. */
789       if ( value->is_object ) {
790          astAddWarning( this, 1, "The Object \"%s = <%s>\" was "
791                         "not recognised as valid input.", "astRead", status,
792                         value->name, astGetClass( value->ptr.object ) );
793       } else {
794          astAddWarning( this, 1, "The value \"%s = %s\" was not "
795                         "recognised as valid input.", "astRead", status,
796                         value->name, value->ptr.string );
797       }
798 
799 /* Remove the Value structure from the list (which updates the head of
800    list pointer) and free its resources. */
801       RemoveValue( value, head, status );
802       value = FreeValue( value, status );
803    }
804 }
805 
FreeValue(AstChannelValue * value,int * status)806 static AstChannelValue *FreeValue( AstChannelValue *value, int *status ) {
807 /*
808 *  Name:
809 *     FreeValue
810 
811 *  Purpose:
812 *     Free a dynamically allocated Value structure.
813 
814 *  Type:
815 *     Private function.
816 
817 *  Synopsis:
818 *     #include "channel.h"
819 *     AstChannelValue *FreeValue( AstChannelValue *value, int *status )
820 
821 *  Class Membership:
822 *     Channel member function.
823 
824 *  Description:
825 *     This function frees a dynamically allocated Value structure,
826 *     releasing all resources used by it. The structure contents must
827 *     have been correctly initialised.
828 
829 *  Parameters:
830 *     value
831 *        Pointer to the Value structure to be freed.
832 *     status
833 *        Pointer to the inherited status variable.
834 
835 *  Returned Value:
836 *     A NULL pointer is always returned.
837 
838 *  Notes:
839 *     - This function attempts to execute even if the global error
840 *     status is set on entry, although no further error report will be
841 *     made if it subsequently fails under these circumstances.
842 */
843 
844 /* Check that a non-NULL pointer has been supplied. */
845    if ( value ) {
846 
847 /* If the "name" component has been allocated, then free it. */
848       if ( value->name ) value->name = astFree( value->name );
849 
850 /* If the "ptr" component identifies an Object, then annul the Object
851    pointer. */
852       if ( value->is_object ) {
853          if ( value->ptr.object ) {
854             value->ptr.object = astAnnul( value->ptr.object );
855          }
856 
857 /* Otherwise, if it identifies a string, then free the string. */
858       } else {
859          if ( value->ptr.string ) {
860             value->ptr.string = astFree( value->ptr.string );
861          }
862       }
863 
864 /* Free the Value structure itself. */
865       value = astFree( value );
866    }
867 
868 /* Return a NULL pointer. */
869    return NULL;
870 }
871 
GetAttrib(AstObject * this_object,const char * attrib,int * status)872 static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
873 /*
874 *  Name:
875 *     GetAttrib
876 
877 *  Purpose:
878 *     Get the value of a specified attribute for a Channel.
879 
880 *  Type:
881 *     Private function.
882 
883 *  Synopsis:
884 *     #include "channel.h"
885 *     const char *GetAttrib( AstObject *this, const char *attrib, int *status )
886 
887 *  Class Membership:
888 *     Channel member function (over-rides the protected astGetAttrib
889 *     method inherited from the Object class).
890 
891 *  Description:
892 *     This function returns a pointer to the value of a specified
893 *     attribute for a Channel, formatted as a character string.
894 
895 *  Parameters:
896 *     this
897 *        Pointer to the Channel.
898 *     attrib
899 *        Pointer to a null terminated string containing the name of
900 *        the attribute whose value is required. This name should be in
901 *        lower case, with all white space removed.
902 *     status
903 *        Pointer to the inherited status variable.
904 
905 *  Returned Value:
906 *     - Pointer to a null terminated string containing the attribute
907 *     value.
908 
909 *  Notes:
910 *     - The returned string pointer may point at memory allocated
911 *     within the Channel, or at static memory. The contents of the
912 *     string may be over-written or the pointer may become invalid
913 *     following a further invocation of the same function or any
914 *     modification of the Channel. A copy of the string should
915 *     therefore be made if necessary.
916 *     - A NULL pointer will be returned if this function is invoked
917 *     with the global error status set, or if it should fail for any
918 *     reason.
919 */
920 
921 /* Local Variables: */
922    astDECLARE_GLOBALS            /* Declare the thread specific global data */
923    AstChannel *this;             /* Pointer to the Channel structure */
924    const char *result;           /* Pointer value to return */
925    int comment;                  /* Comment attribute value */
926    int full;                     /* Full attribute value */
927    int indent;                   /* Indent attribute value */
928    int report_level;             /* ReportLevel attribute value */
929    int skip;                     /* Skip attribute value */
930    int strict;                   /* Report errors insead of warnings? */
931 
932 /* Initialise. */
933    result = NULL;
934 
935 /* Check the global error status. */
936    if ( !astOK ) return result;
937 
938 /* Get a pointer to the structure holding thread-specific global data. */
939    astGET_GLOBALS(this_object);
940 
941 /* Obtain a pointer to the Channel structure. */
942    this = (AstChannel *) this_object;
943 
944 /* Compare "attrib" with each recognised attribute name in turn,
945    obtaining the value of the required attribute. If necessary, write
946    the value into "getattrib_buff" as a null terminated string in an appropriate
947    format.  Set "result" to point at the result string. */
948 
949 /* Comment. */
950 /* -------- */
951    if ( !strcmp( attrib, "comment" ) ) {
952       comment = astGetComment( this );
953       if ( astOK ) {
954          (void) sprintf( getattrib_buff, "%d", comment );
955          result = getattrib_buff;
956       }
957 
958 /* Full. */
959 /* ----- */
960    } else if ( !strcmp( attrib, "full" ) ) {
961       full = astGetFull( this );
962       if ( astOK ) {
963          (void) sprintf( getattrib_buff, "%d", full );
964          result = getattrib_buff;
965       }
966 
967 /* Indent. */
968 /* ------- */
969    } else if ( !strcmp( attrib, "indent" ) ) {
970       indent = astGetIndent( this );
971       if ( astOK ) {
972          (void) sprintf( getattrib_buff, "%d", indent );
973          result = getattrib_buff;
974       }
975 
976 /* ReportLevel. */
977 /* ------------ */
978    } else if ( !strcmp( attrib, "reportlevel" ) ) {
979       report_level = astGetReportLevel( this );
980       if ( astOK ) {
981          (void) sprintf( getattrib_buff, "%d", report_level );
982          result = getattrib_buff;
983       }
984 
985 /* Skip. */
986 /* ----- */
987    } else if ( !strcmp( attrib, "skip" ) ) {
988       skip = astGetSkip( this );
989       if ( astOK ) {
990          (void) sprintf( getattrib_buff, "%d", skip );
991          result = getattrib_buff;
992       }
993 
994 /* SourceFile. */
995 /* ----------- */
996    } else if ( !strcmp( attrib, "sourcefile" ) ) {
997       result = astGetSourceFile( this );
998 
999 /* SinkFile. */
1000 /* --------- */
1001    } else if ( !strcmp( attrib, "sinkfile" ) ) {
1002       result = astGetSinkFile( this );
1003 
1004 /* Strict. */
1005 /* ------- */
1006    } else if ( !strcmp( attrib, "strict" ) ) {
1007       strict = astGetStrict( this );
1008       if ( astOK ) {
1009          (void) sprintf( getattrib_buff, "%d", strict );
1010          result = getattrib_buff;
1011       }
1012 
1013 /* If the attribute name was not recognised, pass it on to the parent
1014    method for further interpretation. */
1015    } else {
1016       result = (*parent_getattrib)( this_object, attrib, status );
1017    }
1018 
1019 /* Return the result. */
1020    return result;
1021 
1022 }
1023 
GetNextData(AstChannel * this,int skip,char ** name,char ** val,int * status)1024 static void GetNextData( AstChannel *this, int skip, char **name,
1025                          char **val, int *status ) {
1026 /*
1027 *+
1028 *  Name:
1029 *     astGetNextData
1030 
1031 *  Purpose:
1032 *     Read the next item of data from a data source.
1033 
1034 *  Type:
1035 *     Protected virtual function.
1036 
1037 *  Synopsis:
1038 *     #include "channel.h"
1039 *     void astGetNextData( AstChannel *this, int skip, char **name,
1040 *                          char **val )
1041 
1042 *  Class Membership:
1043 *     Channel method.
1044 
1045 *  Description:
1046 *     This function reads the next item of input data from a data
1047 *     source associated with a Channel and returns the result.
1048 *
1049 *     It is layered conceptually on the astGetNextText method, but
1050 *     instead of returning the raw input text, it decodes it and
1051 *     returns name/value pairs ready for use. Note that in some
1052 *     derived classes, where the data are not stored as text, this
1053 *     function may not actually use astGetNextText, but will access
1054 *     the data directly.
1055 
1056 *  Parameters:
1057 *     this
1058 *        Pointer to the Channel.
1059 *     skip
1060 *        A non-zero value indicates that a new Object is to be read,
1061 *        and that all input data up to the next "Begin" item are to be
1062 *        skipped in order to locate it. This is useful if the data
1063 *        source contains AST objects interspersed with other data (but
1064 *        note that these other data cannot appear inside AST Objects,
1065 *        only between them).
1066 *
1067 *        A zero value indicates that all input data are significant
1068 *        and the next item will therefore be read and an attempt made
1069 *        to interpret it whatever it contains. Any other data
1070 *        inter-mixed with AST Objects will then result in an error.
1071 *     name
1072 *        An address at which to store a pointer to a null-terminated
1073 *        dynamically allocated string containing the name of the next
1074 *        item in the input data stream. This name will be in lower
1075 *        case with no surrounding white space.  It is the callers
1076 *        responsibilty to free the memory holding this string (using
1077 *        astFree) when it is no longer required.
1078 *
1079 *        A NULL pointer value will be returned (without error) to
1080 *        indicate when there are no further input data items to be
1081 *        read.
1082 *     val
1083 *        An address at which to store a pointer to a null-terminated
1084 *        dynamically allocated string containing the value associated
1085 *        with the next item in the input data stream. No case
1086 *        conversion is performed on this string and all white space is
1087 *        potentially significant.  It is the callers responsibilty to
1088 *        free the memory holding this string (using astFree) when it
1089 *        is no longer required.
1090 *
1091 *        The returned pointer will be NULL if an Object data item is
1092 *        read (see the "Data Representation" section).
1093 
1094 *  Data Representation:
1095 *     The returned data items fall into the following categories:
1096 *
1097 *     - Begin: Identified by the name string "begin", this indicates
1098 *     the start of an Object definition. The associated value string
1099 *     gives the class name of the Object being defined.
1100 *
1101 *     - IsA: Identified by the name string "isa", this indicates the
1102 *     end of the data associated with a particular class structure
1103 *     within the definiton of a larger Object. The associated value
1104 *     string gives the name of the class whose data have just been
1105 *     read.
1106 *
1107 *     - End: Identified by the name string "end", this indicates the
1108 *     end of the data associated with a complete Object
1109 *     definition. The associated value string gives the class name of
1110 *     the Object whose definition is being ended.
1111 *
1112 *     - Non-Object: Identified by any other name string plus a
1113 *     non-NULL "val" pointer, this gives the value of a non-Object
1114 *     structure component (instance variable). The name identifies
1115 *     which instance variable it is (within the context of the class
1116 *     whose data are being read) and the value is encoded as a string.
1117 *
1118 *     - Object: Identified by any other name string plus a NULL "val"
1119 *     pointer, this identifies the value of an Object structure
1120 *     component (instance variable).  The name identifies which
1121 *     instance variable it is (within the context of the class whose
1122 *     data are being read) and the value is given by subsequent data
1123 *     items (so the next item should be a "Begin" item).
1124 
1125 *  Notes:
1126 *     - NULL pointer values will be returned if this function is
1127 *     invoked with the global error status set, or if it should fail
1128 *     for any reason.
1129 *     - This method is provided primarily so that derived classes may
1130 *     over-ride it in order to read from alternative data sources. It
1131 *     provides a higher-level interface than astGetNextText, so is
1132 *     suitable for classes that either need to read textual data in a
1133 *     different format, or to read from non-textual data sources.
1134 *-
1135 */
1136 
1137 /* Local Variables: */
1138    char *line;                   /* Pointer to input text line */
1139    int done;                     /* Data item read? */
1140    int i;                        /* Loop counter for string characters */
1141    int len;                      /* Length of input text line */
1142    int nc1;                      /* Offset to start of first field */
1143    int nc2;                      /* Offset to end of first field */
1144    int nc3;                      /* Offset to start of second field */
1145    int nc;                       /* Number of charaters read by "astSscanf" */
1146 
1147 /* Initialise the returned values. */
1148    *name = NULL;
1149    *val = NULL;
1150 
1151 /* Check the global error status. */
1152    if ( !astOK ) return;
1153 
1154 /* Read the next input line as text (the loop is needed to allow
1155    initial lines to be skipped if the "skip" flag is set). */
1156    done = 0;
1157    while ( !done && ( line = InputTextItem( this, status ) ) && astOK ) {
1158 
1159 /* If OK, determine the line length. */
1160       len = strlen( line );
1161 
1162 /* Non-Object value. */
1163 /* ----------------- */
1164 /* Test for lines of the form " name = value" (or similar), where the
1165    name is no more than MAX_NAME characters long (the presence of a
1166    value on the right hand side indicates that this is a non-Object
1167    value, encoded as a string). Ignore these lines if the "skip" flag
1168    is set. */
1169       if ( nc = 0,
1170            ( !skip
1171              && ( 0 == astSscanf( line,
1172                                " %n%*" MAX_NAME "[^ \t=]%n = %n%*[^\n]%n",
1173                                &nc1, &nc2, &nc3, &nc ) )
1174              && ( nc >= len ) ) ) {
1175 
1176 /* Note we have found a data item. */
1177          done = 1;
1178 
1179 /* Extract the name and value fields. */
1180          *name = astString( line + nc1, nc2 - nc1 );
1181          *val = astString( line + nc3, len - nc3 );
1182 
1183 /* If OK, truncate the value to remove any trailing white space. */
1184          if ( astOK ) {
1185             i = len - nc3 - 1;
1186             while ( ( i >= 0 ) && isspace( ( *val )[ i ] ) ) i--;
1187             ( *val )[ i + 1 ] = '\0';
1188 
1189 /* Also remove any quotes from the string. */
1190             Unquote( this, *val, status );
1191          }
1192 
1193 /* Object value. */
1194 /* ------------- */
1195 /* Test for lines of the form " name = " (or similar), where the name
1196    is no more than MAX_NAME characters long (the absence of a value on
1197    the right hand side indicates that this is an Object, whose
1198    definition follows on subsequent input lines). Ignore these lines
1199    if the "skip" flag is set. */
1200       } else if ( nc = 0,
1201                   ( !skip
1202                     && ( 0 == astSscanf( line,
1203                                       " %n%*" MAX_NAME "[^ \t=]%n = %n",
1204                                       &nc1, &nc2, &nc ) )
1205                     && ( nc >= len ) ) ) {
1206 
1207 /* Note we have found a data item. */
1208          done = 1;
1209 
1210 /* Extract the name field but leave the value pointer as NULL. */
1211          *name = astString( line + nc1, nc2 - nc1 );
1212 
1213 /* Begin. */
1214 /* ------ */
1215 /* Test for lines of the form " Begin Class " (or similar). */
1216       } else if ( nc = 0,
1217              ( ( 0 == astSscanf( line,
1218                              " %*1[Bb]%*1[Ee]%*1[Gg]%*1[Ii]%*1[Nn] %n%*s%n %n",
1219                               &nc1, &nc2, &nc ) )
1220                && ( nc >= len ) ) ) {
1221 
1222 /* Note we have found a data item. */
1223          done = 1;
1224 
1225 /* Set the returned name to "begin" and extract the associated class
1226    name for the value. Store both of these in dynamically allocated
1227    strings. */
1228          *name = astString( "begin", 5 );
1229          *val = astString( line + nc1, nc2 - nc1 );
1230 
1231 /* IsA. */
1232 /* ---- */
1233 /* Test for lines of the form " IsA Class " (or similar). Ignore these
1234    lies if the "skip" flag is set. */
1235       } else if ( nc = 0,
1236                   ( !skip
1237                     && ( 0 == astSscanf( line,
1238                                       " %*1[Ii]%*1[Ss]%*1[Aa] %n%*s%n %n",
1239                                       &nc1, &nc2, &nc ) )
1240                     && ( nc >= len ) ) ) {
1241 
1242 /* Note we have found a data item. */
1243          done = 1;
1244 
1245 /* Set the returned name to "isa" and extract the associated class
1246    name for the value. */
1247          *name = astString( "isa", 3 );
1248          *val = astString( line + nc1, nc2 - nc1 );
1249 
1250 /* End. */
1251 /* ---- */
1252 /* Test for lines of the form " End Class " (or similar). Ignore these
1253    lines if the "skip" flag is set. */
1254       } else if ( nc = 0,
1255                   ( !skip
1256                     && ( 0 == astSscanf( line,
1257                                       " %*1[Ee]%*1[Nn]%*1[Dd] %n%*s%n %n",
1258                                       &nc1, &nc2, &nc ) )
1259                     && ( nc >= len ) ) ) {
1260 
1261 /* Note we have found a data item. */
1262          done = 1;
1263 
1264 /* If found, set the returned name to "end" and extract the associated
1265    class name for the value. */
1266          *name = astString( "end", 3 );
1267          *val = astString( line + nc1, nc2 - nc1 );
1268 
1269 /* If the input line didn't match any of the above and the "skip" flag
1270    is not set, then report an error. */
1271       } else if ( !skip ) {
1272          astError( AST__BADIN,
1273                    "astRead(%s): Cannot interpret the input data: \"%s\".", status,
1274                    astGetClass( this ), line );
1275       }
1276 
1277 /* Free the memory holding the input data as text. */
1278       line = astFree( line );
1279    }
1280 
1281 /* If successful, convert the name to lower case. */
1282    if ( astOK && *name ) {
1283       for ( i = 0; ( *name )[ i ]; i++ ) {
1284          ( *name )[ i ] = tolower( ( *name )[ i ] );
1285       }
1286    }
1287 
1288 /* If an error occurred, ensure that any memory allocated is freed and
1289    that NULL pointer values are returned. */
1290    if ( !astOK ) {
1291       *name = astFree( *name );
1292       *val = astFree( *val );
1293    }
1294 }
1295 
GetNextText(AstChannel * this,int * status)1296 static char *GetNextText( AstChannel *this, int *status ) {
1297 /*
1298 *+
1299 *  Name:
1300 *     GetNextText
1301 
1302 *  Purpose:
1303 *     Read the next line of input text from a data source.
1304 
1305 *  Type:
1306 *     Protected virtual function.
1307 
1308 *  Synopsis:
1309 *     #include "channel.h"
1310 *     char *astGetNextText( AstChannel *this )
1311 
1312 *  Class Membership:
1313 *     Channel method.
1314 
1315 *  Description:
1316 *     This function reads the next "raw" input line of text from the
1317 *     data source associated with a Channel.
1318 *
1319 *     Each line is returned as a pointer to a null-terminated string
1320 *     held in dynamic memory, and it is the caller's responsibility to
1321 *     free this memory (using astFree) when it is no longer
1322 *     required. A NULL pointer is returned if there are no more input
1323 *     lines to be read.
1324 
1325 *  Parameters:
1326 *     this
1327 *        Pointer to the Channel.
1328 
1329 *  Returned Value:
1330 *     Pointer to a null-terminated string containing the input line
1331 *     (held in dynamically allocated memory, which must be freed by
1332 *     the caller when no longer required). A NULL pointer is returned
1333 *     if there are no more input lines to be read.
1334 
1335 *  Notes:
1336 *     - A NULL pointer will be returned if this function is invoked
1337 *     with the global error status set, or if it should fail for any
1338 *     reason.
1339 *     - This method is provided primarily so that derived classes may
1340 *     over-ride it in order to read from alternative (textual) data
1341 *     sources.
1342 *-
1343 */
1344 
1345 /* Local Constants: */
1346 #define MIN_CHARS 81             /* Initial size for allocating memory */
1347 #define ERRBUF_LEN 80
1348 
1349 /* Local Variables: */
1350    FILE *fd;                     /* Input file descriptor */
1351    char *errstat;                /* Pointer for system error message */
1352    char *line;                   /* Pointer to line data to be returned */
1353    char errbuf[ ERRBUF_LEN ];    /* Buffer for system error message */
1354    const char *sink_file;        /* Path to output sink file */
1355    const char *source_file;      /* Path to source file */
1356    int c;                        /* Input character */
1357    int len;                      /* Length of input line */
1358    int readstat;                 /* "errno" value set by "getchar" */
1359    int size;                     /* Size of allocated memory */
1360 
1361 /* Initialise. */
1362    line = NULL;
1363 
1364 /* Check the global error status. */
1365    if ( !astOK ) return line;
1366 
1367 /* If the SourceFile attribute of the Channel specifies an input file,
1368    but no input file has yet been opened, open it now. Report an error if
1369    it is the same as the sink file. */
1370    if( astTestSourceFile( this ) && !this->fd_in ) {
1371       source_file = astGetSourceFile( this );
1372 
1373       if( this->fd_out ) {
1374          sink_file = astGetSinkFile( this );
1375          if( astOK && !strcmp( sink_file, source_file ) ) {
1376             astError( AST__RDERR, "astRead(%s): Failed to open input "
1377                       "SourceFile '%s' - the file is currently being used "
1378                       "as the output SinkFile.", status, astGetClass( this ),
1379                       source_file );
1380          }
1381       }
1382 
1383       if( astOK ) {
1384          this->fd_in = fopen( source_file, "r" );
1385          if( !this->fd_in ) {
1386             if ( errno ) {
1387 #if HAVE_STRERROR_R
1388                strerror_r( errno, errbuf, ERRBUF_LEN );
1389                errstat = errbuf;
1390 #else
1391                errstat = strerror( errno );
1392 #endif
1393                astError( AST__RDERR, "astRead(%s): Failed to open input "
1394                          "SourceFile '%s' - %s.", status, astGetClass( this ),
1395                          source_file, errstat );
1396             } else {
1397                astError( AST__RDERR, "astRead(%s): Failed to open input "
1398                          "SourceFile '%s'.", status, astGetClass( this ),
1399                          source_file );
1400             }
1401          }
1402 
1403       }
1404    }
1405 
1406 /* Source function defined, but no input file. */
1407 /* ------------------------------------------- */
1408 /* If no active input file descriptor is stored in the Channel, but
1409    a source function (and its wrapper function) is defined for the
1410    Channel, use the wrapper function to invoke the source function to
1411    read a line of input text. This is returned in a dynamically
1412    allocated string. */
1413    if ( !this->fd_in && this->source && this->source_wrap ) {
1414 
1415 /* About to call an externally supplied function which may not be
1416    thread-safe, so lock a mutex first. Also store the channel data
1417    pointer in a global variable so that it can be accessed in the source
1418    function using macro astChannelData. */
1419       astStoreChannelData( this );
1420       LOCK_MUTEX3;
1421       line = ( *this->source_wrap )( this->source, status );
1422       UNLOCK_MUTEX3;
1423 
1424 /* Input file defined, or no source function. */
1425 /* ------------------------------------------ */
1426 /* Read the line from the input file or from standard input. */
1427    } else if( astOK ) {
1428       c = '\0';
1429       len = 0;
1430       size = 0;
1431 
1432 /* Choose the file descriptor to use. */
1433       fd = this->fd_in ? this->fd_in : stdin;
1434 
1435 /* Loop to read input characters, saving any "errno" value that may be
1436    set by "getchar" if an error occurs. Quit if an end of file (or
1437    error) occurs or if a newline character is read. */
1438       while ( errno = 0, c = getc( fd ), readstat = errno,
1439               ( c != EOF ) && ( c != '\n' ) ) {
1440 
1441 /* If no memory has yet been allocated to hold the line, allocate some
1442    now, using MIN_CHARS as the initial line length. */
1443          if ( !line ) {
1444             line = astMalloc( sizeof( char ) * (size_t) MIN_CHARS );
1445             size = MIN_CHARS;
1446 
1447 /* If memory has previously been allocated, extend it when necessary
1448    to hold the new input character (plus a terminating null) and note
1449    the new size. */
1450          } else if ( ( len + 2 ) > size ) {
1451             line = astGrow( line, len + 2, sizeof( char ) );
1452             if ( !astOK ) break;
1453             size = (int) astSizeOf( line );
1454          }
1455 
1456 /* Store the character just read. */
1457          line[ len++ ] = c;
1458       }
1459 
1460 /* If the above loop completed without setting the global error
1461    status, check the last character read and use "ferror" to see if a
1462    read error occurred. If so, report the error, using the saved
1463    "errno" value (but only if one was set). */
1464       if ( astOK && ( c == EOF ) && ferror( fd ) ) {
1465          if ( readstat ) {
1466 #if HAVE_STRERROR_R
1467             strerror_r( readstat, errbuf, ERRBUF_LEN );
1468             errstat = errbuf;
1469 #else
1470             errstat = strerror( readstat );
1471 #endif
1472             astError( AST__RDERR,
1473                       "astRead(%s): Read error on standard input - %s.", status,
1474                       astGetClass( this ), errstat );
1475          } else {
1476             astError( AST__RDERR,
1477                       "astRead(%s): Read error on standard input.", status,
1478                       astGetClass( this ) );
1479          }
1480       }
1481 
1482 /* If an empty line has been read, allocate memory to hold an empty
1483    string. */
1484       if ( !line && ( c == '\n' ) ) {
1485          line = astMalloc( sizeof( char ) );
1486       }
1487 
1488 /* If memory has been allocated and there has been no error,
1489    null-terminate the string of input characters. */
1490       if ( line ) {
1491          if ( astOK ) {
1492             line[ len ] = '\0';
1493 
1494 /* If there has been an error, free the allocated memory. */
1495          } else {
1496             line = astFree( line );
1497          }
1498       }
1499    }
1500 
1501 
1502 /* Return the result pointer. */
1503    return line;
1504 
1505 /* Undefine macros local to this function. */
1506 #undef MIN_CHARS
1507 #undef ERRBUF_LEN
1508 }
1509 
Warnings(AstChannel * this,int * status)1510 static AstKeyMap *Warnings( AstChannel *this, int *status ){
1511 /*
1512 *++
1513 *  Name:
1514 c     astWarnings
1515 f     AST_WARNINGS
1516 
1517 *  Purpose:
1518 *     Returns any warnings issued by the previous read or write operation.
1519 
1520 *  Type:
1521 *     Public virtual function.
1522 
1523 *  Synopsis:
1524 c     #include "channel.h"
1525 c     AstKeyMap *astWarnings( AstChannel *this )
1526 f     RESULT = AST_WARNINGS( THIS, STATUS )
1527 
1528 *  Class Membership:
1529 *     Channel member function.
1530 
1531 *  Description:
1532 *     This function returns an AST KeyMap object holding the text of any
1533 *     warnings issued as a result of the previous invocation of the
1534 c     astRead or astWrite
1535 f     AST_READ or AST_WRITE
1536 *     function on the Channel. If no warnings were issued, a
1537 c     a NULL value
1538 f     AST__NULL
1539 *     will be returned.
1540 *
1541 *     Such warnings are non-fatal and will not prevent the
1542 *     read or write operation succeeding. However, the converted object
1543 *     may not be identical to the original object in all respects.
1544 *     Differences which would usually be deemed as insignificant in most
1545 *     usual cases will generate a warning, whereas more significant
1546 *     differences will generate an error.
1547 *
1548 *     The "Strict" attribute allows this warning facility to be switched
1549 *     off, so that a fatal error is always reported for any conversion
1550 *     error.
1551 
1552 *  Parameters:
1553 c     this
1554 f     THIS = INTEGER (Given)
1555 *        Pointer to the Channel.
1556 f     STATUS = INTEGER (Given and Returned)
1557 f        The global status.
1558 
1559 *  Returned Value:
1560 c     astWarnings()
1561 f     AST_WARNINGS = INTEGER
1562 *        A pointer to the KeyMap holding the warning messages, or
1563 c        NULL
1564 f        AST__NULL
1565 *        if no warnings were issued during the previous read operation.
1566 
1567 *  Applicability:
1568 *     Channel
1569 *        The basic Channel class generates a warning when ever an
1570 *        un-recognised item is encountered whilst reading an Object from
1571 *        an external data source. If Strict is zero (the default), then
1572 *        unexpected items in the Object description are simply ignored,
1573 *        and any remaining items are used to construct the returned
1574 *        Object. If Strict is non-zero, an error will be reported and a
1575 *        NULL Object pointer returned if any unexpected items are
1576 *        encountered.
1577 *
1578 *        As AST continues to be developed, new attributes are added
1579 *        occasionally to selected classes. If an older version of AST is
1580 *        used to read external Object descriptions created by a more
1581 *        recent version of AST, then the Channel class will, by default,
1582 *        ignore the new attributes, using the remaining attributes to
1583 *        construct the Object. This is usually a good thing. However,
1584 *        since external Object descriptions are often stored in plain
1585 *        text, it is possible to edit them using a text editor. This
1586 *        gives rise to the possibility of genuine errors in the
1587 *        description due to finger-slips, typos, or simple
1588 *        mis-understanding. Such inappropriate attributes will be ignored
1589 *        if Strict is left at its default zero value. This will cause the
1590 *        mis-spelled attribute to revert to its default value,
1591 *        potentially causing subtle changes in the behaviour of
1592 *        application software. If such an effect is suspected, the Strict
1593 *        attribute can be set non-zero, resulting in the erroneous
1594 *        attribute being identified in an error message.
1595 *     FitsChan
1596 *        The returned KeyMap will contain warnings for all conditions
1597 *        listed in the Warnings attribute.
1598 *     XmlChan
1599 *        Reports conversion errors that result in what are usally
1600 *        insignificant  changes.
1601 
1602 *  Notes:
1603 *     - The returned KeyMap uses keys of the form "Warning_1",
1604 *     "Warning_2", etc.
1605 *     - A value of
1606 c     NULL will be returned if this function is invoked with the AST
1607 c     error status set,
1608 f     AST__NULL will be returned if this function is invoked with STATUS
1609 f     set to an error value,
1610 *     or if it should fail for any reason.
1611 *--
1612 */
1613 
1614 /* Local Variables: */
1615    AstKeyMap *result;
1616    char key[ 20 ];
1617    int i;
1618 
1619 /* Check the global status, and supplied keyword name. */
1620    result = NULL;
1621    if( !astOK ) return result;
1622 
1623 /* Check there are some warnings to return. */
1624    if( this->nwarn && this->warnings ) {
1625 
1626 /* Create the KeyMap. */
1627       result = astKeyMap( "", status );
1628 
1629 /* Loop round all warnings, adding them into the KeyMap. */
1630       for( i = 0; i < this->nwarn; i++ ){
1631          sprintf( key, "Warning_%d", i + 1 );
1632          astMapPut0C( result, key, (this->warnings)[ i ], " " );
1633       }
1634    }
1635 
1636 /* Return the KeyMap. */
1637    return result;
1638 }
1639 
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)1640 AstChannel *astInitChannel_( void *mem, size_t size, int init,
1641                              AstChannelVtab *vtab, const char *name,
1642                              const char *(* source)( void ),
1643                              char *(* source_wrap)( const char *(*)( void ), int * ),
1644                              void (* sink)( const char * ),
1645                              void (* sink_wrap)( void (*)( const char * ),
1646                                                  const char *, int * ), int *status ) {
1647 /*
1648 *+
1649 *  Name:
1650 *     astInitChannel
1651 
1652 *  Purpose:
1653 *     Initialise a Channel.
1654 
1655 *  Type:
1656 *     Protected function.
1657 
1658 *  Synopsis:
1659 *     #include "channel.h"
1660 *     AstChannel *astInitChannel( void *mem, size_t size, int init,
1661 *                                 AstChannelVtab *vtab, const char *name,
1662 *                                 const char *(* source)( void ),
1663 *                                 char *(* source_wrap)( const char *(*)( void ), int * ),
1664 *                                 void (* sink)( const char * ),
1665 *                                 void (* sink_wrap)( void (*)( const char * ),
1666 *                                                     const char *, int * ) )
1667 
1668 *  Class Membership:
1669 *     Channel initialiser.
1670 
1671 *  Description:
1672 *     This function is provided for use by class implementations to
1673 *     initialise a new Channel object. It allocates memory (if
1674 *     necessary) to accommodate the Channel plus any additional data
1675 *     associated with the derived class.  It then initialises a
1676 *     Channel structure at the start of this memory. If the "init"
1677 *     flag is set, it also initialises the contents of a virtual
1678 *     function table for a Channel at the start of the memory passed
1679 *     via the "vtab" parameter.
1680 
1681 *  Parameters:
1682 *     mem
1683 *        A pointer to the memory in which the Channel is to be
1684 *        initialised.  This must be of sufficient size to accommodate
1685 *        the Channel data (sizeof(Channel)) plus any data used by the
1686 *        derived class. If a value of NULL is given, this function
1687 *        will allocate the memory itself using the "size" parameter to
1688 *        determine its size.
1689 *     size
1690 *        The amount of memory used by the Channel (plus derived class
1691 *        data).  This will be used to allocate memory if a value of
1692 *        NULL is given for the "mem" parameter. This value is also
1693 *        stored in the Channel structure, so a valid value must be
1694 *        supplied even if not required for allocating memory.
1695 *     init
1696 *        A boolean flag indicating if the Channel's virtual function
1697 *        table is to be initialised. If this value is non-zero, the
1698 *        virtual function table will be initialised by this function.
1699 *     vtab
1700 *        Pointer to the start of the virtual function table to be
1701 *        associated with the new Channel.
1702 *     name
1703 *        Pointer to a constant null-terminated character string which
1704 *        contains the name of the class to which the new object
1705 *        belongs (it is this pointer value that will subsequently be
1706 *        returned by the astGetClass method).
1707 *     source
1708 *        Pointer to a "source" function which will be used to obtain
1709 *        lines of input text. Generally, this will be obtained by
1710 *        casting a pointer to a source function which is compatible
1711 *        with the "source_wrap" wrapper function (below). The pointer
1712 *        should later be cast back to its original type by the
1713 *        "source_wrap" function before the function is invoked.
1714 *
1715 *        If "source" is NULL, the Channel will read from standard
1716 *        input instead.
1717 *     source_wrap
1718 *        Pointer to a function which can be used to invoke the
1719 *        "source" function supplied (above). This wrapper function is
1720 *        necessary in order to hide variations in the nature of the
1721 *        source function, such as may arise when it is supplied by a
1722 *        foreign (non-C) language interface.
1723 *
1724 *        The single parameter of the "source_wrap" function is a
1725 *        pointer to the "source" function, and it should cast this
1726 *        function pointer (as necessary) and invoke the function with
1727 *        appropriate arguments to obtain the next line of input
1728 *        text. The "source_wrap" function should then return a pointer
1729 *        to a dynamically allocated, null terminated string containing
1730 *        the text that was read. The string will be freed (using
1731 *        astFree) when no longer required and the "source_wrap"
1732 *        function need not concern itself with this. A NULL pointer
1733 *        should be returned if there is no more input to read.
1734 *
1735 *        If "source_wrap" is NULL, the Channel will read from standard
1736 *        input instead.
1737 *     sink
1738 *        Pointer to a "sink" function which will be used to deliver
1739 *        lines of output text. Generally, this will be obtained by
1740 *        casting a pointer to a sink function which is compatible with
1741 *        the "sink_wrap" wrapper function (below). The pointer should
1742 *        later be cast back to its original type by the "sink_wrap"
1743 *        function before the function is invoked.
1744 *
1745 *        If "sink" is NULL, the Channel will write to standard output
1746 *        instead.
1747 *     sink_wrap
1748 *        Pointer to a function which can be used to invoke the "sink"
1749 *        function supplied (above). This wrapper function is necessary
1750 *        in order to hide variations in the nature of the sink
1751 *        function, such as may arise when it is supplied by a foreign
1752 *        (non-C) language interface.
1753 *
1754 *        The first parameter of the "sink_wrap" function is a pointer
1755 *        to the "sink" function, and the second parameter is a pointer
1756 *        to a const, null-terminated character string containing the
1757 *        text to be written.  The "sink_wrap" function should cast the
1758 *        "sink" function pointer (as necessary) and invoke the
1759 *        function with appropriate arguments to deliver the line of
1760 *        output text. The "sink_wrap" function then returns void.
1761 *
1762 *        If "sink_wrap" is NULL, the Channel will write to standard
1763 *        output instead.
1764 
1765 *  Returned Value:
1766 *     A pointer to the new Channel.
1767 
1768 *  Notes:
1769 *     - A null pointer will be returned if this function is invoked
1770 *     with the global error status set, or if it should fail for any
1771 *     reason.
1772 *-
1773 */
1774 
1775 /* Local Variables: */
1776    AstChannel *new;              /* Pointer to new Channel */
1777 
1778 /* Check the global status. */
1779    if ( !astOK ) return NULL;
1780 
1781 /* If necessary, initialise the virtual function table. */
1782    if ( init ) astInitChannelVtab( vtab, name );
1783 
1784 /* Initialise an Object structure (the parent class) as the first
1785    component within the Channel structure, allocating memory if
1786    necessary. */
1787    new = (AstChannel *) astInitObject( mem, size, 0,
1788                                        (AstObjectVtab *) vtab, name );
1789 
1790    if ( astOK ) {
1791 
1792 /* Initialise the Channel data. */
1793 /* ---------------------------- */
1794 /* Save the pointers to the source and sink functions and the wrapper
1795    functions that invoke them. */
1796       new->source = source;
1797       new->source_wrap = source_wrap;
1798       new->sink = sink;
1799       new->sink_wrap = sink_wrap;
1800 
1801 /* Indicate no input or output files have been associated with the
1802    Channel. */
1803       new->fd_in = NULL;
1804       new->fn_in = NULL;
1805       new->fd_out = NULL;
1806       new->fn_out = NULL;
1807 
1808 /* Set all attributes to their undefined values. */
1809       new->comment = -INT_MAX;
1810       new->full = -INT_MAX;
1811       new->indent = -INT_MAX;
1812       new->report_level = -INT_MAX;
1813       new->skip = -INT_MAX;
1814       new->strict = -INT_MAX;
1815       new->data = NULL;
1816       new->warnings = NULL;
1817       new->nwarn = 0;
1818 
1819 /* If an error occurred, clean up by deleting the new object. */
1820       if ( !astOK ) new = astDelete( new );
1821    }
1822 
1823 /* Return a pointer to the new object. */
1824    return new;
1825 }
1826 
astInitChannelVtab_(AstChannelVtab * vtab,const char * name,int * status)1827 void astInitChannelVtab_(  AstChannelVtab *vtab, const char *name, int *status ) {
1828 /*
1829 *+
1830 *  Name:
1831 *     astInitChannelVtab
1832 
1833 *  Purpose:
1834 *     Initialise a virtual function table for a Channel.
1835 
1836 *  Type:
1837 *     Protected function.
1838 
1839 *  Synopsis:
1840 *     #include "channel.h"
1841 *     void astInitChannelVtab( AstChannelVtab *vtab, const char *name )
1842 
1843 *  Class Membership:
1844 *     Channel vtab initialiser.
1845 
1846 *  Description:
1847 *     This function initialises the component of a virtual function
1848 *     table which is used by the Channel class.
1849 
1850 *  Parameters:
1851 *     vtab
1852 *        Pointer to the virtual function table. The components used by
1853 *        all ancestral classes will be initialised if they have not already
1854 *        been initialised.
1855 *     name
1856 *        Pointer to a constant null-terminated character string which contains
1857 *        the name of the class to which the virtual function table belongs (it
1858 *        is this pointer value that will subsequently be returned by the Object
1859 *        astClass function).
1860 *-
1861 */
1862 
1863 /* Local Variables: */
1864    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
1865    AstObjectVtab *object;        /* Pointer to Object component of Vtab */
1866 
1867 /* Check the local error status. */
1868    if ( !astOK ) return;
1869 
1870 /* Get a pointer to the thread specific global data structure. */
1871    astGET_GLOBALS(NULL);
1872 
1873 /* Initialize the component of the virtual function table used by the
1874    parent class. */
1875    astInitObjectVtab( (AstObjectVtab *) vtab, name );
1876 
1877 /* Store a unique "magic" value in the virtual function table. This
1878    will be used (by astIsAChannel) to determine if an object belongs
1879    to this class.  We can conveniently use the address of the (static)
1880    class_check variable to generate this unique value. */
1881    vtab->id.check = &class_check;
1882    vtab->id.parent = &(((AstObjectVtab *) vtab)->id);
1883 
1884 /* Initialise member function pointers. */
1885 /* ------------------------------------ */
1886 /* Store pointers to the member functions (implemented here) that
1887    provide virtual methods for this class. */
1888    vtab->AddWarning = AddWarning;
1889    vtab->ClearComment = ClearComment;
1890    vtab->ClearFull = ClearFull;
1891    vtab->ClearSkip = ClearSkip;
1892    vtab->ClearStrict = ClearStrict;
1893    vtab->GetComment = GetComment;
1894    vtab->GetFull = GetFull;
1895    vtab->GetNextData = GetNextData;
1896    vtab->GetNextText = GetNextText;
1897    vtab->GetSkip = GetSkip;
1898    vtab->GetStrict = GetStrict;
1899    vtab->Warnings = Warnings;
1900    vtab->PutNextText = PutNextText;
1901    vtab->Read = Read;
1902    vtab->ReadClassData = ReadClassData;
1903    vtab->ReadDouble = ReadDouble;
1904    vtab->ReadInt = ReadInt;
1905    vtab->ReadObject = ReadObject;
1906    vtab->ReadString = ReadString;
1907    vtab->SetComment = SetComment;
1908    vtab->SetFull = SetFull;
1909    vtab->SetSkip = SetSkip;
1910    vtab->SetStrict = SetStrict;
1911    vtab->TestComment = TestComment;
1912    vtab->TestFull = TestFull;
1913    vtab->TestSkip = TestSkip;
1914    vtab->TestStrict = TestStrict;
1915    vtab->Write = Write;
1916    vtab->WriteBegin = WriteBegin;
1917    vtab->WriteDouble = WriteDouble;
1918    vtab->WriteEnd = WriteEnd;
1919    vtab->WriteInt = WriteInt;
1920    vtab->WriteIsA = WriteIsA;
1921    vtab->WriteObject = WriteObject;
1922    vtab->WriteString = WriteString;
1923    vtab->PutChannelData = PutChannelData;
1924 
1925    vtab->ClearReportLevel = ClearReportLevel;
1926    vtab->GetReportLevel = GetReportLevel;
1927    vtab->SetReportLevel = SetReportLevel;
1928    vtab->TestReportLevel = TestReportLevel;
1929 
1930    vtab->ClearIndent = ClearIndent;
1931    vtab->GetIndent = GetIndent;
1932    vtab->SetIndent = SetIndent;
1933    vtab->TestIndent = TestIndent;
1934 
1935    vtab->ClearSourceFile = ClearSourceFile;
1936    vtab->GetSourceFile = GetSourceFile;
1937    vtab->SetSourceFile = SetSourceFile;
1938    vtab->TestSourceFile = TestSourceFile;
1939 
1940    vtab->ClearSinkFile = ClearSinkFile;
1941    vtab->GetSinkFile = GetSinkFile;
1942    vtab->SetSinkFile = SetSinkFile;
1943    vtab->TestSinkFile = TestSinkFile;
1944 
1945 /* Save the inherited pointers to methods that will be extended, and
1946    replace them with pointers to the new member functions. */
1947    object = (AstObjectVtab *) vtab;
1948 
1949    parent_clearattrib = object->ClearAttrib;
1950    object->ClearAttrib = ClearAttrib;
1951    parent_getattrib = object->GetAttrib;
1952    object->GetAttrib = GetAttrib;
1953    parent_setattrib = object->SetAttrib;
1954    object->SetAttrib = SetAttrib;
1955    parent_testattrib = object->TestAttrib;
1956    object->TestAttrib = TestAttrib;
1957 
1958 /* Declare the destructor and copy constructor. */
1959    astSetDelete( (AstObjectVtab *) vtab, Delete );
1960    astSetCopy( (AstObjectVtab *) vtab, Copy );
1961 
1962 /* Declare the Dump function for this class. There is no destructor or
1963    copy constructor. */
1964    astSetDump( vtab, Dump, "Channel", "Basic I/O Channel" );
1965 
1966 /* If we have just initialised the vtab for the current class, indicate
1967    that the vtab is now initialised, and store a pointer to the class
1968    identifier in the base "object" level of the vtab. */
1969    if( vtab == &class_vtab ) {
1970       class_init = 1;
1971       astSetVtabClassIdentifier( vtab, &(vtab->id) );
1972    }
1973 }
1974 
InputTextItem(AstChannel * this,int * status)1975 static char *InputTextItem( AstChannel *this, int *status ) {
1976 /*
1977 *  Name:
1978 *     InputTextItem
1979 
1980 *  Purpose:
1981 *     Read the next item from a data source as text.
1982 
1983 *  Type:
1984 *     Private function.
1985 
1986 *  Synopsis:
1987 *     #include "channel.h"
1988 *     char *InputTextItem( AstChannel *this )
1989 
1990 *  Class Membership:
1991 *     Channel member function.
1992 
1993 *  Description:
1994 *     This function reads the next input data item as text from the
1995 *     data source associated with a Channel. It is similar to the
1996 *     astGetNextText method (which it invokes), except that it strips
1997 *     off any comments along with leading and trailing white
1998 *     space. Input lines which are empty or do not contain significant
1999 *     characters (e.g. all comment) are skipped, so that only
2000 *     significant lines are returned.
2001 *
2002 *     Each line is returned as a pointer to a null-terminated string
2003 *     held in dynamic memory, and it is the caller's responsibility to
2004 *     free this memory (using astFree) when it is no longer
2005 *     required. A NULL pointer is returned if there are no more input
2006 *     lines to be read.
2007 
2008 *  Parameters:
2009 *     this
2010 *        Pointer to the Channel.
2011 
2012 *  Returned Value:
2013 *     Pointer to a null-terminated string containing the input line
2014 *     (held in dynamically allocated memory, which must be freed by
2015 *     the caller when no longer required). A NULL pointer is returned
2016 *     if there are no more input lines to be read.
2017 
2018 *  Notes:
2019 *     - A NULL pointer will be returned if this function is invoked
2020 *     with the global error status set, or if it should fail for any
2021 *     reason.
2022 */
2023 
2024 /* Local Variables: */
2025    char *line;                   /* Pointer to line data to be returned */
2026    int i;                        /* Loop counter for line characters */
2027    int j;                        /* Counter for characters */
2028    int len;                      /* Length of result line */
2029    int nonspace;                 /* Non-space character encountered? */
2030    int quoted;                   /* Character is inside quotes? */
2031 
2032 /* Initialise. */
2033    line = NULL;
2034 
2035 /* Check the global error status. */
2036    if ( !astOK ) return line;
2037 
2038 /* Loop to read input lines until one is found which contains useful
2039    characters or end of file is reached (or a read error occurs). */
2040    while ( !line && ( line = astGetNextText( this ) ) && astOK ) {
2041 
2042 /* Loop to remove comments and leading and trailing white space. */
2043       len = 0;
2044       nonspace = 0;
2045       quoted = 0;
2046       for ( i = j = 0; line[ i ]; i++ ) {
2047 
2048 /* Note quote characters and ignore all text after the first unquoted
2049    comment character. */
2050          if ( line[ i ] == '"' ) quoted = !quoted;
2051          if ( ( line[ i ] == '#' ) && !quoted ) break;
2052 
2053 /* Note the first non-space character and ignore everything before
2054    it. */
2055          if ( ( nonspace = nonspace || !isspace( line[ i ] ) ) ) {
2056 
2057 /* Move each character to its new position in the string. */
2058             line[ j++ ] = line[ i ];
2059 
2060 /* Note the final length of the string (ignoring trailing spaces). */
2061             if ( !isspace( line[ i ] ) ) len = j;
2062          }
2063       }
2064 
2065 /* If the string is not empty, terminate it. */
2066      if ( len ) {
2067         line[ len ] = '\0';
2068 
2069 /* Otherwise, free the memory used for the string so that another
2070    input line will be read. */
2071       } else {
2072          line = astFree( line );
2073       }
2074    }
2075 
2076 /* Return the result pointer. */
2077    return line;
2078 
2079 /* Undefine macros local to this function. */
2080 #undef MIN_CHARS
2081 }
2082 
LookupValue(const char * name,int * status)2083 static AstChannelValue *LookupValue( const char *name, int *status ) {
2084 /*
2085 *  Name:
2086 *     LookupValue
2087 
2088 *  Purpose:
2089 *     Look up a Value structure by name.
2090 
2091 *  Type:
2092 *     Private function.
2093 
2094 *  Synopsis:
2095 *     #include "channel.h"
2096 *     AstChannelValue *LookupValue( const char *name )
2097 
2098 *  Class Membership:
2099 *     Channel member function.
2100 
2101 *  Description:
2102 *     This function searches the current values list (i.e. at the
2103 *     current nesting level) to identify a Value structure with a
2104 *     specified name. If one is found, it is removed from the list and
2105 *     a pointer to it is returned. If no suitable Value can be found,
2106 *     a NULL pointer is returned instead.
2107 
2108 *  Parameters:
2109 *     name
2110 *        Pointer to a constant null-terminated character string
2111 *        containing the name of the required Value. This must be in
2112 *        lower case with no surrounding white space. Note that names
2113 *        longer than NAME_MAX characters will not match any Value.
2114 
2115 *  Returned value:
2116 *     Pointer to the required Value structure, or NULL if no suitable
2117 *     Value exists.
2118 
2119 *  Notes:
2120 *     - The returned pointer refers to a dynamically allocated
2121 *     structure and it is the callers responsibility to free this when
2122 *     no longer required. The FreeValue function must be used for this
2123 *     purpose.
2124 *     - A NULL pointer will be returned if this function is invoked
2125 *     with the global error status set, or if it should fail for any
2126 *     reason.
2127 */
2128 
2129 /* Local Variables: */
2130    astDECLARE_GLOBALS            /* Declare the thread specific global data */
2131    AstChannelValue **head;       /* Address of head of list pointer */
2132    AstChannelValue *result;      /* Pointer value to return */
2133    AstChannelValue *value;       /* Pointer to list element */
2134 
2135 /* Initialise. */
2136    result = NULL;
2137 
2138 /* Check the global error status. */
2139    if ( !astOK ) return result;
2140 
2141 /* Get a pointer to the structure holding thread-specific global data. */
2142    astGET_GLOBALS(NULL);
2143 
2144 /* Check that the "values_ok" flag is set. If not, the Values in the
2145    values list belong to a different class to that of the current
2146    class loader, so we cannot return any Value. */
2147    if ( values_ok[ nest ] ) {
2148 
2149 /* Obtain the address of the current "head of list" pointer for the
2150    values list (at the current nesting level). */
2151       head = values_list + nest;
2152 
2153 /* Obtain the head of list pointer itself and check the list is not
2154    empty. */
2155       if ( ( value = *head ) ) {
2156 
2157 /* Loop to inspect each list element. */
2158          while ( 1 ) {
2159 
2160 /* If a name match is found, remove the element from the list, return
2161    a pointer to it and quit searching. */
2162             if ( !strcmp( name, value->name ) ) {
2163                RemoveValue( value, head, status );
2164                result = value;
2165                break;
2166             }
2167 
2168 /* Follow the list until we return to the head. */
2169             value = value->flink;
2170             if ( value == *head ) break;
2171          }
2172       }
2173    }
2174 
2175 /* Return the result. */
2176    return result;
2177 }
2178 
OutputTextItem(AstChannel * this,const char * line,int * status)2179 static void OutputTextItem( AstChannel *this, const char *line, int *status ) {
2180 /*
2181 *  Name:
2182 *     OutputTextItem
2183 
2184 *  Purpose:
2185 *     Output a data item formatted as text.
2186 
2187 *  Type:
2188 *     Private function.
2189 
2190 *  Synopsis:
2191 *     #include "channel.h"
2192 *     void OutputTextItem( AstChannel *this, const char *line, int *status )
2193 
2194 *  Class Membership:
2195 *     Channel member function.
2196 
2197 *  Description:
2198 *     This function outputs a data item formatted as a text string to
2199 *     a data sink associated with a Channel. It keeps track of the
2200 *     number of items written.
2201 
2202 *  Parameters:
2203 *     this
2204 *        Pointer to the Channel.
2205 *     line
2206 *        Pointer to a constant null-terminated string containing the
2207 *        data item to be output (no newline character should be
2208 *        appended).
2209 *     status
2210 *        Pointer to the inherited status variable.
2211 */
2212 
2213 /* Local Variables: */
2214    astDECLARE_GLOBALS            /* Declare the thread specific global data */
2215 
2216 /* Check the global error status. */
2217    if ( !astOK ) return;
2218 
2219 /* Get a pointer to the structure holding thread-specific global data. */
2220    astGET_GLOBALS(this);
2221 
2222 /* Write out the line of text using the astPutNextText method (which
2223    may be over-ridden). */
2224    astPutNextText( this, line );
2225 
2226 /* If successful, increment the count of items written. */
2227    if ( astOK ) items_written++;
2228 }
2229 
PutChannelData(AstChannel * this,void * data,int * status)2230 static void PutChannelData( AstChannel *this, void *data, int *status ) {
2231 /*
2232 c++
2233 *  Name:
2234 *     astPutChannelData
2235 
2236 *  Purpose:
2237 *     Store arbitrary data to be passed to a source or sink function.
2238 
2239 *  Type:
2240 *     Public function.
2241 
2242 *  Synopsis:
2243 *     #include "channel.h"
2244 *     void astPutChannelData( AstChannel *this, void *data )
2245 
2246 *  Class Membership:
2247 *     Channel method.
2248 
2249 *  Description:
2250 *     This function stores a supplied arbitrary pointer in the Channel.
2251 *     When a source or sink function is invoked by the Channel, the
2252 *     invoked function can use the astChannelData macro to retrieve the
2253 *     pointer. This provides a thread-safe alternative to passing file
2254 *     descriptors, etc, via global static variables.
2255 
2256 *  Parameters:
2257 *     this
2258 *        Pointer to the Channel.
2259 *     data
2260 *        A pointer to be made available to the source and sink functions
2261 *        via the astChannelData macro. May be NULL.
2262 
2263 *  Applicability:
2264 *     Channel
2265 *        All Channels have this function.
2266 
2267 *  Notes:
2268 *     - This routine is not available in the Fortran 77 interface to
2269 *     the AST library.
2270 c--
2271 */
2272 
2273 /* Check the global error status. */
2274    if ( !astOK ) return;
2275 
2276 /* Store the pointer. */
2277    this->data = data;
2278 }
2279 
PutNextText(AstChannel * this,const char * line,int * status)2280 static void PutNextText( AstChannel *this, const char *line, int *status ) {
2281 /*
2282 *+
2283 *  Name:
2284 *     astPutNextText
2285 
2286 *  Purpose:
2287 *     Write a line of output text to a data sink.
2288 
2289 *  Type:
2290 *     Protected virtual function.
2291 
2292 *  Synopsis:
2293 *     #include "channel.h"
2294 *     void astPutNextText( AstChannel *this, const char *line )
2295 
2296 *  Class Membership:
2297 *     Channel method.
2298 
2299 *  Description:
2300 *     This function writes an output line of text to a data sink
2301 *     associated with a Channel.
2302 
2303 *  Parameters:
2304 *     this
2305 *        Pointer to the Channel.
2306 *     line
2307 *        Pointer to a constant null-terminated string containing the
2308 *        line of output text to be written (no newline character
2309 *        should be appended).
2310 
2311 *  Notes:
2312 *     - This method is provided primarily so that derived classes may
2313 *     over-ride it in order to write to alternative (textual) data
2314 *     sinks.
2315 *-
2316 */
2317 
2318 /* Local Constants: */
2319 #define ERRBUF_LEN 80
2320 
2321 /* Local Variables: */
2322    char *errstat;                /* Pointer for system error message */
2323    char errbuf[ ERRBUF_LEN ];    /* Buffer for system error message */
2324    const char *sink_file;        /* Path to output sink file */
2325    const char *source_file;      /* Path to output source file */
2326 
2327 /* Check the global error status. */
2328    if ( !astOK ) return;
2329 
2330 /* If the SinkFile attribute of the Channel specifies an output file,
2331    but no output file has yet been opened, open it now. Report an error
2332    if it is the same as the source file. */
2333    if( astTestSinkFile( this ) && !this->fd_out ) {
2334       sink_file = astGetSinkFile( this );
2335 
2336       if( this->fd_out ) {
2337          source_file = astGetSourceFile( this );
2338          if( astOK && !strcmp( sink_file, source_file ) ) {
2339             astError( AST__WRERR, "astWrite(%s): Failed to open output "
2340                       "SinkFile '%s' - the file is currently being used "
2341                       "as the input SourceFile.", status, astGetClass( this ),
2342                       sink_file );
2343          }
2344       }
2345 
2346       if( astOK ) {
2347          this->fd_out = fopen( sink_file, "w" );
2348          if( !this->fd_out ) {
2349             if ( errno ) {
2350 #if HAVE_STRERROR_R
2351                strerror_r( errno, errbuf, ERRBUF_LEN );
2352                errstat = errbuf;
2353 #else
2354                errstat = strerror( errno );
2355 #endif
2356                astError( AST__WRERR, "astWrite(%s): Failed to open output "
2357                          "SinkFile '%s' - %s.", status, astGetClass( this ),
2358                          sink_file, errstat );
2359             } else {
2360                astError( AST__WRERR, "astWrite(%s): Failed to open output "
2361                          "SinkFile '%s'.", status, astGetClass( this ),
2362                          sink_file );
2363             }
2364          }
2365       }
2366    }
2367 
2368 /* Check no error occurred above. */
2369    if( astOK ) {
2370 
2371 /* If an active output file descriptor is stored in the channel, write
2372    the text to it, with a newline appended. */
2373       if( this->fd_out ) {
2374          (void) fprintf( this->fd_out, "%s\n", line );
2375 
2376 /* Otherwise, if a sink function (and its wrapper function) is defined for
2377    the Channel, use the wrapper function to invoke the sink function to
2378    output the text line. Since we are about to call an externally supplied
2379    function which may not be thread-safe, lock a mutex first. Also store
2380    the channel data pointer in a global variable so that it can be accessed
2381    in the source function using macro astChannelData. */
2382       } else if ( this->sink && this->sink_wrap ) {
2383          astStoreChannelData( this );
2384          LOCK_MUTEX2;
2385          ( *this->sink_wrap )( *this->sink, line, status );
2386          UNLOCK_MUTEX2;
2387 
2388 /* Otherwise, simply write the text to standard output with a newline
2389    appended. */
2390       } else {
2391          (void) printf( "%s\n", line );
2392       }
2393    }
2394 }
2395 
Read(AstChannel * this,int * status)2396 static AstObject *Read( AstChannel *this, int *status ) {
2397 /*
2398 *++
2399 *  Name:
2400 c     astRead
2401 f     AST_READ
2402 
2403 *  Purpose:
2404 *     Read an Object from a Channel.
2405 
2406 *  Type:
2407 *     Public function.
2408 
2409 *  Synopsis:
2410 c     #include "channel.h"
2411 c     AstObject *astRead( AstChannel *this )
2412 f     RESULT = AST_READ( THIS, STATUS )
2413 
2414 *  Class Membership:
2415 *     Channel method.
2416 
2417 *  Description:
2418 *     This function reads the next Object from a Channel and returns a
2419 *     pointer to the new Object.
2420 
2421 *  Parameters:
2422 c     this
2423 f     THIS = INTEGER (Given)
2424 *        Pointer to the Channel.
2425 f     STATUS = INTEGER (Given and Returned)
2426 f        The global status.
2427 
2428 *  Returned Value:
2429 c     astRead()
2430 f     AST_READ = INTEGER
2431 *        A pointer to the new Object. The class to which this will
2432 *        belong is determined by the input data, so is not known in
2433 *        advance.
2434 
2435 *  Applicability:
2436 *     FitsChan
2437 c        All successful use of astRead on a FitsChan is destructive, so that
2438 f        All successful use of AST_READ on a FitsChan is destructive, so that
2439 *        FITS header cards are consumed in the process of reading an Object,
2440 *        and are removed from the FitsChan (this deletion can be prevented
2441 *        for specific cards by calling the FitsChan
2442 c        astRetainFits function).
2443 f        AST_RETAINFITS routine).
2444 *        An unsuccessful call of
2445 c        astRead
2446 f        AST_READ
2447 *        (for instance, caused by the FitsChan not containing the necessary
2448 *        FITS headers cards needed to create an Object) results in the
2449 *        contents of the FitsChan being left unchanged.
2450 *     StcsChan
2451 *        The AST Object returned by a successful use of
2452 c        astRead
2453 f        AST_READ
2454 *        on an StcsChan, will be either a Region or a KeyMap, depending
2455 *        on the values of the StcsArea, StcsCoords and StcsProps
2456 *        attributes. See the documentation for these attributes for further
2457 *        information.
2458 
2459 *  Notes:
2460 *     - A null Object pointer (AST__NULL) will be returned, without
2461 *     error, if the Channel contains no further Objects to be read.
2462 *     - A null Object pointer will also be returned if this function
2463 c     is invoked with the AST error status set, or if it should fail
2464 f     is invoked with STATUS set to an error value, or if it should fail
2465 *     for any reason.
2466 *--
2467 */
2468 
2469 /* Local Variables: */
2470    astDECLARE_GLOBALS            /* Declare the thread specific global data */
2471    AstLoaderType *loader;        /* Pointer to loader for Object */
2472    AstObject *new;               /* Pointer to new Object */
2473    char *class;                  /* Pointer to Object class name string */
2474    char *name;                   /* Pointer to data item name */
2475    int skip;                     /* Skip non-AST data? */
2476    int top;                      /* Reading top-level Object definition? */
2477 
2478 /* Initialise. */
2479    new = NULL;
2480 
2481 /* Check the global error status. */
2482    if ( !astOK ) return new;
2483 
2484 /* Get a pointer to the structure holding thread-specific global data. */
2485    astGET_GLOBALS(this);
2486 
2487 /* Determine if we are reading a top-level (i.e. user-level) Object
2488    definition, as opposed to the definition of an Object contained
2489    within another Object. This is indicated by the current nesting
2490    level. */
2491    top = ( nest == -1 );
2492 
2493 /* If reading a top-level object, determine if data lying in between
2494    Object definitions in the input data stream are to be skipped. */
2495    skip = ( top && astGetSkip( this ) );
2496 
2497 /* Read the next input data item. If we are reading a top-level Object
2498    definition, skip any unrelated data beforehand. Otherwise read the
2499    data strictly as it comes (there should be no unrelated data
2500    embedded within Object definitions themselves). */
2501    astGetNextData( this, skip, &name, &class );
2502 
2503 /* If no suitable data item was found (and no error occurred), we have
2504    reached the end of data. For a top-level Object a NULL Object
2505    pointer is simply returned, but for a nested Object this indicates
2506    that part of the Object definition is missing, so report an
2507    error. */
2508    if ( astOK ) {
2509       if ( !name ) {
2510          if ( !top ) {
2511             astError( AST__EOCHN,
2512                       "astRead(%s): End of input encountered while trying to "
2513                       "read an AST Object.", status, astGetClass( this ) );
2514          }
2515 
2516 /* If a data item was found, check it is a "Begin" item. If not, there
2517    is a data item missing, so report an error and free all memory. */
2518       } else if ( strcmp( name, "begin" ) ) {
2519          astError( AST__BADIN,
2520                    "astRead(%s): Missing \"Begin\" when expecting an Object.", status,
2521                    astGetClass( this ) );
2522          name = astFree( name );
2523          if ( class ) class = astFree( class );
2524 
2525 /* If the required "Begin" item was found, free the memory used for the
2526    name string. */
2527       } else {
2528          name = astFree( name );
2529 
2530 /* Use the associated class name to locate the loader for that
2531    class. This function will then be used to build the Object. */
2532          loader = astGetLoader( class, status );
2533 
2534 /* Extend all necessary stack arrays to accommodate entries for the
2535    next nesting level (this allocates space if none has yet been
2536    allocated). */
2537          end_of_object = astGrow( end_of_object, nest + 2, sizeof( int ) );
2538          object_class = astGrow( object_class, nest + 2, sizeof( char * ) );
2539          values_class = astGrow( values_class, nest + 2, sizeof( char * ) );
2540          values_list = astGrow( values_list, nest + 2, sizeof( AstChannelValue * ) );
2541          values_ok = astGrow( values_ok, nest + 2, sizeof( int ) );
2542 
2543 /* If an error occurred, free the memory used by the class string,
2544    which will not now be used. */
2545          if ( !astOK ) {
2546             class = astFree( class );
2547 
2548 /* Otherwise, increment the nesting level and initialise the new stack
2549    elements for this new level. This includes clearing the
2550    "end_of_object" flag so that ReadClassData can read more data, and
2551    storing the class name of the object we are about to read. */
2552          } else {
2553             nest++;
2554             end_of_object[ nest ] = 0;
2555             object_class[ nest ] = class;
2556             values_class[ nest ] = NULL;
2557             values_list[ nest ] = NULL;
2558             values_ok[ nest ] = 0;
2559 
2560 /* Invoke the loader, which reads the Object definition from the input
2561    data stream and builds the Object. Supply NULL/zero values to the
2562    loader so that it will substitute values appropriate to its own
2563    class. */
2564             new = (*loader)( NULL, (size_t) 0, NULL, NULL, this, status );
2565 
2566 /* Clear the values list for the current nesting level. If the list
2567    has not been read or any Values remain in it, an error will
2568    result. */
2569             ClearValues( this, status );
2570 
2571 /* If no error has yet occurred, check that the "end_of_object" flag
2572    has been set. If not, the input data were not correctly terminated,
2573    so report an error. */
2574             if ( astOK && !end_of_object[ nest ] ) {
2575                astError( AST__BADIN,
2576                          "astRead(%s): Unexpected end of input (missing end "
2577                          "of %s).", status,
2578                          astGetClass( this ), object_class[ nest ] );
2579             }
2580 
2581 /* If an error occurred, report contextual information. Only do this
2582    for top-level Objects to avoid multple messages. */
2583             if ( !astOK && top ) {
2584                astError( astStatus, "Error while reading a %s from a %s.", status,
2585                          class, astGetClass( this ) );
2586             }
2587 
2588 /* Clear the Object's class string, freeing the associated memory
2589    (note this is the memory allocated for the "class" string
2590    earlier). */
2591             object_class[ nest ] = astFree( object_class[ nest ] );
2592 
2593 /* Restore the previous nesting level. */
2594             nest--;
2595          }
2596 
2597 /* Once the top-level Object has been built, free the memory used by
2598    the stack arrays. */
2599          if ( top ) {
2600             end_of_object = astFree( end_of_object );
2601             object_class = astFree( object_class );
2602             values_class = astFree( values_class );
2603             values_list = astFree( values_list );
2604             values_ok = astFree( values_ok );
2605          }
2606       }
2607    }
2608 
2609 /* If an error occurred, clean up by deleting the new Object and
2610    return a NULL pointer. */
2611    if ( !astOK ) new = astDelete( new );
2612 
2613 /* Return the pointer to the new Object. */
2614    return new;
2615 }
2616 
ReadClassData(AstChannel * this,const char * class,int * status)2617 static void ReadClassData( AstChannel *this, const char *class, int *status ) {
2618 /*
2619 *+
2620 *  Name:
2621 *     astReadClassData
2622 
2623 *  Purpose:
2624 *     Read values from a data source for a class loader.
2625 
2626 *  Type:
2627 *     Protected virtual function.
2628 
2629 *  Synopsis:
2630 *     #include "channel.h"
2631 *     void astReadClassData( AstChannel *this, const char *class )
2632 
2633 *  Class Membership:
2634 *     Channel method.
2635 
2636 *  Description:
2637 *     This function reads the data for a class from the data source
2638 *     associated with a Channel, so as to provide values for
2639 *     initialising the instance variables of that class as part of
2640 *     building a complete Object. This function should be invoked by
2641 *     the loader for each class immediately before it attempts to read
2642 *     these values.
2643 *
2644 *     The values read are placed into the current values list by this
2645 *     function. They may then be read from this list by the class
2646 *     loader making calls to astReadDouble, astReadInt, astReadObject
2647 *     and astReadString. The order in which values are read by the
2648 *     loader is unimportant (although using the same order for reading
2649 *     as for writing will usually be more efficient) and values are
2650 *     removed from the list as they are read.
2651 
2652 *  Parameters:
2653 *     this
2654 *        Pointer to the Channel.
2655 *     class
2656 *        A pointer to a constant null-terminated string containing the
2657 *        name of the class whose loader is requesting the data (note
2658 *        this is not usually the same as the class name of the Object
2659 *        being built). This value allows the class structure of the
2660 *        input data to be validated.
2661 *-
2662 */
2663 
2664 /* Local Variables: */
2665    astDECLARE_GLOBALS            /* Declare the thread specific global data */
2666    AstObject *object;            /* Pointer to new Object */
2667    AstChannelValue *value;       /* Pointer to Value structure */
2668    char *name;                   /* Pointer to data item name string */
2669    char *val;                    /* Pointer to data item value string */
2670    int done;                     /* All class data read? */
2671 
2672 /* Check the global error status. */
2673    if ( !astOK ) return;
2674 
2675 /* Get a pointer to the structure holding thread-specific global data. */
2676    astGET_GLOBALS(this);
2677 
2678 /* If the "values_ok" flag is set, this indicates that the values list
2679    (at the current nesting level) has been filled by a previous
2680    invocation of this function and has then been read by the
2681    appropriate class loader. In this case, clear any entries which may
2682    remain in the current values list. If any such entries are found,
2683    they represent input data that were not read, so an error will
2684    result. */
2685    if ( values_ok[ nest ] ) ClearValues( this, status );
2686 
2687 /* If "values_class" is non-NULL, this indicates that the values list
2688    (at the current nesting level) has been filled by a previous
2689    invocation of this function, but that the values belong to a class
2690    whose loader has not yet tried to read them. In this case, we must
2691    continue to keep the values until they are needed, so we do not
2692    read any more input data this time. */
2693    if ( values_class[ nest ] ) {
2694 
2695 /* If the class to which the previously saved values belong matches
2696    the class we now want values for, set the "values_ok" flag. This
2697    then allows the values to be accessed (by LookupValue). */
2698       values_ok[ nest ] = !strcmp( values_class[ nest ], class );
2699 
2700 /* If the current values list is empty, we must read in values for the
2701    next class that appears in the input data. However, first check
2702    that the "end_of_object" flag has not been set. If it has, we have
2703    already reached the end of this Object's data, so there is some
2704    kind of problem with the order in which class loaders have been
2705    invoked. This will probably never happen, but report an error if
2706    necessary. */
2707    } else if ( end_of_object[ nest ] ) {
2708       astError( AST__LDERR,
2709                 "astRead(%s): Invalid attempt to read further %s data "
2710                 "following an end of %s.", status,
2711                 astGetClass( this ), class, object_class[ nest ] );
2712       astError( AST__LDERR,
2713                 "Perhaps the wrong class loader has been invoked?" , status);
2714 
2715 /* If we need new values, loop to read input data items until the end
2716    of the data for a class is reached. */
2717    } else {
2718       done = 0;
2719       while ( astOK && !done ) {
2720 
2721 /* Read the next input data item. */
2722          astGetNextData( this, 0, &name, &val );
2723          if ( astOK ) {
2724 
2725 /* Unexpected end of input. */
2726 /* ------------------------ */
2727 /* If no "name" value is returned, we have reached the end of the
2728    input data stream without finding the required end of class
2729    terminator, so report an error. */
2730             if ( !name ) {
2731                astError( AST__EOCHN,
2732                          "astRead(%s): Unexpected end of input (missing end "
2733                          "of %s).", status,
2734                          astGetClass( this ), object_class[ nest ] );
2735 
2736 /* "IsA" item. */
2737 /* ----------- */
2738 /* Otherwise, if an "IsA" item was read, it indicates the end of the
2739    data for a class. Store the pointer to the name of this class in
2740    "values_class" and note whether this is the class whose data we
2741    wanted in "values_ok". If the data we have read do not belong to
2742    the class we wanted, they will simply be kept until the right class
2743    comes looking for them. */
2744             } else if ( !strcmp( name, "isa" ) ) {
2745                values_class[ nest ] = val;
2746                values_ok[ nest ] = !strcmp( val, class );
2747 
2748 /* Free the memory holding the name string. */
2749                name = astFree( name );
2750 
2751 /* Note we have finished reading class data. */
2752                done = 1;
2753 
2754 /* "End" item. */
2755 /* ----------- */
2756 /* If an "End" item was read, it indicates the end of the data both
2757    for a class and for the current Object definition as a whole. Set
2758    the "end_of_object" flag (for the current nesting level) which
2759    prevents any further data being read for this Object. This flag is
2760    also used (by Read) to check that an "End" item was actually
2761    read. */
2762             } else if ( !strcmp( name, "end" ) ) {
2763                end_of_object[ nest ] = 1;
2764 
2765 /* Check that the class name in the "End" item matches that of the
2766    Object being built. If so, store the pointer to the name of this
2767    class in "values_class" and note whether this is the class whose
2768    data we wanted in "values_ok". If the data we have read do not
2769    belong to the class we wanted, they will simply be kept until the
2770    right class comes looking for them. */
2771                if ( !strcmp( val, object_class[ nest ] ) ) {
2772                   values_class[ nest ] = val;
2773                   values_ok[ nest ] = !strcmp( class, val );
2774 
2775 /* If the "End" item contains the wrong class name (i.e. not matching
2776    the corresponding "Begin" item), then report an error. */
2777                } else {
2778                   astError( AST__BADIN,
2779                             "astRead(%s): Bad class structure in input data.", status,
2780                             astGetClass( this ) );
2781                   astError( AST__BADIN,
2782                             "End of %s read when expecting end of %s.", status,
2783                             val, object_class[ nest ] );
2784 
2785 /* Free the memory used by the class string, which will not now be
2786    used. */
2787                   val = astFree( val );
2788                }
2789 
2790 /* Free the memory holding the name string. */
2791                name = astFree( name );
2792 
2793 /* Note we have finished reading class data. */
2794                done = 1;
2795 
2796 /* String value. */
2797 /* ------------- */
2798 /* If any other name is obtained and "val" is not NULL, we have read a
2799    non-Object value, encoded as a string. Allocate memory for a Value
2800    structure to describe it. */
2801             } else if ( val ) {
2802                value = astMalloc( sizeof( AstChannelValue ) );
2803                if ( astOK ) {
2804 
2805 /* Store pointers to the name and value string in the Value structure
2806    and note this is not an Object value. */
2807                   value->name = name;
2808                   value->ptr.string = val;
2809                   value->is_object = 0;
2810 
2811 /* Append the Value structure to the values list for the current
2812    nesting level. */
2813                   AppendValue( value, values_list + nest, status );
2814 
2815 /* If an error occurred, free the memory holding the "name" and "val"
2816    strings. */
2817                } else {
2818                   name = astFree( name );
2819                   val = astFree( val );
2820                }
2821 
2822 /* Object value. */
2823 /* ------------- */
2824 /* If "val" is NULL, we have read an Object item, and the Object
2825    definition should follow. Allocate memory for a Value structure to
2826    describe it. */
2827             } else {
2828                value = astMalloc( sizeof( AstChannelValue ) );
2829 
2830 /* Invoke astRead to read the Object definition from subsequent data
2831    items and to build the Object, returning a pointer to it. This will
2832    result in recursive calls to the current function, but as these
2833    will use higher nesting levels they will not interfere with the
2834    current invocation. */
2835                astreadclassdata_msg = 0;
2836                object = astRead( this );
2837                if ( astOK ) {
2838 
2839 /* Store pointers to the name and Object in the Value structure and
2840    note this is an Object value. */
2841                   value->name = name;
2842                   value->ptr.object = object;
2843                   value->is_object = 1;
2844 
2845 /* Append the Value structure to the values list for the current
2846    nesting level. */
2847                   AppendValue( value, values_list + nest, status );
2848 
2849 /* If an error occurred, report a contextual error maessage and set
2850    the "astreadclassdata_msg" flag (this prevents multiple messages if this function is
2851    invoked recursively to deal with nested Objects). */
2852                } else {
2853                   if ( !astreadclassdata_msg ) {
2854                      astError( astStatus,
2855                                "Failed to read the \"%s\" Object value.", status,
2856                                name );
2857                      astreadclassdata_msg = 1;
2858                   }
2859 
2860 /* Free the memory holding the "name" string and any Value structure
2861    that was allocated. */
2862                   name = astFree( name );
2863                   value = astFree( value );
2864                }
2865             }
2866          }
2867       }
2868    }
2869 }
2870 
ReadDouble(AstChannel * this,const char * name,double def,int * status)2871 static double ReadDouble( AstChannel *this, const char *name, double def, int *status ) {
2872 /*
2873 *+
2874 *  Name:
2875 *     astReadDouble
2876 
2877 *  Purpose:
2878 *     Read a double value as part of loading a class.
2879 
2880 *  Type:
2881 *     Protected virtual function.
2882 
2883 *  Synopsis:
2884 *     #include "channel.h"
2885 *     double astReadDouble( AstChannel *this, const char *name, double def )
2886 
2887 *  Class Membership:
2888 *     Channel method.
2889 
2890 *  Description:
2891 *     This function searches the current values list of a Channel to
2892 *     identify a double value with a specified name. If such a value
2893 *     is found, it is returned, otherwise a default value is returned
2894 *     instead.
2895 *
2896 *     This function should only be invoked from within the loader
2897 *     function associated with a class, in order to return a double
2898 *     value to be assigned to an instance variable. It must be
2899 *     preceded by a call to the astReadClassData function, which loads
2900 *     the values associated with the class into the current values
2901 *     list from the input data source.
2902 
2903 *  Parameters:
2904 *     this
2905 *        Pointer to the Channel.
2906 *     name
2907 *        Pointer to a constant null-terminated character string
2908 *        containing the name of the required value. This must be in
2909 *        lower case with no surrounding white space. Note that names
2910 *        longer than 6 characters will not match any value.
2911 *     def
2912 *        If no suitable value can be found (e.g. it is absent from the
2913 *        data stream being read), then this value will be returned
2914 *        instead.
2915 
2916 *  Returned Value:
2917 *     The required value, or the default if the value was not found.
2918 
2919 *  Notes:
2920 *     - A value of 0.0 will be returned if this function is invoked
2921 *     with the global error status set, or if it should fail for any
2922 *     reason.
2923 *-
2924 */
2925 
2926 /* Local Variables: */
2927    AstChannelValue *value;       /* Pointer to required Value structure */
2928    double result;                /* Value to be returned */
2929    int nc;                       /* Number of characters read by astSscanf */
2930 
2931 /* Initialise. */
2932    result = 0.0;
2933 
2934 /* Check the global error status. */
2935    if ( !astOK ) return result;
2936 
2937 /* Search for a Value structure with the required name in the current
2938    values list.*/
2939    value = LookupValue( name, status );
2940    if ( astOK ) {
2941 
2942 /* If a Value was found, check that it describes a string (as opposed
2943    to an Object). */
2944       if ( value ) {
2945          if ( !value->is_object ) {
2946 
2947 /* If so, then attempt to decode the string to give a double value,
2948    checking that the entire string is read (and checking for the magic string
2949    used to represent bad values). If this fails, then the wrong name has
2950    probably been given, or the input data are corrupt, so report an error. */
2951             nc = 0;
2952             if ( ( 0 == astSscanf( value->ptr.string, " " BAD_STRING " %n",
2953                                                       &nc ) )
2954                     && ( nc >= (int) strlen( value->ptr.string ) ) ) {
2955                result = AST__BAD;
2956 
2957             } else if ( !( ( 1 == astSscanf( value->ptr.string, " %lf %n",
2958                                                       &result, &nc ) )
2959                     && ( nc >= (int) strlen( value->ptr.string ) ) ) ) {
2960                astError( AST__BADIN,
2961                          "astRead(%s): The value \"%s = %s\" cannot "
2962                          "be read as a double precision floating point "
2963                          "number.", status, astGetClass( this ),
2964                          value->name, value->ptr.string );
2965 
2966             } else if( !astISFINITE( result ) ) {
2967                astError( AST__BADIN,
2968                          "astRead(%s): Illegal double precision floating "
2969                          "point value \"%s\" read for \"%s\".", status,
2970                          astGetClass( this ), value->ptr.string, value->name );
2971 
2972             }
2973 
2974 /* Report a similar error if the Value does not describe a string. */
2975          } else {
2976             astError( AST__BADIN,
2977                       "astRead(%s): The Object \"%s = <%s>\" cannot "
2978                       "be read as a double precision floating point number.", status,
2979                       astGetClass( this ),
2980                       value->name, astGetClass( value->ptr.object ) );
2981          }
2982 
2983 /* Free the Value structure and the resources it points at. */
2984          value = FreeValue( value, status );
2985 
2986 /* If no suitable Value structure was found, then use the default
2987    value instead. */
2988       } else {
2989          result = def;
2990       }
2991    }
2992 
2993 /* Return the result. */
2994    return result;
2995 }
2996 
ReadInt(AstChannel * this,const char * name,int def,int * status)2997 static int ReadInt( AstChannel *this, const char *name, int def, int *status ) {
2998 /*
2999 *+
3000 *  Name:
3001 *     astReadInt
3002 
3003 *  Purpose:
3004 *     Read an int value as part of loading a class.
3005 
3006 *  Type:
3007 *     Protected virtual function.
3008 
3009 *  Synopsis:
3010 *     #include "channel.h"
3011 *     int astReadInt( AstChannel *this, const char *name, int def )
3012 
3013 *  Class Membership:
3014 *     Channel method.
3015 
3016 *  Description:
3017 *     This function searches the current values list of a Channel to
3018 *     identify an int value with a specified name. If such a value is
3019 *     found, it is returned, otherwise a default value is returned
3020 *     instead.
3021 *
3022 *     This function should only be invoked from within the loader
3023 *     function associated with a class, in order to return an int
3024 *     value to be assigned to an instance variable. It must be
3025 *     preceded by a call to the astReadClassData function, which loads
3026 *     the values associated with the class into the current values
3027 *     list from the input data source.
3028 
3029 *  Parameters:
3030 *     this
3031 *        Pointer to the Channel.
3032 *     name
3033 *        Pointer to a constant null-terminated character string
3034 *        containing the name of the required value. This must be in
3035 *        lower case with no surrounding white space. Note that names
3036 *        longer than 6 characters will not match any value.
3037 *     def
3038 *        If no suitable value can be found (e.g. it is absent from the
3039 *        data stream being read), then this value will be returned
3040 *        instead.
3041 
3042 *  Returned Value:
3043 *     The required value, or the default if the value was not found.
3044 
3045 *  Notes:
3046 *     - A value of zero will be returned if this function is invoked
3047 *     with the global error status set, or if it should fail for any
3048 *     reason.
3049 *-
3050 */
3051 
3052 /* Local Variables: */
3053    AstChannelValue *value;       /* Pointer to required Value structure */
3054    int nc;                       /* Number of characters read by astSscanf */
3055    int result;                   /* Value to be returned */
3056 
3057 /* Initialise. */
3058    result = 0;
3059 
3060 /* Check the global error status. */
3061    if ( !astOK ) return result;
3062 
3063 /* Search for a Value structure with the required name in the current
3064    values list.*/
3065    value = LookupValue( name, status );
3066    if ( astOK ) {
3067 
3068 /* If a Value was found, check that it describes a string (as opposed
3069    to an Object). */
3070       if ( value ) {
3071          if ( !value->is_object ) {
3072 
3073 /* If so, then attempt to decode the string to give an int value,
3074    checking that the entire string is read. If this fails, then the
3075    wrong name has probably been given, or the input data are corrupt,
3076    so report an error. */
3077             nc = 0;
3078             if ( !( ( 1 == astSscanf( value->ptr.string, " %d %n",
3079                                                       &result, &nc ) )
3080                     && ( nc >= (int) strlen( value->ptr.string ) ) ) ) {
3081                astError( AST__BADIN,
3082                          "astRead(%s): The value \"%s = %s\" cannot "
3083                          "be read as an integer.", status, astGetClass( this ),
3084                          value->name, value->ptr.string );
3085             }
3086 
3087 /* Report a similar error if the Value does not describe a string. */
3088          } else {
3089             astError( AST__BADIN,
3090                       "astRead(%s): The Object \"%s = <%s>\" cannot "
3091                       "be read as an integer.", status, astGetClass( this ),
3092                       value->name, astGetClass( value->ptr.object ) );
3093          }
3094 
3095 /* Free the Value structure and the resources it points at. */
3096          value = FreeValue( value, status );
3097 
3098 /* If no suitable Value structure was found, then use the default
3099    value instead. */
3100       } else {
3101          result = def;
3102       }
3103    }
3104 
3105 /* Return the result. */
3106    return result;
3107 }
3108 
ReadObject(AstChannel * this,const char * name,AstObject * def,int * status)3109 static AstObject *ReadObject( AstChannel *this, const char *name,
3110                               AstObject *def, int *status ) {
3111 /*
3112 *+
3113 *  Name:
3114 *     astReadObject
3115 
3116 *  Purpose:
3117 *     Read a (sub)Object as part of loading a class.
3118 
3119 *  Type:
3120 *     Protected virtual function.
3121 
3122 *  Synopsis:
3123 *     #include "channel.h"
3124 *     AstObject *astReadObject( AstChannel *this, const char *name,
3125 *                               AstObject *def )
3126 
3127 *  Class Membership:
3128 *     Channel method.
3129 
3130 *  Description:
3131 *     This function searches the current values list of a Channel to
3132 *     identify an Object with a specified name. If such an Object is
3133 *     found, a pointer to it is returned, otherwise a default pointer
3134 *     is returned instead.
3135 *
3136 *     This function should only be invoked from within the loader
3137 *     function associated with a class, in order to return an Object
3138 *     pointer value to be assigned to an instance variable. It must be
3139 *     preceded by a call to the astReadClassData function, which loads
3140 *     the values associated with the class into the current values
3141 *     list from the input data source.
3142 
3143 *  Parameters:
3144 *     this
3145 *        Pointer to the Channel.
3146 *     name
3147 *        Pointer to a constant null-terminated character string
3148 *        containing the name of the required Object. This must be in
3149 *        lower case with no surrounding white space. Note that names
3150 *        longer than 6 characters will not match any Object.
3151 *     def
3152 *        If no suitable Object can be found (e.g. the Object is absent
3153 *        from the data stream being read), then a clone of this
3154 *        default Object pointer will be returned instead (or NULL if
3155 *        this default pointer is NULL).
3156 
3157 *  Returned Value:
3158 *     A pointer to the Object, or a clone of the default pointer if
3159 *     the Object was not found.
3160 
3161 *  Notes:
3162 *     - A NULL pointer will be returned if this function is invoked
3163 *     with the global error status set, or if it should fail for any
3164 *     reason.
3165 *-
3166 */
3167 
3168 /* Local Variables: */
3169    AstObject *result;            /* Pointer value to return */
3170    AstChannelValue *value;       /* Pointer to required Value structure */
3171 
3172 /* Initialise. */
3173    result = NULL;
3174 
3175 /* Check the global error status. */
3176    if ( !astOK ) return result;
3177 
3178 /* Search for a Value structure with the required name in the current
3179    values list.*/
3180    value = LookupValue( name, status );
3181    if ( astOK ) {
3182 
3183 /* If a Value was found, check that it describes an Object (as opposed to
3184    a string). */
3185       if ( value ) {
3186          if ( value->is_object ) {
3187 
3188 /* If so, then extract the Object pointer, replacing it with NULL. */
3189             result = value->ptr.object;
3190             value->ptr.object = NULL;
3191 
3192 /* If the Value does not describe an Object, then the wrong name has
3193    probably been given, or the input data are corrupt, so report an
3194    error. */
3195          } else {
3196             astError( AST__BADIN,
3197                       "astRead(%s): The value \"%s = %s\" cannot be "
3198                       "read as an Object.", status, astGetClass( this ),
3199                       value->name, value->ptr.string );
3200          }
3201 
3202 /* Free the Value structure and the resources it points at. */
3203          value = FreeValue( value, status );
3204 
3205 /* If no suitable Value structure was found, clone the default
3206    pointer, if given. */
3207       } else if ( def ) {
3208          result = astClone( def );
3209       }
3210    }
3211 
3212 /* Return the result. */
3213    return result;
3214 }
3215 
ReadString(AstChannel * this,const char * name,const char * def,int * status)3216 static char *ReadString( AstChannel *this, const char *name,
3217                          const char *def, int *status ) {
3218 /*
3219 *+
3220 *  Name:
3221 *     astReadString
3222 
3223 *  Purpose:
3224 *     Read a string value as part of loading a class.
3225 
3226 *  Type:
3227 *     Protected virtual function.
3228 
3229 *  Synopsis:
3230 *     #include "channel.h"
3231 *     char *astReadString( AstChannel *this, const char *name,
3232 *                          const char *def )
3233 
3234 *  Class Membership:
3235 *     Channel method.
3236 
3237 *  Description:
3238 *     This function searches the current values list of a Channel to
3239 *     identify a string value with a specified name. If such a value
3240 *     is found, a pointer to the string is returned, otherwise a
3241 *     pointer to a copy of a default string is returned instead.
3242 *
3243 *     This function should only be invoked from within the loader
3244 *     function associated with a class, in order to return a string
3245 *     pointer value to be assigned to an instance variable. It must be
3246 *     preceded by a call to the astReadClassData function, which loads
3247 *     the values associated with the class into the current values
3248 *     list from the input data source.
3249 
3250 *  Parameters:
3251 *     this
3252 *        Pointer to the Channel.
3253 *     name
3254 *        Pointer to a constant null-terminated character string
3255 *        containing the name of the required value. This must be in
3256 *        lower case with no surrounding white space. Note that names
3257 *        longer than 6 characters will not match any value.
3258 *     def
3259 *        If no suitable string can be found (e.g. the value is absent
3260 *        from the data stream being read), then a dynamically
3261 *        allocated copy of the null-terminated string pointed at by
3262 *        "def" will be made, and a pointer to this copy will be
3263 *        returned instead (or NULL if this default pointer is NULL).
3264 
3265 *  Returned Value:
3266 *     A pointer to a dynamically allocated null-terminated string
3267 *     containing the value required, or to a copy of the default
3268 *     string if the value was not found (or NULL if the "def" pointer
3269 *     was NULL).
3270 
3271 *  Notes:
3272 *     - It is the caller's responsibility to arrange for the memory
3273 *     holding the returned string to be freed (using astFree) when it
3274 *     is no longer required.
3275 *     - A NULL pointer will be returned if this function is invoked
3276 *     with the global error status set, or if it should fail for any
3277 *     reason.
3278 *-
3279 */
3280 
3281 /* Local Variables: */
3282    AstChannelValue *value;       /* Pointer to required Value structure */
3283    char *result;                 /* Pointer value to return */
3284 
3285 /* Initialise. */
3286    result = NULL;
3287 
3288 /* Check the global error status. */
3289    if ( !astOK ) return result;
3290 
3291 /* Search for a Value structure with the required name in the current
3292    values list.*/
3293    value = LookupValue( name, status );
3294    if ( astOK ) {
3295 
3296 /* If a Value was found, check that it describes a string (as opposed
3297    to an Object). */
3298       if ( value ) {
3299          if ( !value->is_object ) {
3300 
3301 /* If so, then extract the string pointer, replacing it with NULL. */
3302             result = value->ptr.string;
3303             value->ptr.string = NULL;
3304 
3305 /* If the Value does not describe a string, then the wrong name has
3306    probably been given, or the input data are corrupt, so report an
3307    error. */
3308          } else {
3309             astError( AST__BADIN,
3310                       "astRead(%s): The Object \"%s = <%s>\" cannot "
3311                       "be read as a string.", status, astGetClass( this ),
3312                       value->name, astGetClass( value->ptr.object ) );
3313          }
3314 
3315 /* Free the Value structure and the resources it points at. */
3316          value = FreeValue( value, status );
3317 
3318 /* If no suitable Value structure was found, then make a dynamic copy
3319    of the default string (if given) and return a pointer to this. */
3320       } else if ( def ) {
3321          result = astStore( NULL, def, strlen( def ) + (size_t) 1 );
3322       }
3323    }
3324 
3325 /* Return the result. */
3326    return result;
3327 }
3328 
RemoveValue(AstChannelValue * value,AstChannelValue ** head,int * status)3329 static void RemoveValue( AstChannelValue *value, AstChannelValue **head, int *status ) {
3330 /*
3331 *  Name:
3332 *     RemoveValue
3333 
3334 *  Purpose:
3335 *     Remove a Value structure from a circular linked list.
3336 
3337 *  Type:
3338 *     Private function.
3339 
3340 *  Synopsis:
3341 *     #include "channel.h"
3342 *     void RemoveValue( AstChannelValue *value, AstChannelValue **head, int *status );
3343 
3344 *  Class Membership:
3345 *     Channel member function.
3346 
3347 *  Description:
3348 *     This function removes a Value structure from a doubly linked
3349 *     circular list of such structures. The "head of list" pointer is
3350 *     updated to point at the element following the one removed.
3351 
3352 *  Parameters:
3353 *     value
3354 *        Pointer to the structure to be removed (note that this must
3355 *        actually be in the list, although this function does not
3356 *        check).
3357 *     head
3358 *        Address of a pointer to the element at the head of the
3359 *        list. This pointer will be updated to point at the list
3360 *        element that follows the one removed. If the list becomes
3361 *        empty, the pointer will be set to NULL.
3362 *     status
3363 *        Pointer to the inherited status variable.
3364 
3365 *  Notes:
3366 *     - This function does not perform error chacking and does not
3367 *     generate errors.
3368 */
3369 
3370 /* Remove the Value structure from the list by re-establishing links
3371    between the elements on either side of it. */
3372    value->blink->flink = value->flink;
3373    value->flink->blink = value->blink;
3374 
3375 /* Update the head of list pointer to identify the following
3376    element. */
3377    *head = value->flink;
3378 
3379 /* If the head of list identifies the removed element, then note that
3380    the list is now empty. */
3381    if ( *head == value ) *head = NULL;
3382 
3383 /* Make the removed element point at itself. */
3384    value->flink = value;
3385    value->blink = value;
3386 }
3387 
SetAttrib(AstObject * this_object,const char * setting,int * status)3388 static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
3389 /*
3390 *  Name:
3391 *     SetAttrib
3392 
3393 *  Purpose:
3394 *     Set an attribute value for a Channel.
3395 
3396 *  Type:
3397 *     Private function.
3398 
3399 *  Synopsis:
3400 *     #include "channel.h"
3401 *     void SetAttrib( AstObject *this, const char *setting )
3402 
3403 *  Class Membership:
3404 *     Channel member function (over-rides the astSetAttrib protected
3405 *     method inherited from the Object class).
3406 
3407 *  Description:
3408 *     This function assigns an attribute value for a Channel, the
3409 *     attribute and its value being specified by means of a string of
3410 *     the form:
3411 *
3412 *        "attribute= value "
3413 *
3414 *     Here, "attribute" specifies the attribute name and should be in
3415 *     lower case with no white space present. The value to the right
3416 *     of the "=" should be a suitable textual representation of the
3417 *     value to be assigned and this will be interpreted according to
3418 *     the attribute's data type.  White space surrounding the value is
3419 *     only significant for string attributes.
3420 
3421 *  Parameters:
3422 *     this
3423 *        Pointer to the Channel.
3424 *     setting
3425 *        Pointer to a null terminated string specifying the new attribute
3426 *        value.
3427 */
3428 
3429 /* Local Variables: */
3430    AstChannel *this;             /* Pointer to the Channel structure */
3431    int comment;                  /* Comment attribute value */
3432    int full;                     /* Full attribute value */
3433    int indent;                   /* Indent attribute value */
3434    int len;                      /* Length of setting string */
3435    int nc;                       /* Number of characters read by "astSscanf" */
3436    int report_level;             /* Skip attribute value */
3437    int skip;                     /* Skip attribute value */
3438    int sourcefile;               /* Offset of SourceFile string */
3439    int sinkfile;                 /* Offset of SinkFile string */
3440    int strict;                   /* Report errors instead of warnings? */
3441 
3442 /* Check the global error status. */
3443    if ( !astOK ) return;
3444 
3445 /* Obtain a pointer to the Channel structure. */
3446    this = (AstChannel *) this_object;
3447 
3448 /* Obtain the length of the setting string. */
3449    len = (int) strlen( setting );
3450 
3451 /* Test for each recognised attribute in turn, using "astSscanf" to parse
3452    the setting string and extract the attribute value (or an offset to
3453    it in the case of string values). In each case, use the value set
3454    in "nc" to check that the entire string was matched. Once a value
3455    has been obtained, use the appropriate method to set it. */
3456 
3457 /* Comment. */
3458 /* ---------*/
3459    if ( nc = 0,
3460         ( 1 == astSscanf( setting, "comment= %d %n", &comment, &nc ) )
3461         && ( nc >= len ) ) {
3462       astSetComment( this, comment );
3463 
3464 /* Full. */
3465 /* ----- */
3466    } else if ( nc = 0,
3467                ( 1 == astSscanf( setting, "full= %d %n", &full, &nc ) )
3468                && ( nc >= len ) ) {
3469       astSetFull( this, full );
3470 
3471 /* Indent. */
3472 /* ------- */
3473    } else if ( nc = 0,
3474                ( 1 == astSscanf( setting, "indent= %d %n", &indent, &nc ) )
3475                && ( nc >= len ) ) {
3476       astSetIndent( this, indent );
3477 
3478 /* ReportLavel. */
3479 /* ------------ */
3480    } else if ( nc = 0,
3481                ( 1 == astSscanf( setting, "reportlevel= %d %n", &report_level, &nc ) )
3482                && ( nc >= len ) ) {
3483       astSetReportLevel( this, report_level );
3484 
3485 /* Skip. */
3486 /* ----- */
3487    } else if ( nc = 0,
3488                ( 1 == astSscanf( setting, "skip= %d %n", &skip, &nc ) )
3489                && ( nc >= len ) ) {
3490       astSetSkip( this, skip );
3491 
3492 /* SinkFile. */
3493 /* --------- */
3494    } else if ( nc = 0,
3495                ( 0 == astSscanf( setting, "sinkfile=%n%*[^\n]%n", &sinkfile, &nc ) )
3496                && ( nc >= len ) ) {
3497       astSetSinkFile( this, setting + sinkfile );
3498 
3499 /* SourceFile. */
3500 /* ----------- */
3501    } else if ( nc = 0,
3502                ( 0 == astSscanf( setting, "sourcefile=%n%*[^\n]%n", &sourcefile, &nc ) )
3503                && ( nc >= len ) ) {
3504       astSetSourceFile( this, setting + sourcefile );
3505 
3506 /* Strict. */
3507 /* ------- */
3508    } else if ( nc = 0,
3509                ( 1 == astSscanf( setting, "strict= %d %n", &strict, &nc ) )
3510                && ( nc >= len ) ) {
3511       astSetStrict( this, strict );
3512 
3513 /* If the attribute is still not recognised, pass it on to the parent
3514    method for further interpretation. */
3515    } else {
3516       (*parent_setattrib)( this_object, setting, status );
3517    }
3518 }
3519 
SinkWrap(void (* sink)(const char *),const char * line,int * status)3520 static void SinkWrap( void (* sink)( const char * ), const char *line, int *status ) {
3521 /*
3522 *  Name:
3523 *     SinkWrap
3524 
3525 *  Purpose:
3526 *     Wrapper function to invoke a C Channel sink function.
3527 
3528 *  Type:
3529 *     Private function.
3530 
3531 *  Synopsis:
3532 *     #include "channel.h"
3533 *     void SinkWrap( void (* sink)( const char * ), const char *line, int *status )
3534 
3535 *  Class Membership:
3536 *     Channel member function.
3537 
3538 *  Description:
3539 *     This function invokes the sink function whose pointer is
3540 *     supplied in order to write an output line to an external data
3541 *     store.
3542 
3543 *  Parameters:
3544 *     sink
3545 *        Pointer to a sink function, whose single parameter is a
3546 *        pointer to a const, null-terminated string containing the
3547 *        text to be written, and which returns void. This is the form
3548 *        of Channel sink function employed by the C language interface
3549 *        to the AST library.
3550 *     status
3551 *        Pointer to the inherited status variable.
3552 */
3553 
3554 /* Check the global error status. */
3555    if ( !astOK ) return;
3556 
3557 /* Invoke the sink function. */
3558    ( *sink )( line );
3559 }
3560 
SourceWrap(const char * (* source)(void),int * status)3561 static char *SourceWrap( const char *(* source)( void ), int *status ) {
3562 /*
3563 *  Name:
3564 *     SourceWrap
3565 
3566 *  Purpose:
3567 *     Wrapper function to invoke a C Channel source function.
3568 
3569 *  Type:
3570 *     Private function.
3571 
3572 *  Synopsis:
3573 *     #include "channel.h"
3574 *     char *SourceWrap( const char *, int *status(* source)( void ) )
3575 
3576 *  Class Membership:
3577 *     Channel member function.
3578 
3579 *  Description:
3580 *     This function invokes the source function whose pointer is
3581 *     supplied in order to read the next input line from an external
3582 *     data store. It then returns a pointer to a dynamic string
3583 *     containing a copy of the text that was read.
3584 
3585 *  Parameters:
3586 *     source
3587 *        Pointer to a source function, with no parameters, that
3588 *        returns a pointer to a const, null-terminated string
3589 *        containing the text that it read. This is the form of Channel
3590 *        source function employed by the C language interface to the
3591 *        AST library.
3592 *     status
3593 *        Pointer to the inherited status variable.
3594 
3595 *  Returned Value:
3596 *     A pointer to a dynamically allocated, null terminated string
3597 *     containing a copy of the text that was read. This string must be
3598 *     freed by the caller (using astFree) when no longer required.
3599 *
3600 *     A NULL pointer will be returned if there is no more input text
3601 *     to read.
3602 
3603 *  Notes:
3604 *     - A NULL pointer value will be returned if this function is
3605 *     invoked with the global error status set or if it should fail
3606 *     for any reason.
3607 */
3608 
3609 /* Local Variables: */
3610    char *result;                 /* Pointer value to return */
3611    const char *line;             /* Pointer to input line */
3612 
3613 /* Initialise. */
3614    result = NULL;
3615 
3616 /* Check the global error status. */
3617    if ( !astOK ) return result;
3618 
3619 /* Invoke the source function to read the next input line and return a
3620    pointer to the resulting string. */
3621    line = ( *source )();
3622 
3623 /* If a string was obtained, make a dynamic copy of it and save the
3624    resulting pointer. */
3625    if ( line ) result = astString( line, (int) strlen( line ) );
3626 
3627 /* Return the result. */
3628    return result;
3629 }
3630 
astStoreChannelData_(AstChannel * this,int * status)3631 void astStoreChannelData_( AstChannel *this, int *status ) {
3632 /*
3633 *+
3634 *  Name:
3635 *     astStoreChannelData
3636 
3637 *  Purpose:
3638 *     Store the Channel's channel-data pointer in a thread-specific
3639 *     global variable.
3640 
3641 *  Type:
3642 *     Protected virtual function.
3643 
3644 *  Synopsis:
3645 *     #include "channel.h"
3646 *     astStoreChannelData( AstChannel *this )
3647 
3648 *  Class Membership:
3649 *     Channel method.
3650 
3651 *  Description:
3652 *     This function stores the Channel's channel-data pointer (if any)
3653 *     established by the previous call to astPutChannelData, in a
3654 *     thread-specific global variable from where the astChannelData macro
3655 *     can access it.
3656 
3657 *  Parameters:
3658 *     this
3659 *        Pointer to the Channel.
3660 *-
3661 */
3662 
3663 /* Local Variables: */
3664    astDECLARE_GLOBALS            /* Declare the thread specific global data */
3665 
3666 /* Check the global error status. */
3667    if ( !astOK ) return;
3668 
3669 /* Get a pointer to the structure holding thread-specific global data. */
3670    astGET_GLOBALS(this);
3671 
3672 /* Store the pointer int he global variable. */
3673    channel_data = this->data;
3674 }
3675 
TestAttrib(AstObject * this_object,const char * attrib,int * status)3676 static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
3677 /*
3678 *  Name:
3679 *     TestAttrib
3680 
3681 *  Purpose:
3682 *     Test if a specified attribute value is set for a Channel.
3683 
3684 *  Type:
3685 *     Private function.
3686 
3687 *  Synopsis:
3688 *     #include "channel.h"
3689 *     int TestAttrib( AstObject *this, const char *attrib, int *status )
3690 
3691 *  Class Membership:
3692 *     Channel member function (over-rides the astTestAttrib protected
3693 *     method inherited from the Object class).
3694 
3695 *  Description:
3696 *     This function returns a boolean result (0 or 1) to indicate whether
3697 *     a value has been set for one of a Channel's attributes.
3698 
3699 *  Parameters:
3700 *     this
3701 *        Pointer to the Channel.
3702 *     attrib
3703 *        Pointer to a null terminated string specifying the attribute
3704 *        name.  This should be in lower case with no surrounding white
3705 *        space.
3706 *     status
3707 *        Pointer to the inherited status variable.
3708 
3709 *  Returned Value:
3710 *     One if a value has been set, otherwise zero.
3711 
3712 *  Notes:
3713 *     - A value of zero will be returned if this function is invoked
3714 *     with the global status set, or if it should fail for any reason.
3715 */
3716 
3717 /* Local Variables: */
3718    AstChannel *this;             /* Pointer to the Channel structure */
3719    int result;                   /* Result value to return */
3720 
3721 /* Initialise. */
3722    result = 0;
3723 
3724 /* Check the global error status. */
3725    if ( !astOK ) return result;
3726 
3727 /* Obtain a pointer to the Channel structure. */
3728    this = (AstChannel *) this_object;
3729 
3730 /* Check the attribute name and test the appropriate attribute. */
3731 
3732 /* Comment. */
3733 /* -------- */
3734    if ( !strcmp( attrib, "comment" ) ) {
3735       result = astTestComment( this );
3736 
3737 /* Full. */
3738 /* ----- */
3739    } else if ( !strcmp( attrib, "full" ) ) {
3740       result = astTestFull( this );
3741 
3742 /* Indent. */
3743 /* ------- */
3744    } else if ( !strcmp( attrib, "indent" ) ) {
3745       result = astTestIndent( this );
3746 
3747 /* ReportLevel. */
3748 /* ------------ */
3749    } else if ( !strcmp( attrib, "reportlevel" ) ) {
3750       result = astTestReportLevel( this );
3751 
3752 /* Skip. */
3753 /* ----- */
3754    } else if ( !strcmp( attrib, "skip" ) ) {
3755       result = astTestSkip( this );
3756 
3757 /* SourceFile. */
3758 /* ----------- */
3759    } else if ( !strcmp( attrib, "sourcefile" ) ) {
3760       result = astTestSourceFile( this );
3761 
3762 /* SinkFile. */
3763 /* ----------- */
3764    } else if ( !strcmp( attrib, "sinkfile" ) ) {
3765       result = astTestSinkFile( this );
3766 
3767 /* Strict. */
3768 /* ------- */
3769    } else if ( !strcmp( attrib, "strict" ) ) {
3770       result = astTestStrict( this );
3771 
3772 /* If the attribute is still not recognised, pass it on to the parent
3773    method for further interpretation. */
3774    } else {
3775       result = (*parent_testattrib)( this_object, attrib, status );
3776    }
3777 
3778 /* Return the result, */
3779    return result;
3780 }
3781 
Unquote(AstChannel * this,char * str,int * status)3782 static void Unquote( AstChannel *this, char *str, int *status ) {
3783 /*
3784 *  Name:
3785 *     Unquote
3786 
3787 *  Purpose:
3788 *     Remove quotes from a (possibly) quoted string.
3789 
3790 *  Type:
3791 *     Private function.
3792 
3793 *  Synopsis:
3794 *     #include "channel.h"
3795 *     void Unquote( AstChannel *this, char *str, int *status )
3796 
3797 *  Class Membership:
3798 *     Channel member function.
3799 
3800 *  Description:
3801 *     This function removes one layer of quote characters (") from a
3802 *     string which is possibly quoted. Any quotes within quotes (which
3803 *     should have been doubled when the string was originally quoted)
3804 *     are also converted back to single quotes again.
3805 *
3806 *     The quotes need not start or end at the ends of the string, and
3807 *     there may be any number of quoted sections within the string. No
3808 *     error results if the string does not contain any quotes at all
3809 *     (it is simply returned unchanged), but an error results if any
3810 *     unmatched quotes are found.
3811 
3812 *  Parameters:
3813 *     this
3814 *        Pointer to a Channel. This is only used for constructing error
3815 *        messages and has no influence on the string processing.
3816 *     str
3817 *        Pointer to the null-terminated string to be processed. This
3818 *        is modified in place. The new string starts at the same
3819 *        location as the original but has a new null character
3820 *        appended if necessary (it will usually be shorter than the
3821 *        original).
3822 *     status
3823 *        Pointer to the inherited status variable.
3824 */
3825 
3826 /* Local Variables: */
3827    int i;                        /* Loop counter for "input" characters */
3828    int j;                        /* Counter for "output" characters */
3829    int quoted;                   /* Inside a quoted string? */
3830 
3831 /* Check the global error status. */
3832    if ( !astOK ) return;
3833 
3834 /* Loop to inspect each character in the string. */
3835    quoted = 0;
3836    for ( i = j = 0; str[ i ]; i++ ) {
3837 
3838 /* Non-quote characters are simply copied to their new location in the
3839    string. */
3840       if ( str[ i ] != '"' ) {
3841          str[ j++ ] = str[ i ];
3842 
3843 /* If a quote character '"' is encountered and we are not already in a
3844    quoted string, then note the start of a quoted string (and discard
3845    the quote). */
3846       } else if ( !quoted ) {
3847          quoted = 1;
3848 
3849 /* If a quote character is encountered inside a quoted string, then
3850    check if the next character is also a quote. If so, convert this
3851    double quote to a single one. */
3852       } else if ( str[ i + 1 ] == '"' ) {
3853          str[ j++ ] = '"';
3854          i++;
3855 
3856 /* If a single quote character is encountered inside a quoted string,
3857    then note the end of the quoted string (and discard the quote). */
3858       } else {
3859          quoted = 0;
3860       }
3861    }
3862 
3863 /* Append a null to terminate the processed string. */
3864    str[ j ] = '\0';
3865 
3866 /* If the "quoted" flag is still set, then there were unmatched
3867    quotes, so report an error. */
3868    if ( quoted ) {
3869       astError( AST__UNMQT,
3870                 "astRead(%s): Unmatched quotes in input data: %s.", status,
3871                 astGetClass( this ), str );
3872    }
3873 }
3874 
Use(AstChannel * this,int set,int helpful,int * status)3875 static int Use( AstChannel *this, int set, int helpful, int *status ) {
3876 /*
3877 *  Name:
3878 *     Use
3879 
3880 *  Purpose:
3881 *     Decide whether to write a value to a data sink.
3882 
3883 *  Type:
3884 *     Private function.
3885 
3886 *  Synopsis:
3887 *     #include "channel.h"
3888 *     int Use( AstChannel *this, int set, int helpful, int *status )
3889 
3890 *  Class Membership:
3891 *     Channel member function.
3892 
3893 *  Description:
3894 *     This function decides whether a value supplied by a class "Dump"
3895 *     function, via a call to one of the astWrite... protected
3896 *     methods, should actually be written to the data sink associated
3897 *     with a Channel.
3898 *
3899 *     This decision is based on the settings of the "set" and
3900 *     "helpful" flags supplied to the astWrite... method, plus the
3901 *     attribute settings of the Channel.
3902 
3903 *  Parameters:
3904 *     this
3905 *        A pointer to the Channel.
3906 *     set
3907 *        The "set" flag supplied.
3908 *     helpful
3909 *        The "helpful" value supplied.
3910 *     status
3911 *        Pointer to the inherited status variable.
3912 
3913 *  Returned Value:
3914 *     One if the value should be written out, otherwise zero.
3915 
3916 *  Notes:
3917 *     - A value of zero will be returned if this function is invoked
3918 *     with the global error status set or if it should fail for any
3919 *     reason.
3920 */
3921 
3922 /* Local Variables: */
3923    int full;                     /* Full attribute value */
3924    int result;                   /* Result value to be returned */
3925 
3926 /* Check the global error status. */
3927    if ( !astOK ) return 0;
3928 
3929 /* If "set" is non-zero, then so is the result ("set" values must
3930    always be written out). */
3931    result = ( set != 0 );
3932 
3933 /* Otherwise, obtain the value of the Channel's Full attribute. */
3934    if ( !set ) {
3935       full = astGetFull( this );
3936 
3937 /* If Full is positive, display all values, if zero, display only
3938    "helpful" values, if negative, display no (un-"set") values. */
3939       if ( astOK ) result = ( ( helpful && ( full > -1 ) ) || ( full > 0 ) );
3940    }
3941 
3942 /* Return the result. */
3943    return result;
3944 }
3945 
Write(AstChannel * this,AstObject * object,int * status)3946 static int Write( AstChannel *this, AstObject *object, int *status ) {
3947 /*
3948 *++
3949 *  Name:
3950 c     astWrite
3951 f     AST_WRITE
3952 
3953 *  Purpose:
3954 *     Write an Object to a Channel.
3955 
3956 *  Type:
3957 *     Public function.
3958 
3959 *  Synopsis:
3960 c     #include "channel.h"
3961 c     int astWrite( AstChannel *this, AstObject *object )
3962 f     RESULT = AST_WRITE( THIS, OBJECT, STATUS )
3963 
3964 *  Class Membership:
3965 *     Channel method.
3966 
3967 *  Description:
3968 *     This function writes an Object to a Channel, appending it to any
3969 *     previous Objects written to that Channel.
3970 
3971 *  Parameters:
3972 c     this
3973 f     THIS = INTEGER (Given)
3974 *        Pointer to the Channel.
3975 c     object
3976 f     OBJECT = INTEGER (Given)
3977 *        Pointer to the Object which is to be written.
3978 f     STATUS = INTEGER (Given and Returned)
3979 f        The global status.
3980 
3981 *  Returned Value:
3982 c     astWrite()
3983 f     AST_WRITE = INTEGER
3984 *        The number of Objects written to the Channel by this
3985 c        invocation of astWrite (normally, this will be one).
3986 f        invocation of AST_WRITE (normally, this will be one).
3987 
3988 *  Applicability:
3989 *     FitsChan
3990 *        If the FitsChan uses a foreign encoding (e.g. FITS-WCS) rather
3991 *        than the native AST encoding, then storing values in the
3992 *        FitsChan for keywords NAXIS1, NAXIS2, etc., before invoking
3993 c        astWrite
3994 f        AST_WRITE
3995 *        can help to produce a successful write.
3996 
3997 *  Notes:
3998 *     - A value of zero will be returned if this function is invoked
3999 c     with the AST error status set, or if it should fail for any
4000 f     with STATUS set to an error value, or if it should fail for any
4001 *     reason.
4002 *     - Invoking this function will usually cause the sink function
4003 *     associated with the channel to be called in order to transfer a
4004 *     textual description of the supplied object to some external data
4005 *     store. However, the FitsChan class behaves differently. Invoking
4006 *     this function on a FitsChan causes new FITS header cards to be
4007 *     added to an internal buffer (the sink function is not invoked).
4008 *     This buffer is written out through the sink function only when the
4009 *     FitsChan is deleted.
4010 *--
4011 */
4012 
4013 /* Check the global error status. */
4014    if ( !astOK ) return 0;
4015 
4016 /* The work of this function is actually performed by the protected
4017    astDump method of the Object. The fact that this is further
4018    encapsulated within the astWrite method (which belongs to the
4019    Channel) is simply a trick to allow it to be over-ridden either by
4020    a derived Channel, or a derived Object (or both), and hence to
4021    adapt to the nature of either argument. */
4022    astDump( object, this );
4023 
4024 /* Return the number of Objects written. */
4025    return astOK ? 1 : 0;
4026 }
4027 
WriteBegin(AstChannel * this,const char * class,const char * comment,int * status)4028 static void WriteBegin( AstChannel *this, const char *class,
4029                         const char *comment, int *status ) {
4030 /*
4031 *+
4032 *  Name:
4033 *     astWriteBegin
4034 
4035 *  Purpose:
4036 *     Write a "Begin" data item to a data sink.
4037 
4038 *  Type:
4039 *     Protected virtual function.
4040 
4041 *  Synopsis:
4042 *     #include "channel.h"
4043 *     void astWriteBegin( AstChannel *this, const char *class,
4044 *                         const char *comment )
4045 
4046 *  Class Membership:
4047 *     Channel method.
4048 
4049 *  Description:
4050 *     This function writes a "Begin" data item to the data sink
4051 *     associated with a Channel, so as to begin the output of a new
4052 *     Object definition.
4053 
4054 *  Parameters:
4055 *     this
4056 *        Pointer to the Channel.
4057 *     class
4058 *        Pointer to a constant null-terminated string containing the
4059 *        name of the class to which the Object belongs.
4060 *     comment
4061 *        Pointer to a constant null-terminated string containing a
4062 *        textual comment to be associated with the "Begin"
4063 *        item. Normally, this will describe the purpose of the Object.
4064 
4065 *  Notes:
4066 *     - The comment supplied may not actually be used, depending on
4067 *     the nature of the Channel supplied.
4068 *-
4069 */
4070 
4071 /* Local Variables: */
4072    astDECLARE_GLOBALS            /* Declare the thread specific global data */
4073    char *line;                   /* Pointer to dynamic output string */
4074    int i;                        /* Loop counter for indentation characters */
4075    int nc;                       /* Number of output characters */
4076 
4077 /* Check the global error status. */
4078    if ( !astOK ) return;
4079 
4080 /* Get a pointer to the structure holding thread-specific global data. */
4081    astGET_GLOBALS(this);
4082 
4083 /* Start building a dynamic string with an initial space. Then add
4084    further spaces to suit the current indentation level. */
4085    line = astAppendString( NULL, &nc, " " );
4086    for ( i = 0; i < current_indent; i++ ) {
4087       line = astAppendString( line, &nc, " " );
4088    }
4089 
4090 /* Append the "Begin" keyword followed by the class name. */
4091    line = astAppendString( line, &nc, "Begin " );
4092    line = astAppendString( line, &nc, class );
4093 
4094 /* If required, also append the comment. */
4095    if ( astGetComment( this ) && *comment ) {
4096       line = astAppendString( line, &nc, " \t# " );
4097       line = astAppendString( line, &nc, comment );
4098    }
4099 
4100 /* Write out the resulting line of text. */
4101    OutputTextItem( this, line, status );
4102 
4103 /* Free the dynamic string. */
4104    line = astFree( line );
4105 
4106 /* Increment the indentation level and clear the count of items written
4107    for this Object. */
4108    current_indent += astGetIndent( this );
4109    items_written = 0;
4110 }
4111 
WriteDouble(AstChannel * this,const char * name,int set,int helpful,double value,const char * comment,int * status)4112 static void WriteDouble( AstChannel *this, const char *name,
4113                          int set, int helpful,
4114                          double value, const char *comment, int *status ) {
4115 /*
4116 *+
4117 *  Name:
4118 *     astWriteDouble
4119 
4120 *  Purpose:
4121 *     Write a double value to a data sink.
4122 
4123 *  Type:
4124 *     Protected virtual function.
4125 
4126 *  Synopsis:
4127 *     #include "channel.h"
4128 *     void astWriteDouble( AstChannel *this, const char *name,
4129 *                          int set, int helpful,
4130 *                          double value, const char *comment )
4131 
4132 *  Class Membership:
4133 *     Channel method.
4134 
4135 *  Description:
4136 *     This function writes a named double value, representing the
4137 *     value of a class instance variable, to the data sink associated
4138 *     with a Channel. It is intended for use by class "Dump" functions
4139 *     when writing out class information which will subsequently be
4140 *     re-read.
4141 
4142 *  Parameters:
4143 *     this
4144 *        Pointer to the Channel.
4145 *     name
4146 *        Pointer to a constant null-terminated string containing the
4147 *        name to be used to identify the value in the external
4148 *        representation. This will form the key for identifying it
4149 *        again when it is re-read. The name supplied should be unique
4150 *        within its class.
4151 *
4152 *        Mixed case may be used and will be preserved in the external
4153 *        representation (where possible) for cosmetic effect. However,
4154 *        case is not significant when re-reading values.
4155 *
4156 *        It is recommended that a maximum of 6 alphanumeric characters
4157 *        (starting with an alphabetic character) be used. This permits
4158 *        maximum flexibility in adapting to standard external data
4159 *        representations (e.g. FITS).
4160 *     set
4161 *        If this is zero, it indicates that the value being written is
4162 *        a default value (or can be re-generated from other values) so
4163 *        need not necessarily be written out. Such values will
4164 *        typically be included in the external representation with
4165 *        (e.g.) a comment character so that they are available to
4166 *        human readers but will be ignored when re-read. They may also
4167 *        be completely omitted in some circumstances.
4168 *
4169 *        If "set" is non-zero, the value will always be explicitly
4170 *        included in the external representation so that it can be
4171 *        re-read.
4172 *     helpful
4173 *        This flag provides a hint about whether a value whose "set"
4174 *        flag is zero (above) should actually appear at all in the
4175 *        external representaton.
4176 *
4177 *        If the external representation allows values to be "commented
4178 *        out" then, by default, values will be included in this form
4179 *        only if their "helpful" flag is non-zero. Otherwise, they
4180 *        will be omitted entirely. When possible, omitting the more
4181 *        obscure values associated with a class is recommended in
4182 *        order to improve readability.
4183 *
4184 *        This default behaviour may be further modified if the
4185 *        Channel's Full attribute is set - either to permit all values
4186 *        to be shown, or to suppress non-essential information
4187 *        entirely.
4188 *     value
4189 *        The value to be written.
4190 *     comment
4191 *        Pointer to a constant null-terminated string containing a
4192 *        textual comment to be associated with the value.
4193 *
4194 *        Note that this comment may not actually be used, depending on
4195 *        the nature of the Channel supplied and the setting of its
4196 *        Comment attribute.
4197 *-
4198 */
4199 
4200 /* Local Constants: */
4201 #define BUFF_LEN 100             /* Size of local formatting buffer */
4202 
4203 /* Local Variables: */
4204    astDECLARE_GLOBALS            /* Declare the thread specific global data */
4205    char *line;                   /* Pointer to dynamic output string */
4206    char buff[ BUFF_LEN + 1 ];    /* Local formatting buffer */
4207    int i;                        /* Loop counter for indentation characters */
4208    int nc;                       /* Number of output characters */
4209 
4210 /* Check the global error status. */
4211    if ( !astOK ) return;
4212 
4213 /* Get a pointer to the structure holding thread-specific global data. */
4214    astGET_GLOBALS(this);
4215 
4216 /* Use the "set" and "helpful" flags, along with the Channel's
4217    attributes to decide whether this value should actually be
4218    written. */
4219    if ( Use( this, set, helpful, status ) ) {
4220 
4221 /* Start building a dynamic string with an initial space, or a comment
4222    character if "set" is zero. Then add further spaces to suit the
4223    current indentation level. */
4224       line = astAppendString( NULL, &nc, set ? " " : "#" );
4225       for ( i = 0; i < current_indent; i++ ) {
4226          line = astAppendString( line, &nc, " " );
4227       }
4228 
4229 /* Append the name string followed by " = ". */
4230       line = astAppendString( line, &nc, name );
4231       line = astAppendString( line, &nc, " = " );
4232 
4233 /* Format the value as a string and append this. Make sure "-0" isn't
4234    produced. Use a magic string to represent bad values. */
4235       if( value != AST__BAD ) {
4236          (void) sprintf( buff, "%.*g", DBL_DIG, value );
4237          if ( !strcmp( buff, "-0" ) ) {
4238             buff[ 0 ] = '0';
4239             buff[ 1 ] = '\0';
4240          }
4241       } else {
4242          strcpy( buff, BAD_STRING );
4243       }
4244       line = astAppendString( line, &nc, buff );
4245 
4246 /* If required, also append the comment. */
4247       if ( astGetComment( this ) && *comment ) {
4248          line = astAppendString( line, &nc, " \t# " );
4249          line = astAppendString( line, &nc, comment );
4250       }
4251 
4252 /* Write out the resulting line of text. */
4253       OutputTextItem( this, line, status );
4254 
4255 /* Free the dynamic string. */
4256       line = astFree( line );
4257    }
4258 
4259 /* Undefine macros local to this function. */
4260 #undef BUFF_LEN
4261 }
4262 
WriteEnd(AstChannel * this,const char * class,int * status)4263 static void WriteEnd( AstChannel *this, const char *class, int *status ) {
4264 /*
4265 *+
4266 *  Name:
4267 *     astWriteEnd
4268 
4269 *  Purpose:
4270 *     Write an "End" data item to a data sink.
4271 
4272 *  Type:
4273 *     Protected virtual function.
4274 
4275 *  Synopsis:
4276 *     #include "channel.h"
4277 *     void astWriteEnd( AstChannel *this, const char *class )
4278 
4279 *  Class Membership:
4280 *     Channel method.
4281 
4282 *  Description:
4283 *     This function writes an "End" data item to the data sink
4284 *     associated with a Channel. This item delimits the end of an
4285 *     Object definition.
4286 
4287 *  Parameters:
4288 *     this
4289 *        Pointer to the Channel.
4290 *     class
4291 *        Pointer to a constant null-terminated string containing the
4292 *        class name of the Object whose definition is being terminated
4293 *        by the "End" item.
4294 *-
4295 */
4296 
4297 /* Local Variables: */
4298    astDECLARE_GLOBALS            /* Declare the thread specific global data */
4299    char *line;                   /* Pointer to dynamic output string */
4300    int i;                        /* Loop counter for indentation characters */
4301    int nc;                       /* Number of output characters */
4302 
4303 /* Check the global error status. */
4304    if ( !astOK ) return;
4305 
4306 /* Get a pointer to the structure holding thread-specific global data. */
4307    astGET_GLOBALS(this);
4308 
4309 /* Decrement the indentation level so that the "End" item matches the
4310    corresponding "Begin" item. */
4311    current_indent -= astGetIndent( this );
4312 
4313 /* Start building a dynamic string with an initial space. Then add
4314    further spaces to suit the current indentation level. */
4315    line = astAppendString( NULL, &nc, " " );
4316    for ( i = 0; i < current_indent; i++ ) {
4317       line = astAppendString( line, &nc, " " );
4318    }
4319 
4320 /* Append the "End" keyword followed by the class name. */
4321    line = astAppendString( line, &nc, "End " );
4322    line = astAppendString( line, &nc, class );
4323 
4324 /* Write out the resulting line of text. */
4325    OutputTextItem( this, line, status );
4326 
4327 /* Free the dynamic string. */
4328    line = astFree( line );
4329 }
4330 
WriteInt(AstChannel * this,const char * name,int set,int helpful,int value,const char * comment,int * status)4331 static void WriteInt( AstChannel *this, const char *name, int set, int helpful,
4332                       int value, const char *comment, int *status ) {
4333 /*
4334 *+
4335 *  Name:
4336 *     astWriteInt
4337 
4338 *  Purpose:
4339 *     Write an integer value to a data sink.
4340 
4341 *  Type:
4342 *     Protected virtual function.
4343 
4344 *  Synopsis:
4345 *     #include "channel.h"
4346 *     void astWriteInt( AstChannel *this, const char *name,
4347 *                       int set, int helpful,
4348 *                       int value, const char *comment )
4349 
4350 *  Class Membership:
4351 *     Channel method.
4352 
4353 *  Description:
4354 *     This function writes a named integer value, representing the
4355 *     value of a class instance variable, to the data sink associated
4356 *     with a Channel. It is intended for use by class "Dump" functions
4357 *     when writing out class information which will subsequently be
4358 *     re-read.
4359 
4360 *  Parameters:
4361 *     this
4362 *        Pointer to the Channel.
4363 *     name
4364 *        Pointer to a constant null-terminated string containing the
4365 *        name to be used to identify the value in the external
4366 *        representation. This will form the key for identifying it
4367 *        again when it is re-read. The name supplied should be unique
4368 *        within its class.
4369 *
4370 *        Mixed case may be used and will be preserved in the external
4371 *        representation (where possible) for cosmetic effect. However,
4372 *        case is not significant when re-reading values.
4373 *
4374 *        It is recommended that a maximum of 6 alphanumeric characters
4375 *        (starting with an alphabetic character) be used. This permits
4376 *        maximum flexibility in adapting to standard external data
4377 *        representations (e.g. FITS).
4378 *     set
4379 *        If this is zero, it indicates that the value being written is
4380 *        a default value (or can be re-generated from other values) so
4381 *        need not necessarily be written out. Such values will
4382 *        typically be included in the external representation with
4383 *        (e.g.) a comment character so that they are available to
4384 *        human readers but will be ignored when re-read. They may also
4385 *        be completely omitted in some circumstances.
4386 *
4387 *        If "set" is non-zero, the value will always be explicitly
4388 *        included in the external representation so that it can be
4389 *        re-read.
4390 *     helpful
4391 *        This flag provides a hint about whether a value whose "set"
4392 *        flag is zero (above) should actually appear at all in the
4393 *        external representaton.
4394 *
4395 *        If the external representation allows values to be "commented
4396 *        out" then, by default, values will be included in this form
4397 *        only if their "helpful" flag is non-zero. Otherwise, they
4398 *        will be omitted entirely. When possible, omitting the more
4399 *        obscure values associated with a class is recommended in
4400 *        order to improve readability.
4401 *
4402 *        This default behaviour may be further modified if the
4403 *        Channel's Full attribute is set - either to permit all values
4404 *        to be shown, or to suppress non-essential information
4405 *        entirely.
4406 *     value
4407 *        The value to be written.
4408 *     comment
4409 *        Pointer to a constant null-terminated string containing a
4410 *        textual comment to be associated with the value.
4411 *
4412 *        Note that this comment may not actually be used, depending on
4413 *        the nature of the Channel supplied and the setting of its
4414 *        Comment attribute.
4415 *-
4416 */
4417 
4418 /* Local Constants: */
4419 #define BUFF_LEN 50              /* Size of local formatting buffer */
4420 
4421 /* Local Variables: */
4422    astDECLARE_GLOBALS            /* Declare the thread specific global data */
4423    char *line;                   /* Pointer to dynamic output string */
4424    char buff[ BUFF_LEN + 1 ];    /* Local formatting buffer */
4425    int i;                        /* Loop counter for indentation characters */
4426    int nc;                       /* Number of output characters */
4427 
4428 /* Check the global error status. */
4429    if ( !astOK ) return;
4430 
4431 /* Get a pointer to the structure holding thread-specific global data. */
4432    astGET_GLOBALS(this);
4433 
4434 /* Use the "set" and "helpful" flags, along with the Channel's
4435    attributes to decide whether this value should actually be
4436    written. */
4437    if ( Use( this, set, helpful, status ) ) {
4438 
4439 /* Start building a dynamic string with an initial space, or a comment
4440    character if "set" is zero. Then add further spaces to suit the
4441    current indentation level. */
4442       line = astAppendString( NULL, &nc, set ? " " : "#" );
4443       for ( i = 0; i < current_indent; i++ ) {
4444          line = astAppendString( line, &nc, " " );
4445       }
4446 
4447 /* Append the name string followed by " = ". */
4448       line = astAppendString( line, &nc, name );
4449       line = astAppendString( line, &nc, " = " );
4450 
4451 /* Format the value as a decimal string and append this. */
4452       (void) sprintf( buff, "%d", value );
4453       line = astAppendString( line, &nc, buff );
4454 
4455 /* If required, also append the comment. */
4456       if ( astGetComment( this ) && *comment ) {
4457          line = astAppendString( line, &nc, " \t# " );
4458          line = astAppendString( line, &nc, comment );
4459       }
4460 
4461 /* Write out the resulting line of text. */
4462       OutputTextItem( this, line, status );
4463 
4464 /* Free the dynamic string. */
4465       line = astFree( line );
4466    }
4467 
4468 /* Undefine macros local to this function. */
4469 #undef BUFF_LEN
4470 }
4471 
astWriteInvocations_(int * status)4472 int astWriteInvocations_( int *status ){
4473 /*
4474 *+
4475 *  Name:
4476 *     astWriteInvocations
4477 
4478 *  Purpose:
4479 *     Returns the number of invocations of the astWrite method.
4480 
4481 *  Type:
4482 *     Protected function.
4483 
4484 *  Synopsis:
4485 *     #include "channel.h"
4486 *     int astWriteInvocations
4487 
4488 *  Class Membership:
4489 *     Channel method.
4490 
4491 *  Description:
4492 *     This function returns the number of invocations of astWrite which
4493 *     have been made so far, excluding those made from within the
4494 *     astWriteObject method. An example of its use is to allow a Dump
4495 *     function to determine if a sub-object has already been dumped
4496 *     during the current invocation of astWrite. See the Dump method for
4497 *     the AstUnit class as an example.
4498 *-
4499 */
4500    astDECLARE_GLOBALS
4501    astGET_GLOBALS(NULL);
4502    return nwrite_invoc;
4503 }
4504 
WriteIsA(AstChannel * this,const char * class,const char * comment,int * status)4505 static void WriteIsA( AstChannel *this, const char *class,
4506                       const char *comment, int *status ) {
4507 /*
4508 *+
4509 *  Name:
4510 *     astWriteIsA
4511 
4512 *  Purpose:
4513 *     Write an "IsA" data item to a data sink.
4514 
4515 *  Type:
4516 *     Protected virtual function.
4517 
4518 *  Synopsis:
4519 *     #include "channel.h"
4520 *     void astWriteIsA( AstChannel *this, const char *class,
4521 *                       const char *comment )
4522 
4523 *  Class Membership:
4524 *     Channel method.
4525 
4526 *  Description:
4527 *     This function writes an "IsA" data item to the data sink
4528 *     associated with a Channel. This item delimits the end of the
4529 *     data associated with the instance variables of a class, as part
4530 *     of an overall Object definition.
4531 
4532 *  Parameters:
4533 *     this
4534 *        Pointer to the Channel.
4535 *     class
4536 *        Pointer to a constant null-terminated string containing the
4537 *        name of the class whose data are terminated by the "IsA"
4538 *        item.
4539 *     comment
4540 *        Pointer to a constant null-terminated string containing a
4541 *        textual comment to be associated with the "IsA"
4542 *        item. Normally, this will describe the purpose of the class
4543 *        whose data are being terminated.
4544 
4545 *  Notes:
4546 *     - The comment supplied may not actually be used, depending on
4547 *     the nature of the Channel supplied.
4548 *-
4549 */
4550 
4551 /* Local Variables: */
4552    astDECLARE_GLOBALS            /* Declare the thread specific global data */
4553    char *line;                   /* Pointer to dynamic output string */
4554    int i;                        /* Loop counter for indentation characters */
4555    int indent_inc;               /* Indentation increment */
4556    int nc;                       /* Number of output characters */
4557 
4558 /* Check the global error status. */
4559    if ( !astOK ) return;
4560 
4561 /* Get a pointer to the structure holding thread-specific global data. */
4562    astGET_GLOBALS(this);
4563 
4564 /* Output an "IsA" item only if there has been at least one item
4565    written since the last "Begin" or "IsA" item, or if the Full
4566    attribute for the Channel is greater than zero (requesting maximum
4567    information). */
4568    if ( items_written || astGetFull( this ) > 0 ) {
4569 
4570 /* Start building a dynamic string with an initial space. Then add
4571    further spaces to suit the current indentation level, but reduced
4572    by one to allow the "IsA" item to match the "Begin" and "End" items
4573    which enclose it. */
4574       indent_inc = astGetIndent( this );
4575       line = astAppendString( NULL, &nc, " " );
4576       for ( i = 0; i < ( current_indent - indent_inc ); i++ ) {
4577          line = astAppendString( line, &nc, " " );
4578       }
4579 
4580 /* Append the "IsA" keyword followed by the class name. */
4581       line = astAppendString( line, &nc, "IsA " );
4582       line = astAppendString( line, &nc, class );
4583 
4584 /* If required, also append the comment. */
4585       if ( astGetComment( this ) && *comment ) {
4586          line = astAppendString( line, &nc, " \t# " );
4587          line = astAppendString( line, &nc, comment );
4588       }
4589 
4590 /* Write out the resulting line of text. */
4591       OutputTextItem( this, line, status );
4592 
4593 /* Free the dynamic string. */
4594       line = astFree( line );
4595 
4596 /* Clear the count of items written for this class. */
4597       items_written = 0;
4598    }
4599 }
4600 
WriteObject(AstChannel * this,const char * name,int set,int helpful,AstObject * value,const char * comment,int * status)4601 static void WriteObject( AstChannel *this, const char *name,
4602                          int set, int helpful,
4603                          AstObject *value, const char *comment, int *status ) {
4604 /*
4605 *+
4606 *  Name:
4607 *     astWriteObject
4608 
4609 *  Purpose:
4610 *     Write an Object as a value to a data sink.
4611 
4612 *  Type:
4613 *     Protected virtual function.
4614 
4615 *  Synopsis:
4616 *     #include "channel.h"
4617 *     void astWriteObject( AstChannel *this, const char *name,
4618 *                          int set, int helpful,
4619 *                          AstObject *value, const char *comment )
4620 
4621 *  Class Membership:
4622 *     Channel method.
4623 
4624 *  Description:
4625 *     This function writes an Object as a named value, representing
4626 *     the value of a class instance variable, to the data sink
4627 *     associated with a Channel. It is intended for use by class
4628 *     "Dump" functions when writing out class information which will
4629 *     subsequently be re-read.
4630 
4631 *  Parameters:
4632 *     this
4633 *        Pointer to the Channel.
4634 *     name
4635 *        Pointer to a constant null-terminated string containing the
4636 *        name to be used to identify the value in the external
4637 *        representation. This will form the key for identifying it
4638 *        again when it is re-read. The name supplied should be unique
4639 *        within its class.
4640 *
4641 *        Mixed case may be used and will be preserved in the external
4642 *        representation (where possible) for cosmetic effect. However,
4643 *        case is not significant when re-reading values.
4644 *
4645 *        It is recommended that a maximum of 6 alphanumeric characters
4646 *        (starting with an alphabetic character) be used. This permits
4647 *        maximum flexibility in adapting to standard external data
4648 *        representations (e.g. FITS).
4649 *     set
4650 *        If this is zero, it indicates that the value being written is
4651 *        a default value (or can be re-generated from other values) so
4652 *        need not necessarily be written out. Such values will
4653 *        typically be included in the external representation with
4654 *        (e.g.) a comment character so that they are available to
4655 *        human readers but will be ignored when re-read. They may also
4656 *        be completely omitted in some circumstances.
4657 *
4658 *        If "set" is non-zero, the value will always be explicitly
4659 *        included in the external representation so that it can be
4660 *        re-read.
4661 *     helpful
4662 *        This flag provides a hint about whether a value whose "set"
4663 *        flag is zero (above) should actually appear at all in the
4664 *        external representaton.
4665 *
4666 *        If the external representation allows values to be "commented
4667 *        out" then, by default, values will be included in this form
4668 *        only if their "helpful" flag is non-zero. Otherwise, they
4669 *        will be omitted entirely. When possible, omitting the more
4670 *        obscure values associated with a class is recommended in
4671 *        order to improve readability.
4672 *
4673 *        This default behaviour may be further modified if the
4674 *        Channel's Full attribute is set - either to permit all values
4675 *        to be shown, or to suppress non-essential information
4676 *        entirely.
4677 *     value
4678 *        A Pointer to the Object to be written.
4679 *     comment
4680 *        Pointer to a constant null-terminated string containing a
4681 *        textual comment to be associated with the value.
4682 *
4683 *        Note that this comment may not actually be used, depending on
4684 *        the nature of the Channel supplied and the setting of its
4685 *        Comment attribute.
4686 *-
4687 */
4688 
4689 /* Local Variables: */
4690    astDECLARE_GLOBALS            /* Declare the thread specific global data */
4691    char *line;                   /* Pointer to dynamic output string */
4692    int i;                        /* Loop counter for indentation characters */
4693    int indent_inc;               /* Indentation increment */
4694    int nc;                       /* Number of output characters */
4695 
4696 /* Check the global error status. */
4697    if ( !astOK ) return;
4698 
4699 /* Get a pointer to the structure holding thread-specific global data. */
4700    astGET_GLOBALS(this);
4701 
4702 /* Use the "set" and "helpful" flags, along with the Channel's
4703    attributes to decide whether this value should actually be
4704    written. */
4705    if ( Use( this, set, helpful, status ) ) {
4706 
4707 /* Start building a dynamic string with an initial space, or a comment
4708    character if "set" is zero. Then add further spaces to suit the
4709    current indentation level. */
4710       line = astAppendString( NULL, &nc, set ? " " : "#" );
4711       for ( i = 0; i < current_indent; i++ ) {
4712          line = astAppendString( line, &nc, " " );
4713       }
4714 
4715 /* Append the name string followed by " =". The absence of a value on
4716    the right hand side indicates an Object value, whose definition
4717    follows. */
4718       line = astAppendString( line, &nc, name );
4719       line = astAppendString( line, &nc, " =" );
4720 
4721 /* If required, also append the comment. */
4722       if ( astGetComment( this ) && *comment ) {
4723          line = astAppendString( line, &nc, " \t# " );
4724          line = astAppendString( line, &nc, comment );
4725       }
4726 
4727 /* Write out the resulting line of text. */
4728       OutputTextItem( this, line, status );
4729 
4730 /* Free the dynamic string. */
4731       line = astFree( line );
4732 
4733 /* If the value is not a default, write the Object to the Channel as
4734    well, suitably indented (this is omitted if the value is commented
4735    out). */
4736       if ( set ) {
4737          indent_inc = astGetIndent( this );
4738          current_indent += indent_inc;
4739          (void) astWrite( this, value );
4740          current_indent -= indent_inc;
4741       }
4742    }
4743 }
4744 
WriteString(AstChannel * this,const char * name,int set,int helpful,const char * value,const char * comment,int * status)4745 static void WriteString( AstChannel *this, const char *name,
4746                          int set, int helpful,
4747                          const char *value, const char *comment, int *status ) {
4748 /*
4749 *+
4750 *  Name:
4751 *     astWriteString
4752 
4753 *  Purpose:
4754 *     Write a string value to a data sink.
4755 
4756 *  Type:
4757 *     Protected virtual function.
4758 
4759 *  Synopsis:
4760 *     #include "channel.h"
4761 *     void astWriteString( AstChannel *this, const char *name,
4762 *                          int set, int helpful,
4763 *                          const char *value, const char *comment )
4764 
4765 *  Class Membership:
4766 *     Channel method.
4767 
4768 *  Description:
4769 *     This function writes a named string value, representing the
4770 *     value of a class instance variable, to the data sink associated
4771 *     with a Channel. It is intended for use by class "Dump" functions
4772 *     when writing out class information which will subsequently be
4773 *     re-read.
4774 
4775 *  Parameters:
4776 *     this
4777 *        Pointer to the Channel.
4778 *     name
4779 *        Pointer to a constant null-terminated string containing the
4780 *        name to be used to identify the value in the external
4781 *        representation. This will form the key for identifying it
4782 *        again when it is re-read. The name supplied should be unique
4783 *        within its class.
4784 *
4785 *        Mixed case may be used and will be preserved in the external
4786 *        representation (where possible) for cosmetic effect. However,
4787 *        case is not significant when re-reading values.
4788 *
4789 *        It is recommended that a maximum of 6 alphanumeric characters
4790 *        (starting with an alphabetic character) be used. This permits
4791 *        maximum flexibility in adapting to standard external data
4792 *        representations (e.g. FITS).
4793 *     set
4794 *        If this is zero, it indicates that the value being written is
4795 *        a default value (or can be re-generated from other values) so
4796 *        need not necessarily be written out. Such values will
4797 *        typically be included in the external representation with
4798 *        (e.g.) a comment character so that they are available to
4799 *        human readers but will be ignored when re-read. They may also
4800 *        be completely omitted in some circumstances.
4801 *
4802 *        If "set" is non-zero, the value will always be explicitly
4803 *        included in the external representation so that it can be
4804 *        re-read.
4805 *     helpful
4806 *        This flag provides a hint about whether a value whose "set"
4807 *        flag is zero (above) should actually appear at all in the
4808 *        external representaton.
4809 *
4810 *        If the external representation allows values to be "commented
4811 *        out" then, by default, values will be included in this form
4812 *        only if their "helpful" flag is non-zero. Otherwise, they
4813 *        will be omitted entirely. When possible, omitting the more
4814 *        obscure values associated with a class is recommended in
4815 *        order to improve readability.
4816 *
4817 *        This default behaviour may be further modified if the
4818 *        Channel's Full attribute is set - either to permit all values
4819 *        to be shown, or to suppress non-essential information
4820 *        entirely.
4821 *     value
4822 *        Pointer to a constant null-terminated string containing the
4823 *        value to be written.
4824 *     comment
4825 *        Pointer to a constant null-terminated string containing a
4826 *        textual comment to be associated with the value.
4827 *
4828 *        Note that this comment may not actually be used, depending on
4829 *        the nature of the Channel supplied and the setting of its
4830 *        Comment attribute.
4831 *-
4832 */
4833 
4834 /* Local Variables: */
4835    astDECLARE_GLOBALS            /* Declare the thread specific global data */
4836    char *line;                   /* Pointer to dynamic output string */
4837    int i;                        /* Loop counter for characters */
4838    int nc;                       /* Number of output characters */
4839    int quote;                    /* Quote character found? */
4840    int size;                     /* Size of allocated memory */
4841 
4842 /* Check the global error status. */
4843    if ( !astOK ) return;
4844 
4845 /* Get a pointer to the structure holding thread-specific global data. */
4846    astGET_GLOBALS(this);
4847 
4848 /* Use the "set" and "helpful" flags, along with the Channel's
4849    attributes to decide whether this value should actually be
4850    written. */
4851    if ( Use( this, set, helpful, status ) ) {
4852 
4853 /* Start building a dynamic string with an initial space, or a comment
4854    character if "set" is zero. Then add further spaces to suit the
4855    current indentation level. */
4856       line = astAppendString( NULL, &nc, set ? " " : "#" );
4857       for ( i = 0; i < current_indent; i++ ) {
4858          line = astAppendString( line, &nc, " " );
4859       }
4860 
4861 /* Append the name string followed by " = " and an opening quote
4862    character (the string will be quoted to protect leading and
4863    trailing spaces). */
4864       line = astAppendString( line, &nc, name );
4865       line = astAppendString( line, &nc, " = \"" );
4866 
4867 /* We now append the value string, but must inspect each character so
4868    that quotes (appearing inside quotes) can be doubled. Determine the
4869    current size of memory allocated for the dynamic string. */
4870       size = (int) astSizeOf( line );
4871 
4872 /* Loop to inspect each character and see if it is a quote. */
4873       for ( i = 0; value[ i ]; i++ ) {
4874          quote = ( value[ i ] == '"' );
4875 
4876 /* If more memory is required, extend the dynamic string (allowing for
4877    doubling of quotes and the final null) and save its new size. */
4878          if ( nc + 2 + quote > size ) {
4879             line = astGrow( line, nc + 2 + quote, sizeof( char ) );
4880             if ( astOK ) {
4881                size = (int) astSizeOf( line );
4882 
4883 /* Quit if an error occurs. */
4884             } else {
4885                break;
4886             }
4887          }
4888 
4889 /* Append the value character to the dynamic string, duplicating each
4890    quote character. */
4891          line[ nc++ ] = value[ i ];
4892          if ( quote ) line[ nc++ ] = '"';
4893       }
4894 
4895 /* Append the closing quote. */
4896       line = astAppendString( line, &nc, "\"" );
4897 
4898 /* If required, also append the comment. */
4899       if ( astGetComment( this ) && *comment ) {
4900          line = astAppendString( line, &nc, " \t# " );
4901          line = astAppendString( line, &nc, comment );
4902       }
4903 
4904 /* Write out the resulting line of text. */
4905       OutputTextItem( this, line, status );
4906 
4907 /* Free the dynamic string. */
4908       line = astFree( line );
4909    }
4910 }
4911 
4912 /* Functions which access class attributes. */
4913 /* ---------------------------------------- */
4914 /* Implement member functions to access the attributes associated with
4915    this class using the macros defined for this purpose in the
4916    "object.h" file. */
4917 
4918 /*
4919 *att++
4920 *  Name:
4921 *     SourceFile
4922 
4923 *  Purpose:
4924 *     Input file from which to read data.
4925 
4926 *  Type:
4927 *     Public attribute.
4928 
4929 *  Synopsis:
4930 *     String.
4931 
4932 *  Description:
4933 *     This attribute specifies the name of a file from which the Channel
4934 *     should read data. If specified it is used in preference to any source
4935 *     function specified when the Channel was created.
4936 *
4937 *     Assigning a new value to this attribute will cause any previously
4938 *     opened SourceFile to be closed. The first subsequent call to
4939 c     astRead
4940 f     AST_READ
4941 *     will attempt to open the new file (an error will be reported if the
4942 *     file cannot be opened), and read data from it. All subsequent call to
4943 c     astRead
4944 f     AST_READ
4945 *     will read data from the new file, until the SourceFile attribute is
4946 *     cleared or changed.
4947 *
4948 *     Clearing the attribute causes any open SourceFile to be closed. All
4949 *     subsequent data reads will use the source function specified when the
4950 *     Channel was created, or will read from standard input if no source
4951 *     function was specified.
4952 *
4953 *     If no value has been assigned to SourceFile, a null string will be
4954 *     returned if an attempt is made to get the attribute value.
4955 
4956 *  Notes:
4957 *     - Any open SourceFile is closed when the Channel is deleted.
4958 *     - If the Channel is copied or dumped
4959 c     (using astCopy or astShow)
4960 f     (using AST_COPY or AST_SHOW)
4961 *     the SourceFile attribute is left in a cleared state in the output
4962 *     Channel (i.e. the value of the SourceFile attribute is not copied).
4963 
4964 *  Applicability:
4965 *     FitsChan
4966 *        In the case of a FitsChan, the specified SourceFile supplements
4967 *        the source function specified when the FitsChan was created,
4968 *        rather than replacing the source function. The source file
4969 *        should be a text file (not a FITS file) containing one header per
4970 *        line. When a value is assigned to SourceFile, the file is opened
4971 *        and read immediately, and all headers read from the file are
4972 *        appended to the end of any header already in the FitsChan. The file
4973 *        is then closed. Clearing the SourceFile attribute has no further
4974 *        effect, other than nullifying the string (i.e. the file name)
4975 *        associated with the attribute.
4976 
4977 *att--
4978 */
4979 
4980 /* Clear the SourceFile value by closing any open file, freeing the
4981    allocated memory and assigning a NULL pointer. */
4982 astMAKE_CLEAR(Channel,SourceFile,fn_in,((this->fd_in=(this->fd_in?(fclose(this->fd_in),NULL):NULL)),astFree(this->fn_in)))
4983 
4984 /* If the SourceFile value is not set, supply a default in the form of a
4985    pointer to the constant string "". */
4986 astMAKE_GET(Channel,SourceFile,const char *,NULL,( this->fn_in ? this->fn_in : "" ))
4987 
4988 /* Set a SourceFile value by closing any open file, freeing any previously
4989    allocated memory, allocating new memory, storing the string and saving
4990    the pointer to the copy. */
4991 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 )))
4992 
4993 /* The SourceFile value is set if the pointer to it is not NULL. */
4994 astMAKE_TEST(Channel,SourceFile,( this->fn_in != NULL ))
4995 
4996 /*
4997 *att++
4998 *  Name:
4999 *     SinkFile
5000 
5001 *  Purpose:
5002 *     Output file to which to data should be written.
5003 
5004 *  Type:
5005 *     Public attribute.
5006 
5007 *  Synopsis:
5008 *     String.
5009 
5010 *  Description:
5011 *     This attribute specifies the name of a file to which the Channel
5012 *     should write data. If specified it is used in preference to any sink
5013 *     function specified when the Channel was created.
5014 *
5015 *     Assigning a new value to this attribute will cause any previously
5016 *     opened SinkFile to be closed. The first subsequent call to
5017 c     astWrite
5018 f     AST_WRITE
5019 *     will attempt to open the new file (an error will be reported if the
5020 *     file cannot be opened), and write data to it. All subsequent call to
5021 c     astWrite
5022 f     AST_WRITE
5023 *     will write data to the new file, until the SinkFile attribute is
5024 *     cleared or changed.
5025 *
5026 *     Clearing the attribute causes any open SinkFile to be closed. All
5027 *     subsequent data writes will use the sink function specified when the
5028 *     Channel was created, or will write to standard output if no sink
5029 *     function was specified.
5030 *
5031 *     If no value has been assigned to SinkFile, a null string will be
5032 *     returned if an attempt is made to get the attribute value.
5033 
5034 *  Notes:
5035 *     - A new SinkFile will over-write any existing file with the same
5036 *     name unless the existing file is write protected, in which case an
5037 *     error will be reported.
5038 *     - Any open SinkFile is closed when the Channel is deleted.
5039 *     - If the Channel is copied or dumped
5040 c     (using astCopy or astShow)
5041 f     (using AST_COPY or AST_SHOW)
5042 *     the SinkFile attribute is left in a cleared state in the output
5043 *     Channel (i.e. the value of the SinkFile attribute is not copied).
5044 
5045 *  Applicability:
5046 *     FitsChan
5047 *        When the FitsChan is destroyed, any headers in the FitsChan will be
5048 *        written out to the sink file, if one is specified (if not, the
5049 *        sink function used when the FitsChan was created is used). The
5050 *        sink file is a text file (not a FITS file) containing one header
5051 *        per line.
5052 
5053 *att--
5054 */
5055 
5056 /* Clear the SinkFile value by closing any open file, freeing the allocated
5057    memory and assigning a NULL pointer. */
5058 astMAKE_CLEAR(Channel,SinkFile,fn_out,((this->fd_out=(this->fd_out?(fclose(this->fd_out),NULL):NULL)),astFree(this->fn_out)))
5059 
5060 /* If the SinkFile value is not set, supply a default in the form of a
5061    pointer to the constant string "". */
5062 astMAKE_GET(Channel,SinkFile,const char *,NULL,( this->fn_out ? this->fn_out : "" ))
5063 
5064 /* Set a SinkFile value by closing any open file, freeing any previously
5065    allocated memory, allocating new memory, storing the string and saving
5066    the pointer to the copy. */
5067 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 )))
5068 
5069 /* The SinkFile value is set if the pointer to it is not NULL. */
5070 astMAKE_TEST(Channel,SinkFile,( this->fn_out != NULL ))
5071 
5072 
5073 /*
5074 *att++
5075 *  Name:
5076 *     Comment
5077 
5078 *  Purpose:
5079 *     Include textual comments in output?
5080 
5081 *  Type:
5082 *     Public attribute.
5083 
5084 *  Synopsis:
5085 *     Integer (boolean).
5086 
5087 *  Description:
5088 *     This is a boolean attribute which controls whether textual
5089 *     comments are to be included in the output generated by a
5090 *     Channel. If included, they will describe what each item of
5091 *     output represents.
5092 *
5093 *     If Comment is non-zero, then comments will be included. If
5094 *     it is zero, comments will be omitted.
5095 
5096 *  Applicability:
5097 *     Channel
5098 *        The default value is non-zero for a normal Channel.
5099 *     FitsChan
5100 *        The default value is non-zero for a FitsChan.
5101 *     XmlChan
5102 *        The default value is zero for an XmlChan.
5103 
5104 *att--
5105 */
5106 
5107 /* This is a boolean value (0 or 1) with a value of -INT_MAX when
5108    undefined but yielding a default of one. */
5109 astMAKE_CLEAR(Channel,Comment,comment,-INT_MAX)
5110 astMAKE_GET(Channel,Comment,int,0,( this->comment != -INT_MAX ? this->comment : 1 ))
5111 astMAKE_SET(Channel,Comment,int,comment,( value != 0 ))
5112 astMAKE_TEST(Channel,Comment,( this->comment != -INT_MAX ))
5113 
5114 /*
5115 *att++
5116 *  Name:
5117 *     Full
5118 
5119 *  Purpose:
5120 *     Set level of output detail.
5121 
5122 *  Type:
5123 *     Public attribute.
5124 
5125 *  Synopsis:
5126 *     Integer.
5127 
5128 *  Description:
5129 *     This attribute is a three-state flag and takes values of -1, 0
5130 *     or +1.  It controls the amount of information included in the
5131 *     output generated by a Channel.
5132 *
5133 *     If Full is zero, then a modest amount of
5134 *     non-essential but useful information will be included in the
5135 *     output. If Full is negative, all non-essential information will
5136 *     be suppressed to minimise the amount of output, while if it is
5137 *     positive, the output will include the maximum amount of detailed
5138 *     information about the Object being written.
5139 
5140 *  Applicability:
5141 *     Channel
5142 *        The default value is zero for a normal Channel.
5143 *     FitsChan
5144 *        The default value is zero for a FitsChan.
5145 *     XmlChan
5146 *        The default value is -1 for an XmlChan.
5147 *     StcsChan
5148 *        The default value is zero for an StcsChan. Set a positive value
5149 *        to cause default values to be included in STC-S descriptions.
5150 
5151 *  Notes:
5152 *     - All positive values supplied for this attribute are converted
5153 *     to +1 and all negative values are converted to -1.
5154 *att--
5155 */
5156 
5157 /* This ia a 3-state value (-1, 0 or +1) with a value of -INT_MAX when
5158    undefined but yielding a default of zero. */
5159 astMAKE_CLEAR(Channel,Full,full,-INT_MAX)
5160 astMAKE_GET(Channel,Full,int,0,( this->full != -INT_MAX ? this->full : 0 ))
5161 astMAKE_SET(Channel,Full,int,full,( value > 0 ? 1 : ( value < 0 ? -1 : 0 ) ))
5162 astMAKE_TEST(Channel,Full,( this->full != -INT_MAX ))
5163 
5164 /*
5165 *att++
5166 *  Name:
5167 *     Indent
5168 
5169 *  Purpose:
5170 *     Specifies the indentation to use in text produced by a Channel.
5171 
5172 *  Type:
5173 *     Public attribute.
5174 
5175 *  Synopsis:
5176 *     Integer (boolean).
5177 
5178 *  Description:
5179 *     This attribute controls the indentation within the output text produced by
5180 f     the AST_WRITE function.
5181 c     the astWrite function.
5182 *     It gives the increase in the indentation for each level in the object
5183 *     heirarchy. If it is set to zero, no indentation will be used. [3]
5184 
5185 *  Applicability:
5186 *     Channel
5187 *        The default value is zero for a basic Channel.
5188 *     FitsChan
5189 *        The FitsChan class ignores this attribute.
5190 *     StcsChan
5191 *        The default value for an StcsChan is zero, which causes the entire
5192 *        STC-S description is written out by a single invocation of the sink
5193 *        function. The text supplied to the sink function will not contain
5194 *        any linefeed characters, and each pair of adjacent words will be
5195 *        separated by a single space. The text may thus be arbitrarily large
5196 *        and the StcsLength attribute is ignored.
5197 *
5198 *        If Indent is non-zero, then the text is written out via multiple
5199 *        calls to the sink function, each call corresponding to a single
5200 *        "line" of text (although no line feed characters will be inserted
5201 *        by AST). The complete STC-S description is broken into lines so that:
5202 *
5203 *        - the line length specified by attribute StcsLength is not exceeded
5204 *        - each sub-phrase (time, space, etc.) starts on a new line
5205 *        - each argument in a compound spatial region starts on a new line
5206 *
5207 *        If this causes a sub-phrase to extend to two or more lines, then the
5208 *        second and subsequent lines will be indented by three spaces compared
5209 *        to the first line. In addition, lines within a compound spatial region
5210 *        will have extra indentation to highlight the nesting produced by the
5211 *        parentheses. Each new level of nesting will be indented by a further
5212 *        three spaces.
5213 f
5214 f        Note, the default value of zero is unlikely to be appropriate when
5215 f        an StcsChan is used within Fortran code. In this case, Indent
5216 f        should usually be set non-zero, and the StcsLength attribute set to
5217 f        the size of the CHARACTER variable used to
5218 f        receive the text returned by AST_GETLINE within the sink function.
5219 f        This avoids the possibility of long lines being truncated invisibly
5220 f        within AST_GETLINE.
5221 *     XmlChan
5222 *        The default value for an XmlChan is zero, which results in no
5223 *        linefeeds or indentation strings being added to output text.
5224 *        If any non-zero value is assigned to Indent, then extra linefeed and
5225 *        space characters will be inserted as necessary to ensure that each
5226 *        XML tag starts on a new line, and each tag will be indented by
5227 *        a further 3 spaces to show its depth in the containment hierarchy.
5228 *att--
5229 */
5230 
5231 /* This is an integer value with a value of -INT_MAX when undefined,
5232    yielding a default of 3. Sub-classes may over-ride theis default. */
5233 astMAKE_CLEAR(Channel,Indent,indent,-INT_MAX)
5234 astMAKE_GET(Channel,Indent,int,3,( this->indent != -INT_MAX ? this->indent : 3 ))
astMAKE_SET(Channel,Indent,int,indent,value)5235 astMAKE_SET(Channel,Indent,int,indent,value)
5236 astMAKE_TEST(Channel,Indent,( this->indent != -INT_MAX ))
5237 
5238 /*
5239 *att++
5240 *  Name:
5241 *     ReportLevel
5242 
5243 *  Purpose:
5244 *     Determines which read/write conditions are reported.
5245 
5246 *  Type:
5247 *     Public attribute.
5248 
5249 *  Synopsis:
5250 *     Integer (boolean).
5251 
5252 *  Description:
5253 *     This attribute determines which, if any, of the conditions that occur
5254 *     whilst reading or writing an Object should be reported. These
5255 *     conditions will generate either a fatal error or a warning, as
5256 *     determined by attribute Strict. ReportLevel can take any of the
5257 *     following values:
5258 *
5259 *     0 - Do not report any conditions.
5260 *
5261 *     1 - Report only conditions where significant information content has been
5262 *     changed. For instance, an unsupported time-scale has been replaced by a
5263 *     supported near-equivalent time-scale. Another example is if a basic
5264 *     Channel unexpected encounters data items that may have been introduced
5265 *     by later versions of AST.
5266 *
5267 *     2 - Report the above, and in addition report significant default
5268 *     values. For instance, if no time-scale was specified when reading an
5269 *     Object from an external data source, report the default time-scale
5270 *     that is being used.
5271 *
5272 *     3 - Report the above, and in addition report any other potentially
5273 *     interesting conditions that have no significant effect on the
5274 *     conversion. For instance, report if a time-scale of "TT"
5275 *     (terrestrial time) is used in place of "ET" (ephemeris time). This
5276 *     change has no signficiant effect because ET is the predecessor of,
5277 *     and is continuous with, TT. Synonyms such as "IAT" and "TAI" are
5278 *     another example.
5279 *
5280 *     The default value is 1. Note, there are many other conditions that
5281 *     can occur whilst reading or writing an Object that completely
5282 *     prevent the conversion taking place. Such conditions will always
5283 *     generate errors, irrespective of the ReportLevel and Strict attributes.
5284 
5285 *  Applicability:
5286 *     Channel
5287 *        All Channels have this attribute.
5288 *     FitsChan
5289 *        All the conditions selected by the FitsChan Warnings attribute are
5290 *        reported at level 1.
5291 *att--
5292 */
5293 
5294 /* This is an integer value with a value of -INT_MAX when undefined,
5295    yielding a default of one. */
5296 astMAKE_CLEAR(Channel,ReportLevel,report_level,-INT_MAX)
5297 astMAKE_GET(Channel,ReportLevel,int,1,( this->report_level != -INT_MAX ? this->report_level : 1 ))
5298 astMAKE_SET(Channel,ReportLevel,int,report_level,value)
5299 astMAKE_TEST(Channel,ReportLevel,( this->report_level != -INT_MAX ))
5300 
5301 /*
5302 *att++
5303 *  Name:
5304 *     Skip
5305 
5306 *  Purpose:
5307 *     Skip irrelevant data?
5308 
5309 *  Type:
5310 *     Public attribute.
5311 
5312 *  Synopsis:
5313 *     Integer (boolean).
5314 
5315 *  Description:
5316 *     This is a boolean attribute which indicates whether the Object
5317 *     data being read through a Channel are inter-mixed with other,
5318 *     irrelevant, external data.
5319 *
5320 *     If Skip is zero (the default), then the source of input data is
5321 *     expected to contain descriptions of AST Objects and comments and
5322 *     nothing else (if anything else is read, an error will
5323 *     result). If Skip is non-zero, then any non-Object data
5324 *     encountered between Objects will be ignored and simply skipped
5325 *     over in order to reach the next Object.
5326 
5327 *  Applicability:
5328 *     Channel
5329 *        All Channels have this attribute.
5330 *     FitsChan
5331 *        The FitsChan class sets the default value of this attribute
5332 *        to 1, so that all irrelevant FITS headers will normally be
5333 *        ignored.
5334 *att--
5335 */
5336 
5337 /* This ia a boolean value (0 or 1) with a value of -INT_MAX when
5338    undefined but yielding a default of zero. */
5339 astMAKE_CLEAR(Channel,Skip,skip,-INT_MAX)
5340 astMAKE_GET(Channel,Skip,int,0,( this->skip != -INT_MAX ? this->skip : 0 ))
5341 astMAKE_SET(Channel,Skip,int,skip,( value != 0 ))
5342 astMAKE_TEST(Channel,Skip,( this->skip != -INT_MAX ))
5343 
5344 /*
5345 *att++
5346 *  Name:
5347 *     Strict
5348 
5349 *  Purpose:
5350 *     Report an error if any unexpeted data items are found?
5351 
5352 *  Type:
5353 *     Public attribute.
5354 
5355 *  Synopsis:
5356 *     Integer (boolean).
5357 
5358 *  Description:
5359 *     This is a boolean attribute which indicates whether a warning
5360 *     rather than an error should be issed for insignificant conversion
5361 *     problems. If it is set non-zero, then fatal errors are issued
5362 *     instead of warnings, resulting in the
5363 c     AST error status being set.
5364 f     inherited STATUS variable being set to an error value.
5365 *     If Strict is zero (the default), then execution continues after minor
5366 *     conversion problems, and a warning message is added to the Channel
5367 *     structure. Such messages can be retrieved using the
5368 c     astWarnings
5369 f     AST_WARNINGS
5370 *     function.
5371 
5372 *  Notes:
5373 *     - This attribute was introduced in AST version 5.0. Prior to this
5374 *     version of AST unexpected data items read by a basic Channel always
5375 *     caused an error to be reported. So applications linked against
5376 *     versions of AST prior to version 5.0 may not be able to read Object
5377 *     descriptions created by later versions of AST, if the Object's class
5378 *     description has changed.
5379 
5380 *  Applicability:
5381 *     Channel
5382 *        All Channels have this attribute.
5383 *att--
5384 */
5385 
5386 /* This ia a boolean value (0 or 1) with a value of -INT_MAX when
5387    undefined but yielding a default of zero. */
5388 astMAKE_CLEAR(Channel,Strict,strict,-INT_MAX)
5389 astMAKE_GET(Channel,Strict,int,0,( this->strict != -INT_MAX ? this->strict : 0 ))
5390 astMAKE_SET(Channel,Strict,int,strict,( value != 0 ))
5391 astMAKE_TEST(Channel,Strict,( this->strict != -INT_MAX ))
5392 
5393 /* Destructor. */
5394 /* ----------- */
5395 static void Delete( AstObject *obj, int *status ) {
5396 /*
5397 *  Name:
5398 *     Delete
5399 
5400 *  Purpose:
5401 *     Destructor for Channel objects.
5402 
5403 *  Type:
5404 *     Private function.
5405 
5406 *  Synopsis:
5407 *     void Delete( AstObject *obj, int *status )
5408 
5409 *  Description:
5410 *     This function implements the destructor for Channel objects.
5411 
5412 *  Parameters:
5413 *     obj
5414 *        Pointer to the object to be deleted.
5415 *     status
5416 *        Pointer to the inherited status variable.
5417 
5418 *  Notes:
5419 *     This function attempts to execute even if the global error status is
5420 *     set.
5421 */
5422 
5423 /* Local Variables: */
5424    AstChannel *this;               /* Pointer to Channel */
5425 
5426 /* Obtain a pointer to the Channel structure. */
5427    this = (AstChannel *) obj;
5428 
5429 /* Free memory used to store warnings. */
5430    astAddWarning( this, 0, NULL, NULL, status );
5431 
5432 /* Close any open input or output files. */
5433    if( this->fd_in ) fclose( this->fd_in );
5434    if( this->fd_out ) fclose( this->fd_out );
5435 
5436 /* Free file name memory. */
5437    this->fn_in = astFree( this->fn_in );
5438    this->fn_out = astFree( this->fn_out );
5439 }
5440 
5441 /* Copy constructor. */
5442 /* ----------------- */
Copy(const AstObject * objin,AstObject * objout,int * status)5443 static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
5444 /*
5445 *  Name:
5446 *     Copy
5447 
5448 *  Purpose:
5449 *     Copy constructor for Channel objects.
5450 
5451 *  Type:
5452 *     Private function.
5453 
5454 *  Synopsis:
5455 *     void Copy( const AstObject *objin, AstObject *objout, int *status )
5456 
5457 *  Description:
5458 *     This function implements the copy constructor for Channel objects.
5459 
5460 *  Parameters:
5461 *     objin
5462 *        Pointer to the object to be copied.
5463 *     objout
5464 *        Pointer to the object being constructed.
5465 *     status
5466 *        Pointer to the inherited status variable.
5467 
5468 *  Notes:
5469 *     -  This constructor makes a deep copy.
5470 */
5471 
5472 /* Local Variables: */
5473    AstChannel *out;                /* Pointer to output Channel */
5474 
5475 /* Check the global error status. */
5476    if ( !astOK ) return;
5477 
5478 /* Obtain pointers to the input and output Channels. */
5479    out = (AstChannel *) objout;
5480 
5481 /* Just clear any references to the input memory from the output Channel. */
5482    out->warnings = NULL;
5483    out->nwarn = 0;
5484    out->fd_in = NULL;
5485    out->fn_in = NULL;
5486    out->fd_out = NULL;
5487    out->fn_out = NULL;
5488 }
5489 
5490 /* Dump function. */
5491 /* -------------- */
Dump(AstObject * this_object,AstChannel * channel,int * status)5492 static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
5493 /*
5494 *  Name:
5495 *     Dump
5496 
5497 *  Purpose:
5498 *     Dump function for Channel objects.
5499 
5500 *  Type:
5501 *     Private function.
5502 
5503 *  Synopsis:
5504 *     void Dump( AstObject *this, AstChannel *channel, int *status )
5505 
5506 *  Description:
5507 *     This function implements the Dump function which writes out data
5508 *     for the Channel class to an output Channel.
5509 
5510 *  Parameters:
5511 *     this
5512 *        Pointer to the Object whose data are being written.
5513 *     channel
5514 *        Pointer to the Channel to which the data are being written.
5515 *     status
5516 *        Pointer to the inherited status variable.
5517 */
5518 
5519 /* Local Variables: */
5520    AstChannel *this;             /* Pointer to the Channel structure */
5521    const char *comment;          /* Pointer to comment string */
5522    int ival;                     /* Integer value */
5523    int set;                      /* Attribute value set? */
5524 
5525 /* Check the global error status. */
5526    if ( !astOK ) return;
5527 
5528 /* Obtain a pointer to the Channel structure. */
5529    this = (AstChannel *) this_object;
5530 
5531 /* Write out values representing the instance variables for the
5532    Channel class.  Accompany these with appropriate comment strings,
5533    possibly depending on the values being written.*/
5534 
5535 /* In the case of attributes, we first use the appropriate (private)
5536    Test...  member function to see if they are set. If so, we then use
5537    the (private) Get... function to obtain the value to be written
5538    out.
5539 
5540    For attributes which are not set, we use the astGet... method to
5541    obtain the value instead. This will supply a default value
5542    (possibly provided by a derived class which over-rides this method)
5543    which is more useful to a human reader as it corresponds to the
5544    actual default attribute value.  Since "set" will be zero, these
5545    values are for information only and will not be read back. */
5546 
5547 /* Indent */
5548 /* ------------ */
5549    set = TestIndent( this, status );
5550    ival = set ? GetIndent( this, status ) : astGetIndent( this );
5551    astWriteInt( channel, "Indnt", set, 0, ival, "Indentation increment" );
5552 
5553 /* ReportLevel. */
5554 /* ------------ */
5555    set = TestReportLevel( this, status );
5556    ival = set ? GetReportLevel( this, status ) : astGetReportLevel( this );
5557    astWriteInt( channel, "RpLev", set, 0, ival, "Error reporting level" );
5558 
5559 /* Skip. */
5560 /* ----- */
5561    set = TestSkip( this, status );
5562    ival = set ? GetSkip( this, status ) : astGetSkip( this );
5563    astWriteInt( channel, "Skip", set, 0, ival,
5564                 ival ? "Ignore data between Objects" :
5565                        "No data allowed between Objects" );
5566 
5567 /* Strict. */
5568 /* ------- */
5569    set = TestStrict( this, status );
5570    ival = set ? GetStrict( this, status ) : astGetStrict( this );
5571    astWriteInt( channel, "Strict", set, 0, ival,
5572                 ival ? "Report errors insead of warnings" :
5573                        "Report warnings instead of errors" );
5574 
5575 /* Full. */
5576 /* ----- */
5577    set = TestFull( this, status );
5578    ival = set ? GetFull( this, status ) : astGetFull( this );
5579    if ( ival < 0 ) {
5580       comment = "Suppress non-essential output";
5581    }else if ( ival == 0 ) {
5582       comment = "Output standard information";
5583    } else {
5584       comment = "Output maximum information";
5585    }
5586    astWriteInt( channel, "Full", set, 0, ival, comment );
5587 
5588 /* Comment. */
5589 /* -------- */
5590    set = TestComment( this, status );
5591    ival = set ? GetComment( this, status ) : astGetComment( this );
5592    astWriteInt( channel, "Comm", set, 0, ival,
5593                 ival ? "Display comments" :
5594                        "Omit comments" );
5595 }
5596 
5597 /* Standard class functions. */
5598 /* ========================= */
5599 /* Implement the astIsAChannel and astCheckChannel functions using the
5600    macros defined for this purpose in the "object.h" header file. */
astMAKE_ISA(Channel,Object)5601 astMAKE_ISA(Channel,Object)
5602 astMAKE_CHECK(Channel)
5603 
5604 AstChannel *astChannel_( const char *(* source)( void ),
5605                          void (* sink)( const char * ),
5606                          const char *options, int *status, ...) {
5607 /*
5608 *+
5609 *  Name:
5610 *     astChannel
5611 
5612 *  Purpose:
5613 *     Create a Channel.
5614 
5615 *  Type:
5616 *     Protected function.
5617 
5618 *  Synopsis:
5619 *     #include "channel.h"
5620 *     AstChannel *astChannel( const char *(* source)( void ),
5621 *                             void (* sink)( const char * ),
5622 *                             const char *options, ..., int *status )
5623 
5624 *  Class Membership:
5625 *     Channel constructor.
5626 
5627 *  Description:
5628 *     This function creates a new Channel and optionally initialises
5629 *     its attributes.
5630 *
5631 *     A Channel implements low-level input/output for the AST library.
5632 *     Writing an Object to a Channel (using astWrite) will generate a
5633 *     textual representation of that Object, and reading from a
5634 *     Channel (using astRead) will create a new Object from its
5635 *     textual representation.
5636 *
5637 *     Normally, when you use a Channel, you should provide "source"
5638 *     and "sink" functions which connect it to an external data store
5639 *     by reading and writing the resulting text. By default, however,
5640 *     a Channel will read from standard input and write to standard
5641 *     output.
5642 
5643 *  Parameters:
5644 *     source
5645 *        Pointer to a "source" function that takes no arguments and
5646 *        returns a pointer to a null-terminated string.
5647 *
5648 *        This function will be used by the Channel to obtain lines of
5649 *        input text. On each invocation, it should return a pointer to
5650 *        the next input line read from some external data store, and a
5651 *        NULL pointer when there are no more lines to read.
5652 *
5653 *        If "source" is NULL, the Channel will read from standard
5654 *        input instead.
5655 *     sink
5656 *        Pointer to a "sink" function that takes a pointer to a
5657 *        null-terminated string as an argument and returns void.
5658 *
5659 *        This function will be used by the Channel to deliver lines of
5660 *        output text. On each invocation, it should deliver the
5661 *        contents of the string supplied to some external data store.
5662 *
5663 *        If "sink" is NULL, the Channel will write to standard output
5664 *        instead.
5665 *     options
5666 *        Pointer to a null-terminated string containing an optional
5667 *        comma-separated list of attribute assignments to be used for
5668 *        initialising the new Channel. The syntax used is identical to
5669 *        that for the astSet function and may include "printf" format
5670 *        specifiers identified by "%" symbols in the normal way.
5671 *     status
5672 *        Pointer to the inherited status variable.
5673 *     ...
5674 *        If the "options" string contains "%" format specifiers, then
5675 *        an optional list of additional arguments may follow it in
5676 *        order to supply values to be substituted for these
5677 *        specifiers. The rules for supplying these are identical to
5678 *        those for the astSet function (and for the C "printf"
5679 *        function).
5680 
5681 *  Returned Value:
5682 *     astChannel()
5683 *        A pointer to the new Channel.
5684 
5685 *  Notes:
5686 *     - A NULL pointer value will be returned if this function is
5687 *     invoked with the global error status set, or if it should fail
5688 *     for any reason.
5689 *-
5690 
5691 *  Implementation Notes:
5692 *     - This function implements the basic Channel constructor which
5693 *     is available via the protected interface to the Channel class.
5694 *     A public interface is provided by the astChannelId_ function.
5695 */
5696 
5697 /* Local Variables: */
5698    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
5699    AstChannel *new;              /* Pointer to new Channel */
5700    va_list args;                 /* Variable argument list */
5701 
5702 /* Get a pointer to the thread specific global data structure. */
5703    astGET_GLOBALS(NULL);
5704 
5705 /* Check the global status. */
5706    if ( !astOK ) return NULL;
5707 
5708 /* Initialise the Channel, allocating memory and initialising the
5709    virtual function table as well if necessary. Supply pointers to
5710    (local) wrapper functions that can invoke the source and sink
5711    functions with appropriate arguments for the C language. */
5712    new = astInitChannel( NULL, sizeof( AstChannel ), !class_init, &class_vtab,
5713                          "Channel", source, SourceWrap, sink, SinkWrap );
5714 
5715 /* If successful, note that the virtual function table has been
5716    initialised. */
5717    if ( astOK ) {
5718       class_init = 1;
5719 
5720 /* Obtain the variable argument list and pass it along with the
5721    options string to the astVSet method to initialise the new
5722    Channel's attributes. */
5723       va_start( args, status );
5724       astVSet( new, options, NULL, args );
5725       va_end( args );
5726 
5727 /* If an error occurred, clean up by deleting the new object. */
5728       if ( !astOK ) new = astDelete( new );
5729    }
5730 
5731 /* Return a pointer to the new Channel. */
5732    return new;
5733 }
5734 
astChannelId_(const char * (* source)(void),void (* sink)(const char *),const char * options,...)5735 AstChannel *astChannelId_( const char *(* source)( void ),
5736                            void (* sink)( const char * ),
5737                            const char *options, ... ) {
5738 /*
5739 *++
5740 *  Name:
5741 c     astChannel
5742 f     AST_CHANNEL
5743 
5744 *  Purpose:
5745 *     Create a Channel.
5746 
5747 *  Type:
5748 *     Public function.
5749 
5750 *  Synopsis:
5751 c     #include "channel.h"
5752 c     AstChannel *astChannel( const char *(* source)( void ),
5753 c                             void (* sink)( const char * ),
5754 c                             const char *options, ... )
5755 f     RESULT = AST_CHANNEL( SOURCE, SINK, OPTIONS, STATUS )
5756 
5757 *  Class Membership:
5758 *     Channel constructor.
5759 
5760 *  Description:
5761 *     This function creates a new Channel and optionally initialises
5762 *     its attributes.
5763 *
5764 *     A Channel implements low-level input/output for the AST library.
5765 c     Writing an Object to a Channel (using astWrite) will generate a
5766 f     Writing an Object to a Channel (using AST_WRITE) will generate a
5767 *     textual representation of that Object, and reading from a
5768 c     Channel (using astRead) will create a new Object from its
5769 f     Channel (using AST_READ) will create a new Object from its
5770 *     textual representation.
5771 *
5772 *     Normally, when you use a Channel, you should provide "source"
5773 c     and "sink" functions which connect it to an external data store
5774 f     and "sink" routines which connect it to an external data store
5775 *     by reading and writing the resulting text. By default, however,
5776 *     a Channel will read from standard input and write to standard
5777 *     output. Alternatively, a Channel can be told to read or write from
5778 *     specific text files using the SinkFile and SourceFile attributes,
5779 *     in which case no sink or source function need be supplied.
5780 
5781 *  Parameters:
5782 c     source
5783 f     SOURCE = SUBROUTINE (Given)
5784 c        Pointer to a source function that takes no arguments and
5785 c        returns a pointer to a null-terminated string.  If no value
5786 c        has been set for the SourceFile attribute, this function
5787 c        will be used by the Channel to obtain lines of input text. On
5788 c        each invocation, it should return a pointer to the next input
5789 c        line read from some external data store, and a NULL pointer
5790 c        when there are no more lines to read.
5791 c
5792 c        If "source" is NULL and no value has been set for the SourceFile
5793 c        attribute, the Channel will read from standard input instead.
5794 f        A source routine, which is a subroutine which takes a single
5795 f        integer error status argument.   If no value has been set
5796 f        for the SourceFile attribute, this routine will be used by
5797 f        the Channel to obtain lines of input text. On each
5798 f        invocation, it should read the next input line from some
5799 f        external data store, and then return the resulting text to
5800 f        the AST library by calling AST_PUTLINE. It should supply a
5801 f        negative line length when there are no more lines to read.
5802 f        If an error occurs, it should set its own error status
5803 f        argument to an error value before returning.
5804 f
5805 f        If the null routine AST_NULL is suppied as the SOURCE value,
5806 f        and no value has been set for the SourceFile attribute,
5807 f        the Channel will read from standard input instead.
5808 c     sink
5809 f     SINK = SUBROUTINE (Given)
5810 c        Pointer to a sink function that takes a pointer to a
5811 c        null-terminated string as an argument and returns void.
5812 c        If no value has been set for the SinkFile attribute, this
5813 c        function will be used by the Channel to deliver lines of
5814 c        output text. On each invocation, it should deliver the
5815 c        contents of the string supplied to some external data store.
5816 c
5817 c        If "sink" is NULL, and no value has been set for the SinkFile
5818 c        attribute, the Channel will write to standard output instead.
5819 f        A sink routine, which is a subroutine which takes a single
5820 f        integer error status argument.  If no value has been set
5821 f        for the SinkFile attribute, this routine will be used by
5822 f        the Channel to deliver lines of output text. On each
5823 f        invocation, it should obtain the next output line from the
5824 f        AST library by calling AST_GETLINE, and then deliver the
5825 f        resulting text to some external data store.  If an error
5826 f        occurs, it should set its own error status argument to an
5827 f        error value before returning.
5828 f
5829 f        If the null routine AST_NULL is suppied as the SINK value,
5830 f        and no value has been set for the SinkFile attribute,
5831 f        the Channel will write to standard output instead.
5832 c     options
5833 f     OPTIONS = CHARACTER * ( * ) (Given)
5834 c        Pointer to a null-terminated string containing an optional
5835 c        comma-separated list of attribute assignments to be used for
5836 c        initialising the new Channel. The syntax used is identical to
5837 c        that for the astSet function and may include "printf" format
5838 c        specifiers identified by "%" symbols in the normal way.
5839 f        A character string containing an optional comma-separated
5840 f        list of attribute assignments to be used for initialising the
5841 f        new Channel. The syntax used is identical to that for the
5842 f        AST_SET routine.
5843 c     ...
5844 c        If the "options" string contains "%" format specifiers, then
5845 c        an optional list of additional arguments may follow it in
5846 c        order to supply values to be substituted for these
5847 c        specifiers. The rules for supplying these are identical to
5848 c        those for the astSet function (and for the C "printf"
5849 c        function).
5850 f     STATUS = INTEGER (Given and Returned)
5851 f        The global status.
5852 
5853 *  Returned Value:
5854 c     astChannel()
5855 f     AST_CHANNEL = INTEGER
5856 *        A pointer to the new Channel.
5857 
5858 *  Notes:
5859 c     - Application code can pass arbitrary data (such as file
5860 c     descriptors, etc) to source and sink functions using the
5861 c     astPutChannelData function. The source or sink function should use
5862 c     the astChannelData macro to retrieve this data.
5863 f     - The names of the routines supplied for the SOURCE and SINK
5864 f     arguments should appear in EXTERNAL statements in the Fortran
5865 f     routine which invokes AST_CHANNEL. However, this is not generally
5866 f     necessary for the null routine AST_NULL (so long as the AST_PAR
5867 f     include file has been used).
5868 *     - A null Object pointer (AST__NULL) will be returned if this
5869 c     function is invoked with the AST error status set, or if it
5870 f     function is invoked with STATUS set to an error value, or if it
5871 *     should fail for any reason.
5872 f     - Note that the null routine AST_NULL (one underscore) is
5873 f     different to AST__NULL (two underscores), which is the null Object
5874 f     pointer.
5875 *--
5876 
5877 *  Implementation Notes:
5878 *     - This function implements the external (public) interface to
5879 *     the astChannel constructor function. It returns an ID value
5880 *     (instead of a true C pointer) to external users, and must be
5881 *     provided because astChannel_ has a variable argument list which
5882 *     cannot be encapsulated in a macro (where this conversion would
5883 *     otherwise occur).
5884 *     - The variable argument list also prevents this function from
5885 *     invoking astChanel_ directly, so it must be a re-implementation
5886 *     of it in all respects, except for the final conversion of the
5887 *     result to an ID value.
5888 */
5889 
5890 /* Local Variables: */
5891    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
5892    AstChannel *new;              /* Pointer to new Channel */
5893    va_list args;                 /* Variable argument list */
5894 
5895    int *status;                  /* Pointer to inherited status value */
5896 
5897 /* Get a pointer to the inherited status value. */
5898    status = astGetStatusPtr;
5899 
5900 /* Get a pointer to the thread specific global data structure. */
5901    astGET_GLOBALS(NULL);
5902 
5903 /* Check the global status. */
5904    if ( !astOK ) return NULL;
5905 
5906 /* Initialise the Channel, allocating memory and initialising the
5907    virtual function table as well if necessary. Supply pointers to
5908    (local) wrapper functions that can invoke the source and sink
5909    functions with appropriate arguments for the C language. */
5910    new = astInitChannel( NULL, sizeof( AstChannel ), !class_init, &class_vtab,
5911                          "Channel", source, SourceWrap, sink, SinkWrap );
5912 
5913 /* If successful, note that the virtual function table has been
5914    initialised. */
5915    if ( astOK ) {
5916       class_init = 1;
5917 
5918 /* Obtain the variable argument list and pass it along with the
5919    options string to the astVSet method to initialise the new
5920    Channel's attributes. */
5921       va_start( args, options );
5922       astVSet( new, options, NULL, args );
5923       va_end( args );
5924 
5925 /* If an error occurred, clean up by deleting the new object. */
5926       if ( !astOK ) new = astDelete( new );
5927    }
5928 
5929 /* Return an ID value for the new Channel. */
5930    return astMakeId( new );
5931 }
5932 
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,...)5933 AstChannel *astChannelForId_( const char *(* source)( void ),
5934                               char *(* source_wrap)( const char *(*)( void ), int * ),
5935                               void (* sink)( const char * ),
5936                               void (* sink_wrap)( void (*)( const char * ),
5937                                                   const char *, int * ),
5938                               const char *options, ... ) {
5939 /*
5940 *+
5941 *  Name:
5942 *     astChannelFor
5943 
5944 *  Purpose:
5945 *     Initialise a Channel from a foreign language interface.
5946 
5947 *  Type:
5948 *     Public function.
5949 
5950 *  Synopsis:
5951 *     #include "channel.h"
5952 *     AstChannel *astChannelFor( const char *(* source)( void ),
5953 *                                char *(* source_wrap)( const char *(*)
5954 *                                                       ( void ), int * ),
5955 *                                void (* sink)( const char * ),
5956 *                                void (* sink_wrap)( void (*)( const char * ),
5957 *                                                    const char *, int * ),
5958 *                                const char *options, ... )
5959 
5960 *  Class Membership:
5961 *     Channel constructor.
5962 
5963 *  Description:
5964 *     This function creates a new Channel from a foreign language
5965 *     interface and optionally initialises its attributes.
5966 *
5967 *     A Channel implements low-level input/output for the AST library.
5968 *     Writing an Object to a Channel (using astWrite) will generate a
5969 *     textual representation of that Object, and reading from a
5970 *     Channel (using astRead) will create a new Object from its
5971 *     textual representation.
5972 *
5973 *     Normally, when you use a Channel, you should provide "source"
5974 *     and "sink" functions which connect it to an external data store
5975 *     by reading and writing the resulting text. This function also
5976 *     requires you to provide "wrapper" functions which will invoke
5977 *     the source and sink functions. By default, however, a Channel
5978 *     will read from standard input and write to standard output.
5979 
5980 *  Parameters:
5981 *     source
5982 *        Pointer to a "source" function which will be used to obtain
5983 *        lines of input text. Generally, this will be obtained by
5984 *        casting a pointer to a source function which is compatible
5985 *        with the "source_wrap" wrapper function (below). The pointer
5986 *        should later be cast back to its original type by the
5987 *        "source_wrap" function before the function is invoked.
5988 *
5989 *        If "source" is NULL, the Channel will read from standard
5990 *        input instead.
5991 *     source_wrap
5992 *        Pointer to a function which can be used to invoke the
5993 *        "source" function supplied (above). This wrapper function is
5994 *        necessary in order to hide variations in the nature of the
5995 *        source function, such as may arise when it is supplied by a
5996 *        foreign (non-C) language interface.
5997 *
5998 *        The single parameter of the "source_wrap" function is a
5999 *        pointer to the "source" function, and it should cast this
6000 *        function pointer (as necessary) and invoke the function with
6001 *        appropriate arguments to obtain the next line of input
6002 *        text. The "source_wrap" function should then return a pointer
6003 *        to a dynamically allocated, null terminated string containing
6004 *        the text that was read. The string will be freed (using
6005 *        astFree) when no longer required and the "source_wrap"
6006 *        function need not concern itself with this. A NULL pointer
6007 *        should be returned if there is no more input to read.
6008 *
6009 *        If "source_wrap" is NULL, the Channel will read from standard
6010 *        input instead.
6011 *     sink
6012 *        Pointer to a "sink" function which will be used to deliver
6013 *        lines of output text. Generally, this will be obtained by
6014 *        casting a pointer to a sink function which is compatible with
6015 *        the "sink_wrap" wrapper function (below). The pointer should
6016 *        later be cast back to its original type by the "sink_wrap"
6017 *        function before the function is invoked.
6018 *
6019 *        If "sink" is NULL, the Channel will write to standard output
6020 *        instead.
6021 *     sink_wrap
6022 *        Pointer to a function which can be used to invoke the "sink"
6023 *        function supplied (above). This wrapper function is necessary
6024 *        in order to hide variations in the nature of the sink
6025 *        function, such as may arise when it is supplied by a foreign
6026 *        (non-C) language interface.
6027 *
6028 *        The first parameter of the "sink_wrap" function is a pointer
6029 *        to the "sink" function, and the second parameter is a pointer
6030 *        to a const, null-terminated character string containing the
6031 *        text to be written.  The "sink_wrap" function should cast the
6032 *        "sink" function pointer (as necessary) and invoke the
6033 *        function with appropriate arguments to deliver the line of
6034 *        output text. The "sink_wrap" function then returns void.
6035 *
6036 *        If "sink_wrap" is NULL, the Channel will write to standard
6037 *        output instead.
6038 *     options
6039 *        Pointer to a null-terminated string containing an optional
6040 *        comma-separated list of attribute assignments to be used for
6041 *        initialising the new Channel. The syntax used is identical to
6042 *        that for the astSet function and may include "printf" format
6043 *        specifiers identified by "%" symbols in the normal way.
6044 *     ...
6045 *        If the "options" string contains "%" format specifiers, then
6046 *        an optional list of additional arguments may follow it in
6047 *        order to supply values to be substituted for these
6048 *        specifiers. The rules for supplying these are identical to
6049 *        those for the astSet function (and for the C "printf"
6050 *        function).
6051 
6052 *  Returned Value:
6053 *     astChannelFor()
6054 *        A pointer to the new Channel.
6055 
6056 *  Notes:
6057 *     - A null Object pointer (AST__NULL) will be returned if this
6058 *     function is invoked with the global error status set, or if it
6059 *     should fail for any reason.
6060 *     - This function is only available through the public interface
6061 *     to the Channel class (not the protected interface) and is
6062 *     intended solely for use in implementing foreign language
6063 *     interfaces to this class.
6064 *-
6065 
6066 *  Implememtation Notes:
6067 *     - This function behaves exactly like astChannelId_, in that it
6068 *     returns ID values and not true C pointers, but it has two
6069 *     additional arguments. These are pointers to the "wrapper
6070 *     functions" which are needed to accommodate foreign language
6071 *     interfaces.
6072 */
6073 
6074 /* Local Variables: */
6075    astDECLARE_GLOBALS            /* Declare the thread specific global data */
6076    AstChannel *new;              /* Pointer to new Channel */
6077    va_list args;                 /* Variable argument list */
6078    int *status;                  /* Pointer to inherited status value */
6079 
6080 /* Get a pointer to the inherited status value. */
6081    status = astGetStatusPtr;
6082 
6083 /* Check the global status. */
6084    if ( !astOK ) return NULL;
6085 
6086 /* Get a pointer to the thread specific global data structure. */
6087    astGET_GLOBALS(NULL);
6088 
6089 /* Initialise the Channel, allocating memory and initialising the
6090    virtual function table as well if necessary. */
6091    new = astInitChannel( NULL, sizeof( AstChannel ), !class_init, &class_vtab,
6092                          "Channel", source, source_wrap, sink, sink_wrap );
6093 
6094 /* If successful, note that the virtual function table has been
6095    initialised. */
6096    if ( astOK ) {
6097       class_init = 1;
6098 
6099 /* Obtain the variable argument list and pass it along with the
6100    options string to the astVSet method to initialise the new
6101    Channel's attributes. */
6102       va_start( args, options );
6103       astVSet( new, options, NULL, args );
6104       va_end( args );
6105 
6106 /* If an error occurred, clean up by deleting the new object. */
6107       if ( !astOK ) new = astDelete( new );
6108    }
6109 
6110 /* Return an ID value for the new Channel. */
6111    return astMakeId( new );
6112 }
6113 
astLoadChannel_(void * mem,size_t size,AstChannelVtab * vtab,const char * name,AstChannel * channel,int * status)6114 AstChannel *astLoadChannel_( void *mem, size_t size,
6115                              AstChannelVtab *vtab, const char *name,
6116                              AstChannel *channel, int *status ) {
6117 /*
6118 *+
6119 *  Name:
6120 *     astLoadChannel
6121 
6122 *  Purpose:
6123 *     Load a Channel.
6124 
6125 *  Type:
6126 *     Protected function.
6127 
6128 *  Synopsis:
6129 *     #include "channel.h"
6130 *     AstChannel *astLoadChannel( void *mem, size_t size,
6131 *                                 AstChannelVtab *vtab, const char *name,
6132 *                                 AstChannel *channel )
6133 
6134 *  Class Membership:
6135 *     Channel loader.
6136 
6137 *  Description:
6138 *     This function is provided to load a new Channel using data read
6139 *     from a Channel. It first loads the data used by the parent class
6140 *     (which allocates memory if necessary) and then initialises a
6141 *     Channel structure in this memory, using data read from the input
6142 *     Channel.
6143 *
6144 *     If the "init" flag is set, it also initialises the contents of a
6145 *     virtual function table for a Channel at the start of the memory
6146 *     passed via the "vtab" parameter.
6147 
6148 
6149 *  Parameters:
6150 *     mem
6151 *        A pointer to the memory into which the Channel is to be
6152 *        loaded.  This must be of sufficient size to accommodate the
6153 *        Channel data (sizeof(Channel)) plus any data used by derived
6154 *        classes. If a value of NULL is given, this function will
6155 *        allocate the memory itself using the "size" parameter to
6156 *        determine its size.
6157 *     size
6158 *        The amount of memory used by the Channel (plus derived class
6159 *        data).  This will be used to allocate memory if a value of
6160 *        NULL is given for the "mem" parameter. This value is also
6161 *        stored in the Channel structure, so a valid value must be
6162 *        supplied even if not required for allocating memory.
6163 *
6164 *        If the "vtab" parameter is NULL, the "size" value is ignored
6165 *        and sizeof(AstChannel) is used instead.
6166 *     vtab
6167 *        Pointer to the start of the virtual function table to be
6168 *        associated with the new Channel. If this is NULL, a pointer
6169 *        to the (static) virtual function table for the Channel class
6170 *        is used instead.
6171 *     name
6172 *        Pointer to a constant null-terminated character string which
6173 *        contains the name of the class to which the new object
6174 *        belongs (it is this pointer value that will subsequently be
6175 *        returned by the astGetClass method).
6176 *
6177 *        If the "vtab" parameter is NULL, the "name" value is ignored
6178 *        and a pointer to the string "Channel" is used instead.
6179 
6180 *  Returned Value:
6181 *     A pointer to the new Channel.
6182 
6183 *  Notes:
6184 *     - A null pointer will be returned if this function is invoked
6185 *     with the global error status set, or if it should fail for any
6186 *     reason.
6187 *-
6188 */
6189 
6190 /* Local Variables: */
6191    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
6192    AstChannel *new;              /* Pointer to the new Channel */
6193 
6194 /* Initialise. */
6195    new = NULL;
6196 
6197 /* Check the global error status. */
6198    if ( !astOK ) return new;
6199 
6200 /* Get a pointer to the thread specific global data structure. */
6201    astGET_GLOBALS(channel);
6202 
6203 /* If a NULL virtual function table has been supplied, then this is
6204    the first loader to be invoked for this Channel. In this case the
6205    Channel belongs to this class, so supply appropriate values to be
6206    passed to the parent class loader (and its parent, etc.). */
6207    if ( !vtab ) {
6208       size = sizeof( AstChannel );
6209       vtab = &class_vtab;
6210       name = "Channel";
6211 
6212 /* If required, initialise the virtual function table for this class. */
6213       if ( !class_init ) {
6214          astInitChannelVtab( vtab, name );
6215          class_init = 1;
6216       }
6217    }
6218 
6219 /* Invoke the parent class loader to load data for all the ancestral
6220    classes of the current one, returning a pointer to the resulting
6221    partly-built Channel. */
6222    new = astLoadObject( mem, size, (AstObjectVtab *) vtab, name,
6223                         channel );
6224 
6225    if ( astOK ) {
6226 
6227 /* Read input data. */
6228 /* ================ */
6229 /* Request the input Channel to read all the input data appropriate to
6230    this class into the internal "values list". */
6231       astReadClassData( channel, "Channel" );
6232 
6233 /* Set the pointers to the source and sink functions, and their
6234    wrapper functions, to NULL (we cannot restore these since they
6235    refer to process-specific addresses). */
6236       new->source = NULL;
6237       new->source_wrap = NULL;
6238       new->sink = NULL;
6239       new->sink_wrap = NULL;
6240 
6241 /* We do not have any data to pass to the source and sink functions. */
6242       new->data = NULL;
6243 
6244 /* No warnings yet. */
6245       new->warnings = NULL;
6246       new->nwarn = 0;
6247 
6248 /* Indicate no input or output files have been associated with the
6249    Channel. */
6250       new->fd_in = NULL;
6251       new->fn_in = NULL;
6252       new->fd_out = NULL;
6253       new->fn_out = NULL;
6254 
6255 /* Now read each individual data item from this list and use it to
6256    initialise the appropriate instance variable(s) for this class. */
6257 
6258 /* In the case of attributes, we first read the "raw" input value,
6259    supplying the "unset" value as the default. If a "set" value is
6260    obtained, we then use the appropriate (private) Set... member
6261    function to validate and set the value properly. */
6262 
6263 /* Indent. */
6264 /* ------- */
6265       new->indent = astReadInt( channel, "indnt", -INT_MAX );
6266       if ( TestIndent( new, status ) ) SetIndent( new, new->indent, status );
6267 
6268 /* ReportLevel. */
6269 /* ------------ */
6270       new->report_level = astReadInt( channel, "rplev", -INT_MAX );
6271       if ( TestReportLevel( new, status ) ) SetReportLevel( new,
6272                                                             new->report_level,
6273                                                             status );
6274 
6275 /* Skip. */
6276 /* ----- */
6277       new->skip = astReadInt( channel, "skip", -INT_MAX );
6278       if ( TestSkip( new, status ) ) SetSkip( new, new->skip, status );
6279 
6280 /* Strict. */
6281 /* ------- */
6282       new->strict = astReadInt( channel, "strict", -INT_MAX );
6283       if ( TestStrict( new, status ) ) SetStrict( new, new->strict, status );
6284 
6285 /* Full. */
6286 /* ----- */
6287       new->full = astReadInt( channel, "full", -INT_MAX );
6288       if ( TestFull( new, status ) ) SetFull( new, new->full, status );
6289 
6290 /* Comment. */
6291 /* -------- */
6292       new->comment = astReadInt( channel, "comm", -INT_MAX );
6293       if ( TestComment( new, status ) ) SetComment( new, new->comment, status );
6294 
6295 /* If an error occurred, clean up by deleting the new Channel. */
6296       if ( !astOK ) new = astDelete( new );
6297    }
6298 
6299 /* Return the new Channel pointer. */
6300    return new;
6301 }
6302 
6303 /* Virtual function interfaces. */
6304 /* ============================ */
6305 /* These provide the external interface to the virtual functions
6306    defined by this class. Each simply checks the global error status
6307    and then locates and executes the appropriate member function,
6308    using the function pointer stored in the object's virtual function
6309    table (this pointer is located using the astMEMBER macro defined in
6310    "object.h").
6311 
6312    Note that the member function may not be the one defined here, as
6313    it may have been over-ridden by a derived class. However, it should
6314    still have the same interface. */
astGetNextData_(AstChannel * this,int begin,char ** name,char ** val,int * status)6315 void astGetNextData_( AstChannel *this, int begin, char **name, char **val, int *status ) {
6316    *name = NULL;
6317    *val = NULL;
6318    if ( !astOK ) return;
6319    (**astMEMBER(this,Channel,GetNextData))( this, begin, name, val, status );
6320 }
astGetNextText_(AstChannel * this,int * status)6321 char *astGetNextText_( AstChannel *this, int *status ) {
6322    if ( !astOK ) return NULL;
6323    return (**astMEMBER(this,Channel,GetNextText))( this, status );
6324 }
astPutNextText_(AstChannel * this,const char * line,int * status)6325 void astPutNextText_( AstChannel *this, const char *line, int *status ) {
6326    if ( !astOK ) return;
6327    (**astMEMBER(this,Channel,PutNextText))( this, line, status );
6328 }
astRead_(AstChannel * this,int * status)6329 AstObject *astRead_( AstChannel *this, int *status ) {
6330    if ( !astOK ) return NULL;
6331    astAddWarning( this, 0, NULL, NULL, status );
6332    return (**astMEMBER(this,Channel,Read))( this, status );
6333 }
astReadClassData_(AstChannel * this,const char * class,int * status)6334 void astReadClassData_( AstChannel *this, const char *class, int *status ) {
6335    if ( !astOK ) return;
6336    (**astMEMBER(this,Channel,ReadClassData))( this, class, status );
6337 }
astReadDouble_(AstChannel * this,const char * name,double def,int * status)6338 double astReadDouble_( AstChannel *this, const char *name, double def, int *status ) {
6339    if ( !astOK ) return 0.0;
6340    return (**astMEMBER(this,Channel,ReadDouble))( this, name, def, status );
6341 }
astReadInt_(AstChannel * this,const char * name,int def,int * status)6342 int astReadInt_( AstChannel *this, const char *name, int def, int *status ) {
6343    if ( !astOK ) return 0;
6344    return (**astMEMBER(this,Channel,ReadInt))( this, name, def, status );
6345 }
astReadObject_(AstChannel * this,const char * name,AstObject * def,int * status)6346 AstObject *astReadObject_( AstChannel *this, const char *name,
6347                            AstObject *def, int *status ) {
6348    if ( !astOK ) return NULL;
6349    return (**astMEMBER(this,Channel,ReadObject))( this, name, def, status );
6350 }
astReadString_(AstChannel * this,const char * name,const char * def,int * status)6351 char *astReadString_( AstChannel *this, const char *name, const char *def, int *status ) {
6352    if ( !astOK ) return NULL;
6353    return (**astMEMBER(this,Channel,ReadString))( this, name, def, status );
6354 }
astWriteBegin_(AstChannel * this,const char * class,const char * comment,int * status)6355 void astWriteBegin_( AstChannel *this, const char *class,
6356                      const char *comment, int *status ) {
6357    if ( !astOK ) return;
6358    (**astMEMBER(this,Channel,WriteBegin))( this, class, comment, status );
6359 }
astWriteDouble_(AstChannel * this,const char * name,int set,int helpful,double value,const char * comment,int * status)6360 void astWriteDouble_( AstChannel *this, const char *name, int set, int helpful,
6361                       double value, const char *comment, int *status ) {
6362    if ( !astOK ) return;
6363    (**astMEMBER(this,Channel,WriteDouble))( this, name, set, helpful, value,
6364                                             comment, status );
6365 }
astWriteEnd_(AstChannel * this,const char * class,int * status)6366 void astWriteEnd_( AstChannel *this, const char *class, int *status ) {
6367    if ( !astOK ) return;
6368    (**astMEMBER(this,Channel,WriteEnd))( this, class, status );
6369 }
astWriteInt_(AstChannel * this,const char * name,int set,int helpful,int value,const char * comment,int * status)6370 void astWriteInt_( AstChannel *this, const char *name, int set, int helpful,
6371                    int value, const char *comment, int *status ) {
6372    if ( !astOK ) return;
6373    (**astMEMBER(this,Channel,WriteInt))( this, name, set, helpful, value,
6374                                          comment, status );
6375 }
astWriteIsA_(AstChannel * this,const char * class,const char * comment,int * status)6376 void astWriteIsA_( AstChannel *this, const char *class, const char *comment, int *status ) {
6377    if ( !astOK ) return;
6378    (**astMEMBER(this,Channel,WriteIsA))( this, class, comment, status );
6379 }
astWriteString_(AstChannel * this,const char * name,int set,int helpful,const char * value,const char * comment,int * status)6380 void astWriteString_( AstChannel *this, const char *name, int set, int helpful,
6381                       const char *value, const char *comment, int *status ) {
6382    if ( !astOK ) return;
6383    (**astMEMBER(this,Channel,WriteString))( this, name, set, helpful, value,
6384                                             comment, status );
6385 }
astPutChannelData_(AstChannel * this,void * data,int * status)6386 void astPutChannelData_( AstChannel *this, void *data, int *status ) {
6387    if ( !astOK ) return;
6388    (**astMEMBER(this,Channel,PutChannelData))( this, data, status );
6389 }
6390 
astWarnings_(AstChannel * this,int * status)6391 AstKeyMap *astWarnings_( AstChannel *this, int *status ){
6392    if( !astOK ) return NULL;
6393    return (**astMEMBER(this,Channel,Warnings))( this, status );
6394 }
6395 
6396 /* Because of the variable argument list, we need to work a bit harder on
6397    astAddWarning. Functions that provide implementations of the
6398    astAddWarning method recieve the fully expanded message and so do not
6399    need a variable argument list. */
6400 
astAddWarning_(void * this_void,int level,const char * fmt,const char * method,int * status,...)6401 void astAddWarning_( void *this_void, int level, const char *fmt,
6402                      const char *method, int *status, ... ) {
6403    AstChannel *this;
6404    char buff[ 201 ];
6405    va_list args;
6406    int nc;
6407 
6408    this = astCheckChannel( this_void );
6409 
6410    if( fmt ) {
6411       if( astOK ) {
6412          va_start( args, status );
6413          nc = vsprintf( buff, fmt, args );
6414          va_end( args );
6415          if( nc > 200 ) {
6416             astError( AST__INTER, "astAddWarning(%s): Message buffer size "
6417                       "exceeded (internal AST programming error).",
6418                       status, astGetClass( this ) );
6419          } else {
6420             (**astMEMBER(this,Channel,AddWarning))( this, level, buff, method, status );
6421          }
6422       }
6423    } else {
6424       (**astMEMBER(this,Channel,AddWarning))( this, level, NULL, method, status );
6425    }
6426 }
6427 
6428 /* Count the number of times astWrite is invoked (excluding invocations
6429    made from within the astWriteObject method - see below). The count is
6430    done here so that invocations of astWrite within a sub-class will be
6431    included. */
astWrite_(AstChannel * this,AstObject * object,int * status)6432 int astWrite_( AstChannel *this, AstObject *object, int *status ) {
6433    astDECLARE_GLOBALS
6434    if ( !astOK ) return 0;
6435    astGET_GLOBALS(this);
6436    nwrite_invoc++;
6437    astAddWarning( this, 0, NULL, NULL, status );
6438    return (**astMEMBER(this,Channel,Write))( this, object, status );
6439 }
6440 
6441 /* We do not want to count invocations of astWrite made from within the
6442    astWriteObject method. So decrement the number of invocations first
6443    (this assumes that each invocation of astWriteObject will only invoke
6444    astWrite once). */
astWriteObject_(AstChannel * this,const char * name,int set,int helpful,AstObject * value,const char * comment,int * status)6445 void astWriteObject_( AstChannel *this, const char *name, int set,
6446                       int helpful, AstObject *value, const char *comment, int *status ) {
6447    astDECLARE_GLOBALS
6448    if ( !astOK ) return;
6449    astGET_GLOBALS(this);
6450    nwrite_invoc--;
6451    (**astMEMBER(this,Channel,WriteObject))( this, name, set, helpful, value,
6452                                             comment, status );
6453 }
6454 
6455 
6456 
6457 
6458 
6459