diff --git a/Fennix Userspace.code-workspace b/Fennix Userspace.code-workspace
index de72dea0..b8f074ad 100644
--- a/Fennix Userspace.code-workspace
+++ b/Fennix Userspace.code-workspace
@@ -18,7 +18,8 @@
"userspace/libs/libm",
"devcontainer",
"userspace/libs",
- "userspace/apps/test"
+ "userspace/apps/test",
+ "userspace/apps/usr"
]
}
}
diff --git a/Userspace/apps/usr/Makefile b/Userspace/apps/usr/Makefile
new file mode 100644
index 00000000..8ea8a10f
--- /dev/null
+++ b/Userspace/apps/usr/Makefile
@@ -0,0 +1,9 @@
+MAKE_TARGETS := build clean
+DIRECTORIES := $(sort $(dir $(wildcard ./*/)))
+
+.PHONY: $(MAKE_TARGETS) $(DIRECTORIES)
+
+$(MAKE_TARGETS): $(DIRECTORIES)
+
+$(DIRECTORIES):
+ $(MAKE) -C $@ $(MAKECMDGOALS)
diff --git a/Userspace/apps/usr/mdview/Makefile b/Userspace/apps/usr/mdview/Makefile
new file mode 100644
index 00000000..90165364
--- /dev/null
+++ b/Userspace/apps/usr/mdview/Makefile
@@ -0,0 +1,33 @@
+default:
+ $(error Do not run this Makefile directly!)
+
+S_SOURCES = $(shell find ./ -type f -name '*.S')
+C_SOURCES = $(shell find ./ -type f -name '*.c')
+CXX_SOURCES = $(shell find ./ -type f -name '*.cpp')
+
+OBJ = $(S_SOURCES:.S=.o) $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o)
+
+FILENAME = $(notdir $(shell pwd))
+WARNCFLAG = -Wall -Wextra
+
+build: $(FILENAME).elf
+ cp $(FILENAME).elf $(WORKSPACE_DIR)/out/usr/bin/$(FILENAME)
+
+$(FILENAME).elf: $(OBJ)
+ $(info Linking $@)
+ $(CC) $(LDFLAGS) $(SYSROOT) $(OBJ) -o $@
+
+%.o: %.c $(HEADERS)
+ $(info Compiling $<)
+ $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@
+
+%.o: %.cpp $(HEADERS)
+ $(info Compiling $<)
+ $(CXX) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fexceptions -c $< -o $@ -fno-rtti
+
+%.o: %.S
+ $(info Compiling $<)
+ $(AS) -o $@ $<
+
+clean:
+ rm -f $(OBJ) $(FILENAME).elf
diff --git a/Userspace/apps/usr/mdview/mdview.c b/Userspace/apps/usr/mdview/mdview.c
new file mode 100644
index 00000000..d1e7c86a
--- /dev/null
+++ b/Userspace/apps/usr/mdview/mdview.c
@@ -0,0 +1,419 @@
+/*
+ This file is part of Fennix Userspace.
+
+ Fennix Userspace is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ Fennix Userspace is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fennix Userspace. If not, see .
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define ANSI_COLOR_RED "\x1b[31m"
+#define ANSI_COLOR_GREEN "\x1b[32m"
+#define ANSI_COLOR_YELLOW "\x1b[33m"
+#define ANSI_COLOR_BLUE "\x1b[34m"
+#define ANSI_COLOR_MAGENTA "\x1b[35m"
+#define ANSI_COLOR_CYAN "\x1b[36m"
+#define ANSI_COLOR_WHITE "\x1b[37m"
+#define ANSI_COLOR_BLACK "\x1b[30m"
+#define ANSI_COLOR_RESET "\x1b[0m"
+
+#define ANSI_BG_BLACK "\x1b[40m"
+#define ANSI_BG_RED "\x1b[41m"
+#define ANSI_BG_GREEN "\x1b[42m"
+#define ANSI_BG_YELLOW "\x1b[43m"
+#define ANSI_BG_BLUE "\x1b[44m"
+#define ANSI_BG_MAGENTA "\x1b[45m"
+#define ANSI_BG_CYAN "\x1b[46m"
+#define ANSI_BG_GRAY "\x1b[47m"
+#define ANSI_BG_DARK_GRAY "\x1b[100m"
+
+#define ANSI_BOLD "\x1b[1m"
+#define ANSI_UNDERLINE "\x1b[4m"
+#define ANSI_STRIKETHROUGH "\x1b[9m"
+#define ANSI_HIGHLIGHT "\x1b[43m"
+
+void print_horizontal_rule()
+{
+ int width;
+ struct winsize ws;
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
+ width = ws.ws_col;
+ for (int i = 0; i < width - 2; i++)
+ printf("-");
+}
+
+void print_admonition(const char *type, const char *title, const char *content)
+{
+ if (strcmp(type, "warning") == 0)
+ {
+ printf(ANSI_BG_YELLOW);
+ printf(ANSI_COLOR_BLACK);
+ printf("! Warning: %s %s\n", title, content);
+ }
+ else if (strcmp(type, "note") == 0)
+ {
+ printf(ANSI_BG_BLUE);
+ printf(ANSI_COLOR_WHITE);
+ printf("i Note: %s %s\n", title, content);
+ }
+ else if (strcmp(type, "tip") == 0)
+ {
+ printf(ANSI_BG_GREEN);
+ printf(ANSI_COLOR_WHITE);
+ printf("? Tip: %s %s\n", title, content);
+ }
+ else if (strcmp(type, "important") == 0)
+ {
+ printf(ANSI_BG_RED);
+ printf(ANSI_COLOR_BLACK);
+ printf("! Important: %s %s\n", title, content);
+ }
+ else if (strcmp(type, "caution") == 0)
+ {
+ printf(ANSI_BG_MAGENTA);
+ printf(ANSI_COLOR_WHITE);
+ printf("! Caution: %s %s\n", title, content);
+ }
+ else if (strcmp(type, "danger") == 0)
+ {
+ printf(ANSI_BG_RED);
+ printf(ANSI_COLOR_BLACK);
+ printf("! Danger: %s %s\n", title, content);
+ }
+ printf(ANSI_COLOR_RESET);
+}
+
+void print_formatted_text(const char *text)
+{
+ bool bold = false;
+ bool underline = false;
+ bool inline_code = false;
+ bool strikethrough = false;
+ bool highlight = false;
+
+ for (int i = 0; text[i] != '\0'; ++i)
+ {
+ if (strncmp(&text[i], "!!!", 3) == 0)
+ {
+ char admonition_type[20] = "";
+ char admonition_title[256] = "";
+ int type_start = i + 4;
+ int type_end = -1;
+ int title_start = -1;
+ int title_end = -1;
+ int content_start = -1;
+
+ for (int j = type_start; text[j] != '\0' && text[j] != ' ' && text[j] != '"'; ++j)
+ {
+ type_end = j;
+ }
+ if (type_end != -1)
+ {
+ strncpy(admonition_type, &text[type_start], type_end - type_start + 1);
+ admonition_type[type_end - type_start + 1] = '\0';
+ i = type_end + 1;
+ }
+
+ if (text[i] == ' ' && text[i + 1] == '"')
+ {
+ title_start = i + 2;
+ for (int j = title_start; text[j] != '\0' && text[j] != '"'; ++j)
+ {
+ title_end = j;
+ }
+ if (title_end != -1)
+ {
+ strncpy(admonition_title, &text[title_start], title_end - title_start + 1);
+ admonition_title[title_end - title_start + 1] = '\0';
+ i = title_end + 1;
+ }
+ }
+
+ content_start = i + 1;
+ print_admonition(admonition_type, admonition_title, text + content_start);
+ return;
+ }
+
+ if (text[i] == '`')
+ {
+ if (!inline_code)
+ {
+ printf(ANSI_BG_MAGENTA);
+ printf(ANSI_UNDERLINE);
+ printf(ANSI_BOLD);
+ inline_code = true;
+ }
+ else
+ {
+ printf(ANSI_COLOR_RESET);
+ inline_code = false;
+ }
+ }
+ else if (text[i] == '*' && text[i + 1] == '*')
+ {
+ if (!bold)
+ {
+ printf(ANSI_BOLD);
+ bold = true;
+ i++;
+ }
+ else
+ {
+ printf(ANSI_COLOR_RESET);
+ bold = false;
+ i++;
+ }
+ }
+ else if (text[i] == '_' && text[i + 1] == '_')
+ {
+ if (!underline)
+ {
+ printf(ANSI_UNDERLINE);
+ underline = true;
+ i++;
+ }
+ else
+ {
+ printf(ANSI_COLOR_RESET);
+ underline = false;
+ i++;
+ }
+ }
+ else if (text[i] == '~' && text[i + 1] == '~')
+ {
+ if (!strikethrough)
+ {
+ printf(ANSI_STRIKETHROUGH);
+ strikethrough = true;
+ i++;
+ }
+ else
+ {
+ printf(ANSI_COLOR_RESET);
+ strikethrough = false;
+ i++;
+ }
+ }
+ else if (text[i] == '=' && text[i + 1] == '=')
+ {
+ if (!highlight)
+ {
+ printf(ANSI_HIGHLIGHT);
+ highlight = true;
+ i++;
+ }
+ else
+ {
+ printf(ANSI_COLOR_RESET);
+ highlight = false;
+ i++;
+ }
+ }
+ else if (text[i] == '[')
+ {
+ int start_link_text = i + 1;
+ int end_link_text = -1;
+ int start_link_url = -1;
+ int end_link_url = -1;
+
+ for (int j = start_link_text; text[j] != '\0'; ++j)
+ {
+ if (text[j] == ']')
+ {
+ end_link_text = j;
+ if (text[j + 1] == '(')
+ {
+ start_link_url = j + 2;
+
+ for (int k = start_link_url; text[k] != '\0'; ++k)
+ {
+ if (text[k] == ')')
+ {
+ end_link_url = k;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ if (end_link_text != -1 && start_link_url != -1 && end_link_url != -1)
+ {
+
+ char link_text[256];
+ strncpy(link_text, &text[start_link_text], end_link_text - start_link_text);
+ link_text[end_link_text - start_link_text] = '\0';
+
+ char link_url[256];
+ strncpy(link_url, &text[start_link_url], end_link_url - start_link_url);
+ link_url[end_link_url - start_link_url] = '\0';
+
+ printf(ANSI_UNDERLINE);
+ printf(link_text);
+ printf(ANSI_COLOR_RESET);
+ printf("(%s)", link_url);
+ i = end_link_url;
+ continue;
+ }
+ }
+ else if (text[i] == '<')
+ {
+ while (text[i] != '\0' && text[i] != '>')
+ {
+ i++;
+ }
+ if (text[i] == '>')
+ {
+ continue;
+ }
+ }
+ else if (strncmp(&text[i], "---\n", 4) == 0 || strncmp(&text[i], "***\n", 4) == 0 || strncmp(&text[i], "_________________\n", 18) == 0)
+ {
+ print_horizontal_rule();
+ i += 2;
+ continue;
+ }
+ else if (strncmp(&text[i], "- [x] ", 6) == 0)
+ {
+ printf("[X] ");
+ i += 5;
+ continue;
+ }
+ else if (strncmp(&text[i], "- [ ] ", 6) == 0)
+ {
+ printf("[ ] ");
+ i += 5;
+ continue;
+ }
+ else
+ {
+ printf("%c", text[i]);
+ }
+ }
+ printf(ANSI_COLOR_RESET);
+}
+
+void process_markdown_file(const char *filename)
+{
+ FILE *file = fopen(filename, "r");
+ if (file == NULL)
+ {
+ perror("fopen");
+ return;
+ }
+
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t read;
+
+ while ((read = getline(&line, &len, file)) != -1)
+ {
+ if (strncmp(line, "# ", 2) == 0)
+ {
+ printf(ANSI_BOLD);
+ printf(ANSI_UNDERLINE);
+ printf("%s", line);
+ printf(ANSI_COLOR_RESET);
+ }
+ else if (strncmp(line, "## ", 3) == 0)
+ {
+ printf(ANSI_BOLD);
+ printf("%s", line);
+ printf(ANSI_COLOR_RESET);
+ }
+ else if (strncmp(line, "### ", 4) == 0)
+ {
+ printf(ANSI_UNDERLINE);
+ printf("%s", line);
+ printf(ANSI_COLOR_RESET);
+ }
+ else if (strncmp(line, "#### ", 5) == 0)
+ {
+ printf(ANSI_BOLD);
+ printf(ANSI_UNDERLINE);
+ printf(ANSI_COLOR_RED);
+ printf("%s", line);
+ printf(ANSI_COLOR_RESET);
+ }
+ else if (strncmp(line, "##### ", 6) == 0)
+ {
+ printf(ANSI_BOLD);
+ printf(ANSI_COLOR_MAGENTA);
+ printf("%s", line);
+ printf(ANSI_COLOR_RESET);
+ }
+ else if (strncmp(line, "###### ", 7) == 0)
+ {
+ printf(ANSI_UNDERLINE);
+ printf(ANSI_COLOR_BLUE);
+ printf("%s", line);
+ printf(ANSI_COLOR_RESET);
+ }
+ else if (strncmp(line, "```", 3) == 0)
+ {
+ printf(ANSI_BG_DARK_GRAY);
+ printf(ANSI_COLOR_WHITE);
+ printf("%s", line);
+ while ((read = getline(&line, &len, file)) != -1 && strncmp(line, "```", 3) != 0)
+ {
+ printf("%s", line);
+ }
+ printf("%s", line);
+ printf(ANSI_COLOR_RESET);
+ }
+ else if (strncmp(line, "> ", 2) == 0 && strncmp(line, ">> ", 3) != 0)
+ {
+ printf(ANSI_COLOR_WHITE);
+ printf(ANSI_BG_GRAY " " ANSI_BG_DARK_GRAY "%s", line + (strncmp(line, ">>", 2) == 0 ? 2 : 1));
+ printf(ANSI_COLOR_RESET);
+ }
+ else if (strncmp(line, ">\n", 2) == 0)
+ {
+ printf(ANSI_COLOR_WHITE);
+ printf(ANSI_BG_GRAY " \n");
+ printf(ANSI_COLOR_RESET);
+ }
+ else
+ {
+ print_formatted_text(line);
+ }
+ }
+
+ fclose(file);
+ if (line)
+ free(line);
+}
+
+void print_usage()
+{
+ printf("Usage: mdview \n");
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ {
+ fprintf(stderr, "mdview: invalid arguments\n");
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ process_markdown_file(argv[1]);
+ return 0;
+}
diff --git a/Userspace/apps/usr/mdview/test.md b/Userspace/apps/usr/mdview/test.md
new file mode 100644
index 00000000..9b4d4043
--- /dev/null
+++ b/Userspace/apps/usr/mdview/test.md
@@ -0,0 +1,436 @@
+## Admonitions
+
+> :warning: **Warning:** This is a warning.
+
+> :memo: **Note:** This is a note.
+
+> :bulb: **Tip:** This is a tip.
+
+## GitHub Admonitions
+
+> [!NOTE]
+> This is a note.
+
+> [!TIP]
+> This is a tip.
+
+> [!IMPORTANT]
+> This is important.
+
+> [!WARNING]
+> This is a warning.
+
+> [!CAUTION]
+> This is a caution.
+
+## python-markdown Admonitions
+
+!!! note "This is a note."
+ Hello World!
+
+!!! danger "This is a danger."
+ Hello World!
+
+!!! important "This is important."
+ Hello World!
+
+!!! warning "This is a warning."
+ Hello World!
+
+!!! caution "This is a caution."
+ Hello World!
+
+[The section below is an excerpt of the original.](https://www.markdownguide.org/cheat-sheet/)
+The original content is licensed under a CC BY-SA 4.0 license. The original content can be found at the link above.
+
+## Headings
+
+# Heading level 1
+## Heading level 2
+### Heading level 3
+#### Heading level 4
+##### Heading level 5
+###### Heading level 6
+
+## Alternate Syntax
+
+Heading level 1
+===============
+
+Heading level 2
+---------------
+
+---
+
+## Paragraphs
+
+I really like using Markdown.
+
+I think I'll use it to format all of my documents from now on.
+
+---
+
+## Line Breaks
+
+This is the first line.
+And this is the second line.
+
+---
+
+## Emphasis
+
+I just love **bold text**.
+
+I just love __bold text__.
+
+Love**is**bold
+
+Italicized text is the *cat's meow*.
+
+Italicized text is the _cat's meow_.
+
+A*cat*meow
+
+This text is ***really important***.
+
+This text is ___really important___.
+
+This text is __*really important*__.
+
+This text is **_really important_**.
+
+This is really***very***important text.
+
+---
+
+## Blockquotes
+
+> Dorothy followed her through many of the beautiful rooms in her castle.
+
+> Dorothy followed her through many of the beautiful rooms in her castle.
+>
+> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood.
+
+> Dorothy followed her through many of the beautiful rooms in her castle.
+>
+>> The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood.
+
+> #### The quarterly results look great!
+>
+> - Revenue was off the chart.
+> - Profits were higher than ever.
+>
+> *Everything* is going according to **plan**.
+
+---
+
+## Lists
+
+List 1
+
+1. First item
+2. Second item
+3. Third item
+4. Fourth item
+
+List 2
+
+1. First item
+1. Second item
+1. Third item
+1. Fourth item
+
+List 3
+
+1. First item
+8. Second item
+3. Third item
+5. Fourth item
+
+List 4
+
+1. First item
+2. Second item
+3. Third item
+ 1. Indented item
+ 2. Indented item
+4. Fourth item
+
+List 5
+
+- First item
+- Second item
+- Third item
+- Fourth item
+
+List 6
+
+* First item
+* Second item
+* Third item
+* Fourth item
+
+List 7
+
++ First item
++ Second item
++ Third item
++ Fourth item
+
+List 8
+
+- First item
+- Second item
+- Third item
+ - Indented item
+ - Indented item
+- Fourth item
+
+List 9
+
+- 1968\. A great year!
+- I think 1969 was second best.
+
+---
+
+## Paragraphs
+
+* This is the first list item.
+* Here's the second list item.
+
+ I need to add another paragraph below the second list item.
+
+* And here's the third list item.
+
+* This is the first list item.
+* Here's the second list item.
+
+ > A blockquote would look great below the second list item.
+
+* And here's the third list item.
+
+---
+
+## Code Blocks
+
+1. Open the file.
+2. Find the following code block on line 21:
+
+
+
+ Test
+
+
+3. Update the title to match the name of your website.
+
+---
+
+## Images
+
+1. Open the file containing the Linux mascot.
+2. Marvel at its beauty.
+
+ 
+
+3. Close the file.
+
+---
+
+## Code
+
+At the command prompt, type `nano`.
+
+``Use `code` in your Markdown file.``
+
+
+
+
+
+
+---
+
+## Horizontal Rules
+
+***
+
+---
+
+_________________
+
+---
+
+## Links
+
+My favorite search engine is [Duck Duck Go](https://duckduckgo.com).
+
+My favorite search engine is [Duck Duck Go](https://duckduckgo.com "The best search engine for privacy").
+
+
+
+
+
+I love supporting the **[EFF](https://eff.org)**.
+
+This is the *[Markdown Guide](https://www.markdownguide.org)*.
+
+See the section on [`code`](#code).
+
+In a hole in the ground there lived a hobbit. Not a nasty, dirty, wet hole, filled with the ends
+of worms and an oozy smell, nor yet a dry, bare, sandy hole with nothing in it to sit down on or to
+eat: it was a [hobbit-hole][1], and that means comfort.
+
+[1]: "Hobbit lifestyles"
+
+---
+
+## Escaping Characters
+
+\* Without the backslash, this would be a bullet in an unordered list.
+
+\\ test
+
+\` test
+
+\* test
+
+\_ test
+
+\{ test
+
+\} test
+
+\[ test
+
+\] test
+
+\< test
+
+\> test
+
+\( test
+
+\) test
+
+\# test
+
+\+ test
+
+\- test
+
+\. test
+
+\! test
+
+\| test
+
+---
+
+## HTML
+
+This **word** is bold. This word is italic.
+
+## Tables
+
+| Syntax | Description |
+| ----------- | ----------- |
+| Header | Title |
+| Paragraph | Text |
+
+| Syntax | Description |
+| --- | ----------- |
+| Header | Title |
+| Paragraph | Text |
+
+| Syntax | Description | Test Text |
+| :--- | :----: | ---: |
+| Header | Title | Here's this |
+| Paragraph | Text | And more |
+
+---
+
+## Fenced Code Blocks
+
+```
+{
+ "firstName": "John",
+ "lastName": "Smith",
+ "age": 25
+}
+```
+
+```json
+{
+ "firstName": "John",
+ "lastName": "Smith",
+ "age": 25
+}
+```
+
+---
+
+## Footnotes
+
+Here's a simple footnote,[^1] and here's a longer one.[^bignote]
+
+[^1]: This is the first footnote.
+
+[^bignote]: Here's one with multiple paragraphs and code.
+
+ Indent paragraphs to include them in the footnote.
+
+ `{ my code }`
+
+ Add as many paragraphs as you like.
+
+---
+
+## Heading IDs
+
+# Heading level 1 {#custom-id}
+## Heading level 2 {#custom-id}
+### Heading level 3 {#custom-id}
+#### Heading level 4 {#custom-id}
+##### Heading level 5 {#custom-id}
+###### Heading level 6 {#custom-id}
+
+---
+
+## Strikethrough
+
+~~The world is flat.~~ We now know that the world is round.
+
+---
+
+## Task Lists
+
+- [x] Write the press release
+- [ ] Update the website
+- [ ] Contact the media
+
+---
+
+## Emoji
+
+Gone camping! :tent: Be back soon.
+
+That is so funny! :joy:
+
+---
+
+## Highlight
+
+I need to highlight these ==very important words==.
+
+---
+
+## Subscript and Superscript
+
+H~2~O
+
+X^2^
+
+---
+
+## Disabling Automatic URL Linking
+
+`http://www.example.com`