A C showstopper

S

spinoza1111

To develop the "unlimited string" processor which I discuss in the
thread A C Adventure, I have to recreate my utilities library of 1991,
which I did because then (and now) one needs to use the printf
approach to format data: but if one needs, as one often does need, to
format to storage, sprintf has a built in danger that as far as I know
(and correct me if I'm wrong) nobody has or will fix inside of C.

The problem is that any sprintf whatsoever, insofar as it formats
strings, has no control over string length. Most code I've seen
decides to create some silly "buffer" of some silly size.

But in

sprintf(buf, "%s\n", ptr)

characters past the allocated end of "buf" may be overwritten.

The C99 solution (limit the number of characters) is extraordinarily
poor and reinforces my Dim View of the ethics of people in that
effort: some of them participated in the "get Schildt" campaign, and
none of them seems to have been competent working programmers.

In 1991, I implemented the GNU solution, but I don't have the code
anymore. This is asprintf; its contract is to always allocate enough
memory to hold the final string.

There are two ways of implementing asprintf. Either iterate formatting
until everything's formatted, reallocating larger and larger blocks of
memory (and copying previously formatted output characters). Or make a
pass through the data without doing output to find the size needed. My
1991 solution was the former. Today, when I write the code, I shall
use the latter solution.

The GNU solution , not the C99 solution, is the one that occurs to the
competent programmer: the C99 solution cuts off the user at the knees
blindly and is the sort of solution that occurs to managers...not
competent programmers.

However, in 1991, I felt uncomfortable to bill a user for implementing
this type of code. It should be available to applications programs as
it is in most other languages.

This alone demonstrates that C is not a viable programming language
anymore, and may never have been, for applications other than writing
operating systems and compilers.

I'm suspending work on the "adventure" project and shall return when
I'm not so disgusted as I am now.
 
R

Rui Maciel

spinoza1111 said:
The problem is that any sprintf whatsoever, insofar as it formats
strings, has no control over string length.

What about snprintf() ?


Rui Maciel
 
F

Flash Gordon

Rui said:
What about snprintf() ?

He refers to it, but does not understand it well enough to see that it
provides exactly the tool he needs to implement what he wants whilst
also providing the flexibility to do other things.
 
S

Sri Harsha Dandibhotla

What about snprintf() ?

Rui Maciel

"The problem is that any sprintf whatsoever, insofar as it formats
strings, has no control over string length. Most code I've seen
decides to create some silly "buffer" of some silly size. "

you still have to create some silly buffer of some silly size.
 
C

Chris M. Thomasson

spinoza1111 said:
To develop the "unlimited string" processor which I discuss in the
thread A C Adventure, I have to recreate my utilities library of 1991,
which I did because then (and now) one needs to use the printf
approach to format data: but if one needs, as one often does need, to
format to storage, sprintf has a built in danger that as far as I know
(and correct me if I'm wrong) nobody has or will fix inside of C.

The problem is that any sprintf whatsoever, insofar as it formats
strings, has no control over string length. Most code I've seen
decides to create some silly "buffer" of some silly size.

But in

sprintf(buf, "%s\n", ptr)

characters past the allocated end of "buf" may be overwritten.
[...]

Perhaps something like this may be of service:
_________________________________________________________________
#include <stdio.h>


int main(void)
{
char buffer[6] = { '\0' };
char const string[] = "Hello";

size_t size = sprintf(buffer, "%.1s", string);
printf("%lu - %s\n", (unsigned long int)size, buffer);

size = sprintf(buffer, "%.2s", string);
printf("%lu - %s\n", (unsigned long int)size, buffer);

size = sprintf(buffer, "%.3s", string);
printf("%lu - %s\n", (unsigned long int)size, buffer);

size = sprintf(buffer, "%.4s", string);
printf("%lu - %s\n", (unsigned long int)size, buffer);

size = sprintf(buffer, "%.5s", string);
printf("%lu - %s\n", (unsigned long int)size, buffer);

return 0;
}
_________________________________________________________________
 
F

Flash Gordon

Sri said:
"The problem is that any sprintf whatsoever, insofar as it formats
strings, has no control over string length. Most code I've seen
decides to create some silly "buffer" of some silly size. "

you still have to create some silly buffer of some silly size.

Now read what Rui wrote. snprintf does not have that problem and can
even be used to find out the size of the buffer you do need.
 
S

Sri Harsha Dandibhotla

void *create_some_silly_buffer(size_t some_silly_size)
{
        return malloc(some_silly_size);

}

:)

I like his asprintf better though..

if I were to concatenate multiple strings with my own formatted
strings thrown in middle, it is easier to simply use asprintf rather
than calculate size of each string and also my own formatted strings..

like for instance
sprintf(buf, "%s number1 is : %d number2 is %d %s", str1, random1,
random2, str2);
the numbers can have any number of digits.
 
C

Chris M. Thomasson

spinoza1111 said:
To develop the "unlimited string" processor which I discuss in the
thread A C Adventure, I have to recreate my utilities library of 1991,
which I did because then (and now) one needs to use the printf
approach to format data: but if one needs, as one often does need, to
format to storage, sprintf has a built in danger that as far as I know
(and correct me if I'm wrong) nobody has or will fix inside of C.

The problem is that any sprintf whatsoever, insofar as it formats
strings, has no control over string length. Most code I've seen
decides to create some silly "buffer" of some silly size.

But in

sprintf(buf, "%s\n", ptr)

characters past the allocated end of "buf" may be overwritten.

[...]

Check out this funny little hack:
_________________________________________________________________________
#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", "w");
if (! g_stdnull)
{
g_stdnull = fopen("nul", "w");
if (! g_stdnull)
{
return 0;
}
}
}
return 1;
}


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


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

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




