package hostlist import ( "fmt" "regexp" "sort" "strconv" "strings" ) func Expand(in string) (result []string, err error) { // Create ranges regular expression reStNumber := "[[:digit:]]+" reStRange := reStNumber + "-" + reStNumber reStOptionalNumberOrRange := "(" + reStNumber + ",|" + reStRange + ",)*" reStNumberOrRange := "(" + reStNumber + "|" + reStRange + ")" reStBraceLeft := "[[]" reStBraceRight := "[]]" reStRanges := reStBraceLeft + reStOptionalNumberOrRange + reStNumberOrRange + reStBraceRight reRanges := regexp.MustCompile(reStRanges) // Create host list regular expression reStDNSChars := "[a-zA-Z0-9-]+" reStPrefix := "^(" + reStDNSChars + ")" reStOptionalSuffix := "(" + reStDNSChars + ")?" re := regexp.MustCompile(reStPrefix + "([[][0-9,-]+[]])?" + reStOptionalSuffix) // Remove all delimiters from the input in = strings.TrimLeft(in, ", ") for len(in) > 0 { if v := re.FindStringSubmatch(in); v != nil { // Remove matched part from the input lenPrefix := len(v[0]) in = in[lenPrefix:] // Remove all delimiters from the input in = strings.TrimLeft(in, ", ") // matched prefix, range and suffix hlPrefix := v[1] hlRanges := v[2] hlSuffix := v[3] // Single node without ranges if hlRanges == "" { result = append(result, hlPrefix) continue } // Node with ranges if v := reRanges.FindStringSubmatch(hlRanges); v != nil { // Remove braces hlRanges = hlRanges[1 : len(hlRanges)-1] // Split host ranges at , for _, hlRange := range strings.Split(hlRanges, ",") { // Split host range at - RangeStartEnd := strings.Split(hlRange, "-") // Range is only a single number if len(RangeStartEnd) == 1 { result = append(result, hlPrefix+RangeStartEnd[0]+hlSuffix) continue } // Range has a start and an end widthRangeStart := len(RangeStartEnd[0]) widthRangeEnd := len(RangeStartEnd[1]) iStart, _ := strconv.ParseUint(RangeStartEnd[0], 10, 64) iEnd, _ := strconv.ParseUint(RangeStartEnd[1], 10, 64) if iStart > iEnd { return nil, fmt.Errorf("single range start is greater than end: %s", hlRange) } // Create print format string for range numbers doPadding := widthRangeStart == widthRangeEnd widthPadding := widthRangeStart var formatString string if doPadding { formatString = "%0" + fmt.Sprint(widthPadding) + "d" } else { formatString = "%d" } formatString = hlPrefix + formatString + hlSuffix // Add nodes from this range for i := iStart; i <= iEnd; i++ { result = append(result, fmt.Sprintf(formatString, i)) } } } else { return nil, fmt.Errorf("not at hostlist range: %s", hlRanges) } } else { return nil, fmt.Errorf("not a hostlist: %s", in) } } if result != nil { // sort sort.Strings(result) // uniq previous := 1 for current := 1; current < len(result); current++ { if result[current-1] != result[current] { if previous != current { result[previous] = result[current] } previous++ } } result = result[:previous] } return }