mirror of https://github.com/vbatts/go-mtree.git
Merge c2328249f0
into d54530a564
This commit is contained in:
commit
4b5bd7df4f
4
Makefile
4
Makefile
|
@ -39,10 +39,10 @@ CLEAN_FILES += .test .test.tags
|
|||
NO_VENDOR_DIR := $(shell find . -type f -name '*.go' ! -path './vendor*' ! -path './.git*' ! -path './.vscode*' -exec dirname "{}" \; | sort -u)
|
||||
|
||||
.test: $(SOURCE_FILES)
|
||||
go test -v $(NO_VENDOR_DIR) && touch $@
|
||||
go test -p 1 -v $(NO_VENDOR_DIR) && touch $@
|
||||
|
||||
.test.tags: $(SOURCE_FILES)
|
||||
set -e ; for tag in $(TAGS) ; do go test -tags $$tag -v $(NO_VENDOR_DIR) ; done && touch $@
|
||||
set -e ; for tag in $(TAGS) ; do go test -p 1 -tags $$tag -v $(NO_VENDOR_DIR) ; done && touch $@
|
||||
|
||||
.PHONY: lint
|
||||
lint: .lint
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
cli "github.com/urfave/cli/v2"
|
||||
"github.com/vbatts/go-mtree"
|
||||
)
|
||||
|
||||
func NewMutateCommand() *cli.Command {
|
||||
|
||||
return &cli.Command{
|
||||
Name: "mutate",
|
||||
Usage: "mutate an mtree",
|
||||
Description: `Mutate an mtree to have different shapes.
|
||||
TODO: more info examples`,
|
||||
Action: mutateAction,
|
||||
ArgsUsage: "<path to mtree> [path to output]",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringSliceFlag{
|
||||
Name: "strip-prefix",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "keep-comments",
|
||||
Value: false,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "keep-blank",
|
||||
Value: false,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "output",
|
||||
TakesFile: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func mutateAction(c *cli.Context) error {
|
||||
mtreePath := c.Args().Get(0)
|
||||
outputPath := c.Args().Get(1)
|
||||
stripPrexies := c.StringSlice("strip-prefix")
|
||||
keepComments := c.Bool("keep-comments")
|
||||
keepBlank := c.Bool("keep-blank")
|
||||
|
||||
if mtreePath == "" {
|
||||
return fmt.Errorf("mtree path is required.")
|
||||
} else if outputPath == "" {
|
||||
outputPath = mtreePath
|
||||
}
|
||||
|
||||
file, err := os.Open(mtreePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening %s: %w", mtreePath, err)
|
||||
}
|
||||
|
||||
spec, err := mtree.ParseSpec(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing mtree %s: %w", mtreePath, err)
|
||||
}
|
||||
|
||||
stripPrefixVisitor := stripPrefixVisitor{
|
||||
prefixes: stripPrexies,
|
||||
}
|
||||
tidyVisitor := tidyVisitor{
|
||||
keepComments: keepComments,
|
||||
keepBlank: keepBlank,
|
||||
}
|
||||
visitors := []Visitor{
|
||||
&stripPrefixVisitor,
|
||||
&tidyVisitor,
|
||||
}
|
||||
|
||||
dropped := map[int]bool{}
|
||||
entries := []mtree.Entry{}
|
||||
|
||||
skip:
|
||||
for _, entry := range spec.Entries {
|
||||
if entry.Parent != nil {
|
||||
if _, ok := dropped[entry.Parent.Pos]; ok {
|
||||
if entry.Type == mtree.DotDotType {
|
||||
// directory for this .. has been dropped so shall this
|
||||
continue
|
||||
}
|
||||
entry.Parent = entry.Parent.Parent
|
||||
// TODO: i am not sure if this is the correct behavior
|
||||
entry.Raw = strings.TrimPrefix(entry.Raw, " ")
|
||||
}
|
||||
}
|
||||
|
||||
for _, visitor := range visitors {
|
||||
drop, err := visitor.Visit(&entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if drop {
|
||||
dropped[entry.Pos] = true
|
||||
continue skip
|
||||
}
|
||||
}
|
||||
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
||||
spec.Entries = entries
|
||||
|
||||
var writer io.Writer = os.Stdout
|
||||
if outputPath != "-" {
|
||||
writer, err = os.Create(outputPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating output %s: %w", outputPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = spec.WriteTo(writer)
|
||||
if err != nil {
|
||||
return fmt.Errorf("writing mtree %s: %w", outputPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Visitor interface {
|
||||
Visit(entry *mtree.Entry) (bool, error)
|
||||
}
|
||||
|
||||
type tidyVisitor struct {
|
||||
keepComments bool
|
||||
keepBlank bool
|
||||
}
|
||||
|
||||
func (m *tidyVisitor) Visit(entry *mtree.Entry) (bool, error) {
|
||||
if !m.keepComments && entry.Type == mtree.CommentType {
|
||||
return true, nil
|
||||
} else if !m.keepBlank && entry.Type == mtree.BlankType {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
type stripPrefixVisitor struct {
|
||||
prefixes []string
|
||||
}
|
||||
|
||||
func (m *stripPrefixVisitor) Visit(entry *mtree.Entry) (bool, error) {
|
||||
if entry.Type != mtree.FullType && entry.Type != mtree.RelativeType {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
fp, err := entry.Path()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
pathSegments := strings.Split(fp, "/")
|
||||
|
||||
for _, prefix := range m.prefixes {
|
||||
|
||||
prefixSegments := strings.Split(prefix, "/")
|
||||
minLen := int(math.Min(float64(len(pathSegments)), float64(len(prefixSegments))))
|
||||
matches := make([]string, minLen)
|
||||
for i := 0; i < minLen; i++ {
|
||||
if pathSegments[i] == prefixSegments[i] {
|
||||
matches[i] = prefixSegments[i]
|
||||
}
|
||||
}
|
||||
|
||||
strip := strings.Join(matches, "/")
|
||||
if entry.Type == mtree.FullType {
|
||||
entry.Name = strings.TrimPrefix(entry.Name, strip)
|
||||
entry.Name = strings.TrimPrefix(entry.Name, "/")
|
||||
if entry.Name == "" {
|
||||
return true, nil
|
||||
}
|
||||
} else if fp == strip {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
|
@ -42,6 +42,7 @@ to support xattrs and interacting with tar archives.`
|
|||
}
|
||||
app.Commands = []*cli.Command{
|
||||
cmd.NewValidateCommand(),
|
||||
cmd.NewMutateCommand(),
|
||||
}
|
||||
|
||||
// Unfortunately urfave/cli is not at good at using DefaultCommand
|
||||
|
|
5
entry.go
5
entry.go
|
@ -134,7 +134,10 @@ func (e Entry) String() string {
|
|||
if e.Type == SpecialType || e.Type == FullType || inKeyValSlice("type=dir", e.Keywords) {
|
||||
return fmt.Sprintf("%s %s", e.Name, strings.Join(KeyValToString(e.Keywords), " "))
|
||||
}
|
||||
return fmt.Sprintf(" %s %s", e.Name, strings.Join(KeyValToString(e.Keywords), " "))
|
||||
if e.Parent != nil && e.Type != DotDotType {
|
||||
return fmt.Sprintf(" %s %s", e.Name, strings.Join(KeyValToString(e.Keywords), " "))
|
||||
}
|
||||
return fmt.Sprintf("%s %s", e.Name, strings.Join(KeyValToString(e.Keywords), " "))
|
||||
}
|
||||
|
||||
// AllKeys returns the full set of KeyVal for the given entry, based on the
|
||||
|
|
1
parse.go
1
parse.go
|
@ -58,6 +58,7 @@ func ParseSpec(r io.Reader) (*DirectoryHierarchy, error) {
|
|||
e.Type = DotDotType
|
||||
e.Raw = str
|
||||
if creator.curDir != nil {
|
||||
e.Parent = creator.curDir
|
||||
creator.curDir = creator.curDir.Parent
|
||||
}
|
||||
// nothing else to do here
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# ./lib
|
||||
lib type=dir mode=0644
|
||||
foo mode=0644 size=12288 time=1457644483.833957552 type=file
|
||||
|
||||
..
|
||||
|
||||
./lib type=dir mode=0644
|
||||
|
||||
|
||||
ayo mode=0644 size=12288 time=1457644483.833957552 type=file
|
||||
|
||||
lib/dir/sub type=dir
|
||||
lib/dir/sub/file.txt type=file
|
||||
|
||||
lib/dir/PKG.info type=file mode=0644
|
Loading…
Reference in New Issue