// Copyright 2012 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::c_str::ToCStr; use std::libc::{c_char, c_int}; use std::{local_data, str, rt}; use std::unstable::finally::Finally; pub mod rustrt { use std::libc::{c_char, c_int}; externfn!(fn linenoise(prompt: *c_char) -> *c_char) externfn!(fn linenoiseHistoryAdd(line: *c_char) -> c_int) externfn!(fn linenoiseHistorySetMaxLen(len: c_int) -> c_int) externfn!(fn linenoiseHistorySave(file: *c_char) -> c_int) externfn!(fn linenoiseHistoryLoad(file: *c_char) -> c_int) externfn!(fn linenoiseSetCompletionCallback(callback: extern "C" fn(*i8, *()))) externfn!(fn linenoiseAddCompletion(completions: *(), line: *c_char)) externfn!(fn rust_take_linenoise_lock()) externfn!(fn rust_drop_linenoise_lock()) } macro_rules! locked { ($expr:expr) => { { // FIXME #9105: can't use a static mutex in pure Rust yet. rustrt::rust_take_linenoise_lock(); let x = $expr; rustrt::rust_drop_linenoise_lock(); x } } } /// Add a line to history pub fn add_history(line: &str) -> bool { do line.with_c_str |buf| { unsafe { (locked!(rustrt::linenoiseHistoryAdd(buf))) == 1 as c_int } } } /// Set the maximum amount of lines stored pub fn set_history_max_len(len: int) -> bool { unsafe { (locked!(rustrt::linenoiseHistorySetMaxLen(len as c_int))) == 1 as c_int } } /// Save line history to a file pub fn save_history(file: &str) -> bool { do file.with_c_str |buf| { // 0 on success, -1 on failure unsafe { (locked!(rustrt::linenoiseHistorySave(buf))) == 0 as c_int } } } /// Load line history from a file pub fn load_history(file: &str) -> bool { do file.with_c_str |buf| { // 0 on success, -1 on failure unsafe { (locked!(rustrt::linenoiseHistoryLoad(buf))) == 0 as c_int } } } /// Print out a prompt and then wait for input and return it pub fn read(prompt: &str) -> Option<~str> { do prompt.with_c_str |buf| { let line = unsafe { locked!(rustrt::linenoise(buf)) }; if line.is_null() { None } else { unsafe { do (|| { Some(str::raw::from_c_str(line)) }).finally { // linenoise's return value is from strdup, so we // better not leak it. rt::global_heap::exchange_free(line); } } } } } /// The callback used to perform completions. pub trait CompletionCb { /// Performs a completion. fn complete(&self, line: ~str, suggestion: &fn(~str)); } local_data_key!(complete_key: @CompletionCb) /// Bind to the main completion callback in the current task. /// /// The completion callback should not call any `extra::rl` functions /// other than the closure that it receives as its second /// argument. Calling such a function will deadlock on the mutex used /// to ensure that the calls are thread-safe. pub unsafe fn complete(cb: @CompletionCb) { local_data::set(complete_key, cb); extern fn callback(line: *c_char, completions: *()) { do local_data::get(complete_key) |opt_cb| { // only fetch completions if a completion handler has been // registered in the current task. match opt_cb { None => {} Some(cb) => { unsafe { do cb.complete(str::raw::from_c_str(line)) |suggestion| { do suggestion.with_c_str |buf| { rustrt::linenoiseAddCompletion(completions, buf); } } } } } } } locked!(rustrt::linenoiseSetCompletionCallback(callback)); }