Back to home page
Back to previous page

Author: R. Koucha
Last update: 02-May-2010

SourceForge.net Logo
Support This Project


CMDLINE



Introduction
Download
Installation
Installation from the sources with cmake
Installation from the sources with a custom script
Installation from the DEB binary package
Installation from the RPM binary package
Packaging
Generation of a DEB binary package
Generation of a RPM binary package
Generation of a ZIP tar file
API
Services
cmdline_new
cmdline_delete
cmdline_interact
cmdline_split
cmdline_set_debug_level
cmdline_set_echo
cmdline_set_histo
cmdline_histo_list
cmdline_histo_get
cmdline_function_key
cmdline_restore_term_settings
cmdline_set_term_settings
Return value
Example
About the author
Related links



Introduction

cmdline is a command line manager API much more simple and light than GNU readline. It does not use any database or configuration files like terminfo, termcap and so on... It is destined to command line oriented programs.

The API manages the command line and all the related goodies such as the line edition, the history of commands, the function keys and the character echoing.

The keys which are managed by the service are emacs-like:
Move the cursor one char forward
Same as CTRL f
Move the cursor one char backward
Same as CTRL b
Move the cursor to the end of line
Move the cursor to the beginning of line
Same as CTRL a
Same as CTRL e
Remove the character on which the cursor is
Same as SUPPR.  But when the command line is empty, it triggers a "End Of File" on the input.
Remove the character on the left side of the cursor
Display the previous command line of the history (i.e. go up in the history)
Display the following command line of the history (i.e. go down in the history)
Display the oldest command line of the history
Display the newest command line of the history
Triggers  the  auto-completion  or  displays  spaces  depending on the way the CMDLINE object has been initialized.


Download

CMDLINE can be downloaded from this page in one of the following three ways:
The include directory contains the file cmdline.h in which are defined the services and data structures of the API.
The lib directory contains the implementation of the library libcmdline.so which is destined to be dynamically linked to the user program.
The man directory contains the on-line manuals.


Installation

Installation from the sources with cmake

The installation from the sources supposes that cmake is installed on your Linux machine.

Unpack the tar compressed file cmdline-xxx.tgz into a directory. This will create a sub-directory called cmdline-xxx with the source files of the program:

$ tar xvfz cmdline-xxx.tgz

Go into the newly created directory:

$ cd cmdline-xxx

While building the makefiles, you can choose where the files will be installed with the environment variable CMAKE_INSTALL_PREFIX. If not defined, the installation defaults to /usr/local.

$ cmake . -DCMAKE_INSTALL_PREFIX=<path_of_installation>

Then the build is triggered by invoking make:

$ make

And the installation is done specifying the install target:

$ make install

It is possible to trigger a cleanup of the built objects with the clean target:

$ make clean

If your MANPATH variable is correctly set, CMDLINE's online manual can be displayed:

$ man 3 cmdline


Installation from the sources with a custom script

The installation from the sources supposes that cmake is installed on your Linux machine.

Unpack the tar compressed file cmdline-xxx.tgz into a directory. This will create a sub-directory called cmdline-xxx with the source files of the program:

$ tar xvfz cmdline-xxx.tgz

Go into the newly created directory:

$ cd cmdline-xxx

Make sure the file cmdline_install.sh has the execute permission:

$ chmod +x cmdline_install.sh

Launch the script cmdline_install.sh to get the help:

$ ./cmdline_install.sh -h

Usage: cmdline_install.sh [-d install_root_dir] [-P DEB | RPM] [-B] [-I] [-A] [-h]

             -d : Installation directory (default: /usr/local)
             -P : Generate a DEB or RPM package
             -B : Build the software
             -I : Install the software
             -A : Generate an archive of the software (sources)
             -h : this help

Under root identity, launch the installation by passing '-I' and optionnaly '-d' to specify an installation directory different than /usr/local. For example, for an installation in /usr/local, type:

$ sudo ./cmdline_install.sh -I

For an installation in '/usr', type;

$ sudo ./cmdline_install.sh -I -d /usr

If your MANPATH variable is correctly set, CMDLINE's online manual can be displayed:

$ man 3 cmdline

Installation from the DEB binary package

The files are installed via the command:

$ sudo dpkg -i cmdline-xxx.deb

Installation from the RPM binary package

The files are installed via the command:

$ sudo rpm -i cmdline-xxx.rpm


Packaging

The packaging process supposes that cmake is installed on your Linux machine.

Generation of a DEB binary package

