/* read-fd.c, liboop, copyright 2000 Ian jackson
   
   This is free software; you can redistribute it and/or modify it under the
   terms of the GNU Lesser General Public License, version 2.1 or later.
   See the file COPYING for details. */

#include "oop.h"
#include "oop-read.h"

#include <assert.h>
#include <limits.h>
#include <errno.h>

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

typedef struct {
  oop_readable ra;
  oop_source *oop;
  int fd;
  oop_readable_call *call;
  void *opaque;
} rafd_intern;

static void *process(oop_source *oop, int fd, oop_event event, void *rafd_void) {
  rafd_intern *rafd= rafd_void;

  assert(event == OOP_READ);
  assert(fd == rafd->fd);
  assert(oop == rafd->oop);

  return
    rafd->call(oop,&rafd->ra,rafd->opaque);
}

static void on_cancel(struct oop_readable *ra) {
  rafd_intern *rafd= (void*)ra;

  rafd->oop->cancel_fd(rafd->oop,rafd->fd,OOP_READ);
}
  
static int on_read(oop_readable *ra, oop_readable_call *call, void *opaque) {
  rafd_intern *rafd= (void*)ra;

  rafd->call= call;
  rafd->opaque= opaque;

  return
    rafd->oop->on_fd(rafd->oop,rafd->fd,OOP_READ,process,rafd), 0; /* fixme */
}

static ssize_t try_read(oop_readable *ra, void *buffer, size_t length) {
  rafd_intern *rafd= (void*)ra;
  ssize_t nread;

  for (;;) {
    nread= read(rafd->fd,buffer,length);
    if (nread != -1) break;
    if (errno != EINTR) return nread;
  }

  assert(nread >= 0);
  return nread;
}

static void delete_kill(struct oop_readable *ra) {
  oop_free(ra);
}

static int delete_tidy(struct oop_readable *ra) {
  rafd_intern *rafd= (void*)ra;
  int err;

  err= oop_fd_nonblock(rafd->fd,0);
  delete_kill(ra);
  return err;
}

static const oop_readable functions= {
  on_read, on_cancel, try_read, delete_tidy, delete_kill
};

oop_readable *oop_readable_fd(oop_source *oop, int fd) {
  rafd_intern *rafd;

  rafd= oop_malloc(sizeof(*rafd));  if (!rafd) return 0;

  rafd->ra= functions;
  rafd->oop= oop;
  rafd->fd= fd;

  if (oop_fd_nonblock(fd,1)) { oop_free(rafd); return 0; }
  return (oop_readable*)rafd;
}

int oop_fd_nonblock(int fd, int nonblock) {
  int flags;
  
  flags= fcntl(fd, F_GETFL);  if (flags == -1) return errno;
  if (nonblock) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK;
  return fcntl(fd, F_SETFL, flags) ? errno : 0;
}


syntax highlighted by Code2HTML, v. 0.9.1