Logo Search packages:      
Sourcecode: saoimage version File versions

ctrldisk.c

#ifndef lint
static char SccsId[] = "%W%  %G%";
#endif

/* Module:  ctrldisk.c (Control Disk Access)
 * Purpose: Open, close, read, and write disk files and streams.
 *          Centralized to facilitate selecting system specific code.
 * Subroutine:    open_disk()       returns: int
 * Subroutine:    fcntl_disk()            returns: void
 * Subroutine:    flush_disk()            returns: void
 * Subroutine:    lseek_disk()            returns: int
 * Subroutine:    read_disk()       returns: int
 * Subroutine:    write_disk()            returns: int
 * Subroutine:    close_disk()            returns: void
 * UNIX calls:    open(), fcntl(), lseek(), read(), write(), close()
 * Copyright:     1999 Smithsonian Astrophysical Observatory
 *          You may do anything you like with this file except remove
 *          this copyright.  The Smithsonian Astrophysical Observatory
 *          makes no representations about the suitability of this
 *          software for any purpose.  It is provided "as is" without
 *          express or implied warranty.
 * Modified:      {0} Michael VanHilst    initial version           6 Oct 1989
 *          {1} Jay Travisano (STScI)  VMS changes           10 Nov 1989
 *          {2} Doug Mink  blocking set to 0 in flush_disk   18 Oct 1995
 *          {3} Doug Mink  use stdio for I/O declarations    13 Aug 1998
 *          {4} Doug Mink  fix lseek dec. in VMS section     26 Feb 1999
 *          {n} <who> -- <does what> -- <when>
 */

#ifndef VMS

#include <stdio.h>            /* define stderr, FILE, NULL, etc */
#include <unistd.h>           /* define read, write, lseek */
#include <sys/file.h>         /* define open */
#include <fcntl.h>            /* define fcntl */

/*
 * Subroutine:    open_disk
 * Returns: File descriptor of open file (suitable for read())
 */
int
open_disk ( filename, write_flag, no_block )
     char *filename;
     int write_flag;    /* i: 1=WriteOnly, 0=ReadWrite, -1=ReadOnly */
     int no_block;      /* i: flag to set non-blocking flag */
{
  int flags;
  int fd;

  if( write_flag > 0 ) {
    flags = O_WRONLY;
    if( write_flag > 1 )
      /* create file if it doesn't exist, else condition causes error */
      flags |= O_CREAT;
  } else {
    if( write_flag < 0 )
      flags = O_RDONLY;
    else
      flags = O_RDWR;
  }
  if( no_block )
    flags |= O_NDELAY;
  if( (fd = open(filename, flags, 0)) == -1 ) {
    perror("open error");
    (void)fprintf(stderr, "ERROR: unable to open %s\n", filename);
    (void)fflush(stderr);
  }
  return( fd );
}

/*
 * Subroutine:    fcntl_disk
 * Purpose: Change an open file's flags, check for errors
 */
void
fcntl_disk ( fd, write_flag, no_block, filename )
     int fd;            /* i: already open file descriptor */
     int write_flag;    /* i: 1=WriteOnly, 0=ReadWrite, -1=ReadOnly */
     int no_block;      /* i: include non-blocking flag */
     char *filename;    /* [i]: name of file being accessed */
{
  int flags;

  if( write_flag > 0 ) {
    flags = O_WRONLY;
  } else {
    if( write_flag < 0 )
      flags = O_RDWR;
    else
      flags = O_RDONLY;
  }
  if( no_block )
    flags |= O_NDELAY;
  if( fcntl(fd, F_SETFL, flags) == -1) {
    perror("fcntl error");
    if( filename != NULL ) {
      (void)fprintf(stderr, "Error changing flags on %s\n", filename);
      (void)fflush(stderr);
    }
  }
}

/*
 * Subroutine:    flush_disk
 * Purpose: Read to functional end of file (useful with dynamic IO)
 */
