Initial revision
[platform/upstream/curl.git] / src / urlglob.c
1 /*****************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  *  The contents of this file are subject to the Mozilla Public License
9  *  Version 1.0 (the "License"); you may not use this file except in
10  *  compliance with the License. You may obtain a copy of the License at
11  *  http://www.mozilla.org/MPL/
12  *
13  *  Software distributed under the License is distributed on an "AS IS"
14  *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
15  *  License for the specific language governing rights and limitations
16  *  under the License.
17  *
18  *  The Original Code is Curl.
19  *
20  *  The Initial Developer of the Original Code is Daniel Stenberg.
21  *
22  *  Portions created by the Initial Developer are Copyright (C) 1998.
23  *  All Rights Reserved.
24  *
25  * ------------------------------------------------------------
26  * Main author:
27  * - Daniel Stenberg <Daniel.Stenberg@haxx.nu>
28  *
29  *      http://curl.haxx.nu
30  *
31  * $Source$
32  * $Revision$
33  * $Date$
34  * $Author$
35  * $State$
36  * $Locker$
37  *
38  * ------------------------------------------------------------
39  ****************************************************************************/
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <curl/curl.h>
46 #include "urlglob.h"
47
48 char glob_buffer[URL_MAX_LENGTH];
49 URLGlob *glob_expand;
50
51 int glob_word(char*, int);
52
53 int glob_set(char *pattern, int pos) {
54   /* processes a set expression with the point behind the opening '{'
55      ','-separated elements are collected until the next closing '}'
56   */
57   char* buf = glob_buffer;
58   URLPattern *pat;
59
60   pat = (URLPattern*)&glob_expand->pattern[glob_expand->size / 2];
61   /* patterns 0,1,2,... correspond to size=1,3,5,... */
62   pat->type = UPTSet;
63   pat->content.Set.size = 0;
64   pat->content.Set.ptr_s = 0;
65   pat->content.Set.elements = (char**)malloc(0);
66   ++glob_expand->size;
67
68   while (1) {
69     switch (*pattern) {
70     case '\0':                          /* URL ended while set was still open */
71       printf("error: unmatched brace at pos %d\n", pos);
72       exit (URG_URL_MALFORMAT);
73     case '{':
74     case '[':                           /* no nested expressions at this time */
75       printf("error: nested braces not supported %d\n", pos);
76       exit (URG_URL_MALFORMAT);
77     case ',':
78     case '}':                           /* set element completed */
79       *buf = '\0';
80       pat->content.Set.elements = realloc(pat->content.Set.elements, (pat->content.Set.size + 1) * sizeof(char*));
81       if (!pat->content.Set.elements) {
82         printf("out of memory in set pattern\n");
83         exit(URG_OUT_OF_MEMORY);
84       }
85       pat->content.Set.elements[pat->content.Set.size] = strdup(glob_buffer);
86       ++pat->content.Set.size;
87
88       if (*pattern == '}')              /* entire set pattern completed */
89         /* always check for a literal (may be "") between patterns */
90         return pat->content.Set.size * glob_word(++pattern, ++pos);
91
92       buf = glob_buffer;
93       ++pattern;
94       ++pos;
95       break;
96     case ']':                           /* illegal closing bracket */
97       printf("error: illegal pattern at pos %d\n", pos);
98       exit (URG_URL_MALFORMAT);
99     case '\\':                          /* escaped character, skip '\' */
100       if (*(buf+1) == '\0') {           /* but no escaping of '\0'! */
101         printf("error: illegal pattern at pos %d\n", pos);
102         exit (URG_URL_MALFORMAT);
103       }
104       ++pattern;
105       ++pos;                            /* intentional fallthrough */
106     default:
107       *buf++ = *pattern++;              /* copy character to set element */
108       ++pos;
109     }
110   }
111   exit (URG_FAILED_INIT);
112 }
113
114 int glob_range(char *pattern, int pos) {
115   /* processes a range expression with the point behind the opening '['
116      - char range: e.g. "a-z]", "B-Q]"
117      - num range: e.g. "0-9]", "17-2000]"
118      - num range with leading zeros: e.g. "001-999]"
119      expression is checked for well-formedness and collected until the next ']'
120   */
121   URLPattern *pat;
122   char *c;
123   
124   pat = (URLPattern*)&glob_expand->pattern[glob_expand->size / 2];
125   /* patterns 0,1,2,... correspond to size=1,3,5,... */
126   ++glob_expand->size;
127
128   if (isalpha((int)*pattern)) {         /* character range detected */
129     pat->type = UPTCharRange;
130     if (sscanf(pattern, "%c-%c]", &pat->content.CharRange.min_c, &pat->content.CharRange.max_c) != 2 ||
131         pat->content.CharRange.min_c >= pat->content.CharRange.max_c ||
132         pat->content.CharRange.max_c - pat->content.CharRange.min_c > 'z' - 'a') {
133       /* the pattern is not well-formed */ 
134       printf("error: illegal pattern or range specification after pos %d\n", pos);
135       exit (URG_URL_MALFORMAT);
136     }
137     pat->content.CharRange.ptr_c = pat->content.CharRange.min_c;
138     /* always check for a literal (may be "") between patterns */
139     return (pat->content.CharRange.max_c - pat->content.CharRange.min_c + 1) *
140       glob_word(pattern + 4, pos + 4);
141   }
142   if (isdigit((int)*pattern)) {         /* numeric range detected */
143     pat->type = UPTNumRange;
144     pat->content.NumRange.padlength = 0;
145     if (sscanf(pattern, "%d-%d]", &pat->content.NumRange.min_n, &pat->content.NumRange.max_n) != 2 ||
146         pat->content.NumRange.min_n >= pat->content.NumRange.max_n) {
147       /* the pattern is not well-formed */ 
148       printf("error: illegal pattern or range specification after pos %d\n", pos);
149       exit (URG_URL_MALFORMAT);
150     }
151     if (*pattern == '0') {              /* leading zero specified */
152       c = pattern;  
153       while (isdigit((int)*c++))
154         ++pat->content.NumRange.padlength;      /* padding length is set for all instances
155                                                    of this pattern */
156     }
157     pat->content.NumRange.ptr_n = pat->content.NumRange.min_n;
158     c = (char*)(strchr(pattern, ']') + 1);      /* continue after next ']' */
159     /* always check for a literal (may be "") between patterns */
160     return (pat->content.NumRange.max_n - pat->content.NumRange.min_n + 1) *
161       glob_word(c, pos + (c - pattern));
162   }
163   printf("error: illegal character in range specification at pos %d\n", pos);
164   exit (URG_URL_MALFORMAT);
165 }
166
167 int glob_word(char *pattern, int pos) {
168   /* processes a literal string component of a URL
169      special characters '{' and '[' branch to set/range processing functions
170    */ 
171   char* buf = glob_buffer;
172   int litindex;
173
174   while (*pattern != '\0' && *pattern != '{' && *pattern != '[') {
175     if (*pattern == '}' || *pattern == ']') {
176       printf("illegal character at position %d\n", pos);
177       exit (URG_URL_MALFORMAT);
178     }
179     if (*pattern == '\\') {             /* escape character, skip '\' */
180       ++pattern;
181       ++pos;
182       if (*pattern == '\0') {           /* but no escaping of '\0'! */
183         printf("illegal character at position %d\n", pos);
184         exit (URG_URL_MALFORMAT);
185       }
186     }
187     *buf++ = *pattern++;                /* copy character to literal */
188     ++pos;
189   }
190   *buf = '\0';
191   litindex = glob_expand->size / 2;
192   /* literals 0,1,2,... correspond to size=0,2,4,... */
193   glob_expand->literal[litindex] = strdup(glob_buffer);
194   ++glob_expand->size;
195   if (*pattern == '\0')
196     return 1;                           /* singular URL processed  */
197   if (*pattern == '{') {
198     return glob_set(++pattern, ++pos);  /* process set pattern */
199   }
200   if (*pattern == '[') {
201     return glob_range(++pattern, ++pos);/* process range pattern */
202   }
203   printf("internal error\n");
204   exit (URG_FAILED_INIT);
205 }
206
207 int glob_url(URLGlob** glob, char* url) {
208   int urlnum;           /* counts instances of a globbed pattern */
209
210   glob_expand = (URLGlob*)malloc(sizeof(URLGlob));
211   glob_expand->size = 0;
212   urlnum = glob_word(url, 1);
213   *glob = glob_expand;
214   return urlnum;
215 }
216
217 char *next_url(URLGlob *glob) {
218   static int beenhere = 0;
219   char *buf = glob_buffer;
220   URLPattern *pat;
221   char *lit;
222   signed int i;
223   int carry;
224
225   if (!beenhere)
226     beenhere = 1;
227   else {
228     carry = 1;
229
230     /* implement a counter over the index ranges of all patterns,
231        starting with the rightmost pattern */
232     for (i = glob->size / 2 - 1; carry && i >= 0; --i) {
233       carry = 0;
234       pat = &glob->pattern[i];
235       switch (pat->type) {
236       case UPTSet:
237         if (++pat->content.Set.ptr_s == pat->content.Set.size) {
238           pat->content.Set.ptr_s = 0;
239           carry = 1;
240         }
241         break;
242       case UPTCharRange:
243         if (++pat->content.CharRange.ptr_c > pat->content.CharRange.max_c) {
244           pat->content.CharRange.ptr_c = pat->content.CharRange.min_c;
245           carry = 1;
246         }
247         break;
248       case UPTNumRange:
249         if (++pat->content.NumRange.ptr_n > pat->content.NumRange.max_n) {
250           pat->content.NumRange.ptr_n = pat->content.NumRange.min_n;
251           carry = 1;
252         }
253         break;
254       default:
255         printf("internal error: invalid pattern type (%d)\n", pat->type);
256         exit (URG_FAILED_INIT);
257       }
258     }
259     if (carry)          /* first pattern ptr has run into overflow, done! */
260       return NULL;
261   }
262
263   for (i = 0; i < glob->size; ++i) {
264     if (!(i % 2)) {                     /* every other term (i even) is a literal */
265       lit = glob->literal[i/2];
266       strcpy(buf, lit);
267       buf += strlen(lit);
268     }
269     else {                              /* the rest (i odd) are patterns */
270       pat = &glob->pattern[i/2];
271       switch(pat->type) {
272       case UPTSet:
273         strcpy(buf, pat->content.Set.elements[pat->content.Set.ptr_s]);
274         buf += strlen(pat->content.Set.elements[pat->content.Set.ptr_s]);
275         break;
276       case UPTCharRange:
277         *buf++ = pat->content.CharRange.ptr_c;
278         break;
279       case UPTNumRange:
280         buf += sprintf(buf, "%0*d", pat->content.NumRange.padlength, pat->content.NumRange.ptr_n); 
281         break;
282       default:
283         printf("internal error: invalid pattern type (%d)\n", pat->type);
284         exit (URG_FAILED_INIT);
285       }
286     }
287   }
288   *buf = '\0';
289   return strdup(glob_buffer);
290 }
291
292 char *match_url(char *filename, URLGlob glob) {
293   char *buf = glob_buffer;
294   URLPattern pat;
295   int i;
296
297   while (*filename != '\0') {
298     if (*filename == '#') {
299       if (!isdigit((int)*++filename) ||
300           *filename == '0') {           /* only '#1' ... '#9' allowed */
301         printf("illegal matching expression\n");
302         exit(URG_URL_MALFORMAT);
303       }
304       i = *filename - '1';
305       if (i + 1 > glob.size / 2) {
306         printf("match against nonexisting pattern\n");
307         exit(URG_URL_MALFORMAT);
308       }
309       pat = glob.pattern[i];
310       switch (pat.type) {
311       case UPTSet:
312         strcpy(buf, pat.content.Set.elements[pat.content.Set.ptr_s]);
313         buf += strlen(pat.content.Set.elements[pat.content.Set.ptr_s]);
314         break;
315       case UPTCharRange:
316         *buf++ = pat.content.CharRange.ptr_c;
317         break;
318       case UPTNumRange:
319         buf += sprintf(buf, "%0*d", pat.content.NumRange.padlength, pat.content.NumRange.ptr_n);
320         break;
321       default:
322         printf("internal error: invalid pattern type (%d)\n", pat.type);
323         exit (URG_FAILED_INIT);
324       }
325       ++filename;
326     }
327     else
328       *buf++ = *filename++;
329   }
330   *buf = '\0';
331   return strdup(glob_buffer);
332 }