Problems with making a simple Unix shell - c++

I am trying to create a simple shell in Unix. I read a lot and found that everybody uses the strtok function a lot. But I want to do it without any special functions. So I wrote the code but I can't seem to get it to work. What am I doing wrong here?
void process(char**);
int arg_count;
char **splitcommand(char* input)
{
char temp[81][81] ,*cmdptr[40];
int k,done=0,no=0,arg_count=0;
for(int i=0 ; input[i] != '\0' ; i++)
{
k=0;
while(1)
{
if(input[i] == ' ')
{
arg_count++;
break;
}
if(input[i] == '\0')
{
arg_count++;
done = 1;
break;
}
temp[arg_count][k++] = input[i++];
}
temp[arg_count][k++] = '\0';
if(done == 1)
{
break;
}
}
for(int i=0 ; i<arg_count ; i++)
{
cmdptr[i] = temp[i];
cout<<endl;
}
cout<<endl;
}
void process(char* cmd[])
{
int pid = fork();
if (pid < 0)
{
cout << "Fork Failed" << endl;
exit(-1);
}
else if (pid == 0)
{
cout<<endl<<"in pid";
execvp(cmd[0], cmd);
}
else
{
wait(NULL);
cout << "Job's Done" << endl;
}
}
int main()
{
cout<<"Welcome to shell !!!!!!!!!!!"<<endl;
char input[81];
cin.getline(input,81);
splitcommand(input);
}

Several things:
you don't return anything from the splitcommand function
everything you do in the splitcommand function is done in local variables, so it (esp. the strings you make) will not survive its end
the code that attaches the null terminator is wrong (you put it to the following string, not the current one)
using fixed-size buffer is a great choice; people love it
note that in real UNIX shells, not every space designates an argument, and not every argument is designated by spaces
I'd suggest you use strings and some (real) parser framework, provided it is not too special for you.

This is almost certainly homework. There is no reason to avoid library functions unless you were told to. In fact, most likely you were told to implement strtok.

strtok isn't really a special function as it's standard function in standard include string.h, so there is no good reason not to use it.

If you decide to make your shell more complicated then you may reason about using a tool for lexical analysis.
For example:
http://en.wikipedia.org/wiki/Flex_lexical_analyser

The problem is with the
arg_count++;
inside the if(input[i] == ' ') and if(input[i] == '\0')
when you are parsing the command line and you find a space or you reach the end of the command line you are increment arg_count before you put a \0 at the end of the command you were reading.
So change it to:
if(input[i] == ' ')
{
// arg_count++; REMOVE THIS.
break;
}
if(input[i] == '\0')
{
// arg_count++; REMOVE THIS.
done = 1;
break;
}
temp[arg_count][k++] = input[i++];
}
temp[arg_count][k++] = '\0'; // add null-char at the end.
arg_count++; // increment should happen here.
More bugs:
You are not returning anything from
splitcommand
You cannot just return cmdptr
because they point to local char
arrays(temp) which will not persist
after the function returns. So you'll
have to make sure that the array
temp persists even after function
call by allocating it dynamically or
making it global.
Arguments to execvp look good to
me. Others please take a look.

Related

Recursion cin.getline() doesn't want to ask for input

