Clever vsprintf

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • January Weiner

    #1

    Clever vsprintf

    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_pos tscript ;
    FILE *out ;
    /* other options */
    } opt_s ;

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

    /* check whether we are generating Postscript */
    if(o->ps_generate_po stscript) {
    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(o pt_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_po stscript == 1, the
    output of

    print_output_li ne(o, "just a test") ;

    is

    % just a test

    Fine. But

    print_output_li ne(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_N O_IDEA_HOW_MUCH * sizeof(some_str ing)) ;

    vsprintf(some_s tring, 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$1@s agnix.uni-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.
  • Jeremy Yallop

    #2
    Re: Clever vsprintf

    January Weiner wrote:[color=blue]
    > /* ------------------------------------------------------------- */
    > char *some_string ;
    > some_string = malloc(I_HAVE_N O_IDEA_HOW_MUCH * sizeof(some_str ing)) ;
    >
    > vsprintf(some_s tring, message, vl) ;
    > /* process some_string even one character by one, if necessary */
    > /* ------------------------------------------------------------- */[/color]

    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.

    Comment

    • those who know me have no need of my name

      #3
      Re: Clever vsprintf

      in comp.lang.c i read:
      [color=blue]
      > va_start(vl,mes sage) ;[/color]

      as an aside: i hope you have a va_end somewhere -- you didn't show one.
      [color=blue]
      > 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?[/color]

      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_li ne(o, "just a %s test", "foolish\nm ulti-line");

      you would do something like:

      print_output_li ne(o, "just a %s test", muline(o, "foolish\nm ulti-line"));

      where muline has the requisite magic to transform '\n' into "\n% " when
      o->ps_generate_po stscript == 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_po stscript) /* !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

      Comment

      • NOSPAM_january@uni-muenster.de

        #4
        Re: Clever vsprintf

        those who know me have no need of my name <not-a-real-address@usa.net > wrote:[color=blue][color=green]
        > > va_start(vl,mes sage) ;[/color][/color]
        [color=blue]
        > as an aside: i hope you have a va_end somewhere -- you didn't show one.[/color]

        Ops :-)
        [color=blue]
        > you would do something like:[/color]
        [color=blue]
        > print_output_li ne(o, "just a %s test", muline(o, "foolish\nm ulti-line"));[/color]

        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.

        Comment

        Working...