#ifndef LLSH_SHELL_H #define LLSH_SHELL_H #include "util.h" #include #include /* An interactive user shell running on a private PTY. llsh forwards raw * terminal input to the PTY and streams the PTY's output back to the real * terminal, so the shell's own line editor (Tab completion, history, …) is * used while in shell mode. Mirrors the Rust `InteractiveShell` in main.rs. * POSIX-only (forkpty/raw mode). */ typedef struct interactive_shell { int pid; /* shell PID; 0 once reaped */ int master; /* PTY master fd; -1 once closed */ int reader_fd; /* dup of master, owned by the reader thread */ pthread_t reader_th; pthread_mutex_t done_mu; /* guards reader_done */ bool reader_done; /* set by the reader thread on EOF */ xbuf outbuf; /* owned; output capture (shared with reader thread) */ pthread_mutex_t out_mu; /* guards outbuf */ unsigned long capture_cap; /* max bytes to capture (bounds memory) */ char *last_cwd; /* owned; last observed cwd (fallback after reap) */ } interactive_shell; /* Fork `$SHELL` (else `bash`) with `-i` on a private PTY and start a reader * thread that streams the shell's output to stdout (CRLF collapsed to LF). * Returns NULL on failure. */ interactive_shell *shell_spawn(void); /* Forward raw input bytes to the shell. 0 on success, -1 on error. */ int shell_write(const interactive_shell *s, const void *buf, size_t len); /* Resize the PTY to match the current terminal size. */ void shell_update_winsize(const interactive_shell *s); /* True once the shell has exited, its output is drained, and it has been * reaped. Cheap to call each loop iteration while in shell mode. */ bool shell_reap_if_dead(interactive_shell *s); /* The directory the shell ended in: a live `/proc//cwd` read while it * is still alive, falling back to the last observed directory once reaped. * Returns a freshly allocated string, or NULL when unknown. */ char *shell_final_cwd(const interactive_shell *s); /* Everything the shell has printed so far (owned copy), and reset the * capture. Thread-safe against the reader thread; returns an empty string * when nothing was captured. */ char *shell_captured(interactive_shell *s); /* Kill the shell (whole process group), close the PTY, and join the reader * thread. Safe to call on a fully-reaped shell. */ void shell_free(interactive_shell *s); #endif