Convenience functionality for properties load and miracle_server_id function
[melted] / src / framework / mlt_properties.c
1 /*
2 * mlt_properties.c -- base properties class
3 * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
5 *
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.
10 *
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.
15 *
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.
19 */
20
21 #include "config.h"
22 #include "mlt_properties.h"
23 #include "mlt_property.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <ctype.h>
29
30 /* ---------------- // Private Implementation // ---------------- */
31
32 /** Private implementation of the property list.
33 */
34
35 typedef struct
36 {
37 int hash[ 199 ];
38 char **name;
39 mlt_property *value;
40 int count;
41 int size;
42 mlt_properties mirror;
43 int ref_count;
44 }
45 property_list;
46
47 /** Memory leak checks.
48 */
49
50 //#define _MLT_PROPERTY_CHECKS_
51
52 #ifdef _MLT_PROPERTY_CHECKS_
53 static int properties_created = 0;
54 static int properties_destroyed = 0;
55 #endif
56
57 /** Basic implementation.
58 */
59
60 int mlt_properties_init( mlt_properties this, void *child )
61 {
62 if ( this != NULL )
63 {
64 #ifdef _MLT_PROPERTY_CHECKS_
65 // Increment number of properties created
66 properties_created ++;
67 #endif
68
69 // NULL all methods
70 memset( this, 0, sizeof( struct mlt_properties_s ) );
71
72 // Assign the child of the object
73 this->child = child;
74
75 // Allocate the local structure
76 this->local = calloc( sizeof( property_list ), 1 );
77
78 // Increment the ref count
79 ( ( property_list * )this->local )->ref_count = 1;
80 }
81
82 // Check that initialisation was successful
83 return this != NULL && this->local == NULL;
84 }
85
86 /** Constructor for stand alone object.
87 */
88
89 mlt_properties mlt_properties_new( )
90 {
91 // Construct a standalone properties object
92 mlt_properties this = calloc( sizeof( struct mlt_properties_s ), 1 );
93
94 // Initialise this
95 mlt_properties_init( this, NULL );
96
97 // Return the pointer
98 return this;
99 }
100
101 /** Load properties from a file.
102 */
103
104 mlt_properties mlt_properties_load( char *filename )
105 {
106 // Construct a standalone properties object
107 mlt_properties this = mlt_properties_new( );
108
109 if ( this != NULL )
110 {
111 // Open the file
112 FILE *file = fopen( filename, "r" );
113
114 // Load contents of file
115 if ( file != NULL )
116 {
117 // Temp string
118 char temp[ 1024 ];
119 char last[ 1024 ] = "";
120
121 // Read each string from the file
122 while( fgets( temp, 1024, file ) )
123 {
124 // Chomp the string
125 temp[ strlen( temp ) - 1 ] = '\0';
126
127 // Check if the line starts with a .
128 if ( temp[ 0 ] == '.' )
129 {
130 char temp2[ 1024 ];
131 sprintf( temp2, "%s%s", last, temp );
132 strcpy( temp, temp2 );
133 }
134 else if ( strchr( temp, '=' ) )
135 {
136 strcpy( last, temp );
137 *( strchr( last, '=' ) ) = '\0';
138 }
139
140 // Parse and set the property
141 if ( strcmp( temp, "" ) && temp[ 0 ] != '#' )
142 mlt_properties_parse( this, temp );
143 }
144
145 // Close the file
146 fclose( file );
147 }
148 }
149
150 // Return the pointer
151 return this;
152 }
153
154 static inline int generate_hash( char *name )
155 {
156 int hash = 0;
157 int i = 1;
158 while ( *name )
159 hash = ( hash + ( i ++ * ( *name ++ & 31 ) ) ) % 199;
160 return hash;
161 }
162
163 /** Special case - when a container (such as fezzik) is protecting another
164 producer, we need to ensure that properties are passed through to the
165 real producer.
166 */
167
168 static inline void mlt_properties_do_mirror( mlt_properties this, char *name )
169 {
170 property_list *list = this->local;
171 if ( list->mirror != NULL )
172 {
173 char *value = mlt_properties_get( this, name );
174 if ( value != NULL )
175 mlt_properties_set( list->mirror, name, value );
176 }
177 }
178
179 /** Maintain ref count to allow multiple uses of an mlt object.
180 */
181
182 int mlt_properties_inc_ref( mlt_properties this )
183 {
184 if ( this != NULL )
185 {
186 property_list *list = this->local;
187 return ++ list->ref_count;
188 }
189 return 0;
190 }
191
192 /** Maintain ref count to allow multiple uses of an mlt object.
193 */
194
195 int mlt_properties_dec_ref( mlt_properties this )
196 {
197 if ( this != NULL )
198 {
199 property_list *list = this->local;
200 return -- list->ref_count;
201 }
202 return 0;
203 }
204
205 /** Allow the specification of a mirror.
206 */
207
208 void mlt_properties_mirror( mlt_properties this, mlt_properties that )
209 {
210 property_list *list = this->local;
211 list->mirror = that;
212 }
213
214 /** Inherit all serialisable properties from that into this.
215 */
216
217 int mlt_properties_inherit( mlt_properties this, mlt_properties that )
218 {
219 int count = mlt_properties_count( that );
220 int i = 0;
221 for ( i = 0; i < count; i ++ )
222 {
223 char *value = mlt_properties_get_value( that, i );
224 if ( value != NULL )
225 {
226 char *name = mlt_properties_get_name( that, i );
227 mlt_properties_set( this, name, value );
228 }
229 }
230 return 0;
231 }
232
233 /** Pass all properties from 'that' that match the prefix to 'this' (excluding the prefix).
234 */
235
236 int mlt_properties_pass( mlt_properties this, mlt_properties that, char *prefix )
237 {
238 int count = mlt_properties_count( that );
239 int length = strlen( prefix );
240 int i = 0;
241 for ( i = 0; i < count; i ++ )
242 {
243 char *name = mlt_properties_get_name( that, i );
244 if ( !strncmp( name, prefix, length ) )
245 {
246 char *value = mlt_properties_get_value( that, i );
247 if ( value != NULL )
248 mlt_properties_set( this, name + length, value );
249 }
250 }
251 return 0;
252 }
253
254 /** Locate a property by name
255 */
256
257 static inline mlt_property mlt_properties_find( mlt_properties this, char *name )
258 {
259 property_list *list = this->local;
260 mlt_property value = NULL;
261 int key = generate_hash( name );
262 int i = list->hash[ key ] - 1;
263
264 if ( i >= 0 )
265 {
266 // Check if we're hashed
267 if ( list->count > 0 &&
268 name[ 0 ] == list->name[ i ][ 0 ] &&
269 !strcmp( list->name[ i ], name ) )
270 value = list->value[ i ];
271
272 // Locate the item
273 for ( i = list->count - 1; value == NULL && i >= 0; i -- )
274 if ( name[ 0 ] == list->name[ i ][ 0 ] && !strcmp( list->name[ i ], name ) )
275 value = list->value[ i ];
276 }
277
278 return value;
279 }
280
281 /** Add a new property.
282 */
283
284 static mlt_property mlt_properties_add( mlt_properties this, char *name )
285 {
286 property_list *list = this->local;
287 int key = generate_hash( name );
288
289 // Check that we have space and resize if necessary
290 if ( list->count == list->size )
291 {
292 list->size += 50;
293 list->name = realloc( list->name, list->size * sizeof( char * ) );
294 list->value = realloc( list->value, list->size * sizeof( mlt_property ) );
295 }
296
297 // Assign name/value pair
298 list->name[ list->count ] = strdup( name );
299 list->value[ list->count ] = mlt_property_init( );
300
301 // Assign to hash table
302 if ( list->hash[ key ] == 0 )
303 list->hash[ key ] = list->count + 1;
304
305 // Return and increment count accordingly
306 return list->value[ list->count ++ ];
307 }
308
309 /** Fetch a property by name - this includes add if not found.
310 */
311
312 static mlt_property mlt_properties_fetch( mlt_properties this, char *name )
313 {
314 // Try to find an existing property first
315 mlt_property property = mlt_properties_find( this, name );
316
317 // If it wasn't found, create one
318 if ( property == NULL )
319 property = mlt_properties_add( this, name );
320
321 // Return the property
322 return property;
323 }
324
325 /** Set the property.
326 */
327
328 int mlt_properties_set( mlt_properties this, char *name, char *value )
329 {
330 int error = 1;
331
332 // Fetch the property to work with
333 mlt_property property = mlt_properties_fetch( this, name );
334
335 // Set it if not NULL
336 if ( property != NULL && ( value == NULL || value[ 0 ] != '@' ) )
337 {
338 error = mlt_property_set_string( property, value );
339 mlt_properties_do_mirror( this, name );
340 }
341 else if ( property != NULL && value[ 0 ] == '@' )
342 {
343 int total = 0;
344 int current = 0;
345 char id[ 255 ];
346 char op = '+';
347
348 value ++;
349
350 while ( *value != '\0' )
351 {
352 int length = strcspn( value, "+-*/" );
353
354 // Get the identifier
355 strncpy( id, value, length );
356 id[ length ] = '\0';
357 value += length;
358
359 // Determine the value
360 if ( isdigit( id[ 0 ] ) )
361 current = atof( id );
362 else
363 current = mlt_properties_get_int( this, id );
364
365 // Apply the operation
366 switch( op )
367 {
368 case '+':
369 total += current;
370 break;
371 case '-':
372 total -= current;
373 break;
374 case '*':
375 total *= current;
376 break;
377 case '/':
378 total /= current;
379 break;
380 }
381
382 // Get the next op
383 op = *value != '\0' ? *value ++ : ' ';
384 }
385
386 error = mlt_property_set_int( property, total );
387 mlt_properties_do_mirror( this, name );
388 }
389
390 mlt_events_fire( this, "property-changed", name, NULL );
391
392 return error;
393 }
394
395 /** Set or default the property.
396 */
397
398 int mlt_properties_set_or_default( mlt_properties this, char *name, char *value, char *def )
399 {
400 return mlt_properties_set( this, name, value == NULL ? def : value );
401 }
402
403 /** Get a string value by name.
404 */
405
406 char *mlt_properties_get( mlt_properties this, char *name )
407 {
408 mlt_property value = mlt_properties_find( this, name );
409 return value == NULL ? NULL : mlt_property_get_string( value );
410 }
411
412 /** Get a name by index.
413 */
414
415 char *mlt_properties_get_name( mlt_properties this, int index )
416 {
417 property_list *list = this->local;
418 if ( index >= 0 && index < list->count )
419 return list->name[ index ];
420 return NULL;
421 }
422
423 /** Get a string value by index.
424 */
425
426 char *mlt_properties_get_value( mlt_properties this, int index )
427 {
428 property_list *list = this->local;
429 if ( index >= 0 && index < list->count )
430 return mlt_property_get_string( list->value[ index ] );
431 return NULL;
432 }
433
434 /** Get a data value by index.
435 */
436
437 void *mlt_properties_get_data_at( mlt_properties this, int index, int *size )
438 {
439 property_list *list = this->local;
440 if ( index >= 0 && index < list->count )
441 return mlt_property_get_data( list->value[ index ], size );
442 return NULL;
443 }
444
445 /** Return the number of items in the list.
446 */
447
448 int mlt_properties_count( mlt_properties this )
449 {
450 property_list *list = this->local;
451 return list->count;
452 }
453
454 /** Set a value by parsing a name=value string
455 */
456
457 int mlt_properties_parse( mlt_properties this, char *namevalue )
458 {
459 char *name = strdup( namevalue );
460 char *value = NULL;
461 int error = 0;
462 char *ptr = strchr( name, '=' );
463
464 if ( ptr )
465 {
466 *( ptr ++ ) = '\0';
467
468 if ( *ptr != '\"' )
469 {
470 value = strdup( ptr );
471 }
472 else
473 {
474 ptr ++;
475 value = strdup( ptr );
476 if ( value != NULL && value[ strlen( value ) - 1 ] == '\"' )
477 value[ strlen( value ) - 1 ] = '\0';
478 }
479 }
480 else
481 {
482 value = strdup( "" );
483 }
484
485 error = mlt_properties_set( this, name, value );
486
487 free( name );
488 free( value );
489
490 return error;
491 }
492
493 /** Get a value associated to the name.
494 */
495
496 int mlt_properties_get_int( mlt_properties this, char *name )
497 {
498 mlt_property value = mlt_properties_find( this, name );
499 return value == NULL ? 0 : mlt_property_get_int( value );
500 }
501
502 /** Set a value associated to the name.
503 */
504
505 int mlt_properties_set_int( mlt_properties this, char *name, int value )
506 {
507 int error = 1;
508
509 // Fetch the property to work with
510 mlt_property property = mlt_properties_fetch( this, name );
511
512 // Set it if not NULL
513 if ( property != NULL )
514 {
515 error = mlt_property_set_int( property, value );
516 mlt_properties_do_mirror( this, name );
517 }
518
519 mlt_events_fire( this, "property-changed", name, NULL );
520
521 return error;
522 }
523
524 /** Get a value associated to the name.
525 */
526
527 double mlt_properties_get_double( mlt_properties this, char *name )
528 {
529 mlt_property value = mlt_properties_find( this, name );
530 return value == NULL ? 0 : mlt_property_get_double( value );
531 }
532
533 /** Set a value associated to the name.
534 */
535
536 int mlt_properties_set_double( mlt_properties this, char *name, double value )
537 {
538 int error = 1;
539
540 // Fetch the property to work with
541 mlt_property property = mlt_properties_fetch( this, name );
542
543 // Set it if not NULL
544 if ( property != NULL )
545 {
546 error = mlt_property_set_double( property, value );
547 mlt_properties_do_mirror( this, name );
548 }
549
550 mlt_events_fire( this, "property-changed", name, NULL );
551
552 return error;
553 }
554
555 /** Get a value associated to the name.
556 */
557
558 mlt_position mlt_properties_get_position( mlt_properties this, char *name )
559 {
560 mlt_property value = mlt_properties_find( this, name );
561 return value == NULL ? 0 : mlt_property_get_position( value );
562 }
563
564 /** Set a value associated to the name.
565 */
566
567 int mlt_properties_set_position( mlt_properties this, char *name, mlt_position value )
568 {
569 int error = 1;
570
571 // Fetch the property to work with
572 mlt_property property = mlt_properties_fetch( this, name );
573
574 // Set it if not NULL
575 if ( property != NULL )
576 {
577 error = mlt_property_set_position( property, value );
578 mlt_properties_do_mirror( this, name );
579 }
580
581 mlt_events_fire( this, "property-changed", name, NULL );
582
583 return error;
584 }
585
586 /** Get a value associated to the name.
587 */
588
589 void *mlt_properties_get_data( mlt_properties this, char *name, int *length )
590 {
591 mlt_property value = mlt_properties_find( this, name );
592 return value == NULL ? NULL : mlt_property_get_data( value, length );
593 }
594
595 /** Set a value associated to the name.
596 */
597
598 int mlt_properties_set_data( mlt_properties this, char *name, void *value, int length, mlt_destructor destroy, mlt_serialiser serialise )
599 {
600 int error = 1;
601
602 // Fetch the property to work with
603 mlt_property property = mlt_properties_fetch( this, name );
604
605 // Set it if not NULL
606 if ( property != NULL )
607 error = mlt_property_set_data( property, value, length, destroy, serialise );
608
609 mlt_events_fire( this, "property-changed", name, NULL );
610
611 return error;
612 }
613
614 /** Rename a property.
615 */
616
617 int mlt_properties_rename( mlt_properties this, char *source, char *dest )
618 {
619 mlt_property value = mlt_properties_find( this, dest );
620
621 if ( value == NULL )
622 {
623 property_list *list = this->local;
624 int i = 0;
625
626 // Locate the item
627 for ( i = 0; i < list->count; i ++ )
628 {
629 if ( !strcmp( list->name[ i ], source ) )
630 {
631 free( list->name[ i ] );
632 list->name[ i ] = strdup( dest );
633 list->hash[ generate_hash( dest ) ] = i + 1;
634 break;
635 }
636 }
637 }
638
639 return value != NULL;
640 }
641
642 /** Dump the properties.
643 */
644
645 void mlt_properties_dump( mlt_properties this, FILE *output )
646 {
647 property_list *list = this->local;
648 int i = 0;
649 for ( i = 0; i < list->count; i ++ )
650 if ( mlt_properties_get( this, list->name[ i ] ) != NULL )
651 fprintf( output, "%s=%s\n", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
652 }
653
654 void mlt_properties_debug( mlt_properties this, char *title, FILE *output )
655 {
656 fprintf( stderr, "%s: ", title );
657 if ( this != NULL )
658 {
659 property_list *list = this->local;
660 int i = 0;
661 fprintf( output, "[ ref=%d", list->ref_count );
662 for ( i = 0; i < list->count; i ++ )
663 if ( mlt_properties_get( this, list->name[ i ] ) != NULL )
664 fprintf( output, ", %s=%s", list->name[ i ], mlt_properties_get( this, list->name[ i ] ) );
665 else
666 fprintf( output, ", %s=%p", list->name[ i ], mlt_properties_get_data( this, list->name[ i ], NULL ) );
667 fprintf( output, " ]" );
668 }
669 fprintf( stderr, "\n" );
670 }
671
672 /** Close the list.
673 */
674
675 void mlt_properties_close( mlt_properties this )
676 {
677 if ( this != NULL && mlt_properties_dec_ref( this ) <= 0 )
678 {
679 if ( this->close != NULL )
680 {
681 this->close( this->close_object );
682 }
683 else
684 {
685 property_list *list = this->local;
686 int index = 0;
687
688 #ifdef _MLT_PROPERTY_CHECKS_
689 // Show debug info
690 mlt_properties_debug( this, "Closing", stderr );
691
692 // Increment destroyed count
693 properties_destroyed ++;
694
695 // Show current stats - these should match when the app is closed
696 fprintf( stderr, "Created %d, destroyed %d\n", properties_created, properties_destroyed );
697 #endif
698
699 // Clean up names and values
700 for ( index = list->count - 1; index >= 0; index -- )
701 {
702 free( list->name[ index ] );
703 mlt_property_close( list->value[ index ] );
704 }
705
706 // Clear up the list
707 free( list->name );
708 free( list->value );
709 free( list );
710
711 // Free this now if this has no child
712 if ( this->child == NULL )
713 free( this );
714 }
715 }
716 }
717