From: stirling@ozrout.uucp (Stirling Westrup)
Subject: Re: (Help) dynamic use of sprintf ?
Organization: Andre P. Ozrout
Date: Fri, 24 Jul 1992 16:45:18 GMT

bvs@BitBlocks.COM (Bakul Shah) writes:
>
>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.

[Some code deleted...]

>  /* 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);

[More code deleted...]

>  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;
>  }
>

The above aproach is very similar to the one I took when I needed to
provide flexible handling of format strings (even to the fact that I also
did not bother with the floating point formats).  There is one major
difference between our ways of doing things.  My function (called 'fmt')
accepts two void pointers, and returns a void pointer.  The intent was to
allow for a printf-type function to return something other than an integer
count.  For instance, my implementation of saprintf returns a pointer to 
the newly allocated string.  This does make for some added complexity for
simple cases like the above though, and I'm not entirely sure its worth it.

The basic idea is that one of the void pointers passed to the function is
for state information, and the other is for some sort of accumulating
result.  The ancillary function (called 'sndrain' in the above code) 
would use the result from the previous invocation of itself to compute the
result for this invocation.  This may be clearer if I include the
definition of snprintf from my own library.  I think most of the types
should be clear from context, except perhaps for Nat which is an unsigned
int.  Oh, the Vpl... stuff is just an alias for the va_... stuff from ANSI.
I use my own names due to certain bugs I've had to work around in various
compilers.


Struct StrLen StrLen;

Struct StrLen                   /* String/Length pair for snput */
  { Char *str;                    /* string to write into */
    Nat len;                      /* max characters to put in string */
  };

Static Void *snput              /* put a chunk of text into a string */
  ( Const Char *txt,              /* pointer to thte *
    Nat len,                      /* length of the text */
    Void *ctx,                    /* context (StrLen pointer) */
    Void *rsl                     /* result (pointer to a character counter) */
  )
  { Register Struct StrLen sl = x;      /* recover type of StrLen pointer */
    Register Nat idx = 0;               /* We need an index, why not a reg? */
    Nat *num = rsl;                     /* recover type of counter pointer */

    while( len && sl->len )             /* while text, and a place for it... */
      {
        *(sl->str++) = txt[idx++];      /* Copy a char into the string */
        len--;                          /* one less char to copy */
        sl->len--;                      /* one less space ithe string */
      }
    *(sl->str) = 0;                     /* null terminate the string */
    (*num) += idx;                      /* and bump counter by chars moved */
    return rsl;                         /* return our result pointer */
  }

/*
  snprintf - printf'er to a string, with size maximum 
  Takes a pointer to a buffer, the size of the buffer (including space for the
  trailing 0), a printf format string, and a variable parameter list.
  The function stores the formatted string in the buffer, or as much as will
  fit.  The buffer will be null tminated.
  The function returns the number of characters actually stored.
*/

Nat snprintf
  ( Char *buf,
    Nat len,
    Const Char *str,
    ...
  )
  { StrLen sl;
    Nat num = 0;
    Vpl args;

    if( !buf || len < 2 )
      return 0;
    sl.str = buf;
    sl.len = len-1;
    vplBeg(args,str);
    fmt(snput, &sl, &num, str, args);
    vplEnd(args);
    return num;
  }


-- 
||    BUNGEE SIDEWAYS!                              o   )))
||                                                 /-- ///
|P~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\_  \\\      __ -_
||    stirling@ozrout.uucp                                 ((( (_)T(_)