Limit Console Input Length in C: - c

I am beginning to design a shell application to run within a Linux terminal for a class I am taking.
This, of course, will involve reading variable-length input strings (commands) from the user. I know that I can simply read each command into a buffer of a size that I would consider appropriate, but this has the potential to either a) truncate the command or b) cause a buffer overflow.
If possible, how can way limit the length of user input to the console?
Say, if I set the command length to 3, 123 would be allowed, but if 123 were already present in the input string (before the user has pressed enter) and the user attempted to add 4, no character would print to the console, perhaps even with an 'error ping'.
I realize that I could design such functionality, but if that is needed, I am not sure where to start to do such a thing.
Either a pre-existing solution or advice on implementing my own solution would be greatly appreciated.
Edit:
I suppose a cheap and easy solution would be to read a command on character at a time until an enter signal is reached or the maximum length is reached. Would problems arise with a solution of this sort?

I have little experience with readline, but here's what you could try:
Write a function that checks rl_end (the number of characters in rl_line_buffer)
If you want to allow more, just return rl_getc
If not, you can use rl_ding
Set the rl_getc_function to call your function as described above
As a side note, if you do use readline, you don't need to limit the input at all (the library manages its memory as it goes). Another (simpler) function you might be interested in is getline.

That kind of low-level control of the console is not something that's included in C's rather basic built-in I/O model.
You need to look into something platform-specific, such as ncurses for Unix-like systems.

Without digging into platform-specific controls, you cannot limit how many characters a used may type in a console before hitting "Enter".
What you can do is check for the presence of a newline character in your input buffer; if it isn't there, then the user typed in more characters than you're prepared to deal with. You can reject that input, and then read stdin repeatedly until you see the newline.
Example:
#include <stdio.h>
#include <string.h>
...
char buf[SIZE];
...
printf("Gimme something: ");
fflush(stdout);
if (fgets(buf, sizeof buf, stdin))
{
char *newline = strchr(buf, '\n');
if (!newline)
{
printf("Input too long: \"%s\"\n", buf);
while (!newline && fgets(buf, sizeof buf, stdin))
newline = strchr(buf, '\n');
}
else
{
// do something with buf
}
}

In response to your edit, terminals are usually line-buffered, allowing users to enter as much as they want before hitting enter without you even knowing about it. You could set the terminal to raw or cbreak mode, but then you're entering platform-specific territory.
Instead, I would suggest that you avoid this problem, and accept that a terminal is a silly vestige from 2 million years ago. Most platforms define LINE_MAX to be the maximum line size any program needs to handle. Beyond that, you can simply assume your user is messing with you, and truncate.

Related

How to completely clear stdin and the \n before scanf()

