diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..ba9207f --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,16 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu17", + "cppStandard": "gnu++17", + "intelliSenseMode": "linux-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3fbb310 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "readline.h": "c" + } +} \ No newline at end of file diff --git a/Makefile b/Makefile index e69de29..524095d 100644 --- a/Makefile +++ b/Makefile @@ -0,0 +1,30 @@ +CC = gcc + +# shell headers, code, and targets +INC=src/include +CPPFLAGS=src/lib +OBJDIR=build/dist + +# commands code and libs +CMDIR=src/code +BINDIR=build/bin + +objects = $(addprefix $(OBJDIR)/, basic.o internal.o env.o shell.o) +cmds = $(addprefix $(BINDIR)/, echo hello ls) + + +CFLAGS = -I$(CPPFLAGS) +LDFLAGS = -L$(OBJDIR) -lreadline # -Lbuild/dist + +shell: $(objects) + $(CC) $(objects) $(LDFLAGS) -o ./build/shell + +$(OBJDIR)/%.o: $(CPPFLAGS)/%.c + $(CC) $(CFLAGS) -I$(INC) -c -o $@ $< + +$(BINDIR)/%: $(CMDIR)/%.c + $(CC) -o $@ $< + +commands: $(cmds) + +.PRECIOUS: %.o diff --git a/Readme.md b/Readme.md index aefc336..5b4818f 100644 --- a/Readme.md +++ b/Readme.md @@ -1,24 +1,42 @@ # Simple shell +## dependencies + to install readline, feel free to edit it to use `fgets` or `scanf` refer to this [link](https://stackoverflow.com/questions/17294809/reading-a-line-using-scanf-not-good) for reference on both. ```bash sudo apt-get install libreadline6 libreadline6-dev ``` +## Features + +1. external commands + 1. ls + 2. echo + 3. hello (deprecated, before being released 🤣🤣) +2. internal commands + 1. hello (yes again, this is the one that will actually run) + 2. cd + 3. env + 4. set + +## Make from source + to compile the code + ```bash mkdir build mkdir build/bin -gcc shell.c -lreadline -o ./build/shell -gcc ./lib/echo.c -o ./build/bin/echo +make shell +make commands ``` -to run the command +## Run ```bash -cd build -./shell +./build/shell ``` -You need to change into the build directory for the path to take actual effect \ No newline at end of file +## Notes + +This code is developed to simulate how I expect an actual shell works. any resmeblance between this and reality is mere coincidence. \ No newline at end of file diff --git a/lib/cd.c b/lib/cd.c deleted file mode 100644 index e69de29..0000000 diff --git a/lib/ls.c b/lib/ls.c deleted file mode 100644 index e69de29..0000000 diff --git a/lib/cat.c b/src/code/cat.c similarity index 100% rename from lib/cat.c rename to src/code/cat.c diff --git a/lib/echo.c b/src/code/echo.c similarity index 100% rename from lib/echo.c rename to src/code/echo.c diff --git a/src/code/hello.c b/src/code/hello.c new file mode 100644 index 0000000..e61eaf2 --- /dev/null +++ b/src/code/hello.c @@ -0,0 +1,6 @@ +#include + +int main() { + printf("Hello world"); + return 0; +} \ No newline at end of file diff --git a/src/code/ls.c b/src/code/ls.c new file mode 100644 index 0000000..b6d27f6 --- /dev/null +++ b/src/code/ls.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + DIR *mydir; + struct dirent *myfile; + struct stat mystat; + char buf[512]; + + char* path; + if (argc >= 2) path = argv[1]; + else path = "."; + mydir = opendir(path); + + while((myfile = readdir(mydir)) != NULL) + { + sprintf(buf, "%s/%s", path, myfile->d_name); + stat(buf, &mystat); + printf("%zu\t%s\n",mystat.st_size, myfile->d_name); + } + closedir(mydir); +} \ No newline at end of file diff --git a/src/include/basic.h b/src/include/basic.h new file mode 100644 index 0000000..eff4637 --- /dev/null +++ b/src/include/basic.h @@ -0,0 +1,7 @@ +#ifndef BASIC_H +#define BASIC_H + +int parse_command(char* in, char** argv); +int run_command(char** args); + +#endif \ No newline at end of file diff --git a/src/include/env.h b/src/include/env.h new file mode 100644 index 0000000..0498b9e --- /dev/null +++ b/src/include/env.h @@ -0,0 +1,14 @@ +#ifndef ENV_H +#define ENV_H + +void init_env(); + +char* get_env_variable(char* ); + +int set_env_variable(char* , char *); + +void disp_env(); + +char* find_command(char* command); + +#endif \ No newline at end of file diff --git a/src/include/internal.h b/src/include/internal.h new file mode 100644 index 0000000..870960a --- /dev/null +++ b/src/include/internal.h @@ -0,0 +1,8 @@ +#ifndef INTERNAL_H +#define INTERNAL_H + +int is_internal(char* ); + +int run_internal_command(int , int , char** ); + +#endif \ No newline at end of file diff --git a/shell.c b/src/lib/basic.c similarity index 54% rename from shell.c rename to src/lib/basic.c index a84db5c..6cffe27 100644 --- a/shell.c +++ b/src/lib/basic.c @@ -1,12 +1,8 @@ #include #include -#include #include +#include #include -#include -#include - -#define BIN_PATH "./bin/" int parse_command(char* in, char** argv) { int argc = 0, reader_idx = 0, writer_idx, len = strlen(in); @@ -32,21 +28,11 @@ int parse_command(char* in, char** argv) { // skip trailing spaces while(in[reader_idx] == ' ') ++reader_idx; // skip spaces } + argv[argc] = NULL; return argc; } -char* find_command(char* command) { - char* isPath = strchr(command, '/'); - char* path; - if (isPath == NULL) { - path = strcat(strdup(BIN_PATH), command); - } else { - path = command; - } - return path; -} - int run_command(char** args) { int rc = fork(); if (rc == 0) { @@ -54,18 +40,3 @@ int run_command(char** args) { } wait(NULL); } - -int main(int argc, char *argv[]) { - while (1) { - char* in = readline(">$ "); - char** command = (char**) malloc(100 * sizeof(char*)); - int args_count = parse_command(in, command); - printf("Command recieved: %s, with %d args\n", command[0], args_count - 1); - int i = 0; - for (; i < args_count;i++) printf("argument %d: %s\n", i, command[i]); - char* command_path = find_command(command[0]); - command[0] = command_path; - run_command(command); - } - return 0; -} \ No newline at end of file diff --git a/src/lib/env.c b/src/lib/env.c new file mode 100644 index 0000000..f6988f7 --- /dev/null +++ b/src/lib/env.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +#include "env.h" + +#define PATH "/home/aim97/os/MyShell/build/bin/" + +struct Env_variable { + char* name; + char* value; +}; + +static int variables_count = 0; +static struct Env_variable* env_variables[100]; + +static void add_entry (char* name, char* value) { + env_variables[variables_count] = malloc(sizeof (struct Env_variable)); + env_variables[variables_count]->name = name; + env_variables[variables_count]->value = value; + variables_count++; +} + +void init_env() { + add_entry("PATH", PATH); +} + +char* get_env_variable(char* name) { + int i = 0; + for(;i < variables_count;i++) { + if (!strcmp(env_variables[i]->name, name)) { + return env_variables[i]->value; + } + } + return NULL; +} + +int set_env_variable(char* name, char* value) { + int i = 0; + for(;i < variables_count;i++) { + if (!strcmp(env_variables[i]->name, name)) { + free(env_variables[i]->value); + env_variables[i]->value = value; + return 0; + } + } + add_entry(name, value); + return 0; +} + +void disp_env() { + int i = 0; + for(;i < variables_count;i++) { + printf("%s %s\n",env_variables[i]->name, env_variables[i]->value); + } +} + +char* find_command(char* command) { + char* isPath = strchr(command, '/'); + char* path; + if (isPath == NULL) { + path = strcat(strdup(PATH), command); + } else { + path = command; + } + return path; +} \ No newline at end of file diff --git a/src/lib/internal.c b/src/lib/internal.c new file mode 100644 index 0000000..5f7b41a --- /dev/null +++ b/src/lib/internal.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include + +#include "internal.h" +#include "env.h" + +#define INTERNAL_COMMANDS_COUNT 4 + +struct Internal_command { + char* name; + int (*exec)(int argc, char* args[]); +}; + +static int get_cwd() { + char* v = (char*) malloc(sizeof (char)*1000); + char* ret = getcwd(v, 1000); + if (ret != NULL) { + printf("CWD: %s\n", v); + free(v); + return 0; + } + free(v); + return 1; +} + +static int cd(int argc, char** args) { + if (argc == 0) { + return get_cwd(); + } + return chdir(args[0]); +} + +static int hello (int argc, char** args) { + printf("hello again\n"); + return 0; +} + + +int env(int argc, char** args) { + disp_env(); + return 0; +} + +int set(int argc, char** args) { + if (argc < 2) { + return 1; + } + set_env_variable(args[0], args[1]); + return 0; +} + +static struct Internal_command internal_commands[] = { + { "env", &env }, + { "cd", &cd }, + { "set", &set }, + { "hello", &hello } +}; + +int is_internal(char* command) { + int i = 0,idx = -1; + for (;i < INTERNAL_COMMANDS_COUNT;i++) { + int ret = strcmp(internal_commands[i].name, command); + if (ret == 0) { + idx = i; + break; + } + } + return idx; +} + +int run_internal_command(int code, int argc, char** argv) { + return internal_commands[code].exec(argc, argv); +} \ No newline at end of file diff --git a/src/lib/shell.c b/src/lib/shell.c new file mode 100644 index 0000000..e8a9f04 --- /dev/null +++ b/src/lib/shell.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basic.h" +#include "env.h" +#include "internal.h" + +char prompt[1000]; +char cur_path[1000]; + +int main(int argc, char *argv[]) { + char** command = (char**) malloc(100 * sizeof(char*)); + + init_env(); + + while (1) { + sprintf(prompt, "%s >$ ", getcwd(cur_path, 1000)); + char* in = readline(prompt); + int args_count = parse_command(in, command); + if (!args_count) continue; + + // printf("Command recieved: %s, with %d args\n", command[0], args_count - 1); + // for (; i < args_count;i++) printf("argument %d: %s\n", i, command[i]); + + int internal_code = is_internal(command[0]); + + if (internal_code == -1) { + char* command_path = find_command(command[0]); + command[0] = command_path; + // printf("Command path %s\n", command_path); + run_command(command); + } else { + // printf("Internal command %s recieved, code %d\n", command[0], internal_code); + run_internal_command(internal_code, args_count - 1, (char**)&command[1]); + } + } + return 0; +} \ No newline at end of file