Typical system V implementations (as well as older BSD implementations) have
an unsafe tmpfile(). This is true for Solaris (before 2.6 when we fixed it)
and in IRIX (last time I looked). Perhaps we can check and compile a list?
> If the string exceeds the buffer length, errno is set to ENOMEM
> and the negative string length is returned. The first len-1
> characters are placed in the buffer.
> (Add one for the terminating '\0' when allocating a buffer!)
This is also a problem as many implementations expect snprintf() to
always return a positive count; either bytes required (possible > n)
That's one of the more difficult things of implementing a new function;
what willbe teh exact semantics? (the 2.5/2.5.1 __* function has slightly
different semantcis than the public 2.6 *snprintf())
Here's one I did and it also uses temporary files, but it uses
"mkstemp()" which should be save.
BTW, mkstemp() always uses open(,, 0600) so you get private tmpfiles;
tmpfile() is required to use the umask() in setting the mode. Depending
on how this is implemented, there's a window in which a tmpfile() can be opened
by other processes.
A naive reimplementation of tmpfile() using mkstemp() fails the standard
conformance tests. (s = mkstemp(template); unlink(template); return
fdopen(s,"w+)). Solaris 2.6 does use mkstemp() but does an fchmod() after
unlink()ing the file so there's no race condition either.
Standard are stupid sometimes.
/*
* snprintf() quicky using temporary files.
* Not stress tested, but it seems to work.
*
* Returns the number of bytes that would have been output by printf.
* Does not check whether a negative value if passed in for n (as it's unsigned);
* some implementations cast n to int and than compare with 0.
*
* Casper Dik (Casper.Dik@Holland.Sun.COM)
*/
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
int snprintf(char * buf, size_t n, const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = vsnprintf(buf, n, fmt, ap);
va_end(ap);
return ret;
}
static char template[] = "/tmp/snprintfXXXXXX";
int vsnprintf(char *buf, size_t n, const char *fmt, va_list ap)
{
char templ[sizeof(template)];
int s;
int ret, cnt = 0, nread;
FILE *fp;
strcpy(templ,template);
s = mkstemp(templ);
if (s < 0)
return -1;
(void) unlink(templ);
fp = fdopen(s, "w+");
if (fp == NULL) {
close(s);
return -1;
}
ret = vfprintf(fp, fmt, ap);
if (ret < 0 || fseek(fp, 0, SEEK_SET) < 0) {
fclose(fp);
return -1;
}
while (cnt < n && (nread = fread(buf + cnt, 1, n - cnt, fp)) > 0)
cnt += nread;
buf[cnt-1] = '\0'; /* cnt is atmost n */
fclose(fp);
return ret;
}