getline truncating input in buffer overflow control example - c++

So I'm coming up with a simple solution to control and notify the user if a buffer overflow is detected.
First, the input char array is set up using a size variable, and then the getline function takes in input up to that size.
const int charMax = 20;
const std::string account_number = "CharlieBrown42";
char user_input[charMax];
std::cout << "Enter a value: ";
std::cin.getline(user_input, charMax);
Then I check and see if there is anything left in the buffer -- if so, I let the user know that buffer overflow was attempted, but only the right amount of input was read.
if ((std::cin.rdbuf()->in_avail() - 2) > 0) {
std::cout << "Buffer overflow was attempted, but input was automatically trimmed" << std::endl;
}
std::cout << "You entered: " << user_input << std::endl;
std::cout << "Account Number = " << account_number << std::endl;
However, when tested, the printed final user_input is always only 19 characters. This is problematic because I am supposed to be able to take in up to 20 characters before buffer overflow occurs ( > charMax). getline() is always truncating one character more than I need it to.
Where am I going wrong?

Related

Cannot read with `std::cin.read`: return key not recognized

I am trying to read a string from the keyboard with the std::cin.read() function.
What happens is that it looks like the string is being read as I type it, but the [Return] character is treated as a normal new line, and not as a terminator.
What is the terminator for this function? Is it possible to modify it?
#include <iostream>
char* text;
char text_length = 0;
int main() {
std::cout << "Text length: ";
std::cin.get(text_length);
std::cout << "\nText length: " << text_length << std::endl;
text = new char[1024];
std::cout << "\n\nText: ";
std::cin.read(text, text_length);
std::cout << "\n\nText: " << text << std::endl;
return 0;
}
Code tested on: GCC 11, clang 13. OS: Linux.
On Windows console, the EOF indicator is Ctrl+Z (not the Ctrl+C, which will invoke a signal handler routine instead, which will call ExitProcess by default). But the problem with Ctrl+Z on Windows is that it has to be the first character of a separate line (i.e., you have to press Enter and then Ctrl+Z, otherwise the Ctrl+Z will not be recognized as an EOF). This means that the newline will also be read before EOF.
The std::cin.get(text_length); line is not doing what you expect. It treats the input as a character, not as a number. So, if you press 5 and Enter, the extracted value will be 53 (the ASCII code for the character 5). Instead, do std::cin >> text_length;.
Also, the text_length should be declared as std::streamsize, not as char. If it is supposed to fit in 8 bits, just clamp the value before further processing.
There are other bugs as well: you don't null-terminate your buffer, and you never explicitly delete the memory dynamically allocated by new (although it will be freed implicitly when the OS ends the process, of course).
To sum it up, check out this code:
#include <algorithm>
#include <iostream>
#include <limits>
int main()
{
constexpr std::streamsize min_text_length{ 0 };
constexpr std::streamsize max_text_length{ 255 };
char* text{ nullptr };
std::streamsize text_length{ 0 };
std::cout << "Enter text length: ";
std::cin >> text_length;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "Text length entered: " << text_length << '\n';
text_length = std::clamp(text_length, min_text_length, max_text_length);
std::cout << "Text length to be used: " << text_length << '\n';
text = new char[max_text_length + 1]{}; // Note this zero-fills the buffer
std::cout << "Enter text: ";
std::cin.read(text, text_length);
std::cout << "Text entered BEGINS-->" << text << "<--ENDS\n";
delete[] text;
}
After entering the length, enter some text, and when finished, press Enter, followed by Ctrl+Z, and Enter again. (As already mentioned, the newline character preceding the Ctrl+Z will unfortunately be read in as well, but at least the Ctrl+Z allows you to terminate the input.)

Why when I am using file.peek() in C++ do I get a number rather than the char?

