4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "bcsignals.h"
28 #include "arraylist.h"
32 #include "mainerror.h"
34 // messes up cutads link
36 #define eprintf printf
38 static const char left_delm = '<', right_delm = '>';
40 XMLBuffer::XMLBuffer(long buf_size, long max_size, int del)
43 bfr = new unsigned char[bsz];
50 XMLBuffer::XMLBuffer(const char *buf, long buf_size, int del)
52 bfr = (unsigned char *)buf;
60 XMLBuffer::XMLBuffer(long buf_size, const char *buf, int del)
62 bfr = (unsigned char *)buf;
70 XMLBuffer::~XMLBuffer()
72 if( destroy ) delete [] bfr;
75 unsigned char *&XMLBuffer::demand(long len)
79 unsigned char *np = new unsigned char[len];
80 if( inp > bfr ) memcpy(np,bfr,inp-bfr);
82 outp = np + (outp-bfr);
83 lmt = np + len; bsz = len;
84 delete [] bfr; bfr = np;
89 int XMLBuffer::write(const char *bp, int len)
91 if( !destroy && lmt-inp < len ) len = lmt-inp;
92 if( len <= 0 ) return 0;
99 int XMLBuffer::read(char *bp, int len)
101 long size = inp - outp;
102 if( size <= 0 && len > 0 ) return -1;
103 if( len > size ) len = size;
104 memmove(bp,outp,len);
110 // Precision in base 10
111 // for float is 6 significant figures
112 // for double is 16 significant figures
114 XMLTag::Property::Property(const char *pp, const char *vp)
116 //printf("Property %s = %s\n",pp, vp);
121 XMLTag::Property::~Property()
136 properties.remove_all_objects();
140 const char *XMLTag::get_property(const char *property, char *value)
142 int i = properties.size();
143 while( --i >= 0 && strcasecmp(properties[i]->prop, property) );
145 strcpy(value, properties[i]->value);
152 const char *XMLTag::get_property_text(int i) {
153 return i < properties.size() ? properties[i]->prop : "";
155 int XMLTag::get_property_int(int i) {
156 return i < properties.size() ? atol(properties[i]->value) : 0;
158 float XMLTag::get_property_float(int i) {
159 return i < properties.size() ? atof(properties[i]->value) : 0.;
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;
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);
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);
176 float XMLTag::get_property(const char *prop, float dflt) {
177 const char *cp = get_property(prop);
178 return !cp ? dflt : atof(cp);
180 double XMLTag::get_property(const char *prop, double dflt) {
181 const char *cp = get_property(prop);
182 return !cp ? dflt : atof(cp);
186 int XMLTag::set_title(const char *text) {
190 int XMLTag::set_property(const char *text, const char *value) {
191 properties.append(new XMLTag::Property(text, value));
194 int XMLTag::set_property(const char *text, int32_t value)
196 char text_value[BCSTRLEN];
197 sprintf(text_value, "%d", value);
198 set_property(text, text_value);
201 int XMLTag::set_property(const char *text, int64_t value)
203 char text_value[BCSTRLEN];
204 sprintf(text_value, "" _LD "", value);
205 set_property(text, text_value);
208 int XMLTag::set_property(const char *text, float value)
210 char text_value[BCSTRLEN];
211 if (value - (float)((int64_t)value) == 0)
212 sprintf(text_value, "" _LD "", (int64_t)value);
214 sprintf(text_value, "%.6e", value);
215 set_property(text, text_value);
218 int XMLTag::set_property(const char *text, double value)
220 char text_value[BCSTRLEN];
221 if (value - (double)((int64_t)value) == 0)
222 sprintf(text_value, "" _LD "", (int64_t)value);
224 sprintf(text_value, "%.16e", value);
225 set_property(text, text_value);
230 int XMLTag::reset_tag()
233 properties.remove_all_objects();
237 int XMLTag::write_tag(FileXML *xml)
239 XMLBuffer *buf = xml->buffer;
241 buf->next(left_delm);
242 buf->write(title, strlen(title));
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,' ');
251 xml->append_text(prop, plen);
253 if( need_quotes ) buf->next('\"');
254 xml->append_text(value, vlen);
255 if( need_quotes ) buf->next('\"');
258 buf->next(right_delm);
262 #define ERETURN(s) do { \
263 printf("XMLTag::read_tag:%d tag \"%s\"%s\n",__LINE__,title,s);\
265 #define EOB_RETURN(s) ERETURN(", end of buffer");
267 int XMLTag::read_tag(FileXML *xml)
269 XMLBuffer *buf = xml->buffer;
271 long prop_start, prop_end;
272 long value_start, value_end;
274 int ch = buf->next();
277 while( ch>=0 && ws(ch) ) ch = buf->next();
278 if( ch < 0 ) EOB_RETURN();
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;
285 if( ch < 0 ) EOB_RETURN();
286 len = buf->itell()-1 - ttl;
287 if( len >= MAX_TITLE ) ERETURN(", title too long");
290 memmove(title, buf->pos(ttl), len);
293 // no title but first property.
300 while( ch >= 0 && ch != right_delm ) {
301 // find tag start, skip header leadin
302 while( ch >= 0 && (ch==left_delm || ws(ch)) )
304 // find end of property name
305 prop_start = buf->itell()-1;
306 while( ch >= 0 && (ch!=right_delm && ch!='=' && !ws(ch)) )
308 if( ch < 0 ) EOB_RETURN();
309 prop_end = buf->itell()-1;
311 while( ch >= 0 && ws(ch) )
313 if( ch == '=' ) ch = buf->next();
314 while( ch >= 0 && ws(ch) )
316 if( ch < 0 ) EOB_RETURN();
317 // set terminating char
324 value_start = buf->itell()-1;
325 while( ch >= 0 && (ch!=term && ch!=right_delm && ch!='\n') )
327 if( ch < 0 ) EOB_RETURN();
328 value_end = buf->itell()-1;
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);
339 xml->decode(value, coded_value, vlen);
340 if( prop_end > prop_start ) {
341 Property *property = new Property(prop, value);
342 properties.append(property);
344 // skip the terminating char
345 if( ch != right_delm ) ch = buf->next();
347 if( !properties.size() && !title[0] ) ERETURN(", emtpy");
353 FileXML::FileXML(int coded)
357 buffer = new XMLBuffer();
368 int FileXML::terminate_string()
374 int FileXML::rewind()
382 int FileXML::append_newline()
384 append_data("\n", 1);
388 int FileXML::append_tag()
391 append_text(tag.string, tag.used);
396 int FileXML::append_text(const char *text)
399 append_text(text, strlen(text));
403 int FileXML::append_data(const char *text)
406 append_data(text, strlen(text));
410 int FileXML::append_data(const char *text, long len)
412 if( text != 0 && len > 0 )
413 buffer->write(text, len);
417 int FileXML::append_text(const char *text, long len)
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);
429 char* FileXML::get_data()
431 long ofs = buffer->itell();
432 return (char *)buffer->pos(ofs);
434 char* FileXML::string()
436 return (char *)buffer->pos();
439 char* FileXML::read_text()
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 ) {
452 output_length = len+1;
453 output = new char[output_length];
455 decode(output,(const char *)buffer->pos(ipos), len);
459 int FileXML::read_tag()
461 int ch = buffer->next();
463 while( ch >= 0 && ch != left_delm ) ch = buffer->next();
464 if( ch < 0 ) return 1;
466 return tag.read_tag(this);
469 int FileXML::read_data_until(const char *tag_end, char *out, int len)
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;
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;
497 // if end tag is reached, pos is left on the < of the end tag
498 if( pos >= 0 && !tag_end[pos] )
503 int FileXML::read_text_until(const char *tag_end, char *out, int len)
506 int opos = read_data_until(tag_end, data, len);
507 decode(out, data, opos);
511 int FileXML::write_to_file(const char *filename)
513 strcpy(this->filename, filename);
514 FILE *out = fopen(filename, "wb");
516 eprintf("write_to_file %d \"%s\": %m\n", __LINE__, filename);
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);
532 int FileXML::write_to_file(FILE *file)
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);
546 int FileXML::read_from_file(const char *filename, int ignore_error)
549 strcpy(this->filename, filename);
550 FILE *in = fopen(filename, "rb");
553 eprintf("\"%s\" %m\n", filename);
556 fseek(in, 0, SEEK_END);
557 long length = ftell(in);
558 fseek(in, 0, SEEK_SET);
559 char *fbfr = new char[length+1];
561 (void)fread(fbfr, length, 1, in);
563 buffer = new XMLBuffer(fbfr, length, 1);
568 int FileXML::read_from_string(char *string)
570 strcpy(this->filename, "");
571 long length = strlen(string);
572 char *sbfr = new char[length+1];
573 strcpy(sbfr, string);
575 buffer = new XMLBuffer(sbfr, length, 1);
579 void FileXML::set_coding(int coding)
583 decode = XMLBuffer::decode_data;
584 encode = XMLBuffer::encode_data;
585 coded_length = XMLBuffer::encoded_length;
588 decode = XMLBuffer::copy_data;
589 encode = XMLBuffer::copy_data;
590 coded_length = XMLBuffer::copy_length;
594 int FileXML::get_coding()
599 int FileXML::set_shared_input(char *shared_string, long avail, int coded)
601 strcpy(this->filename, "");
603 buffer = new XMLBuffer(shared_string, avail, 0);
608 int FileXML::set_shared_output(char *shared_string, long avail, int coded)
610 strcpy(this->filename, "");
612 buffer = new XMLBuffer(avail, shared_string, 0);
619 // ================================ XML tag
623 int XMLTag::title_is(const char *tp)
625 return !strcasecmp(title, tp) ? 1 : 0;
628 char* XMLTag::get_title()
633 int XMLTag::get_title(char *value)
635 if( title[0] != 0 ) strcpy(value, title);
639 int XMLTag::test_property(char *property, char *value)
641 for( int i=0; i<properties.size(); ++i ) {
642 if( !strcasecmp(properties[i]->prop, property) &&
643 !strcasecmp(properties[i]->value, value) )
649 static inline int xml_cmp(const char *np, const char *sp)
651 const char *tp = ++np;
652 while( *np ) { if( *np != *sp ) return 0; ++np; ++sp; }
656 char *XMLBuffer::decode_data(char *bp, const char *sp, int n)
659 if( n < 0 ) n = strlen(sp);
660 const char *ep = sp + n;
662 if( (n=*sp++) != '&' ) { *bp++ = n; continue; }
663 switch( (n=*sp++) ) {
665 if( (n=xml_cmp("amp;", sp)) ) { *bp++ = '&'; sp += n; continue; }
668 if( (n=xml_cmp("gt;", sp)) ) { *bp++ = '>'; sp += n; continue; }
671 if( (n=xml_cmp("lt;", sp)) ) { *bp++ = '<'; sp += n; continue; }
674 if( (n=xml_cmp("quot;", sp)) ) { *bp++ = '"'; sp += n; continue; }
676 case '#': { // &#<num>;
677 char *cp = 0; int v = strtoul(sp,&cp,10);
678 if( *cp == ';' ) { *bp++ = (char)v; sp = cp+1; continue; }
687 while( --n >= 0 ) *bp++ = *sp++;
694 char *XMLBuffer::encode_data(char *bp, const char *sp, int n)
697 if( n < 0 ) n = strlen(sp);
698 const char *cp, *ep = sp + n;
702 case '<': cp = "<"; break;
703 case '>': cp = ">"; break;
704 case '&': cp = "&"; break;
705 case '"': cp = """; break;
706 default: *bp++ = ch; continue;
708 while( *cp != 0 ) *bp++ = *cp++;
714 long XMLBuffer::encoded_length(const char *sp, int n)
717 if( n < 0 ) n = strlen(sp);
718 const char *ep = sp + n;
722 case '<': len += 4; break;
723 case '>': len += 4; break;
724 case '&': len += 5; break;
725 case '"': len += 6; break;
726 default: ++len; break;
732 char *XMLBuffer::copy_data(char *bp, const char *sp, int n)
734 int len = n < 0 ? strlen(sp) : n;
740 long XMLBuffer::copy_length(const char *sp, int n)
742 int len = n < 0 ? strlen(sp) : n;