2015-07-10 20:54:06 +00:00
|
|
|
package lfs
|
|
|
|
|
|
|
|
import (
|
2015-07-21 23:53:31 +00:00
|
|
|
"bytes"
|
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/hex"
|
2015-07-10 20:54:06 +00:00
|
|
|
"fmt"
|
2015-07-21 23:53:31 +00:00
|
|
|
"hash"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
2015-07-10 20:54:06 +00:00
|
|
|
"sort"
|
2015-07-21 23:53:31 +00:00
|
|
|
"strings"
|
2015-07-10 20:54:06 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// An Extension describes how to manipulate files during smudge and clean.
|
2015-07-24 04:53:36 +00:00
|
|
|
// Extensions are parsed from the Git config.
|
2015-07-10 20:54:06 +00:00
|
|
|
type Extension struct {
|
|
|
|
Name string
|
|
|
|
Clean string
|
|
|
|
Smudge string
|
|
|
|
Priority int
|
|
|
|
}
|
|
|
|
|
2015-07-24 04:53:36 +00:00
|
|
|
type pipeRequest struct {
|
|
|
|
action string
|
|
|
|
reader io.Reader
|
|
|
|
fileName string
|
|
|
|
extensions []Extension
|
|
|
|
}
|
|
|
|
|
|
|
|
type pipeResponse struct {
|
|
|
|
file *os.File
|
|
|
|
results []*pipeExtResult
|
|
|
|
}
|
|
|
|
|
|
|
|
type pipeExtResult struct {
|
|
|
|
name string
|
|
|
|
oidIn string
|
|
|
|
oidOut string
|
|
|
|
}
|
|
|
|
|
2015-07-21 23:53:31 +00:00
|
|
|
type extCommand struct {
|
|
|
|
cmd *exec.Cmd
|
|
|
|
out io.WriteCloser
|
|
|
|
err *bytes.Buffer
|
|
|
|
hasher hash.Hash
|
2015-07-24 04:53:36 +00:00
|
|
|
result *pipeExtResult
|
2015-07-21 23:53:31 +00:00
|
|
|
}
|
|
|
|
|
2015-07-10 20:54:06 +00:00
|
|
|
// SortExtensions sorts a map of extensions in ascending order by Priority
|
|
|
|
func SortExtensions(m map[string]Extension) ([]Extension, error) {
|
|
|
|
pMap := make(map[int]Extension)
|
2015-08-04 14:48:26 +00:00
|
|
|
priorities := make([]int, 0, len(m))
|
2015-07-10 20:54:06 +00:00
|
|
|
for n, ext := range m {
|
|
|
|
p := ext.Priority
|
|
|
|
if _, exist := pMap[p]; exist {
|
|
|
|
err := fmt.Errorf("duplicate priority %d on %s", p, n)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
pMap[p] = ext
|
|
|
|
priorities = append(priorities, p)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Ints(priorities)
|
|
|
|
|
2015-08-04 14:48:26 +00:00
|
|
|
result := make([]Extension, len(priorities))
|
|
|
|
for i, p := range priorities {
|
|
|
|
result[i] = pMap[p]
|
2015-07-10 20:54:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
2015-07-21 23:53:31 +00:00
|
|
|
|
2015-07-24 04:53:36 +00:00
|
|
|
func pipeExtensions(request *pipeRequest) (response pipeResponse, err error) {
|
2015-07-21 23:53:31 +00:00
|
|
|
var extcmds []*extCommand
|
2015-07-24 04:53:36 +00:00
|
|
|
for _, e := range request.extensions {
|
|
|
|
var pieces []string
|
|
|
|
switch request.action {
|
|
|
|
case "clean":
|
|
|
|
pieces = strings.Split(e.Clean, " ")
|
|
|
|
case "smudge":
|
|
|
|
pieces = strings.Split(e.Smudge, " ")
|
|
|
|
default:
|
|
|
|
err = fmt.Errorf("Invalid action: " + request.action)
|
|
|
|
return
|
|
|
|
}
|
2015-07-21 23:53:31 +00:00
|
|
|
name := strings.Trim(pieces[0], " ")
|
|
|
|
var args []string
|
|
|
|
for _, value := range pieces[1:] {
|
2015-07-24 04:53:36 +00:00
|
|
|
arg := strings.Replace(value, "%f", request.fileName, -1)
|
2015-07-21 23:53:31 +00:00
|
|
|
args = append(args, arg)
|
|
|
|
}
|
|
|
|
cmd := exec.Command(name, args...)
|
2015-07-24 04:53:36 +00:00
|
|
|
ec := &extCommand{cmd: cmd, result: &pipeExtResult{name: e.Name}}
|
2015-07-21 23:53:31 +00:00
|
|
|
extcmds = append(extcmds, ec)
|
|
|
|
}
|
|
|
|
|
2015-07-24 04:53:36 +00:00
|
|
|
hasher := sha256.New()
|
|
|
|
pipeReader, pipeWriter := io.Pipe()
|
|
|
|
multiWriter := io.MultiWriter(hasher, pipeWriter)
|
|
|
|
|
|
|
|
var input io.Reader
|
2015-07-21 23:53:31 +00:00
|
|
|
var output io.WriteCloser
|
2015-07-24 04:53:36 +00:00
|
|
|
input = pipeReader
|
|
|
|
extcmds[0].cmd.Stdin = input
|
|
|
|
if response.file, err = TempFile(""); err != nil {
|
2015-07-21 23:53:31 +00:00
|
|
|
return
|
|
|
|
}
|
2015-07-24 04:53:36 +00:00
|
|
|
defer response.file.Close()
|
|
|
|
output = response.file
|
2015-07-21 23:53:31 +00:00
|
|
|
|
|
|
|
last := len(extcmds) - 1
|
|
|
|
for i, ec := range extcmds {
|
|
|
|
ec.hasher = sha256.New()
|
|
|
|
|
|
|
|
if i == last {
|
|
|
|
ec.cmd.Stdout = io.MultiWriter(ec.hasher, output)
|
|
|
|
ec.out = output
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
nextec := extcmds[i+1]
|
|
|
|
var nextStdin io.WriteCloser
|
|
|
|
var stdout io.ReadCloser
|
|
|
|
if nextStdin, err = nextec.cmd.StdinPipe(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if stdout, err = ec.cmd.StdoutPipe(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ec.cmd.Stdin = input
|
|
|
|
ec.cmd.Stdout = io.MultiWriter(ec.hasher, nextStdin)
|
|
|
|
ec.out = nextStdin
|
|
|
|
|
|
|
|
input = stdout
|
|
|
|
|
|
|
|
var errBuff bytes.Buffer
|
|
|
|
ec.err = &errBuff
|
|
|
|
ec.cmd.Stderr = ec.err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, ec := range extcmds {
|
|
|
|
if err = ec.cmd.Start(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-24 04:53:36 +00:00
|
|
|
if _, err = io.Copy(multiWriter, request.reader); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err = pipeWriter.Close(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-07-21 23:53:31 +00:00
|
|
|
for _, ec := range extcmds {
|
|
|
|
if err = ec.cmd.Wait(); err != nil {
|
2015-07-24 04:53:36 +00:00
|
|
|
if ec.err != nil {
|
|
|
|
errStr := ec.err.String()
|
|
|
|
err = fmt.Errorf("Extension '%s' failed with: %s", ec.result.name, errStr)
|
|
|
|
}
|
2015-07-21 23:53:31 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if err = ec.out.Close(); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-24 04:53:36 +00:00
|
|
|
oid := hex.EncodeToString(hasher.Sum(nil))
|
2015-07-21 23:53:31 +00:00
|
|
|
for _, ec := range extcmds {
|
2015-07-24 04:53:36 +00:00
|
|
|
ec.result.oidIn = oid
|
|
|
|
oid = hex.EncodeToString(ec.hasher.Sum(nil))
|
|
|
|
ec.result.oidOut = oid
|
|
|
|
response.results = append(response.results, ec.result)
|
2015-07-21 23:53:31 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|