Implementing history in own shell C++ - c++

I am implementing the history command in my own shell, in C++. I am writing it in NonCanonicalMode. I have implemented the up arrow key and down arrow key as well as backspace. I do not know how to start history. Should I use a built in function from one of the C++ libraries?
----EDit
char *buf;
rl_bind_key('\t',rl_abort);//disable auto-complete
while((buf = readline("\n >> "))!=NULL)
{
if (strcmp(buf,"quit")==0)
break;
printf("[%s]\n",buf);
if (buf[0]!=0)
add_history(buf);
}

I have not used NonCanonicalMode but here is how I implemented readline's history in one of my projects.
Maybe it will be of some use to you:
#include <string>
#include <memory>
#include <iostream>
#include <algorithm>
#include <readline/readline.h>
#include <readline/history.h>
// clean up user input by deleting spaces from each end
inline std::string& trim(std::string& s, const char* t = " \t")
{
s.erase(s.find_last_not_of(t) + 1);
s.erase(0, s.find_first_not_of(t));
return s;
}
// smart pointer to clean up memory
// allocated by readline
struct malloc_deleter
{
template <class T>
void operator()(T* p) { std::free(p); }
};
typedef std::unique_ptr<char, malloc_deleter> cstring_uptr;
int main()
{
// this directory needs to exist beforehand
const std::string config_dir = "/home/wibble/.prog";
using_history();
read_history((config_dir + "/.history").c_str());
std::string shell_prompt = "> ";
cstring_uptr input;
std::string line, prev;
input.reset(readline(shell_prompt.c_str()));
// copy input into a std::string
while(input && trim(line = input.get()) != "exit")
{
if(!line.empty())
{
// only add line to history if it is different
// from previous line
if(line != prev)
{
add_history(line.c_str());
write_history((config_dir + "/.history").c_str());
prev = line;
}
// process the input
std::reverse(line.begin(), line.end());
// give relevant output
std::cout << "reply: " << line << '\n';
}
input.reset(readline(shell_prompt.c_str()));
}
}
I don't like that I need to call readline() in two places but I wasn't able to figure how to re-write the loop to avoid it. Maybe I'm missing something simple?
It uses a smart pointer std::unique_ptr with a custom deleter to clean up the buffers that readline allocates using malloc().

Related

how to check a string for specific conditions in C++

following snippet shows a very small part of my current output:
1464: ebfffe4d bl da0 <memcpy#plt>
14bc: ebfffe37 bl da0 <memcpy#plt>
every line from the output refers to a string. What I want to archieve is, that in this
case only memcpy#plt will be printed once. When a string contains "bl" then the name
within <...> should be printed and only once printed, since the name within <...> is the same
in both cases. Is there a way to get this?
My current code looks as follows:
class CallFunction {
private:
vector<string> content;
public:
CallFunction(vector<string> content) {
this->content = content;
}
void print() {
for(string line: content) {
if(line.find("bl") != std::string::npos
&& line.find("<") != std::string::npos) {
cout << line << endl;
}
}
}
};
int main() {
string fileName = "libndkmod.s";
vector<string> content = readFile(fileName);
CallFunction cf = CallFunction(content);
cf.print();
}
Thanks in advance and kind regards!
We can use a unordered_set to deduplicate the substring, if the same substring has already been printed, then skip it.
This step can be extracted into a single method to follow the single responsibility principle, which is more natural than process the duplication in print function, this work is left for you.
We use unordered_set instead of std::set since unordered_set will be quicker to search.
I have changed the for loop from for(string line: content) into for(const string& line: content), then we avoid a copy of the original string, to improve the performance. Basically, we will prefer to use for(const auto& item:...) for objects except primitive types to avoid a copy.
We store string_view which is introduced in c++17 to avoid copy the sub-string, it will save memory since we avoid unnecessarily copy.
#include <iostream>
#include <string>
#include <unordered_set>
#include <vector>
using namespace std;
class CallFunction {
private:
vector<string> content;
public:
CallFunction(vector<string> content) { this->content = content; }
void print() {
std::unordered_set<std::string_view> mem;
for (const string& line : content) {
auto left_pos = line.find("bl");
auto right_pos = line.find(">");
if (left_pos != std::string::npos && right_pos != std::string::npos) {
std::string_view sub_view = {&line.front() + left_pos,
right_pos - left_pos + 1};
if (mem.count(sub_view)) continue;
mem.insert(sub_view);
cout << line << endl;
}
}
}
};
int main() {
string fileName = "libndkmod.s";
vector<string> content = {
"1464: ebfffe4d bl da0 <memcpy#plt>",
"14bc: ebfffe37 bl da0 <memcpy#plt>",
"14bc: ebfffe37 bl da0 <memcmp#plt>"};
CallFunction cf = CallFunction(content);
cf.print();
}
Online demo

separating 2 words from a string

I have done a lot of reading on this topic online, and cannot figure out if my code is working. i am working on my phone with the c4droid app, and the debugger is nearly useless as far as i can tell.
as the title says, i need to separate 2 words out of one input. depending on what the first word is, the second may or may not be used. if i do not need the second word everything is fine. if i need and have the second word it works, or seems to. but if i need a second word but only have the first it compiles, but crashes with an out of range exception.
ActionCommand is a vector of strings with 2 elements.
void splitstring(std::string original)
{
std::string
std::istringstream OrigStream(original);
OrigStream >> x;
ActionCommand.at(0) = x;
OrigStream >> x;
ActionCommand.at(1) = x;
return;
}
this code will separate the words right?
any help would be appreciated.
more of the code:
called from main-
void DoAction(Character & Player, room & RoomPlayerIn)
{
ParseAction(Player, GetAction(), RoomPlayerIn);
return;
}
std::string GetAction()
{
std::string action;
std::cout<< ">";
std::cin>>action;
action = Lowercase(action);
return action;
}
maybe Lowercase is the problem.
std::string Lowercase(std::string sourceString)
{
std::string destinationString;
destinationString.resize(sourceString.size());
std::transform(sourceString.begin(), sourceString.end(), destinationString.begin(), ::tolower);
return destinationString;
)
void ParseAction(Character & Player, std::string CommandIn, room & RoomPlayerIn)
(
std::vector<std::string> ActionCommand;
splitstring(CommandIn, ActionCommand);
std::string action = ActionCommand.at(0);
if (ActionCommand.size() >1)
std::string action2 = ActionCommand.at(1);
skipping some ifs
if (action =="wield")
{
if(ActionCommand.size() >1)
DoWield(action2);
else std::cout<<"wield what??"<<std::endl;
return;
}
and splitstring now looks like this
void splitstring(std::string const &original, std::vector<std::string> &ActionCommand)
{
std::string x;
std::istringstream OrigStream(original);
if (OrigStream >>x)
ActionCommand.push_back(x);
else return;
if (OrigStream>>x)
ActionCommand.push_back(x);
return;
}
#include <sstream>
#include <vector>
#include <string>
std::vector<std::string> ActionCommand;
void splitstring(std::string const &original)
{
std::string x;
std::istringstream OrigStream{ original };
if(OrigStream >> x)
ActionCommand.push_back(x);
else return;
if(OrigStream >> x)
ActionCommand.push_back(x);
}
Another idea would be to use the standard library. You can split a string into tokens (using spaces as dividers) with the following function:
#include <string>
#include <vector>
#include <sstream>
#include <iterator>
inline auto tokenize(const std::string &String)
{
auto Stream = std::stringstream(String);
return std::vector<std::string>{std::istream_iterator<std::string>{Stream}, std::istream_iterator<std::string>{}};
}
Here, the result is created in place by using an std::istream_iterator, which basically stands in for the >> operation in your example.
Warning:
This code needs at least c++11 to compile.

portable usage of boost::locale::transform

I implement a search of a substring in strings and i would like to make this search "accent-nutral" or it might be called rough - if i start search "aba" in "rábano" i am supposed to succeed.
in Find substring in string using locale there is a working answer:
#include <locale>
#include <string>
#include <boost/locale.hpp>
std::string NormalizeString(const std::string & input)
{
std::locale loc = boost::locale::generator()("");
const boost::locale::collator<char>& collator = std::use_facet<boost::locale::collator<char> >(loc);
std::string result = collator.transform(boost::locale::collator_base::primary, input);
return result;
}
The only issue with this solution - transform adds several bytes to the end of string. in my case it is "\x1\x1\x1\x1\x0\x0\x0". Four bytes with 1 and several zero-bytes.
Of course it is easy to erase these bytes but i would not like to rely on such subtle implementation details. (The code is supposed to be cross-platform)
Is there a more reliable way?
As #R. Martinho Fernandes said it looks impossible to implement such a search with boost.
I found the solution in chrome sources. it uses ICU.
// This class is for speeding up multiple StringSearchIgnoringCaseAndAccents()
// with the same |find_this| argument. |find_this| is passed as the constructor
// argument, and precomputation for searching is done only at that timing.
class CStringSearchIgnoringCaseAndAccents
{
public:
explicit CStringSearchIgnoringCaseAndAccents(std::u16string find_this);
~CStringSearchIgnoringCaseAndAccents();
// Returns true if |in_this| contains |find_this|. If |match_index| or
// |match_length| are non-NULL, they are assigned the start position and total
// length of the match.
bool SearchIn(const std::u16string& in_this, size_t* match_index = nullptr, size_t* match_length = nullptr);
private:
std::u16string _find_this;
UStringSearch* _search_handle;
};
CStringSearchIgnoringCaseAndAccents::CStringSearchIgnoringCaseAndAccents(std::u16string find_this) :
_find_this(std::move(find_this)),
_search_handle(nullptr)
{
// usearch_open requires a valid string argument to be searched, even if we
// want to set it by usearch_setText afterwards. So, supplying a dummy text.
const std::u16string& dummy = _find_this;
UErrorCode status = U_ZERO_ERROR;
_search_handle = usearch_open((const UChar*)_find_this.data(), _find_this.size(),
(const UChar*)dummy.data(), dummy.size(), uloc_getDefault(), NULL, &status);
if (U_SUCCESS(status)) {
UCollator* collator = usearch_getCollator(_search_handle);
ucol_setStrength(collator, UCOL_PRIMARY);
usearch_reset(_search_handle);
}
}
CStringSearchIgnoringCaseAndAccents::~CStringSearchIgnoringCaseAndAccents()
{
if (_search_handle) usearch_close(_search_handle);
}
bool CStringSearchIgnoringCaseAndAccents::SearchIn(const std::u16string& in_this, size_t* match_index, size_t* match_length)
{
UErrorCode status = U_ZERO_ERROR;
usearch_setText(_search_handle, (const UChar*) in_this.data(), in_this.size(), &status);
// Default to basic substring search if usearch fails. According to
// http://icu-project.org/apiref/icu4c/usearch_8h.html, usearch_open will fail
// if either |find_this| or |in_this| are empty. In either case basic
// substring search will give the correct return value.
if (!U_SUCCESS(status)) {
size_t index = in_this.find(_find_this);
if (index == std::u16string::npos) {
return false;
}
else {
if (match_index)
*match_index = index;
if (match_length)
*match_length = _find_this.size();
return true;
}
}
int32_t index = usearch_first(_search_handle, &status);
if (!U_SUCCESS(status) || index == USEARCH_DONE) return false;
if (match_index)
{
*match_index = static_cast<size_t>(index);
}
if (match_length)
{
*match_length = static_cast<size_t>(usearch_getMatchedLength(_search_handle));
}
return true;
}
usage:
CStringSearchIgnoringCaseAndAccents searcher(a_utf16_string_what.c_str()));
searcher.SearchIn(a_utf16_string_where)
Even though this is an old question, I decided to post my solution, because it might help someone (or someone can tell me if I am wrong). I used the boost text conversion methods. First I applied the normalization form decomposition (NFD), which gave me separated chars. Then I just filtered those that had the code below 255. Then a simple lower case conversion. It worked for your problem (and for mine), but I am not sure if it applies on every case. Here's the solution:
#include <iostream>
#include <algorithm>
#include <string>
#include <locale>
#include <boost/locale.hpp>
static std::locale loc = boost::locale::generator()("en_US.UTF-8");
std::string NormalizeString(const std::string & input)
{
std::string s_norm = boost::locale::normalize(input, boost::locale::norm_nfd, loc);
std::string s;
std::copy_if(s_norm.begin(), s_norm.end(), std::back_inserter(s), [](unsigned int ch){return ch<256;} );
return boost::locale::to_lower(s, loc);
}
void find_norm(const std::string& input, const std::string& query) {
if (NormalizeString(input).find(NormalizeString(query)) != std::string::npos)
std::cout << query << " found in " << input << std::endl;
else
std::cout << query << " not found in " << input << std::endl;
}
int main(int argc, char *argv[])
{
find_norm("rábano", "aba");
find_norm("rábano", "aaa");
return EXIT_SUCCESS;
}

