promptコマンドの実装について
Posted: 2011年9月02日(金) 22:49
今回は文字列処理について質問させていただきます。
僕のパソコンは管理者権限などでコマンドプロンプトが使えなかったのですが、プログラム内からならsystem()を実行できることがわかりました。そこで、次のようなプログラムを作り、コマンドプロンプトを模倣してみました。
func.hは内部で必要なヘッダファイルが#includeされ、
void fnDisplayError( DWORD dwErrorMsgId );
int Trim( char * );
int strpcmp( const char* , const char* );
bool strptncmp(const char*,const char*);
のプロトタイプ宣言をしています。
fnDisplayError()は引数のエラーコードを文字列に直し、画面に出力する関数です。
Trim()は前後のスペースを削除します。
strpcmp()は大文字小文字を無視して文字列比較をする関数です。
strptncmp()は第二引数に.が入っていると任意の一文字にマッチさせることができる文字列比較関数です。
さて、本題に入りますが、cdやsetなどsystem()では使えないコマンドがあり、それを実現するため改良してきました。
そして今回、promptコマンドも使えないことがわかりました。
このコマンドの使用頻度は高いとは言えませんが、文字列処理について詳しく知りたかったので質問させていただきました。
僕が考えた実装の方法は、
(1)promptコマンドが入力されると、引数の各要素を分割し、char型ポインタ配列などにそれをコピーする。
(2) の部分を変更し、引数の要素が入っている配列のアドレスを受け取り、文字列を返す関数を自作し、
それを出力する。
といったものです。
(1)の「promptコマンドが入力されると」に関しては上のコードのコメント部にあるようにelse if(!strpcmp(cmd,"prompt"))でいいと思うのですが、
「各要素を分割、コピー」の部分をどうすればいいかわかりません。
promptコマンドの引数は$を使うことで特殊コードを使えるのですが、普通の文字列と区別してどうやって分割、代入するのでしょうか。
例えば、引数に"$sHello$g"という文字列が与えられたときには、配列の要素は順番に、
"$s","Hello","$g"
といったようになるようにしたいです。
(2)は特殊コードを置き換えていけばいいと思いますが、どうやって特殊コードを見分けるかわかりません。
ヒントなど教えていただけるとありがたいです。
できるだけ詳しく書くよう心がけましたが、まだ分かりにくい箇所もあるかもしれません。
また既存のコードにバグなどがあった場合は、教えていただけるとありがたいです。
ご回答よろしくお願いします。
僕のパソコンは管理者権限などでコマンドプロンプトが使えなかったのですが、プログラム内からならsystem()を実行できることがわかりました。そこで、次のようなプログラムを作り、コマンドプロンプトを模倣してみました。
#include "func.h"
int main(){
char buf [MAX_PATH];
char str[256], *path,*p,cmd[32],set[16],*loc;
unsigned char i;
unsigned long err;
bool flag,echo=true;
while(true){
memset(cmd, 0, sizeof(cmd)); //cmd初期化
memset(set, 0 ,sizeof(set));
memset(str, 0 ,sizeof(str));
if(echo){
GetCurrentDirectory(MAX_PATH,buf);
printf("\n%s>",buf);
}
fgets(str, sizeof(str), stdin);
//改行文字除去処理
p = strchr( str, '\n' );
/* 改行文字があった場合 */
if ( p != NULL )*p = '\0';
if(strlen(str)==0) continue;
Trim(str);//前と後ろのスペース除去
i=0;
if(strchr(str,' ')!=NULL){//空白が入っている場合(引数ありの場合)
while (str[i]!=' ') { i++; } //空白がくるまでカウンタを進める
strncpy(cmd,str,i);
while (str[i]==' ') { i++; } //次の空白以外の文字までカウンタを進める
path = &str[i];
i=0;//カウンタ初期化
if(!strpcmp(cmd,"exit")){
if(!strpcmp(path,"/?")){system("exit /?");
}else{
break;
}
}
if(!strpcmp(cmd,"cd") || !strpcmp(cmd,"chdir")){
if(!strpcmp(path,"/?")){
system("cd /?");
}else{
flag=SetCurrentDirectory(path);
if(!flag){
err= GetLastError();
fnDisplayError(err);
}
}
}else if(!strptncmp(cmd,".:")){
flag=SetCurrentDirectory(cmd);
if(!flag){
err= GetLastError();
fnDisplayError(err);
}
}else if(!strpcmp(cmd,"set")){
if(strchr(path,'=')!=NULL){
while(path[i]!='='){ i++; }
strncpy(set,path,i);
while (path[i]==' ') { i++; }
loc=&path[i];
sprintf(loc,"%s=%s",set,loc);
putenv(loc);
}else{
system(str);
}
}else if(!strpcmp(cmd,"echo")){
i=strlen(path);
if(!strpcmp(path,"on"))echo=true;
else if(!strpcmp(path,"off"))echo=false;
else if(path[0]=='%' && path[i-1]=='%'){
loc=&path[1];
loc[i-2]='\0';
path=getenv(loc);
printf("%s\n",path);
}
else printf("%s\n",path);
}/*else if(!strpcmp(cmd,"prompt")){
}*/else{
system(str);
}
}else{//引数なしの場合
if(strchr(str,'=')!=NULL){
while(str[i]!='='){ i++; }
strncpy(set,str,i);
while (str[i]=='=') { i++; }
loc=&str[i];
}
if(!strpcmp(str,"exit")) break;
if(!strpcmp(str,"cd..")){
flag=SetCurrentDirectory("..");
if(!flag){
err= GetLastError();
fnDisplayError(err);
}
}else if(!strpcmp(str,"cd..\..")){
flag=SetCurrentDirectory("..\..");
if(!flag){
err= GetLastError();
fnDisplayError(err);
}
}else if(!strptncmp(str,".:")){
flag=SetCurrentDirectory(str);
if(!flag){
err= GetLastError();
fnDisplayError(err);
}
}else if(!strpcmp(set,"path")){
sprintf(str,"path=%s",loc);
putenv(str);
}else if(!strpcmp(str,"echo")){
if(echo)printf("ECHO は <ON> です。\n");
else printf("ECHO は <OFF> です。\n");
}else{
system(str);
}
}
}
return 0;
}
void fnDisplayError( DWORD dwErrorMsgId );
int Trim( char * );
int strpcmp( const char* , const char* );
bool strptncmp(const char*,const char*);
のプロトタイプ宣言をしています。
fnDisplayError()は引数のエラーコードを文字列に直し、画面に出力する関数です。
Trim()は前後のスペースを削除します。
strpcmp()は大文字小文字を無視して文字列比較をする関数です。
strptncmp()は第二引数に.が入っていると任意の一文字にマッチさせることができる文字列比較関数です。
さて、本題に入りますが、cdやsetなどsystem()では使えないコマンドがあり、それを実現するため改良してきました。
そして今回、promptコマンドも使えないことがわかりました。
このコマンドの使用頻度は高いとは言えませんが、文字列処理について詳しく知りたかったので質問させていただきました。
僕が考えた実装の方法は、
(1)promptコマンドが入力されると、引数の各要素を分割し、char型ポインタ配列などにそれをコピーする。
(2) の部分を変更し、引数の要素が入っている配列のアドレスを受け取り、文字列を返す関数を自作し、
それを出力する。
といったものです。
(1)の「promptコマンドが入力されると」に関しては上のコードのコメント部にあるようにelse if(!strpcmp(cmd,"prompt"))でいいと思うのですが、
「各要素を分割、コピー」の部分をどうすればいいかわかりません。
promptコマンドの引数は$を使うことで特殊コードを使えるのですが、普通の文字列と区別してどうやって分割、代入するのでしょうか。
例えば、引数に"$sHello$g"という文字列が与えられたときには、配列の要素は順番に、
"$s","Hello","$g"
といったようになるようにしたいです。
(2)は特殊コードを置き換えていけばいいと思いますが、どうやって特殊コードを見分けるかわかりません。
ヒントなど教えていただけるとありがたいです。
できるだけ詳しく書くよう心がけましたが、まだ分かりにくい箇所もあるかもしれません。
また既存のコードにバグなどがあった場合は、教えていただけるとありがたいです。
ご回答よろしくお願いします。