Files
2020-09-06 17:35:07 +05:30

304 lines
7.2 KiB
Go

// this file contain dir type definition
package main
import (
"bytes"
"fmt"
"io"
"log"
"os"
"path/filepath"
"sort"
"strings"
"syscall"
"time"
"github.com/Yash-Handa/logo-ls/ctw"
)
type file struct {
name, ext, indicator string
modTime time.Time
size int64 // in bytes
mode string
modeBits uint32
owner, group string // use syscall package
blocks int64 // blocks required by the file multiply buy 512 to get block size
// 'U'-> untracked file 'M'-> Modified file '●'-> modified dir ' '-> Not Updated/ not in git repo
gitStatus string
icon string
iconColor string
}
type dir struct {
info *file
parent *file
files []*file // all child files and dirs
dirs []string // for recursion contain only child dirs
less func(int, int) bool
}
// define methods on *dir type only not on file type
func newDir(d *os.File) (*dir, error) {
// some flag variable combinations
var long, curDir, showHidden bool = flagVector&(flag_l|flag_o|flag_g) > 0, flagVector&(flag_a|flag_d) > 0, flagVector&(flag_a|flag_A) > 0
t := new(dir)
// filing current dir info
t.info = new(file)
t.info.name = "."
ds, err := d.Stat()
if err != nil {
return nil, err
}
// getting Git Status of the entire repository
var gitRepoStatus map[string]string // could be nil
if flagVector&flag_D > 0 {
gitRepoStatus = getFilesGitStatus(d.Name()) // returns map or nil
}
if curDir {
t.info.size = ds.Size()
t.info.modTime = ds.ModTime()
if long {
t.info.mode = ds.Mode().String()
t.info.modeBits = uint32(ds.Mode())
t.info.owner, t.info.group = getOwnerGroupInfo(ds)
}
if flagVector&flag_s > 0 {
if s, ok := ds.Sys().(*syscall.Stat_t); ok {
t.info.blocks = s.Blocks
}
}
if flagVector&flag_i == 0 {
t.info.icon = iDef["diropen"].getGlyph()
if flagVector&flag_c == 0 {
t.info.iconColor = iDef["diropen"].getColor(1)
}
}
}
// don't fill files info if the -d flag is passed
if flagVector&flag_d > 0 {
t.files = append(t.files, t.info)
return t, nil
}
files, err := d.Readdir(0)
for _, v := range files {
name := v.Name()
if !showHidden && strings.HasPrefix(name, ".") {
continue
}
f := new(file)
f.ext = filepath.Ext(name)
f.name = name[0 : len(name)-len(f.ext)]
f.indicator = getIndicator(v.Mode())
f.size = v.Size()
f.modTime = v.ModTime()
if long {
f.mode = v.Mode().String()
f.modeBits = uint32(v.Mode())
f.owner, f.group = getOwnerGroupInfo(v)
}
if flagVector&flag_s > 0 {
if s, ok := v.Sys().(*syscall.Stat_t); ok {
f.blocks = s.Blocks
}
}
if flagVector&flag_i == 0 {
f.icon, f.iconColor = getIcon(f.name, f.ext, f.indicator)
if flagVector&flag_c != 0 {
f.iconColor = ""
}
}
if gitRepoStatus != nil {
if v.IsDir() {
f.gitStatus = gitRepoStatus[v.Name()+"/"]
} else {
f.gitStatus = gitRepoStatus[v.Name()]
}
}
t.files = append(t.files, f)
if v.IsDir() {
t.dirs = append(t.dirs, name+"/")
}
}
// if -a flag is passed then only eval parent dir and append to files
if flagVector&flag_a > 0 {
t.files = append(t.files, t.info)
p, err := filepath.Abs(d.Name())
if err != nil {
// partial *dir (without parent dir) and error
return t, err
}
pp := filepath.Dir(p)
pds, err := os.Lstat(pp)
if err != nil {
// partial *dir (without parent dir) and error
return t, err
}
t.parent = new(file)
t.parent.name = ".."
t.parent.size = pds.Size()
t.parent.modTime = pds.ModTime()
if long {
t.parent.mode = pds.Mode().String()
t.parent.modeBits = uint32(pds.Mode())
t.parent.owner, t.parent.group = getOwnerGroupInfo(pds)
}
if flagVector&flag_s > 0 {
if s, ok := pds.Sys().(*syscall.Stat_t); ok {
t.parent.blocks = s.Blocks
}
}
if flagVector&flag_i == 0 {
t.parent.icon = iDef["diropen"].getGlyph()
if flagVector&flag_c == 0 {
t.parent.iconColor = iDef["diropen"].getColor(1)
}
}
t.files = append(t.files, t.parent)
}
// return *dir with no error
// or partial *dir with error (produced by Readdir)
return t, err
}
func newDir_ArgFiles(files []os.FileInfo) *dir {
var long bool = flagVector&(flag_l|flag_o|flag_g) > 0
t := new(dir)
for _, v := range files {
name := v.Name()
f := new(file)
f.ext = filepath.Ext(name)
f.name = name[0 : len(name)-len(f.ext)]
f.indicator = getIndicator(v.Mode())
f.size = v.Size()
f.modTime = v.ModTime()
if long {
f.mode = v.Mode().String()
f.modeBits = uint32(v.Mode())
f.owner, f.group = getOwnerGroupInfo(v)
}
if flagVector&flag_s > 0 {
if s, ok := v.Sys().(*syscall.Stat_t); ok {
f.blocks = s.Blocks
}
}
if flagVector&flag_i == 0 {
f.icon, f.iconColor = getIcon(f.name, f.ext, f.indicator)
if flagVector&flag_c != 0 {
f.iconColor = ""
}
}
t.files = append(t.files, f)
}
return t
}
func newDirs_Recussion(d *os.File) {
dd, err := newDir(d)
d.Close()
if err != nil {
log.Printf("partial access to %q: %v\n", d.Name(), err)
_ = set_osExitCode(code_Minor)
}
// print the info of the files of the directory
io.Copy(os.Stdout, dd.print())
if len(dd.dirs) == 0 {
return
}
// at this point dd.print has sorted the children files
// but not using it instead printing children in directory order
temp := make([]string, len(dd.dirs))
for i, v := range dd.dirs {
temp[i] = filepath.Join(d.Name(), v)
}
for _, v := range temp {
fmt.Printf("\n%s:\n", openDir+v)
f, err := os.Open(v)
if err != nil {
log.Printf("cannot access %q: %v\n", v, err)
f.Close()
_ = set_osExitCode(code_Minor)
continue
}
newDirs_Recussion(f)
}
}
func (d *dir) print() *bytes.Buffer {
// take care of printing, extending symbolic links in long forms
//sorting
lessFuncGenerator(d)
if flagVector&flag_U == 0 && flagVector&flag_r > 0 {
sort.Sort(sort.Reverse(d))
} else {
sort.Sort(d)
}
buf := bytes.NewBuffer([]byte(""))
switch {
case flagVector&(flag_l|flag_o|flag_g) > 0:
w := ctw.NewLong(9)
for _, v := range d.files {
if flagVector&flag_s > 0 {
w.AddRow(getSizeInFormate(v.blocks*512), v.mode, v.owner, v.group, getSizeInFormate(v.size), v.modTime.Format(timeFormate), v.icon, v.name+v.ext+v.indicator, v.gitStatus)
} else {
w.AddRow("", v.mode, v.owner, v.group, getSizeInFormate(v.size), v.modTime.Format(timeFormate), v.icon, v.name+v.ext+v.indicator, v.gitStatus)
}
w.IconColor(v.iconColor)
}
w.Flush(buf)
case flagVector&flag_1 > 0:
w := ctw.NewLong(4)
for _, v := range d.files {
if flagVector&flag_s > 0 {
w.AddRow(getSizeInFormate(v.blocks*512), v.icon, v.name+v.ext+v.indicator, v.gitStatus)
} else {
w.AddRow("", v.icon, v.name+v.ext+v.indicator, v.gitStatus)
}
w.IconColor(v.iconColor)
}
w.Flush(buf)
default:
w := ctw.New(terminalWidth)
for _, v := range d.files {
if flagVector&flag_s > 0 {
w.AddRow(getSizeInFormate(v.blocks*512), v.icon, v.name+v.ext+v.indicator, v.gitStatus)
} else {
w.AddRow("", v.icon, v.name+v.ext+v.indicator, v.gitStatus)
}
w.IconColor(v.iconColor)
}
w.Flush(buf)
}
return buf
}
// sorting functions
func (d *dir) Len() int {
return len(d.files)
}
func (d *dir) Swap(i, j int) {
d.files[i], d.files[j] = d.files[j], d.files[i]
}
func (d *dir) Less(i, j int) bool {
return d.less(i, j)
}