void
flush_disk ( fd, filename )
     int fd;
     char *filename;
{
  int flags, tflags;
  int blocking;
  int bytes, total;
  char buf[4096];

  /* get status of device */
  if( (flags = fcntl(fd, F_GETFL, 0)) == -1 ) {
    (void)fprintf(stderr, "Error determining flag status on: %s\n", filename);
    perror("fcntl error");
  }
  /* clear blocking if device would block on read */
  blocking = 0;
  if( (flags & O_NDELAY) == 0 ) {
    blocking = 1;
    tflags = flags & (~O_NDELAY);
    if( fcntl(fd, F_SETFL, tflags) == -1) {
      (void)fprintf(stderr,
                "Error attempting to clear blocking on %s\n", filename);
      perror("fcntl error");
      return;
    }
  }
  total = 0;
  while( (bytes = read(fd, buf, 4096)) > 0 )
    total += bytes;
#ifdef DEBUG
  if( bytes < 0 ) {
    /* though non-blocking, some OS return "Operation would block" error */
    (void)fprintf(stderr, "Error attempting to flush %s\n", filename);
    perror("read error");
  }
  if( total > 0 )
    (void)fprintf(stderr, "Flushed %d bytes from pipe\n", total);
#endif
  /* restore device to oriiginal status if needed */
  if( blocking ) {
    if( fcntl(fd, F_SETFL, flags) == -1 ) {
      (void)fprintf(stderr,
                "Error attempting to restore blocking on %s\n", filename);
      perror("fcntl");
      return;
    }
  }
}

/*
 * Subroutine:    lseek_disk
 * Purpose: Skip into a disk file before reading, check for errors
 */
int
lseek_disk ( fd, nbytes, filename )
     int fd;
     int nbytes;
     char *filename;          /* [i]: name of file being accessed */
{

  if( lseek(fd, (long)nbytes, L_SET) < 0 ) {
    perror("lseek");
    (void)fprintf(stderr, "Can't lseek %d bytes", nbytes);
    if( filename != NULL )
      (void)fprintf(stderr, "into file %s\n", filename);
    else
      (void)fprintf(stderr, "\n");
    (void)fflush(stderr);
    return( -1 );
  }
  return( 0 );
}

/*
 * Subroutine:    read_disk
 * Purpose: Read bytes from a disk file, check for errors
 * Returns: Number of bytes read, or error flag (-1)
 * Note:    If report_error is 0, reading fewer than nbytes
 *          is not treated as an error.
 * Note:    BSD4.3 interrupts reads on any signal.  Thus reads may
 *          be broken into (hopefully consecutive) pieces.
 */
int
read_disk ( fd, buf, nbytes, report_error, filename, detail )
     int fd;
     char *buf;               /* i: buffer to receive read data */
     int nbytes;        /* i: number of bytes expected */
     int report_error;        /* i: report under-count read */
     char *filename;          /* [i]: name of file being read */
     char *detail;            /* [i]: "data", "header", etc */
{
  int got_this_read, got_so_far, left_to_get;
  int zero = 0;   /* Count of times with 0 bytes */

  for( got_so_far = 0; got_so_far < nbytes; got_so_far += got_this_read ) {
    left_to_get = nbytes - got_so_far;
    if( (got_this_read = read(fd, &(buf[got_so_far]), left_to_get) )
        != left_to_get ) {
      if( (got_this_read <= 0) &&
        ((got_this_read < 0) || (++zero > 3)) ) {
      /* Unfortunately, we can't distinguish between a non-blocking ..  *
       *  connection, and a read that was interrupted at the start. ... *
       *  We assume the former is more likely and dangerous.              *
       * Lest we fall into an endless loop, allow only 3 0-byte reads.  */
      if( got_this_read < 0 )
        perror("read error");
      if( report_error ) {
        (void)fprintf(stderr, "Error in reading");
        if( detail != NULL )
          (void)fprintf(stderr, " %s", detail);
        if( filename != NULL )
          (void)fprintf(stderr, " from %s", filename);
        (void)fprintf(stderr, "\n");
        (void)fflush(stderr);
        if( report_error && (got_so_far >= 0) ) {
          (void)fprintf(stderr, "Expected %d bytes, read %d\n",
                    nbytes, got_so_far);
          (void)fflush(stderr);
        }
      }
      return got_so_far;
      }
    }
  }
  return got_so_far;
}

