WvStreams
|
00001 /* 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 1997-2004 Net Integration Technologies, Inc. 00004 * 00005 * Implementation of globbing support through WvRegex 00006 */ 00007 #include "wvglob.h" 00008 00009 WvGlob::WvGlob() : WvRegex() 00010 { 00011 } 00012 00013 WvGlob::WvGlob(WvStringParm glob) : WvRegex() 00014 { 00015 set(glob); 00016 } 00017 00018 bool WvGlob::set(WvStringParm glob) 00019 { 00020 WvString errstr; 00021 WvString regex = glob_to_regex(glob, &errstr); 00022 if (!!errstr) 00023 WvErrorBase::seterr(errstr); 00024 else if (!!regex) 00025 WvRegex::set(regex); 00026 else WvErrorBase::seterr("Failed to convert glob pattern to regex"); 00027 return isok(); 00028 } 00029 00030 const bool WvGlob::normal_quit_chars[256] = { 00031 false, false, false, false, false, false, false, false, 00032 false, false, false, false, false, false, false, false, 00033 false, false, false, false, false, false, false, false, 00034 false, false, false, false, false, false, false, false, 00035 false, false, false, false, false, false, false, false, 00036 false, false, false, false, false, false, false, false, 00037 false, false, false, false, false, false, false, false, 00038 false, false, false, false, false, false, false, false, 00039 false, false, false, false, false, false, false, false, 00040 false, false, false, false, false, false, false, false, 00041 false, false, false, false, false, false, false, false, 00042 false, false, false, false, false, false, false, false, 00043 false, false, false, false, false, false, false, false, 00044 false, false, false, false, false, false, false, false, 00045 false, false, false, false, false, false, false, false, 00046 false, false, false, false, false, false, false, false, 00047 false, false, false, false, false, false, false, false, 00048 false, false, false, false, false, false, false, false, 00049 false, false, false, false, false, false, false, false, 00050 false, false, false, false, false, false, false, false, 00051 false, false, false, false, false, false, false, false, 00052 false, false, false, false, false, false, false, false, 00053 false, false, false, false, false, false, false, false, 00054 false, false, false, false, false, false, false, false, 00055 false, false, false, false, false, false, false, false, 00056 false, false, false, false, false, false, false, false, 00057 false, false, false, false, false, false, false, false, 00058 false, false, false, false, false, false, false, false, 00059 false, false, false, false, false, false, false, false, 00060 false, false, false, false, false, false, false, false, 00061 false, false, false, false, false, false, false, false, 00062 false, false, false, false, false, false, false, false 00063 }; 00064 const bool WvGlob::brace_quit_chars[256] = { 00065 false, false, false, false, false, false, false, false, 00066 false, false, false, false, false, false, false, false, 00067 false, false, false, false, false, false, false, false, 00068 false, false, false, false, false, false, false, false, 00069 false, false, false, false, false, false, false, false, 00070 false, false, false, false, true /* , */, false, false, false, 00071 false, false, false, false, false, false, false, false, 00072 false, false, false, false, false, false, false, false, 00073 false, false, false, false, false, false, false, false, 00074 false, false, false, false, false, false, false, false, 00075 false, false, false, false, false, false, false, false, 00076 false, false, false, false, false, false, false, false, 00077 false, false, false, false, false, false, false, false, 00078 false, false, false, false, false, false, false, false, 00079 false, false, false, false, false, false, false, false, 00080 false, false, false, false, false, true /* } */, false, false, 00081 false, false, false, false, false, false, false, false, 00082 false, false, false, false, false, false, false, false, 00083 false, false, false, false, false, false, false, false, 00084 false, false, false, false, false, false, false, false, 00085 false, false, false, false, false, false, false, false, 00086 false, false, false, false, false, false, false, false, 00087 false, false, false, false, false, false, false, false, 00088 false, false, false, false, false, false, false, false, 00089 false, false, false, false, false, false, false, false, 00090 false, false, false, false, false, false, false, false, 00091 false, false, false, false, false, false, false, false, 00092 false, false, false, false, false, false, false, false, 00093 false, false, false, false, false, false, false, false, 00094 false, false, false, false, false, false, false, false, 00095 false, false, false, false, false, false, false, false, 00096 false, false, false, false, false, false, false, false 00097 }; 00098 00099 // 00100 // Known bugs: 00101 // 00102 // - If / is part of a range it will not be excluded in the resulting regex 00103 // eg. fred[.-0]joe will match fred/joe (this violates glob(7)) 00104 // However, explcit / in bracket expression results in error. 00105 // 00106 WvString WvGlob::glob_to_regex(const char *src, size_t &src_used, 00107 char *dst, size_t &dst_used, const bool quit_chars[256]) 00108 { 00109 enum { NORMAL, BACKSLASH, BRACKET, BRACKET_FIRST } state = NORMAL; 00110 src_used = 0; 00111 dst_used = 0; 00112 bool quit_now = false; 00113 while (!quit_now && src[src_used]) 00114 { 00115 switch (state) 00116 { 00117 case NORMAL: 00118 if (quit_chars[src[src_used]]) 00119 { 00120 quit_now = true; 00121 break; 00122 } 00123 00124 switch (src[src_used]) 00125 { 00126 case '\\': 00127 state = BACKSLASH; 00128 break; 00129 00130 case '[': 00131 if (src[src_used+1] == '^' && src[src_used+2] == ']') 00132 { 00133 // Get rid of degenerate case: 00134 src_used += 2; 00135 if (dst) dst[dst_used] = '\\'; ++dst_used; 00136 if (dst) dst[dst_used] = '^'; ++dst_used; 00137 } 00138 else 00139 { 00140 if (dst) dst[dst_used] = '('; ++dst_used; 00141 state = BRACKET_FIRST; 00142 } 00143 break; 00144 00145 case '*': 00146 if (dst) dst[dst_used] = '('; ++dst_used; 00147 if (dst) dst[dst_used] = '['; ++dst_used; 00148 if (dst) dst[dst_used] = '^'; ++dst_used; 00149 if (dst) dst[dst_used] = '/'; ++dst_used; 00150 if (dst) dst[dst_used] = ']'; ++dst_used; 00151 if (dst) dst[dst_used] = '*'; ++dst_used; 00152 if (dst) dst[dst_used] = ')'; ++dst_used; 00153 break; 00154 00155 case '?': 00156 if (dst) dst[dst_used] = '('; ++dst_used; 00157 if (dst) dst[dst_used] = '['; ++dst_used; 00158 if (dst) dst[dst_used] = '^'; ++dst_used; 00159 if (dst) dst[dst_used] = '/'; ++dst_used; 00160 if (dst) dst[dst_used] = ']'; ++dst_used; 00161 if (dst) dst[dst_used] = ')'; ++dst_used; 00162 break; 00163 00164 case '{': 00165 if (dst) dst[dst_used] = '('; ++dst_used; 00166 ++src_used; 00167 while (true) 00168 { 00169 size_t sub_src_used, sub_dst_used; 00170 00171 WvString errstr = 00172 glob_to_regex(&src[src_used], sub_src_used, 00173 dst? &dst[dst_used]: NULL, sub_dst_used, 00174 brace_quit_chars); 00175 if (errstr) return errstr; 00176 00177 src_used += sub_src_used; 00178 dst_used += sub_dst_used; 00179 00180 if (src[src_used] == '}') 00181 break; 00182 else if (src[src_used] != ',') 00183 return WvString("Unfinished brace expression (index %s)", src_used); 00184 if (dst) dst[dst_used] = '|'; ++dst_used; 00185 ++src_used; 00186 } 00187 if (dst) dst[dst_used] = ')'; ++dst_used; 00188 break; 00189 00190 case '^': 00191 case '.': 00192 case '$': 00193 case '(': 00194 case ')': 00195 case '|': 00196 case '+': 00197 if (dst) dst[dst_used] = '\\'; ++dst_used; 00198 if (dst) dst[dst_used] = src[src_used]; ++dst_used; 00199 break; 00200 00201 default: 00202 if (dst) dst[dst_used] = src[src_used]; ++dst_used; 00203 break; 00204 } 00205 break; 00206 00207 case BACKSLASH: 00208 switch (src[src_used]) 00209 { 00210 case '^': 00211 case '.': 00212 case '$': 00213 case '(': 00214 case ')': 00215 case '|': 00216 case '+': 00217 case '[': 00218 case '{': 00219 case '*': 00220 case '?': 00221 case '\\': 00222 if (dst) dst[dst_used] = '\\'; ++dst_used; 00223 // fall through.. 00224 default: 00225 if (dst) dst[dst_used] = src[src_used]; ++dst_used; 00226 break; 00227 00228 } 00229 state = NORMAL; 00230 break; 00231 00232 case BRACKET_FIRST: 00233 switch (src[src_used]) 00234 { 00235 case '!': 00236 if (dst) dst[dst_used] = '['; ++dst_used; 00237 if (dst) dst[dst_used] = '^'; ++dst_used; 00238 break; 00239 00240 case '^': 00241 if (dst) dst[dst_used] = '\\'; ++dst_used; 00242 if (dst) dst[dst_used] = '^'; ++dst_used; 00243 if (dst) dst[dst_used] = '|'; ++dst_used; 00244 if (dst) dst[dst_used] = '['; ++dst_used; 00245 break; 00246 00247 case '/': 00248 return WvString("Slash not allowed in bracket expression (index %s)", src_used); 00249 00250 default: 00251 if (dst) dst[dst_used] = '['; ++dst_used; 00252 if (dst) dst[dst_used] = src[src_used]; ++dst_used; 00253 break; 00254 } 00255 state = BRACKET; 00256 break; 00257 00258 case BRACKET: 00259 switch (src[src_used]) 00260 { 00261 case ']': 00262 if (dst) dst[dst_used] = ']'; ++dst_used; 00263 if (dst) dst[dst_used] = ')'; ++dst_used; 00264 state = NORMAL; 00265 break; 00266 00267 case '/': 00268 return WvString("Slash not allowed in bracket expression (index %s)", src_used); 00269 00270 default: 00271 if (dst) dst[dst_used] = src[src_used]; ++dst_used; 00272 break; 00273 } 00274 break; 00275 } 00276 00277 if (!quit_now) ++src_used; 00278 } 00279 00280 if (state == BRACKET || state == BRACKET_FIRST) 00281 return WvString("Unfinished bracket expression (index %s)", src_used); 00282 else if (state == BACKSLASH) 00283 return WvString("Unfinished backslash expression (index %s)", src_used); 00284 else return WvString::null; 00285 } 00286 00287 WvString WvGlob::glob_to_regex(WvStringParm glob, WvString *errstr) 00288 { 00289 if (glob.isnull()) 00290 { 00291 if (errstr) *errstr = WvString("Glob is NULL"); 00292 return WvString::null; 00293 } 00294 00295 size_t src_used, dst_used; 00296 WvString local_errstr = glob_to_regex(glob, src_used, NULL, dst_used, normal_quit_chars); 00297 if (!!local_errstr) 00298 { 00299 if (errstr) *errstr = local_errstr; 00300 return WvString::null; 00301 } 00302 00303 WvString result; 00304 result.setsize(1+dst_used+1+1); 00305 char *dst = result.edit(); 00306 *dst++ = '^'; 00307 local_errstr = glob_to_regex(glob, src_used, dst, dst_used, normal_quit_chars); 00308 if (!!local_errstr) 00309 { 00310 if (errstr) *errstr = local_errstr; 00311 return WvString::null; 00312 } 00313 dst += dst_used; 00314 *dst++ = '$'; 00315 *dst++ = '\0'; 00316 00317 return result; 00318 }