altering an av_list variable

Discussion in 'C Programming' started by Punkie, Sep 14, 2007.

  1. Punkie

    Punkie Guest

    When having optional function arguments, the extra arguments are accessed
    via an av_list structure. While traversing is trivial, changing the value
    isnt.

    <code>
    void my_sprintf (char *dest, size_t size, const char *format, ...)
    {
    <snip>
    va_list argptr;
    va_start(argptr, format);
    while((pos = strcspn(format,"%")) != max) {
    switch(format[pos+1]) {
    case 'm':
    format[pos+1] = 's';
    argval = va_arg(cpy,int);
    /* change the argval to a char* */
    break;
    default:
    va_arg(cpy,int);
    }
    }
    sprintf(*dest, const char *format, argptr); /* call the std function */
    va_end(argptr);
    }
    </code>

    In this example you see an attempt to make the sprintf function also
    recognise the "%m" format. It should retrieve the (int) argument and change
    its value.

    Is it possible to alter the arguments in the va_list at all? Are there other
    tricks to do this without rewriting sprintf completely?
     
    Punkie, Sep 14, 2007
    #1
    1. Advertising

  2. Punkie

    Tor Rustad Guest

    Punkie wrote:

    [...]

    > Are there other tricks to do this without rewriting sprintf completely?


    See

    http://www.ijs.si/software/snprintf/


    A quick and non-portable way, is getting the length via:

    null = fopen("/dev/null", "w"); /* UNIX/Linux*/
    null = fopen("NUL", "w"); /* Windows */

    and then calling

    len = vfprintf(null, format, ap);


    Note, vfprintf() return int, not size_t.

    --
    Tor <torust [at] online [dot] no>
     
    Tor Rustad, Sep 14, 2007
    #2
    1. Advertising

  3. Punkie

    Punkie Guest

    Indeed, a good way to do this without rewriting sprintf is to let *others*
    rewrite it. However something i didnt find among them is typechecking.

    Any sprintf with the same std formatting can use the attributes to defer
    typechecking too. Like:
    void my_sprintf (char *dest, size_t size, const char *format, ...)
    __attribute__((format(printf, 3, 4)));

    Altering the types accepted creates a problem as we cant use the attribute
    anymore.
    2routes to solve this:
    *own sprintf implementation, that also does this checking somehow. hmmm but
    how?
    *filter the format and va_list. Like in the code I presented earlier,
    changing the type or eliminaing the arg from the va_list.

    Tor Rustad wrote:

    > Punkie wrote:
    >
    > [...]
    >
    >> Are there other tricks to do this without rewriting sprintf completely?

    >
    > See
    >
    > http://www.ijs.si/software/snprintf/
    >
    >
    > A quick and non-portable way, is getting the length via:
    >
    > null = fopen("/dev/null", "w"); /* UNIX/Linux*/
    > null = fopen("NUL", "w"); /* Windows */
    >
    > and then calling
    >
    > len = vfprintf(null, format, ap);
    >
    >
    > Note, vfprintf() return int, not size_t.
    >
     
    Punkie, Sep 14, 2007
    #3
  4. In article <QEzGi.105220$-ops.be>,
    Punkie <> wrote:
    >When having optional function arguments, the extra arguments are accessed
    >via an av_list structure. While traversing is trivial, changing the value
    >isnt.


    "av_list" and "va_list" are very different things. The people here will
    not talk to you about the former.
     
    Kenny McCormack, Sep 14, 2007
    #4
  5. Punkie

    Ian Collins Guest

    Punkie wrote:

    Please don't top-post.

    > Tor Rustad wrote:
    >
    >> Punkie wrote:
    >>
    >> [...]
    >>
    >>> Are there other tricks to do this without rewriting sprintf completely?

    >> See
    >>
    >> http://www.ijs.si/software/snprintf/
    >>
    >>
    >> A quick and non-portable way, is getting the length via:
    >>
    >> null = fopen("/dev/null", "w"); /* UNIX/Linux*/
    >> null = fopen("NUL", "w"); /* Windows */
    >>
    >> and then calling
    >>
    >> len = vfprintf(null, format, ap);
    >>
    >>
    >> Note, vfprintf() return int, not size_t.
    >>

    > Indeed, a good way to do this without rewriting sprintf is to let *others*
    > rewrite it. However something i didnt find among them is typechecking.
    >
    > Any sprintf with the same std formatting can use the attributes to defer
    > typechecking too. Like:
    > void my_sprintf (char *dest, size_t size, const char *format, ...)
    > __attribute__((format(printf, 3, 4)));
    >

    It should be noted that __attribute__ is a (very handy) gcc extension.

    --
    Ian Collins.
     
    Ian Collins, Sep 14, 2007
    #5
  6. Punkie

    Chris Torek Guest

    In article <MwCGi.105460$-ops.be>
    Punkie <> wrote:
    >Indeed, a good way to do this without rewriting sprintf is
    >to let *others* rewrite it.


    Indeed.

    >Altering the types accepted creates a problem as we cant
    >use the [gcc-specific] attribute anymore.


    You cannot use this portably anyway, even if all your format
    directives match those for printf, since __attribute__ is a
    gcc-specific extension (i.e., it does not work very well, or even
    at all, in various other C compilers).

    If you have "extra" directives, a la syslog() "%m", you have to
    work harder. If your extra directives need to access some of
    the va_list's variable arguments, you must in fact "rewrite sprintf".

    Consider, for instance, the following actual implementation (for
    V9 SPARC, with a fairly old version of GCC):

    /*__va_ll must be 8 bytes regardless of -mlong32; this avoids a pedwarn()*/
    typedef long __va_ll __attribute__((__mode__(__DI__)));

    /*
    * Pre-V9, we simply had up to 6 values in %o registers, then the rest
    * in memory. The V9 convention is different -- it had to change because
    * the registers are wider, and someone took the opportunity to fix the
    * brokenness: floating point arguments are passed in via the first 16
    * %f registers now. Thus, we now have an "argument descriptor" data
    * structure.
    *
    * (Using "long long" for the ni and nd produces shorter code.)
    */
    typedef struct __va_data {
    __va_ll __va_ni; /* # integers remaining */
    __va_ll *__va_ip; /* integer-reg arguments */
    __va_ll __va_nd; /* # doubles remaining */
    double *__va_dp; /* floating-reg arguments */
    __va_ll *__va_rest; /* additional args, if any */
    } va_list[1];

    /*
    * The __builtin_args_info expressions return the number of "registers"
    * (if any) worth of fixed arguments. For integer parameters, this is
    * simply the number of (widened) integers; for float, double, and long
    * double parameters, it is the number of %f registers used, accounting
    * for the fact that "double" parameters are passed in an even/odd pair
    * (leaving a gap if necessary -- e.g., "void f(float x,double y,...)"
    * uses %f0 and <%f2:%f3> for a total of 4 %f registers).
    *
    * When we invoke __builtin_saveregs() here, it dumps those registers
    * that were *not* used up by fixed parameters, integer registers first,
    * then %f registers. The latter always starts with an even register
    * (to form a pair), even if there were an odd number of fixed "float"
    * arguments.
    */
    #define va_start(ap, l) __extension__ ({ \
    __va_ll *__va_base = __builtin_saveregs(); \
    int __va_nfi = __builtin_args_info(0); \
    int __va_nff = __builtin_args_info(1); \
    if (__va_nfi > 6) \
    __va_nfi = 6; \
    if (__va_nff > 16) \
    __va_nff = 16; \
    (ap)->__va_ip = __va_base; \
    (ap)->__va_dp = (double *)(__va_base + __va_nfi) ; \
    (ap)->__va_ni = 6 - __va_nfi; \
    (ap)->__va_nd = 8 - ((__va_nff + 1) / 2); \
    (ap)->__va_rest = (__va_ll *)__builtin_next_arg(l); \
    })
    #define va_end(ap)

    /*
    * Locate (get the address of) the next integer register. Used for
    * integers and pointers (in particular, for the hidden pointer that
    * is used to pass aggregates).
    */
    #define __va_aireg(ap) \
    ((ap)->__va_ni <= 0 ? (ap)->__va_rest++ : \
    ((ap)->__va_ni--, (ap)->__va_ip++))

    /*
    * Internally, this macro sets __va_p to point to the first byte of the
    * object, whatever it is, then extracts the appropriate object.
    *
    * "Real" parameters are the hardest, because we may need to skip over
    * a %f-pair-gap when extracting a long double. Fortunately, the number
    * of doubles remaining is odd iff the corresponding parameter skipped
    * a %f register pair.
    *
    * Integer and aggregate parameters are easy, but since we are locating
    * the bytes of the integer in question, we need to account for our
    * big-endian offset: an int or unsigned int is in the last four, not
    * the first four, bytes of the 8-byte integer register value as stored
    * in memory.
    */
    #define va_arg(ap, ty) __extension__ ({ \
    void *__va_p; \
    const int __vaclass = __builtin_classify_type(*(ty *)0); \
    if (__vaclass >= __record_type_class) \
    __va_p = *(void **)__va_aireg(ap); \
    else if (__vaclass == __real_type_class) { \
    const int __va_n = (sizeof(*(ty *)0) + 7) / 8; \
    if (__va_n > 1 && (ap)->__va_nd & 1) \
    (ap)->__va_nd--, (ap)->__va_dp++; \
    if (((ap)->__va_nd -= __va_n) >= 0) { \
    __va_p = (ap)->__va_dp; \
    (ap)->__va_dp += __va_n; \
    } else { \
    __va_p = (ap)->__va_rest; \
    (ap)->__va_rest += __va_n; \
    } \
    } else { \
    /* oddly, we get better code with two statements here */ \
    __va_p = __va_aireg(ap); \
    __va_p = (char *)__va_p + 8 - sizeof(*(ty *)0); \
    } \
    *(ty *)__va_p; \
    })

    Here, substituting or removing a parameter is pretty tricky,
    especially if the substitute has a different size and/or type from
    the original (e.g., replacing a 4-byte "int" with an 8-byte pointer,
    or pointer with floating-point value). You need to know which of
    the three regions the parameter to be substituted or removed falls
    into, and for substitutions, whether the replacement goes in the
    same region or not. This information has been removed (from the
    structure to which "ap" points) by the time you have used va_arg()
    to extract the original value.

    Another implementation is much simpler:

    #define va_arg(ap, type) \
    ((type *)(ap += sizeof(type)))[-1]

    (where va_start(ap, last) just sets "ap" to point to the right
    place in the [single] stack that this implementation uses). Here
    substitutions or deletions are usually just a matter of poking the
    right value into place, or doing a memmove(). Finding the right
    location is easy; finding the size for memmove() is harder but
    not nearly as hard as the V9 SPARC version.

    Nothing requires the implementation of va_start, va_arg, and so on
    to look like one of the two above, but they are the two common
    methods I have seen. (Most or all of the tricky stuff can be, and
    in my opinion should be, done internally by the compiler, allowing
    the following compiler-specific -- but not machine-specific --
    variant of <stdarg.h>:

    #define va_start(ap, last) __builtin_va_start(ap)
    #define va_arg(ap, ty) __builtin_va_arg(ap, ty)
    #define va_end(ap) __builtin_va_end(ap)

    For some reason, not too many people seem to agree with me on
    this one. :) )
    --
    In-Real-Life: Chris Torek, Wind River Systems
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: forget about it http://web.torek.net/torek/index.html
    Reading email is like searching for food in the garbage, thanks to spammers.
     
    Chris Torek, Sep 15, 2007
    #6
    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. Daragoth
    Replies:
    4
    Views:
    620
    Just an Illusion
    Jun 30, 2004
  2. Eric Sabine
    Replies:
    0
    Views:
    340
    Eric Sabine
    Aug 25, 2003
  3. Rob Heckart
    Replies:
    6
    Views:
    550
    Rob Heckart
    Nov 26, 2003
  4. =?Utf-8?B?QWRhbQ==?=

    altering Request.Headers

    =?Utf-8?B?QWRhbQ==?=, May 19, 2005, in forum: ASP .Net
    Replies:
    1
    Views:
    430
    Steven Cheng[MSFT]
    May 20, 2005
  5. Ben Fidge
    Replies:
    3
    Views:
    623
    Mr Newbie
    Nov 6, 2005
Loading...

Share This Page