1 /*
2 * Name:
3 * memory.c
4
5 * Purpose:
6 * Implement memory allocation/deallocation functions.
7
8 * Description:
9 * This file implements the Memory module which is used for
10 * allocating and freeing memory in the AST library. For a
11 * description of the module and its interface, see the .h file of
12 * the same name.
13
14 * Note, it is assumed that malloc, free and realloc are thread-safe.
15
16 * Copyright:
17 * Copyright (C) 1997-2006 Council for the Central Laboratory of the
18 * Research Councils
19 * Copyright (C) 2009-2010 Science & Technology Facilities Council.
20 * All Rights Reserved.
21
22 * Licence:
23 * This program is free software: you can redistribute it and/or
24 * modify it under the terms of the GNU Lesser General Public
25 * License as published by the Free Software Foundation, either
26 * version 3 of the License, or (at your option) any later
27 * version.
28 *
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU Lesser General Public License for more details.
33 *
34 * You should have received a copy of the GNU Lesser General
35 * License along with this program. If not, see
36 * <http://www.gnu.org/licenses/>.
37
38 * Authors:
39 * RFWS: R.F. Warren-Smith (Starlink)
40 * DSB: D.S. Berry (Starlink)
41
42 * History:
43 * 2-JAN-1996 (RFWS):
44 * Original version.
45 * 26-JAN-1996 (RFWS):
46 * Removed trailing underscores from static functions and
47 * changed to use new error function interfaces.
48 * 20-JUN-1996 (RFWS):
49 * Added astString.
50 * 15-JUL-1996 (RFWS):
51 * Make IsDynamic execute under error conditions to avoid memory
52 * leaks in such situations.
53 * 11-SEP-1996 (RFWS):
54 * Added astStringArray (original written by DSB).
55 * 18-MAR-1998 (RFWS):
56 * Added notes about these functions being available for writing
57 * foreign language and graphics interfaces, etc.
58 * 29-JAN-2002 (DSB):
59 * Added astChrLen and astSscanf.
60 * 15-FEB-2002 (DSB):
61 * Removed use of non-ANSI vsscanf from astSscanf.
62 * 15-NOV-2002 (DSB):
63 * Moved ChrMatch from SkyFrame (etc) to here. Included stdio.h and
64 * ctype.h.
65 * 10-FEB-2003 (DSB):
66 * Added facilities for detecting and tracing memory leaks. These
67 * are only included if AST is compiled with the -DDEBUG flag.
68 * 3-MAR-2004 (DSB):
69 * Modified astSscanf to avoid use of uninitialised values
70 * corresponding to "%n" fields in the format string.
71 * 26-JAN-2004 (DSB):
72 * Modified astRealloc to clarify the nature of the returned pointer
73 * (which is not a "Memory *"). Also correct issuing and deissuing
74 * of pointers in DEBUG code within astRealloc.
75 * 16-FEB-2006 (DSB):
76 * Convert Magic from a function to a macro for extra speed.
77 * 21-FEB-2006 (DSB):
78 * Convert IsDynamic from a function to a macro for extra speed.
79 * 23-FEB-2006 (DSB):
80 * Added the caching system for allocated but unused memory blocks,
81 * controlled by AST tuning parameter MemoryCaching.
82 * 2-MAR-2006 (DSB):
83 * Added astFlushMemory, and renamed the memory debugging functions.
84 * These are now conditionally compiled if the MEM_DEBUG macros is
85 * defined (set by configuring AST with the --with-memdebug option).
86 * Also modified them to take into account MemoryCaching.
87 * 24-MAY-2006 (DSB):
88 * Ensure that pointers to memory returned by this module are all
89 * aligned on 8 byte boundaries. This fixes problems with ualigned
90 * memory access that could cause bus errors on Solaris.
91 * 26-MAY-2006 (DSB):
92 * Cast (void *) pointers to (char *) before doing arithmetic on
93 * them (changes supplied by Micah Johnson).
94 * 4-DEC-2006 (DSB):
95 * Fix bug in astMalloc that caused a non-null pointer to be
96 * returned on error.
97 * 4-JAN-2007 (DSB):
98 * Move definition of astCLASS macro so that it comes before the
99 * inclusion of the AST include files (which test for astCLASS).
100 * 27-JUN-2007 (DSB):
101 * Added astIsDynamic.
102 * 24-OCT-2007 (DSB):
103 * Zero the size of memory blocks stored in the Cache so that an
104 * error will be reported if an attempt is made to free a memory
105 * block that has already been freed.
106 * 25-OCT-2007 (DSB):
107 * Added astRemoveLeadingBlanks.
108 * 28-FEB-2008 (DSB):
109 * Added astChrSub.
110 * 17-MAR-2008 (DSB):
111 * Added "{nnn}" quantifier to astChrSub.
112 * 27-MAR-2008 (DSB):
113 * Added astChrSplitRE, and re-structured regexp functions.
114 * 18-NOV-2008 (DSB):
115 * In astFlushMemory, do not release permanent memory blocks as
116 * they may still be needed.
117 * 9-FEB-2009 (DSB):
118 * Added astChr2Double.
119 * 25-JUN-2009 (DSB):
120 * Fix handling of escape characters in astSplitC.
121 * 19-MAY-2010 (DSB):
122 * - Added astStringCase.
123 * - Changed access from protected to public for commonly used
124 * functions.
125 * 26-MAY-2010 (DSB):
126 * Added astCalloc.
127 * 18-AUG-2010 (DSB):
128 * Added astMemoryStats
129 * 19-AUG-2010 (DSB):
130 * Added astMemoryWarning
131 * 8-OCT-2010 (DSB):
132 * Modify memory allocation to use "calloc" directly, rather than
133 * using "malloc+memset".
134 * 12-APR-2011 (DSB):
135 * Fix regular expression problem where a ".*" template field failed to
136 * match a null string if it occurred before a closing parenthesis at
137 * the end of the template.
138 * 26-MAY-2011 (DSB):
139 * - Changed API for astCalloc to match RTL (i.e. remove "init").
140 * - Changed astChr2Double to check for strigs like "2.", which
141 * some sscanfs fail to read as a floating point value.
142 * 27-MAY-2011 (DSB):
143 * Added astFreeDouble to free a dynamically allocated array of
144 * pointers to other dynamically allocated arrays.
145 * 21-JUN-2011 (DSB):
146 * Added astCheckMemory - an alternative to astFlushMemory that does
147 * not free any memory.
148 * 21-NOV-2011 (DSB):
149 * Correct matchend value returned by astChrSplitRE.
150 * 6-JAN-2014 (DSB):
151 * Optimise access to cache to avoid valgrind warnings.
152 * 16-JAN-2014 (DSB):
153 * Dump details of all active memory blocks if the total memory allocation
154 * specified by astMemoryWarning is exceeded.
155 */
156
157 /* Configuration results. */
158 /* ---------------------- */
159 #if HAVE_CONFIG_H
160 #include <config.h>
161 #endif
162
163 /* Module Macros. */
164 /* ============== */
165 /* Define the astCLASS macro (even although this is not a class
166 implementation) to obtain access to the protected error handling
167 functions. */
168 #define astCLASS memory
169
170 /* The maximum number of fields within a format string allowed by astSscanf. */
171 #define VMAXFLD 20
172
173 /* The maximum number of nested astBeginPM/astEndPM contexts. */
174 #define PM_STACK_MAXSIZE 20
175
176 /* Select the appropriate memory management functions. These will be the
177 system's malloc, calloc, free and realloc unless AST was configured with
178 the "--with-starmem" option, in which case they will be the starmem
179 malloc, calloc, free and realloc. */
180 #ifdef HAVE_STAR_MEM_H
181 # include <star/mem.h>
182 # define MALLOC starMalloc
183 # define CALLOC starCalloc
184 # define FREE starFree
185 # define REALLOC starRealloc
186 #else
187 # define MALLOC malloc
188 # define CALLOC calloc
189 # define FREE free
190 # define REALLOC realloc
191 #endif
192
193
194 #ifdef MEM_DEBUG
195 #define ISSUED "issued"
196 #define FREED "freed"
197 #endif
198
199 /* Include files. */
200 /* ============== */
201 /* Interface definitions. */
202 /* ---------------------- */
203 #include "error.h" /* Error reporting facilities */
204 #include "globals.h" /* Thread-specific global data */
205 #include "memory.h" /* Interface to this module */
206 #include "pointset.h" /* For AST__BAD */
207
208 #ifdef MEM_DEBUG
209 #include "object.h" /* For astMakePointer */
210 #endif
211
212 /* Error code definitions. */
213 /* ----------------------- */
214 #include "ast_err.h" /* AST error codes */
215
216 /* C header files. */
217 /* --------------- */
218 #include <ctype.h>
219 #include <errno.h>
220 #include <string.h>
221 #include <stdlib.h>
222 #include <stdarg.h>
223 #include <stdio.h>
224 #include <limits.h>
225
226 #ifdef THREAD_SAFE
227 #include <pthread.h>
228 #endif
229
230 #ifdef MEM_PROFILE
231 #include <sys/times.h>
232 #endif
233
234 /* Function Macros. */
235 /* =============== */
236 /* These are defined as macros rather than functions to avoid the
237 overhead of a function call since they are called extremely frequently. */
238
239 /*
240 * Name:
241 * IS_DYNAMIC
242
243 * Purpose:
244 * Test whether a memory region has been dynamically allocated.
245
246 * Type:
247 * Private macro
248
249 * Synopsis:
250 * #include "memory.h"
251 * IS_DYNAMIC( ptr, dynamic )
252
253 * Description:
254 * This macro takes a pointer to a region of memory and tests if
255 * the memory has previously been dynamically allocated using other
256 * functions from this module. It does this by checking for the
257 * presence of a "magic" number in the header which precedes the
258 * allocated memory. If the magic number is not present (or the
259 * pointer is invalid for any other reason), an error is reported
260 * and the global error status is set.
261 *
262 * The result of the test is written to the variable specified by "res".
263
264 * Parameters:
265 * ptr
266 * Pointer to the start (as known to the external user) of the
267 * dynamically allocated memory.
268 * dynamic
269 * Name of an "int" variable to recieve the result of the test.
270 * If the memory was allocated dynamically, a value of 1 is
271 * stored in this variable. Otherwise, zero is stored and an error
272 * results.
273
274 * Notes:
275 * - A NULL pointer value produces an error report from this
276 * function, although other functions may wish to regard a NULL
277 * pointer as valid.
278 * - This function attempts to execute even if the global error
279 * status is set, although no further error report will be made if
280 * the memory is not dynamically allocated under these
281 * circumstances.
282 * - The test performed by this function is not 100% secure as the
283 * "magic" value could occur by accident (although this is
284 * unlikely). It is mainly intended to provide security against
285 * programming errors, including accidental corruption of the
286 * memory header and attempts to allocate the same region of memory
287 * more than once.
288 */
289
290 #define IS_DYNAMIC(ptr,dynamic) \
291 \
292 /* Initialise. */ \
293 dynamic = 0; \
294 \
295 /* Check that a NULL pointer has not been supplied and report an error \
296 if it has (but not if the global status is already set). */ \
297 if ( !ptr ) { \
298 if ( astOK ) { \
299 astError( AST__PTRIN, "Invalid NULL pointer (address %p).", status, ptr ); \
300 } \
301 \
302 /* If OK, derive a pointer to the memory header that precedes the \
303 allocated region of memory. */ \
304 } else { \
305 Memory *isdynmem; /* Pointer to memory header */ \
306 isdynmem = (Memory *) ( (char *) ptr - SIZEOF_MEMORY ); \
307 \
308 /* Check if the "magic number" in the header is valid and report an \
309 error if it is not (but not if the global status is already \
310 set). */ \
311 if ( isdynmem->magic != MAGIC( isdynmem, isdynmem->size ) ) { \
312 if ( astOK ) { \
313 astError( AST__PTRIN, \
314 "Invalid pointer or corrupted memory at address %p.", status, \
315 ptr ); \
316 } \
317 \
318 /* Note if the magic number is OK. */ \
319 } else { \
320 dynamic = 1; \
321 } \
322 }
323
324
325
326 /*
327 * Name:
328 * MAGIC
329
330 * Purpose:
331 * Generate a "magic number".
332
333 * Type:
334 * Private macro.
335
336 * Synopsis:
337 * #include "memory.h"
338 * unsigned long MAGIC( void *ptr, size_t size )
339
340 * Description:
341 * This macro generates a "magic number" which is a function of
342 * a memory address and an object size. This number may be stored
343 * in a region of dynamically allocated memory to allow it to be
344 * recognised as dynamically allocated by other routines, and also
345 * to provide security against memory leaks, etc.
346
347 * Parameters:
348 * ptr
349 * The memory pointer.
350 * size
351 * The object size.
352
353 * Returned Value:
354 * The function returns the magic number.
355
356 * Notes:
357 * This function does not perform error checking.
358 */
359
360 /* Form the bit-wise exclusive OR between the memory address and the
361 object size, then add 1 and invert the bits. Return the result as
362 an unsigned long integer. */
363 #define MAGIC(ptr,size) \
364 ( ~( ( ( (unsigned long) ptr ) ^ ( (unsigned long) size ) ) + \
365 ( (unsigned long) 1 ) ) )
366
367 /* A macro that returns the size of the a Memory structure padded to a
368 multiple of 8 bytes. */
369 #define SIZEOF_MEMORY \
370 ( ( sizeof_memory != 0 ) ? sizeof_memory : SizeOfMemory( status ) )
371
372
373 /* Type Definitions. */
374 /* ================= */
375
376 #ifdef MEM_PROFILE
377
378 /* Structure used to record the time spent between matching calls to
379 astStartTimer and astStopTimer. */
380 typedef struct AstTimer {
381 int id; /* Unique integer identifier for timer */
382 clock_t e0; /* Absolute elapsed time at timer start */
383 clock_t u0; /* Absolute user time at timer start */
384 clock_t s0; /* Absolute system time at timer start */
385 clock_t et; /* Cumulative elapsed time within timer */
386 clock_t ut; /* Cumulative user time within timer */
387 clock_t st; /* Cumulative system time within timer */
388 int nentry; /* Number of entries into the timer */
389 const char *name; /* An identifying label for the timer */
390 const char *file; /* Name of source file where timer was started */
391 int line; /* Source file line no. where timer was started */
392 struct AstTimer *parent; /* The parent enclosing timer */
393 int nchild; /* Number of child timers */
394 struct AstTimer **children;/* Timers that count time within this timer */
395 } AstTimer;
396
397 #endif
398
399 /* Module Variables. */
400 /* ================= */
401
402 /* Extra stuff for profiling (can only be used in single threaded
403 environments). */
404 #ifdef MEM_PROFILE
405 static AstTimer *Current_Timer = NULL;
406 static int Enable_Timers = 0;
407 static int Timer_Count = 0;
408 #endif
409
410 /* Extra stuff for debugging of memory management (tracking of leaks
411 etc). */
412 #ifdef MEM_DEBUG
413
414 /* The identifier for the memory block which is to be tracked. */
415 static int Watched_ID = -1;
416
417 /* The next integer to use to identify an active memory block pointer. */
418 static int Next_ID = -1;
419
420 /* Indicates if future memory allocations are permanent (i.e. will not
421 usually be freed explicitly by AST). */
422 static int Perm_Mem = 0;
423
424 /* A "first in, last out" stack of Perm_Mem values used by nested
425 astBeginPM/astEndPM contexts. */
426 static int PM_Stack[ PM_STACK_MAXSIZE ];
427
428 /* The number of values currently in the PM_Stack array. */
429 static int PM_Stack_Size = 0;
430
431 /* A pointer to a double linked list holding pointers to currently active
432 memory blocks (i.e. memory blocks for which a pointer has been issued
433 but not yet freed). This does not include the memory blocks in the
434 Cache array (these are not considered to be active). */
435 static Memory *Active_List = NULL;
436
437 /* Should a new ID be issued each time a cached memory block is returned
438 by astMalloc? Otherwise, the same ID value is used throughout the
439 life of a memory block. */
440 static int Keep_ID = 0;
441
442 /* Suppress all memory use reports except for issuing and freeing? */
443 static int Quiet_Use = 0;
444
445 /* Report the ID of every cached block when the cache is emptied? */
446 static int List_Cache = 0;
447
448 /* Memory allocation at which to issue a warning. */
449 static size_t Warn_Usage = 0;
450
451 /* Current memory allocated by AST. */
452 static size_t Current_Usage = 0;
453
454 /* Peak memory allocated by AST. */
455 static size_t Peak_Usage = 0;
456
457 #ifdef THREAD_SAFE
458 static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
459 #define LOCK_DEBUG_MUTEX pthread_mutex_lock( &mutex2 );
460 #define UNLOCK_DEBUG_MUTEX pthread_mutex_unlock( &mutex2 );
461 #else
462 #define LOCK_DEBUG_MUTEX
463 #define UNLOCK_DEBUG_MUTEX
464 #endif
465
466 #endif
467
468 /* Define macros for accessing all items of thread-safe global data
469 used by this module. */
470 #ifdef THREAD_SAFE
471
472 #define sizeof_memory astGLOBAL(Memory,Sizeof_Memory)
473 #define cache astGLOBAL(Memory,Cache)
474 #define cache_init astGLOBAL(Memory,Cache_Init)
475 #define use_cache astGLOBAL(Memory,Use_Cache)
476
477 /* Define the initial values for the global data for this module. */
478 #define GLOBAL_inits \
479 globals->Sizeof_Memory = 0; \
480 globals->Cache_Init = 0; \
481 globals->Use_Cache = 0; \
482
483 /* Create the global initialisation function. */
484 astMAKE_INITGLOBALS(Memory)
485
486 /* If thread safety is not needed, declare globals at static variables. */
487 /* -------------------------------------------------------------------- */
488 #else
489
490 /* The size of a Memory header structure, padded to a multiple of 8
491 bytes. This value is initialised by the SizeOfMemory function, and
492 should be accessed using the SIZEOF_MEMORY macro. */
493 static size_t sizeof_memory = 0;
494
495 /* A cache of allocated but currently unused memory block. This cache is
496 maintained in order to avoid the overhead of continual calls to malloc to
497 allocate small blocks of memory. The vast majority of memory blocks
498 allocated by AST are under 200 bytes in size. Each element in this array
499 stores a pointer to the header for a free (i.e. allocated but currently
500 unused) memory block. The size of the memory block (not including the
501 Memory header) will equal the index at which the pointer is stored within
502 "cache". Each free memory block contains (in its Memory header) a pointer
503 to the header for another free memory block of the same size (or a NULL
504 pointer if there are no other free memory blocks of the same size). */
505 static Memory *cache[ MXCSIZE + 1 ];
506
507 /* Has the "cache" array been initialised? */
508 static int cache_init = 0;
509
510 /* Should the cache be used? */
511 static int use_cache = 0;
512
513 #endif
514
515 /* Prototypes for Private Functions. */
516 /* ================================= */
517 static size_t SizeOfMemory( int * );
518 static char *CheckTempStart( const char *, const char *, const char *, char *, int *, int *, int *, int *, int *, int *, int *, int * );
519 static char *ChrMatcher( const char *, const char *, const char *, const char *, const char *[], int, int, int, char ***, int *, const char **, int * );
520 static char *ChrSuber( const char *, const char *, const char *[], int, int, char ***, int *, const char **, int * );
521
522 #ifdef MEM_DEBUG
523 static void Issue( Memory *, int * );
524 static void DeIssue( Memory *, int * );
525 #endif
526
527 #ifdef MEM_PROFILE
528 static AstTimer *ReportTimer( AstTimer *, int, AstTimer **, int *, int * );
529 static int CompareTimers( const void *, const void * );
530 static int CompareTimers2( const void *, const void * );
531 #endif
532
533 /* Function implementations. */
534 /* ========================= */
astAppendString_(char * str1,int * nc,const char * str2,int * status)535 char *astAppendString_( char *str1, int *nc, const char *str2, int *status ) {
536 /*
537 *++
538 * Name:
539 * astAppendString
540
541 * Purpose:
542 * Append a string to another string which grows dynamically.
543
544 * Type:
545 * Public function.
546
547 * Synopsis:
548 * #include "memory.h"
549 * char *astAppendString( char *str1, int *nc, const char *str2 )
550
551 * Description:
552 * This function appends one string to another dynamically
553 * allocated string, extending the dynamic string as necessary to
554 * accommodate the new characters (plus the final null).
555
556 * Parameters:
557 * str1
558 * Pointer to the null-terminated dynamic string, whose memory
559 * has been allocated using an AST memory allocation function.
560 * If no space has yet been allocated for this string, a NULL
561 * pointer may be given and fresh space will be allocated by this
562 * function.
563 * nc
564 * Pointer to an integer containing the number of characters in
565 * the dynamic string (excluding the final null). This is used
566 * to save repeated searching of this string to determine its
567 * length and it defines the point where the new string will be
568 * appended. Its value is updated by this function to include
569 * the extra characters appended.
570 *
571 * If "str1" is NULL, the initial value supplied for "*nc" will
572 * be ignored and zero will be used.
573 * str2
574 * Pointer to a constant null-terminated string, a copy of which
575 * is to be appended to "str1".
576
577 * Returned Value:
578 * astAppendString()
579 * A possibly new pointer to the dynamic string with the new string
580 * appended (its location in memory may have to change if it has to
581 * be extended, in which case the original memory is automatically
582 * freed by this function). When the string is no longer required,
583 * its memory should be freed using astFree.
584
585 * Notes:
586 * - If this function is invoked with the global error status set
587 * or if it should fail for any reason, then the returned pointer
588 * will be equal to "str1" and the dynamic string contents will be
589 * unchanged.
590 *--
591 */
592
593 /* Local Variables: */
594 char *result; /* Pointer value to return */
595 int len; /* Length of new string */
596
597 /* Initialise. */
598 result = str1;
599
600 /* If the first string pointer is NULL, also initialise the character
601 count to zero. */
602 if ( !str1 ) *nc = 0;
603
604 /* Check the global error status. */
605 if ( !astOK || !str2 ) return result;
606
607 /* Calculate the total string length once the two strings have been
608 concatenated. */
609 len = *nc + (int) strlen( str2 );
610
611 /* Extend the first (dynamic) string to the required length, including
612 a final null. Save the resulting pointer, which will be
613 returned. */
614 result = astGrow( str1, len + 1, sizeof( char ) );
615
616 /* If OK, append the second string and update the total character
617 count. */
618 if ( astOK ) {
619 (void) strcpy( result + *nc, str2 );
620 *nc = len;
621 }
622
623 /* Return the result pointer. */
624 return result;
625 }
626
astCalloc_(size_t nmemb,size_t size,int * status)627 void *astCalloc_( size_t nmemb, size_t size, int *status ) {
628 /*
629 *++
630 * Name:
631 * astCalloc
632
633 * Purpose:
634 * Allocate and initialise memory.
635
636 * Type:
637 * Public function.
638
639 * Synopsis:
640 * #include "memory.h"
641 * void *astCalloc( size_t nmemb, size_t size )
642
643 * Description:
644 * This function allocates memory in a similar manner to the
645 * standard C "calloc" function, but with improved security
646 * (against memory leaks, etc.) and with error reporting. It also
647 * fills the allocated memory with zeros.
648 *
649 * Like astMalloc, it allows zero-sized memory allocation
650 * (without error), resulting in a NULL returned pointer value.
651
652 * Parameters:
653 * nmemb
654 * The number of array elements for which memory is to be allocated.
655 * size
656 * The size of each array element, in bytes.
657
658 * Returned Value:
659 * astCalloc()
660 * If successful, the function returns a pointer to the start of
661 * the allocated memory region. If the size allocated is zero, this
662 * will be a NULL pointer.
663
664 * Notes:
665 * - A pointer value of NULL is returned if this function is
666 * invoked with the global error status set or if it fails for any
667 * reason.
668 *--
669 */
670 /* Local Variables: */
671 void *result; /* Returned pointer */
672
673 /* Initialise. */
674 result = NULL;
675
676 /* Check the global error status. */
677 if ( !astOK ) return result;
678
679 /* Attempt to allocate and initialise the required amount of memory. */
680 result = astMallocInit( nmemb*size );
681
682 /* If the above call failed due to failure of the system malloc function,
683 issue an extra error giving the number of elements and element size. */
684 if( astStatus == AST__NOMEM ) {
685 astError( AST__NOMEM, "(%lu elements, each of %lu bytes).", status,
686 (unsigned long) nmemb, (unsigned long) size );
687 }
688
689 /* Return the result. */
690 return result;
691 }
692
CheckTempStart(const char * template,const char * temp,const char * pattern,char * allowed,int * ntemp,int * allow,int * min_nc,int * max_nc,int * start_sub,int * end_sub,int * greedy,int * status)693 static char *CheckTempStart( const char *template, const char *temp,
694 const char *pattern,
695 char *allowed, int *ntemp, int *allow,
696 int *min_nc, int *max_nc, int *start_sub,
697 int *end_sub, int *greedy, int *status ){
698 /*
699 * Name:
700 * CheckTempStart
701
702 * Purpose:
703 * Examine the leading field in an astChrSub template.
704
705 * Type:
706 * Private function.
707
708 * Synopsis:
709 * char *CheckTempStart( const char *template, const char *temp,
710 * const char *pattern,
711 * char *allowed, int *ntemp, int *allow,
712 * int *min_nc, int *max_nc, int *start_sub,
713 * int *end_sub, int *greedy, int *status )
714
715 * Description:
716 * This function returns inforation about the leading field in a
717 * template string supplied to astChrSub.
718
719 * Parameters:
720 * template
721 * The full template string (used for error messages).
722 * temp
723 * Pointer to the next character to read from the template string.
724 * pattern
725 * Pointer to the user supplied pattern string (only used in error
726 * messages).
727 * allowed
728 * Pointer to a buffer in which to store a string of characters
729 * that the leading temeplate field will match. A NULL pointer may
730 * be supplied in which case new memory will be allocated. The
731 * supplied memory is expanded as necessary, and a pointer to it is
732 * returned as the function value.
733 * ntemp
734 * Address of an int in which to return the number of characters
735 * consumed from the start of "temp".
736 * allow
737 * Address of an int in which to return a flag which is non-zero if
738 * the returned string contains characters that are allowed in the
739 * test field, or zero if the returned string contains characters that
740 * are disallowed in the test field.
741 * min_nc
742 * Address of an int in which to return the minimum number of
743 * test characters that must belong to the returned set of
744 * allowed characters.
745 * max_nc
746 * Address of an int in which to return the maximum number of
747 * test characters that must belong to the returned set of
748 * allowed characters.
749 * start_sub
750 * Address of an int in which to return a flag which is non-zero if
751 * the leading template field indicates the start of a field to be
752 * substituted. In this case the supplied "allowed" pointer is
753 * returned without change as the function value, "Min_nc" is
754 * returned as zero, and max_nc is returned as zero.
755 * end_sub
756 * Address of an int in which to return a flag which is non-zero if
757 * the leading template field indicates the end of a field to be
758 * substituted. In this case the supplied "allowed" pointer is
759 * returned without change as the function value, "Min_nc" is
760 * returned as zero, and limit is returned as zero.
761 * greedy
762 * Address of an int in which to return a flag which is non-zero if
763 * the template starts with a greedy quantifier.
764 * status
765 * Pointer to the inherited status variable.
766
767 * Returned Value:
768 * Pointer to a (possibly newly allocated) memory area holding a
769 * string of characters that the leading temeplate field will match.
770 * This string should be released using astFree when no longer needed.
771 * If a NULL pointyer is returned, then all characters are allowed
772 * (or disallowed if "*allow" is zero).
773
774 * Notes:
775 * - The returned value is also stored in the module variable
776 * sizeof_memory.
777 */
778
779 /* Local Variables: */
780 char *result;
781 const char *start;
782 const char *end;
783
784 /* Initialise. */
785 result = allowed;
786 *ntemp = 0;
787 *allow = 1;
788 *min_nc = 0;
789 *max_nc = 0;
790 *start_sub = 0;
791 *end_sub = 0;
792 *greedy = 1;
793
794 /* Check global status */
795 if( !astOK ) return result;
796
797 /* If the next character is an opening parenthesis, this marks the start
798 of a substitution field. */
799 if( *temp == '(' ) {
800 *start_sub = 1;
801 *ntemp = 1;
802
803 /* If the next character is an closing parenthesis, this marks the end
804 of a substitution field. */
805 } else if( *temp == ')' ) {
806 *end_sub = 1;
807 *ntemp = 1;
808
809 /* If the next character is an opening bracket, this marks the start of a
810 field of allowed or disallowed characters. */
811 } else {
812 if( *temp == '[' ) {
813
814 /* If the first character in the brackets is "^" this is a field of
815 disallowed characters, otherwise they are allowed. */
816 if( temp[ 1 ] == '^' ) {
817 *allow = 0;
818 start = temp + 2;
819 } else {
820 start = temp + 1;
821 }
822
823 /* Get a pointer to the closing bracket. */
824 end = strchr( temp, ']' );
825
826 /* Copy the intervening string into the returned string. */
827 if( end ) {
828 result = astStore( allowed, start, end - start + 1 );
829 if( result ) result[ end - start ] = 0;
830
831 /* Report an error if no closing bracket was found. */
832 } else {
833 astError( AST__BADSUB, "Invalid pattern matching template \"%s\": "
834 "missing ']'.", status, pattern );
835 }
836
837 /* Indicate how many template characters have been used. */
838 *ntemp = end - temp + 1;
839
840 /* A single dot matches any character. */
841 } else if( *temp == '.' ) {
842 result = astFree( result );
843 *ntemp = 1;
844
845 /* Now deal with escape sequences. */
846 } else if( *temp == '\\' ) {
847
848 /* Digits... */
849 if( temp[ 1 ] == 'd' || temp[ 1 ] == 'D' ) {
850 result = astStore( allowed, "0123456789", 11 );
851 result[ 10 ] = 0;
852 if( temp[ 1 ] == 'D' ) *allow = 0;
853
854 /* White space... */
855 } else if( temp[ 1 ] == 's' || temp[ 1 ] == 'S' ) {
856 result = astStore( allowed, " \n\r", 5 );
857 result[ 4 ] = 0;
858 if( temp[ 1 ] == 'S' ) *allow = 0;
859
860 /* Word characters... */
861 } else if( temp[ 1 ] == 'w' || temp[ 1 ] == 'W' ) {
862 result = astStore( allowed, "abcdefghijklmnopqrstuvwxyz"
863 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_",
864 64 );
865 result[ 63 ] = 0;
866 if( temp[ 1 ] == 'W' ) *allow = 0;
867
868 /* Any other character is treated literally. */
869 } else {
870 result = astStore( allowed, temp + 1, 2 );
871 result[ 1 ] = 0;
872 }
873
874 /* Set number of template characters consumed. */
875 *ntemp = 2;
876
877 /* Everything else must be matched literally. */
878 } else {
879
880 if( *temp == '*' || *temp == '?' || *temp == '+' ){
881 astError( AST__BADSUB, "Invalid pattern matching template \"%s\": "
882 "field starts with '%c'.", status, pattern, temp[ *ntemp ] );
883 } else {
884 result = astStore( allowed, temp, 2 );
885 result[ 1 ] = 0;
886 *ntemp = 1;
887 }
888
889 }
890
891 /* Now see if there is any quantifier. */
892 if( temp[ *ntemp ] == '*' ) {
893 *min_nc = 0;
894 *max_nc = INT_MAX;
895 (*ntemp)++;
896 if( temp[ *ntemp ] == '?' ){
897 *greedy = 0;
898 (*ntemp)++;
899 }
900
901 } else if( temp[ *ntemp ] == '+' ) {
902 *min_nc = 1;
903 *max_nc = INT_MAX;
904 (*ntemp)++;
905 if( temp[ *ntemp ] == '?' ){
906 *greedy = 0;
907 (*ntemp)++;
908 }
909
910 } else if( temp[ *ntemp ] == '?' ) {
911 *min_nc = 0;
912 *max_nc = 1;
913 (*ntemp)++;
914
915 } else {
916
917 /* See if the remaining string starts with "{nnn}". If so, extract the
918 "nnn" and use it as the minimum and maximum field length. */
919 if( temp[ *ntemp ] == '{' ) {
920
921 start = temp + *ntemp + 1;
922 while( isdigit( (int) *start ) ) {
923 *min_nc = 10*( *min_nc ) + (int )( ( *start ) - '0' );
924 start++;
925 }
926
927 if( *start == '}' ) {
928 *max_nc = *min_nc;
929 *ntemp = start - temp + 1;
930 } else {
931 start = NULL;
932 }
933
934 } else {
935 start = NULL;
936 }
937
938 /* If the remaining string does not start with "{nnn}", use a minimum and
939 maximum field length of 1. */
940 if( !start ) {
941 *min_nc = 1;
942 *max_nc = 1;
943 }
944 }
945 }
946
947 /* Return the string of allowed characters. */
948 return result;
949 }
950
astChr2Double_(const char * str,int * status)951 double astChr2Double_( const char *str, int *status ) {
952 /*
953 *++
954 * Name:
955 * astChr2Double
956
957 * Purpose:
958 * read a double value from a string.
959
960 * Type:
961 * Public function.
962
963 * Synopsis:
964 * #include "memory.h"
965 * double astChr2Double( const char *str )
966
967 * Description:
968 * This function reads a double from the supplied null-terminated string,
969 * ignoring leading and trailing white space. AST__BAD is ereturned
970 * without error if the string is not a numerical value.
971
972 * Parameters:
973 * str
974 * Pointer to the string.
975
976 * Returned Value:
977 * astChr2Double()
978 * The double value, or AST__BAD.
979
980 * Notes:
981 * - A value of AST__BAD is returned if this function is invoked with
982 * the global error status set or if it should fail for any reason.
983 *--
984 */
985
986 /* Local Variables: */
987 double result; /* The returned value */
988 int ival; /* Integer value read from string */
989 int len; /* Length of supplied string */
990 int nc; /* Number of characters read from the string */
991
992 /* Check the global error status and supplied pointer. */
993 if ( !astOK || !str ) return AST__BAD;
994
995 /* Save the length of the supplied string. */
996 len = strlen( str );
997
998 /* Use scanf to read the floating point value. This fails if either 1) the
999 string does not begin with a numerical value (in which case astSscanf
1000 returns zero), or 2) there are non-white characters following the
1001 numerical value (in which case "nc" - the number of characters read from
1002 the string - is less than the length of the string). */
1003 if ( nc = 0,
1004 ( 1 != astSscanf( str, " %lg %n", &result, &nc ) ) || ( nc < len ) ) {
1005 result = AST__BAD;
1006 }
1007
1008 /* If the above failed, try again allowing the string to be an integer
1009 followed by a dot (e.g. "1."). Some implementations of sscanf do not
1010 consider this to be a floating point value. */
1011 if( 1 || result == AST__BAD ) {
1012 if ( nc = 0,
1013 ( 1 == astSscanf( str, " %d. %n", &ival, &nc ) ) && ( nc >= len ) ) {
1014 result = ival;
1015 }
1016 }
1017
1018 /* Return the result. */
1019 return result;
1020 }
1021
astChrCase_(const char * in,char * out,int upper,int blen,int * status)1022 void astChrCase_( const char *in, char *out, int upper, int blen, int *status ) {
1023 /*
1024 *++
1025 * Name:
1026 * astChrCase
1027
1028 * Purpose:
1029 * Convert a string to upper or lower case.
1030
1031 * Type:
1032 * Public function.
1033
1034 * Synopsis:
1035 * #include "memory.h"
1036 * void astChrCase( const char *in, char *out, int upper, int blen, int *status )
1037
1038 * Description:
1039 * This function converts a supplied string to upper or lower case,
1040 * storing the result in a supplied buffer. The astStringCase function
1041 * is similar, but stores the result in a dynamically allocated buffer.
1042
1043 * Parameters:
1044 * in
1045 * Pointer to the null terminated string to be converted. If this
1046 * is NULL, the supplied contents of the "out" string are used as
1047 * the input string.
1048 * out
1049 * Pointer to the buffer to receive the converted string. The
1050 * length of this buffer is given by "blen". If NULL is supplied
1051 * for "in", then the supplied contents of "out" are converted and
1052 * written back into "out" over-writing the supplied contents.
1053 * upper
1054 * If non-zero, the string is converted to upper case. Otherwise it
1055 * is converted to lower case.
1056 * blen
1057 * The length of the output buffer. Ignored if "in" is NULL. No
1058 * more than "blen - 1" characters will be copied from "in" to
1059 * "out", and a terminating null character will then be added.
1060
1061 *--
1062 */
1063
1064 /* Local Variables: */
1065 const char *pin;
1066 char *pout;
1067 int i;
1068
1069 /* Check the global error status. */
1070 if ( !astOK ) return;
1071
1072 /* The simple case of over-writing the supplied string. */
1073 if( ! in ) {
1074 pout = out - 1;
1075 while( *(++pout) ) *pout = toupper( (int) *pout );
1076
1077 /* If a separate output buffer has been supplied... */
1078 } else {
1079
1080 /* Initialise pointers to the input and output buffers. */
1081 pin = in;
1082 pout = out;
1083
1084 /* Copy the string character by character, converting the case in the
1085 process. Start counting from 1, not 0, in order to ensure that we are
1086 left with room for a terminating null. */
1087 for( i = 1; i < blen && *pin; i++ ) {
1088 *(pout++) = toupper( (int) *(pin++) );
1089 }
1090
1091 /* Terminate the returned string. */
1092 *pout = 0;
1093 }
1094 }
1095
astChrMatch_(const char * str1,const char * str2,int * status)1096 int astChrMatch_( const char *str1, const char *str2, int *status ) {
1097 /*
1098 *++
1099 * Name:
1100 * astChrMatch
1101
1102 * Purpose:
1103 * Case insensitive string comparison.
1104
1105 * Type:
1106 * Public function.
1107
1108 * Synopsis:
1109 * #include "memory.h"
1110 * int astChrMatch( const char *str1, const char *str2 )
1111
1112 * Description:
1113 * This function compares two null terminated strings for equality,
1114 * discounting differences in case and any trailing white space in either
1115 * string.
1116
1117 * Parameters:
1118 * str1
1119 * Pointer to the first string.
1120 * str2
1121 * Pointer to the second string.
1122
1123 * Returned Value:
1124 * astChrMatch()
1125 * Non-zero if the two strings match, otherwise zero.
1126
1127 * Notes:
1128 * - A value of zero is returned if this function is invoked with the
1129 * global error status set or if it should fail for any reason.
1130 *--
1131 */
1132
1133 /* Local Variables: */
1134 int match; /* Strings match? */
1135
1136 /* Check the global error status. */
1137 if ( !astOK ) return 0;
1138
1139 /* Initialise. */
1140 match = 1;
1141
1142 /* Loop to compare characters in the two strings until a mis-match occurs or
1143 we reach the end of the longer string. */
1144 while ( match && ( *str1 || *str2 ) ) {
1145
1146 /* Two characters match if (a) we are at the end of one string and the other
1147 string contains white space or (b) both strings contain the same character
1148 when converted to lower case. */
1149 match = ( !*str1 && isspace( *str2 ) ) ||
1150 ( !*str2 && isspace( *str1 ) ) ||
1151 ( tolower( *str1 ) == tolower( *str2 ) );
1152
1153 /* Step through each string a character at a time until its end is reached. */
1154 if ( *str1 ) str1++;
1155 if ( *str2 ) str2++;
1156 }
1157
1158 /* Return the result. */
1159 return match;
1160 }
1161
astChrMatchN_(const char * str1,const char * str2,size_t n,int * status)1162 int astChrMatchN_( const char *str1, const char *str2, size_t n, int *status ) {
1163 /*
1164 *++
1165 * Name:
1166 * astChrMatchN
1167
1168 * Purpose:
1169 * Case insensitive string comparison of at most N characters
1170
1171 * Type:
1172 * Public function.
1173
1174 * Synopsis:
1175 * #include "memory.h"
1176 * int astChrMatchN( const char *str1, const char *str2, size_t n )
1177
1178 * Description:
1179 * This function compares two null terminated strings for equality,
1180 * discounting differences in case and any trailing white space in either
1181 * string. No more than "n" characters are compared.
1182
1183 * Parameters:
1184 * str1
1185 * Pointer to the first string.
1186 * str2
1187 * Pointer to the second string.
1188 * n
1189 * Maximum number of characters to compare.
1190
1191 * Returned Value:
1192 * astChrMatchN()
1193 * Non-zero if the two strings match, otherwise zero.
1194
1195 * Notes:
1196 * - A value of zero is returned if this function is invoked with the
1197 * global error status set or if it should fail for any reason.
1198 *--
1199 */
1200
1201 /* Local Variables: */
1202 int match; /* Strings match? */
1203 int nc; /* Number of characters compared so far */
1204
1205 /* Check the global error status. */
1206 if ( !astOK ) return 0;
1207
1208 /* Initialise. */
1209 match = 1;
1210
1211 /* So far we have compared zero characters */
1212 nc = 0;
1213
1214 /* Loop to compare characters in the two strings until a mis-match occurs or
1215 we reach the end of the longer string, or we reach the specified
1216 maximum number of characters. */
1217 while ( match && ( *str1 || *str2 ) && nc++ < n ) {
1218
1219 /* Two characters match if (a) we are at the end of one string and the other
1220 string contains white space or (b) both strings contain the same character
1221 when converted to lower case. */
1222 match = ( !*str1 && isspace( *str2 ) ) ||
1223 ( !*str2 && isspace( *str1 ) ) ||
1224 ( tolower( *str1 ) == tolower( *str2 ) );
1225
1226 /* Step through each string a character at a time until its end is reached. */
1227 if ( *str1 ) str1++;
1228 if ( *str2 ) str2++;
1229 }
1230
1231 /* Return the result. */
1232 return match;
1233 }
1234
astChrSplit_(const char * str,int * n,int * status)1235 char **astChrSplit_( const char *str, int *n, int *status ) {
1236 /*
1237 *++
1238 * Name:
1239 * astChrSplit
1240
1241 * Purpose:
1242 * Extract words from a supplied string.
1243
1244 * Type:
1245 * Public function.
1246
1247 * Synopsis:
1248 * #include "memory.h"
1249 * char **astChrSplit_( const char *str, int *n )
1250
1251 * Description:
1252 * This function extracts all space-separated words form the supplied
1253 * string and returns them in an array of dynamically allocated strings.
1254
1255 * Parameters:
1256 * str
1257 * Pointer to the string to be split.
1258 * n
1259 * Address of an int in which to return the number of words returned.
1260
1261 * Returned Value:
1262 * astChrSplit()
1263 * A pointer to a dynamically allocated array containing "*n" elements.
1264 * Each element is a pointer to a dynamically allocated character
1265 * string containing a word extracted from the supplied string. Each
1266 * of these words will have no leading or trailing white space.
1267
1268 * Notes:
1269 * - A NULL pointer is returned if this function is invoked with the
1270 * global error status set or if it should fail for any reason, or if
1271 * the supplied string contains no words.
1272 *--
1273 */
1274
1275 /* Local Variables: */
1276 char **result;
1277 char *w;
1278 const char *p;
1279 const char *ws;
1280 int first;
1281 int state;
1282 int wl;
1283
1284 /* Check the global error status. */
1285 if ( !astOK ) return NULL;
1286
1287 /* Initialise. */
1288 result = NULL;
1289 ws = NULL;
1290 *n = 0;
1291
1292 /* State 0 is "looking for the next non-white character which marks the
1293 start of the next word". State 1 is "looking for the next white character
1294 which marks the end of the current word". */
1295 state = 0;
1296
1297 /* Loop through all characters in the supplied string, including the
1298 terminating null. */
1299 p = str - 1;
1300 first = 1;
1301 while( *(p++) || first ) {
1302 first = 0;
1303
1304 /* If this is the terminating null or a space, and we are currently looking
1305 for the end of a word, allocate memory for the new word, copy the text
1306 in, terminate it, extend the returned array by one element, and store
1307 the new word in it. */
1308 if( !*p || isspace( *p ) ) {
1309 if( state == 1 ) {
1310 wl = p - ws;
1311 w = astMalloc( wl + 1 );
1312 if( w ) {
1313 strncpy( w, ws, wl );
1314 w[ wl ] = 0;
1315 result = astGrow( result, *n + 1, sizeof( char * ) );
1316 if( result ) result[ (*n)++ ] = w;
1317 }
1318 state = 0;
1319 }
1320
1321 /* If this is non-blank character, and we are currently looking for the
1322 start of a word, note the address of the start of the word, and
1323 indicate that we are now looking for the end of a word. */
1324 } else {
1325 if( state == 0 ) {
1326 state = 1;
1327 ws = p;
1328 }
1329 }
1330 }
1331
1332 /* Return the result. */
1333 return result;
1334 }
1335
astChrSplitC_(const char * str,char c,int * n,int * status)1336 char **astChrSplitC_( const char *str, char c, int *n, int *status ) {
1337 /*
1338 *++
1339 * Name:
1340 * astChrSplitC
1341
1342 * Purpose:
1343 * Split a string using a specified character delimiter.
1344
1345 * Type:
1346 * Public function.
1347
1348 * Synopsis:
1349 * #include "memory.h"
1350 * char **astChrSplitC( const char *str, char c, int *n )
1351
1352 * Description:
1353 * This function extracts all sub-strings separated by a given
1354 * character from the supplied string and returns them in an array
1355 * of dynamically allocated strings. The delimiter character itself
1356 * is not included in the returned strings.
1357 *
1358 * Delimiter characters that are preceded by "\" are not used as
1359 * delimiters but are included in the returned word instead (without
1360 * the "\").
1361
1362 * Parameters:
1363 * str
1364 * Pointer to the string to be split.
1365 * c
1366 * The delimiter character.
1367 * n
1368 * Address of an int in which to return the number of words returned.
1369
1370 * Returned Value:
1371 * astChrSplitC()
1372 * A pointer to a dynamically allocated array containing "*n" elements.
1373 * Each element is a pointer to a dynamically allocated character
1374 * string containing a word extracted from the supplied string.
1375
1376 * Notes:
1377 * - A NULL pointer is returned if this function is invoked with the
1378 * global error status set or if it should fail for any reason, or if
1379 * the supplied string contains no words.
1380 *--
1381 */
1382
1383 /* Local Variables: */
1384 char **result;
1385 char *word;
1386 const char *p;
1387 int escaped;
1388 int wordlen;
1389
1390 /* Initialise returned values. */
1391 *n = 0;
1392 result = NULL;
1393
1394 /* Check the global error status. */
1395 if ( !astOK ) return result;
1396
1397 /* More initialisations. */
1398 word = NULL;
1399 wordlen = 0;
1400 escaped = 0;
1401
1402 /* Loop through all characters in the supplied string, including the
1403 terminating null. */
1404 p = str;
1405 while( *p ) {
1406
1407 /* Is this a delimiter character? */
1408 if( *p == c ) {
1409
1410 /* If it is escaped, it does not mark the end of a word. Put it into the
1411 current output buffer instead, overwriting the escape character that
1412 preceded it. */
1413 if( escaped ) {
1414 word[ wordlen - 1 ] = c;
1415
1416 /* The next character is not escaped. */
1417 escaped = 0;
1418
1419 /* If the delimiter is not escaped, terminate the current word and store
1420 a pointer to it in the returned array. */
1421 } else {
1422 result = astGrow( result, *n + 1, sizeof( char * ) );
1423 word = astGrow( word, wordlen + 1, 1 );
1424 if( result && word ) {
1425 word[ wordlen ] = 0;
1426 result[ (*n)++ ] = word;
1427 wordlen = 0;
1428 word = NULL;
1429 }
1430 }
1431
1432 /* If this is not a delimitier character, store it in the returned word. */
1433 } else {
1434 word = astGrow( word, wordlen + 1, 1 );
1435 if( word ) word[ wordlen++ ] = *p;
1436
1437 /* If the current character was escaped, indicate that the next character
1438 is not escaped. */
1439 if( escaped ) {
1440 escaped = 0;
1441
1442 /* If this character is a unescaped backslash, set a flag indicating that the
1443 next character is escaped. */
1444 } else if( *p == '\\' ){
1445 escaped = 1;
1446 }
1447 }
1448
1449 /* Move on to the next character. */
1450 p++;
1451 }
1452
1453 /* Store the text following the final delimitier. */
1454 result = astGrow( result, *n + 1, sizeof( char * ) );
1455 word = astGrow( word, wordlen + 1, 1 );
1456 if( result && word ) {
1457 word[ wordlen ] = 0;
1458 result[ (*n)++ ] = word;
1459 }
1460
1461 /* Return the result. */
1462 return result;
1463 }
1464
astChrSplitRE_(const char * str,const char * regexp,int * n,const char ** matchend,int * status)1465 char **astChrSplitRE_( const char *str, const char *regexp, int *n,
1466 const char **matchend, int *status ) {
1467 /*
1468 *++
1469 * Name:
1470 * astChrSplitRE
1471
1472 * Purpose:
1473 * Extract sub-strings matching a specified regular expression.
1474
1475 * Type:
1476 * Public function.
1477
1478 * Synopsis:
1479 * #include "memory.h"
1480 * char **astChrSplitRE( const char *str, const char *regexp, int *n,
1481 * const char **matchend )
1482
1483 * Description:
1484 * This function compares the supplied string with the supplied
1485 * regular expression. If they match, each section of the test string
1486 * that corresponds to a parenthesised sub-string in the regular
1487 * expression is copied and stored in the returned array.
1488
1489 * Parameters:
1490 * str
1491 * Pointer to the string to be split.
1492 * regexp
1493 * The regular expression. See "Template Syntax:" in the astChrSub
1494 * prologue. Note, this function differs from astChrSub in that any
1495 * equals signs (=) in the regular expression are treated literally.
1496 * n
1497 * Address of an int in which to return the number of sub-strings
1498 * returned.
1499 * matchend
1500 * A pointer to a location at which to return a pointer to the
1501 * character that follows the last character within the supplied test
1502 * string that matched any parenthesises sub-section of "regexp". A
1503 * NULL pointer is returned if no matches were found. A NULL pointer
1504 * may be supplied if the location of the last matching character is
1505 * not needed.
1506
1507 * Returned Value:
1508 * astChrSplitRE()
1509 * A pointer to a dynamically allocated array containing "*n" elements.
1510 * Each element is a pointer to a dynamically allocated character
1511 * string containing a sub-string extracted from the supplied string.
1512 * The array itself, and the strings within it, should all be freed
1513 * using astFree when no longer needed.
1514
1515 * Notes:
1516 * - If a parenthesised sub-string in the regular expression is matched
1517 * by more than one sub-string within the test string, then only the
1518 * first is returned. To return multiple matches, the regular
1519 * expression should include multiple copies of the parenthesised
1520 * sub-string (for instance, separated by ".+?" if the intervening
1521 * string is immaterial).
1522 * - A NULL pointer is returned if this function is invoked with the
1523 * global error status set or if it should fail for any reason, or if
1524 * the supplied string contains no words.
1525 *--
1526 */
1527
1528 /* Local Variables: */
1529 char **result;
1530 char *temp;
1531 int i;
1532
1533 /* Initialise returned values. */
1534 *n = 0;
1535 result = NULL;
1536
1537 /* Check global status */
1538 if( !astOK ) return result;
1539
1540 /* Call ChrSuber to do the work, saving the matching parts of the test
1541 string. */
1542 temp = ChrSuber( str, regexp, NULL, 0, 1, &result, n, matchend, status );
1543 if( temp ) {
1544 temp = astFree( temp );
1545
1546 /* Free all results if no match was found. */
1547 } else if( result ) {
1548 for( i = 0; i < *n; i++ ) result[ i ] = astFree( result[ i ] );
1549 result = astFree( result );
1550 *n = 0;
1551 }
1552
1553 /* Return the result */
1554 return result;
1555 }
1556
ChrSuber(const char * test,const char * pattern,const char * subs[],int nsub,int ignore_equals,char *** parts,int * npart,const char ** matchend,int * status)1557 char *ChrSuber( const char *test, const char *pattern, const char *subs[],
1558 int nsub, int ignore_equals, char ***parts, int *npart,
1559 const char **matchend, int *status ){
1560 /*
1561 * Name:
1562 * ChrSuber
1563
1564 * Purpose:
1565 * Performs substitutions on a supplied string.
1566
1567 * Type:
1568 * Private function.
1569
1570 * Synopsis:
1571 * #include "memory.h"
1572 * char *ChrSuber( const char *test, const char *pattern,
1573 * const char *subs[], int nsub, int ignore_equals,
1574 * char ***parts, int *npart, const char **matchend,
1575 * int *status )
1576
1577 * Description:
1578 * This function performs the work for astChrSub and astChrSplitRE.
1579
1580 * Parameters:
1581 * test
1582 * The string to be tested.
1583 * pattern
1584 * The template string. See "Template Syntax:" in the astChrSub
1585 prologue.
1586 * subs
1587 * An array of strings that are to replace the sections of the test
1588 * string that match each parenthesised sub-string in "pattern". The
1589 * first element of "subs" replaces the part of the test string that
1590 * matches the first parenthesised sub-string in the template, etc.
1591 *
1592 * If "nsub" is zero, then the "subs" pointer is ignored. In this
1593 * case, and if parameter "ignore_equals" is zero, substitution strings
1594 * may be specified by appended them to the end of the "pattern" string,
1595 * separated by "=" characters
1596 * nsub
1597 * The number of substitution strings supplied in array "subs".
1598 * ignore_equals
1599 * If non-zero, any equals signs in the supplied pattern are
1600 * treated literally, rather than being used to split the template
1601 * from any substitution strigs.
1602 * parts
1603 * Address of a location at which to return a pointer to an array
1604 * of character string pointers. The strings are the sub-sections
1605 * of "test" that matched the parenthesised sub-sections of
1606 * "template". The array will have "*npart" elements. Ignored if NULL.
1607 * npart
1608 * Address of a location at which to return the length of the
1609 * "parts" array. Ignored if "parts" is NULL.
1610 * matchend
1611 * A pointer to a location at which to return a pointer to the
1612 * character that follows the last character within the supplied test
1613 * string that matched any parenthesises sub-section of "regexp". A
1614 * NULL pointer is returned if no matches were found. A NULL pointer
1615 * may be supplied if the location of the last matching character is
1616 * not needed.
1617 * status
1618 * Pointer to the inherited status variable.
1619
1620 * Returned Value:
1621 * A pointer to a dynamically allocated string holding the result
1622 * of the substitutions, or NULL if the test string does not match
1623 * the template string. This string should be freed using astFree
1624 * when no longer needed. If no substituions are specified then a
1625 * copy of the test string is returned if it matches the template.
1626
1627 * Notes:
1628 * - A NULL pointer is returned if this function is invoked with the
1629 * global error status set or if it should fail for any reason, or if
1630 * the supplied test string does not match the template.
1631
1632 */
1633
1634 /* Local Variables: */
1635 char **sections;
1636 char **temps;
1637 char *cptr;
1638 char *result;
1639 char *temp;
1640 char *template;
1641 int i;
1642 int nsec;
1643 int ntemp;
1644 size_t tlen;
1645
1646 /* Initialise */
1647 result = NULL;
1648 if( parts ) *npart = 0;
1649
1650 /* Check global status */
1651 if( !astOK ) return result;
1652
1653 /* If required, split the total "pattern" string into sections, using
1654 (unescaped) "=" characters as the delimiter. The first section is the
1655 actual template, and each subsequent section (if any) holds a
1656 substitution string. */
1657 if( ! ignore_equals ) {
1658 sections = astChrSplitC( pattern, '=', &nsec );
1659
1660 /* If equals signs are being treated literally, just take a copy of the
1661 supplied pattern. */
1662 } else {
1663 cptr = astStore( NULL, pattern, strlen( pattern ) + 1 );
1664 sections = &cptr;
1665 nsec = 1;
1666 }
1667
1668 if( sections ) {
1669
1670 /* If the caller did not provide any substitution strings, use the ones
1671 appended to the end of the pattern string (if any). */
1672 if( nsub == 0 ) {
1673 subs = (void *) ( sections + 1 );
1674 nsub = nsec - 1;
1675 }
1676
1677 /* Split the template section into sub-sections, using (unescaped) "|"
1678 characters as the delimiter. Each sub-section is an alternate pattern
1679 matching template. */
1680 temps = astChrSplitC( sections[ 0 ], '|', &ntemp );
1681
1682 } else {
1683 temps = 0;
1684 ntemp = 0;
1685 }
1686
1687 /* Loop round each template until all templates have been checked or a
1688 match occurs.. */
1689 for( i = 0; i < ntemp && !result; i++ ) {
1690 temp = temps[ i ];
1691 tlen = strlen( temp );
1692
1693 /* If the template starts with "^" or "(^", remove the "^" character.
1694 Otherwise insert ".*?" at the start. Allocate three extra characters
1695 in case we later need to add ".*?" to the end of the string. */
1696 if( temp[ 0 ] == '^' ) {
1697 template = astMalloc( tlen + 3 );
1698 if( template ) {
1699 strcpy( template, temp + 1 );
1700 tlen--;
1701 }
1702
1703 } else if( temp[ 0 ] == '(' && temp[ 1 ] == '^') {
1704 template = astMalloc( tlen + 3 );
1705 if( template ) {
1706 template[ 0 ] = '(';
1707 strcpy( template + 1, temp + 2 );
1708 tlen--;
1709 }
1710
1711 } else {
1712 template = astMalloc( tlen + 7 );
1713 if( template ) {
1714 template[ 0 ] = '.';
1715 template[ 1 ] = '*';
1716 template[ 2 ] = '?';
1717 strcpy( template + 3, temp );
1718 tlen += 3;
1719 }
1720 }
1721
1722 /* If the pattern ends with "$" or "$)", remove the "$" character. Otherwise
1723 insert ".*?" at the end. */
1724 if( template[ tlen - 1 ] == '$' ) {
1725 tlen--;
1726
1727 } else if( template[ tlen - 2 ] == '$' && template[ tlen - 1 ] == ')' ) {
1728 template[ tlen - 2 ] = ')';
1729 tlen--;
1730
1731 } else {
1732 template[ tlen ] = '.';
1733 template[ tlen + 1 ] = '*';
1734 template[ tlen + 2 ] = '?';
1735 tlen += 3;
1736 }
1737
1738 /* Ensure the string is terminated */
1739 template[ tlen ] = 0;
1740
1741 /* See if the test string matches the current template. */
1742 result = ChrMatcher( test, test + strlen( test ), template, pattern,
1743 subs, nsub, 0, 1, parts, npart, matchend, status );
1744
1745 /* Free resources. */
1746 template = astFree( template );
1747 }
1748
1749 if( temps ) {
1750 for( i = 0; i < ntemp; i++ ) temps[ i ] = astFree( temps[ i ] );
1751 temps = astFree( temps );
1752 }
1753
1754 if( sections ) {
1755 for( i = 0; i < nsec; i++ ) sections[ i ] = astFree( sections[ i ] );
1756 if( ! ignore_equals ) sections = astFree( sections );
1757 }
1758
1759 /* Return a NULL pointer if an error has occurred. */
1760 if( !astOK ) result = astFree( result );
1761
1762 /* Return the result */
1763 return result;
1764 }
1765
astChrSub_(const char * test,const char * pattern,const char * subs[],int nsub,int * status)1766 char *astChrSub_( const char *test, const char *pattern, const char *subs[],
1767 int nsub, int *status ){
1768 /*
1769 *++
1770 * Name:
1771 c astChrSub
1772 f AST_CHRSUB
1773
1774 * Purpose:
1775 * Performs substitutions on a supplied string.
1776
1777 * Type:
1778 * Public function.
1779
1780 * Synopsis:
1781 c #include "memory.h"
1782 c char *astChrSub( const char *test, const char *pattern,
1783 c const char *subs[], int nsub )
1784 f MATCH = AST_CHRSUB( TEST, PATTERN, RESULT, STATUS )
1785
1786 * Description:
1787 * This function checks a supplied test string to see if it matches a
1788 * supplied template. If it does, specified sub-sections of the test
1789 * string may optionally be replaced by supplied substitution strings.
1790 * The resulting string is returned.
1791
1792 * Parameters:
1793 c test
1794 f TEST = CHARACTER * ( * ) (Given)
1795 * The string to be tested.
1796 * pattern
1797 f PATTERN = CHARACTER * ( * ) (Given)
1798 * The template string. See "Template Syntax:" below.
1799 * subs
1800 * An array of strings that are to replace the sections of the test
1801 * string that match each parenthesised sub-string in "pattern". The
1802 * first element of "subs" replaces the part of the test string that
1803 * matches the first parenthesised sub-string in the template, etc.
1804 *
1805 * If "nsub" is zero, then the "subs" pointer is ignored. In this
1806 * case, substitution strings may be specified by appended them to
1807 * the end of the "pattern" string, separated by "=" characters.
1808 * Note, if you need to include a literal "=" character in the
1809 * pattern, precede it by an escape "\" character.
1810 * nsub
1811 * The number of substitution strings supplied in array "subs".
1812 f RESULT = CHARACTER * ( * ) (Returned)
1813 f Returned holding the result of the substitutions. If the test
1814 f string does not match the template, then a blank string is
1815 f returned.
1816
1817 * Returned Value:
1818 c astChrSub()
1819 c A pointer to a dynamically allocated string holding the result
1820 c of the substitutions, or NULL if the test string does not match
1821 c the template string. This string should be freed using astFree
1822 c when no longer needed. If no substituions are specified then a
1823 c copy of the test string is returned if it matches the template.
1824 f AST_CHRSUB = LOGICAL
1825 f .TRUE. if the test string matched the supplied template, and
1826 f .FALSE. otherwise.
1827
1828 * Template Syntax:
1829 * The template syntax is a minimal form of regular expression, The
1830 * quantifiers allowed are "*", "?", "+", "{n}", "*?" and "+?" (the
1831 * last two are non-greedy - they match the minimum length possible
1832 * that still gives an overall match to the template). The only
1833 * constraints allowed are "^" and "$". The following atoms are allowed:
1834 *
1835 * - [chars]: Matches any of the specified characters.
1836 *
1837 * - [^chars]: Matches anything but the specified characters.
1838 *
1839 * - .: Matches any single character.
1840 *
1841 * - x: Matches the character x so long as x has no other significance.
1842 *
1843 * - \x: Always matches the character x (except for [dDsSwW]).
1844 *
1845 * - \d: Matches a single digit.
1846 *
1847 * - \D: Matches anything but a single digit.
1848 *
1849 * - \w: Matches any alphanumeric character, and "_".
1850 *
1851 * - \W: Matches anything but alphanumeric characters, and "_".
1852 *
1853 * - \s: Matches white space.
1854 *
1855 * - \S: Matches anything but white space.
1856 *
1857 * Note, minus signs ("-") within brackets have no special significance,
1858 * so ranges of characters must be specified explicitly.
1859 *
1860 * Multiple template strings can be concatenated, using the "|"
1861 * character to separate them. The test string is compared against
1862 * each one in turn until a match is found.
1863 *
1864 c Parentheses are used within each template to identify sub-strings
1865 c that are to be replaced by the strings supplied in "sub".
1866 c
1867 c If "nsub" is supplied as zero, then substitution strings may be
1868 c specified by appended them to the end of the "pattern" string,
1869 c separated by "=" characters. If "nsub" is not zero, then any
1870 c substitution strings appended to the end of "pattern" are ignored.
1871 f
1872 f Parentheses are used within each template to identify sub-strings
1873 f that are to be replaced by new strings. The new strings are
1874 f specified by appended them to the end of the "pattern" string,
1875 f separated by "=" characters.
1876 *
1877 c Each element of "subs"
1878 f Each new string
1879 * may contain a reference to a token of the
1880 * form "$1", "$2", etc. The "$1" token will be replaced by the part
1881 * of the test string that matched the first parenthesised sub-string
1882 * in "pattern". The "$2" token will be replaced by the part of the
1883 * test string that matched the second parenthesised sub-string in
1884 * "pattern", etc.
1885 *
1886
1887 c Notes:
1888 c - A NULL pointer is returned if this function is invoked with the
1889 c global error status set or if it should fail for any reason, or if
1890 c the supplied test string does not match the template.
1891
1892 *--
1893 */
1894
1895 /* Call ChrSuber to do the work, without saving the matching parts of the
1896 test string. */
1897 return ChrSuber( test, pattern, subs, nsub, 0, NULL, NULL, NULL, status );
1898 }
1899
astFree_(void * ptr,int * status)1900 void *astFree_( void *ptr, int *status ) {
1901 /*
1902 *++
1903 * Name:
1904 * astFree
1905
1906 * Purpose:
1907 * Free previously allocated memory.
1908
1909 * Type:
1910 * Public function.
1911
1912 * Synopsis:
1913 * #include "memory.h"
1914 * void *astFree( void *ptr )
1915
1916 * Description:
1917 * This function frees memory that has previouly been dynamically
1918 * allocated using one of the AST memory function.
1919
1920 * Parameters:
1921 * ptr
1922 * Pointer to previously allocated memory. An error will result
1923 * if the memory has not previously been allocated by another
1924 * function in this module. However, a NULL pointer value is
1925 * accepted (without error) as indicating that no memory has yet
1926 * been allocated, so that no action is required.
1927
1928 * Returned Value:
1929 * astFree()
1930 * Always returns a NULL pointer.
1931
1932 *--
1933 */
1934
1935 /* Local Variables: */
1936 astDECLARE_GLOBALS /* Pointer to thread-specific global data */
1937 Memory *mem; /* Pointer to memory header */
1938 int isdynamic; /* Is the memory dynamically allocated? */
1939 size_t size; /* The usable size of the memory block */
1940
1941 /* If needed, get a pointer to the thread specific global data structure. */
1942 astGET_GLOBALS(NULL);
1943
1944 /* If the incoming pointer is NULL, do nothing. Otherwise, check if it
1945 points at dynamically allocated memory (IsDynamic sets the global
1946 error status if it does not). */
1947 if( ptr ) {
1948 IS_DYNAMIC( ptr, isdynamic );
1949 } else {
1950 isdynamic = 0;
1951 }
1952 if ( isdynamic ) {
1953
1954 /* If OK, obtain a pointer to the memory header. */
1955 mem = (Memory *) ( (char *) ptr - SIZEOF_MEMORY );
1956
1957 #ifdef MEM_DEBUG
1958 DeIssue( mem, status );
1959 #endif
1960
1961 /* If the memory block is small enough, and the cache is being used, put it
1962 into the cache rather than freeing it, so that it can be reused. */
1963 size = mem->size;
1964 if( use_cache && size <= MXCSIZE ) {
1965 mem->next = cache[ size ];
1966 cache[ size ] = mem;
1967
1968 /* Set the size to zero to indicate that the memory block has been freed.
1969 The size of the block is implied by the Cache element it is stored in. */
1970 mem->size = (size_t) 0;
1971
1972 /* Simply free other memory blocks, clearing the "magic number" and size
1973 values it contains. This helps prevent accidental re-use of the memory. */
1974 } else {
1975 mem->magic = (unsigned long) 0;
1976 mem->size = (size_t) 0;
1977
1978 /* Free the allocated memory. */
1979 FREE( mem );
1980 }
1981 }
1982
1983 /* Always return a NULL pointer. */
1984 return NULL;
1985
1986 }
1987
astFreeDouble_(void * ptr,int * status)1988 void *astFreeDouble_( void *ptr, int *status ) {
1989 /*
1990 *++
1991 * Name:
1992 * astFreeDouble
1993
1994 * Purpose:
1995 * Free previously double allocated memory.
1996
1997 * Type:
1998 * Public function.
1999
2000 * Synopsis:
2001 * #include "memory.h"
2002 * void *astFreeDouble( void *ptr )
2003
2004 * Description:
2005 * This function frees memory that has previouly been dynamically
2006 * allocated using one of the AST memory function. It assumes that
2007 * the supplied pointer is a pointer to an array of pointers. Each
2008 * of these pointers is first freed, and then the supplied pointer
2009 * is freed.
2010 *
2011 * Note, this routine should not be used with arrays allocated
2012 * by astGrow since astGrow over-allocates and so there may be
2013 * non-initialised pointers at the end of the array.
2014
2015 * Parameters:
2016 * ptr
2017 * Pointer to previously allocated memory. An error will result
2018 * if the memory has not previously been allocated by another
2019 * function in this module. However, a NULL pointer value is
2020 * accepted (without error) as indicating that no memory has yet
2021 * been allocated, so that no action is required.
2022
2023 * Returned Value:
2024 * astFreeDouble()
2025 * Always returns a NULL pointer.
2026
2027 *--
2028 */
2029
2030 /* Local Variables: */
2031 int iptr; /* Index of sub-pointer */
2032 int nptr; /* Number of sub-pointers */
2033 size_t size; /* The usable size of the memory block */
2034 void **ptrs; /* Pointer to array of pointers */
2035
2036 /* Check a pointer was supplied. */
2037 if( ! ptr ) return NULL;
2038
2039 /* Get the size of the memory area. */
2040 size = astSizeOf( ptr );
2041
2042 /* Get the number of points this amount of memory could hold. */
2043 nptr = size/sizeof( void * );
2044
2045 /* Report an error if the size is not an integer multiple of an address
2046 size. */
2047 if( nptr*sizeof( void * ) != size ) {
2048 astError( AST__MEMIN, "Invalid attempt to free double allocated "
2049 "memory: the supplied memory size (%lu bytes) is not "
2050 "an integer multiple of %lu.", status, size,
2051 sizeof( void * ) );
2052
2053 } else {
2054
2055 /* Free each sub-pointer. */
2056 ptrs = (void **) ptr;
2057 for( iptr = 0; iptr < nptr; iptr++ ) {
2058 ptrs[ iptr ] = astFree( ptrs[ iptr ] );
2059 }
2060
2061 /* Free the supplied pointer. */
2062 ptr = astFree( ptr );
2063 }
2064
2065 /* Always return a NULL pointer. */
2066 return NULL;
2067 }
2068
astGrow_(void * ptr,int n,size_t size,int * status)2069 void *astGrow_( void *ptr, int n, size_t size, int *status ) {
2070 /*
2071 *++
2072 * Name:
2073 * astGrow
2074
2075 * Purpose:
2076 * Allocate memory for an adjustable array.
2077
2078 * Type:
2079 * Public function.
2080
2081 * Synopsis:
2082 * #include "memory.h"
2083 * void *astGrow( void *ptr, int n, size_t size )
2084
2085 * Description:
2086 * This function allocates memory in which to store an array of
2087 * data whose eventual size is unknown. It should be invoked
2088 * whenever a new array size is determined and will appropriately
2089 * increase the amount of memory allocated when necessary. In
2090 * general, it will over-allocate in anticipation of future growth
2091 * so that the amount of memory does not need adjusting on every
2092 * invocation.
2093
2094 * Parameters:
2095 * ptr
2096 * Pointer to previously allocated memory (or NULL if none has
2097 * yet been allocated).
2098 * n
2099 * Number of array elements to be stored (may be zero).
2100 * size
2101 * The size of each array element.
2102
2103 * Returned Value:
2104 * astGrow()
2105 * If the memory was allocated successfully, a pointer to the start
2106 * of the possibly new memory region is returned (this may be the
2107 * same as the original pointer).
2108
2109 * Notes:
2110 * - When new memory is allocated, the existing contents are preserved.
2111 * - This function does not free memory once it is allocated, so
2112 * the size allocated grows to accommodate the maximum size of the
2113 * array (or "high water mark"). Other memory handling routines may
2114 * be used to free the memory (or alter its size) if necessary.
2115 * - If this function is invoked with the global error status set,
2116 * or if it fails for any reason, the original pointer value is
2117 * returned and the memory contents are unchanged.
2118 *--
2119 */
2120
2121 /* Local Variables: */
2122 astDECLARE_GLOBALS /* Pointer to thread-specific global data */
2123 int isdynamic; /* Is the memory dynamically allocated? */
2124 Memory *mem; /* Pointer to memory header */
2125 size_t newsize; /* New size to allocate */
2126 void *new; /* Result pointer */
2127
2128 /* Check the global error status. */
2129 if ( !astOK ) return ptr;
2130
2131 /* If needed, get a pointer to the thread specific global data structure. */
2132 astGET_GLOBALS(NULL);
2133
2134 /* Initialise. */
2135 new = ptr;
2136
2137 /* Calculate the total size of memory needed. */
2138 size *= (size_t) n;
2139
2140 /* If no memory has yet been allocated, allocate exactly the amount
2141 required. */
2142 if ( !ptr ) {
2143 new = astMalloc( size );
2144
2145 /* Otherwise, check that the incoming pointer identifies previously
2146 allocated memory. */
2147 } else {
2148 IS_DYNAMIC( ptr, isdynamic );
2149 if ( isdynamic ) {
2150
2151 /* Obtain a pointer to the memory header and check if the new size
2152 exceeds that already allocated. */
2153 mem = (Memory *) ( (char *) ptr - SIZEOF_MEMORY );
2154 if ( mem->size < size ) {
2155
2156 /* If so, calculate a possible new size by doubling the old
2157 size. Increase this further if necessary. */
2158 newsize = mem->size * ( (size_t) 2 );
2159 if ( size > newsize ) newsize = size;
2160
2161 /* Re-allocate the memory. */
2162 new = astRealloc( ptr, newsize );
2163 }
2164 }
2165 }
2166
2167 /* Return the result. */
2168 return new;
2169 }
2170
astIsDynamic_(const void * ptr,int * status)2171 int astIsDynamic_( const void *ptr, int *status ) {
2172 /*
2173 *++
2174 * Name:
2175 * astIsDynamic
2176
2177 * Purpose:
2178 * Returns a flag indicating if memory was allocated dynamically.
2179
2180 * Type:
2181 * Public function.
2182
2183 * Synopsis:
2184 * #include "memory.h"
2185 * int astIsDynamic_( const void *ptr )
2186
2187 * Description:
2188 * This function takes a pointer to a region of memory and tests if
2189 * the memory has previously been dynamically allocated using other
2190 * functions from this module. It does this by checking for the
2191 * presence of a "magic" number in the header which precedes the
2192 * allocated memory. If the magic number is not present (or the
2193 * pointer is invalid for any other reason), zero is returned.
2194 * Otherwise 1 is returned.
2195
2196 * Parameters:
2197 * ptr
2198 * Pointer to test.
2199
2200 * Returned Value:
2201 * astIsDynamic()
2202 * Non-zero if the memory was allocated dynamically. Zero is returned
2203 * if the supplied pointer is NULL.
2204
2205 * Notes:
2206 * - A value of zero is returned if this function is invoked with
2207 * the global error status set, or if it fails for any reason.
2208 *--
2209 */
2210
2211 /* Local Variables: */
2212 astDECLARE_GLOBALS /* Pointer to thread-specific global data */
2213 Memory *isdynmem; /* Pointer to memory header */ \
2214
2215 /* Check the global error status and the supplied pointer. */
2216 if ( !astOK || ! ptr ) return 0;
2217
2218 /* If needed, get a pointer to the thread specific global data structure. */
2219 astGET_GLOBALS(NULL);
2220
2221 /* Derive a pointer to the memory header that precedes the
2222 supplied region of memory. */
2223 isdynmem = (Memory *) ( (char *) ptr - SIZEOF_MEMORY );
2224
2225 /* Check if the "magic number" in the header is valid, returning non-zero
2226 if it is. */
2227 return ( isdynmem->magic == MAGIC( isdynmem, isdynmem->size ) );
2228 }
2229
astMalloc_(size_t size,int init,int * status)2230 void *astMalloc_( size_t size, int init, int *status ) {
2231 /*
2232 *++
2233 * Name:
2234 * astMalloc
2235
2236 * Purpose:
2237 * Allocate memory.
2238
2239 * Type:
2240 * Public function.
2241
2242 * Synopsis:
2243 * #include "memory.h"
2244 * void *astMalloc( size_t size )
2245
2246 * Description:
2247 * This function allocates memory in a similar manner to the
2248 * standard C "malloc" function, but with improved security
2249 * (against memory leaks, etc.) and with error reporting. It also
2250 * allows zero-sized memory allocation (without error), resulting
2251 * in a NULL returned pointer value.
2252
2253 * Parameters:
2254 * size
2255 * The size of the memory region required (may be zero).
2256
2257 * Returned Value:
2258 * astMalloc()
2259 * If successful, the function returns a pointer to the start of
2260 * the allocated memory region. If the size allocated is zero, this
2261 * will be a NULL pointer.
2262
2263 * Notes:
2264 * - A pointer value of NULL is returned if this function is
2265 * invoked with the global error status set or if it fails for any
2266 * reason.
2267 *--
2268
2269 * astMallocInit:
2270 * - This function can be invoked using either the public astMalloc
2271 * macro documented above, or the private astMallocInit macro.
2272 * astMallocInit has the same interface as astMalloc, but calls calloc
2273 * rather than malloc so that the allocated memory is filled with zeros.
2274 * Ideally, we should use an extra layer in the calling heirarchy to
2275 * remove the hidden "init" argument in the astMalloc_ interface, but
2276 * astMalloc is time-critical in many situations and so it is included
2277 * as a "hidden" argument.
2278
2279 */
2280
2281 /* Local Constants: */
2282 #define ERRBUF_LEN 80
2283
2284 /* Local Variables: */
2285 astDECLARE_GLOBALS /* Pointer to thread-specific global data */
2286 char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */
2287 char *errstat; /* Pointer to system error message */
2288 Memory *mem; /* Pointer to space allocated by malloc */
2289 void *result; /* Returned pointer */
2290
2291 /* Initialise. */
2292 result = NULL;
2293
2294 /* Check the global error status. */
2295 if ( !astOK ) return result;
2296
2297 /* If needed, get a pointer to the thread specific global data structure. */
2298 astGET_GLOBALS(NULL);
2299
2300 /* Check that the size requested is not negative and report an error
2301 if it is. */
2302 if ( size < (size_t) 0 ) {
2303 astError( AST__MEMIN,
2304 "Invalid attempt to allocate %lu bytes of memory.", status,
2305 (unsigned long) size );
2306
2307 /* Otherwise, if the size is greater than zero, either get a previously
2308 allocated memory block from the cache, or attempt to use malloc
2309 to allocate the memory, including space for the header structure. */
2310 } else if ( size != (size_t ) 0 ) {
2311
2312 /* If the cache is being used and a cached memory block of the required size
2313 is available, remove it from the cache array and use it. */
2314 mem = ( use_cache && size <= MXCSIZE ) ? cache[ size ] : NULL;
2315 if( mem ) {
2316 cache[ size ] = mem->next;
2317 mem->next = NULL;
2318 mem->size = (size_t) size;
2319
2320 /* Initialise the memory (but not the header) if required. */
2321 if( init ) (void) memset( (char *) mem + SIZEOF_MEMORY, 0, size );
2322
2323 /* Otherwise, allocate a new memory block using "malloc" or "calloc". */
2324 } else {
2325 if( init ) {
2326 mem = CALLOC( 1, SIZEOF_MEMORY + size );
2327 } else {
2328 mem = MALLOC( SIZEOF_MEMORY + size );
2329 }
2330
2331 /* Report an error if malloc failed. */
2332 if ( !mem ) {
2333
2334 #if HAVE_STRERROR_R
2335 strerror_r( errno, errbuf, ERRBUF_LEN );
2336 errstat = errbuf;
2337 #else
2338 errstat = strerror( errno );
2339 #endif
2340 astError( AST__NOMEM, "malloc: %s", status, errstat );
2341 astError( AST__NOMEM, "Failed to allocate %lu bytes of memory.", status,
2342 (unsigned long) size );
2343
2344 /* If successful, set the "magic number" in the header and also store
2345 the size. */
2346 } else {
2347 mem->magic = MAGIC( mem, size );
2348 mem->size = size;
2349 mem->next = NULL;
2350
2351 #ifdef MEM_DEBUG
2352 mem->id = -1;
2353 mem->prev = NULL;
2354 #endif
2355
2356 }
2357 }
2358
2359 /* Do nothing more if no memory is being returned. */
2360 if( mem ) {
2361
2362 #ifdef MEM_DEBUG
2363 Issue( mem, status );
2364 #endif
2365
2366 /* Increment the memory pointer to the start of the region of
2367 allocated memory to be used by the caller.*/
2368 result = mem;
2369 result = (char *) result + SIZEOF_MEMORY;
2370 }
2371 }
2372
2373 /* Return the result. */
2374 return result;
2375 }
2376 #undef ERRBUF_LEN
2377
ChrMatcher(const char * test,const char * end,const char * template,const char * pattern,const char * subs[],int nsub,int ignore,int expdoll,char *** mres,int * mlen,const char ** matchend,int * status)2378 static char *ChrMatcher( const char *test, const char *end, const char *template,
2379 const char *pattern, const char *subs[], int nsub,
2380 int ignore, int expdoll, char ***mres, int *mlen,
2381 const char **matchend, int *status ){
2382 /*
2383 * Name:
2384 * ChrMatcher
2385
2386 * Purpose:
2387 * Performs substitutions on a supplied string.
2388
2389 * Type:
2390 * Private function.
2391
2392 * Synopsis:
2393 * #include "memory.h"
2394 * char *ChrMatcher( const char *test, const char *end, const char *template,
2395 * const char *pattern, const char *subs[], int nsub,
2396 * int ignore, int expdoll, char ***mres, int *mlen,
2397 * const char **matchend, int *status )
2398
2399 * Description:
2400 * This function is performs most of the work for astChrSub.
2401
2402 * Parameters:
2403 * test
2404 * The string to be tested.
2405 * end
2406 * Pointer to the terminating null character at the end of "test".
2407 * template
2408 * The template string. See astChrSub for details.
2409 * pattern
2410 * The user supplied "pattern" string (only used for error messages).
2411 * subs
2412 * An array of strings holding the values that are to be substituted
2413 * into each parenthesised substring in "test".
2414 * nsub
2415 * The length of the subs arrays.
2416 * ignore
2417 * If non-zero, then no substitutions are performed, and any
2418 * inbalance in parentheses is ignored.
2419 * expdoll
2420 * If non-zero, then any "$1", "$2", etc, tokens in the
2421 * substitution fields will be repalced by the corresponding fields
2422 * in the test string.
2423 * mres
2424 * Address of a location at which to return a pointer to an array
2425 * of character string pointers. The strings are the sub-sections
2426 * of "test" that matched the parenthesised sub-sections of
2427 * "template". The array will have "*m" elements. Ignored if NULL.
2428 * mlen
2429 * Address of a location at which to return the length of the
2430 * returned "mres" array. Ignored if "mres" is NULL.
2431 * matchend
2432 * A pointer to a location at which to return a pointer to the
2433 * character that follows the last character within the supplied test
2434 * string that matched any parenthesises sub-section of "regexp". A
2435 * NULL pointer is returned if no matches were found. A NULL pointer
2436 * may be supplied if the location of the last matching character is
2437 * not needed.
2438 * status
2439 * Pointer to the inherited status variable.
2440
2441 * Returned Value:
2442 * A pointer to a dynamically allocated string holding the result of the
2443 * substitutions, or NULL if the test string does not match the template
2444 * string. This string should be freed using astFree when no longer
2445 * needed.
2446
2447 * Notes:
2448 * - A NULL pointer is returned if this function is invoked with the
2449 * global error status set or if it should fail for any reason, or if
2450 * the supplied test string does not match the template.
2451 */
2452
2453 /* Local Variables: */
2454 char **matches;
2455 char **newsubs;
2456 char **parts;
2457 char *allowed;
2458 char *r;
2459 char *result;
2460 char *sres;
2461 char *stest;
2462 char stemp[10];
2463 const char *aaa;
2464 const char *aa;
2465 const char *a;
2466 const char *b;
2467 int allow;
2468 int dollar;
2469 int end_sub;
2470 int greedy;
2471 int i;
2472 int in_sub;
2473 int ipart;
2474 int match;
2475 int matchlen;
2476 int max_na;
2477 int min_na;
2478 int na;
2479 int nb;
2480 int nmatch;
2481 int npart;
2482 int partlen;
2483 int reslen;
2484 int start_sub;
2485 size_t stl;
2486
2487 /* Initialisation. */
2488 if( mres ) *mlen = 0;
2489 aaa = NULL;
2490 if( matchend ) *matchend = NULL;
2491
2492 /* Check the global error status. */
2493 if( !astOK ) return NULL;
2494
2495 /* more initialisation. */
2496 result = NULL;
2497 allowed = NULL;
2498
2499 /* Get memory for a set of pointers to copies of the test sub-strings that
2500 fall between the sub-strings being replaced. */
2501 parts = astMalloc( sizeof( char *)*(size_t) ( nsub + 1 ) );
2502
2503 /* Get memory for a set of pointers to copies of the test sub-strings that
2504 match the parenthesised sub-strings in the template. */
2505 matches = astMalloc( sizeof( char *)*(size_t) nsub );
2506
2507 /* Initialise pointers to the next test and template characters to read. */
2508 a = test;
2509 b = template;
2510
2511 /* Initialise the pointer to the start of the previous test character */
2512 aa = test;
2513
2514 /* Assume the test string matches the template. */
2515 match = 1;
2516
2517 /* The template pointer is not currently in a substitution field. */
2518 in_sub = 0;
2519
2520 /* Initialise the number of substitution fields found so far. */
2521 npart = 0;
2522 nmatch = 0;
2523
2524 /* Loop until we have reached the end of either the test or template
2525 string. We break out of the loop early if we find that the test string
2526 does not match the template string. */
2527 while( match && *a && *b ) {
2528
2529 /* Examine the string at the start of the template string. This returns a
2530 string of allowed (or disallowed) characters that the next test character
2531 can match, the number of template characters consumed, the minimum number
2532 of test characters that must match the allowed character set, and a flag
2533 indicating if the number of matching test characters can exceed the
2534 minimum number or must be exactly equal to the minimum number. */
2535 allowed = CheckTempStart( template, b, pattern, allowed, &nb, &allow,
2536 &min_na, &max_na, &start_sub, &end_sub,
2537 &greedy, status );
2538 if( !astOK ) break;
2539
2540 /* Increment the the pointer to the next template character. */
2541 b += nb;
2542
2543 /* If the leading field in the template indicates the start of a
2544 substitution field, record the test string up to the current point. */
2545 if( start_sub ){
2546
2547 /* Do nothing if we are ignoring substitutions. */
2548 if( ! ignore ){
2549
2550 /* Store a pointer to the first test character that matches the
2551 substitution template. */
2552 aaa = a;
2553
2554 /* Report an error and abort if we are already inside a substitution
2555 field */
2556 if( in_sub ) {
2557 astError( AST__BADSUB, "Invalid pattern matching template \"%s\": "
2558 "missing ')'.", status, pattern );
2559 break;
2560 }
2561
2562 /* Indicate that we are now in a substitution field. */
2563 in_sub = 1;
2564
2565 /* If possible, store a copy of the test string that started at the end
2566 of the previous substitution field and ends at the current point.
2567 First ensure the "parts" array is large enough since the string may
2568 contain more than "nsub" parenthesised sub-strings. */
2569 parts = astGrow( parts, npart + 1, sizeof( char * ) );
2570 if( parts ) {
2571 partlen = ( a - aa );
2572 parts[ npart ] = astStore( NULL, aa, partlen + 1 );
2573 if( parts[ npart ] ) {
2574 parts[ npart ][ partlen ] = 0;
2575 npart++;
2576 }
2577 }
2578 }
2579
2580 /* If the leading field in the template indicates the end of a
2581 substitution field, initialise the start of the next part of the test
2582 string. */
2583 } else if( end_sub ){
2584
2585 /* Do nothing if we are ignoring substitutions. */
2586 if( ! ignore ){
2587
2588 /* Report an error and abort if we are not currently in a substitution
2589 field. */
2590 if( ! in_sub ) {
2591 astError( AST__BADSUB, "Invalid pattern matching template \"%s\": "
2592 "missing '('.", status, pattern );
2593 break;
2594 }
2595
2596 /* We are no longer in a substitution field. */
2597 in_sub = 0;
2598
2599 /* If possible, store a copy of the test string that matched the
2600 substitution template. */
2601 matches = astGrow( matches, nmatch + 1, sizeof( char * ) );
2602 if( matches ) {
2603 matchlen = ( a - aaa );
2604 matches[ nmatch ] = astStore( NULL, aaa, matchlen + 1 );
2605 if( matches[ nmatch ] ) {
2606 matches[ nmatch ][ matchlen ] = 0;
2607 nmatch++;
2608 if( matchend ) *matchend = a;
2609 }
2610 }
2611
2612 /* Record the start of the next test string part. */
2613 aa = a;
2614 }
2615
2616 /* Otherwise, find how many characters at the front of the test string
2617 match the leading field in the template. Find the number of leading
2618 characters in the test string that are contained in the set of
2619 characters allowed by the leading field in the template. */
2620 } else {
2621 if( !allowed ) {
2622 na = strlen( a );
2623
2624 } else if( allow ) {
2625 na = strspn( a, allowed );
2626
2627 } else {
2628 na = strcspn( a, allowed );
2629 }
2630
2631 /* Check that the minmum number of matching characters is available. */
2632 if( na < min_na ){
2633 match = 0;
2634 break;
2635 }
2636
2637 /* Dont match more characters than are needed. */
2638 if( na > max_na ) na = max_na;
2639
2640 /* We cannot match more characters than are available. */
2641 if( na < max_na ) max_na = na;
2642
2643 /* If we have exhausted the template, match the maximum number of
2644 characters. */
2645 if( ! *b ) {
2646 na = max_na;
2647
2648 /* If we still have a match, we may choose to use fewer than the max
2649 allowed number of test characters in order to allow the next template
2650 field to be matched. Don't need to do this if we have reached the end
2651 of the template. */
2652 } else if( max_na > min_na ) {
2653 match = 0;
2654
2655 /* If a greedy quantifier was used, try using a decreasing number of test
2656 characters, starting at the maximum allowed and decreasing down to the
2657 minimum, until a number is found which allows the rest of the string
2658 to be matched. */
2659 if( greedy ) {
2660 for( na = max_na; na >= min_na; na-- ) {
2661 r = ChrMatcher( a + na, end, b, pattern, NULL, 0, 1, 0,
2662 NULL, NULL, NULL, status );
2663 if( r ) {
2664 match = 1;
2665 r = astFree( r );
2666 break;
2667 }
2668 }
2669
2670 /* If a non-greedy quantifier was used, try using an increasing number of
2671 test characters, starting at the minimum allowed and increasing up to
2672 the maximum, until a number is found which allows the rest of the string
2673 to be matched. */
2674 } else {
2675 for( na = min_na; na <= max_na; na++ ) {
2676 r = ChrMatcher( a + na, end, b, pattern, NULL, 0, 1, 0,
2677 NULL, NULL, NULL, status );
2678 if( r ) {
2679 match = 1;
2680 r = astFree( r );
2681 break;
2682 }
2683 }
2684 }
2685 }
2686
2687 /* Increment the the pointer to the next test character. */
2688 a += na;
2689 if( a > end ) a = end;
2690 }
2691 }
2692
2693 /* If the test string is finished but the template string is not, see if
2694 the next part of the template string will match a null test string.
2695 But ignore the ends of substitution fields. */
2696 if( !*a && *b && match ) {
2697 while( *b && *b != ')' ) {
2698 allowed = CheckTempStart( template, b, pattern, allowed, &nb, &allow,
2699 &min_na, &max_na, &start_sub, &end_sub,
2700 &greedy, status );
2701 b += nb;
2702 allowed = astFree( allowed );
2703
2704 if( min_na > 0 ) {
2705 match = 0;
2706 break;
2707 }
2708 }
2709 }
2710
2711 /* If the next character in the template is a closing parenthesis, then
2712 we are finishing a substitution field. */
2713 if( match && *b == ')' ) {
2714
2715 /*Check we are not ignoring substitutions. */
2716 if( ! ignore ){
2717
2718 /* Report an error and abort if we are not currently in a substitution
2719 field. */
2720 if( ! in_sub ) {
2721 astError( AST__BADSUB, "Invalid pattern matching template \"%s\": "
2722 "missing '('.", status, pattern );
2723 }
2724
2725 /* We are no longer in a substitution field. */
2726 in_sub = 0;
2727
2728 /* If possible, store a copy of the test string that matched the
2729 substitution field. */
2730 matches = astGrow( matches, nmatch + 1, sizeof( char * ) );
2731 if( matches ) {
2732 matchlen = ( a - aaa );
2733 matches[ nmatch ] = astStore( NULL, aaa, matchlen + 1 );
2734 if( matches[ nmatch ] ) {
2735 matches[ nmatch ][ matchlen ] = 0;
2736 nmatch++;
2737 if( matchend ) *matchend = a;
2738 }
2739 }
2740
2741 aa = a;
2742 }
2743 b++;
2744 }
2745
2746 /* If the test string is finished but the template string is not, see if
2747 the rest of the template string will match a null test string. */
2748 if( !*a && *b && match ) {
2749
2750 while( *b ) {
2751 allowed = CheckTempStart( template, b, pattern, allowed, &nb, &allow,
2752 &min_na, &max_na, &start_sub, &end_sub,
2753 &greedy, status );
2754 b += nb;
2755 allowed = astFree( allowed );
2756
2757 if( min_na > 0 ) {
2758 match = 0;
2759 break;
2760 }
2761 }
2762
2763 }
2764
2765 /* No match if either string was not used completely. */
2766 if( *a || *b ) match = 0;
2767
2768 /* Report an error if we are still inside a substitution field */
2769 if( match && in_sub && !ignore ) {
2770 astError( AST__BADSUB, "Invalid pattern matching template \"%s\": "
2771 "missing ')'.", status, pattern );
2772 match = 0;
2773 }
2774
2775 /* If we have a match, construct the returned string. */
2776 if( match && parts ) {
2777
2778 /* Store the test string following the final substitution field. */
2779 parts = astGrow( parts, npart + 1, sizeof( char * ) );
2780 if( parts ) {
2781 partlen = ( a - aa );
2782 parts[ npart ] = astStore( NULL, aa, partlen + 1 );
2783 if( parts[ npart ] ) {
2784 parts[ npart ][ partlen ] = 0;
2785 npart++;
2786 }
2787 }
2788
2789 /* If required, expand $1, $2, etc within the replacement strings. */
2790 if( expdoll) {
2791 newsubs = astMalloc( sizeof( char * )*nsub );
2792 if( newsubs ) {
2793 for( i = 0; i < nsub; i++ ) {
2794 stl = strlen( subs[ i ] );
2795 stest = astStore( NULL, subs[ i ], stl + 1 );
2796 for( dollar = 1; dollar <= nsub; dollar++ ) {
2797 sprintf( stemp, ".*($%d).*", dollar );
2798 sres = ChrMatcher( stest, stest + stl, stemp, stemp,
2799 (void *) ( matches + dollar - 1 ),
2800 1, 0, 0, NULL, NULL, NULL, status );
2801 if( sres ) {
2802 (void) astFree( stest );
2803 stest = sres;
2804 }
2805 }
2806 newsubs[ i ] = stest;
2807 }
2808 }
2809
2810 } else {
2811 newsubs = (char **) subs;
2812 }
2813
2814 /* Concatenate the sub-strings to form the final string. */
2815 reslen = 0;
2816 for( ipart = 0; ipart < npart - 1; ipart++ ) {
2817 result = astAppendString( result, &reslen, parts[ ipart ] );
2818 if( ipart < nsub ) {
2819 result = astAppendString( result, &reslen, newsubs[ ipart ] );
2820 } else {
2821 result = astAppendString( result, &reslen, matches[ ipart ] );
2822 }
2823 }
2824 result = astAppendString( result, &reslen, parts[ ipart ] );
2825
2826 /* Free resources. */
2827 if( newsubs && newsubs != (char **) subs ) {
2828 for( i = 0; i < nsub; i++ ) {
2829 newsubs[ i ] = astFree( newsubs[ i ] );
2830 }
2831 newsubs = astFree( newsubs );
2832 }
2833 }
2834
2835 allowed = astFree( allowed );
2836 for( ipart = 0; ipart < npart; ipart++ ) {
2837 parts[ ipart ] = astFree( parts[ ipart ] );
2838 }
2839 parts = astFree( parts );
2840
2841 /* If required, return the array holding the test sub-strings that
2842 matched the parenthesised template sub-strings, together with
2843 the length of the array. Otherwise, free the memory holding these
2844 strings. */
2845 if( mres ) {
2846 *mres = matches;
2847 *mlen = nmatch;
2848
2849 } else if( matches ) {
2850 for( i = 0; i < nmatch; i++ ) {
2851 matches[ i ] = astFree( matches[ i ] );
2852 }
2853 matches = astFree( matches );
2854
2855 }
2856
2857 /* Return the result. */
2858 return result;
2859 }
2860
astMemCaching_(int newval,int * status)2861 int astMemCaching_( int newval, int *status ){
2862 /*
2863 *++
2864 * Name:
2865 * astMemCaching
2866
2867 * Purpose:
2868 * Controls whether allocated but unused memory is cached in this module.
2869
2870 * Type:
2871 * Public function.
2872
2873 * Synopsis:
2874 * #include "memory.h"
2875 * int astMemCaching( int newval )
2876
2877 * Description:
2878 * This function sets a flag indicating if allocated but unused memory
2879 * should be cached or not. It also returns the original value of the
2880 * flag.
2881 *
2882 * If caching is switched on or off as a result of this call, then the
2883 * current contents of the cache are discarded.
2884 *
2885 * Note, each thread has a separate cache. Calling this function
2886 * affects only the currently executing thread.
2887
2888 * Parameters:
2889 * newval
2890 * The new value for the MemoryCaching tuning parameter (see
2891 * astTune in objectc.c). If AST__TUNULL is supplied, the current
2892 * value is left unchanged.
2893
2894 * Returned Value:
2895 * astMemCaching()
2896 * The original value of the MemoryCaching tuning parameter.
2897
2898 *--
2899 */
2900
2901 /* Local Variables: */
2902 astDECLARE_GLOBALS
2903 int i;
2904 int result;
2905 Memory *mem;
2906
2907 #ifdef MEM_DEBUG
2908 int id_list_size;
2909 int *id_list;
2910 #endif
2911
2912 /* Check the global error status. */
2913 if ( !astOK ) return 0;
2914
2915 /* If needed, get a pointer to the thread specific global data structure. */
2916 astGET_GLOBALS(NULL);
2917
2918 /* Store the original value of the tuning parameter. */
2919 result = use_cache;
2920
2921 /* If a new value is to be set. */
2922 if( newval != AST__TUNULL ) {
2923
2924 /* If the cache has been initialised, empty it. */
2925 if( cache_init ) {
2926
2927 /* If we are listing the ID of every memory block in the cache, count the
2928 number of blocks in the cache and then allocate an array to store the ID
2929 values in. This is done so that we can sort them before displaying them. */
2930 #ifdef MEM_DEBUG
2931 if( List_Cache ) {
2932 Memory *next;
2933
2934 id_list_size = 0;
2935 for( i = 0; i <= MXCSIZE; i++ ) {
2936 next = cache[ i ];
2937 while( next ) {
2938 id_list_size++;
2939 next = next->next;
2940 }
2941 }
2942
2943 id_list = MALLOC( sizeof(int)*id_list_size );
2944 if( !id_list ) {
2945 astError( AST__INTER, "astMemCaching: Cannot allocate %lu "
2946 "bytes of memory", status, (unsigned long)(sizeof(int)*id_list_size) );
2947 }
2948
2949 id_list_size = 0;
2950
2951 } else {
2952 id_list = NULL;
2953 }
2954 #endif
2955
2956 for( i = 0; i <= MXCSIZE; i++ ) {
2957 while( cache[ i ] ) {
2958 mem = cache[ i ];
2959 cache[ i ] = mem->next;
2960 mem->size = (size_t) i;
2961
2962 #ifdef MEM_DEBUG
2963 if( id_list ) {
2964 id_list[ id_list_size++ ] = mem->id;
2965 }
2966 #endif
2967
2968 FREE( mem );
2969 }
2970 }
2971
2972 /* If we are displaying the IDs of memory blocks still in the cache, sort
2973 them using a bubblesort algorithm, then display them. */
2974 #ifdef MEM_DEBUG
2975 if( id_list ) {
2976
2977 if( id_list_size == 0 ) {
2978 printf( "Emptying the AST memory cache - (the cache is "
2979 "already empty)\n" );
2980
2981 } else {
2982 int sorted, j, t;
2983
2984 sorted = 0;
2985 for( j = id_list_size - 2; !sorted && j >= 0; j-- ) {
2986 sorted = 1;
2987 for( i = 0; i <= j; i++ ) {
2988 if( id_list[ i ] > id_list[ i + 1 ] ) {
2989 sorted = 0;
2990 t = id_list[ i ];
2991 id_list[ i ] = id_list[ i + 1 ];
2992 id_list[ i + 1 ] = t;
2993 }
2994 }
2995 }
2996
2997 printf( "Emptying the AST memory cache - freeing the "
2998 "following memory blocks: ");
2999 for( i = 0; i < id_list_size; i++ ) printf( "%d ", id_list[ i ] );
3000 printf( "\n" );
3001
3002 }
3003 }
3004 #endif
3005
3006 /* Otherwise, initialise the cache array to hold a NULL pointer at every
3007 element. */
3008 } else {
3009 for( i = 0; i <= MXCSIZE; i++ ) cache[ i ] = NULL;
3010 cache_init = 1;
3011 }
3012
3013 /* Store the new value. */
3014 use_cache = newval;
3015
3016 }
3017
3018 /* Return the original value. */
3019 return result;
3020 }
3021
astRealloc_(void * ptr,size_t size,int * status)3022 void *astRealloc_( void *ptr, size_t size, int *status ) {
3023 /*
3024 *++
3025 * Name:
3026 * astRealloc
3027
3028 * Purpose:
3029 * Change the size of a dynamically allocated region of memory.
3030
3031 * Type:
3032 * Public function.
3033
3034 * Synopsis:
3035 * #include "memory.h"
3036 * void *astRealloc( void *ptr, size_t size )
3037
3038 * Description:
3039 * This function changes the size of a dynamically allocated region
3040 * of memory, preserving its contents up to the minimum of the old
3041 * and new sizes. This may involve copying the contents to a new
3042 * location, so a new pointer is returned (and the old memory freed
3043 * if necessary).
3044 *
3045 * This function is similar to the standard C "realloc" function
3046 * except that it provides better security against programming
3047 * errors and also supports the allocation of zero-size memory
3048 * regions (indicated by a NULL pointer).
3049
3050 * Parameters:
3051 * ptr
3052 * Pointer to previously allocated memory (or NULL if the
3053 * previous size of the allocated memory was zero).
3054 * size
3055 * New size required for the memory region. This may be zero, in
3056 * which case a NULL pointer is returned (no error results). It
3057 * should not be negative.
3058
3059 * Returned Value:
3060 * astRealloc()
3061 * If the memory was reallocated successfully, a pointer to the
3062 * start of the new memory region is returned (this may be the same
3063 * as the original pointer). If size was given as zero, a NULL
3064 * pointer is returned.
3065
3066 * Notes:
3067 * - If this function is invoked with the error status set, or if
3068 * it fails for any reason, the original pointer value is returned
3069 * and the memory contents are unchanged. Note that this behaviour
3070 * differs from that of the standard C "realloc" function which
3071 * returns NULL if it fails.
3072 *--
3073 */
3074
3075 /* Local Constants: */
3076 #define ERRBUF_LEN 80
3077
3078 /* Local Variables: */
3079 astDECLARE_GLOBALS
3080 char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */
3081 char *errstat; /* Pointer to system error message */
3082 int isdynamic; /* Was memory allocated dynamically? */
3083 void *result; /* Returned pointer */
3084 Memory *mem; /* Pointer to memory header */
3085
3086 /* Check the global error status. */
3087 if ( !astOK ) return ptr;
3088
3089 /* Initialise. */
3090 result = ptr;
3091
3092 /* If needed, get a pointer to the thread specific global data structure. */
3093 astGET_GLOBALS(NULL);
3094
3095 /* If a NULL pointer was supplied, use astMalloc to allocate some new
3096 memory. */
3097 if ( !ptr ) {
3098 result = astMalloc( size );
3099
3100 /* Otherwise, check that the pointer supplied points at memory
3101 allocated by a function in this module (IsDynamic sets the global
3102 error status if it does not). */
3103 } else {
3104 IS_DYNAMIC( ptr, isdynamic );
3105 if ( isdynamic ) {
3106
3107 /* Check that a negative size has not been given and report an error
3108 if necessary. */
3109 if ( size < (size_t) 0 ) {
3110 astError( AST__MEMIN,
3111 "Invalid attempt to reallocate a block of memory to %ld bytes.", status,
3112 (long) size );
3113
3114 /* If OK, obtain a pointer to the memory header. */
3115 } else {
3116 mem = (Memory *) ( (char *) ptr - SIZEOF_MEMORY );
3117
3118 /* If the new size is zero, free the old memory and set a NULL return
3119 pointer value. */
3120 if ( size == (size_t) 0 ) {
3121 astFree( ptr );
3122 result = NULL;
3123
3124 /* Otherwise, reallocate the memory. */
3125 } else {
3126
3127 /* If the cache is being used, for small memory blocks, do the equivalent of
3128 mem = REALLOC( mem, SIZEOF_MEMORY + size );
3129
3130 using astMalloc, astFree and memcpy explicitly in order to ensure
3131 that the memory blocks are cached. */
3132 if( use_cache && ( mem->size <= MXCSIZE || size <= MXCSIZE ) ) {
3133 result = astMalloc( size );
3134 if( result ) {
3135 if( mem->size < size ) {
3136 memcpy( result, ptr, mem->size );
3137 } else {
3138 memcpy( result, ptr, size );
3139 }
3140 astFree( ptr );
3141
3142 } else {
3143 result = ptr;
3144 }
3145
3146 /* For other memory blocks simply use realloc. */
3147 } else {
3148
3149 #ifdef MEM_DEBUG
3150 DeIssue( mem, status );
3151 #endif
3152
3153 mem = REALLOC( mem, SIZEOF_MEMORY + size );
3154
3155 /* If this failed, report an error and return the original pointer
3156 value. */
3157 if ( !mem ) {
3158 #if HAVE_STRERROR_R
3159 strerror_r( errno, errbuf, ERRBUF_LEN );
3160 errstat = errbuf;
3161 #else
3162 errstat = strerror( errno );
3163 #endif
3164 astError( AST__NOMEM, "realloc: %s", status, errstat );
3165 astError( AST__NOMEM, "Failed to reallocate a block of "
3166 "memory to %ld bytes.", status, (long) size );
3167
3168 /* If successful, set the new "magic" value and size in the memory
3169 header and obtain a pointer to the start of the region of memory to
3170 be used by the caller. */
3171 } else {
3172 mem->magic = MAGIC( mem, size );
3173 mem->size = size;
3174 mem->next = NULL;
3175 #ifdef MEM_DEBUG
3176 mem->id = -1;
3177 mem->prev = NULL;
3178 Issue( mem, status );
3179 #endif
3180 result = mem;
3181 result = (char *) result + SIZEOF_MEMORY;
3182 }
3183 }
3184 }
3185 }
3186 }
3187 }
3188
3189 /* Return the result. */
3190 return result;
3191 }
3192 #undef ERRBUF_LEN
3193
astRemoveLeadingBlanks_(char * string,int * status)3194 void astRemoveLeadingBlanks_( char *string, int *status ) {
3195 /*
3196 *++
3197 * Name:
3198 * astRemoveLeadingBlanks
3199
3200 * Purpose:
3201 * Remove any leading white space from a string.
3202
3203 * Type:
3204 * Public function.
3205
3206 * Synopsis:
3207 * #include "memory.h"
3208 * void astRemoveLeadingBlanks( char *string )
3209
3210 * Description:
3211 * This function moves characters in the supplied string to the left
3212 * in order to remove any leading white space.
3213
3214 * Parameters:
3215 * string
3216 * Pointer to the string.
3217
3218 *--
3219 */
3220
3221 /* Local Variables: */
3222 char *c, *d;
3223
3224 /* Check a string has been supplied. */
3225 if( string ){
3226
3227 /* Get a pointer to the first non-white character in the string. */
3228 c = string;
3229 while( *c && isspace( *c ) ) c++;
3230
3231 /* Do nothing more if there are no leading spaces. */
3232 if( c > string ) {
3233
3234 /* Copy all characters (excluding the trailing null) to the left to
3235 over-write the leading spaces. */
3236 d = string;
3237 while( *c ) *(d++) = *(c++);
3238
3239 /* Terminate the returned string. */
3240 *d = 0;
3241 }
3242 }
3243 }
3244
astSizeOf_(const void * ptr,int * status)3245 size_t astSizeOf_( const void *ptr, int *status ) {
3246 /*
3247 *++
3248 * Name:
3249 * astSizeOf
3250
3251 * Purpose:
3252 * Determine the size of a dynamically allocated region of memory.
3253
3254 * Type:
3255 * Public function.
3256
3257 * Synopsis:
3258 * #include "memory.h"
3259 * size_t astSizeOf( const void *ptr )
3260
3261 * Description:
3262 * This function returns the size of a region of dynamically
3263 * allocated memory.
3264
3265 * Parameters:
3266 * ptr
3267 * Pointer to dynamically allocated memory (or NULL if the size
3268 * of the allocated memory was zero).
3269
3270 * Returned Value:
3271 * astSizeOf()
3272 * The allocated size. This will be zero if a NULL pointer was
3273 * supplied (no error will result).
3274
3275 * Notes:
3276 * - A value of zero is returned if this function is invoked with
3277 * the global error status set, or if it fails for any reason.
3278 *--
3279 */
3280
3281 /* Local Variables: */
3282 astDECLARE_GLOBALS /* Pointer to thread-specific global data */
3283 int isdynamic; /* Was the memory allocated dynamically? */
3284 size_t size; /* Memory size */
3285
3286 /* Check the global error status. */
3287 if ( !astOK ) return (size_t) 0;
3288
3289 /* Initialise. */
3290 size = (size_t) 0;
3291
3292 /* If needed, get a pointer to the thread specific global data structure. */
3293 astGET_GLOBALS(NULL);
3294
3295 /* Check if a non-NULL valid pointer has been given. If so, extract
3296 the memory size from the header which precedes it. */
3297 if ( ptr ){
3298 IS_DYNAMIC( ptr, isdynamic );
3299 if( isdynamic ) size = ( (Memory *) ( (char *) ptr - SIZEOF_MEMORY ) )->size;
3300 }
3301
3302 /* Return the result. */
3303 return size;
3304 }
3305
SizeOfMemory(int * status)3306 static size_t SizeOfMemory( int *status ){
3307 /*
3308 * Name:
3309 * SizeOfMemory
3310
3311 * Purpose:
3312 * Returns the size of a Memory structure, padded to an 8 byte
3313 * boundary.
3314
3315 * Type:
3316 * Private function.
3317
3318 * Synopsis:
3319 * size_t SizeOfMemory( int *status )
3320
3321 * Description:
3322 * This function returns the size of a Memory structure used to
3323 * store header information about any block of memory allocated by this
3324 * module. The returned value may be larger than the actual size of
3325 * the Memory structure in order to ensure that the pointer returned by
3326 * astMalloc etc points to an 8 byte boundary. Failure to do this can
3327 * result in some operating systems having problems. E.g Solaris
3328 * requires this alignment if the returned pointer is going to be used to
3329 * store doubles.
3330
3331 * Parameters:
3332 * status
3333 * Pointer to the inherited status variable.
3334
3335 * Returned Value:
3336 * The size to use for a Memory structure.
3337
3338 * Notes:
3339 * - The returned value is also stored in the module variable
3340 * sizeof_memory.
3341 */
3342
3343 /* Local Variables: */
3344 astDECLARE_GLOBALS /* Pointer to thread-specific global data */
3345
3346 /* If needed, get a pointer to the thread specific global data structure. */
3347 astGET_GLOBALS(NULL);
3348
3349 /* Get the basic size of a Memory structure. */
3350 sizeof_memory = sizeof( Memory );
3351
3352 /* Now increase the returned value to ensure it is a multiple of 8. Mask
3353 off all but the last 3 bits, xor with 0x7 to get the remainder, add 1
3354 to make it a multiple of 8 bytes. */
3355 sizeof_memory += ((sizeof_memory & 0x7) ? ((sizeof_memory & 0x7) ^ 0x7) + 1 : 0);
3356
3357 /* Return the value */
3358 return sizeof_memory;
3359
3360 }
3361
astTSizeOf_(const void * ptr,int * status)3362 size_t astTSizeOf_( const void *ptr, int *status ) {
3363 /*
3364 *+
3365 * Name:
3366 * astTSizeOf
3367
3368 * Purpose:
3369 * Determine the total size of a dynamically allocated region of memory.
3370
3371 * Type:
3372 * Protected function.
3373
3374 * Synopsis:
3375 * #include "memory.h"
3376 * size_t astTSizeOf( const void *ptr )
3377
3378 * Description:
3379 * This function returns the size of a region of dynamically
3380 * allocated memory, including the extra memory used to store
3381 * the header information for the memory block (size and magic number).
3382
3383 * Parameters:
3384 * ptr
3385 * Pointer to dynamically allocated memory (or NULL if the size
3386 * of the allocated memory was zero).
3387
3388 * Returned Value:
3389 * The allocated size. This will be zero if a NULL pointer was
3390 * supplied (no error will result).
3391
3392 * Notes:
3393 * - A value of zero is returned if this function is invoked with
3394 * the global error status set, or if it fails for any reason.
3395 * - This function is documented as protected because it should not
3396 * be invoked by external code. However, it is available via the
3397 * external C interface so that it may be used when writing (e.g.)
3398 * foreign language or graphics interfaces.
3399 *-
3400 */
3401
3402 /* Local Variables: */
3403 astDECLARE_GLOBALS /* Pointer to thread-specific global data */
3404 int isdynamic; /* Was the memory allocated dynamically? */
3405 size_t size; /* Memory size */
3406
3407 /* Check the global error status. */
3408 if ( !astOK ) return (size_t) 0;
3409
3410 /* If needed, get a pointer to the thread specific global data structure. */
3411 astGET_GLOBALS(NULL);
3412
3413 /* Initialise. */
3414 size = (size_t) 0;
3415
3416 /* Check if a non-NULL valid pointer has been given. If so, extract
3417 the memory size from the header which precedes it. */
3418 if ( ptr ){
3419 IS_DYNAMIC( ptr, isdynamic );
3420 if( isdynamic ) size = SIZEOF_MEMORY +
3421 ( (Memory *) ( (char *) ptr - SIZEOF_MEMORY ) )->size;
3422 }
3423
3424 /* Return the result. */
3425 return size;
3426 }
3427
astStore_(void * ptr,const void * data,size_t size,int * status)3428 void *astStore_( void *ptr, const void *data, size_t size, int *status ) {
3429 /*
3430 *++
3431 * Name:
3432 * astStore
3433
3434 * Purpose:
3435 * Store data in dynamically allocated memory.
3436
3437 * Type:
3438 * Public function.
3439
3440 * Synopsis:
3441 * #include "memory.h"
3442 * void *astStore( void *ptr, const void *data, size_t size )
3443
3444 * Description:
3445 * This function stores data in dynamically allocated memory,
3446 * allocating the memory (or adjusting the size of previously
3447 * allocated memory) to match the amount of data to be stored.
3448
3449 * Parameters:
3450 * ptr
3451 * Pointer to previously allocated memory (or NULL if none has
3452 * yet been allocated).
3453 * data
3454 * Pointer to the start of the data to be stored. This may be
3455 * given as NULL if there are no data, in which case it will be
3456 * ignored and this function behaves like astRealloc, preserving
3457 * the existing memory contents.
3458 * size
3459 * The total size of the data to be stored and/or the size of
3460 * memory to be allocated. This may be zero, in which case the
3461 * data parameter is ignored, any previously-allocated memory is
3462 * freed and a NULL pointer is returned.
3463
3464 * Returned Value:
3465 * astStore()
3466 * If the data were stored successfully, a pointer to the start of
3467 * the possibly new memory region is returned (this may be the same
3468 * as the original pointer). If size was given as zero, a NULL
3469 * pointer is returned.
3470
3471 * Notes:
3472 * - This is a convenience function for use when storing data of
3473 * arbitrary size in memory which is to be allocated
3474 * dynamically. It is appropriate when the size of the data will
3475 * not change frequently because the size of the memory region will
3476 * be adjusted to fit the data on every invocation.
3477 * - If this function is invoked with the error status set, or if
3478 * it fails for any reason, the original pointer value is returned
3479 * and the memory contents are unchanged.
3480 *--
3481 */
3482
3483 /* Local Variables: */
3484 astDECLARE_GLOBALS /* Pointer to thread-specific global data */
3485 int valid; /* Is the memory pointer usable? */
3486 void *new; /* Pointer to returned memory */
3487
3488 /* Check the global error status. */
3489 if ( !astOK ) return ptr;
3490
3491 /* If needed, get a pointer to the thread specific global data structure. */
3492 astGET_GLOBALS(NULL);
3493
3494 /* Initialise. */
3495 new = ptr;
3496
3497 /* If the new size is zero, use astRealloc to free any previously
3498 allocated memory. Also re-allocate the memory if the data pointer
3499 is NULL (in which case we want to preserve its contents). */
3500 if ( ( size == (size_t) 0 ) || !data ) {
3501 new = astRealloc( ptr, size );
3502
3503 /* In other cases, we do not want to preserve any memory
3504 contents. Check if the incoming memory pointer is valid (IsDynamic
3505 sets the global error status if it is not). */
3506 } else {
3507 if ( !ptr ){
3508 valid = 1;
3509 } else {
3510 IS_DYNAMIC( ptr, valid );
3511 }
3512 if( valid ) {
3513
3514 /* Allocate the new memory. If successful, free the old memory (if
3515 necessary) and copy the data into it. */
3516 new = astMalloc( size );
3517 if ( astOK ) {
3518 if ( ptr ) ptr = astFree( ptr );
3519 (void) memcpy( new, data, size );
3520
3521 /* If memory allocation failed, do not free the old memory but return
3522 a pointer to it. */
3523 } else {
3524 new = ptr;
3525 }
3526 }
3527 }
3528
3529 /* Return the result. */
3530 return new;
3531 }
3532
astString_(const char * chars,int nchars,int * status)3533 char *astString_( const char *chars, int nchars, int *status ) {
3534 /*
3535 *++
3536 * Name:
3537 * astString
3538
3539 * Purpose:
3540 * Create a C string from an array of characters.
3541
3542 * Type:
3543 * Public function.
3544
3545 * Synopsis:
3546 * #include "memory.h"
3547 * char *astString( const char *chars, int nchars )
3548
3549 * Description:
3550 * This function allocates memory to hold a C string and fills the
3551 * string with the sequence of characters supplied. It then
3552 * terminates the string with a null character and returns a
3553 * pointer to its start. The memory used for the string may later
3554 * be de-allocated using astFree.
3555 *
3556 * This function is intended for constructing null terminated C
3557 * strings from arrays of characters which are not null terminated,
3558 * such as when importing a character argument from a Fortran 77
3559 * program.
3560
3561 * Parameters:
3562 * chars
3563 * Pointer to the array of characters to be used to fill the string.
3564 * nchars
3565 * The number of characters in the array (zero or more).
3566
3567 * Returned Value:
3568 * astString()
3569 * If successful, the function returns a pointer to the start of
3570 * the allocated string. If the number of characters is zero, a
3571 * zero-length string is still allocated and a pointer to it is
3572 * returned.
3573
3574 * Notes:
3575 * - A pointer value of NULL is returned if this function is
3576 * invoked with the global error status set or if it fails for any
3577 * reason.
3578 *--
3579 */
3580
3581 /* Local Variables: */
3582 char *result; /* Pointer value to return */
3583
3584 /* Initialise. */
3585 result = NULL;
3586
3587 /* Check the global error status. */
3588 if ( !astOK ) return result;
3589
3590 /* Check that the number of characters in the string is valid and
3591 report an error if it is not. */
3592 if ( nchars < 0 ) {
3593 astError( AST__NCHIN, "astString: Invalid attempt to allocate a string "
3594 "with %d characters.", status, nchars);
3595
3596 /* Allocate memory to hold the string. */
3597 } else {
3598 result = (char *) astMalloc( (size_t) ( nchars + 1 ) );
3599
3600 /* If successful, copy the characters into the string. */
3601 if ( astOK && result ) {
3602 (void) memcpy( result, chars, (size_t) nchars );
3603
3604 /* Terminate the string. */
3605 result[ nchars ] = '\0';
3606 }
3607 }
3608
3609 /* Return the result. */
3610 return result;
3611 }
3612
astStringArray_(const char * chars,int nel,int len,int * status)3613 char **astStringArray_( const char *chars, int nel, int len, int *status ) {
3614 /*
3615 *++
3616 * Name:
3617 * astStringArray
3618
3619 * Purpose:
3620 * Create an array of C strings from an array of characters.
3621
3622 * Type:
3623 * Public function.
3624
3625 * Synopsis:
3626 * #include "memory.h"
3627 * char **astStringArray( const char *chars, int nel, int len )
3628
3629 * Description:
3630 * This function turns an array of fixed-length character data into
3631 * a dynamicllay allocated array of null-terminated C strings with
3632 * an index array that may be used to access them.
3633 *
3634 * The array of character data supplied is assumed to hold "nel"
3635 * adjacent fixed-length strings (without terminating nulls), each
3636 * of length "len" characters. This function allocates memory and
3637 * creates a null-terminated copy of each of these strings. It also
3638 * creates an array of "nel" pointers which point at the start of
3639 * each of these new strings. A pointer to this index array is
3640 * returned.
3641 *
3642 * The memory used is allocated in a single block and should later
3643 * be de-allocated using astFree.
3644 s
3645 * Parameters:
3646 * chars
3647 * Pointer to the array of input characters. The number of characters
3648 * in this array should be at least equal to (nel * len).
3649 * nel
3650 * The number of fixed-length strings in the input character
3651 * array. This may be zero but should not be negative.
3652 * len
3653 * The number of characters in each fixed-length input
3654 * string. This may be zero but should not be negative.
3655
3656 * Returned Value:
3657 * astStringArray()
3658 * A pointer to the start of the index array, which contains "nel"
3659 * pointers pointing at the start of each null-terminated output
3660 * string.
3661 *
3662 * The returned pointer should be passed to astFree to de-allocate
3663 * the memory used when it is no longer required. This will free
3664 * both the index array and the memory used by the strings it
3665 * points at.
3666
3667 * Notes:
3668 * - A NULL pointer will also be returned if the value of "nel" is
3669 * zero, in which case no memory is allocated.
3670 * - A pointer value of NULL will also be returned if this function
3671 * is invoked with the global error status set or if it fails for
3672 * any reason.
3673 *--
3674 */
3675
3676 /* Local Variables: */
3677 char **result; /* Result pointer to return */
3678 char *out_str; /* Pointer to start of next output string */
3679 const char *in_str; /* Pointer to start of next input string */
3680 int i; /* Loop counter for array elements */
3681
3682 /* Initialise. */
3683 result = NULL;
3684
3685 /* Check the global error status. */
3686 if ( !astOK ) return result;
3687
3688 /* Check that the array size is valid and report an error if it is
3689 not. */
3690 if ( nel < 0 ) {
3691 astError( AST__NELIN,
3692 "astStringArray: Invalid attempt to allocate an array of "
3693 "%d strings.", status, nel );
3694
3695 /* If the string length will be used, check that it is valid and
3696 report an error if it is not. */
3697 } else if ( ( nel > 0 ) && ( len < 0 ) ) {
3698 astError( AST__NCHIN,
3699 "astStringArray: Invalid attempt to allocate an "
3700 "array of strings with %d characters in each.", status, len );
3701
3702 /* Allocate memory to hold the array of string pointers plus the
3703 string data (with terminating nulls). */
3704 } else {
3705 result = astMalloc( sizeof( char * ) * (size_t) nel +
3706 (size_t) ( nel * ( len + 1 ) ) );
3707
3708 /* If successful, initialise pointers to the start of the current
3709 input and output strings. */
3710 if( astOK ){
3711 in_str = chars;
3712 out_str = (char *) ( result + nel );
3713
3714 /* Loop to copy each string. */
3715 for ( i = 0; i < nel; i++ ) {
3716 (void) memcpy( out_str, in_str, (size_t) len );
3717
3718 /* Terminate the output string. */
3719 out_str[ len ] = '\0';
3720
3721 /* Store a pointer to the start of the output string in the array of
3722 character pointers. */
3723 result[ i ] = out_str;
3724
3725 /* Increment the pointers to the start of the next string. */
3726 out_str += len + 1;
3727 in_str += len;
3728 }
3729 }
3730 }
3731
3732 /* Return the result. */
3733 return result;
3734 }
3735
astStringCase_(const char * string,int upper,int * status)3736 char *astStringCase_( const char *string, int upper, int *status ) {
3737 /*
3738 *++
3739 * Name:
3740 * astStringCase
3741
3742 * Purpose:
3743 * Convert a string to upper or lower case.
3744
3745 * Type:
3746 * Public function.
3747
3748 * Synopsis:
3749 * #include "memory.h"
3750 * char *astStringCase( const char string, int upper )
3751
3752 * Description:
3753 * This function converts a supplied string to upper or lower case,
3754 * storing the result in dynamically allocated memory. The astChrCase
3755 * function is similar, but stores the result in a supplied buffer.
3756
3757 * Parameters:
3758 * string
3759 * Pointer to the null terminated string to be converted.
3760 * upper
3761 * If non-zero, the string is converted to upper case. Otherwise it
3762 * is converted to lower case.
3763
3764 * Returned Value:
3765 * astStringCase()
3766 * If successful, the function returns a pointer to the start of
3767 * the allocated string. The returned memory should be freed using
3768 * astFree when no longer needed.
3769
3770 * Notes:
3771 * - A pointer value of NULL is returned if this function is
3772 * invoked with the global error status set or if it fails for any
3773 * reason.
3774 *--
3775 */
3776
3777 /* Local Variables: */
3778 char *pout; /* Pointer to next output character */
3779 char *result; /* Pointer value to return */
3780 const char *pin; /* Pointer to next input character */
3781 int i; /* Character index */
3782 int len; /* String length */
3783
3784 /* Initialise. */
3785 result = NULL;
3786
3787 /* Check the global error status. */
3788 if ( !astOK ) return result;
3789
3790 /* Get the length of the supplied string, excluding the trailing null. */
3791 len = strlen( string );
3792
3793 /* Allocate memory to hold the converted string, plus terminating null. */
3794 result = (char *) astMalloc( (size_t) ( len + 1 ) );
3795
3796 /* If successful, copy the characters into the string, converting each
3797 one to the requested case. */
3798 if ( result ) {
3799 pin = string;
3800 pout = result;
3801
3802 if( upper ) {
3803 for( i = 0; i < len; i++ ) {
3804 *(pout++) = toupper( (int) *(pin++) );
3805 }
3806
3807 } else {
3808
3809 for( i = 0; i < len; i++ ) {
3810 *(pout++) = tolower( (int) *(pin++) );
3811 }
3812 }
3813
3814 /* Terminate the string. */
3815 *pout = '\0';
3816 }
3817
3818 /* Return the result. */
3819 return result;
3820 }
3821
astChrLen_(const char * string,int * status)3822 size_t astChrLen_( const char *string, int *status ) {
3823 /*
3824 *++
3825 * Name:
3826 * astChrLen
3827
3828 * Purpose:
3829 * Determine the used length of a string.
3830
3831 * Type:
3832 * Public function.
3833
3834 * Synopsis:
3835 * #include "memory.h"
3836 * size_t astChrLen( const char *string )
3837
3838 * Description:
3839 * This function returns the used length of a string. This excludes any
3840 * trailing white space or non-printable characters (such as the
3841 * trailing null character).
3842
3843 * Parameters:
3844 * string
3845 * Pointer to the string.
3846
3847 * Returned Value:
3848 * astChrLen()
3849 * The number of characters in the supplied string, not including the
3850 * trailing newline, and any trailing white-spaces or non-printable
3851 * characters.
3852
3853 *--
3854 */
3855
3856 /* Local Variables: */
3857 const char *c; /* Pointer to the next character to check */
3858 size_t ret; /* The returned string length */
3859
3860 /* Initialise the returned string length. */
3861 ret = 0;
3862
3863 /* Check a string has been supplied. */
3864 if( string ){
3865
3866 /* Check each character in turn, starting with the last one. */
3867 ret = strlen( string );
3868 c = string + ret - 1;
3869 while( ret ){
3870 if( isprint( (int) *c ) && !isspace( (int) *c ) ) break;
3871 c--;
3872 ret--;
3873 }
3874 }
3875
3876 /* Return the answer. */
3877 return ret;
3878
3879 }
3880
astSscanf_(const char * str,const char * fmt,...)3881 int astSscanf_( const char *str, const char *fmt, ...) {
3882 /*
3883 *+
3884 * Name:
3885 * astSscanf
3886
3887 * Purpose:
3888 * A wrapper for the ANSI sscanf function.
3889
3890 * Type:
3891 * Protected function.
3892
3893 * Synopsis:
3894 * #include "memory.h"
3895 * int astSscanf( const char *str, const char *fmt, ...)
3896
3897 * Description:
3898 * This function is a direct plug-in replacement for sscanf. It ensures ANSI
3899 * behaviour is available on all platforms, including those (such as
3900 * MacOS) on which have the native sscanf function exhibits non-ANSI
3901 * behaviour.
3902
3903 * Parameters:
3904 * str
3905 * Pointer to the string to be scanned.
3906 * fmt
3907 * Pointer to the format string which defines the fields to be
3908 * looked for within "str".
3909 * ...
3910 * Pointers to locations at which to return the value of each
3911 * succesfuly converted field, in the order specified in "fmt".
3912
3913 * Returned Value:
3914 * The number of fields which were succesfully read from "str".
3915 *-
3916 */
3917
3918 /* Local Variables: */
3919 char *c; /* Pointer to the next character to check */
3920 char *newfor; /* Pointer to modified format string */
3921 const char *d; /* Pointer to the next character to check */
3922 int *status; /* Pointer to inherited status value */
3923 int iptr; /* Index into ptr array */
3924 int lfor; /* No. of characters in format string */
3925 int lstr; /* No. of characters in scanned string */
3926 int nc; /* No. of characters read from str */
3927 int nfld; /* No. of counted field specifiers found so far */
3928 int nptr; /* Np. of pointers stored */
3929 int ret; /* The returned number of conversions */
3930 va_list args; /* Variable argument list pointer */
3931 void *fptr; /* The next supplied pointer */
3932 void *ptr[ VMAXFLD ]; /* Array of supplied pointers */
3933
3934 /* Initialise the variable argument list pointer. */
3935 va_start( args, fmt );
3936
3937 /* Get a pointer to the integer holding the inherited status value. */
3938 status = astGetStatusPtr;
3939
3940 /* Initialise the returned string length. */
3941 ret = 0;
3942
3943 /* Check a string and format have been supplied. */
3944 if( str && fmt ){
3945
3946 /* Go through the format string, counting the number of field specifiers which
3947 will return a value, and storing the corresponding points in the ptr
3948 array. */
3949 nptr = 0;
3950 c = (char *) fmt;
3951 while( *c ) {
3952
3953 /* Field specifiers are marked by a % sign. */
3954 if( *c == '%' ) {
3955
3956 /* Look at the character following the % sign. Quit if the end of the string
3957 has been reached. */
3958 c++;
3959 if( *c ) {
3960
3961 /* If the % sign is followed by a "*" or another "%", then there will be no
3962 corresponding pointer in the variable argument list "args". Ignore such
3963 field specifiers. */
3964 if( *c != '*' && *c != '%' ) {
3965
3966 /* If possible store the corresponding pointer from the variable argument
3967 list supplied to this function. Report an error if there are too many. */
3968 if ( nptr < VMAXFLD ) {
3969 ptr[ nptr++ ] = va_arg( args, void *);
3970
3971 /* If the current field specifier is "%n" the corresponding pointer
3972 should be a pointer to an integer. We initialise the integer to zero.
3973 We need to do this because sscanf does not include "%n" values in the
3974 returned count of succesful conversions, and so there is no sure way
3975 of knowing whether a value has been stored for a "%n" field, and so
3976 whether it is safe to use it as an index into the supplied. */
3977 if( *c == 'n' ) *( (int *) ptr[ nptr - 1 ] ) = 0;
3978
3979 } else {
3980 astError( AST__INTER, "astSscanf: Format string "
3981 "'%s' contains more than %d fields "
3982 "(AST internal programming error).", status,
3983 fmt, VMAXFLD );
3984 break;
3985 }
3986 }
3987
3988 /* Move on the first character following the field specifier. */
3989 c++;
3990 }
3991
3992 /* If this is not the start of a field specifier, pass on. */
3993 } else {
3994 c++;
3995 }
3996 }
3997
3998 /* Fill any unused pointers with NULL. */
3999 for( iptr = nptr; iptr < VMAXFLD; iptr++ ) ptr[iptr] = NULL;
4000
4001 /* Get the length of the string to be scanned. */
4002 lstr = strlen( str );
4003
4004 /* Get the length of the format string excluding any trailing white space. */
4005 lfor = astChrLen( fmt );
4006
4007 /* Bill Joye reports that MacOS sscanf fails to return the correct number of
4008 characters read (using a %n conversion) if there is a space before the
4009 %n. So check for this. Does the format string contain " %n"? */
4010 c = strstr( fmt, " %n" );
4011 if( c && astOK ) {
4012
4013 /* Take a copy of the supplied format string (excluding any trailing spaces). */
4014 newfor = (char *) astStore( NULL, (void *) fmt, (size_t) lfor + 1 );
4015 if( newfor ) {
4016
4017 /* Ensure the string is terminated (in case the supplied format string
4018 has any trailing spaces). */
4019 newfor[ lfor ] = 0;
4020
4021 /* Remove all spaces from before any %n. */
4022 c = strstr( (const char *) newfor, " %n" );
4023 while( c ) {
4024 while( *(c++) ) *( c - 1 ) = *c;
4025 c = strstr( newfor, " %n" );
4026 }
4027
4028 /* Use the native sscanf with the modified format string. Note, we cannot
4029 use vsscanf because it is not ANSI C. Instead, we list the pointers
4030 explicitly. */
4031 ret = sscanf( str, newfor, ptr[0], ptr[1], ptr[2], ptr[3],
4032 ptr[4], ptr[5], ptr[6], ptr[7], ptr[8], ptr[9],
4033 ptr[10], ptr[11], ptr[12], ptr[13], ptr[14],
4034 ptr[15], ptr[16], ptr[17], ptr[18], ptr[19] );
4035
4036 /* Now look through the original format string for conversions specifiers.
4037 If any %n conversions are found which are preceded by a space, then
4038 correct the returned character counts to include any spaces following the
4039 corresponding point in the scanned string. */
4040 nfld = 0;
4041 iptr = 0;
4042 c = (char *) fmt;
4043 while( *c ) {
4044
4045 /* Field specifiers are marked by a % sign. */
4046 if( *c == '%' ) {
4047
4048 /* Look at the character following the % sign. Quit if the end of the string
4049 has been reached. */
4050 c++;
4051 if( *c ) {
4052
4053 /* If the % sign is followed by a "*" or another "%", then there will be no
4054 corresponding pointer in the variable argument list "args". Ignore such
4055 field specifiers. */
4056 if( *c != '*' && *c != '%' ) {
4057
4058 /* Get the supplied pointer corresponding to this field specifier. */
4059 fptr = ptr[ iptr++ ];
4060
4061 /* Increment the number of matched fields required. "%n" specifiers are not
4062 included in the value returned by sscanf so skip over them. */
4063 if( *c != 'n' ) {
4064 nfld++;
4065
4066 /* If the % sign is followed by a "n", and was preceded by a space, we
4067 may need to correct the returned character count. */
4068 } else if( c > fmt + 1 && *(c-2) == ' ' ) {
4069
4070 /* Do not correct the returned value if sscanf did not get as far as this
4071 field specifier before an error occurred. */
4072 if( ret >= nfld ) {
4073
4074 /* Get the original character count produced by sscanf. */
4075 nc = *( (int *) fptr );
4076
4077 /* For each space in "str" which follows, increment the returned count by
4078 one (so long as the original count is not zero or more than the length
4079 of the string - this is not foolproof, but I can't think of a better
4080 check - all uses of %n in AST initialize the supplied count to zero
4081 before calling sscanf so a value fo zero is a safe (ish) bet that the
4082 supplied string doesn't match the supplied format). */
4083 if( nc > 0 && nc < lstr ) {
4084 d = str + nc;
4085 while( *(d++) == ' ' ) nc++;
4086 *( (int *) fptr ) = nc;
4087 }
4088 }
4089 }
4090 }
4091
4092 /* Move on the first character following the field specifier. */
4093 c++;
4094 }
4095
4096 /* If this is not the start of a field specifier, pass on. */
4097 } else {
4098 c++;
4099 }
4100 }
4101
4102 /* Release the temporary copy of the format string. */
4103 newfor = (char *) astFree( (void *) newfor );
4104 }
4105
4106 /* If the format string should not trigger any known problems, use sscanf
4107 directly. */
4108 } else if( astOK ) {
4109 ret = sscanf( str, fmt, ptr[0], ptr[1], ptr[2], ptr[3],
4110 ptr[4], ptr[5], ptr[6], ptr[7], ptr[8], ptr[9],
4111 ptr[10], ptr[11], ptr[12], ptr[13], ptr[14],
4112 ptr[15], ptr[16], ptr[17], ptr[18], ptr[19] );
4113 }
4114 }
4115
4116 /* Tidy up the argument pointer. */
4117 va_end( args );
4118
4119 /* Return the answer. */
4120 return ret;
4121
4122 }
4123
4124
4125 /* The next functions are used only when memory debugging is
4126 switched on via the MEM_DEBUG macro. They can be used for locating
4127 memory leaks, etc. */
4128 #ifdef MEM_DEBUG
4129
astActiveMemory_(const char * label)4130 void astActiveMemory_( const char *label ) {
4131 /*
4132 *+
4133 * Name:
4134 * astActiveMemory
4135
4136 * Purpose:
4137 * Display a list of any currently active AST memory pointers.
4138
4139 * Type:
4140 * Protected function.
4141
4142 * Synopsis:
4143 * #include "memory.h"
4144 * astActiveMemory( const char *label )
4145
4146 * Description:
4147 * This function displays a list of the identifiers for all currently
4148 * active AST memory chunks. The list is written to standard output
4149 * using "printf", preceded by the supplied text.
4150
4151 * Parameters:
4152 * label
4153 * A textual label to display before the memody id values (may be
4154 * NULL).
4155
4156 * Notes:
4157 * - This function attempts to execute even if an error has occurred.
4158 * - Memory blocks which are not usually freed are not reported. Such
4159 * blocks are typically used by AST to hold internal state information.
4160 *-
4161 */
4162
4163 Memory *next;
4164
4165 if( label ) printf("%s: ", label );
4166 next = Active_List;
4167 if( next ) {
4168 while( next ) {
4169 if( !next->perm ) {
4170 printf( "%d(%s:%d) ", next->id, next->file, next->line );
4171 }
4172 next = next->next;
4173 }
4174 } else {
4175 printf("There are currently no active AST memory blocks.");
4176 }
4177 printf("\n");
4178
4179 }
4180
astWatchMemory_(int id)4181 void astWatchMemory_( int id ) {
4182 /*
4183 *+
4184 * Name:
4185 * astWatchMemory
4186
4187 * Purpose:
4188 * Indicate uses of the memory block with the specified identifier
4189 * should be reported.
4190
4191 * Type:
4192 * Protected function.
4193
4194 * Synopsis:
4195 * #include "memory.h"
4196 * astWatchMemory( int id )
4197
4198 * Description:
4199 * This function forces astMemoryAlarm to be invoked when key
4200 * operations are performed on a specified memory block. These key
4201 * operations include; allocation, freeing, copying and cloning of
4202 * Objects, etc.
4203 *
4204 * astMemoryAlarm reports a message when called identifying the memory
4205 * block and the action performed on it. When using a debugger, these
4206 * events can be trapped and investigated by setting a debugger
4207 * breakpoint in astMemoryAlarm_.
4208
4209 * Parameters:
4210 * id
4211 * The identifier of the memory block which is to be watched.
4212
4213 * Notes:
4214 * - This function attempts to execute even if an error has occurred.
4215 *-
4216 */
4217 Watched_ID = id;
4218 }
4219
astMemoryId_(const void * ptr,int * status)4220 int astMemoryId_( const void *ptr, int *status ){
4221 /*
4222 *+
4223 * Name:
4224 * astMemoryId
4225
4226 * Purpose:
4227 * Return the integer identifier for a memory block.
4228
4229 * Type:
4230 * Protected function.
4231
4232 * Synopsis:
4233 * #include "memory.h"
4234 * int astMemoryId( const void *ptr )
4235
4236 * Description:
4237 * This function returns the integer identifier associated with a
4238 * memory block allocated by function sin this module.
4239
4240 * Parameters:
4241 * ptr
4242 * The pointer (a genuine C pointer, not an encoded object
4243 * identifier).
4244
4245 * Returned Value:
4246 * The integer identifier. A value of -1 is returned if "ptr" is NULL.
4247
4248 *-
4249 */
4250 astDECLARE_GLOBALS
4251 astGET_GLOBALS(NULL);
4252 return ptr ? ((Memory *)(ptr-SIZEOF_MEMORY))->id : -1;
4253 }
4254
astMemoryPtr_(int id)4255 void *astMemoryPtr_( int id ){
4256 /*
4257 *+
4258 * Name:
4259 * astMemoryPtr
4260
4261 * Purpose:
4262 * Return a pointer to the memory block with a given identifier.
4263
4264 * Type:
4265 * Protected function.
4266
4267 * Synopsis:
4268 * #include "memory.h"
4269 * void *astMemoryPtr( int id )
4270
4271 * Description:
4272 * This function returns a pointer to the memory block with a given
4273 * identifier. NULL is returned if the given identifier is not active.
4274
4275 * Parameters:
4276 * id
4277 * The identifier for an active memory block.
4278
4279 * Returned Value:
4280 * The pointer to the memory block. NULL is returned if no active memory
4281 * with the given ID can be found. Note, this is always a genuine C
4282 * pointer (even for public Object pointers).
4283
4284 *-
4285 */
4286 Memory *next;
4287 void *ret;
4288
4289 ret = NULL;
4290 next = Active_List;
4291 while( next ) {
4292 if( next->id == id ) {
4293 ret = next + 1;
4294 break;
4295 }
4296 }
4297
4298 return ret;
4299 }
4300
astMemoryAlarm_(const char * verb)4301 void astMemoryAlarm_( const char *verb ){
4302 /*
4303 *+
4304 * Name:
4305 * astMemoryAlarm
4306
4307 * Purpose:
4308 * Called when a watched memory ID is used.
4309
4310 * Type:
4311 * Protected function.
4312
4313 * Synopsis:
4314 * #include "memory.h"
4315 * void astMemoryAlarm( const char *verb )
4316
4317 * Description:
4318 * This function is called when a watched memory ID is used. See
4319 * astWatchMemory.
4320
4321 * Parameters:
4322 * verb
4323 * Text to include in message.
4324 *-
4325 */
4326
4327 printf( "astMemoryAlarm: Memory id %d has been %s.\n", Watched_ID, verb );
4328 }
4329
astMemoryStats_(int reset,size_t * peak,size_t * current,int * status)4330 void astMemoryStats_( int reset, size_t *peak, size_t *current, int *status ) {
4331 /*
4332 *+
4333 * Name:
4334 * astMemoryStats
4335
4336 * Purpose:
4337 * Return the current and peak AST memory usage.
4338
4339 * Type:
4340 * Protected function.
4341
4342 * Synopsis:
4343 * #include "memory.h"
4344 * astMemoryStats( int reset, size_t *peak, size_t *current )
4345
4346 * Description:
4347 * This function returns the current amount of memory allocated
4348 * using AST memory management functions, and the peak amount of
4349 * simultaneously allocated memory since the last time the peak value
4350 * was reset.
4351
4352 * Parameters:
4353 * reset
4354 * If non-zero, the peak value is reset to the current usage
4355 * upon return.
4356 * peak
4357 * Address at which to return the peak memory usage since the last
4358 * reset, in bytes.
4359 * current
4360 * Address at which to return the current memory usage, in bytes.
4361
4362 *-
4363 */
4364
4365 LOCK_DEBUG_MUTEX;
4366
4367 if( peak ) *peak = Peak_Usage;
4368 if( current ) *current = Current_Usage;
4369 if( reset ) Peak_Usage = Current_Usage;
4370
4371 UNLOCK_DEBUG_MUTEX;
4372 }
4373
astMemoryWarning_(size_t threshold,int * status)4374 void astMemoryWarning_( size_t threshold, int *status ) {
4375 /*
4376 *+
4377 * Name:
4378 * astMemoryWarning
4379
4380 * Purpose:
4381 * Issues a warning memory goes over a specified threshold.
4382
4383 * Type:
4384 * Protected function.
4385
4386 * Synopsis:
4387 * #include "memory.h"
4388 * astMemoryWarning( size_t threshold )
4389
4390 * Description:
4391 * This function prints a warning message to standard output if the
4392 * AST memory usage exceeds the specified threshold.
4393
4394 * Parameters:
4395 * threshold
4396 * The memory allocation, in bytes, at which a warning should be issued,
4397 * Supply zero to suppress warnings.
4398
4399 * Notes:
4400 * - This function is used to reset the threshold to zero when the first
4401 * warning is issued in order to prevent a flood of warnings appearing.
4402 * Therefore, setting a debugger breakpoint in this function
4403 * ("astMemoryWarning_" - do not forget the trailing underscore)
4404 * allows you to locate the point at which memory allocation first
4405 * exceeds the threshold.
4406
4407 *-
4408 */
4409
4410 LOCK_DEBUG_MUTEX;
4411
4412 Warn_Usage = threshold;
4413
4414 UNLOCK_DEBUG_MUTEX;
4415 }
4416
astMemoryUse_(const void * ptr,const char * verb,int * status)4417 void astMemoryUse_( const void *ptr, const char *verb, int *status ){
4418 /*
4419 *+
4420 * Name:
4421 * astMemoryUse
4422
4423 * Purpose:
4424 * Called to report the use of a memory block pointer.
4425
4426 * Type:
4427 * Protected function.
4428
4429 * Synopsis:
4430 * #include "memory.h"
4431 * void astMemoryUse( void *ptr, const char *verb )
4432
4433 * Description:
4434 * If the supplied memory block is being watched, astMemoryAlarm is
4435 * called to report the use of the pointer. The reported text includes
4436 * the supplied "verb". A memory block can be watched by calling
4437 * astWatchMemory.
4438
4439 * Parameters:
4440 * ptr
4441 * A pointer to the memory block being used. The pointer must have
4442 * been returned by one of the AST memory management functions (e.g.
4443 * astMalloc, astRealloc, etc).
4444 * verb
4445 * A verb indicating what is being done to the pointer.
4446 *-
4447 */
4448
4449 astDECLARE_GLOBALS
4450 astGET_GLOBALS(NULL);
4451
4452 if( ptr && astMemoryId( ptr ) == Watched_ID ) {
4453 if( !Quiet_Use || !strcmp( verb, ISSUED ) ||
4454 !strcmp( verb, FREED ) ) {
4455 astMemoryAlarm( verb );
4456 }
4457 }
4458 }
4459
astMemoryTune_(const char * name,int value,int * status)4460 int astMemoryTune_( const char *name, int value, int *status ){
4461 /*
4462 *+
4463 * Name:
4464 * astMemoryTune
4465
4466 * Purpose:
4467 * Set a tuning parameter for the memory debugging functions.
4468
4469 * Type:
4470 * Protected function.
4471
4472 * Synopsis:
4473 * #include "memory.h"
4474 * int astMemoryTune( const char *name, int value )
4475
4476 * Description:
4477 * There are a few tuning parameters which control the behaviour of
4478 * the memory debugging functions. This function allows these tuning
4479 * parameters to be queried or set.
4480
4481 * Parameters:
4482 * name
4483 * The name of the tuning parameter to query or set. Valid names are:
4484 *
4485 * "Keep_ID": A boolean flag indicating if a new ID should be issued
4486 * for a cached memory block each time it is returned by astMalloc?
4487 * Otherwise, the same ID value is used throughtout the life of a
4488 * memory block. Default is zero (false).
4489 *
4490 * "List_Cache": A boolean flag which if non-zero (true) causes the
4491 * ID of every memory block in the cache to be reported when the
4492 * cache is emptied by astFlushMemory.
4493 *
4494 * "Quiet_Use": A boolean flag controlling the number of reports issued
4495 * when a memory block is being watched (see astWatchMemory). If
4496 * non-zero (true), then the only events which are reported are the
4497 * issuing of a memory block pointer by astMalloc or astRealloc,and
4498 * the freeing (or caching) of a memory block by astFree. If Quiet_Use
4499 * is zero (the default), then additional reports are made for
4500 * memory blocks used to hold AST Objects whenever the Object is
4501 * copied, cloned, or checked.
4502 * value
4503 * The new value for the tuning parameter. If AST__TUNULL is
4504 * supplied, the original value is left unchanged.
4505
4506 * Returned Value:
4507 * The original value of the tuning parameter.
4508
4509 *-
4510 */
4511
4512 int result = AST__TUNULL;
4513
4514 if( name ) {
4515
4516 if( astChrMatch( name, "Keep_ID" ) ) {
4517 result = Keep_ID;
4518 if( value != AST__TUNULL ) Keep_ID = value;
4519
4520 } else if( astChrMatch( name, "Quiet_Use" ) ) {
4521 result = Quiet_Use;
4522 if( value != AST__TUNULL ) Quiet_Use = value;
4523
4524 } else if( astChrMatch( name, "List_Cache" ) ) {
4525 result = List_Cache;
4526 if( value != AST__TUNULL ) List_Cache = value;
4527
4528 } else if( astOK ) {
4529 astError( AST__TUNAM, "astMemoryTune: Unknown AST memory tuning "
4530 "parameter specified \"%s\".", status, name );
4531 }
4532 }
4533
4534 return result;
4535 }
4536
astBeginPM_(int * status)4537 void astBeginPM_( int *status ) {
4538 /*
4539 *+
4540 * Name:
4541 * astBeginPM
4542
4543 * Purpose:
4544 * Start a block of permanent memory allocations.
4545
4546 * Type:
4547 * Protected function.
4548
4549 * Synopsis:
4550 * #include "memory.h"
4551 * astBeginPM
4552
4553 * Description:
4554 * This function indicates that all memory allocations made by calls
4555 * to other functions in this module (e.g. astMalloc), up to the
4556 * astEndPM call which matches the astBeginPM call, will not usually
4557 * be freed explicitly. Matching astBeginPM/astEndPM calls should be
4558 * used to enclose all code which allocates memory which is never
4559 * freed explitly by AST. Such memory allocations may be freed if
4560 * required, using the astFlushMemory function (but note this should
4561 * only be done once all use of AST by an application has finished).
4562 *
4563 * Matching pairs of astBeginPM/astEndPM calls can be nested up to a
4564 * maximum depth of 20.
4565
4566 *-
4567 */
4568
4569 LOCK_DEBUG_MUTEX;
4570
4571 /* The global Perm_Mem flag indicates whether or not subsequent memory
4572 management functions in this module should store pointers to allocated
4573 blocks in the PM_List array. Push the current value of this flag
4574 onto a stack, and set the value to 1. */
4575 if( PM_Stack_Size >= PM_STACK_MAXSIZE ){
4576 if( astOK ) {
4577 astError( AST__INTER, "astBeginPM: Maximum stack size has been "
4578 "exceeded (internal AST programming error)." , status);
4579 }
4580
4581 } else {
4582 PM_Stack[ PM_Stack_Size++ ] = Perm_Mem;
4583 Perm_Mem = 1;
4584 }
4585 UNLOCK_DEBUG_MUTEX;
4586 }
4587
astEndPM_(int * status)4588 void astEndPM_( int *status ) {
4589 /*
4590 *+
4591 * Name:
4592 * astEndPM
4593
4594 * Purpose:
4595 * End a block of permanent memory allocations.
4596
4597 * Type:
4598 * Protected function.
4599
4600 * Synopsis:
4601 * #include "memory.h"
4602 * astEndPM
4603
4604 * Description:
4605 * This function indicates the end of the block of permanent memory
4606 * allocations started by the matching call to astBeginPM. See
4607 * astBeginPM for further details.
4608
4609 *-
4610 */
4611
4612 LOCK_DEBUG_MUTEX;
4613
4614 /* The global Perm_Mem flag indicates whether or not subsequent memory
4615 management functions in this module should store pointers to allocated
4616 blocks in the PM_List array. Pop the value from the top of this stack. */
4617 if( PM_Stack_Size == 0 ){
4618 if( astOK ) {
4619 astError( AST__INTER, "astEndPM: astEndPM called without "
4620 "matching astBeginPM (internal AST programming error)." , status);
4621 }
4622
4623 } else {
4624 Perm_Mem = PM_Stack[ --PM_Stack_Size ];
4625 }
4626
4627 UNLOCK_DEBUG_MUTEX;
4628 }
4629
astFlushMemory_(int leak,int * status)4630 void astFlushMemory_( int leak, int *status ) {
4631 /*
4632 *+
4633 * Name:
4634 * astFlushMemory
4635
4636 * Purpose:
4637 * Free all permanent and cached memory blocks.
4638
4639 * Type:
4640 * Protected function.
4641
4642 * Synopsis:
4643 * #include "memory.h"
4644 * astFlushMemory( int leak );
4645
4646 * Description:
4647 * This function should only be called once all use of AST by an
4648 * application has finished. It frees any allocated but currently
4649 * unused memory stored in an internal cache of unused memory
4650 * pointers. (Note, it does not free any memory used permanently to
4651 * store internal AST state information).
4652 *
4653 * It is not normally necessary to call this function since the memory
4654 * will be freed anyway by the operating system when the application
4655 * terminates. However, it can be called if required in order to
4656 * stop memory management tools such as valgrind from reporting that
4657 * the memory has not been freed at the end of an application.
4658 *
4659 * In addition, if "leak" is non-zero this function will also report
4660 * an error if any active AST memory pointers remain which have not
4661 * been freed (other than pointers for the cached and permanent
4662 * memory described above). Leakage of active memory blocks can be
4663 * investigated using astActiveMemory and astWatchMemory.
4664
4665 * Parameters:
4666 * leak
4667 * Should an error be reported if any non-permanent memory blocks
4668 * are found to be active?
4669
4670 *-
4671 */
4672
4673 /* Local Variables: */
4674 Memory *next;
4675 int nact;
4676 int istat;
4677
4678 /* Empty the cache. */
4679 astMemCaching( astMemCaching( AST__TUNULL ) );
4680
4681 /* Free and count all non-permanent memory blocks. */
4682 nact = 0;
4683 next = Active_List;
4684 while( Active_List ) {
4685 next = Active_List->next;
4686 if( !Active_List->perm ) {
4687 nact++;
4688 FREE( Active_List );
4689 }
4690 Active_List = next;
4691 }
4692
4693 /* Report an error if any active pointers remained. if an error has
4694 already occurred, use the existing status value. */
4695 if( nact && leak ){
4696
4697 if( astOK ) {
4698 istat = AST__INTER;
4699 } else {
4700 istat = astStatus;
4701 }
4702 astError( istat, "astFlushMemory: %d AST memory blocks have not "
4703 "been released (programming error).", status, nact );
4704
4705 } else {
4706 printf("astFlushMemory: All AST memory blocks were released correctly.\n" );
4707 }
4708 }
4709
astCheckMemory_(int * status)4710 void astCheckMemory_( int *status ) {
4711 /*
4712 *+
4713 * Name:
4714 * astCheckMemory
4715
4716 * Purpose:
4717 * Check that all AST memory blocks have been released.
4718
4719 * Type:
4720 * Protected function.
4721
4722 * Synopsis:
4723 * #include "memory.h"
4724 * astCheckMemory
4725
4726 * Description:
4727 * This macro reports an error if any active AST memory pointers
4728 * remain which have not been freed (other than pointers for cached
4729 * and "permanently allocated" memory). Leakage of active memory blocks
4730 * can be investigated using astActiveMemory and astWatchMemory.
4731 *-
4732 */
4733
4734 /* Local Variables: */
4735 Memory *next;
4736 int nact;
4737 int istat;
4738
4739 /* Empty the cache. */
4740 astMemCaching( astMemCaching( AST__TUNULL ) );
4741
4742 /* Count all non-permanent memory blocks. */
4743 nact = 0;
4744 next = Active_List;
4745 while( Active_List ) {
4746 next = Active_List->next;
4747 if( !Active_List->perm ) nact++;
4748 Active_List = next;
4749 }
4750
4751 /* Report an error if any active pointers remained. If an error has
4752 already occurred, use the existing status value. */
4753 if( nact ){
4754
4755 if( astOK ) {
4756 istat = AST__INTER;
4757 } else {
4758 istat = astStatus;
4759 }
4760 astError( istat, "astCheckMemory: %d AST memory blocks have not "
4761 "been released (programming error).", status, nact );
4762
4763 } else {
4764 printf("astCheckMemory: All AST memory blocks were released correctly.\n" );
4765 }
4766 }
4767
Issue(Memory * mem,int * status)4768 static void Issue( Memory *mem, int *status ) {
4769 /*
4770 * Name:
4771 * Issue
4772
4773 * Purpose:
4774 * Indicate that a pointer to a memory block has been issued.
4775
4776 * Type:
4777 * Private function.
4778
4779 * Synopsis:
4780 * #include "memory.h"
4781 * void Issue( Memory *mem, int *status );
4782
4783 * Description:
4784 * Initialises the extra debug items in the Memory header, and adds the
4785 * Memory structure to the list of active memory blocks.
4786
4787 * Parameters:
4788 * mem
4789 * Pointer to the Memory structure.
4790 * status
4791 * Pointer to the inherited status value.
4792 */
4793
4794 /* Local Variables: */
4795 astDECLARE_GLOBALS
4796
4797 /* Return if no pointer was supplied. */
4798 if( !mem ) return;
4799
4800 LOCK_DEBUG_MUTEX;
4801 astGET_GLOBALS(NULL);
4802
4803 /* Store a unique identifier for this pointer. Unless global Keep_ID is
4804 non-zero, a new identifier is used each time the pointer becomes active
4805 (i.e. each time it is remove from the cache or malloced). */
4806 if( !Keep_ID || mem->id < 0 ) mem->id = ++Next_ID;
4807
4808 /* Record the file name and line number where it was issued. */
4809 if( AST__GLOBALS && AST__GLOBALS->Error.Current_File ) {
4810 strncpy( mem->file, AST__GLOBALS->Error.Current_File, sizeof(mem->file) );
4811 mem->file[ sizeof(mem->file) - 1 ] = 0;
4812 mem->line = AST__GLOBALS->Error.Current_Line;
4813 } else {
4814 mem->file[ 0 ] = 0;
4815 mem->line = 0;
4816 }
4817
4818 /* Indicate if this is a permanent memory block (i.e. it will usually not
4819 be freed by AST). */
4820 mem->perm = Perm_Mem;
4821
4822 /* Add it to the double linked list of active pointers. */
4823 mem->next = Active_List;
4824 mem->prev = NULL;
4825 if( Active_List ) Active_List->prev = mem;
4826 Active_List = mem;
4827
4828 /* Report that the pointer is being issued. */
4829 astMemoryUse( (void *) mem + SIZEOF_MEMORY, ISSUED );
4830
4831 /* Update the current and peak memory usage. */
4832 Current_Usage += mem->size + SIZEOF_MEMORY;
4833 if( Current_Usage > Peak_Usage ) Peak_Usage = Current_Usage;
4834
4835 /* If the current allocation is above the threshold set using
4836 astMemoryWarning, issue a warning message, and then reset the threshold
4837 to zero to prevent further warnings being issued, and to allow a
4838 debugger breakpoint to be set. */
4839 if( Current_Usage > Warn_Usage &&
4840 Warn_Usage > 0 ) {
4841 printf( "Warning - AST memory allocation has exceeded %ld bytes - "
4842 "dumping catalogue of active memory blocks to file 'memory.dump'\n",
4843 Warn_Usage );
4844
4845 /* Create a file holding the details of all currently active memory blocks. It can be
4846 examined using topcat. */
4847 FILE *fd = fopen( "memory.dump", "w" );
4848 if( fd ) {
4849 Memory *next;
4850
4851 fprintf( fd, "# id size perm file line\n");
4852 next = Active_List;
4853 if( next ) {
4854 while( next ) {
4855 if( !next->perm ) {
4856 fprintf( fd, "%d %zu %d %s %d\n", next->id, next->size,
4857 next->perm, next->file, next->line );
4858 }
4859 next = next->next;
4860 }
4861 }
4862
4863 fclose(fd );
4864 }
4865
4866 Warn_Usage = 0;
4867 }
4868
4869 UNLOCK_DEBUG_MUTEX;
4870 }
4871
DeIssue(Memory * mem,int * status)4872 static void DeIssue( Memory *mem, int *status ) {
4873 /*
4874 * Name:
4875 * DeIssue
4876
4877 * Purpose:
4878 * Indicate that a pointer to a memory block has been freed.
4879
4880 * Type:
4881 * Private function.
4882
4883 * Synopsis:
4884 * #include "memory.h"
4885 * void DeIssue( Memeory *mem, int *status );
4886
4887 * Description:
4888 * Initialises the extra debug items in the Memory header, and adds the
4889 * Memory structure to the list of active memory blocks.
4890
4891 * Parameters:
4892 * mem
4893 * Pointer to the Memory structure.
4894 * status
4895 * Pointer to the inherited status value.
4896 */
4897
4898 /* Local Variables: */
4899 astDECLARE_GLOBALS
4900 Memory *next;
4901 Memory *prev;
4902
4903 /* Return if no pointer was supplied. */
4904 if( !mem ) return;
4905
4906 LOCK_DEBUG_MUTEX;
4907 astGET_GLOBALS(NULL);
4908
4909 /* Report that the pointer is being freed. */
4910 astMemoryUse( (void *) mem + SIZEOF_MEMORY, FREED );
4911
4912 /* Remove the block from the double linked list of active pointers. */
4913 next = mem->next;
4914 prev = mem->prev;
4915 if( prev ) prev->next = next;
4916 if( next ) next->prev = prev;
4917 if( mem == Active_List ) Active_List = next;
4918 mem->next = NULL;
4919 mem->prev = NULL;
4920
4921 /* Update the current memory usage. */
4922 Current_Usage -= mem->size + SIZEOF_MEMORY;
4923
4924 UNLOCK_DEBUG_MUTEX;
4925 }
4926
4927
4928 #endif
4929
4930
4931
4932
4933
4934
4935 /* The next functions are used only when profiling AST application. */
4936 #ifdef MEM_PROFILE
4937
4938
astStartTimer_(const char * file,int line,const char * name,int * status)4939 void astStartTimer_( const char *file, int line, const char *name, int *status ) {
4940 /*
4941 *+
4942 * Name:
4943 * astStartTimer
4944
4945 * Purpose:
4946 * Measure the time spent until the corresponding call to astStopTimer.
4947
4948 * Type:
4949 * Protected function.
4950
4951 * Synopsis:
4952 * #include "memory.h"
4953 * void astStartTimer( const char *name );
4954
4955 * Description:
4956 * This function looks for a timer with the specified name within the
4957 * current parent timer. If no timer with the given name is found, a
4958 * new timer is created and initialised to zero. The current absolute
4959 * time (elapsed, user and system) is recorded in the timer. The new
4960 * timer then becomes the current timer.
4961
4962 * Parameters:
4963 * name
4964 * A label for the timer. This should be unique within the
4965 * enclosing parent timer.
4966
4967 * Notes:
4968 * - This function should only be used in a single-threaded environment.
4969 * - This function returns without action if timers are currently
4970 * disabled (see astEnableTimers).
4971
4972 *-
4973 */
4974
4975 /* Local Variables: */
4976 int n, found, i;
4977 AstTimer *t;
4978 struct tms buf;
4979
4980 /* Check inherited status. Also return if timers are currently disabled. */
4981 if( !Enable_Timers || *status != 0 ) return;
4982
4983 /* See if a timer with the given name exists in the list of child timers
4984 within the current timer. */
4985 found = 0;
4986 if( Current_Timer ) {
4987 for( i = 0; i < Current_Timer->nchild; i++ ) {
4988 t = Current_Timer->children[ i ];
4989 if( !strcmp( t->name, name ) ) {
4990 found = 1;
4991 break;
4992 }
4993 }
4994 }
4995
4996 /* If not, create and initialise one now, and add it into the list of
4997 children within the current timer. */
4998 if( !found ) {
4999 t = astMalloc( sizeof( AstTimer ) );
5000 t->id = Timer_Count++;
5001 t->et = 0;
5002 t->ut = 0;
5003 t->st = 0;
5004 t->nentry = 0;
5005 t->name = name;
5006 t->file = file;
5007 t->line = line;
5008 t->parent = Current_Timer;
5009 t->nchild = 0;
5010 t->children = NULL;
5011
5012 if( Current_Timer ) {
5013 n = (Current_Timer->nchild)++;
5014 Current_Timer->children = astGrow( Current_Timer->children,
5015 sizeof( AstTimer *),
5016 Current_Timer->nchild );
5017 Current_Timer->children[ n ] = t;
5018 }
5019 }
5020
5021 /* Record the current absolute times (elapsed, user and system) within
5022 the new timer. */
5023 t->e0 = times(&buf);
5024 t->u0 = buf.tms_utime;
5025 t->s0 = buf.tms_stime;
5026
5027 /* Increment the number of entries into the timer. */
5028 (t->nentry)++;
5029
5030 /* Use the new timer as the current timer until the corresponding call to
5031 astStopTimer. */
5032 Current_Timer = t;
5033 }
5034
astEnableTimers_(int enable,int * status)5035 void astEnableTimers_( int enable, int *status ) {
5036 /*
5037 *+
5038 * Name:
5039 * astEnableTimers
5040
5041 * Purpose:
5042 * Set a global flag indicating if the use of AST timers is enabled.
5043
5044 * Type:
5045 * Protected function.
5046
5047 * Synopsis:
5048 * #include "memory.h"
5049 * void astStartTimer( int enable );
5050
5051 * Description:
5052 * This function sets a global flag that enables otr disables the user
5053 * of AST Timers. If timers are disabled, the astStartTimer and
5054 * astStopTimer functions will return without action.
5055
5056 * Parameters:
5057 * enable
5058 * If non-zero, timers will be used.
5059
5060 * Notes:
5061 * - This function should only be used in a single-threaded environment.
5062
5063 *-
5064 */
5065 Enable_Timers = enable;
5066 }
5067
astStopTimer_(int * status)5068 void astStopTimer_( int *status ) {
5069 /*
5070 *+
5071 * Name:
5072 * astStopTimer
5073
5074 * Purpose:
5075 * Record the time spent since the corresponding call to astStartTimer.
5076
5077 * Type:
5078 * Protected function.
5079
5080 * Synopsis:
5081 * #include "memory.h"
5082 * void astStopTimer;
5083
5084 * Description:
5085 * This function obtains the time increments since the corresponding
5086 * call to astStartTimer, and adds these increments onto the total
5087 * times stored in the current timer. It then changes the current
5088 * timer to be the parent timer associated the current timer on entry.
5089 *
5090 * If the current timer on entry has no parent (i.e. is a top level
5091 * timer), the times spent in the top-level timer, and all its
5092 * descendent timers, are displayed.
5093
5094 * Notes:
5095 * - This function should only be used in a single-threaded environment.
5096 * - This function returns without action if timers are currently
5097 * disabled (see astEnableTimers).
5098
5099 *-
5100 */
5101
5102 /* Local Variables: */
5103 AstTimer *flat;
5104 AstTimer *t;
5105 int i;
5106 int nflat;
5107 struct tms buf;
5108
5109 /* Check inherited status. Also return if timers are currently disabled. */
5110 if( !Enable_Timers || !Current_Timer || *status != 0 ) return;
5111
5112 /* Get the current absolute times, and thus find the elapsed times since the
5113 corresponding call to astStartTimer. Use these elapsed times to increment
5114 the total times spent in the timer. */
5115 Current_Timer->et += ( times(&buf) - Current_Timer->e0 );
5116 Current_Timer->st += ( buf.tms_stime - Current_Timer->s0 );
5117 Current_Timer->ut += ( buf.tms_utime - Current_Timer->u0 );
5118
5119 /* If this is a top level timer, display the times spent in the current
5120 timer, and in all its descendent timers. This also frees the memory
5121 used by the timers. */
5122 if( !Current_Timer->parent ) {
5123 flat = NULL;
5124 nflat = 0;
5125 Current_Timer = ReportTimer( Current_Timer, 0, &flat, &nflat, status );
5126
5127 /* Sort and display the flat list of timers, then free the memory used by
5128 the flat list. */
5129 qsort( flat, nflat, sizeof( AstTimer), CompareTimers2 );
5130 printf("\n\n");
5131 t = flat;
5132 for( i = 0; i < nflat; i++,t++ ) {
5133 printf( "%s (%s:%d): ", t->name, t->file, t->line );
5134 printf( "elapsed=%ld ", (long int) t->et );
5135 /*
5136 printf( "system=%ld ", (long int) t->st );
5137 printf( "user=%ld ", (long int) t->ut );
5138 */
5139 printf( "calls=%d ", t->nentry );
5140 printf("\n");
5141 }
5142 flat = astFree( flat );
5143
5144 /* If this is not a top level timer, restore the parent timer as the
5145 curent timer. */
5146 } else {
5147 Current_Timer = Current_Timer->parent;
5148 }
5149 }
5150
ReportTimer(AstTimer * t,int ind,AstTimer ** flat,int * nflat,int * status)5151 static AstTimer *ReportTimer( AstTimer *t, int ind, AstTimer **flat,
5152 int *nflat, int *status ) {
5153 /*
5154 * Name:
5155 * ReportTimer
5156
5157 * Purpose:
5158 * Free and report the times spent in a given timer, and all descendents.
5159
5160 * Type:
5161 * Private function.
5162
5163 * Synopsis:
5164 * #include "memory.h"
5165 * AstTimer *ReportTimer( AstTimer *t, int ind, AstTimer **flat,
5166 * int *nflat, int *status )
5167
5168 * Description:
5169 * This routines reports to standard output the times spent in the
5170 * supplied timer. It then calls itself recursively to report the times
5171 * spent in each of the child timers of the supplied timer.
5172 *
5173 * It also frees the memory used to hold the supplied timer.
5174
5175 * Parameters:
5176 * t
5177 * Pointer to the AstTimer structure.
5178 * ind
5179 * The number of spaces of indentation to display before the timer
5180 * details.
5181 * flat
5182 * Address of a pointer to the start of an array of AstTimers. The
5183 * number of elements in this array is given by "*nflat". Each
5184 * Timer in this array holds the accumulated total for all entries
5185 * into a given timer, from all parent contexts.
5186 * nflat
5187 * Address of an int holding the current length of the "*flat" array.
5188 * status
5189 * Pointer to the inherited status value.
5190 */
5191
5192 /* Local Variables: */
5193 int found;
5194 int i;
5195 AstTimer *ft;
5196 AstTimer *parent;
5197
5198 /* Check inherited status */
5199 if( *status != 0 ) return NULL;
5200
5201 /* Display a single line of text containing the times stored in the supplied
5202 timer, preceded by the requested number of spaces. */
5203 for( i = 0; i < ind; i++ ) printf(" ");
5204 printf( "%s (%s:%d): ", t->name, t->file, t->line );
5205
5206 printf( "id=%d ", t->id );
5207 printf( "elapsed=%ld ", (long int) t->et );
5208 /*
5209 printf( "system=%ld ", (long int) t->st );
5210 printf( "user=%ld ", (long int) t->ut );
5211 */
5212 printf( "calls=%d ", t->nentry );
5213
5214 /* If there are any children, end the line with an opening bvrace. */
5215 if( t->nchild ) printf("{");
5216 printf("\n");
5217
5218 /* If there is more than one child, sort them into descending order of
5219 elapsed time usage. */
5220 if( t->nchild > 1 ) qsort( t->children, t->nchild, sizeof( AstTimer * ),
5221 CompareTimers );
5222
5223 /* Increment the indentation and call this function recursively to
5224 display and free each child timer. */
5225 ind += 3;
5226 for( i = 0; i < t->nchild; i++ ) {
5227 (t->children)[ i ] = ReportTimer( (t->children)[ i ], ind, flat,
5228 nflat, status );
5229 }
5230
5231 /* Delimit the children by displaying a closing brace. */
5232 if( t->nchild ) {
5233 for( i = 0; i < ind - 3; i++ ) printf(" ");
5234 printf("}\n");
5235 }
5236
5237 /* See if this timer is contained within itself at a higher level. */
5238 parent = t->parent;
5239 while( parent && ( parent->line != t->line ||
5240 strcmp( parent->file, t->file ) ) ) {
5241 parent = parent->parent;
5242 }
5243
5244 /* If not, search for a timer in the "flat" array of timers that has the same
5245 source file and line number. */
5246 if( !parent ) {
5247 found = 0;
5248 ft = *flat;
5249 for( i = 0; i < *nflat; i++, ft++ ) {
5250 if( ft->line == t->line &&
5251 !strcmp( ft->file, t->file ) ) {
5252 found = 1;
5253 break;
5254 }
5255 }
5256
5257 /* If not found, add a new timer to the end of the "flat" array and
5258 initialise it. */
5259 if( !found ) {
5260 i = (*nflat)++;
5261 *flat = astGrow( *flat, *nflat, sizeof( AstTimer ) );
5262 ft = (*flat) + i;
5263 ft->id = 0;
5264 ft->et = t->et;
5265 ft->ut = t->ut;
5266 ft->st = t->st;
5267 ft->nentry = t->nentry;
5268 ft->name = t->name;
5269 ft->file = t->file;
5270 ft->line = t->line;
5271 ft->parent = NULL;
5272 ft->nchild = 0;
5273 ft->children = NULL;
5274
5275
5276 /* If found, increment the properites to include the supplied timer. */
5277 } else {
5278 ft->et += t->et;
5279 ft->ut += t->ut;
5280 ft->st += t->st;
5281 ft->nentry += t->nentry;
5282 }
5283 }
5284
5285 /* Free the memory used by the supplied timer. */
5286 t->children = astFree( t->children );
5287 return astFree( t );
5288 }
5289
5290
CompareTimers(const void * a,const void * b)5291 static int CompareTimers( const void *a, const void *b ){
5292 return ((*((AstTimer **) b ))->et) - ((*((AstTimer **) a ))->et);
5293 }
5294
CompareTimers2(const void * a,const void * b)5295 static int CompareTimers2( const void *a, const void *b ){
5296 return (((AstTimer *) b )->et) - (((AstTimer *) a )->et);
5297 }
5298
5299 #endif
5300