473,516 Members | 3,465 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

message buffering for logs, sprintf, etc...

I'm trying to develop (for my own personal use) a general
"messaging facility" in C. The idea is that the facility will be
used for both debugging/logging and user interfacing. I'd like it
to be fairly portable, but as robust and reliable as possible.

My intent is to develop an API for such general messaging. I'll
implement the parts that write to stdout, stderr and files. Later,
I'll add support for writing to (for example) syslog, graphical
windows, network sockets, etc.

So what I'd like to do is create a printf()-like function (i.e. one
that takes variable arguments and is formatted according to the
printf() conversion mechanism). This function would then take the
user's message, plus some additional accounting information (time
message was created, message "level", source file location, etc).

As for making this extendable, I figured I'd typedef something like
this:

int message_function(int flags, const char* message);

So I can quickly create new functions that write to arbitrary
locations.

What this all boils down to is I'll have to use sprintf() and write
to a buffer. It looks like there is no perfect way to do this, so
I'm trying to get an idea of the pros and cons of the different
strategies.

First question would be static memory versus dynamic memory. Keep
in mind that this is a logger/debugger, so I could be writing
messages like "out of memory"---which suggests dynamic memory
probably isn't the way to go. In other words, I'd like this
messaging log to continue working when all else has failed.

But with static memory, obviously, I'm limited to a fixed buffer
size. What is too big and what is too little? The accounting
information alone could easily occupy 80+ bytes. Plus, I don't like
the idea of limiting the length of the user's message.

On the other hand, say I use dynamic memory. Should I just assume
that if malloc() isn't available, the system is pretty unusable
anyway?

As for dynamically allocating buffers for sprintf(), a bit of
research showed that some people fopen() /dev/null (or NUL on
Windows), fprintf() to that file pointer, and malloc() with the
return value of fprintf().

What about using tmpfile() as a dynamic memory store?

FWIW, I took a very cursory glance at the glib (www.gtk.org) source,
and it looks like it uses malloc() for buffering messages (though I
didn't check to see what it does when malloc() fails).

Anyway, any discussion is welcome!

Thank you,
Matt

--
Matt Garman
email at: http://raw-sewage.net/index.php?file=email
Nov 14 '05 #1
9 1920
Matt Garman wrote:
I'm trying to develop (for my own personal use) a general
"messaging facility" in C. The idea is that the facility will be
used for both debugging/logging and user interfacing. I'd like it
to be fairly portable, but as robust and reliable as possible.

My intent is to develop an API for such general messaging. I'll
implement the parts that write to stdout, stderr and files. Later,
I'll add support for writing to (for example) syslog, graphical
windows, network sockets, etc.

So what I'd like to do is create a printf()-like function (i.e. one
that takes variable arguments and is formatted according to the
printf() conversion mechanism). This function would then take the
user's message, plus some additional accounting information (time
message was created, message "level", source file location, etc).

As for making this extendable, I figured I'd typedef something like
this:

int message_function(int flags, const char* message);

So I can quickly create new functions that write to arbitrary
locations.
Umh, why do you not want to use
int message_function(int flags, const char* formatstring, ...);
as wrappers for v([s|sn|f]?)printf()?
What this all boils down to is I'll have to use sprintf() and write
to a buffer. It looks like there is no perfect way to do this, so
I'm trying to get an idea of the pros and cons of the different
strategies.
If you have a wrapper for vsnprintf(), you can break down your
format string if necessary and use a fixed size buffer.
First question would be static memory versus dynamic memory. Keep
in mind that this is a logger/debugger, so I could be writing
messages like "out of memory"---which suggests dynamic memory
probably isn't the way to go. In other words, I'd like this
messaging log to continue working when all else has failed.

But with static memory, obviously, I'm limited to a fixed buffer
size. What is too big and what is too little? The accounting
information alone could easily occupy 80+ bytes. Plus, I don't like
the idea of limiting the length of the user's message.

On the other hand, say I use dynamic memory. Should I just assume
that if malloc() isn't available, the system is pretty unusable
anyway?

As for dynamically allocating buffers for sprintf(), a bit of
research showed that some people fopen() /dev/null (or NUL on
Windows), fprintf() to that file pointer, and malloc() with the
return value of fprintf().
If you have access to (v?)snprintf() (from the C99 standard library),
you can use this instead:
size = snprintf(NULL, 0, format, ....);

