6

All of this is in C89, not C99.

I have a constant.

#define MAX_NAME_LEN 256

I want to use it as the max field width in fscanf, sort of like this.

fscanf(input, "%256s", name);

But I want to use the MAX_NAME_LEN instead of the literal 256 for sake of good style. I have tried all of

fscanf(input, "%MAX_NAME_LENs", name);

char* max_name_len_str = malloc(16 * sizeof *max_name_len_str);
sprintf(max_name_len_str, "%d", MAX_NAME_LEN);
fscanf(input, "%" max_name_len_str "s", name);
free(max_name_len_str);

//works with printf, but has different meaning in scanf
fscanf(input, "%*s", MAX_NAME_LEN, name);

fscanf(input, "%%ds", MAX_NAME_LEN, name);

without success.

char* nameFormat = malloc(16 * sizeof *nameFormat); //I assume I don't ever want more than 10^13 characters in a name
sprintf(nameFormat, "%s%ds", "%", MAX_NAME_LEN);
fscanf(input, nameFormat, name);
free(nameFormat);

does work, but is ungainly as all get out. Is there a more elegant solution?

SonicN
  • 163
  • 7
  • @weather-vane Your deleted answer was off by 1. size vs length issue. – chux - Reinstate Monica Jun 21 '17 at 20:59
  • 1
    @chux: it was missing a cast too: `(rsize_t)(MAX_NAME_LEN + 1)`, or better `sizeof(name)`. – chqrlie Jun 21 '17 at 21:00
  • 1
    @WeatherVane: your deleted answer is useful: `fscanf_s` is not C89, but it is obviously available on the most common non C99 compiler: MSVC. – chqrlie Jun 21 '17 at 21:05
  • agreed, it might be useful for others looking for the same answer. – blackghost Jun 21 '17 at 21:10
  • 2
    [How to prevent `scanf()` causing a buffer overflow?](https://stackoverflow.com/questions/1621394/) recommends the 'ungainly as all get out' solution because it works, more or less regardless of how `MAX_NAME_LEN` expands. Almost any other solution puts limits on the permissible/acceptable values (expansions) of `MAX_NAME_LEN`. It depends on how general and robust you need the code to be. If you only need it to work with your code at the moment, you can hack together a string with the macro value. If you need a general solution, then you probably need the `sprintf()` method. – Jonathan Leffler Jun 21 '17 at 21:21

4 Answers4

5

You can use this macro:

#define STRINGIFY(X) INDIRECT(X)
#define INDIRECT(X) #X

like this:

#define MAX 10
puts("%"STRINGIFY(MAX)"d");

which will print %10d.

In your case it would be

char name[MAX_NAME_LEN + 1];
fscanf(input, "%"STRINGIFY(MAX_NAME_LEN)"s", name);

The # in the macro stringifies (makes " around it) whatever is after it. So the macro must be the decimal number only.
The level of indirection is needed to expand MAX_NAME_LEN to 256. INDIRECT(MAX_NAME_LEN) would expand to "MAX_NAME_LEN".

mch
  • 9,424
  • 2
  • 28
  • 42
3

1st simplification.

// Unlikely need to malloc short array
char nameFormat[16]; // I assume I don't ever want more than 10^13 characters  
sprintf(nameFormat, "%%%ds", MAX_NAME_LEN);  // %% prints a %
fscanf(input, nameFormat, name);

2nd offering or better, use stringify @DevilaN.
Note that the size of the buffer needs to be at least 1 more than the "%s" width.

#define MAX_NAME_LEN 256
#define MAX_NAME_LEN_STR "256"

char name[MAX_NAME_LEN + 1];
fscanf(input, "%" MAX_NAME_LEN_STR "s", name);

3rd, use fgets() (my preference), Of course this reads a line and not a sequence of non-white-space characters. This often does meet that higher level goal though.

#define MAX_NAME_LEN 256
char name[MAX_NAME_LEN + 2];
fgets(name, sizeof name, input);
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

You will struggle with fscanf. I don't know better solution than proposed by you if you are bound with fscanf.

Personally I would switch to fgets like in this simple example:

#define MAX_NAME_LEN 25

int main()
{
        char string[MAX_NAME_LEN];
        fgets(string,MAX_NAME_LEN,stdin);

        printf("%s", string);

}
DevilaN
  • 1,317
  • 10
  • 21
0

As a reference, following is a fscanf_s() approach to prevent overflow as used by earlier version of VS that did not attain C99 compliance. An additional unsigned argument follows name[] indicator the size of the buffer. This appears to apply to VS 2015 too.

char name[MAX_NAME_LEN + 1];
fscanf_s(input, "%s", name, (unsigned) sizeof name);

C11 includes fscanf_s() in its Annex K which is normative, for information only, so a compliant compiler need not implement it. Yet in C11, it is specified as needing a rsize_t/size_t argument .

fscanf_s(input, "%s", name, sizeof name);

If input is more than 256 non-white-space characters, a matching failure occurs. In this case, fscanf() return EOF. I think the VS version does the same. I think both will consume all the non-white-space in the stream, even if there is not enough room to store them all.


Either of these is a bit different than the below in that extra text is not read into name[], so no buffer overflow. Any extra text remains for the next input function call. fscanf() returns 1.

fscanf(input, "%256s", name);
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256