diff --git a/cmd/root.go b/cmd/root.go index 169750d..19a89ab 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -196,8 +196,14 @@ func Execute() { "config file (default: ~/.config/tablizer/config)") // filters - rootCmd.PersistentFlags().StringArrayVarP(&conf.Rawfilters, "filter", "F", nil, "Filter by field (field=regexp)") - rootCmd.PersistentFlags().StringArrayVarP(&conf.Transposers, "regex-transposer", "R", nil, "apply /search/replace/ regexp to fields given in -T") + rootCmd.PersistentFlags().StringArrayVarP(&conf.Rawfilters, + "filter", "F", nil, "Filter by field (field=regexp)") + rootCmd.PersistentFlags().StringArrayVarP(&conf.Transposers, + "regex-transposer", "R", nil, "apply /search/replace/ regexp to fields given in -T") + + // input + rootCmd.PersistentFlags().StringVarP(&conf.InputFile, "read-file", "r", "", + "Read input data from file") rootCmd.SetUsageTemplate(strings.TrimSpace(usage) + "\n") diff --git a/lib/helpers.go b/lib/helpers.go index 1a7a511..176e64e 100644 --- a/lib/helpers.go +++ b/lib/helpers.go @@ -121,19 +121,25 @@ func PrepareColumnVars(columns string, data *Tabdata) ([]int, error) { // is not a regexp (contains no non-word chars) // lc() it so that word searches are case insensitive columnpattern = strings.ToLower(columnpattern) - } - colPattern, err := regexp.Compile(columnpattern) - if err != nil { - msg := fmt.Sprintf("Could not parse columns list %s: %v", columns, err) + for i, head := range data.headers { + if columnpattern == strings.ToLower(head) { + usecolumns = append(usecolumns, i+1) + } + } + } else { + colPattern, err := regexp.Compile("(?i)" + columnpattern) + if err != nil { + msg := fmt.Sprintf("Could not parse columns list %s: %v", columns, err) - return nil, errors.New(msg) - } + return nil, errors.New(msg) + } - // find matching header fields, ignoring case - for i, head := range data.headers { - if colPattern.MatchString(strings.ToLower(head)) { - usecolumns = append(usecolumns, i+1) + // find matching header fields, ignoring case + for i, head := range data.headers { + if colPattern.MatchString(strings.ToLower(head)) { + usecolumns = append(usecolumns, i+1) + } } } } else { diff --git a/lib/helpers_test.go b/lib/helpers_test.go index ecfc8f5..106cc91 100644 --- a/lib/helpers_test.go +++ b/lib/helpers_test.go @@ -67,8 +67,8 @@ func TestPrepareColumns(t *testing.T) { }{ {"1,2,3", []int{1, 2, 3}, false}, {"1,2,", []int{}, true}, - {"T", []int{2, 3}, false}, - {"T,2,3", []int{2, 3}, false}, + {"T.", []int{2, 3}, false}, + {"T.,2,3", []int{2, 3}, false}, {"[a-z,4,5", []int{4, 5}, true}, // invalid regexp } @@ -117,13 +117,13 @@ func TestPrepareTransposerColumns(t *testing.T) { false, }, { - "T", // will match [T]WO and [T]HREE + "T.", // will match [T]WO and [T]HREE []string{`/\d/x/`, `/.//`}, 2, false, }, { - "T,2", + "TH.,2", []string{`/\d/x/`, `/.//`}, 2, false, diff --git a/lib/io.go b/lib/io.go index 3fe28c7..6c25549 100644 --- a/lib/io.go +++ b/lib/io.go @@ -29,7 +29,7 @@ import ( const RWRR = 0755 func ProcessFiles(conf *cfg.Config, args []string) error { - fds, pattern, err := determineIO(conf, args) + fd, pattern, err := determineIO(conf, args) if err != nil { return err @@ -39,28 +39,67 @@ func ProcessFiles(conf *cfg.Config, args []string) error { return err } - for _, fd := range fds { - data, err := Parse(*conf, fd) - if err != nil { - return err - } - - if err = ValidateConsistency(&data); err != nil { - return err - } - - err = PrepareColumns(conf, &data) - if err != nil { - return err - } - - printData(os.Stdout, *conf, &data) + data, err := Parse(*conf, fd) + if err != nil { + return err } + if err = ValidateConsistency(&data); err != nil { + return err + } + + err = PrepareColumns(conf, &data) + if err != nil { + return err + } + + printData(os.Stdout, *conf, &data) + return nil } -func determineIO(conf *cfg.Config, args []string) ([]io.Reader, string, error) { +func determineIO(conf *cfg.Config, args []string) (io.Reader, string, error) { + var filehandle io.Reader + var pattern string + var haveio bool + + switch { + case conf.InputFile == "-": + filehandle = os.Stdin + haveio = true + case conf.InputFile != "": + fd, err := os.OpenFile(conf.InputFile, os.O_RDONLY, RWRR) + + if err != nil { + return nil, "", fmt.Errorf("failed to read input file %s: %w", conf.InputFile, err) + } + + filehandle = fd + haveio = true + } + + if !haveio { + stat, _ := os.Stdin.Stat() + if (stat.Mode() & os.ModeCharDevice) == 0 { + // we're reading from STDIN, which takes precedence over file args + filehandle = os.Stdin + haveio = true + } + } + + if len(args) > 0 { + pattern = args[0] + conf.Pattern = args[0] + } + + if !haveio { + return nil, "", errors.New("no file specified and nothing to read on stdin") + } + + return filehandle, pattern, nil +} + +func _determineIO(conf *cfg.Config, args []string) ([]io.Reader, string, error) { var filehandles []io.Reader var pattern string @@ -80,7 +119,7 @@ func determineIO(conf *cfg.Config, args []string) ([]io.Reader, string, error) { haveio = true } else if len(args) > 0 { - // threre were args left, take a look + // there were args left, take a look if args[0] == "-" { // in traditional unix programs a dash denotes STDIN (forced) filehandles = append(filehandles, os.Stdin)