source: trunk/libtransmission/xml.c @ 2154

Last change on this file since 2154 was 2154, checked in by charles, 15 years ago
  • fix error checking large files reported by Gimp_
  • portability changes to pathname/filename building
  • small gratuitous changes
  • Property svn:keywords set to Date Rev Author Id
File size: 10.6 KB
Line 
1/******************************************************************************
2 * $Id: xml.c 2154 2007-06-18 19:39:52Z charles $
3 *
4 * Copyright (c) 2006 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include "transmission.h"
26
27/* http://www.w3.org/TR/2004/REC-xml-20040204/ */
28
29#define WS( cc ) \
30    ( ' ' == (cc) || '\t' == (cc) || '\n' == (cc) || '\r' == (cc) )
31#define TAGBEGIN                '<'
32#define TAGEND                  '>'
33#define TAGCLOSE                '/'
34#define NAMESPACESEP            ':'
35#define SQUOTEC                 '\''
36#define DQUOTEC                 '"'
37#define SQUOTES                 "'"
38#define DQUOTES                 "\""
39#define COMMENTBEGIN            "<!--"
40#define COMMENTEND              "-->"
41#define PROCINSTBEGIN           "<?"
42#define PROCINSTEND             "?>"
43#define CDATABEGIN              "<![CDATA["
44#define CDATAEND                "]]>"
45#define BANGBEGIN               "<!"
46#define BANGEND                 ">"
47#define CHECKNULL( bb, ee, rr ) \
48    { if( NULL == (bb) || (ee) <= (bb) ) return (rr); }
49#define justskip( bb, ee, ap, ot, ct ) \
50    ( skipthingy( (bb), (ee), (ap), (ot), (ct), NULL, NULL ) )
51
52static char *
53catrange( char * str, const char * begin, const char * end );
54static int
55skipall( const char * begin, const char * end, const char ** afterpos );
56static int
57nexttag( const char * begin, const char * end, const char ** tagpos );
58static int
59overtag( const char * begin, const char * end, const char ** overpos );
60static int
61tagname( const char * begin, const char * end,
62         const char ** tagstart, const char ** namestart, int * namelen );
63static int
64skipthingy( const char * begin, const char * end, const char ** afterpos,
65            const char * openthingy, const char * closethingy,
66            const char ** databegin, const char ** dataend );
67
68/* XXX check document charset, in http headers and/or <?xml> tag */
69
70const char *
71tr_xmlFindTag( const char * begin, const char * end, const char * tag )
72{
73    const char * name;
74    int len;
75
76    CHECKNULL( begin, end, NULL );
77
78    while( tagname( begin, end, &begin, &name, &len ) )
79    {
80        assert( NULL != begin && NULL != name && 0 < len );
81        if( 0 == tr_strncasecmp( tag, name, len ) )
82        {
83            return begin;
84        }
85        begin = tr_xmlSkipTag( begin, end );
86    }
87
88    return NULL;
89}
90
91const char *
92tr_xmlTagName( const char * begin, const char * end, int * len )
93{
94    CHECKNULL( begin, end, NULL );
95
96    if( tagname( begin, end, NULL, &begin, len ) )
97    {
98        return begin;
99    }
100
101    return NULL;
102}
103
104const char *
105tr_xmlTagContents( const char * begin, const char * end )
106{
107    CHECKNULL( begin, end, NULL );
108
109    if( nexttag( begin, end, &begin ) && overtag( begin, end, &begin ) )
110    {
111        begin = NULL;
112    }
113
114    return begin;
115}
116
117int
118tr_xmlVerifyContents( const char * begin, const char * end, const char * data,
119                      int ignorecase )
120{
121    int len;
122
123    CHECKNULL( begin, end, 1 );
124    len = strlen( data );
125
126    while( end > begin && WS( *begin ) )
127    {
128        begin++;
129    }
130    if( end - begin > len )
131    {
132        if( ignorecase )
133        {
134            return ( 0 != tr_strncasecmp( begin, data, len ) );
135        }
136        else
137        {
138            return ( 0 != memcmp( begin, data, len ) );
139        }
140    }
141
142    return 1;
143}
144
145const char *
146tr_xmlSkipTag( const char * begin, const char * end )
147{
148    CHECKNULL( begin, end, NULL );
149
150    if( nexttag( begin, end, &begin ) )
151    {
152        if( overtag( begin, end, &begin ) )
153        {
154            return begin;
155        }
156        while( NULL != begin )
157        {
158            if( nexttag( begin, end, &begin ) )
159            {
160                begin = tr_xmlSkipTag( begin, end );
161            }
162            else
163            {
164                CHECKNULL( begin, end, NULL );
165                overtag( begin, end, &begin );
166                return begin;
167            }
168        }
169    }
170
171    return NULL;
172}
173
174char *
175tr_xmlDupContents( const char * begin, const char * end )
176{
177    const char * ii, * cbegin, * cend;
178    char       * ret;
179    int          len;
180
181    CHECKNULL( begin, end, NULL );
182
183    ret = NULL;
184    len = strlen( CDATABEGIN );
185
186    while( end > begin )
187    {
188        ii = memchr( begin, TAGBEGIN, end - begin );
189        if( NULL == ii )
190        {
191            free( ret );
192            return NULL;
193        }
194        /* XXX expand entity references and such here */
195        ret = catrange( ret, begin, ii );
196        if( NULL == ret )
197        {
198            return NULL;
199        }
200        if( !skipthingy( ii, end, &begin, CDATABEGIN, CDATAEND,
201                         &cbegin, &cend ) )
202        {
203            ret = catrange( ret, cbegin, cend );
204            if( NULL == ret )
205            {
206                return NULL;
207            }
208        }
209        else if( skipall( ii, end, &begin ) )
210        {
211            if( end > ii + 1 && TAGCLOSE == ii[1] )
212            {
213                return ret;
214            }
215            begin = tr_xmlSkipTag( ii, end );
216        }
217    }
218
219    free( ret );
220
221    return NULL;
222}
223
224static char *
225catrange( char * str, const char * begin, const char * end )
226{
227    int    len;
228    char * ret;
229
230    if( NULL == str )
231    {
232        return tr_strndup( begin, end - begin );
233    }
234
235    len = strlen( str );
236    ret = realloc( str, len + end - begin + 1 );
237    if( NULL == ret )
238    {
239        free( str );
240        return NULL;
241    }
242
243    memcpy( ret + len, begin, end - begin );
244    ret[len + end - begin] = '\0';
245
246    return ret;
247}
248
249static int
250skipall( const char * begin, const char * end, const char ** afterpos )
251{
252    return ( justskip( begin, end, afterpos, COMMENTBEGIN, COMMENTEND ) &&
253             justskip( begin, end, afterpos, CDATABEGIN, CDATAEND ) &&
254             justskip( begin, end, afterpos, PROCINSTBEGIN, PROCINSTEND ) &&
255             justskip( begin, end, afterpos, BANGBEGIN, BANGEND ) );
256}
257
258/* returns true if a tag was found and it's a start or empty element tag */
259static int
260nexttag( const char * begin, const char * end, const char ** tagpos )
261{
262    *tagpos = NULL;
263    CHECKNULL( begin, end, 0 );
264
265    while( end > begin )
266    {
267        begin = memchr( begin, TAGBEGIN, end - begin );
268        CHECKNULL( begin, end, 0 );
269        if( justskip( begin, end, &begin, COMMENTBEGIN, COMMENTEND ) &&
270            justskip( begin, end, &begin, CDATABEGIN, CDATAEND ) &&
271            justskip( begin, end, &begin, PROCINSTBEGIN, PROCINSTEND ) &&
272            justskip( begin, end, &begin, BANGBEGIN, BANGEND ) )
273        {
274            *tagpos = begin;
275            begin++;
276            return ( end > begin && TAGCLOSE != *begin );
277        }
278    }
279
280    return 0;
281}
282
283/* returns true if the tag is an empty element such as <foo/> */
284static int
285overtag( const char * begin, const char * end, const char ** overpos )
286{
287    const char * ii;
288
289    assert( NULL != begin && end > begin && TAGBEGIN == *begin );
290
291    ii = begin + 1;
292    while( end > ii )
293    {
294        switch( *ii )
295        {
296            case DQUOTEC:
297                justskip( ii, end, &ii, DQUOTES, DQUOTES );
298                break;
299            case SQUOTEC:
300                justskip( ii, end, &ii, SQUOTES, SQUOTES );
301                break;
302            case TAGEND:
303                *overpos = ii + 1;
304                for( ii--; begin < ii; ii-- )
305                {
306                    if( TAGCLOSE == *ii )
307                    {
308                        return 1;
309                    }
310                    if( !WS( *ii ) )
311                    {
312                        break;
313                    }
314                }
315                return 0;
316            default:
317                ii++;
318                break;
319        }
320    }
321
322    *overpos = NULL;
323    return 0;
324}
325
326static int
327tagname( const char * begin, const char * end,
328         const char ** tagstart, const char ** namestart, int * namelen )
329{
330    const char * name, * ii;
331
332    CHECKNULL( begin, end, 0 );
333
334    if( nexttag( begin, end, &begin ) )
335    {
336        assert( NULL != begin && TAGBEGIN == *begin );
337        ii = begin + 1;
338        while( end > ii && WS( *ii ) )
339        {
340            ii++;
341        }
342        name = ii;
343        while( end > ii && TAGEND != *ii && !WS( *ii ) )
344        {
345            if( NAMESPACESEP == *ii )
346            {
347                name = ii + 1;
348            }
349            ii++;
350        }
351        if( end > ii && ii > name )
352        {
353            if( NULL != tagstart )
354            {
355                *tagstart = begin;
356            }
357            if( NULL != namestart )
358            {
359                *namestart = name;
360            }
361            if( NULL != namelen )
362            {
363                *namelen = ii - name;
364            }
365            return 1;
366        }
367    }
368
369    return 0;
370}
371
372static int
373skipthingy( const char * begin, const char * end, const char ** afterpos,
374            const char * openthingy, const char * closethingy,
375            const char ** databegin, const char ** dataend )
376 {
377    int len;
378
379    CHECKNULL( begin, end, 1 );
380    len = strlen( openthingy );
381    if( 0 != memcmp( begin, openthingy, MIN( end - begin, len ) ) )
382    {
383        return 1;
384    }
385
386    if( NULL != afterpos )
387    {
388        *afterpos = NULL;
389    }
390    if( NULL != databegin )
391    {
392        *databegin = NULL;
393    }
394    if( NULL != dataend )
395    {
396        *dataend = NULL;
397    }
398    if( end - begin <= len )
399    {
400        return 0;
401    }
402
403    begin += len;
404    if( NULL != databegin )
405    {
406        *databegin = begin;
407    }
408
409    len = strlen( closethingy );
410    begin = tr_memmem( begin, end - begin, closethingy, len );
411    if( NULL != dataend )
412    {
413        *dataend = begin;
414    }
415    if( NULL != afterpos && NULL != begin )
416    {
417        *afterpos = ( begin + len >= end ? NULL : begin + len );
418    }
419
420    return 0;
421}
Note: See TracBrowser for help on using the repository browser.