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  }