cat.go 2.2 KB
Newer Older
1 2 3
package commands

import (
rht's avatar
rht committed
4
	"fmt"
5 6
	"io"

7 8 9 10
	cmds "github.com/ipfs/go-ipfs/commands"
	core "github.com/ipfs/go-ipfs/core"
	path "github.com/ipfs/go-ipfs/path"
	uio "github.com/ipfs/go-ipfs/unixfs/io"
11

12 13
	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/cheggaaa/pb"
	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
14 15
)

16 17
const progressBarMinSize = 1024 * 1024 * 8 // show progress bar for outputs > 8MiB

rht's avatar
rht committed
18 19 20 21 22
type clearlineReader struct {
	io.Reader
	out io.Writer
}

23
var CatCmd = &cmds.Command{
24 25 26
	Helptext: cmds.HelpText{
		Tagline: "Show IPFS object data",
		ShortDescription: `
27
Retrieves the object named by <ipfs-or-ipns-path> and outputs the data
28
it contains.
29 30
`,
	},
31 32

	Arguments: []cmds.Argument{
33
		cmds.StringArg("ipfs-path", true, true, "The path to the IPFS object(s) to be outputted").EnableStdin(),
34
	},
35
	Run: func(req cmds.Request, res cmds.Response) {
Jeromy's avatar
Jeromy committed
36
		node, err := req.InvocContext().GetNode()
37
		if err != nil {
38 39
			res.SetError(err, cmds.ErrNormal)
			return
40 41
		}

Jeromy's avatar
Jeromy committed
42
		readers, length, err := cat(req.Context(), node, req.Arguments())
43
		if err != nil {
44 45
			res.SetError(err, cmds.ErrNormal)
			return
46
		}
Matt Bell's avatar
Matt Bell committed
47

48 49
		res.SetLength(length)

Matt Bell's avatar
Matt Bell committed
50
		reader := io.MultiReader(readers...)
51
		res.SetOutput(reader)
Matt Bell's avatar
Matt Bell committed
52
	},
53
	PostRun: func(req cmds.Request, res cmds.Response) {
54 55 56 57 58
		if res.Length() < progressBarMinSize {
			return
		}

		bar := pb.New(int(res.Length())).SetUnits(pb.U_BYTES)
59
		bar.Output = res.Stderr()
60 61 62
		bar.Start()

		reader := bar.NewProxyReader(res.Output().(io.Reader))
rht's avatar
rht committed
63
		res.SetOutput(&clearlineReader{reader, res.Stderr()})
64
	},
65
}
66

67
func cat(ctx context.Context, node *core.IpfsNode, paths []string) ([]io.Reader, uint64, error) {
68
	readers := make([]io.Reader, 0, len(paths))
69
	length := uint64(0)
Jeromy's avatar
Jeromy committed
70
	for _, fpath := range paths {
71
		dagnode, err := core.Resolve(ctx, node, path.Path(fpath))
72
		if err != nil {
73 74 75
			return nil, 0, err
		}

76
		read, err := uio.NewDagReader(ctx, dagnode, node.DAG)
77
		if err != nil {
78
			return nil, 0, err
79 80
		}
		readers = append(readers, read)
81
		length += uint64(read.Size())
82
	}
83
	return readers, length, nil
84
}
rht's avatar
rht committed
85 86 87 88 89 90 91 92

func (r *clearlineReader) Read(p []byte) (n int, err error) {
	n, err = r.Reader.Read(p)
	if err == io.EOF {
		fmt.Fprintf(r.out, "\033[2K\r") // clear progress bar line on EOF
	}
	return
}