修正前 修正後
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
.#include <unistd.h>   // fork, execvp  #include <unistd.h>   // fork, execvp, dup2 
#include <sys/wait.h> // waitpid #include <sys/wait.h> // waitpid
#include <glob.h>     // glob #include <glob.h>     // glob
#include <errno.h>    // errno #include <errno.h>    // errno
#include <limits.h>   // PATH_MAX #include <limits.h>   // PATH_MAX
.  #include <fcntl.h>    // open
   
#define MAX_CMD_LEN 1024 #define MAX_CMD_LEN 1024
#define MAX_ARGS 100 #define MAX_ARGS 100
   
// 組み込みコマンド関連の宣言 // 組み込みコマンド関連の宣言
int cd (char *argv[]); int cd (char *argv[]);
int pwd(char *argv[]); int pwd(char *argv[]);
struct builtin { struct builtin {
    char cmd[256];              // コマンド名     char cmd[256];              // コマンド名
    int (*func)(char *argv[]);  // 関数へのポインタ     int (*func)(char *argv[]);  // 関数へのポインタ
}; };
struct builtin blt_in[] = {{"cd",cd}, {"pwd",pwd}, {"",0}}; struct builtin blt_in[] = {{"cd",cd}, {"pwd",pwd}, {"",0}};
   
.  // リダイレクション関連の宣言
  void handle_redirection(char *argv[]);
  enum Action { SAVE, RESTORE };
  typedef enum { // リダイレクションのタイプを表す列挙型
      REDIR_INPUT,
      REDIR_OUTPUT,
      REDIR_APPEND,
      REDIR_MERGE_STDERR
  } RedirectionType;
  typedef struct { // リダイレクションの設定を格納する構造体
      const char *symbol;      // リダイレクション記号
      RedirectionType type;    // リダイレクションのタイプ
      int target_fd;           // 対象のファイル記述子
      int flags;               // ファイルオープンフラグ
  } RedirectionRule;
  static const RedirectionRule redirection_rules[] = { // リダイレクションルールの定義
      {"<",    REDIR_INPUT,  STDIN_FILENO,  O_RDONLY},
      {">",    REDIR_OUTPUT, STDOUT_FILENO, O_WRONLY | O_CREAT | O_TRUNC},
      {">>",   REDIR_APPEND, STDOUT_FILENO, O_WRONLY | O_CREAT | O_APPEND},
      {"2>&1", REDIR_MERGE_STDERR, STDERR_FILENO, 0} // ファイルフラグ不要
  };
   
// コマンドをスペースで分割する関数 // コマンドをスペースで分割する関数
void parse_command(char *cmd, char *argv[]) { void parse_command(char *cmd, char *argv[]) {
    char *token = strtok(cmd, " \n");     char *token = strtok(cmd, " \n");
    int i = 0;     int i = 0;
    while (token != NULL) {     while (token != NULL) {
        argv[i++] = token;         argv[i++] = token;
        token = strtok(NULL, " \n");         token = strtok(NULL, " \n");
    }     }
    argv[i] = NULL; // 最後の引数をNULLで終わらせる     argv[i] = NULL; // 最後の引数をNULLで終わらせる
} }
   
// 引用符を削除する関数 // 引用符を削除する関数
void remove_quotes(char *str) { void remove_quotes(char *str) {
    char *src = str, *dst = str;     char *src = str, *dst = str;
    while (*src) {     while (*src) {
        if (*src == '"' || *src == '\'') {         if (*src == '"' || *src == '\'') {
            src++;  // 引用符をスキップ             src++;  // 引用符をスキップ
        } else {         } else {
            *dst++ = *src++;             *dst++ = *src++;
        }         }
    }     }
    *dst = '\0';  // 文字列の終端を追加     *dst = '\0';  // 文字列の終端を追加
} }
   
