D言語であそぼう シェルとかつくってみる編。
なんか C で簡単なシェルつくってて、「あーハッシュテーブルでシンボルテーブルつくった方がいいよなあ、でもめんどくせー」と思ったので D で書いてみた。
とりあえず import std.c.linux.linux とかしたり色々すればできんだろー、と思ったら std.c.* のアレコレがまあ conflict しまくる。なので最低限使うのだけ宣言。
extern (C){ int fork(); int wait(int*); char* getenv(char*); void exit(int); void perror(char *); }
D 側の関数で対応するものあればいらなくなんだけど。ちゃんと調べてないのであるかもしれない。
D はとにかく C との相性がいいっすね。C言語向けにインターフェースを提供してるライブラリのバインドが簡単ってのは、すごく有利なんだなと。あとはまあ C言語に比べりゃ文字列操作系の処理が超楽ちんですよねー、みたいなー。
以下うんこシェルのソース。「ハッシュテーブルハッシュテーブル!」言ってたのはどうしたかっつーと、まったくもって活用していなかったりする。しょぼいというか習作以外の何物でもない。今後改良したりとかも、特にしないと思う。
import std.process; import std.cstream; import std.stream; import std.string; import std.file; import std.path; extern (C){ int fork(); int wait(int*); char* getenv(char*); void exit(int); void perror(char *); } const char[] PS1 = "$ "; struct Command { char[][] argv; }; int delegate(Command*)[char[]] builtinFunc; void initFunction(){ int builtin_cd(Command *com){ static char[] prevpath = "."; if(com.argv.length == 1){ prevpath = getcwd(); chdir(toString(getenv("HOME"))); } else if(com.argv.length == 2){ char[] newpath; if(com.argv[1] == "-"){ newpath = prevpath; } else{ newpath = expandTilde(com.argv[1]); } prevpath = getcwd(); chdir(newpath); } else{ derr.writeLine("too may arguments"); return 1; } return 0; } int builtin_exit(Command *com){ exit(0); return 1; } builtinFunc["cd"] = &builtin_cd; builtinFunc["exit"] = &builtin_exit; } int exec_com(Command *com){ int pid; char[] cmdname = com.argv[0]; if((pid = fork()) == 0){ execvp(cmdname, com.argv); perror(toStringz(cmdname)); exit(1); } if(pid < 0){ perror(toStringz("fork")); exit(1); } if(wait(null) < 0){ perror(toStringz("wait")); exit(1); } return 0; } int run(Command *com){ char[] cmdname = com.argv[0]; if(cmdname in builtinFunc){ builtinFunc[cmdname](com); return 0; } else{ exec_com(com); return 0; } } Command *parse(Command *com, char[] line){ if(line.length == 0){ return com; } switch(line[0]){ case ' ': case '\t': return parse(com, line[1 .. line.length]); case '\'': int rightQuot = line[1 .. line.length].find('\'')+1; if(rightQuot != -1){ com.argv ~= line[1 .. rightQuot]; } else{ derr.writeLine("error: close quot(\')"); } if(rightQuot >= 0 && rightQuot+1 <= line.length){ return parse(com, line[rightQuot+1 .. line.length]); } else{ return com; } default: int rightSp; if((rightSp = line.find(' ')) != -1 || (rightSp = line.find('\t')) != -1){ com.argv ~= line[0 .. rightSp]; return parse(com, line[rightSp+1 .. line.length]); } else{ com.argv ~= line[0 .. line.length]; return com; } } } int main(char[][] args){ initFunction(); while(!din.eof()){ dout.write(PS1); char[] line = din.readLine(); if(line.length == 0) continue; Command com; parse(&com, line); run(&com); } return 0; }
クラスってなんですか!!!
あきらかに Command あたりをクラスにした方がすっきりしそう。parse, run とかメソッドにしてほげほげ。
あと実行画面。
% ./shell $ ls shell shell.d shell.o $ ls -aF ./ ../ shell* shell.d shell.o $ cd ~/tmp $ ls coins.tar.bz2 evilwm-1.0.0 home sevilwm-0.6.1-1.i386.rpm swig-d-1-Aug-2004.zip verilog-0.8.4-0.src.rpm echo.php evilwm-1.0.0.tar.gz m sun.tar.bz2 verilog-0.8.4 verilog-0.8.4.tar.gz $ pwd /home/sun/tmp $ cd - $ ls shell shell.d shell.o $ pwd /home/sun/docs/d/shell $ perl -e 'print "hoge\n"' hoge $ exit %