Clever vsprintf

Discussion in 'C Programming' started by January Weiner, Apr 21, 2004.

  1. Hello,

    I have the following problem.

    I have a custom fprintf-like function which is supposed to prepend each
    comment output line with a `%' if the output type is a Postscript
    document (pseudo-code below). (Postscript does not matter here)

    /* ------------------------------------------------------------- */

    /* structure for holding options */
    typedef struct {
    int ps_generate_postscript ;
    FILE *out ;
    /* other options */
    } opt_s ;

    /* this is my custom fprintf-like function */
    void print_output_line(opt_s *o, char *message, ...) {
    va_list vl ;
    va_start(vl,message) ;

    /* check whether we are generating Postscript */
    if(o->ps_generate_postscript) {
    print_ps_line(o, message, vl) ;
    } else {
    vfprintf(o->out, message, vl) ;
    fprintf(o->out, "\n") ;
    }
    }

    /* print a Postscript comment */
    /* the problem sits here */
    void print_ps_line(opt_s *o, char *message, va_list vl) {

    /* some PostScript specific operations go in here... */

    /* print a `%', which is a comment in Postscript */
    fprintf(o->out, "%% ") ;
    vfprintf(o->out, message, vl) ;

    }

    /* ------------------------------------------------------------- */

    OK, here is the problem. Given that o->ps_generate_postscript == 1, the
    output of

    print_output_line(o, "just a test") ;

    is

    % just a test

    Fine. But

    print_output_line(o, "just a %s test", "\n") ;

    produces

    % just a
    test

    Obviously, I would like to have the second line commented as well, or at
    least to replace the newline characters with a space / whatever
    (otherwise the Postscript interpreter sees "test" and does not know what
    to do with it). A way to do it would be first to call

    /* ------------------------------------------------------------- */
    char *some_string ;
    some_string = malloc(I_HAVE_NO_IDEA_HOW_MUCH * sizeof(some_string)) ;

    vsprintf(some_string, message, vl) ;
    /* process some_string even one character by one, if necessary */
    /* ------------------------------------------------------------- */

    However, how can I check how much space should I reserve for
    some_string? Or, alternatively: how can I go through va_list vl and
    examine it's contents?

    Best regards,

    January


    P.S. Some time ago I have posted a problem in a thread called "Weird
    runtime problem" (c37j5t$ee8$-muenster.de). Obviously, some of
    my follow-ups didn't ever got propagated to other news server than
    news.uni-muenster.de. Thank you all very much for help; and BTW -- it was
    a problem with my hashing function.


    --
    Someday, we'll look back on this, laugh nervously and change the subject.
     
    January Weiner, Apr 21, 2004
    #1
    1. Advertising

  2. January Weiner wrote:
    > /* ------------------------------------------------------------- */
    > char *some_string ;
    > some_string = malloc(I_HAVE_NO_IDEA_HOW_MUCH * sizeof(some_string)) ;
    >
    > vsprintf(some_string, message, vl) ;
    > /* process some_string even one character by one, if necessary */
    > /* ------------------------------------------------------------- */


    A few ideas:

    * Iterate twice over the format string: once to parse the
    arguments, and estimate (an upper bound on) the length for each,
    then again (via vsprintf) to actually print them. This is fairly
    hard, in that you have to understand the same specifiers as
    fprintf(), although you can probably ignore modifiers, etc.

    * Print to a temporary file, and count the number of bytes printed
    via the return value of vfprintf().

    * Use snprintf() (if available) either to handle the input a bit at
    a time, or to compute the total input length (by passing NULL as
    the second argument).

    Jeremy.
     
    Jeremy Yallop, Apr 21, 2004
    #2
    1. Advertising

  3. in comp.lang.c i read:

    > va_start(vl,message) ;


    as an aside: i hope you have a va_end somewhere -- you didn't show one.

    > However, how can I check how much space should I reserve for
    > some_string? Or, alternatively: how can I go through va_list vl and
    > examine it's contents?


    there is no generic way to step through the contents of a va_list -- you
    examine a vfprintf-like argument list the same way that vfprintf does, by
    parsing the format string, but you might find that a tad tedious. the most
    portable alternatives use tmpfile and vfprintf, then either read from the
    file or use vfprintf's return value (+1 then *2) to allocate a buffer for a
    subsequent call to vsprintf. horribly non-portable hacks are almost
    certain to be available, but should be avoided if at all possible.

    it may be that you merely need something more cleaver than a bare \n in
    your messages, or some processing before passing it to your print function,
    e.g.,

    instead of:

    print_output_line(o, "just a %s test", "foolish\nmulti-line");

    you would do something like:

    print_output_line(o, "just a %s test", muline(o, "foolish\nmulti-line"));

    where muline has the requisite magic to transform '\n' into "\n% " when
    o->ps_generate_postscript == 1, e.g., a trivial implementation might be:

    [warning: untested, off the top of my head]

    /* cheesy static buffer version */

    #define STEP 1024

    const char * muline (const opt_s * o, const char * in)
    {
    static size_t len;
    static char * out;

    assert(NULL != o);
    assert(NULL != in);

    if (1 != o->ps_generate_postscript) /* !1 means non-ps output */
    {
    return in;
    }

    size_t used; /* needed after loop terminates */
    for (used = 0; *in; in++)
    {
    if (used+4 >= len) /* room for the "% " too */
    {
    assert(SIZE_MAX > len + STEP);
    void * q = realloc(out, len + STEP);
    if (! q)
    {
    /* memory exhausted */
    abort();
    }
    out = q;
    len += STEP;
    }
    out[used++] = *in;
    if ('\n' == *in)
    {
    out[used++] = '%';
    out[used++] = ' ';
    }
    }
    out[used] = 0;
    return out;
    }

    --
    a signature
     
    those who know me have no need of my name, Apr 23, 2004
    #3
  4. January Weiner

    Guest

    those who know me have no need of my name <> wrote:
    > > va_start(vl,message) ;


    > as an aside: i hope you have a va_end somewhere -- you didn't show one.


    Ops :)

    > you would do something like:


    > print_output_line(o, "just a %s test", muline(o, "foolish\nmulti-line"));


    Yes, that would be a solution. I would like not to involve temporary files.
    Thanks!

    January

    --
    Someday, we'll look back on this, laugh nervously and change the subject.
     
    , Apr 23, 2004
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. -
    Replies:
    3
    Views:
    950
    Grant Wagner
    Jul 15, 2003
  2. Thomas Rogg

    vsprintf - safe alternative?

    Thomas Rogg, Aug 18, 2004, in forum: C Programming
    Replies:
    5
    Views:
    1,089
    Moonie
    Aug 25, 2004
  3. Replies:
    4
    Views:
    684
  4. Falcon Kirtaran

    Re: vsprintf without vsnprintf

    Falcon Kirtaran, Jan 14, 2009, in forum: C Programming
    Replies:
    12
    Views:
    3,263
  5. Ben Bacarisse

    Re: vsprintf without vsnprintf

    Ben Bacarisse, Jan 14, 2009, in forum: C Programming
    Replies:
    0
    Views:
    373
    Ben Bacarisse
    Jan 14, 2009
Loading...

Share This Page