// 引数リストにパス名展開を適用する関数 // 引数リストにパス名展開を適用する関数
int expand_arguments(char *argv[], char **expanded_argv) { int expand_arguments(char *argv[], char **expanded_argv) {
    glob_t glob_result;     glob_t glob_result;
    int argc = 0; // 展開後の引数数     int argc = 0; // 展開後の引数数
   
    for (int i = 0; argv[i] != NULL; i++) {     for (int i = 0; argv[i] != NULL; i++) {
        // 引用符が含まれている場合は展開しない         // 引用符が含まれている場合は展開しない
        if ((strchr(argv[i], '*') || strchr(argv[i], '?') || strchr(argv[i], '[')) &&         if ((strchr(argv[i], '*') || strchr(argv[i], '?') || strchr(argv[i], '[')) &&
            (argv[i][0] != '"' && argv[i][0] != '\'')) { // 引用符外でパス名展開             (argv[i][0] != '"' && argv[i][0] != '\'')) { // 引用符外でパス名展開
            if (glob(argv[i], GLOB_NOCHECK | GLOB_TILDE, NULL, &glob_result) != 0) {             if (glob(argv[i], GLOB_NOCHECK | GLOB_TILDE, NULL, &glob_result) != 0) {
                perror("glob failed");                 perror("glob failed");
                return -1;                 return -1;
            }             }
            // 展開された結果を追加             // 展開された結果を追加
            for (size_t j = 0; j < glob_result.gl_pathc; j++) {             for (size_t j = 0; j < glob_result.gl_pathc; j++) {
                if (argc >= MAX_ARGS - 1) {                 if (argc >= MAX_ARGS - 1) {
                    fprintf(stderr, "Too many arguments after glob expansion\n");                     fprintf(stderr, "Too many arguments after glob expansion\n");
                    globfree(&glob_result);                     globfree(&glob_result);
                    return -1;                     return -1;
                }                 }
                expanded_argv[argc++] = strdup(glob_result.gl_pathv[j]);                 expanded_argv[argc++] = strdup(glob_result.gl_pathv[j]);
            }             }
            globfree(&glob_result); // メモリを解放             globfree(&glob_result); // メモリを解放
        } else {         } else {
            // 引用符を取り除く             // 引用符を取り除く
            remove_quotes(argv[i]);             remove_quotes(argv[i]);
   
            // パス名展開が不要な場合             // パス名展開が不要な場合
            if (argc >= MAX_ARGS - 1) {             if (argc >= MAX_ARGS - 1) {
                fprintf(stderr, "Too many arguments\n");                 fprintf(stderr, "Too many arguments\n");
                return -1;                 return -1;
            }             }
            expanded_argv[argc++] = strdup(argv[i]);             expanded_argv[argc++] = strdup(argv[i]);
        }         }
    }     }
    expanded_argv[argc] = NULL; // 最後にNULLを追加     expanded_argv[argc] = NULL; // 最後にNULLを追加
    return argc;     return argc;
} }
   
.  // ファイル記述子の保存または復元を行う関数
  void manage_file_descriptors(enum Action action, int *saved_fds) {
      if (action == SAVE) {
          // 現在の標準入力、標準出力、標準エラー出力を保存
          saved_fds[0] = dup(STDIN_FILENO);
          saved_fds[1] = dup(STDOUT_FILENO);
          saved_fds[2] = dup(STDERR_FILENO);
          if (saved_fds[0] == -1 || saved_fds[1] == -1 || saved_fds[2] == -1) {
              perror("Failed to save file descriptors");
              exit(1);
          }
      } else if (action == RESTORE) {
          // 保存されたファイル記述子を復元
          if (dup2(saved_fds[0], STDIN_FILENO) == -1 ||
              dup2(saved_fds[1], STDOUT_FILENO) == -1 ||
              dup2(saved_fds[2], STDERR_FILENO) == -1) {
              perror("Failed to restore file descriptors");
              exit(1);
          }
          // 保存したファイル記述子を閉じる
          close(saved_fds[0]);
          close(saved_fds[1]);
          close(saved_fds[2]);
      }
  }
   
