New repo setup

This commit is contained in:
2024-05-28 17:19:44 -05:00
commit 5436c4c2ea
20 changed files with 974 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.o
*.idea
shell
init

44
Makefile Normal file
View File

@@ -0,0 +1,44 @@
CC = aarch64-none-elf-gcc
CXX = aarch64-none-elf-g++
AS = aarch64-none-elf-as
AR = aarch64-none-elf-ar
prefix:=$(HOME)/.cros/root
COMMANDS_SRCS = $(wildcard src/commands/*.cpp)
SHELL_OBJS = src/shell.o src/utility.o src/command.o
INIT_OBJ = init.o
COMMANDS_TARGETS = $(COMMANDS_SRCS:src/commands/%.cpp=bin/%)
SHELL_TARGET = shell
INIT_TARGET = init
CXXFLAGS = -I$(prefix)/include -Isrc -Isrc/commands -ffreestanding -nostdlib -fpermissive -fno-exceptions -fno-rtti -Wall -Wextra -ggdb -O0
LDFLAGS = -L$(prefix)/lib -nostdlib
LIBS = -lc -lsyscall
.PHONY: all
all: $(COMMANDS_TARGETS) $(SHELL_TARGET) $(INIT_TARGET)
.PHONY: clean
clean:
rm -rf ./bin $(SHELL_TARGET) $(INIT_TARGET) $(SHELL_OBJS) $(INIT_OBJ) src/commands/*.o
.PHONY: install
install:
mkdir -p $(prefix)/bin
cp -a bin/. $(prefix)/bin
cp $(SHELL_TARGET) $(prefix)/bin
cp $(INIT_TARGET) $(prefix)/bin
# Compiling individual command files
bin/%: src/commands/%.o
@mkdir -p bin
$(CXX) $(LDFLAGS) $(CXXFLAGS) -o $@ $< $(LIBS)
# Compiling shell.cpp
$(SHELL_TARGET): $(SHELL_OBJS)
$(CXX) $(LDFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS)
# Compiling shell INIT
$(INIT_TARGET): $(INIT_OBJ)
$(CXX) $(LDFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS)

25
README.md Normal file
View File

@@ -0,0 +1,25 @@
# SHELL COMMAND DOCUMENTATION:
1. `$ help` provides a quick reference for available commands.
3. `$ echo <(opt) args>` Echos back the provided argument(s). Will include double quote.
4. `$ joke` provides a tasteful joke.
5. `$ ls <(opt) arg>` Provides the files and directories at the provided location. Prints files and directories in current directory if no arg given.
6. `$ cd <arg>` BUILT-IN: Changes current directory to provided file location.
7. `$ pwd` BUILT-IN: Provides an exact location of the current directory.
---
# ./commands/*
Commands created as separate cpp files. Compiled into `./bin` during `make`.
pwd and cd are built-ins.
---
# Compiling and Running
* `make`
* `make install prefix=PATH_TO_INSTALL_DIRECTORY`
* Don't forget `make clean`
Authored by Connor
echo.cpp authored by Kyle

89
init.cpp Normal file
View File

@@ -0,0 +1,89 @@
#include <stdio.h>
#include <sys/syscall.h>
#include <stddef.h>
#include <unistd.h>
#include <stdlib.h>
char **envp_global;
int pipefd_stdout[2];
int pipefd_stdin[2];
void shellThread(void *ptr)
{
fddup(pipefd_stdout[1], 1);
fddup(pipefd_stdin[0], 0);
char *args[] = {"/bin/shell", NULL};
// exec("/bin/shell", args, envp);
execve("shell", args, envp_global);
terminate();
}
int main(int argc, char **argv, char **envp)
{
const int stackSize = 1024;
printf("%s: Starting shell\n", argv[0]);
void *stack = malloc(stackSize);
envp_global = envp;
printf("%s: Creating pipes\n", argv[0]);
create_pipe(pipefd_stdout);
create_pipe(pipefd_stdin);
printf("%s: Spawning new thread\n", argv[0]);
clone(shellThread, stack + stackSize, nullptr, 0);
printf("%s: Entering I/O loop\n", argv[0]);
char line[1024];
char buffer[1024];
int pos = 0;
while (true)
{
// printf("init: I/O loop\n");
int status = read(0, &buffer[pos], 1);
// printf("init: Read status: %d\n", status);
while (status > 0)
{
char str[2];
str[0] = buffer[pos];
str[1] = '\0';
printf("%s", str);
if (str[0] == '\r')
{
printf("\n");
}
pos += status;
if (buffer[pos - 1] == '\r')
{
buffer[pos - 1] = '\n';
buffer[pos] = '\0';
fprintf(&pipefd_stdin[1], "%s", buffer);
pos = 0;
}
else if (pos == 1023)
{
buffer[pos] = '\0';
fprintf(&pipefd_stdin[1], "%s", buffer);
pos = 0;
}
status = read(0, &buffer[pos], 1);
}
if (status < 0)
{
printf("init: Error reading from UART.\n");
}
status = read(pipefd_stdout[0], line, 1023);
// printf("init: Read2 status: %d\n", status);
if (status > 0)
{
line[status] = '\0';
printf("%s", line);
}
yield();
// printk("hi");
}
}

164
src/command.cpp Normal file
View File

@@ -0,0 +1,164 @@
#include "command.h"
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "utility.h"
#include <sys/syscall.h>
void spawn_command(char **args)
{
execvp(args[0], args);
terminate();
}
int Command::parse(std::string input)
{
// if input has \n appended to the end, remove it
if (input[input.size() - 1] == '\n')
{
input = input.substr(0, input.size() - 1);
}
std::string pipe_delim = "|";
std::vector<std::string> parsed_input = split_string(input, pipe_delim);
if (Command::setup_command(parsed_input) < 0)
{
return -1;
}
return 0;
}
int Command::run()
{
int i, j, pid;
int num_instructions = instructions.size();
// If there is no pipe, just fork and execute the first command
if (num_instructions == 1)
{
// Check for built-in commands
if (instructions[0][0] == "cd")
{
if (instructions[0].size() > 1)
{
return cd(instructions[0][1].c_str());
}
return cd("/");
}
else if (instructions[0][0] == "clear")
{
clear();
return 0;
}
// No built-ins, execute elsewhere
exec_instruction(instructions[0]);
wait(NULL);
return 0;
}
//////////////////////////////////
// Piping logic:
// Initialize file descriptors for some piping
int fd[num_instructions][2];
// Init the pipes
int result = -1;
for (i = 0; i < num_instructions; i++)
{
pipe(fd[i]);
}
// Fork every instruction
for (i = 0; i < num_instructions; i++)
{
pid = fork();
if (pid == 0)
{
// If this is not the first instruction
if (i > 0)
{
if (fd[i - 1][0] != 0)
{
dup2(fd[i - 1][0], 0);
}
}
// If this is not the last instruction
if (i < num_instructions - 1)
{
if (fd[i][1] != 1)
{
dup2(fd[i][1], 1);
}
}
// Close all descriptors
for (j = 0; j < num_instructions; j++)
{
close(fd[j][0]);
close(fd[j][1]);
}
// Execute instruction
exec_instruction(instructions[i]);
}
}
// Close parent file descriptors
for (i = 0; i < num_instructions; i++)
{
close(fd[i][0]);
close(fd[i][1]);
}
// Wait for all processes to finish
for (int i = 0; i < num_instructions; i++)
{
wait(NULL);
}
return 0;
}
int Command::setup_command(std::vector<std::string> &parsed_input)
{
std::string space_delim = " ";
while (!parsed_input.empty())
{
std::string input = parsed_input.front();
std::vector<std::string> instruction = split_string(input, space_delim);
// terminate setup if input error
if (instruction.empty())
{
return -1;
}
instructions.push_back(instruction);
parsed_input.remove(0);
}
return 0;
}
int Command::exec_instruction(std::vector<std::string> &instruction)
{
int num_args = instruction.size();
char **arg = new char *[num_args + 1];
for (size_t i = 0; i < num_args; i++)
{
arg[i] = new char[instruction[i].size() + 1];
strcpy(arg[i], instruction[i].c_str());
}
arg[num_args] = nullptr;
void *stack = malloc(1024);
clone(spawn_command, stack + 1024, (void *)arg, 0);
return 0;
}

50
src/command.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef _COMMAND_H_
#define _COMMAND_H_
#include <string>
#include <vector>
/**
* The entire line parsed is a "Command"
* Each process with arguments included are an "Instruction"
* The Command class stores each instruction as a vector and will pipe between
* each instruction in the vector.
*/
class Command
{
public:
/**
* Parse each input by the pipe deliminator.
* Setup the command with each parsed input.
* @return 0 if successful
* @return not 0 otherwise
*/
int parse(std::string command);
/**
* Fork and exec the command.
* Pipe to the next command if applicable.
*/
int run();
private:
std::vector<std::vector<std::string>> instructions;
/**
* Loops through every parsed input in a vector
* Separates each segment o f a command by spaces
* Embeds each segment of a command as an "instruction"
* Each "instruction" is added to a vector called "instructions"
* @param parsed_input a vector of commands separated by a vertical pipe "|"
*/
int setup_command(std::vector<std::string> &instructions);
/**
* Executes the command
* @param curr_command
* @return
*/
int exec_instruction(std::vector<std::string> &instruction);
};
#endif // !_COMMAND_H_

66
src/commands/cp.cpp Normal file
View File

@@ -0,0 +1,66 @@
//
// Created by connor on 2/12/24.
//
//
#include <stdio.h>
#include <stdlib.h>
void cp(char* src_file_name, char* dest_file_name, bool overwrite) {
FILE* src_file = fopen(src_file_name, "r");
if (src_file == NULL) {
fprintf(stderr, "Error opening src file: %s\n", src_file_name);
exit(1);
}
FILE* dest_file = fopen(dest_file_name, "r");
if (dest_file != NULL) {
fclose(dest_file);
if (!overwrite) {
fclose(src_file);
return;
}
}
dest_file = fopen(dest_file_name, "w");
if (dest_file == NULL) {
fprintf(stderr, "Error opening dest file: %s\n", dest_file_name);
fclose(src_file);
exit(1);
}
int ch;
while ((ch = fgetc(src_file)) != EOF) {
fputc(ch, dest_file);
}
fclose(src_file);
fclose(dest_file);
}
int main(int argc, char* argv[]) {
if (argc < 3) {
printf("Usage: cp <flags> <src file> <dest file1> <dest file2> ...\n");
} else {
int i = 1;
char* flags = argv[1];
int size = sizeof(flags);
bool overwrite = false;
if (flags[0] == '-') {
i++;
for (int j = 1; j < size; j++) {
switch (flags[j]) {
case 'o':
overwrite = true;
break;
default:
break;
}
}
}
for (int j = i + 1; j < argc; j++)
cp(argv[i], argv[j], overwrite);
return 0;
}
}

20
src/commands/echo.cpp Normal file
View File

@@ -0,0 +1,20 @@
//
// Created by connor on 2/7/24.
// functions similarly to linux ls
//
#include <stdio.h>
void echo(int argc, char* argv[]) {
for (int i = 0; i < argc; i++) {
printf("%s ", argv[i]);
}
printf("\n");
}
int main(int argc, char* argv[]) {
if (argc < 2)
printf("Usage: echo <prompt>\n");
else
echo(argc, argv);
return 0;
}

23
src/commands/help.cpp Normal file
View File

@@ -0,0 +1,23 @@
//
// Created by connor on 2/12/24.
//
//
#include <stdio.h>
int main() {
printf("Commands:\n");
printf(" joke\n");
printf(" echo <prompt>\n");
printf(" ls <directory>\n");
printf(" cd <path>\n");
printf(" pwd\n");
printf(" search <flags> <token> <file1> <file2> ...\n");
printf(" -v: verbose\n");
printf(" wc <flags> <file1> <file2> ...\n");
printf(" -b: byte count\n");
printf(" -c: character count\n");
printf(" -w: word count\n");
printf(" -l: line count\n");
printf(" cp <flags> <src file> <dest file1> <dest file2> ...\n");
printf(" -o: overwrite file\n");
return 0;
}

35
src/commands/joke.cpp Normal file
View File

@@ -0,0 +1,35 @@
/*
* Created by connor on 2/7/24.
* 16 of the classiest jokes.
*/
#include <stdio.h>
#include <stdlib.h>
void joke() {
const char* jokes[] = {
"What do you call a factory that makes okay products? \nA satisfactory.",
"What did one wall say to the other? \nI'll meet you at the corner.",
"What did the zero say to the eight? \nThat belt looks good on you.",
"If my wife thinks I'm obsessed with programming, she's crazy. \nendif.",
"Hey girl, are you an object-oriented programming language?\nBecause you've got class.",
"How many lightbulbs does it take to change a programmer? \nI don't know, but it sounds like somebody learned about dependency inversion.",
"[\"Hip\",\"Hip\"]\nHip Hip Array!",
"Why do programmers prefer dark mode?\nBecause the light attracts bugs!",
"Why don't scientists trust atoms?\nBecause they make up everything!",
"Why do Java developers wear glasses?\nBecause they don't see sharp!",
"Why did the programmer quit his job?\nHe didn't get arrays!",
"Why did the scarecrow win an award?\nBecause he was outstanding in his field!",
"I told my wife she was drawing her eyebrows too high.\nShe looked surprised!",
"I asked the librarian if the library had any books on paranoia.\nShe whispered, 'They're right behind you.'",
"I used to play piano by ear. \nNow I use my hands and fingers.",
"I told my wife she should embrace her mistakes.\nShe gave me a hug."};
int joke_index = rand() % 16;
printf("%s\n", jokes[joke_index]);
}
int main() {
joke();
return 0;
}

38
src/commands/ls.cpp Normal file
View File

@@ -0,0 +1,38 @@
//
// Created by connor on 2/7/24.
// functions similarly to linux ls
//
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void ls(char* dir_path) {
DIR* dir = opendir(dir_path);
if (dir == NULL) {
fprintf(stderr, "Error opening directory: %s\n", dir_path);
exit(1);
}
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, ".."))
printf("%s\n", entry->d_name);
}
closedir(dir);
}
int main(int argc, char* argv[]) {
if (argc == 1) {
char dir[100];
getcwd(dir, sizeof(dir));
ls(dir);
} else {
for (int i = 1; i < argc; i++)
ls(argv[i]);
}
return 0;
}

