Readline 自定义完成器
Readline custom completer
我正在尝试用 Rust 编写一个 readline 自定义完成器(tab 完成)。我想我什么都弄清楚了,但是当我尝试 en vivo 时,它掉进了杂草中,再也没有回来。奇怪的是,当我直接从 main()
调用它时,我似乎得到了一个有效的指针。在这两种情况下,我都没有看到崩溃或恐慌。回溯输出在运行中不一致(它正忙着做某事)。也许一个线索是 gdb 指示传递给完成者的参数不正确(尽管我实际上并没有使用它们)。例如,回调后:
#2 0x00007f141f701272 in readlinetest::complete (text=0x7f141ff27d10 "", start=2704437, end=499122176) at src/main.rs:24
或者直接在main
:
中断点调用
#0 readlinetest::complete (text=0x555555559190 <complete::hcda8d6cb2ef52a1bKaa> "dH;$%p", start=0, end=0) at src/main.rs:21
我有 ABI 问题吗?似乎不太可能,函数签名也不复杂:(
这是一个小测试项目:Cargo.toml
:
[package]
name = "readlinetest"
version = "0.1.0"
authors = ["You <you@example.com>"]
[dependencies]
libc = "*"
readline = "*"
和main.rs
:
extern crate libc;
extern crate readline;
use libc::{c_char, c_int};
use std::ffi::CString;
use std::process::exit;
use std::ptr;
use std::str;
extern { fn puts(s: *const libc::c_char); }
#[link(name = "readline")]
// Define the global in libreadline that will point to our completion function.
extern {
static mut rl_completion_entry_function: extern fn(text: *const c_char,
start: c_int,
end: c_int) -> *const *const c_char;
}
// Our completion function. Returns two strings.
extern fn complete(text: *const c_char, start: c_int, end: c_int) -> *const *const c_char {
let _ = text; let _ = start; let _ = end;
let mut words:Vec<*const c_char> =
vec!(CString::new("one").unwrap(), CString::new("two").unwrap()).
iter().
map(|arg| arg.as_ptr()).
collect();
words.push(ptr::null()); // append null
words.as_ptr() as *const *const c_char
}
fn main() {
let words = complete(string_to_mut_c_char("hi"), 1, 2);
unsafe { puts(*words) } // prints "one"
//unsafe { puts((*words + ?)) } // not sure hot to get to "two"
unsafe { rl_completion_entry_function = complete }
// Loop until EOF: echo input to stdout
loop {
if let Ok(input) = readline::readline_bare(&CString::new("> ").unwrap()) {
let text = str::from_utf8(&input.to_bytes()).unwrap();
println!("{}", text);
} else { // EOF/^D
exit(0)
}
}
}
// Just for testing
fn string_to_mut_c_char(s: &str) -> *mut c_char {
let mut bytes = s.to_string().into_bytes(); // Vec<u8>
bytes.push(0); // terminating null
let mut cchars = bytes.iter().map(|b| *b as c_char).collect::<Vec<c_char>>();
let name: *mut c_char = cchars.as_mut_ptr();
name
}
Ubuntu 14.04,64 位带 Rust 1.3。
我错过了什么?感谢指点(哈哈...)。
and the function signature isn't complicated
它不是,但它确实有助于拥有正确的...^_^ 来自我本地版本的 readline (6.3.8):
extern rl_compentry_func_t *rl_completion_entry_function;
typedef char *rl_compentry_func_t PARAMS((const char *, int));
此外,您有多个 use after free 错误:
vec![CString::new("one").unwrap()].iter().map(|s| s.as_ptr());
这将创建一个 CString
并获取指向它的指针。语句完成后,nothing owns 拥有字符串的向量。向量将立即被删除,删除字符串,使指针无效。
words.as_ptr() as *const *const c_char
这里有类似的事情——你拿了指针,但是 没有任何东西拥有 words
向量了,所以它被丢弃,使指针无效。所以现在你有一个无效的指针,它试图指向一系列无效的指针。
同样的问题可以在string_to_mut_c_char
中找到。
我对 readline 的了解不足,无法理解谁应该拥有返回的字符串,但看起来您将所有权传递给 readline 并释放它们。如果是这样,那意味着您将不得不使用与 readline 相同的分配器,以便它可以为您释放字符串。您可能必须编写一些自定义代码,使用适当的分配器复制 CString 的数据。
在风格方面,您可以在变量名中使用下划线来表示它们未被使用:
extern fn complete(_text: *const c_char, _start: c_int, _end: c_int)
:
后应该有一个space,不需要指定向量内容的类型:
let mut words: Vec<_>
我正在尝试用 Rust 编写一个 readline 自定义完成器(tab 完成)。我想我什么都弄清楚了,但是当我尝试 en vivo 时,它掉进了杂草中,再也没有回来。奇怪的是,当我直接从 main()
调用它时,我似乎得到了一个有效的指针。在这两种情况下,我都没有看到崩溃或恐慌。回溯输出在运行中不一致(它正忙着做某事)。也许一个线索是 gdb 指示传递给完成者的参数不正确(尽管我实际上并没有使用它们)。例如,回调后:
#2 0x00007f141f701272 in readlinetest::complete (text=0x7f141ff27d10 "", start=2704437, end=499122176) at src/main.rs:24
或者直接在main
:
#0 readlinetest::complete (text=0x555555559190 <complete::hcda8d6cb2ef52a1bKaa> "dH;$%p", start=0, end=0) at src/main.rs:21
我有 ABI 问题吗?似乎不太可能,函数签名也不复杂:(
这是一个小测试项目:Cargo.toml
:
[package]
name = "readlinetest"
version = "0.1.0"
authors = ["You <you@example.com>"]
[dependencies]
libc = "*"
readline = "*"
和main.rs
:
extern crate libc;
extern crate readline;
use libc::{c_char, c_int};
use std::ffi::CString;
use std::process::exit;
use std::ptr;
use std::str;
extern { fn puts(s: *const libc::c_char); }
#[link(name = "readline")]
// Define the global in libreadline that will point to our completion function.
extern {
static mut rl_completion_entry_function: extern fn(text: *const c_char,
start: c_int,
end: c_int) -> *const *const c_char;
}
// Our completion function. Returns two strings.
extern fn complete(text: *const c_char, start: c_int, end: c_int) -> *const *const c_char {
let _ = text; let _ = start; let _ = end;
let mut words:Vec<*const c_char> =
vec!(CString::new("one").unwrap(), CString::new("two").unwrap()).
iter().
map(|arg| arg.as_ptr()).
collect();
words.push(ptr::null()); // append null
words.as_ptr() as *const *const c_char
}
fn main() {
let words = complete(string_to_mut_c_char("hi"), 1, 2);
unsafe { puts(*words) } // prints "one"
//unsafe { puts((*words + ?)) } // not sure hot to get to "two"
unsafe { rl_completion_entry_function = complete }
// Loop until EOF: echo input to stdout
loop {
if let Ok(input) = readline::readline_bare(&CString::new("> ").unwrap()) {
let text = str::from_utf8(&input.to_bytes()).unwrap();
println!("{}", text);
} else { // EOF/^D
exit(0)
}
}
}
// Just for testing
fn string_to_mut_c_char(s: &str) -> *mut c_char {
let mut bytes = s.to_string().into_bytes(); // Vec<u8>
bytes.push(0); // terminating null
let mut cchars = bytes.iter().map(|b| *b as c_char).collect::<Vec<c_char>>();
let name: *mut c_char = cchars.as_mut_ptr();
name
}
Ubuntu 14.04,64 位带 Rust 1.3。
我错过了什么?感谢指点(哈哈...)。
and the function signature isn't complicated
它不是,但它确实有助于拥有正确的...^_^ 来自我本地版本的 readline (6.3.8):
extern rl_compentry_func_t *rl_completion_entry_function;
typedef char *rl_compentry_func_t PARAMS((const char *, int));
此外,您有多个 use after free 错误:
vec![CString::new("one").unwrap()].iter().map(|s| s.as_ptr());
这将创建一个 CString
并获取指向它的指针。语句完成后,nothing owns 拥有字符串的向量。向量将立即被删除,删除字符串,使指针无效。
words.as_ptr() as *const *const c_char
这里有类似的事情——你拿了指针,但是 没有任何东西拥有 words
向量了,所以它被丢弃,使指针无效。所以现在你有一个无效的指针,它试图指向一系列无效的指针。
同样的问题可以在string_to_mut_c_char
中找到。
我对 readline 的了解不足,无法理解谁应该拥有返回的字符串,但看起来您将所有权传递给 readline 并释放它们。如果是这样,那意味着您将不得不使用与 readline 相同的分配器,以便它可以为您释放字符串。您可能必须编写一些自定义代码,使用适当的分配器复制 CString 的数据。
在风格方面,您可以在变量名中使用下划线来表示它们未被使用:
extern fn complete(_text: *const c_char, _start: c_int, _end: c_int)
:
后应该有一个space,不需要指定向量内容的类型:
let mut words: Vec<_>