func readLine(data *[]byte, pattern *regexp.Regexp) ([]string, error) { if len(*data) == 0 { return nil, io.EOF } indexes := pattern.FindSubmatchIndex(*data) if indexes == nil { log.Println(len(*data), string((*data)[:200])) return nil, ErrLineNotFound } else if indexes[0] != 0 { log.Println(indexes) // fmt.Println(string((*data)[:indexes[len(indexes)-1]])) nl := bytes.IndexAny(*data, "\r\n") if nl == -1 && nl+2 < len(*data) { return nil, ErrGarbageData } *data = (*data)[nl+1:] return readLine(data, pattern) } parts := make([]string, len(indexes)/2-1) for i := 2; i < len(indexes); i += 2 { parts[i/2-1] = string((*data)[indexes[i]:indexes[i+1]]) } *data = (*data)[indexes[1]:] return parts, nil }
func (t *transportBasicIO) WaitForRegexp(re *regexp.Regexp) ([]byte, [][]byte, error) { var matches [][]byte out, err := t.WaitForFunc(func(buf []byte) (int, error) { loc := re.FindSubmatchIndex(buf) if loc != nil { for i := 2; i < len(loc); i += 2 { matches = append(matches, buf[loc[i]:loc[i+1]]) } return loc[1], nil } return -1, nil }) return out, matches, err }
// getDate parses out a date from a git header (or blob with a header // following the size and null byte). It returns the date and index // that the unix seconds begins at within h. func getDate(h []byte, rx *regexp.Regexp) (d date, idx int) { m := rx.FindSubmatchIndex(h) if m == nil { log.Fatalf("Failed to match %s in %q", rx, h) } v := string(h[m[2]:m[3]]) space := strings.Index(v, " ") if space < 0 { log.Fatalf("unexpected date %q", v) } n, err := strconv.ParseInt(v[:space], 10, 64) if err != nil { log.Fatalf("unexpected date %q", v) } return date{n, v[space+1:]}, m[2] }
func replaceAll(src []byte, re *regexp.Regexp, replace func(out, src []byte, m []int) []byte) []byte { var out []byte for len(src) > 0 { m := re.FindSubmatchIndex(src) if m == nil { break } out = append(out, src[:m[0]]...) out = replace(out, src, m) src = src[m[1]:] } if out == nil { return src } return append(out, src...) }
// findTitleMatch returns the start of the match and the "title" subgroup of // bytes. If the regexp doesn't match, it will return -1 and nil. func findTitleMatch(titleRE *regexp.Regexp, input []byte) (start int, title []byte) { indices := titleRE.FindSubmatchIndex(input) if len(indices) == 0 { return -1, nil } for i, name := range titleRE.SubexpNames() { if name == "title" { start, end := indices[2*i], indices[2*i+1] return indices[0], input[start:end] } } // there was no grouped named title return -1, nil }
func (exp *Expect) checkForMatch(pat *regexp.Regexp) (m Match, found bool) { matches := pat.FindSubmatchIndex(exp.buffer) if matches != nil { found = true groupCount := len(matches) / 2 m.Groups = make([]string, groupCount) for i := 0; i < groupCount; i++ { start := matches[2*i] end := matches[2*i+1] if start >= 0 && end >= 0 { m.Groups[i] = string(exp.buffer[start:end]) } } m.Before = string(exp.buffer[0:matches[0]]) exp.buffer = exp.buffer[matches[1]:] } return }
func (exp *Expect) checkForMatch(expr *regexp.Regexp) bool { exp.locker.Lock() matches := expr.FindSubmatchIndex(exp.buffer) defer exp.locker.Unlock() if matches != nil { groupCount := len(matches) / 2 exp.Groups = make([]string, groupCount) for i := 0; i < groupCount; i++ { start := matches[2*i] end := matches[2*i+1] if start >= 0 && end >= 0 { exp.Groups[i] = string(exp.buffer[start:end]) } } exp.Before = string(exp.buffer[0:matches[0]]) exp.buffer = exp.buffer[matches[1]:] return true } return false }
func cutAllSubmatch(r *regexp.Regexp, b []byte, n int) []byte { i := r.FindSubmatchIndex(b) return bytesCut(b, i[2*n], i[2*n+1]) }
func (c *CssCompressor) performGeneralCleanup() { // This function does a lot, ok? var sb bytes.Buffer var previousIndex int var re *regexp.Regexp // Remove the spaces before the things that should not have spaces before them. // But, be careful not to turn "p :link {...}" into "p:link{...}" // Swap out any pseudo-class colons with the token, and then swap back. c.Css = RegexFindReplace(c.Css, "(^|\\})(([^\\{:])+:)+([^\\{]*\\{)", func(groups []string) string { s := groups[0] s = strings.Replace(s, ":", "___YUICSSMIN_PSEUDOCLASSCOLON___", -1) s = strings.Replace(s, "\\\\", "\\\\\\\\", -1) s = strings.Replace(s, "\\$", "\\\\\\$", -1) return s }) // Remove spaces before the things that should not have spaces before them. re, _ = regexp.Compile("\\s+([!{};:>+\\(\\)\\],])") c.Css = re.ReplaceAll(c.Css, []byte("$1")) // Restore spaces for !important c.Css = bytes.Replace(c.Css, []byte("!important"), []byte(" !important"), -1) // bring back the colon c.Css = bytes.Replace(c.Css, []byte("___YUICSSMIN_PSEUDOCLASSCOLON___"), []byte(":"), -1) // retain space for special IE6 cases c.Css = RegexFindReplace(c.Css, "(?i):first\\-(line|letter)(\\{|,)", func(groups []string) string { return strings.ToLower(":first-"+groups[1]) + " " + groups[2] }) // no space after the end of a preserved comment c.Css = bytes.Replace(c.Css, []byte("*/ "), []byte("*/"), -1) // If there are multiple @charset directives, push them to the top of the file. c.Css = RegexFindReplace(c.Css, "(?i)^(.*)(@charset)( \"[^\"]*\";)", func(groups []string) string { return strings.ToLower(groups[2]) + groups[3] + groups[1] }) // When all @charset are at the top, remove the second and after (as they are completely ignored). c.Css = RegexFindReplace(c.Css, "(?i)^((\\s*)(@charset)( [^;]+;\\s*))+", func(groups []string) string { return groups[2] + strings.ToLower(groups[3]) + groups[4] }) // lowercase some popular @directives c.Css = RegexFindReplace(c.Css, "(?i)@(charset|font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)", func(groups []string) string { return "@" + strings.ToLower(groups[1]) }) // lowercase some more common pseudo-elements c.Css = RegexFindReplace(c.Css, "(?i):(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)", func(groups []string) string { return ":" + strings.ToLower(groups[1]) }) // lowercase some more common functions c.Css = RegexFindReplace(c.Css, "(?i):(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\\(", func(groups []string) string { return ":" + strings.ToLower(groups[1]) + "(" }) // lower case some common function that can be values // NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us right after this c.Css = RegexFindReplace(c.Css, "(?i)([:,\\( ]\\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)", func(groups []string) string { return groups[1] + strings.ToLower(groups[2]) }) // Put the space back in some cases, to support stuff like // @media screen and (-webkit-min-device-pixel-ratio:0){ re, _ = regexp.Compile("(?i)\\band\\(") c.Css = re.ReplaceAll(c.Css, []byte("and (")) // Remove the spaces after the things that should not have spaces after them. re, _ = regexp.Compile("([!{}:;>+\\(\\[,])\\s+") c.Css = re.ReplaceAll(c.Css, []byte("$1")) // remove unnecessary semicolons re, _ = regexp.Compile(";+}") c.Css = re.ReplaceAll(c.Css, []byte("}")) // Replace 0(px,em,%) with 0. re, _ = regexp.Compile("(?i)(^|[^0-9])(?:0?\\.)?0(?:px|em|%|in|cm|mm|pc|pt|ex|deg|g?rad|m?s|k?hz)") c.Css = re.ReplaceAll(c.Css, []byte("${1}0")) // Replace 0 0 0 0; with 0. re, _ = regexp.Compile(":0 0 0 0(;|})") re2, _ := regexp.Compile(":0 0 0(;|})") re3, _ := regexp.Compile(":0 0(;|})") c.Css = re.ReplaceAll(c.Css, []byte(":0$1")) c.Css = re2.ReplaceAll(c.Css, []byte(":0$1")) c.Css = re3.ReplaceAll(c.Css, []byte(":0$1")) // Replace background-position:0; with background-position:0 0; // same for transform-origin c.Css = RegexFindReplace(c.Css, "(?i)(background-position|webkit-mask-position|transform-origin|webkit-transform-origin|moz-transform-origin|o-transform-origin|ms-transform-origin):0(;|})", func(groups []string) string { return strings.ToLower(groups[1]) + ":0 0" + groups[2] }) // Replace 0.6 to .6, but only when preceded by : or a white-space re, _ = regexp.Compile("(:|\\s)0+\\.(\\d+)") c.Css = re.ReplaceAll(c.Css, []byte("$1.$2")) // Shorten colors from rgb(51,102,153) to #336699 // This makes it more likely that it'll get further compressed in the next step. c.Css = RegexFindReplace(c.Css, "rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)", func(groups []string) string { rgbcolors := strings.Split(groups[1], ",") var hexcolor bytes.Buffer hexcolor.WriteString("#") for _, colour := range rgbcolors { val, _ := strconv.Atoi(colour) if val < 16 { hexcolor.WriteString("0") } // If someone passes an RGB value that's too big to express in two characters, round down. // Probably should throw out a warning here, but generating valid CSS is a bigger concern. if val > 255 { val = 255 } hexcolor.WriteString(fmt.Sprintf("%x", val)) } return hexcolor.String() }) // Shorten colors from #AABBCC to #ABC. Note that we want to make sure // the color is not preceded by either ", " or =. Indeed, the property // filter: chroma(color="#FFFFFF"); // would become // filter: chroma(color="#FFF"); // which makes the filter break in IE. // We also want to make sure we're only compressing #AABBCC patterns inside { }, not id selectors ( #FAABAC {} ) // We also want to avoid compressing invalid values (e.g. #AABBCCD to #ABCD) sb.Reset() re, _ = regexp.Compile("(\\=\\s*?[\"']?)?" + "#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])" + "(:?\\}|[^0-9a-fA-F{][^{]*?\\})") previousIndex = 0 for match := re.Find(c.Css[previousIndex:]); match != nil; match = re.Find(c.Css[previousIndex:]) { index := re.FindIndex(c.Css[previousIndex:]) submatches := re.FindStringSubmatch(string(c.Css[previousIndex:])) submatchIndexes := re.FindSubmatchIndex(c.Css[previousIndex:]) sb.WriteString(string(c.Css[previousIndex : index[0]+len(c.Css[:previousIndex])])) //boolean isFilter = (m.group(1) != null && !"".equals(m.group(1))); // I hope the below is the equivalent of the above :P isFilter := submatches[1] != "" && submatchIndexes[1] != -1 if isFilter { // Restore, as is. Compression will break filters sb.WriteString(submatches[1] + "#" + submatches[2] + submatches[3] + submatches[4] + submatches[5] + submatches[6] + submatches[7]) } else { if strings.ToLower(submatches[2]) == strings.ToLower(submatches[3]) && strings.ToLower(submatches[4]) == strings.ToLower(submatches[5]) && strings.ToLower(submatches[6]) == strings.ToLower(submatches[7]) { // #AABBCC pattern sb.WriteString("#" + strings.ToLower(submatches[3]+submatches[5]+submatches[7])) } else { // Non-compressible color, restore, but lower case. sb.WriteString("#" + strings.ToLower(submatches[2]+submatches[3]+submatches[4]+submatches[5]+submatches[6]+submatches[7])) } } // The "+ 4" below is a crazy hack which will come back to haunt me later. // For now, it makes everything work 100%. previousIndex = submatchIndexes[7] + len(c.Css[:previousIndex]) + 4 } if previousIndex > 0 { sb.WriteString(string(c.Css[previousIndex:])) } if sb.Len() > 0 { c.Css = sb.Bytes() } // Save a few chars by utilizing short colour keywords. // https://github.com/yui/yuicompressor/commit/fe8cf35d3693910103d65bf465d33b0d602dcfea colours := map[string]string{ "#f00": "red", "#000080": "navy", "#808080": "gray", "#808000": "olive", "#800080": "purple", "#c0c0c0": "silver", "#008080": "teal", "#ffa500": "orange", "#800000": "maroon", } for k, v := range colours { re, _ = regexp.Compile("(:|\\s)" + k + "(;|})") c.Css = re.ReplaceAll(c.Css, []byte("${1}"+v+"${2}")) } // border: none -> border:0 c.Css = RegexFindReplace(c.Css, "(?i)(border|border-top|border-right|border-bottom|border-left|outline|background):none(;|})", func(groups []string) string { return strings.ToLower(groups[1]) + ":0" + groups[2] }) // shorter opacity IE filter re, _ = regexp.Compile("(?i)progid:DXImageTransform.Microsoft.Alpha\\(Opacity=") c.Css = re.ReplaceAll(c.Css, []byte("alpha(opacity=")) // Find a fraction that is used for Opera's -o-device-pixel-ratio query // Add token to add the "\" back in later re, _ = regexp.Compile("\\(([\\-A-Za-z]+):([0-9]+)\\/([0-9]+)\\)") c.Css = re.ReplaceAll(c.Css, []byte("(${1}:${2}___YUI_QUERY_FRACTION___${3})")) // Remove empty rules. re, _ = regexp.Compile("[^\\}\\{/;]+\\{\\}") c.Css = re.ReplaceAll(c.Css, []byte("")) // Add "\" back to fix Opera -o-device-pixel-ratio query c.Css = bytes.Replace(c.Css, []byte("___YUI_QUERY_FRACTION___"), []byte("/"), -1) }