18
src/commands/mkdir.cpp Normal file
View File

@@ -0,0 +1,18 @@
//
// Created by connor on 2/12/24.
//
//
#include <stdio.h>
#include <sys/syscall.h>
void mkdir_func(char* dir_path) {
mkdir(dir_path);
}
int main(int argc, char* argv[]) {
if (argc > 1) {
mkdir_func(argv[2]);
}
printf("mkdir not implemented.");
return 0;
}

9
src/commands/mv.cpp Normal file
View File

@@ -0,0 +1,9 @@
//
// Created by connor on 2/12/24.
//
//
#include <stdio.h>
int main() {
printf("mv not implemented.");
return 0;
}

18
src/commands/pwd.cpp Normal file
View File

@@ -0,0 +1,18 @@
//
// Created by connor on 2/12/24.
//
//
#include <stdio.h>
#include <unistd.h>
int pwd() {
char cwd[128];
getcwd(cwd, sizeof(cwd));
return printf("%s\n", cwd);
}
int main() {
pwd();
return 0;
}

9
src/commands/rm.cpp Normal file
View File

@@ -0,0 +1,9 @@
//
// Created by connor on 2/12/24.
//
//
#include <stdio.h>
int main() {
printf("rm not implemented.");
return 0;
}

78
src/commands/search.cpp Normal file
View File

@@ -0,0 +1,78 @@
//
// Created by connor on 2/12/24.
//
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void search(char *token, char *file_name, bool verbose)
{
FILE *file;
file = fopen(file_name, "r");
if (file == NULL)
{
fprintf(stderr, "Error opening file: %s\n", file_name);
exit(1);
}
char line[1024]; // assuming max line length is 1024 chars
int line_number = 1;
if (verbose)
{
printf("%s:\n", file_name);
printf("%s occurences:\n", token);
}
while (fgets(line, sizeof(line), file) != NULL)
{
char *occurrence = strstr(line, token);
while (occurrence != NULL)
{
if (verbose)
printf(" line: %d, index: %d %s", line_number, occurrence - line + 1, line);
else
printf("%d %d %s", line_number, occurrence - line + 1, line);
occurrence = strstr(occurrence + 1, token);
}
line_number++;
}
fclose(file);
}
int main(int argc, char *argv[])
{
if (argc <= 2)
{
printf("Usage: search <flags> <token> <file1> <file2> ...\n");
}
else
{
int i = 2;
char *flags = argv[1];
int size = sizeof(flags);
bool verbose = false;
if (flags[0] == '-')
{
i++;
for (int j = 1; j < size; j++)
{
switch (flags[j])
{
case 'v':
verbose = true;
break;
default:
break;
}
}
}
char *token = argv[i - 1];
for (; i < argc; i++)
search(token, argv[i], verbose);
}
return 0;
}

