github.com/iDigitalFlame/xmt@v0.5.4/util/text/r_no_regexp.go (about) 1 //go:build !regexp 2 // +build !regexp 3 4 // Copyright (C) 2020 - 2023 iDigitalFlame 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with this program. If not, see <https://www.gnu.org/licenses/>. 18 // 19 20 package text 21 22 import ( 23 "strconv" 24 25 "github.com/iDigitalFlame/xmt/util" 26 ) 27 28 type tokenMatcher struct { 29 _ [0]func() 30 v string 31 i bool 32 } 33 34 func isChar(v byte) bool { 35 switch { 36 case v >= 'a' && v <= 'z': 37 fallthrough 38 case v >= 'A' && v <= 'Z': 39 fallthrough 40 case v >= '0' && v <= '9': 41 return true 42 default: 43 return false 44 } 45 } 46 func isUpper(v byte) bool { 47 return v >= 'A' && v <= 'Z' 48 } 49 func isLower(v byte) bool { 50 return v >= 'a' && v <= 'z' 51 } 52 func isLetter(v byte) bool { 53 return (v >= 'a' && v <= 'z') || (v >= 'A' && v <= 'Z') 54 } 55 func isNumber(v byte) bool { 56 return v >= '0' && v <= '9' 57 } 58 func numSize(v, b int) int { 59 var ( 60 n = v 61 c int 62 ) 63 for n > 0 { 64 c++ 65 n = n / b 66 } 67 return c 68 } 69 func isNumberHex(v byte) bool { 70 return (v >= '0' && v <= '9') || (v >= 'a' && v <= 'f') || (v >= 'A' && v <= 'F') 71 } 72 73 // String parses this MatchString value and will preform any replacements and 74 // fill any variables contained. 75 func (s Matcher) String() string { 76 var ( 77 l = -2 78 b = builders.Get().(*util.Builder) 79 f bool 80 n uint64 81 ) 82 b.Grow(len(s)) 83 for i := range s { 84 switch { 85 case s[i] == '%': 86 if l > -1 { 87 if s[l] == '%' { 88 b.WriteString((string)(s[l : i+1])) 89 l = -1 90 continue 91 } 92 b.WriteString((string)(s[l:i])) 93 } 94 l = i 95 case l > -1 && i > 0 && i-l == 1 && (s[i] == 'n' || s[i] == 'c' || s[i] == 'u' || s[i] == 'l'): 96 b.WriteString((string)(s[l : i+1])) 97 l = -1 98 case l > -1 && i > 0 && (s[i] == 'n' || s[i] == 'c' || s[i] == 'u' || s[i] == 'l' || s[i] == 's' || s[i] == 'd' || s[i] == 'h'): 99 if i-l > 1 { 100 if f = s[i-1] == 'f'; f { 101 n, _ = strconv.ParseUint((string)(s[l+1:i-1]), 10, 64) 102 } else { 103 n, _ = strconv.ParseUint((string)(s[l+1:i]), 10, 64) 104 } 105 } else { 106 f, n = false, 0 107 } 108 writeToken(b, s[i], f, int(n)) 109 l = -1 110 case s[i] == 'f': 111 if l > -1 && i-l == 1 { 112 b.WriteString((string)(s[l : i+1])) 113 l = -1 114 continue 115 } 116 fallthrough 117 case s[i] >= '0' && s[i] <= '9': 118 if l > -1 { 119 continue 120 } 121 b.WriteByte(s[i]) 122 default: 123 if l > -1 { 124 b.WriteString((string)(s[l : i+1])) 125 l = -1 126 continue 127 } 128 b.WriteByte(s[i]) 129 } 130 } 131 if l == -2 { 132 return string(s) 133 } 134 if l == -1 { 135 v := b.Output() 136 builders.Put(b) 137 return v 138 } 139 if l < len(s) { 140 b.WriteString((string)(s[l:])) 141 } 142 v := b.Output() 143 builders.Put(b) 144 return v 145 } 146 147 // MatchEx returns a valid Regexp struct that is guaranteed to match any string 148 func matchWithToken(s, v string) bool { 149 if len(s) == 0 || len(v) == 0 { 150 return false 151 } 152 var ( 153 l, w, z = -2, 0, 0 154 f bool 155 n uint64 156 ) 157 for i := range s { 158 switch { 159 case s[i] == '%': 160 if l > -1 { 161 if s[l] == '%' { 162 if s[i] != v[w] { 163 return false 164 } 165 l, w = -1, w+2 166 continue 167 } 168 } 169 l = i 170 case l > -1 && i > 0 && i-l == 1 && (s[i] == 'n' || s[i] == 'c' || s[i] == 'u' || s[i] == 'l'): 171 l = -1 172 case l > -1 && i > 0 && (s[i] == 'n' || s[i] == 'c' || s[i] == 'u' || s[i] == 'l' || s[i] == 's' || s[i] == 'd' || s[i] == 'h'): 173 if i-l > 1 { 174 if f = s[i-1] == 'f'; f { 175 n, _ = strconv.ParseUint(s[l+1:i-1], 10, 64) 176 } else { 177 n, _ = strconv.ParseUint(s[l+1:i], 10, 64) 178 } 179 } else { 180 f, n = false, 0 181 } 182 if z, f = checkToken(s[i], f, int(n), v[w:]); !f { 183 return false 184 } 185 l, w = -1, w+z 186 case s[i] == 'f': 187 if l > -1 && i-l == 1 { 188 if s[i] != v[w] { 189 return false 190 } 191 l, w = -1, w+1 192 continue 193 } 194 fallthrough 195 case s[i] >= '0' && s[i] <= '9': 196 if l > -1 { 197 continue 198 } 199 if s[i] != v[w] { 200 return false 201 } 202 w++ 203 default: 204 if l > -1 { 205 for y := l; y <= i; y++ { 206 if s[y] != v[w+(y-l)] { 207 return false 208 } 209 } 210 w += (i - l) + 1 211 l = -1 212 continue 213 } 214 if len(v) < w { 215 return false 216 } 217 if s[i] != v[w] { 218 return false 219 } 220 w++ 221 } 222 } 223 if l < 0 { 224 return l == -1 || (l == -2 && s == v) 225 } 226 if l < len(s) { 227 return len(s)-l == len(v)-w && s[l:] == v[w:] 228 } 229 return false 230 } 231 func (t tokenMatcher) String() string { 232 return t.v 233 } 234 235 // MatchEx returns a valid Regexp struct that is guaranteed to match any string 236 // generated by the Matcher's 'String' function. MatchEx returns an inverse 237 // matcher if the bool is false. 238 func (s Matcher) MatchEx(o bool) Regexp { 239 if len(s) == 0 { 240 return MatchAny 241 } 242 if s == "*" { 243 return MatchAny 244 } 245 return tokenMatcher{v: string(s), i: !o} 246 } 247 func (t tokenMatcher) Match(b []byte) bool { 248 return t.MatchString(string(b)) 249 } 250 func (t tokenMatcher) MatchString(s string) bool { 251 if t.i { 252 return !matchWithToken(t.v, s) 253 } 254 return matchWithToken(t.v, s) 255 } 256 func writeToken(b *util.Builder, c byte, f bool, n int) { 257 switch { 258 case c == 's' && !f && n == 0: 259 b.WriteString(Rand.StringRange(1, 1+int(util.FastRandN(256)))) 260 case c == 's' && !f && n > 0: 261 b.WriteString(Rand.StringRange(1, n)) 262 case c == 's' && f && n > 0: 263 b.WriteString(Rand.String(n)) 264 case c == 'd' && !f && n == 0: 265 b.WriteString(util.Uitoa(uint64(util.FastRand()))) 266 case c == 'd' && !f && n > 0: 267 b.WriteString(util.Uitoa(uint64(util.FastRandN(n)))) 268 case c == 'd' && f: 269 b.WriteString(util.Uitoa(uint64(n))) 270 case c == 'h' && !f && n == 0: 271 b.WriteString(util.Uitoa16(uint64(util.FastRand()))) 272 case c == 'h' && !f && n > 0: 273 b.WriteString(util.Uitoa16(uint64(util.FastRandN(n)))) 274 case c == 'h' && f: 275 b.WriteString(util.Uitoa16(uint64(n))) 276 case c == 'n' && !f: 277 b.WriteString(Rand.StringNumberRange(1, n)) 278 case c == 'n' && f: 279 b.WriteString(Rand.StringNumber(n)) 280 case c == 'c' && !f: 281 b.WriteString(Rand.StringCharactersRange(1, n)) 282 case c == 'c' && f: 283 b.WriteString(Rand.StringCharacters(n)) 284 case c == 'u' && !f: 285 b.WriteString(Rand.StringUpperRange(1, n)) 286 case c == 'u' && f: 287 b.WriteString(Rand.StringUpper(n)) 288 case c == 'l' && !f: 289 b.WriteString(Rand.StringLowerRange(1, n)) 290 case c == 'l' && f: 291 b.WriteString(Rand.StringLower(n)) 292 } 293 } 294 func checkToken(c byte, f bool, n int, v string) (int, bool) { 295 if len(v) == 0 { 296 return 0, false 297 } 298 var x func(byte) bool 299 switch { 300 case c == 's': 301 x = isChar 302 case c == 'd' || c == 'n': 303 x = isNumber 304 case c == 'h': 305 x = isNumberHex 306 case c == 'c': 307 x = isLetter 308 case c == 'u': 309 x = isUpper 310 case c == 'l': 311 x = isLower 312 default: 313 return 0, false 314 } 315 if !f { 316 if !x(v[0]) { 317 return 0, false 318 } 319 if n == 0 { 320 n = 256 321 } 322 for i := 1; i < len(v) && i < n; i++ { 323 if !x(v[i]) { 324 return i, true 325 } 326 } 327 return n, true 328 } 329 switch c { 330 case 'h': 331 n = numSize(n, 16) 332 case 'd': //, 'n': 333 n = numSize(n, 10) 334 } 335 if len(v) < n { 336 return 0, false 337 } 338 for i := 0; i < n; i++ { 339 if !x(v[i]) { 340 return 0, false 341 } 342 } 343 return n, true 344 }