Environment:
Windows 7 Enterprise SP1
gcc version 6.3.0 (MinGW.org GCC-6.3.0-1)
I'm new to C language (not new to programming).
I'm migrating a big program to C language. All works fine except for this.
I create this small program to reproduce the problem:
int AAA ()
{
const char *p = "Enter to proceed.. X to Exit.. : ";
const char *ok = "OK" ;
char proceed1 ;
char proceed2 ;
printf(p) ;
fflush(stdin) ;
scanf("%c",&proceed1);
if (proceed1 == 'X'){
return 0 ; }
sleep(3) ;
fflush(stdin) ;
printf("\n%s",p) ;
scanf("%c",&proceed2);
if (proceed2 == 'X'){
return 0 ; }
printf("\n%s",ok) ;
return 0 ;
}
All worls fine, BUT if the user (wrongly) hits twice the enter key at the proceed1 then scanf of proceed2 automatically reads the newline character '\n' that remain in the buffer...
I tried all: rewind(stdin), fseek stdin, a getchar before the second prompt to bypass the problem, but let the scanf hang if the user correctly hit enter key once.
I repeat I'm new to C language. I understand the problem can be bypassed writing a scanf that does not accpet the \n alone.
But before proceeding I ask here this question:
How to completely clear the stdin buffer?
Just because this is a common enough question, notwithstanding the links to other like questions and/or duplicates, I think this bears repeating:
If you are trying to fflush(stdin), you are almost certainly doing something wrong
An input buffer flush says, “I don’t care what you typed so far. I’m throwing it away and we’re starting over.”
Such behavior is exceedingly frustrating to users of a program. Users expect their inputs to be recognized and acted on — even if those inputs are wrong! By throwing it away, your program tells the user that their wishes are not respected, leaving them feeling like they have less control of their computer than they should.
It’s an XY problem
The usual suspect behind desires to clear the input is a scanf failure. The user typed something wrong, input is stuck with invalid characters, and the program needs to get past that.
But the program does not need to get any further than the next newline, which is to say, it only needs to discard stuff that was input before the user pressed Enter. This comes from one very simple principle:
The user will always press Enter after every prompted input
The solution comes in three forms:
Crash and burn. This is a strong signal that the user needs to fix his input to work, and actually provides him (or her) with a greater sense of control: the program does only what it is supposed to, accepting only input it should, and does nothing unless the input is correct!This also affords the user greater control in how he or she uses your program, because he can now supply prepared input to the program without having to babysit the keyboard.
Skip to the EOL and ask for the user to try again. This presupposes a human is frobbing the keyboard. But if you are going to go through this grief, then:
Read all input as a string, one line at a time, and endeavor to process that string (using sscanf, for example). This keeps your input and processing pointers all lined up and you will never have need to try to resynchronize with the input buffer.
Obviously, my opinion is that crash and burn is the most correct method for your usual console programs.
This is, in fact, part of the core design of Unix. This behavior enables individual utility programs to combine to perform useful scripted tasks.
The exception is a TUI program.
A text-based user interface is for a program that requires a human to operate it*, and takes over the terminal to behave in a different way, not requiring the user to press Enter to receive and act on input.
Games and text editors are examples of this kind of program.
* It is still possible to automate a TUI, of course, but the point is that the expected interaction with the program is no longer via newline-delimited streams, but rather via direct, individual key presses as input.
The C Standard says that fflush(stdin) has undefined behavior. You should not use it to flush pending input even on platforms where it seems to have the expected effect. There is no portable way to flush pending input, ie: all input that was typed before the prompt was issued.
If you just want to consume the characters typed by the user for a previous entry parsed with scanf(), up to and including the newline, you can use this simple function:
#include <stdio.h>
// Read and discard the rest of the current line of input.
// Returns EOF at end of file, otherwise returns a newline character
int flush_input(FILE *fp) {
int c;
while ((c = getc(fp)) != EOF && c != '\n')
continue;
return c;
}
If you are required to use scanf(), you can use these 2 calls:
scanf("%*[^\n]"); // read the remaining characters on the line if any.
scanf("%1[\n]"); // read the newline character if present.
Note however that you should not combine these 2 as a single call to scanf() because the first conversion will fail if the pending byte is a newline, and this newline will not be consumed by the second conversion.

Read arbitrary number of characters from stdin until pressing enter in C

