Skip to content

Commit

Permalink
Accept filenames at start or end of command
Browse files Browse the repository at this point in the history
When editing a pipeline (e.g. from shell history), it's a lot easier
to change the file if it's near the front, rather than needing to pick
a filename out from between a long list of options to `adifmt edit`
and a long pipeline chain after it.

`adifmt command file1.adi --option1=foo --option2=bar file2.adi` works
`adifmt command --option1=foo file1.adi --option2.bar file2.adi` doesn't
(option parsing stops at the first non-flag).
  • Loading branch information
flwyd committed Sep 17, 2024
1 parent 1af6510 commit 9403231
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 11 deletions.
22 changes: 13 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ found, try `go run github.com/flwyd/adif-multitool/adifmt help`
To do something useful with ADIF Multitool, the syntax is

```
adifmt command [options] files...
adifmt command [files...] [options] [files...]
```

For example, the `cat` command concatenates all input files and outputs ADIF
Expand All @@ -60,10 +60,10 @@ adifmt cat log1.adi log2.adi > combined.adi
prints all of the records in the two `logX.adi` files to the `combined.adi`
file.

Flags control input and output options. For example, to print records with
a UNIX newline between fields, two newlines between records, use lower case for
all field names, and add user defined fields `gain_db` (range ±100) and
`radio_color` (values black, white, or gray):
Options (also known as flags) control input and output options. For example,
to print records with a UNIX newline between fields, two newlines between
records, use lower case for all field names, and add user defined fields
`gain_db` (range ±100) and `radio_color` (values black, white, or gray):

```sh
adifmt cat --adi-field-separator=newline \
Expand All @@ -74,6 +74,10 @@ adifmt cat --adi-field-separator=newline \
log1.csv
```

File names to process can come before or after the options list, but cannot be
intermixed. A `--` by itself ends option parsing, which allows working with a
log file whose name starts with `-` (hyphen/minus/dash).

Multiple input and output formats are supported (currently ADI and ADX per the
ADIF spec, Cabrillo according to the WWROF spec, CSV and TSV with field names
matching the ADIF list, and JSON with a similar format to ADX).
Expand Down Expand Up @@ -844,10 +848,10 @@ Features I plan to add:
* Option for `save` to append records to an existing ADIF file.
* Count the total number of records or the number of distinct values of a
field. (The total number of records can currently be counted with
`--output=tsv`, piping the output to `wc -l`, and subtracting 1 for the
header row.) This could match the format of the “Report” comment in the
test QSOs file produced with the ADIF spec.
* Support for Cabrillo 2.0 format.
`--output=tsv --tsv-omit-header` and piping the output to `wc -l`.) This
could match the format of the “Report” comment in the test QSOs file
produced with the ADIF spec.
* Support for Cabrillo 2.0 format if needed.
See the [issues page](https://github.com/flwyd/adif-multitool/issues) for more
ideas or to suggest your own.
Expand Down
11 changes: 9 additions & 2 deletions adifmt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/flwyd/adif-multitool/adif"
"github.com/flwyd/adif-multitool/adif/spec"
"github.com/flwyd/adif-multitool/cmd"
"golang.org/x/exp/slices"
)

const (
Expand Down Expand Up @@ -104,8 +105,14 @@ func runMain(prepare func(l *adif.Logfile)) int {
if c.Configure != nil {
c.Configure(ctx, fs)
}
fs.Parse(os.Args[2:])
err := c.Run(ctx, fs.Args())
// filenames can come before or after flags, but not interspersed
args := os.Args[2:]
firstflag := slices.IndexFunc(args, func(s string) bool { return strings.HasPrefix(s, "-") })
nonflags := args[0:firstflag]
args = args[firstflag:]
fs.Parse(args)
nonflags = append(nonflags, fs.Args()...)
err := c.Run(ctx, nonflags)
if err != nil {
fmt.Fprintf(os.Stderr, "Error running %s: %v\n", name, err)
return 1
Expand Down
65 changes: 65 additions & 0 deletions adifmt/testdata/cat_flag_order.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Tests that filenames can come before or after flags (or both) but filenames
# can't come in the middle of flags.

# File before flags
exec adifmt cat foo.tsv bar.tsv --output json --json-indent 2
cmp stdout combined.json
! stderr .

# File after flags
exec adifmt cat --output json --json-indent 2 foo.tsv bar.tsv
cmp stdout combined.json
! stderr .

# One file before and one after
exec adifmt cat foo.tsv --output json --json-indent 2 bar.tsv
cmp stdout combined.json
! stderr .

# First filename stops flag parsing, so if there are flags leftover you'll get
# a file-not-found error.
! exec adifmt cat --output json foo.tsv --json-indent 2 bar.tsv
! stdout .
stderr '--json-indent: no such file'

# Double dash stops flag parsing
cp foo.tsv -with-hyphen.tsv
exec adifmt cat --output json --json-indent 2 -- -with-hyphen.tsv bar.tsv
cmp stdout combined.json
! stderr .

-- foo.tsv --
CALL MODE
K1A CW
W2B SSB
-- bar.tsv --
CALL MODE
K3C FM
W4D RTTY
-- combined.json --
{
"HEADER": {
"ADIF_VER": "3.1.4",
"CREATED_TIMESTAMP": "23450607 080910",
"PROGRAMID": "adifmt",
"PROGRAMVERSION": "(devel)"
},
"RECORDS": [
{
"CALL": "K1A",
"MODE": "CW"
},
{
"CALL": "W2B",
"MODE": "SSB"
},
{
"CALL": "K3C",
"MODE": "FM"
},
{
"CALL": "W4D",
"MODE": "RTTY"
}
]
}

0 comments on commit 9403231

Please sign in to comment.