Constructor of inner class with ifstream member returns invalid fstream

I have a class with an inner class which has a member ifstream. When executing the code throws an AccessViolationException in TestDataHelper::getNextLine() at if(!datafile->eof()).
Debugging showed me that the constructor is working fine. The file is created and the first line is read. But when I check the datafile after the assignment is done (after TestDataHelper helper = TestDataHelper("data.csv");), the datafile becomes an invalid pointer.
The code:
IfstreamTester.h
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
class IfstreamTester
{
public:
IfstreamTester(void);
~IfstreamTester(void);
void doStuff();
private:
std::ifstream file;
class TestDataHelper{
private:
std::ifstream *datafile;
public:
TestDataHelper(std::string filename);
~TestDataHelper();
std::vector<double>* getNextLine();
};
};
IfstreamTester.cpp
#include "IfstreamTester.h"
using namespace std;
IfstreamTester::IfstreamTester(void)
{}
IfstreamTester::~IfstreamTester(void)
{}
void IfstreamTester::doStuff()
{
TestDataHelper helper = TestDataHelper("data.csv");
// for every line in the file
vector<double>* v;
do
{
v = helper.getNextLine();
cout << v << endl;
delete v;
} while(v != NULL);
}
IfstreamTester::TestDataHelper::TestDataHelper(std::string filename)
{
datafile = new ifstream(filename.c_str());
// read the header line
string line;
getline(*datafile, line);
}
vector<double>* IfstreamTester::TestDataHelper::getNextLine()
{
if(!datafile->eof())
{
// readline
string line = "";
getline(*datafile, line);
string delimiter = ",";
vector<double>* res = new vector<double>();
size_t pos = 0;
// and tokenize line into vector of doubles
while ((pos = line.find(delimiter)) != std::string::npos)
{
stringstream conv;
double token;
conv << line.substr(0, pos);
conv >> token;
res->push_back(token);
line.erase(0, pos + delimiter.length());
}
return res;
}
else
{
return NULL;
}
}
IfstreamTester::TestDataHelper::~TestDataHelper()
{
datafile->close();
delete datafile;
}
and the main:
#include "IfstreamTester.h"
void main()
{
IfstreamTester s = IfstreamTester();
s.doStuff();
}
Anyone an idea why this is so?
EDIT: I am using Microsoft Visual Studio 2008 Express
Your compiler isn't doing you any favors. It should be puking, telling you std::ifstream isn't copyable and the default copy-constructor of its containing parent , IfstreamTester, should therefore also be implicitly deleted.
I don't know what toolchain you're using, but clang++ 3.3 and g++ 4.7 both puke on this. Your compiler is apparently allowing it through because of a copy-elision assumption, then failing to actually elide the copy in debug. Either add a copy-constructor that doesn't member copy the std::ifstream member, or declare main() as:
int main()
{
IfstreamTester s;
s.doStuff();
}
The non-standard void main() declaration in your code has me (likely incorrectly) deducing you're using either an MS or Borland toolchain. Whoever is allowing this (both the odd main() and the non-copyable std::ifstream member to be copied), isn't playing by the rules.
while(v != NULL) looks fairly wrong. Also did you mean to write your loop as follows?
vector<double>* v = NULL;
do
{
v = helper.getNextLine();
cout << v << endl;
delete v;
} while(v != NULL);
I tried do compile this using g++ 4.6.3:
I had to add #include <fstream> and change IfstreamTester s = IfstreamTester(); to IfstreamTester s;

How to generate 'consecutive' c++ strings?

I would like to generate consecutive C++ strings like e.g. in cameras: IMG001, IMG002 etc. being able to indicate the prefix and the string length.
I have found a solution where I can generate random strings from concrete character set: link
But I cannot find the thing I want to achieve.
A possible solution:
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
std::string make_string(const std::string& a_prefix,
size_t a_suffix,
size_t a_max_length)
{
std::ostringstream result;
result << a_prefix <<
std::setfill('0') <<
std::setw(a_max_length - a_prefix.length()) <<
a_suffix;
return result.str();
}
int main()
{
for (size_t i = 0; i < 100; i++)
{
std::cout << make_string("IMG", i, 6) << "\n";
}
return 0;
}
See online demo at http://ideone.com/HZWmtI.
Something like this would work
#include <string>
#include <iomanip>
#include <sstream>
std::string GetNextNumber( int &lastNum )
{
std::stringstream ss;
ss << "IMG";
ss << std::setfill('0') << std::setw(3) << lastNum++;
return ss.str();
}
int main()
{
int x = 1;
std::string s = GetNextNumber( x );
s = GetNextNumber( x );
return 0;
}
You can call GetNextNumber repeatedly with an int reference to generate new image numbers. You can always use sprintf but it won't be the c++ way :)
const int max_size = 7 + 1; // maximum size of the name plus one
char buf[max_size];
for (int i = 0 ; i < 1000; ++i) {
sprintf(buf, "IMG%.04d", i);
printf("The next name is %s\n", buf);
}
char * seq_gen(char * prefix) {
static int counter;
char * result;
sprintf(result, "%s%03d", prefix, counter++);
return result;
}
This would print your prefix with 3 digit padding string. If you want a lengthy string, all you have to do is provide the prefix as much as needed and change the %03d in the above code to whatever length of digit padding you want.
Well, the idea is rather simple. Just store the current number and increment it each time new string is generated. You can implement it to model an iterator to reduce the fluff in using it (you can then use standard algorithms with it). Using Boost.Iterator (it should work with any string type, too):
#include <boost/iterator/iterator_facade.hpp>
#include <sstream>
#include <iomanip>
// can't come up with a better name
template <typename StringT, typename OrdT>
struct ordinal_id_generator : boost::iterator_facade<
ordinal_id_generator<StringT, OrdT>, StringT,
boost::forward_traversal_tag, StringT
> {
ordinal_id_generator(
const StringT& prefix = StringT(),
typename StringT::size_type suffix_length = 5, OrdT initial = 0
) : prefix(prefix), suffix_length(suffix_length), ordinal(initial)
{}
private:
StringT prefix;
typename StringT::size_type suffix_length;
OrdT ordinal;
friend class boost::iterator_core_access;
void increment() {
++ordinal;
}
bool equal(const ordinal_id_generator& other) const {
return (
ordinal == other.ordinal
&& prefix == other.prefix
&& suffix_length == other.suffix_length
);
}
StringT dereference() const {
std::basic_ostringstream<typename StringT::value_type> ss;
ss << prefix << std::setfill('0')
<< std::setw(suffix_length) << ordinal;
return ss.str();
}
};
And example code:
#include <string>
#include <iostream>
#include <iterator>
#include <algorithm>
typedef ordinal_id_generator<std::string, unsigned> generator;
int main() {
std::ostream_iterator<std::string> out(std::cout, "\n");
std::copy_n(generator("IMG"), 5, out);
// can even behave as a range
std::copy(generator("foo", 1, 2), generator("foo", 1, 4), out);
return 0;
}
Take a look at the standard library's string streams. Have an integer that you increment, and insert into the string stream after every increment. To control the string length, there's the concept of fill characters, and the width() member function.
You have many ways of doing that.
The generic one would be to, like the link that you showed, have an array of possible characters. Then after each iteration, you start from right-most character, increment it (that is, change it to the next one in the possible characters list) and if it overflowed, set it to the first one (index 0) and go the one on the left. This is exactly like incrementing a number in base, say 62.
In your specific example, you are better off with creating the string from another string and a number.
If you like *printf, you can write a string with "IMG%04d" and have the parameter go from 0 to whatever.
If you like stringstream, you can similarly do so.
What exactly do you mean by consecutive strings ?
Since you've mentioned that you're using C++ strings, try using the .string::append method.
string str, str2;
str.append("A");
str.append(str2);
Lookup http://www.cplusplus.com/reference/string/string/append/ for more overloaded calls of the append function.
it's pseudo code. you'll understand what i mean :D
int counter = 0, retval;
do
{
char filename[MAX_PATH];
sprintf(filename, "IMG00%d", counter++);
if(retval = CreateFile(...))
//ok, return
}while(!retval);
You have to keep a counter that is increased everytime you get a new name. This counter has to be saved when your application is ends, and loaded when you application starts.
Could be something like this:
class NameGenerator
{
public:
NameGenerator()
: m_counter(0)
{
// Code to load the counter from a file
}
~NameGenerator()
{
// Code to save the counter to a file
}
std::string get_next_name()
{
// Combine your preferred prefix with your counter
// Increase the counter
// Return the string
}
private:
int m_counter;
}
NameGenerator my_name_generator;
Then use it like this:
std::string my_name = my_name_generator.get_next_name();