]> git.sesse.net Git - mlt/blob - src/framework/mlt_properties.c
Set version to 0.7.0
[mlt] / src / framework / mlt_properties.c
1 /**
2  * \file mlt_properties.c
3  * \brief Properties class definition
4  * \see mlt_properties_s
5  *
6  * Copyright (C) 2003-2009 Ushodaya Enterprises Limited
7  * \author Charles Yates <charles.yates@pandora.be>
8  * \author Dan Dennedy <dan@dennedy.org>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #include "mlt_properties.h"
26 #include "mlt_property.h"
27 #include "mlt_deque.h"
28 #include "mlt_log.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <stdarg.h>
35 #include <pthread.h>
36 #include <sys/types.h>
37 #include <dirent.h>
38
39
40 /** \brief private implementation of the property list */
41
42 typedef struct
43 {
44         int hash[ 199 ];
45         char **name;
46         mlt_property *value;
47         int count;
48         int size;
49         mlt_properties mirror;
50         int ref_count;
51         pthread_mutex_t mutex;
52 }
53 property_list;
54
55 /* Memory leak checks */
56
57 //#define _MLT_PROPERTY_CHECKS_ 2
58 #ifdef _MLT_PROPERTY_CHECKS_
59 static int properties_created = 0;
60 static int properties_destroyed = 0;
61 #endif
62
63 /** Initialize a properties object that was already allocated.
64  *
65  * This does allocate its ::property_list, and it adds a reference count.
66  * \public \memberof mlt_properties_s
67  * \param self the properties structure to initialize
68  * \param child an opaque pointer to a subclass object
69  * \return true if failed
70  */
71
72 int mlt_properties_init( mlt_properties self, void *child )
73 {
74         if ( self != NULL )
75         {
76 #ifdef _MLT_PROPERTY_CHECKS_
77                 // Increment number of properties created
78                 properties_created ++;
79 #endif
80
81                 // NULL all methods
82                 memset( self, 0, sizeof( struct mlt_properties_s ) );
83
84                 // Assign the child of the object
85                 self->child = child;
86
87                 // Allocate the local structure
88                 self->local = calloc( sizeof( property_list ), 1 );
89
90                 // Increment the ref count
91                 ( ( property_list * )self->local )->ref_count = 1;
92                 pthread_mutex_init( &( ( property_list * )self->local )->mutex, NULL );;
93         }
94
95         // Check that initialisation was successful
96         return self != NULL && self->local == NULL;
97 }
98
99 /** Create a properties object.
100  *
101  * This allocates the properties structure and calls mlt_properties_init() on it.
102  * Free the properties object with mlt_properties_close().
103  * \public \memberof mlt_properties_s
104  * \return a new properties object
105  */
106
107 mlt_properties mlt_properties_new( )
108 {
109         // Construct a standalone properties object
110         mlt_properties self = calloc( sizeof( struct mlt_properties_s ), 1 );
111
112         // Initialise self
113         mlt_properties_init( self, NULL );
114
115         // Return the pointer
116         return self;
117 }
118
119 /** Create a properties object by reading a .properties text file.
120  *
121  * Free the properties object with mlt_properties_close().
122  * \deprecated Please start using mlt_properties_parse_yaml().
123  * \public \memberof mlt_properties_s
124  * \param filename a string contain the absolute file name
125  * \return a new properties object
126  */
127
128 mlt_properties mlt_properties_load( const char *filename )
129 {
130         // Construct a standalone properties object
131         mlt_properties self = mlt_properties_new( );
132
133         if ( self != NULL )
134         {
135                 // Open the file
136                 FILE *file = fopen( filename, "r" );
137
138                 // Load contents of file
139                 if ( file != NULL )
140                 {
141                         // Temp string
142                         char temp[ 1024 ];
143                         char last[ 1024 ] = "";
144
145                         // Read each string from the file
146                         while( fgets( temp, 1024, file ) )
147                         {
148                                 // Chomp the string
149                                 temp[ strlen( temp ) - 1 ] = '\0';
150
151                                 // Check if the line starts with a .
152                                 if ( temp[ 0 ] == '.' )
153                                 {
154                                         char temp2[ 1024 ];
155                                         sprintf( temp2, "%s%s", last, temp );
156                                         strcpy( temp, temp2 );
157                                 }
158                                 else if ( strchr( temp, '=' ) )
159                                 {
160                                         strcpy( last, temp );
161                                         *( strchr( last, '=' ) ) = '\0';
162                                 }
163
164                                 // Parse and set the property
165                                 if ( strcmp( temp, "" ) && temp[ 0 ] != '#' )
166                                         mlt_properties_parse( self, temp );
167                         }
168
169                         // Close the file
170                         fclose( file );
171                 }
172         }
173
174         // Return the pointer
175         return self;
176 }
177
178 /** Generate a hash key.
179  *
180  * \private \memberof mlt_properties_s
181  * \param name a string
182  * \return an integer
183  */
184
185 static inline int generate_hash( const char *name )
186 {
187         int hash = 0;
188         int i = 1;
189         while ( *name )
190                 hash = ( hash + ( i ++ * ( *name ++ & 31 ) ) ) % 199;
191         return hash;
192 }
193
194 /** Copy a serializable property to a properties list that is mirroring this one.
195  *
196  * Special case - when a container (such as loader) is protecting another
197  * producer, we need to ensure that properties are passed through to the
198  * real producer.
199  * \private \memberof mlt_properties_s
200  * \param self a properties list
201  * \param name the name of the property to copy
202  */
203
204 static inline void mlt_properties_do_mirror( mlt_properties self, const char *name )
205 {
206         property_list *list = self->local;
207         if ( list->mirror != NULL )
208         {
209                 char *value = mlt_properties_get( self, name );
210                 if ( value != NULL )
211                         mlt_properties_set( list->mirror, name, value );
212         }
213 }
214
215 /** Increment the reference count.
216  *
217  * \public \memberof mlt_properties_s
218  * \param self a properties list
219  * \return the new reference count
220  */
221
222 int mlt_properties_inc_ref( mlt_properties self )
223 {
224         int result = 0;
225         if ( self != NULL )
226         {
227                 property_list *list = self->local;
228                 pthread_mutex_lock( &list->mutex );
229                 result = ++ list->ref_count;
230                 pthread_mutex_unlock( &list->mutex );
231         }
232         return result;
233 }
234
235 /** Decrement the reference count.
236  *
237  * \public \memberof mlt_properties_s
238  * \param self a properties list
239  * \return the new reference count
240  */
241
242 int mlt_properties_dec_ref( mlt_properties self )
243 {
244         int result = 0;
245         if ( self != NULL )
246         {
247                 property_list *list = self->local;
248                 pthread_mutex_lock( &list->mutex );
249                 result = -- list->ref_count;
250                 pthread_mutex_unlock( &list->mutex );
251         }
252         return result;
253 }
254
255 /** Get the reference count.
256  *
257  * \public \memberof mlt_properties_s
258  * \param self a properties list
259  * \return the current reference count
260  */
261
262 int mlt_properties_ref_count( mlt_properties self )
263 {
264         if ( self != NULL )
265         {
266                 property_list *list = self->local;
267                 return list->ref_count;
268         }
269         return 0;
270 }
271
272 /** Set a properties list to be a mirror copy of another.
273  *
274  * Note that this does not copy all existing properties. Rather, you must
275  * call this before setting the properties that you wish to copy.
276  * \public \memberof mlt_properties_s
277  * \param that the properties which will receive copies of the properties as they are set.
278  * \param self the properties to mirror
279  */
280
281 void mlt_properties_mirror( mlt_properties self, mlt_properties that )
282 {
283         property_list *list = self->local;
284         list->mirror = that;
285 }
286
287 /** Copy all serializable properties to another properties list.
288  *
289  * \public \memberof mlt_properties_s
290  * \param self The properties to copy to
291  * \param that The properties to copy from
292  * \return false
293  */
294
295 int mlt_properties_inherit( mlt_properties self, mlt_properties that )
296 {
297         int count = mlt_properties_count( that );
298         int i = 0;
299         for ( i = 0; i < count; i ++ )
300         {
301                 char *value = mlt_properties_get_value( that, i );
302                 if ( value != NULL )
303                 {
304                         char *name = mlt_properties_get_name( that, i );
305                         mlt_properties_set( self, name, value );
306                 }
307         }
308         return 0;
309 }
310
311 /** Pass all serializable properties that match a prefix to another properties object
312  *
313  * \public \memberof mlt_properties_s
314  * \param self the properties to copy to
315  * \param that The properties to copy from
316  * \param prefix the property names to match (required)
317  * \return false
318  */
319
320 int mlt_properties_pass( mlt_properties self, mlt_properties that, const char *prefix )
321 {
322         int count = mlt_properties_count( that );
323         int length = strlen( prefix );
324         int i = 0;
325         for ( i = 0; i < count; i ++ )
326         {
327                 char *name = mlt_properties_get_name( that, i );
328                 if ( !strncmp( name, prefix, length ) )
329                 {
330                         char *value = mlt_properties_get_value( that, i );
331                         if ( value != NULL )
332                                 mlt_properties_set( self, name + length, value );
333                 }
334         }
335         return 0;
336 }
337
338 /** Locate a property by name.
339  *
340  * \private \memberof mlt_properties_s
341  * \param self a properties list
342  * \param name the property to lookup by name
343  * \return the property or NULL for failure
344  */
345
346 static inline mlt_property mlt_properties_find( mlt_properties self, const char *name )
347 {
348         property_list *list = self->local;
349         mlt_property value = NULL;
350         int key = generate_hash( name );
351
352         mlt_properties_lock( self );
353
354         int i = list->hash[ key ] - 1;
355         if ( i >= 0 )
356         {
357                 // Check if we're hashed
358                 if ( list->count > 0 &&
359                         name[ 0 ] == list->name[ i ][ 0 ] &&
360                         !strcmp( list->name[ i ], name ) )
361                         value = list->value[ i ];
362
363                 // Locate the item
364                 for ( i = list->count - 1; value == NULL && i >= 0; i -- )
365                         if ( name[ 0 ] == list->name[ i ][ 0 ] && !strcmp( list->name[ i ], name ) )
366                                 value = list->value[ i ];
367         }
368         mlt_properties_unlock( self );
369
370         return value;
371 }
372
373 /** Add a new property.
374  *
375  * \private \memberof mlt_properties_s
376  * \param self a properties list
377  * \param name the name of the new property
378  * \return the new property
379  */
380
381 static mlt_property mlt_properties_add( mlt_properties self, const char *name )
382 {
383         property_list *list = self->local;
384         int key = generate_hash( name );
385         mlt_property result;
386
387         mlt_properties_lock( self );
388
389         // Check that we have space and resize if necessary
390         if ( list->count == list->size )
391         {
392                 list->size += 50;
393                 list->name = realloc( list->name, list->size * sizeof( const char * ) );
394                 list->value = realloc( list->value, list->size * sizeof( mlt_property ) );
395         }
396
397         // Assign name/value pair
398         list->name[ list->count ] = strdup( name );
399         list->value[ list->count ] = mlt_property_init( );
400
401         // Assign to hash table
402         if ( list->hash[ key ] == 0 )
403                 list->hash[ key ] = list->count + 1;
404
405         // Return and increment count accordingly
406         result = list->value[ list->count ++ ];
407
408         mlt_properties_unlock( self );
409
410         return result;
411 }
412
413 /** Fetch a property by name and add one if not found.
414  *
415  * \private \memberof mlt_properties_s
416  * \param self a properties list
417  * \param name the property to lookup or add
418  * \return the property
419  */
420
421 static mlt_property mlt_properties_fetch( mlt_properties self, const char *name )
422 {
423         // Try to find an existing property first
424         mlt_property property = mlt_properties_find( self, name );
425
426         // If it wasn't found, create one
427         if ( property == NULL )
428                 property = mlt_properties_add( self, name );
429
430         // Return the property
431         return property;
432 }
433
434 /** Copy a property to another properties list.
435  *
436  * \public \memberof mlt_properties_s
437  * \author Zach <zachary.drew@gmail.com>
438  * \param self the properties to copy to
439  * \param that the properties to copy from
440  * \param name the name of the property to copy
441  */
442
443 void mlt_properties_pass_property( mlt_properties self, mlt_properties that, const char *name )
444 {
445         // Make sure the source property isn't null.
446         mlt_property that_prop = mlt_properties_find( that, name );
447         if( that_prop == NULL )
448                 return;
449
450         mlt_property_pass( mlt_properties_fetch( self, name ), that_prop );
451 }
452
453 /** Copy all properties specified in a comma-separated list to another properties list.
454  *
455  * White space is also a delimiter.
456  * \public \memberof mlt_properties_s
457  * \author Zach <zachary.drew@gmail.com>
458  * \param self the properties to copy to
459  * \param that the properties to copy from
460  * \param list a delimited list of property names
461  * \return false
462  */
463
464
465 int mlt_properties_pass_list( mlt_properties self, mlt_properties that, const char *list )
466 {
467         char *props = strdup( list );
468         char *ptr = props;
469         const char *delim = " ,\t\n";   // Any combination of spaces, commas, tabs, and newlines
470         int count, done = 0;
471
472         while( !done )
473         {
474                 count = strcspn( ptr, delim );
475
476                 if( ptr[count] == '\0' )
477                         done = 1;
478                 else
479                         ptr[count] = '\0';      // Make it a real string
480
481                 mlt_properties_pass_property( self, that, ptr );
482
483                 ptr += count + 1;
484                 ptr += strspn( ptr, delim );
485         }
486
487         free( props );
488
489         return 0;
490 }
491
492
493 /** Set a property to a string.
494  *
495  * This makes a copy of the string value you supply.
496  * \public \memberof mlt_properties_s
497  * \param self a properties list
498  * \param name the property to set
499  * \param value the property's new value
500  * \return true if error
501  */
502
503 int mlt_properties_set( mlt_properties self, const char *name, const char *value )
504 {
505         int error = 1;
506
507         // Fetch the property to work with
508         mlt_property property = mlt_properties_fetch( self, name );
509
510         // Set it if not NULL
511         if ( property == NULL )
512         {
513                 mlt_log( NULL, MLT_LOG_FATAL, "Whoops - %s not found (should never occur)\n", name );
514         }
515         else if ( value == NULL )
516         {
517                 error = mlt_property_set_string( property, value );
518                 mlt_properties_do_mirror( self, name );
519         }
520         else if ( *value != '@' )
521         {
522                 error = mlt_property_set_string( property, value );
523                 mlt_properties_do_mirror( self, name );
524         }
525         else if ( value[ 0 ] == '@' )
526         {
527                 double total = 0;
528                 double current = 0;
529                 char id[ 255 ];
530                 char op = '+';
531
532                 value ++;
533
534                 while ( *value != '\0' )
535                 {
536                         int length = strcspn( value, "+-*/" );
537
538                         // Get the identifier
539                         strncpy( id, value, length );
540                         id[ length ] = '\0';
541                         value += length;
542
543                         // Determine the value
544                         if ( isdigit( id[ 0 ] ) )
545                                 current = atof( id );
546                         else
547                                 current = mlt_properties_get_double( self, id );
548
549                         // Apply the operation
550                         switch( op )
551                         {
552                                 case '+':
553                                         total += current;
554                                         break;
555                                 case '-':
556                                         total -= current;
557                                         break;
558                                 case '*':
559                                         total *= current;
560                                         break;
561                                 case '/':
562                                         total = total / current;
563                                         break;
564                         }
565
566                         // Get the next op
567                         op = *value != '\0' ? *value ++ : ' ';
568                 }
569
570                 error = mlt_property_set_double( property, total );
571                 mlt_properties_do_mirror( self, name );
572         }
573
574         mlt_events_fire( self, "property-changed", name, NULL );
575
576         return error;
577 }
578
579 /** Set or default a property to a string.
580  *
581  * This makes a copy of the string value you supply.
582  * \public \memberof mlt_properties_s
583  * \param self a properties list
584  * \param name the property to set
585  * \param value the string value to set or NULL to use the default
586  * \param def the default string if value is NULL
587  * \return true if error
588  */
589
590 int mlt_properties_set_or_default( mlt_properties self, const char *name, const char *value, const char *def )
591 {
592         return mlt_properties_set( self, name, value == NULL ? def : value );
593 }
594
595 /** Get a string value by name.
596  *
597  * Do not free the returned string. It's lifetime is controlled by the property
598  * and this properties object.
599  * \public \memberof mlt_properties_s
600  * \param self a properties list
601  * \param name the property to get
602  * \return the property's string value or NULL if it does not exist
603  */
604
605 char *mlt_properties_get( mlt_properties self, const char *name )
606 {
607         mlt_property value = mlt_properties_find( self, name );
608         return value == NULL ? NULL : mlt_property_get_string( value );
609 }
610
611 /** Get a property name by index.
612  *
613  * Do not free the returned string.
614  * \public \memberof mlt_properties_s
615  * \param self a properties list
616  * \param index the numeric index of the property
617  * \return the name of the property or NULL if index is out of range
618  */
619
620 char *mlt_properties_get_name( mlt_properties self, int index )
621 {
622         property_list *list = self->local;
623         if ( index >= 0 && index < list->count )
624                 return list->name[ index ];
625         return NULL;
626 }
627
628 /** Get a property's string value by index.
629  *
630  * Do not free the returned string.
631  * \public \memberof mlt_properties_s
632  * \param self a properties list
633  * \param index the numeric index of the property
634  * \return the property value as a string or NULL if the index is out of range
635  */
636
637 char *mlt_properties_get_value( mlt_properties self, int index )
638 {
639         property_list *list = self->local;
640         if ( index >= 0 && index < list->count )
641                 return mlt_property_get_string( list->value[ index ] );
642         return NULL;
643 }
644
645 /** Get a data value by index.
646  *
647  * Do not free the returned pointer if you supplied a destructor function when you
648  * set this property.
649  * \public \memberof mlt_properties_s
650  * \param self a properties list
651  * \param index the numeric index of the property
652  * \param[out] size the size of the binary data in bytes or NULL if the index is out of range
653  */
654
655 void *mlt_properties_get_data_at( mlt_properties self, int index, int *size )
656 {
657         property_list *list = self->local;
658         if ( index >= 0 && index < list->count )
659                 return mlt_property_get_data( list->value[ index ], size );
660         return NULL;
661 }
662
663 /** Return the number of items in the list.
664  *
665  * \public \memberof mlt_properties_s
666  * \param self a properties list
667  * \return the number of property objects
668  */
669
670 int mlt_properties_count( mlt_properties self )
671 {
672         property_list *list = self->local;
673         return list->count;
674 }
675
676 /** Set a value by parsing a name=value string.
677  *
678  * \public \memberof mlt_properties_s
679  * \param self a properties list
680  * \param namevalue a string containing name and value delimited by '='
681  * \return true if there was an error
682  */
683
684 int mlt_properties_parse( mlt_properties self, const char *namevalue )
685 {
686         char *name = strdup( namevalue );
687         char *value = NULL;
688         int error = 0;
689         char *ptr = strchr( name, '=' );
690
691         if ( ptr )
692         {
693                 *( ptr ++ ) = '\0';
694
695                 if ( *ptr != '\"' )
696                 {
697                         value = strdup( ptr );
698                 }
699                 else
700                 {
701                         ptr ++;
702                         value = strdup( ptr );
703                         if ( value != NULL && value[ strlen( value ) - 1 ] == '\"' )
704                                 value[ strlen( value ) - 1 ] = '\0';
705                 }
706         }
707         else
708         {
709                 value = strdup( "" );
710         }
711
712         error = mlt_properties_set( self, name, value );
713
714         free( name );
715         free( value );
716
717         return error;
718 }
719
720 /** Get an integer associated to the name.
721  *
722  * \public \memberof mlt_properties_s
723  * \param self a properties list
724  * \param name the property to get
725  * \return The integer value, 0 if not found (which may also be a legitimate value)
726  */
727
728 int mlt_properties_get_int( mlt_properties self, const char *name )
729 {
730         mlt_property value = mlt_properties_find( self, name );
731         return value == NULL ? 0 : mlt_property_get_int( value );
732 }
733
734 /** Set a property to an integer value.
735  *
736  * \public \memberof mlt_properties_s
737  * \param self a properties list
738  * \param name the property to set
739  * \param value the integer
740  * \return true if error
741  */
742
743 int mlt_properties_set_int( mlt_properties self, const char *name, int value )
744 {
745         int error = 1;
746
747         // Fetch the property to work with
748         mlt_property property = mlt_properties_fetch( self, name );
749
750         // Set it if not NULL
751         if ( property != NULL )
752         {
753                 error = mlt_property_set_int( property, value );
754                 mlt_properties_do_mirror( self, name );
755         }
756
757         mlt_events_fire( self, "property-changed", name, NULL );
758
759         return error;
760 }
761
762 /** Get a 64-bit integer associated to the name.
763  *
764  * \public \memberof mlt_properties_s
765  * \param self a properties list
766  * \param name the property to get
767  * \return the integer value, 0 if not found (which may also be a legitimate value)
768  */
769
770 int64_t mlt_properties_get_int64( mlt_properties self, const char *name )
771 {
772         mlt_property value = mlt_properties_find( self, name );
773         return value == NULL ? 0 : mlt_property_get_int64( value );
774 }
775
776 /** Set a property to a 64-bit integer value.
777  *
778  * \public \memberof mlt_properties_s
779  * \param self a properties list
780  * \param name the property to set
781  * \param value the integer
782  * \return true if error
783  */
784
785 int mlt_properties_set_int64( mlt_properties self, const char *name, int64_t value )
786 {
787         int error = 1;
788
789         // Fetch the property to work with
790         mlt_property property = mlt_properties_fetch( self, name );
791
792         // Set it if not NULL
793         if ( property != NULL )
794         {
795                 error = mlt_property_set_int64( property, value );
796                 mlt_properties_do_mirror( self, name );
797         }
798
799         mlt_events_fire( self, "property-changed", name, NULL );
800
801         return error;
802 }
803
804 /** Get a floating point value associated to the name.
805  *
806  * \public \memberof mlt_properties_s
807  * \param self a properties list
808  * \param name the property to get
809  * \return the floating point, 0 if not found (which may also be a legitimate value)
810  */
811
812 double mlt_properties_get_double( mlt_properties self, const char *name )
813 {
814         mlt_property value = mlt_properties_find( self, name );
815         return value == NULL ? 0 : mlt_property_get_double( value );
816 }
817
818 /** Set a property to a floating point value.
819  *
820  * \public \memberof mlt_properties_s
821  * \param self a properties list
822  * \param name the property to set
823  * \param value the floating point value
824  * \return true if error
825  */
826
827 int mlt_properties_set_double( mlt_properties self, const char *name, double value )
828 {
829         int error = 1;
830
831         // Fetch the property to work with
832         mlt_property property = mlt_properties_fetch( self, name );
833
834         // Set it if not NULL
835         if ( property != NULL )
836         {
837                 error = mlt_property_set_double( property, value );
838                 mlt_properties_do_mirror( self, name );
839         }
840
841         mlt_events_fire( self, "property-changed", name, NULL );
842
843         return error;
844 }
845
846 /** Get a position value associated to the name.
847  *
848  * \public \memberof mlt_properties_s
849  * \param self a properties list
850  * \param name the property to get
851  * \return the position, 0 if not found (which may also be a legitimate value)
852  */
853
854 mlt_position mlt_properties_get_position( mlt_properties self, const char *name )
855 {
856         mlt_property value = mlt_properties_find( self, name );
857         return value == NULL ? 0 : mlt_property_get_position( value );
858 }
859
860 /** Set a property to a position value.
861  *
862  * \public \memberof mlt_properties_s
863  * \param self a properties list
864  * \param name the property to get
865  * \param value the position
866  * \return true if error
867  */
868
869 int mlt_properties_set_position( mlt_properties self, const char *name, mlt_position value )
870 {
871         int error = 1;
872
873         // Fetch the property to work with
874         mlt_property property = mlt_properties_fetch( self, name );
875
876         // Set it if not NULL
877         if ( property != NULL )
878         {
879                 error = mlt_property_set_position( property, value );
880                 mlt_properties_do_mirror( self, name );
881         }
882
883         mlt_events_fire( self, "property-changed", name, NULL );
884
885         return error;
886 }
887
888 /** Get a binary data value associated to the name.
889  *
890  * Do not free the returned pointer if you supplied a destructor function
891  * when you set this property.
892  * \public \memberof mlt_properties_s
893  * \param self a properties list
894  * \param name the property to get
895  * \param[out] length The size of the binary data in bytes, if available (often it is not, you should know)
896  */
897
898 void *mlt_properties_get_data( mlt_properties self, const char *name, int *length )
899 {
900         mlt_property value = mlt_properties_find( self, name );
901         return value == NULL ? NULL : mlt_property_get_data( value, length );
902 }
903
904 /** Store binary data as a property.
905  *
906  * \public \memberof mlt_properties_s
907  * \param self a properties list
908  * \param name the property to set
909  * \param value an opaque pointer to binary data
910  * \param length the size of the binary data in bytes (optional)
911  * \param destroy a function to deallocate the binary data when the property is closed (optional)
912  * \param serialise a function that can serialize the binary data as text (optional)
913  * \return true if error
914  */
915
916 int mlt_properties_set_data( mlt_properties self, const char *name, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise )
917 {
918         int error = 1;
919
920         // Fetch the property to work with
921         mlt_property property = mlt_properties_fetch( self, name );
922
923         // Set it if not NULL
924         if ( property != NULL )
925                 error = mlt_property_set_data( property, value, length, destroy, serialise );
926
927         mlt_events_fire( self, "property-changed", name, NULL );
928
929         return error;
930 }
931
932 /** Rename a property.
933  *
934  * \public \memberof mlt_properties_s
935  * \param self a properties list
936  * \param source the property to rename
937  * \param dest the new name
938  * \return true if the name is already in use
939  */
940
941 int mlt_properties_rename( mlt_properties self, const char *source, const char *dest )
942 {
943         mlt_property value = mlt_properties_find( self, dest );
944
945         if ( value == NULL )
946         {
947                 property_list *list = self->local;
948                 int i = 0;
949
950                 // Locate the item
951                 mlt_properties_lock( self );
952                 for ( i = 0; i < list->count; i ++ )
953                 {
954                         if ( !strcmp( list->name[ i ], source ) )
955                         {
956                                 free( list->name[ i ] );
957                                 list->name[ i ] = strdup( dest );
958                                 list->hash[ generate_hash( dest ) ] = i + 1;
959                                 break;
960                         }
961                 }
962                 mlt_properties_unlock( self );
963         }
964
965         return value != NULL;
966 }
967
968 /** Dump the properties to a file handle.
969  *
970  * \public \memberof mlt_properties_s
971  * \param self a properties list
972  * \param output a file handle
973  */
974
975 void mlt_properties_dump( mlt_properties self, FILE *output )
976 {
977         property_list *list = self->local;
978         int i = 0;
979         for ( i = 0; i < list->count; i ++ )
980                 if ( mlt_properties_get( self, list->name[ i ] ) != NULL )
981                         fprintf( output, "%s=%s\n", list->name[ i ], mlt_properties_get( self, list->name[ i ] ) );
982 }
983
984 /** Output the properties to a file handle.
985  *
986  * This version includes reference counts and does not put each property on a new line.
987  * \public \memberof mlt_properties_s
988  * \param self a properties pointer
989  * \param title a string to preface the output
990  * \param output a file handle
991  */
992 void mlt_properties_debug( mlt_properties self, const char *title, FILE *output )
993 {
994         if ( output == NULL ) output = stderr;
995         fprintf( output, "%s: ", title );
996         if ( self != NULL )
997         {
998                 property_list *list = self->local;
999                 int i = 0;
1000                 fprintf( output, "[ ref=%d", list->ref_count );
1001                 for ( i = 0; i < list->count; i ++ )
1002                         if ( mlt_properties_get( self, list->name[ i ] ) != NULL )
1003                                 fprintf( output, ", %s=%s", list->name[ i ], mlt_properties_get( self, list->name[ i ] ) );
1004                         else
1005                                 fprintf( output, ", %s=%p", list->name[ i ], mlt_properties_get_data( self, list->name[ i ], NULL ) );
1006                 fprintf( output, " ]" );
1007         }
1008         fprintf( output, "\n" );
1009 }
1010
1011 /** Save the properties to a file by name.
1012  *
1013  * This uses the dump format - one line per property.
1014  * \public \memberof mlt_properties_s
1015  * \param self a properties list
1016  * \param filename the name of a file to create or overwrite
1017  * \return true if there was an error
1018  */
1019
1020 int mlt_properties_save( mlt_properties self, const char *filename )
1021 {
1022         int error = 1;
1023         FILE *f = fopen( filename, "w" );
1024         if ( f != NULL )
1025         {
1026                 mlt_properties_dump( self, f );
1027                 fclose( f );
1028                 error = 0;
1029         }
1030         return error;
1031 }
1032
1033 /* This is a very basic cross platform fnmatch replacement - it will fail in
1034  * many cases, but for the basic *.XXX and YYY*.XXX, it will work ok.
1035  */
1036
1037 /** Test whether a filename or pathname matches a shell-style pattern.
1038  *
1039  * \private \memberof mlt_properties_s
1040  * \param wild a string containing a wildcard pattern
1041  * \param file the name of a file to test against
1042  * \return true if the file name matches the wildcard pattern
1043  */
1044
1045 static int mlt_fnmatch( const char *wild, const char *file )
1046 {
1047         int f = 0;
1048         int w = 0;
1049
1050         while( f < strlen( file ) && w < strlen( wild ) )
1051         {
1052                 if ( wild[ w ] == '*' )
1053                 {
1054                         w ++;
1055                         if ( w == strlen( wild ) )
1056                                 f = strlen( file );
1057                         while ( f != strlen( file ) && tolower( file[ f ] ) != tolower( wild[ w ] ) )
1058                                 f ++;
1059                 }
1060                 else if ( wild[ w ] == '?' || tolower( file[ f ] ) == tolower( wild[ w ] ) )
1061                 {
1062                         f ++;
1063                         w ++;
1064                 }
1065                 else if ( wild[ 0 ] == '*' )
1066                 {
1067                         w = 0;
1068                 }
1069                 else
1070                 {
1071                         return 0;
1072                 }
1073         }
1074
1075         return strlen( file ) == f &&  strlen( wild ) == w;
1076 }
1077
1078 /** Compare the string or serialized value of two properties.
1079  *
1080  * \private \memberof mlt_properties_s
1081  * \param self a property
1082  * \param that a property
1083  * \return < 0 if \p self less than \p that, 0 if equal, or > 0 if \p self is greater than \p that
1084  */
1085
1086 static int mlt_compare( const void *self, const void *that )
1087 {
1088         return strcmp( mlt_property_get_string( *( const mlt_property * )self ), mlt_property_get_string( *( const mlt_property * )that ) );
1089 }
1090
1091 /** Get the contents of a directory.
1092  *
1093  * Obtains an optionally sorted list of the files found in a directory with a specific wild card.
1094  * Entries in the list have a numeric name (running from 0 to count - 1). Only values change
1095  * position if sort is enabled. Designed to be posix compatible (linux, os/x, mingw etc).
1096  * \public \memberof mlt_properties_s
1097  * \param self a properties list
1098  * \param dirname the name of the directory
1099  * \param pattern a wildcard pattern to filter the directory listing
1100  * \param sort Do you want to sort the directory listing?
1101  * \return the number of items in the directory listing
1102  */
1103
1104 int mlt_properties_dir_list( mlt_properties self, const char *dirname, const char *pattern, int sort )
1105 {
1106         DIR *dir = opendir( dirname );
1107
1108         if ( dir )
1109         {
1110                 char key[ 20 ];
1111                 struct dirent *de = readdir( dir );
1112                 char fullname[ 1024 ];
1113                 while( de != NULL )
1114                 {
1115                         sprintf( key, "%d", mlt_properties_count( self ) );
1116                         snprintf( fullname, 1024, "%s/%s", dirname, de->d_name );
1117                         if ( pattern == NULL )
1118                                 mlt_properties_set( self, key, fullname );
1119                         else if ( de->d_name[ 0 ] != '.' && mlt_fnmatch( pattern, de->d_name ) )
1120                                 mlt_properties_set( self, key, fullname );
1121                         de = readdir( dir );
1122                 }
1123
1124                 closedir( dir );
1125         }
1126
1127         if ( sort && mlt_properties_count( self ) )
1128         {
1129                 property_list *list = self->local;
1130                 mlt_properties_lock( self );
1131                 qsort( list->value, mlt_properties_count( self ), sizeof( mlt_property ), mlt_compare );
1132                 mlt_properties_unlock( self );
1133         }
1134
1135         return mlt_properties_count( self );
1136 }
1137
1138 /** Close a properties object.
1139  *
1140  * Deallocates the properties object and everything it contains.
1141  * \public \memberof mlt_properties_s
1142  * \param self a properties object
1143  */
1144
1145 void mlt_properties_close( mlt_properties self )
1146 {
1147         if ( self != NULL && mlt_properties_dec_ref( self ) <= 0 )
1148         {
1149                 if ( self->close != NULL )
1150                 {
1151                         self->close( self->close_object );
1152                 }
1153                 else
1154                 {
1155                         property_list *list = self->local;
1156                         int index = 0;
1157
1158 #if _MLT_PROPERTY_CHECKS_ == 1
1159                         // Show debug info
1160                         mlt_properties_debug( self, "Closing", stderr );
1161 #endif
1162
1163 #ifdef _MLT_PROPERTY_CHECKS_
1164                         // Increment destroyed count
1165                         properties_destroyed ++;
1166
1167                         // Show current stats - these should match when the app is closed
1168                         mlt_log( NULL, MLT_LOG_DEBUG, "Created %d, destroyed %d\n", properties_created, properties_destroyed );
1169 #endif
1170
1171                         // Clean up names and values
1172                         for ( index = list->count - 1; index >= 0; index -- )
1173                         {
1174                                 mlt_property_close( list->value[ index ] );
1175                                 free( list->name[ index ] );
1176                         }
1177
1178                         // Clear up the list
1179                         pthread_mutex_destroy( &list->mutex );
1180                         free( list->name );
1181                         free( list->value );
1182                         free( list );
1183
1184                         // Free self now if self has no child
1185                         if ( self->child == NULL )
1186                                 free( self );
1187                 }
1188         }
1189 }
1190
1191 /** Determine if the properties list is really just a sequence or ordered list.
1192  *
1193  * \public \memberof mlt_properties_s
1194  * \param properties a properties list
1195  * \return true if all of the property names are numeric (a sequence)
1196  */
1197
1198 int mlt_properties_is_sequence( mlt_properties properties )
1199 {
1200         int i;
1201         int n = mlt_properties_count( properties );
1202         for ( i = 0; i < n; i++ )
1203                 if ( ! isdigit( mlt_properties_get_name( properties, i )[0] ) )
1204                         return 0;
1205         return 1;
1206 }
1207
1208 /** \brief YAML Tiny Parser context structure
1209  *
1210  * YAML is a nifty text format popular in the Ruby world as a cleaner,
1211  * less verbose alternative to XML. See this Wikipedia topic for an overview:
1212  * http://en.wikipedia.org/wiki/YAML
1213  * The YAML specification is at:
1214  * http://yaml.org/
1215  * YAML::Tiny is a Perl module that specifies a subset of YAML that we are
1216  * using here (for the same reasons):
1217  * http://search.cpan.org/~adamk/YAML-Tiny-1.25/lib/YAML/Tiny.pm
1218  * \private
1219  */
1220
1221 struct yaml_parser_context
1222 {
1223         mlt_deque stack;
1224         unsigned int level;
1225         unsigned int index;
1226         char block;
1227         char *block_name;
1228         unsigned int block_indent;
1229
1230 };
1231 typedef struct yaml_parser_context *yaml_parser;
1232
1233 /** Remove spaces from the left side of a string.
1234  *
1235  * \param s the string to trim
1236  * \return the number of characters removed
1237  */
1238
1239 static unsigned int ltrim( char **s )
1240 {
1241         unsigned int i = 0;
1242         char *c = *s;
1243         int n = strlen( c );
1244         for ( i = 0; i < n && *c == ' '; i++, c++ );
1245         *s = c;
1246         return i;
1247 }
1248
1249 /** Remove spaces from the right side of a string.
1250  *
1251  * \param s the string to trim
1252  * \return the number of characters removed
1253  */
1254
1255 static unsigned int rtrim( char *s )
1256 {
1257         int n = strlen( s );
1258         int i;
1259         for ( i = n; i > 0 && s[i - 1] == ' '; --i )
1260                 s[i - 1] = 0;
1261         return n - i;
1262 }
1263
1264 /** Parse a line of YAML Tiny.
1265  *
1266  * Adds a property if needed.
1267  * \private \memberof yaml_parser_context
1268  * \param context a YAML Tiny Parser context
1269  * \param namevalue a line of YAML Tiny
1270  * \return true if there was an error
1271  */
1272
1273 static int parse_yaml( yaml_parser context, const char *namevalue )
1274 {
1275         char *name_ = strdup( namevalue );
1276         char *name = name_;
1277         char *value = NULL;
1278         int error = 0;
1279         char *ptr = strchr( name, ':' );
1280         unsigned int indent = ltrim( &name );
1281         mlt_properties properties = mlt_deque_peek_front( context->stack );
1282
1283         // Ascending one more levels in the tree
1284         if ( indent < context->level )
1285         {
1286                 unsigned int i;
1287                 unsigned int n = ( context->level - indent ) / 2;
1288                 for ( i = 0; i < n; i++ )
1289                         mlt_deque_pop_front( context->stack );
1290                 properties = mlt_deque_peek_front( context->stack );
1291                 context->level = indent;
1292         }
1293
1294         // Descending a level in the tree
1295         else if ( indent > context->level && context->block == 0 )
1296         {
1297                 context->level = indent;
1298         }
1299
1300         // If there is a colon that is not part of a block
1301         if ( ptr && ( indent == context->level ) )
1302         {
1303                 // Reset block processing
1304                 if ( context->block_name )
1305                 {
1306                         free( context->block_name );
1307                         context->block_name = NULL;
1308                         context->block = 0;
1309                 }
1310
1311                 // Terminate the name and setup the value pointer
1312                 *( ptr ++ ) = 0;
1313
1314                 // Trim comment
1315                 char *comment = strchr( ptr, '#' );
1316                 if ( comment )
1317                 {
1318                         *comment = 0;
1319                 }
1320
1321                 // Trim leading and trailing spaces from bare value
1322                 ltrim( &ptr );
1323                 rtrim( ptr );
1324
1325                 // No value means a child
1326                 if ( strcmp( ptr, "" ) == 0 )
1327                 {
1328                         mlt_properties child = mlt_properties_new();
1329                         mlt_properties_set_data( properties, name, child, 0,
1330                                 ( mlt_destructor )mlt_properties_close, NULL );
1331                         mlt_deque_push_front( context->stack, child );
1332                         context->index = 0;
1333                         free( name_ );
1334                         return error;
1335                 }
1336
1337                 // A dash indicates a sequence item
1338                 if ( name[0] == '-' )
1339                 {
1340                         mlt_properties child = mlt_properties_new();
1341                         char key[20];
1342
1343                         snprintf( key, sizeof(key), "%d", context->index++ );
1344                         mlt_properties_set_data( properties, key, child, 0,
1345                                 ( mlt_destructor )mlt_properties_close, NULL );
1346                         mlt_deque_push_front( context->stack, child );
1347
1348                         name ++;
1349                         context->level += ltrim( &name ) + 1;
1350                         properties = child;
1351                 }
1352
1353                 // Value is quoted
1354                 if ( *ptr == '\"' )
1355                 {
1356                         ptr ++;
1357                         value = strdup( ptr );
1358                         if ( value && value[ strlen( value ) - 1 ] == '\"' )
1359                                 value[ strlen( value ) - 1 ] = 0;
1360                 }
1361
1362                 // Value is folded or unfolded block
1363                 else if ( *ptr == '|' || *ptr == '>' )
1364                 {
1365                         context->block = *ptr;
1366                         context->block_name = strdup( name );
1367                         context->block_indent = 0;
1368                         value = strdup( "" );
1369                 }
1370
1371                 // Bare value
1372                 else
1373                 {
1374                         value = strdup( ptr );
1375                 }
1376         }
1377
1378         // A list of scalars
1379         else if ( name[0] == '-' )
1380         {
1381                 // Reset block processing
1382                 if ( context->block_name )
1383                 {
1384                         free( context->block_name );
1385                         context->block_name = NULL;
1386                         context->block = 0;
1387                 }
1388
1389                 char key[20];
1390
1391                 snprintf( key, sizeof(key), "%d", context->index++ );
1392                 ptr = name + 1;
1393
1394                 // Trim comment
1395                 char *comment = strchr( ptr, '#' );
1396                 if ( comment )
1397                         *comment = 0;
1398
1399                 // Trim leading and trailing spaces from bare value
1400                 ltrim( &ptr );
1401                 rtrim( ptr );
1402
1403                 // Value is quoted
1404                 if ( *ptr == '\"' )
1405                 {
1406                         ptr ++;
1407                         value = strdup( ptr );
1408                         if ( value && value[ strlen( value ) - 1 ] == '\"' )
1409                                 value[ strlen( value ) - 1 ] = 0;
1410                 }
1411
1412                 // Value is folded or unfolded block
1413                 else if ( *ptr == '|' || *ptr == '>' )
1414                 {
1415                         context->block = *ptr;
1416                         context->block_name = strdup( key );
1417                         context->block_indent = 0;
1418                         value = strdup( "" );
1419                 }
1420
1421                 // Bare value
1422                 else
1423                 {
1424                         value = strdup( ptr );
1425                 }
1426
1427                 free( name_ );
1428                 name = name_ = strdup( key );
1429         }
1430
1431         // Non-folded block
1432         else if ( context->block == '|' )
1433         {
1434                 if ( context->block_indent == 0 )
1435                         context->block_indent = indent;
1436                 if ( indent > context->block_indent )
1437                         name = &name_[ context->block_indent ];
1438                 rtrim( name );
1439                 char *old_value = mlt_properties_get( properties, context->block_name );
1440                 value = calloc( 1, strlen( old_value ) + strlen( name ) + 2 );
1441                 strcpy( value, old_value );
1442                 if ( strcmp( old_value, "" ) )
1443                         strcat( value, "\n" );
1444                 strcat( value, name );
1445                 name = context->block_name;
1446         }
1447
1448         // Folded block
1449         else if ( context->block == '>' )
1450         {
1451                 ltrim( &name );
1452                 rtrim( name );
1453                 char *old_value = mlt_properties_get( properties, context->block_name );
1454
1455                 // Blank line (prepended with spaces) is new line
1456                 if ( strcmp( name, "" ) == 0 )
1457                 {
1458                         value = calloc( 1, strlen( old_value ) + 2 );
1459                         strcat( value, old_value );
1460                         strcat( value, "\n" );
1461                 }
1462                 // Concatenate with space
1463                 else
1464                 {
1465                         value = calloc( 1, strlen( old_value ) + strlen( name ) + 2 );
1466                         strcat( value, old_value );
1467                         if ( strcmp( old_value, "" ) && old_value[ strlen( old_value ) - 1 ] != '\n' )
1468                                 strcat( value, " " );
1469                         strcat( value, name );
1470                 }
1471                 name = context->block_name;
1472         }
1473
1474         else
1475         {
1476                 value = strdup( "" );
1477         }
1478
1479         error = mlt_properties_set( properties, name, value );
1480
1481         free( name_ );
1482         free( value );
1483
1484         return error;
1485 }
1486
1487 /** Parse a YAML Tiny file by name.
1488  *
1489  * \public \memberof mlt_properties_s
1490  * \param filename the name of a text file containing YAML Tiny
1491  * \return a new properties list
1492  */
1493
1494 mlt_properties mlt_properties_parse_yaml( const char *filename )
1495 {
1496         // Construct a standalone properties object
1497         mlt_properties self = mlt_properties_new( );
1498
1499         if ( self )
1500         {
1501                 // Open the file
1502                 FILE *file = fopen( filename, "r" );
1503
1504                 // Load contents of file
1505                 if ( file )
1506                 {
1507                         // Temp string
1508                         char temp[ 1024 ];
1509                         char *ptemp = &temp[ 0 ];
1510
1511                         // Parser context
1512                         yaml_parser context = calloc( 1, sizeof( struct yaml_parser_context ) );
1513                         context->stack = mlt_deque_init();
1514                         mlt_deque_push_front( context->stack, self );
1515
1516                         // Read each string from the file
1517                         while( fgets( temp, 1024, file ) )
1518                         {
1519                                 // Check for end-of-stream
1520                                 if ( strncmp( ptemp, "...", 3 ) == 0 )
1521                                         break;
1522
1523                                 // Chomp the string
1524                                 temp[ strlen( temp ) - 1 ] = '\0';
1525
1526                                 // Skip blank lines, comment lines, and document separator
1527                                 if ( strcmp( ptemp, "" ) && ptemp[ 0 ] != '#' && strncmp( ptemp, "---", 3 )
1528                                      && strncmp( ptemp, "%YAML", 5 ) && strncmp( ptemp, "% YAML", 6 ) )
1529                                         parse_yaml( context, temp );
1530                         }
1531
1532                         // Close the file
1533                         fclose( file );
1534                         mlt_deque_close( context->stack );
1535                         if ( context->block_name )
1536                                 free( context->block_name );
1537                         free( context );
1538                 }
1539         }
1540
1541         // Return the pointer
1542         return self;
1543 }
1544
1545 /*
1546  * YAML Tiny Serializer
1547  */
1548
1549 /** How many bytes to grow at a time */
1550 #define STRBUF_GROWTH (1024)
1551
1552 /** \brief Private to mlt_properties_s, a self-growing buffer for building strings
1553  * \private
1554  */
1555
1556 struct strbuf_s
1557 {
1558         size_t size;
1559         char *string;
1560 };
1561
1562 typedef struct strbuf_s *strbuf;
1563
1564 /** Create a new string buffer
1565  *
1566  * \private \memberof strbuf_s
1567  * \return a new string buffer
1568  */
1569
1570 static strbuf strbuf_new( )
1571 {
1572         strbuf buffer = calloc( 1, sizeof( struct strbuf_s ) );
1573         buffer->size = STRBUF_GROWTH;
1574         buffer->string = calloc( 1, buffer->size );
1575         return buffer;
1576 }
1577
1578 /** Destroy a string buffer
1579  *
1580  * \private \memberof strbuf_s
1581  * \param buffer the string buffer to close
1582  */
1583
1584 static void strbuf_close( strbuf buffer )
1585 {
1586         // We do not free buffer->string; strbuf user must save that pointer
1587         // and free it.
1588         if ( buffer )
1589                 free( buffer );
1590 }
1591
1592 /** Format a string into a string buffer
1593  *
1594  * A variable number of arguments follows the format string - one for each
1595  * format specifier.
1596  * \private \memberof strbuf_s
1597  * \param buffer the string buffer to write into
1598  * \param format a string that contains text and formatting instructions
1599  * \return the formatted string
1600  */
1601
1602 static char *strbuf_printf( strbuf buffer, const char *format, ... )
1603 {
1604         while ( buffer->string )
1605         {
1606                 va_list ap;
1607                 va_start( ap, format );
1608                 size_t len = strlen( buffer->string );
1609                 size_t remain = buffer->size - len - 1;
1610                 int need = vsnprintf( buffer->string + len, remain, format, ap );
1611                 va_end( ap );
1612                 if ( need > -1 && need < remain )
1613                         break;
1614                 buffer->string[ len ] = 0;
1615                 buffer->size += need + STRBUF_GROWTH;
1616                 buffer->string = realloc( buffer->string, buffer->size );
1617         }
1618         return buffer->string;
1619 }
1620
1621 /** Indent a line of YAML Tiny.
1622  *
1623  * \private \memberof strbuf_s
1624  * \param output a string buffer
1625  * \param indent the number of spaces to indent
1626  */
1627
1628 static inline void indent_yaml( strbuf output, int indent )
1629 {
1630         int j;
1631         for ( j = 0; j < indent; j++ )
1632                 strbuf_printf( output, " " );
1633 }
1634
1635 /** Convert a line string into a YAML block literal.
1636  *
1637  * \private \memberof strbuf_s
1638  * \param output a string buffer
1639  * \param value the string to format as a block literal
1640  * \param indent the number of spaces to indent
1641  */
1642
1643 static void output_yaml_block_literal( strbuf output, const char *value, int indent )
1644 {
1645         char *v = strdup( value );
1646         char *sol = v;
1647         char *eol = strchr( sol, '\n' );
1648
1649         while ( eol )
1650         {
1651                 indent_yaml( output, indent );
1652                 *eol = '\0';
1653                 strbuf_printf( output, "%s\n", sol );
1654                 sol = eol + 1;
1655                 eol = strchr( sol, '\n' );
1656         }
1657         indent_yaml( output, indent );
1658         strbuf_printf( output, "%s\n", sol );
1659 }
1660
1661 /** Recursively serialize a properties list into a string buffer as YAML Tiny.
1662  *
1663  * \private \memberof mlt_properties_s
1664  * \param self a properties list
1665  * \param output a string buffer to hold the serialized YAML Tiny
1666  * \param indent the number of spaces to indent (for recursion, initialize to 0)
1667  * \param is_parent_sequence Is this properties list really just a sequence (for recursion, initialize to 0)?
1668  */
1669
1670 static void serialise_yaml( mlt_properties self, strbuf output, int indent, int is_parent_sequence )
1671 {
1672         property_list *list = self->local;
1673         int i = 0;
1674
1675         for ( i = 0; i < list->count; i ++ )
1676         {
1677                 // This implementation assumes that all data elements are property lists.
1678                 // Unfortunately, we do not have run time type identification.
1679                 mlt_properties child = mlt_property_get_data( list->value[ i ], NULL );
1680
1681                 if ( mlt_properties_is_sequence( self ) )
1682                 {
1683                         // Ignore hidden/non-serialisable items
1684                         if ( list->name[ i ][ 0 ] != '_' )
1685                         {
1686                                 // Indicate a sequence item
1687                                 indent_yaml( output, indent );
1688                                 strbuf_printf( output, "- " );
1689
1690                                 // If the value can be represented as a string
1691                                 const char *value = mlt_properties_get( self, list->name[ i ] );
1692                                 if ( value && strcmp( value, "" ) )
1693                                 {
1694                                         // Determine if this is an unfolded block literal
1695                                         if ( strchr( value, '\n' ) )
1696                                         {
1697                                                 strbuf_printf( output, "|\n" );
1698                                                 output_yaml_block_literal( output, value, indent + strlen( list->name[ i ] ) + strlen( "|" ) );
1699                                         }
1700                                         else
1701                                         {
1702                                                 strbuf_printf( output, "%s\n", value );
1703                                         }
1704                                 }
1705                         }
1706                         // Recurse on child
1707                         if ( child )
1708                                 serialise_yaml( child, output, indent + 2, 1 );
1709                 }
1710                 else
1711                 {
1712                         // Assume this is a normal map-oriented properties list
1713                         const char *value = mlt_properties_get( self, list->name[ i ] );
1714
1715                         // Ignore hidden/non-serialisable items
1716                         // If the value can be represented as a string
1717                         if ( list->name[ i ][ 0 ] != '_' && value && strcmp( value, "" ) )
1718                         {
1719                                 if ( is_parent_sequence == 0 )
1720                                         indent_yaml( output, indent );
1721                                 else
1722                                         is_parent_sequence = 0;
1723
1724                                 // Determine if this is an unfolded block literal
1725                                 if ( strchr( value, '\n' ) )
1726                                 {
1727                                         strbuf_printf( output, "%s: |\n", list->name[ i ] );
1728                                         output_yaml_block_literal( output, value, indent + strlen( list->name[ i ] ) + strlen( ": " ) );
1729                                 }
1730                                 else
1731                                 {
1732                                         strbuf_printf( output, "%s: %s\n", list->name[ i ], value );
1733                                 }
1734                         }
1735
1736                         // Output a child as a map item
1737                         if ( child )
1738                         {
1739                                 indent_yaml( output, indent );
1740                                 strbuf_printf( output, "%s:\n", list->name[ i ] );
1741
1742                                 // Recurse on child
1743                                 serialise_yaml( child, output, indent + 2, 0 );
1744                         }
1745                 }
1746         }
1747 }
1748
1749 /** Serialize a properties list as a string of YAML Tiny.
1750  *
1751  * The caller MUST free the returned string!
1752  * This operates on properties containing properties as a hierarchical data
1753  * structure.
1754  * \public \memberof mlt_properties_s
1755  * \param self a properties list
1756  * \return a string containing YAML Tiny that represents the properties list
1757  */
1758
1759 char *mlt_properties_serialise_yaml( mlt_properties self )
1760 {
1761         strbuf b = strbuf_new();
1762         strbuf_printf( b, "---\n" );
1763         serialise_yaml( self, b, 0, 0 );
1764         strbuf_printf( b, "...\n" );
1765         char *ret = b->string;
1766         strbuf_close( b );
1767         return ret;
1768 }
1769
1770 /** Protect a properties list against concurrent access.
1771  *
1772  * \public \memberof mlt_properties_s
1773  * \param self a properties list
1774  */
1775
1776 void mlt_properties_lock( mlt_properties self )
1777 {
1778         if ( self )
1779                 pthread_mutex_lock( &( ( property_list* )( self->local ) )->mutex );
1780 }
1781
1782 /** End protecting a properties list against concurrent access.
1783  *
1784  * \public \memberof mlt_properties_s
1785  * \param self a properties list
1786  */
1787
1788 void mlt_properties_unlock( mlt_properties self )
1789 {
1790         if ( self )
1791                 pthread_mutex_unlock( &( ( property_list* )( self->local ) )->mutex );
1792 }