I have been asked to write an interview piece for @coder_story ( https://coderstory.io/ ), which I'm really happy about. ๐Ÿ˜„

I think that the best thing I could do for their readers would be to rebuild an old concept and live tweet about my building processes! ๐Ÿ‘จโ€๐Ÿ’ป

Follow Along ๐Ÿ‘‡๐Ÿป

The project I'm going to be revisiting was actually my first attempt at HTML, written just before I started University ๐ŸŽ“ in 2014.

I had just gotten into Linux ๐Ÿง, and wanted to play with HTML, so created an amateurish terminal-themed 'blog':


My old concept was essentially just a novel way of navigating pages and displaying text. ๐Ÿคท๐Ÿปโ€โ™€๏ธ

The markup was formatted very badly, and the site offers minimal interactivity. It's also waaay to inspired by The Matrix! That said, it still holds fondness in my heart.

I'd like the new version to be more interactive - there should be a handful of 'programs' โš™๏ธ (functions) the user can 'execute' and a 'filesystem' ๐Ÿ’พ (just json) that they can navigate.

This is a small project so doesn't need much preparation, but I sketched my ideas on paper:

My @jekyllrb site uses a layout for all new pages that generates the meta tags and general boilerplate. I've done the necessary setup that will allow me to start coding.

The page is here:  https://jetholt.com/terminal/ 

Essentially, I'll be starting with this:

When I'm building, I focus on core functionality first and visuals later, as a working product is better than a useless pretty one.

As such, I'll be using some fairly boring markup with textboxes for the interface and work on the javascript for now.

First step is to set things up so that we can print to the console when the user hits enter. It's a simple first step but a necessary one. I wanted to use the DOM's cloneNode(true) but it would have created duplicate id's. ๐Ÿ™…โ€โ™€๏ธ

Next step: Parsing input and executing it. โœŒ

Now implemented rudimentary "parsing" and executing of user command.

I was going to define a bunch of global commands like `var echo = function(); `, and then have a switch statement to determine the string, but realised it would be easier to lookup properties from objects.

My terminal has a live clock so that I can see what time I ran a command. I was getting tired of seeing the clock be out of date ๐Ÿ• so I added code to make this live ๐Ÿ•–. The timeout at the end runs twice a second to avoid accidental skips.

Next: The 'filesystem'. ๐Ÿ’พโœŒ๏ธ

I'm choosing to write the 'filesystem' portion next because it'll be required for many of the commands I want to write

I'm going to represent it as a set of nested JSON objects with properties like 'name', 'type' (directory, file,...), 'date','protections','contents','owner'...

By the way, I'm pushing my code live every time I tweet, follow along here:

๐Ÿ‘‰  https://jetholt.com/terminal/ 

I have some of my 'filesystem' ๐Ÿ’ฝ in my HTML file and some in my JS file. The stuff in the HTML file lets me use Jekyll to 'pull in' the metadata about my blog posts when the page generates. I'll be adding more here later, this is just a proof of concept.

I'm back after getting food ๐Ÿฒ and am working on implementing a basic form of `cd`.

When you're working on a project, you will often realise that a decision was not thought out; Have the `content` of a directory as an array is hard to work with. An object works much better. ๐Ÿ™ˆ

There were definitely more improvements to be made with how I was creating the file system. ๐Ÿ‘Ž๐Ÿป

I've switched over to some object orientation to reduce code repetition and have made sure that directories have `.` and `..` set in their contents to make provenance much easier. ๐Ÿ‘๐Ÿป

I wrote a helper function to parse a path like `../blog` or `~/blog` or `/home/jetroid/blog` and return it's FilesystemObject (from the tweet above โ˜๐Ÿป).

This will make writing `cd` that much easier (and probably for other functions too!) ๐Ÿค–

Wrote a function to do the opposite of the above.

Looks simple, but I had a bug in my while loop for a while and I kept infinitely looping and crashing my browser. ๐Ÿง ๐Ÿค”

Those functions worked to implement `cd` and (the most basic version of) `ls`! ๐ŸŽ‰

Just for fun, let's do wget next! ๐Ÿ˜„

I'm having trouble with CORS right now blocking me from downloading files with my wget command, so I've set up  https://jetroidcors.herokuapp.com/  using  https://github.com/Rob--W/cors-anywhere โ€ฆ which should hopefully solve the problem. ๐Ÿ‘Œ

Hurray! `wget` is now working too! ๐Ÿ™Œ (Or at least, the most basic functionality is working - I still need to update the text and check for bugs.)

I'm able to download files from both  http://coderstory.io  and  http://jetholt.com  (the two links I tested). ๐Ÿ˜Š

It's late here now - 1:30AM, so I'm going to be going to sleep.

Some stats:

๐Ÿ•– 7 hours
โŒจ 314 lines of code
๐Ÿฆ 20 tweets (including this one)
๐Ÿ’พ 7 git commits
๐Ÿค– 5 semi-implemented commands

See you in the morning! ๐Ÿ˜…

I didn't make it for the morning, but I'm back now and working hard. ๐Ÿ™

I realised I hadn't mentioned that I was going to be mimicking my current terminal rather than the more classic matrix-like  http://hackertyper.com/  style.

A little bit of css and I've got 'the look'. ๐Ÿ‘Œ

Added some fluff to make it look more like you just logged into a neckbeard-admin's server. ๐Ÿ˜…

Obviously today I've broken protocol by focusing on front end visual rather than functionality, but the white background was giving me eye strain. ๐Ÿ˜ณ

I went down a rabbit-hole ๐Ÿ‡๐Ÿ•ณ to get wget looking realistic. The command bloated up to 88 lines! ๐Ÿคฏ

I also went down a ๐Ÿ‡๐Ÿ•ณ to figure out how to record a gif on my laptop - but my laptop is too old and slow ๐Ÿข so I had to ask my friend to help. ๐Ÿ˜…

Back again after eating ๐Ÿœ and I've implemented a `shutdown` command.

I wasn't able to find anywhere to copy some of the shutdown text so I had to write my own from memory. To bulk it up I added plenty of jokes. ๐Ÿ˜…

I was hoping to be able to reuse โ™ป some of the code I had written for  https://jetholt.com/hacking/ , but I found that the code I had grown more complex than I remembered. ๐Ÿ˜ถ

So I just used it for inspiration after reading through and understanding it. ๐Ÿ˜…

I need to write lots of text to the screen repeatedly, so I loop through the text backwards, creating a chain of nested functions. The first function created is the last executed. ๐Ÿค”

I'm sure there are better ways (using the Promise API?) but I'm more confident doing this. ๐Ÿ˜…

Wasn't able to post a sign-off email last night because the WiFi in my apartment in Kuala Lumpur was being shaky.

Some stats from yesterday:

๐Ÿ•” 5 hours
โŒจ 256 lines of code
๐Ÿฆ 6 tweets (not including this one)
๐Ÿ’พ 2 git commits
๐Ÿ’“17 posted emojis

Today, I will be focussing on commands relating to users, permissions, and identity; such as 'su', 'sudo', 'who', and 'whoami'. ๐Ÿ”’๐Ÿ‘ฅ

I'll probably have to learn a bit about how permissions and groups work, and I'll share that process here too. โœŒ

But first, some quick code to make sure I can `cat` the HTML of one of my blog posts.

It's unlikely that the user accesses every blog post, so I decided to represent my blog posts in the filesystem as files with the content of "blogurl(<link-to-blogpost>)" rather than the actual content to reduce the downloaded bytes. โฌ‡

I request the HTML with AJAX when we cat.


I know:

Each unix file has a set of permissions for the owner, the group, and for everyone else. These are whether a user in each category can Read, Write, or eXecute it.

This is represented in a couple of ways...:

rwxrwxrwx format and 777 format


In the rwxrwxrwx style format, each rwx shows the permissions of owner/group/others. If the letter (eg r = read) is there (rather than a dash), then they have the permission.

So rwxr---- means the owner can do everything ๐ŸŒ, groups only read ๐Ÿ“–, nothing for everyone else๐Ÿ‘Ž.


There's also the 777 style format, where again each of these three numbers represents permission of owner/group/others.

7 means the same as 'rwx' and 0 means '---'.

I'm not sure about the other numbers ๐Ÿค”, but I know you can add them to make other combos, like binary.


For example, I don't know if 4 is read or execute. It's something like this

4 = read or execute?
2 = write
1 = execute or read?

But I'm not sure which is the correct order. ๐Ÿค”

3 = 1+2 = write + (execute or read)
5 = 4+1 = read + execute
6 = 4+2 = (read or execute) + write


What I mean by this that 100 in binary is either equal to 4 or 1 depending on if you read it left to right or right to left (endianness). The most common reading is 100 = 4.

This makes me suspect that
4 = read
2 = write
1 = execute

But I'd need to look it up.


Bringing it all together, I suspect:

0 = no permission
1 = execute
2 = write
3 = write + execute
4 = read
5 = read + execute
6 = read + write
7 = read + write + execute

...and I just looked it up and that's correct. Hurray for figuring things out! ๐Ÿฅณ


Thinking about it, I think the thing I need to learn more about the Unix Permission System is about groups.

I don't know:

๐Ÿค” How to find out what groups a user is in
๐Ÿค” What groups are responsible for other than permissions
๐Ÿค” What groups a typical user is in


The nice thing about writing out what you need to know like this is that these are basically questions I can throw into google...

Sure enough:

16:57:44 [email protected]:0~/projects/jetroid.github.io$ groups
power wheel input storage users

I was working my way through implementing permissions functions, when I came upon a question:

What if the permissions is ---rwx--- ?

I can't do anything with it because I'm the owner, but can do everything because of my group? ๐Ÿค”

Which takes precedence?๐Ÿ‘

I find using the correct technical terms (like 'precedence') useful when talking about programming.

Not only will you be able to communicate clearly with others, but you can search for answers more easily.

I would have found it hard to find this particular answer otherwise! ๐Ÿ˜…

I'm a little behind with the tweets, but I've made a ton of progress..!

Newly implemented commands:

๐Ÿ“’ls -al (honestly this took the most time)

Here's a little demo of sudo and su, as well as a few of the other commands.

Note that as `jetroid` chown was denied, but it was allowed as both a `sudo` command (using !!), and with `root`.

It looks so much like my terminal right now that I keep trying to use my shortcuts. ๐Ÿค

And this is a pretty good point to wrap up for today!

๐Ÿ•” 7 working hours
โŒจ 423 lines of code
๐Ÿฆ 17 tweets (including this one)
๐Ÿ’พ 2 git commits
๐Ÿค– 9 new commands
๐Ÿ’ช2 improved commands

I'm getting very close to the functionality I want this thing to have now! โœจ

I've been pretty busy today, so I haven't managed to get much done here.

A command I've implemented (inspired by  http://uni.xkcd.com , which seems to be broken (for me?) right now) is `display`๐Ÿ–ฅ, which when ran against one of my blog posts, displays the text and images. ๐Ÿ˜€

I also implemented read/write permissions for use with cat, wget, and others (when I implement them!).

`jetroid` can't read /etc/shadow nor write to / ๐Ÿ™…โ€โ™‚๏ธ, but root can do both โœ….

You'll see the precedence described at  https://unix.stackexchange.com/questions/134332/precedence-of-user-and-group-owner-in-file-permissions โ€ฆ quite clearly in lines 212-217. ๐Ÿ˜

I just encountered a bug and it stumped me for a while - I think it'd be useful for @coder_story readers if I explain my debugging process too!

I was implementing `rm` and ran it on a file and I got

rm: cannot remove '': Is a directory

Uhh, what? But it's a file!?

I was confused, so added logs to the `rm` function (blue circle), then reran my command. I saw that `rm` was being called as if I had first specified the root `/` (green) by writing blank (orange).

I suspected multiple-spaces in the middle of a command, so I tested it (red).

rm was being called on `/` because:

1) If you're spliting on a character and you have a two of that character one after they other you get an empty string.