Unpack the tar compressed file cmdline-xxx.tgz into a directory. This will create a sub-directory called cmdline-xxx with the source files of the program:

$ tar xvfz cmdline-xxx.tgz

Go into the newly created directory:

$ cd cmdline-xxx

Make sure the file cmdline_install.sh has the execute permission:

$ chmod +x cmdline_install.sh

Launch the script cmdline_install.sh to get the help:

$ ./cmdline_install.sh -h

Usage: cmdline_install.sh [-d install_root_dir] [-P DEB | RPM] [-B] [-I] [-A] [-h]

             -d : Installation directory (default: /usr/local)
             -P : Generate a DEB or RPM package
             -B : Build the software
             -I : Install the software
             -A : Generate an archive of the software (sources)
             -h : this help

Under root identity, launch the installation by passing '-P DEB' and optionnaly '-d' to specify an installation directory different than /usr/local. For example, for a package which will be installed in /usr/local, type:

$ sudo ./cmdline_install.sh -P DEB

For an installation in /usr, type;

$ sudo ./cmdline_install.sh -P DEB -d /usr

Generation of a RPM binary package

Unpack the tar compressed file cmdline-xxx.tgz into a directory. This will create a sub-directory called cmdline-xxx with the source files of the program:

$ tar xvfz cmdline-xxx.tgz

Go into the newly created directory:

$ cd cmdline-xxx

Make sure the file cmdline_install.sh has the execute permission:

$ chmod +x cmdline_install.sh

Launch the script cmdline_install.sh to get the help:

$ ./cmdline_install.sh -h

Usage: cmdline_install.sh [-d install_root_dir] [-P DEB | RPM] [-B] [-I] [-A] [-h]

             -d : Installation directory (default: /usr/local)
             -P : Generate a DEB or RPM package
             -B : Build the software
             -I : Install the software
             -A : Generate an archive of the software (sources)
             -h : this help

Under root identity, launch the installation by passing '-P RPM' and optionnaly '-d' to specify an installation directory different than /usr/local. For example, for a package which will be installed in /usr/local, type:

$ sudo ./cmdline_install.sh -P RPM

For an installation in /usr, type;

$ sudo ./cmdline_install.sh -P RPM -d /usr

Generation of a ZIP tar file

Unpack the tar compressed file cmdline-xxx.tgz into a directory. This will create a sub-directory called cmdline-xxx with the source files of the program:

$ tar xvfz cmdline-xxx.tgz

Go into the newly created directory:

$ cd cmdline-xxx

Make sure the file cmdline_install.sh has the execute permission:

$ chmod +x cmdline_install.sh

Launch the script cmdline_install.sh to get the help:

$ ./cmdline_install.sh -h

Usage: cmdline_install.sh [-d install_root_dir] [-P DEB | RPM] [-B] [-I] [-A] [-h]

             -d : Installation directory (default: /usr/local)
             -P : Generate a DEB or RPM package
             -B : Build the software
             -I : Install the software
             -A : Generate an archive of the software (sources)
             -h : this help

Under root identity, launch the installation by passing '-A':

$ sudo ./cmdline_install.sh -A


API

Services

The API is composed with the following functions:

#include <cmdline.h>

roof_ctx_t *cmdline_new(cmdline_param_t *param);
void cmdline_delete(cmdline_t *pInst);

char *cmdline_interact(cmdline_t *pInst);
int cmdline_split(unsigned char *str, unsigned int *ac, unsigned char ***av, unsigned char comment);

int cmdline_set_debug_level(cmdline_t *pInst, int dbg);
int cmdline_set_echo(cmdline_t *pInst, int echo);

int cmdline_set_histo(cmdline_t *pInst, int histo);
void cmdline_histo_list(cmdline_t *pInst, void (* list)(unsigned char *item, unsigned int idx));

char *cmdline_histo_get(cmdline_t *pInst, unsigned int idx);

cmdline_fn_key_t
cmdline_function_key(cmdline_t *pInst, cmdline_fn_key_t function_key);

int cmdline_restore_term_settings(cmdline_t *pInst);
int cmdline_set_term_settings(cmdline_t *pInst);

cmdline_new

