/* Copyright (C) 1992, the Florida State University
   Distributed by the Florida State University under the terms of the
   GNU Library General Public License.

This file is part of Pthreads.

Pthreads is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation (version 2).

Pthreads is distributed "AS IS" in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with Pthreads; see the file COPYING.  If not, write
to the Free Software Foundation, 675 Mass Ave, Cambridge,
MA 02139, USA.

Report problems and direct all questions to:

  pthreads-bugs@ada.cs.fsu.edu

  @(#)io.c	1.16 3/5/93
  
*/

#include "pthread_internals.h"
#include <sys/types.h>
#include <sys/uio.h>
#include <fcntl.h>

#ifdef IO
/*------------------------------------------------------------*/
/*
 * read - Same as POSIX.1 read except that it blocks only the current thread
 * rather than entire process.
 */
int read (fd, buf, nbytes)
     int fd, nbytes;
     char *buf;
{
  int offset, bytes_read = 0, mode;
  struct iovec iov[1];
  pthread_t p;

  mode = fcntl (fd, F_GETFL, 0);
  fcntl (fd, F_SETFL, mode | FNDELAY);
  iov[0].iov_base = buf;
  iov[0].iov_len = nbytes;

  /*
   * Perform a Non Blocking read. If the read suceeds or the mode is FNDELAY
   * return immediately.
   */
  if (((bytes_read = readv (fd, iov, 1)) == nbytes) || (mode == FNDELAY)) {
    fcntl (fd, F_SETFL, mode);
    return (bytes_read);
  }

  fcntl (fd, F_SETFL, mode);
  if (fd != 0) {
    offset = lseek (fd, 0, 1);
    if (offset == lseek (fd, 0, 2))
      return (bytes_read);
    lseek (fd, offset, 0);
  }

  /*
   * Else issue an asynchronous request for the pending number of bytes.
   */
  offset = tell (fd);
  if (bytes_read != -1)
    nbytes -= bytes_read;
  lseek (fd, nbytes, 1);
  p = mac_pthread_self();
  SET_KERNEL_FLAG;
  p->resultp.aio_return = AIO_INPROGRESS;
  if (aioread (fd, buf, nbytes, offset, 0, &p->resultp) < 0) {
    CLEAR_KERNEL_FLAG;
    return (bytes_read);
  }
  p->sigwaitset |= IO_MASK;
  p->state |= T_BLOCKED | T_INTR_POINT;
  if (p->pending & sigmask(SIGCANCEL) && !(p->mask & sigmask(SIGCANCEL)))
    SIG_CLEAR_KERNEL_FLAG(TRUE);
  else {
    q_deq_head(&ready, PRIMARY_QUEUE);
    SIM_SYSCALL(TRUE);
    CLEAR_KERNEL_FLAG;
  }
  if (p->resultp.aio_return == -1)
    set_errno (p->resultp.aio_errno);
  return (p->resultp.aio_return + ((bytes_read == -1) ? 0 : bytes_read));
}

/*------------------------------------------------------------*/
/*
 * write - Same as POSIX.1 write except that it blocks only the current
 * thread rather than entire process.
 */
int write (fd, buf, nbytes)
     int fd, nbytes;
     char *buf;
{

  int offset, bytes_read = 0, mode;
  struct iovec iov[1];
  pthread_t p;

  mode = fcntl (fd, F_GETFL, 0);
  fcntl (fd, F_SETFL, mode | FNDELAY);
  iov[0].iov_base = buf;
  iov[0].iov_len = nbytes;

  /*
   * Perform a Non Blocking write. If the write suceeds or the mode is
   * FNDELAY return immediately.
   */
  if (((bytes_read = writev (fd, iov, 1)) == nbytes) || (mode & FNDELAY)) {
    fcntl (fd, F_SETFL, mode);
    return (bytes_read);
  }

  /*
   * Else issue an asynchronous request for the pending number of bytes.
   */
  fcntl (fd, F_SETFL, mode);
  offset = tell (fd);
  if (bytes_read != -1)
    nbytes -= bytes_read;
  lseek (fd, nbytes, 1);
  p = mac_pthread_self();
  SET_KERNEL_FLAG;
  p->resultp.aio_return = AIO_INPROGRESS;
  if (aiowrite(fd, buf, nbytes, offset, 0, &p->resultp) < 0) {
    CLEAR_KERNEL_FLAG;
    return (bytes_read);
  }
  p->sigwaitset |= IO_MASK;
  p->state |= T_BLOCKED | T_INTR_POINT;
  if (p->pending & sigmask(SIGCANCEL) && !(p->mask & sigmask(SIGCANCEL)))
    SIG_CLEAR_KERNEL_FLAG(TRUE);
  else {
    q_deq_head(&ready, PRIMARY_QUEUE);
    SIM_SYSCALL(TRUE);
    CLEAR_KERNEL_FLAG;
  }
  if (p->resultp.aio_return == -1)
    set_errno (p->resultp.aio_errno);
  return (p->resultp.aio_return + ((bytes_read == -1) ? 0 : bytes_read));
}

/*------------------------------------------------------------*/
#endif
