Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
N
nccf
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
This is an archived project. Repository and other project resources are read-only.
Show more breadcrumbs
702
Provoz
nccf
Commits
b0e81ea4
Commit
b0e81ea4
authored
4 years ago
by
Yorhel
Browse files
Options
Downloads
Patches
Plain Diff
Implement scanning UI (-0,-1,-2)
parent
9b59d3da
No related branches found
No related tags found
No related merge requests found
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
README.md
+2
-1
2 additions, 1 deletion
README.md
src/browser.zig
+1
-1
1 addition, 1 deletion
src/browser.zig
src/main.zig
+40
-10
40 additions, 10 deletions
src/main.zig
src/scan.zig
+119
-12
119 additions, 12 deletions
src/scan.zig
src/ui.zig
+100
-35
100 additions, 35 deletions
src/ui.zig
with
262 additions
and
59 deletions
README.md
+
2
−
1
View file @
b0e81ea4
...
...
@@ -30,11 +30,11 @@ Missing features:
-
Export/import
-
Most directory listing settings
-
Scaning UI
-
Lots of informational UI windows
-
Directory refresh
-
File deletion
-
Opening a shell
-
OOM handling
### Improvements compared to the C version
...
...
@@ -54,6 +54,7 @@ Already implemented:
(Implemented in the data model, but not displayed in the UI yet)
-
Faster --exclude-kernfs thanks to
`statfs()`
caching.
-
Improved handling of Unicode and special characters.
-
Remembers item position when switching directories.
Potentially to be implemented:
...
...
This diff is collapsed.
Click to expand it.
src/browser.zig
+
1
−
1
View file @
b0e81ea4
...
...
@@ -257,7 +257,7 @@ pub fn key(ch: i32) !void {
defer
current_view
.
save
();
switch
(
ch
)
{
'q'
=>
main
.
state
=
.
quit
,
'q'
=>
ui
.
quit
(),
// TODO: Confirm
quit
// Selection
'j'
,
ui
.
c
.
KEY_DOWN
=>
{
...
...
This diff is collapsed.
Click to expand it.
src/main.zig
+
40
−
10
View file @
b0e81ea4
...
...
@@ -21,6 +21,7 @@ pub const Config = struct {
exclude_patterns
:
std
.
ArrayList
([:
0
]
const
u8
)
=
std
.
ArrayList
([:
0
]
const
u8
).
init
(
allocator
),
update_delay
:
u64
=
100
*
std
.
time
.
ns_per_ms
,
scan_ui
:
enum
{
none
,
line
,
full
}
=
.
full
,
si
:
bool
=
false
,
nc_tty
:
bool
=
false
,
ui_color
:
enum
{
off
,
dark
}
=
.
off
,
...
...
@@ -39,7 +40,7 @@ pub const Config = struct {
pub
var
config
=
Config
{};
pub
var
state
:
enum
{
browse
,
quit
}
=
.
browse
;
pub
var
state
:
enum
{
scan
,
browse
}
=
.
browse
;
// Simple generic argument parser, supports getopt_long() style arguments.
// T can be any type that has a 'fn next(T) ?[:0]const u8' method, e.g.:
...
...
@@ -173,6 +174,9 @@ pub fn main() anyerror!void {
var
args
=
Args
(
std
.
process
.
ArgIteratorPosix
).
init
(
std
.
process
.
ArgIteratorPosix
.
init
());
var
scan_dir
:
?
[]
const
u8
=
null
;
var
import_file
:
?
[]
const
u8
=
null
;
var
export_file
:
?
[]
const
u8
=
null
;
var
has_scan_ui
=
false
;
_
=
args
.
next
();
// program name
while
(
args
.
next
())
|
opt
|
{
if
(
!
opt
.
opt
)
{
...
...
@@ -188,6 +192,11 @@ pub fn main() anyerror!void {
else
if
(
opt
.
is
(
"-e"
))
config
.
extended
=
true
else
if
(
opt
.
is
(
"-r"
)
and
config
.
read_only
)
config
.
can_shell
=
false
else
if
(
opt
.
is
(
"-r"
))
config
.
read_only
=
true
else
if
(
opt
.
is
(
"-0"
))
{
has_scan_ui
=
true
;
config
.
scan_ui
=
.
none
;
}
else
if
(
opt
.
is
(
"-1"
))
{
has_scan_ui
=
true
;
config
.
scan_ui
=
.
line
;
}
else
if
(
opt
.
is
(
"-2"
))
{
has_scan_ui
=
true
;
config
.
scan_ui
=
.
full
;
}
else
if
(
opt
.
is
(
"-o"
))
export_file
=
args
.
arg
()
else
if
(
opt
.
is
(
"-f"
))
import_file
=
args
.
arg
()
else
if
(
opt
.
is
(
"--si"
))
config
.
si
=
true
else
if
(
opt
.
is
(
"-L"
)
or
opt
.
is
(
"--follow-symlinks"
))
config
.
follow_symlinks
=
true
else
if
(
opt
.
is
(
"--exclude"
))
try
config
.
exclude_patterns
.
append
(
args
.
arg
())
...
...
@@ -203,23 +212,34 @@ pub fn main() anyerror!void {
else
if
(
std
.
mem
.
eql
(
u8
,
val
,
"dark"
))
config
.
ui_color
=
.
dark
else
ui
.
die
(
"Unknown --color option: {s}.
\n
"
,
.
{
val
});
}
else
ui
.
die
(
"Unrecognized option '{s}'.
\n
"
,
.
{
opt
.
val
});
// TODO: -o, -f, -0, -1, -2
}
if
(
std
.
builtin
.
os
.
tag
!=
.
linux
and
config
.
exclude_kernfs
)
ui
.
die
(
"The --exclude-kernfs tag is currently only supported on Linux.
\n
"
,
.
{});
const
is_out_tty
=
std
.
io
.
getStdOut
().
isTty
();
if
(
!
has_scan_ui
)
{
if
(
export_file
)
|
f
|
{
if
(
!
is_out_tty
or
std
.
mem
.
eql
(
u8
,
f
,
"-"
))
config
.
scan_ui
=
.
none
else
config
.
scan_ui
=
.
line
;
}
}
if
(
!
is_out_tty
and
(
export_file
==
null
or
config
.
scan_ui
!=
.
none
))
ui
.
die
(
"Standard output is not a TTY, can't initialize ncurses UI.
\n
"
,
.
{});
event_delay_timer
=
try
std
.
time
.
Timer
.
start
();
defer
ui
.
deinit
();
state
=
.
scan
;
try
scan
.
scanRoot
(
scan_dir
orelse
"."
);
try
browser
.
loadDir
();
config
.
scan_ui
=
.
full
;
// in case we're refreshing from the UI, always in full mode.
ui
.
init
();
defer
ui
.
deinit
();
state
=
.
browse
;
try
browser
.
loadDir
();
// TODO: Handle OOM errors
// TODO: Confirm quit
while
(
state
!=
.
quit
)
try
handleEvent
(
true
,
false
);
while
(
true
)
try
handleEvent
(
true
,
false
);
}
var
event_delay_timer
:
std
.
time
.
Timer
=
undefined
;
...
...
@@ -228,16 +248,26 @@ var event_delay_timer: std.time.Timer = undefined;
// In non-blocking mode, screen drawing is rate-limited to keep this function fast.
pub
fn
handleEvent
(
block
:
bool
,
force_draw
:
bool
)
!
void
{
if
(
block
or
force_draw
or
event_delay_timer
.
read
()
>
config
.
update_delay
)
{
_
=
ui
.
c
.
erase
();
try
browser
.
draw
();
_
=
ui
.
c
.
refresh
();
if
(
ui
.
inited
)
_
=
ui
.
c
.
erase
();
switch
(
state
)
{
.
scan
=>
try
scan
.
draw
(),
.
browse
=>
try
browser
.
draw
(),
}
if
(
ui
.
inited
)
_
=
ui
.
c
.
refresh
();
event_delay_timer
.
reset
();
}
if
(
!
ui
.
inited
)
{
std
.
debug
.
assert
(
!
block
);
return
;
}
var
ch
=
ui
.
getch
(
block
);
if
(
ch
==
0
)
return
;
if
(
ch
==
-
1
)
return
handleEvent
(
block
,
true
);
try
browser
.
key
(
ch
);
switch
(
state
)
{
.
scan
=>
try
scan
.
key
(
ch
),
.
browse
=>
try
browser
.
key
(
ch
),
}
}
...
...
This diff is collapsed.
Click to expand it.
src/scan.zig
+
119
−
12
View file @
b0e81ea4
const
std
=
@import
(
"std"
);
const
main
=
@import
(
"main.zig"
);
const
model
=
@import
(
"model.zig"
);
const
ui
=
@import
(
"ui.zig"
);
usingnamespace
@import
(
"util.zig"
);
const
c_statfs
=
@cImport
(
@cInclude
(
"sys/vfs.h"
));
const
c_fnmatch
=
@cImport
(
@cInclude
(
"fnmatch.h"
));
...
...
@@ -98,11 +100,14 @@ const Context = struct {
parents
:
model
.
Parents
=
.
{},
path
:
std
.
ArrayList
(
u8
)
=
std
.
ArrayList
(
u8
).
init
(
main
.
allocator
),
path_indices
:
std
.
ArrayList
(
usize
)
=
std
.
ArrayList
(
usize
).
init
(
main
.
allocator
),
items_seen
:
u32
=
1
,
// 0-terminated name of the top entry, points into 'path', invalid after popPath().
// This is a workaround to Zig's directory iterator not returning a [:0]const u8.
name
:
[:
0
]
const
u8
=
undefined
,
last_error
:
?
[:
0
]
u8
=
null
,
const
Self
=
@This
();
fn
pushPath
(
self
:
*
Self
,
name
:
[]
const
u8
)
!
void
{
...
...
@@ -120,8 +125,27 @@ const Context = struct {
self
.
path
.
items
.
len
=
self
.
path_indices
.
items
[
self
.
path_indices
.
items
.
len
-
1
];
self
.
path_indices
.
items
.
len
-=
1
;
}
fn
pathZ
(
self
:
*
Self
)
[:
0
]
const
u8
{
self
.
path
.
append
(
0
)
catch
unreachable
;
defer
self
.
path
.
items
.
len
-=
1
;
return
self
.
path
.
items
[
0
..
self
.
path
.
items
.
len
-
1
:
0
];
}
// Insert the current path as an error entry
fn
setError
(
self
:
*
Self
)
!
void
{
var
e
=
try
model
.
Entry
.
create
(.
file
,
false
,
self
.
name
);
e
.
insert
(
&
self
.
parents
)
catch
unreachable
;
e
.
set_err
(
&
self
.
parents
);
if
(
self
.
last_error
)
|
p
|
main
.
allocator
.
free
(
p
);
self
.
last_error
=
try
main
.
allocator
.
dupeZ
(
u8
,
self
.
path
.
items
);
}
};
// Context that is currently being used for scanning.
var
active_context
:
?*
Context
=
null
;
// Read and index entries of the given dir. The entry for the directory is already assumed to be in 'ctx.parents'.
// (TODO: shouldn't error on OOM but instead call a function that waits or something)
fn
scanDir
(
ctx
:
*
Context
,
dir
:
std
.
fs
.
Dir
)
std
.
mem
.
Allocator
.
Error
!
void
{
...
...
@@ -131,16 +155,16 @@ fn scanDir(ctx: *Context, dir: std.fs.Dir) std.mem.Allocator.Error!void {
ctx
.
parents
.
top
().
entry
.
set_err
(
&
ctx
.
parents
);
return
;
}
orelse
break
;
ctx
.
items_seen
+=
1
;
try
ctx
.
pushPath
(
entry
.
name
);
try
main
.
handleEvent
(
false
,
false
);
defer
ctx
.
popPath
();
// XXX: This algorithm is extremely slow, can be optimized with some clever pattern parsing.
const
excluded
=
blk
:
{
for
(
main
.
config
.
exclude_patterns
.
items
)
|
pat
|
{
ctx
.
path
.
append
(
0
)
catch
unreachable
;
var
path
=
ctx
.
path
.
items
[
0
..
ctx
.
path
.
items
.
len
-
1
:
0
];
ctx
.
path
.
items
.
len
-=
1
;
var
path
=
ctx
.
pathZ
();
while
(
path
.
len
>
0
)
{
if
(
c_fnmatch
.
fnmatch
(
pat
,
path
,
0
)
==
0
)
break
:
blk
true
;
if
(
std
.
mem
.
indexOfScalar
(
u8
,
path
,
'/'
))
|
idx
|
path
=
path
[
idx
+
1
..
:
0
]
...
...
@@ -157,16 +181,12 @@ fn scanDir(ctx: *Context, dir: std.fs.Dir) std.mem.Allocator.Error!void {
}
var
stat
=
Stat
.
read
(
dir
,
ctx
.
name
,
false
)
catch
{
var
e
=
try
model
.
Entry
.
create
(.
file
,
false
,
entry
.
name
);
e
.
insert
(
&
ctx
.
parents
)
catch
unreachable
;
e
.
set_err
(
&
ctx
.
parents
);
try
ctx
.
setError
();
continue
;
};
if
(
main
.
config
.
same_fs
and
stat
.
dev
!=
model
.
getDev
(
ctx
.
parents
.
top
().
dev
))
{
var
e
=
try
model
.
Entry
.
create
(.
file
,
false
,
entry
.
name
);
e
.
file
()
.?
.
other_fs
=
true
;
e
.
insert
(
&
ctx
.
parents
)
catch
unreachable
;
try
ctx
.
setError
();
continue
;
}
...
...
@@ -184,9 +204,7 @@ fn scanDir(ctx: *Context, dir: std.fs.Dir) std.mem.Allocator.Error!void {
var
edir
=
if
(
stat
.
dir
)
dir
.
openDirZ
(
ctx
.
name
,
.
{
.
access_sub_paths
=
true
,
.
iterate
=
true
,
.
no_follow
=
true
})
catch
{
var
e
=
try
model
.
Entry
.
create
(.
file
,
false
,
entry
.
name
);
e
.
insert
(
&
ctx
.
parents
)
catch
unreachable
;
e
.
set_err
(
&
ctx
.
parents
);
try
ctx
.
setError
();
continue
;
}
else
null
;
defer
if
(
edir
!=
null
)
edir
.?
.
close
();
...
...
@@ -248,5 +266,94 @@ pub fn scanRoot(path: []const u8) !void {
var
ctx
=
Context
{};
try
ctx
.
pushPath
(
full_path
);
const
dir
=
try
std
.
fs
.
cwd
().
openDirZ
(
model
.
root
.
entry
.
name
(),
.
{
.
access_sub_paths
=
true
,
.
iterate
=
true
});
active_context
=
&
ctx
;
defer
active_context
=
null
;
try
scanDir
(
&
ctx
,
dir
);
}
var
animation_pos
:
u32
=
0
;
fn
drawBox
()
!
void
{
ui
.
init
();
const
ctx
=
active_context
.?
;
const
width
=
saturateSub
(
ui
.
cols
,
5
);
const
box
=
ui
.
Box
.
create
(
10
,
width
,
"Scanning..."
);
box
.
move
(
2
,
2
);
ui
.
addstr
(
"Total items: "
);
ui
.
addnum
(.
default
,
ctx
.
items_seen
);
if
(
width
>
48
and
true
)
{
// TODO: When not exporting to file
box
.
move
(
2
,
30
);
ui
.
addstr
(
"size: "
);
ui
.
addsize
(.
default
,
blocksToSize
(
model
.
root
.
entry
.
blocks
));
}
box
.
move
(
3
,
2
);
ui
.
addstr
(
"Current item: "
);
ui
.
addstr
(
try
ui
.
shorten
(
try
ui
.
toUtf8
(
ctx
.
pathZ
()),
saturateSub
(
width
,
18
)));
if
(
ctx
.
last_error
)
|
path
|
{
box
.
move
(
5
,
2
);
ui
.
style
(.
bold
);
ui
.
addstr
(
"Warning: "
);
ui
.
style
(.
default
);
ui
.
addstr
(
"error scanning "
);
ui
.
addstr
(
try
ui
.
shorten
(
try
ui
.
toUtf8
(
path
),
saturateSub
(
width
,
28
)));
box
.
move
(
6
,
3
);
ui
.
addstr
(
"some directory sizes may not be correct."
);
}
box
.
move
(
8
,
saturateSub
(
width
,
18
));
ui
.
addstr
(
"Press "
);
ui
.
style
(.
key
);
ui
.
addch
(
'q'
);
ui
.
style
(.
default
);
ui
.
addstr
(
" to abort"
);
if
(
main
.
config
.
update_delay
<
std
.
time
.
ns_per_s
and
width
>
40
)
{
const
txt
=
"Scanning..."
;
animation_pos
+=
1
;
if
(
animation_pos
>=
txt
.
len
*
2
)
animation_pos
=
0
;
if
(
animation_pos
<
txt
.
len
)
{
var
i
:
u32
=
0
;
box
.
move
(
8
,
2
);
while
(
i
<=
animation_pos
)
:
(
i
+=
1
)
ui
.
addch
(
txt
[
i
]);
}
else
{
var
i
:
u32
=
txt
.
len
-
1
;
while
(
i
>
animation_pos
-
txt
.
len
)
:
(
i
-=
1
)
{
box
.
move
(
8
,
2
+
i
);
ui
.
addch
(
txt
[
i
]);
}
}
}
}
pub
fn
draw
()
!
void
{
switch
(
main
.
config
.
scan_ui
)
{
.
none
=>
{},
.
line
=>
{
var
buf
:
[
256
]
u8
=
undefined
;
var
line
:
[]
const
u8
=
undefined
;
if
(
false
)
{
// TODO: When exporting to file; no total size known
line
=
std
.
fmt
.
bufPrint
(
&
buf
,
"
\x1b
7
\x1b
[J{s: <63} {d:>9} files
\x1b
8"
,
.
{
ui
.
shorten
(
active_context
.?
.
pathZ
(),
63
),
active_context
.?
.
items_seen
}
)
catch
return
;
}
else
{
const
r
=
ui
.
FmtSize
.
fmt
(
blocksToSize
(
model
.
root
.
entry
.
blocks
));
line
=
std
.
fmt
.
bufPrint
(
&
buf
,
"
\x1b
7
\x1b
[J{s: <51} {d:>9} files / {s}{s}
\x1b
8"
,
.
{
ui
.
shorten
(
active_context
.?
.
pathZ
(),
51
),
active_context
.?
.
items_seen
,
r
.
num
(),
r
.
unit
}
)
catch
return
;
}
_
=
std
.
io
.
getStdErr
().
write
(
line
)
catch
{};
},
.
full
=>
try
drawBox
(),
}
}
pub
fn
key
(
ch
:
i32
)
!
void
{
switch
(
ch
)
{
'q'
=>
ui
.
quit
(),
// TODO: Confirm quit
else
=>
{},
}
}
This diff is collapsed.
Click to expand it.
src/ui.zig
+
100
−
35
View file @
b0e81ea4
...
...
@@ -2,6 +2,7 @@
const
std
=
@import
(
"std"
);
const
main
=
@import
(
"main.zig"
);
usingnamespace
@import
(
"util.zig"
);
pub
const
c
=
@cImport
({
@cInclude
(
"stdio.h"
);
...
...
@@ -12,7 +13,7 @@ pub const c = @cImport({
@cInclude
(
"locale.h"
);
});
var
inited
:
bool
=
false
;
pub
var
inited
:
bool
=
false
;
pub
var
rows
:
u32
=
undefined
;
pub
var
cols
:
u32
=
undefined
;
...
...
@@ -23,6 +24,11 @@ pub fn die(comptime fmt: []const u8, args: anytype) noreturn {
std
.
process
.
exit
(
1
);
}
pub
fn
quit
()
noreturn
{
deinit
();
std
.
process
.
exit
(
0
);
}
var
to_utf8_buf
=
std
.
ArrayList
(
u8
).
init
(
main
.
allocator
);
fn
toUtf8BadChar
(
ch
:
u8
)
bool
{
...
...
@@ -141,13 +147,6 @@ extern fn ncdu_acs_lrcorner() c.chtype;
extern
fn
ncdu_acs_hline
()
c
.
chtype
;
extern
fn
ncdu_acs_vline
()
c
.
chtype
;
pub
fn
acs_ulcorner
()
c
.
chtype
{
return
ncdu_acs_ulcorner
();
}
pub
fn
acs_llcorner
()
c
.
chtype
{
return
ncdu_acs_llcorner
();
}
pub
fn
acs_urcorner
()
c
.
chtype
{
return
ncdu_acs_urcorner
();
}
pub
fn
acs_lrcorner
()
c
.
chtype
{
return
ncdu_acs_lrcorner
();
}
pub
fn
acs_hline
()
c
.
chtype
{
return
ncdu_acs_hline
()
;
}
pub
fn
acs_vline
()
c
.
chtype
{
return
ncdu_acs_vline
()
;
}
const
StyleAttr
=
struct
{
fg
:
i16
,
bg
:
i16
,
attr
:
u32
};
const
StyleDef
=
struct
{
name
:
[]
const
u8
,
...
...
@@ -165,6 +164,9 @@ const styles = [_]StyleDef{
.
{
.
name
=
"default"
,
.
off
=
.
{
.
fg
=
-
1
,
.
bg
=
-
1
,
.
attr
=
0
},
.
dark
=
.
{
.
fg
=
-
1
,
.
bg
=
-
1
,
.
attr
=
0
}
},
.
{
.
name
=
"bold"
,
.
off
=
.
{
.
fg
=
-
1
,
.
bg
=
-
1
,
.
attr
=
c
.
A_BOLD
},
.
dark
=
.
{
.
fg
=
-
1
,
.
bg
=
-
1
,
.
attr
=
c
.
A_BOLD
}
},
.
{
.
name
=
"box_title"
,
.
off
=
.
{
.
fg
=
-
1
,
.
bg
=
-
1
,
.
attr
=
c
.
A_BOLD
},
.
dark
=
.
{
.
fg
=
c
.
COLOR_BLUE
,
.
bg
=
-
1
,
.
attr
=
c
.
A_BOLD
}
},
...
...
@@ -266,6 +268,9 @@ fn updateSize() void {
pub
fn
init
()
void
{
if
(
inited
)
return
;
// Send a "clear from cursor to end of screen" instruction, to clear a
// potential line left behind from scanning in -1 mode.
_
=
std
.
io
.
getStdErr
().
write
(
"
\x1b
[J"
)
catch
{};
if
(
main
.
config
.
nc_tty
)
{
var
tty
=
c
.
fopen
(
"/dev/tty"
,
"r+"
);
if
(
tty
==
null
)
die
(
"Error opening /dev/tty: {s}.
\n
"
,
.
{
c
.
strerror
(
std
.
c
.
getErrno
(
-
1
))
});
...
...
@@ -317,37 +322,51 @@ pub fn addch(ch: c.chtype) void {
_
=
c
.
addch
(
ch
);
}
// Print a human-readable size string, formatted into the given bavkground.
// Takes 8 columns in SI mode, 9 otherwise.
// "###.# XB"
// "###.# XiB"
pub
fn
addsize
(
bg
:
Bg
,
v
:
u64
)
void
{
var
f
=
@intToFloat
(
f32
,
v
);
var
unit
:
[:
0
]
const
u8
=
undefined
;
if
(
main
.
config
.
si
)
{
if
(
f
<
1000.0
)
{
unit
=
" B"
;
}
else
if
(
f
<
1e6
)
{
unit
=
" KB"
;
f
/=
1e3
;
}
else
if
(
f
<
1e9
)
{
unit
=
" MB"
;
f
/=
1e6
;
}
else
if
(
f
<
1e12
)
{
unit
=
" GB"
;
f
/=
1e9
;
}
else
if
(
f
<
1e15
)
{
unit
=
" TB"
;
f
/=
1e12
;
}
else
if
(
f
<
1e18
)
{
unit
=
" PB"
;
f
/=
1e15
;
}
else
{
unit
=
" EB"
;
f
/=
1e18
;
}
// Format an integer to a human-readable size string.
// num() = "###.#"
// unit = " XB" or " XiB"
// Concatenated, these take 8 columns in SI mode or 9 otherwise.
pub
const
FmtSize
=
struct
{
buf
:
[
8
:
0
]
u8
,
unit
:
[:
0
]
const
u8
,
pub
fn
fmt
(
v
:
u64
)
@This
()
{
var
r
:
@This
()
=
undefined
;
var
f
=
@intToFloat
(
f32
,
v
);
if
(
main
.
config
.
si
)
{
if
(
f
<
1000.0
)
{
r
.
unit
=
" B"
;
}
else
if
(
f
<
1e6
)
{
r
.
unit
=
" KB"
;
f
/=
1e3
;
}
else
if
(
f
<
1e9
)
{
r
.
unit
=
" MB"
;
f
/=
1e6
;
}
else
if
(
f
<
1e12
)
{
r
.
unit
=
" GB"
;
f
/=
1e9
;
}
else
if
(
f
<
1e15
)
{
r
.
unit
=
" TB"
;
f
/=
1e12
;
}
else
if
(
f
<
1e18
)
{
r
.
unit
=
" PB"
;
f
/=
1e15
;
}
else
{
r
.
unit
=
" EB"
;
f
/=
1e18
;
}
}
else
{
if
(
f
<
1000.0
)
{
r
.
unit
=
" B"
;
}
else
if
(
f
<
1023e3
)
{
r
.
unit
=
" KiB"
;
f
/=
1024.0
;
}
else
if
(
f
<
1023e6
)
{
r
.
unit
=
" MiB"
;
f
/=
1048576.0
;
}
else
if
(
f
<
1023e9
)
{
r
.
unit
=
" GiB"
;
f
/=
1073741824.0
;
}
else
if
(
f
<
1023e12
)
{
r
.
unit
=
" TiB"
;
f
/=
1099511627776.0
;
}
else
if
(
f
<
1023e15
)
{
r
.
unit
=
" PiB"
;
f
/=
1125899906842624.0
;
}
else
{
r
.
unit
=
" EiB"
;
f
/=
1152921504606846976.0
;
}
}
_
=
std
.
fmt
.
bufPrintZ
(
&
r
.
buf
,
"{d:>5.1}"
,
.
{
f
})
catch
unreachable
;
return
r
;
}
else
{
if
(
f
<
1000.0
)
{
unit
=
" B"
;
}
else
if
(
f
<
1023e3
)
{
unit
=
" KiB"
;
f
/=
1024.0
;
}
else
if
(
f
<
1023e6
)
{
unit
=
" MiB"
;
f
/=
1048576.0
;
}
else
if
(
f
<
1023e9
)
{
unit
=
" GiB"
;
f
/=
1073741824.0
;
}
else
if
(
f
<
1023e12
)
{
unit
=
" TiB"
;
f
/=
1099511627776.0
;
}
else
if
(
f
<
1023e15
)
{
unit
=
" PiB"
;
f
/=
1125899906842624.0
;
}
else
{
unit
=
" EiB"
;
f
/=
1152921504606846976.0
;
}
pub
fn
num
(
self
:
*
const
@This
())
[:
0
]
const
u8
{
return
std
.
mem
.
spanZ
(
&
self
.
buf
);
}
var
buf
:
[
8
:
0
]
u8
=
undefined
;
_
=
std
.
fmt
.
bufPrintZ
(
&
buf
,
"{d:>5.1}"
,
.
{
f
})
catch
unreachable
;
};
// Print a formatted human-readable size string onto the given background.
pub
fn
addsize
(
bg
:
Bg
,
v
:
u64
)
void
{
const
r
=
FmtSize
.
fmt
(
v
);
bg
.
fg
(.
num
);
addstr
(
&
buf
);
addstr
(
r
.
num
()
);
bg
.
fg
(.
default
);
addstr
(
unit
);
addstr
(
r
.
unit
);
}
// Print a full decimal number with thousand separators.
...
...
@@ -378,6 +397,52 @@ pub fn hline(ch: c.chtype, len: u32) void {
_
=
c
.
hline
(
ch
,
@intCast
(
i32
,
len
));
}
// Draws a bordered box in the center of the screen.
pub
const
Box
=
struct
{
start_row
:
u32
,
start_col
:
u32
,
const
Self
=
@This
();
pub
fn
create
(
height
:
u32
,
width
:
u32
,
title
:
[:
0
]
const
u8
)
Self
{
const
s
=
Self
{
.
start_row
=
saturateSub
(
rows
>>
1
,
height
>>
1
),
.
start_col
=
saturateSub
(
cols
>>
1
,
width
>>
1
),
};
style
(.
default
);
if
(
width
<
6
or
height
<
3
)
return
s
;
const
ulcorner
=
ncdu_acs_ulcorner
();
const
llcorner
=
ncdu_acs_llcorner
();
const
urcorner
=
ncdu_acs_urcorner
();
const
lrcorner
=
ncdu_acs_lrcorner
();
const
acs_hline
=
ncdu_acs_hline
();
const
acs_vline
=
ncdu_acs_vline
();
var
i
:
u32
=
0
;
while
(
i
<
height
)
:
(
i
+=
1
)
{
s
.
move
(
i
,
0
);
addch
(
if
(
i
==
0
)
ulcorner
else
if
(
i
==
height
-
1
)
llcorner
else
acs_hline
);
hline
(
if
(
i
==
0
or
i
==
height
-
1
)
acs_vline
else
' '
,
width
-
2
);
s
.
move
(
i
,
width
-
1
);
addch
(
if
(
i
==
0
)
urcorner
else
if
(
i
==
height
-
1
)
lrcorner
else
acs_hline
);
}
s
.
move
(
0
,
3
);
style
(.
box_title
);
addch
(
' '
);
addstr
(
title
);
addch
(
' '
);
style
(.
default
);
return
s
;
}
// Move the global cursor to the given coordinates inside the box.
pub
fn
move
(
s
:
Self
,
row
:
u32
,
col
:
u32
)
void
{
ui
.
move
(
s
.
start_row
+
row
,
s
.
start_col
+
col
);
}
};
// Returns 0 if no key was pressed in non-blocking mode.
// Returns -1 if it was KEY_RESIZE, requiring a redraw of the screen.
pub
fn
getch
(
block
:
bool
)
i32
{
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment