2 * mlt_properties.c -- base properties class
3 * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #include "mlt_properties.h"
23 #include "mlt_property.h"
30 #include <sys/types.h>
33 /* ---------------- // Private Implementation // ---------------- */
35 /** Private implementation of the property list.
45 mlt_properties mirror;
50 /** Memory leak checks.
53 //#define _MLT_PROPERTY_CHECKS_ 2
55 #ifdef _MLT_PROPERTY_CHECKS_
56 static int properties_created = 0;
57 static int properties_destroyed = 0;
60 /** Basic implementation.
63 int mlt_properties_init( mlt_properties this, void *child )
67 #ifdef _MLT_PROPERTY_CHECKS_
68 // Increment number of properties created
69 properties_created ++;
73 memset( this, 0, sizeof( struct mlt_properties_s ) );
75 // Assign the child of the object
78 // Allocate the local structure
79 this->local = calloc( sizeof( property_list ), 1 );
81 // Increment the ref count
82 ( ( property_list * )this->local )->ref_count = 1;
85 // Check that initialisation was successful
86 return this != NULL && this->local == NULL;
89 /** Constructor for stand alone object.
92 mlt_properties mlt_properties_new( )
94 // Construct a standalone properties object
95 mlt_properties this = calloc( sizeof( struct mlt_properties_s ), 1 );
98 mlt_properties_init( this, NULL );
100 // Return the pointer
104 /** Load properties from a file.
107 mlt_properties mlt_properties_load( const char *filename )
109 // Construct a standalone properties object
110 mlt_properties this = mlt_properties_new( );
115 FILE *file = fopen( filename, "r" );
117 // Load contents of file
122 char last[ 1024 ] = "";
124 // Read each string from the file
125 while( fgets( temp, 1024, file ) )
128 temp[ strlen( temp ) - 1 ] = '\0';
130 // Check if the line starts with a .
131 if ( temp[ 0 ] == '.' )
134 sprintf( temp2, "%s%s", last, temp );
135 strcpy( temp, temp2 );
137 else if ( strchr( temp, '=' ) )
139 strcpy( last, temp );
140 *( strchr( last, '=' ) ) = '\0';
143 // Parse and set the property
144 if ( strcmp( temp, "" ) && temp[ 0 ] != '#' )
145 mlt_properties_parse( this, temp );
153 // Return the pointer
157 static inline int generate_hash( const char *name )
162 hash = ( hash + ( i ++ * ( *name ++ & 31 ) ) ) % 199;
166 /** Special case - when a container (such as fezzik) is protecting another
167 producer, we need to ensure that properties are passed through to the
171 static inline void mlt_properties_do_mirror( mlt_properties this, const char *name )
173 property_list *list = this->local;
174 if ( list->mirror != NULL )
176 char *value = mlt_properties_get( this, name );
178 mlt_properties_set( list->mirror, name, value );
182 /** Maintain ref count to allow multiple uses of an mlt object.
185 int mlt_properties_inc_ref( mlt_properties this )
189 property_list *list = this->local;
190 return ++ list->ref_count;
195 /** Maintain ref count to allow multiple uses of an mlt object.
198 int mlt_properties_dec_ref( mlt_properties this )
202 property_list *list = this->local;
203 return -- list->ref_count;
208 /** Return the ref count of this object.
211 int mlt_properties_ref_count( mlt_properties this )
215 property_list *list = this->local;
216 return list->ref_count;
221 /** Mirror properties set on 'this' to 'that'.
224 void mlt_properties_mirror( mlt_properties this, mlt_properties that )
226 property_list *list = this->local;
230 /** Inherit all serialisable properties from that into this.
233 int mlt_properties_inherit( mlt_properties this, mlt_properties that )
235 int count = mlt_properties_count( that );
237 for ( i = 0; i < count; i ++ )
239 char *value = mlt_properties_get_value( that, i );
242 char *name = mlt_properties_get_name( that, i );
243 mlt_properties_set( this, name, value );
249 /** Pass all properties from 'that' that match the prefix to 'this' (excluding the prefix).
252 int mlt_properties_pass( mlt_properties this, mlt_properties that, const char *prefix )
254 int count = mlt_properties_count( that );
255 int length = strlen( prefix );
257 for ( i = 0; i < count; i ++ )
259 char *name = mlt_properties_get_name( that, i );
260 if ( !strncmp( name, prefix, length ) )
262 char *value = mlt_properties_get_value( that, i );
264 mlt_properties_set( this, name + length, value );
270 /** Locate a property by name
273 static inline mlt_property mlt_properties_find( mlt_properties this, const char *name )
275 property_list *list = this->local;
276 mlt_property value = NULL;
277 int key = generate_hash( name );
278 int i = list->hash[ key ] - 1;
282 // Check if we're hashed
283 if ( list->count > 0 &&
284 name[ 0 ] == list->name[ i ][ 0 ] &&
285 !strcmp( list->name[ i ], name ) )
286 value = list->value[ i ];
289 for ( i = list->count - 1; value == NULL && i >= 0; i -- )
290 if ( name[ 0 ] == list->name[ i ][ 0 ] && !strcmp( list->name[ i ], name ) )
291 value = list->value[ i ];
297 /** Add a new property.
300 static mlt_property mlt_properties_add( mlt_properties this, const char *name )
302 property_list *list = this->local;
303 int key = generate_hash( name );
305 // Check that we have space and resize if necessary
306 if ( list->count == list->size )
309 list->name = realloc( list->name, list->size * sizeof( const char * ) );
310 list->value = realloc( list->value, list->size * sizeof( mlt_property ) );
313 // Assign name/value pair
314 list->name[ list->count ] = strdup( name );
315 list->value[ list->count ] = mlt_property_init( );
317 // Assign to hash table
318 if ( list->hash[ key ] == 0 )
319 list->hash[ key ] = list->count + 1;
321 // Return and increment count accordingly
322 return list->value[ list->count ++ ];
325 /** Fetch a property by name - this includes add if not found.
328 static mlt_property mlt_properties_fetch( mlt_properties this, const char *name )
330 // Try to find an existing property first
331 mlt_property property = mlt_properties_find( this, name );
333 // If it wasn't found, create one
334 if ( property == NULL )
335 property = mlt_properties_add( this, name );
337 // Return the property
341 /** Pass property 'name' from 'that' to 'this'
342 * Who to blame: Zach <zachary.drew@gmail.com>
345 void mlt_properties_pass_property( mlt_properties this, mlt_properties that, const char *name )
347 // Make sure the source property isn't null.
348 mlt_property that_prop = mlt_properties_find( that, name );
349 if( that_prop == NULL )
352 mlt_property_pass( mlt_properties_fetch( this, name ), that_prop );
355 /** Pass all properties from 'that' to 'this' as found in comma seperated 'list'.
356 * Who to blame: Zach <zachary.drew@gmail.com>
359 int mlt_properties_pass_list( mlt_properties this, mlt_properties that, const char *list )
361 char *props = strdup( list );
363 char *delim = " ,\t\n"; // Any combination of spaces, commas, tabs, and newlines
368 count = strcspn( ptr, delim );
370 if( ptr[count] == '\0' )
373 ptr[count] = '\0'; // Make it a real string
375 mlt_properties_pass_property( this, that, ptr );
378 ptr += strspn( ptr, delim );
387 /** Set the property.
390 int mlt_properties_set( mlt_properties this, const char *name, const char *value )
394 // Fetch the property to work with
395 mlt_property property = mlt_properties_fetch( this, name );
397 // Set it if not NULL
398 if ( property == NULL )
400 fprintf( stderr, "Whoops - %s not found (should never occur)\n", name );
402 else if ( value == NULL )
404 error = mlt_property_set_string( property, value );
405 mlt_properties_do_mirror( this, name );
407 else if ( *value != '@' )
409 error = mlt_property_set_string( property, value );
410 mlt_properties_do_mirror( this, name );
412 else if ( value[ 0 ] == '@' )
421 while ( *value != '\0' )
423 int length = strcspn( value, "+-*/" );
425 // Get the identifier
426 strncpy( id, value, length );
430 // Determine the value
431 if ( isdigit( id[ 0 ] ) )
432 current = atof( id );
434 current = mlt_properties_get_int( this, id );
436 // Apply the operation
454 op = *value != '\0' ? *value ++ : ' ';
457 error = mlt_property_set_int( property, total );
458 mlt_properties_do_mirror( this, name );
461 mlt_events_fire( this, "property-changed", name, NULL );
466 /** Set or default the property.
469 int mlt_properties_set_or_default( mlt_properties this, const char *name, const char *value, const char *def )
471 return mlt_properties_set( this, name, value == NULL ? def : value );
474 /** Get a string value by name.
477 char *mlt_properties_get( mlt_properties this, const char *name )
479 mlt_property value = mlt_properties_find( this, name );
480 return value == NULL ? NULL : mlt_property_get_string( value );
483 /** Get a name by index.
486 char *mlt_properties_get_name( mlt_properties this, int index )
488 property_list *list = this->local;
489 if ( index >= 0 && index < list->count )
490 return list->name[ index ];
494 /** Get a string value by index.
497 char *mlt_properties_get_value( mlt_properties this, int index )
499 property_list *list = this->local;
500 if ( index >= 0 && index < list->count )
501 return mlt_property_get_string( list->value[ index ] );
505 /** Get a data value by index.
508 void *mlt_properties_get_data_at( mlt_properties this, int index, int *size )
510 property_list *list = this->local;
511 if ( index >= 0 && index < list->count )
512 return mlt_property_get_data( list->value[ index ], size );
516 /** Return the number of items in the list.
519 int mlt_properties_count( mlt_properties this )
521 property_list *list = this->local;
525 /** Set a value by parsing a name=value string
528 int mlt_properties_parse( mlt_properties this, const char *namevalue )
530 char *name = strdup( namevalue );
533 char *ptr = strchr( name, '=' );
541 value = strdup( ptr );
546 value = strdup( ptr );
547 if ( value != NULL && value[ strlen( value ) - 1 ] == '\"' )
548 value[ strlen( value ) - 1 ] = '\0';
553 value = strdup( "" );
556 error = mlt_properties_set( this, name, value );
564 /** Get a value associated to the name.
567 int mlt_properties_get_int( mlt_properties this, const char *name )
569 mlt_property value = mlt_properties_find( this, name );
570 return value == NULL ? 0 : mlt_property_get_int( value );
573 /** Set a value associated to the name.
576 int mlt_properties_set_int( mlt_properties this, const char *name, int value )
580 // Fetch the property to work with
581 mlt_property property = mlt_properties_fetch( this, name );
583 // Set it if not NULL
584 if ( property != NULL )
586 error = mlt_property_set_int( property, value );
587 mlt_properties_do_mirror( this, name );
590 mlt_events_fire( this, "property-changed", name, NULL );
595 /** Get a value associated to the name.
598 int64_t mlt_properties_get_int64( mlt_properties this, const char *name )
600 mlt_property value = mlt_properties_find( this, name );
601 return value == NULL ? 0 : mlt_property_get_int64( value );
604 /** Set a value associated to the name.
607 int mlt_properties_set_int64( mlt_properties this, const char *name, int64_t value )
611 // Fetch the property to work with
612 mlt_property property = mlt_properties_fetch( this, name );
614 // Set it if not NULL
615 if ( property != NULL )
617 error = mlt_property_set_int64( property, value );
618 mlt_properties_do_mirror( this, name );
621 mlt_events_fire( this, "property-changed", name, NULL );
626 /** Get a value associated to the name.
629 double mlt_properties_get_double( mlt_properties this, const char *name )
631 mlt_property value = mlt_properties_find( this, name );
632 return value == NULL ? 0 : mlt_property_get_double( value );
635 /** Set a value associated to the name.
638 int mlt_properties_set_double( mlt_properties this, const char *name, double value )
642 // Fetch the property to work with
643 mlt_property property = mlt_properties_fetch( this, name );
645 // Set it if not NULL
646 if ( property != NULL )
648 error = mlt_property_set_double( property, value );
649 mlt_properties_do_mirror( this, name );
652 mlt_events_fire( this, "property-changed", name, NULL );
657 /** Get a value associated to the name.
660 mlt_position mlt_properties_get_position( mlt_properties this, const char *name )
662 mlt_property value = mlt_properties_find( this, name );
663 return value == NULL ? 0 : mlt_property_get_position( value );
666 /** Set a value associated to the name.
669 int mlt_properties_set_position( mlt_properties this, const char *name, mlt_position value )
673 // Fetch the property to work with
674 mlt_property property = mlt_properties_fetch( this, name );
676 // Set it if not NULL
677 if ( property != NULL )
679 error = mlt_property_set_position( property, value );
680 mlt_properties_do_mirror( this, name );
683 mlt_events_fire( this, "property-changed", name, NULL );
688 /** Get a value associated to the name.
691 void *mlt_properties_get_data( mlt_properties this, const char *name, int *length )
693 mlt_property value = mlt_properties_find( this, name );
694 return value == NULL ? NULL : mlt_property_get_data( value, length );
697 /** Set a value associated to the name.
700 int mlt_properties_set_data( mlt_properties this, const char *name, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise )
704 // Fetch the property to work with
705 mlt_property property = mlt_properties_fetch( this, name );
707 // Set it if not NULL
708 if ( property != NULL )
709 error = mlt_property_set_data( property, value, length, destroy, serialise );
711 mlt_events_fire( this, "property-changed", name, NULL );
716 /** Rename a property.
719 int mlt_properties_rename( mlt_properties this, const char *source, const char *dest )
721 mlt_property value = mlt_properties_find( this, dest );
725 property_list *list = this->local;
729 for ( i = 0; i < list->count; i ++ )
731 if ( !strcmp( list->name[ i ], source ) )
733 free( list->name[ i ] );
734 list->name[ i ] = strdup( dest );
735 list->hash[ generate_hash( dest ) ] = i + 1;
741 return value != NULL;
744 /** Dump the properties.
747 void mlt_properties_dump( mlt_properties this, FILE *output )
749 property_list *list = this->local;
751 for ( i = 0; i < list->count; i ++ )
752 if ( mlt_properties_get( this, list->name[ i ] ) != NULL )
753 fprintf( output, "%s=%s\n", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
756 void mlt_properties_debug( mlt_properties this, const char *title, FILE *output )
758 if ( output == NULL ) output = stderr;
759 fprintf( output, "%s: ", title );
762 property_list *list = this->local;
764 fprintf( output, "[ ref=%d", list->ref_count );
765 for ( i = 0; i < list->count; i ++ )
766 if ( mlt_properties_get( this, list->name[ i ] ) != NULL )
767 fprintf( output, ", %s=%s", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
769 fprintf( output, ", %s=%p", list->name[ i ], mlt_properties_get_data( this, list->name[ i ], NULL ) );
770 fprintf( output, " ]" );
772 fprintf( output, "\n" );
775 int mlt_properties_save( mlt_properties this, const char *filename )
778 FILE *f = fopen( filename, "w" );
781 mlt_properties_dump( this, f );
788 /* This is a very basic cross platform fnmatch replacement - it will fail in
789 ** many cases, but for the basic *.XXX and YYY*.XXX, it will work ok.
792 static int mlt_fnmatch( const char *wild, const char *file )
797 while( f < strlen( file ) && w < strlen( wild ) )
799 if ( wild[ w ] == '*' )
802 if ( w == strlen( wild ) )
804 while ( f != strlen( file ) && tolower( file[ f ] ) != tolower( wild[ w ] ) )
807 else if ( wild[ w ] == '?' || tolower( file[ f ] ) == tolower( wild[ w ] ) )
812 else if ( wild[ 0 ] == '*' )
822 return strlen( file ) == f && strlen( wild ) == w;
825 static int mlt_compare( const void *this, const void *that )
827 return strcmp( mlt_property_get_string( *( mlt_property * )this ), mlt_property_get_string( *( mlt_property * )that ) );
830 /* Obtains an optionally sorted list of the files found in a directory with a specific wild card.
831 * Entries in the list have a numeric name (running from 0 to count - 1). Only values change
832 * position if sort is enabled. Designed to be posix compatible (linux, os/x, mingw etc).
835 int mlt_properties_dir_list( mlt_properties this, const char *dirname, const char *pattern, int sort )
837 DIR *dir = opendir( dirname );
842 struct dirent *de = readdir( dir );
843 char fullname[ 1024 ];
846 sprintf( key, "%d", mlt_properties_count( this ) );
847 snprintf( fullname, 1024, "%s/%s", dirname, de->d_name );
848 if ( de->d_name[ 0 ] != '.' && mlt_fnmatch( pattern, de->d_name ) )
849 mlt_properties_set( this, key, fullname );
856 if ( sort && mlt_properties_count( this ) )
858 property_list *list = this->local;
859 qsort( list->value, mlt_properties_count( this ), sizeof( mlt_property ), mlt_compare );
862 return mlt_properties_count( this );
868 void mlt_properties_close( mlt_properties this )
870 if ( this != NULL && mlt_properties_dec_ref( this ) <= 0 )
872 if ( this->close != NULL )
874 this->close( this->close_object );
878 property_list *list = this->local;
881 #if _MLT_PROPERTY_CHECKS_ == 1
883 mlt_properties_debug( this, "Closing", stderr );
886 #ifdef _MLT_PROPERTY_CHECKS_
887 // Increment destroyed count
888 properties_destroyed ++;
890 // Show current stats - these should match when the app is closed
891 fprintf( stderr, "Created %d, destroyed %d\n", properties_created, properties_destroyed );
894 // Clean up names and values
895 for ( index = list->count - 1; index >= 0; index -- )
897 free( list->name[ index ] );
898 mlt_property_close( list->value[ index ] );
906 // Free this now if this has no child
907 if ( this->child == NULL )