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