Editing input


If you’re working on Linux or Mac you’ll notice some weird behaviour when you use the arrow keys to attempt to edit your input.

  1. Lispy Version 0.0.0.0.3
  2. Press Ctrl+c to Exit
  3. lispy> hel^[[D^[[C

Using the arrow keys is creating these weird characters ^[[D or ^[[C, rather than moving the cursor around in the input. What we really want is to be able to move around on the line, deleting and editing the input in case we make a mistake.

On Windows this behaviour is the default. On Linux and Mac it is provided by a library called editline. On Linux and Mac we need to replace our calls to fputs and fgets with calls to functions this library provides.

If you’re developing on Windows and just want to get going, feel free to skip to the end of this chapter as the next few sections may not be relevant.

Using Editline

The library editline provides two functions we are going to use called readline and add_history. This first function, readline is used to read input from some prompt, while allowing for editing of that input. The second function add_history lets us record the history of inputs so that they can be retrieved with the up and down arrows.

We replace fputs and fgets with calls to these functions to get the following.

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <editline/readline.h>
  4. #include <editline/history.h>
  5. int main(int argc, char** argv) {
  6. /* Print Version and Exit Information */
  7. puts("Lispy Version 0.0.0.0.1");
  8. puts("Press Ctrl+c to Exit\n");
  9. /* In a never ending loop */
  10. while (1) {
  11. /* Output our prompt and get input */
  12. char* input = readline("lispy> ");
  13. /* Add input to history */
  14. add_history(input);
  15. /* Echo input back to user */
  16. printf("No you're a %s\n", input);
  17. /* Free retrieved input */
  18. free(input);
  19. }
  20. return 0;
  21. }

We have included a few new headers. There is #include <stdlib.h>, which gives us access to the free function used later on in the code. We have also added #include <editline/readline.h> and #include <editline/history.h> which give us access to the editline functions, readline and add_history.

Instead of prompting, and getting input with fgets, we do it in one go using readline. The result of this we pass to add_history to record it. Finally we print it out as before using printf.

Unlike fgets, the readline function strips the trailing newline character from the input, so we need to add this to our printf function. We also need to delete the input given to us by the readline function using free. This is because unlike fgets, which writes to some existing buffer, the readline function allocates new memory when it is called. When to free memory is something we cover in depth in later chapters.

Compiling with Editline

If you try to compile this right away with the previous command you’ll get an error. This is because you first need to install the editline library on your computer.

  1. fatal error: editline/readline.h: No such file or directory #include <editline/readline.h>

On Mac the editline library comes with Command Line Tools. Instructions for installing these can be found in Chapter 2. You may still get an error about the history header not being found. In this case remove the line #include <editline/history.h>, as this header may not be required.

On Linux you can install editline with sudo apt-get install libedit-dev. On Fedora you can use the command su -c "yum install libedit-dev*"

Once you have installed editline you can try to compile it again. This time you’ll get a different error.

  1. undefined reference to `readline'
  2. undefined reference to `add_history'

This means that you haven’t linked your program to editline. This linking process allows the compiler to directly embed calls to editline in your program. You can make it link by adding the flag -ledit to your compile command, just before the output flag.

  1. cc -std=c99 -Wall prompt.c -ledit -o prompt

Run it and check that now you can edit inputs as you type them in.

It’s still not working!

Some systems might have slight variations on how to install, include, and link to editline. For example on Arch linux the editline history header is histedit.h. If you are having trouble search online and see if you can find distribution specific instructions on how to install and use the editline library. If that fails search for instructions on the readline library. This is a drop-in replacement for editline. On Mac it can be installed using HomeBrew or MacPorts.