Maximum line length with scanf & # 8594; ANSI C

I have:

#define MAX_STR_LEN 100 

and I want to put scanf in the template so that I can control the length of the string:

 scanf("%100[^\n]s",sometext) 

I tried:

 scanf("%MAX_STR_LEN[^\n]s",sometext) scanf("%"MAX_STR_LEN"[^\n]s",sometext) scanf("%",MAX_STR_LEN,"[^\n]s",sometext) 

And it didn’t work. I just want to avoid buffer overflows because "sometext" stands out for malloc(MAX_STR_LEN) ...

Any ideas?

+6
source share
5 answers

I was not happy with any of these solutions, so I researched further and discovered the GNU GCC macro stringification

which can be used as:

 #define XSTR(A) STR(A) #define STR(A) #A #define MAX_STR_LEN 100 scanf("%"XSTR(MAX_STR_LEN)"[^\n]s", sometext) 

Maybe VS2010 offers something similar?

+10
source

I just want to avoid buffer overflows

Then do not use scanf() . For everyone.

If you are viewing lines of text, do not #define MAX_STR . You can use LINE_MAX in <limits.h> (if you are targeting POSIX compatible systems):

 char buf[LINE_MAX]; fgets(buf, sizeof(buf), stdin); 

gotta do the trick.

+7
source

As almost everyone says, it is better to use fgets(..., stdin) to solve this problem.

In the following link, I suggested a safe and proper technique that will allow you to replace scanf() a safer method using a solid macro :

A macro that safely replaces scanf ()

The macro I proposed (working with compatible C99 compilers) is safe_scanf() , as shown in the following program:

 #include <stdio.h> #define safe_scanf(fmt, maxb, ...) { \ char buffer[maxb+1] = { [maxb - 1] = '\0' }; \ fgets(buffer, maxb+1, stdin); \ if ((buffer[maxb - 1] != '\0') && (buffer[maxb - 1] != '\n')) \ while(getchar() != '\n') \ ; \ sscanf(buffer, fmt, __VA_ARGS__); \ } #define MAXBUFF 20 int main(void) { int x; float f; safe_scanf("%d %g", MAXBUFF+1, &x, &f); printf("Your input was: x == %d\t\tf == %g", x, f); return 0; } 

You need to adjust the MAXBUFF value according to your needs ...
Although the safe_scanf() macro is pretty strong,
There is some weakness in using the macro approach:
Lack of type checking for parameters, lack of "return" values ​​(which are hardly different from the "true" scanf() function, which returns int , with valuable information for checking errors), etc.
All these problems have a solution, but this is part of a different topic ...

Perhaps the most accurate solution is to define the my_scanf() function with a variable number of parameters by calling the stdarg.h library, combined with the combination of fgets() and vsscanf() . Here you have the code:

 #include <stdio.h> #include <stdarg.h> int my_scanf(const char* fmt, const unsigned int maxbuff, ...) { va_list ptr; int ret; if (maxbuff <= 0) return EOF; /* Bad size for buffer[] */ char buffer[maxbuff+1]; buffer[maxbuff-1] = '\0'; /* Quick buffer cleaning... */ if (fgets(buffer, maxbuff+1, stdin) == NULL) return EOF; /* Error detected */ else { if ((buffer[maxbuff-1] != '\n') && (buffer[maxbuff-1] != '\0')) /* Condition logically equivalent to: fgets() has not reached an '\n' */ while (getchar() != '\n') ; /* "Flushing" stdin... */ va_start(ptr, maxbuff); ret = vsscanf(buffer, fmt, ptr); va_end(ptr); return ret; } } #define MAXBUFF 20 int main(void) { int x; float z; int scanf_ret = my_scanf("%d %g", MAXBUFF, &x, &z); printf("\nTest:\nx == %d\nz == %g\n scanfret == %d", x, z, scanf_ret); getchar(); return 0; } 

The my_scanf () function has a prototype

 int my_scanf(const char* fmt, const int maxbuff, ...); 

It accepts an fmt format string, which behaves just like any other scanf() -like. The second parameter is the maximum number of characters that will be effectively received from standard input (keyboard).
The return value is an int that is EOF if MAXBUFF not meaningful, or an input error has occurred. If a non-negative value is returned, then it will be returned by the standard sscanf() or vsscanf() functions.

Inside the function, MAXBUFF incremented by 1 because fgets() creates space for the extra character '\ 0'.
Non- MAXBUFF MAXBUFF values MAXBUFF immediately discarded.
fgets() will read a line read from stdin (keyboard) with no more than MAXBUFF , including '\ n'.
If the user entered a very long line, then it will be truncated, and in order to discard all characters until the next "\ n" ( ENTER ), some kind of "flash" mechanism is necessary. If not, the next keyboard reading may have older characters not wanting at all.
The flushing condition is that fgets() does not reach "\ n" after reading stdin .
This is the case if and only if buffer[maxbuff - 1] not equal to '\ 0' and '\ n'.
( Check it out! )
Finally, a suitable combination of stdarg.h macros and the vsscanf() function are used to process a list of variables.

+4
source

Recommend the approach fgets(buffer, sizeof(buffer), stdin) .

If you still want to use scanf (), you can create its format at runtime.

 #define MAX_STR_LEN 100 char format[2 + sizeof(size_t)*3 + 4 + 1]; // Ugly magic # sprintf(format, " %%%zu[^\n]", (size_t) MAX_STR_LEN); scanf(format, sometext); 

or redefine MAX_STR_LEN as a string

 #define MAX_STR_LEN "100" scanf(" %" MAX_STR_LEN "[^\n]", sometext); 

Still recommend fgets() .
Note fgets() will put leading spaces and trailing \n in your buffer, while " %[^\n]" will not.
BTW: The final s in your formats is unlikely to do what you think.

0
source

What about

 scanf("%.*[^\n]s", MAX_STR_LEN, sometext) 
-3
source

Source: https://habr.com/ru/post/948120/


All Articles