/*
 * Subroutine:    write_disk
 * Purpose: Write data to the open disk file or stream
 */
int
write_disk ( fd, buf, nbytes, filename )
     int fd;
     char *buf;
     int nbytes;
     char *filename;
{
  int gave;

  if( (gave = write(fd, buf, nbytes)) < nbytes ) {
    /* if interrupted (or non-blocking reader), try the rest again */
    if( gave > 0 ) {
      /* I haven't tested this way of handling the situation with IRAF */
      nbytes -= gave;
      buf += gave;
      if( (gave = write(fd, buf, nbytes)) == nbytes )
      return( 0 );
    }
    perror(filename);
    return( -1 );
  }
  return( 0 );
}
    
/*
 * Subroutine:    close_disk
 * Purpose: Close a disk file
 */
void close_disk ( fd, filename )
     int fd;
     char *filename;
{
#ifdef DEBUG
  if( close(fd) == -1 ) {
    perror("close error");
    if( filename != NULL ) {
      (void)fprintf(stderr, "Error closing %s\n", filename);
      (void)fflush(stderr);
    }
  }
#else
  (void)close(fd);
#endif
}


#else


/*
 * VMS versions of disk functions.  This implementation of these functions
 * for VMS uses the standard C library calls, just as on Unix.  Although
 * this is the easiest approach for now, there are some problems:
 *
 *    Performance The VAX/VMS C library functions dealing with
 *                file i/o have never been very efficient.  Better
 *                performance (with some loss of generality, of
 *                course) can be gained by using RMS system routines.
 *                The VMS_OPEN_OPTIONS below should help, though.
 *
 *    VMS Files   VMS supports a number of different file types,
 *                which are sometimes handled differently by the
 *                the C library functions.  For example, a read()
 *                operation on a file with a record size of 512
 *                bytes will return a maximum of 512 bytes, regardless
 *                of the number requested in the call.  Thus, for
 *                these functions, we must loop around until all the
 *                i/o requested has been performed.
 *
 *    Seeking           Depending on the type of file being accessed, the
 *                operation of seeking to a byte offset in a file
 *                is somewhat unpredictable.  For stream files, it
 *                is usually okay, but for record files, only byte
 *                offsets that are on a record boundary are allowed.
 *                (As a result of this behavior, the -skip and -header
 *                command line options may not always perform as
 *                expected on VMS.)
 *
 * Note:  perror(NULL) will not work on VMS; some character string must
 *    be specified, even if just "".
 */

#define VMS_OPEN_OPTIONS "mbc=32","mbf=4","rop=RAH"

#include <stdio.h>            /* define stderr, FILE, NULL, etc */
#include <sys/file.h>         /* define open */

/*
 * Subroutine:    open_disk
 * Returns: File descriptor of open file (suitable for read())
 */
int
open_disk ( filename, write_flag, no_block )
     char *filename;
     int write_flag;    /* i: 1=WriteOnly, 0=ReadWrite, -1=ReadOnly */
     int no_block;      /* i: flag to set non-blocking flag */
{
  int flags;
  int fd;

  if( write_flag > 0 ) {
    flags = O_WRONLY;
    if( write_flag > 1 )
      /* create file if it doesn't exist, else condition causes error */
      flags |= O_CREAT;
  } else {
    if( write_flag < 0 )
      flags = O_RDONLY;
    else
      flags = O_RDWR;
  }
  if( no_block )
    flags |= O_NDELAY;
  if( (fd = open(filename, flags, 0, VMS_OPEN_OPTIONS)) == -1 ) {
    (void)fprintf(stderr, "ERROR: unable to open %s\n", filename);
    fflush(stderr);
    perror("open error");
  }
  return( fd );
}

