From a2ed5b669dad95414af0f0ef7b930e55e02b76d5 Mon Sep 17 00:00:00 2001 From: Yuvia Date: Tue, 12 Aug 2025 20:45:14 +0000 Subject: [PATCH] add --- go.mod | 3 + gols.go | 378 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 381 insertions(+) create mode 100644 go.mod create mode 100644 gols.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7a2dec6 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module yuvia/gols + +go 1.23.9 diff --git a/gols.go b/gols.go new file mode 100644 index 0000000..4e1baff --- /dev/null +++ b/gols.go @@ -0,0 +1,378 @@ +package main + +import ( + "fmt" + "io/fs" + "log" + "os" + "slices" + "strings" + "syscall" + "time" +) + +/* + gols + + Throughout the program, the simple approach (the braindead one), was the most taken one. +*/ + +var modes = map[uint32]string{ + 0: "---", + 1: "--x", + 2: "-w-", + 3: "-wx", + 4: "r--", + 5: "r-x", + 6: "rw-", + 7: "rwx", +} + +var vwidth int = 1 +var uwidth int = 1 +var gwidth int = 1 +var lwidth int = 1 +var swidth int = 1 +var mwidth int = 1 +var qwidth int = 1 +var now int64 +var dflag bool = false +var lflag bool = false +var mflag bool = false +var nflag bool = false +var pflag bool = false +var qflag bool = false +var Qflag bool = false +var rflag bool = false +var sflag bool = false +var tflag bool = false +var Tflag bool = false +var uflag bool = false +var Fflag bool = false + +// there is an objectively better way to do this. +// i wont do it though, not feeling like it XXX +func parseargs(args []string) int { + var i int + var j int + + if len(args) == 0 { + return 0 + } else if args[0][0] != '-' { + return 0 + } + + for i = 0; i < len(args); i++ { + if args[i][0] != '-' { + break + } + if args[i] == "-" || args[i] == "--" { // the people are dumb + break + } + for j = 1; j < len(args[i]); j++ { + switch args[i][j] { + case 'F': + Fflag = true + case 'd': + dflag = true + case 'l': + lflag = true + case 'm': + mflag = true + case 'n': + nflag = true + case 'p': + pflag = true + case 'q': + qflag = true + case 'Q': + Qflag = true + case 'r': + rflag = true + case 's': + sflag = true + case 't': + tflag = true + case 'T': + Tflag = true + case 'u': + uflag = true + default: + fmt.Println(args[i], args[i][j]) + fmt.Fprintln(os.Stderr, "usage: gls [-dlmnpqrstuFQT] [file ...]") + os.Exit(1) + } + } + } + + return i +} + +func main() { + args := os.Args[1:] + flag := false + optind := parseargs(args) + if pflag { + flag = true + } + if lflag { + now = time.Now().Unix() + } + if len(args[optind:]) == 0 { + widths([]string{"."}) + err := ls(".", true) + if err != nil { + log.Fatal(err) + } + } + widths(args[optind:]) + for _, arg := range args[optind:] { + err := ls(arg, flag) + if err != nil { + log.Fatal(err) + } + } +} + +func widths(args []string) { + for _, a := range args { + st, err := os.Lstat(a) + if err != nil { + log.Fatal(err) + } + if st.IsDir() && !dflag { + d, err := os.ReadDir(a) + if err != nil { + log.Fatal(err) + } + for _, ent := range d { + dowidths(ent) + } + } else { + dowidths(fs.FileInfoToDirEntry(st)) + } + } +} + +func compar(a os.DirEntry, b os.DirEntry) int { + if tflag { + if uflag { + ai, _ := a.Info() + as := ai.Sys().(*syscall.Dir) + bi, _ := b.Info() + bs := bi.Sys().(*syscall.Dir) + if as.Atime-bs.Atime > 0 { + return -1 + } else { + return 1 + } + } else { + ai, _ := a.Info() + bi, _ := b.Info() + if ai.ModTime().Unix()-bi.ModTime().Unix() > 0 { + return -1 + } else { + return 1 + } + } + } else { + return strings.Compare(a.Name(), b.Name()) + } +} + +func ls(p string, flag bool) error { + st, err := os.Lstat(p) + if err != nil { + return err + } + if st.IsDir() && !dflag { + d, err := os.ReadDir(p) + if err != nil { + return err + } + if !nflag { + slices.SortFunc(d, compar) + if rflag { + slices.Reverse(d) + } + } + for _, ent := range d { + i, err := ent.Info() + if err != nil { + log.Fatalf("the fuck?: %s\n", err) + } + output(i, p, flag) + } + } else { + output(st, p, flag) + } + return nil +} + +/* +as seen from /sys/src/cmd/ls.c, in xcleanname: +Compress slashes, remove trailing slash. Don't worry about . and .. +*/ +func cleanname(s string) string { + if len(s) == 1 { + return s + } + b := new(strings.Builder) + prev := ' ' // ;) + for _, c := range s { + if c == '/' && prev == '/' { + prev = c + continue + } + prev = c + b.WriteRune(c) + } + ret := b.String() + if ret[len(ret)-1] == '/' { + return ret[:len(ret)-2] + } else { + return ret + } +} + +func getpropername(p, f string, flag bool) string { + var c string + + quot := false + quoters := "# '" + if !flag { + if p == "/" { + p = "" + } + c = p + "/" + f + } else { + c = f + } + quot = strings.ContainsAny(c, quoters) + if quot { + b := new(strings.Builder) + b.WriteByte('\'') + for _, e := range c { + b.WriteRune(e) + if e == '\'' { + b.WriteRune(e) + } + } + b.WriteByte('\'') + return b.String() + } else { + return c + } +} + +func fileflag(s *syscall.Dir) string { + if !Fflag { + return "" + } + if s.Qid.Type&syscall.QTDIR == syscall.QTDIR { + return "/" + } + if s.Mode&0111 > 0 { + return "*" + } + return "" +} + +func output(i os.FileInfo, p string, flag bool) { + p = cleanname(p) + s := (i.Sys().(*syscall.Dir)) + if sflag { + fmt.Printf("%*d ", swidth, (i.Size()+1023)/1024) + } + if mflag { + m := getpropername("", s.Muid, true) + fmt.Printf("[%*s] ", mwidth, m) + } + if qflag { + fmt.Printf("(%.16x %*d %.2x) ", s.Qid.Path, qwidth, s.Qid.Vers, s.Qid.Type) + } + if Tflag { + if s.Mode&syscall.DMTMP == syscall.DMTMP { + fmt.Print("t ") + } else { + fmt.Print("- ") + } + } + if lflag { + t := time.Unix(int64(s.Mtime), 0) + fmt.Printf("%s %c %*d %-*s %-*s %*d ", modestr(s.Mode), s.Type, vwidth, s.Dev, uwidth, s.Uid, gwidth, s.Gid, lwidth, i.Size()) + if uflag { + t = time.Unix(int64(s.Atime), 0) + } + if t.Unix() < now-180*24*60*60 || t.Unix() > now+24*60*60 { + fmt.Printf("%s %5s ", t.Format("Jan _2"), t.Format("2006")) + } else { + fmt.Printf("%s ", t.Format("Jan _2 15:04")) + } + } + name := getpropername(p, i.Name(), flag) + fmt.Printf("%s%s\n", name, fileflag(s)) +} + +func modestr(m uint32) string { + b := new(strings.Builder) + if m&syscall.DMDIR == syscall.DMDIR { + b.WriteByte('d') + } else if m&syscall.DMAPPEND == syscall.DMAPPEND { + b.WriteByte('a') + } else if m&syscall.DMAUTH == syscall.DMAUTH { + b.WriteByte('A') + } else { + b.WriteByte('-') + } + if m&syscall.DMEXCL == syscall.DMEXCL { + b.WriteByte('l') + } else { + b.WriteByte('-') + } + b.WriteString(modes[m>>6&7]) + b.WriteString(modes[m>>3&7]) + b.WriteString(modes[m&7]) + return b.String() +} + +func dowidths(d os.DirEntry) { + i, _ := d.Info() + y := (i.Sys().(*syscall.Dir)) + if sflag { + s := fmt.Sprint((i.Size() + 1023) / 1024) + if len(s) > swidth { + swidth = len(s) + } + } + if qflag { + s := fmt.Sprint(y.Qid.Vers) + if len(s) > qwidth { + qwidth = len(s) + } + } + if mflag { + s := fmt.Sprint(y.Muid) + if len(s) > mwidth { + mwidth = len(s) + } + } + if lflag { + s := fmt.Sprint(y.Dev) + if len(s) > vwidth { + vwidth = len(s) + } + s = fmt.Sprint(y.Uid) + if len(s) > uwidth { + uwidth = len(s) + } + s = fmt.Sprint(y.Gid) + if len(s) > gwidth { + gwidth = len(s) + } + s = fmt.Sprint(i.Size()) + if len(s) > lwidth { + lwidth = len(s) + } + } +}