mirror of
https://github.com/sbrow/envr.git
synced 2026-08-26 18:03:32 -04:00
Compare commits
4 Commits
af1860c970
...
f2a98bc782
| Author | SHA1 | Date | |
|---|---|---|---|
| f2a98bc782 | |||
| 7aaa415777 | |||
| ca992f05f4 | |||
| ae42d53f02 |
@@ -16,15 +16,9 @@
|
|||||||
|
|
||||||
8. Add tests for untested commands.
|
8. Add tests for untested commands.
|
||||||
|
|
||||||
9. Update `read_wire_string` to use a slice.
|
9. Consider getting rid of color global.
|
||||||
|
|
||||||
10. Pass allocator to findr?
|
10. `write_flags_table` should never return false.
|
||||||
|
|
||||||
11. Consider getting rid of color global.
|
|
||||||
|
|
||||||
12. `write_flags_table` should never return false.
|
|
||||||
|
|
||||||
13. Add a text filter to the multi_select.
|
|
||||||
|
|
||||||
## Double-check AI output
|
## Double-check AI output
|
||||||
|
|
||||||
@@ -54,7 +48,7 @@
|
|||||||
- [ ] db_test.odin
|
- [ ] db_test.odin
|
||||||
- [ ] flags.odin
|
- [ ] flags.odin
|
||||||
- [x] main.odin
|
- [x] main.odin
|
||||||
- [x] prompt.odin
|
- [ ] prompt.odin
|
||||||
- [x] scan.odin
|
- [x] scan.odin
|
||||||
- [ ] scan_test.odin
|
- [ ] scan_test.odin
|
||||||
- [ ] sodium.odin
|
- [ ] sodium.odin
|
||||||
|
|||||||
+4
-3
@@ -201,8 +201,7 @@ find_git_roots :: proc(
|
|||||||
ok: bool,
|
ok: bool,
|
||||||
) {
|
) {
|
||||||
paths := search_paths(cfg, allocator)
|
paths := search_paths(cfg, allocator)
|
||||||
// TODO: Pass allocator to findr
|
findr.find_repos(paths[:], &roots, os.get_processor_core_count(), allocator)
|
||||||
findr.find_repos(paths[:], &roots, os.get_processor_core_count())
|
|
||||||
ok = true
|
ok = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -214,6 +213,7 @@ search_paths :: proc(cfg: Config, allocator := context.allocator) -> [dynamic]st
|
|||||||
}
|
}
|
||||||
|
|
||||||
paths := new_clone(cfg.scan_config.include, allocator)
|
paths := new_clone(cfg.scan_config.include, allocator)
|
||||||
|
defer free(paths, allocator)
|
||||||
|
|
||||||
for &include in paths {
|
for &include in paths {
|
||||||
expanded, _ := strings.replace(include, "~", home, 1, allocator)
|
expanded, _ := strings.replace(include, "~", home, 1, allocator)
|
||||||
@@ -227,7 +227,8 @@ search_paths :: proc(cfg: Config, allocator := context.allocator) -> [dynamic]st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return paths^
|
result := paths^
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
envr_dir :: proc(config_path: string) -> string {
|
envr_dir :: proc(config_path: string) -> string {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:mem"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:path/filepath"
|
import "core:path/filepath"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
@@ -194,3 +195,14 @@ test_search_paths_expands_tilde :: proc(t: ^testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
test_search_paths_no_leak :: proc(t: ^testing.T) {
|
||||||
|
cfg := Config {
|
||||||
|
scan_config = ScanConfig{include = make([dynamic]string, 0, 1)},
|
||||||
|
}
|
||||||
|
defer delete(cfg.scan_config.include)
|
||||||
|
append(&cfg.scan_config.include, "/tmp")
|
||||||
|
|
||||||
|
_ = search_paths(cfg, context.allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -491,7 +491,7 @@ db_persist :: proc(db: ^Db, f: ^EnvFile, old_path: string) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try_move_dir :: proc(db: ^Db, f: ^EnvFile, allocator: mem.Allocator) -> (bool, SyncError) {
|
try_move_dir :: proc(db: ^Db, f: ^EnvFile, allocator: mem.Allocator) -> (bool, SyncError) {
|
||||||
roots, ok := find_git_roots(db.cfg)
|
roots, ok := find_git_roots(db.cfg, context.allocator)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, .GitRootFailed
|
return false, .GitRootFailed
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-1
@@ -16,7 +16,14 @@ RepoPool :: struct {
|
|||||||
threads: []^thread.Thread,
|
threads: []^thread.Thread,
|
||||||
}
|
}
|
||||||
|
|
||||||
find_repos :: proc(roots: []string, results: ^[dynamic]string, thread_count: int) {
|
find_repos :: proc(
|
||||||
|
roots: []string,
|
||||||
|
results: ^[dynamic]string,
|
||||||
|
thread_count: int,
|
||||||
|
allocator := context.allocator,
|
||||||
|
) {
|
||||||
|
// TODO: This may be a code smell
|
||||||
|
context.allocator = allocator
|
||||||
if len(roots) == 0 do return
|
if len(roots) == 0 do return
|
||||||
|
|
||||||
pool := new(RepoPool)
|
pool := new(RepoPool)
|
||||||
@@ -126,3 +133,4 @@ process_repo_dir :: proc(pool: ^RepoPool, dir_path: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-1
@@ -78,7 +78,15 @@ collect_worker :: proc(t: ^thread.Thread) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
walk :: proc(roots: []string, results: ^[dynamic]string, opts: WalkOptions, thread_count: int) {
|
walk :: proc(
|
||||||
|
roots: []string,
|
||||||
|
results: ^[dynamic]string,
|
||||||
|
opts: WalkOptions,
|
||||||
|
thread_count: int,
|
||||||
|
allocator := context.allocator,
|
||||||
|
) {
|
||||||
|
// TODO: This may be a code smell
|
||||||
|
context.allocator = allocator
|
||||||
if len(roots) == 0 do return
|
if len(roots) == 0 do return
|
||||||
|
|
||||||
ch, _ := chan.create(chan.Chan([]u8), max(2 * thread_count, 2), context.allocator)
|
ch, _ := chan.create(chan.Chan([]u8), max(2 * thread_count, 2), context.allocator)
|
||||||
|
|||||||
+76
-26
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:strings"
|
||||||
import "core:sys/posix"
|
import "core:sys/posix"
|
||||||
import "core:terminal/ansi"
|
import "core:terminal/ansi"
|
||||||
|
|
||||||
@@ -15,6 +16,8 @@ Key :: enum {
|
|||||||
Space,
|
Space,
|
||||||
Enter,
|
Enter,
|
||||||
Escape,
|
Escape,
|
||||||
|
Backspace,
|
||||||
|
Char,
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,11 +41,17 @@ multi_select :: proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
selected = make([dynamic]bool, len(options))
|
selected = make([dynamic]bool, len(options))
|
||||||
|
filter: [dynamic]u8
|
||||||
|
defer delete(filter)
|
||||||
|
filtered: [dynamic]int
|
||||||
|
defer delete(filtered)
|
||||||
|
rebuild_filtered(options, filter[:], &filtered)
|
||||||
|
|
||||||
cursor: int = 0
|
cursor: int = 0
|
||||||
scroll_offset: int = 0
|
scroll_offset: int = 0
|
||||||
|
|
||||||
fmt.printf(ansi.CSI + ansi.DECTCEM_HIDE)
|
fmt.printf(ansi.CSI + ansi.DECTCEM_HIDE)
|
||||||
visible := render_options(prompt, options, selected[:], cursor, scroll_offset)
|
visible := render_options(prompt, options, selected[:], filtered[:], string(filter[:]), cursor, scroll_offset)
|
||||||
|
|
||||||
raw, ok := enable_raw_mode(posix.STDIN_FILENO)
|
raw, ok := enable_raw_mode(posix.STDIN_FILENO)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -52,19 +61,31 @@ multi_select :: proc(
|
|||||||
defer disable_raw_mode(&raw)
|
defer disable_raw_mode(&raw)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
key := read_key()
|
key, ch := read_key()
|
||||||
|
|
||||||
switch key {
|
switch key {
|
||||||
|
case .Char:
|
||||||
|
append(&filter, u8(ch))
|
||||||
|
rebuild_filtered(options, filter[:], &filtered)
|
||||||
|
cursor = clamp(cursor, 0, max(0, len(filtered) - 1))
|
||||||
|
case .Backspace:
|
||||||
|
if len(filter) > 0 {
|
||||||
|
pop(&filter)
|
||||||
|
rebuild_filtered(options, filter[:], &filtered)
|
||||||
|
cursor = clamp(cursor, 0, max(0, len(filtered) - 1))
|
||||||
|
}
|
||||||
case .Up:
|
case .Up:
|
||||||
if cursor > 0 {
|
if cursor > 0 {
|
||||||
cursor -= 1
|
cursor -= 1
|
||||||
}
|
}
|
||||||
case .Down:
|
case .Down:
|
||||||
if cursor < len(options) - 1 {
|
if cursor < len(filtered) - 1 {
|
||||||
cursor += 1
|
cursor += 1
|
||||||
}
|
}
|
||||||
case .Space:
|
case .Space:
|
||||||
selected[cursor] = !selected[cursor]
|
if len(filtered) > 0 {
|
||||||
|
selected[filtered[cursor]] = !selected[filtered[cursor]]
|
||||||
|
}
|
||||||
case .Enter:
|
case .Enter:
|
||||||
fmt.printf(
|
fmt.printf(
|
||||||
ansi.CSI + "%d" + ansi.CUU + ansi.CSI + ansi.ED + ansi.CSI + ansi.DECTCEM_SHOW,
|
ansi.CSI + "%d" + ansi.CUU + ansi.CSI + ansi.ED + ansi.CSI + ansi.DECTCEM_SHOW,
|
||||||
@@ -82,9 +103,21 @@ multi_select :: proc(
|
|||||||
case .Unknown:
|
case .Unknown:
|
||||||
}
|
}
|
||||||
|
|
||||||
scroll_offset = max(0, min(cursor - MAX_VISIBLE / 2, len(options) - MAX_VISIBLE))
|
scroll_offset = max(0, min(cursor - MAX_VISIBLE / 2, len(filtered) - MAX_VISIBLE))
|
||||||
fmt.printf(ansi.CSI + "%d" + ansi.CUU + ansi.CSI + ansi.RESET + ansi.ED, visible + 1)
|
fmt.printf(ansi.CSI + "%d" + ansi.CUU + ansi.CSI + ansi.RESET + ansi.ED, visible + 1)
|
||||||
visible = render_options(prompt, options, selected[:], cursor, scroll_offset)
|
visible = render_options(prompt, options, selected[:], filtered[:], string(filter[:]), cursor, scroll_offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rebuild_filtered :: proc(options: []string, filter: []u8, filtered: ^[dynamic]int) {
|
||||||
|
clear(filtered)
|
||||||
|
filter_str := string(filter)
|
||||||
|
filter_lower := strings.to_lower(filter_str, context.temp_allocator)
|
||||||
|
for opt, i in options {
|
||||||
|
opt_lower := strings.to_lower(opt, context.temp_allocator)
|
||||||
|
if strings.contains(opt_lower, filter_lower) {
|
||||||
|
append(filtered, i)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,22 +125,34 @@ render_options :: proc(
|
|||||||
prompt: string,
|
prompt: string,
|
||||||
options: []string,
|
options: []string,
|
||||||
selected: []bool,
|
selected: []bool,
|
||||||
|
filtered: []int,
|
||||||
|
filter_text: string,
|
||||||
cursor: int,
|
cursor: int,
|
||||||
scroll_offset: int,
|
scroll_offset: int,
|
||||||
) -> int {
|
) -> int {
|
||||||
fmt.printf(
|
fmt.printf(
|
||||||
"%s (↑/↓ move, space select, enter confirm)\r\n",
|
"%s (type to filter, ↑/↓ move, space select, enter confirm)\r\n",
|
||||||
colorize(.Option_Label, prompt),
|
colorize(.Option_Label, prompt),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fmt.printf("filter: %s\r\n", filter_text)
|
||||||
|
|
||||||
|
line_count := 1
|
||||||
|
|
||||||
|
if len(filtered) == 0 {
|
||||||
|
fmt.printf(" No matches\r\n")
|
||||||
|
return line_count + 1
|
||||||
|
}
|
||||||
|
|
||||||
end := scroll_offset + MAX_VISIBLE
|
end := scroll_offset + MAX_VISIBLE
|
||||||
if end > len(options) {
|
if end > len(filtered) {
|
||||||
end = len(options)
|
end = len(filtered)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in scroll_offset ..< end {
|
for i in scroll_offset ..< end {
|
||||||
|
original := filtered[i]
|
||||||
checkbox := " "
|
checkbox := " "
|
||||||
if selected[i] {
|
if selected[original] {
|
||||||
checkbox = "x"
|
checkbox = "x"
|
||||||
}
|
}
|
||||||
if i == cursor {
|
if i == cursor {
|
||||||
@@ -115,14 +160,14 @@ render_options :: proc(
|
|||||||
"%s [%s] %s\r\n",
|
"%s [%s] %s\r\n",
|
||||||
colorize(.Caret, ">"),
|
colorize(.Caret, ">"),
|
||||||
colorize(.Sucess, checkbox),
|
colorize(.Sucess, checkbox),
|
||||||
options[i],
|
options[original],
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
fmt.printf(" [%s] %s\r\n", colorize(.Sucess, checkbox), options[i])
|
fmt.printf(" [%s] %s\r\n", colorize(.Sucess, checkbox), options[original])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return end - scroll_offset
|
return line_count + (end - scroll_offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
enable_raw_mode :: proc(fd: posix.FD) -> (Raw_State, bool) {
|
enable_raw_mode :: proc(fd: posix.FD) -> (Raw_State, bool) {
|
||||||
@@ -152,21 +197,23 @@ disable_raw_mode :: proc(state: ^Raw_State) {
|
|||||||
posix.tcsetattr(state.fd, .TCSAFLUSH, &state.original)
|
posix.tcsetattr(state.fd, .TCSAFLUSH, &state.original)
|
||||||
}
|
}
|
||||||
|
|
||||||
read_key :: proc() -> Key {
|
read_key :: proc() -> (key: Key, ch: rune) {
|
||||||
buf: [3]u8
|
buf: [3]u8
|
||||||
|
|
||||||
n := posix.read(posix.STDIN_FILENO, &buf[0], 1)
|
n := posix.read(posix.STDIN_FILENO, &buf[0], 1)
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
return .Unknown
|
return .Unknown, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
switch buf[0] {
|
switch buf[0] {
|
||||||
case ' ':
|
case ' ':
|
||||||
return .Space
|
return .Space, 0
|
||||||
case '\n', '\r':
|
case '\n', '\r':
|
||||||
return .Enter
|
return .Enter, 0
|
||||||
case 0x03:
|
case 0x03:
|
||||||
return .Escape
|
return .Escape, 0
|
||||||
|
case 0x08, 0x7F:
|
||||||
|
return .Backspace, 0
|
||||||
case 0x1b:
|
case 0x1b:
|
||||||
tv: posix.timeval
|
tv: posix.timeval
|
||||||
tv.tv_sec = 0
|
tv.tv_sec = 0
|
||||||
@@ -178,12 +225,12 @@ read_key :: proc() -> Key {
|
|||||||
|
|
||||||
ready := posix.select(1, &set, nil, nil, &tv)
|
ready := posix.select(1, &set, nil, nil, &tv)
|
||||||
if ready <= 0 {
|
if ready <= 0 {
|
||||||
return .Escape
|
return .Escape, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
n2 := posix.read(posix.STDIN_FILENO, &buf[1], 1)
|
n2 := posix.read(posix.STDIN_FILENO, &buf[1], 1)
|
||||||
if n2 <= 0 || buf[1] != '[' {
|
if n2 <= 0 || buf[1] != '[' {
|
||||||
return .Escape
|
return .Escape, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
posix.FD_ZERO(&set)
|
posix.FD_ZERO(&set)
|
||||||
@@ -193,24 +240,27 @@ read_key :: proc() -> Key {
|
|||||||
|
|
||||||
ready = posix.select(1, &set, nil, nil, &tv)
|
ready = posix.select(1, &set, nil, nil, &tv)
|
||||||
if ready <= 0 {
|
if ready <= 0 {
|
||||||
return .Escape
|
return .Escape, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
n3 := posix.read(posix.STDIN_FILENO, &buf[2], 1)
|
n3 := posix.read(posix.STDIN_FILENO, &buf[2], 1)
|
||||||
if n3 <= 0 {
|
if n3 <= 0 {
|
||||||
return .Escape
|
return .Escape, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
switch buf[2] {
|
switch buf[2] {
|
||||||
case 'A':
|
case 'A':
|
||||||
return .Up
|
return .Up, 0
|
||||||
case 'B':
|
case 'B':
|
||||||
return .Down
|
return .Down, 0
|
||||||
case:
|
case:
|
||||||
return .Escape
|
return .Escape, 0
|
||||||
}
|
}
|
||||||
case:
|
case:
|
||||||
return .Unknown
|
if buf[0] >= 0x20 && buf[0] <= 0x7E {
|
||||||
|
return .Char, rune(buf[0])
|
||||||
|
}
|
||||||
|
return .Unknown, 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,18 +35,18 @@ parse_ssh_public_key :: proc(pub_path: string) -> (pub: [32]u8, ok: bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
offset := 0
|
buf := decoded
|
||||||
key_type, type_ok := read_wire_string(decoded, &offset)
|
key_type, type_ok := read_wire_string(&buf)
|
||||||
if !type_ok || key_type != SSH_ED25519 {
|
if !type_ok || string(key_type) != SSH_ED25519 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pk_data, pk_ok := read_wire_string(decoded, &offset)
|
pk_data, pk_ok := read_wire_string(&buf)
|
||||||
if !pk_ok || len(pk_data) != 32 {
|
if !pk_ok || len(pk_data) != 32 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mem.copy_non_overlapping(&pub[0], raw_data(pk_data), 32)
|
mem.copy_non_overlapping(&pub[0], &pk_data[0], 32)
|
||||||
|
|
||||||
ok = true
|
ok = true
|
||||||
return
|
return
|
||||||
@@ -91,81 +91,63 @@ parse_ssh_private_key :: proc(priv_path: string) -> (kp: Ed25519Keypair, ok: boo
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
offset := len(magic)
|
buf := decoded[len(magic):]
|
||||||
|
|
||||||
ciphername, cipher_ok := read_wire_string(decoded, &offset)
|
ciphername, cipher_ok := read_wire_string(&buf)
|
||||||
if !cipher_ok || ciphername != "none" {
|
if !cipher_ok || string(ciphername) != "none" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
kdfname, kdf_ok := read_wire_string(decoded, &offset)
|
kdfname, kdf_ok := read_wire_string(&buf)
|
||||||
if !kdf_ok || kdfname != "none" {
|
if !kdf_ok || string(kdfname) != "none" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, opts_ok := read_wire_string(decoded, &offset)
|
_, opts_ok := read_wire_string(&buf)
|
||||||
if !opts_ok {
|
if !opts_ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if offset + 4 > len(decoded) {
|
num_keys, nkeys_ok := read_wire_u32(&buf)
|
||||||
|
if !nkeys_ok || num_keys != 1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
num_keys := endian.get_u32(decoded[offset:offset + 4], .Big) or_return
|
_, pub_blob_ok := read_wire_string(&buf)
|
||||||
offset += 4
|
|
||||||
|
|
||||||
if num_keys != 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, pub_blob_ok := read_wire_string(decoded, &offset)
|
|
||||||
if !pub_blob_ok {
|
if !pub_blob_ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
priv_blob, priv_blob_ok := read_wire_string(decoded, &offset)
|
priv_blob, priv_blob_ok := read_wire_string(&buf)
|
||||||
if !priv_blob_ok {
|
if !priv_blob_ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
inner_offset := 0
|
inner := priv_blob
|
||||||
if inner_offset + 8 > len(priv_blob) {
|
|
||||||
|
check1, c1_ok := read_wire_u32(&inner)
|
||||||
|
check2, c2_ok := read_wire_u32(&inner)
|
||||||
|
if !c1_ok || !c2_ok || check1 != check2 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
check1 := endian.get_u32(
|
priv_type, type_ok := read_wire_string(&inner)
|
||||||
transmute([]u8)(priv_blob)[inner_offset:inner_offset + 4],
|
if !type_ok || string(priv_type) != SSH_ED25519 {
|
||||||
.Big,
|
|
||||||
) or_return
|
|
||||||
inner_offset += 4
|
|
||||||
check2 := endian.get_u32(
|
|
||||||
transmute([]u8)(priv_blob)[inner_offset:inner_offset + 4],
|
|
||||||
.Big,
|
|
||||||
) or_return
|
|
||||||
inner_offset += 4
|
|
||||||
|
|
||||||
if check1 != check2 {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
priv_type, type_ok := read_wire_string(transmute([]u8)priv_blob, &inner_offset)
|
pub_wire, pub_ok := read_wire_string(&inner)
|
||||||
if !type_ok || priv_type != SSH_ED25519 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
pub_wire, pub_ok := read_wire_string(transmute([]u8)priv_blob, &inner_offset)
|
|
||||||
if !pub_ok || len(pub_wire) != 32 {
|
if !pub_ok || len(pub_wire) != 32 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mem.copy_non_overlapping(&kp.Public[0], raw_data(pub_wire), 32)
|
mem.copy_non_overlapping(&kp.Public[0], &pub_wire[0], 32)
|
||||||
|
|
||||||
priv_wire, priv_ok := read_wire_string(transmute([]u8)priv_blob, &inner_offset)
|
priv_wire, priv_ok := read_wire_string(&inner)
|
||||||
if !priv_ok || len(priv_wire) != 64 {
|
if !priv_ok || len(priv_wire) != 64 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mem.copy_non_overlapping(&kp.Private[0], raw_data(priv_wire), 32)
|
mem.copy_non_overlapping(&kp.Private[0], &priv_wire[0], 32)
|
||||||
|
|
||||||
ok = true
|
ok = true
|
||||||
return
|
return
|
||||||
@@ -182,19 +164,22 @@ is_ed25519_key :: proc(
|
|||||||
return ok, nil
|
return ok, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
read_wire_string :: proc(data: []u8, offset: ^int) -> (s: string, ok: bool) {
|
read_wire_string :: proc(data: ^[]u8) -> (s: []u8, ok: bool) {
|
||||||
if offset^ + 4 > len(data) {
|
if len(data^) < 4 do return
|
||||||
return
|
length := endian.get_u32(data^[:4], .Big) or_return
|
||||||
}
|
data^ = data^[4:]
|
||||||
length := endian.get_u32(data[offset^:offset^ + 4], .Big) or_return
|
|
||||||
offset^ += 4
|
|
||||||
|
|
||||||
if offset^ + int(length) > len(data) {
|
if len(data^) < int(length) do return
|
||||||
return
|
s = data^[:int(length)]
|
||||||
}
|
data^ = data^[int(length):]
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
s = string(data[offset^:offset^ + int(length)])
|
read_wire_u32 :: proc(data: ^[]u8) -> (v: u32, ok: bool) {
|
||||||
offset^ += int(length)
|
if len(data^) < 4 do return
|
||||||
|
v = endian.get_u32(data^[:4], .Big) or_return
|
||||||
|
data^ = data^[4:]
|
||||||
ok = true
|
ok = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -52,15 +52,15 @@ test_private_key_pub_matches_public_key :: proc(t: ^testing.T) {
|
|||||||
@(test)
|
@(test)
|
||||||
test_read_wire_string :: proc(t: ^testing.T) {
|
test_read_wire_string :: proc(t: ^testing.T) {
|
||||||
data := []u8{0, 0, 0, 5, u8('h'), u8('e'), u8('l'), u8('l'), u8('o'), 0, 0, 0, 0}
|
data := []u8{0, 0, 0, 5, u8('h'), u8('e'), u8('l'), u8('l'), u8('o'), 0, 0, 0, 0}
|
||||||
offset := 0
|
|
||||||
|
|
||||||
s, ok := read_wire_string(data, &offset)
|
buf := data
|
||||||
|
s, ok := read_wire_string(&buf)
|
||||||
testing.expect(t, ok, "expected read_wire_string to succeed")
|
testing.expect(t, ok, "expected read_wire_string to succeed")
|
||||||
testing.expect_value(t, s, "hello")
|
testing.expect_value(t, string(s), "hello")
|
||||||
testing.expect_value(t, offset, 9)
|
testing.expect_value(t, len(data) - len(buf), 9)
|
||||||
|
|
||||||
s2, ok2 := read_wire_string(data, &offset)
|
s2, ok2 := read_wire_string(&buf)
|
||||||
testing.expect(t, ok2, "expected second read to succeed")
|
testing.expect(t, ok2, "expected second read to succeed")
|
||||||
testing.expect_value(t, s2, "")
|
testing.expect_value(t, len(s2), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ test_ansi_aware_width_multiple_escape_sequences :: proc(t: ^testing.T) {
|
|||||||
colorize(.Heading, "b", disable = false),
|
colorize(.Heading, "b", disable = false),
|
||||||
colorize(.Heading, "c", disable = false),
|
colorize(.Heading, "c", disable = false),
|
||||||
},
|
},
|
||||||
|
context.temp_allocator,
|
||||||
)
|
)
|
||||||
testing.expect_value(t, ansi_aware_width(colored), 3)
|
testing.expect_value(t, ansi_aware_width(colored), 3)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user