pre-determine the length of buffer for `sprinf()' (pre-c99)...

C

Chris M. Thomasson

Chris M. Thomasson said:
Here is one that attempts to emulate `asprintf()' in pre-c99:

http://clc.pastebin.com/f63d41ab5

I forgot to determine if the call to `vfprintf()' actually succeeded! Very
bad mistake... Here is fixed version:
__________________________________________________________________
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <assert.h>




static FILE* g_stdnull = NULL;




int
stdnull_init(void)
{
if (! g_stdnull)
{
g_stdnull = fopen("/dev/nul", "r+");
if (! g_stdnull)
{
g_stdnull = fopen("nul", "r+");
if (! g_stdnull)
{
g_stdnull = tmpfile();
if (! g_stdnull)
{
return 0;
}
}
}
}

return 1;
}


void
stdnull_deinit(void)
{
if (g_stdnull)
{
fclose(g_stdnull);
g_stdnull = NULL;
}
}


char*
my_asprintf(char const* format,
...)
{
int size;
va_list args;

va_start(args, format);
size = vfprintf(g_stdnull, format, args);
va_end(args);

fseek(g_stdnull, 0, SEEK_SET);

if (size > -1)
{
char* buffer = malloc(size + 1);

if (buffer)
{
int xsize;

va_start(args, format);
xsize = vsprintf(buffer, format, args);
va_end(args);

assert(size == xsize);

if (size == xsize)
{
return buffer;
}

/* free(buffer); */

abort();
}
}

return NULL;
}




int main(void)
{
if (stdnull_init())
{
int status = EXIT_SUCCESS;
int p1 = 0, p2 = 0;
char* buffer;

printf("current state == ([p1] == %d/[p2] == %d)\n", p1, p2);

buffer = my_asprintf("([++p1] == %d/[p2++] == %d) - %s",
++p1, p2++, "Hello World!");

if (buffer)
{
printf("my_asprintf buffer == %s\n", buffer);
printf("current state == ([p1] == %d/[p2] == %d)\n", p1,
p2);
free(buffer);
}

else
{
status = EXIT_FAILURE;
}

stdnull_deinit();

return status;
}

else
{
fprintf(stderr, "Could not open the NUL device!");
}

return EXIT_FAILURE;
}
__________________________________________________________________
 
K

Keith Thompson

Chris M. Thomasson said:
int
stdnull_init(void)
{
if (! g_stdnull)
{
g_stdnull = fopen("/dev/nul", "r+");
if (! g_stdnull)
{
g_stdnull = fopen("nul", "r+");
if (! g_stdnull)
{
g_stdnull = tmpfile();
if (! g_stdnull)
{
return 0;
}
}
}
}

return 1;
}
[...]

What system has a "/dev/nul"? (It's "/dev/null".)

On the other hand, you've provided a way to test the use of tmpfile().
 
C

Chris M. Thomasson

Keith Thompson said:
Chris M. Thomasson said:
int
stdnull_init(void)
{
if (! g_stdnull)
{
g_stdnull = fopen("/dev/nul", "r+");
if (! g_stdnull)
{
g_stdnull = fopen("nul", "r+");
if (! g_stdnull)
{
g_stdnull = tmpfile();
if (! g_stdnull)
{
return 0;
}
}
}
}

return 1;
}
[...]

What system has a "/dev/nul"? (It's "/dev/null".)

Typo. Humm, funny that `/dev/nul' works for me on WinXP.



On the other hand, you've provided a way to test the use of tmpfile().

;^)




_____________________________________________________________________
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <assert.h>




static FILE* g_stdnull = NULL;




int
stdnull_init(void)
{
if (! g_stdnull)
{
g_stdnull = fopen("/dev/null", "r+");
if (! g_stdnull)
{
g_stdnull = fopen("/dev/nul", "r+");
if (! g_stdnull)
{
g_stdnull = fopen("nul", "r+");
if (! g_stdnull)
{
g_stdnull = tmpfile();
if (! g_stdnull)
{
return 0;
}
}
}
}
}

return 1;
}


void
stdnull_deinit(void)
{
if (g_stdnull)
{
fclose(g_stdnull);
g_stdnull = NULL;
}
}


char*
my_asprintf(char const* format,
...)
{
int size;
va_list args;

va_start(args, format);
size = vfprintf(g_stdnull, format, args);
va_end(args);

fseek(g_stdnull, 0, SEEK_SET);

if (size > -1)
{
char* buffer = malloc(size + 1);

if (buffer)
{
int xsize;

va_start(args, format);
xsize = vsprintf(buffer, format, args);
va_end(args);

assert(size == xsize);

if (size == xsize)
{
return buffer;
}

/* free(buffer); */

abort();
}
}

return NULL;
}




int main(void)
{
if (stdnull_init())
{
int status = EXIT_SUCCESS;
int p1 = 0, p2 = 0;
char* buffer;

printf("current state == ([p1] == %d/[p2] == %d)\n", p1, p2);

buffer = my_asprintf("([++p1] == %d/[p2++] == %d) - %s",
++p1, p2++, "Hello World!");

if (buffer)
{
printf("my_asprintf buffer == %s\n", buffer);
printf("current state == ([p1] == %d/[p2] == %d)\n", p1,
p2);
free(buffer);
}

else
{
status = EXIT_FAILURE;
}

stdnull_deinit();

return status;
}

else
{
fprintf(stderr, "Could not open the NUL device!");
}

return EXIT_FAILURE;
}

_____________________________________________________________________
 
K

Keith Thompson

Chris M. Thomasson said:
What system has a "/dev/nul"? (It's "/dev/null".)

Typo. Humm, funny that `/dev/nul' works for me on WinXP.
On the other hand, you've provided a way to test the use of tmpfile().
[...]
int
stdnull_init(void)
{
if (! g_stdnull)
{
g_stdnull = fopen("/dev/null", "r+");
if (! g_stdnull)
{
g_stdnull = fopen("/dev/nul", "r+");
if (! g_stdnull)
{
g_stdnull = fopen("nul", "r+");
if (! g_stdnull)
{
g_stdnull = tmpfile();
if (! g_stdnull)
{
return 0;
}
}
}
}
}

return 1;
}
[...]

If "/dev/nul" works on WinXP, it's probably just ignoring the
directory and treating any file named "nul" or "NUL" as a null device.
I don't know of any system where "/dev/nul" is really what you want.

BTW, here's how I might write the above to avoid the marching
indentation (untested code):

int stdnull_init(void)
{
if (g_stdnull != NULL) {
return 1;
}
else if ((g_stdnull = fopen("/dev/null", "r+")) != NULL) {
return 1;
}
else if ((g_stdnull = fopen("nul", "r+")) != NULL) {
return 1;
}
else if ((g_stdnull = tmpfile()) != NULL) {
return 1;
}
else {
return 0;
}
}

If I were going to try more than two names, I might loop over
an array.

BTW, I'm not sure I'd fall back to tmpfile() if neither /dev/null
nor nul works -- or if I did I'd give the function a different name
that doesn't suggest it works with a general-purpose null device.
 
S

Stephen Sprunk

Chris said:
Typo. Humm, funny that `/dev/nul' works for me on WinXP.

DOS and its derivatives (e.g. Windows) consider "NUL" to be a magic
filename* regardless of case or any path that precedes it. The
filesystem code also allows use of '/' in place of '\\', though many
user-space applications don't. Therefore, "/dev/nul" is the same thing
as "NUL" on these systems.

S

(* Along with CON, AUX, PRN, COM1-COM4, LPT1, and a few others.)
 
C

Chris M. Thomasson

Keith Thompson said:
Chris M. Thomasson said:
Typo. Humm, funny that `/dev/nul' works for me on WinXP.
[...]

If "/dev/nul" works on WinXP, it's probably just ignoring the
directory and treating any file named "nul" or "NUL" as a null device.
I don't know of any system where "/dev/nul" is really what you want.
[...]

BTW, I'm not sure I'd fall back to tmpfile() if neither /dev/null
nor nul works -- or if I did I'd give the function a different name
that doesn't suggest it works with a general-purpose null device.

Good point. Perhaps I should just allow the user to specify a file:
_________________________________________________________________
int
stdnull_init(FILE* file)
{
if (g_stdnull)
{
return 1;
}

else if (file)
{
g_stdnull = file;
return 1;
}

else if ((g_stdnull = fopen("/dev/null", "r+")))
{
return 1;
}

else if ((g_stdnull = fopen("/nul", "r+")))
{
return 1;
}

return 0;
}
_________________________________________________________________




Then user-code could do something like this:
_________________________________________________________________
int
my_stdnull_init(void)
{
if (stdnull_init(NULL))
{
if (! stdnull_init(tmpfile()))
{
return 0;
}
}

return 1;
}
_________________________________________________________________




http://clc.pastebin.com/f1dcef8e4
 
K

Keith Thompson

Chris M. Thomasson said:
Keith Thompson said:
Chris M. Thomasson said:
news:[email protected]... [...]
What system has a "/dev/nul"? (It's "/dev/null".)

Typo. Humm, funny that `/dev/nul' works for me on WinXP.

On the other hand, you've provided a way to test the use of tmpfile().
[...]

If "/dev/nul" works on WinXP, it's probably just ignoring the
directory and treating any file named "nul" or "NUL" as a null device.
I don't know of any system where "/dev/nul" is really what you want.
[...]

BTW, I'm not sure I'd fall back to tmpfile() if neither /dev/null
nor nul works -- or if I did I'd give the function a different name
that doesn't suggest it works with a general-purpose null device.

Good point. Perhaps I should just allow the user to specify a file:
_________________________________________________________________
int
stdnull_init(FILE* file)
{
if (g_stdnull)
{
return 1;
}

else if (file)
{
g_stdnull = file;
return 1;
}

else if ((g_stdnull = fopen("/dev/null", "r+")))
{
return 1;
}

else if ((g_stdnull = fopen("/nul", "r+")))
{
return 1;
}

return 0;
}
[snip]

I'm a bit concerned about the possiblity that, on a system other than
Unix/Linux or Windows, "nul" or "/nul" might be a valid file name, but
not one you'd want to use.

Sometimes when you're doing system-specific things (even though you're
trying to hide them), it might be better just to bite the bullet and
use system-specific code:

#if defined THIS_IS_UNIX_OR_LINUX
#define NULL_DEVICE "/dev/null"
#elif defined THIS_IS_WINDOWS
#define NULL_DEVICE "NUL"
#else
#undef NULL_DEVICE
#endif

#ifdef NULL_DEVICE
g_stdnull = fopen(NULL_DEVICE", "r+);
...
#else
#error "Need to define NULL_DEVICE for this platform"
#endif

Replace THIS_IS_UNIX_OR_LINUX and THIS_IS_WINDOWS with appropriate
macros that are actually defined on the respective systems, and
consider replacing #error with some other fallback strategy.

Twisty mazes of #ifdefs can be ugly, but you can usually restrict the
ugliness to a very small portion of your code.
 
C

Chris M. Thomasson

Keith Thompson said:
Chris M. Thomasson said:
Keith Thompson said:
[...]
What system has a "/dev/nul"? (It's "/dev/null".)

Typo. Humm, funny that `/dev/nul' works for me on WinXP.

On the other hand, you've provided a way to test the use of tmpfile().
[...]

If "/dev/nul" works on WinXP, it's probably just ignoring the
directory and treating any file named "nul" or "NUL" as a null device.
I don't know of any system where "/dev/nul" is really what you want.
[...]

BTW, I'm not sure I'd fall back to tmpfile() if neither /dev/null
nor nul works -- or if I did I'd give the function a different name
that doesn't suggest it works with a general-purpose null device.

Good point. Perhaps I should just allow the user to specify a file:
_________________________________________________________________
[...]
[snip]

I'm a bit concerned about the possiblity that, on a system other than
Unix/Linux or Windows, "nul" or "/nul" might be a valid file name, but
not one you'd want to use.

That certainly could happen and it would result in the loss of data.



Sometimes when you're doing system-specific things (even though you're
trying to hide them), it might be better just to bite the bullet and
use system-specific code:

#if defined THIS_IS_UNIX_OR_LINUX
#define NULL_DEVICE "/dev/null"
#elif defined THIS_IS_WINDOWS
#define NULL_DEVICE "NUL"
#else
#undef NULL_DEVICE
#endif

#ifdef NULL_DEVICE
g_stdnull = fopen(NULL_DEVICE", "r+);
...
#else
#error "Need to define NULL_DEVICE for this platform"
#endif

Replace THIS_IS_UNIX_OR_LINUX and THIS_IS_WINDOWS with appropriate
macros that are actually defined on the respective systems, and
consider replacing #error with some other fallback strategy.

Agreed. This is a heck of a lot safer.



Twisty mazes of #ifdefs can be ugly, but you can usually restrict the
ugliness to a very small portion of your code.

Indeed.
 
N

Nobody

DOS and its derivatives (e.g. Windows) consider "NUL" to be a magic
filename* regardless of case or any path that precedes it.

.... or any extension that follows it; you can't have a file named
e.g. nul.txt.

It took Microsoft five iterations to convince IIS not to open the serial
port whenever someone requested "http://example.com/com1.html" or
equivalent.

First attempt: filter out specific filenames; fails when characters are
URL-encoded. Subsequent attempts were a game of cat and mouse between
people discovering new ways of persuading IIS to open */com1.* and MS
fixing specific cases. Eventually MS realised that they would keep getting
bitten by this and finally implemented an "is this file a device?" check
immediately before opening the file.
(* Along with CON, AUX, PRN, COM1-COM4, LPT1, and a few others.)

Worse, the set is open-ended; drivers can add their own magic filenames.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,438
Messages
2,571,699
Members
48,796
Latest member
Greg L.
Top