// 組み込みコマンドの判定・実行 // 組み込みコマンドの判定・実行
int built_in(char *argv[], int bg){ int built_in(char *argv[], int bg){
    struct builtin *p;     struct builtin *p;
.      int saved_fds[3];
    // 組み込みコマンド配列(blt_in)の走査     // 組み込みコマンド配列(blt_in)の走査
    for (p = blt_in; *p->cmd != '\0'; p++) {     for (p = blt_in; *p->cmd != '\0'; p++) {
        // 組み込みコマンドでない場合は次をチェック         // 組み込みコマンドでない場合は次をチェック
        if (strcmp(p->cmd, argv[0]) != 0) continue;         if (strcmp(p->cmd, argv[0]) != 0) continue;
.          // 標準入力/出力/エラーのファイル記述子をバックアップ
          manage_file_descriptors(SAVE, saved_fds);
          // リダイレクションの処理
          handle_redirection(argv);
        // バックグラウンド実行の場合         // バックグラウンド実行の場合
        if (bg) {         if (bg) {
            pid_t pid = fork();             pid_t pid = fork();
            if (pid == -1) {             if (pid == -1) {
                perror("fork failed");                 perror("fork failed");
                exit(1);                 exit(1);
            }             }
            // 子プロセス             // 子プロセス
            if (pid == 0) exit ((p->func)(argv)); // 組み込みコマンドの関数を呼び出し             if (pid == 0) exit ((p->func)(argv)); // 組み込みコマンドの関数を呼び出し
            // 親プロセス             // 親プロセス
            fprintf(stderr, "Background process started with PID: %d\n", pid);             fprintf(stderr, "Background process started with PID: %d\n", pid);
.              // ファイル記述子をバックアップから復元
              manage_file_descriptors(RESTORE, saved_fds);
            return 0;             return 0;
        }         }
        // バックグラウンド実行でない場合         // バックグラウンド実行でない場合
        // 組み込みコマンドの関数を呼び出し         // 組み込みコマンドの関数を呼び出し
        (p->func)(argv);         (p->func)(argv);
.          // ファイル記述子をバックアップから復元
          manage_file_descriptors(RESTORE, saved_fds);
        return 0;         return 0;
    }     }
    return -1;     return -1;
} }
   
// cd コマンド // cd コマンド
int cd(char *argv[]) { int cd(char *argv[]) {
    if (argv[1] == NULL) {     if (argv[1] == NULL) {
        // 引数なしの場合はホームディレクトリに移動         // 引数なしの場合はホームディレクトリに移動
        const char *home_dir = getenv("HOME");         const char *home_dir = getenv("HOME");
        if (home_dir == NULL) {         if (home_dir == NULL) {
            perror("cd: HOME not set");             perror("cd: HOME not set");
            return -1;             return -1;
        } else {         } else {
            if (chdir(home_dir) != 0) {             if (chdir(home_dir) != 0) {
                perror("cd failed");                 perror("cd failed");
                return -1;                 return -1;
            }             }
        }         }
    } else {     } else {
        // 引数が指定された場合、そのディレクトリに移動         // 引数が指定された場合、そのディレクトリに移動
        if (chdir(argv[1]) != 0) {         if (chdir(argv[1]) != 0) {
            perror("cd failed");             perror("cd failed");
            return -1;             return -1;
        }         }
    }     }
    return 0;     return 0;
} }
   
// pwd コマンド // pwd コマンド
int pwd(char *argv[]){ int pwd(char *argv[]){
    char buf[PATH_MAX+1];     char buf[PATH_MAX+1];
    if (getcwd(buf, sizeof(buf)) == NULL) {     if (getcwd(buf, sizeof(buf)) == NULL) {
        perror("getcwd failed");         perror("getcwd failed");
        return -1;         return -1;
    }     }
    printf("%s\n", buf);     printf("%s\n", buf);
    return 0;     return 0;
} }
   
