LoginSignup
12
9

More than 5 years have passed since last update.

bashの組み込みコマンドをつくる

Last updated at Posted at 2017-01-23

 概要です。

共有ライブラリをつくれば良いことがわかったのでやっていきます。

実装する

 型などを定義します。スレッドローカルではない値を定義するために Sync をつけているんですけど、これが何を意味しているのかは謎です。俺たちは雰囲気で Rust をやっている。

const EXECUTION_SUCCESS: c_int = 0;
const BUILTIN_ENABLED: c_int = 0x01;

#[repr(C)]
pub struct WORD_DESC {
  word: *mut c_char,
  dollar_present: c_int,
  quoted: c_int,
  assignment: c_int,
}

#[repr(C)]
pub struct WORD_LIST {
  next: *mut WORD_LIST,
  word: *mut WORD_DESC,
}

pub type sh_builtin_func_t = extern fn (*mut WORD_LIST) -> c_int;

#[repr(C)]
pub struct builtin {
  name: *mut c_char,
  function: sh_builtin_func_t,
  flags: c_int,
  long_doc: *mut *const c_char,
  short_doc: *mut c_char,
  handle: *mut c_char,
}

unsafe impl Sync for builtin {}

コマンドを実装します。文字列の定義がとにかく厳しくて、null 終端の byte string を二回キャストしてポインタを取るみたいなことをしています。

pub extern fn hello_world(_list: *mut WORD_LIST) -> c_int {
  println!("Hello, World!");
  EXECUTION_SUCCESS
} 

const LONG_DOC: &'static [*const c_char] = &[
  b"Show a greeting message.\0" as *const _ as *const _,
  b"\0" as *const _ as *const _,
  b"It's far faster than launching executable file\0" as *const _ as *const _,
  b"because it't not necessary to call exec() and fork().\0" as *const _ as *const _,
  0 as *const _,
];

#[no_mangle]
pub static mut hello_world_struct: builtin = builtin {
  name: b"hello_world\0" as *const _ as *mut _,
  function: hello_world,
  flags: BUILTIN_ENABLED,
  long_doc: LONG_DOC as *const _ as *mut _,
  short_doc: b"hello_world\0" as *const _ as *mut _,
  handle: 0 as *mut _,
};

共有ライブラリとしてビルドします。cdylib と書くべきところを dylib と書いて少しハマりました。具体的には、リリースビルドをしたときに文字列のあたりがおかしくなります。

Cargo.toml
[package]
name = "hello_world"
version = "0.1.0"
authors = []

[lib]
name = "hello_world"
path = "./lib.rs"
crate-type = ["cdylib"]
$ cargo build --release
   Compiling hello_world v0.1.0 (file:///Users/woxtu/Workspace/hello_world)
    Finished release [optimized] target(s) in 0.29 secs

以上です。

呼び出す

 先につくった共有ライブラリを組み込みます。

$ enable -f target/release/libhello_world.dylib hello_world
$ enable | grep hello_world
enable hello_world

コマンドを呼び出します。

$ hello_world
Hello, World!
$ help hello_world
hello_world: hello_world
    Show a greeting message.

    It's far faster than launching executable file
    because it't not necessary to call exec() and fork().

やりました。以上です。

終わりに

 組み込みコマンドはともかく、共有ライブラリがつくれるということは他言語との連携も取りやすいということなので、仲良くしていくと良いんじゃないかと思います。各位、仲良く。

 コードは Gist に上げてあります。

参考

12
9
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
9