// J12345 // 山田 太郎 // gcc ex3.c // 起動方法 ./a.out PATH=/bin:/usr/bin 等. // 2006年度 演習1をベースにするので,コマンドや引数は逆転配置. // // redirection は < in 1> out 2> err 等,特に逆転はさせない. // 1> 2> の 1,2と>の間は離してはいけない. // 1> を > と省略してはいけない. // redirection は コマンド名より後でなければならない.例えば, // afo bfo ls 1> out 2> err 等. // input, output error のどれかもしくは全てを省略してよい. // // 操作方法 bashやcshと同様にコマンドと引数を入れる // 終了方法 EOFを入力,具体的にはコントロールDを押す. // // $Id: ex3.c,v 1.8 2007-01-17 22:14:38+09 kaiya Exp kaiya $ #include #include #include #include #include #include #include #include #include #ifndef LINE_MAX #define LINE_MAX 2048 #endif extern char** environ; // 環境変数のリスト extern int errno; // エラーコード /* * str: input string, maybe with "\n" * return: NULL when parse error, list of args when success. */ char** str2args(char* str); /* * argv: list of string * return: 1 when argv is valid, 0 when it is not. * valid string: string with '=' */ int checkArgs(char **argv); void redirect(char*); int redirectFile(char*, int); char* findPath(char*); main(int argc, char* argv[]){ char buf[LINE_MAX]; if(!checkArgs(argv+1)){ fprintf(stderr, "Invalid args\n"); exit(3); } environ=argv+1; while(fgets(buf, LINE_MAX, stdin)!=NULL){ pid_t ch; if((ch=fork())==0){ // child char** args; redirect(buf); // ★★ 演習3で追加 if((args=str2args(buf))==NULL){ // parse error. fprintf(stderr, "invalid inputs, type again.\n"); exit(4); } execvp(*args, args); fprintf(stderr, "exec failed code %d\n", errno); exit(2); }else if(ch>0){ // parent int stat; wait(&stat); }else{ // fail fprintf(stderr, "fork fail\n"); exit(1); } } } /** * コマンド文字列群を文字の配列に分離する * @param str ユーザーの入力文字列 コマンド 引数等が空白文字で区切られている * @return コマンド等を分離した文字列の配列,関数内のstatic変数を利用 **/ char** str2args(char* str){ char* args[LINE_MAX]; // ★ 修正 static char* rets[LINE_MAX]; // ★ 追加 wastefull, but easy char* ptr; int argc=0; for(ptr=str; *ptr; ptr++){ char* etr; if(isspace(*ptr)) continue; for(etr=ptr; *etr && !isspace(*etr); etr++) ; if(*etr){ *etr='\0'; args[argc++]=ptr; ptr=etr; }else{ args[argc++]=ptr; break; } if(argc>=LINE_MAX) return NULL; } // ★ 追加部分開始 // args[argc]=NULL; { int i; for(i=0; argc>0; i++, argc--){ rets[i]=args[argc-1]; } rets[i]=NULL; } return rets; // ★ 追加部分終了 } int checkArgs(char** args){ int ret=0; // default fail; if(args==NULL) return 0; for(ret=0; *args; args++){ if(strchr(*args, '=')==NULL) return 0; else ret=1; } return ret; } /* ★★ 演習3で追加 strの前後にある空白文字をとって非空白文字列を返す. strがNULLならNULLを返す. */ char* findPath(char* str){ char* ptr; if(str==NULL) return NULL; while(*str && isspace(*str)) str++; ptr=str; while(*ptr && !isspace(*ptr)) ptr++; *ptr='\0'; return str; } /* ★★ 演習3で追加 本来 fd で入出力されるデータを file で指定されたファイルから入出力 file をあけそこなったり,dupに失敗したりしたら,負数を返す. */ int redirectFile(char* file, int fd){ int nfd; if((nfd=open(file, O_RDWR | O_CREAT, 0644))<0) return nfd; close(fd); if(dup(nfd)<0) return -1; return nfd; } /* ★★ 演習3で追加 コマンド文字列に含まれるリダイレクション関係の文字列に従いリダイレクト. リダイレクトは 入力,出力,エラーの順番で指定されなければならない. 上記それぞれ < 1> 2> で指定. リダイレクトの一部は省略可能. コマンド文字列からリダイレクション関係の文字列を削除. */ void redirect(char* buf){ char* ind; char* outd; char* errd; char* path; if(buf==NULL) return; ind=strstr(buf, "<"); if(ind!=NULL){ *ind=' '; } outd=strstr(buf, "1>"); if(outd!=NULL){ *outd=' '; outd++; *outd=' '; } errd=strstr(buf, "2>"); if(errd!=NULL){ *errd=' '; errd++; *errd=' '; } // printf("<%s>\n", buf); if(ind!=NULL){ *ind = '\0'; path=findPath(ind+1); if(path==NULL){ fprintf(stderr, "wrong input redirection\n"); }else if(redirectFile(path, 0)<0){ fprintf(stderr, "input redirection error\n"); } } if(outd!=NULL){ *outd = '\0'; path=findPath(outd+1); if(path==NULL){ fprintf(stderr, "wrong output redirection\n"); }else if(redirectFile(path, 1)<0){ fprintf(stderr, "output redirection error\n"); } } if(errd!=NULL){ *errd = '\0'; path=findPath(errd+1); if(path==NULL){ fprintf(stderr, "wrong error redirection\n"); }else if(redirectFile(path, 2)<0){ fprintf(stderr, "error redirection error\n"); } } }