int main(void)
{
if (stdnull_init())
{
int size = get_sprintf_size("%s %d\n", "Hello World", 123);

if (size) {
int xsize;
char* buffer = malloc(size + 1);

buffer[0] = '\0';
xsize = sprintf(buffer, "%s %d\n", "Hello World", 123);
assert(size == xsize);
printf("size(%d/%d) - %s", size, xsize, buffer);
free(buffer);
}

stdnull_deinit();
return EXIT_SUCCESS;
}

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

return EXIT_FAILURE;
}
_________________________________________________________________________





LOL! Well, it does work if you're platform supports something like the NUL
device. Here is the output I am getting:




size(16/16) - Hello World 123




AFAICT, this allows one to safely allocate a buffer that is guaranteed to be
large enough to hold the entire formatted string.




Any thoughts?

;^)
 
C

Chris M. Thomasson

Chris M. Thomasson said:
LOL! Well, it does work if you're platform supports something like the NUL
device.

Humm, I suppose you could also use a dummy file.


[...]
 
C

Chris M. Thomasson

Chris M. Thomasson said:
Chris M. Thomasson said:
LOL! Well, it does work if you're platform supports something like the
NUL device.

Humm, I suppose you could also use a dummy file.


[...]


Here is another version of the program which uses a temporary file as a
"fallback" just in case the file open fails with the standard names for some
platforms NUL device:
______________________________________________________________________
#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", "w");
if (! g_stdnull)
{
g_stdnull = fopen("nul", "w");
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;
}
}


int
get_sprintf_size(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);

return size;
}




int main(void)
{
if (stdnull_init())
{
int size = get_sprintf_size("%s %d\n", "Hello World", 123);

if (size) {
int xsize;
char* buffer = malloc(size + 1);

buffer[0] = '\0';
xsize = sprintf(buffer, "%s %d\n", "Hello World", 123);
assert(size == xsize);
printf("size(%d/%d) - %s", size, xsize, buffer);
free(buffer);
}

stdnull_deinit();
return EXIT_SUCCESS;
}

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

return EXIT_FAILURE;
}
______________________________________________________________________




Also, this version attempts to set the file pointer to the beginning of the
file after every call to `get_sprintf_size()'.





Ahhh, I do love the ability to hack some crap together in C. LOL!


;^)
 
C

Chris M. Thomasson

Chris M. Thomasson said:
Chris M. Thomasson said:
Chris M. Thomasson said:
LOL! Well, it does work if you're platform supports something like the
NUL device.

Humm, I suppose you could also use a dummy file.


[...]


Here is another version of the program which uses a temporary file as a
"fallback" just in case the file open fails with the standard names for
some platforms NUL device:

[...]

Well, even though this is a crappy little hack, I should still check if
`malloc()' succeeds or not!

[...]

_________________________________________________________________________

int main(void)
{
if (stdnull_init())
{
int size = get_sprintf_size("%s %d\n", "Hello World", 123);

if (size) {
int xsize;
char* buffer = malloc(size + 1);

if (buffer)
{
buffer[0] = '\0';
xsize = sprintf(buffer, "%s %d\n", "Hello World", 123);
assert(size == xsize);
printf("size(%d/%d) - %s", size, xsize, buffer);
free(buffer);
}

else
{
fprintf(stderr, "Could not allocate %lu bytes!",
(unsigned long int)(size + 1));
}
}

stdnull_deinit();
return EXIT_SUCCESS;
}

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

return EXIT_FAILURE;
}
_________________________________________________________________________




Sorry about that NON-SENSE!

;^o
 
C

Chris M. Thomasson

Chris M. Thomasson said:
Chris M. Thomasson said:
Chris M. Thomasson said:
[...]

LOL! Well, it does work if you're platform supports something like the
NUL device.

Humm, I suppose you could also use a dummy file.


[...]


Here is another version of the program which uses a temporary file as a
"fallback" just in case the file open fails with the standard names for
some platforms NUL device:

[...]

Well, even though this is a crappy little hack, I should still check if
`malloc()' succeeds or not!

[...]


ROFL!!!


I guess I should not return `EXIT_SUCCESS' when `malloc()' fails. Too funny!


;^)

[...]




BTW, how many of you find that the overall idea contained within the hack I
whipped up might be "useful"?
 
C

Chris M. Thomasson

