Skip to content

Instantly share code, notes, and snippets.

@koturn
Last active June 27, 2020 18:53
Show Gist options
  • Save koturn/b2832db22238588fee4e to your computer and use it in GitHub Desktop.
Save koturn/b2832db22238588fee4e to your computer and use it in GitHub Desktop.
簡易シェル実装
### 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)
/*!
* @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