variable argument va_arg does not work with icu u_vformatMessage

B

braj

Hi Friends,
I was trying a test code to use the icu stuff with variable arguments
and I seem to be heading nowhere.

Can anyone please help.
Given below is the test code and the Output.

Thanks a ton in advance
-braj


#include <stdio.h>
#include <stdarg.h>

#include "unicode/utypes.h"
#include <stdlib.h>
#include <string.h>
#include "unicode/uloc.h"
#include "unicode/umsg.h"
#include "unicode/urename.h"
#include "unicode/ustring.h" /* required for UERROR CODE u_strlen
etc... */

void msgCall(const char *msgid, char *type, ...);

int main()
{
printf("here is goes \n");

msgCall("MSG_VFI_E0332", "HELLO" , "%s: something %s : follows : %s
nothing\n", "ONE", "TWO", "THREE");

return 0;
}

void msgCall(const char *msgid, char *type, ...)
{
char *fmt = NULL, *lfmt = NULL, *lmsg = NULL, *msg = NULL;
va_list ap;
char *finalstr;
UChar *result;
UChar pattern[2048];
int32_t resultlength, resultLengthOut;
UErrorCode status = U_ZERO_ERROR;
char *message = "CLI Tool {0} could not be {1} found with {2}
ending";
int len = 0;
u_uastrcpy(pattern, message);

va_start(ap, type);
fmt = va_arg(ap, char *);

resultlength=0;
resultLengthOut=u_vformatMessage("en_US", pattern, resultlength,
NULL, resultlength, ap, &status);
if(status == U_BUFFER_OVERFLOW_ERROR) {
status = U_ZERO_ERROR;
resultlength = resultLengthOut+1;
result = (UChar *)malloc(sizeof(UChar) * resultlength);
u_vformatMessage("en_US", pattern, resultlength, result,
resultlength, ap, &status);
}
len = u_strlen(result);
finalstr = (char*)malloc(sizeof(char) * (len + 1));
u_austrcpy(finalstr, result);

printf("finalstr = [%s]\n", finalstr);
va_end(ap);
}


/* OUTPUT of the above */
here is goes
finalstr = [CLI Tool ä¹E could not be åOä¹E found with ä¡äEåOä¹E
ending]
 
C

Chris Torek

Given below is the test code and the Output.

Most of these are not standard functions, so there is no predicting
what they require or do.

That said, however, if I replace most of your nonstandard code with
two Standard C functions, I see an immediate problem:

[nonstandard code deleted, Standard C added, as marked]
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h> [stuff removed]

void msgCall(const char *msgid, char *type, ...);

int main()
{
printf("here is goes \n");

msgCall("MSG_VFI_E0332", "HELLO" , "%s: something %s : follows : %s \
nothing\n", "ONE", "TWO", "THREE");

return 0;
}

void msgCall(const char *msgid, char *type, ...)
{
char *fmt = NULL, *lfmt = NULL, *lmsg = NULL, *msg = NULL;
va_list ap; [stuff removed]
va_start(ap, type);
fmt = va_arg(ap, char *);

[insertion of new, Standard C, function call:]

vprintf(fmt, ap);

[stuff removed]
[insertion of new, Standard C, function call, plus skeleton to make
it clearer why there are two calls:]

if (1 /* some condition that decides we should try again */) {
vprintf(fmt, ap);
}

[stuff removed]
va_end(ap);
}

If this new code is compiled and run, the output may (or may not)
be:

ONE: something TWO : follows : THREE nothing
ONE: something TWO : follows : THREE nothing

It seems to me that you are expecting this output. The problem
is that the output may, instead, be something more like:

ONE: something TWO : follows : THREE nothing
keq1: something 8%# : follows : >m,(==_ nothing

(or perhaps a crash when the "%s" directives try to follow invalid
pointers, for instance; in this case, typically the first line will
still get printed though).

The problem is that after calling vprintf() -- or indeed any
function, standard or user-written, that uses va_arg() to access
the various parameters -- the value stored in "ap" is officially
"dead". The only thing you can do is va_end() it. After that,
you can va_start() it again, in this case. (In C99, if you need
to use an "ap" more than once, you can va_copy() it as well.)

So, msgCall() might be better written, e.g.:

void msgCall(const char *msgid, char *type, const char *fmt, ...) {
va_list ap;

va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
if (1 /* replace with appropriate test */) {
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
}

However, note that any time you write a variadic function, you
probably should provide the "v" version of it, for the same reason
that the C library provides not only printf() but also vprintf().
This requires the new C99-specific "va_copy", however:

void msgCall(const char *msgid, char *type, const char *fmt, ...) {
va_list ap;

va_start(ap, fmt);
vmsgCall(msgid, type, fmt, ap);
va_end(ap);
}

void vmsgCall(const char *msgid, char *type, const char *fmt, va_list ap) {
va_list copy;

va_copy(ap, copy); /* make copy before we wreck the original */

vprintf(fmt, ap); /* now use (and wreck) ap */
if (1) {
vprintf(fmt, copy); /* and if we need to repeat, use/wreck copy */
}

va_end(copy); /* in any case, delete the copy we made */
}

If you lack C99 (or at least va_copy), you may be able to get away
with providing only the non-"v" version of msgCall, in the same way
that many programmers get away with using printf() and never needing
vprintf(). (But once you *do* need it, you really need it!)
 

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,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top