// バックグラウンド実行判定 // バックグラウンド実行判定
// コマンドの末尾が '&' かどうかを確認し、'&' を取り除いて戻り値として返す // コマンドの末尾が '&' かどうかを確認し、'&' を取り除いて戻り値として返す
int is_background_command(char *argv[]) { int is_background_command(char *argv[]) {
    int i = 0;     int i = 0;
    while (argv[i] != NULL) i++;     while (argv[i] != NULL) i++;
    if (i > 0 && strcmp(argv[i - 1], "&") == 0) {     if (i > 0 && strcmp(argv[i - 1], "&") == 0) {
        argv[i - 1] = NULL; // '&' をコマンド引数から削除         argv[i - 1] = NULL; // '&' をコマンド引数から削除
        return 1;         return 1;
    }     }
    return 0;     return 0;
} }
   
.  // リダイレクションを処理する関数
  void handle_redirection(char *argv[]) {
      int i = 0;
   
      while (argv[i] != NULL) {
          const RedirectionRule *rule = NULL; // 該当するリダイレクションルール
          char *redirect_file = NULL;
   
          // リダイレクション記号を検索
          for (size_t j = 0; j < sizeof(redirection_rules) / sizeof(redirection_rules[0]); j++) {
              if (strcmp(argv[i], redirection_rules[j].symbol) == 0) {
                  rule = &redirection_rules[j];
                  break;
              }
          }
   
          if (rule) {
              // リダイレクション記号が見つかった場合の処理
              switch (rule->type) {
                  case REDIR_INPUT:
                  case REDIR_OUTPUT:
                  case REDIR_APPEND:
                      // リダイレクト先のファイル名を取得
                      redirect_file = argv[i + 1];
                      if (!redirect_file) {
                          fprintf(stderr, "Syntax error: No file specified for redirection\n");
                          exit(1);
                      }
   
                      // ファイルを開き、リダイレクションを設定
                      int fd = open(redirect_file, rule->flags, 0644);
                      if (fd == -1) {
                          perror("Redirection failed");
                          exit(1);
                      }
                      if (dup2(fd, rule->target_fd) == -1) {
                          perror("dup2 failed");
                          close(fd);
                          exit(1);
                      }
                      close(fd);
                      break;
   
                  case REDIR_MERGE_STDERR:
                      // 標準エラー出力を標準出力にリダイレクト
                      if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) {
                          perror("dup2 failed");
                          exit(1);
                      }
                      break;
              }
   
              // リダイレクション記号と関連引数を削除
              argv[i] = NULL;
              if (redirect_file) {
                  argv[i + 1] = NULL;
                  i++; // ファイル名をスキップ
              }
          }
          i++;
      }
  }
   
