Created
August 6, 2018 05:26
-
-
Save panqiincs/55c7681a115a6fe7caa2bd743029cb1f to your computer and use it in GitHub Desktop.
A Shell program with basic functionality.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <signal.h> | |
#define MAX_ARG_NUM 20 // max number of arguments | |
#define MAX_ARG_LEN 200 // max length of commands | |
#define MAX_PROMPT_LEN 100 // max length of prompt string | |
void print_prompt(char *); | |
void handle_input(char *, char **); | |
void run_cmd(char **); | |
int main(int argc, char **argv) | |
{ | |
char *arg_buf; // store user input strings | |
char *arg_list[MAX_ARG_NUM + 1]; // argument list | |
char prompt_str[MAX_PROMPT_LEN + 1]; // store prompt string | |
// in parent process, ignore SIGINT and SIGQUIT, or process | |
// will quit | |
//signal(SIGINT, SIG_IGN); | |
//signal(SIGQUIT, SIG_IGN); | |
arg_buf = (char *)malloc(sizeof(char) * MAX_ARG_LEN); | |
for (;;) { | |
print_prompt(prompt_str); | |
handle_input(arg_buf, arg_list); | |
run_cmd(arg_list); | |
} | |
free(arg_buf); | |
exit(EXIT_SUCCESS); | |
} | |
/** | |
* Construct a prompt string, store it in an charactor array which address | |
* is stringp and print out. A prompt should be like this: | |
* | |
* [psh]krist@linux-szhw:/home/krist/Workspace$ | |
* | | <--------> <------------------->| | |
* | | | | | | |
* header username hostname pwd prompt | |
* | |
* @param stringp a pointer to a char array that store prompt string, the | |
* function modifies the array content | |
* @return void | |
*/ | |
void print_prompt(char *stringp) | |
{ | |
size_t offset = 0; | |
// header [psh] | |
stringp[offset++] = '['; | |
stringp[offset++] = 'p'; | |
stringp[offset++] = 's'; | |
stringp[offset++] = 'h'; | |
stringp[offset++] = ']'; | |
// username@hostname | |
getlogin_r(stringp + offset, MAX_PROMPT_LEN - offset); | |
offset += strlen(stringp + offset); | |
stringp[offset++] = '@'; | |
gethostname(stringp + offset, MAX_PROMPT_LEN - offset); | |
offset += strlen(stringp + offset); | |
stringp[offset++] = ':'; | |
// current directory | |
getcwd(stringp + offset, MAX_PROMPT_LEN - offset); | |
offset += strlen(stringp + offset); | |
// prompt | |
if (geteuid() == 0) { | |
stringp[offset++] = '#'; // superuser | |
} else { | |
stringp[offset++] = '$'; // normal | |
} | |
stringp[offset++] = ' '; | |
stringp[offset] = '\0'; | |
printf("%s", stringp); | |
} | |
/** | |
* Store user input in input_buf, parse it to arguments, and store the | |
* argument list in arg_vec | |
* | |
* @param input_buf a pointer to a char array, store one line of user | |
* input, the caller should guarantee the buffer size is | |
* big enough | |
* @param arg_vec an array of pointers to arguments, terminated by NULL | |
* @return void | |
*/ | |
void handle_input(char *input_buf, char **arg_vec) | |
{ | |
size_t len; // max length of arg string buffer | |
ssize_t num_read; // number of characters read from stdin | |
size_t arg_num; // arguments count | |
char *token; | |
char *stringp; | |
len = MAX_ARG_LEN; | |
num_read = 0; | |
if ((num_read = getline(&input_buf, &len, stdin)) == -1) { | |
exit(EXIT_FAILURE); | |
} | |
if (len > MAX_ARG_LEN) { | |
printf("Exceeds max argument length limit, please try again!\n"); | |
exit(EXIT_FAILURE); | |
} | |
#ifdef DEBUG | |
printf("input_buf = %p, len = %zu\n", input_buf, len); | |
printf("Retrieved line of length %zu:\n", num_read); | |
printf("%s", input_buf); | |
#endif | |
input_buf[num_read - 1] = '\0'; // overwrite the newline character | |
stringp = input_buf; | |
arg_num = 0; // number of arguments | |
while (((token = strsep(&stringp, " ")) != NULL) | |
&& (arg_num < MAX_ARG_NUM)) { | |
// Token is terminated by overwriting the delimiter with a null | |
// byte('\0'), so continuous space will result in a token with | |
// only a null byte, skip it. | |
if (strcmp(token, "") != 0) { | |
arg_vec[arg_num] = token; | |
arg_num++; | |
} | |
} | |
arg_vec[arg_num] = NULL; | |
#ifdef DEBUG | |
printf("%zu arguments in total:\n", arg_num); | |
char **ptr; | |
for (ptr = arg_vec; *ptr != NULL; ptr++) { | |
printf("%s\n", *ptr); | |
} | |
printf("\n"); | |
#endif | |
} | |
/** | |
* Execute a normal(not built-in) command, fork a child process for each | |
* command to run, the parent process wait for children to exit | |
* | |
* @param arg_vec an array of pointers to arguments, terminated by NULL | |
* @return void | |
*/ | |
void exec_cmd(char **arg_vec) | |
{ | |
int status; | |
pid_t pid; | |
pid = fork(); | |
if (pid == -1) { | |
perror("fork"); | |
exit(EXIT_FAILURE); | |
} else if (pid == 0) { // child process, run the command | |
// in child process, default actions | |
//signal(SIGINT, SIG_DFL); | |
//signal(SIGQUIT, SIG_DFL); | |
// execvp() returns only if an error occurs | |
execvp(arg_vec[0], arg_vec); | |
perror("execvp"); | |
exit(EXIT_FAILURE); | |
} else { // parent process, wait for children to exit | |
while (wait(&status) != pid) | |
; | |
#ifdef DEBUG | |
printf("\nchild exited with status %d, %d\n", | |
status >> 8, status&0377); | |
#endif | |
} | |
} | |
/** | |
* Check if it is a built-in command, if it is, run it in parent process | |
* without a fork, if not, return | |
* | |
* @param arg_vec an array of pointers to arguments, terminated by NULL | |
* @return 0 it is a built-in command, finish running and return | |
* -1 it is not a built-in command and return directly | |
*/ | |
int builtin_cmd(char **arg_vec) | |
{ | |
if (strcmp(arg_vec[0], "cd") == 0) { // cd | |
if ((arg_vec[1] != NULL) && (arg_vec[2] == NULL)) { | |
if (chdir(arg_vec[1]) == -1) { | |
perror("cd"); | |
} | |
} else { | |
printf("Usage: cd [directory]\n"); | |
} | |
return 0; | |
} else if (strcmp(arg_vec[0], "exit") == 0) { // exit | |
exit(EXIT_SUCCESS); | |
} | |
return -1; | |
} | |
/** | |
* Run the command, both built-in and normal commands | |
* | |
* @param arg_vec an array of pointers to arguments, terminated by NULL | |
* @return void | |
*/ | |
void run_cmd(char **arg_vec) | |
{ | |
if (arg_vec[0] == NULL) { | |
return; | |
} | |
if (builtin_cmd(arg_vec) == -1) { // run build-in commands | |
exec_cmd(arg_vec); // run normal commands | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment