mbr has asked for the wisdom of the Perl Monks concerning the following question:

I'm writing and XS perl extension that calls an underlying C function. The C function has prototype 'void fcn(char *output)' and 'output' is null terminated so it is a string. The problem is that I don't seem to be able to get perl to recognize where the end of the 'output' string is. Here is the 'CODE:' section of my .xs file:
CODE: { RETVAL = NEWSV(0, 16); SvPOK_only(RETVAL); SvCUR_set(RETVAL, 16); fcn(SvPV(RETVAL, len)); printf("string: %s length: %d", RETVAL, len); }
If I make use of this code from perl, the length of the string is always 16 even if the C function returns a string has a NULL char at, say, position 8. Can anyone help me? What am I doing wrong?

Thanks!

--mbr

Edit kudra, 2002-08-17 Replaced pre tags

Replies are listed 'Best First'.
Re: perl guts and the SvPV() macro
by derby (Abbot) on Aug 16, 2002 at 12:21 UTC
    mbr, using a macro inside a function call is not considered safe. From perlguts:

    Also remember that C doesn't allow you to safely say
    "foo(SvPV(s, len), len);". It might work with your com­
    piler, but it won't work for everyone.  Break this sort of
    statement up into separate assignments:
    
       SV *s;
       STRLEN len;
       char * ptr;
       ptr = SvPV(s, len);
       foo(ptr, len);
    
    

    And len is filled by SvPV and perl is pretty liberal when grabbing memory.

    -derby

    update Okay now that I read your question a little better ... when you do the NEWSV(0,16), you've basically preallocated the SV to hold upto 17 chars (16 + 1 for the null byte). SvPV will return the amount of space available in the SV (not the amount contained in the string). You're still better off separating the macro from the function and then using the C pointer and system calls

    char *foo = SvPV( RETVAL, 0 ); fcn( foo ); printf( "string: %s length: %d", foo, strlen(foo) );

    And here's a bit of Inline showing that SvPV returns the amount of allocated space, not the length of the contained string (caution: very contrived):

    #!/usr/bin/perl -w use Inline C; svfunc(); __END__ __C__ void svfunc( ) { SV *val; STRLEN x; char *ptr; ptr = malloc( 5 ); memset( ptr, 0, 5); memset( ptr, 65, 4 ); val = newSVpv( ptr, 20 ); ptr = SvPV( val, x ); printf( "String is %s with len of %d (%d)\n", ptr, x, strlen(ptr) ); }

    yields: String is AAAA with len of 20 (4)

Re: perl guts and the SvPV() macro
by rsteinke (Scribe) on Aug 16, 2002 at 18:06 UTC

    It's unlikely that the C function with the prototype you give is passing back a string. You're passing in a character buffer(as char*), and it doesn't know how long it is unless it already contains a null terminated string.

    There are basically two ways to return a string in C:

    const char* fcn(void);

    returns a pointer to an internal buffer. More common (for functions such as read()) is

    int fcn(char *buffer, int bufsize);

    where bufsize is the size of the buffer, and the function returns the number of characters read. In this case, what you want to do is something like:

    const int bufsize = 16; int len; RETVAL = NEWSV(0,bufsize); len = fcn(SvPV_nolen(RETVAL), bufsize); SvCUR_set(RETVAL, len);

    The main problem I see with your code is you never find out the length of the string returned by fcn().

    Ron Steinke rsteinke@w-link.net