Here is the code:
void Reader::read(short& in) {
char* str = new char[6];
char* strbeg = str;
cin.getline(str, 6);
in = 0;
int value = 0;
short sign = 1;
if (*str == '+' || *str == '-') {
if (*str == '-' ) sign = -1;
str++;
}
while (isdigit(*str)) {
value *= 10;
value += (int) (*str - '0');
str++;
if (value > 32767) {
cout.write("Error, value can't fit short. Try again.\n", 41);
delete[] strbeg;
read(in);
return;
}
}
if (sign == -1) { value *= -1; }
in = (short) value;
delete[] strbeg;
return;
}
What happens is that if I type 999999999, it calls itself but on fourth line it's not gonna ask for input again. Debugger couldn't give much info as it is more language-specific question. Thank you in advance. Have a nice day!
Yes, the goal is to parse input as short. I know about losing 1 from min negative, wip :)
=== edit ===
I've tried goto... No, same thing. So it's not about visible variables or addresses, I guess.
=== edit ===
I can't use operator >> as it is forbidden by the task.
999999999 will cause an overflow, thus failbit is set for cin. Then your program reach read(in), then the cin.getline(). Here, beacause of failbit, cin will not ask any input again.
If you tried to figure out why in my code cin do ask for more input, you might find out all this by yourself.
I write you an example.
#include <iostream>
#include <climits>
using namespace std;
int main() {
char str[6];
short x = 0;
bool flag = false;
while (flag == false) {
cin.getline(str, 6);
flag = cin.good();
if (flag) { // if read successfully
char *p = str;
if (*p=='-') // special case for the first character
++p;
while (*p && *p>='0' && *p<='9')
++p;
if (*p) // there is a non digit non '\0' character
flag = false;
}
if (flag == false) {
cout << "An error occurred, try try again." << endl;
if (!cin.eof()) {
cin.unget(); // put back the possibly read '\n'
cin.ignore(INT_MAX, '\n');
}
cin.clear();
} else {
// str is now ready for parsing
// TODO: do your parsing work here
// for exemple x = atoi(str);
}
}
std::cout << x << std::endl;
return 0;
}
As we have discussed, you don't need new.
Check whether the string read is clean before parsing. If you mix checking and parsing, things will be complicated.
And you don't need recursion.
Read characters from stream by istream::getline seems to be the only option we have here. And when an error occurred, this function really doesn't tell us much, we have to deal with overflow and other problem separately.

[c++]checking for end of file

I've built a simple Linux shell in c++. It can take in standard bash commands, redirect to a file, etc. But, what I want is for it to be able to take in input, for example, if I have a commands.txt file that contain
ls -l /
some_invalid_command
echo this displays stuff
and I pipe it into my a.out like so
$./a.out < commands.txt
I want it to run the commands inside as if I had typed them in. However, when I do that, I run into an infinite loop where the '$' prompt displays over and over until I hit ctrl+c. I'm pretty sure the cause is that I'm not checking for an end of file anywhere in my code.
So my question is, how would I go about checking for the end of file in this situation? I think the character is '^d' but I'm not sure how I'd check for that. Would something like this work?
if (inputString == "\^d") {
exit = true;
}
I'm only concerned that perhaps I want to input more information into the shell after the file has ran. setting exit to true would tell the shell to turn off.
Edit: Requested code...
void Shell::getInput() {
emptyQueue();
cout << "$ ";
getline(cin, inputString);
fillQueue();
};
void Shell::fillQueue() {
if (inputString.empty()) {
return;
};
int i = inputString.find_first_not_of(" ");
int j = inputString.find_last_not_of(" \r\n");
int k;
string stringToAdd;
while (i != -1) {
k = i;
while (inputString.at(k) != ' ') {
if (k == j) {
break;
};
k++;
};
if (k != j) {
k--;
};
stringToAdd = inputString.substr(i, k - i + 1);
i = inputString.find_first_not_of(" ", k + 1);
commandQueue.push_back(stringToAdd);
};
i = 0;
while (i < commandQueue.size()) {
pointers[i] = commandQueue[i].c_str();
i++;
};
};
An end of file is not a character itself.
When you reach the end of the file, any attempt to read more characters returns with an indication that 0 characters were read.
You did not show your code in question, but you must be running a loop that reads the next line of text from standard input. Typically, you would have either an explicit check that the number of characters were read is 0, or if you were using a std::istream, the eof() method.
If the file is opened in (default) text mode, the Ctrl+D is translated into end of file. So you just have to make sure that your looping until there's no input anymore, typically:
while ( getline(cin, inputString) ) { // or while (cin>>inputString)
...
}
void Shell::getInput() {
emptyQueue();
cout << "$ ";
getline(cin, inputString);
if(feof(stdin)) {
exit = true;
return;
};
fillQueue();
};

segmentation fault in swap function

