go-hep.org/x/hep@v0.38.1/xrootd/cmd/xrd-ls/main.go (about)

     1  // Copyright ©2018 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Command xrd-ls lists directory contents on a remote xrootd server.
     6  //
     7  // Usage:
     8  //
     9  //	$> xrd-ls [OPTIONS] <dir-1> [<dir-2> [...]]
    10  //
    11  // Example:
    12  //
    13  //	$> xrd-ls root://server.example.com/some/dir
    14  //	$> xrd-ls -l root://server.example.com/some/dir
    15  //	$> xrd-ls -R root://server.example.com/some/dir
    16  //	$> xrd-ls -l -R root://server.example.com/some/dir
    17  //
    18  // Options:
    19  //
    20  //	-R	list subdirectories recursively
    21  //	-l	use a long listing format
    22  package main
    23  
    24  import (
    25  	"context"
    26  	"flag"
    27  	"fmt"
    28  	"io"
    29  	"log"
    30  	"os"
    31  	"path"
    32  	"text/tabwriter"
    33  
    34  	"go-hep.org/x/hep/xrootd"
    35  	"go-hep.org/x/hep/xrootd/xrdfs"
    36  	"go-hep.org/x/hep/xrootd/xrdio"
    37  )
    38  
    39  func init() {
    40  	flag.Usage = func() {
    41  		fmt.Fprintf(os.Stderr, `xrd-ls lists directory contents on a remote xrootd server.
    42  
    43  Usage:
    44  
    45   $> xrd-ls [OPTIONS] <dir-1> [<dir-2> [...]]
    46  
    47  Example:
    48  
    49   $> xrd-ls root://server.example.com/some/dir
    50   $> xrd-ls -l root://server.example.com/some/dir
    51   $> xrd-ls -R root://server.example.com/some/dir
    52   $> xrd-ls -l -R root://server.example.com/some/dir
    53  
    54  Options:
    55  `)
    56  		flag.PrintDefaults()
    57  	}
    58  }
    59  
    60  func main() {
    61  	log.SetPrefix("xrd-ls: ")
    62  	log.SetFlags(0)
    63  
    64  	var (
    65  		recFlag  = flag.Bool("R", false, "list subdirectories recursively")
    66  		longFlag = flag.Bool("l", false, "use a long listing format")
    67  	)
    68  
    69  	flag.Parse()
    70  
    71  	if flag.NArg() == 0 {
    72  		flag.Usage()
    73  		log.Fatalf("missing directory operand")
    74  	}
    75  
    76  	for i, dir := range flag.Args() {
    77  		if i > 0 {
    78  			// separate consecutive files by an empty line
    79  			fmt.Printf("\n")
    80  		}
    81  		err := xrdls(dir, *longFlag, *recFlag)
    82  		if err != nil {
    83  			log.Fatalf("could not list %q content: %+v", dir, err)
    84  		}
    85  	}
    86  }
    87  
    88  func xrdls(name string, long, recursive bool) error {
    89  	url, err := xrdio.Parse(name)
    90  	if err != nil {
    91  		return fmt.Errorf("could not parse %q: %w", name, err)
    92  	}
    93  
    94  	ctx := context.Background()
    95  
    96  	c, err := xrootd.NewClient(ctx, url.Addr, url.User)
    97  	if err != nil {
    98  		return fmt.Errorf("could not create client: %w", err)
    99  	}
   100  	defer c.Close()
   101  
   102  	fs := c.FS()
   103  
   104  	fi, err := fs.Stat(ctx, url.Path)
   105  	// TODO fi.Name() here is an empty string (see handling in format() below)
   106  	if err != nil {
   107  		return fmt.Errorf("could not stat %q: %w", url.Path, err)
   108  	}
   109  	err = display(ctx, fs, url.Path, fi, long, recursive)
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	return nil
   115  }
   116  
   117  func display(ctx context.Context, fs xrdfs.FileSystem, root string, fi os.FileInfo, long, recursive bool) error {
   118  	if !fi.IsDir() {
   119  		// TODO fi.Name() here is an empty string (see handling in format() below)
   120  		format(os.Stdout, root, fi, long)
   121  		return nil
   122  	}
   123  
   124  	end := ""
   125  	if recursive {
   126  		end = ":"
   127  	}
   128  
   129  	dir := path.Join(root, fi.Name())
   130  	fmt.Printf("%s%s\n", dir, end)
   131  	if long {
   132  		fmt.Printf("total %d\n", fi.Size())
   133  	}
   134  	ents, err := fs.Dirlist(ctx, dir)
   135  	if err != nil {
   136  		return fmt.Errorf("could not list dir %q: %w", dir, err)
   137  	}
   138  	o := tabwriter.NewWriter(os.Stdout, 8, 4, 0, ' ', tabwriter.AlignRight)
   139  	for _, e := range ents {
   140  		format(o, dir, e, long)
   141  	}
   142  	o.Flush()
   143  	if recursive {
   144  		for _, e := range ents {
   145  			if !e.IsDir() {
   146  				continue
   147  			}
   148  			// make an empty line before going into a subdirectory.
   149  			fmt.Printf("\n")
   150  			err := display(ctx, fs, dir, e, long, recursive)
   151  			if err != nil {
   152  				return err
   153  			}
   154  		}
   155  	}
   156  
   157  	return nil
   158  }
   159  
   160  func format(o io.Writer, root string, fi os.FileInfo, long bool) {
   161  	if !long {
   162  		fmt.Fprintf(o, "%s\n", path.Join(root, fi.Name()))
   163  		return
   164  	}
   165  
   166  	name := fi.Name()
   167  	if name == "" {
   168  		name = root
   169  	}
   170  	fmt.Fprintf(o, "%v\t %d\t %s\t %s\n", fi.Mode(), fi.Size(), fi.ModTime().Format("Jan 02 15:04"), name)
   171  }