From: bvs@BitBlocks.COM (Bakul Shah)
Newsgroups: comp.lang.c
Subject: Re: (Help) dynamic use of sprintf ?
Date: 21 Jul 92 20:17:12 GMT
Organization: Bit Blocks, Inc.

brself@hal.gnu.ai.mit.edu (Ben Self) writes:
> 
> Could anyone please help me with a C programming dilemma that has plagued me
> for ages.
> 
> Often when formatting strings or converting numerics to alphas, sprintf ()
> appears as an extremely attractive possibility.  Unfortunately, it does not
> fit well with dynamic allocation nor is it easily safe guarded from 
> segmentation faults and bus errors.
   ...
> We must sell -- any serious offer will be accepted :-)
> 
> Thanks in advance.

Here is another solution that will work for this and many other
formatting uses.  PD source code on request.

I have a vfprintf like function, called `vfnprintf' that takes a
_function pointer_ instead of a FILE *.  vfnprintf calls this
function, possibly multiple times, to drain already formatted
characters.  _You_ supply the function.  Here is an example use.

  #include <stdarg.h>

  struct snstate {
  	int	size;
  	int	offset;
  	char *	buffer;
  };

  /* typedef for a function ptr */
  typedef int (*_vfn_t)(void * st, const char * string, unsigned int count);

  int vfnprintf(_vfn_t fn, void * st, const char * format, va_list args);

  /*
   * a function to drain the formatted chars
   * It doesn't allow a write beyond the
   * end of st->bufer and nul terminates
   * the copied string.
   */
  int sndrain(struct snstate * st, char * str, int count) {
  	int space = st->size - st->offset - 1;
  	if (count > space)
  		count = space;
  	if (count > 0) {
  		memcpy(st->buffer + st->offset, str, count);
  		st->offset += count;
  		st->buffer[st->offset] = '\0';
  	}
  	return count;
  }

  /*
   * given sndrain and vfnprintf, snprintf can be defined to
   * to `sprintf' to a string of given size
   */
  int snprintf(char * str, int size, const char * fmt, ...) {
  	int count;
  	va_list args;
  	struct snstate snstate;
  	snstate.size = size;
  	snstate.offset = 0;
  	snstate.buffer = str;

  	va_start(args, fmt);
  	count = vfnprintf((_vfn_t)sndrain, &snstate, fmt, args);
  	va_end(args);
  	return count;
  }

You can equally easily provide another function that allocates
more space as needed.  Since the function ptr arg to vfnprintf
takes a ptr to a `state' struct, you can even collect the output
from multiple snprintf calls.  (And if snstate->buffer mmap()ed
to some file then who needs stdio!  But I digress.)  vfnprintf is
low level enough that stdio can be built atop it though you
probably wouldn't want to do so for performance reasons.

vfnprintf will call `fn' whenever it wants to output one or more
formatted chars. Function `fn' should return the number of chars
it could drain, 0 if none and -1 to indicate an error.  It can
keep any needed state in `st' (unfortunately, C doesn't have
nested functions or else the vfnprintf interface would've been
simpler).  vfnprintf returns the actual number of characters
output or -1 in case of error.

And yes, there is also a fnprintf():

  int fnprintf(_vfn fn, void * st, const char * format, ...);

-- Bakul Shah <bvs@BitBlocks.com>

PS: Note that floating point formats are not yet handled.  But
    vfnprintf does accept all the rest of the ANSI format chars.
Newsgroups: comp.lang.c