im busy writing a line of code for my study.
I already have gotten quite far on the assignment but i keep running into the same problem.
On the swap function i keep running into a segmentation fault when a character is inputted(word & word2) that is not in the main 'dictionary' string.
Could someone explain to me what is causing the problem and how i can solve it? Sorry if anything isnt clear, i've just started learning c++.
code where segmentation fault occures:
void swapWords(char **dict, char *word, char *word2)
{
int i;
int d;
int x;
int y;
char *tmp;
while (1){
for(i = 0; i < MAX_NUMBER_OF_WORDS; i++)
{
if(strcmp(word, dict[i]) != 0)
{
if(i == MAX_NUMBER_OF_WORDS -1)
{
printf("Cannot swap words. Atleast one word missing in the dictionary.\n");
goto error;
}
}
else
{
x = i;
break;
}
}
for(d = 0; d < MAX_NUMBER_OF_WORDS; d++)
{
if(strcmp(word2, dict[d]) != 0)
{
if(d == MAX_NUMBER_OF_WORDS -1)
{
printf("Cannot swap words. Atleast one word missing in the dictionary.\n");
goto error;
}
}
else
{
y = d;
break;
}
}
tmp = dict[x];
dict[x] = dict[y];
dict[y] = tmp;
error: break;
}
}
The entire code:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#define MAX_NUMBER_OF_WORDS 10
void swapWords(char **dict, char *word, char *word2)
{
int i;
int d;
int x;
int y;
char *tmp;
while (1){
for(i = 0; i < MAX_NUMBER_OF_WORDS; i++)
{
if(strcmp(word, dict[i]) != 0)
{
if(i == MAX_NUMBER_OF_WORDS -1)
{
printf("Cannot swap words. Atleast one word missing in the dictionary.\n");
goto error;
}
}
else
{
x = i;
break;
}
}
for(d = 0; d < MAX_NUMBER_OF_WORDS; d++)
{
if(strcmp(word2, dict[d]) != 0)
{
if(d == MAX_NUMBER_OF_WORDS -1)
{
printf("Cannot swap words. Atleast one word missing in the dictionary.\n");
goto error;
}
}
else
{
y = d;
break;
}
}
tmp = dict[x];
dict[x] = dict[y];
dict[y] = tmp;
error: break;
}
}
void removeWord(char **dict, char *word)
{
int i;
int d;
for(i = 0; i < MAX_NUMBER_OF_WORDS; i++)
{
if(strcmp(dict[i], word) == 0)
{ dict[i] = NULL;
for(d = i+1; d < MAX_NUMBER_OF_WORDS; d++)
{ if(dict[d] == NULL)
{ dict[i] = dict[d-1];
dict[d-1] = NULL;
break;
}
}
break;
}
}
}
void printDict(char **dict)
{
int i = 0;
if(dict[0] == NULL)
{
printf("The dictionary is empty.\n");
}
else{
while (dict[i] != NULL)
{
printf("- %s\n", dict[i]);
i++;
}
}
}
void addWord(char **dict, char *word)
{
int d;
char *word1;
for(d = 0; d < MAX_NUMBER_OF_WORDS; d++)
{
if (dict[d] == NULL)
{
word1 = (char*) malloc(sizeof(char)*(strlen(word) + 1));
strcpy(word1, word);
dict[d] = word1;
break;
}
}
}
int numberOfWordsInDict(char **dict)
{
int i = 0;
int d;
for (d = 0; d < MAX_NUMBER_OF_WORDS; d++){
if(dict[d] != NULL)
{
i++;
}
}
return i;
}
int main()
{
char *dict[MAX_NUMBER_OF_WORDS] = {};
char word[36];
char word2[36];
char c;
int i;
while(printf("Command (a/p/r/s/q): "))
{
scanf(" %c", &c);
switch (c){
case 'p': printDict(dict);
break;
case 'a': printf("Enter a word: ");
scanf("%s", word);
addWord(dict, word);
break;
case 'n': i = numberOfWordsInDict(dict);
printf("%d\n", i);
break;
case 'r': printf("Remove a word: ");
scanf("%s", word);
removeWord(dict, word);
break;
case 's': printf("Swap two words:\n");
printf("Enter first word: ");
scanf("%s", word);
printf("Enter second word: ");
scanf("%s", word2);
swapWords(dict, word, word2);
break;
case 'q': return 0;
}
}
}
It will be most helpful to your studies as a student if you find the actual error yourself, though Marco and πάντα ῥεῖ may be right. However, here are a few things to think about, as this will definitely not be your last segfault problem as a programmer (I had at least 20 this month alone).
A segmentation fault is almost always caused by the code trying to modify or read memory that it doesn't have permission to read or modify. When the program starts, it is given a chunk of memory (RAM) to work with. For security reasons, no program is allowed to work with memory outside of that chunk. There are other limitations at play too.
As a general rule, if you try to read memory past the end of an array, you have a high risk of getting a segfault, or in other cases, garbled data. The official word on this actually comes from C, C++'s parent language, in that accessing past the end of an array causes "undefined behavior". Or, as it was once said on USENET, "it is legal for the compiler to make demons fly out of your nose". The behavior is totally unpredictable. Thankfully, that undefined behavior usually IS a segfault.
By the way, if you try to access an uninitialized array, similar weirdness can happen.
NOW, since you are accessing the elements of your array via a loop, another possible cause is that your loop is continuing beyond where you think it is. Sometimes it is helpful to modify your code so that the loop's iterator (i in your case) is printed out each iteration. This can help you catch if the loop is going beyond where it should.
In short, check...
Did I initialize all of my arrays before I tried to read or write
them?
Are my loops starting and stopping where I expected? Check for
"off-by-one" errors (i.e. starting at 1 instead of 0), infinite
loops (forgot to increment the iterator or the stop condition is
never true), and other logical errors.
Am I trying to read/write past the end of the array?
If I'm working with a C-string, did I forget the NULL terminator?
In addition to your debugger, which you should learn how to use well, tools like valgrind are instrumental in finding the cause of memory errors. Oftentimes, it can point you to the exact line of code where the segfault is occuring.
I had figured out myself the problem was in the strcmp. I know that figuring out a problem by myself is the best way to learn and I tried, but I just couldn't figure out why it was returning a seg fault. As this is my fifth assignment I'm only just getting to know how array's and pointers work. I assumed that the array was already initialized as 'NULL', as seen I was already comparing the pointer to 'NULL' in the addWord function. To assume this is ofcourse very stupid of me. I might not have figured the problem out by myself, yet it is still something I will not be forgetting anymore.
Most probably the segmentation fault happens here:
if(strcmp(word, dict[i]) != 0)
Infact it is quite likely that that i > becomes bigger than the size of your dict and if your dict has 3 elements and you try to access the 4th you are accessing an unknown area or ram and that causes a segmentation fault.
The solution is to make sure your for loop stops at the last element of the dictionary with the solution πάντα ῥεῖ has proposed in the above comment.

