[mike@maginot]

This commit is contained in:
2026-08-10 21:51:53 +02:00
parent d26a008763
commit 2bd9a4a169
4 changed files with 292 additions and 296 deletions
+36 -35
View File
@@ -1,8 +1,8 @@
// dx — count files and directories, mwx'2016
//
// Go-Portierung des Perl-Originals (dx 1.0.2), Ausgabe eingefärbt nach der
// mwxcol-Palette (~/src/mwxcol). Ohne Terminal (Pipe, NO_COLOR, --color=never)
// bleibt die Ausgabe unverändert einfarbig.
// Go port of the Perl original (dx 1.0.2), output coloured after the mwxcol
// palette (~/src/mwxcol). Without a terminal (pipe, NO_COLOR, --color=never)
// the output stays plain, exactly as it was.
package main
import (
@@ -22,9 +22,9 @@ import (
"github.com/fatih/color"
)
// version ist eine var (keine const), damit build.sh die gebaute Nummer per
// -ldflags "-X main.version=…" einsetzen kann. Der Wert hier erscheint nur bei
// einem nackten `go build`; die wirklich gebaute Version steht in version.txt.
// version is a var, not a const, so build.sh can put the built number in via
// -ldflags "-X main.version=…". The value here only ever shows up in a bare
// `go build`; the version actually built is the one in version.txt.
var version = "2.1.0"
// ------------------------------------------------------------------ Palette
@@ -48,7 +48,7 @@ var (
func plain(c rgb) *color.Color { return color.RGB(c.r, c.g, c.b) }
func bold(c rgb) *color.Color { return color.New(color.Bold).AddRGB(c.r, c.g, c.b) }
// Rollen wie im eza-Theme, damit dx und eza dieselbe Datei gleich einfärben.
// The roles of the eza theme, so that dx and eza colour the same file alike.
var (
cWhite = plain(white)
cYellow = plain(yellow)
@@ -61,8 +61,8 @@ var (
cGrey = plain(grey)
cDark = plain(dark)
cDarker = plain(darker)
cDirB = bold(blue) // Verzeichnis
cExecB = bold(green) // ausführbar
cDirB = bold(blue) // directory
cExecB = bold(green) // executable
cWhiteB = bold(white)
cYellowB = bold(yellow)
cOrangeB = bold(orange)
@@ -71,14 +71,14 @@ var (
cDarkB = bold(dark)
)
// ------------------------------------------------------------------- Zählen
// ----------------------------------------------------------------- Counting
type entry struct {
name string // Anzeigename, Verzeichnisse mit "/"
name string // display name, directories with a "/"
col *color.Color
n int64
size int64
agg bool // Sammelzeile aus -t, kein echter Eintrag
agg bool // the summary line from -t, not a real entry
}
type result struct {
@@ -103,7 +103,7 @@ func scan(dir string, onlyDirs bool) (result, error) {
name := de.Name()
path := filepath.Join(dir, name)
// Perls -d/-f folgen Symlinks; ein toter Link fällt durch beide Tests.
// Perl's -d/-f follow symlinks; a dead link fails both tests.
st, err := os.Stat(path)
if err != nil {
atomic.AddInt64(&res.unreadbl, 1)
@@ -117,7 +117,7 @@ func scan(dir string, onlyDirs bool) (result, error) {
sem <- struct{}{}
defer func() { <-sem }()
// WalkDir folgt Symlinks nicht — wie `find <pfad> -type f`.
// WalkDir does not follow symlinks — like `find <path> -type f`.
n, size := walk(path, &res.unreadbl)
col := cDirB
@@ -154,7 +154,7 @@ func walk(root string, unreadable *int64) (n, size int64) {
atomic.AddInt64(unreadable, 1)
return nil
}
if !d.Type().IsRegular() { // wie find -type f
if !d.Type().IsRegular() { // like find -type f
return nil
}
info, err := d.Info()
@@ -169,10 +169,10 @@ func walk(root string, unreadable *int64) (n, size int64) {
return
}
// ------------------------------------------------------------------ Ausgabe
// ------------------------------------------------------------------- Output
// Schwellen wie im Perl-Original; unterhalb von 10 der jeweiligen Einheit
// jedoch mit einer Nachkommastelle — "1.9G" statt "2G".
// The thresholds of the Perl original; below 10 of the unit in question,
// though, with one decimal — "1.9G" instead of "2G".
func formSize(b int64) string {
const k = 1024
f := float64(b)
@@ -186,7 +186,7 @@ func formSize(b int64) string {
case b > k:
return scaled(f/k, "K")
default:
return fmt.Sprintf("%.0f", f) // ganze Bytes, nie gebrochen
return fmt.Sprintf("%.0f", f) // whole bytes, never fractions
}
}
@@ -197,7 +197,8 @@ func scaled(v float64, unit string) string {
return fmt.Sprintf("%.0f%s", v, unit)
}
// Größenrampe wie im eza-Theme: byte/kilo grün, mega gelb, giga orange, huge rot.
// The size ramp of the eza theme: byte/kilo green, mega yellow, giga orange,
// huge red.
func sizeColor(b int64, strong bool) *color.Color {
switch {
case b == 0:
@@ -242,7 +243,7 @@ func pad(s string, w int) string {
return ""
}
// Anteilsbalken in Achtelschritten, feste Breite barCells.
// Share bar in eighths of a cell, barCells wide throughout.
const barCells = 10
var eighths = []rune{' ', '▏', '▎', '▍', '▌', '▋', '▊', '▉'}
@@ -251,7 +252,7 @@ func bar(frac float64) string {
if frac > 1 {
frac = 1
}
if !(frac > 0) { // fängt auch NaN
if !(frac > 0) { // catches NaN too
return strings.Repeat(" ", barCells)
}
units := int(frac*barCells*8 + 0.5)
@@ -288,10 +289,10 @@ func printDir(w *bufio.Writer, dir string, res result, o opts) {
if ka, kb := metric(a), metric(b); ka != kb {
return ka < kb
}
return a.name < b.name // Perl ließ Gleichstände der Hash-Ordnung
return a.name < b.name // Perl left ties to the order of the hash
})
// -t N: nur die N größten zeigen, der Rest wird zu einer Zeile.
// -t N: show only the N largest, the rest becomes a single line.
rows := res.entries
var rest *entry
if o.top > 0 && len(rows) > o.top {
@@ -310,7 +311,7 @@ func printDir(w *bufio.Writer, dir string, res result, o opts) {
rows[i], rows[j] = rows[j], rows[i]
}
}
if rest != nil { // Restzeile bleibt am kleinen Ende
if rest != nil { // the summary line stays at the small end
if o.reverse {
rows = append(rows, *rest)
} else {
@@ -347,12 +348,12 @@ func printDir(w *bufio.Writer, dir string, res result, o opts) {
if total > 0 {
frac = float64(metric(e)) / float64(total)
}
// Balken trägt die Farbe der Spalte, deren Anteil er zeigt.
// The bar takes the colour of the column whose share it shows.
col := sizeColor(e.size, false)
if o.byCount {
col = countColor(e.n)
}
if e.agg { // Sammelzeile bleibt zurückhaltend
if e.agg { // the summary line keeps quiet
col = cDark
}
fmt.Fprintf(w, " %s %s",
@@ -368,7 +369,7 @@ type opts struct {
byCount bool // -n
onlyDirs bool // -d
reverse bool // -r
top int // -t N, 0 = alle
top int // -t N, 0 = all
bar bool
}
@@ -398,7 +399,7 @@ func die(format string, a ...any) {
func main() {
var o opts
var barSet bool // sonst folgt der Balken der Farbentscheidung
var barSet bool // otherwise the bar follows the colour decision
var dir string
when := "auto"
@@ -429,7 +430,7 @@ func main() {
os.Exit(1)
}
return
case a == updateRefreshFlag: // der Hintergrundlauf, nicht in der Hilfe
case a == updateRefreshFlag: // the background run, not in the help
selfUpdate.refresh()
return
case a == "--number":
@@ -450,7 +451,7 @@ func main() {
when = strings.TrimPrefix(a, "--color=")
case len(a) > 1 && a[0] == '-':
flags := a[1:]
for j := 0; j < len(flags); j++ { // Bündelung wie Getopt::Std: -dn
for j := 0; j < len(flags); j++ { // bundling as in Getopt::Std: -dn
switch flags[j] {
case 'n':
o.byCount = true
@@ -461,7 +462,7 @@ func main() {
case 'h':
usage(os.Stdout)
return
case 't': // Wert klebt am Flag (-t5) oder folgt (-t 5)
case 't': // the value sticks to the flag (-t5) or follows (-t 5)
val := flags[j+1:]
if val == "" {
if i+1 >= len(args) {
@@ -488,7 +489,7 @@ func main() {
color.NoColor = false
case "never":
color.NoColor = true
case "auto": // fatih/color entscheidet per isatty und NO_COLOR
case "auto": // fatih/color decides, by isatty and NO_COLOR
default:
die("--color: expected auto|always|never, got %q", when)
}
@@ -513,8 +514,8 @@ func main() {
if res.unreadbl > 0 {
fmt.Fprintln(os.Stderr, cDark.Sprintf("dx: %d unreadable entries skipped", res.unreadbl))
}
// Kostet nichts: der Hinweis stammt aus dem Merkzettel, gefragt wird
// höchstens einmal am Tag und dann im Hintergrund.
// Costs nothing: the hint comes from the note in the cache, and the asking
// happens once a day at most, in the background.
if hint := selfUpdate.daily(); hint != "" {
fmt.Fprintln(os.Stderr, cDark.Sprint(hint))
}