func TestToken_qsep_pop(t *testing.T) { str := `hello "world this is" a test where token 2 was quoted` expect := "world this is" fmt.Fprintf(os.Stderr, "testing: (%s)\n", str) ntokens, tokens := token.Tokenise_qpopulated(str, " ") if ntokens != 9 { fmt.Fprintf(os.Stderr, "FAIL: bad number of tokens, expected 9 got %d from (%s)\n", ntokens, str) t.Fail() } if strings.Index(tokens[1], `"`) >= 0 { fmt.Fprintf(os.Stderr, "FAIL: token 2 contained quotes (%s) in %s\n", tokens[1], str) t.Fail() } if tokens[1] != expect { fmt.Fprintf(os.Stderr, "FAIL: token 2 expected to be 'world this is' but was '%s'\n", tokens[1]) t.Fail() } fmt.Fprintf(os.Stderr, "expected: '%s' found: '%s' [OK]\n", expect, tokens[1]) //---------------------------------------------- str = `hello "world"` // specific test on 2014/01/13 bug fix expect = "world" fmt.Fprintf(os.Stderr, "testing: (%s)\n", str) ntokens, tokens = token.Tokenise_qpopulated(str, " ") if strings.Index(tokens[1], `"`) >= 0 { fmt.Fprintf(os.Stderr, "FAIL: token 2 contained quotes (%s) in (%s)\n", tokens[1], str) t.Fail() } if tokens[1] != expect { fmt.Fprintf(os.Stderr, "FAIL: token 2 expected to be 'world this is' but was '%s'\n", tokens[1]) t.Fail() } fmt.Fprintf(os.Stderr, "expected: '%s' found: '%s got %d tokens' [OK]\n", expect, tokens[1], ntokens) //---------------------------------------------- str = `hello "world"` // lots of spaces -- result should be same as previous; 2 tokens expect = "world" fmt.Fprintf(os.Stderr, "testing: (%s)\n", str) ntokens, tokens = token.Tokenise_qpopulated(str, " ") if ntokens != 2 { fmt.Fprintf(os.Stderr, "FAIL: expected 2 tokens bug %d came back\n", ntokens) t.Fail() } if strings.Index(tokens[1], `"`) >= 0 { fmt.Fprintf(os.Stderr, "FAIL: token 2 contained quotes (%s) in (%s)\n", tokens[1], str) t.Fail() } if tokens[1] != expect { fmt.Fprintf(os.Stderr, "FAIL: token 2 expected to be 'world this is' but was '%s'\n", tokens[1]) t.Fail() } fmt.Fprintf(os.Stderr, "expected: '%s' found: '%s got %d tokens' [OK]\n", expect, tokens[1], ntokens) //---------------------------------------------- }
func Cmd2strings(cbuf string) (rdata []string, edata []string, err error) { ntokens, tokens := token.Tokenise_qpopulated(cbuf, " ") if ntokens < 1 { return } cmd := &exec.Cmd{} // set the command up cmd.Path, _ = exec.LookPath(tokens[0]) cmd.Args = tokens stdout, err := cmd.StdoutPipe() // create pipes for stderr/out srdr := bufio.NewReader(stdout) // standard out reader stderr, err := cmd.StderrPipe() erdr := bufio.NewReader(stderr) err = cmd.Start() if err != nil { rdata = make([]string, 1) edata = make([]string, 1) return } rdata = make([]string, 8192) edata = make([]string, 8192) i := 0 for { buf, _, err := srdr.ReadLine() if err != nil { break } if i < len(rdata) { // must read it all before calling wait, but don't overrun our buffer if len(buf) > 0 { nb := make([]byte, len(buf)) copy(nb, buf) rdata[i] = string(nb) i++ } } } if i > 0 { rdata = rdata[0:i] // scale back the output to just what was used } else { rdata = rdata[0:1] } i = 0 for { buf, _, err := erdr.ReadLine() if err != nil { break } if i < len(edata) { // must read it all before calling wait, but don't overrun our buffer if len(buf) > 0 { nb := make([]byte, len(buf)) copy(nb, buf) edata[i] = string(nb) i++ } } } if i > 0 { edata = edata[0:i] // scale back the output to just what was used } else { edata = edata[0:1] } err = cmd.Wait() return }
/* Parses a configuration file containing sections and key/value pairs within the sections. Returns a map of sections (by name) with each entry in the map being a map[string]interface{}. Key/values are converted and stored by the key name as either string pointers or float64s. If the value is quoted, the quotes are removed. Section names may be duplicated which causes values appearing later in subsequent sections to be added to the previously encountered values. Keys within each section must be uniqueue. If a duplicate key is encountered, the last one read will be the one that ends up in the map. If all_str is true, then all values are returned as strings; no attempt is made to convert values that seem to be numeric into actual values as it might make logic in the user programme a bit easier (no interface dreferences). */ func Parse(sectmap map[string]map[string]interface{}, fname string, all_str bool) (m map[string]map[string]interface{}, err error) { var ( rec string // record read from input file sect map[string]interface{} // current section sname string // current section name rerr error = nil // read error ) if sectmap != nil { m = sectmap if m["default"] == nil { // don't count on user creating a default section m["default"] = make(map[string]interface{}) } } else { m = make(map[string]map[string]interface{}) m["default"] = make(map[string]interface{}) } sname = "default" sect = m[sname] // always start in default section f, err := os.Open(fname) if err != nil { return } defer f.Close() br := bufio.NewReader(f) for rerr == nil { rec, rerr = br.ReadString('\n') if rerr == nil { rec = strings.Trim(rec, " \t\n") // ditch lead/trail whitespace if len(rec) == 0 { // blank line continue } switch rec[0] { case ':': // section //sname = rec[1:]; _, tokens := token.Tokenise_qpopulated(rec, " \t") // easy way to ditch everything after the first token sname = tokens[0][1:] if m[sname] == nil { sect = make(map[string]interface{}) m[sname] = sect } else { sect = m[sname] } case '#': // comment // nop case '<': m, err = Parse(m, rec[1:], all_str) if err != nil { return } default: // assume key value pair append := false first_tok := 1 // first token of var is 1, but could be later force_str := strings.Index(rec, "\"") != -1 // if a quote in the buffer, we force it to be a string ntokens, tokens := token.Tokenise_qpopulated(rec, " \t=") if ntokens >= 2 { // if key = "value" # [comment], n will be 3 or more tl := len(tokens[0]) if tokens[0][tl-1:] == "+" { //foo+= bar rather than foo = bar or foo += bar tokens[0] = tokens[0][:tl-1] append = true } else { if tokens[1] == "+" { // += results in lone plus, not to be confused with +9999 append = true first_tok++ } } key := tokens[0] if tokens[first_tok] == "" { // key = (missing value) given tokens[first_tok] = " " } fc := tokens[first_tok][0:1] if !force_str && !all_str && ((fc >= "0" && fc <= "9") || fc == "+" || fc == "-") { // allowed to convert numbers to float sect[key] = clike.Atof(tokens[first_tok]) } else { dup := "" sep := "" for i := first_tok; i < ntokens && tokens[i][0:1] != "#"; i++ { dup += sep + tokens[i] // snarf tokens up to comment reducing white space to 1 blank sep = " " } if append && sect[key] != nil { if old_str, ok := sect[key].(*string); ok { dup = *old_str + " " + dup } } sect[key] = &dup } } // silently discard token that is just a key, allowing the default to override } } } if rerr != io.EOF { err = rerr } return }
/* Parses a configuration file containing sections and key/value pairs within the sections. Returns a map of sections (by name) with each entry in the map being a map[string]interface{}. Key/values are converted and stored by the key name as either string pointers or float64s. If the value is quoted, the quotes are removed. Section names may be duplicated which causes values appearing later in subsequent sections to be added to the previously encountered values. Keys within each section must be uniqueue. If a duplicate key is encountered, the last one read will be the one that ends up in the map. If all_str is true, then all values are returned as strings; no attempt is made to convert values that seem to be numeric into actual values as it might make logic in the user programme a bit easier (no interface dreferences). */ func Parse(sectmap map[string]map[string]interface{}, fname string, all_str bool) (m map[string]map[string]interface{}, err error) { var ( rec string // record read from input file sect map[string]interface{} // current section sname string // current section name rerr error = nil // read error ) if sectmap != nil { m = sectmap if m["default"] == nil { // don't count on user creating a default section m["default"] = make(map[string]interface{}) } } else { m = make(map[string]map[string]interface{}) m["default"] = make(map[string]interface{}) } sname = "default" sect = m[sname] // always start in default section f, err := os.Open(fname) if err != nil { return } defer f.Close() br := bufio.NewReader(f) for rerr == nil { rec, rerr = br.ReadString('\n') if rerr == nil { rec = strings.Trim(rec, " \t\n") // ditch lead/trail whitespace if len(rec) == 0 { // blank line continue } switch rec[0] { case ':': // section //sname = rec[1:]; _, tokens := token.Tokenise_qpopulated(rec, " \t") // easy way to ditch everything after the first token sname = tokens[0][1:] if m[sname] == nil { sect = make(map[string]interface{}) m[sname] = sect } else { sect = m[sname] } case '#': // comment // nop case '<': m, err = Parse(m, rec[1:], all_str) if err != nil { return } default: // assume key value pair ntokens, tokens := token.Tokenise_qpopulated(rec, " \t=") if ntokens >= 2 { // if key = "value" # [comment], n will be 3 or more key := tokens[0] if tokens[1] == "" { // key = (missing value) given tokens[1] = " " } fc := tokens[1][0:1] if !all_str && ((fc >= "0" && fc <= "9") || fc == "+" || fc == "-") { // allowed to convert numbers to float sect[key] = clike.Atof(tokens[1]) } else { dup := "" sep := "" for i := 1; i < ntokens && tokens[i][0:1] != "#"; i++ { dup += sep + tokens[i] // snarf tokens up to comment reducing white space to 1 blank sep = " " } sect[key] = &dup } } // silently discard token that is just a key, allowing the default to override } } } if rerr != io.EOF { err = rerr } return }