/*
 * Subroutine:    fcntl_disk
 * Purpose: Change an open file's flags, check for errors
 */
void
fcntl_disk ( fd, write_flag, no_block, filename )
     int fd;                  /* i: already open file descriptor */
     int write_flag;          /* i: 0=read, else write */
     int no_block;            /* i: include non-blocking flag */
     char *filename;          /* [i]: name of file being accessed */
{
      /* fcntl() not supported on VMS */
}

/*
 * Subroutine:    lseek_disk
 * Purpose: Skip into a disk file before reading, check for errors
 */
int
lseek_disk ( fd, nbytes, filename )
     int fd;
     int nbytes;
     char *filename;          /* [i]: name of file being accessed */
{
  long lseek();               /* type not declared in <sys/file.h> */

  if( lseek(fd, (long)nbytes, SEEK_SET) < 0 ) {
    (void)fprintf(stderr, "Can't lseek %d bytes", nbytes);
    if( filename != NULL )
      (void)fprintf(stderr, "into file %s\n", filename);
    else
      (void)fprintf(stderr, "\n");
    fflush(stderr);
    perror("lseek error");
    return( -1 );
  }
  return( 0 );
}

/*
 * Subroutine:    read_disk
 * Purpose: Read bytes from a disk file, check for errors
 * Returns: Number of bytes read, or error flag (-1)
 * Note:    If report_error is 0, reading fewer than nbytes
 *          is not treated as an error.
 */
int
read_disk ( fd, buf, nbytes, report_error, filename, detail )
     int fd;
     char *buf;               /* i: buffer to receive read data */
     int nbytes;        /* i: number of bytes expected */
     int report_error;        /* i: report under-count read */
     char *filename;          /* [i]: name of file being read */
     char *detail;            /* [i]: "data", "header", etc */
{
      /* Note: On VMS, read() will return the number of bytes requested
       * or the logical record size, whichever is less.  Loop around
       * until we get everything we want.
       */
  int got, count=0;

  while (count < nbytes) {
    got = read(fd, (buf+count), (nbytes-count));
    if( got < 0 ) {
      (void)fprintf(stderr, "Error in reading");
      if( detail != NULL )
      (void)fprintf(stderr, " %s", detail);
      if( filename != NULL )
      (void)fprintf(stderr, " from %s", filename);
      (void)fprintf(stderr, "\n");
      fflush(stderr);
      perror("read error");
      return( got );
    } else if( got == 0 ) {               /* reached EOF ? */
      if( count < nbytes && report_error ) {
        (void)fprintf(stderr, "Expected %d bytes, read %d\n", nbytes, count);
        fflush(stderr);
      }
      break;
    } else {
      count += got;
    }
  }
  return( count );
}

/*
 * Subroutine:    write_disk
 * Purpose: Write data to the open disk file or stream
 */
void
write_disk ( fd, buf, nbytes, filename )
     int fd;
     char *buf;
     int nbytes;
     char *filename;
{
      /* Note: On VMS, write() may write the number of bytes requested
       * or the logical record size, whichever is less.  Loop around
       * until we write everything we want.
       */
  int gave, count=0;

  while (count < nbytes) {
    gave = write(fd, (buf+count), (nbytes-count));
    if( gave < 0 ) {
      perror (filename);
      break;
    } else {
      count += gave;
    }
  }
}
    
/*
 * Subroutine:    close_disk
 * Purpose: Close a disk file
 */
void
close_disk ( fd, filename )
     int fd;
     char *filename;
{
  int close();

  if( close(fd) == -1 ) {
    if( filename != NULL ) {
      (void)fprintf(stderr,"Error closing %s\n", filename);
      fflush(stderr);
    }
    perror("close error");
  }
}

#endif

                                     
                                                               
                                                               
                                                               
                                                              

                                                               
                                                               
                                                               
                                                               
             

Generated by  Doxygen 1.6.0   Back to index