How can I temporarily redirect printf output to a c-string?

I'm writing an assignment which involves adding some functionality to PostgreSQL on a Solaris box. As part of the assignment, we need to print some information on the client side (i.e.: using elog.)

PostgreSQL already has lots of helper methods which print out the required information, however, the helper methods are packed with hundreds of printf calls, and the elog method only works with c-style strings.

Is there I way that I could temporarily redirect printf calls to a buffer so I could easily send it over elog to the client?

If that's not possible, what would be the simplest way to modify the helper methods to end up with a buffer as output?

Answers


If you define your own version of printf and link to it prior to the libc version, your version will supersede the standard version.

You should also be able to supersede the standard version by using LD_PRELOAD to load a library that has printf defined.

To write your own printf, you will want to use stdarg functionality:

int printf(const char *fmt, ...)
{
    int rv;
    va_list ap;
    va_start(ap, fmt);

    if (redirect_printf)
    {
#ifdef HAVE_VLOG
        // If you have a vlog function that takes a va_list
        vlog(fmt, ap);
        rv = ...;
#else
        char buffer[LARGESIZE];
        rv = vsnprintf(buffer, sizeof(buffer), fmt, ap);

        log(buffer);
#endif;
    }
    else
    {
        rv = vprintf(fmt, ap);
    }

    return rv;
}

This simple version will truncate data when the final formatted output is greater than LARGESIZE. If you don't want that, you can also call vsnprintf first with a NULL buffer to get the final size, do a dynamic allocation and then a second call to vsprintf to format the buffer.


You're wrong — elog supports format strings just like printf. Here's an example from Postgres source code:

elog(DEBUG4, "TZ \"%s\" gets max score %d", tzname, i);

So all you need is to add elog where there is printf using the same parameters.


The simplest way is to modify the helper methods to call sprintf(). Whether or not you can hack that in easily, I don't know. Maybe

#define printf(...) sprintf(buffer, __VA_ARGS__)

Will do it for you. You'll still need to define buffer for each helper function, and get its contents returned to whoever cares about them.


If you can tolerate the use of a temporary file you could redirect standard out with the freopen() call:-

newstdout = freopen("/tmp/log", "w", stdout);

This will force all the printf's to be written to /tmp/log instead of the console output. At some convenient point later in your program you could then open the same file for reading:-

readfd = fopen("/tmp/log", "r");

and forward the contents that have been added using something like this:-

void forward_to_elog(void)
{
   int bytesread;
   char buf[100];

   memset(buf,0,100);
   do {
      memset(buf,0,100);
      bytesread = fread(buf, sizeof(buf)-1, 1, readfd);
      /* call elog(buf) */ ;
   } while(bytesread);

}

If you keep the file open you can call forward_to_elog() multiple times to incrementally forward the contents that have been added.

The tmpnam() function can be used to get a name for the temporary file if you don't want to have to statically code one.


Need Your Help

Pass a class to other class SWIFT

ios xcode swift

I would like to pass class A to Class B and call method backwards from Class A. I'm not sure if you understand me.

Redirecting to new page in tab with query string

asp.net vb.net

On click of graph I want to open new page in tab to diplay some data.