T
Tor Rustad
I have a C program, where I control the error behavior according to context:
/* some error handlers */
static void on_error_log (MYSQL *mysql, const char *msg, ...);
....
static void on_error_exit (MYSQL *mysql, const char *msg, ...);
/* the error function */
static void (*error_fatal) (MYSQL *mysql, const char *msg, ...) =
on_error_log;
i.e. on the fly I can change which handler error_fatal() uses, sometimes I
want to call exit(), other times just log error and continue etc.
So far, no problem, but then I wanted a trace function and decided to reuse
the on_error_log() function, since the trace function didn't need the MYSQL
context, a simplified interface could be used:
static void log_err(const char *msg, ...);
Now comes the problem, how to make log_err() call on_err_log(), clearly my
first try didn't work:
$ cat test_argp.c
/*
Demonstrate the problem of calling variable argument
function, from another va function.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#define LOG_FILE_NAME "test.log"
static void on_error_log(const char *msg, ...)
{
va_list argp;
FILE *log;
assert(msg != NULL);
log = fopen(LOG_FILE_NAME, "a");
if (log == NULL)
{
perror(LOG_FILE_NAME);
exit(EXIT_FAILURE);
}
if ( msg != NULL)
{
va_start(argp, msg);
(void) vfprintf(log, msg, argp);
va_end(argp);
}
fclose(log);
}
static void log_err(const char *msg, ...)
{
va_list argp;
assert(msg != NULL);
va_start(argp, msg);
on_error_log(NULL, msg, argp);
va_end(argp);
}
int main(void)
{
const char *string = "Test string";
log_err("Log test.... string");
log_err("Log test.... string");
log_err("string = '%s'\n", string);
log_err("string = '%s'\n", string);
return EXIT_SUCCESS;
}
$ gcc -ansi -W -Wall -pedantic -g test_argp.c
$ ./a.out
a.out: test_argp.c:18: on_error_log: Assertion `msg != ((void *)0)' failed.
Aborted
$ gdb a.out
(gdb) r
Starting program: /****/***/***/******/a.out
a.out: test_argp.c:18: on_error_log: Assertion `msg != ((void *)0)' failed.
Program received signal SIGABRT, Aborted.
0xffffe410 in __kernel_vsyscall ()
(gdb) bt
#0 0xffffe410 in __kernel_vsyscall ()
#1 0xb7e51770 in raise () from /lib/tls/i686/cmov/libc.so.6
#2 0xb7e52ef3 in abort () from /lib/tls/i686/cmov/libc.so.6
#3 0xb7e4adbb in __assert_fail () from /lib/tls/i686/cmov/libc.so.6
#4 0x080484b4 in on_error_log (msg=0x0) at test_argp.c:18
#5 0x0804856b in log_err (msg=0x80486f7 "Log test.... string") at
test_argp.c:44
#6 0x08048591 in main () at test_argp.c:52
(gdb)
so the assert() bomb out on the first logerr() call. Unless I'm missing some
trivial point here, could someone explain how to do this, and why the above
code fail.
/* some error handlers */
static void on_error_log (MYSQL *mysql, const char *msg, ...);
....
static void on_error_exit (MYSQL *mysql, const char *msg, ...);
/* the error function */
static void (*error_fatal) (MYSQL *mysql, const char *msg, ...) =
on_error_log;
i.e. on the fly I can change which handler error_fatal() uses, sometimes I
want to call exit(), other times just log error and continue etc.
So far, no problem, but then I wanted a trace function and decided to reuse
the on_error_log() function, since the trace function didn't need the MYSQL
context, a simplified interface could be used:
static void log_err(const char *msg, ...);
Now comes the problem, how to make log_err() call on_err_log(), clearly my
first try didn't work:
$ cat test_argp.c
/*
Demonstrate the problem of calling variable argument
function, from another va function.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#define LOG_FILE_NAME "test.log"
static void on_error_log(const char *msg, ...)
{
va_list argp;
FILE *log;
assert(msg != NULL);
log = fopen(LOG_FILE_NAME, "a");
if (log == NULL)
{
perror(LOG_FILE_NAME);
exit(EXIT_FAILURE);
}
if ( msg != NULL)
{
va_start(argp, msg);
(void) vfprintf(log, msg, argp);
va_end(argp);
}
fclose(log);
}
static void log_err(const char *msg, ...)
{
va_list argp;
assert(msg != NULL);
va_start(argp, msg);
on_error_log(NULL, msg, argp);
va_end(argp);
}
int main(void)
{
const char *string = "Test string";
log_err("Log test.... string");
log_err("Log test.... string");
log_err("string = '%s'\n", string);
log_err("string = '%s'\n", string);
return EXIT_SUCCESS;
}
$ gcc -ansi -W -Wall -pedantic -g test_argp.c
$ ./a.out
a.out: test_argp.c:18: on_error_log: Assertion `msg != ((void *)0)' failed.
Aborted
$ gdb a.out
(gdb) r
Starting program: /****/***/***/******/a.out
a.out: test_argp.c:18: on_error_log: Assertion `msg != ((void *)0)' failed.
Program received signal SIGABRT, Aborted.
0xffffe410 in __kernel_vsyscall ()
(gdb) bt
#0 0xffffe410 in __kernel_vsyscall ()
#1 0xb7e51770 in raise () from /lib/tls/i686/cmov/libc.so.6
#2 0xb7e52ef3 in abort () from /lib/tls/i686/cmov/libc.so.6
#3 0xb7e4adbb in __assert_fail () from /lib/tls/i686/cmov/libc.so.6
#4 0x080484b4 in on_error_log (msg=0x0) at test_argp.c:18
#5 0x0804856b in log_err (msg=0x80486f7 "Log test.... string") at
test_argp.c:44
#6 0x08048591 in main () at test_argp.c:52
(gdb)
so the assert() bomb out on the first logerr() call. Unless I'm missing some
trivial point here, could someone explain how to do this, and why the above
code fail.