In most of my code, the in.peek() works as getting whatever the next char is supposed to be. However, when it is reading some symbols, it returns a number rather than the char, and I am not sure how to fix it to get the symbol I want.
The text file reads:
print "Good morning!!";
but during the " char I use file.peek() to read the next symbol is a ; to change the state of my switch, but it comes out as a number instead of the symbol.
This is how I am trying to print, I even created a temp char and set it to in.peek(), but that just comes out as a blank space.
char temp = in.peek();
cout<<"hit " << ch << " "<< in.peek()<<" "<<temp << endl;
The output is: "hit " 10 "
With the last bit being a blank space. Does anyone know how I can fix this so I get the ;?
peek returns an int (to account for possibility of eof()). To make sure cout recognizes it as a char, you can cast it (though this will produce incorrect values if you did hit EOF), e.g.:
cout << "hit " << ch << " " << static_cast<char>(in.peek()) << " " << temp << endl;

the characters in a c++ string are being ignored

I'm trying to write to a hid device using signal11's hidapi (here).
In my troubleshooting, I've noticed that part of a string isn't being displayed to the console.
Here is a sample of my code
//device is a hid device and is assigned to in another part of the program.
//dataBuffer is a struct with only a char array called "buffer" and an int which is the size of the array called "size"
void DeviceCommands::Write(hid_device* device, dataBuffer* buf)
{
std::cout << "Attempting write >> buffer...\n";
buf->buffer[0] = 0;
std::cout << "Written to buffer...\n" << "writing buffer to device\n";
int res = hid_write(device, buf->buffer, sizeof(buf->buffer));
std::cout << "Write success: " + '\n';
std::cout << "Write complete\n";
}
I'm expecting for the console to return the following:
Attempting write >> buffer...
Written to buffer...
writing buffer to device
Write success: (0 if the write succeeds, -1 if it fails)
Write complete
But instead, this happens:
Attempting write >> buffer...
Written to buffer...
writing buffer to device
ess: Write complete
The "Write succ", result, and the line break are missing, I'm somewhat new to c++ but I have experience with c#. I'm just confused and some help would be much appreciated, thanks in advance and ask if you need more information!
This line:
std::cout << "Write success: " + '\n';
will print the string "Write success: " with an offset of 10 characters, which is the ascii value of \n. Hence you see ess on the screen.
You probably want:
std::cout << "Write success: " << res << "\n";
assuming res returns 0 or -1 as needed.
Do not 'add' a character to a string. It will not do what you expect.
Here you are thinking you are adding the line feed character to your string "Write success" when in fact you are telling the compiler to take your constant string and only stream from the 10th character. Remember a constant string here is just an array of characters and the single character '\n' is converted to the number 10.
You are also missing the result out of the streaming.
So your second to last line should read:
std::cout << "Write success: " << res << std::endl;

Creating an error message if user inputs too many characters

I'm pretty new to coding so I apologize if this is trivial. I'm supposed to create an error message when the user enters more characters than my const int SIZE2 array, which is 20 characters.
My array is called major:
>cout << "Enter your major: " << endl << endl;
>48 cin.width(SIZE2);
>49 cin.get(major,SIZE2, '\n');
>50 majorLength = strlen(major);
>51
>52 if(majorLength > SIZE2)
>53 {
>54 cout << "Too many characters, Enter major again: " << endl;
>55 cin.get(major, SIZE2, '\n');
>56 cin.ignore(100, '\n');
>57
>58 }
It's compiling just fine but skipping over my if-statement.
iostream.get() (here invoked as cin.get()) reads an exact number of bytes and then ends. In your case, it will specifically never read more than SIZE2 bytes into major; as a result, if(majorLength > SIZE2) will always be false. Also, if you enter too many bytes, major will only have the first 20 - the rest are truncated. (FWIW, your code is only matching 19 characters right now.)
Note that you probably shouldn't try to do this - there's not really a good way to check the length of a stream before you read it, and if you decide to just read it all and then check its size, you run the risk of overflowing your buffer - assuming it's fixed size.
You can, however, determine if, after reading, the buffer is empty or not. To determine if there is any more input in the buffer beyond SIZE2, you can capture one character with std::cin.get() and then examine this character. If the character is \n, it means there was no more input in the buffer; if it is not, that means the character buffer had too much input in it. This will also trigger if the input is completely blank.
#include <iostream>
int main () {
int SIZE2 = 20;
char str[SIZE2];
char c;
std::cin.get (str, SIZE2+1); // get c-string of 20 chars
std::cin.get(c); //get last buffer character
if(c != '\n') {
std::cout << "bad input!" << std::endl;
}
std::cout << str << std::endl;
return 0;
}
Demo

