这是标准答案.
在这个CLI中没有对信号进行任何处理。
#include "wish.h"
#include <ctype.h> // isspace
#include <regex.h> // regcomp, regexec, regfree
#include <stdio.h> // fopen, fclose, fileno, getline, feof
#include <stdlib.h> // exit
#include <sys/types.h>
#include <sys/wait.h> // waitpid
FILE *in = NULL;
char *paths[BUFF_SIZE] = {"/bin", NULL};
char *line = NULL;
void clean(void) {
free(line);
fclose(in);
}
char *trim(char *s) {
// trim leading spaces
while (isspace(*s))
s++;
if (*s == '\0')
return s; // empty string
// trim trailing spaces
char *end = s + strlen(s) - 1;
while (end > s && isspace(*end))
end--;
end[1] = '\0';
return s;
}
void *parseInput(void *arg) {
char *args[BUFF_SIZE];
int args_num = 0;
FILE *output = stdout;
struct function_args *fun_args = (struct function_args *)arg;
char *commandLine = fun_args->command;
char *command = strsep(&commandLine, ">");
if (command == NULL || *command == '\0') {
printError();
return NULL;
}
command = trim(command);
if (commandLine != NULL) {
// contain white space in the middle or ">"
regex_t reg;
if (regcomp(®, "\\S\\s+\\S", REG_CFLAGS) != 0) {
printError();
regfree(®);
return NULL;
}
if (regexec(®, commandLine, 0, NULL, 0) == 0 ||
strstr(commandLine, ">") != NULL) {
printError();
regfree(®);
return NULL;
}
regfree(®);
if ((output = fopen(trim(commandLine), "w")) == NULL) {
printError();
return NULL;
}
}
char **ap = args;
while ((*ap = strsep(&command, " \t")) != NULL)
if (**ap != '\0') {
*ap = trim(*ap);
ap++;
if (++args_num >= BUFF_SIZE)
break;
}
if (args_num > 0)
executeCommands(args, args_num, output);
return NULL;
}
int searchPath(char path[], char *firstArg) {
// search executable file in path
int i = 0;
while (paths[i] != NULL) {
snprintf(path, BUFF_SIZE, "%s/%s", paths[i], firstArg);
if (access(path, X_OK) == 0)
return 0;
i++;
}
return -1;
}
void redirect(FILE *out) {
int outFileno;
if ((outFileno = fileno(out)) == -1) {
printError();
return;
}
if (outFileno != STDOUT_FILENO) {
// redirect output
if (dup2(outFileno, STDOUT_FILENO) == -1) {
printError();
return;
}
if (dup2(outFileno, STDERR_FILENO) == -1) {
printError();
return;
}
fclose(out);
}
}
void executeCommands(char *args[], int args_num, FILE *out) {
// check built-in commands first
if (strcmp(args[0], "exit") == 0) {
if (args_num > 1)
printError();
else {
atexit(clean);
exit(EXIT_SUCCESS);
}
} else if (strcmp(args[0], "cd") == 0) {
if (args_num == 1 || args_num > 2)
printError();
else if (chdir(args[1]) == -1)
printError();
} else if (strcmp(args[0], "path") == 0) {
size_t i = 0;
paths[0] = NULL;
for (; i < args_num - 1; i++)
paths[i] = strdup(args[i + 1]);
paths[i + 1] = NULL;
} else {
// not built-in commands
char path[BUFF_SIZE];
if (searchPath(path, args[0]) == 0) {
pid_t pid = fork();
if (pid == -1)
printError();
else if (pid == 0) {
// child process
redirect(out);
if (execv(path, args) == -1)
printError();
} else
waitpid(pid, NULL, 0); // parent process waits child
} else
printError(); // not fond in path
}
}
int main(int argc, char *argv[]) {
int mode = INTERACTIVE_MODE;
in = stdin;
size_t linecap = 0;
ssize_t nread;
if (argc > 1) {
mode = BATCH_MODE;
if (argc > 2 || (in = fopen(argv[1], "r")) == NULL) {
printError();
exit(EXIT_FAILURE);
}
}
while (1) {
if (mode == INTERACTIVE_MODE)
printf("wish> ");
if ((nread = getline(&line, &linecap, in)) > 0) {
char *command;
int commands_num = 0;
struct function_args args[BUFF_SIZE];
// remove newline character
if (line[nread - 1] == '\n')
line[nread - 1] = '\0';
char *temp = line;
while ((command = strsep(&temp, "&")) != NULL)
if (command[0] != '\0') {
args[commands_num++].command = strdup(command);
if (commands_num >= BUFF_SIZE)
break;
}
for (size_t i = 0; i < commands_num; i++)
if (pthread_create(&args[i].thread, NULL, &parseInput, &args[i]) != 0)
printError();
for (size_t i = 0; i < commands_num; i++) {
if (pthread_join(args[i].thread, NULL) != 0)
printError();
if (args[i].command != NULL)
free(args[i].command);
}
} else if (feof(in) != 0) {
atexit(clean);
exit(EXIT_SUCCESS); // EOF
}
}
return 0;
}
my solution
#include "wish.h"
#include <ctype.h> // isspace
#include <regex.h> // regcomp, regexec, regfree
#include <stdio.h> // fopen, fclose, fileno, getline, feof
#include <stdlib.h> // exit
#include <sys/types.h>
#include <sys/wait.h> // waitpid
#define MAX_COMMAND 20
int parse(char *buf);
void parallel();
char *buf;
size_t size;
char *command[MAX_COMMAND], *path[MAX_COMMAND];
int num_command, num_path;
char *argv[20];
int flag = 0;
void printError()
{
char error_message[30] = "An error has occurred\n";
write(STDERR_FILENO, error_message, strlen(error_message));
}
int parse(char *buf)
{
char *copy = strdup(buf);
char *demi = strdup(" ");
int cnt = 0;
while (copy)
{
argv[cnt] = strsep(©, demi);
if (argv[cnt] == NULL)
break;
++cnt;
}
argv[cnt] = NULL;
return cnt;
}
void call_cd(int argc, char *argv[])
{
if (argc != 2)
{
printError();
return;
}
int res = chdir(argv[1]);
if (res)
return;
char *curpath = (char *)malloc(BUFF_SIZE * 4);
char *tmp = getcwd(curpath, BUFF_SIZE);
if (tmp == NULL)
{
printError();
return;
}
strcat(curpath, argv[1]);
res = chdir(curpath);
if (res)
return;
printError();
return;
}
void change_path(int argc, char *argv[])
{
for (int i = 1; i < argc; i++)
path[i - 1] = argv[i];
return;
}
void call_exit(int argc, char *argv[])
{
if (argc != 1)
{
printError();
return;
}
exit(0);
return;
}
int searchPath(char *path, char *firstArg)
{
char *new_file = strdup(path);
new_file = strcat(new_file, firstArg);
if (access(new_file, X_OK) == 0)
return 1;
return 0;
}
void batch_mode(char *argv[])
{
FILE *fin = fopen(argv[0], "r");
while (getline(&buf, &size, fin) != EOF)
{
if (size == 0)
exit(0);
for (int i = 0;; i++)
if (buf[i] == '\n')
{
buf[i] = '\0';
break;
}
parallel();
memset(buf, 0, sizeof(buf));
}
}
void do_command(int argc, char *argv[])
{
if (strcmp(argv[0], "cd") == 0)
{
call_cd(argc, argv);
return;
}
if (strcmp(argv[0], "path") == 0)
{
change_path(argc, argv);
return;
}
if (strcmp(argv[0], "exit") == 0)
{
call_exit(argc, argv);
return;
}
if (access(argv[0], R_OK) == 0)
{
batch_mode(argv);
exit(0);
}
for (int i = 0; i < num_path; i++)
{
if (searchPath(path[i], argv[0]) == 1)
{
pid_t pid;
pid = fork();
switch (pid)
{
case -1:
{
printError();
break;
}
case 0:
{
char *new_path = strdup(path[i]);
new_path = strcat(path[i], argv[0]);
execv(new_path, argv);
printError();
exit(1);
}
default:
{
int status;
waitpid(pid, &status, 0); // 等待子进程返回
int err = WEXITSTATUS(status); // 读取子进程的返回码
if (err)
{
printf("Error: %s\n", strerror(err));
}
break;
}
}
return;
}
}
printError();
return;
}
void parallel()
{
num_command = 0;
memset(command, '\0', sizeof(command));
char *copy = strdup(buf);
char *demi = strdup("&");
while (1)
{
command[num_command] = strsep(©, demi);
if (command[num_command] == NULL)
break;
++num_command;
}
int argc = 0;
for (int i = 0; i < num_command; i++)
{
argc = parse(command[i]);
do_command(argc, argv);
}
wait(NULL);
return;
}
char *getinput(char *buf, FILE *fin)
{
size = 0;
getline(&buf, &size, fin);
return buf;
}
int main(int argc, char *argv[])
{
// YOUR CODE HERE
buf = (char *)malloc(BUFF_SIZE * sizeof(char));
path[1] = strdup("/bin/");
path[0] = strdup("usr/bin/");
num_path = 2;
while (1)
{
if (!flag)
printf("wish> ");
buf = getinput(buf, stdin);
for (int i = 0;; i++)
if (buf[i] == '\n')
{
buf[i] = '\0';
break;
}
if (size == 0)
continue;
parallel();
memset(buf, 0, sizeof(buf));
}
return 0;
}