127
src/commands/wc.cpp Normal file
View File

@@ -0,0 +1,127 @@
//
// Created by connor on 2/12/24.
//
//
#include <stdio.h>
#include <stdlib.h>
void wc(char *file_name, bool show_chars, bool show_bytes, bool show_words, bool show_lines)
{
FILE *file;
file = fopen(file_name, "r");
if (file == NULL)
{
fprintf(stderr, "Error opening file: %s\n", file_name);
exit(1);
}
int lines = 1;
int words = 0;
int characters = 0;
int bytes = 0;
if (show_chars || show_words || show_lines)
{
int in_word = 0;
int ch = 0;
while ((ch = fgetc(file)) != EOF)
{
characters++;
if (ch == '\n')
{
lines++;
}
if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f')
{
in_word = 0;
}
else if (in_word == 0)
{
in_word = 1;
words++;
}
}
}
if (show_bytes)
{
if (fseek(file, 0, SEEK_END) != 0)
{
fprintf(stderr, "Error reading file: %s\n", file_name);
exit(1);
}
bytes = ftell(file);
if (bytes == -1)
{
fprintf(stderr, "Error reading file: %s\n", file_name);
exit(1);
}
}
fclose(file);
printf("%s:\n", file_name);
if (show_bytes)
printf(" bytes: %d\n", bytes);
if (show_chars)
printf(" characters: %d\n", characters);
if (show_words)
printf(" words: %d\n", words);
if (show_lines)
printf(" lines: %d\n", lines);
}
int main(int argc, char *argv[])
{
if (argc <= 1)
{
printf("Usage: wc <flags> <file1> <file2> ...\n");
}
else
{
int i = 1;
char *flags = argv[1];
int size = sizeof(flags);
bool show_chars = false;
bool show_bytes = false;
bool show_words = false;
bool show_lines = false;
if (flags[0] == '-')
{
i++;
for (int j = 1; j < size; j++)
{
switch (flags[j])
{
case 'c':
show_chars = true;
break;
case 'b':
show_bytes = true;
break;
case 'w':
show_words = true;
break;
case 'l':
show_lines = true;
break;
default:
break;
}
}
}
for (; i < argc; i++)
{
if (!show_chars && !show_bytes && !show_words && !show_lines)
wc(argv[i], show_chars, show_bytes, true, show_lines);
else
wc(argv[i], show_chars, show_bytes, show_words, show_lines);
}
}
return 0;
}

