[comp.unix.i386] Serial I/O null read problem?

sws@holin.ATT.COM (Steve Spear) (03/09/90)

  Can someone please explain to me why the code below can read every character
except NULL from a terminal or serial port?  I'm primarily using this on Xenix
but it failed on Unix also.  I'm using it for an Xmodem receiver and it drops
any block with a NULL ( ascii 0 ) in it.  Since the last xmodem block is full
of zeros for padding the last block always fails.  Somehow it only gets half the
nulls in the block and then times out.  This is the simplest case I have for
the problem - please excuse the sloppy code - just hacking it up to get an idea
of whats going on.  I've been porting a multiuser bbs from dos to Xenix and
everything seems to be working but receiving NULL's - argh!!! I've read all I
can in the Xenix and Unix manuals and no mention of a problem with NULL - 

                                     thanks
                                         steve spear
 
#include  <stdio.h>
#include  <fcntl.h>
#include  <sys/types.h>
#include  <sys/stat.h>
#include  <memory.h>
#include  <string.h>
#include  <ctype.h>
#include  <stdlib.h>
#include  <math.h>
#include  <malloc.h>
#include  <time.h>
#include  <signal.h>
#include  <errno.h>	

static char *portname = "/dev/tty";
static int termfd;
static struct termio tio, oldtio;
typedef int bool;
#define TRUE	1
#define FALSE	0
#define XENIX	TRUE
static struct termio      tiosave;

void setblock( int fd, bool on )
{
  static int blockf, nonblockf;
  static bool first = TRUE;
  int flags;

  if ( first )
     {
       first = FALSE;
       if ( ( flags = fcntl( fd, F_GETFL, 0 ) ) == -1 )
          printf( "setblock error" );
       blockf = flags & O_NDELAY;
       nonblockf = flags | O_NDELAY;
     }
  ( void ) fcntl( fd, F_SETFL, on ? blockf : nonblockf );
}

static int readcond( int fd, char *buf, unsigned nbytes, bool block )
{
  int retval;
#ifdef XENIX
  if ( !block && rdchk( fd ) == 0 )
     return( 0 );
#else
  setblock( fd, block );
#endif
  retval = read( fd, buf, nbytes );
  return( retval ); 
}

#define EMPTY '\0'
static char cbuf = EMPTY;

bool cready()
{
  if ( cbuf != EMPTY )
     return( TRUE );
  switch( readcond( termfd, &cbuf, 1, FALSE ) )
    {
      case -1 :
        printf( "read error" );
        break;
      case 0 :
        return( FALSE );
      default :
        return( TRUE );
    }
}

int cget()
{
  int retval;
  char c;

  if ( cbuf != EMPTY )
     {
       c = cbuf;
       cbuf = EMPTY;
       return( c & 0377 );
     }
  retval = readcond( termfd, &c, 1, TRUE );
  switch( retval )
    {
      case -1 :
         printf( "read 2 error" );
      case 0 :
         return( -1 );
      default :
         return( c & 0377 );
    }
}

void termopen( void )
{
  int fd2;

  termfd = open( portname, O_RDWR | O_NDELAY );
  if ( termfd != -1 )
     {
       ioctl( termfd, TCGETA, &oldtio );	/* get orig modem settings*/
       ioctl( termfd, TCGETA, &tio );		/* get orig modem settings*/
       tio.c_lflag &= ~(ISIG|ICANON|ECHO);
       tio.c_iflag |= (BRKINT|IGNPAR);
       tio.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXANY|IXOFF
			|INPCK|ISTRIP);
       tio.c_oflag &= ~OPOST;
       tio.c_cc[ VMIN ]  = 1;
       tio.c_cc[ VTIME ] = 10;
       ioctl( termfd, TCSETAW, &tio );		/* set the new settings	  */
     }
  fd2 = open( portname, O_RDWR );
  close( termfd );
  termfd = fd2;
}

void termclose( void )
{
  unsetraw();
}

void main()
{
  int k;

  termopen();
  while ( TRUE )
    {
      k = cget();
      if ( k == 'q' )
         break;
      printf( "%c", k );
      if ( !cready() )
         printf( "\r\n" );
    }
  termclose();
}