Skip to content

Commit 959bad7

Browse files
mike-wardspytheman
authored andcommitted
ls implmentation with extras
1 parent 86ba302 commit 959bad7

16 files changed

Lines changed: 2089 additions & 122 deletions

src/ls/auto_wrap.v

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import os
2+
3+
fn set_auto_wrap(options Options) {
4+
if options.no_wrap {
5+
wrap_off := '\e[?7l'
6+
wrap_reset := '\e[?7h'
7+
println(wrap_off)
8+
9+
at_exit(fn [wrap_reset] () {
10+
println(wrap_reset)
11+
}) or {}
12+
13+
// Ctrl-C handler
14+
os.signal_opt(os.Signal.int, fn (sig os.Signal) {
15+
println('\e[?7h')
16+
exit(0)
17+
}) or {}
18+
}
19+
}

src/ls/entry.v

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import os
2+
import crypto.md5
3+
import crypto.sha1
4+
import crypto.sha256
5+
import crypto.sha512
6+
import crypto.blake2b
7+
import math
8+
9+
struct Entry {
10+
name string
11+
dir_name string
12+
stat os.Stat
13+
link_stat os.Stat
14+
dir bool
15+
file bool
16+
link bool
17+
exe bool
18+
fifo bool
19+
block bool
20+
socket bool
21+
character bool
22+
unknown bool
23+
link_origin string
24+
size u64
25+
size_ki string
26+
size_kb string
27+
checksum string
28+
invalid bool // lstat could not access
29+
}
30+
31+
fn get_entries(files []string, options Options) ([]Entry, int) {
32+
mut entries := []Entry{cap: 50}
33+
mut status := 0
34+
35+
for file in files {
36+
if os.is_dir(file) {
37+
dir_files := os.ls(file) or {
38+
status = 1 // see help for meaning of exit codes
39+
eprintln(err)
40+
continue
41+
}
42+
entries << match options.all {
43+
true { dir_files.map(make_entry(it, file, options)) }
44+
else { dir_files.filter(!is_dot_file(it)).map(make_entry(it, file, options)) }
45+
}
46+
} else {
47+
if options.all || !is_dot_file(file) {
48+
entries << make_entry(file, '', options)
49+
}
50+
}
51+
}
52+
return entries, status
53+
}
54+
55+
fn make_entry(file string, dir_name string, options Options) Entry {
56+
mut invalid := false
57+
path := if dir_name == '' { file } else { os.join_path(dir_name, file) }
58+
59+
stat := os.lstat(path) or {
60+
// println('${path} -> ${err.msg()}')
61+
invalid = true
62+
os.Stat{}
63+
}
64+
65+
filetype := stat.get_filetype()
66+
is_link := filetype == .symbolic_link
67+
link_origin := if is_link { read_link(path) } else { '' }
68+
mut size := stat.size
69+
mut link_stat := os.Stat{}
70+
71+
if is_link && options.long_format && !invalid {
72+
// os.stat follows link
73+
link_stat = os.stat(path) or { os.Stat{} }
74+
size = link_stat.size
75+
}
76+
77+
is_dir := filetype == .directory
78+
is_fifo := filetype == .fifo
79+
is_block := filetype == .block_device
80+
is_socket := filetype == .socket
81+
is_character_device := filetype == .character_device
82+
is_unknown := filetype == .unknown
83+
is_exe := !is_dir && is_executable(stat)
84+
is_file := filetype == .regular
85+
indicator := if is_dir && options.dir_indicator { '/' } else { '' }
86+
87+
return Entry{
88+
// vfmt off
89+
name: file + indicator
90+
dir_name: dir_name
91+
stat: stat
92+
link_stat: link_stat
93+
dir: is_dir
94+
file: is_file
95+
link: is_link
96+
exe: is_exe
97+
fifo: is_fifo
98+
block: is_block
99+
socket: is_socket
100+
character: is_character_device
101+
unknown: is_unknown
102+
link_origin: link_origin
103+
size: size
104+
size_ki: if options.size_ki { readable_size(size, true) } else { '' }
105+
size_kb: if options.size_kb { readable_size(size, false) } else { '' }
106+
checksum: if is_file { checksum(file, dir_name, options) } else { '' }
107+
invalid: invalid
108+
// vfmt on
109+
}
110+
}
111+
112+
fn readable_size(size u64, si bool) string {
113+
kb := if si { f64(1024) } else { f64(1000) }
114+
mut sz := f64(size)
115+
for unit in ['', 'k', 'm', 'g', 't', 'p', 'e', 'z'] {
116+
if sz < kb {
117+
readable := match unit == '' {
118+
true { size.str() }
119+
else { math.round_sig(sz + .049999, 1).str() }
120+
}
121+
bytes := match true {
122+
// vfmt off
123+
unit == '' { '' }
124+
si { 'b' }
125+
else { '' }
126+
// vfmt on
127+
}
128+
return '${readable}${unit}${bytes}'
129+
}
130+
sz /= kb
131+
}
132+
return size.str()
133+
}
134+
135+
fn checksum(name string, dir_name string, options Options) string {
136+
if options.checksum == '' {
137+
return ''
138+
}
139+
file := os.join_path(dir_name, name)
140+
bytes := os.read_bytes(file) or { return unknown }
141+
142+
return match options.checksum {
143+
// vfmt off
144+
'md5' { md5.sum(bytes).hex() }
145+
'sha1' { sha1.sum(bytes).hex() }
146+
'sha224' { sha256.sum224(bytes).hex() }
147+
'sha256' { sha256.sum256(bytes).hex() }
148+
'sha512' { sha512.sum512(bytes).hex() }
149+
'blake2b' { blake2b.sum256(bytes).hex() }
150+
else { unknown }
151+
// vfmt on
152+
}
153+
}
154+
155+
@[inline]
156+
fn is_executable(stat os.Stat) bool {
157+
return stat.get_mode().bitmask() & 0b001001001 > 0
158+
}
159+
160+
@[inline]
161+
fn is_dot_file(file string) bool {
162+
return file.starts_with('.')
163+
}

src/ls/entry_test.v

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module main
2+
3+
fn test_readable_size() {
4+
assert readable_size(395, true) == '395'
5+
assert readable_size(395, false) == '395'
6+
7+
assert readable_size(200_000, true) == '195.4kb'
8+
assert readable_size(200_000, false) == '200.0k'
9+
10+
assert readable_size(100_000_000, true) == '95.4mb'
11+
assert readable_size(100_000_000, false) == '100.0m'
12+
13+
assert readable_size(100_000_000_000, true) == '93.2gb'
14+
assert readable_size(100_000_000_000, false) == '100.0g'
15+
16+
assert readable_size(100_000_000_000_000, true) == '91.0tb'
17+
assert readable_size(100_000_000_000_000, false) == '100.0t'
18+
19+
assert readable_size(100_000_000_000_000_000, true) == '88.9pb'
20+
assert readable_size(100_000_000_000_000_000, false) == '100.0p'
21+
22+
assert readable_size(8_000_000_000_000_000_000, true) == '7.0eb'
23+
assert readable_size(8_000_000_000_000_000_000, false) == '8.0e'
24+
}

src/ls/filter.v

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fn filter(entries []Entry, options Options) []Entry {
2+
return match true {
3+
// vfmt off
4+
options.only_dirs { entries.clone().filter(it.dir) }
5+
options.only_files { entries.clone().filter(it.file) }
6+
else { entries }
7+
// vfmt on
8+
}
9+
}

0 commit comments

Comments
 (0)