The service cmdline_new() creates a CMDLINE object. This is a handle that will  be  passed  to all the subsequent calls to the API through the parameter pInst.  The configuration of CMDLINE is passed through the parameter param of type cmdline_param_t:

       typedef struct
       {

         // --- Command line
         unsigned int   line_len;       // Maximum length of the command line

         // --- I/O
         int            non_blocking;   // !0, if I/O in non blocking mode
         int            fd_in;          // Input file descriptor
         int            fd_out;         // Output file descriptor

         // --- History
         unsigned int   histo_len;      // History size
         int            auto_or_sp;     // Auto-completion or spaces for TAB
       #define CMDLINE_TAB_AUTO_COMPLETE   0  // Provide ’auto_complete’ callback (may be NUL)
       #define CMDLINE_TAB_SPACES          1  // Provide the number of ’spaces’ for a TAB (may be 0)

         union
         {
           void         (*auto_complete) // Callback called upon TAB keystroke to trigger auto-completion
                             (
                             void          *ctx,               // User context
                             const char    *cmd_line,          // Current displayed command line
                             unsigned int  *cursor,            // IN: Current cursor position
                                                               // OUT: New cursor position
                             char         **completed_cmd_line // New command line if any (NULL otherwise)
                             );
           unsigned int spaces;         // Number of spaces to display for a TAB
         } tab;
         char           histo_shortcut; // Character to call an history entry (0 if not activated)

         void          *ctx;            // Any user provate information for future reference
       } cmdline_param_t;

line_len
specifies the maximum size in bytes of the command line to manage.

non_blocking
is set to 0 if  the  interactions  on the input are blocking. This means that the service cmdline_interact() will block the caller until a command line is available. If non_blocking is set to a value different than 0, the service cmdline_interact() returns NULL  with  errno set  to  EAGAIN  if  data are not available on the input. The command line will be available only when the cmdline_interact() will return a value different than NULL. This mode is  useful  when the caller needs to manage interactions with multiple file descriptors (e.g. sockect connections, data from terminals, data from pipes...). It will typically invoke the system  call select() to be warned when data are made available on some file descriptors. Then, if one of the concerned file descriptors is managed by a CMDLINE object,  cmdline_interact() is called to get the data.

fd_in
is the file descriptor of the input from which we get the command lines.

fd_out
is the file descriptor of the output to which are echoed the command lines.

       histo_len
         specifies the number of recorded command lines in the history of commands.

       auto_or_sp
specifies  the  significant field in the tab union. If set to CMDLINE_TAB_AUTO_COMPLETE, the caller will need to provide a callback function that will be called upon TAB keystroke.  The field  ctx  is  passed  back to the callback as first parameter.  The callback may be set to NULL if the auto-completion is not required.  If set to CMDLINE_TAB_SPACES, the caller  will need  to  specify  the number of space characters that will be output upon TAB keystroke. It can be set to 0, if TAB key is to be ignored.

histo_shortcut
specifies the character that will trigger the call to an history entry. Each time this character  will appear at first position on the command line, it will trigger a call to the history entry associated to the index. The index is the following numeric value.

ctx
can be set to any value. It is passed back to the history callback.  Typically,  this  field embeds the address of a user context.

cmdline_delete

The  service cmdline_delete() is the counter part of cmdline_new() as it deallocates a CMDLINE object previously allocated by cmdline_new().

cmdline_interact

The service cmdline_interact() reads a command line on the input managing implicitely the line edition  and  the  history.  The  function is either blocking or non blocking depending on the parameter non_blocking passed to cmdline_new().
The returned buffer is either a pointer on a NUL terminated string inside the service (there is no end of line at the end of the buffer) or a control message.
The maximum length of the returned buffer is the size of the line configured with the field line_len in the structure cmdline_param_t passed to cmdline_new().

The control message begins with two bytes: CMDLINE_CTRL_MSG followed the number of bytes composing the message (i.e. at most 255) and the bytes of the message. The format is described below:

          Byte         1           2       3...
               +----------------+------+-------------------
               |CMDLINE_CTRL_MSG|Lenght| ...Length bytes...
               +----------------+------+-------------------

This is convenient for applications needing to exchange control messages inside the character stream.

To know if it is a line of characters or a control message, the user must check the first byte of the returned buffer: if it is a CMDLINE_CTRL_MSG, then it is a control message otherwise it is a NUL terminated line of characters.

The buffer's content stays available until the next call to cmdline_interact().

cmdline_split


The service cmdline_split() splits a command line str into words pointed by av.  The latter is dynamically allocated and so should be deallocated by the caller when no longer needed. The latest entry is set to NULL. The  number  of  non NULL entries in av is stored into ac.  On input, the caller has the ability to pass a preallocated buffer from the heap by setting av with the address of the buffer and ac with the number of entries minus one in this buffer. The service will enlarge the buffer if necessary.  The caller has the ability to specify in comment the character which is used as comment: all the characters from this character up to the end of the line are ignored. Setting this  parameter  to  0 (i.e. '\0') specifies that comments are not managed. After this call, the passed command line str is altered.

