4d34c8fe60236d79de93aff1d0aa7f235fc2ef93
[pspp] / src / pool.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "pool.h"
22 #include "command.h"
23 #include "error.h"
24 #include <stdlib.h>
25 #include "alloc.h"
26 #include "str.h"
27
28 /* Fast, low-overhead memory block suballocator. */
29 struct pool
30   {
31     struct pool *parent;        /* Pool of which this pool is a subpool. */
32     struct pool_block *blocks;  /* Blocks owned by the pool. */
33     struct pool_gizmo *gizmos;  /* Other stuff owned by the pool. */
34   };
35
36 /* Pool block. */
37 struct pool_block 
38   {
39     struct pool_block *prev;
40     struct pool_block *next;
41     size_t ofs;
42   };
43
44 /* Gizmo types. */
45 enum
46   {
47     POOL_GIZMO_MALLOC,
48     POOL_GIZMO_FILE,
49     POOL_GIZMO_SUBPOOL,
50     POOL_GIZMO_REGISTERED,
51   };
52
53 /* Pool routines can maintain objects (`gizmos') as well as doing
54    suballocation.  
55    This structure is used to keep track of them. */
56 struct pool_gizmo
57   {
58     struct pool *pool;
59     struct pool_gizmo *prev;
60     struct pool_gizmo *next;
61
62     long serial;                /* Serial number. */
63     int type;                   /* Type of this gizmo. */
64
65     /* Type-dependent info. */
66     union
67       {
68         FILE *file;             /* POOL_GIZMO_FILE. */
69         struct pool *subpool;   /* POOL_GIZMO_SUBPOOL. */
70
71         /* POOL_GIZMO_REGISTERED. */
72         struct
73           {
74             void (*free) (void *p);
75             void *p;
76           }
77         registered;
78       }
79     p;
80   };
81
82 /* Rounds X up to the next multiple of Y. */
83 #ifndef ROUND_UP
84 #define ROUND_UP(X, Y)                          \
85         (((X) + ((Y) - 1)) / (Y) * (Y))
86 #endif
87
88 /* Types that provide typically useful alignment sizes. */
89 union align
90   {
91     void *op;
92     void (*fp) (void);
93     long l;
94     double d;
95   };
96
97 /* This should be the alignment size used by malloc().  The size of
98    the union above is correct, if not optimal, in all known cases. */
99 #if defined (i386) || defined (__i386__)
100 #define ALIGN_SIZE 4            /* Save some extra memory. */
101 #else
102 #define ALIGN_SIZE sizeof (union align)
103 #endif
104
105 /* DISCRETE_BLOCKS may be declared as nonzero to prevent
106    suballocation of blocks.  This is useful under memory
107    debuggers like Checker or valgrind because it allows the
108    source location of bugs to be more accurately pinpointed.
109
110    On the other hand, if we're testing the library, then we want to
111    test the library's real functionality, not its crippled, slow,
112    simplified functionality. */
113 /*#define DISCRETE_BLOCKS 1*/
114
115 /* Size of each block allocated in the pool, in bytes.
116    Should be at least 1k. */
117 #ifndef BLOCK_SIZE
118 #define BLOCK_SIZE 1024
119 #endif
120
121 /* Maximum size of a suballocated block.  Larger blocks are allocated
122    directly with malloc() to avoid memory wastage at the end of a
123    suballocation block. */
124 #ifndef MAX_SUBALLOC
125 #define MAX_SUBALLOC 64
126 #endif
127
128 /* Sizes of some structures with alignment padding included. */
129 #define POOL_BLOCK_SIZE ROUND_UP (sizeof (struct pool_block), ALIGN_SIZE)
130 #define POOL_GIZMO_SIZE ROUND_UP (sizeof (struct pool_gizmo), ALIGN_SIZE)
131 #define POOL_SIZE ROUND_UP (sizeof (struct pool), ALIGN_SIZE)
132
133 /* Serial number used to keep track of gizmos for mark/release. */
134 static long serial = 0;
135
136 /* Prototypes. */
137 static void add_gizmo (struct pool *, struct pool_gizmo *);
138 static void free_gizmo (struct pool_gizmo *);
139 static void free_all_gizmos (struct pool *pool);
140 static void delete_gizmo (struct pool *, struct pool_gizmo *);
141 static void check_gizmo (struct pool *, struct pool_gizmo *);
142 \f
143 /* General routines. */
144
145 /* Creates and returns a new memory pool, which allows malloc()'d
146    blocks to be suballocated in a time- and space-efficient manner.
147    The entire contents of the memory pool are freed at once.
148
149    In addition, other objects can be associated with a memory pool.
150    These are released when the pool is destroyed. */
151 struct pool *
152 pool_create (void)
153 {
154   struct pool_block *block;
155   struct pool *pool;
156
157   block = xmalloc (BLOCK_SIZE);
158   block->prev = block->next = block;
159   block->ofs = POOL_BLOCK_SIZE + POOL_SIZE;
160   
161   pool = (struct pool *) (((char *) block) + POOL_BLOCK_SIZE);
162   pool->parent = NULL;
163   pool->blocks = block;
164   pool->gizmos = NULL;
165
166   return pool;
167 }
168
169 /* Destroy the specified pool, including all subpools. */
170 void
171 pool_destroy (struct pool *pool)
172 {
173   if (pool == NULL)
174     return;
175
176   /* Remove this pool from its parent's list of gizmos. */
177   if (pool->parent) 
178     delete_gizmo (pool->parent, (void *) (((char *) pool) + POOL_SIZE));
179   
180   free_all_gizmos (pool);
181
182   /* Free all the memory. */
183   {
184     struct pool_block *cur, *next;
185
186     pool->blocks->prev->next = NULL;
187     for (cur = pool->blocks; cur; cur = next)
188       {
189         next = cur->next;
190         free (cur);
191       }
192   }
193 }
194
195 /* Release all the memory and gizmos in POOL.
196    Blocks are not given back with free() but kept for later
197    allocations.  To give back memory, use a subpool instead. */ 
198 void
199 pool_clear (struct pool *pool) 
200 {
201   free_all_gizmos (pool);
202
203   /* Zero out block sizes. */
204   {
205     struct pool_block *cur;
206     
207     cur = pool->blocks;
208     do
209       {
210         cur->ofs = POOL_BLOCK_SIZE;
211         if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool) 
212           {
213             cur->ofs += POOL_SIZE;
214             if (pool->parent != NULL)
215               cur->ofs += POOL_GIZMO_SIZE; 
216           }
217         cur = cur->next;
218       }
219     while (cur != pool->blocks);
220   }
221 }
222 \f
223 /* Suballocation routines. */
224
225 /* Allocates a memory region AMT bytes in size from POOL and returns a
226    pointer to the region's start. */
227 void *
228 pool_alloc (struct pool *pool, size_t amt)
229 {
230   assert (pool != NULL);
231   
232 #ifndef DISCRETE_BLOCKS
233   if (amt <= MAX_SUBALLOC)
234     {
235       /* If there is space in this block, take it. */
236       struct pool_block *b = pool->blocks;
237       b->ofs = ROUND_UP (b->ofs, ALIGN_SIZE);
238       if (b->ofs + amt <= BLOCK_SIZE)
239         {
240           void *const p = ((char *) b) + b->ofs;
241           b->ofs += amt;
242           return p;
243         }
244
245       /* No space in this block, so we must make other
246          arrangements. */
247       if (b->next->ofs == 0) 
248         {
249           /* The next block is empty.  Use it. */
250           b = b->next;
251           b->ofs = POOL_BLOCK_SIZE;
252           if ((char *) b + POOL_BLOCK_SIZE == (char *) pool)
253             b->ofs += POOL_SIZE;
254         }
255       else 
256         {
257           /* Create a new block at the start of the list. */
258           b = xmalloc (BLOCK_SIZE);
259           b->next = pool->blocks;
260           b->prev = pool->blocks->prev;
261           b->ofs = POOL_BLOCK_SIZE;
262           pool->blocks->prev->next = b;
263           pool->blocks->prev = b;
264         }
265       pool->blocks = b;
266
267       /* Allocate space from B. */
268       b->ofs += amt;
269       return ((char *) b) + b->ofs - amt;
270     }
271   else
272 #endif
273     return pool_malloc (pool, amt);
274 }
275
276 /* Allocates a memory region N * S bytes in size from POOL and
277    returns a pointer to the region's start.
278    N must be nonnegative, S must be positive.
279    Terminates the program if the memory cannot be obtained,
280    including the case where N * S overflows the range of size_t. */
281 void *
282 pool_nalloc (struct pool *pool, size_t n, size_t s) 
283 {
284   if (xalloc_oversized (n, s))
285     xalloc_die ();
286   return pool_alloc (pool, n * s);
287 }
288
289 /* Allocates SIZE bytes in POOL, copies BUFFER into it, and
290    returns the new copy. */
291 void *
292 pool_clone (struct pool *pool, const void *buffer, size_t size)
293 {
294   void *block = pool_alloc (pool, size);
295   memcpy (block, buffer, size);
296   return block;
297 }
298
299 /* Duplicates STRING, which has LENGTH characters, within POOL,
300    and returns a pointer to the duplicate.  LENGTH should not
301    include the null terminator, which is always added to the
302    duplicate.  For use only with strings, because the returned
303    pointere may not be aligned properly for other types. */
304 char *
305 pool_strndup (struct pool *pool, const char *string, size_t length)
306 {
307   size_t size;
308   char *copy;
309
310   assert (pool && string);
311   size = length + 1;
312
313   /* Note that strings need not be aligned on any boundary. */
314 #ifndef DISCRETE_BLOCKS
315   {
316     struct pool_block *const b = pool->blocks;
317
318     if (b->ofs + size <= BLOCK_SIZE)
319       {
320         copy = ((char *) b) + b->ofs;
321         b->ofs += size;
322       }
323     else
324       copy = pool_alloc (pool, size);
325   }
326 #else
327   copy = pool_alloc (pool, size);
328 #endif
329
330   memcpy (copy, string, length);
331   copy[length] = '\0';
332   return copy;
333 }
334
335 /* Duplicates null-terminated STRING, within POOL, and returns a
336    pointer to the duplicate.  For use only with strings, because
337    the returned pointere may not be aligned properly for other
338    types. */
339 char *
340 pool_strdup (struct pool *pool, const char *string) 
341 {
342   return pool_strndup (pool, string, strlen (string));
343 }
344 \f
345 /* Standard allocation routines. */
346
347 /* Allocates AMT bytes using malloc(), to be managed by POOL, and
348    returns a pointer to the beginning of the block.
349    If POOL is a null pointer, then allocates a normal memory block
350    with malloc().  */
351 void *
352 pool_malloc (struct pool *pool, size_t amt)
353 {
354   if (pool != NULL)
355     {
356       if (amt != 0)
357         {
358           struct pool_gizmo *g = xmalloc (amt + POOL_GIZMO_SIZE);
359           g->type = POOL_GIZMO_MALLOC;
360           add_gizmo (pool, g);
361
362           return ((char *) g) + POOL_GIZMO_SIZE;
363         }
364       else
365         return NULL;
366     }
367   else
368     return xmalloc (amt);
369 }
370
371 /* Allocates and returns N elements of S bytes each, to be
372    managed by POOL.
373    If POOL is a null pointer, then allocates a normal memory block
374    with malloc().
375    N must be nonnegative, S must be positive.
376    Terminates the program if the memory cannot be obtained,
377    including the case where N * S overflows the range of size_t. */
378 void *
379 pool_nmalloc (struct pool *pool, size_t n, size_t s) 
380 {
381   if (xalloc_oversized (n, s))
382     xalloc_die ();
383   return pool_malloc (pool, n * s);
384 }
385
386 /* Changes the allocation size of the specified memory block P managed
387    by POOL to AMT bytes and returns a pointer to the beginning of the
388    block.
389    If POOL is a null pointer, then the block is reallocated in the
390    usual way with realloc(). */
391 void *
392 pool_realloc (struct pool *pool, void *p, size_t amt)
393 {
394   if (pool != NULL)
395     {
396       if (p != NULL)
397         {
398           if (amt != 0)
399             {
400               struct pool_gizmo *g = (void *) (((char *) p) - POOL_GIZMO_SIZE);
401               check_gizmo (pool, g);
402
403               g = xrealloc (g, amt + POOL_GIZMO_SIZE);
404               if (g->next)
405                 g->next->prev = g;
406               if (g->prev)
407                 g->prev->next = g;
408               else
409                 pool->gizmos = g;
410               check_gizmo (pool, g);
411
412               return ((char *) g) + POOL_GIZMO_SIZE;
413             }
414           else
415             {
416               pool_free (pool, p);
417               return NULL;
418             }
419         }
420       else
421         return pool_malloc (pool, amt);
422     }
423   else
424     return xrealloc (p, amt);
425 }
426
427 /* Changes the allocation size of the specified memory block P
428    managed by POOL to N * S bytes and returns a pointer to the
429    beginning of the block.
430    N must be nonnegative, S must be positive.
431    If POOL is a null pointer, then the block is reallocated in
432    the usual way with xrealloc().
433    Terminates the program if the memory cannot be obtained,
434    including the case where N * S overflows the range of size_t. */
435 void *
436 pool_nrealloc (struct pool *pool, void *p, size_t n, size_t s)
437 {
438   if (xalloc_oversized (n, s))
439     xalloc_die ();
440   return pool_realloc (pool, p, n * s);
441 }
442
443 /* Frees block P managed by POOL.
444    If POOL is a null pointer, then the block is freed as usual with
445    free(). */
446 void
447 pool_free (struct pool *pool, void *p)
448 {
449   if (pool != NULL && p != NULL)
450     {
451       struct pool_gizmo *g = (void *) (((char *) p) - POOL_GIZMO_SIZE);
452       check_gizmo (pool, g);
453       delete_gizmo (pool, g);
454       free (g);
455     }
456   else
457     free (p);
458 }
459 \f
460 /* Gizmo allocations. */
461
462 /* Creates and returns a pool as a subpool of POOL.
463    The subpool will be destroyed automatically when POOL is destroyed.
464    It may also be destroyed explicitly in advance. */
465 struct pool *
466 pool_create_subpool (struct pool *pool)
467 {
468   struct pool *subpool;
469   struct pool_gizmo *g;
470
471   assert (pool != NULL);
472   subpool = pool_create ();
473   subpool->parent = pool;
474
475   g = (void *) (((char *) subpool->blocks) + subpool->blocks->ofs);
476   subpool->blocks->ofs += POOL_GIZMO_SIZE;
477   
478   g->type = POOL_GIZMO_SUBPOOL;
479   g->p.subpool = subpool;
480
481   add_gizmo (pool, g);
482
483   return subpool;
484 }
485
486 /* Opens file FILENAME with mode MODE and returns a handle to it
487    if successful or a null pointer if not.
488    The file will be closed automatically when POOL is destroyed, or it
489    may be closed explicitly in advance using pool_fclose. */
490 FILE *
491 pool_fopen (struct pool *pool, const char *filename, const char *mode)
492 {
493   FILE *f;
494
495   assert (pool && filename && mode);
496   f = fopen (filename, mode);
497   if (f == NULL)
498     return NULL;
499
500   {
501     struct pool_gizmo *g = pool_alloc (pool, sizeof *g);
502     g->type = POOL_GIZMO_FILE;
503     g->p.file = f;
504     add_gizmo (pool, g);
505   }
506
507   return f;
508 }
509
510 /* Closes file FILE managed by POOL. */
511 int
512 pool_fclose (struct pool *pool, FILE *file)
513 {
514   assert (pool && file);
515   if (fclose (file) == EOF)
516     return EOF;
517   
518   {
519     struct pool_gizmo *g;
520
521     for (g = pool->gizmos; g; g = g->next)
522       if (g->type == POOL_GIZMO_FILE && g->p.file == file)
523         {
524           delete_gizmo (pool, g);
525           break;
526         }
527   }
528   
529   return 0;
530 }
531 \f
532 /* Registers FREE to be called with argument P.
533    P should be unique among those registered in POOL so that it can be
534    uniquely identified by pool_unregister().
535    If not unregistered, FREE will be called with argument P when POOL
536    is destroyed. */
537 void
538 pool_register (struct pool *pool, void (*free) (void *), void *p)
539 {
540   assert (pool && free && p);
541
542   {
543     struct pool_gizmo *g = pool_alloc (pool, sizeof *g);
544     g->type = POOL_GIZMO_REGISTERED;
545     g->p.registered.free = free;
546     g->p.registered.p = p;
547     add_gizmo (pool, g);
548   }
549 }
550
551 /* Unregisters previously registered P from POOL.
552    Returns nonzero only if P was found to be registered in POOL. */
553 int
554 pool_unregister (struct pool *pool, void *p)
555 {
556   assert (pool && p);
557   
558   {
559     struct pool_gizmo *g;
560
561     for (g = pool->gizmos; g; g = g->next)
562       if (g->type == POOL_GIZMO_REGISTERED && g->p.registered.p == p)
563         {
564           delete_gizmo (pool, g);
565           return 1;
566         }
567   }
568   
569   return 0;
570 }
571 \f
572 /* Partial freeing. */
573
574 /* Notes the state of POOL into MARK so that it may be restored
575    by a call to pool_release(). */
576 void
577 pool_mark (struct pool *pool, struct pool_mark *mark)
578 {
579   assert (pool && mark);
580
581   mark->block = pool->blocks;
582   mark->ofs = pool->blocks->ofs;
583
584   mark->serial = serial;
585 }
586
587 /* Restores to POOL the state recorded in MARK.
588    Emptied blocks are not given back with free() but kept for
589    later allocations.  To get that behavior, use a subpool
590    instead. */ 
591 void
592 pool_release (struct pool *pool, const struct pool_mark *mark)
593 {
594   assert (pool && mark);
595   
596   {
597     struct pool_gizmo *cur, *next;
598
599     for (cur = pool->gizmos; cur && cur->serial >= mark->serial; cur = next)
600       {
601         next = cur->next;
602         free_gizmo (cur);
603       }
604
605     if (cur != NULL)
606       {
607         cur->prev = NULL;
608         pool->gizmos = cur;
609       }
610     else
611       pool->gizmos = NULL;
612   }
613   
614   {
615     struct pool_block *cur;
616
617     for (cur = pool->blocks; cur != mark->block; cur = cur->next) 
618       {
619         cur->ofs = POOL_BLOCK_SIZE;
620         if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool) 
621           {
622             cur->ofs += POOL_SIZE;
623             if (pool->parent != NULL)
624               cur->ofs += POOL_GIZMO_SIZE; 
625           }
626       }
627     pool->blocks = mark->block;
628     pool->blocks->ofs = mark->ofs;
629   }
630 }
631 \f
632 /* Private functions. */
633
634 /* Adds GIZMO at the beginning of POOL's gizmo list. */
635 static void
636 add_gizmo (struct pool *pool, struct pool_gizmo *gizmo)
637 {
638   assert (pool && gizmo);
639
640   gizmo->pool = pool;
641   gizmo->next = pool->gizmos;
642   gizmo->prev = NULL;
643   if (pool->gizmos)
644     pool->gizmos->prev = gizmo;
645   pool->gizmos = gizmo;
646
647   gizmo->serial = serial++;
648
649   check_gizmo (pool, gizmo);
650 }
651  
652 /* Removes GIZMO from POOL's gizmo list. */
653 static void
654 delete_gizmo (struct pool *pool, struct pool_gizmo *gizmo)
655 {
656   assert (pool && gizmo);
657
658   check_gizmo (pool, gizmo);
659
660   if (gizmo->prev)
661     gizmo->prev->next = gizmo->next;
662   else
663     pool->gizmos = gizmo->next;
664   if (gizmo->next)
665     gizmo->next->prev = gizmo->prev;
666 }
667
668 /* Frees any of GIZMO's internal state.
669    GIZMO's data must not be referenced after calling this function. */
670 static void
671 free_gizmo (struct pool_gizmo *gizmo)
672 {
673   assert (gizmo != NULL);
674
675   switch (gizmo->type)
676     {
677     case POOL_GIZMO_MALLOC:
678       free (gizmo);
679       break;
680     case POOL_GIZMO_FILE:
681       fclose (gizmo->p.file);   /* Ignore errors. */
682       break;
683     case POOL_GIZMO_SUBPOOL:
684       gizmo->p.subpool->parent = NULL;
685       pool_destroy (gizmo->p.subpool);
686       break;
687     case POOL_GIZMO_REGISTERED:
688       gizmo->p.registered.free (gizmo->p.registered.p);
689       break;
690     default:
691       assert (0);
692     }
693 }
694
695 /* Free all the gizmos in POOL. */
696 static void
697 free_all_gizmos (struct pool *pool) 
698 {
699   struct pool_gizmo *cur, *next;
700
701   for (cur = pool->gizmos; cur; cur = next)
702     {
703       next = cur->next;
704       free_gizmo (cur);
705     }
706   pool->gizmos = NULL;
707 }
708
709 static void
710 check_gizmo (struct pool *p, struct pool_gizmo *g) 
711 {
712   assert (g->pool == p);
713   assert (g->next == NULL || g->next->prev == g);
714   assert ((g->prev != NULL && g->prev->next == g)
715           || (g->prev == NULL && p->gizmos == g));
716
717 }
718 \f
719 /* Self-test routine. */
720
721 #include <errno.h>
722 #include <stdio.h>
723 #include <stdlib.h>
724 #include <string.h>
725 #include <time.h>
726
727 #define N_ITERATIONS 8192
728 #define N_FILES 16
729
730 /* Self-test routine.
731    This is not exhaustive, but it can be useful. */
732 int
733 cmd_debug_pool (void)
734 {
735   int seed = time (0) * 257 % 32768;
736
737   for (;;)
738     {
739       struct pool *pool;
740       struct pool_mark m1, m2;
741       FILE *files[N_FILES];
742       int cur_file;
743       long i;
744
745       printf ("Random number seed: %d\n", seed);
746       srand (seed++);
747
748       printf ("Creating pool...\n");
749       pool = pool_create ();
750
751       printf ("Marking pool state...\n");
752       pool_mark (pool, &m1);
753
754       printf ("    Populating pool with random-sized small objects...\n");
755       for (i = 0; i < N_ITERATIONS; i++)
756         {
757           size_t size = rand () % MAX_SUBALLOC;
758           void *p = pool_alloc (pool, size);
759           memset (p, 0, size);
760         }
761
762       printf ("    Marking pool state...\n");
763       pool_mark (pool, &m2);
764       
765       printf ("       Populating pool with random-sized small "
766               "and large objects...\n");
767       for (i = 0; i < N_ITERATIONS; i++)
768         {
769           size_t size = rand () % (2 * MAX_SUBALLOC);
770           void *p = pool_alloc (pool, size);
771           memset (p, 0, size);
772         }
773
774       printf ("    Releasing pool state...\n");
775       pool_release (pool, &m2);
776
777       printf ("    Populating pool with random objects and gizmos...\n");
778       for (i = 0; i < N_FILES; i++)
779         files[i] = NULL;
780       cur_file = 0;
781       for (i = 0; i < N_ITERATIONS; i++)
782         {
783           int type = rand () % 32;
784
785           if (type == 0)
786             {
787               if (files[cur_file] != NULL
788                   && EOF == pool_fclose (pool, files[cur_file]))
789                 printf ("error on fclose: %s\n", strerror (errno));
790
791               files[cur_file] = pool_fopen (pool, "/dev/null", "r");
792
793               if (++cur_file >= N_FILES)
794                 cur_file = 0;
795             }
796           else if (type == 1)
797             pool_create_subpool (pool);
798           else 
799             {
800               size_t size = rand () % (2 * MAX_SUBALLOC);
801               void *p = pool_alloc (pool, size);
802               memset (p, 0, size);
803             }
804         }
805       
806       printf ("Releasing pool state...\n");
807       pool_release (pool, &m1);
808
809       printf ("Destroying pool...\n");
810       pool_destroy (pool);
811
812       putchar ('\n');
813     }
814
815   return CMD_SUCCESS;
816 }
817