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