/*
############################################################################
#
#	File:     fpoll.c
#
#	Subject:  Function to poll file for input
#
#	Author:   Gregg M. Townsend
#
#	Date:     October 27, 2009
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  Contributor:  Cheyenne Wills
#
############################################################################
#
#  fpoll(f, msec) waits until data is available for input from file f,
#  and then returns.  It also returns when end-of-file is reached.
#  If msec is specified, and no data is available after waiting that
#  many milliseconds, then fpoll fails.  If msec is omitted, fpoll
#  waits indefinitely.
#
############################################################################
#
#  Requires:  UNIX, dynamic loading
#
############################################################################
*/

#include <stdio.h>
#include <string.h>	/* for memset call from FD_ZERO (solaris gcc) */
#include <sys/types.h>
#include <sys/time.h>

#if defined(_MUSL)
# include <stdio_ext.h>
#endif

#include "icall.h"

int fpoll(int argc, descriptor *argv)	/*: await data from file */
   {
   FILE *f;
   int msec, r;
   fd_set fds;
   struct timeval tv, *tvp;

   /* check arguments */
   if (argc < 1)
      Error(105);
   if ((IconType(argv[1]) != 'f') || (FileStat(argv[1]) & Fs_Window))
      ArgError(1, 105);
   if (!(FileStat(argv[1]) & Fs_Read))
      ArgError(1, 212);
   f = FileVal(argv[1]);

   if (argc < 2)
      msec = -1;
   else {
      ArgInteger(2);
      msec = IntegerVal(argv[2]);
      }

   /* check for data already in buffer */
   /* there's no legal way to do this in C; we cheat */
#if defined(__GLIBC__) || defined(__HAIKU__)		/* new GCC library */
   if (f->_IO_read_ptr < f->_IO_read_end)
      RetArg(1);
#elif defined(_FSTDIO)					/* new BSD library */
   if (f->_r > 0)
      RetArg(1);
#elif defined(_MUSL)					/* MUSL library */
   if (__freadahead(f))
      RetArg(1);
#else							/* old AT&T library */
   if (f->_cnt > 0)
      RetArg(1);
#endif

   /* set up select(2) structure */
   FD_ZERO(&fds);			/* clear file bits */
   FD_SET(fileno(f), &fds);		/* set bit of interest */

   /* set up timeout and pointer */
   if (msec < 0)
      tvp = NULL;
   else {
      tv.tv_sec = msec / 1000;
      tv.tv_usec = (msec % 1000) * 1000;
      tvp = &tv;
      }

   /* poll the file using select(2) */
   r = select(fileno(f) + 1, &fds, (fd_set*)NULL, (fd_set*)NULL, tvp);

   if (r > 0)
      RetArg(1);			/* success */
   else if (r == 0)
      Fail;				/* timeout */
   else
      ArgError(1, 214);			/* I/O error */

}
