Building a Custom Terminal from Scratch: Devlog #1

Building a Custom Terminal from Scratch: Devlog #1

2 28 37
calendar_today agoschedule3 min read
— Originally published at medium.com

Purpose

I recently got interested in low-level programming and microcontrollers. For the purpose of exploring the area and gaining some experience, I started building my own bash-like terminal. The language of my choosing—unsurprisingly—was C++, as it is commonly used in low-level systems.

I made specific design choices and trade-offs to overcome challenges, which I am going to share in the next few devlogs.

Why did I ditch strings for raw char pointers?

My first task was seemingly simple: separate the input into an array.

Normally, I would have just used a built-in function to split the input. However, since I wanted this to be a challenge that would push me to think at a low level, I decided to opt out of built-in methods. In fact, throughout the entire process, I actively dodged the C++ libraries designed to make a dev’s life easier.

Going back to the question, I first need to explain the difference between strings and char pointers in C++. If you have any experience with programming languages, you already know the string, or text, type. A string is actually just a list of characters, and that is how it is stored under the hood.

The std::string type is just a wrapper around this char list. In fact, C doesn’t even have a native "string" type. Instead, to handle text, you need to define a char array. This is how software works fundamentally, and I went a step further with this approach by using char pointers.

Char pointers just store the memory address of this char list, which is manually allocated by the developer. But that brings up a problem: whenever the char pointer goes out of scope or is destroyed, it only frees the memory space allocated to store the address of the array. The array itself still exists in memory, which causes a memory leak. Because of this, I have to manually call free() (or delete[]) to empty that allocated memory.

I just thought managing this myself would be a great way to better understand how computers work, even though the standard string type normally handles this automatically.

What about scalability?

Soon, I noticed a major issue. Creating these char pointers in various locations throughout the code felt way too disorganized. I also needed to keep track of the maximum size I allocated for each char pointer to hold, since they don't have a size() function. If that allocated space is exceeded, it can create troublesome, hard-to-debug errors. You are basically writing into an unknown space in memory, which is like playing with fire.

My solution was to create a carr (char array) struct, and equip it with functions to expand, delete, and initialize itself.

I also overloaded some operators, such as =, to directly assign these char pointers. Overloading is simply changing the behavior of an operator for the interactions of that specific struct or class.

A small touch

It is true that I wanted to create a kind of bash clone, but I still didn't want a one-to-one copy. So, I decided to create my own command names.

That is why when I coded the features to get the current directory and move between directories, I chose "here" and "go" rather than "pwd" and "cd." It was just a little flavor I wanted to add.

Of course, when I was coding those commands, I still avoided using standard modern libraries like <filesystem> and went for <dirent.h> and <unistd.h>. They were a bit harder to use, but they gave me the exact low-level systems exposure I was looking for.

The switch

So far, I have written about how I tried to avoid C++ features and chose C-style alternatives instead. Well, that begs the question: why not just switch to C entirely?

My initial reasoning for using C++ was simply because I didn’t know C. However, this project actually created a huge opportunity for me to finally learn C, and I took it.

I thought to myself, "How hard could it be since I already coded it in a C-like style?" Well, it turned out to be quite a challenge.

First of all, remember how I added functions to my carr struct and overloaded its operators? Well, that is not possible in C. Because C doesn't support member functions or operator overloading, I had to recode every single place where I manipulated strings—which was a lot.

In the end, though, I gained complete control over my variables and how I manage them. And honestly, that was the whole point.


If you would like to connect with me, you can check out my socials:

2 Comments

1 vote
1
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

Tuesday Coding Tip 02 - Template with type-specific API

Jakub Neruda - Mar 10

Just completed another large-scale WordPress migration — and the client left this

saqib_devmorph - Apr 7

Tuesday Coding Tip 06 - Explicit template instantiation

Jakub Neruda - Apr 7

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelskiverified - Apr 23
chevron_left
2.1k Points67 Badges
13Posts
15Comments
8Connections
A student passionate about programming and AI who likes to write about these topics.

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!