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