mlt_repository.[hc]:
[melted] / src / framework / mlt_properties.c
1 /*
2 * mlt_properties.c -- base properties class
3 * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "mlt_properties.h"
22 #include "mlt_property.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28
29 #include <sys/types.h>
30 #include <dirent.h>
31
32 /* ---------------- // Private Implementation // ---------------- */
33
34 /** Private implementation of the property list.
35 */
36
37 typedef struct
38 {
39 int hash[ 199 ];
40 char **name;
41 mlt_property *value;
42 int count;
43 int size;
44 mlt_properties mirror;
45 int ref_count;
46 }
47 property_list;
48
49 /** Memory leak checks.
50 */
51
52 //#define _MLT_PROPERTY_CHECKS_ 2
53
54 #ifdef _MLT_PROPERTY_CHECKS_
55 static int properties_created = 0;
56 static int properties_destroyed = 0;
57 #endif
58
59 /** Basic implementation.
60 */
61
62 int mlt_properties_init( mlt_properties this, void *child )
63 {
64 if ( this != NULL )
65 {
66 #ifdef _MLT_PROPERTY_CHECKS_
67 // Increment number of properties created
68 properties_created ++;
69 #endif
70
71 // NULL all methods
72 memset( this, 0, sizeof( struct mlt_properties_s ) );
73
74 // Assign the child of the object
75 this->child = child;
76
77 // Allocate the local structure
78 this->local = calloc( sizeof( property_list ), 1 );
79
80 // Increment the ref count
81 ( ( property_list * )this->local )->ref_count = 1;
82 }
83
84 // Check that initialisation was successful
85 return this != NULL && this->local == NULL;
86 }
87
88 /** Constructor for stand alone object.
89 */
90
91 mlt_properties mlt_properties_new( )
92 {
93 // Construct a standalone properties object
94 mlt_properties this = calloc( sizeof( struct mlt_properties_s ), 1 );
95
96 // Initialise this
97 mlt_properties_init( this, NULL );
98
99 // Return the pointer
100 return this;
101 }
102
103 /** Load properties from a file.
104 */
105
106 mlt_properties mlt_properties_load( const char *filename )
107 {
108 // Construct a standalone properties object
109 mlt_properties this = mlt_properties_new( );
110
111 if ( this != NULL )
112 {
113 // Open the file
114 FILE *file = fopen( filename, "r" );
115
116 // Load contents of file
117 if ( file != NULL )
118 {
119 // Temp string
120 char temp[ 1024 ];
121 char last[ 1024 ] = "";
122
123 // Read each string from the file
124 while( fgets( temp, 1024, file ) )
125 {
126 // Chomp the string
127 temp[ strlen( temp ) - 1 ] = '\0';
128
129 // Check if the line starts with a .
130 if ( temp[ 0 ] == '.' )
131 {
132 char temp2[ 1024 ];
133 sprintf( temp2, "%s%s", last, temp );
134 strcpy( temp, temp2 );
135 }
136 else if ( strchr( temp, '=' ) )
137 {
138 strcpy( last, temp );
139 *( strchr( last, '=' ) ) = '\0';
140 }
141
142 // Parse and set the property
143 if ( strcmp( temp, "" ) && temp[ 0 ] != '#' )
144 mlt_properties_parse( this, temp );
145 }
146
147 // Close the file
148 fclose( file );
149 }
150 }
151
152 // Return the pointer
153 return this;
154 }
155
156 static inline int generate_hash( const char *name )
157 {
158 int hash = 0;
159 int i = 1;
160 while ( *name )
161 hash = ( hash + ( i ++ * ( *name ++ & 31 ) ) ) % 199;
162 return hash;
163 }
164
165 /** Special case - when a container (such as fezzik) is protecting another
166 producer, we need to ensure that properties are passed through to the
167 real producer.
168 */
169
170 static inline void mlt_properties_do_mirror( mlt_properties this, const char *name )
171 {
172 property_list *list = this->local;
173 if ( list->mirror != NULL )
174 {
175 char *value = mlt_properties_get( this, name );
176 if ( value != NULL )
177 mlt_properties_set( list->mirror, name, value );
178 }
179 }
180
181 /** Maintain ref count to allow multiple uses of an mlt object.
182 */
183
184 int mlt_properties_inc_ref( mlt_properties this )
185 {
186 if ( this != NULL )
187 {
188 property_list *list = this->local;
189 return ++ list->ref_count;
190 }
191 return 0;
192 }
193
194 /** Maintain ref count to allow multiple uses of an mlt object.
195 */
196
197 int mlt_properties_dec_ref( mlt_properties this )
198 {
199 if ( this != NULL )
200 {
201 property_list *list = this->local;
202 return -- list->ref_count;
203 }
204 return 0;
205 }
206
207 /** Return the ref count of this object.
208 */
209
210 int mlt_properties_ref_count( mlt_properties this )
211 {
212 if ( this != NULL )
213 {
214 property_list *list = this->local;
215 return list->ref_count;
216 }
217 return 0;
218 }
219
220 /** Mirror properties set on 'this' to 'that'.
221 */
222
223 void mlt_properties_mirror( mlt_properties this, mlt_properties that )
224 {
225 property_list *list = this->local;
226 list->mirror = that;
227 }
228
229 /** Inherit all serialisable properties from that into this.
230 */
231
232 int mlt_properties_inherit( mlt_properties this, mlt_properties that )
233 {
234 int count = mlt_properties_count( that );
235 int i = 0;
236 for ( i = 0; i < count; i ++ )
237 {
238 char *value = mlt_properties_get_value( that, i );
239 if ( value != NULL )
240 {
241 char *name = mlt_properties_get_name( that, i );
242 mlt_properties_set( this, name, value );
243 }
244 }
245 return 0;
246 }
247
248 /** Pass all properties from 'that' that match the prefix to 'this' (excluding the prefix).
249 */
250
251 int mlt_properties_pass( mlt_properties this, mlt_properties that, const char *prefix )
252 {
253 int count = mlt_properties_count( that );
254 int length = strlen( prefix );
255 int i = 0;
256 for ( i = 0; i < count; i ++ )
257 {
258 char *name = mlt_properties_get_name( that, i );
259 if ( !strncmp( name, prefix, length ) )
260 {
261 char *value = mlt_properties_get_value( that, i );
262 if ( value != NULL )
263 mlt_properties_set( this, name + length, value );
264 }
265 }
266 return 0;
267 }
268
269 /** Locate a property by name
270 */
271
272 static inline mlt_property mlt_properties_find( mlt_properties this, const char *name )
273 {
274 property_list *list = this->local;
275 mlt_property value = NULL;
276 int key = generate_hash( name );
277 int i = list->hash[ key ] - 1;
278
279 if ( i >= 0 )
280 {
281 // Check if we're hashed
282 if ( list->count > 0 &&
283 name[ 0 ] == list->name[ i ][ 0 ] &&
284 !strcmp( list->name[ i ], name ) )
285 value = list->value[ i ];
286
287 // Locate the item
288 for ( i = list->count - 1; value == NULL && i >= 0; i -- )
289 if ( name[ 0 ] == list->name[ i ][ 0 ] && !strcmp( list->name[ i ], name ) )
290 value = list->value[ i ];
291 }
292
293 return value;
294 }
295
296 /** Add a new property.
297 */
298
299 static mlt_property mlt_properties_add( mlt_properties this, const char *name )
300 {
301 property_list *list = this->local;
302 int key = generate_hash( name );
303
304 // Check that we have space and resize if necessary
305 if ( list->count == list->size )
306 {
307 list->size += 50;
308 list->name = realloc( list->name, list->size * sizeof( const char * ) );
309 list->value = realloc( list->value, list->size * sizeof( mlt_property ) );
310 }
311
312 // Assign name/value pair
313 list->name[ list->count ] = strdup( name );
314 list->value[ list->count ] = mlt_property_init( );
315
316 // Assign to hash table
317 if ( list->hash[ key ] == 0 )
318 list->hash[ key ] = list->count + 1;
319
320 // Return and increment count accordingly
321 return list->value[ list->count ++ ];
322 }
323
324 /** Fetch a property by name - this includes add if not found.
325 */
326
327 static mlt_property mlt_properties_fetch( mlt_properties this, const char *name )
328 {
329 // Try to find an existing property first
330 mlt_property property = mlt_properties_find( this, name );
331
332 // If it wasn't found, create one
333 if ( property == NULL )
334 property = mlt_properties_add( this, name );
335
336 // Return the property
337 return property;
338 }
339
340 /** Pass property 'name' from 'that' to 'this'
341 * Who to blame: Zach <zachary.drew@gmail.com>
342 */
343
344 void mlt_properties_pass_property( mlt_properties this, mlt_properties that, const char *name )
345 {
346 // Make sure the source property isn't null.
347 mlt_property that_prop = mlt_properties_find( that, name );
348 if( that_prop == NULL )
349 return;
350
351 mlt_property_pass( mlt_properties_fetch( this, name ), that_prop );
352 }
353
354 /** Pass all properties from 'that' to 'this' as found in comma seperated 'list'.
355 * Who to blame: Zach <zachary.drew@gmail.com>
356 */
357
358 int mlt_properties_pass_list( mlt_properties this, mlt_properties that, const char *list )
359 {
360 char *props = strdup( list );
361 char *ptr = props;
362 char *delim = " ,\t\n"; // Any combination of spaces, commas, tabs, and newlines
363 int count, done = 0;
364
365 while( !done )
366 {
367 count = strcspn( ptr, delim );
368
369 if( ptr[count] == '\0' )
370 done = 1;
371 else
372 ptr[count] = '\0'; // Make it a real string
373
374 mlt_properties_pass_property( this, that, ptr );
375
376 ptr += count + 1;
377 ptr += strspn( ptr, delim );
378 }
379
380 free( props );
381
382 return 0;
383 }
384
385
386 /** Set the property.
387 */
388
389 int mlt_properties_set( mlt_properties this, const char *name, const char *value )
390 {
391 int error = 1;
392
393 // Fetch the property to work with
394 mlt_property property = mlt_properties_fetch( this, name );
395
396 // Set it if not NULL
397 if ( property == NULL )
398 {
399 fprintf( stderr, "Whoops - %s not found (should never occur)\n", name );
400 }
401 else if ( value == NULL )
402 {
403 error = mlt_property_set_string( property, value );
404 mlt_properties_do_mirror( this, name );
405 }
406 else if ( *value != '@' )
407 {
408 error = mlt_property_set_string( property, value );
409 mlt_properties_do_mirror( this, name );
410 }
411 else if ( value[ 0 ] == '@' )
412 {
413 int total = 0;
414 int current = 0;
415 char id[ 255 ];
416 char op = '+';
417
418 value ++;
419
420 while ( *value != '\0' )
421 {
422 int length = strcspn( value, "+-*/" );
423
424 // Get the identifier
425 strncpy( id, value, length );
426 id[ length ] = '\0';
427 value += length;
428
429 // Determine the value
430 if ( isdigit( id[ 0 ] ) )
431 current = atof( id );
432 else
433 current = mlt_properties_get_int( this, id );
434
435 // Apply the operation
436 switch( op )
437 {
438 case '+':
439 total += current;
440 break;
441 case '-':
442 total -= current;
443 break;
444 case '*':
445 total *= current;
446 break;
447 case '/':
448 total /= current;
449 break;
450 }
451
452 // Get the next op
453 op = *value != '\0' ? *value ++ : ' ';
454 }
455
456 error = mlt_property_set_int( property, total );
457 mlt_properties_do_mirror( this, name );
458 }
459
460 mlt_events_fire( this, "property-changed", name, NULL );
461
462 return error;
463 }
464
465 /** Set or default the property.
466 */
467
468 int mlt_properties_set_or_default( mlt_properties this, const char *name, const char *value, const char *def )
469 {
470 return mlt_properties_set( this, name, value == NULL ? def : value );
471 }
472
473 /** Get a string value by name.
474 */
475
476 char *mlt_properties_get( mlt_properties this, const char *name )
477 {
478 mlt_property value = mlt_properties_find( this, name );
479 return value == NULL ? NULL : mlt_property_get_string( value );
480 }
481
482 /** Get a name by index.
483 */
484
485 char *mlt_properties_get_name( mlt_properties this, int index )
486 {
487 property_list *list = this->local;
488 if ( index >= 0 && index < list->count )
489 return list->name[ index ];
490 return NULL;
491 }
492
493 /** Get a string value by index.
494 */
495
496 char *mlt_properties_get_value( mlt_properties this, int index )
497 {
498 property_list *list = this->local;
499 if ( index >= 0 && index < list->count )
500 return mlt_property_get_string( list->value[ index ] );
501 return NULL;
502 }
503
504 /** Get a data value by index.
505 */
506
507 void *mlt_properties_get_data_at( mlt_properties this, int index, int *size )
508 {
509 property_list *list = this->local;
510 if ( index >= 0 && index < list->count )
511 return mlt_property_get_data( list->value[ index ], size );
512 return NULL;
513 }
514
515 /** Return the number of items in the list.
516 */
517
518 int mlt_properties_count( mlt_properties this )
519 {
520 property_list *list = this->local;
521 return list->count;
522 }
523
524 /** Set a value by parsing a name=value string
525 */
526
527 int mlt_properties_parse( mlt_properties this, const char *namevalue )
528 {
529 char *name = strdup( namevalue );
530 char *value = NULL;
531 int error = 0;
532 char *ptr = strchr( name, '=' );
533
534 if ( ptr )
535 {
536 *( ptr ++ ) = '\0';
537
538 if ( *ptr != '\"' )
539 {
540 value = strdup( ptr );
541 }
542 else
543 {
544 ptr ++;
545 value = strdup( ptr );
546 if ( value != NULL && value[ strlen( value ) - 1 ] == '\"' )
547 value[ strlen( value ) - 1 ] = '\0';
548 }
549 }
550 else
551 {
552 value = strdup( "" );
553 }
554
555 error = mlt_properties_set( this, name, value );
556
557 free( name );
558 free( value );
559
560 return error;
561 }
562
563 /** Get a value associated to the name.
564 */
565
566 int mlt_properties_get_int( mlt_properties this, const char *name )
567 {
568 mlt_property value = mlt_properties_find( this, name );
569 return value == NULL ? 0 : mlt_property_get_int( value );
570 }
571
572 /** Set a value associated to the name.
573 */
574
575 int mlt_properties_set_int( mlt_properties this, const char *name, int value )
576 {
577 int error = 1;
578
579 // Fetch the property to work with
580 mlt_property property = mlt_properties_fetch( this, name );
581
582 // Set it if not NULL
583 if ( property != NULL )
584 {
585 error = mlt_property_set_int( property, value );
586 mlt_properties_do_mirror( this, name );
587 }
588
589 mlt_events_fire( this, "property-changed", name, NULL );
590
591 return error;
592 }
593
594 /** Get a value associated to the name.
595 */
596
597 int64_t mlt_properties_get_int64( mlt_properties this, const char *name )
598 {
599 mlt_property value = mlt_properties_find( this, name );
600 return value == NULL ? 0 : mlt_property_get_int64( value );
601 }
602
603 /** Set a value associated to the name.
604 */
605
606 int mlt_properties_set_int64( mlt_properties this, const char *name, int64_t value )
607 {
608 int error = 1;
609
610 // Fetch the property to work with
611 mlt_property property = mlt_properties_fetch( this, name );
612
613 // Set it if not NULL
614 if ( property != NULL )
615 {
616 error = mlt_property_set_int64( property, value );
617 mlt_properties_do_mirror( this, name );
618 }
619
620 mlt_events_fire( this, "property-changed", name, NULL );
621
622 return error;
623 }
624
625 /** Get a value associated to the name.
626 */
627
628 double mlt_properties_get_double( mlt_properties this, const char *name )
629 {
630 mlt_property value = mlt_properties_find( this, name );
631 return value == NULL ? 0 : mlt_property_get_double( value );
632 }
633
634 /** Set a value associated to the name.
635 */
636
637 int mlt_properties_set_double( mlt_properties this, const char *name, double value )
638 {
639 int error = 1;
640
641 // Fetch the property to work with
642 mlt_property property = mlt_properties_fetch( this, name );
643
644 // Set it if not NULL
645 if ( property != NULL )
646 {
647 error = mlt_property_set_double( property, value );
648 mlt_properties_do_mirror( this, name );
649 }
650
651 mlt_events_fire( this, "property-changed", name, NULL );
652
653 return error;
654 }
655
656 /** Get a value associated to the name.
657 */
658
659 mlt_position mlt_properties_get_position( mlt_properties this, const char *name )
660 {
661 mlt_property value = mlt_properties_find( this, name );
662 return value == NULL ? 0 : mlt_property_get_position( value );
663 }
664
665 /** Set a value associated to the name.
666 */
667
668 int mlt_properties_set_position( mlt_properties this, const char *name, mlt_position value )
669 {
670 int error = 1;
671
672 // Fetch the property to work with
673 mlt_property property = mlt_properties_fetch( this, name );
674
675 // Set it if not NULL
676 if ( property != NULL )
677 {
678 error = mlt_property_set_position( property, value );
679 mlt_properties_do_mirror( this, name );
680 }
681
682 mlt_events_fire( this, "property-changed", name, NULL );
683
684 return error;
685 }
686
687 /** Get a value associated to the name.
688 */
689
690 void *mlt_properties_get_data( mlt_properties this, const char *name, int *length )
691 {
692 mlt_property value = mlt_properties_find( this, name );
693 return value == NULL ? NULL : mlt_property_get_data( value, length );
694 }
695
696 /** Set a value associated to the name.
697 */
698
699 int mlt_properties_set_data( mlt_properties this, const char *name, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise )
700 {
701 int error = 1;
702
703 // Fetch the property to work with
704 mlt_property property = mlt_properties_fetch( this, name );
705
706 // Set it if not NULL
707 if ( property != NULL )
708 error = mlt_property_set_data( property, value, length, destroy, serialise );
709
710 mlt_events_fire( this, "property-changed", name, NULL );
711
712 return error;
713 }
714
715 /** Rename a property.
716 */
717
718 int mlt_properties_rename( mlt_properties this, const char *source, const char *dest )
719 {
720 mlt_property value = mlt_properties_find( this, dest );
721
722 if ( value == NULL )
723 {
724 property_list *list = this->local;
725 int i = 0;
726
727 // Locate the item
728 for ( i = 0; i < list->count; i ++ )
729 {
730 if ( !strcmp( list->name[ i ], source ) )
731 {
732 free( list->name[ i ] );
733 list->name[ i ] = strdup( dest );
734 list->hash[ generate_hash( dest ) ] = i + 1;
735 break;
736 }
737 }
738 }
739
740 return value != NULL;
741 }
742
743 /** Dump the properties.
744 */
745
746 void mlt_properties_dump( mlt_properties this, FILE *output )
747 {
748 property_list *list = this->local;
749 int i = 0;
750 for ( i = 0; i < list->count; i ++ )
751 if ( mlt_properties_get( this, list->name[ i ] ) != NULL )
752 fprintf( output, "%s=%s\n", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
753 }
754
755 void mlt_properties_debug( mlt_properties this, const char *title, FILE *output )
756 {
757 if ( output == NULL ) output = stderr;
758 fprintf( output, "%s: ", title );
759 if ( this != NULL )
760 {
761 property_list *list = this->local;
762 int i = 0;
763 fprintf( output, "[ ref=%d", list->ref_count );
764 for ( i = 0; i < list->count; i ++ )
765 if ( mlt_properties_get( this, list->name[ i ] ) != NULL )
766 fprintf( output, ", %s=%s", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
767 else
768 fprintf( output, ", %s=%p", list->name[ i ], mlt_properties_get_data( this, list->name[ i ], NULL ) );
769 fprintf( output, " ]" );
770 }
771 fprintf( output, "\n" );
772 }
773
774 int mlt_properties_save( mlt_properties this, const char *filename )
775 {
776 int error = 1;
777 FILE *f = fopen( filename, "w" );
778 if ( f != NULL )
779 {
780 mlt_properties_dump( this, f );
781 fclose( f );
782 error = 0;
783 }
784 return error;
785 }
786
787 /* This is a very basic cross platform fnmatch replacement - it will fail in
788 ** many cases, but for the basic *.XXX and YYY*.XXX, it will work ok.
789 */
790
791 static int mlt_fnmatch( const char *wild, const char *file )
792 {
793 int f = 0;
794 int w = 0;
795
796 while( f < strlen( file ) && w < strlen( wild ) )
797 {
798 if ( wild[ w ] == '*' )
799 {
800 w ++;
801 if ( w == strlen( wild ) )
802 f = strlen( file );
803 while ( f != strlen( file ) && tolower( file[ f ] ) != tolower( wild[ w ] ) )
804 f ++;
805 }
806 else if ( wild[ w ] == '?' || tolower( file[ f ] ) == tolower( wild[ w ] ) )
807 {
808 f ++;
809 w ++;
810 }
811 else if ( wild[ 0 ] == '*' )
812 {
813 w = 0;
814 }
815 else
816 {
817 return 0;
818 }
819 }
820
821 return strlen( file ) == f && strlen( wild ) == w;
822 }
823
824 static int mlt_compare( const void *this, const void *that )
825 {
826 return strcmp( mlt_property_get_string( *( mlt_property * )this ), mlt_property_get_string( *( mlt_property * )that ) );
827 }
828
829 /* Obtains an optionally sorted list of the files found in a directory with a specific wild card.
830 * Entries in the list have a numeric name (running from 0 to count - 1). Only values change
831 * position if sort is enabled. Designed to be posix compatible (linux, os/x, mingw etc).
832 */
833
834 int mlt_properties_dir_list( mlt_properties this, const char *dirname, const char *pattern, int sort )
835 {
836 DIR *dir = opendir( dirname );
837
838 if ( dir )
839 {
840 char key[ 20 ];
841 struct dirent *de = readdir( dir );
842 char fullname[ 1024 ];
843 while( de != NULL )
844 {
845 sprintf( key, "%d", mlt_properties_count( this ) );
846 snprintf( fullname, 1024, "%s/%s", dirname, de->d_name );
847 if ( pattern == NULL )
848 mlt_properties_set( this, key, fullname );
849 else if ( de->d_name[ 0 ] != '.' && mlt_fnmatch( pattern, de->d_name ) )
850 mlt_properties_set( this, key, fullname );
851 de = readdir( dir );
852 }
853
854 closedir( dir );
855 }
856
857 if ( sort && mlt_properties_count( this ) )
858 {
859 property_list *list = this->local;
860 qsort( list->value, mlt_properties_count( this ), sizeof( mlt_property ), mlt_compare );
861 }
862
863 return mlt_properties_count( this );
864 }
865
866 /** Close the list.
867 */
868
869 void mlt_properties_close( mlt_properties this )
870 {
871 if ( this != NULL && mlt_properties_dec_ref( this ) <= 0 )
872 {
873 if ( this->close != NULL )
874 {
875 this->close( this->close_object );
876 }
877 else
878 {
879 property_list *list = this->local;
880 int index = 0;
881
882 #if _MLT_PROPERTY_CHECKS_ == 1
883 // Show debug info
884 mlt_properties_debug( this, "Closing", stderr );
885 #endif
886
887 #ifdef _MLT_PROPERTY_CHECKS_
888 // Increment destroyed count
889 properties_destroyed ++;
890
891 // Show current stats - these should match when the app is closed
892 fprintf( stderr, "Created %d, destroyed %d\n", properties_created, properties_destroyed );
893 #endif
894
895 // Clean up names and values
896 for ( index = list->count - 1; index >= 0; index -- )
897 {
898 free( list->name[ index ] );
899 mlt_property_close( list->value[ index ] );
900 }
901
902 // Clear up the list
903 free( list->name );
904 free( list->value );
905 free( list );
906
907 // Free this now if this has no child
908 if ( this->child == NULL )
909 free( this );
910 }
911 }
912 }
913