Excerpts from the mail message of Ian Dash:
) 
) void cardmod(char *cardnum,
)              char *issuer,
)              char *details
)              unsigned long *statflag);
) 
) the cardnum is fixed, but the rest of the args are set by the function.

Okay, think about this.  You pass in C<char *issuer> and the
function "changes" it.  Well, it can't change C<issuer>, the
pointer, because C uses pass-by-value {You don't expect to
be able to do the following in C, do you?

    int n;
    getnumber(n);
    printf( "The number is %d\n", n );

Because you can't.  C passes [a copy of] the value of C<n> to
C<getnumber> and C<getnumber> can change its copy but that has
no effect on C<n>.  The above code leaves C<n> uninitialized
[containing garbage from the stack].}

So the only choice for the function is to change the data that
C<issuer> points to.  Well, if you don't initialize C<issuer>
[with the C<NO_INIT> that you tried], then it [the pointer]
contains garbage from the stack -- so it points to some "random"
address.  So C<cardmod> has no choice but to try to write
the "issuer" information to wherever C<issuer> points so it
will try to write to some random address and die horribly.

So the C<cardmod> routine was either:

    Poorly written
    Poorly documented
    You didn't bother to read the documentation

or even all of the above.  But let's make it work anyway...

Since you imply that the only purpose for C<issuer> is for returning
information to the caller of C<cardmod> [that is, C<cardmod> doesn't
"read" the initial value of C<*issuer>], there is only one thing
possible:  C<cardmod> writes the issuer string to wherever C<issuer>
points.  So you, before calling C<cardmod>, _must_ set C<issuer> to
point to a buffer that is guaranteed to be big enough to hold the
longest possible "issuer" string that C<cardmod> might ever try to
return to you.  This requires you to read the documentation for
C<cardmod> to figure out exactly how big this is.  Since you don't
already appear to know this size, either the writer of C<cardmod>
didn't know the size [poorly written], didn't document it [poorly
documented], or you didn't read it.

So I'll assume that C<cardmod> does something reasonable like give
you a F<cardmod.h> that #define's C<MAX_ISSUER_LEN> to be this
maximum length.  Then you could do [untested]:

void
cardmod( cardnum, issuer, details, statflag )
        char *          cardnum
        char *          issuer= issuer_buf
        char *          details
        unsigned long * statflag
    PREINIT:
        char issuer_buf[MAX_ISSUER_LEN];
    OUTPUT:
        issuer

Of course, this allocates a buffer on the stack so that C<cardmod>
can copy a string to it and then [in the OUTPUT: code] looks up
the length of this string, allocates a buffer of that size, and
copies the string again.  So you could consider just allocating
the final buffer and having C<cardmod> write directly to that:

#define init_buf( var, sv, len )        STMT_START {    \
        STRLEN n_a;                                     \
        if(  ! SvOK(sv)  )    sv_setpvn(sv,"",0);       \
        (void) SvPV_force( sv, n_a );                   \
        var= SvGROW( sv, 1+(len) );                     \
    } STMT_END

[...]

void
cardmod( cardnum, issuer, details, statflag )
        char *          cardnum
        char *          issuer: init_buf( $var, $arg, MAX_ISSUER_LEN );
        char *          details
        unsigned long * statflag

Then you'll need to do similar work for C<details>.
-- 
Tye McQueen    Nothing is obvious unless you are overlooking something
         http://www.metronet.com/~tye/ (scripts, links, nothing fancy)

Reply via email to