Last active
June 27, 2020 18:53
-
-
Save koturn/b2832db22238588fee4e 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
### This Makefile was written for GNU Make. ### | |
ifeq ($(DEBUG),true) | |
COPTFLAGS := -O0 -g3 -ftrapv -fstack-protector-all -D_FORTIFY_SOURCE=2 | |
LDLIBS += -lssp | |
else | |
ifeq ($(OPT),true) | |
COPTFLAGS := -flto -Ofast -march=native -DNDEBUG | |
LDOPTFLAGS := -flto -Ofast -s | |
else | |
ifeq ($(LTO),true) | |
COPTFLAGS := -flto -DNDEBUG | |
LDOPTFLAGS := -flto | |
else | |
COPTFLAGS := -O3 -DNDEBUG | |
LDOPTFLAGS := -O3 -s | |
endif | |
endif | |
endif | |
ifeq ($(OMP),true) | |
COPTFLAGS := -fopenmp | |
LDOPTFLAGS := -fopenmp | |
else | |
COPTFLAGS := -Wno-unknown-pragmas | |
endif | |
C_WARNING_FLAGS := -Wall -Wextra -Wformat=2 -Wstrict-aliasing=2 \ | |
-Wcast-align -Wcast-qual -Wconversion \ | |
-Wfloat-equal -Wpointer-arith -Wswitch-enum \ | |
-Wwrite-strings -pedantic | |
CC := gcc | |
# MACROS := -DMACRO | |
# INCS := -I./include | |
CFLAGS := -pipe $(C_WARNING_FLAGS) $(COPTFLAGS) $(INCS) $(MACROS) $(if $(STD), $(addprefix -std=, $(STD)),) | |
LDFLAGS := -pipe $(LDOPTFLAGS) | |
# LDLIBS := -lm | |
TARGET := shell | |
OBJS := $(addsuffix .o, $(basename $(TARGET))) | |
SRCS := $(OBJS:%.o=%.c) | |
ifeq ($(OS),Windows_NT) | |
TARGET := $(addsuffix .exe, $(TARGET)) | |
else | |
TARGET := $(addsuffix .out, $(TARGET)) | |
endif | |
%.exe: | |
$(CC) $(LDFLAGS) $(filter %.c %.o, $^) $(LDLIBS) -o $@ | |
%.out: | |
$(CC) $(LDFLAGS) $(filter %.c %.o, $^) $(LDLIBS) -o $@ | |
.PHONY: all | |
all: $(TARGET) | |
$(TARGET): $(OBJS) | |
.PHONY: clean | |
clean: | |
$(RM) $(TARGET) $(OBJS) | |
.PHONY: cleanobj | |
cleanobj: | |
$(RM) $(OBJS) |
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 <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 int check_buildin_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]; | |
char *cp; | |
int sh_argc; | |
int status; | |
int fds[2]; | |
int i, cmdpos, _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; /* 改行だけが入力されたとき */ | |
cp = input; | |
for (sh_argc = 0; sh_argc < MAX_ARGS; sh_argc++) { | |
if ((sh_argv[sh_argc] = strtok(cp, " \t\n")) == NULL) { | |
break; | |
} | |
cp = NULL; | |
} | |
for (i = 0; i < sh_argc; i++) { | |
if (strcmp(sh_argv[i], "|")) continue; | |
sh_argv[i] = NULL; | |
if (cmdpos > 0) { | |
if ((_fd = dup(fds[0])) == -1) { | |
perror("dup"); | |
return EXIT_FAILURE; | |
} | |
close(fds[0]); | |
} | |
pipe(fds); | |
/* シェル組み込みコマンドの確認 */ | |
if (check_buildin_cmd(i - cmdpos, &sh_argv[cmdpos])) { | |
continue; | |
} | |
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], 1) == -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 (cmdpos > 0) { | |
if ((_fd = dup(fds[0])) == -1) { | |
perror("dup"); | |
return EXIT_FAILURE; | |
} | |
close(fds[0]); | |
} | |
/* シェル組み込みコマンドの確認 */ | |
if (check_buildin_cmd(i - cmdpos, &sh_argv[cmdpos])) { | |
continue; | |
} | |
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); | |
} | |
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] argc コマンドライン引数の数 | |
* @param [in] argv コマンドライン引数の配列 | |
* @return 組み込みコマンドであれば1を,そうでなければ0を返す | |
*/ | |
static int check_buildin_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