ls.go 5.49 KB
Newer Older
1 2 3 4 5
package unixfs

import (
	"fmt"
	"io"
6
	"sort"
7 8
	"text/tabwriter"

Overbool's avatar
Overbool committed
9
	cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
10 11
	iface "github.com/ipfs/go-ipfs/core/coreapi/interface"

12 13
	cmds "gx/ipfs/QmaAP56JAwdjwisPTu4yx17whcjTr6y5JCSCF77Y1rahWV/go-ipfs-cmds"
	unixfs "gx/ipfs/Qmbvw7kpSM2p6rbQ57WGRhhqNfCiNGW6EKH4xgHLw4bsnB/go-unixfs"
Steven Allen's avatar
Steven Allen committed
14
	merkledag "gx/ipfs/QmdV35UHnL1FM52baPkeUo6u7Fxm2CRUkPTLRPxeF8a4Ap/go-merkledag"
15
	cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
16 17 18 19 20
)

type LsLink struct {
	Name, Hash string
	Size       uint64
21
	Type       string
22 23 24
}

type LsObject struct {
25 26 27
	Hash  string
	Size  uint64
	Type  string
28
	Links []LsLink
29 30 31
}

type LsOutput struct {
32 33
	Arguments map[string]string
	Objects   map[string]*LsObject
34 35 36
}

var LsCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
37
	Helptext: cmdkit.HelpText{
38
		Tagline: "List directory contents for Unix filesystem objects.",
39
		ShortDescription: `
Richard Littauer's avatar
Richard Littauer committed
40 41 42 43 44
Displays the contents of an IPFS or IPNS object(s) at the given path.

The JSON output contains size information. For files, the child size
is the total size of the file contents. For directories, the child
size is the IPFS link size.
45 46 47

This functionality is deprecated, and will be removed in future versions. If
possible, please use 'ipfs ls' instead.
Richard Littauer's avatar
Richard Littauer committed
48 49 50
`,
		LongDescription: `
Displays the contents of an IPFS or IPNS object(s) at the given path.
51

52 53
The JSON output contains size information. For files, the child size
is the total size of the file contents. For directories, the child
54
size is the IPFS link size.
Richard Littauer's avatar
Richard Littauer committed
55 56 57 58 59 60 61 62 63 64

The path can be a prefixless ref; in this case, we assume it to be an
/ipfs ref and not /ipns.

Example:

    > ipfs file ls QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ
    cat.jpg
    > ipfs file ls /ipfs/QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ
    cat.jpg
65 66 67

This functionality is deprecated, and will be removed in future versions. If
possible, please use 'ipfs ls' instead.
68 69 70
`,
	},

Jan Winkelmann's avatar
Jan Winkelmann committed
71 72
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("ipfs-path", true, true, "The path to the IPFS object(s) to list links from.").EnableStdin(),
73
	},
Overbool's avatar
Overbool committed
74 75
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		nd, err := cmdenv.GetNode(env)
76
		if err != nil {
Overbool's avatar
Overbool committed
77
			return err
78 79
		}

Overbool's avatar
Overbool committed
80
		api, err := cmdenv.GetApi(env)
81
		if err != nil {
Overbool's avatar
Overbool committed
82
			return err
83 84
		}

Overbool's avatar
Overbool committed
85 86 87 88 89
		if err := req.ParseBodyArgs(); err != nil {
			return err
		}

		paths := req.Arguments
90

91 92 93 94 95
		output := LsOutput{
			Arguments: map[string]string{},
			Objects:   map[string]*LsObject{},
		}

96
		for _, p := range paths {
Overbool's avatar
Overbool committed
97
			ctx := req.Context
98

99 100
			fpath, err := iface.ParsePath(p)
			if err != nil {
Overbool's avatar
Overbool committed
101
				return err
102 103
			}

104
			merkleNode, err := api.ResolveNode(ctx, fpath)
105
			if err != nil {
Overbool's avatar
Overbool committed
106
				return err
107 108
			}

Jeromy's avatar
Jeromy committed
109
			c := merkleNode.Cid()
110

Jeromy's avatar
Jeromy committed
111
			hash := c.String()
112
			output.Arguments[p] = hash
113 114 115 116 117 118

			if _, ok := output.Objects[hash]; ok {
				// duplicate argument for an already-listed node
				continue
			}

119 120
			ndpb, ok := merkleNode.(*merkledag.ProtoNode)
			if !ok {
Overbool's avatar
Overbool committed
121
				return merkledag.ErrNotProtobuf
122 123
			}

Overbool's avatar
Overbool committed
124
			unixFSNode, err := unixfs.FSNodeFromBytes(ndpb.Data())
125
			if err != nil {
Overbool's avatar
Overbool committed
126
				return err
127
			}
128

Overbool's avatar
Overbool committed
129
			t := unixFSNode.Type()
130 131

			output.Objects[hash] = &LsObject{
Jeromy's avatar
Jeromy committed
132
				Hash: c.String(),
133
				Type: t.String(),
Overbool's avatar
Overbool committed
134
				Size: unixFSNode.FileSize(),
135 136
			}

137
			switch t {
Overbool's avatar
Overbool committed
138
			case unixfs.TFile:
139
				break
Overbool's avatar
Overbool committed
140
			case unixfs.THAMTShard:
141
				// We need a streaming ls API for this.
Overbool's avatar
Overbool committed
142
				return fmt.Errorf("cannot list large directories yet")
Overbool's avatar
Overbool committed
143
			case unixfs.TDirectory:
144
				links := make([]LsLink, len(merkleNode.Links()))
145
				output.Objects[hash].Links = links
146
				for i, link := range merkleNode.Links() {
Overbool's avatar
Overbool committed
147
					linkNode, err := link.GetNode(ctx, nd.DAG)
148
					if err != nil {
Overbool's avatar
Overbool committed
149
						return err
150
					}
151 152
					lnpb, ok := linkNode.(*merkledag.ProtoNode)
					if !ok {
Overbool's avatar
Overbool committed
153
						return merkledag.ErrNotProtobuf
154 155
					}

Overbool's avatar
Overbool committed
156
					d, err := unixfs.FSNodeFromBytes(lnpb.Data())
157
					if err != nil {
Overbool's avatar
Overbool committed
158
						return err
159
					}
Overbool's avatar
Overbool committed
160
					t := d.Type()
161 162
					lsLink := LsLink{
						Name: link.Name,
163
						Hash: link.Cid.String(),
164
						Type: t.String(),
165
					}
Overbool's avatar
Overbool committed
166
					if t == unixfs.TFile {
Overbool's avatar
Overbool committed
167
						lsLink.Size = d.FileSize()
168 169 170
					} else {
						lsLink.Size = link.Size
					}
171
					links[i] = lsLink
172
				}
Overbool's avatar
Overbool committed
173
			case unixfs.TSymlink:
Overbool's avatar
Overbool committed
174
				return fmt.Errorf("cannot list symlinks yet")
Jeromy's avatar
Jeromy committed
175
			default:
Overbool's avatar
Overbool committed
176
				return fmt.Errorf("unrecognized type: %s", t)
177 178 179
			}
		}

Overbool's avatar
Overbool committed
180
		return cmds.EmitOnce(res, &output)
181
	},
Overbool's avatar
Overbool committed
182 183 184
	Encoders: cmds.EncoderMap{
		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *LsOutput) error {
			tw := tabwriter.NewWriter(w, 1, 2, 1, ' ', 0)
185 186 187

			nonDirectories := []string{}
			directories := []string{}
Overbool's avatar
Overbool committed
188 189
			for argument, hash := range out.Arguments {
				object, ok := out.Objects[hash]
190
				if !ok {
Overbool's avatar
Overbool committed
191
					return fmt.Errorf("unresolved hash: %s", hash)
192 193
				}

194
				if object.Type == "Directory" {
195
					directories = append(directories, argument)
196 197
				} else {
					nonDirectories = append(nonDirectories, argument)
198 199 200 201 202 203
				}
			}
			sort.Strings(nonDirectories)
			sort.Strings(directories)

			for _, argument := range nonDirectories {
Overbool's avatar
Overbool committed
204
				fmt.Fprintf(tw, "%s\n", argument)
205 206 207 208
			}

			seen := map[string]bool{}
			for i, argument := range directories {
Overbool's avatar
Overbool committed
209
				hash := out.Arguments[argument]
210 211 212 213 214
				if _, ok := seen[hash]; ok {
					continue
				}
				seen[hash] = true

Overbool's avatar
Overbool committed
215
				object := out.Objects[hash]
216
				if i > 0 || len(nonDirectories) > 0 {
Overbool's avatar
Overbool committed
217
					fmt.Fprintln(tw)
218
				}
Overbool's avatar
Overbool committed
219
				if len(out.Arguments) > 1 {
220
					for _, arg := range directories[i:] {
Overbool's avatar
Overbool committed
221 222
						if out.Arguments[arg] == hash {
							fmt.Fprintf(tw, "%s:\n", arg)
223
						}
224
					}
225 226
				}
				for _, link := range object.Links {
Overbool's avatar
Overbool committed
227
					fmt.Fprintf(tw, "%s\n", link.Name)
228 229
				}
			}
Overbool's avatar
Overbool committed
230
			tw.Flush()
231

Overbool's avatar
Overbool committed
232 233
			return nil
		}),
234 235 236
	},
	Type: LsOutput{},
}