Skip to content

Commit

Permalink
cmd/txtar: support listing files
Browse files Browse the repository at this point in the history
This is useful when wanting to round-trip a txtar file by extracting its
files into a new directory and then write the same archive again. We
can't simply read the directory to write the files to a txtar archive
again, as we'll likely just end up with sorted filenames.

That is not often what one wants; for example, for Go tests, it's often
best to keep go.mod at the top. It's also useful to keep an order that
makes sense for the human consuming the file in context.

Now, the roundtrip can be done correctly by listing the files and using
that list again when writing a txtar file back. Note that no variable
expansion happens when listing files, as otherwise it would be
impossible to keep the original variables in our round-trip scenario.

Change-Id: I384cca7ac4ce4dbfb0d3d0f437687b5a2f6298eb
Reviewed-on: https://go-review.googlesource.com/c/exp/+/437635
Reviewed-by: Ian Lance Taylor <[email protected]>
Reviewed-by: Bryan Mills <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
Run-TryBot: Daniel Martí <[email protected]>
  • Loading branch information
mvdan committed Nov 11, 2022
1 parent d0897a7 commit ab4555d
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 5 deletions.
35 changes: 30 additions & 5 deletions cmd/txtar/txtar.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@
// from stdin and extract all of its files to corresponding locations relative
// to the current, writing the archive's comment to stdout.
//
// The --list flag instructs txtar to instead read the archive file from stdin
// and list all of its files to stdout. Note that shell variables in paths are
// not expanded in this mode.
//
// Archive files are by default extracted only to the current directory or its
// subdirectories. To allow extracting outside the current directory, use the
// --unsafe flag.
//
// Shell variables in paths are expanded (using os.Expand) if the corresponding
// variable is set in the process environment. When writing an archive, the
// variables (before expansion) are preserved in the archived paths.
// When extracting, shell variables in paths are expanded (using os.Expand) if
// the corresponding variable is set in the process environment. When writing an
// archive, the variables (before expansion) are preserved in the archived paths.
//
// Example usage:
//
Expand All @@ -47,6 +51,7 @@ import (

var (
extractFlag = flag.Bool("extract", false, "if true, extract files from the archive instead of writing to it")
listFlag = flag.Bool("list", false, "if true, list files from the archive instead of writing to it")
unsafeFlag = flag.Bool("unsafe", false, "allow extraction of files outside the current directory")
)

Expand All @@ -58,13 +63,20 @@ func main() {
flag.Parse()

var err error
if *extractFlag {
switch {
case *extractFlag:
if len(flag.Args()) > 0 {
fmt.Fprintln(os.Stderr, "Usage: txtar --extract <archive.txt")
os.Exit(2)
}
err = extract()
} else {
case *listFlag:
if len(flag.Args()) > 0 {
fmt.Fprintln(os.Stderr, "Usage: txtar --list <archive.txt")
os.Exit(2)
}
err = list()
default:
paths := flag.Args()
if len(paths) == 0 {
paths = []string{"."}
Expand Down Expand Up @@ -125,6 +137,19 @@ func extract() (err error) {
return nil
}

func list() (err error) {
b, err := io.ReadAll(os.Stdin)
if err != nil {
return err
}

ar := txtar.Parse(b)
for _, f := range ar.Files {
fmt.Println(f.Name)
}
return nil
}

func archive(paths []string) (err error) {
txtarHeader := regexp.MustCompile(`(?m)^-- .* --$`)

Expand Down
18 changes: 18 additions & 0 deletions cmd/txtar/txtar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ two
three
`

var filelist = `
one.txt
dir/two.txt
$SPECIAL_LOCATION/three.txt
`[1:]

func TestMain(m *testing.M) {
code := m.Run()
txtarBin.once.Do(func() {})
Expand All @@ -49,6 +55,18 @@ func TestRoundTrip(t *testing.T) {
if err := os.Mkdir(dir, 0755); err != nil {
t.Fatal(err)
}

if out, err := txtar(t, dir, testdata, "--list"); err != nil {
t.Fatal(err)
} else if out != filelist {
t.Fatalf("txtar --list: stdout:\n%s\nwant:\n%s", out, filelist)
}
if entries, err := os.ReadDir(dir); err != nil {
t.Fatal(err)
} else if len(entries) > 0 {
t.Fatalf("txtar --list: did not expect any extracted files")
}

if out, err := txtar(t, dir, testdata, "--extract"); err != nil {
t.Fatal(err)
} else if out != comment {
Expand Down

0 comments on commit ab4555d

Please sign in to comment.