ls.go 5.27 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'.
Richard Littauer's avatar
Richard Littauer committed
46 47 48
`,
		LongDescription: `
Displays the contents of an IPFS or IPNS object(s) at the given path.
49

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

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
63

Chaitanya's avatar
Chaitanya committed
64
This functionality is deprecated, and will be removed in future versions as it duplicates the functionality of 'ipfs ls'.
65 66 67
`,
	},

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

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

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

		paths := req.Arguments
87

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

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

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

Jeromy's avatar
Jeromy committed
101
			c := merkleNode.Cid()
102

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

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

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

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

Overbool's avatar
Overbool committed
121
			t := unixFSNode.Type()
122 123

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

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

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

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

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

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

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

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

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

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