diff --git a/yeeshell b/yeeshell new file mode 100755 index 0000000..cbf51a2 Binary files /dev/null and b/yeeshell differ diff --git a/yeeshell.c b/yeeshell.c new file mode 100644 index 0000000..4338544 --- /dev/null +++ b/yeeshell.c @@ -0,0 +1,415 @@ +#include +#include +#include +#include +#include +#include +#include +#include "yeeshell.h" + +/* record cmdline history */ +char **history = NULL; +int cmdline_amount = 0; + +/* next job ID to allocate */ +int nextjid = 1; + +/* environment variable */ +extern char **environ; + +/* The job list */ +struct job_t jobs[JOBS_MAX_QUANTITY]; + +int main() +{ + char *cmdline = NULL, *pwd = NULL; + char *args[ARGS_MAX_QUANTITY]; + int status = 1; + pwd = (char *)calloc(PATH_MAX, sizeof(char)); + if (!(history = (char **)calloc(CMDLINE_HISTORY_MAX_QUANTITY, CMDLINE_MAX_SIZE * sizeof(char *)))) + { + printf("yeeshell: allocation error!\n"); + exit(EXIT_FAILURE); + } + + /* execute the shell's read, parse and execution loop */ + do + { + if (!getcwd(pwd, PATH_MAX)) + { + printf("yeeshell: The current path cannot be obtained!\n"); + exit(0); + } + printf("[yeeshell %s]# ", pwd); + cmdline = readline(); + history[cmdline_amount++] = cmdline; + status = execute(cmdline, args); + free(cmdline); + } while (status); + free(history); + exit(EXIT_SUCCESS); +} + +char *readline() +{ + char *cmdline = NULL; + ssize_t bufsize = 0; + getline(&cmdline, &bufsize, stdin); + return cmdline; +} + +int parseline(char *cmdline, char **args) +{ + static char array[CMDLINE_MAX_SIZE]; /* holds local copy of command line */ + char *buf = array; /* ptr that traverses command line */ + char *delim; /* points to first space delimiter */ + int argc; /* number of args */ + int bg; /* background job? */ + + strcpy(buf, cmdline); + buf[strlen(buf) - 1] = ' '; /* replace trailing '\n' with space */ + while (*buf && (*buf == ' ')) /* ignore leading spaces */ + { + buf++; + } + + /* Build the argv list */ + argc = 0; + if (*buf == '\'') + { + buf++; + delim = strchr(buf, '\''); + } + else + { + delim = strchr(buf, ' '); + } + + while (delim) + { + args[argc++] = buf; + *delim = '\0'; + buf = delim + 1; + while (*buf && (*buf == ' ')) /* ignore spaces */ + { + buf++; + } + + if (*buf == '\'') + { + buf++; + delim = strchr(buf, '\''); + } + else + { + delim = strchr(buf, ' '); + } + } + args[argc] = NULL; + + if (argc == 0) /* ignore blank line */ + { + return 1; + } + + /* should the job run in the background? */ + if ((bg = (*args[argc - 1] == '&')) != 0) + { + args[--argc] = NULL; + } + return bg; +} + +int execute(char *cmdline, char **args) +{ + int bg = 0, i = 0; + pid_t pid; + sigset_t mask_all, mask_prev; + sigprocmask(SIG_BLOCK, NULL, &mask_all); + sigaddset(&mask_all, SIGCHLD); + bg = parseline(cmdline, args); + + if (args[0] == NULL) + { + return 1; + } + if (!built_in(args)) + { + /* Prevent child processes from ending between parent processes, that is, between addJob and deleteJob. */ + sigprocmask(SIG_BLOCK, &mask_all, &mask_prev); /* Shield SIGCHLD */ + + if ((pid = fork()) == 0) /* Child process */ + { + /* The child process inherits the parent's signal mask and will inherit it after exec, + so it needs to restore the signal mask before executing. */ + sigprocmask(SIG_SETMASK, &mask_prev, NULL); /* Child process, unblock SIGCHLD */ + + /* Set the pid of the current process to the group number of the process group it belongs to. */ + /* avoid being grouped with tsh */ + setpgid(0, 0); + if (execve(args[0], args, environ) <= 0) + { + printf("%s: Command not found\n", args[0]); + exit(0); + } + } + else + { + do + { + } while (!args[i++]); + + if (bg) /* bg task */ + { + addjob(jobs, pid, BG, cmdline); + } + else /* fg task */ + { + addjob(jobs, pid, FG, cmdline); + } + + sigprocmask(SIG_SETMASK, &mask_prev, NULL); + + if (bg) /* Don't wait for background tasks to finish */ + { + printf("[%d](%d)%s", pid2jid(pid), pid, cmdline); + } + else /* Wait for foreground tasks to finish */ + { + waitfg(pid); + } + } + } + return 1; +} + +int built_in(char **args) +{ + if (!strcmp(args[0], "exit")) + { + exit(0); + } + else if (!strcmp(args[0], "cd")) + { + return builtin_cd(args); + } + else if (!strcmp(args[0], "history")) + { + return builtin_history(args); + } + else if (!strcmp(args[0], "mytop")) + { + return builtin_mytop(args); + } + else if (!strcmp(args[0], "ls")) + { + return builtin_ls(args); + } + else if (!strcmp(args[0], "jobs")) + { + return builtin_jobs(args); + } + else + { + return -1; + } +} + +int builtin_cd(char **args) +{ + if (args[1] == NULL) + { + return 1; + } + else + { + if (chdir(args[1]) != 0) + { + perror("yeeshell"); + } + return 1; + } +} + +int builtin_ls(char **args) +{ + DIR *dp; + struct dirent *dirp; + char *path = NULL; + + if (args[1] == NULL) + { + path = (char *)calloc(PATH_MAX, sizeof(char)); + getcwd(path, PATH_MAX) + } + else if (args[1]) + + if ((dp = opendir(args[1])) == NULL) + { + perror("yeeshell"); + } + while ((dirp = readdir(dp)) != NULL) + { + printf("%s\n", dirp->d_name); + } + + closedir(dp); + return 0; +} + +int builtin_history(char **args) +{ + int n = atoi(args[1]); + printf("ID\tCommandline\n"); + for (int i = 0; i < ((n < cmdline_amount) ? n : cmdline_amount); i++) + { + printf("%d\t%s\n", i, history[i]); + } + return 1; +} + +int builtin_jobs(char **args) +{ + /* To prevent interruptions, block all signals. */ + sigset_t mask_all, mask_prev; + sigfillset(&mask_all); + sigprocmask(SIG_SETMASK, &mask_all, &mask_prev); + + for (int i = 0; i < JOBS_MAX_QUANTITY; i++) + { + if (jobs[i].pid != 0) + { + printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid); + switch (jobs[i].state) + { + case BG: + printf("Running "); + break; + case FG: + printf("Foreground "); + break; + case ST: + printf("Stopped "); + break; + default: + printf("listjobs: Internal error: job[%d].state=%d ", + i, jobs[i].state); + } + printf("%s", jobs[i].cmdline); + } + } + + sigprocmask(SIG_SETMASK, &mask_prev, NULL); /* unclock */ + return 1; +} + +int builtin_mytop(char **args) +{ +} + +int do_bgfg(char **args) +{ +} + +/************************ + * manipulate job list + ************************/ +int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline) +{ + int i; + + if (pid < 1) + return 0; + + for (i = 0; i < JOBS_MAX_QUANTITY; i++) + { + if (jobs[i].pid == 0) + { + jobs[i].pid = pid; + jobs[i].state = state; + jobs[i].jid = nextjid++; + if (nextjid > JOBS_MAX_QUANTITY) + nextjid = 1; + strcpy(jobs[i].cmdline, cmdline); + + printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline); + + return 1; + } + } + printf("Tried to create too many jobs\n"); + return 0; +} + +int pid2jid(pid_t pid) +{ + int i; + + if (pid < 1) + return 0; + for (i = 0; i < JOBS_MAX_QUANTITY; i++) + if (jobs[i].pid == pid) + { + return jobs[i].jid; + } + return 0; +} + +void listjobs(struct job_t *jobs) +{ + for (int i = 0; i < JOBS_MAX_QUANTITY; i++) + { + if (jobs[i].pid != 0) + { + printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid); + switch (jobs[i].state) + { + case BG: + printf("Running "); + break; + case FG: + printf("Foreground "); + break; + case ST: + printf("Stopped "); + break; + default: + printf("listjobs: Internal error: job[%d].state=%d ", i, jobs[i].state); + } + printf("%s", jobs[i].cmdline); + } + } +} + +void waitfg(pid_t pid) +{ + sigset_t m; + sigemptyset(&m); + while (pid == fgpid(jobs)) + { + /* Wake up when there is a signal to check whether the foreground process PID change, */ + /* change means that the foreground process is over. */ + sigsuspend(&m); + } + return; +} + +pid_t fgpid(struct job_t *jobs) +{ + for (int i = 0; i < JOBS_MAX_QUANTITY; i++) + if (jobs[i].state == FG) + return jobs[i].pid; + return 0; +} + +int hide(const char *path) +{ + if (*path == '.') + { + return 1; + } + else + { + return 0; + } +} \ No newline at end of file diff --git a/yeeshell.h b/yeeshell.h new file mode 100644 index 0000000..a3ef706 --- /dev/null +++ b/yeeshell.h @@ -0,0 +1,59 @@ +#pragma once + +#define CMDLINE_MAX_SIZE 1024 /* max length of a single command line */ +#define ARGS_MAX_QUANTITY 128 /* max args on a command line */ +#define BUFFER_MAX_SIZE 64 /* max size of a buffer which contains parsed arguments */ +#define CMDLINE_DIV ' \t\r\n\a' +#define CMDLINE_HISTORY_MAX_QUANTITY 256 +#define JOBS_MAX_QUANTITY 16 +#define PATH_MAX_SIZE 256 + +#define FG 1 /* running in foreground */ +#define BG 2 /* running in background */ +#define ST 3 /* stopped */ + +/* job_t - The job struct */ +struct job_t +{ + pid_t pid; /* job PID */ + int jid; /* job ID [1, 2, ...] */ + int state; /* UNDEF, BG, FG, or ST */ + char cmdline[CMDLINE_MAX_SIZE]; /* command line */ +}; + +/* readline - Get the command line */ +char *readline(); + +/* parseline - Evaluate the command line that the user has just typed in */ +int parseline(char *cmdline, char **args); + +/* execute - Execute the command line */ +int execute(char *cmdline, char **args); + +/* builtin functions - Handle built-in command */ +int built_in(char **args); +int builtin_cd(char **args); +int builtin_ls(char **args); +int builtin_history(char **args); +int builtin_jobs(char **args); +int builtin_mytop(char **args); + +/* do_bgfg - Execute background/foregroung tasks */ +int do_bgfg(char **args); + +/* addjob - Add jobs to joblist */ +int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline); + +/* pide2jid - Map process ID to job ID*/ +int pid2jid(pid_t pid); + +/* waitfg - Wait foreground jobs to finish */ +void waitfg(pid_t pid); + +/* fgpid - Return PID of current foreground job, 0 if no such job */ +pid_t fgpid(struct job_t *jobs); + +/* determine if a file is hidden */ +int hide(const char *path); + +/* */ \ No newline at end of file