]> git.sesse.net Git - mlt/blob - src/framework/mlt_properties.c
7e55da91c72d09a96025625c0b82d7c83f4d34ee
[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  * Contributor: Dan Dennedy <dan@dennedy.org>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "mlt_properties.h"
23 #include "mlt_property.h"
24 #include "mlt_deque.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <stdarg.h>
31 #include <pthread.h>
32 #include <sys/types.h>
33 #include <dirent.h>
34
35 /* ---------------- // Private Implementation // ---------------- */
36
37 /** Private implementation of the property list.
38 */
39
40 typedef struct
41 {
42         int hash[ 199 ];
43         char **name;
44         mlt_property *value;
45         int count;
46         int size;
47         mlt_properties mirror;
48         int ref_count;
49         pthread_mutex_t mutex;
50 }
51 property_list;
52
53 /** Memory leak checks.
54 */
55
56 //#define _MLT_PROPERTY_CHECKS_ 2
57
58 #ifdef _MLT_PROPERTY_CHECKS_
59 static int properties_created = 0;
60 static int properties_destroyed = 0;
61 #endif
62
63 /** Basic implementation.
64 */
65
66 int mlt_properties_init( mlt_properties this, void *child )
67 {
68         if ( this != NULL )
69         {
70 #ifdef _MLT_PROPERTY_CHECKS_
71                 // Increment number of properties created
72                 properties_created ++;
73 #endif
74
75                 // NULL all methods
76                 memset( this, 0, sizeof( struct mlt_properties_s ) );
77
78                 // Assign the child of the object
79                 this->child = child;
80
81                 // Allocate the local structure
82                 this->local = calloc( sizeof( property_list ), 1 );
83
84                 // Increment the ref count
85                 ( ( property_list * )this->local )->ref_count = 1;
86                 pthread_mutex_init( &( ( property_list * )this->local )->mutex, NULL );;
87         }
88
89         // Check that initialisation was successful
90         return this != NULL && this->local == NULL;
91 }
92
93 /** Constructor for stand alone object.
94 */
95
96 mlt_properties mlt_properties_new( )
97 {
98         // Construct a standalone properties object
99         mlt_properties this = calloc( sizeof( struct mlt_properties_s ), 1 );
100
101         // Initialise this
102         mlt_properties_init( this, NULL );
103
104         // Return the pointer
105         return this;
106 }
107
108 /** Load properties from a .properties file (DEPRECATED).
109 */
110
111 mlt_properties mlt_properties_load( const char *filename )
112 {
113         // Construct a standalone properties object
114         mlt_properties this = mlt_properties_new( );
115
116         if ( this != NULL )
117         {
118                 // Open the file
119                 FILE *file = fopen( filename, "r" );
120
121                 // Load contents of file
122                 if ( file != NULL )
123                 {
124                         // Temp string
125                         char temp[ 1024 ];
126                         char last[ 1024 ] = "";
127
128                         // Read each string from the file
129                         while( fgets( temp, 1024, file ) )
130                         {
131                                 // Chomp the string
132                                 temp[ strlen( temp ) - 1 ] = '\0';
133
134                                 // Check if the line starts with a .
135                                 if ( temp[ 0 ] == '.' )
136                                 {
137                                         char temp2[ 1024 ];
138                                         sprintf( temp2, "%s%s", last, temp );
139                                         strcpy( temp, temp2 );
140                                 }
141                                 else if ( strchr( temp, '=' ) )
142                                 {
143                                         strcpy( last, temp );
144                                         *( strchr( last, '=' ) ) = '\0';
145                                 }
146
147                                 // Parse and set the property
148                                 if ( strcmp( temp, "" ) && temp[ 0 ] != '#' )
149                                         mlt_properties_parse( this, temp );
150                         }
151
152                         // Close the file
153                         fclose( file );
154                 }
155         }
156
157         // Return the pointer
158         return this;
159 }
160
161 static inline int generate_hash( const char *name )
162 {
163         int hash = 0;
164         int i = 1;
165         while ( *name )
166                 hash = ( hash + ( i ++ * ( *name ++ & 31 ) ) ) % 199;
167         return hash;
168 }
169
170 /** Special case - when a container (such as fezzik) is protecting another 
171         producer, we need to ensure that properties are passed through to the
172         real producer.
173 */
174
175 static inline void mlt_properties_do_mirror( mlt_properties this, const char *name )
176 {
177         property_list *list = this->local;
178         if ( list->mirror != NULL ) 
179         {
180                 char *value = mlt_properties_get( this, name );
181                 if ( value != NULL )
182                         mlt_properties_set( list->mirror, name, value );
183         }
184 }
185
186 /** Maintain ref count to allow multiple uses of an mlt object.
187 */
188
189 int mlt_properties_inc_ref( mlt_properties this )
190 {
191         int result = 0;
192         if ( this != NULL )
193         {
194                 property_list *list = this->local;
195                 pthread_mutex_lock( &list->mutex );
196                 result = ++ list->ref_count;
197                 pthread_mutex_unlock( &list->mutex );
198         }
199         return result;
200 }
201
202 /** Maintain ref count to allow multiple uses of an mlt object.
203 */
204
205 int mlt_properties_dec_ref( mlt_properties this )
206 {
207         int result = 0;
208         if ( this != NULL )
209         {
210                 property_list *list = this->local;
211                 pthread_mutex_lock( &list->mutex );
212                 result = -- list->ref_count;
213                 pthread_mutex_unlock( &list->mutex );
214         }
215         return result;
216 }
217
218 /** Return the ref count of this object.
219 */
220
221 int mlt_properties_ref_count( mlt_properties this )
222 {
223         if ( this != NULL )
224         {
225                 property_list *list = this->local;
226                 return list->ref_count;
227         }
228         return 0;
229 }
230
231 /** Mirror properties set on 'this' to 'that'.
232 */
233
234 void mlt_properties_mirror( mlt_properties this, mlt_properties that )
235 {
236         property_list *list = this->local;
237         list->mirror = that;
238 }
239
240 /** Inherit all serialisable properties from that into this.
241 */
242
243 int mlt_properties_inherit( mlt_properties this, mlt_properties that )
244 {
245         int count = mlt_properties_count( that );
246         int i = 0;
247         for ( i = 0; i < count; i ++ )
248         {
249                 char *value = mlt_properties_get_value( that, i );
250                 if ( value != NULL )
251                 {
252                         char *name = mlt_properties_get_name( that, i );
253                         mlt_properties_set( this, name, value );
254                 }
255         }
256         return 0;
257 }
258
259 /** Pass all properties from 'that' that match the prefix to 'this' (excluding the prefix).
260 */
261
262 int mlt_properties_pass( mlt_properties this, mlt_properties that, const char *prefix )
263 {
264         int count = mlt_properties_count( that );
265         int length = strlen( prefix );
266         int i = 0;
267         for ( i = 0; i < count; i ++ )
268         {
269                 char *name = mlt_properties_get_name( that, i );
270                 if ( !strncmp( name, prefix, length ) )
271                 {
272                         char *value = mlt_properties_get_value( that, i );
273                         if ( value != NULL )
274                                 mlt_properties_set( this, name + length, value );
275                 }
276         }
277         return 0;
278 }
279
280 /** Locate a property by name
281 */
282
283 static inline mlt_property mlt_properties_find( mlt_properties this, const char *name )
284 {
285         property_list *list = this->local;
286         mlt_property value = NULL;
287         int key = generate_hash( name );
288         int i = list->hash[ key ] - 1;
289
290         if ( i >= 0 )
291         {
292                 // Check if we're hashed
293                 if ( list->count > 0 &&
294                         name[ 0 ] == list->name[ i ][ 0 ] && 
295                         !strcmp( list->name[ i ], name ) )
296                         value = list->value[ i ];
297
298                 // Locate the item 
299                 for ( i = list->count - 1; value == NULL && i >= 0; i -- )
300                         if ( name[ 0 ] == list->name[ i ][ 0 ] && !strcmp( list->name[ i ], name ) )
301                                 value = list->value[ i ];
302         }
303
304         return value;
305 }
306
307 /** Add a new property.
308 */
309
310 static mlt_property mlt_properties_add( mlt_properties this, const char *name )
311 {
312         property_list *list = this->local;
313         int key = generate_hash( name );
314
315         // Check that we have space and resize if necessary
316         if ( list->count == list->size )
317         {
318                 list->size += 50;
319                 list->name = realloc( list->name, list->size * sizeof( const char * ) );
320                 list->value = realloc( list->value, list->size * sizeof( mlt_property ) );
321         }
322
323         // Assign name/value pair
324         list->name[ list->count ] = strdup( name );
325         list->value[ list->count ] = mlt_property_init( );
326
327         // Assign to hash table
328         if ( list->hash[ key ] == 0 )
329                 list->hash[ key ] = list->count + 1;
330
331         // Return and increment count accordingly
332         return list->value[ list->count ++ ];
333 }
334
335 /** Fetch a property by name - this includes add if not found.
336 */
337
338 static mlt_property mlt_properties_fetch( mlt_properties this, const char *name )
339 {
340         // Try to find an existing property first
341         mlt_property property = mlt_properties_find( this, name );
342
343         // If it wasn't found, create one
344         if ( property == NULL )
345                 property = mlt_properties_add( this, name );
346
347         // Return the property
348         return property;
349 }
350
351 /** Pass property 'name' from 'that' to 'this' 
352 * Who to blame: Zach <zachary.drew@gmail.com>
353 */
354
355 void mlt_properties_pass_property( mlt_properties this, mlt_properties that, const char *name )
356 {
357         // Make sure the source property isn't null.
358         mlt_property that_prop = mlt_properties_find( that, name );
359         if( that_prop == NULL )
360                 return;
361
362         mlt_property_pass( mlt_properties_fetch( this, name ), that_prop );
363 }
364
365 /** Pass all properties from 'that' to 'this' as found in comma seperated 'list'.
366 * Who to blame: Zach <zachary.drew@gmail.com>
367 */
368
369 int mlt_properties_pass_list( mlt_properties this, mlt_properties that, const char *list )
370 {
371         char *props = strdup( list );
372         char *ptr = props;
373         char *delim = " ,\t\n"; // Any combination of spaces, commas, tabs, and newlines
374         int count, done = 0;
375
376         while( !done )
377         {
378                 count = strcspn( ptr, delim );
379
380                 if( ptr[count] == '\0' )
381                         done = 1;
382                 else
383                         ptr[count] = '\0';      // Make it a real string
384
385                 mlt_properties_pass_property( this, that, ptr );
386
387                 ptr += count + 1;
388                 ptr += strspn( ptr, delim );
389         }
390
391         free( props );
392
393         return 0;
394 }
395
396
397 /** Set the property.
398 */
399
400 int mlt_properties_set( mlt_properties this, const char *name, const char *value )
401 {
402         int error = 1;
403
404         // Fetch the property to work with
405         mlt_property property = mlt_properties_fetch( this, name );
406
407         // Set it if not NULL
408         if ( property == NULL )
409         {
410                 fprintf( stderr, "Whoops - %s not found (should never occur)\n", name );
411         }
412         else if ( value == NULL )
413         {
414                 error = mlt_property_set_string( property, value );
415                 mlt_properties_do_mirror( this, name );
416         }
417         else if ( *value != '@' )
418         {
419                 error = mlt_property_set_string( property, value );
420                 mlt_properties_do_mirror( this, name );
421         }
422         else if ( value[ 0 ] == '@' )
423         {
424                 double total = 0;
425                 double current = 0;
426                 char id[ 255 ];
427                 char op = '+';
428
429                 value ++;
430
431                 while ( *value != '\0' )
432                 {
433                         int length = strcspn( value, "+-*/" );
434
435                         // Get the identifier
436                         strncpy( id, value, length );
437                         id[ length ] = '\0';
438                         value += length;
439
440                         // Determine the value
441                         if ( isdigit( id[ 0 ] ) )
442                                 current = atof( id );
443                         else
444                                 current = mlt_properties_get_double( this, id );
445
446                         // Apply the operation
447                         switch( op )
448                         {
449                                 case '+':
450                                         total += current;
451                                         break;
452                                 case '-':
453                                         total -= current;
454                                         break;
455                                 case '*':
456                                         total *= current;
457                                         break;
458                                 case '/':
459                                         total = total / current;
460                                         break;
461                         }
462
463                         // Get the next op
464                         op = *value != '\0' ? *value ++ : ' ';
465                 }
466
467                 error = mlt_property_set_double( property, total );
468                 mlt_properties_do_mirror( this, name );
469         }
470
471         mlt_events_fire( this, "property-changed", name, NULL );
472
473         return error;
474 }
475
476 /** Set or default the property.
477 */
478
479 int mlt_properties_set_or_default( mlt_properties this, const char *name, const char *value, const char *def )
480 {
481         return mlt_properties_set( this, name, value == NULL ? def : value );
482 }
483
484 /** Get a string value by name.
485 */
486
487 char *mlt_properties_get( mlt_properties this, const char *name )
488 {
489         mlt_property value = mlt_properties_find( this, name );
490         return value == NULL ? NULL : mlt_property_get_string( value );
491 }
492
493 /** Get a name by index.
494 */
495
496 char *mlt_properties_get_name( mlt_properties this, int index )
497 {
498         property_list *list = this->local;
499         if ( index >= 0 && index < list->count )
500                 return list->name[ index ];
501         return NULL;
502 }
503
504 /** Get a string value by index.
505 */
506
507 char *mlt_properties_get_value( mlt_properties this, int index )
508 {
509         property_list *list = this->local;
510         if ( index >= 0 && index < list->count )
511                 return mlt_property_get_string( list->value[ index ] );
512         return NULL;
513 }
514
515 /** Get a data value by index.
516 */
517
518 void *mlt_properties_get_data_at( mlt_properties this, int index, int *size )
519 {
520         property_list *list = this->local;
521         if ( index >= 0 && index < list->count )
522                 return mlt_property_get_data( list->value[ index ], size );
523         return NULL;
524 }
525
526 /** Return the number of items in the list.
527 */
528
529 int mlt_properties_count( mlt_properties this )
530 {
531         property_list *list = this->local;
532         return list->count;
533 }
534
535 /** Set a value by parsing a name=value string
536 */
537
538 int mlt_properties_parse( mlt_properties this, const char *namevalue )
539 {
540         char *name = strdup( namevalue );
541         char *value = NULL;
542         int error = 0;
543         char *ptr = strchr( name, '=' );
544
545         if ( ptr )
546         {
547                 *( ptr ++ ) = '\0';
548
549                 if ( *ptr != '\"' )
550                 {
551                         value = strdup( ptr );
552                 }
553                 else
554                 {
555                         ptr ++;
556                         value = strdup( ptr );
557                         if ( value != NULL && value[ strlen( value ) - 1 ] == '\"' )
558                                 value[ strlen( value ) - 1 ] = '\0';
559                 }
560         }
561         else
562         {
563                 value = strdup( "" );
564         }
565
566         error = mlt_properties_set( this, name, value );
567
568         free( name );
569         free( value );
570
571         return error;
572 }
573
574 /** Get a value associated to the name.
575 */
576
577 int mlt_properties_get_int( mlt_properties this, const char *name )
578 {
579         mlt_property value = mlt_properties_find( this, name );
580         return value == NULL ? 0 : mlt_property_get_int( value );
581 }
582
583 /** Set a value associated to the name.
584 */
585
586 int mlt_properties_set_int( mlt_properties this, const char *name, int value )
587 {
588         int error = 1;
589
590         // Fetch the property to work with
591         mlt_property property = mlt_properties_fetch( this, name );
592
593         // Set it if not NULL
594         if ( property != NULL )
595         {
596                 error = mlt_property_set_int( property, value );
597                 mlt_properties_do_mirror( this, name );
598         }
599
600         mlt_events_fire( this, "property-changed", name, NULL );
601
602         return error;
603 }
604
605 /** Get a value associated to the name.
606 */
607
608 int64_t mlt_properties_get_int64( mlt_properties this, const char *name )
609 {
610         mlt_property value = mlt_properties_find( this, name );
611         return value == NULL ? 0 : mlt_property_get_int64( value );
612 }
613
614 /** Set a value associated to the name.
615 */
616
617 int mlt_properties_set_int64( mlt_properties this, const char *name, int64_t value )
618 {
619         int error = 1;
620
621         // Fetch the property to work with
622         mlt_property property = mlt_properties_fetch( this, name );
623
624         // Set it if not NULL
625         if ( property != NULL )
626         {
627                 error = mlt_property_set_int64( property, value );
628                 mlt_properties_do_mirror( this, name );
629         }
630
631         mlt_events_fire( this, "property-changed", name, NULL );
632
633         return error;
634 }
635
636 /** Get a value associated to the name.
637 */
638
639 double mlt_properties_get_double( mlt_properties this, const char *name )
640 {
641         mlt_property value = mlt_properties_find( this, name );
642         return value == NULL ? 0 : mlt_property_get_double( value );
643 }
644
645 /** Set a value associated to the name.
646 */
647
648 int mlt_properties_set_double( mlt_properties this, const char *name, double value )
649 {
650         int error = 1;
651
652         // Fetch the property to work with
653         mlt_property property = mlt_properties_fetch( this, name );
654
655         // Set it if not NULL
656         if ( property != NULL )
657         {
658                 error = mlt_property_set_double( property, value );
659                 mlt_properties_do_mirror( this, name );
660         }
661
662         mlt_events_fire( this, "property-changed", name, NULL );
663
664         return error;
665 }
666
667 /** Get a value associated to the name.
668 */
669
670 mlt_position mlt_properties_get_position( mlt_properties this, const char *name )
671 {
672         mlt_property value = mlt_properties_find( this, name );
673         return value == NULL ? 0 : mlt_property_get_position( value );
674 }
675
676 /** Set a value associated to the name.
677 */
678
679 int mlt_properties_set_position( mlt_properties this, const char *name, mlt_position value )
680 {
681         int error = 1;
682
683         // Fetch the property to work with
684         mlt_property property = mlt_properties_fetch( this, name );
685
686         // Set it if not NULL
687         if ( property != NULL )
688         {
689                 error = mlt_property_set_position( property, value );
690                 mlt_properties_do_mirror( this, name );
691         }
692
693         mlt_events_fire( this, "property-changed", name, NULL );
694
695         return error;
696 }
697
698 /** Get a value associated to the name.
699 */
700
701 void *mlt_properties_get_data( mlt_properties this, const char *name, int *length )
702 {
703         mlt_property value = mlt_properties_find( this, name );
704         return value == NULL ? NULL : mlt_property_get_data( value, length );
705 }
706
707 /** Set a value associated to the name.
708 */
709
710 int mlt_properties_set_data( mlt_properties this, const char *name, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise )
711 {
712         int error = 1;
713
714         // Fetch the property to work with
715         mlt_property property = mlt_properties_fetch( this, name );
716
717         // Set it if not NULL
718         if ( property != NULL )
719                 error = mlt_property_set_data( property, value, length, destroy, serialise );
720
721         mlt_events_fire( this, "property-changed", name, NULL );
722
723         return error;
724 }
725
726 /** Rename a property.
727 */
728
729 int mlt_properties_rename( mlt_properties this, const char *source, const char *dest )
730 {
731         mlt_property value = mlt_properties_find( this, dest );
732
733         if ( value == NULL )
734         {
735                 property_list *list = this->local;
736                 int i = 0;
737
738                 // Locate the item 
739                 for ( i = 0; i < list->count; i ++ )
740                 {
741                         if ( !strcmp( list->name[ i ], source ) )
742                         {
743                                 free( list->name[ i ] );
744                                 list->name[ i ] = strdup( dest );
745                                 list->hash[ generate_hash( dest ) ] = i + 1;
746                                 break;
747                         }
748                 }
749         }
750
751         return value != NULL;
752 }
753
754 /** Dump the properties.
755 */
756
757 void mlt_properties_dump( mlt_properties this, FILE *output )
758 {
759         property_list *list = this->local;
760         int i = 0;
761         for ( i = 0; i < list->count; i ++ )
762                 if ( mlt_properties_get( this, list->name[ i ] ) != NULL )
763                         fprintf( output, "%s=%s\n", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
764 }
765
766 void mlt_properties_debug( mlt_properties this, const char *title, FILE *output )
767 {
768         if ( output == NULL ) output = stderr;
769         fprintf( output, "%s: ", title );
770         if ( this != NULL )
771         {
772                 property_list *list = this->local;
773                 int i = 0;
774                 fprintf( output, "[ ref=%d", list->ref_count );
775                 for ( i = 0; i < list->count; i ++ )
776                         if ( mlt_properties_get( this, list->name[ i ] ) != NULL )
777                                 fprintf( output, ", %s=%s", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
778                         else
779                                 fprintf( output, ", %s=%p", list->name[ i ], mlt_properties_get_data( this, list->name[ i ], NULL ) );
780                 fprintf( output, " ]" );
781         }
782         fprintf( output, "\n" );
783 }
784
785 int mlt_properties_save( mlt_properties this, const char *filename )
786 {
787         int error = 1;
788         FILE *f = fopen( filename, "w" );
789         if ( f != NULL )
790         {
791                 mlt_properties_dump( this, f );
792                 fclose( f );
793                 error = 0;
794         }
795         return error;
796 }
797
798 /* This is a very basic cross platform fnmatch replacement - it will fail in
799 ** many cases, but for the basic *.XXX and YYY*.XXX, it will work ok.
800 */
801
802 static int mlt_fnmatch( const char *wild, const char *file )
803 {
804         int f = 0;
805         int w = 0;
806
807         while( f < strlen( file ) && w < strlen( wild ) )
808         {
809                 if ( wild[ w ] == '*' )
810                 {
811                         w ++;
812                         if ( w == strlen( wild ) )
813                                 f = strlen( file );
814                         while ( f != strlen( file ) && tolower( file[ f ] ) != tolower( wild[ w ] ) )
815                                 f ++;
816                 }
817                 else if ( wild[ w ] == '?' || tolower( file[ f ] ) == tolower( wild[ w ] ) )
818                 {
819                         f ++;
820                         w ++;
821                 }
822                 else if ( wild[ 0 ] == '*' )
823                 {
824                         w = 0;
825                 }
826                 else
827                 {
828                         return 0;
829                 }
830         }
831
832         return strlen( file ) == f &&  strlen( wild ) == w;
833 }
834
835 static int mlt_compare( const void *this, const void *that )
836 {
837         return strcmp( mlt_property_get_string( *( mlt_property * )this ), mlt_property_get_string( *( mlt_property * )that ) );
838 }
839
840 /* Obtains an optionally sorted list of the files found in a directory with a specific wild card.
841  * Entries in the list have a numeric name (running from 0 to count - 1). Only values change
842  * position if sort is enabled. Designed to be posix compatible (linux, os/x, mingw etc).
843  */
844
845 int mlt_properties_dir_list( mlt_properties this, const char *dirname, const char *pattern, int sort )
846 {
847         DIR *dir = opendir( dirname );
848
849         if ( dir )
850         {
851                 char key[ 20 ];
852                 struct dirent *de = readdir( dir );
853                 char fullname[ 1024 ];
854                 while( de != NULL )
855                 {
856                         sprintf( key, "%d", mlt_properties_count( this ) );
857                         snprintf( fullname, 1024, "%s/%s", dirname, de->d_name );
858                         if ( pattern == NULL )
859                                 mlt_properties_set( this, key, fullname );
860                         else if ( de->d_name[ 0 ] != '.' && mlt_fnmatch( pattern, de->d_name ) )
861                                 mlt_properties_set( this, key, fullname );
862                         de = readdir( dir );
863                 }
864
865                 closedir( dir );
866         }
867
868         if ( sort && mlt_properties_count( this ) )
869         {
870                 property_list *list = this->local;
871                 qsort( list->value, mlt_properties_count( this ), sizeof( mlt_property ), mlt_compare );
872         }
873
874         return mlt_properties_count( this );
875 }
876
877 /** Close the list.
878 */
879
880 void mlt_properties_close( mlt_properties this )
881 {
882         if ( this != NULL && mlt_properties_dec_ref( this ) <= 0 )
883         {
884                 if ( this->close != NULL )
885                 {
886                         this->close( this->close_object );
887                 }
888                 else
889                 {
890                         property_list *list = this->local;
891                         int index = 0;
892
893 #if _MLT_PROPERTY_CHECKS_ == 1
894                         // Show debug info
895                         mlt_properties_debug( this, "Closing", stderr );
896 #endif
897
898 #ifdef _MLT_PROPERTY_CHECKS_
899                         // Increment destroyed count
900                         properties_destroyed ++;
901
902                         // Show current stats - these should match when the app is closed
903                         fprintf( stderr, "Created %d, destroyed %d\n", properties_created, properties_destroyed );
904 #endif
905
906                         // Clean up names and values
907                         for ( index = list->count - 1; index >= 0; index -- )
908                         {
909                                 free( list->name[ index ] );
910                                 mlt_property_close( list->value[ index ] );
911                         }
912         
913                         // Clear up the list
914                         pthread_mutex_destroy( &list->mutex );
915                         free( list->name );
916                         free( list->value );
917                         free( list );
918         
919                         // Free this now if this has no child
920                         if ( this->child == NULL )
921                                 free( this );
922                 }
923         }
924 }
925
926 /** Determine if the properties list is really just a sequence or ordered list.
927     Returns 0 if false or 1 if true.
928 */
929 int mlt_properties_is_sequence( mlt_properties properties )
930 {
931         int i;
932         int n = mlt_properties_count( properties );
933         for ( i = 0; i < n; i++ )
934                 if ( ! isdigit( mlt_properties_get_name( properties, i )[0] ) )
935                         return 0;
936         return 1;
937 }
938
939 /** YAML Tiny Parser
940  * YAML is a nifty text format popular in the Ruby world as a cleaner,
941  * less verbose alternative to XML. See this Wikipedia topic for an overview:
942  * http://en.wikipedia.org/wiki/YAML
943  * The YAML specification is at:
944  * http://yaml.org/
945  * YAML::Tiny is a Perl module that specifies a subset of YAML that we are
946  * using here (for the same reasons):
947  * http://search.cpan.org/~adamk/YAML-Tiny-1.25/lib/YAML/Tiny.pm
948  */
949
950 struct yaml_parser_context
951 {
952         mlt_deque stack;
953         unsigned int level;
954         unsigned int index;
955         char block;
956         char *block_name;
957         unsigned int block_indent;
958         
959 };
960 typedef struct yaml_parser_context *yaml_parser;
961
962 static unsigned int ltrim( char **s )
963 {
964         unsigned int i = 0;
965         char *c = *s;
966         int n = strlen( c );
967         for ( i = 0; i < n && *c == ' '; i++, c++ );
968         *s = c;
969         return i;
970 }
971
972 static unsigned int rtrim( char *s )
973 {
974         int n = strlen( s );
975         int i;
976         for ( i = n; i > 0 && s[i - 1] == ' '; --i )
977                 s[i - 1] = 0;
978         return n - i;
979 }
980
981 static int parse_yaml( yaml_parser context, const char *namevalue )
982 {
983         char *name_ = strdup( namevalue );
984         char *name = name_;
985         char *value = NULL;
986         int error = 0;
987         char *ptr = strchr( name, ':' );
988         unsigned int indent = ltrim( &name );
989         mlt_properties properties = mlt_deque_peek_front( context->stack );
990         
991         // Ascending one more levels in the tree
992         if ( indent < context->level )
993         {
994                 unsigned int i;
995                 unsigned int n = ( context->level - indent ) / 2;
996                 for ( i = 0; i < n; i++ )
997                         mlt_deque_pop_front( context->stack );
998                 properties = mlt_deque_peek_front( context->stack );
999                 context->level = indent;
1000         }
1001         
1002         // Descending a level in the tree
1003         else if ( indent > context->level && context->block == 0 )
1004         {
1005                 context->level = indent;
1006         }
1007         
1008         // If there is a colon that is not part of a block
1009         if ( ptr && ( indent == context->level ) )
1010         {
1011                 // Reset block processing
1012                 if ( context->block_name )
1013                 {
1014                         free( context->block_name );
1015                         context->block_name = NULL;
1016                         context->block = 0;
1017                 }
1018                 
1019                 // Terminate the name and setup the value pointer
1020                 *( ptr ++ ) = 0;
1021                 
1022                 // Trim comment
1023                 char *comment = strchr( ptr, '#' );
1024                 if ( comment )
1025                 {
1026                         *comment = 0;
1027                 }
1028                 
1029                 // Trim leading and trailing spaces from bare value 
1030                 ltrim( &ptr );
1031                 rtrim( ptr );
1032                 
1033                 // No value means a child
1034                 if ( strcmp( ptr, "" ) == 0 )
1035                 {
1036                         mlt_properties child = mlt_properties_new();
1037                         mlt_properties_set_data( properties, name, child, 0, 
1038                                 ( mlt_destructor )mlt_properties_close, NULL );
1039                         mlt_deque_push_front( context->stack, child );
1040                         context->index = 0;
1041                         free( name_ );
1042                         return error;
1043                 }
1044                 
1045                 // A dash indicates a sequence item
1046                 if ( name[0] == '-' )
1047                 {
1048                         mlt_properties child = mlt_properties_new();
1049                         char key[20];
1050                         
1051                         snprintf( key, sizeof(key), "%d", context->index++ );
1052                         mlt_properties_set_data( properties, key, child, 0, 
1053                                 ( mlt_destructor )mlt_properties_close, NULL );
1054                         mlt_deque_push_front( context->stack, child );
1055
1056                         name ++;
1057                         context->level += ltrim( &name ) + 1;
1058                         properties = child;
1059                 }
1060                 
1061                 // Value is quoted
1062                 if ( *ptr == '\"' )
1063                 {
1064                         ptr ++;
1065                         value = strdup( ptr );
1066                         if ( value && value[ strlen( value ) - 1 ] == '\"' )
1067                                 value[ strlen( value ) - 1 ] = 0;
1068                 }
1069                 
1070                 // Value is folded or unfolded block
1071                 else if ( *ptr == '|' || *ptr == '>' )
1072                 {
1073                         context->block = *ptr;
1074                         context->block_name = strdup( name );
1075                         context->block_indent = 0;
1076                         value = strdup( "" );
1077                 }
1078                 
1079                 // Bare value
1080                 else
1081                 {
1082                         value = strdup( ptr );
1083                 }
1084         }
1085         
1086         // A list of scalars
1087         else if ( name[0] == '-' )
1088         {
1089                 // Reset block processing
1090                 if ( context->block_name )
1091                 {
1092                         free( context->block_name );
1093                         context->block_name = NULL;
1094                         context->block = 0;
1095                 }
1096                 
1097                 char key[20];
1098                 
1099                 snprintf( key, sizeof(key), "%d", context->index++ );
1100                 ptr = name + 1;
1101                 
1102                 // Trim comment
1103                 char *comment = strchr( ptr, '#' );
1104                 if ( comment )
1105                         *comment = 0;
1106                 
1107                 // Trim leading and trailing spaces from bare value 
1108                 ltrim( &ptr );
1109                 rtrim( ptr );
1110                 
1111                 // Value is quoted
1112                 if ( *ptr == '\"' )
1113                 {
1114                         ptr ++;
1115                         value = strdup( ptr );
1116                         if ( value && value[ strlen( value ) - 1 ] == '\"' )
1117                                 value[ strlen( value ) - 1 ] = 0;
1118                 }
1119                 
1120                 // Value is folded or unfolded block
1121                 else if ( *ptr == '|' || *ptr == '>' )
1122                 {
1123                         context->block = *ptr;
1124                         context->block_name = strdup( key );
1125                         context->block_indent = 0;
1126                         value = strdup( "" );
1127                 }
1128                 
1129                 // Bare value
1130                 else
1131                 {
1132                         value = strdup( ptr );
1133                 }
1134                 
1135                 free( name_ );
1136                 name = name_ = strdup( key );
1137         }
1138
1139         // Non-folded block
1140         else if ( context->block == '|' )
1141         {
1142                 if ( context->block_indent == 0 )
1143                         context->block_indent = indent;
1144                 if ( indent > context->block_indent )
1145                         name = &name_[ context->block_indent ];
1146                 rtrim( name );
1147                 char *old_value = mlt_properties_get( properties, context->block_name );
1148                 value = calloc( 1, strlen( old_value ) + strlen( name ) + 2 );
1149                 strcpy( value, old_value );
1150                 if ( strcmp( old_value, "" ) )
1151                         strcat( value, "\n" );
1152                 strcat( value, name );
1153                 name = context->block_name;
1154         }
1155         
1156         // Folded block
1157         else if ( context->block == '>' )
1158         {
1159                 ltrim( &name );
1160                 rtrim( name );
1161                 char *old_value = mlt_properties_get( properties, context->block_name );
1162                 
1163                 // Blank line (prepended with spaces) is new line
1164                 if ( strcmp( name, "" ) == 0 )
1165                 {
1166                         value = calloc( 1, strlen( old_value ) + 2 );
1167                         strcat( value, old_value );
1168                         strcat( value, "\n" );
1169                 }
1170                 // Concatenate with space
1171                 else
1172                 {
1173                         value = calloc( 1, strlen( old_value ) + strlen( name ) + 2 );
1174                         strcat( value, old_value );
1175                         if ( strcmp( old_value, "" ) && old_value[ strlen( old_value ) - 1 ] != '\n' )
1176                                 strcat( value, " " );
1177                         strcat( value, name );
1178                 }
1179                 name = context->block_name;
1180         }
1181         
1182         else
1183         {
1184                 value = strdup( "" );
1185         }
1186
1187         error = mlt_properties_set( properties, name, value );
1188
1189         free( name_ );
1190         free( value );
1191
1192         return error;
1193 }
1194
1195 mlt_properties mlt_properties_parse_yaml( const char *filename )
1196 {
1197         // Construct a standalone properties object
1198         mlt_properties this = mlt_properties_new( );
1199
1200         if ( this )
1201         {
1202                 // Open the file
1203                 FILE *file = fopen( filename, "r" );
1204
1205                 // Load contents of file
1206                 if ( file )
1207                 {
1208                         // Temp string
1209                         char temp[ 1024 ];
1210                         char *ptemp = &temp[ 0 ];
1211                         
1212                         // Parser context
1213                         yaml_parser context = calloc( 1, sizeof( struct yaml_parser_context ) );
1214                         context->stack = mlt_deque_init();
1215                         mlt_deque_push_front( context->stack, this );
1216
1217                         // Read each string from the file
1218                         while( fgets( temp, 1024, file ) )
1219                         {
1220                                 // Check for end-of-stream
1221                                 if ( strncmp( ptemp, "...", 3 ) == 0 )
1222                                         break;
1223                                         
1224                                 // Chomp the string
1225                                 temp[ strlen( temp ) - 1 ] = '\0';
1226
1227                                 // Skip blank lines, comment lines, and document separator
1228                                 if ( strcmp( ptemp, "" ) && ptemp[ 0 ] != '#' && strncmp( ptemp, "---", 3 ) 
1229                                      && strncmp( ptemp, "%YAML", 5 ) && strncmp( ptemp, "% YAML", 6 ) )
1230                                         parse_yaml( context, temp );
1231                         }
1232
1233                         // Close the file
1234                         fclose( file );
1235                         mlt_deque_close( context->stack );
1236                         if ( context->block_name )
1237                                 free( context->block_name );
1238                         free( context );
1239                 }
1240         }
1241
1242         // Return the pointer
1243         return this;
1244 }
1245
1246 /** YAML Tiny Serialiser
1247 */
1248
1249 /* strbuf is a self-growing string buffer */
1250
1251 #define STRBUF_GROWTH (1024)
1252
1253 struct strbuf_s
1254 {
1255         size_t size;
1256         char *string;
1257 };
1258
1259 typedef struct strbuf_s *strbuf;
1260
1261 static strbuf strbuf_new( )
1262 {
1263         strbuf buffer = calloc( 1, sizeof( struct strbuf_s ) );
1264         buffer->size = STRBUF_GROWTH;
1265         buffer->string = calloc( 1, buffer->size );
1266         return buffer;
1267 }
1268
1269 static void strbuf_close( strbuf buffer )
1270 {
1271         // We do not free buffer->string; strbuf user must save that pointer
1272         // and free it.
1273         if ( buffer )
1274                 free( buffer );
1275 }
1276
1277 static char *strbuf_printf( strbuf buffer, const char *format, ... )
1278 {
1279         while ( buffer->string )
1280         {
1281                 va_list ap;
1282                 va_start( ap, format );
1283                 size_t len = strlen( buffer->string );
1284                 size_t remain = buffer->size - len - 1;
1285                 int need = vsnprintf( buffer->string + len, remain, format, ap );
1286                 va_end( ap );
1287                 if ( need > -1 && need < remain )
1288                         break;
1289                 buffer->string[ len ] = 0;
1290                 buffer->size += need + STRBUF_GROWTH;
1291                 buffer->string = realloc( buffer->string, buffer->size );
1292         }
1293         return buffer->string;
1294 }
1295
1296 static inline void indent_yaml( strbuf output, int indent )
1297 {
1298         int j;
1299         for ( j = 0; j < indent; j++ )
1300                 strbuf_printf( output, " " );
1301 }
1302
1303 static void output_yaml_block_literal( strbuf output, const char *value, int indent )
1304 {
1305         char *v = strdup( value );
1306         char *sol = v;
1307         char *eol = strchr( sol, '\n' );
1308         
1309         while ( eol )
1310         {
1311                 indent_yaml( output, indent );
1312                 *eol = '\0';
1313                 strbuf_printf( output, "%s\n", sol );
1314                 sol = eol + 1;
1315                 eol = strchr( sol, '\n' );
1316         }       
1317         indent_yaml( output, indent );
1318         strbuf_printf( output, "%s\n", sol );
1319 }
1320
1321 static void serialise_yaml( mlt_properties this, strbuf output, int indent, int is_parent_sequence )
1322 {
1323         property_list *list = this->local;
1324         int i = 0;
1325         
1326         for ( i = 0; i < list->count; i ++ )
1327         {
1328                 // This implementation assumes that all data elements are property lists.
1329                 // Unfortunately, we do not have run time type identification.
1330                 mlt_properties child = mlt_property_get_data( list->value[ i ], NULL );
1331                 
1332                 if ( mlt_properties_is_sequence( this ) )
1333                 {
1334                         // Ignore hidden/non-serialisable items
1335                         if ( list->name[ i ][ 0 ] != '_' )
1336                         {
1337                                 // Indicate a sequence item
1338                                 indent_yaml( output, indent );
1339                                 strbuf_printf( output, "- " );
1340                                 
1341                                 // If the value can be represented as a string
1342                                 const char *value = mlt_properties_get( this, list->name[ i ] );
1343                                 if ( value && strcmp( value, "" ) )
1344                                 {
1345                                         // Determine if this is an unfolded block literal
1346                                         if ( strchr( value, '\n' ) )
1347                                         {
1348                                                 strbuf_printf( output, "|\n" );
1349                                                 output_yaml_block_literal( output, value, indent + strlen( list->name[ i ] ) + strlen( "|" ) );
1350                                         }
1351                                         else
1352                                         {
1353                                                 strbuf_printf( output, "%s\n", value );
1354                                         }
1355                                 }
1356                         }
1357                         // Recurse on child
1358                         if ( child )
1359                                 serialise_yaml( child, output, indent + 2, 1 );
1360                 }
1361                 else 
1362                 {
1363                         // Assume this is a normal map-oriented properties list
1364                         const char *value = mlt_properties_get( this, list->name[ i ] );
1365                         
1366                         // Ignore hidden/non-serialisable items
1367                         // If the value can be represented as a string
1368                         if ( list->name[ i ][ 0 ] != '_' && value && strcmp( value, "" ) )
1369                         {
1370                                 if ( is_parent_sequence == 0 )
1371                                         indent_yaml( output, indent );
1372                                 else
1373                                         is_parent_sequence = 0;
1374                                 
1375                                 // Determine if this is an unfolded block literal
1376                                 if ( strchr( value, '\n' ) )
1377                                 {
1378                                         strbuf_printf( output, "%s: |\n", list->name[ i ] );
1379                                         output_yaml_block_literal( output, value, indent + strlen( list->name[ i ] ) + strlen( ": " ) );
1380                                 }
1381                                 else
1382                                 {
1383                                         strbuf_printf( output, "%s: %s\n", list->name[ i ], value );
1384                                 }
1385                         }
1386                         
1387                         // Output a child as a map item
1388                         if ( child )
1389                         {
1390                                 indent_yaml( output, indent );
1391                                 strbuf_printf( output, "%s:\n", list->name[ i ] );
1392
1393                                 // Recurse on child
1394                                 serialise_yaml( child, output, indent + 2, 0 );
1395                         }
1396                 }
1397         }
1398 }
1399
1400 /** Serialise the properties as a string of YAML Tiny.
1401     The caller must free the returned string.
1402 */
1403
1404 char *mlt_properties_serialise_yaml( mlt_properties this )
1405 {
1406         strbuf b = strbuf_new();
1407         strbuf_printf( b, "---\n" );
1408         serialise_yaml( this, b, 0, 0 );
1409         strbuf_printf( b, "...\n" );
1410         char *ret = b->string;
1411         strbuf_close( b );
1412         return ret;
1413 }