Why is Jansson's is_json_object() failing to recognize my JSON string?

I am new to C++ and cannot figure out how to strip some miscellaneous data from a string and then parse it as JSON.
I've ended up using the most documented JSON parser I could find - jansson. It seems excellent, although I'm stuck at the first hurdle.
My program receives a string in the following format:
5::/chat:{"name":"steve","args":[{"connection":"true"}, { "chatbody" : "I am the body" }]}
I've stripped everything outside the curly brackets with:
std::string str=message;
unsigned pos = str.find("{");
std::string string = str.substr (pos);
That leaves:
{
"name": "steve",
"args": [
{
"connection": "true"
},
{
"chatbody": "I am the body"
}
]
}
I'm stuck at stage one parsing this. I have converted the string to a char and then tried to use json_loads, but I don't get anything useful out...
The whole thing looks like this:
void processJson(string message)
{
json_t *root;
json_error_t error;
size_t i;
std::string str=message;
unsigned pos = str.find("{");
std::string str3 = str.substr (pos);
const char * c = str.c_str();
json_t *data, *sha, *name;
root = json_loads(c, 0, &error);
data = json_array_get(root, i);
cout << data;
if(!json_is_object(root))
{
fprintf(stderr, "error: commit data %d is not an object\n", i + 1);
}
}
I need to get the values out, but I just get 01, 02, 03....
is_json_object just says:
error: commit data 1068826 is not an object
error: commit data 1068825 is not an object
error: commit data 1068822 is not an object
What am I doing wrong and how can I properly format this? Ultimately I'll need to iterate over an array but cannot get past this. I'm sure this is just a beginner's mistake.
-EDIT-
Trying to avoid using Boost because of a strict size requirement.
You could always use an existing solution like Boost's property tree, which has a function for automatically parsing JSON files. It's literally as easy as adding these two headers:
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
And then adding this small snippet of code in, where jsonfile obviously means your filename.
boost::property_tree::ptree jsontree;
boost::property_tree::read_json(jsonfile, jsontree);
If you ever want to extract information from your JSON tree, you can do it like this, where type is the type of the data you want you extract, and insert.key.path.here is the path to your key, with each parent key separated by periods.
jsonfile.get<type>(insert.key.path.here);
In addition, I don't believe the JSON string you have there is valid. You did good removing the excess around the JSON string itself, but I believe there's a problem here:
"connection" : true,
You can check the validity of your JSON string here: http://jsonformatter.curiousconcept.com/
For JSON formatting, I've searched for a pretty print solution via C++ to no avail. Finally, I found some java code which I eventually converted to C++. Try the following for JSON formatting:
std::string formattedJson(char *json)
{
std::string pretty;
if (json == NULL || strlen(json) == 0)
{
return pretty;
}
std::string str = std::string(json);
bool quoted = false;
bool escaped = false;
std::string INDENT = " ";
int indent = 0;
int length = (int) str.length();
int i;
for (i = 0 ; i < length ; i++)
{
char ch = str[i];
switch (ch)
{
case '{':
case '[':
pretty += ch;
if (!quoted)
{
pretty += "\n";
if (!(str[i+1] == '}' || str[i+1] == ']'))
{
++indent;
for (int j = 0 ; j < indent ; j++)
{
pretty += INDENT;
}
}
}
break;
case '}':
case ']':
if (!quoted)
{
if ((i > 0) && (!(str[i-1] == '{' || str[i-1] == '[')))
{
pretty += "\n";
--indent;
for (int j = 0 ; j < indent ; j++)
{
pretty += INDENT;
}
}
else if ((i > 0) && ((str[i-1] == '[' && ch == ']') || (str[i-1] == '{' && ch == '}')))
{
for (int j = 0 ; j < indent ; j++)
{
pretty += INDENT;
}
}
}
pretty += ch;
break;
case '"':
pretty += ch;
escaped = false;
if (i > 0 && str[i-1] == '\\')
{
escaped = !escaped;
}
if (!escaped)
{
quoted = !quoted;
}
break;
case ',':
pretty += ch;
if (!quoted)
{
pretty += "\n";
for (int j = 0 ; j < indent ; j++)
{
pretty += INDENT;
}
}
break;
case ':':
pretty += ch;
if (!quoted)
{
pretty += " ";
}
break;
default:
pretty += ch;
break;
}
}
return pretty;
}
I'm not familiar with whatever JSON library you're using, but here's a few suggestions about what might be wrong.
size_t i is not initialized to anything, and is then passed into json_array_get.
json_array_get is passed the root object, which is not a JSON array, but a JSON object. In JSON terminology, root["args"] would be the array.
Of course, depending on the semantics of your JSON library, neither of this might be issues at all, but they seem like red flags to me.
Casablanca (REST C++ SDK) has a pretty nice JSON parser which you can use even if you don't use the HTTP functionality.
You can extract the JSON parser files into a static library and link it with your existing project. The files to extract are:
src\json\json.cpp
src\json\json_parsing.cpp
src\json\json_serialization.cpp
src\utilities\asyncrt_utils.cpp
include\cpprest\json.h
include\cpprest\xxpublic.h
include\cpprest\basic_types.h
include\cpprest\asyncrt_utils.h
I can confirm this works as I've used it recently as a static library for my projects.
I also tried to use Jansson but the parser in Casablanca simply feels easier to use and has better Unicode support.

