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

A server I am connecting to with a perl script is sending me a C struct in binary form. How would I get it and the values inside the struct, then send the server back a C struct also in binary form? I know that perl doesn't have structures and that one fix is using hashes, but im kinda in a bind figuring out how to do the aforementioned task. There is a proto.h file that I figured is supposed to be used in conjunction with the server:
#define PORT 2345 #define CHALLENGE 0 #define CORRECT 1 #define WRONG 2 struct recv { int flag; int b[4]; char nextpass[10]; }; typedef struct recv t_recv; struct send { int answer; char prevpass[10]; }; typedef struct send t_send;
But obviously I can't do #include "proto.h" in my perl code. any/all help would be appreciated.


-tengo mas que aprender.

Replies are listed 'Best First'.
(crazyinsomniac) Re: Managing C structs -with Perl-
by crazyinsomniac (Prior) on Jan 01, 2002 at 17:55 UTC
    Hello. I know of two modules which are capable of doing this. One is C::DynaLoad::Struct and the other is C::Include. You could probably do it using C::Inline as well. C::Include is perl based, but requires Storable.pm for caching (I find this kinda odd). It however, is your best bet. If you wish to do this without installing modules ( a silly requirement), you can probably rip off C::Include real easy (I would at least strip the Storable caching stuff). All of these have a lot of documentation. Knowledge of c is required. I tried out C::Include, and all the samples included work. Good luck (btw - I am looking into the future, and I see a tutorial on one of these, or at least a meditation on how you dealt with it - and code, always code ;D)

    update:

    #!/usr/bin/perl -w use C::Include; use strict; use vars qw/$inc $send $buffer/; $inc = new C::Include( \*DATA ); # Make struct instance $send = $inc->make_struct('send'); use Data::Dumper; print Dumper $send; # Fill struct fields $$send{prevpass} = '0123456789'; $$send{answer} = 1; # Pack struct to buffer $buffer = $send->pack(); # Struct size printf "Size of struct send: %d bytes\n", $send->size; printf "Size of unsigned long: %d bytes\n", $inc->sizeof('unsigned lon +g'); # Print buffer to STDOUT &hview( \$buffer ); #require 'hview.pl'; ## crazyinsomniac's note ~ hview.pl is distributed with the module, pr +ops to the author sub hview(\$){ my $str = shift; my @lines = unpack 'a16'x(length($$str)/16+(length($$str)%16?1:0)) +, $$str; for( 0..$#lines ){ printf "%08X: %-17sł %-17s %-16s\n", $_*16, (map{ sprintf '%-2s ' x 8, map{ sprintf'%02X',$_ }unpack 'C*', $_ }unpack 'a8a8', $lines[$_]), $lines[$_]; } } 1; __END__ #define PORT 2345 #define CHALLENGE 0 #define CORRECT 1 #define WRONG 2 struct recv { int flag; int b[4]; char nextpass[10]; }; typedef struct recv t_recv; struct send { int answer; char prevpass[10]; }; typedef struct send t_send;
    I tried the above, and got
    $>perl test4.pl $VAR1 = bless( { '' => { 'length' => 14, 'mask' => 'iZ10', 'tag' => 'send', 'buffers' => [ \undef, \undef ] }, 'prevpass' => ${$VAR1->{''}{'buffers'}[1]}, 'answer' => ${$VAR1->{''}{'buffers'}[0]} }, 'C::Include::Struct' ); Size of struct send: 14 bytes Size of unsigned long: 4 bytes 00000000: 01 00 00 00 30 31 32 33 ¦ 34 35 36 37 38 00  012 +345678 $>
    That seems pretty cool.
    ** Note: I am using C::Include $VERSION = 1.40;

    I tried $VERSION = 1.20;, but It didn't like your macro (WARNING: Unashed code ...) so I upgraded ... and all this after plenty of rum and coke (and some Jose Quervo tequila to boot)

     
    ______crazyinsomniac_____________________________
    Of all the things I've lost, I miss my mind the most.
    perl -e "$q=$_;map({chr unpack qq;H*;,$_}split(q;;,q*H*));print;$q/$q;"

Re: Managing C structs -with Perl-
by clintp (Curate) on Jan 01, 2002 at 20:49 UTC
    Another solution is to use pack and unpack to take the C structure and convert it back-and-forth into an appropriate Perl structure. A small example of this can be found on the current sample page for my book.

    There may also be an example in the proposed perlpacktut that's been floating around the perl 5 porters mailing list.

Re: Managing C structs -with Perl-
by khkramer (Scribe) on Jan 02, 2002 at 02:26 UTC
    This is a perfect application for pack and unpack. It looks to me like your two transformation statements should be something like so:
    my ($flag, $b1, $b2, $b3, $b4, $nextpass) = unpack ( "iiiiiZ*", $received_binary_struct ); my $binary_struct_to_send = pack ( "iZ*", $answer, $prevpass );
    I wrote a little test-case using Inline::C to make sure that it looks like that to gcc, too <laugh>...
    #!/usr/bin/perl -w use Inline C; use strict; $|++; my $recv = test_recv_struct(); print "recv: " . join ( ",", unpack ( "iiiiiZ*", $recv ) ) . "\n"; my $send = pack ( "iZ*", 1, "string two" ); test_send_struct ( $send ); __END__ __C__ struct recv { int flag; int b[4]; char nextpass[10]; }; typedef struct recv t_recv; struct send { int answer; char prevpass[10]; }; typedef struct send t_send; //----- SV* test_recv_struct() { t_recv* foo = malloc ( sizeof(t_recv) ); foo->flag = 1; foo->b[0] = 2; foo->b[1] = 3; foo->b[2] = 4; foo->b[3] = 5; sprintf( foo->nextpass, "a string" ); return newSVpv ( (char*)foo, sizeof(t_recv) ); } void test_send_struct ( char* perl_packed ) { t_send* foo = perl_packed; printf ( "send: %d,%s\n", foo->answer, foo->prevpass ); }

    Inline is really nifty!

    Kwin
Re: Managing C structs -with Perl-
by Zaxo (Archbishop) on Jan 02, 2002 at 05:49 UTC

      I'm not sure Inline::Struct does do the right thing, here. Inline::Struct provides a simple way to manipulate a a C struct as a Perl object (which is very often exactly what one needs to do in the context of writing a bunch of Inline::C code). But AFAICT there are no documented methods for reading in a C struct from a blob of bytes or for dumping one out.

      That's exactly what pack and unpack are for.

      Having said that, it's probably worth writing to the Inline mailing list and suggesting such an addition to the API.

      Kwin