static const char *RCS="$Id: autoinit.c,v 4.3 1994/09/29 23:09:02 jraja Exp $";
/*
 *	autoinit.c - SAS/C autoinitialization functions for bsdsocket.library
 *
 *	Copyright © 1994 AmiTCP/IP Group,
 *			 Network Solutions Development Inc.
 *			 All rights reserved.
 */

#include <exec/types.h>
#include <exec/libraries.h>

#include <intuition/intuition.h>

#include <proto/socket.h>
#include <proto/exec.h>

#include <clib/intuition_protos.h>

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>

#include <amitcp/socketbasetags.h>
#define _INTERNAL_FILE /* to get internal file definition */
#define KERNEL	       /* to get FREAD, FWRITE */
#include <bsdsocket.h>
#include <sys/fcntl.h>
#include <sys/cdefs.h>
#include <sys/file.h>
#include <errno.h>
#include <amitcp/socketbasetags.h>
#include <syslog.h>
#include <clib/netlib_protos.h>
#include <user.h>
#include <exec/execbase.h>

long fdCallback(int fd, int action);
extern int errno;
/* struct file * __get_file(int fd); */
int __tcpioctl(struct file *f, unsigned int request, int inout, int arglen,char *argp);
int __tcpread(struct file *ufb, char *buffer, unsigned int length);
int __tcpwrite(struct file *ufb, char *buffer, unsigned int length);
int __tcpclose(struct file *ufb);

#define usr (*((struct user *)((struct Task *)(FindTask(0))->tc_TrapData)))
#define u_save (*((struct user *)((struct Task *)(FindTask(0))->tc_TrapData)))

struct Library *SocketBase = NULL;
static const char SOCKETNAME[] = "bsdsocket.library";

#define SOCKETVERSION 3 	/* minimum bsdsocket version to use */

extern STRPTR _ProgramName;	/* SAS startup module defines this :-) */

/*
 * Global h_errno
 */
int h_errno = 0;

/****** net.lib/autoinit *********************************************

    NAME
	autoinit - SAS C Autoinitialization Functions

    SYNOPSIS
	LONG _STI_200_openSockets(void)

	void _STD_200_closeSockets(void)

    FUNCTION
	These functions open and close the bsdsocket.library at the startup
	and exit of the program, respectively.	For a program to use these
	functions, it must be linked with netlib:net.lib (or some variant).
	These functions are linked in only if the program references the
	global symbol "SocketBase".

	If the library can be opened, the _STI_200_openSockets() calls
	bsdsocket.library function SocketBaseTags() to tell the library the
	address and the size of the errno variable of the calling program,
	the program name (to be used in syslog() messages) and the address
	of the h_error variable (in which the name resolver errors are
	returned).

    NOTES
	_STI_200_openSockets() also checks that the system version is at
	least 37. It also puts up a requester if the bsdsocket.library is
	not found or is of wrong version.

	The autoinitialization and autotermination functions are features
	specific to the SAS C6.  However, these functions can be used with
	other (ANSI) C compilers, too. Example follows:

	\* at start of main() *\

	atexit(_STD_200_closeSockets);
	if (_STI_200_openSockets() != 0)
	   exit(20);

    BUGS
	The same autoinitialization won't work for both SAS C 6.3 and SAS C
	6.50 or latter.  Only way to terminate an initialization function is
	by exit() call with SAS C 6.3 binary.  If an autoinitialization
	function is terminated by exit() call with SAS C 6.50 binary, the
	autotermination functions won't be called.  Due this braindamage
	these compilers require separate net.lib libraries.

    SEE ALSO
	bsdsocket.library/SocketBaseTags(),
	SAS/C 6 User's Guide p. 145 for details of autoinitialization and
	autotermination functions.

*****************************************************************************
*/

/*
 * The number 200 in the function names is the priority assigned to
 * shared library autoinitialization functions by SAS/C 6.50.
 */
LONG
_STI_200_openSockets(void)
{
  struct Library *IntuitionBase;
  STRPTR errorStr;

  /*
   * Check OS version
   */
  if ((*(struct Library **)4)->lib_Version < 37)
    exit(20);

  /*
   * Open bsdsocket.library
   */
  if ((SocketBase = OpenLibrary((STRPTR)SOCKETNAME, SOCKETVERSION)) != NULL) {
    /*
     * Successful. Now tell bsdsocket.library:
     * - the address of our errno
     * - the address of our h_errno
     * - our program name
     */
    if (SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), &errno,
		       SBTM_SETVAL(SBTC_HERRNOLONGPTR), &h_errno,
		       SBTM_SETVAL(SBTC_LOGTAGPTR), _ProgramName,
		       TAG_END))
      exit(30);

    Dup2Socket(fileno(stdin),-1);
    Dup2Socket(fileno(stdout),-1);
    Dup2Socket(fileno(stderr),-1);

    return 0;
  }
  else
    errorStr = "AmiTCP/IP version 4 or later must be started first.";

  IntuitionBase = OpenLibrary("intuition.library", 36);

  if (IntuitionBase != NULL) {
    struct EasyStruct libraryES;

    libraryES.es_StructSize = sizeof(libraryES);
    libraryES.es_Flags = 0;
    libraryES.es_Title = _ProgramName;
    libraryES.es_TextFormat = errorStr;
    libraryES.es_GadgetFormat = "Exit %s";

    EasyRequestArgs(NULL, &libraryES, NULL, (APTR)&_ProgramName);

    CloseLibrary(IntuitionBase);
  }
  exit(20);
}

void _STD_200_closeSockets(void)
{
  if (SocketBase) {
    CloseLibrary(SocketBase);
    SocketBase = NULL;
  }
}

/*
 * The initializator priority is just above the standard I/O, so that this
 * will be called after the standard I/O is initialized
 */
long
_STI_510_install_AmiTCP_callback(void)
{
  if (SocketBaseTags(SBTM_SETVAL(SBTC_FDCALLBACK), fdCallback, TAG_END)) {
    syslog(LOG_ERR, "Cannot install fdCallback!");
    exit(20);
  }

  return 0;
}

long fdCallback(int fd, int action) {
  register int d0 __asm("d0");
  register int d1 __asm("d1");
  struct file *ufb, **f;
  int fd2,i;

  fd =d0;
  action = d1;

  switch (action) {

    case FDCB_FREE:
	f = &usr.u_ofile[fd];
	ufb = *f;

	if (ufb == NULL) return EBADF;

	if (ufb->f_type != DTYPE_SOCKET) {
	    syslog(LOG_ERR, "fdCallback: fd (%d) is not a socket!", fd);
	    return ENOTSOCK;
	}
	*f = 0;
	return 0;
    break;


    case FDCB_ALLOC:
	fd2=fd;
	falloc(&ufb,&fd2);
	if (fd2 != fd) syslog (LOG_ERR,"fd2 != fd   - wanted %d, got %d",fd,fd2);

	if (ufb == NULL)
	    return ENOMEM;

	ufb->f_type  = DTYPE_SOCKET;
	ufb->f_write = __tcpwrite;
	ufb->f_read = __tcpread;
	ufb->f_ioctl = __tcpioctl;
	ufb->f_select = 0;
	ufb->f_close = __tcpclose;
	ufb->f_flags = FREAD|FWRITE; /* read/write socket */
	ufb->f_stb.st_mode = S_IFSOCK;
	ufb->f_fh = (struct FileHandle *)fd;
	return 0;
    break;

    case FDCB_CHECK:
	if (usr.u_ofile[fd] == NULL) return 0;
	return EBADF;
    break;

    default:
	syslog(LOG_ERR, "fdCallback: invalid action.");
	return EINVAL;
  }/* switch */
}

#if 0
struct file * __get_file(int fd)
{
  int i;
  struct file *f;

  for (i=0; i < NOFILE; i++) {
    f=usr.u_ofile[i];
    if (f->f_type == DTYPE_SOCKET && (int)f->f_fh == fd) {
	return f;
    }
  }

  if ((int)f->f_fh != fd); syslog(LOG_ERR,"filehandle does not indicate a socket");

  errno = EBADF;
  return NULL;
}
#endif

asm ("  .text;  .stabs \"___CTOR_LIST__\",22,0,0, __STI_510_install_AmiTCP_callback");
asm ("  .text;  .stabs \"___CTOR_LIST__\",22,0,0, __STI_200_openSockets");
asm ("  .text;  .stabs \"___DTOR_LIST__\",22,0,0, __STD_200_closeSockets");

