Browse Source

Init

master
Apostolos Fanakis 7 years ago
commit
370841b91d
  1. 21
      Makefile
  2. BIN
      Project.pdf
  3. 40
      README.md
  4. 212
      apostolofShellFunctions.c
  5. 44
      apostolofShellFunctions.h
  6. 15
      apostolofShellMain.c
  7. 6
      testScript

21
Makefile

@ -0,0 +1,21 @@
TARGET = apostolofShell
CC = gcc
CFLAGS = -Wall -O3 -I.
OBJ = apostolofShellMain.o apostolofShellFunctions.o
DEPS = apostolofShellFunctions.h
.PHONY: default all clean
default: $(TARGET)
all: default
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
.PRECIOUS: $(TARGET) $(OBJ)
$(TARGET): $(OBJ)
$(CC) -o $@ $^ $(CFLAGS)
clean:
$(RM) *.o *~

BIN
Project.pdf

Binary file not shown.

40
README.md

@ -0,0 +1,40 @@
# Shell
From [Wikipedia]:
>A Unix shell is a command-line interpreter or shell that provides a traditional Unix-like command line user interface. Users direct the operation of the computer by entering commands as text for a command line interpreter to execute, or by creating text scripts of one or more such commands.
## Repository
This repository provides an implementation of a simple Unix shell in C language. This shell was developed as part of the "Operating Systems" course at AUTH and has very limited functionality. All code in this repository is intended for academic and learning purposes only, not for everyday usage.
## Dependencies
Only a compiler is needed (e.g. gcc).
## Compilation
To compile, make sure all necessary packages and dependencies are installed. Then run:
```sh
$ make
```
## Usage
Run the shell in interactive mode with the command:
```sh
$ ./apostolofShell
```
or in batch mode using the command:
```sh
$ ./apostolofShell script
```
where **script** is the path and filename of the script you want to run.
**Free Software, Hell Yeah!**
[//]: # (Links)
[Wikipedia]: <https://en.wikipedia.org/wiki/Unix_shell>

212
apostolofShellFunctions.c

@ -0,0 +1,212 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/wait.h>
#include "apostolofShellFunctions.h"
const char semicolonDelimeter[2] = ";";
const char ampersandDelimeter[2] = "&";
void interactiveMode(){
char input[BUFFER_SIZE];
int quit_called = 0;
while (1){
printf(GRN "\x1B[1mfanakis_8261> \x1B[0m" RESET);
//Gets user input and checks for errors
if (!fgets(input, BUFFER_SIZE, stdin) || feof(stdin) || ferror(stdin)){
continue;
}
//Checks for input buffer overflow
if ((strlen(input) == BUFFER_SIZE - 1) && input[BUFFER_SIZE - 2] != '\n'){
printf("Input too long! Please confine input to 512 characters.\n");
//Clears garbage from stdin
char discarded;
while ((discarded = fgetc(stdin)) != '\n' && discarded != EOF);
continue;
}
//Trims possible leading and trailing whitespace
strcpy(input, trimWhitespaces(input));
if (strcmp(input, "\n") == 0 || input == 0){
continue;
}
//Parses semicolon-separated chunks first
unconditionalRun(input, &quit_called);
if (quit_called == QUIT_CALLED){
exit(EXIT_SUCCESS);
}
}
}
void batchFileMode(char *filename){
char line[BUFFER_SIZE];
int quit_called = 0;
//Opens file
FILE* file = fopen(filename, "r");
if (!file){
printf("Couldn't open file.\n");
exit(EXIT_FAILURE);
}
//Reads file one line at a time
while (fgets(line, sizeof(line), file)) {
strcpy(line, trimWhitespaces(line));
if (strcmp(line, "\n") == 0 || line == 0){
continue;
}
unconditionalRun(line, &quit_called);
if (quit_called == QUIT_CALLED){
fclose(file);
exit(EXIT_SUCCESS);
}
}
fclose(file);
return;
}
void unconditionalRun(char *input, int *quit_called){
char *rest, *token;
//Parses chunks of input split by semicolons (";")
//Gets first token
token = strtok_r(input, semicolonDelimeter, &rest);
while (token != NULL){
//Further parses and conditionally runs chunks
conditionalRun(token, quit_called);
//Parses ampersand-separated chunks and runs them successively
if (*quit_called == QUIT_CALLED){
return;
} /*else if ((*rest == 0)){
return 1;
}*/
token = strtok_r(NULL, semicolonDelimeter, &rest);
}
}
void conditionalRun(char *input, int *quit_called){
char *rest, *token;
//Parses ampersand-separated chunks
//Gets first token
token = strtok_r(input, ampersandDelimeter, &rest);
while (token != NULL){
char *command, **argv;
int argc = 0;
//Parses the command
parseCommand(trimWhitespaces(token), &command, &argv, &argc);
if (strcmp(command, "quit") == 0){
*quit_called = QUIT_CALLED;
return;
}
//Executes the command
if (execute(command, argv)){
//Exits on failure because this is a conditional run
return;
}
//Cleans up space allocated
free(command);
for (int i=0; i<argc; ++i){
free(argv[i]);
}
free(argv);
//Checks if chunk isn't split by a double ampersand
if (strlen(rest) != 0){
if (!(rest[0] == '&' && isspace(rest[1]))){
printf("Not supported\n");
return;
}
}
//Gets next token
token = strtok_r(NULL, ampersandDelimeter, &rest);
}
}
int execute(char *command, char **argv){
pid_t pid;
int status = 0;
if ((pid = fork()) < 0) {
//Failed to create child process
perror("Can't fork");
exit(EXIT_FAILURE);
} else if (pid == 0) {
//Child process
if (execvp(command, argv)) {
perror("Error executing command");
_exit(EXIT_FAILURE);
}
} else {
//Parent process
while (wait(&status) != pid)
;
return status;
}
return status;
}
void parseCommand(char *input, char **command, char ***argv, int *argc){
char *token = strtok(input, " ");
*argc = 1;
//Allocates memory and copies command
*command = (char*)(malloc((strlen(token) + 1) * sizeof(char)));
strcpy(*command, trimWhitespaces(token));
(*command)[strlen(*command)] = '\0';
//Allocates memory and copies first argument (command)
*argv = (char**)(malloc((*argc) * sizeof(char*)));
(*argv)[(*argc) - 1] = (char*)(malloc((strlen(*command) + 1) * sizeof(char)));
strcpy((*argv)[(*argc) - 1], *command);
(*argv)[(*argc) - 1][strlen((*argv)[(*argc) - 1])] = '\0';
//Parses rest of the arguments
token = strtok(NULL, " ");
while (token != NULL){
++(*argc);
*argv = (char**)(realloc(*argv, (*argc) * sizeof(char*)));
(*argv)[(*argc) - 1] = (char*)(malloc((strlen(token) + 1) * sizeof(char)));
strcpy((*argv)[(*argc) - 1], trimWhitespaces(token));
(*argv)[(*argc) - 1][strlen((*argv)[(*argc) - 1])] = '\0';
token = strtok(NULL, " ");
}
//NULL-terminates arguments array
++(*argc);
*argv = (char**)(realloc(*argv, (*argc) * sizeof(char*)));
(*argv)[(*argc) - 1] = NULL;
}
char *trimWhitespaces(char *string){
char *end;
//Trims leading spaces
while (isspace( (unsigned char) *string )){
string++;
}
if(*string == 0){ //String was all spaces
return string;
}
//Trims trailing spaces
end = string + strlen(string) - 1;
while (end > string && isspace( (unsigned char) *end )){
end--;
}
//Writes new null terminator
*(end+1) = 0;
return string;
}

44
apostolofShellFunctions.h

@ -0,0 +1,44 @@
#ifndef APOSTOLOFSHELLFUNCTIONS_H_ /* Include guard */
#define APOSTOLOFSHELLFUNCTIONS_H_
//Read buffer size is the maximum number of characters (512) plus the newline character and the null
//terminator
#define BUFFER_SIZE 512 + 1 + 1
#define QUIT_CALLED 11880
//Cool colors
#define GRN "\x1B[32m"
#define RESET "\x1B[0m"
const char semicolonDelimeter[2];
const char ampersandDelimeter[2];
//Function interactiveMode implements an interactive shell
void interactiveMode();
//Function batchFileMode runs a script file
void batchFileMode(char *filename); //Filename and path of the script
//Function unconditionalRun splits a string on semicolon character (";") and runs each chunk
//unconditionally
void unconditionalRun(char *input, //String to be split
int *quit_called); //A flag indicating if quit command was called
//Function conditionalRun splits a string on the and character ("&"), runs each chunk conditionally
//if the first character of the next chunk is also the and character and unconditionally otherwise
void conditionalRun(char *input, //String to be split
int *quit_called); //A flag indicating if quit command was called
//Function execute creates a fork and runs a command using execvp
int execute(char *command, //String containing the command
char **argv); //NULL terminated string array containing command's arguments
//Function parseCommand parses a command and its arguments out of a string
void parseCommand(char *input, //String to be parsed
char **command, //String containing the command
char ***argv, //NULL terminated string array containing command's arguments
int *argc); //Number of arguments
//Function trimWhitespaces trims leading and trailing whitespaces of a string
char *trimWhitespaces(char *string); //String to be trimmed
#endif // APOSTOLOFSHELLFUNCTIONS_H_

15
apostolofShellMain.c

@ -0,0 +1,15 @@
#include <stdio.h>
#include <stdlib.h>
#include "apostolofShellFunctions.h"
int main(int argc, char *argv[]){
if (argc == 1){
interactiveMode();
} else if (argc == 2) {
batchFileMode(argv[1]);
} else {
printf("Not supported\n");
}
exit(EXIT_SUCCESS);
}

6
testScript

@ -0,0 +1,6 @@
mkdir someDir && touch someDir/file
mkdir someDir/dir2 ; touch someDir/file2
ls someDir
touch someDir/dir2/file3
Loading…
Cancel
Save