package Net::Hotline::Shared;

## Copyright(c) 1998-2002 by John C. Siracusa.  All rights reserved.  This
## program is free software; you can redistribute it and/or modify it under
## the same terms as Perl itself.

use strict;

use Carp;
use IO::Handle;
use POSIX qw(F_GETFL F_SETFL O_NONBLOCK EINTR EWOULDBLOCK EAGAIN);

use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS $VERSION);

require Exporter;
@ISA       = qw(Exporter);
@EXPORT_OK = qw(_encode _write _read _hexdump _debug _set_blocking);
%EXPORT_TAGS = (all => \@EXPORT_OK);

$VERSION = '0.80';

sub _debug
{
  if($Net::Hotline::Client::DEBUG)
  {
    print STDERR join('', @_);
  }
}

sub _encode
{
  my($data) = join('', @_);

  my($i, $len, $enc);

  $len = length($data);
  $enc = '';

  for($i = 0; $i < $len; $i++)
  {
    $enc .= pack("C", (255 - unpack("C", substr($data, $i, 1))));
  }

  return $enc;
}

sub _write
{
  my($fh, $data_ref, $length) = @_;

  my($written, $offset);

  $offset = 0;

  while($length > 0) # Handle partial writes
  {
    $written = syswrite($fh, $$data_ref, $length, $offset);
    next  if($! == EINTR);
    unless(defined($written))
    {
      next  if($! == EWOULDBLOCK || $! == EAGAIN);
      croak("System write error(", $! + 0, "): $!\n");
    }
    $length -= $written;
    $offset += $written;
  }

  return $offset;
}

sub _read
{
  my($fh, $data_ref, $length, $blocking) = @_;

  my($offset)   = 0;
  my($read)     = 0;

  $blocking = 1  unless(defined($blocking));

  #_debug("Reading $length...");

  while($length > 0) # Handle partial reads
  {
    $read = sysread($fh, $$data_ref, $length, $offset);

    unless(defined($read))
    {
      next  if($! == EINTR);

      # Once we read a little bit, we keep readinuntil we get it all
      # Otherwise, we can return undef and treat it as a WOULDBLOCK
      if($blocking || $offset > 0)  { next }
      else                 { return }
    }

    $offset   += $read;
    $length   -= $read;
  }

  #_debug("read $offset ($length)\n");
  return($offset);
}

sub _set_blocking
{
  my($fh, $blocking) = @_;

  if($IO::VERSION >= 1.19) # The easy way, with the IO module
  {
    $fh->blocking($blocking);
  }
  else # The hard way...not 100% successful :-/
  {
    my($flags) = fcntl($fh, F_GETFL, 0);

    defined($flags) || croak "Can't get flags for socket: $!\n";

    if($blocking)
    {
      fcntl($fh, F_SETFL, $flags & ~O_NONBLOCK) ||
        croak "Can't make socket blocking: $!\n";
    }
    else
    {
      fcntl($fh, F_SETFL, $flags | O_NONBLOCK) ||
        croak "Can't make socket nonblocking: $!\n";
    }     
  }
}

sub _hexdump
{
  my($data) = join('', @_);

  my($ret, $hex, $ascii, $len, $i);

  $len = length($data);

  for($i = 0; $i < $len; $i++)
  {
    if($i > 0)
    {
      if($i % 4 == 0)
      {
        $hex .= ' ';
      }

      if($i % 16 == 0)
      {
        $ret .= "$hex$ascii\n";
        $ascii = $hex = '';
      }
    }

    $hex .= sprintf("%02x ", ord(substr($data, $i, 1)));

    $ascii .= sprintf("%c", (ord(substr($data, $i, 1)) > 31 and
                             ord(substr($data, $i, 1)) < 127) ?
                             ord(substr($data, $i, 1)) : 46);
  }

  if(length($hex) < 50)
  {
    $hex .= ' ' x (50 - length($hex));
  }

  $ret .= "$hex  $ascii\n";

  return $ret;
}

1;