]> www.fi.muni.cz Git - evince.git/blob - backend/impress/iksemel.c
Bump api and libtool versions and rename libview and libdocument libraries
[evince.git] / backend / impress / iksemel.c
1 /* iksemel (XML parser for Jabber)
2 ** Copyright (C) 2000-2003 Gurer Ozen <madcat@e-kolay.net>
3 ** This code is free software; you can redistribute it and/or
4 ** modify it under the terms of GNU Lesser General Public License.
5 */
6
7 /* minimum sax buffer size */
8 #define SAX_BUFFER_MIN_SIZE 128
9
10 /* sax parser structure plus extra data of dom parser */
11 #define DEFAULT_DOM_CHUNK_SIZE 256
12
13 /* sax parser structure plus extra data of stream parser */
14 #define DEFAULT_STREAM_CHUNK_SIZE 256
15
16 /* iks structure, its data, child iks structures, for stream parsing */
17 #define DEFAULT_IKS_CHUNK_SIZE 1024
18
19 /* iks structure, its data, child iks structures, for file parsing */
20 #define DEFAULT_DOM_IKS_CHUNK_SIZE 2048
21
22 /* rule structure and from/to/id/ns strings */
23 #define DEFAULT_RULE_CHUNK_SIZE 128
24
25 /* file is read by blocks with this size */
26 #define FILE_IO_BUF_SIZE 4096
27
28 /* network receive buffer */
29 #define NET_IO_BUF_SIZE 4096
30 /* iksemel (XML parser for Jabber)
31 ** Copyright (C) 2000-2003 Gurer Ozen <madcat@e-kolay.net>
32 ** This code is free software; you can redistribute it and/or
33 ** modify it under the terms of GNU Lesser General Public License.
34 */
35
36 #include <config.h>
37 #include <errno.h>
38
39 #include "common.h"
40 #include "iksemel.h"
41
42 /*****  malloc wrapper  *****/
43
44 static void *(*my_malloc_func)(size_t size);
45 static void (*my_free_func)(void *ptr);
46
47 void *
48 iks_malloc (size_t size)
49 {
50         if (my_malloc_func)
51                 return my_malloc_func (size);
52         else
53                 return malloc (size);
54 }
55
56 void
57 iks_free (void *ptr)
58 {
59         if (my_free_func)
60                 my_free_func (ptr);
61         else
62                 free (ptr);
63 }
64
65 void
66 iks_set_mem_funcs (void *(*malloc_func)(size_t size), void (*free_func)(void *ptr))
67 {
68         my_malloc_func = malloc_func;
69         my_free_func = free_func;
70 }
71
72 /*****  NULL-safe Functions  *****/
73
74 char *
75 iks_strdup (const char *src)
76 {
77         if (src) return strdup(src);
78         return NULL;
79 }
80
81 char *
82 iks_strcat (char *dest, const char *src)
83 {
84         size_t len;
85
86         if (!src) return dest;
87
88         len = strlen (src);
89         memcpy (dest, src, len);
90         dest[len] = '\0';
91         return dest + len;
92 }
93
94 int
95 iks_strcmp (const char *a, const char *b)
96 {
97         if (!a || !b) return -1;
98         return strcmp (a, b);
99 }
100
101 int
102 iks_strcasecmp (const char *a, const char *b)
103 {
104         if (!a || !b) return -1;
105         return strcasecmp (a, b);
106 }
107
108 int
109 iks_strncmp (const char *a, const char *b, size_t n)
110 {
111         if (!a || !b) return -1;
112         return strncmp (a, b, n);
113 }
114
115 int
116 iks_strncasecmp (const char *a, const char *b, size_t n)
117 {
118         if (!a || !b) return -1;
119         return strncasecmp (a, b, n);
120 }
121
122 size_t
123 iks_strlen (const char *src)
124 {
125         if (!src) return 0;
126         return strlen (src);
127 }
128
129 /*****  XML Escaping  *****/
130
131 char *
132 iks_escape (ikstack *s, char *src, size_t len)
133 {
134         char *ret;
135         int i, j, nlen;
136
137         if (!src || !s) return NULL;
138         if (len == -1) len = strlen (src);
139
140         nlen = len;
141         for (i=0; i<len; i++) {
142                 switch (src[i]) {
143                 case '&': nlen += 4; break;
144                 case '<': nlen += 3; break;
145                 case '>': nlen += 3; break;
146                 case '\'': nlen += 5; break;
147                 case '"': nlen += 5; break;
148                 }
149         }
150         if (len == nlen) return src;
151
152         ret = iks_stack_alloc (s, nlen + 1);
153         if (!ret) return NULL;
154
155         for (i=j=0; i<len; i++) {
156                 switch (src[i]) {
157                 case '&': memcpy (&ret[j], "&amp;", 5); j += 5; break;
158                 case '\'': memcpy (&ret[j], "&apos;", 6); j += 6; break;
159                 case '"': memcpy (&ret[j], "&quot;", 6); j += 6; break;
160                 case '<': memcpy (&ret[j], "&lt;", 4); j += 4; break;
161                 case '>': memcpy (&ret[j], "&gt;", 4); j += 4; break;
162                 default: ret[j++] = src[i];
163                 }
164         }
165         ret[j] = '\0';
166
167         return ret;
168 }
169
170 char *
171 iks_unescape (ikstack *s, char *src, size_t len)
172 {
173         int i,j;
174         char *ret;
175
176         if (!s || !src) return NULL;
177         if (!strchr (src, '&')) return src;
178         if (len == -1) len = strlen (src);
179
180         ret = iks_stack_alloc (s, len + 1);
181         if (!ret) return NULL;
182
183         for (i=j=0; i<len; i++) {
184                 if (src[i] == '&') {
185                         i++;
186                         if (strncmp (&src[i], "amp;", 4) == 0) {
187                                 ret[j] = '&';
188                                 i += 3;
189                         } else if (strncmp (&src[i], "quot;", 5) == 0) {
190                                 ret[j] = '"';
191                                 i += 4;
192                         } else if (strncmp (&src[i], "apos;", 5) == 0) {
193                                 ret[j] = '\'';
194                                 i += 4;
195                         } else if (strncmp (&src[i], "lt;", 3) == 0) {
196                                 ret[j] = '<';
197                                 i += 2;
198                         } else if (strncmp (&src[i], "gt;", 3) == 0) {
199                                 ret[j] = '>';
200                                 i += 2;
201                         } else {
202                                 ret[j] = src[--i];
203                         }
204                 } else {
205                         ret[j] = src[i];
206                 }
207                 j++;
208         }
209         ret[j] = '\0';
210
211         return ret;
212 }
213 /* iksemel (XML parser for Jabber)
214 ** Copyright (C) 2000-2004 Gurer Ozen <madcat@e-kolay.net>
215 ** This code is free software; you can redistribute it and/or
216 ** modify it under the terms of GNU Lesser General Public License.
217 */
218
219 #include "common.h"
220 #include "iksemel.h"
221
222 struct align_test { char a; double b; };
223 #define DEFAULT_ALIGNMENT  ((size_t) ((char *) &((struct align_test *) 0)->b - (char *) 0))
224 #define ALIGN_MASK ( DEFAULT_ALIGNMENT - 1 )
225 #define MIN_CHUNK_SIZE ( DEFAULT_ALIGNMENT * 8 )
226 #define MIN_ALLOC_SIZE DEFAULT_ALIGNMENT
227 #define ALIGN(x) ( (x) + (DEFAULT_ALIGNMENT - ( (x) & ALIGN_MASK)) )
228
229 typedef struct ikschunk_struct {
230         struct ikschunk_struct *next;
231         size_t size;
232         size_t used;
233         size_t last;
234         char data[4];
235 } ikschunk;
236
237 struct ikstack_struct {
238         size_t allocated;
239         ikschunk *meta;
240         ikschunk *data;
241 };
242
243 static ikschunk *
244 find_space (ikstack *s, ikschunk *c, size_t size)
245 {
246         /* FIXME: dont use *2 after over allocated chunks */
247         while (1) {
248                 if (c->size - c->used >= size) return c;
249                 if (!c->next) {
250                         if ((c->size * 2) > size) size = c->size * 2;
251                         c->next = iks_malloc (sizeof (ikschunk) + size);
252                         if (!c->next) return NULL;
253                         s->allocated += sizeof (ikschunk) + size;
254                         c = c->next;
255                         c->next = NULL;
256                         c->size = size;
257                         c->used = 0;
258                         c->last = (size_t) -1;
259                         return c;
260                 }
261                 c = c->next;
262         }
263         return NULL;
264 }
265
266 ikstack *
267 iks_stack_new (size_t meta_chunk, size_t data_chunk)
268 {
269         ikstack *s;
270         size_t len;
271
272         if (meta_chunk < MIN_CHUNK_SIZE) meta_chunk = MIN_CHUNK_SIZE;
273         if (meta_chunk & ALIGN_MASK) meta_chunk = ALIGN (meta_chunk);
274         if (data_chunk < MIN_CHUNK_SIZE) data_chunk = MIN_CHUNK_SIZE;
275         if (data_chunk & ALIGN_MASK) data_chunk = ALIGN (data_chunk);
276
277         len = sizeof (ikstack) + meta_chunk + data_chunk + (sizeof (ikschunk) * 2);
278         s = iks_malloc (len);
279         if (!s) return NULL;
280         s->allocated = len;
281         s->meta = (ikschunk *) ((char *) s + sizeof (ikstack));
282         s->meta->next = NULL;
283         s->meta->size = meta_chunk;
284         s->meta->used = 0;
285         s->meta->last = (size_t) -1;
286         s->data = (ikschunk *) ((char *) s + sizeof (ikstack) + sizeof (ikschunk) + meta_chunk);
287         s->data->next = NULL;
288         s->data->size = data_chunk;
289         s->data->used = 0;
290         s->data->last = (size_t) -1;
291         return s;
292 }
293
294 void *
295 iks_stack_alloc (ikstack *s, size_t size)
296 {
297         ikschunk *c;
298         void *mem;
299
300         if (size < MIN_ALLOC_SIZE) size = MIN_ALLOC_SIZE;
301         if (size & ALIGN_MASK) size = ALIGN (size);
302
303         c = find_space (s, s->meta, size);
304         if (!c) return NULL;
305         mem = c->data + c->used;
306         c->used += size;
307         return mem;
308 }
309
310 char *
311 iks_stack_strdup (ikstack *s, const char *src, size_t len)
312 {
313         ikschunk *c;
314         char *dest;
315
316         if (!src) return NULL;
317         if (0 == len) len = strlen (src);
318
319         c = find_space (s, s->data, len + 1);
320         if (!c) return NULL;
321         dest = c->data + c->used;
322         c->last = c->used;
323         c->used += len + 1;
324         memcpy (dest, src, len);
325         dest[len] = '\0';
326         return dest;
327 }
328
329 char *
330 iks_stack_strcat (ikstack *s, char *old, size_t old_len, const char *src, size_t src_len)
331 {
332         char *ret;
333         ikschunk *c;
334
335         if (!old) {
336                 return iks_stack_strdup (s, src, src_len);
337         }
338         if (0 == old_len) old_len = strlen (old);
339         if (0 == src_len) src_len = strlen (src);
340
341         for (c = s->data; c; c = c->next) {
342                 if (c->data + c->last == old) break;
343         }
344         if (!c) {
345                 c = find_space (s, s->data, old_len + src_len + 1);
346                 if (!c) return NULL;
347                 ret = c->data + c->used;
348                 c->last = c->used;
349                 c->used += old_len + src_len + 1;
350                 memcpy (ret, old, old_len);
351                 memcpy (ret + old_len, src, src_len);
352                 ret[old_len + src_len] = '\0';
353                 return ret;
354         }
355
356         if (c->size - c->used > src_len) {
357                 ret = c->data + c->last;
358                 memcpy (ret + old_len, src, src_len);
359                 c->used += src_len;
360                 ret[old_len + src_len] = '\0';
361         } else {
362                 /* FIXME: decrease c->used before moving string to new place */
363                 c = find_space (s, s->data, old_len + src_len + 1);
364                 if (!c) return NULL;
365                 c->last = c->used;
366                 ret = c->data + c->used;
367                 memcpy (ret, old, old_len);
368                 c->used += old_len;
369                 memcpy (c->data + c->used, src, src_len);
370                 c->used += src_len;
371                 c->data[c->used] = '\0';
372                 c->used++;
373         }
374         return ret;
375 }
376
377 void
378 iks_stack_stat (ikstack *s, size_t *allocated, size_t *used)
379 {
380         ikschunk *c;
381
382         if (allocated) {
383                 *allocated = s->allocated;
384         }
385         if (used) {
386                 *used = 0;
387                 for (c = s->meta; c; c = c->next) {
388                         (*used) += c->used;
389                 }
390                 for (c = s->data; c; c = c->next) {
391                         (*used) += c->used;
392                 }
393         }
394 }
395
396 void
397 iks_stack_delete (ikstack *s)
398 {
399         ikschunk *c, *tmp;
400
401         c = s->meta->next;
402         while (c) {
403                 tmp = c->next;
404                 iks_free (c);
405                 c = tmp;
406         }
407         c = s->data->next;
408         while (c) {
409                 tmp = c->next;
410                 iks_free (c);
411                 c = tmp;
412         }
413         iks_free (s);
414 }
415 /* iksemel (XML parser for Jabber)
416 ** Copyright (C) 2000-2004 Gurer Ozen <madcat@e-kolay.net>
417 ** This code is free software; you can redistribute it and/or
418 ** modify it under the terms of GNU Lesser General Public License.
419 */
420
421 #include "common.h"
422 #include "iksemel.h"
423
424 enum cons_e {
425         C_CDATA = 0,
426         C_TAG_START,
427         C_TAG,
428         C_TAG_END,
429         C_ATTRIBUTE,
430         C_ATTRIBUTE_1,
431         C_ATTRIBUTE_2,
432         C_VALUE,
433         C_VALUE_APOS,
434         C_VALUE_QUOT,
435         C_WHITESPACE,
436         C_ENTITY,
437         C_COMMENT,
438         C_COMMENT_1,
439         C_COMMENT_2,
440         C_COMMENT_3,
441         C_MARKUP,
442         C_MARKUP_1,
443         C_SECT,
444         C_SECT_CDATA,
445         C_SECT_CDATA_1,
446         C_SECT_CDATA_2,
447         C_SECT_CDATA_3,
448         C_SECT_CDATA_4,
449         C_SECT_CDATA_C,
450         C_SECT_CDATA_E,
451         C_SECT_CDATA_E2,
452         C_PI
453 };
454
455 /* if you add a variable here, dont forget changing iks_parser_reset */
456 struct iksparser_struct {
457         ikstack *s;
458         void *user_data;
459         iksTagHook *tagHook;
460         iksCDataHook *cdataHook;
461         iksDeleteHook *deleteHook;
462         /* parser context */
463         char *stack;
464         size_t stack_pos;
465         size_t stack_max;
466
467         enum cons_e context;
468         enum cons_e oldcontext;
469
470         char *tag_name;
471         enum ikstagtype tagtype;
472
473         unsigned int attmax;
474         unsigned int attcur;
475         int attflag;
476         char **atts;
477         int valflag;
478
479         unsigned int entpos;
480         char entity[8];
481
482         unsigned long nr_bytes;
483         unsigned long nr_lines;
484
485         int uni_max;
486         int uni_len;
487 };
488
489 iksparser *
490 iks_sax_new (void *user_data, iksTagHook *tagHook, iksCDataHook *cdataHook)
491 {
492         iksparser *prs;
493
494         prs = iks_malloc (sizeof (iksparser));
495         if (NULL == prs) return NULL;
496         memset (prs, 0, sizeof (iksparser));
497         prs->user_data = user_data;
498         prs->tagHook = tagHook;
499         prs->cdataHook = cdataHook;
500         return prs;
501 }
502
503 iksparser *
504 iks_sax_extend (ikstack *s, void *user_data, iksTagHook *tagHook, iksCDataHook *cdataHook, iksDeleteHook *deleteHook)
505 {
506         iksparser *prs;
507
508         prs = iks_stack_alloc (s, sizeof (iksparser));
509         if (NULL == prs) return NULL;
510         memset (prs, 0, sizeof (iksparser));
511         prs->s = s;
512         prs->user_data = user_data;
513         prs->tagHook = tagHook;
514         prs->cdataHook = cdataHook;
515         prs->deleteHook = deleteHook;
516         return prs;
517 }
518
519 ikstack *
520 iks_parser_stack (iksparser *prs)
521 {
522         return prs->s;
523 }
524
525 void *
526 iks_user_data (iksparser *prs)
527 {
528         return prs->user_data;
529 }
530
531 unsigned long
532 iks_nr_bytes (iksparser *prs)
533 {
534         return prs->nr_bytes;
535 }
536
537 unsigned long
538 iks_nr_lines (iksparser *prs)
539 {
540         return prs->nr_lines;
541 }
542
543 #define IS_WHITESPACE(x) ' ' == (x) || '\t' == (x) || '\r' == (x) || '\n' == (x)
544 #define NOT_WHITESPACE(x) ' ' != (x) && '\t' != (x) && '\r' != (x) && '\n' != (x)
545
546 static int
547 stack_init (iksparser *prs)
548 {
549         prs->stack = iks_malloc (128);
550         if (!prs->stack) return 0;
551         prs->stack_max = 128;
552         prs->stack_pos = 0;
553         return 1;
554 }
555
556 static int
557 stack_expand (iksparser *prs, int len)
558 {
559         size_t need;
560         off_t diff;
561         char *tmp;
562         need = len - (prs->stack_max - prs->stack_pos);
563         if (need < prs->stack_max) {
564                 need = prs->stack_max * 2;
565         } else {
566                 need = prs->stack_max + (need * 1.2);
567         }
568         tmp = iks_malloc (need);
569         if (!tmp) return 0;
570         diff = tmp - prs->stack;
571         memcpy (tmp, prs->stack, prs->stack_max);
572         iks_free (prs->stack);
573         prs->stack = tmp;
574         prs->stack_max = need;
575         prs->tag_name += diff;
576         if (prs->attflag != 0) {
577                 int i = 0;
578                 while (i < (prs->attmax * 2)) {
579                         if (prs->atts[i]) prs->atts[i] += diff;
580                         i++;
581                 }
582         }
583         return 1;
584 }
585
586 #define STACK_INIT \
587         if (NULL == prs->stack && 0 == stack_init (prs)) return IKS_NOMEM
588
589 #define STACK_PUSH_START (prs->stack + prs->stack_pos)
590
591 #define STACK_PUSH(buf,len) \
592 { \
593         char *sbuf = (buf); \
594         size_t slen = (len); \
595         if (prs->stack_max - prs->stack_pos <= slen) { \
596                 if (0 == stack_expand (prs, slen)) return IKS_NOMEM; \
597         } \
598         memcpy (prs->stack + prs->stack_pos, sbuf, slen); \
599         prs->stack_pos += slen; \
600 }
601
602 #define STACK_PUSH_END \
603 { \
604         if (prs->stack_pos >= prs->stack_max) { \
605                 if (0 == stack_expand (prs, 1)) return IKS_NOMEM; \
606         } \
607         prs->stack[prs->stack_pos] = '\0'; \
608         prs->stack_pos++; \
609 }
610
611 static enum ikserror
612 sax_core (iksparser *prs, char *buf, int len)
613 {
614         enum ikserror err;
615         int pos = 0, old = 0, re, stack_old = -1;
616         unsigned char c;
617
618         while (pos < len) {
619                 re = 0;
620                 c = buf[pos];
621                 if (0 == c || 0xFE == c || 0xFF == c) return IKS_BADXML;
622                 if (prs->uni_max) {
623                         if ((c & 0xC0) != 0x80) return IKS_BADXML;
624                         prs->uni_len++;
625                         if (prs->uni_len == prs->uni_max) prs->uni_max = 0;
626                         goto cont;
627                 } else {
628                         if (c & 0x80) {
629                                 unsigned char mask;
630                                 if ((c & 0x60) == 0x40) {
631                                         prs->uni_max = 2;
632                                         mask = 0x1F;
633                                 } else if ((c & 0x70) == 0x60) {
634                                         prs->uni_max = 3;
635                                         mask = 0x0F;
636                                 } else if ((c & 0x78) == 0x70) {
637                                         prs->uni_max = 4;
638                                         mask = 0x07;
639                                 } else if ((c & 0x7C) == 0x78) {
640                                         prs->uni_max = 5;
641                                         mask = 0x03;
642                                 } else if ((c & 0x7E) == 0x7C) {
643                                         prs->uni_max = 6;
644                                         mask = 0x01;
645                                 } else {
646                                         return IKS_BADXML;
647                                 }
648                                 if ((c & mask) == 0) return IKS_BADXML;
649                                 prs->uni_len = 1;
650                                 if (stack_old == -1) stack_old = pos;
651                                 goto cont;
652                         }
653                 }
654
655                 switch (prs->context) {
656                         case C_CDATA:
657                                 if ('&' == c) {
658                                         if (old < pos && prs->cdataHook) {
659                                                 err = prs->cdataHook (prs->user_data, &buf[old], pos - old);
660                                                 if (IKS_OK != err) return err;
661                                         }
662                                         prs->context = C_ENTITY;
663                                         prs->entpos = 0;
664                                         break;
665                                 }
666                                 if ('<' == c) {
667                                         if (old < pos && prs->cdataHook) {
668                                                 err = prs->cdataHook (prs->user_data, &buf[old], pos - old);
669                                                 if (IKS_OK != err) return err;
670                                         }
671                                         STACK_INIT;
672                                         prs->tag_name = STACK_PUSH_START;
673                                         if (!prs->tag_name) return IKS_NOMEM;
674                                         prs->context = C_TAG_START;
675                                 }
676                                 break;
677
678                         case C_TAG_START:
679                                 prs->context = C_TAG;
680                                 if ('/' == c) {
681                                         prs->tagtype = IKS_CLOSE;
682                                         break;
683                                 }
684                                 if ('?' == c) {
685                                         prs->context = C_PI;
686                                         break;
687                                 }
688                                 if ('!' == c) {
689                                         prs->context = C_MARKUP;
690                                         break;
691                                 }
692                                 prs->tagtype = IKS_OPEN;
693                                 stack_old = pos;
694                                 break;
695
696                         case C_TAG:
697                                 if (IS_WHITESPACE(c)) {
698                                         if (IKS_CLOSE == prs->tagtype)
699                                                 prs->oldcontext = C_TAG_END;
700                                         else
701                                                 prs->oldcontext = C_ATTRIBUTE;
702                                         prs->context = C_WHITESPACE;
703                                         if (stack_old != -1) STACK_PUSH (buf + stack_old, pos - stack_old);
704                                         stack_old = -1;
705                                         STACK_PUSH_END;
706                                         break;
707                                 }
708                                 if ('/' == c) {
709                                         if (IKS_CLOSE == prs->tagtype) return IKS_BADXML;
710                                         prs->tagtype = IKS_SINGLE;
711                                         prs->context = C_TAG_END;
712                                         if (stack_old != -1) STACK_PUSH (buf + stack_old, pos - stack_old);
713                                         stack_old = -1;
714                                         STACK_PUSH_END;
715                                         break;
716                                 }
717                                 if ('>' == c) {
718                                         prs->context = C_TAG_END;
719                                         if (stack_old != -1) STACK_PUSH (buf + stack_old, pos - stack_old);
720                                         stack_old = -1;
721                                         STACK_PUSH_END;
722                                         re = 1;
723                                 }
724                                 if (stack_old == -1) stack_old = pos;
725                                 break;
726
727                         case C_TAG_END:
728                                 if (c != '>') return IKS_BADXML;
729                                 if (prs->tagHook) {
730                                         char **tmp;
731                                         if (prs->attcur == 0) tmp = NULL; else tmp = prs->atts;
732                                         err = prs->tagHook (prs->user_data, prs->tag_name, tmp, prs->tagtype);
733                                         if (IKS_OK != err) return err;
734                                 }
735                                 prs->stack_pos = 0;
736                                 stack_old = -1;
737                                 prs->attcur = 0;
738                                 prs->attflag = 0;
739                                 prs->context = C_CDATA;
740                                 old = pos + 1;
741                                 break;
742
743                         case C_ATTRIBUTE:
744                                 if ('/' == c) {
745                                         prs->tagtype = IKS_SINGLE;
746                                         prs->context = C_TAG_END;
747                                         break;
748                                 }
749                                 if ('>' == c) {
750                                         prs->context = C_TAG_END;
751                                         re = 1;
752                                         break;
753                                 }
754                                 if (!prs->atts) {
755                                         prs->attmax = 12;
756                                         prs->atts = iks_malloc (sizeof(char *) * 2 * 12);
757                                         if (!prs->atts) return IKS_NOMEM;
758                                         memset (prs->atts, 0, sizeof(char *) * 2 * 12);
759                                         prs->attcur = 0;
760                                 } else {
761                                         if (prs->attcur >= (prs->attmax * 2)) {
762                                                 void *tmp;
763                                                 prs->attmax += 12;
764                                                 tmp = iks_malloc (sizeof(char *) * (2 * prs->attmax + 1));
765                                                 if (!tmp) return IKS_NOMEM;
766                                                 memset (tmp, 0, sizeof(char *) * (2 * prs->attmax + 1));
767                                                 memcpy (tmp, prs->atts, sizeof(char *) * prs->attcur);
768                                                 iks_free (prs->atts);
769                                                 prs->atts = tmp;
770                                         }
771                                 }
772                                 prs->attflag = 1;
773                                 prs->atts[prs->attcur] = STACK_PUSH_START;
774                                 stack_old = pos;
775                                 prs->context = C_ATTRIBUTE_1;
776                                 break;
777
778                         case C_ATTRIBUTE_1:
779                                 if ('=' == c) {
780                                         if (stack_old != -1) STACK_PUSH (buf + stack_old, pos - stack_old);
781                                         stack_old = -1;
782                                         STACK_PUSH_END;
783                                         prs->context = C_VALUE;
784                                         break;
785                                 }
786                                 if (stack_old == -1) stack_old = pos;
787                                 break;
788
789                         case C_ATTRIBUTE_2:
790                                 if ('/' == c) {
791                                         prs->tagtype = IKS_SINGLE;
792                                         prs->atts[prs->attcur] = NULL;
793                                         prs->context = C_TAG_END;
794                                         break;
795                                 }
796                                 if ('>' == c) {
797                                         prs->atts[prs->attcur] = NULL;
798                                         prs->context = C_TAG_END;
799                                         re = 1;
800                                         break;
801                                 }
802                                 prs->context = C_ATTRIBUTE;
803                                 re = 1;
804                                 break;
805
806                         case C_VALUE:
807                                 prs->atts[prs->attcur + 1] = STACK_PUSH_START;
808                                 if ('\'' == c) {
809                                         prs->context = C_VALUE_APOS;
810                                         break;
811                                 }
812                                 if ('"' == c) {
813                                         prs->context = C_VALUE_QUOT;
814                                         break;
815                                 }
816                                 return IKS_BADXML;
817
818                         case C_VALUE_APOS:
819                                 if ('\'' == c) {
820                                         if (stack_old != -1) STACK_PUSH (buf + stack_old, pos - stack_old);
821                                         stack_old = -1;
822                                         STACK_PUSH_END;
823                                         prs->oldcontext = C_ATTRIBUTE_2;
824                                         prs->context = C_WHITESPACE;
825                                         prs->attcur += 2;
826                                 }
827                                 if (stack_old == -1) stack_old = pos;
828                                 break;
829
830                         case C_VALUE_QUOT:
831                                 if ('"' == c) {
832                                         if (stack_old != -1) STACK_PUSH (buf + stack_old, pos - stack_old);
833                                         stack_old = -1;
834                                         STACK_PUSH_END;
835                                         prs->oldcontext = C_ATTRIBUTE_2;
836                                         prs->context = C_WHITESPACE;
837                                         prs->attcur += 2;
838                                 }
839                                 if (stack_old == -1) stack_old = pos;
840                                 break;
841
842                         case C_WHITESPACE:
843                                 if (NOT_WHITESPACE(c)) {
844                                         prs->context = prs->oldcontext;
845                                         re = 1;
846                                 }
847                                 break;
848
849                         case C_ENTITY:
850                                 if (';' == c) {
851                                         char hede[2];
852                                         char t = '?';
853                                         prs->entity[prs->entpos] = '\0';
854                                         if (strcmp(prs->entity, "amp") == 0)
855                                                 t = '&';
856                                         else if (strcmp(prs->entity, "quot") == 0)
857                                                 t = '"';
858                                         else if (strcmp(prs->entity, "apos") == 0)
859                                                 t = '\'';
860                                         else if (strcmp(prs->entity, "lt") == 0)
861                                                 t = '<';
862                                         else if (strcmp(prs->entity, "gt") == 0)
863                                                 t = '>';
864                                         old = pos + 1;
865                                         hede[0] = t;
866                                         if (prs->cdataHook) {
867                                                 err = prs->cdataHook (prs->user_data, &hede[0], 1);
868                                                 if (IKS_OK != err) return err;
869                                         }
870                                         prs->context = C_CDATA;
871                                 } else {
872                                         prs->entity[prs->entpos++] = buf[pos];
873                                         if (prs->entpos > 7) return IKS_BADXML;
874                                 }
875                                 break;
876
877                         case C_COMMENT:
878                                 if ('-' != c) return IKS_BADXML;
879                                 prs->context = C_COMMENT_1;
880                                 break;
881
882                         case C_COMMENT_1:
883                                 if ('-' == c) prs->context = C_COMMENT_2;
884                                 break;
885
886                         case C_COMMENT_2:
887                                 if ('-' == c)
888                                         prs->context = C_COMMENT_3;
889                                 else
890                                         prs->context = C_COMMENT_1;
891                                 break;
892
893                         case C_COMMENT_3:
894                                 if ('>' != c) return IKS_BADXML;
895                                 prs->context = C_CDATA;
896                                 old = pos + 1;
897                                 break;
898
899                         case C_MARKUP:
900                                 if ('[' == c) {
901                                         prs->context = C_SECT;
902                                         break;
903                                 }
904                                 if ('-' == c) {
905                                         prs->context = C_COMMENT;
906                                         break;
907                                 }
908                                 prs->context = C_MARKUP_1;
909
910                         case C_MARKUP_1:
911                                 if ('>' == c) {
912                                         old = pos + 1;
913                                         prs->context = C_CDATA;
914                                 }
915                                 break;
916
917                         case C_SECT:
918                                 if ('C' == c) {
919                                         prs->context = C_SECT_CDATA;
920                                         break;
921                                 }
922                                 return IKS_BADXML;
923
924                         case C_SECT_CDATA:
925                                 if ('D' != c) return IKS_BADXML;
926                                 prs->context = C_SECT_CDATA_1;
927                                 break;
928
929                         case C_SECT_CDATA_1:
930                                 if ('A' != c) return IKS_BADXML;
931                                 prs->context = C_SECT_CDATA_2;
932                                 break;
933
934                         case C_SECT_CDATA_2:
935                                 if ('T' != c) return IKS_BADXML;
936                                 prs->context = C_SECT_CDATA_3;
937                                 break;
938
939                         case C_SECT_CDATA_3:
940                                 if ('A' != c) return IKS_BADXML;
941                                 prs->context = C_SECT_CDATA_4;
942                                 break;
943
944                         case C_SECT_CDATA_4:
945                                 if ('[' != c) return IKS_BADXML;
946                                 old = pos + 1;
947                                 prs->context = C_SECT_CDATA_C;
948                                 break;
949
950                         case C_SECT_CDATA_C:
951                                 if (']' == c) {
952                                         prs->context = C_SECT_CDATA_E;
953                                         if (prs->cdataHook && old < pos) {
954                                                 err = prs->cdataHook (prs->user_data, &buf[old], pos - old);
955                                                 if (IKS_OK != err) return err;
956                                         }
957                                 }
958                                 break;
959
960                         case C_SECT_CDATA_E:
961                                 if (']' == c) {
962                                         prs->context = C_SECT_CDATA_E2;
963                                 } else {
964                                         if (prs->cdataHook) {
965                                                 err = prs->cdataHook (prs->user_data, "]", 1);
966                                                 if (IKS_OK != err) return err;
967                                         }
968                                         old = pos;
969                                         prs->context = C_SECT_CDATA_C;
970                                 }
971                                 break;
972
973                         case C_SECT_CDATA_E2:
974                                 if ('>' == c) {
975                                         old = pos + 1;
976                                         prs->context = C_CDATA;
977                                 } else {
978                                         if (prs->cdataHook) {
979                                                 err = prs->cdataHook (prs->user_data, "]]", 2);
980                                                 if (IKS_OK != err) return err;
981                                         }
982                                         old = pos;
983                                         prs->context = C_SECT_CDATA_C;
984                                 }
985                                 break;
986
987                         case C_PI:
988                                 old = pos + 1;
989                                 if ('>' == c) prs->context = C_CDATA;
990                                 break;
991                 }
992 cont:
993                 if (0 == re) {
994                         pos++;
995                         prs->nr_bytes++;
996                         if ('\n' == c) prs->nr_lines++;
997                 }
998         }
999
1000         if (stack_old != -1)
1001                 STACK_PUSH (buf + stack_old, pos - stack_old);
1002
1003         err = IKS_OK;
1004         if (prs->cdataHook && (prs->context == C_CDATA || prs->context == C_SECT_CDATA_C) && old < pos)
1005                 err = prs->cdataHook (prs->user_data, &buf[old], pos - old);
1006         return err;
1007 }
1008
1009 int
1010 iks_parse (iksparser *prs, const char *data, size_t len, int finish)
1011 {
1012         if (!data) return IKS_OK;
1013         if (len == 0) len = strlen (data);
1014         return sax_core (prs, (char *) data, len);
1015 }
1016
1017 void
1018 iks_parser_reset (iksparser *prs)
1019 {
1020         if (prs->deleteHook) prs->deleteHook (prs->user_data);
1021         prs->stack_pos = 0;
1022         prs->context = 0;
1023         prs->oldcontext = 0;
1024         prs->tagtype = 0;
1025         prs->attcur = 0;
1026         prs->attflag = 0;
1027         prs->valflag = 0;
1028         prs->entpos = 0;
1029         prs->nr_bytes = 0;
1030         prs->nr_lines = 0;
1031         prs->uni_max = 0;
1032         prs->uni_len = 0;
1033 }
1034
1035 void
1036 iks_parser_delete (iksparser *prs)
1037 {
1038         if (prs->deleteHook) prs->deleteHook (prs->user_data);
1039         if (prs->stack) iks_free (prs->stack);
1040         if (prs->atts) iks_free (prs->atts);
1041         if (prs->s) iks_stack_delete (prs->s); else iks_free (prs);
1042 }
1043 /* iksemel (XML parser for Jabber)
1044 ** Copyright (C) 2000-2004 Gurer Ozen <madcat@e-kolay.net>
1045 ** This code is free software; you can redistribute it and/or
1046 ** modify it under the terms of GNU Lesser General Public License.
1047 */
1048
1049 #include "common.h"
1050 #include "iksemel.h"
1051
1052 #define IKS_COMMON \
1053         struct iks_struct *next, *prev; \
1054         struct iks_struct *parent; \
1055         enum ikstype type; \
1056         ikstack *s
1057
1058 struct iks_struct {
1059         IKS_COMMON;
1060 };
1061
1062 struct iks_tag {
1063         IKS_COMMON;
1064         struct iks_struct *children, *last_child;
1065         struct iks_struct *attribs, *last_attrib;
1066         char *name;
1067 };
1068
1069 #define IKS_TAG_NAME(x) ((struct iks_tag *) (x) )->name
1070 #define IKS_TAG_CHILDREN(x) ((struct iks_tag *) (x) )->children
1071 #define IKS_TAG_LAST_CHILD(x) ((struct iks_tag *) (x) )->last_child
1072 #define IKS_TAG_ATTRIBS(x) ((struct iks_tag *) (x) )->attribs
1073 #define IKS_TAG_LAST_ATTRIB(x) ((struct iks_tag *) (x) )->last_attrib
1074
1075 struct iks_cdata {
1076         IKS_COMMON;
1077         char *cdata;
1078         size_t len;
1079 };
1080
1081 #define IKS_CDATA_CDATA(x) ((struct iks_cdata *) (x) )->cdata
1082 #define IKS_CDATA_LEN(x) ((struct iks_cdata *) (x) )->len
1083
1084 struct iks_attrib {
1085         IKS_COMMON;
1086         char *name;
1087         char *value;
1088 };
1089
1090 #define IKS_ATTRIB_NAME(x) ((struct iks_attrib *) (x) )->name
1091 #define IKS_ATTRIB_VALUE(x) ((struct iks_attrib *) (x) )->value
1092
1093 /*****  Node Creating & Deleting  *****/
1094
1095 iks *
1096 iks_new (const char *name)
1097 {
1098         ikstack *s;
1099         iks *x;
1100
1101         s = iks_stack_new (sizeof (struct iks_tag) * 6, 256);
1102         if (!s) return NULL;
1103         x = iks_new_within (name, s);
1104         if (!x) {
1105                 iks_stack_delete (s);
1106                 return NULL;
1107         }
1108         return x;
1109 }
1110
1111 iks *
1112 iks_new_within (const char *name, ikstack *s)
1113 {
1114         iks *x;
1115         size_t len;
1116
1117         if (name) len = sizeof (struct iks_tag); else len = sizeof (struct iks_cdata);
1118         x = iks_stack_alloc (s, len);
1119         if (!x) return NULL;
1120         memset (x, 0, len);
1121         x->s = s;
1122         x->type = IKS_TAG;
1123         if (name) {
1124                 IKS_TAG_NAME (x) = iks_stack_strdup (s, name, 0);
1125                 if (!IKS_TAG_NAME (x)) return NULL;
1126         }
1127         return x;
1128 }
1129
1130 iks *
1131 iks_insert (iks *x, const char *name)
1132 {
1133         iks *y;
1134
1135         if (!x) return NULL;
1136
1137         y = iks_new_within (name, x->s);
1138         if (!y) return NULL;
1139         y->parent = x;
1140         if (!IKS_TAG_CHILDREN (x)) IKS_TAG_CHILDREN (x) = y;
1141         if (IKS_TAG_LAST_CHILD (x)) {
1142                 IKS_TAG_LAST_CHILD (x)->next = y;
1143                 y->prev = IKS_TAG_LAST_CHILD (x);
1144         }
1145         IKS_TAG_LAST_CHILD (x) = y;
1146         return y;
1147 }
1148
1149 iks *
1150 iks_insert_cdata (iks *x, const char *data, size_t len)
1151 {
1152         iks *y;
1153
1154         if(!x || !data) return NULL;
1155         if(len == 0) len = strlen (data);
1156
1157         y = IKS_TAG_LAST_CHILD (x);
1158         if (y && y->type == IKS_CDATA) {
1159                 IKS_CDATA_CDATA (y) = iks_stack_strcat (x->s, IKS_CDATA_CDATA (y), IKS_CDATA_LEN (y), data, len);
1160                 IKS_CDATA_LEN (y) += len;
1161         } else {
1162                 y = iks_insert (x, NULL);
1163                 if (!y) return NULL;
1164                 y->type = IKS_CDATA;
1165                 IKS_CDATA_CDATA (y) = iks_stack_strdup (x->s, data, len);
1166                 if (!IKS_CDATA_CDATA (y)) return NULL;
1167                 IKS_CDATA_LEN (y) = len;
1168         }
1169         return y;
1170 }
1171
1172 iks *
1173 iks_insert_attrib (iks *x, const char *name, const char *value)
1174 {
1175         iks *y;
1176         size_t len;
1177
1178         if (!x) return NULL;
1179
1180         y = IKS_TAG_ATTRIBS (x);
1181         while (y) {
1182                 if (strcmp (name, IKS_ATTRIB_NAME (y)) == 0) break;
1183                 y = y->next;
1184         }
1185         if (NULL == y) {
1186                 if (!value) return NULL;
1187                 y = iks_stack_alloc (x->s, sizeof (struct iks_attrib));
1188                 if (!y) return NULL;
1189                 memset (y, 0, sizeof (struct iks_attrib));
1190                 y->type = IKS_ATTRIBUTE;
1191                 IKS_ATTRIB_NAME (y) = iks_stack_strdup (x->s, name, 0);
1192                 y->parent = x;
1193                 if (!IKS_TAG_ATTRIBS (x)) IKS_TAG_ATTRIBS (x) = y;
1194                 if (IKS_TAG_LAST_ATTRIB (x)) {
1195                         IKS_TAG_LAST_ATTRIB (x)->next = y;
1196                         y->prev = IKS_TAG_LAST_ATTRIB (x);
1197                 }
1198                 IKS_TAG_LAST_ATTRIB (x) = y;
1199         }
1200
1201         if (value) {
1202                 len = strlen (value);
1203                 IKS_ATTRIB_VALUE (y) = iks_stack_strdup (x->s, value, len);
1204                 if (!IKS_ATTRIB_VALUE (y)) return NULL;
1205         } else {
1206                 if (y->next) y->next->prev = y->prev;
1207                 if (y->prev) y->prev->next = y->next;
1208                 if (IKS_TAG_ATTRIBS (x) == y) IKS_TAG_ATTRIBS (x) = y->next;
1209                 if (IKS_TAG_LAST_ATTRIB (x) == y) IKS_TAG_LAST_ATTRIB (x) = y->prev;
1210         }
1211
1212         return y;
1213 }
1214
1215 iks *
1216 iks_insert_node (iks *x, iks *y)
1217 {
1218         y->parent = x;
1219         if (!IKS_TAG_CHILDREN (x)) IKS_TAG_CHILDREN (x) = y;
1220         if (IKS_TAG_LAST_CHILD (x)) {
1221                 IKS_TAG_LAST_CHILD (x)->next = y;
1222                 y->prev = IKS_TAG_LAST_CHILD (x);
1223         }
1224         IKS_TAG_LAST_CHILD (x) = y;
1225         return y;
1226 }
1227
1228 void
1229 iks_hide (iks *x)
1230 {
1231         iks *y;
1232
1233         if (!x) return;
1234
1235         if (x->prev) x->prev->next = x->next;
1236         if (x->next) x->next->prev = x->prev;
1237         y = x->parent;
1238         if (y) {
1239                 if (IKS_TAG_CHILDREN (y) == x) IKS_TAG_CHILDREN (y) = x->next;
1240                 if (IKS_TAG_LAST_CHILD (y) == x) IKS_TAG_LAST_CHILD (y) = x->prev;
1241         }
1242 }
1243
1244 void
1245 iks_delete (iks *x)
1246 {
1247         if (x) iks_stack_delete (x->s);
1248 }
1249
1250 /*****  Node Traversing  *****/
1251
1252 iks *
1253 iks_next (iks *x)
1254 {
1255         if (x) return x->next;
1256         return NULL;
1257 }
1258
1259 iks *
1260 iks_next_tag (iks *x)
1261 {
1262         if (x) {
1263                 while (1) {
1264                         x = x->next;
1265                         if (NULL == x) break;
1266                         if (IKS_TAG == x->type) return x;
1267                 }
1268         }
1269         return NULL;
1270 }
1271
1272 iks *
1273 iks_prev (iks *x)
1274 {
1275         if (x) return x->prev;
1276         return NULL;
1277 }
1278
1279 iks *
1280 iks_prev_tag (iks *x)
1281 {
1282         if (x) {
1283                 while (1) {
1284                         x = x->prev;
1285                         if (NULL == x) break;
1286                         if (IKS_TAG == x->type) return x;
1287                 }
1288         }
1289         return NULL;
1290 }
1291
1292 iks *
1293 iks_parent (iks *x)
1294 {
1295         if (x) return x->parent;
1296         return NULL;
1297 }
1298
1299 iks *
1300 iks_root (iks *x)
1301 {
1302         if (x) {
1303                 while (x->parent)
1304                         x = x->parent;
1305         }
1306         return x;
1307 }
1308
1309 iks *
1310 iks_child (iks *x)
1311 {
1312         if (x) return IKS_TAG_CHILDREN (x);
1313         return NULL;
1314 }
1315
1316 iks *
1317 iks_first_tag (iks *x)
1318 {
1319         if (x) {
1320                 x = IKS_TAG_CHILDREN (x);
1321                 while (x) {
1322                         if (IKS_TAG == x->type) return x;
1323                         x = x->next;
1324                 }
1325         }
1326         return NULL;
1327 }
1328
1329 iks *
1330 iks_attrib (iks *x)
1331 {
1332         if (x) return IKS_TAG_ATTRIBS (x);
1333         return NULL;
1334 }
1335
1336 iks *
1337 iks_find (iks *x, const char *name)
1338 {
1339         iks *y;
1340
1341         if (!x) return NULL;
1342         y = IKS_TAG_CHILDREN (x);
1343         while (y) {
1344                 if (IKS_TAG == y->type && IKS_TAG_NAME (y) && strcmp (IKS_TAG_NAME (y), name) == 0) return y;
1345                 y = y->next;
1346         }
1347         return NULL;
1348 }
1349
1350 char *
1351 iks_find_cdata (iks *x, const char *name)
1352 {
1353         iks *y;
1354
1355         y = iks_find (x, name);
1356         if (!y) return NULL;
1357         y = IKS_TAG_CHILDREN (y);
1358         if (!y || IKS_CDATA != y->type) return NULL;
1359         return IKS_CDATA_CDATA (y);
1360 }
1361
1362 char *
1363 iks_find_attrib (iks *x, const char *name)
1364 {
1365         iks *y;
1366
1367         if (!x) return NULL;
1368
1369         y = IKS_TAG_ATTRIBS (x);
1370         while (y) {
1371                 if (IKS_ATTRIB_NAME (y) && strcmp (IKS_ATTRIB_NAME (y), name) == 0)
1372                         return IKS_ATTRIB_VALUE (y);
1373                 y = y->next;
1374         }
1375         return NULL;
1376 }
1377
1378 iks *
1379 iks_find_with_attrib (iks *x, const char *tagname, const char *attrname, const char *value)
1380 {
1381         iks *y;
1382
1383         if (NULL == x) return NULL;
1384
1385         if (tagname) {
1386                 for (y = IKS_TAG_CHILDREN (x); y; y = y->next) {
1387                         if (IKS_TAG == y->type
1388                                 && strcmp (IKS_TAG_NAME (y), tagname) == 0
1389                                 && iks_strcmp (iks_find_attrib (y, attrname), value) == 0) {
1390                                         return y;
1391                         }
1392                 }
1393         } else {
1394                 for (y = IKS_TAG_CHILDREN (x); y; y = y->next) {
1395                         if (IKS_TAG == y->type
1396                                 && iks_strcmp (iks_find_attrib (y, attrname), value) == 0) {
1397                                         return y;
1398                         }
1399                 }
1400         }
1401         return NULL;
1402 }
1403
1404 /*****  Node Information  *****/
1405
1406 ikstack *
1407 iks_stack (iks *x)
1408 {
1409         if (x) return x->s;
1410         return NULL;
1411 }
1412
1413 enum ikstype
1414 iks_type (iks *x)
1415 {
1416         if (x) return x->type;
1417         return IKS_NONE;
1418 }
1419
1420 char *
1421 iks_name (iks *x)
1422 {
1423         if (x) {
1424                 if (IKS_TAG == x->type)
1425                         return IKS_TAG_NAME (x);
1426                 else
1427                         return IKS_ATTRIB_NAME (x);
1428         }
1429         return NULL;
1430 }
1431
1432 char *
1433 iks_cdata (iks *x)
1434 {
1435         if (x) {
1436                 if (IKS_CDATA == x->type)
1437                         return IKS_CDATA_CDATA (x);
1438                 else
1439                         return IKS_ATTRIB_VALUE (x);
1440         }
1441         return NULL;
1442 }
1443
1444 size_t
1445 iks_cdata_size (iks *x)
1446 {
1447         if (x) return IKS_CDATA_LEN (x);
1448         return 0;
1449 }
1450
1451 int
1452 iks_has_children (iks *x)
1453 {
1454         if (x && IKS_TAG == x->type && IKS_TAG_CHILDREN (x)) return 1;
1455         return 0;
1456 }
1457
1458 int
1459 iks_has_attribs (iks *x)
1460 {
1461         if (x && IKS_TAG == x->type && IKS_TAG_ATTRIBS (x)) return 1;
1462         return 0;
1463 }
1464
1465 /*****  Serializing  *****/
1466
1467 static size_t
1468 escape_size (char *src, size_t len)
1469 {
1470         size_t sz;
1471         char c;
1472         int i;
1473
1474         sz = 0;
1475         for (i = 0; i < len; i++) {
1476                 c = src[i];
1477                 switch (c) {
1478                         case '&': sz += 5; break;
1479                         case '\'': sz += 6; break;
1480                         case '"': sz += 6; break;
1481                         case '<': sz += 4; break;
1482                         case '>': sz += 4; break;
1483                         default: sz++; break;
1484                 }
1485         }
1486         return sz;
1487 }
1488
1489 static char *
1490 my_strcat (char *dest, char *src, size_t len)
1491 {
1492         if (0 == len) len = strlen (src);
1493         memcpy (dest, src, len);
1494         return dest + len;
1495 }
1496
1497 static char *
1498 escape (char *dest, char *src, size_t len)
1499 {
1500         char c;
1501         int i;
1502         int j = 0;
1503
1504         for (i = 0; i < len; i++) {
1505                 c = src[i];
1506                 if ('&' == c || '<' == c || '>' == c || '\'' == c || '"' == c) {
1507                         if (i - j > 0) dest = my_strcat (dest, src + j, i - j);
1508                         j = i + 1;
1509                         switch (c) {
1510                         case '&': dest = my_strcat (dest, "&amp;", 5); break;
1511                         case '\'': dest = my_strcat (dest, "&apos;", 6); break;
1512                         case '"': dest = my_strcat (dest, "&quot;", 6); break;
1513                         case '<': dest = my_strcat (dest, "&lt;", 4); break;
1514                         case '>': dest = my_strcat (dest, "&gt;", 4); break;
1515                         }
1516                 }
1517         }
1518         if (i - j > 0) dest = my_strcat (dest, src + j, i - j);
1519         return dest;
1520 }
1521
1522 char *
1523 iks_string (ikstack *s, iks *x)
1524 {
1525         size_t size;
1526         int level, dir;
1527         iks *y, *z;
1528         char *ret, *t;
1529
1530         if (!x) return NULL;
1531
1532         if (x->type == IKS_CDATA) {
1533                 if (s) {
1534                         return iks_stack_strdup (s, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
1535                 } else {
1536                         ret = iks_malloc (IKS_CDATA_LEN (x));
1537                         memcpy (ret, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
1538                         return ret;
1539                 }
1540         }
1541
1542         size = 0;
1543         level = 0;
1544         dir = 0;
1545         y = x;
1546         while (1) {
1547                 if (dir==0) {
1548                         if (y->type == IKS_TAG) {
1549                                 size++;
1550                                 size += strlen (IKS_TAG_NAME (y));
1551                                 for (z = IKS_TAG_ATTRIBS (y); z; z = z->next) {
1552                                         size += 4 + strlen (IKS_ATTRIB_NAME (z))
1553                                                 + escape_size (IKS_ATTRIB_VALUE (z), strlen (IKS_ATTRIB_VALUE (z)));
1554                                 }
1555                                 if (IKS_TAG_CHILDREN (y)) {
1556                                         size++;
1557                                         y = IKS_TAG_CHILDREN (y);
1558                                         level++;
1559                                         continue;
1560                                 } else {
1561                                         size += 2;
1562                                 }
1563                         } else {
1564                                 size += escape_size (IKS_CDATA_CDATA (y), IKS_CDATA_LEN (y));
1565                         }
1566                 }
1567                 z = y->next;
1568                 if (z) {
1569                         if (0 == level) {
1570                                 if (IKS_TAG_CHILDREN (y)) size += 3 + strlen (IKS_TAG_NAME (y));
1571                                 break;
1572                         }
1573                         y = z;
1574                         dir = 0;
1575                 } else {
1576                         y = y->parent;
1577                         level--;
1578                         if (level >= 0) size += 3 + strlen (IKS_TAG_NAME (y));
1579                         if (level < 1) break;
1580                         dir = 1;
1581                 }
1582         }
1583
1584         if (s) ret = iks_stack_alloc (s, size + 1);
1585         else ret = iks_malloc (size + 1);
1586
1587         if (!ret) return NULL;
1588
1589         t = ret;
1590         level = 0;
1591         dir = 0;
1592         while (1) {
1593                 if (dir==0) {
1594                         if (x->type == IKS_TAG) {
1595                                 *t++ = '<';
1596                                 t = my_strcat (t, IKS_TAG_NAME (x), 0);
1597                                 y = IKS_TAG_ATTRIBS (x);
1598                                 while (y) {
1599                                         *t++ = ' ';
1600                                         t = my_strcat (t, IKS_ATTRIB_NAME (y), 0);
1601                                         *t++ = '=';
1602                                         *t++ = '\'';
1603                                         t = escape (t, IKS_ATTRIB_VALUE (y), strlen (IKS_ATTRIB_VALUE (y)));
1604                                         *t++ = '\'';
1605                                         y = y->next;
1606                                 }
1607                                 if (IKS_TAG_CHILDREN (x)) {
1608                                         *t++ = '>';
1609                                         x = IKS_TAG_CHILDREN (x);
1610                                         level++;
1611                                         continue;
1612                                 } else {
1613                                         *t++ = '/';
1614                                         *t++ = '>';
1615                                 }
1616                         } else {
1617                                 t = escape (t, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
1618                         }
1619                 }
1620                 y = x->next;
1621                 if (y) {
1622                         if (0 == level) {
1623                                 if (IKS_TAG_CHILDREN (x)) {
1624                                         *t++ = '<';
1625                                         *t++ = '/';
1626                                         t = my_strcat (t, IKS_TAG_NAME (x), 0);
1627                                         *t++ = '>';
1628                                 }
1629                                 break;
1630                         }
1631                         x = y;
1632                         dir = 0;
1633                 } else {
1634                         x = x->parent;
1635                         level--;
1636                         if (level >= 0) {
1637                                         *t++ = '<';
1638                                         *t++ = '/';
1639                                         t = my_strcat (t, IKS_TAG_NAME (x), 0);
1640                                         *t++ = '>';
1641                                 }
1642                         if (level < 1) break;
1643                         dir = 1;
1644                 }
1645         }
1646         *t = '\0';
1647
1648         return ret;
1649 }
1650
1651 /*****  Copying  *****/
1652
1653 iks *
1654 iks_copy_within (iks *x, ikstack *s)
1655 {
1656         int level=0, dir=0;
1657         iks *copy = NULL;
1658         iks *cur = NULL;
1659         iks *y;
1660
1661         while (1) {
1662                 if (dir == 0) {
1663                         if (x->type == IKS_TAG) {
1664                                 if (copy == NULL) {
1665                                         copy = iks_new_within (IKS_TAG_NAME (x), s);
1666                                         cur = copy;
1667                                 } else {
1668                                         cur = iks_insert (cur, IKS_TAG_NAME (x));
1669                                 }
1670                                 for (y = IKS_TAG_ATTRIBS (x); y; y = y->next) {
1671                                         iks_insert_attrib (cur, IKS_ATTRIB_NAME (y), IKS_ATTRIB_VALUE (y));
1672                                 }
1673                                 if (IKS_TAG_CHILDREN (x)) {
1674                                         x = IKS_TAG_CHILDREN (x);
1675                                         level++;
1676                                         continue;
1677                                 } else {
1678                                         cur = cur->parent;
1679                                 }
1680                         } else {
1681                                 iks_insert_cdata (cur, IKS_CDATA_CDATA (x), IKS_CDATA_LEN (x));
1682                         }
1683                 }
1684                 y = x->next;
1685                 if (y) {
1686                         if (0 == level) break;
1687                         x = y;
1688                         dir = 0;
1689                 } else {
1690                         if (level < 2) break;
1691                         level--;
1692                         x = x->parent;
1693                         cur = cur->parent;
1694                         dir = 1;
1695                 }
1696         }
1697         return copy;
1698 }
1699
1700 iks *
1701 iks_copy (iks *x)
1702 {
1703         return iks_copy_within (x, iks_stack_new (sizeof (struct iks_tag) * 6, 256));
1704 }
1705 /* iksemel (XML parser for Jabber)
1706 ** Copyright (C) 2000-2003 Gurer Ozen <madcat@e-kolay.net>
1707 ** This code is free software; you can redistribute it and/or
1708 ** modify it under the terms of GNU Lesser General Public License.
1709 */
1710
1711 #include "common.h"
1712 #include "iksemel.h"
1713
1714 struct dom_data {
1715         iks **iksptr;
1716         iks *current;
1717         size_t chunk_size;
1718 };
1719
1720 static int
1721 tagHook (struct dom_data *data, char *name, char **atts, int type)
1722 {
1723         iks *x;
1724
1725         if (IKS_OPEN == type || IKS_SINGLE == type) {
1726                 if (data->current) {
1727                         x = iks_insert (data->current, name);
1728                 } else {
1729                         ikstack *s;
1730                         s = iks_stack_new (data->chunk_size, data->chunk_size);
1731                         x = iks_new_within (name, s);
1732                 }
1733                 if (atts) {
1734                         int i=0;
1735                         while (atts[i]) {
1736                                 iks_insert_attrib (x, atts[i], atts[i+1]);
1737                                 i += 2;
1738                         }
1739                 }
1740                 data->current = x;
1741         }
1742         if (IKS_CLOSE == type || IKS_SINGLE == type) {
1743                 x = iks_parent (data->current);
1744                 if (x)
1745                         data->current = x;
1746                 else {
1747                         *(data->iksptr) = data->current;
1748                         data->current = NULL;
1749                 }
1750         }
1751         return IKS_OK;
1752 }
1753
1754 static int
1755 cdataHook (struct dom_data *data, char *cdata, size_t len)
1756 {
1757         if (data->current) iks_insert_cdata (data->current, cdata, len);
1758         return IKS_OK;
1759 }
1760
1761 static void
1762 deleteHook (struct dom_data *data)
1763 {
1764         if (data->current) iks_delete (data->current);
1765         data->current = NULL;
1766 }
1767
1768 iksparser *
1769 iks_dom_new (iks **iksptr)
1770 {
1771         ikstack *s;
1772         struct dom_data *data;
1773
1774         *iksptr = NULL;
1775         s = iks_stack_new (DEFAULT_DOM_CHUNK_SIZE, 0);
1776         if (!s) return NULL;
1777         data = iks_stack_alloc (s, sizeof (struct dom_data));
1778         data->iksptr = iksptr;
1779         data->current = NULL;
1780         data->chunk_size = DEFAULT_DOM_IKS_CHUNK_SIZE;
1781         return iks_sax_extend (s, data, (iksTagHook *) tagHook, (iksCDataHook *) cdataHook, (iksDeleteHook *) deleteHook);
1782 }
1783
1784 void
1785 iks_set_size_hint (iksparser *prs, size_t approx_size)
1786 {
1787         size_t cs;
1788         struct dom_data *data = iks_user_data (prs);
1789
1790         cs = approx_size / 10;
1791         if (cs < DEFAULT_DOM_IKS_CHUNK_SIZE) cs = DEFAULT_DOM_IKS_CHUNK_SIZE;
1792         data->chunk_size = cs;
1793 }
1794
1795 iks *
1796 iks_tree (const char *xml_str, size_t len, int *err)
1797 {
1798         iksparser *prs;
1799         iks *x;
1800         int e;
1801
1802         if (0 == len) len = strlen (xml_str);
1803         prs = iks_dom_new (&x);
1804         if (!prs) {
1805                 if (err) *err = IKS_NOMEM;
1806                 return NULL;
1807         }
1808         e = iks_parse (prs, xml_str, len, 1);
1809         if (err) *err = e;
1810         iks_parser_delete (prs);
1811         return x;
1812 }
1813
1814 int
1815 iks_load (const char *fname, iks **xptr)
1816 {
1817         iksparser *prs;
1818         char *buf;
1819         FILE *f;
1820         int len, done = 0;
1821         int ret;
1822
1823         *xptr = NULL;
1824
1825         buf = iks_malloc (FILE_IO_BUF_SIZE);
1826         if (!buf) return IKS_NOMEM;
1827         ret = IKS_NOMEM;
1828         prs = iks_dom_new (xptr);
1829         if (prs) {
1830                 f = fopen (fname, "r");
1831                 if (f) {
1832                         while (0 == done) {
1833                                 len = fread (buf, 1, FILE_IO_BUF_SIZE, f);
1834                                 if (len < FILE_IO_BUF_SIZE) {
1835                                         if (0 == feof (f)) {
1836                                                 ret = IKS_FILE_RWERR;
1837                                                 len = 0;
1838                                         }
1839                                         done = 1;
1840                                 }
1841                                 if (len > 0) {
1842                                         int e;
1843                                         e = iks_parse (prs, buf, len, done);
1844                                         if (IKS_OK != e) {
1845                                                 ret = e;
1846                                                 break;
1847                                         }
1848                                         if (done) ret = IKS_OK;
1849                                 }
1850                         }
1851                         fclose (f);
1852                 } else {
1853                         if (ENOENT == errno) ret = IKS_FILE_NOFILE;
1854                         else ret = IKS_FILE_NOACCESS;
1855                 }
1856                 iks_parser_delete (prs);
1857         }
1858         iks_free (buf);
1859         return ret;
1860 }
1861
1862 int
1863 iks_save (const char *fname, iks *x)
1864 {
1865         FILE *f;
1866         char *data;
1867         int ret;
1868
1869         ret = IKS_NOMEM;
1870         data = iks_string (NULL, x);
1871         if (data) {
1872                 ret = IKS_FILE_NOACCESS;
1873                 f = fopen (fname, "w");
1874                 if (f) {
1875                         ret = IKS_FILE_RWERR;
1876                         if (fputs (data, f) >= 0) ret = IKS_OK;
1877                         fclose (f);
1878                 }
1879                 iks_free (data);
1880         }
1881         return ret;
1882 }