2

I'm using linenoise with a c command line style program. The standard way of using linenoise is to do a simple

char* line;

line = linenoise("prompt: ");

// Use the line in the app

free(line);

This presents the prompt, and then the user can enter a line, with great editing capabilities including history and hint management. The software is nice, and the code documentation is pretty good.

What I was wondering is, using the linenoise api, is there a way to "preset" the line buffer so that a default line is displayed and editable by the user? Perhaps using the asynch api?

6
  • Aside: in a Windows console program, an editable history of entries for fgets(..., stdin) is available, in the same way as the command line. It persists across each run of the program. Have you tried that in your environment? Commented Feb 9 at 20:07
  • I am working in a linux environment so am unfamiliar with Windows console programs. Commented Feb 9 at 20:20
  • But have you explored the behaviour of fgets()? Commented Feb 9 at 20:23
  • Unfortunately, fgets() doesn't provide the line editing capabilities like using the arrow keys to edit the text as in Linenoise or readline. Because of this, I moved on from using fgets() to provide a more user intuitive and friendly interface for line editing. Commented Feb 9 at 20:46
  • Gnu readline supports adding a string to history then fetching it wth ctrl-p Commented Feb 9 at 21:28

2 Answers 2

1

Linenoise does not have an API function to prefill the input buffer with a default string. The standard usage:

char *line = linenoise("prompt: ");

always starts with an empty buffer for the user to edit. Neither the synchronous nor the asynchronous API provides a way to preset the line buffer.

Sign up to request clarification or add additional context in comments.

4 Comments

OP says "great editing capabilities including history" so is the history exposed to the caller? Or is the linenoise() interface all you get – the one and only function?
History is managed by linenoise, and is accessed during line input with up and down arrows. History is saved in a text file that can be accessed if needed by other methods.
@MarkS. so can you overwrite that file before you call linenoise()?
I haven't tried this, but it would probably work to preload a certain history. This would require the user to press the up arrow to access a particular line loaded in the history file. Doesn't really work for my use case.
0

The linenoise api is minimal. Preloading the buffer to display a default input string is not possible without either changing the source, or fiddling with tricks that may or may not provide for the use case. Indeed, one thought that I had was to emulate keystrokes using uinput to load the desired default value into the input of linenoise.

For a better solution, I looked more closely at the historically available GNU Readline library. The api is extensive and actually illustrates the support of this preloading of the buffer that I need. Here is a copy of the sample program provided on the first page of the GNU Readline documentation under Other Resources (rl.c)

/*
 * rl - command-line interface to read a line from the standard input
 *      (or another fd) using readline.
 *
 * usage: rl [-p prompt] [-u unit] [-d default] [-n nchars]
 */

/* Copyright (C) 1987-2009 Free Software Foundation, Inc.

   This file is part of the GNU Readline Library (Readline), a library for
   reading lines of text with interactive input and history editing.

   Readline is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   Readline is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Readline.  If not, see <http://www.gnu.org/licenses/>.
*/

#if defined (HAVE_CONFIG_H)
#  include <config.h>
#endif

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>

#ifdef HAVE_STDLIB_H
#  include <stdlib.h>
#else 
extern void exit();
#endif

#ifdef HAVE_LOCALE_H
#  include <locale.h>
#endif

#if defined (READLINE_LIBRARY)
#  include "posixstat.h"
#  include "readline.h"
#  include "history.h"
#else
#  include <sys/stat.h>
#  include <readline/readline.h>
#  include <readline/history.h>
#endif

extern int optind;
extern char *optarg;

#if !defined (strchr) && !defined (__STDC__)
extern char *strrchr();
#endif

static char *progname;
static char *deftext;

static int
set_deftext ()
{
  if (deftext)
    {
      rl_insert_text (deftext);
      deftext = (char *)NULL;
      rl_startup_hook = (rl_hook_func_t *)NULL;
    }
  return 0;
}

static void
usage()
{
  fprintf (stderr, "%s: usage: %s [-p prompt] [-u unit] [-d default] [-n nchars]\n",
        progname, progname);
}

int
main (argc, argv)
     int argc;
     char **argv;
{
  char *temp, *prompt;
  struct stat sb;
  int opt, fd, nch;
  FILE *ifp;

  progname = strrchr(argv[0], '/');
  if (progname == 0)
    progname = argv[0];
  else
    progname++;

#ifdef HAVE_SETLOCALE
  setlocale (LC_ALL, "");
#endif

  /* defaults */
  prompt = "readline$ ";
  fd = nch = 0;
  deftext = (char *)0;

  while ((opt = getopt(argc, argv, "p:u:d:n:")) != EOF)
    {
      switch (opt)
    {
    case 'p':
      prompt = optarg;
      break;
    case 'u':
      fd = atoi(optarg);
      if (fd < 0)
        {
          fprintf (stderr, "%s: bad file descriptor `%s'\n", progname, optarg);
          exit (2);
        }
      break;
    case 'd':
      deftext = optarg;
      break;
    case 'n':
      nch = atoi(optarg);
      if (nch < 0)
        {
          fprintf (stderr, "%s: bad value for -n: `%s'\n", progname, optarg);
          exit (2);
        }
      break;
    default:
      usage ();
      exit (2);
    }
    }

  if (fd != 0)
    {
      if (fstat (fd, &sb) < 0)
    {
      fprintf (stderr, "%s: %d: bad file descriptor\n", progname, fd);
      exit (1);
    }
      ifp = fdopen (fd, "r");
      rl_instream = ifp;
    }

  if (deftext && *deftext)
    rl_startup_hook = set_deftext;

  if (nch > 0)
    rl_num_chars_to_read = nch;

  temp = readline (prompt);

  /* Test for EOF. */
  if (temp == 0)
    exit (1);

  printf ("%s\n", temp);
  exit (0);
}

Focus on the functionality provided by set_deftext() that provides exactly what I needed.

Happy coding!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.