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