What about using tmpfile() as a dynamic memory store?
Same argument as with malloc() calls failing.
FWIW, I took a very cursory glance at the glib (www.gtk.org) source,
and it looks like it uses malloc() for buffering messages (though I
didn't check to see what it does when malloc() fails).

Anyway, any discussion is welcome!


HTH
Michael
--
E-Mail: Mine is an /at/ gmx /dot/ de address.
Nov 14 '05 #2
# As for making this extendable, I figured I'd typedef something like
# this:
#
# int message_function(int flags, const char* message);

That's a prototype, not a typedef.

If your system has vasprintf, you can do something like

int message_function(int flags, const char* message,...);
int vmessage_function(int flags, const char* message,va_list);
int smessage_function(int flags, char *buffer);

int message_function(int flags, const char* message,...) {
va_list list; int r;
va_start(list,message); r = vmessage_function(flags,message,list); va_end(list);
return r;
}
int vmessage_function(int flags, const char* message,va_list list) {
char *buffer; vasprintf(&buffer,message,list);
int r = smessage_function(flags ,buffer);
free(buffer);
return r;
}
If not, you have to do a more cumbersome method to restart the va_list
after sizing the buffer.

int message_function(int flags, const char* message,...) {
va_list list; int r,n; char staticbuffer[1000],*dynamicbuffer = 0;
va_start(list,message);
n = vsnprintf(staticbuffer,sizeof staticbuffer,message,list);
va_end(list);
if (n<=(sizeof staticbuffer)-1) {
r = smessage_function(flags,staticbuffer);
}else {
va_start(list,message);
dynamicbuffer = malloc(n+1);
vsnprintf(dynamicbuffer,n+1,message,list);
va_end(list);
r = smessage_function(flags,dynamicbuffer);
free(dynamicbuffer);
}
return r;
}
Some implementations make it easier to restart the va_list

int vmessage_function(int flags, const char* message,va_list list) {
va_list list0 = list; char staticbuffer[1000],*dynamicbuffer = 0;
int r,n = vsnprintf(staticbuffer,sizeof staticbuffer,message,list);
if (n<=(sizeof staticbuffer)-1) {
r = smessage_function(flags,staticbuffer);
}else {
list = list0;
dynamicbuffer = malloc(n+1);
vsnprintf(dynamicbuffer,n+1,message,list);
r = smessage_function(flags,dynamicbuffer);
free(dynamicbuffer);
}
return r;
}

--
SM Ryan http://www.rawbw.com/~wyrmwif/
Death is the worry of the living. The dead, like myself,
only worry about decay and necrophiliacs.
Nov 14 '05 #3
Matt Garman <fa**@not-real.bogus> writes:
[...]
What this all boils down to is I'll have to use sprintf() and write
to a buffer. It looks like there is no perfect way to do this, so
I'm trying to get an idea of the pros and cons of the different
strategies.

First question would be static memory versus dynamic memory. Keep
in mind that this is a logger/debugger, so I could be writing
messages like "out of memory"---which suggests dynamic memory
probably isn't the way to go. In other words, I'd like this
messaging log to continue working when all else has failed.

But with static memory, obviously, I'm limited to a fixed buffer
size. What is too big and what is too little? The accounting
information alone could easily occupy 80+ bytes. Plus, I don't like
the idea of limiting the length of the user's message.

On the other hand, say I use dynamic memory. Should I just assume
that if malloc() isn't available, the system is pretty unusable
anyway?

[...]

Here's an idea.

Declare a static buffer of some reasonable size, likely to be big
enough for most messages.

For each message, figure out how big a buffer you need to hold it
(this could be the tricky part).

If it fits in the static buffer
Use the static buffer
else
Try to malloc a larger buffer.
If malloc succeeds
Use the malloced buffer
else
Truncate and use the static buffer
Log an out-of-memory message. Be brief.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Nov 14 '05 #4
Keith Thompson wrote:
Matt Garman <fa**@not-real.bogus> writes:
[...]

.... snip ...

But with static memory, obviously, I'm limited to a fixed buffer
size. What is too big and what is too little? The accounting
information alone could easily occupy 80+ bytes. Plus, I don't like
the idea of limiting the length of the user's message.

On the other hand, say I use dynamic memory. Should I just assume
that if malloc() isn't available, the system is pretty unusable
anyway?

[...]

Here's an idea.

Declare a static buffer of some reasonable size, likely to be big
enough for most messages.

For each message, figure out how big a buffer you need to hold it
(this could be the tricky part).


Here is some code to output numbers to a stream, and you can choose
between a buffer or recursion. A modification would be to replace
the FILE* parameter with a pointer to a putchar function. Since it
can return the output char. count without actually doing so, it can
be incorporated in other formatting routines. The idea is to avoid
the heavy overhead of the printf family, together with the
insecurities of variadic functions.

/* Convert unsigned value to stream of digits in base
A NULL value for f returns a char count with no output
Returns count of chars. output
return is negative for any output error */
int unum2txt(unsigned long n, unsigned int base, FILE *f)
{
/* MUST be a power of 2 in length */
static char hexchars[] = "0123456789abcdef";

/* allow for terminal '\0' */
#define MAXBASE (sizeof(hexchars)-1)
#ifdef NORECURSE
#include <limits.h>
int count, ix;

/* sufficient for a binary expansion */
char buf[CHAR_BIT * sizeof(long)];
#else
int count, err;
#endif

if ((base < 2) || (base > MAXBASE)) base = 10;
#ifdef NORECURSE
count = ix = 0;
do {
buf[ix++] = hexchars[(n % base) & (MAXBASE-1)];
} while ((n = n / base));
while (ix--) {
count++;
if (f && (putc(buf[ix], f) < 0)) return -count;
}
#else
count = 1;
if (n / base) {
if ((err = unum2txt(n / base, base, f)) < 0) return err;
else count += err;
}
if (f && (putc(hexchars[(n % base) & (MAXBASE-1)], f) < 0))
return -count;
#endif
return count;
} /* unum2txt */

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
Nov 14 '05 #5
"Matt Garman" <fa**@not-real.bogus> wrote

So what I'd like to do is create a printf()-like function (i.e. one
that takes variable arguments and is formatted according to the
printf() conversion mechanism). This function would then take the
user's message, plus some additional accounting information (time
message was created, message "level", source file location, etc).