cmdline_set_debug_level

The service cmdline_set_debug_level() sets the debug level to dbg.  The more dbg is  big,  the more  traces will appear. The value 0 cancels the debug mode.  dbg must be greater or equal to 0.

cmdline_set_echo

The service cmdline_set_echo() activates  (parameter  echo  greater  than  0)  or  deactivates (parameter echo equal to 0) the echo of the input characters.

cmdline_set_histo

The  service  cmdline_set_histo()  activates  (parameter  histo greater than 0) or deactivates (parameter histo equal to 0) the history.

cmdline_histo_list

The service cmdline_histo_list() lists all the entries currently present in the  history.  For each entry, the callback list is invoked. It is passed the history entry along with its index. The end of list is denoted when the parameters passed to the callback are NULL.

cmdline_histo_get

The service cmdline_histo_get() returns the history entry associated to the index idx.

cmdline_function_key

The function cmdline_function_key() sets the callback function_key to be invoked when a  function key is pressed. The callback type is:

         typedef const char * (* cmdline_fn_key_t)
                                      (
                                       cmdline_t    *pInst,
                                       unsigned int  fn,
                                       const char   *cmd_line,
                                       unsigned int *cursor
                                      );

The  callback  is  passed  the  parameter  fn  which  contains  the  function  key number CMDLINE_FUNC_KEY_1 to CMDLINE_FUNC_KEY_12, the current command line and the current cursor position  in the command line. The callback returns the new command line to display (NULL if nothing to do) and the new cursor position.

cmdline_restore_term_settings

The service cmdline_restore_term_settings() sets back the  terminal  settings  to  the  values which  were  set before the call to cmdline_new().  This service is typically used in conjunction with cmdline_set_term_settings() when the program needs to fork and execute  a  new  program.  The  service is typically called before forking to avoid the inheritance of the CMDLINE terminal settings by the child process.

cmdline_set_term_settings

The service cmdline_set_term_settings() sets the terminal settings  to  the  values  requested upon  the  call  to  cmdline_new().   This  service is typically used in conjunction with cmdline_restore_term_settings() when the program needs to fork and execute  a  new  program.  The service is typically called after the termination of the child process.

Return value

The function cmdline_new(), returns a CMDLINE object. NULL if error.

The function cmdline_interact(), returns the command line or NULL if error.  In  non  blocking mode,  NULL  may  be  returned  along with errno set to EAGAIN if no data are available on the input.

The function cmdline_split() returns 0 if OK and -1 if error (errno is set).

The function cmdline_histo_get() returns the command line or NULL if error.

The functions cmdline_set_debug_level(), cmdline_set_echo() and cmdline_set_histo() return the previous setting or -1 if error.

The  function cmdline_function_key() returns the previous setting or NULL if no function was set. When NULL is returned, this means an error if errno is set (i.e. different than 0).

The  functions  cmdline_restore_term_settings() and cmdline_set_term_settings() return 0 if OK and -1 if error.

Example

The  following  example  shows  a complete application using the API in blocking mode:

#define _GNU_SOURCE
#include <getopt.h>
#include <unistd.h>
#include <stdio.h>
#include <libgen.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <sys/select.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <cmdline.h>


// ----------------------------------------------------------------------------
// Name   : cmdline_prompt
// Usage  : Prompt on the command line
// ----------------------------------------------------------------------------
static const char *cmdline_prompt = "$$";


// ----------------------------------------------------------------------------
// Name   : cmdline_inst
// Usage  : CMDLINE instance
// ----------------------------------------------------------------------------
static cmdline_t *cmdline_inst;


// ----------------------------------------------------------------------------
// Name   : cmdline_longops
// Usage  : Option on the command line
// ----------------------------------------------------------------------------
static struct option cmdline_longopts[] =
{
  { "debug",      required_argument, NULL, 'd' },
  { "help",       no_argument,       NULL, 'h' },

  // Last entry
  { NULL,         0,                 NULL, 0   }
};




//---------------------------------------------------------------------------
// Name : cmdline_help
// Usage: Display help
//----------------------------------------------------------------------------
static void cmdline_help(char *p)
{
  fprintf(stderr,
          "\n"
          "Usage: %s [<options> ...]\n"
          "\n"
          "Options:\n"
          "\n"
          "\t-d | --debug level : Set debug mode to level\n"
          "\t-h | --help        : This help\n"
          ,
          basename(p)
          );
} // cmdline_help


