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