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  }