extern crate libc;
extern crate tongue;
use std::io::{self, Write, BufWriter, Read, BufReader, BufRead};
use std::{mem, env, str};
use std::process::exit;
use std::fs::File;
use std::collections::HashMap;
use tongue::{lexer, parser, evaluator};
use tongue::config::Config;
extern "C" {
fn signal(sig: u32, cb: extern fn(u32)) -> fn(u32);
}
extern fn interrupt(_:u32) {
}
fn disable_sigint() {
let SIGINT = 2;
unsafe {
signal(SIGINT, interrupt);
}
}
fn tcgetattr() -> libc::termios {
let mut term: libc::termios;
unsafe {
term = mem::zeroed();
libc::tcgetattr(libc::STDIN_FILENO, &mut term);
}
term
}
fn tcsetattr(mut term: libc::termios) {
unsafe {
libc::tcsetattr(libc::STDIN_FILENO, libc::TCSAFLUSH, &mut term);
}
}
fn enable_raw_mode() {
unsafe {
let mut term: libc::termios = mem::zeroed();
libc::tcgetattr(libc::STDIN_FILENO, &mut term);
term.c_iflag &= !(libc::BRKINT | libc::ICRNL | libc::INPCK | libc::ISTRIP | libc::IXON);
term.c_oflag &= !(libc::OPOST);
term.c_cflag |= (libc::CS8);
term.c_lflag &= !(libc::ECHO | libc::ICANON | libc::IEXTEN | libc::ISIG);
libc::tcsetattr(libc::STDIN_FILENO, libc::TCSAFLUSH, &mut term);
}
}
struct CursorPosition {
x: usize,
y: usize,
}
fn get_cursor_position() -> CursorPosition {
let mut buf = [0; 32];
output("\x1b[6n");
read_stdin(&mut buf);
let buf = str::from_utf8(&buf[2 .. 32]).unwrap();
let index_end = buf.find('R').unwrap();
let index_middle = buf.find(';').unwrap();
let cursor_position = CursorPosition {
y: buf.get(0..index_middle)
.unwrap()
.parse::<usize>()
.unwrap(),
x: buf.get(index_middle+1..index_end)
.unwrap()
.parse::<usize>()
.unwrap(),
};
cursor_position
}
fn output(s: &str) {
let stdout = io::stdout();
let mut buf_writer = BufWriter::new(stdout.lock());
buf_writer.write(s.as_bytes());
buf_writer.flush();
}
const CtrlB: u8 = 2;
const CtrlC: u8 = 3;
const CtrlD: u8 = 4;
const CtrlF: u8 = 6;
const Tab: u8 = 9;
const Enter: u8 = 13;
const CtrlU: u8 = 21;
const Escape: u8 = 27;
const Backspace: u8 = 127;
fn read_stdin(mut buffer: &mut [u8]) {
let stdin = io::stdin();
let mut handle = stdin.lock();
handle.read(&mut buffer);
}
fn read_from_stdin(config: &mut Config, orig_term: libc::termios) {
let mut line = "".to_string();
let ps1 = " $ ";
output(ps1);
loop {
let mut buffer = [0; 4];
read_stdin(&mut buffer);
let c = buffer[0] as char;
let stdout = io::stdout();
let mut buf_writer = BufWriter::new(stdout.lock());
match buffer[0] {
CtrlB => {
if line.len() == 0 {
continue;
}
let cursor_position = get_cursor_position();
if cursor_position.x == ps1.len() {
continue;
}
output("\x1b[D");
}
CtrlC => {
}
CtrlD => {
if line.len() == 0 {
output("\r\n");
break;
}
}
CtrlF => {
if line.len() == 0 {
continue;
}
let cursor_position = get_cursor_position();
if cursor_position.x == ps1.len() {
continue;
}
output("\x1b[C");
}
Tab => {
}
Enter => {
output("\r\n");
tcsetattr(orig_term);
let tokens = lexer::tokenize(line.as_str());
let tree = parser::parse(tokens.clone());
evaluator::eval(tree, config);
enable_raw_mode();
line = "".to_string();
output(ps1);
}
CtrlU => {
output("\x1b[0K");
line = "".to_string();
}
Escape => {
;
}
Backspace => {
if line.len() == 0 {
continue;
}
let cursor_position = get_cursor_position();
output(format!("\x1b[{};0H", cursor_position.y).as_str());
output("\x1b[0K");
output(ps1);
line.remove(cursor_position.x-2-ps1.len());
output(line.as_str());
output(format!("\x1b[{};{}H", cursor_position.y, cursor_position.x-1).as_str());
}
_ => {
let cursor_position = get_cursor_position();
output(format!("\x1b[{};0H", cursor_position.y).as_str());
output("\x1b[0K");
output(ps1);
line.insert(cursor_position.x-1-ps1.len(), c);
output(line.as_str());
output(format!("\x1b[{};{}H", cursor_position.y, cursor_position.x+1).as_str());
}
}
}
}
fn read_rc(config: &mut Config) {
}
fn tongue_main() {
disable_sigint();
let orig_term = tcgetattr();
enable_raw_mode();
let config = &mut Config {
aliase: HashMap::new(),
variable: HashMap::new(),
home: String::new(),
};
match env::var("HOME") {
Ok(home) => config.home = home,
Err(e) => config.home = e.to_string(),
}
for argument in env::args() {
if argument == "--help" {
print!("tongue [option]\r\n");
tcsetattr(orig_term);
exit(0);
} else if argument == "--version" {
print!("{}", env!("CARGO_PKG_VERSION"));
tcsetattr(orig_term);
exit(0);
}
}
read_rc(config);
read_from_stdin(config, orig_term);
tcsetattr(orig_term);
}
fn main() {
tongue_main();
}