On the other hand, say I use dynamic memory. Should I just assume
that if malloc() isn't available, the system is pretty unusable
anyway?

Why not

/*
get the size of buffer needed for printf
*/
int vprintfbufflen(const char *fmt, va_list args)

The size of buffer you need is the size of the format string, plus the size
of any strings, plus a generous allowance for any numerical fields. Unless
you need the program to be robust against people deliberately trying to
sabotage you by printing intgers with a width of a hundred characters and
the like, you don't need to deal with all the complexities.

As for dynamic verus static, have a static buffer of reasonable size, and
keep malloc() in reserve for big messages. If malloc() fails then you could
print out an "out of memory" message using the static buffer.
Nov 14 '05 #6

In article <3g************@individual.net>, Michael Mair <Mi**********@invalid.invalid> writes:

If you have access to (v?)snprintf() (from the C99 standard library),
you can use this instead:
size = snprintf(NULL, 0, format, ....);


Note, however, that there are prominent snprintf implementations
which are not compliant with C99 (or SUSv2, which also fixed
snprintf's return semantics - based on a proposal from Chris Torek,
IIRC). These implementations return -1 if the buffer is too small
for the formatted string, not the length of the formatted string.

Of course, if you do use snprintf to determine the length of the
formatted string, you should check the sign of the return value
anyway, since snprintf may return -1 for formatting errors.

The major implementations I know of which have this flaw are at least
some versions of Microsoft Visual C and HP C for HP-UX and Tru64. I
haven't checked the most recent releases.

--
Michael Wojcik mi************@microfocus.com

Let's say the conservative is the quiet green grin of the crocodile ...
an' the liberal is the SNAP! -- Walt Kelly
Nov 14 '05 #7

In article <d7**********@nwrdmz03.dmz.ncs.ea.ibs-infra.bt.com>, "Malcolm" <re*******@btinternet.com> writes:

Why not

/*
get the size of buffer needed for printf
*/
int vprintfbufflen(const char *fmt, va_list args)

The size of buffer you need is the size of the format string, plus the size
of any strings, plus a generous allowance for any numerical fields. Unless
you need the program to be robust against people deliberately trying to
sabotage you by printing intgers with a width of a hundred characters and
the like, you don't need to deal with all the complexities.


