Couple cli and shell
This commit is contained in:
parent
a3e9ee9807
commit
275e5c3f7d
3 changed files with 142 additions and 135 deletions
217
cli/cli.odin
217
cli/cli.odin
|
|
@ -1,5 +1,7 @@
|
||||||
package cli
|
package cli
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:log"
|
import "core:log"
|
||||||
|
|
@ -16,55 +18,63 @@ INPUT_MAX :: 4096
|
||||||
HISTORY_MAX :: 2048
|
HISTORY_MAX :: 2048
|
||||||
HISTORY_FILE :: "skal.history"
|
HISTORY_FILE :: "skal.history"
|
||||||
|
|
||||||
Term :: struct {
|
History :: struct {
|
||||||
input_buffer: [dynamic]rune,
|
data: [dynamic][]rune,
|
||||||
pos: i32,
|
file: string,
|
||||||
min_pos: i32,
|
maybe_idx: Maybe(i32),
|
||||||
history: [dynamic][]rune,
|
maybe_cached_input: Maybe([]rune),
|
||||||
history_pos: Maybe(i32),
|
|
||||||
history_file: string,
|
|
||||||
written_line: Maybe([]rune),
|
|
||||||
orig_mode: posix.termios
|
|
||||||
}
|
}
|
||||||
|
|
||||||
term := Term{pos = 0, min_pos = 0, history_pos = nil, written_line = nil}
|
Input :: struct {
|
||||||
|
buffer: [dynamic]rune,
|
||||||
|
prompt_size: i32,
|
||||||
|
cursor_offset: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
Term :: struct {
|
||||||
|
orig_mode: posix.termios,
|
||||||
|
input: Input,
|
||||||
|
history: History,
|
||||||
|
}
|
||||||
|
|
||||||
|
term := Term{}
|
||||||
|
|
||||||
init_cli :: proc () {
|
init_cli :: proc () {
|
||||||
mem_err: mem.Allocator_Error
|
mem_err: mem.Allocator_Error
|
||||||
term.input_buffer, mem_err = make([dynamic]rune, context.allocator) //todo deinit
|
term.input.buffer, mem_err = make([dynamic]rune, context.allocator)
|
||||||
log.assertf(mem_err == nil, "Memory allocation failed")
|
log.assertf(mem_err == nil, "Memory allocation failed")
|
||||||
|
|
||||||
term.history, mem_err = make([dynamic][]rune, context.allocator) //todo deinit
|
term.history.data, mem_err = make([dynamic][]rune, context.allocator)
|
||||||
log.assertf(mem_err == nil, "Memory allocation failed")
|
log.assertf(mem_err == nil, "Memory allocation failed")
|
||||||
|
|
||||||
home_path := string(posix.getenv("HOME"))
|
home_path := string(posix.getenv("HOME"))
|
||||||
log.assertf(home_path != "", "Home path not found")
|
log.assertf(home_path != "", "Home path not found")
|
||||||
|
|
||||||
term.history_file = filepath.join({home_path, HISTORY_FILE}, context.allocator) //todo deinit
|
term.history.file = filepath.join({home_path, HISTORY_FILE}, context.allocator)
|
||||||
log.assertf(mem_err == nil, "Memory allocation failed")
|
log.assertf(mem_err == nil, "Memory allocation failed")
|
||||||
|
|
||||||
if !os.exists(term.history_file) {
|
USER_PERMISSIONS :: 0o600
|
||||||
handle, ferr := os.open(term.history_file, os.O_CREATE | os.O_RDWR, 0o600)
|
if !os.exists(term.history.file) {
|
||||||
|
handle, ferr := os.open(term.history.file, os.O_CREATE | os.O_RDWR, USER_PERMISSIONS)
|
||||||
log.assertf(ferr == nil, "Failed to create history file")
|
log.assertf(ferr == nil, "Failed to create history file")
|
||||||
os.close(handle)
|
os.close(handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
history_content, err := os.read_entire_file_from_filename_or_err(term.history_file, context.allocator)
|
history_content, err := os.read_entire_file_from_filename_or_err(term.history.file, context.allocator)
|
||||||
if err != nil {
|
log.assertf(err == nil, "Failed to read history file")
|
||||||
fmt.printfln("skal: Couldn't read history file: %s, because of: %s, continuing without history...", term.history_file, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer delete(history_content)
|
defer delete(history_content)
|
||||||
|
|
||||||
history_content_it := string(history_content)
|
history_content_it := string(history_content)
|
||||||
for line in strings.split_lines_iterator(&history_content_it) {
|
for line in strings.split_lines_iterator(&history_content_it) {
|
||||||
append(&term.history, utf8.string_to_runes(line))
|
append(&term.history.data, utf8.string_to_runes(line))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit_cli :: proc () {
|
deinit_cli :: proc () {
|
||||||
delete(term.input_buffer)
|
delete(term.input.buffer)
|
||||||
|
delete(term.history.data)
|
||||||
|
delete(term.history.file)
|
||||||
}
|
}
|
||||||
|
|
||||||
disable_raw_mode :: proc "c" () {
|
disable_raw_mode :: proc "c" () {
|
||||||
|
|
@ -109,17 +119,11 @@ get_prompt_prefix :: proc() -> string {
|
||||||
return prompt
|
return prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
|
||||||
reset_prompt :: proc() {
|
|
||||||
clear(&term.input_buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
handle_esc_seq :: proc(in_stream: ^io.Stream) {
|
handle_esc_seq :: proc(in_stream: ^io.Stream) {
|
||||||
NAV_SEQ_START :: '['
|
NAV_SEQ_START :: '['
|
||||||
|
|
||||||
next_rn, _, err := io.read_rune(in_stream^)
|
next_rn, _, err := io.read_rune(in_stream^)
|
||||||
|
|
||||||
log.assertf(err == nil, "Couldn't read from stdin")
|
log.assertf(err == nil, "Couldn't read from stdin")
|
||||||
|
|
||||||
if next_rn == NAV_SEQ_START do handle_nav(in_stream)
|
if next_rn == NAV_SEQ_START do handle_nav(in_stream)
|
||||||
|
|
@ -137,132 +141,155 @@ handle_nav :: proc(in_stream: ^io.Stream) {
|
||||||
|
|
||||||
switch rn {
|
switch rn {
|
||||||
case UP:
|
case UP:
|
||||||
if term.history_pos == nil {
|
cur_history_idx, scrolling_history := term.history.maybe_idx.?
|
||||||
term.written_line = slice.clone(term.input_buffer[:], context.temp_allocator)
|
if !scrolling_history {
|
||||||
|
term.history.maybe_cached_input = slice.clone(term.input.buffer[:], context.temp_allocator)
|
||||||
|
} else if cur_history_idx == 0 {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for len(term.input_buffer) > 0 {
|
pop_runes(i32(len(term.input.buffer)))
|
||||||
backwards()
|
|
||||||
}
|
|
||||||
history_pos := term.history_pos.? or_else i32(len(term.history))
|
|
||||||
history_pos = max(history_pos-1, 0)
|
|
||||||
term.history_pos = history_pos
|
|
||||||
|
|
||||||
new_input := term.history[history_pos]
|
history_idx := term.history.maybe_idx.? or_else i32(len(term.history.data))
|
||||||
append(&term.input_buffer, ..new_input[:])
|
history_idx -= 1
|
||||||
fmt.printf("%s", term.input_buffer[:])
|
term.history.maybe_idx = history_idx
|
||||||
|
|
||||||
|
new_input := term.history.data[history_idx]
|
||||||
|
write_runes(new_input[:])
|
||||||
|
|
||||||
term.pos = term.min_pos + i32(len(term.input_buffer))
|
|
||||||
case DOWN:
|
case DOWN:
|
||||||
history_pos, ok := term.history_pos.?
|
history_idx, scrolling_history := term.history.maybe_idx.?
|
||||||
if !ok do return
|
if !scrolling_history do return
|
||||||
|
|
||||||
for len(term.input_buffer) > 0 {
|
pop_runes(i32(len(term.input.buffer)))
|
||||||
backwards()
|
|
||||||
}
|
|
||||||
|
|
||||||
history_pos += 1
|
history_idx += 1
|
||||||
history_pos_max := i32(len(term.history)-1)
|
history_len := i32(len(term.history.data))
|
||||||
new_input: []rune
|
new_input: []rune
|
||||||
if history_pos > history_pos_max {
|
|
||||||
term.history_pos = nil
|
if history_idx >= history_len {
|
||||||
history_pos = history_pos_max
|
term.history.maybe_idx = nil
|
||||||
new_input, ok = term.written_line.?; assert(ok)
|
history_idx = history_len
|
||||||
term.written_line = nil
|
|
||||||
|
ok: bool
|
||||||
|
new_input, ok = term.history.maybe_cached_input.?; assert(ok)
|
||||||
|
term.history.maybe_cached_input = nil
|
||||||
} else {
|
} else {
|
||||||
term.history_pos = history_pos
|
term.history.maybe_idx = history_idx
|
||||||
new_input = term.history[history_pos]
|
new_input = term.history.data[history_idx]
|
||||||
}
|
}
|
||||||
|
write_runes(new_input[:])
|
||||||
|
|
||||||
append(&term.input_buffer, ..new_input[:])
|
|
||||||
fmt.printf("%s", term.input_buffer[:])
|
|
||||||
|
|
||||||
term.pos = term.min_pos + i32(len(term.input_buffer))
|
|
||||||
case RIGHT:
|
case RIGHT:
|
||||||
if i32(len(term.input_buffer)) + term.min_pos > term.pos {
|
if term.input.cursor_offset > 0 {
|
||||||
fmt.print("\x1b[C")
|
fmt.print("\x1b[C")
|
||||||
term.pos += 1
|
term.input.cursor_offset -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
case LEFT:
|
case LEFT:
|
||||||
if term.min_pos < term.pos {
|
if term.input.cursor_offset < i32(len(term.input.buffer)) {
|
||||||
fmt.print("\x1b[D")
|
fmt.print("\x1b[D")
|
||||||
term.pos -= 1
|
term.input.cursor_offset += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
write_runes :: proc(rns: []rune) {
|
||||||
|
append(&term.input.buffer, ..rns[:])
|
||||||
|
fmt.print(utf8.runes_to_string(rns[:]))
|
||||||
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
backwards :: proc() {
|
write_rune :: proc(rn: rune) {
|
||||||
|
append(&term.input.buffer, rn)
|
||||||
|
fmt.print(rn)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
pop_runes :: proc(amount: i32) {
|
||||||
DELETE_AND_REVERSE :: "\b \b"
|
DELETE_AND_REVERSE :: "\b \b"
|
||||||
|
log.assertf(amount <= i32(len(term.input.buffer)), "Cannot remove more runes that written in buffer")
|
||||||
|
|
||||||
if term.pos == term.min_pos do return
|
start_idx := i32(len(term.input.buffer)) - amount
|
||||||
|
runes := term.input.buffer[start_idx:]
|
||||||
last_rune := term.input_buffer[len(term.input_buffer)-1]
|
_, _, width := utf8.grapheme_count(utf8.runes_to_string(runes, context.temp_allocator))
|
||||||
_, _, width := utf8.grapheme_count(utf8.runes_to_string({last_rune}, context.temp_allocator))
|
resize(&term.input.buffer, i32(len(term.input.buffer)) - amount)
|
||||||
resize(&term.input_buffer, len(term.input_buffer)-1)
|
|
||||||
|
|
||||||
for _ in 0..<width {
|
for _ in 0..<width {
|
||||||
fmt.print(DELETE_AND_REVERSE)
|
fmt.print(DELETE_AND_REVERSE)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
term.pos -=1
|
@(private)
|
||||||
|
pop_rune_at_cursor :: proc() {
|
||||||
|
DELETE_AND_REVERSE :: "\b \b"
|
||||||
|
|
||||||
|
if len(term.input.buffer) == 0 do return
|
||||||
|
|
||||||
|
last_rune := term.input.buffer[len(term.input.buffer)-1]
|
||||||
|
_, _, width := utf8.grapheme_count(utf8.runes_to_string({last_rune}, context.temp_allocator))
|
||||||
|
resize(&term.input.buffer, len(term.input.buffer)-1)
|
||||||
|
|
||||||
|
for _ in 0..<width {
|
||||||
|
fmt.print(DELETE_AND_REVERSE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
append_history_file :: proc(data: []rune) {
|
append_history_file :: proc(data: []rune) {
|
||||||
append(&term.history, slice.clone(data))
|
append(&term.history.data, slice.clone(data))
|
||||||
term.history_pos = nil
|
term.history.maybe_idx = nil
|
||||||
term.written_line = nil
|
term.history.maybe_cached_input = nil
|
||||||
|
|
||||||
fd, err := os.open(term.history_file, os.O_WRONLY | os.O_APPEND | os.O_CREATE)
|
fd, err := os.open(term.history.file, os.O_WRONLY | os.O_APPEND | os.O_CREATE)
|
||||||
|
log.assertf(err == nil, "Failed to write to history file")
|
||||||
defer os.close(fd)
|
defer os.close(fd)
|
||||||
if err != nil {
|
|
||||||
fmt.eprintfln("skal: Couldn't write to history file")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
history_data := strings.concatenate({utf8.runes_to_string(data[:]), "\n"}, context.temp_allocator)
|
history_data := strings.concatenate({utf8.runes_to_string(data[:]), "\n"}, context.temp_allocator)
|
||||||
_, werr := os.write_string(fd, history_data)
|
_, werr := os.write_string(fd, history_data)
|
||||||
log.ensuref(werr == nil, "skal: Couldn't write to history file")
|
log.assertf(werr == nil, "Failed to write to history file")
|
||||||
}
|
}
|
||||||
|
|
||||||
skip_and_clear :: proc() {
|
clear_input :: proc() {
|
||||||
reset_prompt()
|
clear(&term.input.buffer)
|
||||||
prompt_prefix := get_prompt_prefix()
|
prompt_prefix := get_prompt_prefix()
|
||||||
fmt.printf("%s", prompt_prefix)
|
fmt.printf("%s", prompt_prefix)
|
||||||
|
|
||||||
term.min_pos = i32(len(prompt_prefix))
|
term.input.prompt_size = i32(len(prompt_prefix))
|
||||||
term.pos = term.min_pos
|
|
||||||
}
|
}
|
||||||
|
|
||||||
run_prompt :: proc() -> string {
|
skip_and_clear :: proc() {
|
||||||
enable_raw_mode()
|
fmt.println()
|
||||||
|
clear_input()
|
||||||
|
}
|
||||||
|
|
||||||
skip_and_clear()
|
run_prompt :: proc() -> Maybe(string) {
|
||||||
|
enable_raw_mode()
|
||||||
|
clear_input()
|
||||||
|
|
||||||
in_stream := os.stream_from_handle(os.stdin)
|
in_stream := os.stream_from_handle(os.stdin)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
rn, size, err := io.read_rune(in_stream)
|
rn, _, err := io.read_rune(in_stream)
|
||||||
log.assertf(err == nil, "Couldn't read from stdin")
|
log.assertf(err == nil, "Couldn't read from stdin")
|
||||||
|
|
||||||
switch rn {
|
switch rn {
|
||||||
case '\n':
|
case '\n':
|
||||||
fmt.println()
|
fmt.println()
|
||||||
if len(term.input_buffer) == 0 do return ""
|
if len(term.input.buffer) == 0 do return ""
|
||||||
|
|
||||||
append_history_file(term.input_buffer[:])
|
append_history_file(term.input.buffer[:])
|
||||||
input := utf8.runes_to_string(term.input_buffer[:], context.temp_allocator)
|
input := utf8.runes_to_string(term.input.buffer[:], context.temp_allocator)
|
||||||
|
|
||||||
return input
|
return input
|
||||||
|
|
||||||
case '\f':
|
case '\f':
|
||||||
fmt.println()
|
|
||||||
return "clear" // This is a terminal history wipe, usually isn't
|
return "clear" // This is a terminal history wipe, usually isn't
|
||||||
|
|
||||||
case '\u007f':
|
case '\u007f':
|
||||||
backwards()
|
pop_rune_at_cursor()
|
||||||
|
|
||||||
case '\x1b':
|
case '\x1b':
|
||||||
handle_esc_seq(&in_stream)
|
handle_esc_seq(&in_stream)
|
||||||
|
|
@ -270,14 +297,10 @@ run_prompt :: proc() -> string {
|
||||||
case utf8.RUNE_EOF:
|
case utf8.RUNE_EOF:
|
||||||
fallthrough
|
fallthrough
|
||||||
case '\x04':
|
case '\x04':
|
||||||
fmt.println()
|
return nil
|
||||||
skip_and_clear()
|
|
||||||
|
|
||||||
case: // Bug: if user moved to earlier character
|
case: // Bug: if user moved to earlier character
|
||||||
append(&term.input_buffer, rn)
|
write_rune(rn)
|
||||||
term.pos += 1
|
|
||||||
|
|
||||||
fmt.print(rn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
main.odin
11
main.odin
|
|
@ -26,16 +26,23 @@ main :: proc() {
|
||||||
|
|
||||||
shell.init_shell()
|
shell.init_shell()
|
||||||
cli.init_cli()
|
cli.init_cli()
|
||||||
|
defer cli.deinit_cli()
|
||||||
|
|
||||||
for true {
|
for true {
|
||||||
input := cli.run_prompt()
|
defer free_all(context.temp_allocator)
|
||||||
|
|
||||||
|
maybe_input := cli.run_prompt()
|
||||||
|
input, ok := maybe_input.?
|
||||||
|
if !ok {
|
||||||
|
fmt.println()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
cmd_seq, parser_err := parser.parse(input)
|
cmd_seq, parser_err := parser.parse(input)
|
||||||
log.assertf(parser_err == nil, "Could not parse input")
|
log.assertf(parser_err == nil, "Could not parse input")
|
||||||
|
|
||||||
stop := shell.execute(&cmd_seq)
|
stop := shell.execute(&cmd_seq)
|
||||||
|
|
||||||
free_all(context.temp_allocator)
|
|
||||||
if stop == .Stop do break
|
if stop == .Stop do break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import "core:os"
|
||||||
import "core:log"
|
import "core:log"
|
||||||
|
|
||||||
import "../parser"
|
import "../parser"
|
||||||
|
import "../cli"
|
||||||
|
|
||||||
ShellState :: enum {
|
ShellState :: enum {
|
||||||
Continue,
|
Continue,
|
||||||
|
|
@ -16,20 +17,20 @@ ShellState :: enum {
|
||||||
}
|
}
|
||||||
|
|
||||||
maybe_foreground_pid: Maybe(posix.pid_t) = nil
|
maybe_foreground_pid: Maybe(posix.pid_t) = nil
|
||||||
ctx: runtime.Context
|
|
||||||
|
|
||||||
handle_ctrl_c :: proc"c"(sig: posix.Signal) {
|
handle_ctrl_c :: proc"c"(sig: posix.Signal) {
|
||||||
|
context = runtime.default_context()
|
||||||
foreground_pid, foreground_running := maybe_foreground_pid.?
|
foreground_pid, foreground_running := maybe_foreground_pid.?
|
||||||
|
|
||||||
if foreground_running {
|
if foreground_running {
|
||||||
posix.kill(foreground_pid, .SIGINT)
|
posix.kill(foreground_pid, .SIGINT)
|
||||||
} else {
|
|
||||||
// Clear prompt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cli.skip_and_clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_backround_process :: proc"c"(sig: posix.Signal) {
|
handle_backround_process :: proc"c"(sig: posix.Signal) {
|
||||||
context = ctx
|
context = runtime.default_context()
|
||||||
foreground_pid, foreground_running := maybe_foreground_pid.?
|
foreground_pid, foreground_running := maybe_foreground_pid.?
|
||||||
|
|
||||||
if foreground_running do return
|
if foreground_running do return
|
||||||
|
|
@ -49,36 +50,10 @@ handle_backround_process :: proc"c"(sig: posix.Signal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
init_shell :: proc() {
|
init_shell :: proc() {
|
||||||
ctx = context
|
|
||||||
posix.signal(.SIGINT, handle_ctrl_c)
|
posix.signal(.SIGINT, handle_ctrl_c)
|
||||||
posix.signal(.SIGCHLD, handle_backround_process)
|
posix.signal(.SIGCHLD, handle_backround_process)
|
||||||
}
|
}
|
||||||
|
|
||||||
get_prompt :: proc() -> string {
|
|
||||||
dir := os.get_current_directory(context.temp_allocator)
|
|
||||||
home := string(posix.getenv("HOME"))
|
|
||||||
|
|
||||||
if strings.contains(dir, home) {
|
|
||||||
dir, _ = strings.replace(dir, home, "~", 1, context.temp_allocator)
|
|
||||||
}
|
|
||||||
|
|
||||||
uid := posix.getuid()
|
|
||||||
pw := posix.getpwuid(uid)
|
|
||||||
|
|
||||||
user: string
|
|
||||||
if pw == nil {
|
|
||||||
user = "Skal"
|
|
||||||
} else {
|
|
||||||
user = string(pw.pw_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt_parts := []string {user, " :: ", dir, " » "}
|
|
||||||
prompt, err := strings.concatenate(prompt_parts[:], context.temp_allocator)
|
|
||||||
log.assertf(err == nil, "Memory allocation failed")
|
|
||||||
|
|
||||||
return prompt
|
|
||||||
}
|
|
||||||
|
|
||||||
pipe_command :: proc(pipe_seq: ^parser.PipeSequence, maybe_fd: Maybe(posix.FD), idx: int) {
|
pipe_command :: proc(pipe_seq: ^parser.PipeSequence, maybe_fd: Maybe(posix.FD), idx: int) {
|
||||||
cmd := pipe_seq.commands[idx]
|
cmd := pipe_seq.commands[idx]
|
||||||
|
|
||||||
|
|
@ -169,10 +144,10 @@ pipe_command :: proc(pipe_seq: ^parser.PipeSequence, maybe_fd: Maybe(posix.FD),
|
||||||
change_dir :: proc(cmd: ^parser.Command) {
|
change_dir :: proc(cmd: ^parser.Command) {
|
||||||
if len(cmd.args) > 0 {
|
if len(cmd.args) > 0 {
|
||||||
ch_status := posix.chdir(cmd.args[0])
|
ch_status := posix.chdir(cmd.args[0])
|
||||||
if ch_status != .OK do fmt.printf("cd: No such file or directory: %s\n", cmd.args[0])
|
if ch_status != .OK do fmt.printfln("cd: No such file or directory: %s", cmd.args[0])
|
||||||
} else {
|
} else {
|
||||||
ch_status := posix.chdir(posix.getenv("HOME"))
|
ch_status := posix.chdir(posix.getenv("HOME"))
|
||||||
if ch_status != .OK do fmt.printf("cd: No such file or directory: %s\n", posix.getenv("HOME"))
|
if ch_status != .OK do fmt.printfln("cd: No such file or directory: %s", posix.getenv("HOME"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,7 +194,9 @@ execute_cmd_seq :: proc(cmd_seq: ^parser.CommandSequence) {
|
||||||
}
|
}
|
||||||
|
|
||||||
execute :: proc(cmd_seq: ^parser.CommandSequence) -> ShellState {
|
execute :: proc(cmd_seq: ^parser.CommandSequence) -> ShellState {
|
||||||
if len(cmd_seq^.pipe_sequences) == 0 do return .Continue
|
if len(cmd_seq^.pipe_sequences) == 0 {
|
||||||
|
return .Continue
|
||||||
|
}
|
||||||
|
|
||||||
// if pipe sequence is above 0 it has at least one command
|
// if pipe sequence is above 0 it has at least one command
|
||||||
first_cmd := cmd_seq^.pipe_sequences[0].commands[0]
|
first_cmd := cmd_seq^.pipe_sequences[0].commands[0]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue