Skip to content

Instantly share code, notes, and snippets.

@Bogdan-Ciurea
Last active November 17, 2021 10:59
Show Gist options
  • Save Bogdan-Ciurea/f92324472dc96ade582f0c6ce36fa9d4 to your computer and use it in GitHub Desktop.
Save Bogdan-Ciurea/f92324472dc96ade582f0c6ce36fa9d4 to your computer and use it in GitHub Desktop.

My Shell

The next program is intended to simulate a terminal/command shell in the XV6 Operating System. This program was written for a coursework from University.

How to use

In order to use the code you will have to git clone https://github.com/mit-pdos/xv6-riscv on your local machine. In the Makefile add the following code on line 130: $U/_mysh\ . Then run make, make qemu. After the files have been compiled the terminal should display the next text: hart 2 starting hart 1 starting init: starting sh $ After this just type mysh and the program will run. When the program runs, the shell should show the next characters: >>>.

Usable commands

The program can process more types of commands like:

  • simple commands: ls, echo hello, wc hello world, cd file or mkdir file;
  • Input / output redirections: echo hello > foo or cat < foo;
  • Pipes: ls | wc or echo hello world | wc;
  • Multi element pipes: ls | wc | wc or echo hello world | wc | wc;
  • Non-trivial combinations of pipes and redirection: ls | wc | wc > foo (the code will not be able to read from a file and only write to one);
  • ; between the commands will make more commands from a line

Disclaimer

The following code is only usable in a XV6 machine although the code is written in a simple type of C. Not sure if the program can run in normal C on another machine.

