66 "io"
77 "os"
88 "path"
9+ "strings"
910
1011 "github.com/dustin/go-humanize"
1112 "github.com/ipfs/go-cid"
@@ -94,11 +95,16 @@ func ListCar(c *cli.Context) error {
9495 if pbl .Name .Exists () {
9596 name = pbl .Name .Must ().String ()
9697 }
97- size := 0
98+ fmt .Fprintf (outStream , "\t \t %s" , name )
99+
100+ size := uint64 (0 )
98101 if pbl .Tsize .Exists () {
99- size = int (pbl .Tsize .Must ().Int ())
102+ size = uint64 (pbl .Tsize .Must ().Int ())
103+ }
104+ sizePart := sizeStr (c , size )
105+ if sizePart != "" {
106+ fmt .Fprintf (outStream , "%s" , sizePart )
100107 }
101- fmt .Fprintf (outStream , "\t \t %s[%s]" , name , humanize .Bytes (uint64 (size )))
102108
103109 if c .Bool ("cids" ) {
104110 hsh := "<unknown>"
@@ -125,8 +131,8 @@ func ListCar(c *cli.Context) error {
125131 }
126132 fmt .Fprintf (outStream , "\t Unixfs %s\n " , data .DataTypeNames [ufd .FieldDataType ().Int ()])
127133 }
128- } else if c . Bool ( "cids" ) {
129- fmt . Fprintf ( outStream , "%s \n " , blk .Cid () )
134+ } else {
135+ printEntry ( c , blk . Cid () , "" , uint64 ( len ( blk .RawData ())), outStream )
130136 }
131137 }
132138
@@ -161,86 +167,166 @@ func listUnixfs(c *cli.Context, outStream io.Writer) error {
161167 return err
162168 }
163169 for _ , r := range roots {
164- if err := printUnixFSNode (c , "" , r , & ls , outStream ); err != nil {
170+ if _ , err := printUnixFSNode (c , "" , r , & ls , outStream ); err != nil {
165171 return err
166172 }
167173 }
168174 return nil
169175}
170176
171- func printUnixFSNode (c * cli.Context , prefix string , node cid.Cid , ls * ipld.LinkSystem , outStream io.Writer ) error {
172- // it might be a raw file (bytes) node. if so, not actually an error.
177+ func printUnixFSNode (c * cli.Context , prefix string , node cid.Cid , ls * ipld.LinkSystem , outStream io.Writer ) (uint64 , error ) {
173178 if node .Prefix ().Codec == cid .Raw {
174- return nil
179+ link := cidlink.Link {Cid : node }
180+
181+ rawBytes , err := ls .LoadRaw (
182+ ipld.LinkContext {Ctx : c .Context },
183+ link ,
184+ )
185+
186+ if err != nil {
187+ return 0 , err
188+ }
189+
190+ return uint64 (len (rawBytes )), nil
175191 }
176192
177193 pbn , err := ls .Load (ipld.LinkContext {}, cidlink.Link {Cid : node }, dagpb .Type .PBNode )
178194 if err != nil {
179- return err
195+ return 0 , err
180196 }
181197
182198 pbnode := pbn .(dagpb.PBNode )
183199
184200 ufd , err := data .DecodeUnixFSData (pbnode .Data .Must ().Bytes ())
185201 if err != nil {
186- return err
202+ return 0 , err
187203 }
188204
189- // only show CIDs if explicitly requested
190- printCids := c .IsSet ("cids" ) && c .Bool ("cids" )
205+ var totalSize uint64 = 0
191206
192- if ufd .FieldDataType ().Int () == data .Data_Directory {
207+ switch ufd .FieldDataType ().Int () {
208+
209+ case data .Data_Directory :
193210 i := pbnode .Links .Iterator ()
194211 for ! i .Done () {
195- _ , l := i .Next ()
196- name := path .Join (prefix , l .Name .Must ().String ())
197- if printCids {
198- cidL , _ := l .Hash .AsLink ()
199- fmt .Fprintf (outStream , "%s %s\n " , cidL .(cidlink.Link ).Cid , name )
200- } else {
201- fmt .Fprintf (outStream , "%s\n " , name )
212+ _ , link := i .Next ()
213+ name := link .Name .Must ().String ()
214+ if name == "" {
215+ continue
202216 }
203- // recurse into the file/directory
204- cl , err := l .Hash .AsLink ()
217+
218+ chPath := path .Join (prefix , name )
219+
220+ chLink , err := link .Hash .AsLink ()
205221 if err != nil {
206- return err
222+ return 0 , err
207223 }
208- if cidl , ok := cl .(cidlink.Link ); ok {
209- if err := printUnixFSNode (c , name , cidl .Cid , ls , outStream ); err != nil {
210- return err
211- }
224+
225+ chCid := chLink .(cidlink.Link ).Cid
226+
227+ chSize , err := printUnixFSNode (c , chPath , chCid , ls , outStream )
228+ if err != nil {
229+ return 0 , err
212230 }
213231
232+ totalSize += chSize
233+
234+ printEntry (c , chCid , chPath , chSize , outStream )
214235 }
215- } else if ufd .FieldDataType ().Int () == data .Data_HAMTShard {
236+
237+ case data .Data_HAMTShard :
216238 hn , err := hamt .AttemptHAMTShardFromNode (c .Context , pbn , ls )
217239 if err != nil {
218- return err
240+ return 0 , err
219241 }
242+
220243 i := hn .Iterator ()
221244 for ! i .Done () {
222- n , l := i .Next ()
223- if printCids {
224- cl , _ := l .AsLink ()
225- fmt .Fprintf (outStream , "%s %s\n " , cl .(cidlink.Link ).Cid , path .Join (prefix , n .String ()))
226- } else {
227- fmt .Fprintf (outStream , "%s\n " , path .Join (prefix , n .String ()))
228- }
229- // recurse into the file/directory
230- cl , err := l .AsLink ()
245+ key , val := i .Next ()
246+
247+ chPath := path .Join (prefix , key .String ())
248+
249+ chLink , err := val .AsLink ()
231250 if err != nil {
232- return err
251+ return 0 , err
233252 }
234- if cidl , ok := cl .(cidlink.Link ); ok {
235- if err := printUnixFSNode (c , path .Join (prefix , n .String ()), cidl .Cid , ls , outStream ); err != nil {
236- return err
237- }
253+
254+ chCid := chLink .(cidlink.Link ).Cid
255+
256+ chSize , err := printUnixFSNode (c , chPath , chCid , ls , outStream )
257+ if err != nil {
258+ return 0 , err
238259 }
260+
261+ totalSize += chSize
262+
263+ printEntry (c , chCid , chPath , chSize , outStream )
264+ }
265+
266+ case data .Data_File :
267+ size := uint64 (0 )
268+ if ufd .FieldFileSize ().Exists () {
269+ size = uint64 (ufd .FieldFileSize ().Must ().Int ())
239270 }
240- } else {
241- // file, file chunk, symlink, other un-named entities.
242- return nil
271+
272+ return size , nil
273+
274+ case data .Data_Raw :
275+ size := uint64 (0 )
276+ if ufd .FieldData ().Exists () {
277+ size = uint64 (len (ufd .FieldData ().Must ().Bytes ()))
278+ }
279+
280+ return size , nil
281+
282+ default :
283+ return 0 , nil
243284 }
244285
245- return nil
286+ return totalSize , nil
287+ }
288+
289+ func printEntry (c * cli.Context , cid cid.Cid , path string , size uint64 , outStream io.Writer ) {
290+ parts := make ([]string , 0 , 3 )
291+
292+ if path != "" {
293+ // For unixfs only show CIDs if explicitly requested
294+ if c .IsSet ("cids" ) && c .Bool ("cids" ) {
295+ parts = append (parts , cid .String ())
296+ }
297+
298+ parts = append (parts , path )
299+ } else if c .Bool ("cids" ) {
300+ parts = append (parts , cid .String ())
301+ }
302+
303+ sizePart := sizeStr (c , size )
304+ if sizePart != "" {
305+ parts = append (parts , sizePart )
306+ }
307+
308+ if len (parts ) == 0 {
309+ return
310+ }
311+
312+ fmt .Fprintln (outStream , strings .Join (parts , " " ))
313+ }
314+
315+ func sizeStr (c * cli.Context , size uint64 ) string {
316+ mode := ""
317+
318+ if c .IsSet ("sizes" ) {
319+ mode = c .String ("sizes" )
320+ } else if c .Bool ("verbose" ) {
321+ mode = "human"
322+ }
323+
324+ switch mode {
325+ case "human" :
326+ return fmt .Sprintf ("[%s]" , humanize .Bytes (uint64 (size )))
327+ case "bytes" :
328+ return fmt .Sprintf ("[%d]" , size )
329+ }
330+
331+ return ""
246332}
0 commit comments