ls.go 5.47 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

Jakub Sztandera's avatar
Jakub Sztandera committed
11 12 13
	cmds "github.com/ipfs/go-ipfs-cmds"
	merkledag "github.com/ipfs/go-merkledag"
	unixfs "github.com/ipfs/go-unixfs"
14
	path "github.com/ipfs/interface-go-ipfs-core/path"
15 16 17 18 19
)

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

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

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

var LsCmd = &cmds.Command{
Steven Allen's avatar
Steven Allen committed
36
	Helptext: cmds.HelpText{
37
		Tagline: "List directory contents for Unix filesystem objects. Deprecated: Use 'ipfs ls' instead",
38
		ShortDescription: `
Richard Littauer's avatar
Richard Littauer committed
39 40 41 42 43
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.
44 45 46

This functionality is deprecated, and will be removed in future versions. If
possible, please use 'ipfs ls' instead.
47 48

It is deprecated because 'ipfs file ls' drop some of the ipfs specific info and almost same as 'ipfs ls'
Richard Littauer's avatar
Richard Littauer committed
49 50 51
`,
		LongDescription: `
Displays the contents of an IPFS or IPNS object(s) at the given path.
52

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

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
66 67 68

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

It is deprecated because 'ipfs file ls' drop some of the ipfs specific info and almost same as 'ipfs ls'
71 72 73
`,
	},

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

83
		api, err := cmdenv.GetApi(env, req)
84
		if err != nil {
Overbool's avatar
Overbool committed
85
			return err
86 87
		}

Overbool's avatar
Overbool committed
88 89 90 91 92
		if err := req.ParseBodyArgs(); err != nil {
			return err
		}

		paths := req.Arguments
93

94 95 96 97 98
		output := LsOutput{
			Arguments: map[string]string{},
			Objects:   map[string]*LsObject{},
		}

99
		for _, p := range paths {
Overbool's avatar
Overbool committed
100
			ctx := req.Context
101

102
			merkleNode, err := api.ResolveNode(ctx, path.New(p))
103
			if err != nil {
Overbool's avatar
Overbool committed
104
				return err
105 106
			}

Jeromy's avatar
Jeromy committed
107
			c := merkleNode.Cid()
108

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

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

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

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

Overbool's avatar
Overbool committed
127
			t := unixFSNode.Type()
128 129

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

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

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

Overbool's avatar
Overbool committed
178
		return cmds.EmitOnce(res, &output)
179
	},
Overbool's avatar
Overbool committed
180 181 182
	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)
183 184 185

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

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

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

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

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

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