#include "kernel/types.h"
#include "user/user.h"
#include "kernel/fcntl.h"
#define false 0
#define true 1
#define STDOUT_FILENO 0
#define STDIN_FILENO 1
typedef enum{
WRITE = 1,
APPEND,
READ
} redirect;
struct cmd {
char** argv;
int argc;
};
// Will return a string representing the part until the sep
char* get_left(char *initial_string, char sep) {
int i = 0;
while(initial_string[i] != sep && initial_string[i] != '\0')
i++;
char *first_word = (char*) malloc((i + 1) * sizeof(char));
i = 0;
while(initial_string[i] != sep && initial_string[i] != '\0'){
first_word[i] = initial_string[i];
i++;
}
first_word[i] = '\0';
return first_word;
} // get_left
// Will return a string representing the part after the sep
char* get_right(char *initial_string, char sep) {
int start = 0;
int i;
while (initial_string[start] != sep && initial_string[start] != '\0')
start++;
i = start + 1;
while (i < strlen(initial_string) && initial_string[i] != '\0')
i++;
char *first_word = (char*) malloc((i - start + 1) * sizeof(char));
i = 0;
while (i + start + 1 < strlen(initial_string) && initial_string[i + start + 1] != '\0'){
first_word[i] = initial_string[i + start + 1];
i++;
}
first_word[i] = '\0';
return first_word;
} // get_right
// This functuon will return an enum value representing what kind of
// redirection we have to process
int is_redirection(char *string){
int higher = 0;
int smaller = 0;
for(int i = 0; i < strlen(string); i++){
if (string[i] == '<')
smaller++;
else if(string[i] == '>')
higher++;
}
if(smaller == 0 && higher == 1)
return WRITE;
else if(smaller == 0 && higher == 2)
return APPEND;
else if(smaller == 1 && higher == 0)
return READ;
return 0;
} // is_redirection
// This function will return the number of times a chr is in a string
int number_of_chr(char *string, char chr){
int s = 0;
for(int i = 0; i < strlen(string); i++)
if (string[i] == chr)
s++;
return s;
} // number_of_chr
// This function will return the first part of a redirection
char* get_first_redirection(char *string){
int i = 0;
while(string[i] != '<' && string[i] != '>')
i++;
char *new_string = (char*) malloc((i + 1) * sizeof(char));
i = 0;
while(string[i] != '<' && string[i] != '>'){
new_string[i] = string[i];
i++;
}
new_string[i] = '\0';
return new_string;
} // get_first_redirection
// This function will return the second part of a redirection
char* get_last_redirection(char *string){
int i = 0;
int start = 0;
while(string[start] != '<' && string[start] != '>')
start++;
// In case we have to append
if(string[start + 1] == '>')
start++;
// In case we have a spaces after the redirection character
while (string[start + 1] == ' ')
start++;
i = start + 1;
while (string[i] != '\0')
i++;
char *new_string = (char*) malloc((i - start + 1) * sizeof(char));
i = 0;
while (string[i + start + 1] != '\0'){
new_string[i] = string[i + start + 1];
i++;
}
new_string[i] = '\0';
return new_string;
} // get_last_redirection
// This function will make a cmd struct out of a string
struct cmd parse_command(char *string) {
struct cmd command;
// Eliminate the spaces at the end of the string
int i = strlen(string) - 1;
while(string[i] == ' ')
i--;
string[i + 1] = '\0';
// Elininate the spaces at the start of the string
i = 0;
while(string[i++] == ' ')
string++;
if(strlen(get_right(string, ' ')) > 0){
command.argv = (char**) malloc(3 * sizeof(char*));
command.argc = 2;
command.argv[0] = get_left(string, ' ');
command.argv[1] = get_right(string, ' ');
i = 0;// Eliminate the spaces at the start of the arguments
while(string[i++] == ' ')
command.argv[1]++;
command.argv[2] = 0;
}
else{
command.argv = (char**) malloc(2 * sizeof(char*));
command.argc = 1;
command.argv[0] = get_left(string, ' ');
command.argv[1] = 0;
}
return command;
} // parse_command
// This function will make a list of lipes into an array of struct cmds
struct cmd* parse_more_pipes(char *string) {
int i;
int n = number_of_chr(string, '|') + 1;
struct cmd *command = (struct cmd*) malloc(n * sizeof(struct cmd));
for(i = 0; i < n - 1; i++) {
command[i] = parse_command(get_left(string, '|'));
string = get_right(string, '|');
}
command[i] = parse_command(string);
return command;
} // parse_more_pipes
// This function will process a simple command
void process_command(char *string) {
struct cmd command = parse_command(string);
if (strcmp(command.argv[0], "cd") == 0){
chdir(command.argv[1]);
}
else if (strcmp(command.argv[0], "exit") == 0){
exit(0);
}
else if (strlen(command.argv[0]) > 0){
if (fork() == 0) {
exec(command.argv[0], command.argv);
}
else {
wait(0);
}
}
for (int i = 0; i < command.argc; i++)
free(command.argv[i]);
free(command.argv);
} // process_command
// This function will exec a command with pipes
void make_pipe(int *in, int *out, struct cmd *command) {
if (fork() == 0) {
if (*in != STDOUT_FILENO) {
close(STDOUT_FILENO);
dup(*in);
close (*in);
}
if (*out != STDIN_FILENO) {
close(STDIN_FILENO);
dup(*out);
close (*out);
}
exec(command->argv[0], command->argv);
}
} // make_pipe
// This function will process more pipe processes
void process_pipes(char *string) {
int in;
int p[2];
int i;
int j;
int n = number_of_chr(string, '|') + 1; // when we have 3 '|',
// the function will return 2, therefore we have 2 + 1 pipes
struct cmd *command = parse_more_pipes(string);
in = STDOUT_FILENO;
for (i = 0; i < n - 1; ++i){
pipe(p);
make_pipe(&in, &p[1], &command[i]);
close(p[1]);
in = p[0];
}
if (in != STDOUT_FILENO) {
close(STDOUT_FILENO);
dup(in);
}
exec(command[i].argv[0], command[i].argv);
// Free the data
for (i = 0; i < n ; i++){
for (j = 0; j < command[i].argc; j++)
free(command[i].argv[j]);
free(command[i].argv);
}
free(command);
} // process_pipes
// This function will process a redirection process
void process_redirection(char *string){
char *left = get_first_redirection(string);
char *file_name = get_last_redirection(string);
struct cmd command = parse_command(left);
int i;
switch(is_redirection(string)){
case WRITE:
if (fork() == 0){
close(1);
open(file_name, O_WRONLY|O_CREATE|O_TRUNC);
exec(command.argv[0], command.argv);
}
else{
wait(0);
}
break;
case READ:
if (fork() == 0) {
close(0);
open(file_name, O_RDONLY);
exec(command.argv[0], command.argv);
}
else {
wait(0);
}
break;
default:
free(left);
free(file_name);
for(i = 0; i < command.argc; i++)
free(command.argv[i]);
free(command.argv);
break;
}
} // process_redirection
// This function will process pipes and redirections
void process_pipe_and_redirection(char *string) {
int i = 0, j;
int p[2];
int in;
int n = number_of_chr(string, '|') + 1;
struct cmd *command;
char *left = 0;
char *right = 0;
left = get_first_redirection(string);
right = get_last_redirection(string);
command = parse_more_pipes(left);
in = STDIN_FILENO;
for (i = 0; i < n - 1; ++i){
pipe(p);
make_pipe(&in, &p[1], &command[i]);
close(p[1]);
in = p[0];
}
if (in != STDOUT_FILENO) {
close(STDOUT_FILENO);
dup(in);
}
close(STDIN_FILENO);
open(right, O_WRONLY|O_CREATE|O_TRUNC);
exec(command[n - 1].argv[0], command[n -1].argv);
// Free the data
for (i = 0; i < n ; i++){
for (j = 0; j < command[i].argc; j++)
free(command[i].argv[j]);
free(command[i].argv);
}
free(command);
free(left);
free(right);
} // process_pipe_and_redirection
int main(int argc, char *argv[]) {
char *command = (char*) malloc(300 * sizeof(char));
char *small_command;
int n;
while (true) {
printf(">>>");
gets (command, 300);
command[strlen(command) - 1] = '\0';
// Will go through all the commands that are parsed by ';'
n = number_of_chr(command, ';');
for (int i = 0 ; i <= n; i++){
small_command = get_left(command, ';');
command = get_right(command, ';');
if ((number_of_chr(small_command, '>') || number_of_chr(small_command, '<'))
&& number_of_chr(small_command, '|')) {
if(fork() == 0) {
process_pipe_and_redirection(small_command);
}
else {
wait(0);
}
}
else if (is_redirection(small_command)){
process_redirection(small_command);
}
else if (number_of_chr(small_command, '|') > 0) {
if (fork() == 0){
process_pipes(small_command);
}
else {
wait(0);
}
}
else{
process_command(small_command);
}
free(small_command);
}
free(command);
} // while
exit(0);
} // main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment