github.com/rogpeppe/go-internal@v1.12.1-0.20240509064211-c8567cf8e95f/cmd/txtar-c/savedir.go (about)

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