74
src/shell.cpp Normal file
View File

@@ -0,0 +1,74 @@
//
// Created by connor on 2/20/24.
//
#include <stdio.h>
#include <unistd.h>
#include "command.h"
#include "utility.h"
#include <string>
#define INPUT_MAX 128
#define HISTORY_MAX 10
char HISTORY[HISTORY_MAX];
/**
* Main loop for the terminal.
* 1. Print the prompt including current fs location
* 2. Get the userinput using fgets into input
* 3. Convert char input into string input_str
* 4. Create a new command object and parse the input into the object.
* 5. Call the run() method on the command object if parsed successfully.
* 6. Flush input
*/
int main()
{
// stuff from utility.h
clear();
PRINT_BANNER();
//
while (1)
{
// user input and file system location
int parse_status, run_status;
char input[INPUT_MAX], cwd[128];
// get the current fs location and print a prompt
getcwd(cwd, sizeof(cwd));
printf("\033[32m%s\033[0m$ ", cwd);
if (!fgets(input, sizeof(input), stdin)) {
// if something is wrong with stdin, we're screwed.
continue;
}
// get user input as a string and check for empty input
std::string input_str(input);
// BLANK INPUT
if (input_str.size() <= 1)
{
continue;
}
// printf("%s", input);
// parse the new command
Command command;
parse_status = command.parse(input_str);
if (parse_status < 0) {
printf("\033[1;31mUnrecognized command..\n\033[0m");
continue;
}
run_status = command.run();
if (run_status < 0) {
printf("\033[1;31mUnexpected failure running command..\n\033[0m");
}
// flush input
// scanf("%*[^\n]%*1[\n]");
// getchar();
}
}

