ls.go 5.39 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{
Chaitanya's avatar
Chaitanya committed
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

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

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

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
64

Chaitanya's avatar
Chaitanya committed
65
This functionality is deprecated, and will be removed in future versions as it duplicates the functionality of 'ipfs ls'.
Chaitanya's avatar
Chaitanya committed
66
If possible, please use 'ipfs ls' instead.
67 68 69
`,
	},

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

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

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

		paths := req.Arguments
89

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

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

98
			merkleNode, err := api.ResolveNode(ctx, path.New(p))
99
			if err != nil {
Overbool's avatar
Overbool committed
100
				return err
101 102
			}

Jeromy's avatar
Jeromy committed
103
			c := merkleNode.Cid()
104

Jeromy's avatar
Jeromy committed
105
			hash := c.String()
106
			output.Arguments[p] = hash
107 108 109 110 111 112

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

113 114
			ndpb, ok := merkleNode.(*merkledag.ProtoNode)
			if !ok {
Overbool's avatar
Overbool committed
115
				return merkledag.ErrNotProtobuf
116 117
			}

Overbool's avatar
Overbool committed
118
			unixFSNode, err := unixfs.FSNodeFromBytes(ndpb.Data())
119
			if err != nil {
Overbool's avatar
Overbool committed
120
				return err
121
			}
122

Overbool's avatar
Overbool committed
123
			t := unixFSNode.Type()
124 125

			output.Objects[hash] = &LsObject{
Jeromy's avatar
Jeromy committed
126
				Hash: c.String(),
127
				Type: t.String(),
Overbool's avatar
Overbool committed
128
				Size: unixFSNode.FileSize(),
129 130
			}

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

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

Overbool's avatar
Overbool committed
174
		return cmds.EmitOnce(res, &output)
175
	},
Overbool's avatar
Overbool committed
176 177 178
	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)
179 180 181

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

188
				if object.Type == "Directory" {
189
					directories = append(directories, argument)
190 191
				} else {
					nonDirectories = append(nonDirectories, argument)
192 193 194 195 196 197
				}
			}
			sort.Strings(nonDirectories)
			sort.Strings(directories)

			for _, argument := range nonDirectories {
Overbool's avatar
Overbool committed
198
				fmt.Fprintf(tw, "%s\n", argument)
199 200 201 202
			}

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

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

Overbool's avatar
Overbool committed
226 227
			return nil
		}),
228 229 230
	},
	Type: LsOutput{},
}