mirror of
https://github.com/Yash-Handa/logo-ls.git
synced 2026-08-27 10:12:45 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78dd15eb08 | ||
|
|
a1aa1bd4b2 | ||
|
|
c529ae0abb | ||
|
|
825b66af81 | ||
|
|
2b6accffa4 | ||
|
|
34036520ba |
@@ -5,3 +5,5 @@ echo -e "\`\`\`txt" > HELP.md
|
||||
go run . -? >> HELP.md
|
||||
echo -e "\`\`\`" >> HELP.md
|
||||
exec git add HELP.md
|
||||
|
||||
exit 0
|
||||
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
R_NAME=$1
|
||||
R_URL=$2
|
||||
|
||||
error_exit()
|
||||
{
|
||||
if [ "$?" != "0" ]; then
|
||||
nl=$'\n'
|
||||
echo "$1""$nl""$2""$nl""$nl""Aborting push to ""$R_NAME"" (""$R_URL"")" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Build binary for variety of arch
|
||||
# linux amd64
|
||||
ERROR=$(go env -w GOOS=linux GOARCH=amd64 2>&1 > /dev/null)
|
||||
error_exit "Cannot set GOOS=linux and GOARCH=amd64:" "$ERROR"
|
||||
ERROR=$(go build -o build/logo-ls-linux-amd64 2>&1 > /dev/null)
|
||||
error_exit "Cannot build logo-ls-linux-amd64:" "$ERROR"
|
||||
|
||||
# linux arm64
|
||||
ERROR=$(go env -w GOOS=linux GOARCH=arm64 2>&1 > /dev/null)
|
||||
error_exit "Cannot set GOOS=linux and GOARCH=arm64:" "$ERROR"
|
||||
ERROR=$(go build -o build/logo-ls-linux-arm64 2>&1 > /dev/null)
|
||||
error_exit "Cannot build logo-ls-linux-arm64:" "$ERROR"
|
||||
|
||||
# linux 386
|
||||
ERROR=$(go env -w GOOS=linux GOARCH=386 2>&1 > /dev/null)
|
||||
error_exit "Cannot set GOOS=linux and GOARCH=386:" "$ERROR"
|
||||
ERROR=$(go build -o build/logo-ls-linux-386 2>&1 > /dev/null)
|
||||
error_exit "Cannot build logo-ls-linux-386:" "$ERROR"
|
||||
|
||||
# darwin amd64
|
||||
ERROR=$(go env -w GOOS=darwin GOARCH=amd64 2>&1 > /dev/null)
|
||||
error_exit "Cannot set GOOS=darwin and GOARCH=amd64:" "$ERROR"
|
||||
ERROR=$(go build -o build/logo-ls-darwin-amd64 2>&1 > /dev/null)
|
||||
error_exit "Cannot build logo-ls-darwin-amd64:" "$ERROR"
|
||||
|
||||
# reset env to default
|
||||
go env -u GOOS GOARCH
|
||||
|
||||
exit 0
|
||||
@@ -16,3 +16,4 @@
|
||||
|
||||
# Do not commit the executable
|
||||
logo-ls
|
||||
build/
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
// custom/ in-house implementation of go's tab-writer
|
||||
// because ls shows table in column major fation and according to screen size
|
||||
package ctw
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
func widthsSum(w []int, p int) int {
|
||||
s := 0
|
||||
for _, v := range w {
|
||||
s += v + p
|
||||
}
|
||||
s -= p
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func findMax(arr []int) int {
|
||||
max := 0
|
||||
for _, v := range arr {
|
||||
if v >= max {
|
||||
max = v
|
||||
}
|
||||
}
|
||||
|
||||
return max
|
||||
}
|
||||
|
||||
//Ctw has complexity of no. of columns x no. of files
|
||||
// termW is width of the terminal
|
||||
func Ctw(buf *bytes.Buffer, data []string, termW int) {
|
||||
dn := len(data) // length of data slice
|
||||
if dn == 0 {
|
||||
return
|
||||
}
|
||||
const pad = 2 // padding b/w columns
|
||||
var lens []int // slice of lengths of each element in the data slice
|
||||
widths := make([]int, 0) // slice of widths of each column (don't use because it over run once)
|
||||
var prevWidths []int // accurate widths value
|
||||
for _, v := range data {
|
||||
lens = append(lens, len(v))
|
||||
}
|
||||
|
||||
prevj := 0 // prevj is previous jump value (row value if you may)
|
||||
|
||||
// for loop runs for all possible nos. of column (1,2,3,4....)
|
||||
for {
|
||||
cols := len(widths) + 1
|
||||
widths = append(widths, 0)
|
||||
j := int(math.Ceil(float64(dn) / float64(cols))) // jump value corresponding to cols
|
||||
if prevj == j { // removes redundant calculations
|
||||
continue
|
||||
}
|
||||
b := 0 // begining of column
|
||||
e := j // end of column
|
||||
// find optimal widths (width of each column and total no of columns)
|
||||
for i := 0; i < cols && e <= dn; i++ {
|
||||
widths[i] = findMax(lens[b:e])
|
||||
b, e = e, e+j
|
||||
}
|
||||
|
||||
// for last column if last column is not complete
|
||||
if e-j < dn {
|
||||
widths[cols-1] = findMax(lens[e-j : dn])
|
||||
}
|
||||
|
||||
prevj = j
|
||||
|
||||
totW := widthsSum(widths, pad) //total width of the ls block
|
||||
if totW > termW {
|
||||
break
|
||||
} else if totW > termW/2 { // if total width of the ls block is more than half of terminal
|
||||
// copy widths to prevWidths
|
||||
prevWidths = make([]int, len(widths))
|
||||
for i := range widths {
|
||||
prevWidths[i] = widths[i]
|
||||
}
|
||||
}
|
||||
if cols == dn { // if content comes in one line of terminal
|
||||
// copy widths to prevWidths
|
||||
prevWidths = make([]int, len(widths))
|
||||
for i := range widths {
|
||||
prevWidths[i] = widths[i]
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// total no of rows
|
||||
rows := int(math.Ceil(float64(dn) / float64(len(prevWidths))))
|
||||
|
||||
// loop to write entire ls block to buffer
|
||||
for i := 0; i < rows; i++ {
|
||||
for j := 0; j < len(prevWidths); j++ {
|
||||
if i+j*rows >= dn { // checks for last column if incomplete
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(buf, "%-*s", prevWidths[j]+pad, data[i+j*rows])
|
||||
}
|
||||
fmt.Fprintf(buf, "\n")
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"syscall"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/Yash-Handa/logo-ls/ctw"
|
||||
)
|
||||
|
||||
type file struct {
|
||||
@@ -24,10 +26,11 @@ type file struct {
|
||||
}
|
||||
|
||||
type dir struct {
|
||||
info *file
|
||||
files []*file // all child files and dirs
|
||||
dirs []*file // for recursion contain only child dirs
|
||||
less func(int, int) bool
|
||||
info *file
|
||||
parent *file
|
||||
files []*file // all child files and dirs
|
||||
dirs []*file // for recursion contain only child dirs
|
||||
less func(int, int) bool
|
||||
}
|
||||
|
||||
// define methods on *dir type only not on file type
|
||||
@@ -40,8 +43,7 @@ func newDir(d *os.File) (*dir, error) {
|
||||
|
||||
// filing current dir info
|
||||
t.info = new(file)
|
||||
t.info.name = d.Name()
|
||||
t.info.ext = ""
|
||||
t.info.name = "."
|
||||
if curDir {
|
||||
ds, err := d.Stat()
|
||||
if err != nil {
|
||||
@@ -96,8 +98,35 @@ func newDir(d *os.File) (*dir, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
t.files = append(t.files, t.parent)
|
||||
}
|
||||
|
||||
// return *dir with no error
|
||||
@@ -105,6 +134,34 @@ func newDir(d *os.File) (*dir, error) {
|
||||
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
|
||||
}
|
||||
}
|
||||
t.files = append(t.files, f)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (d *dir) print() *bytes.Buffer {
|
||||
// take care of printing, extending symbolic links in long forms
|
||||
|
||||
@@ -128,6 +185,7 @@ func (d *dir) print() *bytes.Buffer {
|
||||
}
|
||||
fmt.Fprintf(w, fmtStr, v.mode, v.owner, v.group, getSizeInFormate(v.size), v.modTime.Format(time.Stamp), v.name+v.ext+v.indicator)
|
||||
}
|
||||
w.Flush()
|
||||
case flagVector&flag_1 > 0:
|
||||
w = tabwriter.NewWriter(buf, 0, 0, 1, ' ', tabwriter.DiscardEmptyColumns)
|
||||
for _, v := range d.files {
|
||||
@@ -136,18 +194,19 @@ func (d *dir) print() *bytes.Buffer {
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t\n", v.name+v.ext+v.indicator)
|
||||
}
|
||||
w.Flush()
|
||||
default:
|
||||
w = tabwriter.NewWriter(buf, 0, 0, 2, ' ', tabwriter.DiscardEmptyColumns)
|
||||
for _, v := range d.files {
|
||||
// w = tabwriter.NewWriter(buf, 0, 0, 2, ' ', tabwriter.DiscardEmptyColumns)
|
||||
temp := make([]string, len(d.files))
|
||||
for i, v := range d.files {
|
||||
s := ""
|
||||
if flagVector&flag_s > 0 {
|
||||
s = getSizeInFormate(v.blocks*512) + " "
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t", s+v.name+v.ext+v.indicator)
|
||||
temp[i] = s + v.name + v.ext + v.indicator
|
||||
}
|
||||
fmt.Fprintln(w)
|
||||
ctw.Ctw(buf, temp, terminalWidth)
|
||||
}
|
||||
w.Flush()
|
||||
return buf
|
||||
}
|
||||
|
||||
|
||||
+12
-2
@@ -10,6 +10,16 @@ import (
|
||||
)
|
||||
|
||||
func mainSort(a, b string) bool {
|
||||
switch a {
|
||||
case ".", "..":
|
||||
default:
|
||||
a = strings.TrimPrefix(a, ".")
|
||||
}
|
||||
switch b {
|
||||
case ".", "..":
|
||||
default:
|
||||
b = strings.TrimPrefix(b, ".")
|
||||
}
|
||||
return strings.ToLower(a) < strings.ToLower(b)
|
||||
}
|
||||
|
||||
@@ -107,14 +117,14 @@ func getIndicator(modebit os.FileMode) (i string) {
|
||||
switch {
|
||||
case modebit&os.ModeDir > 0:
|
||||
i = "/"
|
||||
case modebit&1000000 > 0:
|
||||
i = "*"
|
||||
case modebit&os.ModeNamedPipe > 0:
|
||||
i = "|"
|
||||
case modebit&os.ModeSymlink > 0:
|
||||
i = "@"
|
||||
case modebit&os.ModeSocket > 0:
|
||||
i = "="
|
||||
case modebit&1000000 > 0:
|
||||
i = "*"
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
@@ -2,4 +2,7 @@ module github.com/Yash-Handa/logo-ls
|
||||
|
||||
go 1.15
|
||||
|
||||
require github.com/pborman/getopt/v2 v2.0.0
|
||||
require (
|
||||
github.com/pborman/getopt/v2 v2.0.0
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||
)
|
||||
|
||||
@@ -1,2 +1,10 @@
|
||||
github.com/pborman/getopt/v2 v2.0.0 h1:Tn8XVmhb93Wbc346Tk4P6KutfpMVp+iztUzkZrTSyB4=
|
||||
github.com/pborman/getopt/v2 v2.0.0/go.mod h1:4NtW75ny4eBw9fO1bhtNdYTlZKYX5/tBLtsOpwKIKd0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
||||
@@ -5,8 +5,10 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/pborman/getopt/v2"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
// flags with corresponding bit values
|
||||
@@ -35,6 +37,9 @@ const (
|
||||
// flagVector has all the options set in it. Each bit represent an option.
|
||||
var flagVector uint
|
||||
|
||||
// terminal width for formatting
|
||||
var terminalWidth int
|
||||
|
||||
func main() {
|
||||
// content flags
|
||||
f_a := getopt.BoolLong("all", 'a', "do not ignore entries starting with .")
|
||||
@@ -144,32 +149,78 @@ func main() {
|
||||
flagVector |= flag_g
|
||||
case *f_l:
|
||||
flagVector |= flag_l
|
||||
case *f_1:
|
||||
default:
|
||||
// screen width for custom tw
|
||||
var e error = nil
|
||||
terminalWidth, _, e = terminal.GetSize(int(os.Stdout.Fd()))
|
||||
if e != nil {
|
||||
terminalWidth = 80
|
||||
}
|
||||
}
|
||||
|
||||
// extract files/dir from arguments
|
||||
dirs := getopt.Args()
|
||||
if len(dirs) == 0 {
|
||||
// use pwd
|
||||
pwd, err := os.Open(".")
|
||||
dirs = append(dirs, ".")
|
||||
}
|
||||
|
||||
sort.Strings(dirs)
|
||||
|
||||
args := struct {
|
||||
files []os.FileInfo
|
||||
dirs []*os.File
|
||||
}{}
|
||||
|
||||
// segregate args in files and dirs, and print error for those which cannot be opened
|
||||
for _, v := range dirs {
|
||||
d, err := os.Open(v)
|
||||
if err != nil {
|
||||
log.Printf("cannot access \".\": %v\n", err)
|
||||
pwd.Close()
|
||||
os.Exit(2)
|
||||
}
|
||||
d, err := newDir(pwd)
|
||||
pwd.Close()
|
||||
if err != nil {
|
||||
log.Printf("partial access to \".\": %v\n", err)
|
||||
log.Printf("cannot access %q: %v\n", v, err)
|
||||
d.Close()
|
||||
defer os.Exit(2)
|
||||
continue
|
||||
}
|
||||
// print the info of the files of pwd
|
||||
io.Copy(os.Stdout, d.print())
|
||||
} else {
|
||||
for _, v := range dirs {
|
||||
// use this list containing both dirs and files
|
||||
_ = v
|
||||
ds, err := d.Stat()
|
||||
if err != nil {
|
||||
log.Printf("cannot access %q: %v\n", v, err)
|
||||
d.Close()
|
||||
defer os.Exit(2)
|
||||
continue
|
||||
}
|
||||
if ds.IsDir() {
|
||||
args.dirs = append(args.dirs, d)
|
||||
} else {
|
||||
args.files = append(args.files, ds)
|
||||
}
|
||||
}
|
||||
|
||||
// process and display all files
|
||||
io.Copy(os.Stdout, newDir_ArgFiles(args.files).print())
|
||||
if len(args.files) > 0 && len(args.dirs) > 0 {
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// process and display all the dirs in arg
|
||||
pName := len(dirs) > 1
|
||||
for i, v := range args.dirs {
|
||||
if pName {
|
||||
fmt.Printf("%s:\n", v.Name())
|
||||
}
|
||||
d, err := newDir(v)
|
||||
v.Close()
|
||||
if err != nil {
|
||||
log.Printf("partial access to %q: %v\n", v.Name(), err)
|
||||
defer os.Exit(2)
|
||||
}
|
||||
// print the info of the files of the directory
|
||||
io.Copy(os.Stdout, d.print())
|
||||
if i < len(args.dirs)-1 {
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -177,5 +228,3 @@ func init() {
|
||||
log.SetPrefix("logo-ls: ")
|
||||
log.SetFlags(0)
|
||||
}
|
||||
|
||||
// todo: i. multiple dir/ file handle
|
||||
|
||||
Reference in New Issue
Block a user