]> git.sesse.net Git - mlt/blob - src/framework/mlt_properties.c
move binary modules to libdir - affects MLT_REPOSITORY
[mlt] / 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 ( de->d_name[ 0 ] != '.' && mlt_fnmatch( pattern, de->d_name ) )
848                                 mlt_properties_set( this, key, fullname );
849                         de = readdir( dir );
850                 }
851
852                 closedir( dir );
853         }
854
855         if ( sort && mlt_properties_count( this ) )
856         {
857                 property_list *list = this->local;
858                 qsort( list->value, mlt_properties_count( this ), sizeof( mlt_property ), mlt_compare );
859         }
860
861         return mlt_properties_count( this );
862 }
863
864 /** Close the list.
865 */
866
867 void mlt_properties_close( mlt_properties this )
868 {
869         if ( this != NULL && mlt_properties_dec_ref( this ) <= 0 )
870         {
871                 if ( this->close != NULL )
872                 {
873                         this->close( this->close_object );
874                 }
875                 else
876                 {
877                         property_list *list = this->local;
878                         int index = 0;
879
880 #if _MLT_PROPERTY_CHECKS_ == 1
881                         // Show debug info
882                         mlt_properties_debug( this, "Closing", stderr );
883 #endif
884
885 #ifdef _MLT_PROPERTY_CHECKS_
886                         // Increment destroyed count
887                         properties_destroyed ++;
888
889                         // Show current stats - these should match when the app is closed
890                         fprintf( stderr, "Created %d, destroyed %d\n", properties_created, properties_destroyed );
891 #endif
892
893                         // Clean up names and values
894                         for ( index = list->count - 1; index >= 0; index -- )
895                         {
896                                 free( list->name[ index ] );
897                                 mlt_property_close( list->value[ index ] );
898                         }
899         
900                         // Clear up the list
901                         free( list->name );
902                         free( list->value );
903                         free( list );
904         
905                         // Free this now if this has no child
906                         if ( this->child == NULL )
907                                 free( this );
908                 }
909         }
910 }
911