package crontab import ( "fmt" "math" "strconv" "strings" ) func getRange(expr string, r bounds) (uint64, error) { var ( start, end, step uint rangeAndStep = strings.Split(expr, "/") lowAndHigh = strings.Split(rangeAndStep[0], "-") singleDigit = len(lowAndHigh) == 1 err error ) var extra uint64 if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" { start = r.min end = r.max extra = starBit } else { if lowAndHigh[0] == "L" { return 0, nil } start, err = parseIntOrName(lowAndHigh[0], r.names) if err != nil { return 0, err } switch len(lowAndHigh) { case 1: end = start case 2: end, err = parseIntOrName(lowAndHigh[1], r.names) if err != nil { return 0, err } default: return 0, fmt.Errorf("Too many hyphens: %s", expr) } } switch len(rangeAndStep) { case 1: step = 1 case 2: step, err = mustParseInt(rangeAndStep[1]) if err != nil { return 0, err } // Special handling: "N/step" means "N-max/step". if singleDigit { end = r.max } default: return 0, fmt.Errorf("Too many slashes: %s", expr) } if start < r.min { return 0, fmt.Errorf("Beginning of range (%d) below minimum (%d): %s", start, r.min, expr) } if end > r.max { return 0, fmt.Errorf("End of range (%d) above maximum (%d): %s", end, r.max, expr) } if start > end { return 0, fmt.Errorf("Beginning of range (%d) beyond end of range (%d): %s", start, end, expr) } if step == 0 { return 0, fmt.Errorf("Step of range should be a positive number: %s", expr) } return getBits(start, end, step) | extra, nil } func parseIntOrName(expr string, names map[string]uint) (uint, error) { if names != nil { if namedInt, ok := names[strings.ToLower(expr)]; ok { return namedInt, nil } } return mustParseInt(expr) } // mustParseInt parses the given expression as an int or returns an error. func mustParseInt(expr string) (uint, error) { num, err := strconv.Atoi(expr) if err != nil { return 0, fmt.Errorf("Failed to parse int from %s: %s", expr, err) } if num < 0 { return 0, fmt.Errorf("Negative number (%d) not allowed: %s", num, expr) } return uint(num), nil } // getBits sets all bits in the range [min, max], modulo the given step size. func getBits(min, max, step uint) uint64 { var bits uint64 // If step is 1, use shifts. if step == 1 { return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min) } // Else, use a simple loop. for i := min; i <= max; i += step { bits |= 1 << i } return bits } func getField(field string, r bounds) (uint64, error) { var bits uint64 ranges := strings.FieldsFunc(field, func(r rune) bool { return r == ',' }) for _, expr := range ranges { bit, err := getRange(expr, r) if err != nil { return bits, err } bits |= bit } return bits, nil }