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