#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <ctype.h> #include <fcntl.h> #include <dirent.h> #include <signal.h> #include <sys/wait.h> #define MAXLENGTH 1000 #define MAXDIRECTORY 1024 #define MAXSUSPEND 100 int main() { char *baseDirectory = malloc(sizeof(char) * MAXDIRECTORY); char *curDirectory = malloc(sizeof(char) * MAXDIRECTORY); getcwd(baseDirectory, MAXDIRECTORY); strcpy(curDirectory, baseDirectory); struct Job { pid_t pid; char *pname; } suspendedJobs[MAXSUSPEND]; int suspendedJobCount = 0; signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTSTP, SIG_IGN); while (1) { // Get the base name and print the prompt char *curBaseName = strrchr(curDirectory, '/'); printf("[nyush %s]$ ", (curBaseName && strcmp(curBaseName + 1, "")) ? curBaseName + 1 : curDirectory); fflush(stdout); // Get the command line as a string char *cmdLine = malloc(sizeof(char) * MAXLENGTH); if (fgets(cmdLine, MAXLENGTH, stdin) == NULL) break; // Parse the command line by spaces as an array of tokens char *cmdLineBuf = malloc(sizeof(char) * (strlen(cmdLine) + 1)); strcpy(cmdLineBuf, cmdLine); int cmdLineTokenCount = 0; char **cmdLineTokens = malloc(sizeof(char *) * MAXLENGTH); char *cmdLineToken = strtok(cmdLineBuf, " \t\n"); while (cmdLineToken != NULL) { cmdLineTokens[cmdLineTokenCount ++] = cmdLineToken; cmdLineToken = strtok(NULL, " \t\n"); } cmdLineTokens[cmdLineTokenCount] = NULL; // Parse the command line by pipes as an array of subcmd structs struct subcmd { char **cmd; int cmdLength; char *inFile; char *outFile; int outType; } *subcmds = malloc(sizeof(struct subcmd) * MAXLENGTH); int subcmdError = 0; int tokenIndex = 0; int subcmdCount = 0; while (tokenIndex < cmdLineTokenCount) { subcmds[subcmdCount] = (struct subcmd) { .cmd = malloc(sizeof(char *) * MAXLENGTH), .cmdLength = 0, .inFile = NULL, .outFile = NULL, .outType = 0, }; while (tokenIndex < cmdLineTokenCount && strcmp(cmdLineTokens[tokenIndex], "|")) { if (!strcmp(cmdLineTokens[tokenIndex], "<")) { if (subcmds[subcmdCount].inFile == NULL) subcmds[subcmdCount].inFile = cmdLineTokens[++ tokenIndex]; else { subcmdError = -1; tokenIndex ++; } if (tokenIndex >= cmdLineTokenCount) subcmdError = -4; } else if (!strcmp(cmdLineTokens[tokenIndex], ">")) { if (subcmds[subcmdCount].outFile == NULL) subcmds[subcmdCount].outFile = cmdLineTokens[++ tokenIndex]; else { subcmdError = -2; tokenIndex ++; } if (tokenIndex >= cmdLineTokenCount) subcmdError = -5; } else if (!strcmp(cmdLineTokens[tokenIndex], ">>")) { if (subcmds[subcmdCount].outFile == NULL) { subcmds[subcmdCount].outFile = cmdLineTokens[++ tokenIndex]; subcmds[subcmdCount].outType = 1; } else { subcmdError = -3; tokenIndex ++; } if (tokenIndex >= cmdLineTokenCount) subcmdError = -6; } else subcmds[subcmdCount].cmd[subcmds[subcmdCount].cmdLength ++] = cmdLineTokens[tokenIndex]; tokenIndex ++; } if (subcmds[subcmdCount].cmdLength == 0) subcmdError = -7; subcmdCount ++; tokenIndex ++; } // Check command line errors if (subcmdError < 0) { fprintf(stderr, "Error: invalid command\n"); continue; } // Create pipes pid_t pid[subcmdCount]; int status; int *fds = malloc(sizeof(int) * (2 * subcmdCount - 2)); for (int i = 0; i < 2 * subcmdCount - 3; i += 2) { if (pipe(fds + i) == -1) continue; } // Execute each subcommand for (int i = 0; i < subcmdCount; i ++) { char **curcmd = subcmds[i].cmd; int curcmdLength = subcmds[i].cmdLength; // Check for and execute built-in commands if (!strcmp(curcmd[0], "exit")) { if (curcmdLength != 1 || subcmdCount != 1) fprintf(stderr, "Error: invalid command\n"); else if (suspendedJobCount != 0) fprintf(stderr, "Error: there are suspended jobs\n"); else exit(0); continue; } else if (!strcmp(curcmd[0], "jobs")) { if (curcmdLength != 1 || subcmdCount != 1) { fprintf(stderr, "Error: invalid command\n"); continue; } for (int i = 0; i < suspendedJobCount; i ++) printf("[%d] %s\n", i + 1, suspendedJobs[i].pname); continue; } else if (!strcmp(curcmd[0], "fg")) { if (curcmdLength != 2 || subcmdCount != 1) { fprintf(stderr, "Error: invalid command\n"); continue; } int jobIndex = atoi(curcmd[1]); if (jobIndex <= 0 || jobIndex > suspendedJobCount) { fprintf(stderr, "Error: invalid job\n"); continue; } pid_t curpid = suspendedJobs[jobIndex - 1].pid; char *curpname = suspendedJobs[jobIndex - 1].pname; kill(curpid, SIGCONT); for (int j = jobIndex - 1; j < suspendedJobCount; j ++) suspendedJobs[j] = suspendedJobs[j + 1]; suspendedJobCount --; int status; waitpid(curpid, &status, WUNTRACED); if (WIFSTOPPED(status)) { suspendedJobs[suspendedJobCount].pid = curpid; suspendedJobs[suspendedJobCount].pname = malloc(sizeof(char) * (strlen(curpname) + 1)); strcpy(suspendedJobs[suspendedJobCount ++].pname, curpname); } continue; } else if (!strcmp(curcmd[0], "cd")) { if (curcmdLength != 2 || subcmdCount != 1) fprintf(stderr, "Error: invalid command\n"); else if (chdir(curcmd[1]) == 0) getcwd(curDirectory, MAXDIRECTORY); else fprintf(stderr, "Error: invalid directory\n"); continue; } pid[i] = fork(); suspendedJobs[suspendedJobCount].pid = pid[i]; suspendedJobs[suspendedJobCount].pname = malloc(sizeof(char) * (strlen(cmdLine) + 1)); strcpy(suspendedJobs[suspendedJobCount].pname, cmdLine); suspendedJobs[suspendedJobCount ++].pname[strlen(cmdLine) - 1] = '\0'; if (pid[i] < 0) continue; else if (pid[i] == 0) { if (subcmds[i].inFile != NULL) { int infd = open(subcmds[i].inFile, O_RDONLY, S_IRWXU); if (infd == -1) { fprintf(stderr, "Error: invalid file\n"); exit(-1); } else { dup2(infd, 0); close(infd); } } if (subcmds[i].outFile != NULL) { int outfd; if (subcmds[i].outType == 0) outfd = open(subcmds[i].outFile, O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU); else outfd = open(subcmds[i].outFile, O_CREAT|O_WRONLY|O_APPEND, S_IRWXU); if (outfd == -1) { fprintf(stderr, "Error: invalid file\n"); exit(-1); } else { dup2(outfd, 1); close(outfd); } } if (i != 0) dup2(fds[2 * i - 2], 0); if (i != subcmdCount - 1) dup2(fds[2 * i + 1], 1); for (int i = 0; i < 2 * subcmdCount - 2; i ++) close(fds[i]); signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGTSTP, SIG_DFL); if (strrchr(curcmd[0], '/') == NULL) { char *path = malloc(sizeof(char) * MAXLENGTH); strcpy(path, "/usr/bin/"); strcat(path, curcmd[0]); if (access(path, F_OK) != 0) { fprintf(stderr, "Error: invalid program\n"); exit(-1); } } if (execvp(curcmd[0], curcmd) == -1) { fprintf(stderr, "Error: invalid program\n"); exit(-1); } } } // Close all pipes and wait for child processes for (int i = 0; i < 2 * subcmdCount - 2; i ++) close(fds[i]); for (int i = 0; i < subcmdCount; i ++) { pid_t curpid = waitpid(pid[i], &status, WUNTRACED); if (!WIFSTOPPED(status)) { for (int i = 0; i < suspendedJobCount; i ++) { if (suspendedJobs[i].pid == curpid) { for (int j = i; j < suspendedJobCount; j ++) suspendedJobs[j] = suspendedJobs[j + 1]; suspendedJobCount --; break; } } } } } return 0; }