1// Copyright 2018 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// The txtar-c command archives a directory tree as a txtar archive printed to standard output. 6// 7// Usage: 8// 9// txtar-c /path/to/dir >saved.txt 10// 11// See https://godoc.org/github.com/rogpeppe/go-internal/txtar for details of the format 12// and how to parse a txtar file. 13// 14package main 15 16import ( 17 "bytes" 18 stdflag "flag" 19 "fmt" 20 "io/ioutil" 21 "log" 22 "os" 23 "path/filepath" 24 "strings" 25 "unicode/utf8" 26 27 "github.com/rogpeppe/go-internal/txtar" 28) 29 30var flag = stdflag.NewFlagSet(os.Args[0], stdflag.ContinueOnError) 31 32func usage() { 33 fmt.Fprintf(os.Stderr, "usage: txtar-c dir >saved.txt\n") 34 flag.PrintDefaults() 35} 36 37var ( 38 quoteFlag = flag.Bool("quote", false, "quote files that contain txtar file markers instead of failing") 39 allFlag = flag.Bool("a", false, "include dot files too") 40) 41 42func main() { 43 os.Exit(main1()) 44} 45 46func main1() int { 47 flag.Usage = usage 48 if flag.Parse(os.Args[1:]) != nil { 49 return 2 50 } 51 if flag.NArg() != 1 { 52 usage() 53 return 2 54 } 55 56 log.SetPrefix("txtar-c: ") 57 log.SetFlags(0) 58 59 dir := flag.Arg(0) 60 61 a := new(txtar.Archive) 62 dir = filepath.Clean(dir) 63 filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 64 if path == dir { 65 return nil 66 } 67 name := info.Name() 68 if strings.HasPrefix(name, ".") && !*allFlag { 69 if info.IsDir() { 70 return filepath.SkipDir 71 } 72 return nil 73 } 74 if !info.Mode().IsRegular() { 75 return nil 76 } 77 data, err := ioutil.ReadFile(path) 78 if err != nil { 79 log.Fatal(err) 80 } 81 if !utf8.Valid(data) { 82 log.Printf("%s: ignoring file with invalid UTF-8 data", path) 83 return nil 84 } 85 if len(data) > 0 && !bytes.HasSuffix(data, []byte("\n")) { 86 log.Printf("%s: adding final newline", path) 87 data = append(data, '\n') 88 } 89 filename := strings.TrimPrefix(path, dir+string(filepath.Separator)) 90 if txtar.NeedsQuote(data) { 91 if !*quoteFlag { 92 log.Printf("%s: ignoring file with txtar marker in", path) 93 return nil 94 } 95 data, err = txtar.Quote(data) 96 if err != nil { 97 log.Printf("%s: ignoring unquotable file: %v", path, err) 98 return nil 99 } 100 a.Comment = append(a.Comment, []byte("unquote "+filename+"\n")...) 101 } 102 a.Files = append(a.Files, txtar.File{ 103 Name: filepath.ToSlash(filename), 104 Data: data, 105 }) 106 return nil 107 }) 108 109 data := txtar.Format(a) 110 os.Stdout.Write(data) 111 112 return 0 113} 114