func spProgramLocal(portname string, boardname string, filePath string, commandline string, extraInfo boardExtraInfo) error {

	var err error
	if extraInfo.use_1200bps_touch {
		portname, err = touch_port_1200bps(portname, extraInfo.wait_for_upload_port)
	}

	if err != nil {
		log.Println("Could not touch the port")
		return err
	}

	log.Printf("Received commandline (unresolved):" + commandline)

	commandline = strings.Replace(commandline, "{build.path}", filepath.ToSlash(filepath.Dir(filePath)), 1)
	commandline = strings.Replace(commandline, "{build.project_name}", strings.TrimSuffix(filepath.Base(filePath), filepath.Ext(filepath.Base(filePath))), 1)
	commandline = strings.Replace(commandline, "{serial.port}", portname, 1)
	commandline = strings.Replace(commandline, "{serial.port.file}", filepath.Base(portname), 1)

	// search for runtime variables and replace with values from globalToolsMap
	var runtimeRe = regexp.MustCompile("\\{(.*?)\\}")
	runtimeVars := runtimeRe.FindAllString(commandline, -1)

	fmt.Println(runtimeVars)

	for _, element := range runtimeVars {

		// use string similarity to resolve a runtime var with a "similar" map element
		if globalToolsMap[element] == "" {
			max_similarity := 0.0
			for i, candidate := range globalToolsMap {
				similarity := smetrics.Jaro(element, i)
				if similarity > 0.8 && similarity > max_similarity {
					max_similarity = similarity
					globalToolsMap[element] = candidate
				}
			}
		}

		commandline = strings.Replace(commandline, element, globalToolsMap[element], 1)
	}

	z, _ := shellwords.Parse(commandline)
	return spHandlerProgram(z[0], z[1:])
}
// GetLocation extracts the toolname from a command like
func (t *Tools) GetLocation(command string) (string, error) {
	command = strings.Replace(command, "{runtime.tools.", "", 1)
	command = strings.Replace(command, ".path}", "", 1)

	var location string
	var ok bool

	// use string similarity to resolve a runtime var with a "similar" map element
	if location, ok = t.installed[command]; !ok {
		maxSimilarity := 0.0
		for i, candidate := range t.installed {
			similarity := smetrics.Jaro(command, i)
			if similarity > 0.8 && similarity > maxSimilarity {
				maxSimilarity = similarity
				location = candidate
			}
		}
	}

	return filepath.ToSlash(location), nil
}
func findTool(pack, name, version string, data index) (tool, system) {
	var correctTool tool
	correctTool.Version = "0.0"

	for _, p := range data.Packages {
		if p.Name != pack {
			continue
		}
		for _, t := range p.Tools {
			if version != "latest" {
				if t.Name == name && t.Version == version {
					correctTool = t
				}
			} else {
				// Find latest
				v1, _ := semver.Make(t.Version)
				v2, _ := semver.Make(correctTool.Version)
				if t.Name == name && v1.Compare(v2) > 0 {
					correctTool = t
				}
			}
		}
	}

	// Find the url based on system
	var correctSystem system
	max_similarity := 0.7

	for _, s := range correctTool.Systems {
		similarity := smetrics.Jaro(s.Host, systems[runtime.GOOS+runtime.GOARCH])
		if similarity > max_similarity {
			correctSystem = s
			max_similarity = similarity
		}
	}

	return correctTool, correctSystem
}