]> git.mxchange.org Git - flightgear.git/blob - utils/xmlgrep/xml.c
fix a buffer overflow
[flightgear.git] / utils / xmlgrep / xml.c
1 /* Copyright (c) 2007-2009 by Adalin B.V.
2  * Copyright (c) 2007-2009 by Erik Hofman
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *      * Redistributions of source code must retain the above copyright
8  *         notice, this list of conditions and the following disclaimer.
9  *      * Redistributions in binary form must reproduce the above copyright
10  *         notice, this list of conditions and the following disclaimer in the
11  *         documentation and/or other materials provided with the distribution.
12  *      * Neither the name of (any of) the copyrightholder(s) nor the
13  *         names of its contributors may be used to endorse or promote products
14  *         derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
19  * THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #ifdef WIN32
29 # define WIN32_LEAN_AND_MEAN
30 # include <windows.h>
31 # include <io.h>
32
33 typedef struct
34 {
35     HANDLE m;
36     void *p;
37 } SIMPLE_UNMMAP;
38
39 #else   /* !WIN32 */
40 # include <sys/mman.h>
41 # include <unistd.h>
42 #endif
43
44 #ifndef NDEBUG
45 # include <stdio.h>
46 #endif
47 #include <stdlib.h>     /* free, malloc */
48 #include <string.h>
49 #include <fcntl.h>
50 #include <sys/stat.h>
51 #include <sys/types.h>
52 #include <assert.h>
53 #include <ctype.h>
54 #ifndef _MSC_VER
55 # include <strings.h>  /* strncasecmp */
56 #else
57 # define strncasecmp strnicmp
58 #endif
59
60 #ifndef XML_NONVALIDATING
61 #include "xml.h"
62
63 static const char *__xml_error_str[XML_MAX_ERROR];
64
65 struct _xml_error
66 {
67     char *pos;
68     int err_no;
69 };
70
71 static void __xmlErrorSet(const void *, const char *, unsigned int);
72 # define xmlErrorSet(a, b, c)   __xmlErrorSet(a, b, c)
73 #else /* !XML_NONVALIDATING */
74 # define xmlErrorSet(a, b, c)
75 #endif
76
77 /*
78  * It is required for both the rood node and the normal xml nodes to both
79  * have 'char *name' defined as the first entry. The code tests whether
80  * name == 0 to detect the root node.
81  */
82 struct _root_id
83 {
84     char *name;
85     char *start;
86     size_t len;
87     int fd;
88 #ifndef XML_NONVALIDATING
89     struct _xml_error *info;
90 #endif
91 # ifdef WIN32
92     SIMPLE_UNMMAP un;
93 # endif
94 };
95
96 struct _xml_id
97 {
98     char *name;
99     char *start;
100     size_t len;
101     size_t name_len;
102 #ifndef XML_NONVALIDATING
103     struct _root_id *root;
104 #endif
105 };
106
107 static char *__xmlNodeCopy(const char *, size_t *, const char **);
108 static char *__xmlNodeGetPath(const char *, size_t *, char **, size_t *);
109 static char *__xmlNodeGet(const char *, size_t *, char **, size_t *, size_t *);
110 static char *__xmlCommentSkip(const char *, size_t);
111 static char *__xmlInfoProcess(const char *, size_t);
112
113 static void *__xml_memncasecmp(const char *, size_t *, char **, size_t *);
114
115 #ifdef WIN32
116 /*
117  * map 'filename' and return a pointer to it.
118  */
119 static void *simple_mmap(int, size_t, SIMPLE_UNMMAP *);
120 static void simple_unmmap(SIMPLE_UNMMAP *);
121
122 # define mmap(a,b,c,d,e,f)       simple_mmap((e), (b), &rid->un)
123 # define munmap(a,b)             simple_unmmap(&rid->un)
124 #endif
125
126 #ifndef NDEBUG
127 # define PRINT(a, b, c) { \
128    if (a) { \
129       size_t q, len = c; \
130       if (b < c) len = b; \
131       if (len < 50000) { \
132          printf("(%i) '", len); \
133          for (q=0; q<len; q++) printf("%c", ((char *)(a))[q]); \
134          printf("'\n"); \
135       } else printf("Length (%u) seems too large at line %i\n",len, __LINE__); \
136    } else printf("NULL pointer at line %i\n", __LINE__); \
137 }
138 #endif
139
140 void *
141 xmlOpen(const char *filename)
142 {
143     struct _root_id *rid = 0;
144
145     if (filename)
146     {
147         int fd = open(filename, O_RDONLY);
148         if (fd > 0)
149         {
150             rid = malloc(sizeof(struct _root_id));
151             if (rid)
152             {
153                 struct stat statbuf;
154                 void *mm;
155
156                 fstat(fd, &statbuf);
157                 mm = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0L);
158                 if (mm != (void *)-1)
159                 {
160                     rid->fd = fd;
161                     rid->start = mm;
162                     rid->len = statbuf.st_size;
163                     rid->name = 0;
164 #ifndef XML_NONVALIDATING
165                     rid->info = 0;
166 #endif
167                 }
168             }
169         }
170     }
171
172     return (void *)rid;
173 }
174
175 void
176 xmlClose(void *id)
177 {
178      struct _root_id *rid = (struct _root_id *)id;
179
180      assert(rid != 0);
181      assert(rid->name == 0);
182
183      munmap(rid->start, rid->len);
184      close(rid->fd);
185
186      if (rid->info) free(rid->info);
187      free(rid);
188      id = 0;
189 }
190
191 void *
192 xmlNodeGet(const void *id, const char *path)
193 {
194     struct _xml_id *xid = (struct _xml_id *)id;
195     struct _xml_id *xsid = 0;
196     size_t len, slen;
197     char *ptr, *node;
198
199     assert(id != 0);
200     assert(path != 0);
201
202     node = (char *)path;
203     len = xid->len;
204     slen = strlen(path);
205     ptr = __xmlNodeGetPath(xid->start, &len, &node, &slen);
206     if (ptr)
207     {
208         xsid = malloc(sizeof(struct _xml_id));
209         if (xsid)
210         {
211              xsid->len = len;
212              xsid->start = ptr;
213              xsid->name_len = slen;
214              xsid->name = node;
215 #ifndef XML_NONVALIDATING
216              if (xid->name)
217                 xsid->root = xid->root;
218              else
219                 xsid->root = (struct _root_id *)xid;
220 #endif
221         }
222         else
223         {
224             xmlErrorSet(xid, 0, XML_OUT_OF_MEMORY);
225         }
226     }
227     else if (slen == 0)
228     {
229         xmlErrorSet(xid, node, len);
230     }
231
232     return (void *)xsid;
233 }
234
235 void *
236 xmlNodeCopy(const void *id, const char *path)
237 {
238     struct _xml_id *xid = (struct _xml_id *)id;
239     struct _xml_id *xsid = 0;
240     char *ptr, *node, *p;
241     size_t slen, len;
242
243     node = (char *)path;
244     len = xid->len;
245     slen = strlen(path);
246     ptr = __xmlNodeGetPath(xid->start, &len, &node, &slen);
247     if (ptr)
248     {
249         xsid = malloc(sizeof(struct _xml_id) + len);
250         if (xsid)
251         {
252             p = (char *)xsid + sizeof(struct _xml_id);
253
254             xsid->len = len;
255             xsid->start = p;
256             xsid->name_len = slen;
257             xsid->name = node;
258 #ifndef XML_NONVALIDATING
259             if (xid->name)
260                 xsid->root = xid->root;
261             else
262                 xsid->root = (struct _root_id *)xid;
263 #endif
264
265             memcpy(xsid->start, ptr, len);
266         }
267         else
268         {
269             xmlErrorSet(xid, 0, XML_OUT_OF_MEMORY);
270         }
271     }
272     else if (slen == 0)
273     {
274         xmlErrorSet(xid, node, len);
275     }
276
277    return (void *)xsid;
278 }
279
280 char *
281 xmlNodeGetName(const void *id)
282 {
283     struct _xml_id *xid = (struct _xml_id *)id;
284     size_t len;
285     char *ret;
286
287     assert(xid != 0);
288
289     len = xid->name_len;
290     ret = malloc(len+1);
291     if (ret)
292     {
293         memcpy(ret, xid->name, len);
294         *(ret + len) = 0;
295     }
296     else
297     {
298         xmlErrorSet(xid, 0, XML_OUT_OF_MEMORY);
299     }
300
301     return ret;
302 }
303
304 size_t
305 xmlNodeCopyName(const void *id, char *buf, size_t buflen)
306 {
307     struct _xml_id *xid = (struct _xml_id *)id;
308     size_t slen = 0;
309  
310     assert(buf != 0);
311     assert(buflen > 0);
312
313     slen = xid->name_len;
314     if (slen >= buflen)
315     {
316         slen = buflen-1;
317         xmlErrorSet(xid, 0, XML_TRUNCATE_RESULT);
318     }
319     memcpy(buf, xid->name, slen);
320     *(buf + slen) = 0;
321
322     return slen;
323 }
324
325 unsigned int
326 xmlNodeGetNum(const void *id, const char *path)
327 {
328     struct _xml_id *xid = (struct _xml_id *)id;
329     size_t num = 0;
330
331     assert(xid != 0);
332     assert(path != 0);
333
334     if (xid->len)
335     {
336         char *nodename, *pathname;
337         size_t len, slen;
338         char *p;
339
340         nodename = (char *)path;
341         if (*path == '/') nodename++;
342         slen = strlen(nodename);
343
344         pathname = strchr(nodename, '/');
345         if (pathname)
346         {
347            char *node;
348
349            len = xid->len;
350            pathname++;
351            slen -= pathname-nodename;
352            node = pathname;
353            p = __xmlNodeGetPath(xid->start, &len, &node, &slen);
354            if (p == 0 && slen == 0)
355            {
356                xmlErrorSet(xid, node, len);
357            }
358         }
359         else
360         {
361             p = xid->start;
362             len = xid->len;
363         }
364
365         if (p)
366         {
367             char *ret, *node = nodename;
368             ret = __xmlNodeGet(p, &len, &node, &slen, &num);
369             if (ret == 0 && slen == 0)
370             {
371                 xmlErrorSet(xid, node, len);
372                 num = 0;
373             }
374         }
375     }
376
377     return num;
378 }
379
380 void *
381 xmlNodeGetPos(const void *pid, void *id, const char *element, size_t num)
382 {
383     struct _xml_id *xpid = (struct _xml_id *)pid;
384     struct _xml_id *xid = (struct _xml_id *)id;
385     size_t len, slen;
386     char *ptr, *node;
387     void *ret = 0;
388
389     assert(xpid != 0);
390     assert(xid != 0);
391     assert(element != 0);
392
393     len = xpid->len;
394     slen = strlen(element);
395     node = (char *)element;
396     ptr = __xmlNodeGet(xpid->start, &len, &node, &slen, &num);
397     if (ptr)
398     {
399          xid->len = len;
400          xid->start = ptr;
401          xid->name = node;
402          xid->name_len = slen;
403          ret = xid;
404     }
405     else if (slen == 0)
406     {
407         xmlErrorSet(xpid, node, len);
408     }
409
410     return ret;
411 }
412
413 char *
414 xmlGetString(const void *id)
415 {
416     struct _xml_id *xid = (struct _xml_id *)id;
417     char *str = 0;
418
419     assert(xid != 0);
420
421     if (xid->len)
422     {
423         char *ps, *pe, *pend;
424         int nlen;
425
426         ps = xid->start;
427         nlen = xid->len;
428         pend = ps+nlen;
429         pe = pend;
430
431         while ((ps<pe) && isspace(*ps)) ps++;
432         while ((pe>ps) && isspace(*pe)) pe--;
433         nlen = (pe-ps);
434         if (nlen)
435         {
436             str = malloc(nlen+1);
437             if (str)
438             {
439                 memcpy(str, ps, nlen);
440                 *(str+nlen) = 0;
441             }
442             else
443             {
444                 xmlErrorSet(xid, 0, XML_OUT_OF_MEMORY);
445             }
446         }
447     }
448
449     return str;
450 }
451
452 size_t
453 xmlCopyString(const void *id, char *buffer, size_t buflen)
454 {
455     struct _xml_id *xid = (struct _xml_id *)id;
456     size_t ret = 0;
457  
458     assert(xid != 0);
459     assert(buffer != 0);
460     assert(buflen > 0);
461
462     if (xid->len)
463     {
464         char *ps, *pe, *pend;
465         size_t slen, nlen;
466
467         *buffer = '\0';
468         nlen = buflen-1;
469         ps = xid->start;
470         slen = xid->len;
471         pend = ps+slen;
472         pe = pend;
473
474         while ((ps<pe) && isspace(*ps)) ps++;
475         while ((pe>ps) && isspace(*pe)) pe--;
476         nlen = (pe-ps);
477         if (nlen > slen) nlen = slen;
478
479         if (nlen)
480         {
481             if (nlen >= buflen)
482             {
483                 nlen = buflen-1;
484                 xmlErrorSet(xid, 0, XML_TRUNCATE_RESULT);
485             }
486             memcpy(buffer, ps, nlen);
487             *(buffer+nlen) = 0;
488             ret = nlen;
489         }
490     }
491
492     return ret;
493 }
494
495 int
496 xmlCompareString(const void *id, const char *s)
497 {
498     struct _xml_id *xid = (struct _xml_id *)id;
499     int ret = -1;
500
501     assert(xid != 0);
502     assert(s != 0);
503
504     if (xid->len && (strlen(s) > 0))
505     {
506         char *ps, *pe;
507
508         ps = xid->start;
509         pe = ps + xid->len;
510         pe--;
511
512         while ((ps<pe) && isspace(*ps)) ps++;
513         while ((pe>ps) && isspace(*pe)) pe--;
514         pe++;
515
516         ret = strncasecmp(ps, s, pe-ps);
517     }
518
519     return ret;
520 }
521
522 char *
523 xmlNodeGetString(const void *id, const char *path)
524 {
525     struct _xml_id *xid = (struct _xml_id *)id;
526     char *str = 0;
527
528     assert(xid != 0);
529     assert(path != 0);
530
531     if (xid->len)
532     {
533         size_t len = xid->len;
534         char *node = (char *)path;
535
536         str = __xmlNodeCopy(xid->start, &len, &path);
537         if (str)
538         {
539             char *ps, *pe, *pend;
540             int slen;
541
542             ps = str;
543             slen = strlen(str);
544             pend = ps+slen;
545             pe = pend-1;
546
547             while ((ps<pe) && isspace(*ps)) ps++;
548             while ((pe>ps) && isspace(*pe)) pe--;
549
550             *++pe = 0;
551             slen = (pe-ps);
552             if (slen && (ps>str)) memmove(str, ps, slen);
553             else if (!slen) *str = 0;
554         }
555         else
556         {
557             xmlErrorSet(xid, node, len);
558         }
559     }
560
561     return str;
562 }
563
564 size_t
565 xmlNodeCopyString(const void *id, const char *path, char *buffer, size_t buflen)
566 {
567     struct _xml_id *xid = (struct _xml_id *)id;
568     size_t ret = 0;
569
570     assert(xid != 0);
571     assert(path != 0);
572     assert(buffer != 0);
573     assert(buflen > 0);
574
575     if (xid->len)
576     {
577         char *str, *node;
578         size_t slen, nlen;
579
580         *buffer = '\0';
581         nlen = xid->len;
582         slen = strlen(path);
583         node = (char *)path;
584         str = __xmlNodeGetPath(xid->start, &nlen, &node, &slen);
585         if (str)
586         {
587             char *ps, *pe;
588
589             ps = str;
590             pe = ps+nlen-1;
591
592             while ((ps<pe) && isspace(*ps)) ps++;
593             while ((pe>ps) && isspace(*pe)) pe--;
594
595             nlen = (pe-ps)+1;
596             if (nlen >= buflen)
597             {
598                 nlen = buflen-1;
599                 xmlErrorSet(xid, 0, XML_TRUNCATE_RESULT);
600             }
601
602             memcpy(buffer, ps, nlen);
603             *(buffer + nlen) = '\0';
604             ret = nlen;
605         }
606         else if (slen == 0)
607         {
608             xmlErrorSet(xid, node, nlen);
609         }
610     }
611
612     return ret;
613 }
614
615 int
616 xmlNodeCompareString(const void *id, const char *path, const char *s)
617 {
618     struct _xml_id *xid = (struct _xml_id *)id;
619     int ret = -1;
620
621     assert(xid != 0);
622     assert(path != 0);
623     assert(s != 0);
624
625     if (xid->len && (strlen(s) > 0))
626     {
627         char *node, *str, *ps, *pe;
628         size_t len, slen;
629
630         len = xid->len;
631         slen = strlen(path);
632         node = (char *)path;
633         str = __xmlNodeGetPath(xid->start, &len, &node, &slen);
634         if (str)
635         {
636             ps = str;
637             pe = ps + len;
638             pe--;
639
640             while ((ps<pe) && isspace(*ps)) ps++;
641             while ((pe>ps) && isspace(*pe)) pe--;
642             pe++;
643
644             ret = strncasecmp(ps, s, pe-ps);
645         }
646         else if (slen == 0)
647         {
648             xmlErrorSet(xid, node, len);
649         }
650     }
651
652     return ret;
653 }
654
655 long int
656 xmlGetInt(const void *id)
657 {
658     struct _xml_id *xid = (struct _xml_id *)id;
659     long int li = 0;
660
661     assert(xid != 0);
662
663     if (xid->len)
664     {
665         char *end = xid->start + xid->len;
666         li = strtol(xid->start, &end, 10);
667     }
668
669     return li;
670 }
671
672 long int
673 xmlNodeGetInt(const void *id, const char *path)
674 {
675     struct _xml_id *xid = (struct _xml_id *)id;
676     long int li = 0;
677
678     assert(xid != 0);
679     assert(path != 0);
680
681     if (xid->len)
682     {
683         size_t len, slen;
684         char *str, *node;
685
686         len = xid->len;
687         slen = strlen(path);
688         node = (char *)path;
689         str = __xmlNodeGetPath(xid->start, &len, &node, &slen);
690         if (str)
691         {
692             char *end = str+len;
693             li = strtol(str, &end, 10);
694         }
695         else if (slen == 0)
696         {
697             xmlErrorSet(xid, node, len);
698         }
699     }
700
701     return li;
702 }
703
704 double
705 xmlGetDouble(const void *id)
706 {
707     struct _xml_id *xid = (struct _xml_id *)id;
708     double d = 0.0;
709
710     assert(xid != 0);
711
712     if (xid->len)
713     {
714         char *end = xid->start + xid->len;
715         d = strtod(xid->start, &end);
716     }
717
718     return d;
719 }
720
721 double
722 xmlNodeGetDouble(const void *id, const char *path)
723 {
724     struct _xml_id *xid = (struct _xml_id *)id;
725     double d = 0.0;
726
727     assert(xid != 0);
728     assert(path != 0);
729
730     if (xid->len)
731     {
732         size_t len, slen;
733         char *str, *node;
734
735         len = xid->len;
736         slen = strlen(path);
737         node = (char *)path;
738         str = __xmlNodeGetPath(xid->start, &len, &node, &slen);
739         if (str)
740         {
741             char *end = str+len;
742             d = strtod(str, &end);
743         }
744         else if (slen == 0)
745         {
746             xmlErrorSet(xid, node, len);
747         }
748     }
749
750     return d;
751 }
752
753 void *
754 xmlMarkId(const void *id)
755 {
756     struct _xml_id *xmid = 0;
757
758     assert(id != 0);
759
760     xmid = malloc(sizeof(struct _xml_id));
761     if (xmid)
762     {
763         struct _root_id *xrid = (struct _root_id *)id;
764         if (xrid->name == 0)
765         {
766             xmid->name = "";
767             xmid->start = xrid->start;
768             xmid->len = xrid->len;
769             xmid->name_len = 0;
770 #ifndef XML_NONVALIDATING
771             xmid->root = xrid;
772 #endif
773         }
774         else
775         {
776             memcpy(xmid, id, sizeof(struct _xml_id));
777         }
778     }
779     else
780     {
781         xmlErrorSet(id, 0, XML_OUT_OF_MEMORY);
782     }
783
784     return (void *)xmid;
785 }
786
787 double
788 xmlAttributeGetDouble(const void *id, const char *name)
789 {
790     struct _xml_id *xid = (struct _xml_id *)id;
791     double ret = 0.0;
792
793     assert(xid != 0);
794     assert(name != 0);
795
796     if (xid->name_len)
797     {
798         size_t slen = strlen(name);
799         char *ps, *pe;
800
801         assert(xid->start > xid->name);
802
803         ps = xid->name + xid->name_len + 1;
804         pe = xid->start - 1;
805         while (ps<pe)
806         {
807             while ((ps<pe) && isspace(*ps)) ps++;
808             if (((size_t)(pe-ps) > slen) && (strncasecmp(ps, name, slen) == 0))
809             {
810                 ps += slen;
811                 if ((ps<pe) && (*ps == '='))
812                 {
813                     char *start;
814
815                     ps++;
816                     if (*ps == '"' || *ps == '\'') ps++;
817                     else
818                     {
819                         xmlErrorSet(xid, ps, XML_ATTRIB_NO_OPENING_QUOTE);
820                         return 0;
821                     }
822
823                     start = ps;
824                     while ((ps<pe) && (*ps != '"') && (*ps != '\'')) ps++;
825                     if (ps<pe)
826                     {
827                         ret = strtod(start, &ps);
828                     }
829                     else
830                     {
831                         xmlErrorSet(xid, ps, XML_ATTRIB_NO_CLOSING_QUOTE);
832                         return 0;
833                     }
834                 }
835                 else
836                 {
837                     while ((ps<pe) && !isspace(*ps)) ps++;
838                     continue;
839                 }
840
841                 break;
842             }
843
844             while ((ps<pe) && !isspace(*ps)) ps++;
845         }
846     }
847
848     return ret;
849 }
850
851 long int
852 xmlAttributeGetInt(const void *id, const char *name)
853 {
854     struct _xml_id *xid = (struct _xml_id *)id;
855     long int ret = 0;
856
857     assert(xid != 0);
858     assert(name != 0);
859
860     if (xid->name_len)
861     {
862         size_t slen = strlen(name);
863         char *ps, *pe;
864
865         assert(xid->start > xid->name);
866
867         ps = xid->name + xid->name_len + 1;
868         pe = xid->start - 1;
869         while (ps<pe)
870         {
871             while ((ps<pe) && isspace(*ps)) ps++;
872             if (((size_t)(pe-ps) > slen) && (strncasecmp(ps, name, slen) == 0))
873             {
874                 ps += slen;
875                 if ((ps<pe) && (*ps == '='))
876                 {
877                     char *start;
878
879                     ps++;
880                     if (*ps == '"' || *ps == '\'') ps++;
881                     else
882                     {
883                         xmlErrorSet(xid, ps, XML_ATTRIB_NO_OPENING_QUOTE);
884                         return 0;
885                     }
886
887                     start = ps;
888                     while ((ps<pe) && (*ps != '"') && (*ps != '\'')) ps++;
889                     if (ps<pe)
890                     {
891                         ret = strtol(start, &ps, 10);
892                     }
893                     else
894                     {
895                        xmlErrorSet(xid, ps, XML_ATTRIB_NO_CLOSING_QUOTE);
896                        return 0;
897                    }
898                }
899                else
900                {
901                    while ((ps<pe) && isspace(*ps)) ps++;
902                    continue;
903                }
904
905                break;
906            }
907
908            while ((ps<pe) && !isspace(*ps)) ps++;
909         }
910     }
911
912     return ret;
913 }
914
915 char *
916 xmlAttributeGetString(const void *id, const char *name)
917 {
918     struct _xml_id *xid = (struct _xml_id *)id;
919     char *ret = 0;
920
921     assert(xid != 0);
922     assert(name != 0);
923
924     if (xid->name_len)
925     {
926         size_t slen = strlen(name);
927         char *ps, *pe;
928
929         assert(xid->start > xid->name);
930
931         ps = xid->name + xid->name_len + 1;
932         pe = xid->start - 1;
933         while (ps<pe)
934         {
935              while ((ps<pe) && isspace(*ps)) ps++;
936              if (((size_t)(pe-ps) > slen) && (strncasecmp(ps, name, slen) == 0))
937              {
938                  ps += slen;
939                  if ((ps<pe) && (*ps == '='))
940                  {
941                      char *start;
942
943                      ps++;
944                      if (*ps == '"' || *ps == '\'') ps++;
945                      else
946                      {
947                          xmlErrorSet(xid, ps, XML_ATTRIB_NO_OPENING_QUOTE);
948                          return 0;
949                      }
950
951                      start = ps;
952                      while ((ps<pe) && (*ps != '"') && (*ps != '\'')) ps++;
953                      if (ps<pe)
954                      {
955                          ret = malloc(ps-start);
956                          if (ret)
957                          {
958                              memcpy(ret, start, (ps-start));
959                              *(ret+(ps-start)) = '\0';
960                          }
961                          else
962                          {
963                              xmlErrorSet(xid, 0, XML_OUT_OF_MEMORY);
964                          }
965                      }
966                      else
967                      {
968                          xmlErrorSet(xid, ps, XML_ATTRIB_NO_CLOSING_QUOTE);
969                         return 0;
970                      }
971                  }
972                  else
973                  {
974                      while ((ps<pe) && !isspace(*ps)) ps++;
975                      continue;
976                  }
977
978
979                  break;
980              }
981
982              while ((ps<pe) && !isspace(*ps)) ps++;
983         }
984     }
985
986     return ret;
987 }
988
989 size_t
990 xmlAttributeCopyString(const void *id, const char *name,
991                                        char *buffer, size_t buflen)
992 {
993     struct _xml_id *xid = (struct _xml_id *)id;
994     size_t ret = 0;
995
996     assert(xid != 0);
997     assert(name != 0);
998     assert(buffer != 0);
999     assert(buflen > 0);
1000
1001     if (xid->name_len)
1002     {
1003         size_t slen = strlen(name);
1004         char *ps, *pe;
1005
1006         assert(xid->start > xid->name);
1007
1008         *buffer = '\0';
1009         ps = xid->name + xid->name_len + 1;
1010         pe = xid->start - 1;
1011         while (ps<pe)
1012         {
1013             while ((ps<pe) && isspace(*ps)) ps++;
1014             if (((size_t)(pe-ps) > slen) && (strncasecmp(ps, name, slen) == 0))
1015             {
1016                 ps += slen;
1017                 if ((ps<pe) && (*ps == '='))
1018                 {
1019                     char *start;
1020
1021                     ps++;
1022                     if (*ps == '"' || *ps == '\'') ps++;
1023                     else
1024                     {
1025                         xmlErrorSet(xid, ps, XML_ATTRIB_NO_OPENING_QUOTE);
1026                         return 0;
1027                     }
1028
1029                     start = ps;
1030                     while ((ps<pe) && (*ps != '"') && (*ps != '\'')) ps++;
1031                     if (ps<pe)
1032                     {
1033                         size_t restlen = ps-start;
1034                         if (restlen >= buflen)
1035                         {
1036                             restlen = buflen-1;
1037                             xmlErrorSet(xid, ps, XML_TRUNCATE_RESULT);
1038                         }
1039
1040                         memcpy(buffer, start, restlen);
1041                         *(buffer+restlen) = 0;
1042                         ret = restlen;
1043                     }
1044                     else
1045                     {
1046                         xmlErrorSet(xid, ps, XML_ATTRIB_NO_CLOSING_QUOTE);
1047                         return 0;
1048                     }
1049                 }
1050                 else
1051                 {
1052                     while ((ps<pe) && isspace(*ps)) ps++;
1053                     continue;
1054                 }
1055
1056                 break;
1057             }
1058
1059             while ((ps<pe) && !isspace(*ps)) ps++;
1060         }
1061     }
1062
1063     return ret;
1064 }
1065
1066 int
1067 xmlAttributeCompareString(const void *id, const char *name, const char *s)
1068 {
1069     struct _xml_id *xid = (struct _xml_id *)id;
1070     int ret = -1;
1071  
1072     assert(xid != 0);
1073     assert(name != 0);
1074     assert(s != 0);
1075
1076     if (xid->name_len && strlen(s))
1077     {
1078         size_t slen = strlen(name);
1079         char *ps, *pe;
1080
1081         assert(xid->start > xid->name);
1082
1083         ps = xid->name + xid->name_len + 1;
1084         pe = xid->start - 1;
1085         while (ps<pe)
1086         {
1087             while ((ps<pe) && isspace(*ps)) ps++;
1088             if (((size_t)(pe-ps) > slen) && (strncasecmp(ps, name, slen) == 0))
1089             {
1090                 ps += slen;
1091                 if ((ps<pe) && (*ps == '='))
1092                 {
1093                     char *start;
1094
1095                     ps++;
1096                     if (*ps == '"' || *ps == '\'') ps++;
1097                     else
1098                     {
1099                         xmlErrorSet(xid, ps, XML_ATTRIB_NO_OPENING_QUOTE);
1100                         return 0;
1101                     }
1102
1103                     start = ps;
1104                     while ((ps<pe) && (*ps != '"') && (*ps != '\'')) ps++;
1105                     if (ps<pe)
1106                     {
1107                         ret = strncasecmp(start, s, ps-start);
1108                     }
1109                     else
1110                     {
1111                         xmlErrorSet(xid, ps, XML_ATTRIB_NO_CLOSING_QUOTE);
1112                         return 0;
1113                     }
1114                 }
1115                 else
1116                 {
1117                     while ((ps<pe) && !isspace(*ps)) ps++;
1118                     continue;
1119                 }
1120
1121                 break;
1122             }
1123
1124             while ((ps<pe) && !isspace(*ps)) ps++;
1125         }
1126     }
1127
1128     return ret;
1129 }
1130
1131
1132 #ifndef XML_NONVALIDATING
1133 int
1134 xmlErrorGetNo(const void *id, int clear)
1135 {
1136     int ret = 0;
1137
1138     if (id)
1139     {
1140         struct _xml_id *xid = (struct _xml_id *)id;
1141         struct _root_id *rid;
1142
1143         if (xid->name) rid = xid->root;
1144         else rid = (struct _root_id *)xid;
1145
1146         assert(rid != 0);
1147
1148         if (rid->info)
1149         {
1150             struct _xml_error *err = rid->info;
1151
1152             ret = err->err_no;
1153             if (clear) err->err_no = 0;
1154         }
1155     }
1156
1157     return ret;
1158 }
1159
1160 size_t
1161 xmlErrorGetLineNo(const void *id, int clear)
1162 {
1163     size_t ret = 0;
1164
1165     if (id)
1166     {
1167         struct _xml_id *xid = (struct _xml_id *)id;
1168         struct _root_id *rid;
1169
1170         if (xid->name) rid = xid->root;
1171         else rid = (struct _root_id *)xid;
1172
1173         assert(rid != 0);
1174
1175         if (rid->info)
1176         {
1177             struct _xml_error *err = rid->info;
1178             char *ps = rid->start;
1179             char *pe = err->pos;
1180             char *new;
1181
1182             ret++;
1183             while (ps<pe)
1184             {
1185                new = memchr(ps, '\n', pe-ps);
1186                if (new)
1187                {
1188                   ps = new;
1189                   ret++;
1190                }
1191                ps++;
1192             }       
1193
1194             if (clear) err->err_no = 0;
1195         }
1196     }
1197
1198     return ret;
1199 }
1200
1201 const char *
1202 xmlErrorGetString(const void *id, int clear)
1203 {
1204     char *ret = 0;
1205
1206     if (id)
1207     {
1208         struct _xml_id *xid = (struct _xml_id *)id;
1209         struct _root_id *rid;
1210
1211         if (xid->name) rid = xid->root;
1212         else rid = (struct _root_id *)xid;
1213
1214         assert(rid != 0);
1215
1216         if (rid->info)
1217         {
1218             struct _xml_error *err = rid->info;
1219             if (XML_NO_ERROR <= err->err_no && err->err_no < XML_MAX_ERROR)
1220             {
1221                ret = (char *)__xml_error_str[err->err_no];
1222             }
1223             else
1224             {
1225                ret = "incorrect error number.";
1226             }
1227
1228             if (clear) err->err_no = 0;
1229         }
1230     }
1231
1232     return ret;
1233 }
1234 #endif
1235
1236 /* -------------------------------------------------------------------------- */
1237
1238 #ifndef XML_NONVALIDATING
1239 static const char *__xml_error_str[XML_MAX_ERROR] =
1240 {
1241     "no error.",
1242     "unable to allocate enough memory.",
1243     "unable to open file for reading.",
1244     "buffer too small to hold all data, truncating.",
1245     "incorrect comment section.",
1246     "bad information block.",
1247     "unexpected end of section.",
1248     "incompatible opening tag for element.",
1249     "missing or invalid closing tag for element.",
1250     "missing or invalid opening quote for attribute.",
1251     "missing or invalid closing quote for attribute."
1252 };
1253 #endif
1254
1255 char *
1256 __xmlNodeCopy(const char *start, size_t *len, const char **path)
1257 {
1258     char *node, *p, *ret = 0;
1259     size_t rlen, slen;
1260
1261     rlen = *len;
1262     slen = strlen(*path);
1263     node = (char *)*path;
1264     p = __xmlNodeGetPath(start, &rlen, &node, &slen);
1265     if (p && rlen)
1266     {
1267         ret = malloc(rlen+1);
1268         if (ret)
1269         {
1270             memcpy(ret, p, rlen);
1271             *(ret+rlen) = '\0';
1272         }
1273         else
1274         {
1275             xmlErrorSet(0, 0, XML_OUT_OF_MEMORY);
1276         }
1277     }
1278     else if (slen == 0)
1279     {
1280         *path = node;
1281         *len = rlen;
1282     }
1283
1284     return ret;
1285 }
1286
1287 char *
1288 __xmlNodeGetPath(const char *start, size_t *len, char **name, size_t *plen)
1289 {
1290     char *node;
1291     char *ret = 0;
1292
1293     assert(start != 0);
1294     assert(len != 0);
1295     assert(*len != 0);
1296     assert(name != 0);
1297     assert(*name != 0);
1298     assert(plen != 0);
1299     assert(*plen != 0);
1300
1301     if (*plen > *len) return 0;
1302
1303     node = *name;
1304     if (*node == '/') node++;
1305     if (*node != 0)
1306     {
1307         size_t plen, slen;
1308         char *path;
1309         size_t num;
1310
1311         slen = strlen(node);
1312         path = strchr(node, '/');
1313
1314         if (!path) plen = slen;
1315         else plen = path++ - node;
1316
1317         num = 0;
1318         ret = __xmlNodeGet(start, len, &node, &plen, &num);
1319         if (ret && path)
1320         {
1321             plen = slen - (path - *name);
1322             ret = __xmlNodeGetPath(ret, len, &path, &plen);
1323             *name = path;
1324         }
1325         else if (plen == 0)
1326         {
1327            *name = node;
1328         }
1329     }
1330
1331     return ret;
1332 }
1333
1334 char *
1335 __xmlNodeGet(const char *start, size_t *len, char **name, size_t *rlen, size_t *nodenum)
1336 {
1337     char *new, *cur, *ne, *ret = 0;
1338     char *element, *start_tag=0;
1339     size_t restlen, elementlen;
1340     size_t return_len = 0;
1341     int found, num;
1342
1343     assert(start != 0);
1344     assert(len != 0);
1345     assert(*len != 0);
1346     assert(name != 0);
1347     assert(*name != 0);
1348     assert(rlen != 0);
1349     assert(*rlen != 0);
1350     assert(nodenum != 0);
1351
1352     if (*rlen > *len)
1353     {
1354         *rlen = 0;
1355         *name = (char *)start;
1356         *len = XML_UNEXPECTED_EOF;
1357         return 0;
1358     }
1359
1360     found = 0;
1361     num = *nodenum;
1362     restlen = *len;
1363     cur = (char *)start;
1364     ne = cur + restlen;
1365
1366     /* search for an opening tag */
1367     while ((new = memchr(cur, '<', restlen)) != 0)
1368     {
1369         size_t len_remaining;
1370         char *rptr;
1371
1372         if (*(new+1) == '/')            /* end of section */
1373         {
1374             *len -= restlen-1;
1375              break;
1376         }
1377
1378         new++;
1379         restlen -= new-cur;
1380         cur = new;
1381
1382         if (*cur == '!') /* comment */
1383         {
1384             new = __xmlCommentSkip(cur, restlen);
1385             if (!new)
1386             {
1387                 *rlen = 0;
1388                 *name = cur;
1389                 *len = XML_INVALID_COMMENT;
1390                 return 0;
1391             }
1392             restlen -= new-cur;
1393             cur = new;
1394             continue;
1395         }
1396         else if (*cur == '?') /* info block */
1397         {
1398             new = __xmlInfoProcess(cur, restlen);
1399             if (!new)
1400             {
1401                 *rlen = 0;
1402                 *name = cur;
1403                 *len = XML_INVALID_INFO_BLOCK;
1404                 return 0;
1405             }
1406
1407             restlen -= new-cur;
1408             cur = new;
1409             continue;
1410         }
1411
1412         /*
1413          * get element name and a pointer to after the opening tag
1414          */
1415         element = *name;
1416         elementlen = *rlen;
1417         len_remaining = restlen;
1418         new = rptr = __xml_memncasecmp(cur, &restlen, &element, &elementlen);
1419         if (rptr)                       /* requested element was found */
1420         {
1421             return_len = elementlen;
1422             if (found == num)
1423             {
1424                 ret = new;
1425                 start_tag = element;
1426                 *rlen = elementlen;
1427             }
1428             else start_tag = 0;
1429         }
1430         else                            /* different element name was foud */
1431         {
1432             new = cur + (len_remaining - restlen);
1433             if (new >= ne)
1434             {
1435                 *rlen = 0;
1436                 *name = cur;
1437                 *len = XML_UNEXPECTED_EOF;
1438                 return 0;
1439             }
1440             element = *name;
1441         }
1442
1443         if (*(new-2) == '/')                            /* e.g. <test/> */
1444         {
1445             cur = new;
1446             if (rptr)
1447             {
1448                 if (found == num)
1449                 {
1450                     *name = start_tag;
1451                     *len = 0;
1452                 }
1453                 found++;
1454             }
1455             continue;
1456         }
1457
1458         /*
1459          * get the next xml tag
1460          */
1461         /* restlen -= new-cur; not necessary because of __xml_memncasecmp */
1462         cur = new;
1463         new = memchr(cur, '<', restlen);
1464         if (!new)
1465         {
1466             *rlen = 0;
1467             *name = cur;
1468             *len = XML_ELEMENT_NO_CLOSING_TAG;
1469             return 0;
1470         }       
1471
1472         new++;
1473         restlen -= new-cur;
1474         cur = new;
1475         if (*cur == '!')                                /* comment */
1476         {
1477             new = __xmlCommentSkip(cur, restlen);
1478             if (!new)
1479             {
1480                 *rlen = 0;
1481                 *name = cur;
1482                 *len = XML_INVALID_COMMENT;
1483                 return 0;
1484             }
1485             restlen -= new-cur;
1486             cur = new;
1487
1488             /*
1489              * look for the closing tag of the cascading block
1490              */
1491             new = memchr(cur, '<', restlen);
1492             if (!new)
1493             {
1494                 *rlen = 0;
1495                 *name = cur;
1496                 *len = XML_ELEMENT_NO_CLOSING_TAG;
1497                 return 0;
1498             }
1499             new++;
1500             restlen -= new-cur;
1501             cur = new;
1502         }
1503
1504         if (*cur != '/')                        /* cascading tag found */
1505         {
1506             char *node = "*";
1507             size_t slen = restlen+1;
1508             size_t nlen = 1;
1509             size_t pos = -1;
1510
1511             /*
1512              * recursively walk the xml tree from here
1513              */
1514             new = __xmlNodeGet(cur-1, &slen, &node, &nlen, &pos);
1515             if (!new)
1516             {
1517                 if (slen == restlen)
1518                 {
1519                     *rlen = 0;
1520                     *name = cur;
1521                     *len = XML_UNEXPECTED_EOF;
1522                     return 0;
1523                 }
1524                 else new = cur + slen;
1525             }
1526
1527             restlen -= slen;
1528             cur = new;
1529
1530             /* 
1531              * look for the closing tag of the cascading block
1532              */
1533             new = memchr(cur, '<', restlen);
1534             if (!new)
1535             {
1536                 *rlen = 0;
1537                 *name = cur;
1538                 *len = XML_ELEMENT_NO_CLOSING_TAG;
1539                 return 0;
1540             }
1541             new++;
1542             restlen -= new-cur;
1543             cur = new;
1544         }
1545
1546         if (*cur == '/')                                /* closing tag found */
1547         {
1548             if (!strncasecmp(new+1, element, elementlen))
1549             {
1550                 if (*(new+elementlen+1) != '>')
1551                 {
1552                     *rlen = 0;
1553                     *name = new+1;
1554                     *len = XML_ELEMENT_NO_CLOSING_TAG;
1555                     return 0;
1556                 }
1557
1558                 if (found == num)
1559                 {
1560                    if (start_tag)
1561                    {
1562                        *len = new-ret-1;
1563                        *name = start_tag;
1564                    }
1565                    else /* report error */
1566                    {
1567                        *rlen = 0;
1568                        *name = new;
1569                        *len = XML_ELEMENT_NO_OPENING_TAG;
1570                        return 0;
1571                    }
1572                 }
1573                 found++;
1574             }
1575             /* else proper closing tag not yet found, continue.     */
1576             /* TODO: could be a bad match to the opening tag though */
1577             /*       like: <test></teft>                            */
1578
1579             new = memchr(cur, '>', restlen);
1580             if (!new)
1581             {
1582                 *rlen = 0;
1583                 *name = cur;
1584                 *len = XML_ELEMENT_NO_CLOSING_TAG;
1585                 return 0;
1586             }
1587
1588             restlen -= new-cur;
1589             cur = new;
1590         }
1591         else
1592         {
1593             *rlen = 0;
1594             *name = cur;
1595             *len = XML_ELEMENT_NO_CLOSING_TAG;
1596             return 0;
1597         }
1598     }
1599
1600     if (found == 0)
1601     {
1602         ret = 0;
1603         *rlen = 0;
1604         *name = start_tag;
1605         *len = XML_NO_ERROR;    /* element not found, no real error */
1606     }
1607     else
1608     {
1609         *rlen = return_len;
1610         *nodenum = found;
1611     }
1612
1613     return ret;
1614 }
1615
1616 char *
1617 __xmlCommentSkip(const char *start, size_t len)
1618 {
1619     char *cur, *new;
1620
1621     cur = (char *)start;
1622     new = 0;
1623
1624     if (memcmp(cur, "!--", 3) == 0)
1625     {
1626         if (len < 6) return 0;                          /* <!-- --> */
1627
1628         cur += 3;
1629         len -= 3;
1630         do
1631         {
1632             new = memchr(cur, '-', len);
1633             if (new)
1634             {
1635                 len -= new - cur;
1636                 if ((len > 3) && (memcmp(new, "-->", 3) == 0))
1637                 {
1638                     new += 3;
1639                     len -= 3;
1640                     break;
1641                 }
1642                 cur = new+1;
1643             }
1644             else break;
1645         }
1646         while (new && (len > 2));
1647     }
1648
1649     return new;
1650 }
1651
1652 char *
1653 __xmlInfoProcess(const char *start, size_t len)
1654 {
1655     char *cur, *new;
1656
1657     cur = (char *)start;
1658     new = 0;
1659
1660     if (*cur == '?')
1661     {
1662         if (len < 3) return 0;                          /* <? ?> */
1663
1664         cur++;
1665         len--;
1666         new = memchr(cur, '?', len);
1667         if (!new || *(new+1) != '>') return 0;
1668
1669         new += 2;
1670     }
1671
1672     return new;
1673 }
1674
1675
1676 #define NOCASECMP(a,b)  ( ((a)^(b)) & 0xdf )
1677 void *
1678 __xml_memncasecmp(const char *haystack, size_t *haystacklen,
1679                   char **needle, size_t *needlelen)
1680 {
1681     char *rptr = 0;
1682
1683     if (haystack && needle && needlelen && (*needlelen > 0)
1684         && (*haystacklen >= *needlelen))
1685     {
1686         char *hs = (char *)haystack;
1687         char *ns;
1688         size_t i;
1689
1690         ns = *needle;
1691
1692         /* search for everything */
1693         if ((*ns == '*') && (*needlelen == 1))
1694         {
1695            char *p, *he = hs + *haystacklen;
1696
1697            while ((hs < he) && !isspace(*hs) && (*hs != '>')) hs++;
1698            if (*(hs-1) == '/') hs--;
1699
1700            *needle = (char *)haystack;
1701            *needlelen = hs - haystack;
1702
1703            p = hs;
1704            while ((hs < he) && (*hs != '>')) hs++;
1705            hs++;
1706      
1707            rptr = hs;
1708         }
1709         else
1710         {
1711             size_t nlen = *needlelen;
1712             char *he = hs + *haystacklen;
1713
1714             for (i=0; i<nlen; i++)
1715             {
1716                 if (NOCASECMP(*hs,*ns) && (*ns != '?')) break;
1717                 if (isspace(*hs) || (*hs == '/') || (*hs == '>')) break;
1718                 hs++;
1719                 ns++;
1720             }
1721
1722             if (i == nlen)
1723             {
1724                 *needle = (char *)haystack;
1725                 *needlelen = hs - haystack;
1726
1727                 while ((hs < he) && (*hs != '>')) hs++;
1728                 hs++;
1729
1730                 rptr = hs;
1731             }
1732             else /* not found */
1733             {
1734                 while((hs < he) && !isspace(*hs) && (*hs != '>')) hs++;
1735                 if (*(hs-1) == '/') hs--;
1736
1737                 *needle = (char *)haystack;
1738                 *needlelen = hs - haystack;
1739
1740                 while ((hs < he) && (*hs != '>')) hs++;
1741                 hs++;
1742             }
1743         }
1744
1745         *haystacklen -= hs - haystack;
1746     }
1747
1748     return rptr;
1749 }
1750
1751 #ifndef XML_NONVALIDATING
1752 void
1753 __xmlErrorSet(const void *id, const char *pos, unsigned int err_no)
1754 {
1755    struct _xml_id *xid = (struct _xml_id *)id;
1756    struct _root_id *rid;
1757
1758    assert(xid != 0);
1759
1760    if (xid->name) rid = xid->root;
1761    else rid = (struct _root_id *)xid;
1762
1763    assert(rid != 0);
1764    if (rid->info == 0)
1765    {
1766       rid->info = malloc(sizeof(struct _xml_error));
1767    }
1768
1769    if (rid->info)
1770    {
1771       struct _xml_error *err = rid->info;
1772
1773       err->pos = (char *)pos;
1774       err->err_no = err_no;
1775    }
1776 }
1777 #endif
1778
1779 #ifdef WIN32
1780 /* Source:
1781  * https://mollyrocket.com/forums/viewtopic.php?p=2529
1782  */
1783
1784 void *
1785 simple_mmap(int fd, size_t length, SIMPLE_UNMMAP *un)
1786 {
1787     HANDLE f;
1788     HANDLE m;
1789     void *p;
1790
1791     f = (HANDLE)_get_osfhandle(fd);
1792     if (!f) return NULL;
1793
1794     m = CreateFileMapping(f, NULL, PAGE_READONLY, 0, 0, NULL);
1795     if (!m) return NULL;
1796
1797     p = MapViewOfFile(m, FILE_MAP_READ, 0,0,0);
1798     if (!p)
1799     {
1800         CloseHandle(m);
1801         return NULL;
1802     }
1803
1804     if (un)
1805     {
1806         un->m = m;
1807         un->p = p;
1808     }
1809
1810     return p;
1811 }
1812
1813 void
1814 simple_unmmap(SIMPLE_UNMMAP *un)
1815 {
1816     UnmapViewOfFile(un->p);
1817     CloseHandle(un->m);
1818 }
1819 #endif