1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  * Copyright by The HDF Group.                                               *
3  * Copyright by the Board of Trustees of the University of Illinois.         *
4  * All rights reserved.                                                      *
5  *                                                                           *
6  * This file is part of HDF.  The full HDF copyright notice, including       *
7  * terms governing use, modification, and redistribution, is contained in    *
8  * the COPYING file, which can be found at the root of the source code       *
9  * distribution tree, or in https://support.hdfgroup.org/ftp/HDF/releases/.  *
10  * If you do not have access to either file, you may request a copy from     *
11  * help@hdfgroup.org.                                                        *
12  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
13 
14 /* $Id$ */
15 
16 /*
17 FILE
18     atom.c - Internal storage routines for handling "atoms"
19 
20 REMARKS
21     Atoms are just ID's which allow objects (void *'s currently) to be
22     bundled into "groups" for more general storage.
23 
24 DESIGN
25     The groups are stored in an array of pointers to store each group in an
26     element. Each "atomic group" node contains a link to a hash table to
27     manage the atoms in each group.  The allowed "atomic groups" are stored
28     in an enum (called group_t) in atom.h.
29 
30 BUGS/LIMITATIONS
31     Can't interate over the atoms in a group.
32 
33 LOCAL ROUTINES
34   HAIfind_atom      - Returns a pointer to an atom_info_t from a atom ID
35   HAIget_atom_node  - Gets an atom node (uses the atom free list)
36   HAIrelease_atom_node - Releases an atom node (uses the atom free list)
37 EXPORTED ROUTINES
38  Atom Functions:
39   HAregister_atom   - Register an object in a group and get an atom for it
40   HAatom_object     - Get the object for an atom
41   HAatom_group      - Get the group for an atom
42   HAremove_atom     - Remove an atom from a group
43   HAsearch_atom     - Search a group for a particular object
44  Atom Group Functions:
45   HAinit_group      - Initialize a group to store atoms in
46   HAdestroy_group   - Destroy an atomic group
47  Atom Group Cleanup:
48   HAshutdown        - Terminate various static buffers.
49 
50 AUTHOR
51    Quincey Koziol
52 
53 MODIFICATION HISTORY
54    1/3/96  - Starting writing specs & coding prototype
55    1/7/96  - Finished coding prototype
56 */
57 
58 #define ATOM_MASTER
59 #include "hdf.h"
60 #include "atom.h"
61 #include <assert.h>
62 
63 /* Private function prototypes */
64 static atom_info_t *HAIfind_atom(atom_t atm);
65 
66 static atom_info_t *HAIget_atom_node(void);
67 
68 static void HAIrelease_atom_node(atom_info_t *atm);
69 
70 /******************************************************************************
71  NAME
72      HAinit_group - Initialize an atomic group
73 
74  DESCRIPTION
75     Creates a global atomic group to store atoms in.  If the group has already
76     been initialized, this routine just increments the count of # of
77     initializations and returns without trying to change the size of the hash
78     table.
79 
80  RETURNS
81     Returns SUCCEED if successful and FAIL otherwise
82 
83 *******************************************************************************/
HAinit_group(group_t grp,intn hash_size)84 intn HAinit_group(group_t grp,      /* IN: Group to initialize */
85     intn hash_size                  /* IN: Minimum hash table size to use for group */
86 )
87 {
88     CONSTR(FUNC, "HAinit_group");	/* for HERROR */
89     atom_group_t *grp_ptr=NULL;     /* ptr to the atomic group */
90     intn ret_value=SUCCEED;
91 
92     HEclear();
93     if((grp<=BADGROUP || grp>=MAXGROUP) && hash_size>0)
94         HGOTO_ERROR(DFE_ARGS, FAIL);
95 
96 #ifdef ATOMS_CACHE_INLINE
97 /* Assertion necessary for faster pointer swapping */
98 assert(sizeof(hdf_pint_t)==sizeof(void *));
99 #endif /* ATOMS_CACHE_INLINE */
100 
101 #ifdef HASH_SIZE_POWER_2
102     if(hash_size & (hash_size-1))
103         HGOTO_ERROR(DFE_ARGS, FAIL);
104 #endif /* HASH_SIZE_POWER_2 */
105 
106     if(atom_group_list[grp]==NULL)
107       {     /* Allocate the group information */
108           grp_ptr=(atom_group_t *)HDcalloc(1,sizeof(atom_group_t));
109           if(grp_ptr==NULL)
110               HGOTO_ERROR(DFE_NOSPACE, FAIL);
111           atom_group_list[grp]=grp_ptr;
112       } /* end if */
113     else /* Get the pointer to the existing group */
114         grp_ptr=atom_group_list[grp];
115 
116     if(grp_ptr->count==0)
117       {     /* Initialize the atom group structure */
118         grp_ptr->hash_size=hash_size;
119         grp_ptr->atoms=0;
120         grp_ptr->nextid=0;
121         if((grp_ptr->atom_list=(atom_info_t **)HDcalloc(hash_size,sizeof(atom_info_t *)))==NULL)
122             HGOTO_ERROR(DFE_NOSPACE, FAIL);
123       } /* end if */
124 
125     /* Increment the count of the times this group has been initialized */
126     grp_ptr->count++;
127 
128 done:
129   if(ret_value == FAIL)
130     { /* Error condition cleanup */
131         if(grp_ptr!=NULL)
132           {
133             if(grp_ptr->atom_list!=NULL)
134                 HDfree(grp_ptr->atom_list);
135             HDfree(grp_ptr);
136           } /* end if */
137     } /* end if */
138 
139   /* Normal function cleanup */
140 
141   return ret_value;
142 }   /* end HAinit_group() */
143 
144 /******************************************************************************
145  NAME
146      HAdestroy_group - Destroy an atomic group
147 
148  DESCRIPTION
149     Destroys an atomic group which atoms are stored in.  If the group still
150     has atoms which are registered, this routine fails.  If there have been
151     multiple initializations of the group, this routine just decrements the
152     count of initializations and does not check the atoms out-standing.
153 
154  RETURNS
155     Returns SUCCEED if successful and FAIL otherwise
156 
157 *******************************************************************************/
HAdestroy_group(group_t grp)158 intn HAdestroy_group(group_t grp       /* IN: Group to destroy */
159 )
160 {
161     CONSTR(FUNC, "HAdestroy_group");	/* for HERROR */
162     atom_group_t *grp_ptr=NULL;     /* ptr to the atomic group */
163     intn ret_value=SUCCEED;
164 
165     HEclear();
166     if(grp<=BADGROUP || grp>=MAXGROUP)
167         HGOTO_ERROR(DFE_ARGS, FAIL);
168 
169     grp_ptr=atom_group_list[grp];
170     if(grp_ptr==NULL || grp_ptr->count<=0)
171         HGOTO_ERROR(DFE_INTERNAL, FAIL);
172 
173     /* Decrement the number of users of the atomic group */
174     if((--(grp_ptr->count))==0)
175       {
176 #ifdef ATOMS_ARE_CACHED
177       {
178         uintn i;
179 
180         for(i=0; i<ATOM_CACHE_SIZE; i++)
181             if(ATOM_TO_GROUP(atom_id_cache[i])==grp)
182               {
183                 atom_id_cache[i]=(-1);
184                 atom_obj_cache[i]=NULL;
185               } /* end if */
186       } /* end block */
187 #endif /* ATOMS_ARE_CACHED */
188         HDfree(grp_ptr->atom_list);
189 	grp_ptr->atom_list = NULL;
190       } /* end if */
191 
192 done:
193   if(ret_value == FAIL)
194     { /* Error condition cleanup */
195 
196     } /* end if */
197 
198   /* Normal function cleanup */
199 
200   return ret_value;
201 }   /* end HAdestroy_group() */
202 
203 /******************************************************************************
204  NAME
205      HAregister_atom - Register an object in a group and get an atom for it.
206 
207  DESCRIPTION
208     Registers an object in a group and returns an atom for it.  This routine
209     does _not_ check for unique-ness of the objects, if you register an object
210     twice, you will get two different atoms for it.  This routine does make
211     certain that each atom in a group is unique.  Atoms are created by getting
212     a unique number for the group the atom is in and incorporating the group
213     into the atom which is returned to the user.
214 
215  RETURNS
216     Returns atom if successful and FAIL otherwise
217 
218 *******************************************************************************/
HAregister_atom(group_t grp,VOIDP object)219 atom_t HAregister_atom(group_t grp,     /* IN: Group to register the object in */
220     VOIDP object                        /* IN: Object to attach to atom */
221 )
222 {
223     CONSTR(FUNC, "HAregister_atom");	/* for HERROR */
224     atom_group_t *grp_ptr=NULL;     /* ptr to the atomic group */
225     atom_info_t *atm_ptr=NULL;      /* ptr to the new atom */
226     atom_t atm_id;                  /* new atom ID */
227     uintn hash_loc;                 /* new item's hash table location */
228     atom_t ret_value=SUCCEED;
229 
230     HEclear();
231     if(grp<=BADGROUP || grp>=MAXGROUP)
232         HGOTO_ERROR(DFE_ARGS, FAIL);
233 
234     grp_ptr=atom_group_list[grp];
235     if(grp_ptr==NULL || grp_ptr->count<=0)
236         HGOTO_ERROR(DFE_INTERNAL, FAIL);
237 
238     if((atm_ptr=HAIget_atom_node())==NULL)
239         HGOTO_ERROR(DFE_NOSPACE, FAIL);
240 
241     /* Create the atom & it's ID */
242     atm_id=MAKE_ATOM(grp,grp_ptr->nextid);
243     atm_ptr->id=atm_id;
244     atm_ptr->obj_ptr=object;
245     atm_ptr->next=NULL;
246 
247     /* hash bucket already full, prepend to front of chain */
248     hash_loc=grp_ptr->nextid%(uintn)grp_ptr->hash_size;
249     if(grp_ptr->atom_list[hash_loc]!=NULL)
250         atm_ptr->next=grp_ptr->atom_list[hash_loc];
251 
252     /* Insert into the group */
253     grp_ptr->atom_list[hash_loc]=atm_ptr;
254     grp_ptr->atoms++;
255     grp_ptr->nextid++;
256 
257     ret_value=atm_id;
258 
259 done:
260   if(ret_value == FAIL)
261     { /* Error condition cleanup */
262 
263     } /* end if */
264 
265   /* Normal function cleanup */
266 
267   return ret_value;
268 }   /* end HAregister_atom() */
269 
270 /******************************************************************************
271  NAME
272      HAatom_object - Returns to the object ptr for the atom
273 
274  DESCRIPTION
275     Retrieves the object ptr which is associated with the atom.
276 
277  RETURNS
278     Returns object ptr if successful and NULL otherwise
279 
280 *******************************************************************************/
281 #ifdef ATOMS_CACHE_INLINE
HAPatom_object(atom_t atm)282 VOIDP HAPatom_object(atom_t atm   /* IN: Atom to retrieve object for */
283 )
284 #else /* ATOMS_CACHE_INLINE */
285 VOIDP HAatom_object(atom_t atm   /* IN: Atom to retrieve object for */
286 )
287 #endif /* ATOMS_CACHE_INLINE */
288 {
289     CONSTR(FUNC, "HAatom_object");	/* for HERROR */
290 #ifndef ATOMS_CACHE_INLINE
291 #ifdef ATOMS_ARE_CACHED
292     uintn i;                        /* local counter */
293 #endif /* ATOMS_ARE_CACHED */
294 #endif /* ATOMS_CACHE_INLINE */
295     atom_info_t *atm_ptr=NULL;      /* ptr to the new atom */
296     VOIDP ret_value=NULL;
297 
298     HEclear();
299 
300 #ifndef ATOMS_CACHE_INLINE
301 #ifdef ATOMS_ARE_CACHED
302     /* Look for the atom in the cache first */
303     for(i=0; i<ATOM_CACHE_SIZE; i++)
304         if(atom_id_cache[i]==atm)
305           {
306             ret_value=atom_obj_cache[i];
307             if(i>0)
308               { /* Implement a simple "move forward" caching scheme */
309                 atom_t t_atom=atom_id_cache[i-1];
310                 VOIDP  t_obj=atom_obj_cache[i-1];
311 
312                 atom_id_cache[i-1]=atom_id_cache[i];
313                 atom_obj_cache[i-1]=atom_obj_cache[i];
314                 atom_id_cache[i]=t_atom;
315                 atom_obj_cache[i]=t_obj;
316               } /* end if */
317             HGOTO_DONE(ret_value);
318           } /* end if */
319 #endif /* ATOMS_ARE_CACHED */
320 #endif /* ATOMS_CACHE_INLINE */
321 
322     /* General lookup of the atom */
323     if((atm_ptr=HAIfind_atom(atm))==NULL)
324         HGOTO_ERROR(DFE_INTERNAL, NULL);
325 
326     /* Check if we've found the correct atom */
327     if(atm_ptr!=NULL)
328         ret_value=atm_ptr->obj_ptr;
329 
330 done:
331   if(ret_value == NULL)
332     { /* Error condition cleanup */
333 
334     } /* end if */
335 
336   /* Normal function cleanup */
337 
338   return ret_value;
339 }   /* end HAatom_object() */
340 
341 /******************************************************************************
342  NAME
343      HAatom_group - Returns to the group for the atom
344 
345  DESCRIPTION
346     Retrieves the group which is associated with the atom.
347 
348  RETURNS
349     Returns group if successful and BADGROUP otherwise
350 
351 *******************************************************************************/
HAatom_group(atom_t atm)352 group_t HAatom_group(atom_t atm   /* IN: Atom to retrieve group for */
353 )
354 {
355     CONSTR(FUNC, "HAatom_group");	/* for HERROR */
356     group_t ret_value=BADGROUP;
357 
358     HEclear();
359     ret_value=ATOM_TO_GROUP(atm);
360     if(ret_value<=BADGROUP || ret_value>=MAXGROUP)
361         HGOTO_ERROR(DFE_ARGS, BADGROUP);
362 
363 done:
364   if(ret_value == BADGROUP)
365     { /* Error condition cleanup */
366 
367     } /* end if */
368 
369   /* Normal function cleanup */
370 
371   return ret_value;
372 }   /* end HAatom_group() */
373 
374 /******************************************************************************
375  NAME
376      HAremove_atom - Removes an atom from a group
377 
378  DESCRIPTION
379     Removes an atom from a group.
380 
381  RETURNS
382     Returns atom's object if successful and NULL otherwise
383 
384 *******************************************************************************/
HAremove_atom(atom_t atm)385 VOIDP HAremove_atom(atom_t atm   /* IN: Atom to remove */
386 )
387 {
388     CONSTR(FUNC, "HAremove_atom");	/* for HERROR */
389     atom_group_t *grp_ptr=NULL;     /* ptr to the atomic group */
390     atom_info_t *curr_atm,          /* ptr to the current atom */
391         *last_atm;                  /* ptr to the last atom */
392     group_t grp;                    /* atom's atomic group */
393     uintn hash_loc;                 /* atom's hash table location */
394 #ifdef ATOMS_ARE_CACHED
395     uintn i;                        /* local counting variable */
396 #endif /* ATOMS_ARE_CACHED */
397     VOIDP ret_value=NULL;
398 
399     HEclear();
400     grp=ATOM_TO_GROUP(atm);
401     if(grp<=BADGROUP || grp>=MAXGROUP)
402         HGOTO_ERROR(DFE_ARGS, NULL);
403 
404     grp_ptr=atom_group_list[grp];
405     if(grp_ptr==NULL || grp_ptr->count<=0)
406         HGOTO_ERROR(DFE_INTERNAL, NULL);
407 
408     /* Get the location in which the atom is located */
409     hash_loc=(uintn)ATOM_TO_LOC(atm,grp_ptr->hash_size);
410     curr_atm=grp_ptr->atom_list[hash_loc];
411     if(curr_atm==NULL)
412         HGOTO_ERROR(DFE_INTERNAL, NULL);
413 
414     last_atm=NULL;
415     while(curr_atm!=NULL)
416       {
417           if(curr_atm->id==atm)
418               break;
419           last_atm=curr_atm;
420           curr_atm=curr_atm->next;
421       } /* end while */
422 
423     if(curr_atm!=NULL)
424       {
425           if(last_atm==NULL)    /* atom is the first the chain */
426               grp_ptr->atom_list[hash_loc]=curr_atm->next;
427           else
428               last_atm->next=curr_atm->next;
429           ret_value=curr_atm->obj_ptr;
430           HAIrelease_atom_node(curr_atm);
431       } /* end if */
432     else    /* couldn't find the atom in the proper place */
433         HGOTO_ERROR(DFE_INTERNAL, NULL);
434 
435 #ifdef ATOMS_ARE_CACHED
436     /* Delete object from cache */
437     for(i=0; i<ATOM_CACHE_SIZE; i++)
438         if(atom_id_cache[i]==atm)
439           {
440             atom_id_cache[i]=(-1);
441             atom_obj_cache[i]=NULL;
442             break;  /* we assume there is only one instance in the cache */
443           } /* end if */
444 #endif /* ATOMS_ARE_CACHED */
445 
446     /* Decrement the number of atoms in the group */
447     (grp_ptr->atoms)--;
448 
449 done:
450   if(ret_value == NULL)
451     { /* Error condition cleanup */
452 
453     } /* end if */
454 
455   /* Normal function cleanup */
456 
457   return ret_value;
458 }   /* end HAremove_atom() */
459 
460 /******************************************************************************
461  NAME
462      HAsearch_atom - Search for an object in a group and get it's pointer.
463 
464  DESCRIPTION
465     Searchs for an object in a group and returns the pointer to it.
466     This routine calls the function pointer passed in for each object in the
467     group until it finds a match.  Currently there is no way to resume a
468     search.
469 
470  RETURNS
471     Returns pointer an atom's object if successful and NULL otherwise
472 
473 *******************************************************************************/
HAsearch_atom(group_t grp,HAsearch_func_t func,const void * key)474 void * HAsearch_atom(group_t grp,        /* IN: Group to search for the object in */
475     HAsearch_func_t func,               /* IN: Ptr to the comparison function */
476     const void * key                     /* IN: pointer to key to compare against */
477 )
478 {
479     CONSTR(FUNC, "HAsearch_atom");	/* for HERROR */
480     atom_group_t *grp_ptr=NULL;     /* ptr to the atomic group */
481     atom_info_t *atm_ptr=NULL;      /* ptr to the new atom */
482     intn i;                         /* local counting variable */
483     void * ret_value=NULL;
484 
485     HEclear();
486     if(grp<=BADGROUP || grp>=MAXGROUP)
487         HGOTO_ERROR(DFE_ARGS, NULL);
488 
489     grp_ptr=atom_group_list[grp];
490     if(grp_ptr==NULL || grp_ptr->count<=0)
491         HGOTO_ERROR(DFE_INTERNAL, NULL);
492 
493     /* Start at the beginning of the array */
494     for(i=0; i<grp_ptr->hash_size; i++)
495       {
496         atm_ptr=grp_ptr->atom_list[i];
497         while(atm_ptr!=NULL)
498           {
499               if((*func)(atm_ptr->obj_ptr,key))
500                   HGOTO_DONE(atm_ptr->obj_ptr); /* found the item we are looking for */
501               atm_ptr=atm_ptr->next;
502           } /* end while */
503       } /* end for */
504 
505 done:
506   if(ret_value == NULL)
507     { /* Error condition cleanup */
508 
509     } /* end if */
510 
511   /* Normal function cleanup */
512 
513   return ret_value;
514 }   /* end HAsearch_atom() */
515 
516 /******************************************************************************
517  NAME
518      HAIfind_atom - Finds a atom in a group
519 
520  DESCRIPTION
521     Retrieves the atom ptr which is associated with the atom.
522 
523  RETURNS
524     Returns atom ptr if successful and NULL otherwise
525 
526 *******************************************************************************/
HAIfind_atom(atom_t atm)527 static atom_info_t *HAIfind_atom(atom_t atm   /* IN: Atom to retrieve atom for */
528 )
529 {
530     CONSTR(FUNC, "HAIfind_atom");	/* for HERROR */
531     atom_group_t *grp_ptr=NULL;     /* ptr to the atomic group */
532     atom_info_t *atm_ptr=NULL;      /* ptr to the new atom */
533     group_t grp;                    /* atom's atomic group */
534     uintn hash_loc;                 /* atom's hash table location */
535     atom_info_t *ret_value=NULL;
536 
537     HEclear();
538     grp=ATOM_TO_GROUP(atm);
539     if(grp<=BADGROUP || grp>=MAXGROUP)
540         HGOTO_ERROR(DFE_ARGS, NULL);
541 
542     grp_ptr=atom_group_list[grp];
543     if(grp_ptr==NULL || grp_ptr->count<=0)
544         HGOTO_ERROR(DFE_INTERNAL, NULL);
545 
546     /* Get the location in which the atom is located */
547     hash_loc=(uintn)ATOM_TO_LOC(atm,grp_ptr->hash_size);
548     atm_ptr=grp_ptr->atom_list[hash_loc];
549     if(atm_ptr==NULL)
550         HGOTO_ERROR(DFE_INTERNAL, NULL);
551 
552     while(atm_ptr!=NULL)
553       {
554           if(atm_ptr->id==atm)
555               break;
556           atm_ptr=atm_ptr->next;
557       } /* end while */
558 
559 #ifdef ATOMS_ARE_CACHED
560     if (atm_ptr){
561 	/* if found, add it to the end of the cached list */
562 	atom_id_cache[ATOM_CACHE_SIZE-1]=atm;
563 	atom_obj_cache[ATOM_CACHE_SIZE-1]=atm_ptr->obj_ptr;
564     }
565 #endif /* ATOMS_ARE_CACHED */
566 
567     ret_value=atm_ptr;
568 
569 done:
570   if(ret_value == NULL)
571     { /* Error condition cleanup */
572 
573     } /* end if */
574 
575   /* Normal function cleanup */
576 
577   return ret_value;
578 }   /* end HAIfind_atom() */
579 
580 /******************************************************************************
581  NAME
582      HAIget_atom_node - Gets an atom node
583 
584  DESCRIPTION
585     Either gets an atom node from the free list (if there is one available)
586     or allocate a node.
587 
588  RETURNS
589     Returns atom ptr if successful and NULL otherwise
590 
591 *******************************************************************************/
HAIget_atom_node(void)592 static atom_info_t *HAIget_atom_node(void)
593 {
594     CONSTR(FUNC, "HAIget_atom_node");	/* for HERROR */
595     atom_info_t *ret_value=NULL;
596 
597     HEclear();
598     if(atom_free_list!=NULL)
599       {
600         ret_value=atom_free_list;
601         atom_free_list=atom_free_list->next;
602       } /* end if */
603     else
604       {
605         if((ret_value=(atom_info_t *)HDmalloc(sizeof(atom_info_t)))==NULL)
606             HGOTO_ERROR(DFE_NOSPACE, NULL);
607       } /* end else */
608 
609 done:
610   if(ret_value == NULL)
611     { /* Error condition cleanup */
612 
613     } /* end if */
614 
615   /* Normal function cleanup */
616 
617   return ret_value;
618 }   /* end HAIget_atom_node() */
619 
620 /******************************************************************************
621  NAME
622      HAIrelease_atom_node - Releases an atom node
623 
624  DESCRIPTION
625     Puts an atom node into the free list
626 
627  RETURNS
628     No return value
629 
630 *******************************************************************************/
HAIrelease_atom_node(atom_info_t * atm)631 static void HAIrelease_atom_node(atom_info_t *atm)
632 {
633 #ifdef LATER
634     CONSTR(FUNC, "HAIrelease_atom_node");	/* for HERROR */
635 #endif /* LATER */
636 
637     /* Insert the atom at the beginning of the free list */
638     atm->next=atom_free_list;
639     atom_free_list=atm;
640 }   /* end HAIrelease_atom_node() */
641 
642 /*--------------------------------------------------------------------------
643  NAME
644     HAshutdown
645  PURPOSE
646     Terminate various static buffers.
647  USAGE
648     intn HAshutdown()
649  RETURNS
650     Returns SUCCEED/FAIL
651  DESCRIPTION
652     Free various buffers allocated in the HA routines.
653  GLOBAL VARIABLES
654  COMMENTS, BUGS, ASSUMPTIONS
655     Should only ever be called by the "atexit" function HDFend
656  EXAMPLES
657  REVISION LOG
658 --------------------------------------------------------------------------*/
659 intn
HAshutdown(void)660 HAshutdown(void)
661 {
662     atom_info_t *curr;
663     intn i;
664 
665     /* Release the free-list if it exists */
666     if(atom_free_list!=NULL)
667       {
668         while(atom_free_list!=NULL)
669           {
670             curr=atom_free_list;
671             atom_free_list=atom_free_list->next;
672             HDfree(curr);
673           } /* end while */
674       } /* end if */
675 
676     for(i=0; i<(intn)MAXGROUP; i++)
677         if(atom_group_list[i]!=NULL)
678           {
679             HDfree(atom_group_list[i]);
680             atom_group_list[i]=NULL;
681           } /* end if */
682   return (SUCCEED);
683 }	/* end HAshutdown() */
684 
685