And lo, a security hole is born.

The software security business would be a lot smaller and duller if
it weren't for all the programmers who thought they didn't need their
code "to be robust against people deliberately trying to sabotage" it.

--
Michael Wojcik mi************@microfocus.com

Most people believe that anything that is true is true for a reason.
These theorems show that some things are true for no reason at all,
i.e., accidentally, or at random. -- G J Chaitin
Nov 14 '05 #8

"Michael Wojcik" <mw*****@newsguy.com> wrote

And lo, a security hole is born.

The software security business would be a lot smaller and duller if
it weren't for all the programmers who thought they didn't need their
code "to be robust against people deliberately trying to sabotage" it.

A programmer who is calling a C api can generally cause a buffer overflow if
he has malicious intent.
So if the format string is supplied by the programmer it is no more or less
dangerous than a call to strcpy(). If it is generated by a non C programmer,
then of course there is a potential problem.

If you use C at all, you normally have to assume that the person writing the
calling code is not malicious.
Nov 14 '05 #9
in comp.lang.c i read:
In article <3g************@individual.net>, Michael Mair
<Mi**********@invalid.invalid> writes:

If you have access to (v?)snprintf() (from the C99 standard library),
you can use this instead:
size = snprintf(NULL, 0, format, ....);


Note, however, that there are prominent snprintf implementations
which are not compliant with C99


the easiest non-snprintf solution is to make use of tmpfile() and
fprintf(), though it might draw fire for having other issues (sometimes
rightly so, sometimes not).

--
a signature
Nov 14 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
42109
by: huey_jiang | last post by:
Hi All, I am trying to figure out a right syntax to convert an integer array into hex array. sprintf worked for me on doing single integer: int i, Iarray, n=15; char buf; sprintf(buf, "0x%02x", n); The above code worked. Howeve, what I am trying to do is to convert an
6
2481
by: jt | last post by:
I need to produce 1 character array from 3 others. I tried sprintf and it terminates on the first 0, null, 0x00 it sees in tmp data. All 3 args print out nice by themselves. By trying to make the character array(alerts.msg) with sprintf doesn't work for the obvious reasons in my first sentence with tmp having those control characters. Is...
1
3510
by: jimjim | last post by:
Hello, I was wondering about the implications of giving as an argument to sprintf a different data type from the one specified in the format argument. This type of question along with some others are asked below: 1. #include <stdio.h> int main(){ char buffer;
2
4707
by: aap | last post by:
I have the following code #define MAX 32 struct A { char carr; int iarr; int i; }; void main() {
7
15576
by: Mathias Herrmann | last post by:
Hi. I have the following problem: Using popen() to execute a program and read its stdout works usually fine. Now I try to do this with a program called xsupplicant (maybe one knows), but I dont get the output of it while it is running. This is probably a problem of stdout being buffered, because if I use fflush() after a printf() in the...
12
4586
by: Henryk | last post by:
Hey there, I have some problems with the following code snippet on a Virtex-4 PowerPC with a GCC based compiler char chData; sprintf(&chData, "%+05.0f", -0.038f); --I get "-000" ??? sprintf(&chData, "%+05.0f", -0.380f); --I get "-000" ??? sprintf(&chData, "%+05.0f", -3.800f); --I get "-0004" ok
6
11627
by: Henryk | last post by:
I did a lot of delphi GUI programming recently. In my experience most of the time you just want to throw a standard exception with a descriptive message. Then all calling functions can handle this exception as they like and finally show a message to the user or not. Only in a few occasions I need some special exception types to behave...
15
3504
by: krister | last post by:
Hello, I'm working in a quite large system that has some limitations. One of those is that I can't use printf() to get an output on a screen. I'm forced to use a special function, let's call it PrintOnConsole(), to get the output on a console. The problem with PrintOnConsole() is that it only takes strings as input arguments. On the other...
3
2602
by: google | last post by:
Consider the following code: char str; char str2; strcpy(str, "%alfa% %beta% d%100%d %gamma% %delta%"); printf("printf: "); printf("1%s2", str); printf("\nsprintf: "); sprintf(str2, "1%s2", str); //Interesting stuff happens here printf(str2);
0
7273
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main...
0
7182
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language...
0
7405
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. ...
0
7574
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that...
0
5712
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then...
0
4769
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert...
0
1620
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
1
823
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
0
487
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.