allow ffmpeg video to resample curr_pos, add bluray format
[goodguy/history.git] / cinelerra-5.0 / cinelerra / filexml.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  */
21
22 #include <stdio.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "bcsignals.h"
28 #include "arraylist.h"
29 #include "cstrdup.h"
30 #include "format.inc"
31 #include "filexml.h"
32 #include "mainerror.h"
33
34 // messes up cutads link
35 #undef eprintf
36 #define eprintf printf
37
38 static const char left_delm = '<', right_delm = '>';
39
40 XMLBuffer::XMLBuffer(long buf_size, long max_size, int del)
41 {
42         bsz = buf_size;
43         bfr = new unsigned char[bsz];
44         inp = outp = bfr;
45         lmt = bfr + bsz;
46         isz = max_size;
47         destroy = del;
48 }
49
50 XMLBuffer::XMLBuffer(const char *buf, long buf_size, int del)
51 {       // reading
52         bfr = (unsigned char *)buf;
53         bsz = buf_size;
54         outp = bfr;
55         lmt = inp = bfr+bsz;
56         isz = bsz;
57         destroy = del;
58 }
59
60 XMLBuffer::XMLBuffer(long buf_size, const char *buf, int del)
61 {       // writing
62         bfr = (unsigned char *)buf;
63         bsz = buf_size;
64         inp = bfr;
65         lmt = outp = bfr+bsz;
66         isz = bsz;
67         destroy = del;
68 }
69
70 XMLBuffer::~XMLBuffer()
71 {
72         if( destroy ) delete [] bfr;
73 }
74
75 unsigned char *&XMLBuffer::demand(long len)
76 {
77         if( len > bsz ) {
78                 len += BCTEXTLEN;
79                 unsigned char *np = new unsigned char[len];
80                 if( inp > bfr ) memcpy(np,bfr,inp-bfr);
81                 inp = np + (inp-bfr);
82                 outp = np + (outp-bfr);
83                 lmt = np + len;  bsz = len;
84                 delete [] bfr;   bfr = np;
85         }
86         return bfr;
87 }
88
89 int XMLBuffer::write(const char *bp, int len)
90 {
91         if( !destroy && lmt-inp < len ) len = lmt-inp;
92         if( len <= 0 ) return 0;
93         demand(otell()+len);
94         memmove(inp,bp,len);
95         inp += len;
96         return len;
97 }
98
99 int XMLBuffer::read(char *bp, int len)
100 {
101         long size = inp - outp;
102         if( size <= 0 && len > 0 ) return -1;
103         if( len > size ) len = size;
104         memmove(bp,outp,len);
105         outp += len;
106         return len;
107 }
108
109
110 // Precision in base 10
111 // for float is 6 significant figures
112 // for double is 16 significant figures
113
114 XMLTag::Property::Property(const char *pp, const char *vp)
115 {
116 //printf("Property %s = %s\n",pp, vp);
117         prop = cstrdup(pp);
118         value = cstrdup(vp);
119 }
120
121 XMLTag::Property::~Property()
122 {
123         delete [] prop;
124         delete [] value;
125 }
126
127
128 XMLTag::XMLTag()
129 {
130         string = 0;
131         avail = used = 0;
132 }
133
134 XMLTag::~XMLTag()
135 {
136         properties.remove_all_objects();
137         delete [] string;
138 }
139
140 const char *XMLTag::get_property(const char *property, char *value)
141 {
142         int i = properties.size();
143         while( --i >= 0 && strcasecmp(properties[i]->prop, property) );
144         if( i >= 0 )
145                 strcpy(value, properties[i]->value);
146         else
147                 *value = 0;
148         return value;
149 }
150
151 //getters
152 const char *XMLTag::get_property_text(int i) {
153         return i < properties.size() ? properties[i]->prop : "";
154 }
155 int XMLTag::get_property_int(int i) {
156         return i < properties.size() ? atol(properties[i]->value) : 0;
157 }
158 float XMLTag::get_property_float(int i) {
159         return i < properties.size() ? atof(properties[i]->value) : 0.;
160 }
161 const char* XMLTag::get_property(const char *prop) {
162         for( int i=0; i<properties.size(); ++i ) {
163                 if( !strcasecmp(properties[i]->prop, prop) )
164                         return properties[i]->value;
165         }
166         return 0;
167 }
168 int32_t XMLTag::get_property(const char *prop, int32_t dflt) {
169         const char *cp = get_property(prop);
170         return !cp ? dflt : atol(cp);
171 }
172 int64_t XMLTag::get_property(const char *prop, int64_t dflt) {
173         const char *cp = get_property(prop);
174         return !cp ? dflt : strtoll(cp,0,0);
175 }
176 float XMLTag::get_property(const char *prop, float dflt) {
177         const char *cp = get_property(prop);
178         return !cp ? dflt : atof(cp);
179 }
180 double XMLTag::get_property(const char *prop, double dflt) {
181         const char *cp = get_property(prop);
182         return !cp ? dflt : atof(cp);
183 }
184
185 //setters
186 int XMLTag::set_title(const char *text) {
187         strcpy(title, text);
188         return 0;
189 }
190 int XMLTag::set_property(const char *text, const char *value) {
191         properties.append(new XMLTag::Property(text, value));
192         return 0;
193 }
194 int XMLTag::set_property(const char *text, int32_t value)
195 {
196         char text_value[BCSTRLEN];
197         sprintf(text_value, "%d", value);
198         set_property(text, text_value);
199         return 0;
200 }
201 int XMLTag::set_property(const char *text, int64_t value)
202 {
203         char text_value[BCSTRLEN];
204         sprintf(text_value, "" _LD "", value);
205         set_property(text, text_value);
206         return 0;
207 }
208 int XMLTag::set_property(const char *text, float value)
209 {
210         char text_value[BCSTRLEN];
211         if (value - (float)((int64_t)value) == 0)
212                 sprintf(text_value, "" _LD "", (int64_t)value);
213         else
214                 sprintf(text_value, "%.6e", value);
215         set_property(text, text_value);
216         return 0;
217 }
218 int XMLTag::set_property(const char *text, double value)
219 {
220         char text_value[BCSTRLEN];
221         if (value - (double)((int64_t)value) == 0)
222                 sprintf(text_value, "" _LD "", (int64_t)value);
223         else
224                 sprintf(text_value, "%.16e", value);
225         set_property(text, text_value);
226         return 0;
227 }
228
229
230 int XMLTag::reset_tag()
231 {
232         used = 0;
233         properties.remove_all_objects();
234         return 0;
235 }
236
237 int XMLTag::write_tag(FileXML *xml)
238 {
239         XMLBuffer *buf = xml->buffer;
240 // title header
241         buf->next(left_delm);
242         buf->write(title, strlen(title));
243
244 // properties
245         for( int i=0; i<properties.size(); ++i ) {
246                 const char *prop = properties[i]->prop;
247                 const char *value = properties[i]->value;
248                 int plen = strlen(prop), vlen = strlen(value);
249                 bool need_quotes = !vlen || strchr(value,' ');
250                 buf->next(' ');
251                 xml->append_text(prop, plen);
252                 buf->next('=');
253                 if( need_quotes ) buf->next('\"');
254                 xml->append_text(value, vlen);
255                 if( need_quotes ) buf->next('\"');
256         }
257
258         buf->next(right_delm);
259         return 0;
260 }
261
262 #define ERETURN(s) do { \
263   printf("XMLTag::read_tag:%d tag \"%s\"%s\n",__LINE__,title,s);\
264   return 1; } while(0)
265 #define EOB_RETURN(s) ERETURN(", end of buffer");
266
267 int XMLTag::read_tag(FileXML *xml)
268 {
269         XMLBuffer *buf = xml->buffer;
270         int len, term;
271         long prop_start, prop_end;
272         long value_start, value_end;
273         long ttl;
274         int ch = buf->next();
275         title[0] = 0;
276 // skip ws
277         while( ch>=0 && ws(ch) ) ch = buf->next();
278         if( ch < 0 ) EOB_RETURN();
279         
280 // read title
281         ttl = buf->itell() - 1;
282         for( int i=0; i<MAX_TITLE && ch>=0; ++i, ch=buf->next() ) {
283                 if( ch == right_delm || ch == '=' || ws(ch) ) break;
284         }
285         if( ch < 0 ) EOB_RETURN();
286         len = buf->itell()-1 - ttl;
287         if( len >= MAX_TITLE ) ERETURN(", title too long");
288 // if title
289         if( ch != '=' ) {
290                 memmove(title, buf->pos(ttl), len);
291                 title[len] = 0;
292         }
293 // no title but first property.
294         else {
295                 title[0] = 0;
296                 buf->iseek(ttl);
297                 ch = buf->next();
298         }
299 // read properties
300         while( ch >= 0 && ch != right_delm ) {
301 // find tag start, skip header leadin
302                 while( ch >= 0 && (ch==left_delm || ws(ch)) )
303                         ch = buf->next();
304 // find end of property name
305                 prop_start = buf->itell()-1;
306                 while( ch >= 0 && (ch!=right_delm && ch!='=' && !ws(ch)) )
307                         ch = buf->next();
308                 if( ch < 0 ) EOB_RETURN();
309                 prop_end = buf->itell()-1;
310 // skip ws = ws
311                 while( ch >= 0 && ws(ch) )
312                         ch = buf->next();
313                 if( ch == '=' ) ch = buf->next();
314                 while( ch >= 0 && ws(ch) )
315                         ch = buf->next();
316                 if( ch < 0 ) EOB_RETURN();
317 // set terminating char
318                 if( ch == '\"' ) {
319                         term = ch;
320                         ch = buf->next();
321                 }
322                 else
323                         term = ' ';
324                 value_start = buf->itell()-1;
325                 while( ch >= 0 && (ch!=term && ch!=right_delm && ch!='\n') )
326                         ch = buf->next();
327                 if( ch < 0 ) EOB_RETURN();
328                 value_end = buf->itell()-1;
329 // add property
330                 int plen = prop_end-prop_start;
331                 if( !plen ) continue;
332                 int vlen = value_end-value_start;
333                 char prop[plen+1], value[vlen+1];
334                 const char *coded_prop = (const char *)buf->pos(prop_start);
335                 const char *coded_value = (const char *)buf->pos(value_start);
336 // props should not have coded text
337                 memcpy(prop, coded_prop, plen);
338                 prop[plen] = 0;
339                 xml->decode(value, coded_value, vlen);
340                 if( prop_end > prop_start ) {
341                         Property *property = new Property(prop, value);
342                         properties.append(property);
343                 }
344 // skip the terminating char
345                 if( ch != right_delm ) ch = buf->next();
346         }
347         if( !properties.size() && !title[0] ) ERETURN(", emtpy");
348         return 0;
349 }
350
351
352
353 FileXML::FileXML(int coded)
354 {
355         output = 0;
356         output_length = 0;
357         buffer = new XMLBuffer();
358         set_coding(coded);
359 }
360
361 FileXML::~FileXML()
362 {
363         delete buffer;
364         delete [] output;
365 }
366
367
368 int FileXML::terminate_string()
369 {
370         append_data("", 1);
371         return 0;
372 }
373
374 int FileXML::rewind()
375 {
376         terminate_string();
377         buffer->iseek(0);
378         return 0;
379 }
380
381
382 int FileXML::append_newline()
383 {
384         append_data("\n", 1);
385         return 0;
386 }
387
388 int FileXML::append_tag()
389 {
390         tag.write_tag(this);
391         append_text(tag.string, tag.used);
392         tag.reset_tag();
393         return 0;
394 }
395
396 int FileXML::append_text(const char *text)
397 {
398         if( text != 0 )
399                 append_text(text, strlen(text));
400         return 0;
401 }
402
403 int FileXML::append_data(const char *text)
404 {
405         if( text != 0 )
406                 append_data(text, strlen(text));
407         return 0;
408 }
409
410 int FileXML::append_data(const char *text, long len)
411 {
412         if( text != 0 && len > 0 )
413                 buffer->write(text, len);
414         return 0;
415 }
416
417 int FileXML::append_text(const char *text, long len)
418 {
419         if( text != 0 && len > 0 ) {
420                 int size = coded_length(text, len);
421                 char coded_text[size+1];
422                 encode(coded_text, text, len);
423                 buffer->write(coded_text, size);
424         }
425         return 0;
426 }
427
428
429 char* FileXML::get_data()
430 {
431         long ofs = buffer->itell();
432         return (char *)buffer->pos(ofs);
433 }
434 char* FileXML::string()
435 {
436         return (char *)buffer->pos();
437 }
438
439 char* FileXML::read_text()
440 {
441         int ch = buffer->next();
442 // filter out first char is new line
443         if( ch == '\n' ) ch = buffer->next();
444         long ipos = buffer->itell()-1;
445 // scan for delimiter
446         while( ch >= 0 && ch != left_delm ) ch = buffer->next();
447         long pos = buffer->itell()-1;
448         if( ch >= 0 ) buffer->iseek(pos);
449         long len = pos - ipos;
450         if( len >= output_length ) {
451                 delete [] output;
452                 output_length = len+1;
453                 output = new char[output_length];
454         }
455         decode(output,(const char *)buffer->pos(ipos), len);
456         return output;
457 }
458
459 int FileXML::read_tag()
460 {
461         int ch = buffer->next();
462 // scan to next tag
463         while( ch >= 0 && ch != left_delm ) ch = buffer->next();
464         if( ch < 0 ) return 1;
465         tag.reset_tag();
466         return tag.read_tag(this);
467 }
468
469 int FileXML::read_data_until(const char *tag_end, char *out, int len)
470 {
471         long ipos = buffer->itell();
472         int opos = 0, pos = -1;
473         int ch = buffer->next();
474         for( int olen=len-1; ch>=0 && opos<olen; ch=buffer->next() ) {
475                 if( pos < 0 ) { // looking for next tag
476                         if( ch == left_delm ) {
477                                 ipos = buffer->itell()-1;
478                                 pos = 0;
479                         }
480                         else
481                                 out[opos++] = ch;
482                         continue;
483                 }
484                 // check for end of match
485                 if( !tag_end[pos] && ch == right_delm ) break;
486                 // if mismatched, copy prefix to out
487                 if( tag_end[pos] != ch ) {
488                         out[opos++] = left_delm;
489                         for( int i=0; i<pos && opos<olen; ++i )
490                                 out[opos++] = tag_end[i];
491                         if( opos < olen ) out[opos++] = ch;
492                         pos = -1;
493                         continue;
494                 }
495                 ++pos;
496         }
497 // if end tag is reached, pos is left on the < of the end tag
498         if( pos >= 0 && !tag_end[pos] )
499                 buffer->iseek(ipos);
500         return opos;
501 }
502
503 int FileXML::read_text_until(const char *tag_end, char *out, int len)
504 {
505         char data[len+1];
506         int opos = read_data_until(tag_end, data, len);
507         decode(out, data, opos);
508         return 0;
509 }
510
511 int FileXML::write_to_file(const char *filename)
512 {
513         strcpy(this->filename, filename);
514         FILE *out = fopen(filename, "wb");
515         if( !out ) {
516                 eprintf("write_to_file %d \"%s\": %m\n", __LINE__, filename);
517                 return 1;
518         }
519 // Position may have been rewound after storing
520         const char *str = string();
521         long len = strlen(str);
522         fprintf(out, "<?xml version=\"1.0\"?>\n");
523         if( len && !fwrite(str, len, 1, out) ) {
524                 eprintf("write_to_file %d \"%s\": %m\n", __LINE__, filename);
525                 fclose(out);
526                 return 1;
527         }
528         fclose(out);
529         return 0;
530 }
531
532 int FileXML::write_to_file(FILE *file)
533 {
534         strcpy(filename, "");
535         fprintf(file, "<?xml version=\"1.0\"?>\n");
536         const char *str = string();
537         long len = strlen(str);
538 // Position may have been rewound after storing
539         if( len && !fwrite(str, len, 1, file) ) {
540                 eprintf("\"%s\": %m\n", filename);
541                 return 1;
542         }
543         return 0;
544 }
545
546 int FileXML::read_from_file(const char *filename, int ignore_error)
547 {
548         
549         strcpy(this->filename, filename);
550         FILE *in = fopen(filename, "rb");
551         if( !in ) {
552                 if(!ignore_error) 
553                         eprintf("\"%s\" %m\n", filename);
554                 return 1;
555         }
556         fseek(in, 0, SEEK_END);
557         long length = ftell(in);
558         fseek(in, 0, SEEK_SET);
559         char *fbfr = new char[length+1];
560         delete buffer;
561         (void)fread(fbfr, length, 1, in);
562         fbfr[length] = 0;
563         buffer = new XMLBuffer(fbfr, length, 1);
564         fclose(in);
565         return 0;
566 }
567
568 int FileXML::read_from_string(char *string)
569 {
570         strcpy(this->filename, "");
571         long length = strlen(string);
572         char *sbfr = new char[length+1];
573         strcpy(sbfr, string);
574         delete buffer;
575         buffer = new XMLBuffer(sbfr, length, 1);
576         return 0;
577 }
578
579 void FileXML::set_coding(int coding)
580 {
581         coded = coding;
582         if( coded ) {
583                 decode = XMLBuffer::decode_data;
584                 encode = XMLBuffer::encode_data;
585                 coded_length = XMLBuffer::encoded_length;
586         }
587         else {
588                 decode = XMLBuffer::copy_data;
589                 encode = XMLBuffer::copy_data;
590                 coded_length = XMLBuffer::copy_length;
591         }
592 }
593
594 int FileXML::get_coding()
595 {
596         return coded;
597 }
598
599 int FileXML::set_shared_input(char *shared_string, long avail, int coded)
600 {
601         strcpy(this->filename, "");
602         delete buffer;
603         buffer = new XMLBuffer(shared_string, avail, 0);
604         set_coding(coded);
605         return 0;
606 }
607
608 int FileXML::set_shared_output(char *shared_string, long avail, int coded)
609 {
610         strcpy(this->filename, "");
611         delete buffer;
612         buffer = new XMLBuffer(avail, shared_string, 0);
613         set_coding(coded);
614         return 0;
615 }
616
617
618
619 // ================================ XML tag
620
621
622
623 int XMLTag::title_is(const char *tp)
624 {
625         return !strcasecmp(title, tp) ? 1 : 0;
626 }
627
628 char* XMLTag::get_title()
629 {
630         return title;
631 }
632
633 int XMLTag::get_title(char *value)
634 {
635         if( title[0] != 0 ) strcpy(value, title);
636         return 0;
637 }
638
639 int XMLTag::test_property(char *property, char *value)
640 {
641         for( int i=0; i<properties.size(); ++i ) {
642                 if( !strcasecmp(properties[i]->prop, property) &&
643                     !strcasecmp(properties[i]->value, value) )
644                         return 1;
645         }
646         return 0;
647 }
648
649 static inline int xml_cmp(const char *np, const char *sp)
650 {
651    const char *tp = ++np;
652    while( *np ) { if( *np != *sp ) return 0;  ++np; ++sp; }
653    return np - tp;
654 }
655
656 char *XMLBuffer::decode_data(char *bp, const char *sp, int n)
657 {
658    char *ret = bp;
659    if( n < 0 ) n = strlen(sp);
660    const char *ep = sp + n;
661    while( sp < ep ) {
662       if( (n=*sp++) != '&' ) { *bp++ = n; continue; }
663       switch( (n=*sp++) ) {
664       case 'a': // &amp;
665          if( (n=xml_cmp("amp;", sp)) ) { *bp++ = '&';  sp += n;  continue; }
666          break;
667       case 'g': // &gt;
668          if( (n=xml_cmp("gt;", sp)) ) { *bp++ = '>';  sp += n;  continue; }
669          break;
670       case 'l': // &lt;
671          if( (n=xml_cmp("lt;", sp)) ) { *bp++ = '<';  sp += n;  continue; }
672          break;
673       case 'q': // &quot;
674          if( (n=xml_cmp("quot;", sp)) ) { *bp++ = '"';  sp += n;  continue; }
675          break;
676       case '#': { // &#<num>;
677          char *cp = 0;  int v = strtoul(sp,&cp,10);
678          if( *cp == ';' ) { *bp++ = (char)v;  sp = cp+1;  continue; }
679          n = cp - sp; }
680          break;
681       default:
682          *bp++ = '&';
683          *bp++ = (char)n;
684          continue;
685       }
686       sp -= 2;  n += 2;
687       while( --n >= 0 ) *bp++ = *sp++;
688    }
689    *bp = 0;
690    return ret;
691 }
692
693
694 char *XMLBuffer::encode_data(char *bp, const char *sp, int n)
695 {
696         char *ret = bp;
697         if( n < 0 ) n = strlen(sp);
698         const char *cp, *ep = sp + n;
699         while( sp < ep ) {
700                 int ch = *sp++;
701                 switch( ch ) {
702                 case '<':  cp = "&lt;";    break;
703                 case '>':  cp = "&gt;";    break;
704                 case '&':  cp = "&amp;";   break;
705                 case '"':  cp = "&quot;";  break;
706                 default:  *bp++ = ch;      continue;
707                 }
708                 while( *cp != 0 ) *bp++ = *cp++;
709         }
710         *bp = 0;
711         return ret;
712 }
713
714 long XMLBuffer::encoded_length(const char *sp, int n)
715 {
716         long len = 0;
717         if( n < 0 ) n = strlen(sp);
718         const char *ep = sp + n;
719         while( sp < ep ) {
720                 int ch = *sp++;
721                 switch( ch ) {
722                 case '<':  len += 4;  break;
723                 case '>':  len += 4;  break;
724                 case '&':  len += 5;  break;
725                 case '"':  len += 6;  break;
726                 default:   ++len;     break;
727                 }
728         }
729         return len;
730 }
731
732 char *XMLBuffer::copy_data(char *bp, const char *sp, int n)
733 {
734         int len = n < 0 ? strlen(sp) : n;
735         memmove(bp,sp,len);
736         bp[len] = 0;
737         return bp;
738 }
739
740 long XMLBuffer::copy_length(const char *sp, int n)
741 {
742         int len = n < 0 ? strlen(sp) : n;
743         return len;
744 }
745