前提・実現したいこと
学校の課題でC言語でシェルを作っています.その中のディレクトリの履歴をpushedコマンドでスタックに格納してdirsコマンドでスタックの上から表示させたいです.スタックは単方向リストで作っています.
(外部コマンドなどは使用不可)
字数制限によりコマンド解析のparse関数を消しています.execute_command関数でのargs[i]に第i引数のポインタが入る仕組みです.(iは0以上の整数)
発生している問題・エラーメッセージ
実行してみたところprintfを挟んだ実行から連結リストは生成されているようなのですが,ディレクトリを移動してpushedコマンドを打つとそれまでのディレクトリの履歴が全て書き換わってしまいます.(最後にうったpushedのカレントディレクトリに書き換わります.)おそらく,よく理解できでない子プロセス親プロセスに関わる何かが悪さしているのではないかと考えています.
現在作成途中ですのでcdコマンドはホームディレクトリに移動するものしか作っていません.後はpwdコマンドで現在のカレントディレクトリを表示させる機能です.
構造体の定義は下に説明させていただいてます.
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> #include <string.h> #define BUFLEN 1024 /* コマンド用のバッファの大きさ */ #define MAXARGNUM 256 /* 最大の引数の数 */ #define MAXBUF 256 /* * ローカルプロトタイプ宣言 */ typedef struct dirstack{ char *name; struct dirstack* next; }dir_st; typedef struct stacklist { dir_st* Top; }top_node; int parse(char [], char *[]); void execute_command(char *[], int); void changedir(char *[]); void dirpush(char **); void dirpop(char **); char* GetCd(char*); void printdir(dir_st*); dir_st* initnode(char*); top_node* inittop(char*); void push(top_node*,char*); static char *command_list[]={ "cd", "pwd","pushed","dirs" }; //とりあえずの便利な文字を置く場所 char buf[MAXBUF]; //文字の置き場所 top_node *Top = NULL; /*---------------------------------------------------------------------------- * * 関数名 : main * * 作業内容 : シェルのプロンプトを実現する *--------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { char command_buffer[BUFLEN]; /* コマンド用のバッファ */ char *args[MAXARGNUM]; /* 引数へのポインタの配列 */ int command_status; /* コマンドの状態を表す command_status = 0 : フォアグラウンドで実行 command_status = 1 : バックグラウンドで実行 command_status = 2 : シェルの終了 command_status = 3 : 何もしない */ /* * 無限にループする */ for(;;) { /* * プロンプトを表示する */ printf("Command : "); /* * 標準入力から1行を command_buffer へ読み込む * 入力が何もなければ改行を出力してプロンプト表示へ戻る */ if(fgets(command_buffer, BUFLEN, stdin) == NULL) { printf("\n"); continue; } /* * 入力されたバッファ内のコマンドを解析する * * 返り値はコマンドの状態 */ command_status = parse(command_buffer, args); /* * 終了コマンドならばプログラムを終了 * 引数が何もなければプロンプト表示へ戻る */ if(command_status == 2) { printf("done.\n"); exit(EXIT_SUCCESS); } else if(command_status == 3) { continue; } /* * コマンドを実行する */ execute_command(args, command_status); } return 0; } /*---------------------------------------------------------------------------- * * 関数名 : parse * * 作業内容 : バッファ内のコマンドと引数を解析する * * 引数 : * * 返り値 : コマンドの状態を表す : * 0 : フォアグラウンドで実行 * 1 : バックグラウンドで実行 * 2 : シェルの終了 * 3 : 何もしない * *--------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------- * * 関数名 : execute_command * * 作業内容 : 引数として与えられたコマンドを実行する * コマンドの状態がフォアグラウンドならば、コマンドを * 実行している子プロセスの終了を待つ * バックグラウンドならば子プロセスの終了を待たずに * main 関数に返る(プロンプト表示に戻る) * * 引数 : * * 返り値 : * * 注意 : * *--------------------------------------------------------------------------*/ void execute_command(char *args[], /* 引数の配列 */ int command_status) /* コマンドの状態 */ { int pidid; /* プロセスID */ int status; /* 子プロセスの終了ステータス */ pid_t pid; pid_t wait_pid; char *command; int state=0; char *crentdir; /* * * 生成できたかを確認し、失敗ならばプログラムを終了する */ pid = fork(); if(pid == -1){ exit(-1); }else if(pid == 0){ /******** Your Program ********/ if(strcmp(*(command_list+1),*args)==0){ GetCd(crentdir); exit(EXIT_SUCCESS); } } /* * 子プロセスの場合には引数として与えられたものを実行する * * 引数の配列は以下を仮定している * ・第1引数には実行されるプログラムを示す文字列が格納されている * ・引数の配列はヌルポインタで終了している */ /******** Your Program ********/ /* * コマンドの状態がバックグラウンドなら関数を抜ける */ /******** Your Program ********/ /* * ここにくるのはコマンドの状態がフォアグラウンドの場合 * * 親プロセスの場合に子プロセスの終了を待つ */ wait_pid = wait(&state); if(strcmp(*command_list,*args) == 0){ changedir(args); } if(strcmp(*(command_list+2),*args)==0){ //*pushed if(Top==NULL){ Top = inittop(getcwd(buf,sizeof(buf)-1)); }else{ push(Top,getcwd(buf,sizeof(buf)-1)); } } if(strcmp(*(command_list+3),*args)==0){ //dirs printdir(Top->Top); } /******** Your Program ********/ return; } void changedir(char *args[]){ if(*(args+1) == '\0' ){ const char *home = getenv("HOME"); printf("%s\n",home); if(chdir(home) != 0){ printf("ERROR\n"); } chdir(home); } } char *GetCd(char* p){ p = getcwd(buf,sizeof(buf)-1); if(p == NULL){ printf("getcwd error\n"); } printf("%s\n",p); return p; } void printdir(dir_st* node){ if(node != NULL){ printdir(node->next); printf("%s\n",node->name); } } dir_st* initnode(char* name){ dir_st* pNode = NULL; pNode = (dir_st*)malloc(sizeof(dir_st)); if(pNode == NULL){ printf("malloc error\n"); return NULL; } pNode->name = name; pNode->next = NULL; return pNode; } top_node* inittop(char* name){ top_node* pNode = NULL; pNode = (top_node*)malloc(sizeof(top_node)); if(pNode == NULL){ printf("malloc error\n"); return NULL; } pNode->Top = initnode(name); if(pNode->Top == NULL){ printf("TopError\n"); free(pNode); } printdir(pNode->Top); return pNode; } void push(top_node* Node,char* name){ dir_st* pNode = NULL; if(Node == NULL){ printf("Error\n"); return; } if(Node->Top == NULL){ Node->Top = initnode(name); if(Node->Top == NULL){ printf("pusherror\n"); } return; } pNode = Node->Top; while(pNode->next != NULL){ pNode = pNode->next; } pNode->next = initnode(name); if(pNode->next == NULL){ printf("push error\n"); return; } printdir(Node->Top); return; } /*-- END OF FILE -----------------------------------------------------------*/
試したこと
このコードを実行したところ,以下のような結果になりました.
Command : pushed /Users/myname/Desktop/c Command : pushed /Users/myname/Desktop/c /Users/myname/Desktop/c Command : pushed /Users/myname/Desktop/c /Users/myname/Desktop/c /Users/myname/Desktop/c Command : cd /Users/myname Command : pushed /Users/myname /Users/myname /Users/myname /Users/my-name
問題に関係のあるところははexecute_command関数内のpushedとコメントした部分だと思っています.
まず構造体の定義が,
typedef struct dirstack{ char *name; struct dirstack* next; }dir_st; typedef struct stacklist { dir_st* Top; }top_node;
で,dir_stがノードでありtop_nodeが先頭を指すポインタです.
以下がexecute_command関数です.ここでコマンドの実行をしています.
printdir関数で単方向リストを再帰を使い逆順に表示をしています.
initnode関数で各ノードを作っています.
inittop関数で先頭ノードを生成しています.
push関数でpushしています.pushする内容はcommand_execuでカレントディレクトリを取得しそのデータをpushしています.また先頭ノードは大域変数で宣言しています.
小さい別のプログラムで線形リストのスタック自体は作れていることは確認したので,別のところに問題があると思います.よろしくお願いします.
また親プロセス子プロセスについてよくわかってないので何をどっちで実行すればいいのか教えてくだされば幸いです.
補足情報(FW/ツールのバージョンなど)
ここにより詳細な情報を記載してください。