![]() |
![]() |
|
|
readline C++ Wrapper
Author / Автор: Sergey SatskiyPublication date / Опубликовано: 09.05.2006
|
// The wrapper is in a single header file.
#include "SReadline.h"
using namespace swift;
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main( int argc, char ** argv )
{
// First parameter is a file name to save / load the
// commands history. The second is a max number of stored commands
// Both parameters could be ommited. In this case the history size
// will be 64 and no save/restore operations will be performed.
SReadline Reader( "/tmp/.testhist", 32 );
// Prepare the list of my own completers
vector< string > Completers;
// The following is supported:
// - "identifiers"
// - special identifier %file - means to perform a file name completion
Completers.push_back( "command1 opt1" );
Completers.push_back( "command1 opt2" );
Completers.push_back( "command1 opt3 %file" );
Completers.push_back( "command2 opt4" );
Completers.push_back( "command2 opt5 %file %file" );
// Now register the completers.
// Actually it is possible to re-register another set at any time
Reader.RegisterCompletions( Completers );
// Now we can ask user for a line
string UserInput;
bool EndOfInput( false );
for ( ; ; )
{
// The last parameter could be ommited
UserInput = Reader.GetLine( "Please input your command> ", EndOfInput );
if ( EndOfInput )
{
cout << "End of the session. Exiting." << endl;
break;
}
cout << "User input: '" << UserInput << "'." << endl;
cout << "Press Ctrl+D for gracefull exit" << endl;
}
// The history could be saved to an arbitrary file at any time
Reader.SaveHistory( "/tmp/BackupFileJustInCase" );
// The history could be cleared
Reader.ClearHistory();
// And the history could be loaded at any time
Reader.LoadHistory( "/tmp/BackupFileJustInCase" );
return 0;
}
|
The following dialog with a user can take place:
[swift@swifthome examples]# ./SReadlineExample Please input your command> command command1 command2 Please input your command> command1 opt opt1 opt2 opt3 Please input your command> command1 opt2 User input: 'command1 opt2'. Press Ctrl+D for gracefull exit Please input your command> End of the session. Exiting. [swift@swifthome examples]# |
The TAB key was pressed at the lines 2 and 4 to get the supported commands completions.
Notes:
Suppose somebody wants to have the following container to store the completers:
typedef pair< string, int > Element; typedef list< Element > Container; |
(Here the int type is taken just to simplify the story)
In this case it will be required to provide a way to convert the Element into a string. That could be done as follows:
struct MyElement : public Element { operator string () const { return first; } MyElement( const string & Arg1, int Arg2 ) : Element( Arg1, Arg2 ) {} }; |
Then
typedef list< MyElement > MyContainer;
|
And now
. . . MyContainer Completers; Completers.push_back( MyElement( "command1 opt1", 1 ) ); Completers.push_back( MyElement( "command1 opt2", 2 ) ); Completers.push_back( MyElement( "command1 opt3 %file", 3 ) ); Completers.push_back( MyElement( "command2 opt4", 4 ) ); Completers.push_back( MyElement( "command2 opt5 %file %file", 5 ) ); . . . Reader.RegisterCompletions( Completers ); . . . |
A similar approach could be useful in case if the command processing function pointers (or a functional equivalent) and commands variations are stored in the same container.
The wrapper doxygen documentation, the source code of the example given above and the source code of a more complicated example with more complicated completions container elements are available at the author's web site http://satsky.spb.ru
The wrapper relies on the following libraries:
Be aware that the SReadline wrapper class is not thread safe. The main reason is the nature of the underlying readline library. It is based on a pure C interface and one session of the library usage supposes unpredictable number of calls of the callback functions. Nevertheless the wrapper is still useful as soon as many applications do not need to have a user input concurrently from different threads.
It is also not garanteed that the wrapper works correctly in case of using it from many compilation units. It is caused by the fact that data are located in a header file inside an anonymous name space. So each compilation unit will have its own copy of the data. This problem can be resolved by moving the data declarations into a separate compilation unit.
The code has been tested on gcc 3.4.4 and Linux.
Be aware that the libreadline 4.3 has a memory leak. It leaks each time when SIGWINCH signal comes. As soon as the readline was updated to 5.1 no leaks were found.
Most probably the code will work perfectly without any changes on other UNIX platforms.
There are at least two alternatives: