| #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 |
| #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 |
| |
| |
| #define MAX_CMD_LEN 1024 |
| #define MAX_CMD_LEN 1024 |
| #define MAX_ARGS 100 |
| #define MAX_ARGS 100 |
| |
| |
. | |
| // 組み込みコマンド関連の宣言 |
| |
| int cd (char *argv[]); |
| |
| int pwd(char *argv[]); |
| |
| struct builtin { |
| |
| char cmd[256]; // コマンド名 |
| |
| int (*func)(char *argv[]); // 関数へのポインタ |
| |
| }; |
| |
| struct builtin blt_in[] = {{"cd",cd}, {"pwd",pwd}, {"",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; |
| } |
| } |
| |
| |
. | |
| // 組み込みコマンドの判定・実行 |
| |
| int built_in(char *argv[]){ |
| |
| struct builtin *p; |
| |
| // 組み込みコマンド配列(blt_in)の走査 |
| |
| for (p = blt_in; *p->cmd != '\0'; p++) { |
| |
| // 組み込みコマンドでない場合は次をチェック |
| |
| if (strcmp(p->cmd, argv[0]) != 0) continue; |
| |
| // 組み込みコマンドの関数を呼び出し |
| |
| (p->func)(argv); |
| |
| return 0; |
| |
| } |
| |
| return -1; |
| |
| } |
| |
| |
| |
| // cd コマンド |
| |
| int cd(char *argv[]) { |
| |
| if (argv[1] == NULL) { |
| |
| // 引数なしの場合はホームディレクトリに移動 |
| |
| const char *home_dir = getenv("HOME"); |
| |
| if (home_dir == NULL) { |
| |
| perror("cd: HOME not set"); |
| |
| return -1; |
| |
| } else { |
| |
| if (chdir(home_dir) != 0) { |
| |
| perror("cd failed"); |
| |
| return -1; |
| |
| } |
| |
| } |
| |
| } else { |
| |
| // 引数が指定された場合、そのディレクトリに移動 |
| |
| if (chdir(argv[1]) != 0) { |
| |
| perror("cd failed"); |
| |
| return -1; |
| |
| } |
| |
| } |
| |
| return 0; |
| |
| } |
| |
| |
| |
| // pwd コマンド |
| |
| int pwd(char *argv[]){ |
| |
| char buf[PATH_MAX+1]; |
| |
| if (getcwd(buf, sizeof(buf)) == NULL) { |
| |
| perror("getcwd failed"); |
| |
| return -1; |
| |
| } |
| |
| printf("%s\n", buf); |
| |
| return 0; |
| |
| } |
| |
| |
| 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; |
| pid_t pid; |
| int status; |
| int status; |
| |
| |
| // シェルのメインループ |
| // シェルのメインループ |
| while (1) { |
| while (1) { |
| // プロンプトを表示 |
| // プロンプトを表示 |
| 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; |
| } |
| } |
| |
| |
| // 引数を展開 |
| // 引数を展開 |
| 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; |
| |
| } |
| |
| |
| |
| // 組み込みコマンド判定・実行 |
| |
| if (built_in(expanded_argv) == 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) { |
| // 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 (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; |
| } |
| } |
| |
| |