I want to make a program that accepts an arbitrary number of characters from user input, until "enter" is pressed, and stores them in a buffer.
Is there a way that to read characters from stdin without extracting them, count the characters, then allocate a buffer of precise size, finally copy the characters to the buffer.
Basically I do NOT want the way of using getc in loop and doubling the buffer size as it's running out.
EDIT:
To make my intentions more clear, let me express my intuition. I imagine the stdin buffer the same as a file (which may or may not grow dynamically). So I should be able to seek to the end of it (representing the end of user input), counting the offset then rewind back. Something like
long const start = ftell(stdin);
fseek(stdin, 0, SEEK_END);
long const length = ftell(stdin) - start;
rewind(stdin);
The readline() command is supported on some systems, and can be added to those that don't support it.
From the man page:
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
char *
readline (const char *prompt);
readline will read a line from the terminal and return it, using prompt as a prompt. If prompt is NULL or the empty string, no prompt is issued. The line returned is allocated with malloc(3); the caller must free it when finished. The line returned has the final newline removed, so only the text of the line remains.
So, from reading the comments and other responses, you don't like the solution of using readline because it's not standard. (well, it's not an ISO or ANSI standard, but it is pretty popular anyway)
You don't like the convention of doubling the buffer size (most probably the approach used by readline internally) but you don't specify why you don't like it. There are more approaches than doubling the buffer size, you can increment it a fixed amount for example, but I think you'll not be glad with that solution either.
What can we do then, if you receive 1Hb (One HexaByte, 1.0E18 bytes) of data before you receive the first newline character? How can we deal with that?
How can any standarization office define a way to deal with this, and specify a way to proceed correctly?
Do you actually believe you are asking the right question?

gets works the first time but then it gets bypassed [duplicate]

I have problems with my C program when I try to read / parse input.
Help?
This is a FAQ entry.
StackOverflow has many questions related to reading input in C, with answers usually focussed on the specific problem of that particular user without really painting the whole picture.
This is an attempt to cover a number of common mistakes comprehensively, so this specific family of questions can be answered simply by marking them as duplicates of this one:
Why does the last line print twice?
Why does my scanf("%d", ...) / scanf("%c", ...) fail?
Why does gets() crash?
...
The answer is marked as community wiki. Feel free to improve and (cautiously) extend.
The Beginner's C Input Primer
Text mode vs. Binary mode
Check fopen() for failure
Pitfalls
Check any functions you call for success
EOF, or "why does the last line print twice"
Do not use gets(), ever
Do not use fflush() on stdin or any other stream open for reading, ever
Do not use *scanf() for potentially malformed input
When *scanf() does not work as expected
Read, then parse
Read (part of) a line of input via fgets()
Parse the line in-memory
Clean Up
Text mode vs. Binary mode
A "binary mode" stream is read in exactly as it has been written. However, there might (or might not) be an implementation-defined number of null characters ('\0') appended at the end of the stream.
A "text mode" stream may do a number of transformations, including (but not limited to):
removal of spaces immediately before a line-end;
changing newlines ('\n') to something else on output (e.g. "\r\n" on Windows) and back to '\n' on input;
adding, altering, or deleting characters that are neither printing characters (isprint(c) is true), horizontal tabs, or new-lines.
It should be obvious that text and binary mode do not mix. Open text files in text mode, and binary files in binary mode.
Check fopen() for failure
The attempt to open a file may fail for various reasons -- lack of permissions, or file not found being the most common ones. In this case, fopen() will return a NULL pointer. Always check whether fopen returned a NULL pointer, before attempting to read or write to the file.
When fopen fails, it usually sets the global errno variable to indicate why it failed. (This is technically not a requirement of the C language, but both POSIX and Windows guarantee to do it.) errno is a code number which can be compared against constants in errno.h, but in simple programs, usually all you need to do is turn it into an error message and print that, using perror() or strerror(). The error message should also include the filename you passed to fopen; if you don't do that, you will be very confused when the problem is that the filename isn't what you thought it was.
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(int argc, char **argv)
{
if (argc < 2) {
fprintf(stderr, "usage: %s file\n", argv[0]);
return 1;
}
FILE *fp = fopen(argv[1], "r");
if (!fp) {
// alternatively, just `perror(argv[1])`
fprintf(stderr, "cannot open %s: %s\n", argv[1], strerror(errno));
return 1;
}
// read from fp here
fclose(fp);
return 0;
}
Pitfalls
Check any functions you call for success
This should be obvious. But do check the documentation of any function you call for their return value and error handling, and check for those conditions.
These are errors that are easy when you catch the condition early, but lead to lots of head-scratching if you do not.
EOF, or "why does the last line print twice"
The function feof() returns true if EOF has been reached. A misunderstanding of what "reaching" EOF actually means makes many beginners write something like this:
// BROKEN CODE
while (!feof(fp)) {
fgets(buffer, BUFFER_SIZE, fp);
printf("%s", buffer);
}
This makes the last line of the input print twice, because when the last line is read (up to the final newline, the last character in the input stream), EOF is not set.
EOF only gets set when you attempt to read past the last character!
So the code above loops once more, fgets() fails to read another line, sets EOF and leaves the contents of buffer untouched, which then gets printed again.
Instead, check whether fgets failed directly:
// GOOD CODE
while (fgets(buffer, BUFFER_SIZE, fp)) {
printf("%s", buffer);
}
Do not use gets(), ever
There is no way to use this function safely. Because of this, it has been removed from the language with the advent of C11.
Do not use fflush() on stdin or any other stream open for reading, ever
Many people expect fflush(stdin) to discard user input that has not yet been read. It does not do that. In plain ISO C, calling fflush() on an input stream has undefined behaviour. It does have well-defined behavior in POSIX and in MSVC, but neither of those make it discard user input that has not yet been read.
Usually, the right way to clear pending input is read and discard characters up to and including a newline, but not beyond:
int c;
do c = getchar(); while (c != EOF && c != '\n');
Do not use *scanf() for potentially malformed input
Many tutorials teach you to use *scanf() for reading any kind of input, because it is so versatile.
But the purpose of *scanf() is really to read bulk data that can be somewhat relied upon being in a predefined format. (Such as being written by another program.)
Even then *scanf() can trip the unobservant:
Using a format string that in some way can be influenced by the user is a gaping security hole.
If the input does not match the expected format, *scanf() immediately stops parsing, leaving any remaining arguments uninitialized.
It will tell you how many assignments it has successfully done -- which is why you should check its return code (see above) -- but not where exactly it stopped parsing the input, making graceful error recovery difficult.
It skips any leading whitespaces in the input, except when it does not ([, c, and n conversions). (See next paragraph.)
It has somewhat peculiar behaviour in some corner cases.
When *scanf() does not work as expected
A frequent problem with *scanf() is when there is an unread whitespace (' ', '\n', ...) in the input stream that the user did not account for.
Reading a number ("%d" et al.), or a string ("%s"), stops at any whitespace. And while most *scanf() conversion specifiers skip leading whitespace in the input, [, c and n do not. So the newline is still the first pending input character, making either %c and %[ fail to match.
You can skip over the newline in the input, by explicitly reading it e.g. via fgetc(), or by adding a whitespace to your *scanf() format string. (A single whitespace in the format string matches any number of whitespace in the input.)
Read, then parse
We just adviced against using *scanf() except when you really, positively, know what you are doing. So, what to use as a replacement?
Instead of reading and parsing the input in one go, as *scanf() attempts to do, separate the steps.
Read (part of) a line of input via fgets()
fgets() has a parameter for limiting its input to at most that many bytes, avoiding overflow of your buffer. If the input line did fit into your buffer completely, the last character in your buffer will be the newline ('\n'). If it did not all fit, you are looking at a partially-read line.
Parse the line in-memory
Especially useful for in-memory parsing are the strtol() and strtod() function families, which provide similar functionality to the *scanf() conversion specifiers d, i, u, o, x, a, e, f, and g.
But they also tell you exactly where they stopped parsing, and have meaningful handling of numbers too large for the target type.
Beyond those, C offers a wide range of string processing functions. Since you have the input in memory, and always know exactly how far you have parsed it already, you can walk back as many times you like trying to make sense of the input.
And if all else fails, you have the whole line available to print a helpful error message for the user.
Clean Up
Make sure you explicitly close any stream you have (successfully) opened. This flushes any as-yet unwritten buffers, and avoids resource leaks.
fclose(fp);

Using scanf for question, allow user to skip question

I'm working on a final project for a C programming course. The project is to create a database to store drug information. I've completed all of the elements and now it's down to fine tuning everything.
There's a requirement in the project in the function where the user can change the drugs information. In the requirement, the user should be able to skip a field by hitting enter. For example, the user can change the producer of the drug and the quantity. If the user didn't want to change the producer, they'd hit enter and move onto the quantity.
I've looked around the internet and was able to let the user skip entering a string for the producer. However, I cannot get it work with an integer.
This is what I used so that the user can skip entering a string:
scanf("%30[^\n]", fentry[found].producer);
For clarity sake, fentry.producer is a string with 30 characters and found is an integer variable.
I've tried doing something similar with the integer input (EDIT: By integer input, I meant the one to enter the quantity, not the 'found' varible). It will let you skip entering something, but if you do enter something, it stores a random value.
Anyone know how to go about this?
Rather than using scanf(), a better way to get interactive input from a user is to use fgets(). This function gets a complete line of input up to where the user presses Enter, and returns it so you can parse it.
You can then use sscanf() to read the information out of the line, if you like. sscanf() works almost exactly like scanf() except it reads data from a string rather than from standard input.
Have you initialized the integer to 0 before the scanf?
What about this?
char s[50];
fgets(s, 50, stdin);
if(s[0] != '\n') {
fentry[found].producer = atoi(s);
}
You have to remove all spaces from s though.
scanf("%29s", fentry[found].producer); while('\n'!=getchar());
This works at all, is strictly ANSI C conform, max. portable.
Input buffer is cleared, chararray-length is supported, chararray contains ever a (terminating) '\0' (no initialization needed) and your array contains never the '\n' from user input.
I suspect the keyboard buffer is causing the problems in your code. The first time you call the code, it will read a maximum of 30 characters - so long as they are not the NEWLINE character. They will terminate when they read the NEWLINE. The NEWLINE will remain in the buffer.
However... if you call scanf in the same way again, the NEWLINE from the previous call will still be in the keyboard buffer. This will be read before requesting more input from the keyboard. The NEWLINE will immediately fail the scanf pattern, the char array will not be updated, and you will be left to a char pointer that points to an uninitialized area of memory.

why gets() is not working?

I am programming in C in Unix,
and I am using gets to read the inputs from keyboard.
I always get this warning and the program stop running:
warning: this program uses gets(), which is unsafe.
Can anybody tell me the reason why this is happening?
gets is unsafe because you give it a buffer, but you don't tell it how big the buffer is. The input may write past the end of the buffer, blowing up your program fairly spectacularly. Using fgets instead is a bit better because you tell it how big the buffer is, like this:
const int bufsize = 4096; /* Or a #define or whatever */
char buffer[bufsize];
fgets(buffer, bufsize, stdin);
...so provided you give it the correct information, it doesn't write past the end of the buffer and blow things up.
Slightly OT, but:
You don't have to use a const int for the buffer size, but I would strongly recommend you don't just put a literal number in both places, because inevitably you'll change one but not the other later. The compiler can help:
char buffer[4096];
fgets(buffer, (sizeof buffer / sizeof buffer[0]), stdin);
That expression gets resolved at compile-time, not runtime. It's a pain to type, so I used to use a macro in my usual set of headers:
#define ARRAYCOUNT(a) (sizeof a / sizeof a[0])
...but I'm a few years out of date with my pure C, there's probably a better way these days.
As mentioned in the previous answers use fgets instead of gets.
But it is not like gets doesn't work at all, it is just very very unsafe. My guess is that you have a bug in your code that would appear with fgets as well so please post your source.
EDIT
Based on the updated information you gave in your comment I have a few suggestions.
I recommend searching for a good C tutorial in your native language, Google is your friend here. As a book I would recommend The C Programming Language
If you have new information it is a good idea to edit them into your original post, especially if it is code, it will make it easier for people to understand what you mean.
You are trying to read a string, basically an array of characters, into a single character, that will of course fail. What you want to do is something like the following.
char username[256];
char password[256];
scanf("%s%s", username, password);
Feel free to comment/edit, I am very rusty even in basic C.
EDIT 2 As jamesdlin warned, usage of scanf is as dangerous as gets.
man gets says:
Never use gets(). Because it is
impossible to tell without knowing the
data in advance how many characters
gets() will read, and because
gets() will continue to store
characters past the end of the buffer,
it is extremely dangerous to use. It
has been used to break computer
security. Use fgets() instead.
gets() is unsafe. It takes one parameter, a pointer to a char buffer. Ask yourself how big you have to make that buffer and how long a user can type input without hitting the return key.
Basically, there is no way to prevent a buffer overflow with gets() - use fgets().

Resources