mirror of
https://github.com/sbrow/envr.git
synced 2026-10-25 08:41:55 -04:00
158 lines
4.2 KiB
Odin
158 lines
4.2 KiB
Odin
package main
|
|
|
|
import "core:fmt"
|
|
import "core:io"
|
|
import "core:os"
|
|
import "core:path/filepath"
|
|
import "core:strconv"
|
|
import "core:strings"
|
|
import "core:terminal"
|
|
|
|
cmd_view :: proc(cmd: ^Command) -> bool {
|
|
if len(cmd.args) > 1 {
|
|
print_command_help(cmd)
|
|
return false
|
|
}
|
|
|
|
path := len(cmd.args) == 1 ? cmd.args[0] : ""
|
|
|
|
db := db_open(cmd.flags.config_file) or_return
|
|
defer db_close(&db)
|
|
|
|
files := db_list(&db) or_return
|
|
paths := make([]string, len(files), context.temp_allocator)
|
|
for file, i in files do paths[i] = file.path
|
|
resolved_path := choose_view_path(path, paths, cmd.err) or_return
|
|
|
|
file := db_fetch(&db, resolved_path) or_return
|
|
|
|
if cmd.flags.stdout || !terminal.is_terminal(os.stdout) {
|
|
fmt.wprintf(cmd.out, "%s", file.contents, flush = false)
|
|
return true
|
|
}
|
|
|
|
page_file_contents(file.contents, cmd.err)
|
|
return true
|
|
}
|
|
|
|
choose_view_path :: proc(path: string, paths: []string, err_out: io.Writer) -> (string, bool) {
|
|
if len(strings.trim_space(path)) > 0 {
|
|
resolved, _, _, is_remote, path_ok := normalize_remote_identity(path)
|
|
if is_remote && !path_ok do return "", false
|
|
if !is_remote {
|
|
abs_path, abs_err := filepath.abs(path, context.temp_allocator)
|
|
if abs_err == nil do resolved = abs_path
|
|
}
|
|
for stored in paths {
|
|
if stored == resolved do return stored, true
|
|
}
|
|
if number, number_ok := strconv.parse_int(path, 10);
|
|
number_ok && number > 0 && number <= len(paths) {
|
|
return paths[number - 1], true
|
|
}
|
|
}
|
|
|
|
matches := matching_view_paths(path, paths)
|
|
if len(matches) == 0 {
|
|
fmt.wprintf(err_out, "No backed-up file matches: %s\n", path, flush = false)
|
|
return "", false
|
|
}
|
|
if len(path) > 0 && len(matches) == 1 {
|
|
return matches[0], true
|
|
}
|
|
if !terminal.is_terminal(os.stdin) || !terminal.is_terminal(os.stderr) {
|
|
fmt.wprintln(
|
|
err_out,
|
|
"An interactive terminal is required to choose a backup; pass a more specific path.",
|
|
flush = false,
|
|
)
|
|
return "", false
|
|
}
|
|
selected := single_select("Select a backed-up file:", matches[:])
|
|
if selected < 0 {
|
|
return "", false
|
|
}
|
|
return matches[selected], true
|
|
}
|
|
|
|
matching_view_paths :: proc(path: string, paths: []string) -> [dynamic]string {
|
|
matches := make([dynamic]string, 0, len(paths), context.temp_allocator)
|
|
needle := strings.to_lower(path, context.temp_allocator)
|
|
for stored in paths {
|
|
if strings.contains(strings.to_lower(stored, context.temp_allocator), needle) {
|
|
append(&matches, stored)
|
|
}
|
|
}
|
|
return matches
|
|
}
|
|
|
|
page_file_contents :: proc(contents: string, err_out: io.Writer) {
|
|
pager := os.get_env("PAGER", context.temp_allocator)
|
|
if len(strings.trim_space(pager)) == 0 {
|
|
when ODIN_OS == .Windows {
|
|
pager = "more"
|
|
} else {
|
|
pager = "less"
|
|
}
|
|
}
|
|
|
|
parts := strings.split(strings.trim_space(pager), " ", context.temp_allocator)
|
|
command := make([dynamic]string, 0, len(parts) + 1, context.temp_allocator)
|
|
for part in parts {
|
|
if len(part) > 0 {
|
|
append(&command, part)
|
|
}
|
|
}
|
|
if len(command) == 0 {
|
|
append(&command, "less")
|
|
}
|
|
when ODIN_OS == .Windows {
|
|
if command[0] != "more" do append(&command, "-")
|
|
} else {
|
|
append(&command, "-")
|
|
}
|
|
|
|
read_end, write_end, pipe_err := os.pipe()
|
|
if pipe_err != nil {
|
|
fmt.wprintf(err_out, "Error creating pager pipe: %v\n", pipe_err, flush = false)
|
|
return
|
|
}
|
|
defer os.close(write_end)
|
|
|
|
desc := os.Process_Desc {
|
|
command = command[:],
|
|
stdin = read_end,
|
|
stdout = os.stdout,
|
|
stderr = os.stderr,
|
|
}
|
|
process, start_err := os.process_start(desc)
|
|
os.close(read_end)
|
|
if start_err != nil {
|
|
fmt.wprintf(err_out, "Error starting pager %q: %v\n", command[0], start_err, flush = false)
|
|
return
|
|
}
|
|
|
|
remaining := transmute([]u8)contents
|
|
write_err: os.Error
|
|
for len(remaining) > 0 {
|
|
written, err := os.write(write_end, remaining)
|
|
if err != nil {
|
|
write_err = err
|
|
break
|
|
}
|
|
if written == 0 do break
|
|
remaining = remaining[written:]
|
|
}
|
|
os.close(write_end)
|
|
state, wait_err := os.process_wait(process)
|
|
if write_err != nil {
|
|
fmt.wprintf(err_out, "Error writing to pager: %v\n", write_err, flush = false)
|
|
}
|
|
if wait_err != nil {
|
|
fmt.wprintf(err_out, "Error waiting for pager: %v\n", wait_err, flush = false)
|
|
} else if state.exit_code != 0 {
|
|
fmt.wprintf(err_out, "Pager exited with status %d\n", state.exit_code, flush = false)
|
|
}
|
|
}
|
|
|