mirror of
https://github.com/rclone/rclone.git
synced 2026-02-18 18:33:36 +00:00
198 lines
5.0 KiB
Go
198 lines
5.0 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/a8m/tree"
|
|
)
|
|
|
|
var (
|
|
// List
|
|
a = flag.Bool("a", false, "")
|
|
d = flag.Bool("d", false, "")
|
|
f = flag.Bool("f", false, "")
|
|
ignorecase = flag.Bool("ignore-case", false, "")
|
|
noreport = flag.Bool("noreport", false, "")
|
|
l = flag.Bool("l", false, "")
|
|
L = flag.Int("L", 3, "")
|
|
P = flag.String("P", "", "")
|
|
I = flag.String("I", "", "")
|
|
o = flag.String("o", "", "")
|
|
// Files
|
|
s = flag.Bool("s", false, "")
|
|
h = flag.Bool("h", false, "")
|
|
p = flag.Bool("p", false, "")
|
|
u = flag.Bool("u", false, "")
|
|
g = flag.Bool("g", false, "")
|
|
Q = flag.Bool("Q", false, "")
|
|
D = flag.Bool("D", false, "")
|
|
inodes = flag.Bool("inodes", false, "")
|
|
device = flag.Bool("device", false, "")
|
|
// Sort
|
|
U = flag.Bool("U", false, "")
|
|
v = flag.Bool("v", false, "")
|
|
t = flag.Bool("t", false, "")
|
|
c = flag.Bool("c", false, "")
|
|
r = flag.Bool("r", false, "")
|
|
dirsfirst = flag.Bool("dirsfirst", false, "")
|
|
sort = flag.String("sort", "", "")
|
|
// Graphics
|
|
i = flag.Bool("i", false, "")
|
|
C = flag.Bool("C", false, "")
|
|
)
|
|
|
|
var usage = `Usage: tree [options...] [paths...]
|
|
|
|
Options:
|
|
------- Listing options -------
|
|
-a All files are listed.
|
|
-d List directories only.
|
|
-l Follow symbolic links like directories.
|
|
-f Print the full path prefix for each file.
|
|
-L Descend only level directories deep.
|
|
-P List only those files that match the pattern given.
|
|
-I Do not list files that match the given pattern.
|
|
--ignore-case Ignore case when pattern matching.
|
|
--noreport Turn off file/directory count at end of tree listing.
|
|
-o filename Output to file instead of stdout.
|
|
-------- File options ---------
|
|
-Q Quote filenames with double quotes.
|
|
-p Print the protections for each file.
|
|
-u Displays file owner or UID number.
|
|
-g Displays file group owner or GID number.
|
|
-s Print the size in bytes of each file.
|
|
-h Print the size in a more human readable way.
|
|
-D Print the date of last modification or (-c) status change.
|
|
--inodes Print inode number of each file.
|
|
--device Print device ID number to which each file belongs.
|
|
------- Sorting options -------
|
|
-v Sort files alphanumerically by version.
|
|
-t Sort files by last modification time.
|
|
-c Sort files by last status change time.
|
|
-U Leave files unsorted.
|
|
-r Reverse the order of the sort.
|
|
--dirsfirst List directories before files (-U disables).
|
|
--sort X Select sort: name,version,size,mtime,ctime.
|
|
------- Graphics options ------
|
|
-i Don't print indentation lines.
|
|
-C Turn colorization on always.
|
|
`
|
|
|
|
type fs struct{}
|
|
|
|
func (f *fs) Stat(path string) (os.FileInfo, error) {
|
|
return os.Lstat(path)
|
|
}
|
|
func (f *fs) ReadDir(path string) ([]string, error) {
|
|
dir, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
names, err := dir.Readdirnames(-1)
|
|
dir.Close()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return names, nil
|
|
}
|
|
|
|
func main() {
|
|
flag.Usage = func() { fmt.Fprint(os.Stderr, usage) }
|
|
var nd, nf int
|
|
var dirs = []string{"."}
|
|
flag.Parse()
|
|
// Make it work with leading dirs
|
|
if args := flag.Args(); len(args) > 0 {
|
|
dirs = args
|
|
}
|
|
// Output file
|
|
var outFile = os.Stdout
|
|
var err error
|
|
if *o != "" {
|
|
outFile, err = os.Create(*o)
|
|
if err != nil {
|
|
errAndExit(err)
|
|
}
|
|
}
|
|
defer outFile.Close()
|
|
// Check sort-type
|
|
if *sort != "" {
|
|
switch *sort {
|
|
case "version", "mtime", "ctime", "name", "size":
|
|
default:
|
|
msg := fmt.Sprintf("sort type '%s' not valid, should be one of: "+
|
|
"name,version,size,mtime,ctime", *sort)
|
|
errAndExit(errors.New(msg))
|
|
}
|
|
}
|
|
// Set options
|
|
opts := &tree.Options{
|
|
// Required
|
|
Fs: new(fs),
|
|
OutFile: outFile,
|
|
// List
|
|
All: *a,
|
|
DirsOnly: *d,
|
|
FullPath: *f,
|
|
DeepLevel: *L,
|
|
FollowLink: *l,
|
|
Pattern: *P,
|
|
IPattern: *I,
|
|
IgnoreCase: *ignorecase,
|
|
// Files
|
|
ByteSize: *s,
|
|
UnitSize: *h,
|
|
FileMode: *p,
|
|
ShowUid: *u,
|
|
ShowGid: *g,
|
|
LastMod: *D,
|
|
Quotes: *Q,
|
|
Inodes: *inodes,
|
|
Device: *device,
|
|
// Sort
|
|
NoSort: *U,
|
|
ReverSort: *r,
|
|
DirSort: *dirsfirst,
|
|
VerSort: *v || *sort == "version",
|
|
ModSort: *t || *sort == "mtime",
|
|
CTimeSort: *c || *sort == "ctime",
|
|
NameSort: *sort == "name",
|
|
SizeSort: *sort == "size",
|
|
// Graphics
|
|
NoIndent: *i,
|
|
Colorize: *C,
|
|
}
|
|
for _, dir := range dirs {
|
|
inf := tree.New(dir)
|
|
d, f := inf.Visit(opts)
|
|
nd, nf = nd+d, nf+f
|
|
inf.Print(opts)
|
|
}
|
|
// Print footer report
|
|
if !*noreport {
|
|
footer := fmt.Sprintf("\n%d directories", nd)
|
|
if !opts.DirsOnly {
|
|
footer += fmt.Sprintf(", %d files", nf)
|
|
}
|
|
fmt.Fprintln(outFile, footer)
|
|
}
|
|
}
|
|
|
|
func usageAndExit(msg string) {
|
|
if msg != "" {
|
|
fmt.Fprintf(os.Stderr, msg)
|
|
fmt.Fprintf(os.Stderr, "\n\n")
|
|
}
|
|
flag.Usage()
|
|
fmt.Fprintf(os.Stderr, "\n")
|
|
os.Exit(1)
|
|
}
|
|
|
|
func errAndExit(err error) {
|
|
fmt.Fprintf(os.Stderr, "tree: \"%s\"\n", err)
|
|
os.Exit(1)
|
|
}
|