ie 'rm <filename>'.split(' ')` becomes:

2) the path '' (empty string) defaults to root (`/`)

We can solve that first point pretty easily by sanitising our input a little better, and replacing repeated spaces with a single space, then trimming off all whitespace to the left or right of what the user wrote.

It's a pretty simple fix, all things considered. ๐Ÿ‘

But there was also a second 'bug' - did you notice it?

The natural whitespace collapsing is affecting the 'past terminal entry' in an undesired way! Multiple spaces got represented as a single space! ๐Ÿคฆโ€โ™‚๏ธ

To fix this, we can wrap it in a <pre> tag or use CSS white-space:pre;

`rm` is now implemented! ๐ŸŽ‰

We're coming to the end of this little project now:

1.) One final `hacker` command
2.) Replace the command textbox with more realistic typing using event binding
3.) Add a 'Made By' + explanation to the lower corner, like with  https://jetholt.com/hacking/ 

Wi-Fi has been out in my apartment all day again. ๐Ÿ˜’

The `hacker` command I mentioned turns the colorscheme from the default back into what I used with  https://jetholt.com/old-terminal/ !

It's very simple - just adds the hacker class to the body and overrides css.

It makes me nostalgic!


Implementing more realistic typing took a while, and I ended up taking a different approach than I had expected.

I was planning on binding all kinds of different keypress, keyup, events and correlating keys to letters. It was a very convoluted method that didn't work well.


Instead, it was far easier to add a 1px by 1px <input> tag (with all kinds of styles to make it nearly invisible) in the upper left corner.

Size increased to 10px by 10px for demonstration.

(Extension used is  http://getcssscan.com  by @gvrizzo)

Then all it takes is a little function to take the data from that input tag and display it where I want, which I call onkeyup and oninput. A final onclick to the whole body to autofocus on the 1px by 1px text input completes the illusion. ๐Ÿง™โ€โ™‚๏ธ

And it works really well! ๐Ÿ˜„

There are so many nice thing about using the native <input> element rather than hacking together something with event handlers - for one, it automatically brings up the keyboard on mobiles, which you can't do otherwise. For another, it's a lot simpler!

Think outside the box! ๐Ÿคช

...and I just finished adding the 'Made By' widget!

This was really easy as I could copy it from  https://jetholt.com/hacking/  - not only does it create a consistent style, but it saves a lot of time!

I'd say that about wraps up the project! ๐Ÿ‘

Check it out here:
๐Ÿ‘‰  https://jetholt.com/terminal/  ๐Ÿ‘ˆ

Keep an eye out for my @coder_story interview, coming soon at  https://coderstory.io ! โœŒ

Some final statistics:

๐Ÿ•– 25 hours (how many composing tweets? ๐Ÿ˜)
โŒจ 1223 lines of code
๐Ÿฆ 60 tweets (including this one)
๐Ÿ’พ 15 git commits
๐Ÿค– 18 commands
๐Ÿคช Too many emojis...

Crazy ride! Thanks for following along!


You can follow @JetroidMakes.


Tip: mention @threader_app on a Twitter thread with the keyword โ€œcompileโ€ to get a link to it.

Enjoy Threader? Sign up.