github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/core/commands/unixfs/ls.go (about) 1 package unixfs 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "sort" 8 "text/tabwriter" 9 10 cmds "github.com/ipfs/go-ipfs/commands" 11 core "github.com/ipfs/go-ipfs/core" 12 path "github.com/ipfs/go-ipfs/path" 13 unixfs "github.com/ipfs/go-ipfs/unixfs" 14 unixfspb "github.com/ipfs/go-ipfs/unixfs/pb" 15 ) 16 17 type LsLink struct { 18 Name, Hash string 19 Size uint64 20 Type string 21 } 22 23 type LsObject struct { 24 Hash string 25 Size uint64 26 Type string 27 Links []LsLink 28 } 29 30 type LsOutput struct { 31 Arguments map[string]string 32 Objects map[string]*LsObject 33 } 34 35 var LsCmd = &cmds.Command{ 36 Helptext: cmds.HelpText{ 37 Tagline: "List directory contents for Unix-filesystem objects", 38 ShortDescription: ` 39 Retrieves the object named by <ipfs-or-ipns-path> and displays the 40 contents. 41 42 The JSON output contains size information. For files, the child size 43 is the total size of the file contents. For directories, the child 44 size is the IPFS link size. 45 `, 46 }, 47 48 Arguments: []cmds.Argument{ 49 cmds.StringArg("ipfs-path", true, true, "The path to the IPFS object(s) to list links from").EnableStdin(), 50 }, 51 Run: func(req cmds.Request, res cmds.Response) { 52 node, err := req.InvocContext().GetNode() 53 if err != nil { 54 res.SetError(err, cmds.ErrNormal) 55 return 56 } 57 58 paths := req.Arguments() 59 60 output := LsOutput{ 61 Arguments: map[string]string{}, 62 Objects: map[string]*LsObject{}, 63 } 64 65 for _, fpath := range paths { 66 ctx := req.Context() 67 merkleNode, err := core.Resolve(ctx, node, path.Path(fpath)) 68 if err != nil { 69 res.SetError(err, cmds.ErrNormal) 70 return 71 } 72 73 key, err := merkleNode.Key() 74 if err != nil { 75 res.SetError(err, cmds.ErrNormal) 76 return 77 } 78 79 hash := key.B58String() 80 output.Arguments[fpath] = hash 81 82 if _, ok := output.Objects[hash]; ok { 83 // duplicate argument for an already-listed node 84 continue 85 } 86 87 unixFSNode, err := unixfs.FromBytes(merkleNode.Data) 88 if err != nil { 89 res.SetError(err, cmds.ErrNormal) 90 return 91 } 92 93 t := unixFSNode.GetType() 94 95 output.Objects[hash] = &LsObject{ 96 Hash: key.String(), 97 Type: t.String(), 98 Size: unixFSNode.GetFilesize(), 99 } 100 101 switch t { 102 case unixfspb.Data_File: 103 break 104 case unixfspb.Data_Directory: 105 links := make([]LsLink, len(merkleNode.Links)) 106 output.Objects[hash].Links = links 107 for i, link := range merkleNode.Links { 108 link.Node, err = link.GetNode(ctx, node.DAG) 109 if err != nil { 110 res.SetError(err, cmds.ErrNormal) 111 return 112 } 113 d, err := unixfs.FromBytes(link.Node.Data) 114 if err != nil { 115 res.SetError(err, cmds.ErrNormal) 116 return 117 } 118 t := d.GetType() 119 lsLink := LsLink{ 120 Name: link.Name, 121 Hash: link.Hash.B58String(), 122 Type: t.String(), 123 } 124 if t == unixfspb.Data_File { 125 lsLink.Size = d.GetFilesize() 126 } else { 127 lsLink.Size = link.Size 128 } 129 links[i] = lsLink 130 } 131 case unixfspb.Data_Symlink: 132 res.SetError(fmt.Errorf("cannot list symlinks yet"), cmds.ErrNormal) 133 return 134 default: 135 res.SetError(fmt.Errorf("unrecognized type: %s", t), cmds.ErrImplementation) 136 return 137 } 138 } 139 140 res.SetOutput(&output) 141 }, 142 Marshalers: cmds.MarshalerMap{ 143 cmds.Text: func(res cmds.Response) (io.Reader, error) { 144 145 output := res.Output().(*LsOutput) 146 buf := new(bytes.Buffer) 147 w := tabwriter.NewWriter(buf, 1, 2, 1, ' ', 0) 148 149 nonDirectories := []string{} 150 directories := []string{} 151 for argument, hash := range output.Arguments { 152 object, ok := output.Objects[hash] 153 if !ok { 154 return nil, fmt.Errorf("unresolved hash: %s", hash) 155 } 156 157 if object.Type == "Directory" { 158 directories = append(directories, argument) 159 } else { 160 nonDirectories = append(nonDirectories, argument) 161 } 162 } 163 sort.Strings(nonDirectories) 164 sort.Strings(directories) 165 166 for _, argument := range nonDirectories { 167 fmt.Fprintf(w, "%s\n", argument) 168 } 169 170 seen := map[string]bool{} 171 for i, argument := range directories { 172 hash := output.Arguments[argument] 173 if _, ok := seen[hash]; ok { 174 continue 175 } 176 seen[hash] = true 177 178 object := output.Objects[hash] 179 if i > 0 || len(nonDirectories) > 0 { 180 fmt.Fprintln(w) 181 } 182 if len(output.Arguments) > 1 { 183 for _, arg := range directories[i:] { 184 if output.Arguments[arg] == hash { 185 fmt.Fprintf(w, "%s:\n", arg) 186 } 187 } 188 } 189 for _, link := range object.Links { 190 fmt.Fprintf(w, "%s\n", link.Name) 191 } 192 } 193 w.Flush() 194 195 return buf, nil 196 }, 197 }, 198 Type: LsOutput{}, 199 }