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