Created
December 9, 2015 13:34
-
-
Save koturn/20ec2b6ffee97ee068a1 to your computer and use it in GitHub Desktop.
クソコードなシェル実装
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* @file shell.c | |
* @brief 簡易シェル実装 | |
*/ | |
#include <signal.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <fcntl.h> | |
#include <sys/wait.h> | |
#include <sys/types.h> | |
#include <unistd.h> | |
#ifndef __UNUSED__ | |
# if defined(__cplusplus) | |
# define __UNUSED__(x) | |
# elif defined(__GNUC__) | |
# define __UNUSED__(x) __UNUSED___ ## x __attribute__((unused)) | |
# elif defined(_MSC_VER) | |
# define __UNUSED__(x) __pragma(warning(suppress: 4100)) x | |
# elif defined(__LCLINT__) | |
# define __UNUSED__(x) /*@unused@*/ x | |
# else | |
# define __UNUSED__(x) x | |
# endif | |
#endif | |
#define MAX_ARGS 256 | |
#define MAX_LEN 8192 | |
#define MAX_CMDS 256 | |
#define MAX_PATH_LEN 8192 | |
#ifndef TRUE | |
# define TRUE 1 | |
#endif | |
#ifndef FALSE | |
# define FALSE 0 | |
#endif | |
static void show_prompt(void); | |
static void separate_input(char *input, int *argc, char *argv[]); | |
static int check_builtin_cmd(int argc, char *argv[]); | |
static void invoke_child(char *argv[], int cndpos); | |
static void sigint_handler(int sig); | |
/*! | |
* @brief プログラムのエントリポイント | |
* @return 終了コード | |
*/ | |
int main(void) | |
{ | |
static char input[MAX_LEN]; | |
static char *sh_argv[MAX_ARGS]; | |
int sh_argc, i; | |
int status; | |
int fds[2]; | |
int cmdpos; | |
int _fd, redir_fd; | |
if (signal(SIGINT, sigint_handler) == SIG_ERR) { | |
perror("signal"); | |
return EXIT_FAILURE; | |
} | |
while (1) { | |
cmdpos = 0; | |
/* プロンプトの表示 */ | |
show_prompt(); | |
if (fgets(input, sizeof(input), stdin) == NULL) { | |
return EXIT_SUCCESS; | |
} | |
if (!strcmp(input, "\n")) continue; /* 改行だけが入力されたとき */ | |
separate_input(input, &sh_argc, sh_argv); | |
/* シェル組み込みコマンドの確認 */ | |
if (check_builtin_cmd(i - cmdpos, &sh_argv[cmdpos])) { | |
continue; | |
} | |
for (i = 0; i < sh_argc; i++) { | |
if (strcmp(sh_argv[i], "|")) continue; | |
redir_fd = 1; | |
if (i - cmdpos > 2) { | |
if (!strcmp(sh_argv[i - 2], ">")) { | |
redir_fd = open(sh_argv[i - 1], O_BINARY | O_CREAT | O_WRONLY | S_IWRITE, S_IREAD | S_IWRITE); | |
i -= 2; | |
} else if (!strcmp(sh_argv[i - 2], ">>")) { | |
redir_fd = open(sh_argv[i - 1], O_BINARY | O_APPEND | O_CREAT | O_WRONLY | S_IWRITE, S_IREAD | S_IWRITE); | |
i -= 2; | |
} | |
} | |
sh_argv[i] = NULL; | |
if (cmdpos > 0) { | |
if ((_fd = dup(fds[0])) == -1) { | |
perror("dup"); | |
return EXIT_FAILURE; | |
} | |
close(fds[0]); | |
} | |
pipe(fds); | |
switch (fork()) { | |
case -1: | |
perror("fork"); | |
return EXIT_FAILURE; | |
case 0: | |
if (cmdpos > 0) { | |
if (dup2(_fd, 0) == -1) { | |
perror("dup2"); | |
return EXIT_FAILURE; | |
} | |
close(_fd); | |
} | |
if (dup2(fds[1], redir_fd) == -1) { | |
perror("dup2"); | |
return EXIT_FAILURE; | |
} | |
close(fds[1]); | |
invoke_child(sh_argv, cmdpos); | |
return EXIT_FAILURE; | |
default: | |
if (cmdpos > 0) { | |
close(_fd); | |
} | |
close(fds[1]); | |
wait(&status); | |
cmdpos = i + 1; | |
break; | |
} | |
if (redir_fd != 1) { | |
i += 2; | |
} | |
} | |
if (cmdpos > 0) { | |
if ((_fd = dup(fds[0])) == -1) { | |
perror("dup"); | |
return EXIT_FAILURE; | |
} | |
close(fds[0]); | |
} | |
/* シェル組み込みコマンドの確認 */ | |
if (check_builtin_cmd(i - cmdpos, &sh_argv[cmdpos])) { | |
continue; | |
} | |
redir_fd = 1; | |
if (i - cmdpos > 2) { | |
if (!strcmp(sh_argv[i - 2], ">")) { | |
redir_fd = open(sh_argv[i - 1], O_BINARY | O_TRUNC | O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); | |
sh_argv[i - 2] = NULL; | |
i -= 2; | |
} else if (!strcmp(sh_argv[i - 2], ">>")) { | |
redir_fd = open(sh_argv[i - 1], O_BINARY | O_APPEND | O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); | |
sh_argv[i - 2] = NULL; | |
i -= 2; | |
} | |
} | |
switch (fork()) { | |
case -1: | |
perror("fork"); | |
return EXIT_FAILURE; | |
case 0: /* 子プロセス */ | |
if (cmdpos > 0) { | |
if (dup2(_fd, 0) == -1) { | |
perror("dup2"); | |
return EXIT_FAILURE; | |
} | |
close(_fd); | |
} | |
if (redir_fd != 1) { | |
close(1); | |
if (dup2(redir_fd, 1) == -1) { | |
perror("dup2"); | |
} | |
close(redir_fd); | |
} | |
invoke_child(sh_argv, cmdpos); | |
return EXIT_FAILURE; | |
default: /* 親プロセス */ | |
if (cmdpos > 0) { | |
close(_fd); | |
} | |
wait(&status); | |
break; | |
} | |
} | |
return EXIT_SUCCESS; | |
} | |
/*! | |
* @brief プロンプトを表示する | |
*/ | |
static void show_prompt(void) | |
{ | |
static char cwd[MAX_PATH_LEN]; | |
const char *username; | |
if (getcwd(cwd, sizeof(cwd)) == NULL) { | |
perror("getcwd"); | |
} | |
if ((username = getenv("USERNAME")) == NULL) { | |
username = ""; | |
} | |
printf("[%s] %s $ ", cwd, username); | |
fflush(stdout); | |
} | |
/*! | |
* @brief 入力をホワイトスペース(半角スペース, タブ)で分割する | |
* @param [in] input 入力行 | |
* @param [out] argc コマンドライン引数の数 | |
* @param [out] argv[] コマンドライン引数 | |
*/ | |
static void | |
separate_input(char *input, int *argc, char *argv[]) | |
{ | |
char *cp = input; | |
for (*argc = 0; *argc < MAX_ARGS; (*argc)++) { | |
if ((argv[*argc] = strtok(cp, " \t\n")) == NULL) { | |
break; | |
} | |
cp = NULL; | |
} | |
} | |
/*! | |
* @brief 組み込みコマンドかどうかをチェックし,組み込みコマンドであれば実行する | |
* @param [in] argc コマンドライン引数の数 | |
* @param [in] argv コマンドライン引数の配列 | |
* @return 組み込みコマンドであれば1を,そうでなければ0を返す | |
*/ | |
static int check_builtin_cmd(int argc, char *argv[]) | |
{ | |
if (!strcmp(argv[0], "cd")) { | |
if (argc == 1) { | |
chdir(getenv("HOME")); | |
} else if (argc == 2) { | |
chdir(argv[1]); | |
} else { | |
fprintf(stderr, "cd: too many arguments\n"); | |
} | |
return TRUE; | |
} else if (!strcmp(argv[0], "exit")) { | |
if (argc == 1) { | |
exit(EXIT_SUCCESS); | |
} else if (argc == 2) { | |
exit(atoi(argv[1])); | |
} else { | |
fprintf(stderr, "exit: too many arguments\n"); | |
} | |
return TRUE; | |
} | |
return FALSE; | |
} | |
/*! | |
* @brief forkされた子プロセスをexecする | |
* @param [in] argc コマンドライン引数の数 | |
* @param [in] argv コマンドライン引数の配列 | |
*/ | |
static void invoke_child(char *argv[], int cmdpos) | |
{ | |
execvp(argv[cmdpos], &argv[cmdpos]); | |
fprintf(stderr, "shell: command not found: %s\n", argv[0]); | |
exit(EXIT_FAILURE); /* エラーが発生すると制御が戻るので,エラー終了する */ | |
} | |
/*! | |
* @brief CTRL-Cが押されたときのシグナルハンドラ | |
* @param [in] sig シグナルID | |
*/ | |
static void sigint_handler(int __UNUSED__(sig)) | |
{ | |
putchar('\n'); | |
show_prompt(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment