RCS_ID_C="$Id: _chkufb.c,v 4.1 1994/09/29 23:09:02 jraja Exp $";
/*
 *      _chkufb.c - return struct ufb * from a file handle (SAS/C)
 *
 *      Copyright © 1994 AmiTCP/IP Group, 
 *                       Network Solutions Development Inc.
 *                       All rights reserved.
 */

#include <ios1.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <bsdsocket.h>
#include <sys/cdefs.h>
#include <amitcp/socketbasetags.h>
#include <syslog.h>
#include <constructor.h>
#include "libcheck.h"

extern unsigned long __fmask;
extern int (*__closefunc)(int);

long ASM fdCallback(REG(d0) int fd, REG(d1) int action);

/*
 * The initializator priority is just above the standard I/O, so that this
 * will be called after the standard I/O is initialized
 */
//long __stdargs
//_STI_510_install_AmiTCP_callback(void)
//CONSTRUCTOR_P(install_AmiTCP_callback,510)

/*
* Actually this doesn't work with Python 1.5.
* You must call it after the interpreter has been initialised.
*/
long _install_AmiTCP_callback(void)
{
  /* needs bsdsocket.library -- I.J. */
  if(checksocketlib())
  {
	if (SocketBaseTags(SBTM_SETVAL(SBTC_FDCALLBACK), &fdCallback, TAG_END)) {
	syslog(LOG_ERR, "Cannot install fdCallback!");

	return 1;
/****
#if __VERSION__ > 6 || __REVISION__ > 3
	return 1;
#else
	exit(20);
#endif
****/
	}
  }
  else
	{
		PyErr_Clear();             /* don't report error if socketlib not found */
	}

  /*
   * Set up __closefunc (which is used at stdio cleanup)
   */
  __closefunc = __close;

  /*
   * Set default file mask to UNIX style
   */
  __fmask = 0644; 

  return 0;
}

long ASM SAVEDS
fdCallback(REG(d0) int fd, REG(d1) int action)
{
  struct UFB *ufb;
  int fd2;

#ifdef DEBUG
  syslog(LOG_INFO, "fdCallback(fd: %d, action: %d)", fd, action);
#endif

  switch (action) {
  case FDCB_FREE:
	ufb = __chkufb(fd);
	if (ufb == NULL)
	  return EBADF;

	if (!(ufb->ufbflg & UFB_SOCK) && ufb->ufbflg != 0) {
#ifdef DEBUG
	  syslog(LOG_ERR, "fdCallback: fd (%d) is not a socket!", fd);
#endif
	  return ENOTSOCK;
	}

	ufb->ufbflg = 0;
	return 0;

  case FDCB_ALLOC:
	do {
	  ufb = __allocufb(&fd2);
	  if (ufb == NULL)
	return ENOMEM;
#ifdef DEBUG
	  if (fd2 > fd) {
	syslog(LOG_ERR, "fdCallback: fd2(%d) > fd(%d)!", fd2, fd);
	return EINVAL;
	  }
#endif
	  ufb->ufbflg = UFB_SOCK | UFB_WA | UFB_RA; /* read/write socket */
	  ufb->ufbfh = NULL; /* no file handle */
	  ufb->ufbfn = NULL; /* no name */
	} while (fd2 < fd);
	return 0;

  case FDCB_CHECK:
	ufb = __chkufb(fd);
	if (ufb != NULL && ufb->ufbflg != 0) 
	  return EBADF;
	
	return 0;

  default:
#ifdef DEBUG
	syslog(LOG_ERR, "fdCallback: invalid action.");
#endif
	return EINVAL;
  }
}


struct UFB *
__chkufb(int fd)
{
  struct UFB *ufb;

  /* a single element cache */
  static struct UFB *last_ufb = NULL;
  static int         last_fd = -1;

  _OSERR = 0;

  if ((unsigned int)fd >= __nufbs) { /* unsigned cast checks for (fd < 0) */
	errno = EBADF;
	return NULL;
  }

  /*
   * Check the cache first
   */
  if (fd == last_fd)
	return last_ufb;

  last_fd = fd; /* update cache */
  ufb = __ufbs;
  while (fd > 0 && ufb != NULL) {
	fd--;
	ufb = ufb->ufbnxt;
  }
  last_ufb = ufb; /* update cache */
  
  if (ufb == NULL) {
	last_fd = -1; /* invalidate cache */
	errno = EIO;
	return NULL;
  }
  else
	return ufb;
}
