source: trunk/libtransmission/xml.c @ 1125

Last change on this file since 1125 was 920, checked in by joshe, 15 years ago

Merge nat-traversal branch to trunk.

  • Property svn:keywords set to Date Rev Author Id
File size: 10.6 KB
Line 
1/******************************************************************************
2 * $Id: xml.c 920 2006-09-25 18:37:45Z livings124 $
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                overtag( begin, end, &begin );
165                return begin;
166            }
167        }
168    }
169
170    return NULL;
171}
172
173char *
174tr_xmlDupContents( const char * begin, const char * end )
175{
176    const char * ii, * cbegin, * cend;
177    char       * ret;
178    int          len;
179
180    CHECKNULL( begin, end, NULL );
181
182    ret = NULL;
183    len = strlen( CDATABEGIN );
184
185    while( end > begin )
186    {
187        ii = memchr( begin, TAGBEGIN, end - begin );
188        if( NULL == ii )
189        {
190            free( ret );
191            return NULL;
192        }
193        /* XXX expand entity references and such here */
194        ret = catrange( ret, begin, ii );
195        if( NULL == ret )
196        {
197            return NULL;
198        }
199        if( !skipthingy( ii, end, &begin, CDATABEGIN, CDATAEND,
200                         &cbegin, &cend ) )
201        {
202            ret = catrange( ret, cbegin, cend );
203            if( NULL == ret )
204            {
205                return NULL;
206            }
207        }
208        else if( skipall( ii, end, &begin ) )
209        {
210            if( end > ii + 1 && TAGCLOSE == ii[1] )
211            {
212                return ret;
213            }
214            begin = tr_xmlSkipTag( ii, end );
215        }
216    }
217
218    free( ret );
219
220    return NULL;
221}
222
223static char *
224catrange( char * str, const char * begin, const char * end )
225{
226    int    len;
227    char * ret;
228
229    if( NULL == str )
230    {
231        return tr_dupstr( begin, end - begin );
232    }
233
234    len = strlen( str );
235    ret = realloc( str, len + end - begin + 1 );
236    if( NULL == ret )
237    {
238        free( str );
239        return NULL;
240    }
241
242    memcpy( ret + len, begin, end - begin );
243    ret[len + end - begin] = '\0';
244
245    return ret;
246}
247
248static int
249skipall( const char * begin, const char * end, const char ** afterpos )
250{
251    return ( justskip( begin, end, afterpos, COMMENTBEGIN, COMMENTEND ) &&
252             justskip( begin, end, afterpos, CDATABEGIN, CDATAEND ) &&
253             justskip( begin, end, afterpos, PROCINSTBEGIN, PROCINSTEND ) &&
254             justskip( begin, end, afterpos, BANGBEGIN, BANGEND ) );
255}
256
257/* returns true if a tag was found and it's a start or empty element tag */
258static int
259nexttag( const char * begin, const char * end, const char ** tagpos )
260{
261    CHECKNULL( begin, end, 0 );
262
263    while( end > begin )
264    {
265        begin = memchr( begin, TAGBEGIN, end - begin );
266        CHECKNULL( begin, end, 0 );
267        if( justskip( begin, end, &begin, COMMENTBEGIN, COMMENTEND ) &&
268            justskip( begin, end, &begin, CDATABEGIN, CDATAEND ) &&
269            justskip( begin, end, &begin, PROCINSTBEGIN, PROCINSTEND ) &&
270            justskip( begin, end, &begin, BANGBEGIN, BANGEND ) )
271        {
272            *tagpos = begin;
273            begin++;
274            if( end > begin )
275            {
276                return ( TAGCLOSE != *begin );
277            }
278            break;
279        }
280    }
281
282    *tagpos = NULL;
283    return 0;
284}
285
286/* returns true if the tag is an empty element such as <foo/> */
287static int
288overtag( const char * begin, const char * end, const char ** overpos )
289{
290    const char * ii;
291
292    assert( NULL != begin && end > begin && TAGBEGIN == *begin );
293
294    ii = begin + 1;
295    while( end > ii )
296    {
297        switch( *ii )
298        {
299            case DQUOTEC:
300                justskip( ii, end, &ii, DQUOTES, DQUOTES );
301                break;
302            case SQUOTEC:
303                justskip( ii, end, &ii, SQUOTES, SQUOTES );
304                break;
305            case TAGEND:
306                *overpos = ii + 1;
307                for( ii--; begin < ii; ii-- )
308                {
309                    if( TAGCLOSE == *ii )
310                    {
311                        return 1;
312                    }
313                    if( !WS( *ii ) )
314                    {
315                        break;
316                    }
317                }
318                return 0;
319            default:
320                ii++;
321                break;
322        }
323    }
324
325    *overpos = NULL;
326    return 0;
327}
328
329static int
330tagname( const char * begin, const char * end,
331         const char ** tagstart, const char ** namestart, int * namelen )
332{
333    const char * name, * ii;
334
335    CHECKNULL( begin, end, 0 );
336
337    if( nexttag( begin, end, &begin ) )
338    {
339        assert( NULL != begin && TAGBEGIN == *begin );
340        ii = begin + 1;
341        while( end > ii && WS( *ii ) )
342        {
343            ii++;
344        }
345        name = ii;
346        while( end > ii && TAGEND != *ii && !WS( *ii ) )
347        {
348            if( NAMESPACESEP == *ii )
349            {
350                name = ii + 1;
351            }
352            ii++;
353        }
354        if( end > ii && ii > name )
355        {
356            if( NULL != tagstart )
357            {
358                *tagstart = begin;
359            }
360            if( NULL != namestart )
361            {
362                *namestart = name;
363            }
364            if( NULL != namelen )
365            {
366                *namelen = ii - name;
367            }
368            return 1;
369        }
370    }
371
372    return 0;
373}
374
375static int
376skipthingy( const char * begin, const char * end, const char ** afterpos,
377            const char * openthingy, const char * closethingy,
378            const char ** databegin, const char ** dataend )
379 {
380    int len;
381
382    CHECKNULL( begin, end, 1 );
383    len = strlen( openthingy );
384    if( 0 != memcmp( begin, openthingy, MIN( end - begin, len ) ) )
385    {
386        return 1;
387    }
388
389    if( NULL != afterpos )
390    {
391        *afterpos = NULL;
392    }
393    if( NULL != databegin )
394    {
395        *databegin = NULL;
396    }
397    if( NULL != dataend )
398    {
399        *dataend = NULL;
400    }
401    if( end - begin <= len )
402    {
403        return 0;
404    }
405
406    begin += len;
407    if( NULL != databegin )
408    {
409        *databegin = begin;
410    }
411
412    len = strlen( closethingy );
413    begin = tr_memmem( begin, end - begin, closethingy, len );
414    if( NULL != dataend )
415    {
416        *dataend = begin;
417    }
418    if( NULL != afterpos && NULL != begin )
419    {
420        *afterpos = ( begin + len >= end ? NULL : begin + len );
421    }
422
423    return 0;
424}
Note: See TracBrowser for help on using the repository browser.