51
src/utility.cpp Normal file
View File

@@ -0,0 +1,51 @@
//
// Created by connor on 2/21/24.
//
#include "utility.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
std::vector<std::string> split_string(std::string &str,
const std::string &delimiter) {
size_t pos = 0;
std::vector<std::string> parsed_input;
if (str.find(delimiter) != std::string::npos) {
while ((pos = str.find(delimiter)) != std::string::npos) {
std::string token = str.substr(0, pos);
if (token.size() > 0) {
parsed_input.push_back(token);
}
str.erase(0, pos + delimiter.size());
}
}
if (str.size() > 0)
parsed_input.push_back(str);
return parsed_input;
}
int cd(const char *str) {
if (chdir(str) != 0) {
printf("Unable to find path.\n");
return -1;
}
return 0;
}
void PRINT_BANNER() {
printf("\033[32m________ ________ ________ ________\n\033[0m");
printf("\033[32m|\\ ____\\|\\ __ \\|\\ __ \\|\\ ____\\\n\033[0m");
printf("\033[32m\\ \\ \\___|\\ \\ \\|\\ \\ \\ \\|\\ \\ \\ \\___|_\n\033[0m");
printf("\033[32m \\ \\ \\ \\ \\ _ _\\ \\ \\\\\\ \\ \\_____ \\\n\033[0m");
printf("\033[32m \\ \\ \\____\\ \\ \\\\ \\\\ \\ \\\\\\ \\|____|\\ \\\n\033[0m");
printf("\033[32m \\ \\_______\\ \\__\\\\ _\\\\ \\_______\\____\\_\\ \\\n\033[0m");
printf("\033[32m \\|_______|\\|__|\\|__|\\|_______|\\_________\\\n\033[0m");
printf("\033[32m \\|_________|\n\033[0m");
printf("\033[32m \"Powered by terminal gremlins and questionable memory accessing.\"\n\n\033[0m");
printf("Input a command or use '\033[34mhelp\033[0m' for some ideas\n");
}
void clear() {
printf("\033[H\033[J");
}

32
src/utility.h Normal file
View File

@@ -0,0 +1,32 @@
//
// Created by connor on 2/21/24.
//
#ifndef EOS_UTILITY_H
#define EOS_UTILITY_H
#include <string>
#include <vector>
/*
* split up a string into a vector of strings based on the given delimiter
*/
std::vector<std::string> split_string(std::string &str,
const std::string &delimiter);
/*
* change directory
*/
int cd(const char *str);
/*
* Prints a banner.
*/
void PRINT_BANNER();
/*
* Clears the screen.
*/
void clear();
#endif // EOS_UTILITY_H