-1

While contributing to exim, I saw many values where hard-coded :

uschar filebuffer[256];
(void)sprintf(CS filebuffer, "%.256s.db", filename);
 rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups,
  "dbm", errmsg);
if (rc < 0)        /* stat() failed */
  {
  (void)sprintf(CS filebuffer, "%.256s.dir", filename);
  rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups,
    "dbm", errmsg);
   if (rc == 0)     /* x.dir was OK */
     {
     (void)sprintf(CS filebuffer, "%.256s.pag", filename);
     rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups,
       "dbm", errmsg);
     }
   }
 }

As the code isn’t windows specific, every256values should be converted toPATH_MAX.

I know that expanding macros inside quoted strings isn’t possible, but that string concatenation is trivial :

#define STR "string"
size_t len=strlen("part"STR"part 2");

However, things like :

"%."PATH_MAX".db"

Shouldn’t work becausePATH_MAXexpands to an integer, not a string.
So is there a way to do this without calling a function that convert integers to C strings ?

user2284570
  • 2,891
  • 3
  • 26
  • 74
  • Why are you casting function calls instead of analyzing their return values? – Iharob Al Asimi May 30 '16 at 22:11
  • @iharob : because I didn’t wrote this piece of code. Just read the first line, it’s part of exim code I didn’t modified. Many things are done wrong : most of the code isn’t indented at all and there are even returns values of calls to`malloc()`which aren’t checked. – user2284570 May 30 '16 at 22:15

2 Answers2

3

The right way to do this is to use a * in your format string, which will cause it to take the value from your argument list. For example:

printf("%.*s\n", 3, "abcde");

This is equivalent to:

printf("%.3s\n", "abcde");

That way you can use PATH_MAX or any other value to control the format without having to worry about how they're defined (e.g., whether they contain parentheses or addition operators, etc.)

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
Tom Karzes
  • 22,815
  • 2
  • 22
  • 41
1

You can stringify a macro argument by using the # operator. But you need an indirect macro invocation to expand the argument:

#define Q(x) Q_(x)
#define Q_(x) #x

So you could do something like:

char filebuffer[PATH_MAX + 10];
sprintf(filebuffer, "%." Q(PATH_MAX)"s.db", filename);

The existing code limits the string sibstitution to 256 characters but then adds a file extension (and a NUL terminator) which will be a buffer overflow when the length is close to 256. I used an arbitrary 10-byte overallocation above, but it would be better to use a checked-length sprintf like snprintf. That would have the additional advantage of not requiring macro games.

rici
  • 234,347
  • 28
  • 237
  • 341
  • @user2284570 no, it would not. `#` stringifies the value attached to it. Doing it the way you show would produce `"%.PATH_MAX.db"` instead of `"%.256.db"`. You need to pass `PATH_MAX` to a macro to evaluate it to its value, and then stringify that value. – Remy Lebeau May 30 '16 at 22:33
  • @RemyLebeau: Actually, what OP wrote in that comment would stringify only the `(` (assuming that it appeared in a macro replacement, because at least in theory, # is an error outside of macro replacements. – rici May 30 '16 at 22:54
  • This should be the accepted answer, as it uses compile-time processing like the original. If you just would elaborate a bit more. – too honest for this site May 31 '16 at 00:28
  • @Olaf You're joking, right? What if `PATH_MAX` is defined as `(2*512)`? Then this solution cannot possibly work, ever. It's the wrong way to do it. – Tom Karzes May 31 '16 at 00:54
  • @tom: i agree that there are better solutions than relying on macros to be simple numbers. However, I don't believe length interpolation is really any better, since it depends on a manual computation of how many extra characters are required by the format string; in general, snprintf is the solution even if you have to find an implementation for systems so old that they lack it, of which there can't be that many any more. – rici May 31 '16 at 00:54
  • @TomKarzes: Good point, I really forgot about that. As an embedded engineer, I tend to accept such restrictions, because run-time (and often code/constant space) is precious. For the code shown, It still might be a suitable solution (better than magic values anyway). – too honest for this site May 31 '16 at 00:54
  • Anyway, it's always good to know how to stringify, and that was what the OP actually asked for. – rici May 31 '16 at 00:55