console not returning expected number of characters in cin buffer

I am creating a console version of "Bull Cow Game". In the game, the user has a certain number of tries to guess what the secret word is. Every time they guess, the program returns the number of "Bulls" and "Cows" they guessed correctly. The user gets a "Bull" for every character that they guess in the right place and a "Cow" for every character that they guess correctly but is not in the right place.
My problem is in my getGuess() function. In the do-while loop, the program is supposed to loop if the user inputs anything other than the number of characters in "answer". When I run my program, I get some unexpected and confusing results:
1) No matter what I input for the first "guess", the program tells me that cin's gcount() is 0 or 1 characters after setw(). I could input 50 characters or 2 and the program would output the same result. If the gcount is 1, then this counts as one of the allotted guesses which is an undesirable result. If the cin.gcount() is 0, the program correctly does not count the guess as valid but I am still confused as to why the cin.gcount() is 0 at all.
2) If I change the number of characters in my guess from the previous guess, the program tells me that the cin.gcount() is whatever the cin.gcount() was after the previous guess instead of after the current guess. This is also an undesirable result since if the user decides to input the correct number of characters, the program will not accept the user's guess as valid.
I am confused as to why this is happening since isn't cin.ignore() supposed to dump all of the extraneous characters that setw() doesn't accept? Why would the number of characters in the cin buffer carry over from one guess to the other?
Here is the function in question:
string getGuess()
{
string guess = "";
const int MAX_LENGTH = 4;
/*ensures that "guess" is the same length as answer. This
will make it so that the program avoids comparing "guess"
to "answer" if "guess" has more characters than "answer".
This do-while loop also ensures that a user can't overflow
the cin buffer by theoretically inputting more characters
than the buffer could contain*/
bool endLoop = false;
do {
cout << "Enter a word containing exactly " << MAX_LENGTH << " characters: ";
cin >> setw(MAX_LENGTH) >> guess;
cout << "cin.gcount() after setw(): " << cin.gcount() << " characters" << endl;
/*ensures that the only character in the cin is '\n'. Otherwise
do-while loop continues*/
if (cin.gcount() != 1)
{
cout << "Invalid number of characters. Please input exactly " << MAX_LENGTH
<< " characters" << endl;
}
else
{
endLoop = true;
}
cin.ignore(numeric_limits<streamsize>::max(), '\n');
cout << "cin.gcount() after cin.ignore(): "
<< cin.gcount() << " characters" << endl;
cout << "guess: " << guess << endl;
cout << endl;
} while ( endLoop == false );
cout << endl;
return guess;
}
Note: This was compiled with Microsoft Visual C++, ISO standard c++17.
A couple of misunderstandings I think
1) gcount only tells you how many characters have been read after an unformatted input operation, cin >> guess is not an unformatted input operation.
2) setw on input does not limit the numbers of characters read. If less than the specified width characters are read then the input is padded to make it equal the given width, but it does not stop more characters being read.
Your code is too tricky, forget about fancy I/O operations, do it the straightforward way. Just read a line of characters into a string using getline and check if the characters entered are what you expect. For instance remove the spaces at the beginning and end of that string, then check for internal spaces and finally check if the string is the length you require.