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