diff --git a/Makefile b/Makefile
index 2584e4a..62fd4bb 100644
--- a/Makefile
+++ b/Makefile
@@ -118,6 +118,7 @@ mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h
UPROGS=\
$U/_cat\
$U/_echo\
+ $U/_fnr\
$U/_forktest\
$U/_grep\
$U/_init\
@@ -128,13 +129,16 @@ UPROGS=\
$U/_rm\
$U/_sh\
$U/_stressfs\
+ $U/_test\
+ $U/_tolower\
+ $U/_tosh\
$U/_usertests\
$U/_grind\
$U/_wc\
$U/_zombie\
fs.img: mkfs/mkfs README.md $(UPROGS)
- mkfs/mkfs fs.img README.md $(UPROGS)
+ mkfs/mkfs fs.img README.md input.txt append.txt test.sh tosh_test1.sh tosh_test2.sh tosh_test3.sh tosh_test4.sh $(UPROGS)
-include kernel/*.d user/*.d
diff --git a/README.md b/README.md
index 8524b54..a19d573 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,77 @@
-# FogOS
+
FogOS
+
+ An Operating System Built on Top of Xv6
+
+ Explore the docs ยป
+
+
+ Report Bug
+ ยท
+ Request Feature
+
+
+
+
+## ๐ About the Project

+This project builds on top of the foundational xv6 operating system, a teaching OS developed by MIT based on Unix Version 6, serving as a hands-on tool for understanding OS internals, experimenting with kernel-level programming, and exploring Unix-like system development in a manageable and accessible codebase.
+
+### **โจ Features**
+- [test: check file types and compare values](https://github.com/USF-OS/FogOS/pull/77)
+- [tosh: command line shell](https://github.com/nestrada2/FogOS/blob/main/docs/TOSH.md)
+
+### **๐ ๏ธ Tech Stack**
+[![C][C.com]][C-url]
+
+[![Unix-like][Unix-like.com]][Unix-like-url]
+
+[![Xv6][Xv6.com]][Xv6-url]
+
+[![Qemu][Qemu.com]][Qemu-url]
+
+
+
+## ๐ฆ Getting Started
+
+### **๐พ Installation**
+1. **Install QEMU**
+
+ QEMU is required to emulate and test FogOS on your local machine. https://www.qemu.org/download/
+2. **Clone the repo**
+ ```sh
+ git clone https://github.com/nestrada2/FogOS.git
+ ```
+
+### **โถ๏ธ Running the Program**
+1. **Build FogOS**
+ ```sh
+ make
+ ```
+2. **Run FogOS with QEMU**
+ ```sh
+ make qemu
+ ```
+
+
+## ๐ License
+Distributed under the xv6 License. See [`xv6-LICENSE`](xv6-LICENSE) for more information.
+
+
+## ๐ Resources
+[Man](https://www.man7.org/linux/man-pages/index.html),
+[QEMU](https://www.qemu.org/docs/master/),
+[Stack Overflow](https://stackoverflow.com/),
+[W3 School](https://www.w3schools.com/),
+[Geeks for Geeks](https://www.geeksforgeeks.org/)
+
+
+[C.com]: https://img.shields.io/badge/C-00599C?style=for-the-badge&logo=c&logoColor=white
+[C-url]: https://www.iso.org/standard/74528.html
+[Unix-like.com]: https://img.shields.io/badge/Unix-like-4285F4?style=for-the-badge&logo=unix-like
+[Unix-like-url]: #
+[Xv6.com]: https://img.shields.io/badge/Xv6-100000?style=for-the-badge&logo=xv6
+[Xv6-url]: https://pdos.csail.mit.edu/6.828/2012/xv6.html
+[Qemu.com]: https://img.shields.io/badge/Qemu-EE0000?style=for-the-badge&logo=qemu
+[Qemu-url]: https://www.qemu.org/
diff --git a/append.txt b/append.txt
new file mode 100644
index 0000000..9e2434b
--- /dev/null
+++ b/append.txt
@@ -0,0 +1 @@
+Hello World! This is append.txt
diff --git a/docs/TEST.md b/docs/TEST.md
new file mode 100644
index 0000000..cb7e921
--- /dev/null
+++ b/docs/TEST.md
@@ -0,0 +1,58 @@
+
+
+
+
+## ๐ About the Command
+The `test` command evaluates a conditional expression. It is an if statement that will return zero (true), 1 (false), or greater than 1 (error). The `test` command will check if any flags were inputted and, based on the expression, will return a truthy or falsy value.
+
+
+### **๐โ Files Added**
+user:
+- test.c (new: call sys_open, pass a path to that and that will give us a file descriptor. With the file descriptor we call sys_fstat, and pass in the file descriptor, and a pointer to an empty stat struct which will be populated with the file's metadata)
+
+
+### **โจ Expressions Implemented in Test**
+| Command | Description |
+| :---------: | :-----------: |
+| -d file | True if file exists and is a directory. |
+| -e file | True if file exists (regardless of type). |
+| -f file | True if file exists and is a regular file. |
+| file1 -ef file2 | True if file1 and file2 exist and refer to the same file. |
+| s1 = s2 | True if the strings s1 and s2 are identical. |
+| s1 != s2 | True if the strings s1 and s2 are not identical. |
+| n1 -gt n2 | True if the integer n1 is algebraically greater than the integer n2. |
+| n1 -lt n2 | True if the integer n1 is algebraically less than the integer n2. |
+
+
+### **๐ Examples**
+ **Example 1: File Analysis**
+ * `test -f cat`
+ * Expected Output: 0
+
+ **Example 2: String Operators**
+ * `test "hello" = "hell"`
+ * Expected Output: 1
+
+ **Example 3: Number Operators**
+ * `test 23 -gt 32 `
+ * Expected Output: 1
+
+
+### **๐ฌ Tests**
+| Command | Description |
+| :---------: | :-----------: |
+| sh test.sh | Runs a series of automated tests to validate the behavior of the program. |
+
+**โ ๏ธ Note:** The test script only runs if you add shell scripting support.
diff --git a/docs/TOSH.md b/docs/TOSH.md
new file mode 100644
index 0000000..9808224
--- /dev/null
+++ b/docs/TOSH.md
@@ -0,0 +1,238 @@
+
+
+
+
+
+ Table of Contents
+
+ -
+ About the Shell
+
+ -
+ File Changes Overview
+
+
+ -
+ Synopsis
+
+
+ -
+ Examples
+
+
+ -
+ Special Features
+
+ -
+ Tests
+
+ -
+ Running the Program
+
+
+
+
+
+## ๐ About the Shell
+Tosh - The Operating Shell
+Tosh is a custom command-line shell with features such as command execution, built-in commands, I/O redirection, and background job management, providing an interactive user experience. Enhanced functionality with scripting mode (including shebang support), dynamic prompts displaying exit status, command history, and current working directory. Integrating custom path execution for seamless interaction with Unix-like environments. Tosh Implements advanced features such as executable script support and background job handling, optimizing user interaction with the operating system.
+
+
+
+
+## ๐ File Changes Overview
+
+### **๐โ Files Added**
+user:
+- **tosh.c**
+
+
+### **๐๐๏ธ Files Modified**
+kernel:
+- **fcntl.h**
+- **fs.c**
+- **sysfile.c**
+- **defs.h**
+- **syscall.c**
+- **syscall.h**
+
+user:
+- **user.h**
+- **usys.pl**
+
+
+
+
+## ๐ Synopsis
+ ```sh
+ tosh [options] [script]
+ ```
+
+ **โ ๏ธ Note:** Parameters in square brackets are optional.
+
+
+### **โ๏ธ Options**
+| Option | Description |
+| :---------: | :-----------: |
+| -v, --version | Displays shell version |
+| --about | Displays an about section of the shell |
+| --license | Shows the license |
+| -lc, --list-commands | Shows a list of all the commands |
+| -p, --private | Private mode is turned on |
+
+
+### **๐ง Built-In Commands**
+| Command | Description |
+| :---------: | :-----------: |
+| cat filename | Displays file content |
+| echo [string] | Displays text to the terminal |
+| ls [/dir] | List directory contents |
+| cd /dir | Change the current directory |
+| mkdir dir_name | Make directories |
+| pwd | Prints the working directory |
+| wc filename | Shows the number of lines, words, and bytes in a file |
+| history [-t] | Display the history list of the last 100 commands with their command numbers |
+| test | Evaluates a conditional expression |
+| uname [-a] | Displays system information |
+| joke | Tells a programming joke |
+| fact | Shares a fun programming-related fact |
+| quote | Provides a programming-related quote |
+| menu | Display the main menu |
+| ref | Provides a list of commands with their descriptions |
+| reset | Resets the shell |
+| exit | Exit the shell |
+
+
+### **โณ History Execution**
+| Command | Description |
+| :---------: | :-----------: |
+| !num | A history execution that will re-run command number num |
+| !! | A history execution that re-runs the last command that was entered |
+| !prefix | A history execution that re-runs the last command with that prefix |
+
+
+### **๐ I/O Redirection**
+| Command | Description |
+| :---------: | :-----------: |
+| command1 \| command2 | Allows the output of one command serves as the input for another command |
+| command > filename | Redirects the output of a command to a file |
+| filename < command | Redirects the content of a file to be input for a command |
+| command >> filename | Append the output of a command to the end of a file |
+
+
+### **๐ทโโ๏ธ Job Control**
+| Command | Description |
+| :---------: | :-----------: |
+| command & | Runs a command in the background |
+
+
+
+
+## **๐ Examples**
+ **Options**
+ ```sh
+ tosh -v
+ tosh --about
+ tosh -p
+ ```
+
+ **Basic Unix/Linux Commands**
+ ```sh
+ cat append.txt
+ echo "mwahahaha"
+ mkdir dog
+ cd dog
+ pwd
+ ```
+
+ **History Executions**
+ ```sh
+ !!
+ !23
+ !l
+ ```
+
+ **Scripting Support**
+ ```sh
+ tosh history_test1.sh
+ ```
+
+ **I/O Redirection**
+ ```sh
+ cat input.txt | toLower | fnr the teh a 4 e 3 i ! | fnr l 1 o 0 s 5 > output.txt
+ toLower < input.txt >> append.txt
+ ```
+
+ **Job Control**
+ ```sh
+ cat README.md &
+ toLower < input.txt >> append.txt &
+ ```
+
+
+
+
+## **โจ Special Features**
+| Feature | Description |
+| :---------: | :-----------: |
+| Splash Screen | Displays a welcome screen with the shell's name and key features |
+| Color Coding | Uses different colors to differentiate between commands, output, and errors for improved readability and usability |
+| Text Formatiing | Add text styling like italics, underline, and bold for clearer, more improved terminal display |
+| Case Insensitive (Built-In Commands) | Allows commands to be entered in any letter case for a more user-friendly experience |
+| Private Mode | Empowers users to interact with the shell incognito, ensuring commands are not recorded in the history |
+
+
+
+
+## **๐งโ๐ฌ Tests**
+| Command | Description |
+| :---------: | :-----------: |
+| tosh tosh_test1.sh | Runs a series of automated tests to validate the behavior of the built-in commands |
+| tosh tosh_test2.sh | Similar test series for built-in commands |
+| tosh tosh_test3.sh | Similar test series for built-in commands |
+| tosh tosh_test4.sh | Similar test series for built-in commands |
+
+
+
+
+## **โถ๏ธ Running the Program**
+1. **Build FogOS**
+ ```sh
+ make
+ ```
+2. **Run FogOS with QEMU**
+ ```sh
+ make qemu
+ ```
+3. **Run Tosh**
+ ```sh
+ tosh [options] [script]
+ ```
diff --git a/input.txt b/input.txt
new file mode 100644
index 0000000..2e77ad2
--- /dev/null
+++ b/input.txt
@@ -0,0 +1,4 @@
+Mechanic: Somebody set up us the bomb.
+Operator: Main screen turn on.
+CATS: All your base are belong to us.
+CATS: You have no chance to survive make your time.
diff --git a/kernel/defs.h b/kernel/defs.h
index a3c962b..a981ca9 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -53,6 +53,7 @@ int readi(struct inode*, int, uint64, uint, uint);
void stati(struct inode*, struct stat*);
int writei(struct inode*, int, uint64, uint, uint);
void itrunc(struct inode*);
+int getcwd(char *, uint);
// ramdisk.c
void ramdiskinit(void);
diff --git a/kernel/exec.c b/kernel/exec.c
index e18bbb6..fd8eea2 100644
--- a/kernel/exec.c
+++ b/kernel/exec.c
@@ -39,6 +39,28 @@ exec(char *path, char **argv)
}
ilock(ip);
+ // Check for #! at the beginning
+ // Shell scripts look like this at the start:
+ // #!/sh argument (argument not implemented)
+ char shebang[2];
+ readi(ip, 0, (uint64)&shebang, 0, sizeof(shebang));
+ if (shebang[0] == '#' && shebang[1] == '!') {
+ char interpreter[MAXPATH];
+ int read_sz = readi(
+ ip, 0, (uint64)&interpreter, sizeof(shebang), sizeof(interpreter));
+ for (int i = 0; i < MAXPATH && i < read_sz; ++i) {
+ if (interpreter[i] == '\n' || interpreter[i] == ' ') {
+ interpreter[i] = '\0';
+ break;
+ }
+ }
+
+ char *new_argv[] = { interpreter, argv[0], 0 };
+ iunlockput(ip);
+ end_op();
+ return exec(interpreter, new_argv);
+ }
+
// Check ELF header
if(readi(ip, 0, (uint64)&elf, 0, sizeof(elf)) != sizeof(elf))
goto bad;
diff --git a/kernel/fcntl.h b/kernel/fcntl.h
index 44861b9..174f08f 100644
--- a/kernel/fcntl.h
+++ b/kernel/fcntl.h
@@ -3,3 +3,4 @@
#define O_RDWR 0x002
#define O_CREATE 0x200
#define O_TRUNC 0x400
+#define O_APPEND 0x800
diff --git a/kernel/fs.c b/kernel/fs.c
index c6bab15..0063580 100644
--- a/kernel/fs.c
+++ b/kernel/fs.c
@@ -695,3 +695,96 @@ nameiparent(char *path, char *name)
{
return namex(path, 1, name);
}
+
+// Get the name of a particular inode in a directory.
+int
+diriname(struct inode *dp, ushort inum, char name[DIRSIZ], ushort *iparent)
+{
+ ilock(dp);
+
+ if (dp->type != T_DIR)
+ panic("diriname not DIR");
+
+ struct dirent de;
+ if (iparent) {
+ // Read second entry ('..', aka parent)
+ readi(dp, 0, (uint64) &de, sizeof(struct dirent), sizeof(struct dirent));
+ *iparent = de.inum;
+ }
+
+ for (uint off = 0; off < dp->size; off += sizeof(de)){
+ if (readi(dp, 0, (uint64) &de, off, sizeof(de)) != sizeof(de))
+ panic("diriname read");
+ if (de.inum == 0)
+ continue;
+ if (de.inum == inum && name) {
+ // Found the inode
+ strncpy(name, de.name, DIRSIZ);
+ iunlockput(dp);
+ return strlen(name);
+ }
+ }
+
+ iunlockput(dp);
+ return -1;
+}
+
+int
+getcwd(char *buf, uint size)
+{
+ struct inode *ip = myproc()->cwd;
+ ilock(ip);
+
+ if(ip->type != T_DIR)
+ panic("getcwd not DIR");
+
+ if (!buf || size <= 1) {
+ iunlock(ip);
+ return -1;
+ }
+
+ if (ip->inum == 1 && size > 1) {
+ buf[0] = '/';
+ buf[1] = '\0';
+ iunlock(ip);
+ return 0;
+ }
+
+ // We copy the path to the *end* of the buffer instead of the beginning, then
+ // work backwards. Start by null terminating the string.
+ buf[size - 1] = '\0';
+ uint bufp = size - 2;
+
+ struct inode *i = iget(ip->dev, ip->inum);
+
+ ushort iparent;
+ ushort last_parent = ip->inum;
+ ushort icurrent = ip->inum;
+ char name[DIRSIZ];
+ iunlock(ip);
+
+ // Starting with the CWD inode, go up one level (..), read that directory, and
+ // search for the CWD inode in it. Repeat the process until we reach /.
+ while (icurrent != 1 && diriname(i, icurrent, name, &iparent) != -1) {
+ if (icurrent != last_parent) {
+ // We found another path element. Add it to the string.
+ int len = strlen(name);
+ if (len < bufp) {
+ bufp -= len;
+ memmove(buf + bufp, name, len);
+ buf[--bufp] = '/';
+ } else {
+ // No more space to store the path.
+ break;
+ }
+ }
+ i = iget(ip->dev, iparent);
+ icurrent = last_parent;
+ last_parent = iparent;
+ }
+
+ // If we didn't use the entire buffer, slide the string over to the beginning.
+ memmove(buf, buf + bufp, size - bufp);
+
+ return 0;
+}
diff --git a/kernel/memlayout.h b/kernel/memlayout.h
index 776f98c..1ea22e5 100644
--- a/kernel/memlayout.h
+++ b/kernel/memlayout.h
@@ -25,6 +25,9 @@
#define VIRTIO0 0x10001000
#define VIRTIO0_IRQ 1
+// Goldfish Real Time Clock
+#define GOLDFISH_RTC 0x101000
+
// core local interruptor (CLINT), which contains the timer.
#define CLINT 0x2000000L
#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8*(hartid))
diff --git a/kernel/syscall.c b/kernel/syscall.c
index ed65409..3536631 100644
--- a/kernel/syscall.c
+++ b/kernel/syscall.c
@@ -101,6 +101,8 @@ extern uint64 sys_unlink(void);
extern uint64 sys_link(void);
extern uint64 sys_mkdir(void);
extern uint64 sys_close(void);
+extern uint64 sys_getcwd(void);
+extern uint64 sys_gettime(void);
// An array mapping syscall numbers from syscall.h
// to the function that handles the system call.
@@ -126,6 +128,8 @@ static uint64 (*syscalls[])(void) = {
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
+[SYS_getcwd] sys_getcwd,
+[SYS_gettime] sys_gettime,
};
void
diff --git a/kernel/syscall.h b/kernel/syscall.h
index bc5f356..7a48751 100644
--- a/kernel/syscall.h
+++ b/kernel/syscall.h
@@ -20,3 +20,5 @@
#define SYS_link 19
#define SYS_mkdir 20
#define SYS_close 21
+#define SYS_getcwd 22
+#define SYS_gettime 23
diff --git a/kernel/sysfile.c b/kernel/sysfile.c
index 16b668c..7e4be92 100644
--- a/kernel/sysfile.c
+++ b/kernel/sysfile.c
@@ -354,7 +354,7 @@ sys_open(void)
f->major = ip->major;
} else {
f->type = FD_INODE;
- f->off = 0;
+ f->off = (omode & O_APPEND) ? ip->size: 0; // Offset by the size of the file
}
f->ip = ip;
f->readable = !(omode & O_WRONLY);
@@ -406,6 +406,34 @@ sys_mknod(void)
return 0;
}
+uint64
+sys_getcwd(void)
+{
+ uint64 ubuf;
+ int sz;
+
+ begin_op();
+ argaddr(0, &ubuf);
+ argint(1, &sz);
+
+ if (sz > PGSIZE) {
+ end_op();
+ return -1;
+ }
+
+ char *buf = kalloc();
+ getcwd(buf, sz);
+
+ if (copyout(myproc()->pagetable, ubuf, buf, sz) < 0) {
+ end_op();
+ return -1;
+ }
+
+ kfree(buf);
+ end_op();
+ return 0;
+}
+
uint64
sys_chdir(void)
{
diff --git a/kernel/sysproc.c b/kernel/sysproc.c
index 1de184e..cb7e370 100644
--- a/kernel/sysproc.c
+++ b/kernel/sysproc.c
@@ -89,3 +89,13 @@ sys_uptime(void)
release(&tickslock);
return xticks;
}
+
+// returns the current Unix timestamp in nano seconds
+uint64
+sys_gettime(void)
+{
+ volatile uint64 *rtc_dev = (uint64 *) GOLDFISH_RTC;
+
+ // Deference to get Time
+ return *rtc_dev;
+}
diff --git a/kernel/vm.c b/kernel/vm.c
index 9f69783..f1d8e3a 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -30,6 +30,9 @@ kvmmake(void)
// virtio mmio disk interface
kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
+ // Goldfish Real Time Clock
+ kvmmap(kpgtbl, GOLDFISH_RTC, GOLDFISH_RTC, PGSIZE, PTE_R | PTE_W);
+
// PLIC
kvmmap(kpgtbl, PLIC, PLIC, 0x400000, PTE_R | PTE_W);
diff --git a/test.sh b/test.sh
new file mode 100644
index 0000000..bf395fc
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,297 @@
+#/**
+# * File: test.sh
+# * Author: Nino Estrada
+# * Date: September 23, 2024
+# *
+# * Description:
+# * This script runs the command 'test' and checks its output
+# * against the expected result. It supports multiple test cases.
+# *
+# * Usage:
+# * Compile: make qemu
+# * Run: sh test.sh
+# *
+# * Results:
+# * The expected results should be 0 for true, 1 for false, and 2 for error or syntax error.
+# * Note: Everytime there is an error the help text is printing out.
+# *
+# * Note: This script only runs if you add shell scripting support.
+# * https://github.com/USF-OS/FogOS/pull/71
+# */
+
+
+#!/bin/sh
+
+
+#/**
+# * Tests valid number of arguments
+# *
+# * Test 1: Check if valid number of arguments for any test implementation
+# */
+echo -------------------- Testing if No Arguments were Provided --------------------
+echo Input: test
+echo ***** Expected Result *****
+echo Result: 2
+echo ***** Actual Result *****
+test
+
+
+#/**
+# * Tests the help flag
+# *
+# * Test 1: Check if help flag is outputting the implementations for the "test" command
+# */
+echo -------------------- Testing Help Flag --------------------
+echo Input: test -h
+echo ***** Expected Result *****
+echo A list of commands with their respective description
+echo ***** Actual Result *****
+test -h
+
+
+#/**
+# * Tests the file flags
+# *
+# * Test 1: Check if file is directory (output=1)
+# * Test 2: Check if file exist (output=0)
+# * Test 3: Check if non-existing file exist (output=1)
+# * Test 4: Check if empty file exist (output=0)
+# * Test 5: Check if file is a regular file (output=0)
+# * Test 6: Check if valid file flag (output=2)
+# * Test 7: Check if there are enough arguments (output=2)
+# */
+echo -------------------- Testing File Flags --------------------
+echo ---------- "-d" ----------
+echo Input: test -d cat
+echo ***** Expected Result *****
+echo Result: 1
+echo ***** Actual Result *****
+test -d cat
+
+echo ----- "-e" -----
+echo Input: test -e cat
+echo ***** Expected Result *****
+echo Result: 0
+echo ***** Actual Result *****
+test -e cat
+
+echo ----- "-e for non-existing" -----
+echo Input: test -e dog
+echo ***** Expected Result *****
+echo Result: 1
+echo ***** Actual Result *****
+test -e dog
+
+echo ----- "-e for empty file" -----
+echo > empty.txt
+echo Input: test -e empty.txt
+echo ***** Expected Result *****
+echo Result: 0
+echo ***** Actual Result *****
+test -e empty.txt
+
+echo ---------- "-f" ----------
+echo Input: test -f cat
+echo ***** Expected Result *****
+echo Result: 0
+echo ***** Actual Result *****
+test -f cat
+
+echo ---------- invalid flag ----------
+echo Input: test -g cat
+echo ***** Expected Result *****
+echo Result: 2
+echo ***** Actual Result *****
+test -g cat
+
+echo ---------- not enough arguments ----------
+echo Input: test -f
+echo ***** Expected Result *****
+echo Result: 2
+echo ***** Actual Result *****
+test -f
+
+
+#/**
+# * Tests the string expressions
+# *
+# * Test 1: Check if two strings are equal (output=0)
+# * Test 2: Check if two strings are equal (output=1)
+# * Test 3: Check if two strings are not equal (output=1)
+# * Test 4: Check if two strings are not equal (output=0)
+# * Test 5: Check if valid string operator (output=2)
+# * Test 6: Check strings with spaces are equal (output=0)
+# * Test 7: Check strings with spaces are equal (output=1)
+# * Test 8: Check strings with spaces are not equal (output=0)
+# * Test 9: Check strings with spaces are not equal (output=1)
+# * Test 10: Check if there are enough arguments (output=2)
+# */
+echo -------------------- Testing String Operators --------------------
+echo ---------- "=" ----------
+echo Input: test "hello" = "hello"
+echo ***** Expected Result *****
+echo Result: 0
+echo ***** Actual Result *****
+test "hello" = "hello"
+
+echo ----- "=" -----
+echo Input: test "hello" = "hell"
+echo ***** Expected Result *****
+echo Result: 1
+echo ***** Actual Result *****
+test "hello" = "hell"
+
+echo ---------- "!=" ----------
+echo Input: test "hello" != "hello"
+echo ***** Expected Result *****
+echo Result: 1
+echo ***** Actual Result *****
+test "hello" != "hello"
+
+echo ---------- "!=" ----------
+echo Input: test "hello" != "hell"
+echo ***** Expected Result *****
+echo Result: 0
+echo ***** Actual Result *****
+test "hello" != "hell"
+
+echo ---------- invalid operator ----------
+echo Input: test "hello" += "hello"
+echo ***** Expected Result *****
+echo Result: 2
+echo ***** Actual Result *****
+test "hello" += "hello"
+
+echo ---------- strings with spaces: "=" ----------
+echo Input: test "hi hello" = "hi hello"
+echo ***** Expected Result *****
+echo Result: 0
+echo ***** Actual Result *****
+test "hi hello" = "hi hello"
+
+echo ---------- strings with spaces "=" ----------
+echo Input: test "hi hello" = "hi hell"
+echo ***** Expected Result *****
+echo Result: 1
+echo ***** Actual Result *****
+test "hi hello" = "hi hell"
+
+echo ---------- strings with spaces "!=" ----------
+echo Input: test "hi hello" != "hi hell"
+echo ***** Expected Result *****
+echo Result: 0
+echo ***** Actual Result *****
+test "hi hello" != "hi hell"
+
+echo ---------- strings with spaces "!=" ----------
+echo Input: test "hi hello" != "hi hello"
+echo ***** Expected Result *****
+echo Result: 1
+echo ***** Actual Result *****
+test "hi hello" != "hi hello"
+
+echo ---------- no closing quote ----------
+echo Input: test "hi hi" != "hi hi
+echo ***** Expected Result *****
+echo Result: 2
+echo ***** Actual Result *****
+test "hi hi" != "hi hi
+
+echo ---------- not enough arguments ----------
+echo Input: test "hell"
+echo ***** Expected Result *****
+echo Result: 2
+echo ***** Actual Result *****
+test "hell"
+
+
+#/**
+# * Tests the number expressions
+# *
+# * Test 1: Check if num1 is less than num2 (output=0)
+# * Test 2: Check if num1 is less than num2 (output=1)
+# * Test 3: Check if num1 is greater than num2 (output=0)
+# * Test 4: Check if num1 is greater than num2 (output=1)
+# * Test 5: Check if valid number operator (output=2)
+# * Test 6: Check if there are enough arguments (output=2)
+# * Test 7: Check if argument is a valid number to compare: num1 (output=2)
+# * Test 8: Check if argument is a valid number to compare: num2 (output=2)
+# */
+echo -------------------- Testing Number Operators --------------------
+echo ---------- "-lt" ----------
+echo Input: test 23 -lt 32
+echo ***** Expected Result *****
+echo Result: 0
+echo ***** Actual Result *****
+test 23 -lt 32
+
+echo ----- "-lt" -----
+echo Input: test 32 -lt 23
+echo ***** Expected Result *****
+echo Result: 1
+echo ***** Actual Result *****
+test 32 -lt 23
+
+echo ---------- "-gt" ----------
+echo Input: test 32 -gt 23
+echo ***** Expected Result *****
+echo Result: 0
+echo ***** Actual Result *****
+test 32 -gt 23
+
+echo ---------- "-gt" ----------
+echo Input: test 23 -gt 32
+echo ***** Expected Result *****
+echo Result: 1
+echo ***** Actual Result *****
+test 23 -gt 32
+
+echo ---------- invalid operator ----------
+echo Input: test 23 -g 32
+echo ***** Expected Result *****
+echo Result: 2
+echo ***** Actual Result *****
+test 23 -g 32
+
+echo ---------- not enough arguments ----------
+echo Input: test 23
+echo ***** Expected Result *****
+echo Result: 2
+echo ***** Actual Result *****
+test 23
+
+echo ---------- valid number to compare ----------
+echo Input: test 23a5 -lt 45
+echo ***** Expected Result *****
+echo Result: 2
+echo ***** Actual Result *****
+test 23a5 -lt 45
+
+echo ---------- valid number to compare ----------
+echo Input: test 23 -lt 45ed7
+echo ***** Expected Result *****
+echo Result: 2
+echo ***** Actual Result *****
+test 23 -lt 45ed7
+
+
+#/**
+# * Tests the file comparisons
+# *
+# * Test 1: Check if file exist and refers to the same file (output=0)
+# * Test 2: Check if there are enough arguments (output=2)
+# */
+echo -------------------- Testing File Comparisons --------------------
+echo ---------- "-ef" ----------
+echo Input: test cat -ef cat
+echo ***** Expected Result *****
+echo Result: 0
+echo ***** Actual Result *****
+test cat -ef cat
+
+echo ---------- not enough arguments ----------
+echo Input: test cat -ef
+echo ***** Expected Result *****
+echo Result: 2
+echo ***** Actual Result *****
+test cat -ef
diff --git a/tosh_test1.sh b/tosh_test1.sh
new file mode 100644
index 0000000..3c124d2
--- /dev/null
+++ b/tosh_test1.sh
@@ -0,0 +1,33 @@
+# Start of file. This line should not cause errors
+echo Command 000
+echo Command 001
+echo Command 002
+ehchhho Command 003
+echo Command 004
+echo Command 005
+echo Command 006 # Look, it's command 6. This shouldn't print
+echo Command 007
+echo Command 008
+# Empty commands (like this one) and blank lines should not go in history
+
+
+
+
+
+/ls /
+mkdir a
+mkdir a/b
+mkdir a/b/c
+mkdir a/b/c/hello
+cd a
+cd b/c/hello
+/ls
+/wc /README.md
+cd /
+/wc README.md
+echo ---
+history
+echo ---
+history -t
+exit
+echo If you see this line, exit is not working.
diff --git a/tosh_test2.sh b/tosh_test2.sh
new file mode 100644
index 0000000..d07fb12
--- /dev/null
+++ b/tosh_test2.sh
@@ -0,0 +1,433 @@
+# Make sure empty lines (including this one) are handled
+# properly to pass this test.
+#
+# Bad/unknown commands **do** go into the history.
+
+thisisacommandthatprobablydoesntexistbutletstryitanywayhuh
+echo Command 000
+echo Command 001
+echo Command 002
+echo Command 003
+echo Command 004
+echo Command 005
+
+echo Command 006
+
+echo Command 007
+
+echo Command 008
+
+echo Command 009
+echo Command 010
+echo Command 011
+echo Command 012
+echo Command 013
+echo Command 014
+echo Command 015
+echo Command 016
+echo Command 017
+echo Command 018
+echo Command 019
+echo Command 020
+echo Command 021
+echo Command 022
+echo Command 023
+echo Command 024
+echo Command 025
+echo Command 026
+echo Command 027
+echo Command 028
+echo Command 029
+echo Command 030
+echo Command 031
+echo Command 032
+echo Command 033
+echo Command 034
+echo Command 035
+echo Command 036
+echo Command 037
+echo Command 038
+echo Command 039
+echo Command 040
+echo Command 041
+echo Command 042
+echo Command 043
+echo Command 044
+echo Command 045
+echo Command 046
+echo Command 047
+echo Command 048
+echo Command 049
+echo Command 050
+echo Command 051
+echo Command 052
+echo Command 053
+echo Command 054
+echo Command 055
+echo Command 056
+echo Command 057
+echo Command 058
+echo Command 059
+echo Command 060
+echo Command 061
+echo Command 062
+echo Command 063
+echo Command 064
+echo Command 065
+echo Command 066
+echo Command 067
+echo Command 068
+echo Command 069
+echo Command 070
+echo Command 071
+echo Command 072
+echo Command 073
+echo Command 074
+echo Command 075
+echo Command 076
+echo Command 077
+echo Command 078
+echo Command 079
+echo Command 080
+echo Command 081
+echo Command 082
+echo Command 083
+echo Command 084
+echo Command 085
+echo Command 086
+echo Command 087
+echo Command 088
+echo Command 089
+echo Command 090
+echo Command 091
+echo Command 092
+echo Command 093
+echo Command 094
+echo Command 095
+echo Command 096
+echo Command 097
+echo Command 098
+echo Command 099
+echo Command 100
+echo Command 101
+echo Command 102
+echo Command 103
+echo Command 104
+echo Command 105
+echo Command 106
+echo Command 107
+echo Command 108
+echo Command 109
+echo Command 110
+echo Command 111
+echo Command 112
+echo Command 113
+echo Command 114
+echo Command 115
+echo Command 116
+echo Command 117
+echo Command 118
+echo Command 119
+echo Command 120
+echo Command 121
+echo Command 122
+echo Command 123
+echo Command 124
+echo Command 125
+echo Command 126
+echo Command 127
+echo Command 128
+echo Command 129
+echo Command 130
+echo Command 131
+echo Command 132
+echo Command 133
+echo Command 134
+echo Command 135
+echo Command 136
+echo Command 137
+echo Command 138
+echo Command 139
+echo Command 140
+echo Command 141
+echo Command 142
+echo Command 143
+echo Command 144
+echo Command 145
+echo Command 146
+echo Command 147
+echo Command 148
+echo Command 149
+echo Command 150
+echo Command 151
+echo Command 152
+echo Command 153
+echo Command 154
+echo Command 155
+echo Command 156
+echo Command 157
+echo Command 158
+echo Command 159
+echo Command 160
+echo Command 161
+echo Command 162
+echo Command 163
+echo Command 164
+echo Command 165
+echo Command 166
+echo Command 167
+echo Command 168
+echo Command 169
+echo Command 170
+echo Command 171
+echo Command 172
+echo Command 173
+echo Command 174
+echo Command 175
+echo Command 176
+echo Command 177
+echo Command 178
+echo Command 179
+echo Command 180
+echo Command 181
+echo Command 182
+echo Command 183
+echo Command 184
+echo Command 185
+echo Command 186
+echo Command 187
+echo Command 188
+echo Command 189
+echo Command 190
+echo Command 191
+echo Command 192
+echo Command 193
+echo Command 194
+echo Command 195
+echo Command 196
+echo Command 197
+echo Command 198
+echo Command 199
+echo Command 200
+echo Command 201
+echo Command 202
+echo Command 203
+echo Command 204
+echo Command 205
+echo Command 206
+echo Command 207
+echo Command 208
+echo Command 209
+echo Command 210
+echo Command 211
+echo Command 212
+echo Command 213
+echo Command 214
+echo Command 215
+echo Command 216
+echo Command 217
+echo Command 218
+echo Command 219
+echo Command 220
+echo Command 221
+echo Command 222
+echo Command 223
+echo Command 224
+echo Command 225
+echo Command 226
+echo Command 227
+echo Command 228
+echo Command 229
+echo Command 230
+echo Command 231
+echo Command 232
+echo Command 233
+echo Command 234
+echo Command 235
+echo Command 236
+echo Command 237
+echo Command 238
+echo Command 239
+echo Command 240
+echo Command 241
+echo Command 242
+echo Command 243
+echo Command 244
+echo Command 245
+echo Command 246
+echo Command 247
+echo Command 248
+echo Command 249
+echo Command 250
+echo Command 251
+echo Command 252
+echo Command 253
+echo Command 254
+echo Command 255
+echo Command 256
+echo Command 257
+echo Command 258
+echo Command 259
+echo Command 260
+echo Command 261
+echo Command 262
+echo Command 263
+echo Command 264
+echo Command 265
+echo Command 266
+echo Command 267
+echo Command 268
+echo Command 269
+echo Command 270
+echo Command 271
+echo Command 272
+echo Command 273
+echo Command 274
+echo Command 275
+echo Command 276
+echo Command 277
+echo Command 278
+echo Command 279
+echo Command 280
+echo Command 281
+echo Command 282
+echo Command 283
+echo Command 284
+echo Command 285
+echo Command 286
+echo Command 287
+echo Command 288
+echo Command 289
+echo Command 290
+echo Command 291
+echo Command 292
+echo Command 293
+echo Command 294
+echo Command 295
+echo Command 296
+echo Command 297
+echo Command 298
+echo Command 299
+echo Command 300
+echo Command 301
+echo Command 302
+echo Command 303
+echo Command 304
+echo Command 305
+echo Command 306
+echo Command 307
+echo Command 308
+echo Command 309
+echo Command 310
+echo Command 311
+echo Command 312
+echo Command 313
+echo Command 314
+echo Command 315
+echo Command 316
+echo Command 317
+echo Command 318
+echo Command 319
+echo Command 320
+echo Command 321
+echo Command 322
+echo Command 323
+echo Command 324
+echo Command 325
+echo Command 326
+echo Command 327
+echo Command 328
+echo Command 329
+echo Command 330
+echo Command 331
+echo Command 332
+echo Command 333
+echo Command 334
+echo Command 335
+echo Command 336
+echo Command 337
+echo Command 338
+echo Command 339
+echo Command 340
+echo Command 341
+echo Command 342
+echo Command 343
+echo Command 344
+echo Command 345
+echo Command 346
+echo Command 347
+echo Command 348
+echo Command 349
+echo Command 350
+echo Command 351
+echo Command 352
+echo Command 353
+echo Command 354
+echo Command 355
+echo Command 356
+echo Command 357
+echo Command 358
+echo Command 359
+echo Command 360
+echo Command 361
+echo Command 362
+echo Command 363
+echo Command 364
+echo Command 365
+echo Command 366
+echo Command 367
+echo Command 368
+echo Command 369
+echo Command 370
+echo Command 371
+echo Command 372
+echo Command 373
+echo Command 374
+echo Command 375
+echo Command 376
+echo Command 377
+echo Command 378
+echo Command 379
+echo Command 380
+echo Command 381
+echo Command 382
+echo Command 383
+echo Command 384
+echo Command 385
+echo Command 386
+echo Command 387
+echo Command 388
+echo Command 389
+echo Command 390
+echo Command 391
+echo Command 392
+echo Command 393
+echo Command 394
+echo Command 395
+echo Command 396
+echo Command 397
+echo Command 398
+echo Command 399
+echo Command 400
+echo Command 401
+echo Command 402
+echo Command 403
+echo Command 404
+echo Command 405
+echo Command 406
+pwd # May or may not exist...
+echo Command 407
+echo Command 408
+echo Command 409
+echo Command 410
+echo Command 411
+echo Command 412
+echo Command 413
+echo Command 414
+echo Command 415
+echo Command 416
+echo Command 417
+echo Command 418
+echo Command 419
+echo -----
+history
diff --git a/tosh_test3.sh b/tosh_test3.sh
new file mode 100644
index 0000000..6c4e8d8
--- /dev/null
+++ b/tosh_test3.sh
@@ -0,0 +1,430 @@
+!542 # Doesn't actually exist :-)
+history
+echo Command 002
+echo Command 003
+echo Command 004
+echo Command 005
+echo Command 006
+echo Command 007
+echo Command 008
+echo Command 009
+echo Command 010
+echo Command 011
+echo Command 012
+echo Command 013
+echo Command 014
+echo Command 015
+echo Command 016
+echo Command 017
+echo Command 018
+echo Command 019
+echo Command 020
+echo Command 021
+echo Command 022
+echo Command 023
+echo Command 024
+echo Command 025
+echo Command 026
+echo Command 027
+echo Command 028
+echo Command 029
+echo Command 030
+echo Command 031
+echo Command 032
+echo Command 033
+echo Command 034
+echo Command 035
+echo Command 036
+echo Command 037
+echo Command 038
+echo Command 039
+echo Command 040
+echo Command 041
+echo Command 042
+echo Command 043
+echo Command 044
+echo Command 045
+echo Command 046
+echo Command 047
+echo Command 048
+echo Command 049
+echo Command 050
+echo Command 051
+echo Command 052
+echo Command 053
+echo Command 054
+echo Command 055
+echo Command 056
+echo Command 057
+echo Command 058
+echo Command 059
+echo Command 060
+echo Command 061
+echo Command 062
+echo Command 063
+echo Command 064
+echo Command 065
+echo Command 066
+echo Command 067
+echo Command 068
+echo Command 069
+echo Command 070
+echo Command 071
+echo Command 072
+echo Command 073
+echo Command 074
+echo Command 075
+echo Command 076
+echo Command 077
+echo Command 078
+echo Command 079
+echo Command 080
+echo Command 081
+echo Command 082
+echo Command 083
+echo Command 084
+echo Command 085
+echo Command 086
+echo Command 087
+echo Command 088
+echo Command 089
+echo Command 090
+echo Command 091
+echo Command 092
+echo Command 093
+echo Command 094
+echo Command 095
+echo Command 096
+echo Command 097
+echo Command 098
+echo Command 099
+echo Command 100
+echo Command 101
+echo Command 102
+echo Command 103
+echo Command 104
+echo Command 105
+echo Command 106
+echo Command 107
+echo Command 108
+echo Command 109
+echo Command 110
+echo Command 111
+echo Command 112
+echo Command 113
+echo Command 114
+echo Command 115
+echo Command 116
+echo Command 117
+echo Command 118
+echo Command 119
+echo Command 120
+echo Command 121
+echo Command 122
+echo Command 123
+echo Command 124
+echo Command 125
+echo Command 126
+echo Command 127
+echo Command 128
+echo Command 129
+echo Command 130
+echo Command 131
+echo Command 132
+echo Command 133
+echo Command 134
+echo Command 135
+echo Command 136
+echo Command 137
+echo Command 138
+echo Command 139
+echo Command 140
+echo Command 141
+echo Command 142
+echo Command 143
+echo Command 144
+echo Command 145
+echo Command 146
+echo Command 147
+echo Command 148
+echo Command 149
+echo Command 150
+echo Command 151
+echo Command 152
+echo Command 153
+echo Command 154
+echo Command 155
+echo Command 156
+echo Command 157
+echo Command 158
+echo Command 159
+echo Command 160
+echo Command 161
+echo Command 162
+echo Command 163
+echo Command 164
+echo Command 165
+echo Command 166
+echo Command 167
+echo Command 168
+echo Command 169
+echo Command 170
+echo Command 171
+echo Command 172
+echo Command 173
+echo Command 174
+echo Command 175
+echo Command 176
+echo Command 177
+echo Command 178
+echo Command 179
+echo Command 180
+echo Command 181
+echo Command 182
+echo Command 183
+echo Command 184
+echo Command 185
+echo Command 186
+echo Command 187
+echo Command 188
+echo Command 189
+echo Command 190
+echo Command 191
+echo Command 192
+echo Command 193
+echo Command 194
+echo Command 195
+echo Command 196
+echo Command 197
+echo Command 198
+echo Command 199
+echo Command 200
+echo Command 201
+echo Command 202
+echo Command 203
+echo Command 204
+echo Command 205
+echo Command 206
+echo Command 207
+echo Command 208
+echo Command 209
+echo Command 210
+echo Command 211
+echo Command 212
+echo Command 213
+echo Command 214
+echo Command 215
+echo Command 216
+echo Command 217
+echo Command 218
+echo Command 219
+echo Command 220
+echo Command 221
+echo Command 222
+echo Command 223
+echo Command 224
+echo Command 225
+echo Command 226
+echo Command 227
+echo Command 228
+echo Command 229
+echo Command 230
+echo Command 231
+echo Command 232
+echo Command 233
+echo Command 234
+echo Command 235
+echo Command 236
+echo Command 237
+echo Command 238
+echo Command 239
+echo Command 240
+echo Command 241
+echo Command 242
+echo Command 243
+echo Command 244
+echo Command 245
+echo Command 246
+echo Command 247
+echo Command 248
+echo Command 249
+echo Command 250
+echo Command 251
+echo Command 252
+echo Command 253
+echo Command 254
+echo Command 255
+echo Command 256
+echo Command 257
+echo Command 258
+echo Command 259
+echo Command 260
+echo Command 261
+echo Command 262
+echo Command 263
+echo Command 264
+echo Command 265
+echo Command 266
+echo Command 267
+echo Command 268
+echo Command 269
+echo Command 270
+echo Command 271
+echo Command 272
+echo Command 273
+echo Command 274
+echo Command 275
+echo Command 276
+echo Command 277
+echo Command 278
+echo Command 279
+echo Command 280
+echo Command 281
+echo Command 282
+echo Command 283
+echo Command 284
+echo Command 285
+echo Command 286
+echo Command 287
+echo Command 288
+echo Command 289
+echo Command 290
+echo Command 291
+echo Command 292
+echo Command 293
+echo Command 294
+echo Command 295
+echo Command 296
+echo Command 297
+echo Command 298
+echo Command 299
+echo Command 300
+echo Command 301
+echo Command 302
+echo Command 303
+echo Command 304
+echo Command 305
+echo Command 306
+echo Command 307
+echo Command 308
+echo Command 309
+echo Command 310
+echo Command 311
+echo Command 312
+echo Command 313
+echo Command 314
+echo Command 315
+echo Command 316
+echo Command 317
+echo Command 318
+echo Command 319
+echo Command 320
+echo Command 321
+echo Command 322
+echo Command 323
+echo Command 324
+echo Command 325
+echo Command 326
+echo Command 327
+echo Command 328
+echo Command 329
+echo Command 330
+echo Command 331
+echo Command 332
+echo Command 333
+echo Command 334
+echo Command 335
+echo Command 336
+echo Command 337
+echo Command 338
+echo Command 339
+echo Command 340
+echo Command 341
+echo Command 342
+echo Command 343
+echo Command 344
+echo Command 345
+echo Command 346
+echo Command 347
+echo Command 348
+echo Command 349
+echo Command 350
+echo Command 351
+echo Command 352
+echo Command 353
+echo Command 354
+echo Command 355
+echo Command 356
+echo Command 357
+echo Command 358
+echo Command 359
+echo Command 360
+echo Command 361
+echo Command 362
+echo Command 363
+echo Command 364
+echo Command 365
+echo Command 366
+echo Command 367
+echo Command 368
+echo Command 369
+echo Command 370
+echo Command 371
+echo Command 372
+echo Command 373
+echo Command 374
+echo Command 375
+echo Command 376
+echo Command 377
+echo Command 378
+echo Command 379
+echo Command 380
+echo Command 381
+echo Command 382
+echo Command 383
+echo Command 384
+echo Command 385
+echo Command 386
+echo Command 387
+echo Command 388
+echo Command 389
+echo Command 390
+echo Command 391
+echo Command 392
+echo Command 393
+echo Command 394
+echo Command 395
+echo Command 396
+echo Command 397
+echo Command 398
+echo Command 399
+echo Command 400
+echo Command 401
+echo Command 402
+echo Command 403
+echo Command 404
+echo Command 405
+echo Command 406
+echo Command 407
+echo Command 408
+echo Command 409
+echo Command 410
+echo Command 411
+echo Command 412
+echo Command 413
+echo Command 414
+echo Command 415
+echo Command 416
+echo Command 417
+echo Command 418
+echo Command 419
+echo Command 420
+echo -----
+history
+echo -----
+!420 # Works!
+!325 # Works!
+!325 # Shouldn't work the 2nd time -- gets popped off history.
+!401 # Works!
+!23 # Does not exist anymore
+!999 # Hasn't happened yet
diff --git a/tosh_test4.sh b/tosh_test4.sh
new file mode 100644
index 0000000..22e012c
--- /dev/null
+++ b/tosh_test4.sh
@@ -0,0 +1,432 @@
+echo Command 000
+echo Command 001
+cat /README.md
+echo Command 003
+echo Command 004
+echo Command 005
+echo Command 006
+echo Command 007
+echo Command 008
+echo Command 009
+echo Command 010
+echo Command 011
+echo Command 012
+echo Command 013
+echo Command 014
+echo Command 015
+echo Command 016
+echo Command 017
+echo Command 018
+echo Command 019
+echo Command 020
+echo Command 021
+echo Command 022
+echo Command 023
+echo Command 024
+echo Command 025
+echo Command 026
+echo Command 027
+echo Command 028
+echo Command 029
+echo Command 030
+echo Command 031
+echo Command 032
+echo Command 033
+echo Command 034
+echo Command 035
+echo Command 036
+echo Command 037
+echo Command 038
+echo Command 039
+echo Command 040
+echo Command 041
+echo Command 042
+echo Command 043
+echo Command 044
+echo Command 045
+echo Command 046
+echo Command 047
+echo Command 048
+echo Command 049
+echo Command 050
+echo Command 051
+echo Command 052
+echo Command 053
+echo Command 054
+echo Command 055
+echo Command 056
+echo Command 057
+echo Command 058
+echo Command 059
+echo Command 060
+echo Command 061
+echo Command 062
+echo Command 063
+echo Command 064
+echo Command 065
+echo Command 066
+echo Command 067
+echo Command 068
+echo Command 069
+echo Command 070
+echo Command 071
+echo Command 072
+echo Command 073
+echo Command 074
+echo Command 075
+echo Command 076
+echo Command 077
+echo Command 078
+echo Command 079
+echo Command 080
+echo Command 081
+echo Command 082
+echo Command 083
+echo Command 084
+echo Command 085
+echo Command 086
+echo Command 087
+echo Command 088
+echo Command 089
+echo Command 090
+echo Command 091
+echo Command 092
+echo Command 093
+echo Command 094
+echo Command 095
+echo Command 096
+echo Command 097
+echo Command 098
+echo Command 099
+echo Command 100
+echo Command 101
+echo Command 102
+echo Command 103
+echo Command 104
+echo Command 105
+echo Command 106
+echo Command 107
+echo Command 108
+echo Command 109
+echo Command 110
+echo Command 111
+echo Command 112
+echo Command 113
+echo Command 114
+echo Command 115
+echo Command 116
+echo Command 117
+echo Command 118
+echo Command 119
+echo Command 120
+echo Command 121
+echo Command 122
+echo Command 123
+echo Command 124
+echo Command 125
+echo Command 126
+echo Command 127
+echo Command 128
+echo Command 129
+echo Command 130
+# Potato
+echo Command 131
+echo Command 132
+echo Command 133
+echo Command 134
+echo Command 135
+echo Command 136
+echo Command 137
+echo Command 138
+echo Command 139
+echo Command 140
+echo Command 141
+echo Command 142
+echo Command 143
+echo Command 144
+echo Command 145
+echo Command 146
+echo Command 147
+echo Command 148
+echo Command 149
+echo Command 150
+echo Command 151
+echo Command 152
+echo Command 153
+echo Command 154
+echo Command 155
+echo Command 156
+echo Command 157
+echo Command 158
+echo Command 159
+echo Command 160
+echo Command 161
+echo Command 162
+echo Command 163
+echo Command 164
+echo Command 165
+echo Command 166
+echo Command 167
+echo Command 168
+echo Command 169
+echo Command 170
+echo Command 171
+echo Command 172
+echo Command 173
+echo Command 174
+echo Command 175
+echo Command 176
+echo Command 177
+echo Command 178
+echo Command 179
+echo Command 180
+echo Command 181
+echo Command 182
+echo Command 183
+echo Command 184
+echo Command 185
+echo Command 186
+echo Command 187
+echo Command 188
+echo Command 189
+echo Command 190
+echo Command 191
+echo Command 192
+echo Command 193
+echo Command 194
+echo Command 195
+echo Command 196
+echo Command 197
+echo Command 198
+echo Command 199
+echo Command 200
+echo Command 201
+echo Command 202
+echo Command 203
+echo Command 204
+echo Command 205
+echo Command 206
+echo Command 207
+echo Command 208
+echo Command 209
+echo Command 210
+echo Command 211
+echo Command 212
+echo Command 213
+echo Command 214
+echo Command 215
+echo Command 216
+echo Command 217
+echo Command 218
+echo Command 219
+echo Command 220
+echo Command 221
+echo Command 222
+echo Command 223
+echo Command 224
+echo Command 225
+echo Command 226
+echo Command 227
+echo Command 228
+echo Command 229
+echo Command 230
+echo Command 231
+echo Command 232
+echo Command 233
+echo Command 234
+echo Command 235
+echo Command 236
+echo Command 237
+echo Command 238
+echo Command 239
+echo Command 240
+echo Command 241
+echo Command 242
+echo Command 243
+echo Command 244
+echo Command 245
+echo Command 246
+echo Command 247
+echo Command 248
+echo Command 249
+echo Command 250
+echo Command 251
+echo Command 252
+echo Command 253
+echo Command 254
+echo Command 255
+echo Command 256
+echo Command 257
+echo Command 258
+echo Command 259
+echo Command 260
+echo Command 261
+echo Command 262
+echo Command 263
+echo Command 264
+echo Command 265
+echo Command 266
+echo Command 267
+echo Command 268
+echo Command 269
+echo Command 270
+echo Command 271
+echo Command 272
+echo Command 273
+echo Command 274
+echo Command 275
+echo Command 276
+echo Command 277
+echo Command 278
+echo Command 279
+echo Command 280
+echo Command 281
+echo Command 282
+echo Command 283
+echo Command 284
+echo Command 285
+echo Command 286
+echo Command 287
+echo Command 288
+echo Command 289
+echo Command 290
+echo Command 291
+echo Command 292
+echo Command 293
+echo Command 294
+echo Command 295
+echo Command 296
+echo Command 297
+echo Command 298
+echo Command 299
+echo Command 300
+echo Command 301
+echo Command 302
+echo Command 303
+echo Command 304
+echo Command 305
+echo Command 306
+echo Command 307
+echo Command 308
+echo Command 309
+echo Command 310
+echo Command 311
+echo Command 312
+echo Command 313
+echo Command 314
+echo Command 315
+echo Command 316
+echo Command 317
+echo Command 318
+echo Command 319
+echo Command 320
+echo Command 321
+echo Command 322
+echo Command 323
+echo Command 324
+echo Command 325
+echo Command 326
+echo Command 327
+echo Command 328
+echo Command 329
+echo Command 330
+echo Command 331
+echo Command 332
+echo Command 333
+echo Command 334
+echo Command 335
+echo Command 336
+echo Command 337
+echo Command 338
+echo Command 339
+echo Command 340
+echo Command 341
+echo Command 342
+echo Command 343
+echo Command 344
+echo Command 345
+echo Command 346
+echo Command 347
+echo Command 348
+echo Command 349
+echo Command 350
+echo Command 351
+echo Command 352
+echo Command 353
+echo Command 354
+echo Command 355
+echo Command 356
+echo Command 357
+echo Command 358
+echo Command 359
+echo Command 360
+echo Command 361
+echo Command 362
+echo Command 363
+echo Command 364
+echo Command 365
+echo Command 366
+echo Command 367
+echo Command 368
+echo Command 369
+echo Command 370
+echo Command 371
+ls /
+echo Command 373
+echo Command 374
+echo Command 375
+echo Command 376
+echo Command 377
+echo Command 378
+echo Command 379
+echo Command 380
+echo Command 381
+echo Command 383
+echo Command 384
+mkdir /a
+ls /a
+echo Command 385
+echo Command 386
+echo Command 387
+echo Command 388
+mysterycommand
+echo Command 389
+echo Command 390
+echo Command 391
+echo Command 392
+grep Potato$ /README.md
+grep Potato$ /4.sh
+echo Command 394
+echo Command 395
+echo Command 396
+echo Command 397
+echo Command 398
+echo Command 399
+echo Command 400
+echo Command 401
+echo Command 402
+echo Command 403
+echo Command 404
+echo Command 405
+wc /README.md
+echo Command 407
+echo Command 408
+echo Command 409
+echo Command 410
+echo Command 411
+echo Command 412
+echo Command 413
+echo Command 414
+echo Command 415
+echo Command 416
+echo Command 417
+echo Command 418
+echo Command 419
+echo -----
+!ls # ls an empty dir
+!m # run mysterycommand and fail since it doesn't exist
+!grepfzw # no command had this prefix
+!grep # find 'Potato' in this script (*not* in README.md)
+!w # word count README.md
+!gr # find 'Potato' again
+!e # last echo command (-----)
diff --git a/user/fnr.c b/user/fnr.c
new file mode 100644
index 0000000..cba690b
--- /dev/null
+++ b/user/fnr.c
@@ -0,0 +1,30 @@
+#include "kernel/types.h"
+#include "user/user.h"
+
+int main(int argc, char *argv[]) {
+ char *line;
+ uint sz;
+ while (getline(&line, &sz, 0) > 0) {
+ for (int i = 1; i < argc; i += 2) {
+ char *find = argv[i];
+ char *repl = argv[i + 1];
+ uint find_len = strlen(find);
+ if (strlen(line) < find_len) {
+ continue;
+ }
+ for (int j = 0; j < strlen(line) - find_len; ++j) {
+ char *p = line + j;
+ char *q = find;
+ int k;
+ for (k = 0; *p && *p == *q && k < find_len; ++k) {
+ p++, q++;
+ }
+ if (k == find_len) {
+ memcpy(line + j, repl, find_len);
+ }
+ }
+ }
+ printf("%s", line);
+ }
+ return 0;
+}
diff --git a/user/sh.c b/user/sh.c
index 836ebcb..f2ca477 100644
--- a/user/sh.c
+++ b/user/sh.c
@@ -132,18 +132,19 @@ runcmd(struct cmd *cmd)
}
int
-getcmd(char *buf, int nbuf)
+getcmd(char *buf, int nbuf, int fd)
{
- write(2, "$ ", 2);
+ if (fd == 0)
+ write(2, "$ ", 2); // only print prompt if interactive (no script)
memset(buf, 0, nbuf);
- gets(buf, nbuf);
+ fgets(fd, buf, nbuf);
if(buf[0] == 0) // EOF
return -1;
return 0;
}
int
-main(void)
+main(int argc, char *argv[])
{
static char buf[100];
int fd;
@@ -156,8 +157,16 @@ main(void)
}
}
+ fd = 0;
+ if (argc > 1) {
+ fd = open(argv[1], O_RDONLY);
+ }
+
// Read and run input commands.
- while(getcmd(buf, sizeof(buf)) >= 0){
+ while(getcmd(buf, sizeof(buf), fd) >= 0){
+ if (buf[0] == '#') {
+ continue;
+ }
if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' '){
// Chdir must be called by the parent, not the child.
buf[strlen(buf)-1] = 0; // chop \n
diff --git a/user/test.c b/user/test.c
new file mode 100644
index 0000000..0c2ceb3
--- /dev/null
+++ b/user/test.c
@@ -0,0 +1,433 @@
+/**
+ * File: test.c
+ * Author: Nino Estrada
+ * Date: September 23, 2024
+ *
+ * Description:
+ * The "test" command evaluates a conditional expression. It is an if statement
+ * that will return zero (true) or 1 (false), or greater than 1 (error).
+ * The "test" command will check if any flags were inputted and, based on the expression,
+ * will return a truthy or falsy value.
+ *
+ * Usage:
+ * Compile: make qemu
+ * Run: test [conditional expression]
+ *
+ * Examples:
+ * // Example 1: File Analysis
+ * test -f cat
+ * // Expected Output: 0
+ *
+ * // Example 2: String Operators
+ * test "hello" = "hell"
+ * // Expected Output: 1
+ *
+ * // Example 3: Number Operators
+ * test 23 -gt 32
+ * // Expected Output: 1
+ */
+
+
+#include "kernel/types.h"
+#include "kernel/stat.h"
+#include "user/user.h"
+#include "kernel/fcntl.h"
+#include
+#include
+
+
+const char *usageString = "Command Description\n"
+"-d file True if file exists and is a directory.\n"
+"-e file True if file exists (regardless of type).\n"
+"-f file True if file exists and is a regular file.\n"
+"file1 -ef file2 True if file1 and file2 exist and refer to the same file.\n"
+"s1 = s2 True if the strings s1 and s2 are identical.\n"
+"s1 != s2 True if the strings s1 and s2 are not identical.\n"
+"n1 -gt n2 True if the integer n1 is algebraically greater than the integer n2.\n"
+"n1 -lt n2 True if the integer n1 is algebraically less than the integer n2.\n";
+
+
+/**
+ * Checks if the string can be converted to a number
+ *
+ * @param str The string to check if it can be converted to a number
+ * @returns a boolean value representing whether a string is an integer or not
+ */
+bool isInt(char *str)
+{
+
+ for (int i = 0; i < strlen(str); i += 1)
+ {
+ if (str[i] < '0' || str[i] > '9')
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Opens a file and retrieves the metadata for the file
+ *
+ * @param file The file to be open
+ * @param fileInfo The struct to be filled in with the metadata or null if not required
+ * @return The file descriptor
+ */
+int openFile(char *file, struct stat *fileInfo)
+{
+ int fd = open(file, O_RDONLY);
+
+ // Non Existing File
+ if (fd < 0)
+ {
+ printf("Couldn't open file: %s\n", file);
+ }
+ // File Exist
+ else if (fileInfo != NULL)
+ {
+ // Populate the File Info in the struct
+ fstat(fd, fileInfo);
+ }
+
+ close(fd);
+ return fd;
+}
+
+
+/**
+ * Implements the file flags conditions
+ *
+ * @param flag The file flag
+ * @param file The file argument for the unary operator
+ * @return int 0 for true, 1 for false and 2 for error
+ */
+int fileFlags(char *flag, char *file)
+{
+ char *flags[] = {"-d", "-e", "-f"};
+
+ int i;
+
+ // Check if Passed in Flag is in the Flags Array
+ for (i = 0; i < 3; i += 1)
+ {
+ if (strcmp(flag, flags[i]) == 0)
+ {
+ break;
+ }
+ }
+
+ // Error Handling: Checking if User put in a Valid File Flag
+ if (i == 3)
+ {
+ printf("Unknown file flag: %s \n\n%s \n", flag, usageString);
+ return 2;
+ }
+
+ // Open File and Store its Metadata in a struct
+ struct stat fileInfo;
+ int fd = openFile(file, &fileInfo);
+
+ // File Doesn't Exist
+ if (fd < 0)
+ {
+ return 1;
+ }
+
+ // is directory
+ if (strcmp(flag, "-d") == 0)
+ {
+ if (fileInfo.type == 1)
+ {
+ printf("Type: %d\n", fileInfo.type);
+ return 0;
+ }
+ }
+ // file exists
+ else if (strcmp(flag, "-e") == 0)
+ {
+ return 0;
+ }
+ // file exist and is a regular file type
+ else if (strcmp(flag, "-f") == 0)
+ {
+ if (fileInfo.type == 2)
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ printf("Unknown file flag: %s \n\n%s \n", flag, usageString);
+ return 2;
+ }
+
+ return 1;
+}
+
+
+/**
+ * Implements the string expressions
+ *
+ * @param string1 The first string to compare
+ * @param string2 The second string to compare
+ * @param operator The operator which can be either "=" or "!="
+ * @return int 0 for true, 1 for false and 2 for error
+ */
+int stringOperators(char *string1, char *string2, char *operator)
+{
+ if (strcmp(operator, "=") == 0)
+ {
+ if (strcmp(string1, string2) == 0)
+ {
+ return 0;
+ }
+ }
+ else if (strcmp(operator, "!=") == 0)
+ {
+ if (strcmp(string1, string2) != 0)
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ printf("Unknown string comparison: %s \n\n%s \n", operator, usageString);
+ return 2;
+ }
+
+ return 1;
+}
+
+
+/**
+ * Implements the number expressions
+ *
+ * @param num1 The string representing the first number to compare
+ * @param num2 The string representing the second number to compare
+ * @param flag The comparison flag which can be either "-lt" (<) or "-gt" (>)
+ * @return int 0 for true, 1 for false and 2 for error
+ */
+int numberOperators(char *num1, char *num2, char *flag)
+{
+ // Error Handling: Checking if the First String can be Converted to a Number
+ if (!isInt(num1))
+ {
+ printf("Not a number: %s \n\n%s \n", num1, usageString);
+ return 2;
+ }
+
+ // Error Handling: Checking if the Second String can be Converted to a Number
+ if (!isInt(num2))
+ {
+ printf("Not a number: %s \n\n%s \n", num2, usageString);
+ return 2;
+ }
+
+ // Convert the Strings to Numbers
+ int firstNum = atoi(num1);
+ int secondNum = atoi(num2);
+
+ if (strcmp(flag, "-lt") == 0)
+ {
+ return firstNum < secondNum ? 0 : 1;
+ }
+ else if (strcmp(flag, "-gt") == 0)
+ {
+ return firstNum > secondNum ? 0 : 1;
+ }
+ else
+ {
+ printf("Unknown number flag: %s \n\n%s \n", flag, usageString);
+ return 2;
+ }
+}
+
+/**
+ * Parses the arguments because FogOS doesn't respect quotes when creating the argv array
+ *
+ * For example, when pass in "hi hello", it is treated as two separate arguments.
+ * So, we have to build the string one word at a time.
+ *
+ * @param argc The number of command line arguments
+ * @param argv The array of command line argumetns
+ * @return int The number of parse arguments (the new argc) or -1 for error
+ */
+int parseArgs(int argc, char **argv, char** newArgs)
+{
+ char buf[128]; // String Builder
+ char *ptrBuf; // Should Always Point to the Current End of the String being Built ('buf')
+ int newIndex = 0; // Keeping Track of where we are in the Arguments
+
+ // Loop through the Arguments
+ for (int i = 0; i < argc; i += 1)
+ {
+ // Argument is a String
+ if (argv[i][0] == '"')
+ {
+ ptrBuf = buf;
+
+ int len = strlen(argv[i]);
+
+ // Keep Adding Words to the String Builder
+ while (argv[i][len - 1] != '"' && i < argc)
+ {
+ // Copy Current Word into the Buffer
+ strcpy(ptrBuf, argv[i]);
+
+ // Point 'ptrBuf' at the End of the Buffer
+ ptrBuf += len;
+ *ptrBuf = ' '; // Add Space
+
+ // Since we Added a Space, Need to Increment Pointer to be At the End of Buf
+ ptrBuf += 1;
+
+ // Reset "len" for the Next Word
+ i += 1;
+ len = strlen(argv[i]);
+ }
+
+ // Error Handling: Checking if User is Missing the Closing Quote
+ if (i == argc)
+ {
+ printf("Invalid syntax: missing closing quote\n\n%s \n", usageString);
+ return -1;
+ }
+
+ // Copy Final Word into Buf
+ strcpy(ptrBuf, argv[i]);
+
+ // Setting up NewArgs to be Able to Store Buf with the Null Terminator Character
+ newArgs[newIndex] = (char *)malloc(strlen(buf) + 1);
+
+ // Copying Buf into the New Arguments
+ strcpy(newArgs[newIndex], buf);
+ newIndex += 1;
+ }
+ // Any Arguments without Quotes
+ else
+ {
+ newArgs[newIndex] = argv[i];
+ newIndex += 1;
+ }
+ }
+
+ return newIndex;
+}
+
+
+/**
+ * The entry point of the program
+ *
+ * @param argc The number of command line arguments
+ * @param argv The array of command line arguments
+ * @return 0 on success or 1 on error
+ */
+int main(int argc, char **argv)
+{
+ // Corrected Arguments
+ char **newArgs = (char **)malloc(argc *sizeof(char *));
+ int newArgc = 0;
+
+ // Return Value
+ int result = 1;
+
+ // Error Handling: Checking if Any Arguments were Provided
+ if (argc < 2)
+ {
+ printf("No arguments provided. \n\n%s \n", usageString);
+ result = 2;
+ }
+ // Help Flag
+ else if (strcmp(argv[1], "-h") == 0)
+ {
+ printf("Usage: \n\n%s \n", usageString);
+ exit(0);
+ }
+ // Error Handling: Checking if Enough Arguments were Provided
+ else if (argc < 3)
+ {
+ printf("Not enough arguments provided. \n\n%s \n", usageString);
+ result = 2;
+ }
+ // Error Handling: Missing Closing Quote
+ else if ((newArgc = parseArgs(argc, argv, newArgs)) < 0)
+ {
+ result = 2;
+ }
+ // File Flags
+ else if (newArgs[1][0] == '-')
+ {
+ if (newArgc < 3)
+ {
+ printf("File expression requires 2 arguments. \n\n%s \n", usageString);
+ result = 2;
+ }
+ else
+ {
+ result = fileFlags(newArgs[1], newArgs[2]);
+ }
+ }
+ // String Operators
+ else if (strcmp(newArgs[2], "=") == 0 || strcmp(newArgs[2], "!=") == 0)
+ {
+ if (newArgc < 4)
+ {
+ printf("String comparisons requires 3 arguments. \n\n%s \n", usageString);
+ result = 2;
+ }
+ else
+ {
+ result = stringOperators(newArgs[1], newArgs[3], newArgs[2]);
+ }
+ }
+ // File Comparisons
+ else if (strcmp(newArgs[2], "-ef") == 0)
+ {
+ if (newArgc < 4)
+ {
+ printf("File comparisons requires 3 arguments. \n\n%s \n", usageString);
+ result = 2;
+ }
+ else
+ {
+ // Open Files and Store their Metadata in their Respective structs
+ struct stat fileInfo;
+ struct stat fileInfo2;
+ int fd = openFile(newArgs[1], &fileInfo);
+ int fd2 = openFile(newArgs[3], &fileInfo2);
+
+ if (fd < 0 || fd2 < 0)
+ {
+ result = 2;
+ }
+ else if (fileInfo.ino == fileInfo2.ino)
+ {
+ result = 0;
+ }
+ }
+ }
+ // Number Operators
+ else if (strcmp(newArgs[2], "-gt") == 0 || strcmp(newArgs[2], "-lt") == 0)
+ {
+ if (newArgc < 4)
+ {
+ printf("Number comparisons requires 3 arguments. \n\n%s \n", usageString);
+ result = 2;
+ }
+ else
+ {
+ result = numberOperators(newArgs[1], newArgs[3], newArgs[2]);
+ }
+ }
+ else
+ {
+ printf("Unrecognized operation \n\n%s \n", usageString);
+ result = 2;
+ }
+
+ printf("Result: %d\n", result);
+
+ exit(0);
+}
diff --git a/user/tolower.c b/user/tolower.c
new file mode 100644
index 0000000..55eb0a5
--- /dev/null
+++ b/user/tolower.c
@@ -0,0 +1,12 @@
+#include "kernel/types.h"
+#include "user/user.h"
+
+int main() {
+ for (int c = 0; read(0, &c, 1) > 0; ) {
+ if (c >= 'A' && c <= 'Z') {
+ c = c - 'A' + 'a';
+ }
+ write(1, &c, 1);
+ }
+ return 0;
+}
diff --git a/user/tosh.c b/user/tosh.c
new file mode 100644
index 0000000..47b5cbf
--- /dev/null
+++ b/user/tosh.c
@@ -0,0 +1,2048 @@
+/**
+ * Tosh - The Operating Shell (v1.0)
+ *
+ *
+ * File: tosh.c
+ * Author: Nino Estrada
+ * Date: October 16, 2024
+ *
+ *
+ * Description:
+ * Tosh is a custom command-line shell with features such as command execution, built-in commands, I/O redirection,
+ * and background job management, providing an interactive user experience. Enhanced functionality with scripting mode
+ * (including shebang support), dynamic prompts displaying exit status, command history, and current working directory.
+ * Integrating custom path execution for seamless interaction with Unix-like environments. Tosh Implements advanced features
+ * such as executable script support and background job handling, optimizing user interaction with the operating system.
+ *
+ *
+ * Usage:
+ * Run: tosh [options] [script]
+ *
+ *
+ *
+ *
+ * TABLE OF CONTENTS
+ * -----------------
+ * 1. IMPORTS : Line 47
+ * 2. COLORS & TEXT STYLING : Line 58
+ * 3. DATA STRUCTURE : Line 82
+ * 4. GLOBAL VARIABLES : Line 109
+ * 5. FUNCTION DECLARATIONS : Line 177
+ * 5. HELPER FUNCTIONS : Line 268
+ * 6. MAIN FUNCTION HELPERS : Line 443
+ * 7. EXECUTION FUNCTIONS : Line 514
+ * 8. COMMAND FUNCTIONS : Line 875
+ * 9. EXECUTION HELPERS : Line 1248
+ * 10. OPTIONS : Line 1393
+ * 11. SPECIAL FEATURES : Line 1575
+ * 12. MAIN FUNCTION : Line 1933
+ * 13. REFERENCES : Line 2028
+ *
+ */
+
+
+// -------------------------------------------------- IMPORTS --------------------------------------------------
+
+
+#include "kernel/fcntl.h"
+#include "kernel/types.h"
+#include "user/user.h"
+#include
+#include
+
+
+// -------------------------------------------------- COLORS & TEXT STYLING --------------------------------------------------
+
+
+/**
+ * Colors
+ */
+#define CYAN "\033[38;2;174;242;241m"
+#define WHITE "\033[38;2;255;245;240m"
+#define RED "\033[38;5;217m"
+#define YELLOW "\033[38;5;229m"
+#define ORANGE "\033[38;2;255;179;102m"
+#define BLUE "\033[38;2;48;116;143m"
+#define BLUE_BACKGROUND "\033[48;2;48;116;143m"
+
+
+/**
+ * Text Styling
+ */
+ #define ITALIC "\033[3m"
+ #define BOLD "\033[1m"
+ #define UNDERLINE "\033[4m"
+ #define RESET "\033[0m"
+
+
+// -------------------------------------------------- DATA STRUCTURES --------------------------------------------------
+
+
+/**
+ * Structure to store information about a command within the history
+ */
+struct cmd_info
+{
+ int cmd_number; /* The number representing where the command is in relation to the history */
+ char *cmd; /* The command itself */
+ uint time; /* The time it took to execute the command */
+};
+
+
+/**
+ * Represents a command in a pipeline.
+ */
+struct command
+{
+ char **tokens; /* An array of strings that describe the command to run */
+ bool stdout_pipe; /* Set to true if this command's output should be written to pipe */
+ char *stdout_file; /*Set to a file name if this command's output should be written */
+ char *stdin_file; /* set to a file name if this command's output should be written */
+ bool append_file; /* set to true if this command's output should be appended to a file */
+};
+
+
+// -------------------------------------------------- GLOBAL VARIABLES --------------------------------------------------
+
+
+/**
+ * Global Variables
+ */
+char cwd[128]; /* Current Working Directory */
+char *arguments[128]; /* Tokens parse for the current command */
+struct cmd_info history[100]; /* User's command history */
+int history_end = 0; /* Kepp track of the starting point of the history array */
+int command_number = 1; /* Counter of commands enter so far in this session of 'tosh' */
+bool private_mode = false; /* Private mode option */
+char *error_msg = WHITE "-tosh: " RED UNDERLINE "error" RESET RED ":" WHITE; /* The Beginning of every error message */
+
+
+/**
+ * Usage Strings
+ */
+const char *options = WHITE BOLD " " BLUE_BACKGROUND "OPTIONS" RESET "\n\n"
+ WHITE UNDERLINE "Option" RESET " " WHITE UNDERLINE "Description\n" RESET
+ CYAN BOLD "-" RESET WHITE "v" RESET WHITE ", " CYAN BOLD "--" RESET WHITE "version Displays shell version\n" RESET
+ CYAN BOLD "--" RESET WHITE "about Displays an about section of the shelln\n" RESET
+ CYAN "--" RESET WHITE "license Shows the license\n" RESET
+ CYAN BOLD "-" RESET WHITE "lc" RESET WHITE ", " CYAN BOLD "--" RESET WHITE "list" CYAN BOLD "-" RESET WHITE "commands Shows a list of all the commands\n" RESET
+ CYAN BOLD "-" RESET WHITE "p" RESET WHITE ", " CYAN BOLD "--" RESET WHITE "private Private mode is turned on\n" RESET WHITE;
+
+
+const char *built_in_cmds = WHITE BOLD " " BLUE_BACKGROUND "BUILT-IN COMMANDS" RESET "\n\n" RESET
+ WHITE UNDERLINE "Command" RESET " " WHITE UNDERLINE "Description\n" RESET
+ CYAN "cat" RESET WHITE " filename Displays file content\n" RESET
+ CYAN "echo"RESET WHITE " [" ORANGE "string" RESET WHITE "] Displays text to the terminal\n" RESET
+ CYAN "ls" RESET WHITE " [" ORANGE "/dir" RESET WHITE "] List directory contents\n" RESET
+ CYAN "cd" RESET WHITE " /dir Change the current directory\n" RESET
+ CYAN "mkdir" RESET WHITE " dir_name Make directories\n" RESET
+ CYAN "pwd" RESET WHITE " Prints the working directory\n" RESET
+ CYAN "wc" RESET WHITE " filename Shows the number of lines, words, and bytes in a file\n" RESET
+ CYAN "history" RESET WHITE " [" ORANGE "-t" RESET WHITE "] Display the history list of the last 100 commands with their command numbers\n" RESET
+ CYAN "test" RESET WHITE " Evaluates a conditonal expression\n" RESET
+ CYAN "uname" RESET WHITE " [" ORANGE "-a" RESET WHITE "] Displays system information\n" RESET
+ CYAN "joke" RESET WHITE " Tells a programming-related joke\n" RESET
+ CYAN "fact" RESET WHITE " Shares a fun programming-related fact\n" RESET
+ CYAN "quote" RESET WHITE " Provides a programming-related quote\n" RESET
+ CYAN "menu" WHITE " Display the main menu\n" RESET
+ CYAN "ref" RESET WHITE " Provides a list of commands with their descriptions\n" RESET
+ CYAN "reset" RESET WHITE " Resets the shell\n" RESET
+ CYAN "exit" RESET WHITE " Exit the shell\n";
+
+
+const char *history_execution = WHITE BOLD " " BLUE_BACKGROUND "HISTORY EXECUTION" RESET "\n\n" RESET
+ WHITE UNDERLINE "Command" RESET " " WHITE UNDERLINE "Description\n" RESET
+ CYAN "!" WHITE "num A history execution that will re-run command number num\n" RESET
+ CYAN "!!" WHITE " A history execution that re-runs the last command that was entered\n" RESET
+ CYAN "!" WHITE "prefix A history execution that re-runs the last command with that prefix\n" RESET;
+
+
+const char *io_redirection = WHITE BOLD " " BLUE_BACKGROUND "I/O REDIRECTION" RESET "\n\n" RESET
+ WHITE UNDERLINE "Command" RESET " " WHITE UNDERLINE "Description\n" RESET
+ WHITE "command1 " CYAN "|" WHITE " command2 Allows the output of one command serves as the input for another command\n" RESET
+ WHITE "command " CYAN ">" WHITE " filename Redirects the output of a command to a file\n" RESET
+ WHITE "filename " CYAN "<" WHITE " command Redirects the content of a file to be input for a command\n" RESET
+ WHITE "command " CYAN ">>" WHITE " filename Append the output of a command to the end of a file\n" RESET WHITE;
+
+
+const char *job_control = WHITE BOLD " " BLUE_BACKGROUND "JOB CONTROL" RESET "\n\n" RESET
+ WHITE UNDERLINE "Command" RESET " " WHITE UNDERLINE "Description\n" RESET
+ WHITE "command " CYAN "&" WHITE " Runs a command in the background\n" RESET WHITE;
+
+
+// -------------------------------------------------- FUNCTION DECLARATIONS --------------------------------------------------
+
+
+/**
+ * String Helper Functions
+ */
+uint strspn(const char *str, const char *chars);
+uint strcspn(const char *str, const char *chars);
+int strncmp2(const char *p, const char *q, uint n);
+bool isInt2(char *str);
+void lowercase(char *str);
+
+
+/**
+ * Parsing Arguments Helper Functions
+ */
+char *next_token(char **str_ptr, const char *delim);
+int tokenize(char *str, char **tokens);
+
+
+/**
+ * Main() Helper Functions
+ */
+void initialize_history();
+void reset_history();
+void shell_initial_state();
+bool too_many_arguments_error(int num, char *cmd, char *usage);
+
+
+/**
+ * Execution Functions
+ */
+ int execvp(char *pathname, char **args);
+ void execute_pipeline(struct command *cmd);
+ int execute(char *cmd);
+
+
+/**
+ * Command Functions
+ */
+ bool is_built_in_cmd(char *cmd);
+ int exit_cmd();
+ int history_cmd();
+ int pwd_cmd(struct cmd_info *cur_cmd);
+ int uname_cmd(struct cmd_info *cur_cmd);
+ int cd_cmd(struct cmd_info *cur_cmd);
+ struct cmd_info *repeat_last_cmd();
+ struct cmd_info *repeat_cmd_number();
+ struct cmd_info *repeat_cmd_prefix();
+ struct cmd_info *redo_cmd();
+ int reset_cmd();
+
+
+/**
+ * Execution Helper Functions
+ */
+void reset_arguments();
+void truncate_str_at_comment(char *cmd);
+int handle_comments_and_empty_string(char *cmd);
+void populate_history_struct(struct cmd_info *cur_cmd, char *cmd_copy);
+void parse_and_configure_pipeline(struct command *cmds, int argc);
+
+
+/**
+ * Options
+ */
+int tosh_options(char *option);
+void version_opt();
+void about_opt();
+void license_opt();
+void list_cmds_opt();
+void private_opt();
+void standard_opt();
+
+
+/**
+ * Special Features
+ */
+void splash_screen();
+int main_menu_cmd(struct cmd_info *cur_cmd);
+int reference_cmd(struct cmd_info *cur_cmd);
+int joke_cmd(struct cmd_info *cur_cmd);
+int fact_cmd(struct cmd_info *cur_cmd);
+int quote_cmd(struct cmd_info *cur_cmd);
+void off_message();
+
+
+// -------------------------------------------------- HELPER FUNCTIONS --------------------------------------------------
+
+
+/**
+ * Counts how many characters from the start of str are in chars
+ *
+ * @param str The string we want to validate
+ * @param chars The string we want to match
+ * @return the number representing how many prefix characters in string are in chars
+ */
+uint strspn(const char *str, const char *chars)
+{
+ uint i, j;
+ for (i = 0; str[i] != '\0'; i++) {
+ for (j = 0; chars[j] != str[i]; j++) {
+ if (chars[j] == '\0')
+ return i;
+ }
+ }
+ return i;
+}
+
+
+/**
+ * Counts how many characters in str are NOT in chars
+ *
+ * @param str The string we want to validate
+ * @param chars The string we want to match
+ * @return the number representing how many characters in string are NOT in chars
+ */
+uint strcspn(const char *str, const char *chars)
+{
+ const char *p, *sp;
+ char c, sc;
+ for (p = str;;) {
+ c = *p++;
+ sp = chars;
+ do {
+ if ((sc = *sp++) == c) {
+ return (p - 1 - str);
+ }
+ } while (sc != 0);
+ }
+}
+
+
+/**
+ * Compares two strings based on a specified portion of the strings
+ *
+ * @param p The first string to compare
+ * @param q The second string to compare
+ * @return 0 (equal), > 0 (p > q), < 0 (p < q)
+ */
+int strncmp2(const char *p, const char *q, uint n)
+{
+ while(n > 0 && *p && *p == *q)
+ n--, p++, q++;
+ if(n == 0)
+ return 0;
+ return (uchar)*p - (uchar)*q;
+}
+
+
+/**
+ * Reads the terminal input up until the next instance of delim and whatever it gets it is considered one token
+ *
+ * @param str_ptr Pointing to the next token to be parsed
+ * @param delim An array of possible separators between tokens
+ * @return a pointer to the end of the token that was just parsed, so we can start there next time
+ */
+char *next_token(char **str_ptr, const char *delim)
+{
+ if (*str_ptr == NULL) {
+ return NULL;
+ }
+
+ uint tok_start = strspn(*str_ptr, delim);
+ uint tok_end = strcspn(*str_ptr + tok_start, delim);
+
+ /* Zero length token. We must be finished. */
+ if (tok_end == 0) {
+ *str_ptr = NULL;
+ return NULL;
+ }
+
+
+ /* Take note of the start of the current token. We'll return it later. */
+ char *current_ptr = *str_ptr + tok_start;
+
+ /* Shift pointer forward (to the end of the current token) */
+ *str_ptr += tok_start + tok_end;
+
+ if (**str_ptr == '\0') {
+ /* If the end of the current token is also the end of the string, we
+ * must be at the last token. */
+ *str_ptr = NULL;
+ } else {
+ /* Replace the matching delimiter with a NUL character to terminate the
+ * token string. */
+ **str_ptr = '\0';
+
+ /* Shift forward one character over the newly-placed NUL so that
+ * next_pointer now points at the first character of the next token. */
+ (*str_ptr)++;
+ }
+
+ return current_ptr;
+}
+
+
+/**
+ * Parses all tokens in the given string
+ *
+ * @param str The string from which to parse tokens
+ * @param tokens An array where tokens will be returned
+ * @return the number of tokens parsed
+ */
+int tokenize(char *str, char **tokens)
+{
+ char *next_tok = str;
+ char *curr_tok;
+ int n_tokens = 0;
+
+ // Tokenize
+ while ((curr_tok = next_token(&next_tok, " \n")) != NULL)
+ {
+ if (strcmp(curr_tok, "#") == 0)
+ {
+ break;
+ }
+
+ tokens[n_tokens++] = curr_tok;
+ }
+
+ return n_tokens;
+}
+
+
+/**
+ * Checks if the string can be converted to a number
+ *
+ * @param str The string to check if it can be converted to a number
+ * @returns a boolean value representing whether a string is an integer or not
+ * - `true` if the string is an integer
+ * - `-false` if a string is not an integer
+ */
+bool isInt2(char *str)
+{
+
+ for (int i = 0; i < strlen(str); i += 1)
+ {
+ if (str[i] < '0' || str[i] > '9')
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Lowercases the string based on its ASCII value
+ *
+ * e.g. To convert 'A' (65) to 'a' (97) we add 32.
+ */
+void lowercase(char *str)
+{
+ for (int i = 0; i < strlen(str); i += 1)
+ {
+ if (str[i] >= 'A' && str[i] <= 'Z')
+ {
+ // Convert to Lowercase
+ str[i] = str[i] + 32;
+ }
+ }
+}
+
+
+// -------------------------------------------------- MAIN FUNCTION HELPERS --------------------------------------------------
+
+
+/**
+ * Initializes all of the history's command numbers to be -1 to indicate they have not been used yet
+ */
+void initialize_history()
+{
+ for (int i = 0; i < 100; i += 1)
+ {
+ history[i].cmd_number = -1;
+ }
+}
+
+
+/**
+ * Sets the history to its initial state
+ */
+void reset_history()
+{
+ initialize_history();
+ history_end = 0; /* Kepp track of the starting point of the history array */
+ command_number = 1; /* Counter of commands enter so far in this session of 'tosh' */
+}
+
+
+/**
+ * Restoring the shell to its initial state
+ */
+void shell_initial_state()
+{
+ private_mode = false;
+ reset_history();
+ reset_arguments();
+ chdir("/");
+ getcwd(cwd, 128);
+}
+
+
+/**
+ * Evaluates if a command or option has been given more arguments than expected
+ *
+ * @param num The limit of arguments for the command or option
+ * @param cmd The command or option
+ * @param usage The string representing the correct usage of the command or option
+ * @returns a boolean value representing whether the number of arguments exceeds the command's or option's limit
+ * - `true` if there are too many arguments
+ * - `-false` if arguments are within the command's or option's constraints
+ */
+bool too_many_arguments_error(int num, char *cmd, char *usage)
+{
+ // Error Handling
+ if (arguments[num] != NULL)
+ {
+ fprintf(2, "%s %s: too many arguments\n", error_msg, cmd);
+ printf("\n");
+ printf(WHITE "Usage: \n");
+ printf("%s\n", usage);
+ return true;
+ }
+
+ return false;
+}
+
+
+// -------------------------------------------------- EXECUTION FUNCTIONS --------------------------------------------------
+
+
+/**
+ * Determines where the binary files are located and then runs exec
+ *
+ * @param pathname The executable or binary file to run
+ * @param args The list of arguments to that command
+ * @return an exit status
+ * - `0` on success
+ * - `-1` on error
+ */
+int execvp(char *pathname, char **args)
+{
+ char *fullpath;
+
+ // Option 1: Checking if it is an Absolute or Relative Path
+ if (pathname[0] == '/' || (pathname[0] == '.' && pathname[1] == '/') || (pathname[0] == '.' && pathname[1] == '.' && pathname[2] == '/'))
+ {
+ fullpath = pathname;
+ }
+ else
+ {
+ // Option 2: Search the Root Directory
+ fullpath = (char*)malloc(strlen(pathname) + 2); // extra space for slash and null terminator character
+ fullpath[0] = '/';
+ strcpy(&fullpath[1], pathname);
+
+ int fd = open(fullpath, O_RDONLY);
+
+ // Non Existing File in the Root Directory
+ if (fd < 0)
+ {
+ // Option 3: Search in the Current Directory
+ strcpy(fullpath, pathname);
+ fd = open(fullpath, O_RDONLY);
+
+ // Non Existing File in the Current Directory
+ if (fd < 0)
+ {
+ fprintf(2, "%s Command not found: " ITALIC "%s\n" RESET WHITE, error_msg, pathname);
+ printf("\n");
+ printf("Usage:\n");
+ printf("%s\n", built_in_cmds);
+ return -1;
+ }
+ }
+
+ close(fd);
+ }
+
+ return exec(fullpath, args);
+}
+
+
+/**
+ * Execute a list of commands where the output of the previous becomes the input of the next
+ *
+ * @param cmd An array of structs that represents a command in a pipeline
+ */
+void
+execute_pipeline(struct command *cmd)
+{
+ int i = 0;
+
+ // Creates a pipe.
+ int fd[2];
+
+ // Keep Looping through Commands until Don't get a stdout (Implying on the Last Command)
+ while (cmd[i].stdout_pipe)
+ {
+ // Error Handling
+ if (pipe(fd) == -1)
+ {
+ fprintf(2, "%s Could not create pipe\n" RESET, error_msg);
+ return;
+ }
+
+ int pid = fork();
+
+ // Error Handling
+ if (pid == -1)
+ {
+ fprintf(2, "%s Fork failed \n" RESET, error_msg);
+ return;
+ }
+ else if (pid == 0)
+ {
+ // Child
+ close(fd[0]); // Close Read End of the Pipe
+ close(1); // STDOUT
+ dup(fd[1]); // STDOUT now Comes from the Pipe
+
+ // Execute Current Command
+ execvp(cmd[i].tokens[0], cmd[i].tokens);
+
+ // CLose the Pipe
+ close(fd[1]);
+ }
+ else
+ {
+ // Parent
+
+ close(fd[1]); // Close the Write end of the Pipe (Next Command)
+
+ // Next Command will Read from the Pipe
+ close(0); // STDIN
+ dup(fd[0]); // STDIN now goes to the Pipe
+ }
+
+ i += 1;
+ }
+
+ int out_fd = -1, in_fd = -1;
+
+ // Checking if there is an Output File to Redirect
+ if (cmd[i].stdin_file != NULL)
+ {
+ int open_flags = O_RDONLY;
+
+ in_fd = open(cmd[i].stdin_file, open_flags);
+
+ if (in_fd == -1)
+ {
+ fprintf(2, "%s Could not open file: " ITALIC "%s\n" RESET, error_msg, cmd[i].stdin_file);
+ return;
+ }
+
+ close(0); // close stdin
+
+ if (dup(in_fd) == -1)
+ {
+ fprintf(2, "%s dup failed\n" RESET, error_msg);
+ return;
+ }
+ }
+
+ // Checking if there is an Output File to Redirect
+ if (cmd[i].stdout_file != NULL)
+ {
+ int open_flags = O_RDWR | O_CREATE;
+
+ if (cmd[i].append_file)
+ {
+ open_flags = open_flags | O_APPEND;
+ }
+ else
+ {
+ open_flags = open_flags | O_TRUNC;
+ }
+
+ out_fd = open(cmd[i].stdout_file, open_flags);
+
+ if (out_fd == -1)
+ {
+ fprintf(2, "%s Could not open file\n" RESET, error_msg);
+ return;
+ }
+
+ close(1); // close stdout
+
+ if (dup(out_fd) == -1)
+ {
+ fprintf(2, "%s dup failed\n" RESET, error_msg);
+ return;
+ }
+ }
+
+ // 1 More Command Left: Doesn't Write to a Pipe
+ execvp(cmd[i].tokens[0], cmd[i].tokens);
+
+ if (out_fd >= 0)
+ {
+ // Close the Output File
+ close(out_fd);
+ }
+
+ if (in_fd >= 0)
+ {
+ close(in_fd);
+ }
+
+ // Close the Pipe
+ close(fd[0]);
+}
+
+
+/**
+ * Executes the given commands
+ *
+ * @param cmd The given command
+ * @return an exit status
+ * - `0` for success
+ * - `1` for error
+ * - `-1` for exit
+ */
+int execute(char *cmd)
+{
+ int exit_status = 0;
+
+ // Strings prefixed with a comment ('#') or empty strings will be ignored by the shell
+ if (handle_comments_and_empty_string(cmd) == 0)
+ {
+ return 0;
+ }
+
+ // Resets the Arguments List to Avoid Reusing Old Commands
+ reset_arguments();
+
+ // Ignore Inline Comments
+ truncate_str_at_comment(cmd);
+
+ // Make a Copy of the Command
+ char *cmd_copy = (char*)malloc(strlen(cmd) + 1);
+ strcpy(cmd_copy, cmd);
+
+ // Tokenize
+ int argc = tokenize(cmd, arguments);
+ lowercase(arguments[0]);
+
+ // Buil-In Commands
+ if (strcmp(arguments[0], "exit") == 0)
+ {
+ return exit_cmd();
+ }
+ else if (strcmp(arguments[0], "history") == 0)
+ {
+ return history_cmd();
+ }
+ else if (arguments[0][0] == '!')
+ {
+ // History Execution
+ struct cmd_info *repeat_cmd = redo_cmd();
+
+ // Error Handling
+ if (repeat_cmd == NULL)
+ {
+ return 1;
+ }
+
+ // Copy of the Command
+ char *cmd = (char*)malloc(strlen(repeat_cmd->cmd) + 1);
+
+ // Copy Command
+ strcpy(cmd, repeat_cmd->cmd);
+ execute(cmd);
+ }
+ else
+ {
+ // Current Command
+ struct cmd_info *cur_cmd = &history[history_end];
+
+ // Popuate History Struct
+ populate_history_struct(cur_cmd, cmd_copy);
+
+ // Execute Command
+ if (strcmp(arguments[0], "cd") == 0)
+ {
+ return cd_cmd(cur_cmd);
+ }
+ else if (strcmp(arguments[0], "pwd") == 0)
+ {
+ return pwd_cmd(cur_cmd);
+ }
+ else if (strcmp(arguments[0], "uname") == 0)
+ {
+ return uname_cmd(cur_cmd);
+ }
+ else if (strcmp(arguments[0], "ref") == 0)
+ {
+ return reference_cmd(cur_cmd);
+ }
+ else if (strcmp(arguments[0], "joke") == 0)
+ {
+ return joke_cmd(cur_cmd);
+ }
+ else if (strcmp(arguments[0], "fact") == 0)
+ {
+ return fact_cmd(cur_cmd);
+ }
+ else if (strcmp(arguments[0], "quote") == 0)
+ {
+ return quote_cmd(cur_cmd);
+ }
+ else if (strcmp(arguments[0], "menu") == 0)
+ {
+ return main_menu_cmd(cur_cmd);
+ }
+ else if (strcmp(arguments[0], "reset") == 0)
+ {
+ return reset_cmd();
+ }
+ else
+ {
+ struct command cmds[16] = { 0 };
+
+ cmds[0].tokens = arguments;
+ bool background = strcmp(arguments[argc - 1], "&") == 0;
+
+ if (background)
+ {
+ arguments[argc - 1] = NULL;
+ }
+
+ // Set up Commands for Pipeline Execution
+ parse_and_configure_pipeline(cmds, argc);
+
+ // Executing all Regular Commands: (NOT cd, history, !, comments, exit)
+ int child = fork();
+
+ if (child == -1)
+ {
+ /* Something went wrong */
+ fprintf(2, "%s fork failed\n" RESET, error_msg);
+ }
+ else if (child == 0)
+ {
+ // Run Command
+ execute_pipeline(cmds);
+ }
+ else
+ {
+ // Parent: Shell
+
+ if (!background)
+ {
+ uint64 elapsed = 0;
+
+ // Timing Current Commmand
+ uint64 start = gettime();
+
+ int finished_pid = 0;
+
+ while (finished_pid != child)
+ {
+ finished_pid = wait(&exit_status);
+
+ // Error Handling
+ if (finished_pid < 0)
+ {
+ fprintf(2, "Wait failed\n" RESET, error_msg);
+ break;
+ }
+ }
+
+ uint64 end = gettime();
+ elapsed = end - start;
+
+ // Storing Time in the History
+ cur_cmd->time = elapsed;
+ }
+ else
+ {
+ // Background Job
+ }
+ }
+ }
+ }
+
+ return exit_status;
+}
+
+
+// -------------------------------------------------- COMMAND FUNCTIONS --------------------------------------------------
+
+
+/**
+ * Displays the history
+ *
+ * @return an exit status
+ * - `0` for success
+ * - `1` for error
+ */
+int history_cmd()
+{
+ if (private_mode)
+ {
+ printf("๐ต๏ธ\n");
+ printf(BOLD WHITE "Private mode is on. " UNDERLINE BOLD "No history is being recorded.\n" RESET);
+ return 0;
+ }
+
+ // Error Handling
+ if (strcmp(arguments[1], "-t") == 0)
+ {
+ // Error Handling
+ if (too_many_arguments_error(2, "-t", "history -t"))
+ {
+ return 1;
+ }
+ }
+ else if (arguments[1] != NULL)
+ {
+ fprintf(2, "%s Unrecognize flag: " ITALIC "%s\n" RESET WHITE, error_msg, arguments[1]);
+ printf("\n");
+ printf("Usage:\n");
+ printf("history -t\n");
+ return 1;
+ }
+
+ // Loop through the History (Circular Buffer)
+ for (int i = 0; i < 100; i += 1)
+ {
+ struct cmd_info *item = &history[(i + history_end) % 100];
+
+ // Once an item hasn't been used, break out printing out the history
+ if (item->cmd_number < 0)
+ {
+ continue;
+ }
+
+ if (strcmp(arguments[1], "-t") == 0)
+ {
+ // Print History Info of the Current Item with Time
+ printf(WHITE BOLD "[" ORANGE "%d" WHITE BOLD "|" RESET ORANGE "%dms" RESET WHITE BOLD "]" RESET CYAN" %s" RESET, item->cmd_number, item->time / 1000000, item->cmd);
+ }
+ else
+ {
+ // Print History Info of the Current Item
+ printf(WHITE " %d" RESET CYAN " %s" RESET, item->cmd_number, item->cmd);
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * Prints the working directory name
+ *
+ * @return an exit status
+ * - `0` for success
+ * - `1` for error
+ */
+int pwd_cmd(struct cmd_info *cur_cmd)
+{
+ // Error Handling
+ if (too_many_arguments_error(1, "pwd", "pwd"))
+ {
+ return 1;
+ }
+
+ char cwd[128];
+ uint64 elapsed = 0;
+
+ // Timing the 'pwd' Command
+ uint64 start = gettime();
+
+ // Get Current Working Directory
+ getcwd(cwd, 128);
+
+ uint64 end = gettime();
+ elapsed = end - start;
+
+ if (!private_mode)
+ {
+ // Storing Time in History
+ cur_cmd->time = elapsed;
+ }
+
+ // Print the Working Directory Name
+ printf(WHITE ITALIC "%s\n"RESET, cwd);
+ return 0;
+}
+
+
+/**
+ * Prints the operating system name and information
+ *
+ * @return an exit status
+ * - `0` for success
+ * - `1` for error
+ */
+int uname_cmd(struct cmd_info *cur_cmd)
+{
+ uint64 elapsed = 0;
+
+ // Timing the 'pwd' Command
+ uint64 start = gettime();
+
+ if (strcmp(arguments[1], "-a") == 0)
+ {
+ // Error Handling
+ if (too_many_arguments_error(2, "-a", "uname -a"))
+ {
+ return 1;
+ }
+
+ // Print the Working Directory Name
+ printf(CYAN BOLD "OS: " RESET WHITE ITALIC "FogOS " CYAN "(" WHITE "based on xv6" CYAN ")\n" RESET);
+ printf(CYAN BOLD "Host: " RESET WHITE ITALIC "Gojira\n" RESET);
+ printf(CYAN BOLD "Kernel: " RESET WHITE ITALIC "xv6\n" RESET);
+ printf(CYAN BOLD "Shell: " RESET WHITE ITALIC "Tosh v1" CYAN "." WHITE "0\n" RESET);
+ }
+ else if (arguments[1] != NULL)
+ {
+ fprintf(2, "%s Unrecognize flag:" WHITE " " ITALIC "%s\n" RESET WHITE, error_msg, arguments[1]);
+ printf("\n");
+ printf("Usage: \n");
+ printf("uname -a\n");
+ return 1;
+ }
+ else
+ {
+ // Print the Working Directory Name
+ printf(WHITE ITALIC "FogOS " CYAN "(" WHITE "based on xv6" CYAN ")\n" RESET WHITE);
+ }
+
+ uint64 end = gettime();
+ elapsed = end - start;
+
+ if (!private_mode)
+ {
+ // Storing Time in History
+ cur_cmd->time = elapsed;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Changes the directory
+ *
+ * @return an exit status
+ * - `0` for success
+ * - `1` for error
+ */
+int cd_cmd(struct cmd_info *cur_cmd)
+{
+ uint64 elapsed = 0;
+
+ // Timing the 'cd' Command
+ uint64 start = gettime();
+ int status = chdir(arguments[1]);
+
+ // Error Handling
+ if (status < 0)
+ {
+ fprintf(2, "%s chdir: no such file or directory: " ITALIC "%s\n" RESET WHITE, error_msg, arguments[1]);
+ return 1;
+ }
+
+ uint64 end = gettime();
+ elapsed = end - start;
+
+ if (!private_mode)
+ {
+ // Storing Time in History
+ cur_cmd->time = elapsed;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Resets the shell
+ *
+ * @return an exit status
+ * - `0` for success
+ * - `1` for error
+ */
+int reset_cmd()
+{
+ // Error Handling
+ if (too_many_arguments_error(1, "reset", "reset"))
+ {
+ return 1;
+ }
+
+ // Resetting Message
+ printf("\n");
+ printf("Resetting " ITALIC "Tosh\n" RESET);
+ printf("\n");
+
+ // Loading Message
+ printf(WHITE ITALIC "\n");
+ printf("Loading");
+ sleep(6);
+ printf(CYAN);
+ printf(".");
+ sleep(7);
+ printf(".");
+ sleep(8);
+ printf(".");
+ sleep(9);
+ printf("\n");
+ printf("\n" WHITE);
+
+ // Restoring the Shell to its Initial State
+ shell_initial_state();
+ splash_screen();
+
+ return 0;
+}
+
+
+/**
+ * Exits the shell
+ *
+ * @return an exit status
+ * - `-1` for success
+ * - `1` for error
+ */
+int exit_cmd()
+{
+ // Error Handling
+ if (too_many_arguments_error(1, "exit", "exit"))
+ {
+ return 1;
+ }
+
+ return -1;
+}
+
+
+/**
+ * Repeat the last command
+ *
+ * @return a pointer to the `struct cmd_info` representing the last command to repeat,
+ * or NULL if there was no last command found in the history.
+ */
+struct cmd_info *repeat_last_cmd()
+{
+ int last_cmd_index = (history_end == 0) ? 99 : history_end - 1;
+ struct cmd_info *repeat_cmd = &history[last_cmd_index];
+
+ // Checking If There was a Last Command
+ if (repeat_cmd->cmd_number < 0)
+ {
+ fprintf(2, "%s There was no last command\n" RESET WHITE, error_msg);
+ printf("\n");
+ printf("Usage:\n");
+ printf("%s\n", history_execution);
+ return NULL;
+ }
+
+ return repeat_cmd;
+}
+
+
+/**
+ * Repeat command based on a given number
+ *
+ * @return a pointer to the `struct cmd_info` representing the command number to repeat,
+ * or NULL if no matching command is found in the history.
+ */
+struct cmd_info *repeat_cmd_number()
+{
+ int i = 0;
+
+ // Convert String to a Number
+ int idx = atoi(&arguments[0][1]);
+
+ // Check if Repeated Command is in the History
+ for (i = 0; i < 100; i += 1)
+ {
+ if (history[i].cmd_number == idx)
+ {
+ return &history[i];
+ }
+ }
+
+ fprintf(2, "%s Command number " ITALIC "'%d'" RESET WHITE " is not in the history\n", error_msg, idx);
+ printf(WHITE"\n");
+ printf("Usage:\n");
+ printf("%s\n", history_execution);
+ return NULL;
+}
+
+
+/**
+ * Repeat command based on the prefix
+ *
+ * @return a pointer to the `struct cmd_info` representing the command prefix to repeat,
+ * or NULL if there was no command prefix found in the history.
+ */
+struct cmd_info *repeat_cmd_prefix()
+{
+ int i = 0;
+
+ int prefix_length = strlen(arguments[0]) - 1;
+
+ struct cmd_info *repeat_cmd = NULL;
+
+ // Loop through the History Struct
+ for (i = 0; i < 100; i += 1)
+ {
+ // Check Current Command in the History
+ repeat_cmd = &history[(history_end - i + 99) % 100];
+
+ // Prefix is in the History Struct
+ if (strncmp2(repeat_cmd->cmd, &arguments[0][1], prefix_length) == 0)
+ {
+ return repeat_cmd;
+ }
+ }
+
+ fprintf(2, "%s Command " ITALIC "'%s'" RESET WHITE " is not in the history\n", error_msg, &arguments[0][1]);
+ printf(WHITE"\n");
+ printf("Usage:\n");
+ printf("%s\n", history_execution);
+ return NULL;
+}
+
+
+/**
+ * Repeat a command
+ *
+ * @return a pointer to the `struct cmd_info` representing the command to repeat,
+ * or NULL if there was no matching command found.
+ */
+struct cmd_info *redo_cmd()
+{
+ struct cmd_info *repeat_cmd = NULL;
+
+ // Last Command
+ if (strcmp(arguments[0], "!!") == 0)
+ {
+ repeat_cmd = repeat_last_cmd();
+ }
+ // Number (e.g. !23)
+ else if (isInt2(&arguments[0][1]))
+ {
+ repeat_cmd = repeat_cmd_number();
+ }
+ // Prefix (e.g. !c)
+ else
+ {
+ repeat_cmd = repeat_cmd_prefix();
+ }
+
+ return repeat_cmd;
+}
+
+
+// -------------------------------------------------- EXECUTION HELPERS --------------------------------------------------
+
+
+/**
+ * Parses the commands and configures them for pipeline execution
+ *
+ * @param cmds An array of structs that represents a command in a pipeline
+ * @param argc The number of command line arguments
+ */
+void parse_and_configure_pipeline(struct command *cmds, int argc)
+{
+ int cmds_idx = 0;
+
+ // Check if There are Pipes
+ for (int i = 0; i < argc; i += 1)
+ {
+ // Last Command
+ if (i == argc - 1)
+ {
+ cmds[cmds_idx].stdout_pipe = false; /* Last command so set stdout_pipe = false */
+ }
+
+ // Found a Pipe
+ if (strcmp(arguments[i], "|") == 0)
+ {
+ cmds[cmds_idx].stdout_pipe = true;
+ cmds[cmds_idx].stdout_file = NULL; /* This command is not writing to a file. */
+ arguments[i] = NULL; // Remove the '|'
+ cmds_idx += 1;
+
+ // Next Token starts 1 position after the Pipe
+ cmds[cmds_idx].tokens = &arguments[i + 1];
+ }
+ else if (strcmp(arguments[i], "<") == 0)
+ {
+ arguments[i] = NULL; // Removes the '<'
+ cmds[cmds_idx].stdin_file = arguments[i + 1]; /* Input Redirection */
+ i += 1;
+ arguments[i] = NULL; // Removes the filename
+ }
+ else if (strcmp(arguments[i], ">") == 0)
+ {
+ arguments[i] = NULL; // Removes the '>'
+ cmds[cmds_idx].stdout_file = arguments[i + 1]; /* Output Redirection */
+ cmds[cmds_idx].append_file = false;
+ i += 1;
+ arguments[i] = NULL; // Removes the filename
+ }
+ else if (strcmp(arguments[i], ">>") == 0)
+ {
+ arguments[i] = NULL; // Removes the '>>'
+ cmds[cmds_idx].stdout_file = arguments[i + 1]; /* Output Redirection to a File */
+ cmds[cmds_idx].append_file = true;
+ i += 1;
+ arguments[i] = NULL; /* Removes the filename */
+ }
+ }
+}
+
+
+/**
+ * Resets the arguments list to avoid reusing old commands
+ */
+void reset_arguments()
+{
+ for (int i = 0; i < 128; i += 1)
+ {
+ arguments[i] = NULL;
+ }
+}
+
+
+/**
+ * Stores data in the history struct
+ */
+void populate_history_struct(struct cmd_info *cur_cmd, char *cmd_copy)
+{
+ uint64 elapsed = 0;
+
+ // Overwrite the Next Item in the History List
+ if (cur_cmd->cmd_number >= 0)
+ {
+ free(cur_cmd->cmd);
+ }
+
+ // Add Data to the History Struct
+ cur_cmd->cmd = cmd_copy;
+ cur_cmd->cmd_number = command_number; /* Storing the command counter for this session of 'tosh' */
+ cur_cmd->time = elapsed; /* Time it took to execute current command */
+
+ // Wrap Around to the Top of the History Array
+ history_end = (history_end + 1) % 100;
+
+ // Increment Command Number Counter
+ command_number += 1;
+}
+
+
+/**
+ * Truncates the string at the comment ('#')
+ */
+void truncate_str_at_comment(char *cmd)
+{
+ // Truncate the String at the '#'
+ for (int i = 0; i < strlen(cmd); i += 1)
+ {
+ // Found Comment
+ if (cmd[i] == '#')
+ {
+ cmd[i] = '\n'; /* Ensures that the next command is in a new line */
+ cmd[i + 1] = '\0';
+ }
+ }
+}
+
+
+/**
+ * Strings prefixed with a comment ('#') or empty strings will be ignored by the shell
+ *
+ * @param cmd The given command
+ * @return an exit status
+ * - `0` for success if a comment or empty string was found
+ * - `1` otherwise
+ */
+int handle_comments_and_empty_string(char *cmd)
+{
+ // # (comments): strings prefixed with # will be ignored by the shell / Empty strings will be ignoredy
+ if (cmd[0] == '#' || strlen(cmd) == 1)
+ {
+ return 0;
+ }
+
+ for (int i = 0; i < strlen(cmd); i += 1)
+ {
+ if (cmd[i] != ' ' && cmd[i] != '\t' && cmd[i] != '\n')
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+// -------------------------------------------------- OPTIONS --------------------------------------------------
+
+
+/**
+ * Identifies which option to execute
+ *
+ * @return an option type
+ * - `0` for regular option
+ * - `1` standalone option
+ */
+int tosh_options(char *option)
+{
+ if (strcmp(option, "-v") == 0 || strcmp(option, "--version") == 0)
+ {
+ version_opt();
+ return 1;
+ }
+ else if (strcmp(option, "--about") == 0)
+ {
+ about_opt();
+ return 1;
+ }
+ else if (strcmp(option, "--license") == 0)
+ {
+ license_opt();
+ return 1;
+ }
+ else if (strcmp(option, "-lc") == 0 || strcmp(option, "--list-commands") == 0)
+ {
+ list_cmds_opt();
+ return 1;
+ }
+ else if (strcmp(option, "-p") == 0 || strcmp(option, "--private") == 0)
+ {
+ private_opt();
+ return 0;
+ }
+ else
+ {
+ fprintf(2, "%s Unrecognize option: " ITALIC "%s\n" RESET WHITE, error_msg, option);
+ printf(WHITE"\n");
+ printf("Usage:\n");
+ printf("%s\n" RESET WHITE, options);
+ return 1;
+ }
+}
+
+
+/**
+ * Shows which version of 'Tosh'
+ */
+void version_opt()
+{
+ // Error Handling
+ if (too_many_arguments_error(2, "-v, --version", "tosh -v | --version"))
+ {
+ return;
+ }
+
+ printf(ITALIC WHITE "Tosh v1" CYAN "." WHITE "0\n" RESET WHITE);
+}
+
+
+/**
+ * Displays the xv6 license
+ */
+void license_opt()
+{
+ // Error Handling
+ if (too_many_arguments_error(2, "--license", "tosh --license"))
+ {
+ return;
+ }
+
+ printf(WHITE"\n");
+
+ printf("The xv6 software is:\n");
+ printf("\n");
+ printf("Copyright (c) 2006-2019 Frans Kaashoek, Robert Morris, Russ Cox,\n\t\t\tMassachusetts Institute of Technology\n");
+ printf("\n");
+ printf("Permission is hereby granted, free of charge, to any person obtaining\n");
+ printf("a copy of this software and associated documentation files (the\n");
+ printf("\"Software\"), to deal in the Software without restriction, including\n");
+ printf("without limitation the rights to use, copy, modify, merge, publish,\n");
+ printf("distribute, sublicense, and/or sell copies of the Software, and to\n");
+ printf("permit persons to whom the Software is furnished to do so, subject to\n");
+ printf("the following conditions:\n");
+ printf("\n");
+ printf("\n");
+ printf("The above copyright notice and this permission notice shall be\n");
+ printf("included in all copies or substantial portions of the Software.\n");
+ printf("\n");
+ printf("\n");
+ printf("THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n");
+ printf("EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n");
+ printf("MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n");
+ printf("NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n");
+ printf("LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n");
+ printf("OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n");
+ printf("WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n");
+
+ printf("\n");
+}
+
+
+/**
+ * Prints out an about section for 'Tosh'
+ */
+void about_opt()
+{
+ // Error Handling
+ if (too_many_arguments_error(2, "--about", "tosh --about"))
+ {
+ return;
+ }
+
+ printf(ITALIC WHITE "Tosh v1" CYAN "." WHITE "0\n");
+ printf( "Created by " BOLD "NINO ESTRADA\n" RESET ITALIC WHITE);
+ printf("" CYAN "ยฉ" WHITE " 2024\n" RESET WHITE);
+ printf("\n");
+ printf("" ITALIC "Tosh" RESET WHITE " was built to bring simplicity and creativity to the shell" CYAN ".\n" RESET WHITE);
+ printf("\n");
+ printf("The name " CYAN "'" WHITE "Tosh" CYAN "'" WHITE " was inspired by an operating systems class where the professor created a shell and called it " CYAN "'" WHITE ITALIC "Trash" RESET CYAN ".'" WHITE " I wanted to come up with a just as bold name" CYAN "," WHITE " so I chose " CYAN "'" WHITE ITALIC "Tosh" RESET CYAN "'โ" WHITE "a British slang word meaning " CYAN "'" WHITE "rubbish" CYAN "'" WHITE " or " CYAN "'" WHITE "nonsense" CYAN ".'\n" WHITE);
+ printf("\n");
+ printf("" ITALIC "Tosh" RESET WHITE " was built to bridge the gap between practicality and personality" CYAN "," WHITE " hopefully helping users enjoyably explore their system" CYAN ".\n" WHITE);
+ printf("\n");
+ printf("Thank you for using " ITALIC "Tosh" RESET CYAN "!\n" RESET WHITE);
+}
+
+
+/**
+ * Displays a list of all the commands in 'Tosh'
+ */
+void list_cmds_opt()
+{
+ // Error Handling
+ if (too_many_arguments_error(2, "-lc, --list-commands", "tosh -lc | --list-commands"))
+ {
+ return;
+ }
+
+ const char *list_of_cmds = WHITE UNDERLINE "Command\n" RESET CYAN
+ "cat\n"
+ "echo\n"
+ "ls\n"
+ "cd\n"
+ "mkdir\n"
+ "pwd\n"
+ "wc\n"
+ "history\n"
+ "test\n"
+ "uname\n"
+ "joke\n"
+ "fact\n"
+ "quote\n"
+ "menu\n"
+ "ref\n"
+ "exit\n";
+
+ printf("\n%s \n" WHITE, list_of_cmds);
+}
+
+
+/**
+ * Sets the shell to private mode where commands are not stored in the history
+ */
+void private_opt()
+{
+ // Error Handling
+ if (too_many_arguments_error(2, "-p, --private", "tosh -p | --private"))
+ {
+ return;
+ }
+
+ if (private_mode)
+ {
+ printf("Private mode is already on" CYAN ".\n" RESET WHITE);
+ return;
+ }
+
+ private_mode = true;
+ printf("๐ต๏ธ \n");
+ printf("Prvate mode is on" CYAN ".\n" RESET WHITE);
+}
+
+
+// -------------------------------------------------- SPECIAL FEATURES --------------------------------------------------
+
+
+/**
+ * The first screen that appears when tosh is booted up
+ */
+void splash_screen()
+{
+ printf(WHITE ITALIC "\n");
+ printf("Okay");
+ sleep(1);
+ printf(CYAN ", " WHITE);
+ sleep(3);
+ printf("3");
+ sleep(3);
+ printf(CYAN ", " WHITE);
+ sleep(3);
+ printf("2");
+ sleep(3);
+ printf(CYAN ", " WHITE);
+ sleep(3);
+ printf("1");
+ sleep(3);
+ printf(CYAN ", " WHITE);
+ sleep(3);
+ printf(UNDERLINE "let" CYAN "'" WHITE "s jam");
+ sleep(3);
+ printf("\n\n\n" RESET);
+ sleep(2);
+ printf(BLUE);
+ printf(" โโโโโโโโโ โโโโโโโ โโโโโโโโโโโ โโโ\n");
+ sleep(2);
+ printf(" โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโ\n");
+ sleep(2);
+ printf(" โโโ โโโ โโโโโโโโโโโโโโโโโโโ\n");
+ sleep(2);
+ printf(" โโโ โโโ โโโโโโโโโโโโโโโโโโโ\n");
+ sleep(2);
+ printf(" โโโ โโโโโโโโโโโโโโโโโโโโ โโโ\n");
+ sleep(2);
+ printf(" โโโ โโโโโโโ โโโโโโโโโโโ โโโ\n");
+ sleep(2);
+ printf(ITALIC " the operating shell\n" RESET);
+ sleep(2);
+ printf(YELLOW "\n");
+ sleep(2);
+ printf(" ๐ Change Folders cd\n");
+ sleep(2);
+ printf(" ๐ Make Folder mkdir\n");
+ sleep(2);
+ printf(" ๐ List Content ls\n");
+ sleep(2);
+ printf(" ๐ข Output echo\n");
+ sleep(2);
+ printf(" โณ History history\n");
+ sleep(2);
+ printf(" ๐ Reference ref\n");
+ sleep(2);
+ printf(" ๐ช Quit exit\n");
+ sleep(2);
+ printf("\n"RESET);
+ printf(BLUE ITALIC " created by NINO ESTRADA\n" RESET);
+ sleep(2);
+}
+
+
+/**
+ * The main menu of tosh highlighting key features
+ *
+ * @return an exit status
+ * - `0` for success
+ * - `1` for error
+ */
+int main_menu_cmd(struct cmd_info *cur_cmd)
+{
+ // Error Handling
+ if (too_many_arguments_error(1, "menu", "menu"))
+ {
+ return 1;
+ }
+
+ uint64 elapsed = 0;
+
+ // Timing the 'menu' Command
+ uint64 start = gettime();
+
+ printf("\n\n");
+ printf(BLUE);
+ printf(" โโโโโโโโโ โโโโโโโ โโโโโโโโโโโ โโโ\n");
+ printf(" โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโ\n");
+ printf(" โโโ โโโ โโโโโโโโโโโโโโโโโโโ\n");
+ printf(" โโโ โโโ โโโโโโโโโโโโโโโโโโโ\n");
+ printf(" โโโ โโโโโโโโโโโโโโโโโโโโ โโโ\n");
+ printf(" โโโ โโโโโโโ โโโโโโโโโโโ โโโ\n");;
+ printf(ITALIC " the operating shell\n" RESET);
+ printf(YELLOW "\n");
+ printf(" ๐ Change Folders cd\n");
+ printf(" ๐ Make Folder mkdir\n");
+ printf(" ๐ List Content ls\n");
+ printf(" ๐ข Output echo\n");
+ printf(" โณ History history\n");
+ printf(" ๐ Reference ref\n");
+ printf(" ๐ช Quit exit\n");
+ printf("\n"RESET);
+ printf(BLUE ITALIC " created by NINO ESTRADA\n" RESET);
+
+ uint64 end = gettime();
+ elapsed = end - start;
+
+ if (!private_mode)
+ {
+ // Storing Time in History
+ cur_cmd->time = elapsed;
+ }
+
+ return 0;
+}
+
+
+/**
+ * The reference command displays all the available commands and their descriptions
+ *
+ * @return an exit status
+ * - `0` for success
+ * - `1` for error
+ */
+int reference_cmd(struct cmd_info *cur_cmd)
+{
+ // Error Handling
+ if (too_many_arguments_error(1, "ref", "ref"))
+ {
+ return 1;
+ }
+
+ uint64 elapsed = 0;
+
+ // Timing the 'help' Command
+ uint64 start = gettime();
+
+ const char *synopsis = WHITE BOLD " " BLUE_BACKGROUND "SYNOPSIS" RESET "\n\n" RESET
+ WHITE "tosh " BOLD "[" RESET ORANGE "options" BOLD WHITE "] [" RESET ORANGE "script" BOLD WHITE "]\n" RESET WHITE;
+
+ const char *note = WHITE BOLD "Note" RESET WHITE": Parameters in square brackets are optional.";
+
+ printf(WHITE BOLD UNDERLINE "Reference Guide" RESET " \n\n%s \n%s \n\n%s \n\n%s \n\n%s \n\n%s \n\n%s \n" RESET WHITE, synopsis, note, options, built_in_cmds, history_execution, io_redirection, job_control);
+
+ uint64 end = gettime();
+ elapsed = end - start;
+
+ if (!private_mode)
+ {
+ // Storing Time in History
+ cur_cmd->time = elapsed;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Displays a programming-related joke to the terminal
+ *
+ * @return an exit status
+ * - `0` for success
+ * - `1` for error
+ */
+int joke_cmd(struct cmd_info *cur_cmd)
+{
+ // Error Handling
+ if (too_many_arguments_error(1, "joke", "joke"))
+ {
+ return 1;
+ }
+
+ uint64 elapsed = 0;
+
+ // Timing the 'joke' Command
+ uint64 start = gettime();
+
+ char *jokes[20];
+ jokes[0] = "How many computer programmers does it take to change a light bulb" CYAN "?\n๐ค..." WHITE "\nNone" CYAN "," WHITE " that" CYAN "'" WHITE "s a hardware problem" CYAN "!" RESET WHITE;
+ jokes[1] = "There are 10 types of people in the world" CYAN "." WHITE " Those who understand binary" CYAN "," WHITE " and those who don" CYAN "'" WHITE "t" CYAN "." RESET WHITE;
+ jokes[2] = "My dog ate my computer sceince project" CYAN "." WHITE " It only took him a few bytes" CYAN "!" RESET WHITE;
+ jokes[3] = "I don" CYAN "'" WHITE "t talk about computer science at home" CYAN "." WHITE " It makes my mother board" CYAN "!" RESET WHITE;
+ jokes[4] = "I don" CYAN "'" WHITE "t like computer science jokes" CYAN "." WHITE " Not one bit" CYAN "!" RESET WHITE;
+ jokes[5] = "Why couldn" CYAN "'" WHITE "t the computer science student read his text book" CYAN "?\n๐ค..." WHITE "\nHe couldn" CYAN "'" WHITE "t find page 404" CYAN "!" RESET WHITE;
+ jokes[6] = "What do you get if you cross computer science with a pond" CYAN "?\n๐ค..." WHITE "\nAlgae rhythms" CYAN "!" RESET WHITE;
+ jokes[7] = "Which farm animal likes computing the best" CYAN "?\n๐ค..." WHITE "\nThe RAM" CYAN "!" RESET WHITE;
+ jokes[8] = "Why was the computer programmer fired from M15" CYAN "?\n๐ค..." WHITE "\nHe couldn" CYAN "'" WHITE "t hack it" CYAN "!" RESET WHITE;
+ jokes[9] = "Why should you be scared of computers" CYAN "?\n๐ค..." WHITE "\nThey byte" CYAN "!" RESET WHITE;
+ jokes[10] = "Where do computer programmers go for fun" CYAN "?\n๐ค..." WHITE "\nThe disc" CYAN "-" WHITE "o" CYAN "!" RESET WHITE;
+ jokes[11] = "Why do computer scientists prefer the metric system" CYAN "?\n๐ค..." WHITE "\nThey are all pro" CYAN "-" WHITE "gram" CYAN "-" WHITE "ers" CYAN "!" RESET WHITE;
+ jokes[12] = "What" CYAN "'" WHITE "s a computer scientist" CYAN "'" WHITE "s favorite type of coffee" CYAN "?\n๐ค..." WHITE "\nJava" CYAN "!" RESET WHITE;
+ jokes[13] = "What does a baby computer call its dad" CYAN "?\n๐ค..." WHITE "\nData" CYAN "!" RESET WHITE;
+ jokes[14] = "What" CYAN "'" WHITE "s a computer programmer" CYAN "'" WHITE "s house made of" CYAN "?\n๐ค..." WHITE "\nFirewalls" CYAN "!" RESET WHITE;
+ jokes[15] = "Why did the computer programmer quit" CYAN "?\n๐ค..." WHITE "\nHe didn" CYAN "'" WHITE "t get arrays" CYAN "!" RESET WHITE;
+ jokes[16] = "I could tell you a joke about UDP" CYAN "," WHITE " but I don" CYAN "'" WHITE "t know if you" CYAN "'" WHITE "d get it" CYAN "." RESET WHITE;
+ jokes[17] = "Who" CYAN "'" WHITE "s a computer scientist" CYAN "'" WHITE "s favorite Star Wars character" CYAN "?\n๐ค..." WHITE "\nAdobe Wan Kenobi" CYAN "!" RESET WHITE;
+ jokes[18] = "What kind of money do computer sientists use" CYAN "?\n๐ค..." WHITE "\nCache" CYAN "!" RESET WHITE;
+ jokes[19] = "Why was the computer scientist bad at driving" CYAN "?\n๐ค..." WHITE "\nThey kept crashing" CYAN "!" RESET WHITE;
+
+ // Print a Joke Randomly
+ printf(WHITE ITALIC "%s\n" RESET WHITE, jokes[cur_cmd->cmd_number%20]);
+
+ uint64 end = gettime();
+ elapsed = end - start;
+
+ if (!private_mode)
+ {
+ // Storing Time in History
+ cur_cmd->time = elapsed;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Displays a fun programming-related fact to the terminal
+ *
+ * @return an exit status
+ * - `0` for success
+ * - `1` for error
+ */
+int fact_cmd(struct cmd_info *cur_cmd)
+{
+ // Error Handling
+ if (too_many_arguments_error(1, "fact", "fact"))
+ {
+ return 1;
+ }
+
+ uint64 elapsed = 0;
+
+ // Timing the 'fact' Command
+ uint64 start = gettime();
+
+ char *facts[21];
+ facts[0] = "The first computer virus was created in 1986" CYAN "." RESET WHITE;
+ facts[1] = "Ada Lovelace" CYAN "," WHITE" a woman" CYAN "," WHITE " was the world" CYAN "'" WHITE "s first computer programmer" CYAN "." RESET WHITE;
+ facts[2] = "The world" CYAN "'" WHITE "s first electronic computer" CYAN "," WHITE " the Colossus" CYAN "," WHITE " was built in 1943" CYAN "." RESET WHITE;
+ facts[3] = "The first webcam was invented at the University of Cambridge to monitor the coffee pot in the computer science department" CYAN "." RESET WHITE;
+ facts[4] = "The first email was sent in 1971 by Ray Tomlinson" CYAN "." RESET WHITE;
+ facts[5] = "The first computer game" CYAN ", '" WHITE "Spacewar" CYAN "!'," WHITE " was invented in 1962" CYAN "." RESET WHITE;
+ facts[6] = "The first computer" CYAN "," WHITE " known as the ENIAC" CYAN "," WHITE " weighed 30 tons and took up an entire room" CYAN "." RESET WHITE;
+ facts[7] = "The first website was created in 1991 by Tim Berners" CYAN "-" WHITE "Lee" CYAN "." RESET WHITE;
+ facts[8] = "There are over 700 programming languages" CYAN "!" RESET WHITE;
+ facts[9] = "At least 30k websites are hacked daily" CYAN "!" RESET WHITE;
+ facts[10] = "Just 10" CYAN "%" WHITE " of the money in the world is physical currency" CYAN "," WHITE " the remaining being computer currency" CYAN "!" RESET WHITE;
+ facts[11] = "A teenager " CYAN "(" WHITE "Johnathan James" CYAN ")" WHITE " once hacked NASA" CYAN "'" WHITE "s computer systems" CYAN "!" RESET WHITE;
+ facts[12] = "Domain names were free until 1995" CYAN "!" RESET WHITE;
+ facts[13] = "People blink less when they use computers" CYAN "." RESET WHITE;
+ facts[14] = "More than 80" CYAN "%" WHITE " of daily emails in the U" CYAN "." WHITE "S" CYAN "." WHITE " are spam" CYAN "." RESET WHITE;
+ facts[15] = "The password of the computers in charge of controlling the nuclear missiles of the United States Army for years was 0000000000" CYAN "." RESET WHITE;
+ facts[16] = "The three most commonly used passwords in the world are 123456" CYAN "," WHITE " password" CYAN "," WHITE " and 12345" CYAN "." RESET WHITE;
+ facts[17] = "NASA still uses programs from the 70s in their spacecraft" CYAN "." RESET WHITE;
+ facts[18] = "It took less code to send a man to the Moon than to run a smartphone" CYAN "." RESET WHITE;
+ facts[19] = "Computer Programming played a vital role in the ending of World War II" CYAN "." RESET WHITE;
+ facts[20] = "JAVA was born out of a coffee session" CYAN "." RESET WHITE;
+
+ // Print a Fact
+ printf(WHITE ITALIC "%s\n" RESET WHITE, facts[cur_cmd->cmd_number%21]);
+
+ uint64 end = gettime();
+ elapsed = end - start;
+
+ if (!private_mode)
+ {
+ // Storing Time in History
+ cur_cmd->time = elapsed;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Displays a programming-related quote to the terminal
+ *
+ * @return an exit status
+ * - `0` for success
+ * - `1` for error
+ */
+int quote_cmd(struct cmd_info *cur_cmd)
+{
+ // Error Handling
+ if (too_many_arguments_error(1, "quote", "quote"))
+ {
+ return 1;
+ }
+
+ uint64 elapsed = 0;
+
+ // Timing the 'quote' Command
+ uint64 start = gettime();
+
+ char *quotes[21];
+ quotes[0] = "\"Code is like humor" CYAN "." RESET WHITE ITALIC " When you have to explain it" CYAN"," RESET WHITE ITALIC " it" CYAN "'" RESET WHITE ITALIC "s bad" CYAN "." RESET WHITE ITALIC "\" " RESET CYAN BOLD "-" RESET WHITE " Cory House";
+ quotes[1] = CYAN "\"" WHITE "Coding is like poetry should be short and concise" CYAN ".\" " RESET CYAN BOLD "-" RESET WHITE " Santosh Kalwar";
+ quotes[2] = CYAN "\"" WHITE "Software is like sex" CYAN ";" WHITE " it" CYAN"'" WHITE "s better when it" CYAN "'" WHITE "s free" CYAN".\" " RESET CYAN BOLD "-" RESET WHITE " Linus Torvalds";
+ quotes[3] = CYAN "\"" WHITE "If we want users to like our software" CYAN "," WHITE " we should design it to behave like a likable person" CYAN ".\" " RESET CYAN BOLD "-" RESET WHITE " Alan Cooper";
+ quotes[4] = CYAN "\"" WHITE "Software and cathedrals are much the same " CYAN BOLD "โ" RESET WHITE ITALIC" first we build them" CYAN "," WHITE " then we pray" CYAN ".\"" RESET WHITE;
+ quotes[5] = CYAN "\"" WHITE "When I wrote this code" CYAN "," WHITE " only God and I understood what I did" CYAN "." WHITE " Now only God knows" CYAN ".\" " RESET CYAN BOLD "โ" RESET WHITE " Anonymous";
+ quotes[6] = CYAN "\"" WHITE "You might not think that programmers are artists" CYAN "," WHITE " but programming is an extremely creative profession" CYAN "." WHITE" It" CYAN "โ" WHITE "s logic" CYAN "-" WHITE "based creativity" CYAN ".\" " RESET CYAN BOLD "โ" RESET WHITE" John Romero";
+ quotes[7] = CYAN "\"" WHITE "If debugging is the process of removing bugs" CYAN "," WHITE " then programming must be the process of putting them in" CYAN ".\" " RESET CYAN BOLD "โ" RESET WHITE" Sam Redwine";
+ quotes[8] = CYAN "\"" WHITE "The most dangerous phrase in the language is" CYAN ", โ" WHITE "We" CYAN "โ" WHITE "ve always done it this way" CYAN ".'\" " RESET CYAN "-" RESET WHITE " Grace Hopper";
+ quotes[9] = CYAN "\"" WHITE "Code is read much more often than it is written" CYAN ".\" " RESET CYAN BOLD "-" RESET WHITE " Guido Van Rossum";
+ quotes[10] = CYAN "\"" WHITE "This was one of the best parts of being a coder" CYAN "," WHITE " and an artist" CYAN ":" WHITE " the thrill of being in the middle of creating something delightful" CYAN "." WHITE " It" CYAN "โ" WHITE "s like the anticipation of eating freshly baked bread after its aroma fills the room" CYAN ".\" " RESET CYAN BOLD "-" RESET WHITE " Dr. Joy Buolamwini";
+ quotes[11] = CYAN "\"" WHITE "Every great developer you know got there by solving problems they were unqualified to solve until they actually did it" CYAN ".\" " RESET CYAN BOLD "-" RESET WHITE " Patrick McKenzie";
+ quotes[12] = CYAN "\"" WHITE "The code you write makes you a programmer" CYAN "." WHITE " The code you delete makes you a good one" CYAN "." WHITE " The code you don" CYAN "โ" WHITE "t have to write makes you a great one" CYAN ".\" " RESET CYAN BOLD "-" RESET WHITE " Mario Fusco";
+ quotes[13] = CYAN "\"" WHITE "Any fool can write code that a computer can understand" CYAN "." WHITE " Good programmers write code that humans can understand" CYAN ".\" " RESET CYAN BOLD "-" RESET WHITE " Martin Fowler";
+ quotes[14] = CYAN "\"" WHITE "The function of good software is to make the complex appear to be simple" CYAN ".\" " RESET CYAN BOLD "-" RESET WHITE " Grady Booch";
+ quotes[15] = CYAN "\"" WHITE "Simplicity is the soul of efficiency" CYAN ".\" " RESET CYAN BOLD "-" RESET WHITE " Austin Freeman";
+ quotes[16] = CYAN "\"" WHITE "It" CYAN "'" WHITE "s not a bug " CYAN "โ" WHITE " it" CYAN "'" WHITE "s an undocumented feature" CYAN ".\"" RESET WHITE;
+ quotes[17] = CYAN "\"" WHITE "In order to understand recursion" CYAN "," WHITE " one must first understand recursion" CYAN ".\" " RESET CYAN BOLD "-" RESET WHITE " Mouna Ben Ali";
+ quotes[18] = CYAN "\"" WHITE "Code never lies" CYAN "," WHITE " comments sometimes do" CYAN ".\" " RESET CYAN BOLD "-" RESET WHITE " Ron Jeffries";
+ quotes[19] = CYAN "\"" WHITE "Don" CYAN "โ" WHITE "t comment bad code " RESET CYAN BOLD "โ" RESET WHITE ITALIC " rewrite it" CYAN ".\" " RESET CYAN BOLD "-" RESET WHITE " Brian Kernighan";
+ quotes[20] = CYAN "\"" WHITE "Debugging becomes significantly easier if you first admit that you are the problem" CYAN ".\" " RESET CYAN BOLD "-" RESET WHITE " William Laeder";
+
+ // Print a Quote
+ printf(WHITE ITALIC "%s\n" RESET WHITE, quotes[cur_cmd->cmd_number%21]);
+
+ uint64 end = gettime();
+ elapsed = end - start;
+
+ if (!private_mode)
+ {
+ // Storing Time in History
+ cur_cmd->time = elapsed;
+ }
+
+ return 0;
+}
+
+
+/**
+ * The last screen that appears when tosh is shutdown
+ */
+void off_message()
+{
+ printf("\n\n");
+ printf(BLUE);
+ printf(" โโโโโโโ โโโโ โโโโโโโ โโโโโโโโโโโโโโโโ\n");
+ printf(" โโโโโโโโ โโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโ\n");
+ printf(" โโโโโโโโโโโโโโ โโโ โโโโโโโโโ โโโโโโ \n");
+ printf(" โโโโโโโโโโโโโโ โโโ โโโโโโโโโ โโโโโโ \n");
+ printf(" โโโโโโ โโโ โโโ โโโโโโโโโโโโ โโโโโโ \n");
+ printf(" โโโโโโ โโโ โโโโโโโ โโโ โโโโโโ \n");
+ printf(" \n" RESET);
+}
+
+
+// -------------------------------------------------- MAIN FUNCTION --------------------------------------------------
+
+
+/**
+ * The entry point of the program
+ *
+ * @param argc The number of command line arguments
+ * @param argv The array of command line arguments
+ * @return an exit status
+ * - `0` for success
+ * - `1` for error
+ */
+int main(int argc, char **argv)
+{
+ int exit_status = 0;
+ uint cmd_size = 512;
+ char *cmd = (char *)malloc(cmd_size); /* buffer */
+ int input_fd = 0; /* Receive commands from STDIN by default */
+ bool show_splash = true;
+
+ // Shell Script Support
+ if (argc > 1)
+ {
+ // Option
+ if (argv[1][0] == '-')
+ {
+ for (int i = 0; i < argc; i += 1)
+ {
+ arguments[i] = argv[i];
+ }
+
+ if (tosh_options(argv[1]) == 1)
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ // Receive commands from specify script file
+ input_fd = open(argv[1], 0);
+
+ // Error Handling
+ if (input_fd < 0)
+ {
+ fprintf(2, "%s " WHITE "cannot open: " ITALIC "%s" RESET "\n", error_msg, argv[1]);
+ exit(1);
+ }
+
+ show_splash = false;
+ }
+ }
+
+ if (show_splash)
+ {
+ splash_screen();
+ printf("\n");
+ }
+
+ // Initilaize all the command numbers to be -1 to indicate they have not been used yet
+ initialize_history();
+
+ // Keep Retreiving Commands until User Exits or End of Script File has Reached
+ while (exit_status != -1)
+ {
+ // STDIN
+ if (input_fd == 0)
+ {
+ // Get Current Working Directory
+ getcwd(cwd, 128);
+
+ if (private_mode)
+ {
+ printf(WHITE BOLD "[" RESET WHITE "%d" BOLD "]" CYAN "-" BOLD WHITE "[" RESET WHITE "%s" BOLD "]" RESET CYAN "$ " WHITE, exit_status, cwd);
+ }
+ else
+ {
+ // Printing the Prompt
+ printf(WHITE BOLD "[" RESET WHITE "%d" BOLD "]" CYAN "-" BOLD WHITE "[" RESET WHITE "%d" BOLD "]" CYAN "-" BOLD WHITE "[" RESET WHITE "%s" BOLD "]" RESET CYAN "$ " WHITE, exit_status, command_number, cwd);
+ }
+ }
+
+ // Getting the Next Command
+ int read = getline(&cmd, &cmd_size, input_fd);
+
+ // Run the Current Command
+ exit_status = execute(cmd);
+
+ // Reach the End of the Script File
+ if (read < 1)
+ {
+ break;
+ }
+ }
+
+ if (show_splash)
+ {
+ off_message();
+ }
+
+ return 0;
+}
+
+
+// -------------------------------------------------- REFERENCES --------------------------------------------------
+
+
+/*
+strncmp - https://www.tutorialspoint.com/c_standard_library/c_function_strncmp.htm
+Avoiding Zombie Processes in Background Jobs - https://www.geeksforgeeks.org/zombie-processes-prevention/
+Adding Colors to Terminal - https://www.theurbanpenguin.com/4184-2/
+Colors - https://en.wikipedia.org/wiki/ANSI_escape_code
+ASCII Table - https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html
+ASCII Font - https://patorjk.com/software/taag/#p=display&f=Graffiti&t=Type%20Something%20
+Goodbye ASCII Art - https://emojicombos.com/bye-ascii-art
+Computer Science Jokes - https://www.beano.com/jokes/science/computer-science-jokes
+Fun Facts about Computer Science - https://www.idtech.com/blog/computer-science-facts-collection-of-interesting-statistics
+Fun Facts about Computer Science 2 - https://mp.moonpreneur.com/blog/computer-science-facts-for-kids/
+Fun Facts about Computer Science 3 - https://www.computercpr.com/computer-facts/
+Fun Facts about Computer Science 4 - https://pandorafms.com/blog/computing-facts/
+Fun Facts about Computer Science 5 - https://www.21kschool.com/us/blog/15-facts-about-coding/
+Quotes - https://techvify-software.com/35-best-coding-programming-quotes/
+Quotes 2 - https://www.codecademy.com/resources/blog/inspirational-coding-quotes/
+Quotes 3 - https://www.geeksforgeeks.org/coding-quotes-for-software-engineers/
+Quotes 4 - https://devtechnosys.com/insights/inspiring-programming-quotes-for-developer/
+Random - https://wiki.osdev.org/Random_Number_Generator
+*/
diff --git a/user/user.h b/user/user.h
index 2e6fc55..49af6dd 100644
--- a/user/user.h
+++ b/user/user.h
@@ -22,6 +22,8 @@ int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
+int getcwd(char *, int);
+uint64 gettime(void);
// ulib.c
int stat(const char*, struct stat*);
diff --git a/user/usys.pl b/user/usys.pl
index 01e426e..fab2333 100755
--- a/user/usys.pl
+++ b/user/usys.pl
@@ -36,3 +36,5 @@ sub entry {
entry("sbrk");
entry("sleep");
entry("uptime");
+entry("getcwd");
+entry("gettime");