r870: Merge 2.1:
[cinelerra_cv.git] / cinelerra / filexml.C
blob01a1bf3172f40470cd43f402cc392cc92e4e397c
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"
13 // Precision in base 10
14 // for float is 6 significant figures
15 // for double is 16 significant figures
19 FileXML::FileXML(char left_delimiter, char right_delimiter)
21         tag.set_delimiters(left_delimiter, right_delimiter);
22         this->left_delimiter = left_delimiter;
23         this->right_delimiter = right_delimiter;
24         available = 64;
25         string = new char[available];
26         string[0] = 0;
27         position = length = 0;
28         output_length = 0;
29         share_string = 0;
32 FileXML::~FileXML()
34         if(!share_string) delete [] string;
35         if(output_length) delete [] output;
38 void FileXML::dump()
40         printf("FileXML::dump:\n%s\n", string);
43 int FileXML::terminate_string()
45         append_text("", 1);
46         return 0;
49 int FileXML::rewind()
51         terminate_string();
52         length = strlen(string);
53         position = 0;
54         return 0;
58 int FileXML::append_newline()
60         append_text("\n", 1);
61         return 0;
64 int FileXML::append_tag()
66         tag.write_tag();
67         append_text(tag.string, tag.len);
68         tag.reset_tag();
69         return 0;
72 int FileXML::append_text(char *text)
74         append_text(text, strlen(text));
75         return 0;
78 int FileXML::append_text(char *text, long len)
80         while(position + len > available)
81         {
82                 reallocate_string(available * 2);
83         }
85         for(int i = 0; i < len; i++, position++)
86         {
87                 string[position] = text[i];
88         }
89         return 0;
92 int FileXML::encode_text(char *text)
94 // We have to encode at least the '<' char
95 // We encode three things:
96 // '<' -> '&lt;' 
97 // '>' -> '&gt;'
98 // '&' -> '&amp;'
99         char leftb[] = "&lt;";
100         char rightb[] = "&gt;";
101         char amp[] = "&amp;";
102         char *replacement;
103         int len = strlen(text);
104         int lastpos = 0;
105         for (int i = 0; i < len; i++)
106         {
107                 switch (text[i]) {
108                         case '<': replacement = leftb; break;
109                         case '>': replacement = rightb; break;
110                         case '&': replacement = amp; break;
111                         default: replacement = 0; break;
112                 }
113                 if (replacement)
114                 {
115                         if (i - lastpos > 0)
116                                 append_text(text + lastpos, i - lastpos);
117                         append_text(replacement, strlen(replacement));
118                         lastpos = i + 1;
119                 }
120         }
121         append_text(text + lastpos, len - lastpos);
122         return 0;
127 int FileXML::reallocate_string(long new_available)
129         if(!share_string)
130         {
131                 char *new_string = new char[new_available];
132                 for(int i = 0; i < position; i++) new_string[i] = string[i];
133                 available = new_available;
134                 delete [] string;
135                 string = new_string;
136         }
137         return 0;
140 char* FileXML::read_text()
142         long text_position = position;
143         int i;
145 // use < to mark end of text and start of tag
147 // find end of text
148         for(; position < length && string[position] != left_delimiter; position++)
149         {
150                 ;
151         }
153 // allocate enough space
154         if(output_length) delete [] output;
155         output_length = position - text_position;
156         output = new char[output_length + 1];
158 //printf("FileXML::read_text %d %c\n", text_position, string[text_position]);
159         for(i = 0; text_position < position; text_position++)
160         {
161 // filter out first newline
162                 if((i > 0 && i < output_length - 1) || string[text_position] != '\n') 
163                 {
164 // check if we have to decode special characters
165 // but try to be most backward compatible possible
166                         int character = string[text_position];
167                         if (string[text_position] == '&')
168                         {
169                                 if (text_position + 3 < length)
170                                 {
171                                         if (string[text_position + 1] == 'l' && string[text_position + 2] == 't' && string[text_position + 3] == ';')
172                                         {
173                                                 character = '<';
174                                                 text_position += 3;
175                                         }               
176                                         if (string[text_position + 1] == 'g' && string[text_position + 2] == 't' && string[text_position + 3] == ';')
177                                         {
178                                                 character = '>';
179                                                 text_position += 3;
180                                         }               
181                                 }
182                                 if (text_position + 4 < length)
183                                 {
184                                         if (string[text_position + 1] == 'a' && string[text_position + 2] == 'm' && string[text_position + 3] == 'p' && string[text_position + 4] == ';')
185                                         {
186                                                 character = '&';
187                                                 text_position += 4;
188                                         }               
189                                 }
190                         }
191                         output[i] = character;
192                         i++;
193                 }
194         }
195         output[i] = 0;
197         return output;
200 int FileXML::read_tag()
202 // scan to next tag
203         while(position < length && string[position] != left_delimiter)
204         {
205                 position++;
206         }
207         tag.reset_tag();
208         if(position >= length) return 1;
209 //printf("FileXML::read_tag %s\n", &string[position]);
210         return tag.read_tag(string, position, length);
213 int FileXML::read_text_until(char *tag_end, char *output, int max_len)
215 // read to next tag
216         int out_position = 0;
217         int test_position1, test_position2;
218         int result = 0;
219         
220         while(!result && position < length && out_position < max_len - 1)
221         {
222                 while(position < length && string[position] != left_delimiter)
223                 {
224 //printf("FileXML::read_text_until 1 %c\n", string[position]);
225                         output[out_position++] = string[position++];
226                 }
227                 
228                 if(position < length && string[position] == left_delimiter)
229                 {
230 // tag reached
231 // test for tag_end
232                         result = 1;         // assume end
233                         
234                         for(test_position1 = 0, test_position2 = position + 1;   // skip < 
235                                 test_position2 < length &&
236                                 tag_end[test_position1] != 0 &&
237                                 result; 
238                                 test_position1++, test_position2++)
239                         {
240 // null result when first wrong character is reached
241 //printf("FileXML::read_text_until 2 %c\n", string[test_position2]);
242                                 if(tag_end[test_position1] != string[test_position2]) result = 0;
243                         }
245 // no end tag reached to copy <
246                         if(!result)
247                         {
248                                 output[out_position++] = string[position++];
249                         }
250                 }
251         }
252         output[out_position] = 0;
253 // if end tag is reached, position is left on the < of the end tag
254         return 0;
258 int FileXML::write_to_file(char *filename)
260         FILE *out;
261         strcpy(this->filename, filename);
262         if(out = fopen(filename, "wb"))
263         {
264                 fprintf(out, "<?xml version=\"1.0\"?>\n");
265 // Position may have been rewound after storing so we use a strlen
266                 if(!fwrite(string, strlen(string), 1, out) && strlen(string))
267                 {
268                         fprintf(stderr, "FileXML::write_to_file 1 \"%s\": %s\n",
269                                 filename,
270                                 strerror(errno));
271                         fclose(out);
272                         return 1;
273                 }
274                 else
275                 {
276                 }
277         }
278         else
279         {
280                 fprintf(stderr, "FileXML::write_to_file 2 \"%s\": %s\n",
281                         filename,
282                         strerror(errno));
283                 return 1;
284         }
285         fclose(out);
286         return 0;
289 int FileXML::write_to_file(FILE *file)
291         strcpy(filename, "");
292         fprintf(file, "<?xml version=\"1.0\"?>\n");
293 // Position may have been rewound after storing
294         if(fwrite(string, strlen(string), 1, file) || !strlen(string))
295         {
296                 return 0;
297         }
298         else
299         {
300                 fprintf(stderr, "FileXML::write_to_file \"%s\": %s\n",
301                         filename,
302                         strerror(errno));
303                 return 1;
304         }
305         return 0;
308 int FileXML::read_from_file(char *filename, int ignore_error)
310         FILE *in;
311         
312         strcpy(this->filename, filename);
313         if(in = fopen(filename, "rb"))
314         {
315                 fseek(in, 0, SEEK_END);
316                 int new_length = ftell(in);
317                 fseek(in, 0, SEEK_SET);
318                 reallocate_string(new_length + 1);
319                 fread(string, new_length, 1, in);
320                 string[new_length] = 0;
321                 position = 0;
322                 length = new_length;
323         }
324         else
325         {
326                 if(!ignore_error) 
327                         fprintf(stderr, "FileXML::read_from_file \"%s\" %s\n",
328                                 filename,
329                                 strerror(errno));
330                 return 1;
331         }
332         fclose(in);
333         return 0;
336 int FileXML::read_from_string(char *string)
338         strcpy(this->filename, "");
339         reallocate_string(strlen(string) + 1);
340         strcpy(this->string, string);
341         length = strlen(string);
342         position = 0;
343         return 0;
346 int FileXML::set_shared_string(char *shared_string, long available)
348         strcpy(this->filename, "");
349         if(!share_string)
350         {
351                 delete [] string;
352                 share_string = 1;
353                 string = shared_string;
354                 this->available = available;
355                 length = available;
356                 position = 0;
357         }
358         return 0;
363 // ================================ XML tag
366 XMLTag::XMLTag()
368         total_properties = 0;
369         len = 0;
372 XMLTag::~XMLTag()
374         reset_tag();
377 int XMLTag::set_delimiters(char left_delimiter, char right_delimiter)
379         this->left_delimiter = left_delimiter;
380         this->right_delimiter = right_delimiter;
381         return 0;
384 int XMLTag::reset_tag()     // clear all structures
386         len = 0;
387         for(int i = 0; i < total_properties; i++) delete [] tag_properties[i];
388         for(int i = 0; i < total_properties; i++) delete [] tag_property_values[i];
389         total_properties = 0;
390         return 0;
393 int XMLTag::write_tag()
395         int i, j;
396         char *current_property, *current_value;
398 // opening bracket
399         string[len] = left_delimiter;        
400         len++;
401         
402 // title
403         for(i = 0; tag_title[i] != 0 && len < MAX_LENGTH; i++, len++) string[len] = tag_title[i];
405 // properties
406         for(i = 0; i < total_properties && len < MAX_LENGTH; i++)
407         {
408                 string[len++] = ' ';         // add a space before every property
409                 
410                 current_property = tag_properties[i];
412 // property title
413                 for(j = 0; current_property[j] != 0 && len < MAX_LENGTH; j++, len++)
414                 {
415                         string[len] = current_property[j];
416                 }
417                 
418                 if(len < MAX_LENGTH) string[len++] = '=';
419                 
420                 current_value = tag_property_values[i];
422 // property value
423                 if( len < MAX_LENGTH) string[len++] = '\"';
424 // write the value
425                 for(j = 0; current_value[j] != 0 && len < MAX_LENGTH; j++, len++)
426                 {
427                         string[len] = current_value[j];
428                 }
429                 if(len < MAX_LENGTH) string[len++] = '\"';
430         }     // next property
431         
432         if(len < MAX_LENGTH) string[len++] = right_delimiter;   // terminating bracket
433         return 0;
436 int XMLTag::read_tag(char *input, long &position, long length)
438         long tag_start;
439         int i, j, terminating_char;
441 // search for beginning of a tag
442         while(input[position] != left_delimiter && position < length) position++;
443         
444         if(position >= length) return 1;
446 // find the start
447         while(position < length &&
448                 (input[position] == ' ' ||         // skip spaces
449                 input[position] == '\n' ||       // also skip new lines
450                 input[position] == left_delimiter))           // skip <
451                 position++;
453         if(position >= length) return 1;
454         
455         tag_start = position;
456         
457 // read title
458         for(i = 0; 
459                 i < MAX_TITLE && 
460                 position < length && 
461                 input[position] != '=' && 
462                 input[position] != ' ' &&       // space ends title
463                 input[position] != right_delimiter;
464                 position++, i++)
465         {
466                 tag_title[i] = input[position];
467         }
468         tag_title[i] = 0;
469         
470         if(position >= length) return 1;
471         
472         if(input[position] == '=')
473         {
474 // no title but first property
475                 tag_title[0] = 0;
476                 position = tag_start;       // rewind
477         }
479 // read properties
480         for(i = 0;
481                 i < MAX_PROPERTIES &&
482                 position < length &&
483                 input[position] != right_delimiter;
484                 i++)
485         {
486 // read a tag
487 // find the start
488                 while(position < length &&
489                         (input[position] == ' ' ||         // skip spaces
490                         input[position] == '\n' ||         // also skip new lines
491                         input[position] == left_delimiter))           // skip <
492                         position++;
494 // read the property description
495                 for(j = 0; 
496                         j < MAX_LENGTH &&
497                         position < length &&
498                         input[position] != right_delimiter &&
499                         input[position] != ' ' &&
500                         input[position] != '\n' &&      // also new line ends it
501                         input[position] != '=';
502                         j++, position++)
503                 {
504                         string[j] = input[position];
505                 }
506                 string[j] = 0;
509 // store the description in a property array
510                 tag_properties[total_properties] = new char[strlen(string) + 1];
511                 strcpy(tag_properties[total_properties], string);
513 // find the start of the value
514                 while(position < length &&
515                         (input[position] == ' ' ||         // skip spaces
516                         input[position] == '\n' ||         // also skip new lines
517                         input[position] == '='))           // skip =
518                         position++;
520 // find the terminating char
521                 if(position < length && input[position] == '\"')
522                 {
523                         terminating_char = '\"';     // use quotes to terminate
524                         if(position < length) position++;   // don't store the quote itself
525                 }
526                 else 
527                         terminating_char = ' ';         // use space to terminate
529 // read until the terminating char
530                 for(j = 0;
531                         j < MAX_LENGTH &&
532                         position < length &&
533                         input[position] != right_delimiter &&
534                         input[position] != '\n' &&
535                         input[position] != terminating_char;
536                         j++, position++)
537                 {
538                         string[j] = input[position];
539                 }
540                 string[j] = 0;
542 // store the value in a property array
543                 tag_property_values[total_properties] = new char[strlen(string) + 1];
544                 strcpy(tag_property_values[total_properties], string);
545                 
546 // advance property if one was just loaded
547                 if(tag_properties[total_properties][0] != 0) total_properties++;
549 // get the terminating char
550                 if(position < length && input[position] != right_delimiter) position++;
551         }
553 // skip the >
554         if(position < length && input[position] == right_delimiter) position++;
556         if(total_properties || tag_title[0]) 
557                 return 0; 
558         else 
559                 return 1;
560         return 0;
563 int XMLTag::title_is(char *title)
565         if(!strcasecmp(title, tag_title)) return 1;
566         else return 0;
569 char* XMLTag::get_title()
571         return tag_title;
574 int XMLTag::get_title(char *value)
576         if(tag_title[0] != 0) strcpy(value, tag_title);
577         return 0;
580 int XMLTag::test_property(char *property, char *value)
582         int i, result;
583         for(i = 0, result = 0; i < total_properties && !result; i++)
584         {
585                 if(!strcasecmp(tag_properties[i], property) && !strcasecmp(value, tag_property_values[i]))
586                 {
587                         return 1;
588                 }
589         }
590         return 0;
593 char* XMLTag::get_property(char *property, char *value)
595         int i, result;
596         for(i = 0, result = 0; i < total_properties && !result; i++)
597         {
598                 if(!strcasecmp(tag_properties[i], property))
599                 {
600 //printf("XMLTag::get_property %s %s\n", tag_properties[i], tag_property_values[i]);
601                         int j = 0, k = 0;
602                         char *tv = tag_property_values[i];
603                         while (j < strlen(tag_property_values[i])) {
604                                 if (!strncmp(tv + j,"&#034;",6)) {
605                                         value[k++] = '\"';
606                                         j += 6;
607                                 } else {
608                                         value[k++] = tv[j++];
609                                 }
610                         }
611                         value[k] = 0;
612                         result = 1;
613                 }
614         }
615         return value;
618 char* XMLTag::get_property_text(int number)
620         if(number < total_properties) 
621                 return tag_properties[number];
622         else
623                 return "";
626 int XMLTag::get_property_int(int number)
628         if(number < total_properties) 
629                 return atol(tag_properties[number]);
630         else
631                 return 0;
634 float XMLTag::get_property_float(int number)
636         if(number < total_properties) 
637                 return atof(tag_properties[number]);
638         else
639                 return 0;
642 char* XMLTag::get_property(char *property)
644         int i, result;
645         for(i = 0, result = 0; i < total_properties && !result; i++)
646         {
647                 if(!strcasecmp(tag_properties[i], property))
648                 {
649                         return tag_property_values[i];
650                 }
651         }
652         return 0;
656 int32_t XMLTag::get_property(char *property, int32_t default_)
658         temp_string[0] = 0;
659         get_property(property, temp_string);
660         if(temp_string[0] == 0) 
661                 return default_;
662         else 
663                 return atol(temp_string);
666 int64_t XMLTag::get_property(char *property, int64_t default_)
668         int64_t result;
669         temp_string[0] = 0;
670         get_property(property, temp_string);
671         if(temp_string[0] == 0) 
672                 result = default_;
673         else 
674         {
675                 sscanf(temp_string, "%lld", &result);
676         }
677         return result;
679 // 
680 // int XMLTag::get_property(char *property, int default_)
681 // {
682 //      temp_string[0] = 0;
683 //      get_property(property, temp_string);
684 //      if(temp_string[0] == 0) return default_;
685 //      else return atol(temp_string);
686 // }
687 // 
688 float XMLTag::get_property(char *property, float default_)
690         temp_string[0] = 0;
691         get_property(property, temp_string);
692         if(temp_string[0] == 0) 
693                 return default_;
694         else 
695                 return atof(temp_string);
698 double XMLTag::get_property(char *property, double default_)
700         temp_string[0] = 0;
701         get_property(property, temp_string);
702         if(temp_string[0] == 0) 
703                 return default_;
704         else 
705                 return atof(temp_string);
708 int XMLTag::set_title(char *text)       // set the title field
710         strcpy(tag_title, text);
711         return 0;
714 int XMLTag::set_property(char *text, int32_t value)
716         sprintf(temp_string, "%ld", value);
717         set_property(text, temp_string);
718         return 0;
721 int XMLTag::set_property(char *text, int64_t value)
723         sprintf(temp_string, "%lld", value);
724         set_property(text, temp_string);
725         return 0;
728 int XMLTag::set_property(char *text, float value)
730         if (value - (float)((int64_t)value) == 0)
731                 sprintf(temp_string, "%lld", (int64_t)value);
732         else
733                 sprintf(temp_string, "%.6e", value);
734         set_property(text, temp_string);
735         return 0;
738 int XMLTag::set_property(char *text, double value)
740         if (value - (double)((int64_t)value) == 0)
741                 sprintf(temp_string, "%lld", (int64_t)value);
742         else
743                 sprintf(temp_string, "%.16e", value);
744         set_property(text, temp_string);
745         return 0;
748 int XMLTag::set_property(char *text, char *value)
750         tag_properties[total_properties] = new char[strlen(text) + 1];
751         strcpy(tag_properties[total_properties], text);
753         // Count quotes
754         int qcount = 0;
755         for (int i = strlen(value)-1; i >= 0; i--)
756                 if (value[i] == '"')
757                         qcount++;
759         // Allocate space, and replace quotes with &#034;
760         tag_property_values[total_properties] = new char[strlen(value) + qcount*5 + 1];
761         int j = 0;
762         for (int i = 0; i < strlen(value); i++) {
763                 switch (value[i]){
764                 case '"':
765                         tag_property_values[total_properties][j] = 0;
766                         strcat(tag_property_values[total_properties],"&#034;");
767                         j += 6;
768                         break;
769                 default:
770                         tag_property_values[total_properties][j++] = value[i];
771                 }
772         }
773         tag_property_values[total_properties][j] = 0;
774         
775         total_properties++;
776         return 0;