Practice: Build a Command-Line Tool

Introduction

This lesson is a mix between a practice session and a coding challenge. View it as a follow-along tutorial!

There won’t be test cases or tasks that you have to solve. We’ll code the application together, step by step. Before we build each part of the application, you’re encouraged to try to write the code on your own, based on the description.

Problem statement

We want to write the backbone of a command-line application. The application will feature several commands, and we need to add more commands in the future. Therefore, we should register the commands dynamically, without having to write a bunch of if and else cases for every single one. Moreover, we want to have multiple names for the same command. We should be allowed to register aliases for the same command without duplicating the code.

The application could be anything, but to make the example easier to follow, we’ll use a theme of a word processor.

  • The commands can be cut, copy, paste, and other similar commands.
  • The same action can have multiple names. For example, we may want to register the same command twice, once as search and once as find.
  • In the future, we may add a lot of complex word processing commands, so the application must be easily extensible.
  • Each command may have a different number of arguments or arguments of different types. We need to standardize the header of the command function handlers and have a uniform header.

The application will continuously read commands from stdin. It will execute each command and then wait for another. To stop, we’ll use the end keyword. When the user types the end command, the application stops executing, meaning that we completed all the necessary processing.

Example input and output

Input:

cut 5 7
copy 3
paste 8
find abcd 3 6
search abcd 3 8

The meaning of the arguments isn’t defined. We don’t care about them, we’re just building the backbone right now! We must build the architecture to pass the arguments to the commands, without caring about what the arguments may mean.

But, for example, we may assume that find abcd 3 6 may mean finding the string abcd starting from pages 3–6, inside the currently opened document.

If an unknown command is received, skip it.

Implementation

This lesson is a follow-along coding session. Feel free to have a code editor open and implement the code in parallel!

Reading user input

Inside main, we have to read and process the user input.

We have to read an unknown number of commands, so we do it in while(1). We read the input using fgets, which is a safe function as we can specify the dimension of the buffer and avoid buffer overflow errors.

The input is in the form of command arguments_list\n, and we have to split it to isolate the command and the arguments.

For splitting, we use strtok. First, with a space (" ") as a delimiter to separate command and arguments_list\n. We add a second delimiter of "\n" for the case when there are no arguments, and the input looks like "command\n" as we need to remove the new line character (line 11).

Then, we use "\n" as a delimiter to remove the new line character from the arguments (line 12). If there were no arguments, args would be empty, as there wouldn’t be anything to split.

Get hands-on with 1400+ tech skills courses.