C++ Remove new line from multiline string

Whats the most efficient way of removing a 'newline' from a std::string?
#include <algorithm>
#include <string>
std::string str;
str.erase(std::remove(str.begin(), str.end(), '\n'), str.cend());
The behavior of std::remove may not quite be what you'd expect.
A call to remove is typically followed by a call to a container's erase method, which erases the unspecified values and reduces the physical size of the container to match its new logical size.
See an explanation of it here.
If the newline is expected to be at the end of the string, then:
if (!s.empty() && s[s.length()-1] == '\n') {
s.erase(s.length()-1);
}
If the string can contain many newlines anywhere in the string:
std::string::size_type i = 0;
while (i < s.length()) {
i = s.find('\n', i);
if (i == std::string:npos) {
break;
}
s.erase(i);
}
You should use the erase-remove idiom, looking for '\n'. This will work for any standard sequence container; not just string.
Here is one for DOS or Unix new line:
void chomp( string &s)
{
int pos;
if((pos=s.find('\n')) != string::npos)
s.erase(pos);
}
Slight modification on edW's solution to remove all exisiting endline chars
void chomp(string &s){
size_t pos;
while (((pos=s.find('\n')) != string::npos))
s.erase(pos,1);
}
Note that size_t is typed for pos, it is because npos is defined differently for different types, for example, -1 (unsigned int) and -1 (unsigned float) are not the same, due to the fact the max size of each type are different. Therefore, comparing int to size_t might return false even if their values are both -1.
s.erase(std::remove(s.begin(), s.end(), '\n'), s.end());
The code removes all newlines from the string str.
O(N) implementation best served without comments on SO and with comments in production.
unsigned shift=0;
for (unsigned i=0; i<length(str); ++i){
if (str[i] == '\n') {
++shift;
}else{
str[i-shift] = str[i];
}
}
str.resize(str.length() - shift);
std::string some_str = SOME_VAL;
if ( some_str.size() > 0 && some_str[some_str.length()-1] == '\n' )
some_str.resize( some_str.length()-1 );
or (removes several newlines at the end)
some_str.resize( some_str.find_last_not_of(L"\n")+1 );
Another way to do it in the for loop
void rm_nl(string &s) {
for (int p = s.find("\n"); p != (int) string::npos; p = s.find("\n"))
s.erase(p,1);
}
Usage:
string data = "\naaa\nbbb\nccc\nddd\n";
rm_nl(data);
cout << data; // data = aaabbbcccddd
All these answers seem a bit heavy to me.
If you just flat out remove the '\n' and move everything else back a spot, you are liable to have some characters slammed together in a weird-looking way. So why not just do the simple (and most efficient) thing: Replace all '\n's with spaces?
for (int i = 0; i < str.length();i++) {
if (str[i] == '\n') {
str[i] = ' ';
}
}
There may be ways to improve the speed of this at the edges, but it will be way quicker than moving whole chunks of the string around in memory.
If its anywhere in the string than you can't do better than O(n).
And the only way is to search for '\n' in the string and erase it.
for(int i=0;i<s.length();i++) if(s[i]=='\n') s.erase(s.begin()+i);
For more newlines than:
int n=0;
for(int i=0;i<s.length();i++){
if(s[i]=='\n'){
n++;//we increase the number of newlines we have found so far
}else{
s[i-n]=s[i];
}
}
s.resize(s.length()-n);//to delete only once the last n elements witch are now newlines
It erases all the newlines once.
About answer 3 removing only the last \n off string code :
if (!s.empty() && s[s.length()-1] == '\n') {
s.erase(s.length()-1);
}
Will the if condition not fail if the string is really empty ?
Is it not better to do :
if (!s.empty())
{
if (s[s.length()-1] == '\n')
s.erase(s.length()-1);
}
To extend #Greg Hewgill's answer for C++11:
If you just need to delete a newline at the very end of the string:
This in C++98:
if (!s.empty() && s[s.length()-1] == '\n') {
s.erase(s.length()-1);
}
...can now be done like this in C++11:
if (!s.empty() && s.back() == '\n') {
s.pop_back();
}
Optionally, wrap it up in a function. Note that I pass it by ptr here simply so that when you take its address as you pass it to the function, it reminds you that the string will be modified in place inside the function.
void remove_trailing_newline(std::string* str)
{
if (str->empty())
{
return;
}
if (str->back() == '\n')
{
str->pop_back();
}
}
// usage
std::string str = "some string\n";
remove_trailing_newline(&str);
Whats the most efficient way of removing a 'newline' from a std::string?
As far as the most efficient way goes--that I'd have to speed test/profile and see. I'll see if I can get back to you on that and run some speed tests between the top two answers here, and a C-style way like I did here: Removing elements from array in C. I'll use my nanos() timestamp function for speed testing.
Other References:
See these "new" C++11 functions in this reference wiki here: https://en.cppreference.com/w/cpp/string/basic_string
https://en.cppreference.com/w/cpp/string/basic_string/empty
https://en.cppreference.com/w/cpp/string/basic_string/back
https://en.cppreference.com/w/cpp/string/basic_string/pop_back