r915:
[cinelerra_cv/mob.git] / cinelerra / filexml.C
blobebd991e117e0b24990d8183f1bd37e2ac4dae260
1 #include <ctype.h>
2 #include <errno.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
7 #include "bcsignals.h"
8 #include "filexml.h"
9 #include "mainerror.h"
12 // Precision in base 10
13 // for float is 6 significant figures
14 // for double is 16 significant figures
18 FileXML::FileXML(char left_delimiter, char right_delimiter)
20         tag.set_delimiters(left_delimiter, right_delimiter);
21         this->left_delimiter = left_delimiter;
22         this->right_delimiter = right_delimiter;
23         available = 64;
24         string = new char[available];
25         string[0] = 0;
26         position = length = 0;
27         output_length = 0;
28         share_string = 0;
31 FileXML::~FileXML()
33         if(!share_string) delete [] string;
34         if(output_length) delete [] output;
37 void FileXML::dump()
39         printf("FileXML::dump:\n%s\n", string);
42 int FileXML::terminate_string()
44         append_text("", 1);
45         return 0;
48 int FileXML::rewind()
50         terminate_string();
51         length = strlen(string);
52         position = 0;
53         return 0;
57 int FileXML::append_newline()
59         append_text("\n", 1);
60         return 0;
63 int FileXML::append_tag()
65         tag.write_tag();
66         append_text(tag.string, tag.len);
67         tag.reset_tag();
68         return 0;
71 int FileXML::append_text(char *text)
73         append_text(text, strlen(text));
74         return 0;
77 int FileXML::append_text(char *text, long len)
79         while(position + len > available)
80         {
81                 reallocate_string(available * 2);
82         }
84         for(int i = 0; i < len; i++, position++)
85         {
86                 string[position] = text[i];
87         }
88         return 0;
91 int FileXML::encode_text(char *text)
93 // We have to encode at least the '<' char
94 // We encode three things:
95 // '<' -> '&lt;' 
96 // '>' -> '&gt;'
97 // '&' -> '&amp;'
98         char leftb[] = "&lt;";
99         char rightb[] = "&gt;";
100         char amp[] = "&amp;";
101         char *replacement;
102         int len = strlen(text);
103         int lastpos = 0;
104         for (int i = 0; i < len; i++)
105         {
106                 switch (text[i]) {
107                         case '<': replacement = leftb; break;
108                         case '>': replacement = rightb; break;
109                         case '&': replacement = amp; break;
110                         default: replacement = 0; break;
111                 }
112                 if (replacement)
113                 {
114                         if (i - lastpos > 0)
115                                 append_text(text + lastpos, i - lastpos);
116                         append_text(replacement, strlen(replacement));
117                         lastpos = i + 1;
118                 }
119         }
120         append_text(text + lastpos, len - lastpos);
121         return 0;
126 int FileXML::reallocate_string(long new_available)
128         if(!share_string)
129         {
130                 char *new_string = new char[new_available];
131                 for(int i = 0; i < position; i++) new_string[i] = string[i];
132                 available = new_available;
133                 delete [] string;
134                 string = new_string;
135         }
136         return 0;
139 char* FileXML::read_text()
141         long text_position = position;
142         int i;
144 // use < to mark end of text and start of tag
146 // find end of text
147         for(; position < length && string[position] != left_delimiter; position++)
148         {
149                 ;
150         }
152 // allocate enough space
153         if(output_length) delete [] output;
154         output_length = position - text_position;
155         output = new char[output_length + 1];
157 //printf("FileXML::read_text %d %c\n", text_position, string[text_position]);
158         for(i = 0; text_position < position; text_position++)
159         {
160 // filter out first newline
161                 if((i > 0 && i < output_length - 1) || string[text_position] != '\n') 
162                 {
163 // check if we have to decode special characters
164 // but try to be most backward compatible possible
165                         int character = string[text_position];
166                         if (string[text_position] == '&')
167                         {
168                                 if (text_position + 3 < length)
169                                 {
170                                         if (string[text_position + 1] == 'l' && string[text_position + 2] == 't' && string[text_position + 3] == ';')
171                                         {
172                                                 character = '<';
173                                                 text_position += 3;
174                                         }               
175                                         if (string[text_position + 1] == 'g' && string[text_position + 2] == 't' && string[text_position + 3] == ';')
176                                         {
177                                                 character = '>';
178                                                 text_position += 3;
179                                         }               
180                                 }
181                                 if (text_position + 4 < length)
182                                 {
183                                         if (string[text_position + 1] == 'a' && string[text_position + 2] == 'm' && string[text_position + 3] == 'p' && string[text_position + 4] == ';')
184                                         {
185                                                 character = '&';
186                                                 text_position += 4;
187                                         }               
188                                 }
189                         }
190                         output[i] = character;
191                         i++;
192                 }
193         }
194         output[i] = 0;
196         return output;
199 int FileXML::read_tag()
201 // scan to next tag
202         while(position < length && string[position] != left_delimiter)
203         {
204                 position++;
205         }
206         tag.reset_tag();
207         if(position >= length) return 1;
208 //printf("FileXML::read_tag %s\n", &string[position]);
209         return tag.read_tag(string, position, length);
212 int FileXML::read_text_until(char *tag_end, char *output, int max_len)
214 // read to next tag
215         int out_position = 0;
216         int test_position1, test_position2;
217         int result = 0;
218         
219         while(!result && position < length && out_position < max_len - 1)
220         {
221                 while(position < length && string[position] != left_delimiter)
222                 {
223 //printf("FileXML::read_text_until 1 %c\n", string[position]);
224                         output[out_position++] = string[position++];
225                 }
226                 
227                 if(position < length && string[position] == left_delimiter)
228                 {
229 // tag reached
230 // test for tag_end
231                         result = 1;         // assume end
232                         
233                         for(test_position1 = 0, test_position2 = position + 1;   // skip < 
234                                 test_position2 < length &&
235                                 tag_end[test_position1] != 0 &&
236                                 result; 
237                                 test_position1++, test_position2++)
238                         {
239 // null result when first wrong character is reached
240 //printf("FileXML::read_text_until 2 %c\n", string[test_position2]);
241                                 if(tag_end[test_position1] != string[test_position2]) result = 0;
242                         }
244 // no end tag reached to copy <
245                         if(!result)
246                         {
247                                 output[out_position++] = string[position++];
248                         }
249                 }
250         }
251         output[out_position] = 0;
252 // if end tag is reached, position is left on the < of the end tag
253         return 0;
257 int FileXML::write_to_file(char *filename)
259         FILE *out;
260         strcpy(this->filename, filename);
261         if(out = fopen(filename, "wb"))
262         {
263                 fprintf(out, "<?xml version=\"1.0\"?>\n");
264 // Position may have been rewound after storing so we use a strlen
265                 if(!fwrite(string, strlen(string), 1, out) && strlen(string))
266                 {
267                         eprintf("Error while writing to \"%s\": %m\n",
268                                 filename);
269                         fclose(out);
270                         return 1;
271                 }
272                 else
273                 {
274                 }
275         }
276         else
277         {
278                 eprintf("Error while opening \"%s\" for writing. \n%m\n", filename);
279                 return 1;
280         }
281         fclose(out);
282         return 0;
285 int FileXML::write_to_file(FILE *file)
287         strcpy(filename, "");
288         fprintf(file, "<?xml version=\"1.0\"?>\n");
289 // Position may have been rewound after storing
290         if(fwrite(string, strlen(string), 1, file) || !strlen(string))
291         {
292                 return 0;
293         }
294         else
295         {
296                 eprintf("Error while writing to \"%s\": %m\n",
297                         filename);
298                 return 1;
299         }
300         return 0;
303 int FileXML::read_from_file(char *filename, int ignore_error)
305         FILE *in;
306         
307         strcpy(this->filename, filename);
308         if(in = fopen(filename, "rb"))
309         {
310                 fseek(in, 0, SEEK_END);
311                 int new_length = ftell(in);
312                 fseek(in, 0, SEEK_SET);
313                 reallocate_string(new_length + 1);
314                 fread(string, new_length, 1, in);
315                 string[new_length] = 0;
316                 position = 0;
317                 length = new_length;
318         }
319         else
320         {
321                 if(!ignore_error) 
322                         eprintf("Error while opening \"%s\" for reading. \n%m\n", filename);
323                 return 1;
324         }
325         fclose(in);
326         return 0;
329 int FileXML::read_from_string(char *string)
331         strcpy(this->filename, "");
332         reallocate_string(strlen(string) + 1);
333         strcpy(this->string, string);
334         length = strlen(string);
335         position = 0;
336         return 0;
339 int FileXML::set_shared_string(char *shared_string, long available)
341         strcpy(this->filename, "");
342         if(!share_string)
343         {
344                 delete [] string;
345                 share_string = 1;
346                 string = shared_string;
347                 this->available = available;
348                 length = available;
349                 position = 0;
350         }
351         return 0;
356 // ================================ XML tag
359 XMLTag::XMLTag()
361         total_properties = 0;
362         len = 0;
365 XMLTag::~XMLTag()
367         reset_tag();
370 int XMLTag::set_delimiters(char left_delimiter, char right_delimiter)
372         this->left_delimiter = left_delimiter;
373         this->right_delimiter = right_delimiter;
374         return 0;
377 int XMLTag::reset_tag()     // clear all structures
379         len = 0;
380         for(int i = 0; i < total_properties; i++) delete [] tag_properties[i];
381         for(int i = 0; i < total_properties; i++) delete [] tag_property_values[i];
382         total_properties = 0;
383         return 0;
386 int XMLTag::write_tag()
388         int i, j;
389         char *current_property, *current_value;
391 // opening bracket
392         string[len] = left_delimiter;        
393         len++;
394         
395 // title
396         for(i = 0; tag_title[i] != 0 && len < MAX_LENGTH; i++, len++) string[len] = tag_title[i];
398 // properties
399         for(i = 0; i < total_properties && len < MAX_LENGTH; i++)
400         {
401                 string[len++] = ' ';         // add a space before every property
402                 
403                 current_property = tag_properties[i];
405 // property title
406                 for(j = 0; current_property[j] != 0 && len < MAX_LENGTH; j++, len++)
407                 {
408                         string[len] = current_property[j];
409                 }
410                 
411                 if(len < MAX_LENGTH) string[len++] = '=';
412                 
413                 current_value = tag_property_values[i];
415 // property value
416                 if( len < MAX_LENGTH) string[len++] = '\"';
417 // write the value
418                 for(j = 0; current_value[j] != 0 && len < MAX_LENGTH; j++, len++)
419                 {
420                         string[len] = current_value[j];
421                 }
422                 if(len < MAX_LENGTH) string[len++] = '\"';
423         }     // next property
424         
425         if(len < MAX_LENGTH) string[len++] = right_delimiter;   // terminating bracket
426         return 0;
429 int XMLTag::read_tag(char *input, long &position, long length)
431         long tag_start;
432         int i, j, terminating_char;
434 // search for beginning of a tag
435         while(input[position] != left_delimiter && position < length) position++;
436         
437         if(position >= length) return 1;
439 // find the start
440         while(position < length &&
441                 (input[position] == ' ' ||         // skip spaces
442                 input[position] == '\n' ||       // also skip new lines
443                 input[position] == left_delimiter))           // skip <
444                 position++;
446         if(position >= length) return 1;
447         
448         tag_start = position;
449         
450 // read title
451         for(i = 0; 
452                 i < MAX_TITLE && 
453                 position < length && 
454                 input[position] != '=' && 
455                 input[position] != ' ' &&       // space ends title
456                 input[position] != right_delimiter;
457                 position++, i++)
458         {
459                 tag_title[i] = input[position];
460         }
461         tag_title[i] = 0;
462         
463         if(position >= length) return 1;
464         
465         if(input[position] == '=')
466         {
467 // no title but first property
468                 tag_title[0] = 0;
469                 position = tag_start;       // rewind
470         }
472 // read properties
473         for(i = 0;
474                 i < MAX_PROPERTIES &&
475                 position < length &&
476                 input[position] != right_delimiter;
477                 i++)
478         {
479 // read a tag
480 // find the start
481                 while(position < length &&
482                         (input[position] == ' ' ||         // skip spaces
483                         input[position] == '\n' ||         // also skip new lines
484                         input[position] == left_delimiter))           // skip <
485                         position++;
487 // read the property description
488                 for(j = 0; 
489                         j < MAX_LENGTH &&
490                         position < length &&
491                         input[position] != right_delimiter &&
492                         input[position] != ' ' &&
493                         input[position] != '\n' &&      // also new line ends it
494                         input[position] != '=';
495                         j++, position++)
496                 {
497                         string[j] = input[position];
498                 }
499                 string[j] = 0;
502 // store the description in a property array
503                 tag_properties[total_properties] = new char[strlen(string) + 1];
504                 strcpy(tag_properties[total_properties], string);
506 // find the start of the value
507                 while(position < length &&
508                         (input[position] == ' ' ||         // skip spaces
509                         input[position] == '\n' ||         // also skip new lines
510                         input[position] == '='))           // skip =
511                         position++;
513 // find the terminating char
514                 if(position < length && input[position] == '\"')
515                 {
516                         terminating_char = '\"';     // use quotes to terminate
517                         if(position < length) position++;   // don't store the quote itself
518                 }
519                 else 
520                         terminating_char = ' ';         // use space to terminate
522 // read until the terminating char
523                 for(j = 0;
524                         j < MAX_LENGTH &&
525                         position < length &&
526                         input[position] != right_delimiter &&
527                         input[position] != '\n' &&
528                         input[position] != terminating_char;
529                         j++, position++)
530                 {
531                         string[j] = input[position];
532                 }
533                 string[j] = 0;
535 // store the value in a property array
536                 tag_property_values[total_properties] = new char[strlen(string) + 1];
537                 strcpy(tag_property_values[total_properties], string);
538                 
539 // advance property if one was just loaded
540                 if(tag_properties[total_properties][0] != 0) total_properties++;
542 // get the terminating char
543                 if(position < length && input[position] != right_delimiter) position++;
544         }
546 // skip the >
547         if(position < length && input[position] == right_delimiter) position++;
549         if(total_properties || tag_title[0]) 
550                 return 0; 
551         else 
552                 return 1;
553         return 0;
556 int XMLTag::title_is(char *title)
558         if(!strcasecmp(title, tag_title)) return 1;
559         else return 0;
562 char* XMLTag::get_title()
564         return tag_title;
567 int XMLTag::get_title(char *value)
569         if(tag_title[0] != 0) strcpy(value, tag_title);
570         return 0;
573 int XMLTag::test_property(char *property, char *value)
575         int i, result;
576         for(i = 0, result = 0; i < total_properties && !result; i++)
577         {
578                 if(!strcasecmp(tag_properties[i], property) && !strcasecmp(value, tag_property_values[i]))
579                 {
580                         return 1;
581                 }
582         }
583         return 0;
586 char* XMLTag::get_property(char *property, char *value)
588         int i, result;
589         for(i = 0, result = 0; i < total_properties && !result; i++)
590         {
591                 if(!strcasecmp(tag_properties[i], property))
592                 {
593 //printf("XMLTag::get_property %s %s\n", tag_properties[i], tag_property_values[i]);
594                         int j = 0, k = 0;
595                         char *tv = tag_property_values[i];
596                         while (j < strlen(tag_property_values[i])) {
597                                 if (!strncmp(tv + j,"&#034;",6)) {
598                                         value[k++] = '\"';
599                                         j += 6;
600                                 } else {
601                                         value[k++] = tv[j++];
602                                 }
603                         }
604                         value[k] = 0;
605                         result = 1;
606                 }
607         }
608         return value;
611 char* XMLTag::get_property_text(int number)
613         if(number < total_properties) 
614                 return tag_properties[number];
615         else
616                 return "";
619 int XMLTag::get_property_int(int number)
621         if(number < total_properties) 
622                 return atol(tag_properties[number]);
623         else
624                 return 0;
627 float XMLTag::get_property_float(int number)
629         if(number < total_properties) 
630                 return atof(tag_properties[number]);
631         else
632                 return 0;
635 char* XMLTag::get_property(char *property)
637         int i, result;
638         for(i = 0, result = 0; i < total_properties && !result; i++)
639         {
640                 if(!strcasecmp(tag_properties[i], property))
641                 {
642                         return tag_property_values[i];
643                 }
644         }
645         return 0;
649 int32_t XMLTag::get_property(char *property, int32_t default_)
651         temp_string[0] = 0;
652         get_property(property, temp_string);
653         if(temp_string[0] == 0) 
654                 return default_;
655         else 
656                 return atol(temp_string);
659 int64_t XMLTag::get_property(char *property, int64_t default_)
661         int64_t result;
662         temp_string[0] = 0;
663         get_property(property, temp_string);
664         if(temp_string[0] == 0) 
665                 result = default_;
666         else 
667         {
668                 sscanf(temp_string, "%lld", &result);
669         }
670         return result;
672 // 
673 // int XMLTag::get_property(char *property, int default_)
674 // {
675 //      temp_string[0] = 0;
676 //      get_property(property, temp_string);
677 //      if(temp_string[0] == 0) return default_;
678 //      else return atol(temp_string);
679 // }
680 // 
681 float XMLTag::get_property(char *property, float default_)
683         temp_string[0] = 0;
684         get_property(property, temp_string);
685         if(temp_string[0] == 0) 
686                 return default_;
687         else 
688                 return atof(temp_string);
691 double XMLTag::get_property(char *property, double default_)
693         temp_string[0] = 0;
694         get_property(property, temp_string);
695         if(temp_string[0] == 0) 
696                 return default_;
697         else 
698                 return atof(temp_string);
701 int XMLTag::set_title(char *text)       // set the title field
703         strcpy(tag_title, text);
704         return 0;
707 int XMLTag::set_property(char *text, int32_t value)
709         sprintf(temp_string, "%ld", value);
710         set_property(text, temp_string);
711         return 0;
714 int XMLTag::set_property(char *text, int64_t value)
716         sprintf(temp_string, "%lld", value);
717         set_property(text, temp_string);
718         return 0;
721 int XMLTag::set_property(char *text, float value)
723         if (value - (float)((int64_t)value) == 0)
724                 sprintf(temp_string, "%lld", (int64_t)value);
725         else
726                 sprintf(temp_string, "%.6e", value);
727         set_property(text, temp_string);
728         return 0;
731 int XMLTag::set_property(char *text, double value)
733         if (value - (double)((int64_t)value) == 0)
734                 sprintf(temp_string, "%lld", (int64_t)value);
735         else
736                 sprintf(temp_string, "%.16e", value);
737         set_property(text, temp_string);
738         return 0;
741 int XMLTag::set_property(char *text, char *value)
743         tag_properties[total_properties] = new char[strlen(text) + 1];
744         strcpy(tag_properties[total_properties], text);
746         // Count quotes
747         int qcount = 0;
748         for (int i = strlen(value)-1; i >= 0; i--)
749                 if (value[i] == '"')
750                         qcount++;
752         // Allocate space, and replace quotes with &#034;
753         tag_property_values[total_properties] = new char[strlen(value) + qcount*5 + 1];
754         int j = 0;
755         for (int i = 0; i < strlen(value); i++) {
756                 switch (value[i]){
757                 case '"':
758                         tag_property_values[total_properties][j] = 0;
759                         strcat(tag_property_values[total_properties],"&#034;");
760                         j += 6;
761                         break;
762                 default:
763                         tag_property_values[total_properties][j++] = value[i];
764                 }
765         }
766         tag_property_values[total_properties][j] = 0;
767         
768         total_properties++;
769         return 0;