diff --git a/ls/display.go b/ls/display.go index 2535d1b..bf9151d 100644 --- a/ls/display.go +++ b/ls/display.go @@ -2,7 +2,9 @@ package ls import ( "fmt" + "io/fs" "os" + "path/filepath" "github.com/pee2pee/lse/ls/color" "github.com/profclems/glab/pkg/tableprinter" @@ -19,7 +21,20 @@ func (l *LS) display(dirs []Dir) (err error) { for i := range dirs { dir := dirs[i] name := dir.Info.Name() - + if l.F { + if dir.Info.IsDir() { + name = fmt.Sprintf(name + "/") + } else if dir.Info.Mode()&os.ModeSymlink == os.ModeSymlink { + originalPath, _ := filepath.EvalSymlinks(dir.Path) + name = fmt.Sprintf(name + "@" + " -> " + originalPath) + } else if dir.Info.Mode().Type() == fs.ModeSocket { + name = fmt.Sprintf(name + "=") + } else if isExecAll(dir.Info.Mode()) { + name = fmt.Sprintf(name + "*") + } else { + dir.Info.Name() + } + } if l.Q { name = fmt.Sprintf("%q", name) } diff --git a/ls/display_unix.go b/ls/display_unix.go index 8ec00a6..7e4a1e6 100644 --- a/ls/display_unix.go +++ b/ls/display_unix.go @@ -25,7 +25,7 @@ func (l *LS) _display(table *tableprinter.TablePrinter, name string, dir *Dir) ( timeStr := dir.Info.ModTime().UTC().Format("Jan 02 15:04") - table.AddRow(dir.Info.Mode(), stat.Nlink, usr.Username, group.Name, l.minifySize(dir.Info.Size()), timeStr, name) + table.AddRow(dir.Info.Mode(), stat.Nlink, usr.Username, group.Name, l.evaluateFileAndDirSize(*dir), timeStr, name) return int(stat.Blocks), nil } diff --git a/ls/display_windows.go b/ls/display_windows.go index f88ed2d..e338af1 100644 --- a/ls/display_windows.go +++ b/ls/display_windows.go @@ -3,25 +3,40 @@ package ls import ( + "github.com/profclems/glab/pkg/tableprinter" + "log" + "os" "os/user" "syscall" - - "github.com/profclems/glab/pkg/tableprinter" ) func (l *LS) _display(table *tableprinter.TablePrinter, name string, dir *Dir) (int, error) { - stat := dir.Info.Sys().(*syscall.Win32FileAttributeData) + //stat := dir.Info.Sys().(*syscall.Win32FileAttributeData) usr, err := user.Current() if err != nil { return 0, err } - blocks := stat.FileSizeLow - nlink := 1 + var data syscall.ByHandleFileInformation - timeStr := dir.Info.ModTime().UTC().Format("Jan 02 15:04") + f, err := os.Open(dir.Path) + if err != nil { + return 0, err + } + log.Println(f.Fd(), dir.Path) - table.AddRow(dir.Info.Mode(), nlink, usr.Username, usr.Gid, l.minifySize(dir.Info.Size()), timeStr, name) + err = syscall.GetFileInformationByHandle(syscall.Handle(f.Fd()), &data) + if err != nil { + return 0, err + } + + blocks := data.FileSizeLow + size := uint64(data.FileSizeHigh)<<32 | uint64(data.FileSizeLow) + //nlink := 1 + + timeStr := dir.Info.ModTime().UTC().Format("Jan 02 15:04") + table.AddRow(dir.Info.Mode(), data.NumberOfLinks, usr.Username, usr.Gid, l.minifySize(int64(size)), timeStr, name) + return int(blocks), nil } diff --git a/ls/ls.go b/ls/ls.go index f013471..24fed3e 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -15,10 +15,13 @@ import ( ) const dotCharacter = 46 +const expValue = 3 +const threshold = 1.0 type Flags struct { A bool // ls -a D bool // ls -d + F bool // ls -F G bool // ls --group L bool // ls -l Q bool // ls --quote @@ -133,7 +136,6 @@ func (l *LS) listDir(dirs []fs.DirEntry) error { }) d = append(dirs, fileDirs...) } - return l.display(d) } @@ -200,7 +202,7 @@ func (l *LS) showDirStructure() error { return nil } -func getFilesAndDirs(d []Dir) (dirs []Dir, fileDirs []Dir) { +func getFilesAndDirs(d []Dir) (dirs, fileDirs []Dir) { for _, file := range d { if file.Info.IsDir() { dirs = append(dirs, file) @@ -222,9 +224,9 @@ func (l *LS) minifySize(size int64) (sizeString string) { } for i := len(units) - 1; i >= 0; i-- { - divisor := math.Pow(10, float64(3*i)) + divisor := math.Pow(10, float64(expValue*i)) result := float64(size) / divisor - if result > 1.0 { + if result >= threshold { if int64(result) == size { sizeString = fmt.Sprintf("%d%s", size, units[i]) } else { @@ -235,3 +237,60 @@ func (l *LS) minifySize(size int64) (sizeString string) { } return } + +func (l *LS) calculateDirSize(dir Dir) (size int64) { + dirContent, filesContents := getFilesAndDirsForSize(dir) + + for _, subDir := range dirContent { + size += l.calculateDirSize(subDir) + } + + for _, file := range filesContents { + size += file.Info.Size() + } + + return +} + +func getFilesAndDirsForSize(dirP Dir) (dirs, files []Dir) { + dirContent, dirReadErr := os.ReadDir(dirP.Path) + + if dirReadErr != nil { + return nil, nil + } + + for _, dir := range dirContent { + dirInfo, dirInfoErr := dir.Info() + + if dirInfoErr != nil { + return nil, nil + } + + dirD := Dir{ + Info: dirInfo, + Path: filepath.Join(dirP.Path, dir.Name()), + } + + if dir.IsDir() { + dirs = append(dirs, dirD) + } else { + files = append(files, dirD) + } + + } + return +} + +func (l *LS) evaluateFileAndDirSize(dir Dir) string { + var size int64 + if dir.Info.IsDir() { + size = l.calculateDirSize(dir) + } else { + size = dir.Info.Size() + } + return l.minifySize(size) +} + +func isExecAll(mode os.FileMode) bool { + return mode&0100 != 0 +} diff --git a/main.go b/main.go index ee7a3a3..ee87f81 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ func main() { cmd.Flags().BoolVarP(&lsf.One, "force-entry-per-line", "1", false, "(The numeric digit “one”.) Force output to be one entry per line. This is the default when output is not to a terminal. (-l) output, and don't materialize dataless directories when listing them.") cmd.Flags().BoolVarP(&lsf.A, "all", "a", false, "show all files including hidden files") cmd.Flags().BoolVarP(&lsf.D, "directory", "d", false, "show directory structure") + cmd.Flags().BoolVarP(&lsf.F, "classify", "F", false, "classify directory files") cmd.Flags().BoolVarP(&lsf.G, "group", "g", false, "group directories before files") cmd.Flags().BoolVarP(&lsf.L, "tabular", "l", false, "show detailed directory structure in tabular form") cmd.Flags().BoolVarP(&lsf.Q, "quote", "q", false, "enclose entry names in double quotes")