#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
// CMD 구조체 정의: 명령어 이름, 설명, 함수 포인터 포함
typedef struct CMD
{
char *name; // 명령어 이름
char *desc; // 명령어 설명
int (*cmd)(int argc, char *argv[]); // 명령어 실행 함수 포인터
} CMD;
// 함수 선언
int cmd_cd(int argc, char *argv[]);
int cmd_pwd(int argc, char *argv[]);
int cmd_exit(int argc, char *argv[]);
int cmd_help(int argc, char *argv[]);
int cmdProcessing(void);
int cmd_history(int argc, char *argv[]);
void save_cmd(const char *command);
void load_history();
// 내장 명령어 목록 정의
CMD builtin[] = {
{"cd", "작업 디렉터리 바꾸기", cmd_cd},
{"pwd", "현재 작업 디렉터리", cmd_pwd},
{"exit", "셀 실행을 종료합니다", cmd_exit},
{"help", "도움말 보여주기", cmd_help},
{"history", "명령어 기록 보기", cmd_history},
};
// 내장 명령어 개수
const int builtins = sizeof(builtin) / sizeof(CMD);
// 상수 정의
#define STR_LEN 1024
#define MAX_TOKENS 128
#define HISTORY_FILE "history.txt"
#define MAX_HISTORY 100
// 명령 기록 저장 배열과 개수
char history[MAX_HISTORY][STR_LEN];
int history_count = 0;
// 프로그램의 메인 함수
int main(void) {
int isExit = 0; // 종료 플래그
load_history(); // 이전 기록 불러오기
while (!isExit) {
isExit = cmdProcessing(); // 명령 처리 반복
}
printf("My shell을 종료합니다\n");
return 0;
}
// 명령어 처리 함수
int cmdProcessing(void)
{
char cmdLine[STR_LEN]; // 입력 명령어
char *cmdTokens[MAX_TOKENS]; // 토큰화된 명령어
char delim[] = " \t\n\r"; // 구분자
char *token;
int tokenNum, exitCode = 0;
pid_t pid;
int status;
printf("[mysh v0.1] $ ");
fgets(cmdLine, STR_LEN, stdin); // 명령어 입력
save_cmd(cmdLine); // 명령 기록 저장
tokenNum = 0;
token = strtok(cmdLine, delim);
while (token) {
cmdTokens[tokenNum++] = token; // 토큰 저장
token = strtok(NULL, delim);
}
cmdTokens[tokenNum] = NULL;
if (tokenNum == 0) return exitCode; // 명령어가 없으면 반환
for (int i = 0; i < builtins; ++i) {
if (strcmp(cmdTokens[0], builtin[i].name) == 0) {
return builtin[i].cmd(tokenNum, cmdTokens); // 내장 명령어 실행
}
}
pid = fork(); // 외부 명령어 처리
if (pid == 0) { // 자식 프로세스
if (execvp(cmdTokens[0], cmdTokens) == -1) {
perror("외부 명령 실행 실패");
}
exit(EXIT_FAILURE);
}
if (pid > 0) {
waitpid(pid, &status, 0); // 부모 프로세스: 자식 종료 대기
}
return exitCode;
}
// cd 명령어: 디렉터리 변경
int cmd_cd(int argc, char *argv[]) {
if (argc < 2) {
const char *home = getenv("HOME"); // 홈 디렉터리 경로 가져오기
if (!home) {
fprintf(stderr, "cd: 홈 디렉터리를 찾을 수 없습니다.\n");
return 0;
}
if (chdir(home) != 0) {
perror("cd");
}
} else {
if (chdir(argv[1]) != 0) {
perror("cd");
}
}
return 0;
}
// pwd 명령어: 현재 디렉터리 출력
int cmd_pwd(int argc, char *argv[]) {
char cwd[STR_LEN];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf("%s\n", cwd);
} else {
perror("pwd");
}
return 0;
}
// exit 명령어: 셸 종료
int cmd_exit(int argc, char *argv[]) {
return 1; // 종료 플래그 반환
}
// help 명령어: 명령어 설명 출력
int cmd_help(int argc, char *argv[]) {
if (argc == 2) {
for (int i = 0; i < builtins; ++i) {
if (strcmp(argv[1], builtin[i].name) == 0) {
printf("%s: %s\n", builtin[i].name, builtin[i].desc);
return 0;
}
}
printf("'%s' 명령어를 찾을 수 없습니다.\n", argv[1]);
} else {
printf("지원하는 명령어 목록:\n");
for (int i = 0; i < builtins; ++i) {
printf(" %s: %s\n", builtin[i].name, builtin[i].desc);
}
}
return 0;
}
// history 명령어: 명령어 기록 출력
int cmd_history(int argc, char *argv[]) {
if (argc == 1) {
for (int i = 0; i < history_count; ++i) {
printf("%d %s", i + 1, history[i]);
}
} else {
printf("인자가 너무 많습니다 (관련 옵션 구현 안함)\n");
}
return 0;
}
// 명령어 기록 저장 함수
void save_cmd(const char *command) {
if (history_count == MAX_HISTORY) {
for (int i = 1; i < MAX_HISTORY; ++i) {
strcpy(history[i - 1], history[i]);
}
--history_count;
}
strncpy(history[history_count++], command, STR_LEN);
FILE *file = fopen(HISTORY_FILE, "w");
if (!file) {
perror("history 파일 열기 실패");
return;
}
for (int i = 0; i < history_count; ++i) {
fputs(history[i], file);
}
fclose(file);
}
// 명령어 기록 불러오기 함수
void load_history() {
FILE *file = fopen(HISTORY_FILE, "r");
if (!file) return;
while (fgets(history[history_count], STR_LEN, file)) {
history_count++;
if (history_count >= MAX_HISTORY) break;
}
fclose(file);
}