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