int main() { int main() {
    char cmd[MAX_CMD_LEN]; // コマンド入力用のバッファ     char cmd[MAX_CMD_LEN]; // コマンド入力用のバッファ
    char *argv[MAX_ARGS];  // コマンド引数     char *argv[MAX_ARGS];  // コマンド引数
    char *expanded_argv[MAX_ARGS]; // 展開後の引数     char *expanded_argv[MAX_ARGS]; // 展開後の引数
    pid_t pid, wpid;     pid_t pid, wpid;
    int status, bg;     int status, bg;
   
    // シェルのメインループ     // シェルのメインループ
    while (1) {     while (1) {
        // ゾンビを消滅させる         // ゾンビを消滅させる
        while ((wpid = waitpid(-1, &status, WNOHANG)) > 0)         while ((wpid = waitpid(-1, &status, WNOHANG)) > 0)
            fprintf(stderr, "process %d done\n", wpid);             fprintf(stderr, "process %d done\n", wpid);
   
        // プロンプトを表示         // プロンプトを表示
        fprintf(stderr, "mysh> ");         fprintf(stderr, "mysh> ");
   
        // コマンドを読み込む         // コマンドを読み込む
        if (fgets(cmd, sizeof(cmd), stdin) == NULL) {         if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
            if (feof(stdin)) {             if (feof(stdin)) {
                break; // EOFが入力された場合、終了                 break; // EOFが入力された場合、終了
            }             }
            perror("fgets failed");             perror("fgets failed");
            continue;             continue;
        }         }
   
        // コマンドを解析         // コマンドを解析
        parse_command(cmd, argv);         parse_command(cmd, argv);
   
        // 空のコマンドの場合は次の入力へスキップ         // 空のコマンドの場合は次の入力へスキップ
        if (argv[0] == NULL) {         if (argv[0] == NULL) {
            continue;             continue;
        }         }
   
        // 終了コマンドの場合         // 終了コマンドの場合
        if (strcmp(argv[0], "exit") == 0) {         if (strcmp(argv[0], "exit") == 0) {
            break;             break;
        }         }
   
        // バックグラウンド実行判定         // バックグラウンド実行判定
        bg = is_background_command(argv);         bg = is_background_command(argv);
   
        // 引数を展開         // 引数を展開
        if (expand_arguments(argv, expanded_argv) == -1) {         if (expand_arguments(argv, expanded_argv) == -1) {
            fprintf(stderr, "Argument expansion failed\n");             fprintf(stderr, "Argument expansion failed\n");
            continue;             continue;
        }         }
   
        // 組み込みコマンド判定・実行         // 組み込みコマンド判定・実行
        if (built_in(expanded_argv, bg) == 0) {         if (built_in(expanded_argv, bg) == 0) {
            continue;             continue;
        }         }
   
        // 子プロセスを作成してコマンドを実行         // 子プロセスを作成してコマンドを実行
        pid = fork();         pid = fork();
        if (pid == -1) {         if (pid == -1) {
            perror("fork failed");             perror("fork failed");
            exit(1);             exit(1);
        }         }
   
        // 子プロセス         // 子プロセス
        if (pid == 0) {         if (pid == 0) {
.              // リダイレクションの処理
              handle_redirection(expanded_argv);
   
            // execvpを使用してコマンドを実行             // execvpを使用してコマンドを実行
            if (execvp(expanded_argv[0], expanded_argv) == -1) {             if (execvp(expanded_argv[0], expanded_argv) == -1) {
                perror("exec failed");                 perror("exec failed");
                exit(1);                 exit(1);
            }             }
        }         }
   
        // 親プロセス         // 親プロセス
        while (1) {         while (1) {
            // バックグラウンド実行の場合             // バックグラウンド実行の場合
            if (bg) {             if (bg) {
                fprintf(stderr, "Background process started with PID: %d\n", pid);                 fprintf(stderr, "Background process started with PID: %d\n", pid);
                break; // バックグラウンド実行の場合は待機しない                 break; // バックグラウンド実行の場合は待機しない
            }             }
            // バックグラウンド実行でない場合             // バックグラウンド実行でない場合
            if (waitpid(pid, &status, 0) == (pid_t)-1) { // 子プロセスの終了を待つ             if (waitpid(pid, &status, 0) == (pid_t)-1) { // 子プロセスの終了を待つ
                if (errno == EINTR) {                 if (errno == EINTR) {
                    continue; // 割り込み発生時はリトライする                     continue; // 割り込み発生時はリトライする
                } else {                 } else {
                    perror("waitpid failed");                     perror("waitpid failed");
                    exit(1);                     exit(1);
                }                 }
            }             }
            break;             break;
        }         }
   
        // メモリを解放         // メモリを解放
        for (int i = 0; expanded_argv[i] != NULL; i++) {         for (int i = 0; expanded_argv[i] != NULL; i++) {
            free(expanded_argv[i]);             free(expanded_argv[i]);
        }         }
    }     }
   
    printf("Exiting shell...\n");     printf("Exiting shell...\n");
    return 0;     return 0;
} }