Skip to content

Instantly share code, notes, and snippets.

@koturn
Created December 9, 2015 13:34
Show Gist options
  • Save koturn/20ec2b6ffee97ee068a1 to your computer and use it in GitHub Desktop.
Save koturn/20ec2b6ffee97ee068a1 to your computer and use it in GitHub Desktop.
クソコードなシェル実装
/*!
* @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