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