dual-license fix
[platform/upstream/curl.git] / src / urlglob.c
1 /*****************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * In order to be useful for every potential user, curl and libcurl are
11  * dual-licensed under the MPL and the MIT/X-derivate licenses.
12  *
13  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
14  * copies of the Software, and permit persons to whom the Software is
15  * furnished to do so, under the terms of the MPL or the MIT/X-derivate
16  * licenses. You may pick one of these licenses.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * $Id$
22  *****************************************************************************/
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <curl/curl.h>
29 #include "urlglob.h"
30
31 #ifdef MALLOCDEBUG
32 #include "../lib/memdebug.h"
33 #endif
34
35 int glob_word(URLGlob *, char*, int);
36
37 int glob_set(URLGlob *glob, char *pattern, int pos)
38 {
39   /* processes a set expression with the point behind the opening '{'
40      ','-separated elements are collected until the next closing '}'
41   */
42   char* buf = glob->glob_buffer;
43   URLPattern *pat;
44
45   pat = (URLPattern*)&glob->pattern[glob->size / 2];
46   /* patterns 0,1,2,... correspond to size=1,3,5,... */
47   pat->type = UPTSet;
48   pat->content.Set.size = 0;
49   pat->content.Set.ptr_s = 0;
50   pat->content.Set.elements = (char**)malloc(0);
51   ++glob->size;
52
53   while (1) {
54     switch (*pattern) {
55     case '\0':                          /* URL ended while set was still open */
56       printf("error: unmatched brace at pos %d\n", pos);
57       exit (CURLE_URL_MALFORMAT);
58     case '{':
59     case '[':                           /* no nested expressions at this time */
60       printf("error: nested braces not supported %d\n", pos);
61       exit (CURLE_URL_MALFORMAT);
62     case ',':
63     case '}':                           /* set element completed */
64       *buf = '\0';
65       pat->content.Set.elements =
66         realloc(pat->content.Set.elements,
67                 (pat->content.Set.size + 1) * sizeof(char*));
68       if (!pat->content.Set.elements) {
69         printf("out of memory in set pattern\n");
70         exit(CURLE_OUT_OF_MEMORY);
71       }
72       pat->content.Set.elements[pat->content.Set.size] =
73         strdup(glob->glob_buffer);
74       ++pat->content.Set.size;
75
76       if (*pattern == '}')              /* entire set pattern completed */
77         /* always check for a literal (may be "") between patterns */
78         return pat->content.Set.size * glob_word(glob, ++pattern, ++pos);
79
80       buf = glob->glob_buffer;
81       ++pattern;
82       ++pos;
83       break;
84     case ']':                           /* illegal closing bracket */
85       printf("error: illegal pattern at pos %d\n", pos);
86       exit (CURLE_URL_MALFORMAT);
87     case '\\':                          /* escaped character, skip '\' */
88       if (*(buf+1) == '\0') {           /* but no escaping of '\0'! */
89         printf("error: illegal pattern at pos %d\n", pos);
90         exit (CURLE_URL_MALFORMAT);
91       }
92       ++pattern;
93       ++pos;                            /* intentional fallthrough */
94     default:
95       *buf++ = *pattern++;              /* copy character to set element */
96       ++pos;
97     }
98   }
99   exit (CURLE_FAILED_INIT);
100 }
101
102 int glob_range(URLGlob *glob, char *pattern, int pos)
103 {
104   /* processes a range expression with the point behind the opening '['
105      - char range: e.g. "a-z]", "B-Q]"
106      - num range: e.g. "0-9]", "17-2000]"
107      - num range with leading zeros: e.g. "001-999]"
108      expression is checked for well-formedness and collected until the next ']'
109   */
110   URLPattern *pat;
111   char *c;
112   
113   pat = (URLPattern*)&glob->pattern[glob->size / 2];
114   /* patterns 0,1,2,... correspond to size=1,3,5,... */
115   ++glob->size;
116
117   if (isalpha((int)*pattern)) {         /* character range detected */
118     pat->type = UPTCharRange;
119     if (sscanf(pattern, "%c-%c]", &pat->content.CharRange.min_c, &pat->content.CharRange.max_c) != 2 ||
120         pat->content.CharRange.min_c >= pat->content.CharRange.max_c ||
121         pat->content.CharRange.max_c - pat->content.CharRange.min_c > 'z' - 'a') {
122       /* the pattern is not well-formed */ 
123       printf("error: illegal pattern or range specification after pos %d\n", pos);
124       exit (CURLE_URL_MALFORMAT);
125     }
126     pat->content.CharRange.ptr_c = pat->content.CharRange.min_c;
127     /* always check for a literal (may be "") between patterns */
128     return (pat->content.CharRange.max_c - pat->content.CharRange.min_c + 1) *
129       glob_word(glob, pattern + 4, pos + 4);
130   }
131   if (isdigit((int)*pattern)) {         /* numeric range detected */
132     pat->type = UPTNumRange;
133     pat->content.NumRange.padlength = 0;
134     if (sscanf(pattern, "%d-%d]", &pat->content.NumRange.min_n, &pat->content.NumRange.max_n) != 2 ||
135         pat->content.NumRange.min_n >= pat->content.NumRange.max_n) {
136       /* the pattern is not well-formed */ 
137       printf("error: illegal pattern or range specification after pos %d\n", pos);
138       exit (CURLE_URL_MALFORMAT);
139     }
140     if (*pattern == '0') {              /* leading zero specified */
141       c = pattern;  
142       while (isdigit((int)*c++))
143         ++pat->content.NumRange.padlength;      /* padding length is set for all instances
144                                                    of this pattern */
145     }
146     pat->content.NumRange.ptr_n = pat->content.NumRange.min_n;
147     c = (char*)(strchr(pattern, ']') + 1);      /* continue after next ']' */
148     /* always check for a literal (may be "") between patterns */
149     return (pat->content.NumRange.max_n - pat->content.NumRange.min_n + 1) *
150       glob_word(glob, c, pos + (c - pattern));
151   }
152   printf("error: illegal character in range specification at pos %d\n", pos);
153   exit (CURLE_URL_MALFORMAT);
154 }
155
156 int glob_word(URLGlob *glob, char *pattern, int pos)
157 {
158   /* processes a literal string component of a URL
159      special characters '{' and '[' branch to set/range processing functions
160    */ 
161   char* buf = glob->glob_buffer;
162   int litindex;
163
164   while (*pattern != '\0' && *pattern != '{' && *pattern != '[') {
165     if (*pattern == '}' || *pattern == ']') {
166       printf("illegal character at position %d\n", pos);
167       exit (CURLE_URL_MALFORMAT);
168     }
169     if (*pattern == '\\') {             /* escape character, skip '\' */
170       ++pattern;
171       ++pos;
172       if (*pattern == '\0') {           /* but no escaping of '\0'! */
173         printf("illegal character at position %d\n", pos);
174         exit (CURLE_URL_MALFORMAT);
175       }
176     }
177     *buf++ = *pattern++;                /* copy character to literal */
178     ++pos;
179   }
180   *buf = '\0';
181   litindex = glob->size / 2;
182   /* literals 0,1,2,... correspond to size=0,2,4,... */
183   glob->literal[litindex] = strdup(glob->glob_buffer);
184   ++glob->size;
185   if (*pattern == '\0')
186     return 1;                           /* singular URL processed  */
187   if (*pattern == '{') {
188     return glob_set(glob, ++pattern, ++pos);    /* process set pattern */
189   }
190   if (*pattern == '[') {
191     return glob_range(glob, ++pattern, ++pos);/* process range pattern */
192   }
193   printf("internal error\n");
194   exit (CURLE_FAILED_INIT);
195 }
196
197 int glob_url(URLGlob** glob, char* url, int *urlnum)
198 {
199   /*
200    * We can deal with any-size, just make a buffer with the same length
201    * as the specified URL!
202    */
203   URLGlob *glob_expand;
204   char *glob_buffer=(char *)malloc(strlen(url)+1);
205   if(NULL == glob_buffer)
206     return CURLE_OUT_OF_MEMORY;
207
208   glob_expand = (URLGlob*)malloc(sizeof(URLGlob));
209   if(NULL == glob_expand) {
210     free(glob_buffer);
211     return CURLE_OUT_OF_MEMORY;
212   }
213   glob_expand->size = 0;
214   glob_expand->urllen = strlen(url);
215   glob_expand->glob_buffer = glob_buffer;
216   *urlnum = glob_word(glob_expand, url, 1);
217   *glob = glob_expand;
218   return CURLE_OK;
219 }
220
221 void glob_cleanup(URLGlob* glob)
222 {
223   int i, elem;
224
225   for (i = glob->size - 1; i >= 0; --i) {
226     if (!(i & 1)) {     /* even indexes contain literals */
227       free(glob->literal[i/2]);
228     } else {            /* odd indexes contain sets or ranges */
229       if (glob->pattern[i/2].type == UPTSet) {
230         for (elem = glob->pattern[i/2].content.Set.size - 1; elem >= 0; --elem) {
231           free(glob->pattern[i/2].content.Set.elements[elem]);
232         }
233         free(glob->pattern[i/2].content.Set.elements);
234       }
235     }
236   }
237   free(glob->glob_buffer);
238   free(glob);
239 }
240
241 char *next_url(URLGlob *glob)
242 {
243   static int beenhere = 0;
244   char *buf = glob->glob_buffer;
245   URLPattern *pat;
246   char *lit;
247   signed int i;
248   int carry;
249
250   if (!beenhere)
251     beenhere = 1;
252   else {
253     carry = 1;
254
255     /* implement a counter over the index ranges of all patterns,
256        starting with the rightmost pattern */
257     for (i = glob->size / 2 - 1; carry && i >= 0; --i) {
258       carry = 0;
259       pat = &glob->pattern[i];
260       switch (pat->type) {
261       case UPTSet:
262         if (++pat->content.Set.ptr_s == pat->content.Set.size) {
263           pat->content.Set.ptr_s = 0;
264           carry = 1;
265         }
266         break;
267       case UPTCharRange:
268         if (++pat->content.CharRange.ptr_c > pat->content.CharRange.max_c) {
269           pat->content.CharRange.ptr_c = pat->content.CharRange.min_c;
270           carry = 1;
271         }
272         break;
273       case UPTNumRange:
274         if (++pat->content.NumRange.ptr_n > pat->content.NumRange.max_n) {
275           pat->content.NumRange.ptr_n = pat->content.NumRange.min_n;
276           carry = 1;
277         }
278         break;
279       default:
280         printf("internal error: invalid pattern type (%d)\n", pat->type);
281         exit (CURLE_FAILED_INIT);
282       }
283     }
284     if (carry)          /* first pattern ptr has run into overflow, done! */
285       return NULL;
286   }
287
288   for (i = 0; i < glob->size; ++i) {
289     if (!(i % 2)) {                     /* every other term (i even) is a literal */
290       lit = glob->literal[i/2];
291       strcpy(buf, lit);
292       buf += strlen(lit);
293     }
294     else {                              /* the rest (i odd) are patterns */
295       pat = &glob->pattern[i/2];
296       switch(pat->type) {
297       case UPTSet:
298         strcpy(buf, pat->content.Set.elements[pat->content.Set.ptr_s]);
299         buf += strlen(pat->content.Set.elements[pat->content.Set.ptr_s]);
300         break;
301       case UPTCharRange:
302         *buf++ = pat->content.CharRange.ptr_c;
303         break;
304       case UPTNumRange:
305         sprintf(buf, "%0*d", pat->content.NumRange.padlength, pat->content.NumRange.ptr_n); 
306         buf += strlen(buf); /* make no sprint() return code assumptions */
307         break;
308       default:
309         printf("internal error: invalid pattern type (%d)\n", pat->type);
310         exit (CURLE_FAILED_INIT);
311       }
312     }
313   }
314   *buf = '\0';
315   return strdup(glob->glob_buffer);
316 }
317
318 char *match_url(char *filename, URLGlob *glob)
319 {
320   char *target;
321   URLPattern pat;
322   int i;
323   int allocsize;
324   int stringlen=0;
325   char numbuf[18];
326   char *appendthis;
327   size_t appendlen;
328
329   /* We cannot use the glob_buffer for storage here since the filename may
330    * be longer than the URL we use. We allocate a good start size, then
331    * we need to realloc in case of need.
332    */
333   allocsize=strlen(filename);
334   target = malloc(allocsize);
335   if(NULL == target)
336     return NULL; /* major failure */
337
338   while (*filename != '\0') {
339     if (*filename == '#') {
340       if (!isdigit((int)*++filename) ||
341           *filename == '0') {           /* only '#1' ... '#9' allowed */
342         /* printf("illegal matching expression\n");
343            exit(CURLE_URL_MALFORMAT);*/
344         continue;
345       }
346       i = *filename - '1';
347       if (i + 1 > glob->size / 2) {
348         /*printf("match against nonexisting pattern\n");
349           exit(CURLE_URL_MALFORMAT);*/
350         continue;
351       }
352       pat = glob->pattern[i];
353       switch (pat.type) {
354       case UPTSet:
355         appendthis = pat.content.Set.elements[pat.content.Set.ptr_s];
356         appendlen = strlen(pat.content.Set.elements[pat.content.Set.ptr_s]);
357         break;
358       case UPTCharRange:
359         numbuf[0]=pat.content.CharRange.ptr_c;
360         numbuf[1]=0;
361         appendthis=numbuf;
362         appendlen=1;
363         break;
364       case UPTNumRange:
365         sprintf(numbuf, "%0*d", pat.content.NumRange.padlength, pat.content.NumRange.ptr_n);
366         appendthis = numbuf;
367         appendlen = strlen(numbuf);
368         break;
369       default:
370         printf("internal error: invalid pattern type (%d)\n", pat.type);
371         return NULL;
372       }
373       ++filename;
374     }
375     else {
376       appendthis=filename++;
377       appendlen=1;
378     }
379     if(appendlen + stringlen >= allocsize) {
380       char *newstr;
381       allocsize = (appendlen + stringlen)*2;
382       newstr=realloc(target, allocsize);
383       if(NULL ==newstr) {
384         free(target);
385         return NULL;
386       }
387       target=newstr;
388     }
389     memcpy(&target[stringlen], appendthis, appendlen);
390     stringlen += appendlen;
391   }
392   target[stringlen]= '\0';
393   return target;
394 }