vclient_sih.cc
上传用户:psq1974
上传日期:2007-01-06
资源大小:1195k
文件大小:19k
源码类别:

mpeg/mp3

开发平台:

C/C++

  1. /* Copyright (C) 1998, 1999 State University of New York at Stony Brook
  2.    Author: Andrew V. Shuvalov ( andrew@ecsl.cs.sunysb.edu )
  3.    Software license is located in file "COPYING"
  4. */
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <sys/socket.h>
  8. #include <sys/un.h> 
  9. #include <arpa/inet.h>
  10. #undef  HAVE_UNISTD_H
  11. #include <mtvp/mtvp.h>
  12. #include "mtvp/misc.h"
  13. #include "mtvp/sih_api.h"
  14. #undef assert
  15. #include <assert.h>
  16. #include "session.h"
  17. #include "playback_window.h"
  18. #include "../../pusher/src/packet_header.h"
  19. #include "../../pusher/src/timeval.h"
  20. #include "../../pusher/src/sockaddr.h"
  21. //#define DEBUG
  22. #define CLOSE_SOCK
  23. const unsigned maxPacketLength = 1472;
  24. static void debug( int level, const SIH_RESOURCES *sih_resources,
  25.    const char *format, ... )
  26. {
  27. # ifdef DEBUG
  28.   va_list ap;
  29.   va_start( ap, format );
  30.   vprintf( format, ap );
  31.   va_end( ap );
  32. # else
  33.   va_list ap;
  34.   va_start( ap, format );
  35.   mtv_msg *msg = (mtv_msg *) new char[ sizeof( mtv_msg ) + 1024 ];
  36.   msg->type = msg->debug;
  37.   msg->int_param = level;
  38.   vsnprintf( msg->data, 1024, format, ap );
  39.   va_end( ap );
  40.   (*sih_resources->feedback)(sih_resources, msg,
  41.      sizeof( mtv_msg ) + strlen( msg->data ) );
  42.   delete msg;
  43. # endif
  44. }
  45. /** just forvard declarations...
  46.  */
  47. class HANDLER_DATA;
  48. class STREAM_DATA {
  49. public:
  50.   HANDLER_DATA *handler_data;     /* should always be there */
  51.   int  sock;
  52.   /** UDP packet is loaded into FIFO buffer, including header. When output to
  53.       the playerSocket, header must be stripped */
  54.   queue< PacketHeader * > dataToPlay;
  55.   int sequence;
  56.   STREAM_DATA( const Sockaddr &addr, const SIH_RESOURCES *sih_resources ) 
  57.     : sock( -1 )
  58.     {
  59. #     ifdef CLOSE_SOCK
  60.       sock = open_socket( addr, sih_resources );
  61. #     endif
  62.     }
  63.   static int open_socket( const Sockaddr &addr, 
  64.   const SIH_RESOURCES *sih_resources )
  65.     {
  66.       int sock = socket( PF_INET, SOCK_DGRAM, 0 );
  67.       if( -1 == sock )
  68. {
  69.   debug( 1, sih_resources, 
  70.  "SIH plugin: socket fails, %sn", sys_errlist[ errno ] );
  71.   return -1;
  72. }
  73.       if( -1 == bind ( sock, (struct sockaddr *) &addr.addr, 
  74.        sizeof(addr.addr)))
  75. {
  76.   sock = -1;
  77.   debug( 1, sih_resources,
  78.  "SIH plugin: bind playback %d port fails, %sn", 
  79.  sock, sys_errlist[ errno ] );
  80.   return -1;
  81. }
  82.       // set non-block mode
  83.       int oldflags = fcntl( sock, F_GETFL, 0 );
  84.       if( oldflags == -1 )
  85. {
  86.   close( sock );
  87.   sock = -1;
  88.   debug( 1, sih_resources,
  89.  "SIH plugin: fcntl fails, %sn", sys_errlist[ errno ] );
  90.   return -1;
  91. }
  92.       oldflags |= O_NONBLOCK;
  93.       if( -1 == fcntl( sock, F_SETFL, oldflags ))
  94. {
  95.   close( sock );
  96.   sock = -1;
  97.   debug( 1, sih_resources,
  98.  "SIH plugin: fcntl fails, %sn", sys_errlist[ errno ] );
  99.   return -1;
  100. }
  101.       debug( 2, sih_resources,
  102.      "SIH: new sockaddr is %dn", sock );
  103.       return sock;
  104.     }
  105.   /**  do not close socket - all sockets remains open until program ends,
  106.        if not directed by #ifdef CLOSE_SOCK
  107.    */
  108.   ~STREAM_DATA()
  109.     {
  110.       while( dataToPlay.size() > 0 )
  111. {
  112.   delete dataToPlay.front();
  113.   dataToPlay.pop();
  114. }
  115. #     ifdef CLOSE_SOCK
  116.       close( sock );
  117. #     ifdef DEBUG
  118.       cout << "SIH: close socketn";
  119. #     endif
  120. #     endif
  121.     }
  122. };
  123. class static_stream_dataC {
  124.   STREAM_DATA *stream_data;
  125.   Sockaddr saddr;
  126. # ifndef CLOSE_SOCK
  127.   /** when allocates stream_data - copy this sockaddr there 
  128.    */
  129.   int  static_sockaddr;
  130. # endif
  131. public:
  132.   static_stream_dataC() : stream_data( NULL )
  133. #   ifndef CLOSE_SOCK
  134.     , static_sockaddr( -1 )
  135. #   endif
  136.     {}
  137.   /** we expect the url to be plugin_url_prefix host_name_or_ip:port
  138.       ( plugin_url_prefix must end by :// ) in the case when host is 
  139.       specified or in the form plugin_url_prefix port 
  140.       if host must be set to htonl (INADDR_ANY);
  141.    */
  142.   static_stream_dataC( const char *url, HANDLER_DATA *handler_data,
  143.        const SIH_RESOURCES *sih_resources )
  144.     : stream_data( NULL )
  145. #   ifndef CLOSE_SOCK
  146.     , static_sockaddr( -1 )
  147. #   endif
  148.     {
  149.       cout << "static_stream_dataCn";
  150.       if( strncmp( url, plugin_url_prefix, sizeof( plugin_url_prefix )-1 ))
  151. {
  152.   debug( 1, sih_resources,
  153.  "SIH plugin: wrong url prefixn" ); 
  154.   return;  // error
  155. }
  156.       string site_and_port( url + sizeof( plugin_url_prefix ) - 1 );
  157.       // and fill out the saddr structure:
  158.       saddr.addr.sin_family = AF_INET;
  159.       
  160.       int pos = site_and_port.find( ':' );
  161.       if( pos == -1 )
  162. {
  163.   // no hostaddr -> any addr
  164.   saddr.addr.sin_addr.s_addr = htonl (INADDR_ANY);       // input
  165.   saddr.addr.sin_port = htons( atoi( site_and_port.c_str() ) );
  166. }
  167.       else
  168. {
  169.   string s_site( site_and_port, 0, pos );
  170.   string s_port( site_and_port, pos + 1 );
  171.   // detect if site is hostname or ip addr
  172.   if( s_site[0] < '0' || s_site[0] > '9' )
  173.     {
  174.       // hostname
  175.       struct hostent *he = gethostbyname( s_site.c_str() );
  176.       if( he == NULL )
  177. {
  178.   perror( "gethostbyname" );
  179.   return;
  180. }
  181.       saddr.addr.sin_addr = *(struct in_addr *) he->h_addr;
  182.     }
  183.   else
  184.     {
  185.       // just numbers of IP address
  186.       if( !inet_aton( s_site.c_str(), &saddr.addr.sin_addr ))
  187. {
  188.   perror( "Error converting inet_aton" );
  189.   return;
  190. }
  191.     }
  192.   saddr.addr.sin_port = htons( atoi( s_port.c_str() ) );
  193. }
  194. #     ifndef CLOSE_SOCK
  195.       static_sockaddr = 
  196. STREAM_DATA::open_socket( saddr, sih_resources );
  197. #     endif
  198.     }
  199.   /** close socket, finally
  200.    */
  201.   ~static_stream_dataC()
  202.     {
  203.       if( stream_data )
  204. {
  205.   cerr << "SIH: delete stream_data for opened stream" << endl;
  206.   delete stream_data;
  207. }
  208. #     ifndef CLOSE_SOCK
  209.       close( static_sockaddr );
  210.       cerr << "SIH: close socket " << static_sockaddr << endl;
  211. #     endif
  212.     }
  213.   
  214.   bool isValid() 
  215.     { 
  216.       // port is not zero only if constructor executed without errors
  217.       return saddr.addr.sin_port != 0;
  218.     }
  219.   STREAM_DATA *allocate_stream_data( HANDLER_DATA *handler_data,
  220.      const SIH_RESOURCES *sih_resources )
  221.     {
  222.       if( stream_data != NULL )
  223. {
  224.   cerr << "SIH: try to allocate stream_data when it is already "
  225.     "allocated" << endl;
  226.   return stream_data;
  227. }
  228.       stream_data = new STREAM_DATA( saddr, sih_resources );
  229.       stream_data->handler_data = handler_data;
  230. #     ifndef CLOSE_SOCK
  231.       stream_data->sock = static_sockaddr;
  232. #     endif
  233.       return stream_data;
  234.     }
  235.   /** just return true if this is ours stream data
  236.    */
  237.   bool check_by_stream_data( STREAM_DATA *_stream_data )
  238.     {
  239.       return stream_data == _stream_data;
  240.     }
  241.   void deallocate_stream_data_struct()
  242.     {
  243. #     if defined( CLOSE_SOCK ) && defined( DEBUG )
  244.       cout << "SIH: deallocate_stream_datan";
  245. #     endif
  246.       delete stream_data;
  247.       stream_data = NULL;
  248.     }
  249. };
  250. /** map from url name to static stream data
  251.  */
  252. typedef map< string, static_stream_dataC * > url_to_streamdataT;
  253. /** will be allocated by the SIH_init
  254.  */
  255. class HANDLER_DATA {
  256.   const SIH_RESOURCES *sih_resources;
  257.   url_to_streamdataT url_to_streamdata;
  258. public:
  259.   stack< PacketHeader * > inusedBuffers;
  260.   int previous_timestamp_sec;
  261. public:
  262.   HANDLER_DATA(const SIH_RESOURCES *r) : sih_resources(r) {}
  263.   ~HANDLER_DATA()
  264.     {
  265.       // loop to close all streams
  266.       url_to_streamdataT::iterator it;
  267.       
  268.       for( it = url_to_streamdata.begin(); it != url_to_streamdata.end(); 
  269.    it++ )
  270. {
  271.   // remove static_stream_dataC
  272.   delete (*it).second;
  273. }
  274.       // and free the map itself
  275.       url_to_streamdata.clear();
  276.     }
  277.   /** find existing stream ( socket ) or create the new one
  278.    */
  279.   STREAM_DATA *get_stream_dat_byUrl( const char *url,
  280.      HANDLER_DATA *handler_data )
  281.     {
  282.       if( strncmp( url, plugin_url_prefix, sizeof( plugin_url_prefix )-1 ))
  283. return NULL;  // error
  284.       // test if we already have it
  285.       url_to_streamdataT::iterator it = url_to_streamdata.find( url );
  286.       if( it != url_to_streamdata.end() )
  287. {
  288.   // this socket was opened previously. 
  289.   return (*it).second->allocate_stream_data( handler_data,
  290.      sih_resources );
  291. }
  292.       // create the new one
  293.       static_stream_dataC *sd = new static_stream_dataC( url, handler_data,
  294.  sih_resources );
  295.       if( ! sd->isValid() )
  296. return NULL;
  297.       // only if it is valid
  298.       url_to_streamdata[ url ] = sd;
  299.       return sd->allocate_stream_data( handler_data, sih_resources );
  300.     }
  301.   void deallocate_stream_data( STREAM_DATA *stream_data )
  302.     {
  303.       // loop over all stream data's and locate which to clear
  304.       url_to_streamdataT::iterator it;
  305.       
  306.       for( it = url_to_streamdata.begin(); it != url_to_streamdata.end(); 
  307.    it++ )
  308. {
  309.   if( (*it).second->check_by_stream_data( stream_data ))
  310.     {
  311.       (*it).second->deallocate_stream_data_struct();
  312.       break;
  313.     }
  314. }
  315.     }
  316.   void debug( int level, const char *format, ... )
  317.     {
  318. # ifdef DEBUG
  319.       va_list ap;
  320.       va_start( ap, format );
  321.       vprintf( format, ap );
  322.       va_end( ap );
  323. # else
  324.       va_list ap;
  325.       va_start( ap, format );
  326.       mtv_msg *msg = (mtv_msg *) new char[ sizeof( mtv_msg ) + 1024 ];
  327.       msg->type = msg->debug;
  328.       msg->int_param = level;
  329.       vsnprintf( msg->data, 1024, format, ap );
  330.       va_end( ap );
  331.       (*sih_resources->feedback)(sih_resources, msg,
  332.  sizeof( mtv_msg ) + strlen( msg->data ) );
  333.       delete msg;
  334. # endif
  335.     }
  336.   void send_timestamp( const PacketHeader *data )
  337.     {
  338.       // data may come with no timestamp. Don't send it
  339.       if( data->when.tv_sec == 0 )
  340. return;
  341.       if( data->when.tv_sec == previous_timestamp_sec ) // not changed
  342. return;
  343.       previous_timestamp_sec = data->when.tv_sec;
  344.       mtv_msg *msg = (mtv_msg *) new char[ sizeof( mtv_msg ) 
  345.  + sizeof( struct timeval ) ];
  346.       msg->type = msg->timestamp;
  347.       *( struct timeval * ) msg->data = data->when;
  348.       (*sih_resources->feedback)(sih_resources, msg, sizeof( mtv_msg ) 
  349.  + sizeof( struct timeval ) );
  350.       delete msg;
  351.     }
  352. };
  353. /** tests if it can handle this URL
  354.  */
  355. SIH_probe_url(const SIH_RESOURCES *, const char *url)
  356. {
  357.   if( !strncmp( url, plugin_url_prefix, sizeof( plugin_url_prefix )-1 ))
  358.     return 0;
  359.   return -1;
  360. }
  361. static void *SIH_init( const SIH_RESOURCES *sih_resources, const char *p )
  362. {
  363.   printf("SIH_init with "%s"n", p);
  364.   HANDLER_DATA *handler_data = new HANDLER_DATA( sih_resources );
  365.   if (handler_data == NULL) {
  366.     perror("SIH_init");
  367.     return NULL;
  368.   }
  369.   return (void *)handler_data;
  370. }
  371. static int SIH_free( void *void_handler_data )
  372. {
  373.   /* this call should be thread safe. do synchronisation */
  374.   static bool execute_flag = false;
  375.   if( execute_flag )
  376.     return 0;
  377.   execute_flag = true;
  378.   HANDLER_DATA *handler_data = (HANDLER_DATA *)void_handler_data;
  379.   printf("SIH_freen");
  380.   delete handler_data;
  381.   execute_flag = false;
  382.   return 0;
  383. }
  384. static void *SIH_open_url( void *void_handler_data, const char *url )
  385. {
  386. # ifdef DEBUG
  387.   cout << "SIH: open url " << url << endl;
  388. # endif
  389.   HANDLER_DATA *handler_data = (HANDLER_DATA *)void_handler_data;
  390.   /*
  391.    * The parameter of SIH_open_url is an URL passed by the application
  392.    * to the COM_SIH_OPEN_URL command.  The URL identifies the stream
  393.    * to be opened.
  394.    *
  395.    * SIH_open_url returns a pointer to an opaque object that
  396.    * identifies the stream, i.e.
  397.    * only your SIH plug-in known about the structure of the
  398.    * opaque object.  If the specified stream cannot be opened,
  399.    * SIH_open_url returns NULL and sets errno.
  400.    */
  401.   STREAM_DATA *stream_data = 
  402.     handler_data->get_stream_dat_byUrl( url, handler_data );
  403.   if (stream_data == NULL) {
  404.     perror("SIH_open_url");
  405.     return NULL;
  406.   }
  407.   return stream_data;
  408. }
  409. static int SIH_close( void *void_stream_data )
  410. {
  411.   STREAM_DATA *stream_data = (STREAM_DATA *)void_stream_data;
  412.   /*
  413.    * Close the stream.
  414.    * 
  415.    * This routine must close every fd opened by SIH_open_url and free the 
  416.    * PLUGIN_DATA
  417.    * structure.  All the resources allocated for the stream should be released.
  418.    * 
  419.    * After a call to SIH_close, the "void *stream"
  420.    * should be considered invalid.
  421.    */
  422.   printf("SIH_closen");
  423.   // we deallocate stream_data, but do not actually close the socket.
  424.   if( !stream_data->handler_data )
  425.     {
  426.       cerr << "SIH: deallocate null stream data" << endl;
  427.       return 0;
  428.     }
  429.   stream_data->handler_data->deallocate_stream_data( stream_data );
  430.   return 0;
  431. }
  432. static int SIH_fd( void *void_stream_data )
  433. {
  434.   STREAM_DATA *stream_data = (STREAM_DATA *)void_stream_data;
  435.   /*
  436.    * Returns the file descriptor associated with the stream.
  437.    * 
  438.    * This routine must return a real file descriptor.
  439.    * The Player calls a blocking select on this file descriptor
  440.    * and if there is something available for reading on it, calls SIH_read
  441.    * (provided that there is buffer space available to read some input data).
  442.    * 
  443.    * After calling SIH_fd, mtvp calls fcntl with F_SETFL
  444.    * to set O_NONBLOCK on the fd.
  445.    */
  446.   printf( "SIH_fd returns %dn", stream_data->sock );
  447.   return stream_data->sock;
  448. }
  449. static int SIH_read( void *void_stream_data, char *buf, size_t count )
  450. {
  451.   STREAM_DATA *stream_data = (STREAM_DATA *)void_stream_data;
  452. # ifdef DEBUG
  453.   // stream_data->handler_data->debug( 4, "SIH read %x, %dn", buf, count );
  454. # endif
  455.   /*
  456.    * SIH_read must behave exactly like the
  457.    * regular read routine when used on a fd
  458.    * that has O_NONBLOCK set.
  459.    * 
  460.    * If data becomes available for reading on the fd returned by SIH_fd,
  461.    * SIH_read is called. Note that mtvp may call SIH_read at any time, even
  462.    * when there is no data available on the fd.
  463.    * 
  464.    * SIH_read should always behave like a non-blocking read
  465.    * (i.e. like read behaves when the flag O_NONBLOCK set).
  466.    * This means that SIH_read should never block, even
  467.    * if there is not enough data available.
  468.    * 
  469.    * If there is no data available,
  470.    * SIH_read should return -1 with errno set to EAGAIN.
  471.    * 
  472.    * If there is less data available than requested,
  473.    * SIH_read should copy all the data available into the
  474.    * buffer and return the number of bytes copied.
  475.    * 
  476.    * SIH_read returning 0 means EOF.
  477.    * 
  478.    * SIH_read returning -1 with errno NOT set to EAGAIN
  479.    * means an error occured.  All read errors (except EAGAIN)
  480.    * are considered like EOF.
  481.    */
  482.   // Don't call select - we have non-blocking sockets opened
  483.   PacketHeader *data = 0;
  484.   int data_available = 0;
  485.   int num_of_buffers_we_need = count / maxPacketLength + 1;
  486.   while( --num_of_buffers_we_need >= 0  )
  487.     {
  488.       // get empty buffer
  489.       if( stream_data->handler_data->inusedBuffers.size() )
  490. {
  491.   data = stream_data->handler_data->inusedBuffers.top();
  492.   stream_data->handler_data->inusedBuffers.pop();
  493. }
  494.       else 
  495. data = (PacketHeader *)( new char[ maxPacketLength ] );
  496.       if( !data )
  497. {
  498.   stream_data->handler_data->debug( 1, "can't allocaten" );
  499.   return -1;
  500. }
  501.       // read
  502.       int size = read( stream_data->sock, data, maxPacketLength );
  503.       if( size == -1 && errno == EAGAIN )
  504. {
  505.   // non-blocking call - no data in socket
  506.   stream_data->handler_data->inusedBuffers.push( data );
  507.   break;
  508. }
  509.       if( size == -1 )
  510. {
  511.   // error
  512.   stream_data->handler_data->debug( 1, "recv error, %sn", 
  513.     sys_errlist[ errno ] );
  514.   stream_data->handler_data->inusedBuffers.push( data );
  515.   return size;
  516. }
  517.       if( size == 0 )  // EOF
  518. {
  519.   stream_data->handler_data->debug( 1,
  520.  "no data in buffer, %sn", sys_errlist[ errno ] );
  521.   stream_data->handler_data->inusedBuffers.push( data );
  522.   break;
  523. }
  524.       if( size < maxPacketLength )
  525. {
  526.   stream_data->handler_data->debug( 1,
  527.  "warning: udp packet corruptedn" );
  528. }
  529.       stream_data->dataToPlay.push( data );
  530.       stream_data->handler_data->send_timestamp( data );
  531. #     ifdef DEBUG
  532.       stream_data->handler_data->debug( 1, "got packet " );
  533. #     endif
  534.     }  
  535.   
  536.   // maybe we have data in old buffers?
  537.   int amount_read = 0;
  538. # ifdef DEBUG
  539.   if( stream_data->dataToPlay.size() == 0 )
  540.     stream_data->handler_data->debug( 1, "no data in buffersn" );
  541. # endif
  542.   while( count > 0 && stream_data->dataToPlay.size() )
  543.     {
  544.       data = stream_data->dataToPlay.front();
  545.       // reset sequence if needed
  546.       if( data->sequence == 0 )
  547. stream_data->sequence = 0; // this is local counter
  548.       // if sequence is corrupted, than you have to skip data until next
  549.       // I-frame. Than reset sequence
  550.       if( stream_data->sequence > 0 && 
  551.   stream_data->sequence + 1 != data->sequence )
  552. {
  553.   cout << "sequence is " << data->sequence << " expected " << 
  554.     stream_data->sequence + 1 << endl;
  555.   // reset sequence anyway, to start again
  556.   stream_data->sequence = 0;
  557.   bool got_it = false;
  558.   while( stream_data->dataToPlay.size() )
  559.     {
  560.       // fing a seqience of 7 times FF FF FF FF FF
  561.       int num = data->packetSize - data->dataOffset;
  562.       for( unsigned char *ptr = (unsigned char*)data+data->dataOffset; 
  563.    num > 7; num--, ptr++ )
  564. {
  565.   if( *ptr == 0xff && ptr[1] == 0xff && ptr[2] == 0xff && 
  566.       ptr[3] == 0xff && ptr[4] == 0xff && ptr[5] == 0xff &&
  567.       ptr[6] == 0xff )
  568.     {
  569.       got_it = true;
  570.       data->dataOffset = data->packetSize - num;
  571.       break;
  572.     }
  573. }
  574.       if( got_it == false )
  575. {
  576.   stream_data->handler_data->inusedBuffers.push( data );
  577.   stream_data->dataToPlay.pop();
  578.   if( stream_data->dataToPlay.size() )
  579.     data = stream_data->dataToPlay.front();
  580.   continue;
  581. }
  582.       break;
  583.     }
  584. }
  585.       // copy data to buffer
  586.       if( !stream_data->dataToPlay.empty() 
  587.   && data->dataOffset < data->packetSize )
  588. {
  589.   int size = min( data->packetSize - data->dataOffset, count );
  590.   memcpy( buf, (char*)data + data->dataOffset, size );
  591.   buf += size;
  592.   data->dataOffset += size;
  593.   amount_read += size;
  594.   count -= size;
  595. }
  596.       // remove packet from queue
  597.       if( data->dataOffset == data->packetSize )
  598. {
  599.   stream_data->handler_data->inusedBuffers.push( data );
  600.   // update sequence when current packet is gone
  601.   stream_data->sequence = data->sequence;
  602.   stream_data->dataToPlay.pop();
  603. }
  604.     }
  605.   // If there is no data available,
  606.   // SII_read should return -1 with errno set to EAGAIN
  607.   if( amount_read == 0 )
  608.     {
  609.       errno = EAGAIN;
  610.       return -1;
  611.     }
  612. # ifdef DEBUG
  613.   //  cout << amount_read << " readn";
  614. # endif
  615.   return amount_read;
  616. }
  617. static off_t SIH_lseek( void *void_stream_data, off_t offset, int whence )
  618. {
  619.   STREAM_DATA *stream_data = (STREAM_DATA *)void_stream_data;
  620.   stream_data->handler_data->debug( 3, "SIH lseek %offset %d, how %dn", 
  621.     offset, whence );
  622.   return -1;
  623. }
  624. int SIH_register(SIH_EXPOSED *exposed, int version)
  625. {
  626.   if (version == SIH_VERSION) 
  627.     { 
  628.       exposed->SIH_version = SIH_VERSION;
  629.       exposed->SIH_init = SIH_init;
  630.       exposed->SIH_free = SIH_free;
  631.       exposed->SIH_probe_url = SIH_probe_url;
  632.       exposed->SIH_open_url = SIH_open_url;
  633.       exposed->SIH_close = SIH_close;
  634.       exposed->SIH_read = SIH_read;
  635.       exposed->SIH_lseek = SIH_lseek;
  636.       exposed->SIH_cntl = NULL;
  637.       exposed->SIH_fd = SIH_fd;
  638.       exposed->SIH_size = NULL;
  639.       exposed->SIH_buffer = NULL;
  640.     }
  641.   return SIH_VERSION;
  642. }