//----------------------------------------------------------------------------
// Name        : cmdline_is_number
// Description : Check if a string is a number
// Return      : 1, if yes
//               0, if not
//----------------------------------------------------------------------------
static int cmdline_is_number(const unsigned char *str)
{
  while (*str)
  {
    if (!isdigit(*str))
    {
      return 0;
    }
    str ++;
  }

  return 1;
} // cmdline_is_number



static void disp_histo_item
                    (
                    unsigned char  *item,
                    unsigned int    idx
                    )
{
  if (item)
  {
    printf("%u. %s\n", idx, item);
  }
} // disp_histo_item



//---------------------------------------------------------------------------
// Name : function_key
// Usage: Callback for the function key
//----------------------------------------------------------------------------
static const unsigned char *function_key(
                                         cmdline_t           *pInst,
                                         unsigned int         fn,
                                         const unsigned char *cmd_line,
                                         unsigned int *cursor
                                        )
{
char buf[128];

(void)cmd_line;
(void)pInst;

snprintf(buf, sizeof(buf), "\nFunction key %u has been pressed (cursor pos = %u)\n%s ", fn + 1, *cursor, cmdline_prompt);
buf[sizeof(buf) - 1] = '\0';
write(1, buf, strlen(buf));

// New cursor position
*cursor = 4;

return (const unsigned char *)"New cmd_line";
} // function_key



//---------------------------------------------------------------------------
// Name : main
// Usage: Entry point of the program
//----------------------------------------------------------------------------
int main(int ac, char *av[])
{
unsigned int      options;
int               opt;
int               rc;
int               dbg = 0;
cmdline_param_t   params;
unsigned char    *p;

options = 0;

// Parse the command line
optind = 0;
while ((opt = getopt_long(ac, av, "d:h", cmdline_longopts, NULL)) != EOF)
{
  switch(opt)
  {
    case 'd' : // Debug level
    {
      if (!cmdline_is_number((unsigned char *)optarg))
      {
        fprintf(stderr, "The debug level must be a number instead of '%s'\n", optarg);
        rc = 1;
        goto error;
      }
      dbg = atoi(optarg);
      options |= 0x01;
    }
    break;

    case 'h' : // Help
    {
      cmdline_help(av[0]);
      options |= 0x02;
      exit(0);
    }
    break;

    case '?' :
    default :
    {
      cmdline_help(av[0]);
      exit(1);
    }
  } // End switch
} // End while

// Get a CMDLINE context
memset(&params, 0, sizeof(params));
params.line_len       = 128;
params.non_blocking   = 0;
params.fd_in          = 0;
params.fd_out         = 1;
params.histo_len      = 5;
params.auto_or_sp     = CMDLINE_TAB_SPACES;
params.tab.spaces     = 3;
params.histo_shortcut = '!';
cmdline_inst = cmdline_new(&params);
if (NULL == cmdline_inst)
{
  fprintf(stderr, "Unable to allocate a CMDLINE instance (errno = %d)\n", errno);
  rc = 1;
  goto error;
}

// Set debug level
cmdline_set_debug_level(cmdline_inst, dbg);

// Set function key callback
cmdline_function_key(cmdline_inst, function_key);

do
{
  // Display the prompt
  printf("%s ", cmdline_prompt);
  fflush(stdout);
  p = cmdline_interact(cmdline_inst);
  if (p)
  {
    printf("The command line is: <%s>\n", p);
  }
  else
  {
    if (ECONNRESET == errno)
    {
      printf("End of session\n");
      rc = 0;
      break;
    }

    rc = 1;
    break;
  }
} while (p);

// Deallocate the command line instance
cmdline_delete(cmdline_inst);

rc = 0;

error:

return rc;
} // main


The previous example can be compiled as follow:

> gcc test.c -lcmdline
> ./a.out
$$ qwerty
The command line is: <qwerty>
$$ foo bar
The command line is: <foo bar>
$$
Function key 4 has been pressed (cursor pos = 0)
$$ New cmd_line
The command line is: <New cmd_line>
$$ <CTRL D> End of session
>


About the author

The author is an engineer in computer sciences located in France. He is glad to graciously offer this utility under the GPL open source license. He can be contacted at "rachid dot koucha at free dot fr" or you can have a look at his WEB home page.

Related links




Back to home page
Back to previous page