| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | /*- | 
| 2 |  |  |  |  |  |  | * Copyright (c) 2003-2007 Tim Kientzle | 
| 3 |  |  |  |  |  |  | * All rights reserved. | 
| 4 |  |  |  |  |  |  | * | 
| 5 |  |  |  |  |  |  | * Redistribution and use in source and binary forms, with or without | 
| 6 |  |  |  |  |  |  | * modification, are permitted provided that the following conditions | 
| 7 |  |  |  |  |  |  | * are met: | 
| 8 |  |  |  |  |  |  | * 1. Redistributions of source code must retain the above copyright | 
| 9 |  |  |  |  |  |  | *    notice, this list of conditions and the following disclaimer | 
| 10 |  |  |  |  |  |  | *    in this position and unchanged. | 
| 11 |  |  |  |  |  |  | * 2. Redistributions in binary form must reproduce the above copyright | 
| 12 |  |  |  |  |  |  | *    notice, this list of conditions and the following disclaimer in the | 
| 13 |  |  |  |  |  |  | *    documentation and/or other materials provided with the distribution. | 
| 14 |  |  |  |  |  |  | * | 
| 15 |  |  |  |  |  |  | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR | 
| 16 |  |  |  |  |  |  | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
| 17 |  |  |  |  |  |  | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 
| 18 |  |  |  |  |  |  | * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, | 
| 19 |  |  |  |  |  |  | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
| 20 |  |  |  |  |  |  | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
| 21 |  |  |  |  |  |  | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
| 22 |  |  |  |  |  |  | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
| 23 |  |  |  |  |  |  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
| 24 |  |  |  |  |  |  | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
| 25 |  |  |  |  |  |  | */ | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | /* | 
| 28 |  |  |  |  |  |  | * Copyright (c) 2012, cPanel, Inc. | 
| 29 |  |  |  |  |  |  | * All rights reserved. | 
| 30 |  |  |  |  |  |  | * http://cpanel.net/ | 
| 31 |  |  |  |  |  |  | * | 
| 32 |  |  |  |  |  |  | * This is free software; you can redistribute it and/or modify it under the | 
| 33 |  |  |  |  |  |  | * same terms as Perl itself.  See the Perl manual section 'perlartistic' for | 
| 34 |  |  |  |  |  |  | * further information. | 
| 35 |  |  |  |  |  |  | * | 
| 36 |  |  |  |  |  |  | * Modified for use in Archive::Tar::Builder. | 
| 37 |  |  |  |  |  |  | */ | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | #include | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | #include "match_path.h" | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | /* | 
| 44 |  |  |  |  |  |  | * Check whether a character 'c' is matched by a list specification [...]: | 
| 45 |  |  |  |  |  |  | *    * Leading '!' or '^' negates the class. | 
| 46 |  |  |  |  |  |  | *    * - is a range of characters | 
| 47 |  |  |  |  |  |  | *    * \ removes any special meaning for | 
| 48 |  |  |  |  |  |  | * | 
| 49 |  |  |  |  |  |  | * Some interesting boundary cases: | 
| 50 |  |  |  |  |  |  | *   a-d-e is one range (a-d) followed by two single characters - and e. | 
| 51 |  |  |  |  |  |  | *   \a-\d is same as a-d | 
| 52 |  |  |  |  |  |  | *   a\-d is three single characters: a, d, - | 
| 53 |  |  |  |  |  |  | *   Trailing - is not special (so [a-] is two characters a and -). | 
| 54 |  |  |  |  |  |  | *   Initial - is not special ([a-] is same as [-a] is same as [\\-a]) | 
| 55 |  |  |  |  |  |  | *   This function never sees a trailing \. | 
| 56 |  |  |  |  |  |  | *   [] always fails | 
| 57 |  |  |  |  |  |  | *   [!] always succeeds | 
| 58 |  |  |  |  |  |  | */ | 
| 59 |  |  |  |  |  |  | static int | 
| 60 | 56 |  |  |  |  |  | pm_list(const char *start, const char *end, const char c, int flags) | 
| 61 |  |  |  |  |  |  | { | 
| 62 | 56 |  |  |  |  |  | const char *p = start; | 
| 63 | 56 |  |  |  |  |  | char rangeStart = '\0', nextRangeStart; | 
| 64 | 56 |  |  |  |  |  | int match = 1, nomatch = 0; | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | /* This will be used soon... */ | 
| 67 |  |  |  |  |  |  | (void)flags; /* UNUSED */ | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | /* If this is a negated class, return success for nomatch. */ | 
| 70 | 56 | 50 |  |  |  |  | if ((*p == '!' || *p == '^') && p < end) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 71 | 56 |  |  |  |  |  | match = 0; | 
| 72 | 56 |  |  |  |  |  | nomatch = 1; | 
| 73 | 56 |  |  |  |  |  | ++p; | 
| 74 |  |  |  |  |  |  | } | 
| 75 |  |  |  |  |  |  |  | 
| 76 | 112 | 100 |  |  |  |  | while (p < end) { | 
| 77 | 56 |  |  |  |  |  | nextRangeStart = '\0'; | 
| 78 | 56 |  |  |  |  |  | switch (*p) { | 
| 79 |  |  |  |  |  |  | case '-': | 
| 80 |  |  |  |  |  |  | /* Trailing or initial '-' is not special. */ | 
| 81 | 28 | 50 |  |  |  |  | if ((rangeStart == '\0') || (p == end - 1)) { | 
|  |  | 0 |  |  |  |  |  | 
| 82 | 28 | 50 |  |  |  |  | if (*p == c) | 
| 83 | 0 |  |  |  |  |  | return (match); | 
| 84 |  |  |  |  |  |  | } else { | 
| 85 | 0 |  |  |  |  |  | char rangeEnd = *++p; | 
| 86 | 0 | 0 |  |  |  |  | if (rangeEnd == '\\') | 
| 87 | 0 |  |  |  |  |  | rangeEnd = *++p; | 
| 88 | 0 | 0 |  |  |  |  | if ((rangeStart <= c) && (c <= rangeEnd)) | 
|  |  | 0 |  |  |  |  |  | 
| 89 | 0 |  |  |  |  |  | return (match); | 
| 90 |  |  |  |  |  |  | } | 
| 91 | 28 |  |  |  |  |  | break; | 
| 92 |  |  |  |  |  |  | case '\\': | 
| 93 | 0 |  |  |  |  |  | ++p; | 
| 94 |  |  |  |  |  |  | /* Fall through */ | 
| 95 |  |  |  |  |  |  | default: | 
| 96 | 28 | 50 |  |  |  |  | if (*p == c) | 
| 97 | 0 |  |  |  |  |  | return (match); | 
| 98 | 28 |  |  |  |  |  | nextRangeStart = *p; /* Possible start of range. */ | 
| 99 |  |  |  |  |  |  | } | 
| 100 | 56 |  |  |  |  |  | rangeStart = nextRangeStart; | 
| 101 | 56 |  |  |  |  |  | ++p; | 
| 102 |  |  |  |  |  |  | } | 
| 103 | 56 |  |  |  |  |  | return (nomatch); | 
| 104 |  |  |  |  |  |  | } | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | /* | 
| 107 |  |  |  |  |  |  | * If s is pointing to "./", ".//", "./././" or the like, skip it. | 
| 108 |  |  |  |  |  |  | */ | 
| 109 |  |  |  |  |  |  | static const char * | 
| 110 | 28 |  |  |  |  |  | pm_slashskip(const char *s) { | 
| 111 | 56 | 100 |  |  |  |  | while ((*s == '/') | 
| 112 | 28 | 50 |  |  |  |  | || (s[0] == '.' && s[1] == '/') | 
|  |  | 0 |  |  |  |  |  | 
| 113 | 28 | 50 |  |  |  |  | || (s[0] == '.' && s[1] == '\0')) | 
|  |  | 0 |  |  |  |  |  | 
| 114 | 28 |  |  |  |  |  | ++s; | 
| 115 | 28 |  |  |  |  |  | return (s); | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | static int | 
| 119 | 1883 |  |  |  |  |  | pm(const char *p, const char *s, int flags) | 
| 120 |  |  |  |  |  |  | { | 
| 121 |  |  |  |  |  |  | const char *end; | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | /* | 
| 124 |  |  |  |  |  |  | * Ignore leading './', './/', '././', etc. | 
| 125 |  |  |  |  |  |  | */ | 
| 126 | 1883 | 100 |  |  |  |  | if (s[0] == '.' && s[1] == '/') | 
|  |  | 50 |  |  |  |  |  | 
| 127 | 0 |  |  |  |  |  | s = pm_slashskip(s + 1); | 
| 128 | 1883 | 50 |  |  |  |  | if (p[0] == '.' && p[1] == '/') | 
|  |  | 0 |  |  |  |  |  | 
| 129 | 0 |  |  |  |  |  | p = pm_slashskip(p + 1); | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | for (;;) { | 
| 132 | 3087 |  |  |  |  |  | switch (*p) { | 
| 133 |  |  |  |  |  |  | case '\0': | 
| 134 | 140 | 100 |  |  |  |  | if (s[0] == '/') { | 
| 135 | 56 | 50 |  |  |  |  | if (flags & PATHMATCH_NO_ANCHOR_END) | 
| 136 | 56 |  |  |  |  |  | return (1); | 
| 137 |  |  |  |  |  |  | /* "dir" == "dir/" == "dir/." */ | 
| 138 | 0 |  |  |  |  |  | s = pm_slashskip(s); | 
| 139 |  |  |  |  |  |  | } | 
| 140 | 84 |  |  |  |  |  | return (*s == '\0'); | 
| 141 |  |  |  |  |  |  | case '?': | 
| 142 |  |  |  |  |  |  | /* ? always succeeds, unless we hit end of 's' */ | 
| 143 | 0 | 0 |  |  |  |  | if (*s == '\0') | 
| 144 | 0 |  |  |  |  |  | return (0); | 
| 145 | 0 |  |  |  |  |  | break; | 
| 146 |  |  |  |  |  |  | case '*': | 
| 147 |  |  |  |  |  |  | /* "*" == "**" == "***" ... */ | 
| 148 | 168 | 100 |  |  |  |  | while (*p == '*') | 
| 149 | 84 |  |  |  |  |  | ++p; | 
| 150 |  |  |  |  |  |  | /* Trailing '*' always succeeds. */ | 
| 151 | 84 | 100 |  |  |  |  | if (*p == '\0') | 
| 152 | 28 |  |  |  |  |  | return (1); | 
| 153 | 196 | 50 |  |  |  |  | while (*s) { | 
| 154 | 196 | 100 |  |  |  |  | if (lafe_pathmatch(p, s, flags)) | 
| 155 | 56 |  |  |  |  |  | return (1); | 
| 156 | 140 |  |  |  |  |  | ++s; | 
| 157 |  |  |  |  |  |  | } | 
| 158 | 0 |  |  |  |  |  | return (0); | 
| 159 |  |  |  |  |  |  | case '[': | 
| 160 |  |  |  |  |  |  | /* | 
| 161 |  |  |  |  |  |  | * Find the end of the [...] character class, | 
| 162 |  |  |  |  |  |  | * ignoring \] that might occur within the class. | 
| 163 |  |  |  |  |  |  | */ | 
| 164 | 56 |  |  |  |  |  | end = p + 1; | 
| 165 | 168 | 50 |  |  |  |  | while (*end != '\0' && *end != ']') { | 
|  |  | 100 |  |  |  |  |  | 
| 166 | 112 | 50 |  |  |  |  | if (*end == '\\' && end[1] != '\0') | 
|  |  | 0 |  |  |  |  |  | 
| 167 | 0 |  |  |  |  |  | ++end; | 
| 168 | 112 |  |  |  |  |  | ++end; | 
| 169 |  |  |  |  |  |  | } | 
| 170 | 56 | 50 |  |  |  |  | if (*end == ']') { | 
| 171 |  |  |  |  |  |  | /* We found [...], try to match it. */ | 
| 172 | 56 | 50 |  |  |  |  | if (!pm_list(p + 1, end, *s, flags)) | 
| 173 | 0 |  |  |  |  |  | return (0); | 
| 174 | 56 |  |  |  |  |  | p = end; /* Jump to trailing ']' char. */ | 
| 175 | 56 |  |  |  |  |  | break; | 
| 176 |  |  |  |  |  |  | } else | 
| 177 |  |  |  |  |  |  | /* No final ']', so just match '['. */ | 
| 178 | 0 | 0 |  |  |  |  | if (*p != *s) | 
| 179 | 0 |  |  |  |  |  | return (0); | 
| 180 | 0 |  |  |  |  |  | break; | 
| 181 |  |  |  |  |  |  | case '\\': | 
| 182 |  |  |  |  |  |  | /* Trailing '\\' matches itself. */ | 
| 183 | 0 | 0 |  |  |  |  | if (p[1] == '\0') { | 
| 184 | 0 | 0 |  |  |  |  | if (*s != '\\') | 
| 185 | 0 |  |  |  |  |  | return (0); | 
| 186 |  |  |  |  |  |  | } else { | 
| 187 | 0 |  |  |  |  |  | ++p; | 
| 188 | 0 | 0 |  |  |  |  | if (*p != *s) | 
| 189 | 0 |  |  |  |  |  | return (0); | 
| 190 |  |  |  |  |  |  | } | 
| 191 | 0 |  |  |  |  |  | break; | 
| 192 |  |  |  |  |  |  | case '/': | 
| 193 | 14 | 50 |  |  |  |  | if (*s != '/' && *s != '\0') | 
|  |  | 0 |  |  |  |  |  | 
| 194 | 0 |  |  |  |  |  | return (0); | 
| 195 |  |  |  |  |  |  | /* Note: pattern "/\./" won't match "/"; | 
| 196 |  |  |  |  |  |  | * pm_slashskip() correctly stops at backslash. */ | 
| 197 | 14 |  |  |  |  |  | p = pm_slashskip(p); | 
| 198 | 14 |  |  |  |  |  | s = pm_slashskip(s); | 
| 199 | 14 | 50 |  |  |  |  | if (*p == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)) | 
|  |  | 0 |  |  |  |  |  | 
| 200 | 0 |  |  |  |  |  | return (1); | 
| 201 | 14 |  |  |  |  |  | --p; /* Counteract the increment below. */ | 
| 202 | 14 |  |  |  |  |  | --s; | 
| 203 | 14 |  |  |  |  |  | break; | 
| 204 |  |  |  |  |  |  | case '$': | 
| 205 |  |  |  |  |  |  | /* '$' is special only at end of pattern and only | 
| 206 |  |  |  |  |  |  | * if PATHMATCH_NO_ANCHOR_END is specified. */ | 
| 207 | 0 | 0 |  |  |  |  | if (p[1] == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)){ | 
|  |  | 0 |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | /* "dir" == "dir/" == "dir/." */ | 
| 209 | 0 |  |  |  |  |  | return (*pm_slashskip(s) == '\0'); | 
| 210 |  |  |  |  |  |  | } | 
| 211 |  |  |  |  |  |  | /* Otherwise, '$' is not special. */ | 
| 212 |  |  |  |  |  |  | /* FALL THROUGH */ | 
| 213 |  |  |  |  |  |  | default: | 
| 214 | 2793 | 100 |  |  |  |  | if (*p != *s) | 
| 215 | 1659 |  |  |  |  |  | return (0); | 
| 216 | 1134 |  |  |  |  |  | break; | 
| 217 |  |  |  |  |  |  | } | 
| 218 | 1204 |  |  |  |  |  | ++p; | 
| 219 | 1204 |  |  |  |  |  | ++s; | 
| 220 | 1204 |  |  |  |  |  | } | 
| 221 |  |  |  |  |  |  | } | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | /* Main entry point. */ | 
| 224 |  |  |  |  |  |  | int | 
| 225 | 1043 |  |  |  |  |  | lafe_pathmatch(const char *p, const char *s, int flags) | 
| 226 |  |  |  |  |  |  | { | 
| 227 |  |  |  |  |  |  | /* Empty pattern only matches the empty string. */ | 
| 228 | 1043 | 50 |  |  |  |  | if (p == NULL || *p == '\0') | 
|  |  | 50 |  |  |  |  |  | 
| 229 | 0 | 0 |  |  |  |  | return (s == NULL || *s == '\0'); | 
|  |  | 0 |  |  |  |  |  | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | /* Leading '^' anchors the start of the pattern. */ | 
| 232 | 1043 | 50 |  |  |  |  | if (*p == '^') { | 
| 233 | 0 |  |  |  |  |  | ++p; | 
| 234 | 0 |  |  |  |  |  | flags &= ~PATHMATCH_NO_ANCHOR_START; | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  |  | 
| 237 | 1043 | 50 |  |  |  |  | if (*p == '/' && *s != '/') | 
|  |  | 0 |  |  |  |  |  | 
| 238 | 0 |  |  |  |  |  | return (0); | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | /* Certain patterns and file names anchor implicitly. */ | 
| 241 | 1043 | 50 |  |  |  |  | if (*p == '*' || *p == '/' || *p == '/') { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 242 | 0 | 0 |  |  |  |  | while (*p == '/') | 
| 243 | 0 |  |  |  |  |  | ++p; | 
| 244 | 0 | 0 |  |  |  |  | while (*s == '/') | 
| 245 | 0 |  |  |  |  |  | ++s; | 
| 246 | 0 |  |  |  |  |  | return (pm(p, s, flags)); | 
| 247 |  |  |  |  |  |  | } | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | /* If start is unanchored, try to match start of each path element. */ | 
| 250 | 1043 | 100 |  |  |  |  | if (flags & PATHMATCH_NO_ANCHOR_START) { | 
| 251 | 2191 | 100 |  |  |  |  | for ( ; s != NULL; s = strchr(s, '/')) { | 
| 252 | 1603 | 100 |  |  |  |  | if (*s == '/') | 
| 253 | 910 |  |  |  |  |  | s++; | 
| 254 | 1603 | 100 |  |  |  |  | if (pm(p, s, flags)) | 
| 255 | 175 |  |  |  |  |  | return (1); | 
| 256 |  |  |  |  |  |  | } | 
| 257 | 588 |  |  |  |  |  | return (0); | 
| 258 |  |  |  |  |  |  | } | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | /* Default: Match from beginning. */ | 
| 261 | 280 |  |  |  |  |  | return (pm(p, s, flags)); | 
| 262 |  |  |  |  |  |  | } |