Search code examples
cshellgetlinestdio

In a get_line implementation, how do I allow the user to move their cursor?


So, I'm currently working on a small shell.
I get user input with my own getline implementation, which is repeatedly calling fgetc(stdin) and realloc-ing to read a line.

How do I allow the user to use the left and right keys to move the cursor in the input he's currently writing?

The function:

#define LINE_BUFSIZE 256
static char *get_line(void)
{
    char  *line   = malloc(LINE_BUFSIZE);
    char  *linep  = line;
    size_t lenmax = LINE_BUFSIZE;
    size_t len    = lenmax;
    int    c;

    if (!line)
        return NULL;

    for (;;) {
        c = fgetc(stdin);
        if (c == EOF)
            break;

        if (--len == 0) {
            len = lenmax;
            lenmax *= 3;
            lenmax /= 2;
            char *linen = realloc(linep, lenmax);

            if (!linen) {
                free(linep);
                return NULL;
            }

            line  = linen + (line - linep);
            linep = linen;
        }

        if ((*line++ = c) == '\n')
            break;
    }
    *line = '\0';
    return linep;
}

Solution

  • There are basically three ways to do this. In decreasing order of effort:

    1. Put terminal in raw mode to be able to receive characters such as Ctrl-B and so on, then process them. That's reinventing the wheel, really, and unless you're willing to spend lots of time for nothing (unless for learning), don't go there.
    2. As this has been solved a hundred times over, and is needed by many programs, a library named termcap has been developed to abstract the terminal capabilities. If you want your program to not just work in an xterm, but also on other terminals, this is the way to go.
    3. Use the GNU readline library. From its manual:

      #include <stdio.h>
      #include <readline/readline.h>
      #include <readline/history.h>
      
      char *
      readline (const char *prompt);
      

    DESCRIPTION

    readline will read a line from the terminal and return it, using prompt as a prompt. If prompt is NULL or the empty string, no prompt is issued. The line returned is allocated with malloc(3); the caller must free it when finished. The line returned has the final newline removed, so only the text of the line remains.

    readline offers editing capabilities while the user is entering the line. By default, the line editing commands are similar to those of emacs. A vi-style line editing interface is also available.