[...]
For most applications vanilla sprintf() with a big buffer is good enough.
It just segfaults on a stupid input, like a name of over a thousand
characters, and that is acceptable. This behaviour is fine for my current
programs, for instance.

seg-fault or viral outbreak?
 
J

jacob navia

Richard said:
The problem is that applying a format specification to a set of
arguments produces a string of unknown length. Snprintf "solves"
the problem by failing to complete the production of the output
string.


This is just not true. Before writing something like that
you could *read* the specs of snprintf!

The first time you do:

unsigned siz=1+snprintf(NULL,0,format_string,arg1,arg2,arg3);
char *buf = malloc(siz);
snprintf(buf,siz,format_string,arg1,arg2,arg3);

As you say, one can use snprintf to find out the size of the
buffer one does need; one way is to keep increasing the size of
the buffer until there is enough space.

You should know better. Regulars are really spewing nonsense here.

[snip]
Richard Harter, (e-mail address removed)
http://home.tiac.net/~cri, http://www.varinoma.com
No one asks if a tree falls in the forest
if there is no one there to see it fall.

Sure, but if the tree falls in a theater everybody sees it...
 
W

Willem

Sri Harsha Dandibhotla wrote:
) "The problem is that any sprintf whatsoever, insofar as it formats
) strings, has no control over string length. Most code I've seen
) decides to create some silly "buffer" of some silly size. "
)
) you still have to create some silly buffer of some silly size.

snprintf returns the number of characters that would have been printed.
This enables you to exactly allocate the amount of memory needed.
asprintf is probably implemented on top of snprintf.


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
B

bartc

jacob navia said:
This is just not true. Before writing something like that
you could *read* the specs of snprintf!

The first time you do:

unsigned siz=1+snprintf(NULL,0,format_string,arg1,arg2,arg3);
char *buf = malloc(siz);
snprintf(buf,siz,format_string,arg1,arg2,arg3);

You're evaluating each argument twice. Unless you store each argument into a
simple variable first, it's possible the size of the output can change. And
he mentioned side-effects which can have further impact.

As an example of the former problem, imagine a function getmonthname(),
executed an instant before midnight on 31-Aug. The first call will produce
"August" while the second could well produce "September", which is 3
characters longer.

And this is not necessarily fixed by storing a char*, you may need to copy
the full string.
 
C

Chris M. Thomasson

jacob navia said:
This is just not true. Before writing something like that
you could *read* the specs of snprintf!

The first time you do:

unsigned siz=1+snprintf(NULL,0,format_string,arg1,arg2,arg3);
char *buf = malloc(siz);
snprintf(buf,siz,format_string,arg1,arg2,arg3);

Should that be something like:
_________________________________________________________________
int size = snprintf(NULL,0,format_string,arg1,arg2,arg3);

if (size > -1)
{
char* buf;

++size;
buf = malloc(size);
if (buf)
{
snprintf(buf,siz,format_string,arg1,arg2,arg3);
/* [...]; */
free(buf);
}
}
_________________________________________________________________

?

[...]
 
C

Chris M. Thomasson

bartc said:
You're evaluating each argument twice. Unless you store each argument into
a simple variable first, it's possible the size of the output can change.
And he mentioned side-effects which can have further impact.

As an example of the former problem, imagine a function getmonthname(),
executed an instant before midnight on 31-Aug. The first call will produce
"August" while the second could well produce "September", which is 3
characters longer.

And this is not necessarily fixed by storing a char*, you may need to copy
the full string.

you would need to copy the string if `getmonthname()' returned a pointer to
a buffer that can be changed by some other locus of control/mysterious
force. For instance:
______________________________________________________________________
char const* getmonthname(void)
{
static char buffer[100] = { '\0' };

/* calc month name; store in `buffer' */

return buffer;
}


void foo()
{
char const* monthname = getmonthname();
int size = snprintf(NULL, 0, "%s", monthname);

if (size > -1)
{
char* buf;

++size;
buf = malloc(size);
if (buf)
{
int xsize = snprintf(buf, size, "%s", monthname);

if (size != xsize)
{
puts("OH SH%T! the buffer was changed by something!!!");

if (xsize > size)
{
puts("OH FUC%! we are DOOMED!!!!!!");
}
}

free(buf);
}
}
}
______________________________________________________________________
 
M

Moi

That, too. The task of creating a well behaved snprintf is rather more
difficult than it seems at first sight. Neither of Don Quixote's
suggestions is well behaved because they potentially require evaluating
the arguments more than once. What one needs to do is to resize
dynamically as one scans the argument. IIANM (but I often am) the %s
specifier is the only that is potentially unbounded, and one can do a
strlen on pointers. The game then is to break the format string into
pieces and evaluate a bound for the output size of each piece. My
impression is that it's doable but it's tricky.

A nice hack IMHO to implement snprintf() on systems where it is not
available,
is tho fprintf() to a file, and read back the answer. This will only need
one pass, and the arguments are only evaluated once.

On a unix-like systems, the file can even be unlinked after creation.
Since all of the printing takes place in OS-buffers, this will be almost
optimal.

AvK
 

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

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top