#ifndef NOIDENT
const static char RCSid[] = "$Header: qc:c-src/common/misclib/rcs/s_socket.c,v 1.6 1993/10/03 17:48:42 alph Exp $";
#endif

/*
 *----------------------------------------------------------------------
 *
 * $Source: qc:c-src/common/misclib/rcs/s_socket.c,v $
 * $Revision: 1.6 $
 * $Date: 1993/10/03 17:48:42 $
 * $State: Exp $
 * $Author: alph $
 * $Locker:  $
 *
 *----------------------------------------------------------------------
 *
 * alph 
 * 
 * This code is (C) Copyright 1993 by Carsten Heyl. All rights reserved.
 * This code is NOT in the Public Domain.
 * It may be copied, modified or distributed as long as this notice is
 * present.
 *
 *----------------------------------------------------------------------
 * $Log: s_socket.c,v $
 * Revision 1.6  1993/10/03  17:48:42  alph
 * *** empty log message ***
 *
 * Revision 1.5  1993/10/03  16:36:54  alph
 * corrected sendto parameter
 *
 * Revision 1.4  1993/10/02  19:34:00  alph
 * added s_sendto
 *
 * Revision 1.3  1993/10/02  19:10:09  alph
 * added header
 *
 *----------------------------------------------------------------------
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

#include <errno.h>

#include <sys/types.h>
#include <netinet/in.h>

#ifdef __SASC
#if 0
#include <clib/socket_protos.h>
#endif
#include <pragmas/socket_pragmas.h>
#include <proto/socket.h>
#endif

#define PRIVATE_SOCKET_H
#include "s_socket.h"

#define MAXFH S_MAXFH

/* FIXME: set atexit-handler for sockets too */

/* extern int SocketBase; */

static FILEEXT *files;

static void *zmalloc(size_t l)
{
    unsigned char *p = malloc(l);

    if(p)
	memset(p, 0x00, l);

    return p;
}

#define BUFSIZE 512

/*
 does a FILE exists, which references fd ?
 */

static int fd_has_refs(FILE *as, int fd)
{
    while(as)
    {
	if(fileno(as) == fd)
	    return 1;
	as = as->_next;
    }
    return 0;
}

static void s_fcloseall(void)
{
    while(files)
	s_fclose(files);
}

FILE *s_fdopen(int fd, const char *mode)
{
    FILEEXT *af;
    unsigned char *buf;

    if(fd < MAXFH)
	return fdopen(fd,mode);
    else
    {
	af = zmalloc(sizeof(FILEEXT));
	if(!af)
	    return NULL;
	buf = zmalloc(BUFSIZE);
	if(!buf)
	{
	    free(af);
	    return NULL;
	}
	if(!strcmp(mode,"r"))
	{
	    af->_flag = _IOREAD | _IOMYBUF;
	}
	else if(!strcmp(mode,"w"))
	{
	    af->_flag = _IOWRT | _IOMYBUF;
	}
	else
	{
	    fprintf(stderr,"s_fdopen: mode \"%s\" not supported\n", mode);
 
	    free(af);
	    free(buf);
	    return 0;
	}
    
	if(!files)
	    atexit(s_fcloseall);

	af->_next = files;
	af->_ptr = buf;
	af->_base = buf;
	af->_size = BUFSIZE;
	af->_flag |= _IOSOCK;
	af->_file = fd;
	files = af;

	return af;
    }
}

void s_fclose(FILE *af)
{
    FILE *pf = NULL, *sf = files;

    if(af->_file == 1)
    {
	fprintf(stderr,"s_fclose: someone tries to close stdout ;-(\n");
    }

    if(!(af->_flag & _IOSOCK))
    {
	fclose(af);
    }
    else
    {
	if(af->_flag & _IOWRT)
	    s_fflush(af);

	while(sf && sf != af)
	{
	    pf = sf;
	    sf = sf->_next;
	}
	if(sf)
	{
	    if(pf)
		pf->_next = sf->_next;
	    else
		files = sf->_next;
#if 1
	    if(! fd_has_refs(files, fileno(sf)))
		s_close(fileno(sf)); /* FIXME: is this correct ? */
#endif
	    free(sf->_base);
	    free(sf);
	}
	else
	{
	    fprintf(stderr,"s_flose: illegal argument !!\n");
	}
    }
}

size_t s_fread(void *b, size_t bsize, size_t n, FILE *fp)
{
    if(!(fp->_flag & _IOSOCK))
    {
	return fread(b, bsize, n, fp);
    }
    else
    {
	fprintf(stderr,"s_fread: not supported for sockets\n");
	exit(20);
    }
}
size_t s_fwrite(void *b, size_t bsize, size_t n, FILE *fp)
{
    if(!(fp->_flag & _IOSOCK))
    {
	return fwrite(b, bsize, n, fp);
    }
    else
    {
	fprintf(stderr,"s_ffwrite: not supported for sockets\n");
	exit(20);
    }
}

static __inline int _s_fgetc(FILE *af)
{
    int n;

    if(af->_flag & _IOEOF)
    {
	return EOF;
    }
    if(--(af->_rcnt) >= 0)
    {
	return *(af->_ptr)++;
    }
    else
    {
	af->_ptr = af->_base;
	n = s_recv(fileno(af), af->_base, af->_size, 0);
	if(n > 0)
	{
	    af->_rcnt = n-1;

	    return *(af->_ptr)++;
	}
	else if(n == -1)
	{
	    af->_flag |= _IOERR;
		
	    return EOF;
	}
	else
	{
	    af->_flag |= _IOEOF;

	    return EOF;
	}
    }
}

char *s_fgets(char *b, int bsize, FILE *af)
{
    char *p;
    int c;

    if(!(af->_flag & _IOSOCK))
    {
	return fgets(b, bsize, af);
    }
    else
    {
	p = b;
	if(!(af->_flag & _IOREAD))
	{
	    fprintf(stderr,"s_fgets: illegal operation (read)\n");
	    errno = EBADF;
	    
	    return NULL;
	}
	while(bsize-- && ((c = _s_fgetc(af)) != EOF) && (c != '\n'))
	    *p++ = c; 
	if(p==b)
	    return NULL;
	if((c == EOF) && errno)
	    return NULL;
	*p = '\0';
	return b;
    }
}

int s_fputs(const char *b, FILE *fp)
{
    if(!(fp->_flag & _IOSOCK))
    {
	return fputs(b, fp);
    }
    else
    {
	fprintf(stderr,"s_fputs: not supported for sockets\n");
	exit(20);
    }
}

int s_fseek(FILE *fp, long rpos, int mode)
{
    if(!(fp->_flag & _IOSOCK))
    {
	return fseek(fp, rpos, mode);
    }
    else
    {
	fprintf(stderr,"s_fseek: not supported for sockets\n");
	exit(20);
    }
}


int s_fgetc(FILE *af)
{
    if(!(af->_flag & _IOSOCK))
    {
	return fgetc(af);
    }
    else
    {
	if(!(af->_flag & _IOREAD))
	{
	    fprintf(stderr,"s_fgetc: illegal operation (read)\n");
	    errno = EBADF;

	    return EOF;
	}
	return _s_fgetc(af);
    }
}

int s_fflush(FILEEXT *af)
{
    int nbytes, RetVal;

    if(!(af->_flag & _IOSOCK))
    {
	return fflush(af);
    }
    else
    {
	if(!(af->_flag & _IOWRT))
	{
	    fprintf(stderr,"s_fflush: illegal operation (not write)\n");
	    errno = EBADF;

	    return EOF;
	}
	if(af->_wcnt)
	{
	    nbytes = s_send(fileno(af), af->_base, af->_wcnt, 0);
	    if(nbytes != af->_wcnt)
	    {
		af->_flag |= _IOERR;
		RetVal = EOF;
	    }
	    else
		RetVal = 0;

	    af->_wcnt = 0;
	    af->_ptr = af->_base;
	}
	else
	    RetVal = 0;

	return RetVal;
    }
}

int s_vfprintf(FILE *af, const char *fmt, va_list args)
{
    int n;

/*     va_start(args, fmt);*/
    
    /* if buffer gets exceeded, we loose */

    if(!(af->_flag & _IOSOCK))
    {
	n = vfprintf(af, (char *) fmt, args);
    }
    else
    {
	s_fflush(af);
	n = af->_wcnt = vsprintf(af->_ptr, fmt, args);
	af->_ptr += n;
	if(n > af->_size)
	{
	    fprintf(stderr,"vfprintf: file buf exceeded\n");
	    exit(20);
	}
    }
    va_end(args);

    return n;
}

int s_fprintf(FILEEXT *af, const char *fmt, ...)
{
    int n;
    va_list args;

    va_start(args, fmt);
    
    /* if buffer gets exceeded, we loose */

    if(!(af->_flag & _IOSOCK))
    {
	n = vfprintf(af, (char *) fmt, args);
    }
    else
    {
	s_fflush(af);
	n = af->_wcnt = vsprintf(af->_ptr, fmt, (va_list) args);
	af->_ptr += n;
	if(n > af->_size)
	{
	    fprintf(stderr,"fprintf: file buf exceeded\n");
	    exit(20);
	}
    }
    va_end(args);

    return n;
}

int s_fputc(int c, FILEEXT *af)
{
    int success;

    if(!(af->_flag & _IOSOCK))
    {
	return fputc(c, af);
    }
    else
    {
	if(!(af->_flag & _IOWRT))
	{
	    fprintf(stderr,"s_fflush: illegal operation (write)\n");
	    errno = EBADF;

	    return -1;
	}
	if(af->_wcnt > af->_size)
	{
	    success = s_fflush(af);
	}
	(af->_wcnt)++;
	*(af->_ptr)++ = c;

	return success ? success : c;
    }
}

int s_ferror(FILE *af)
{
    if(!(af->_flag & _IOSOCK))
    {
	return ferror(af);
    }
    else
    {
	return 0;		/* FIXME */
    }
}

int s_read(int fh, void *buf, unsigned int len)
{
    if(fh < MAXFH)
	return read(fh, buf, len);
    else
	return recv(fh-MAXFH, buf, len, 0);
}

int s_write(int fh, void *buf, unsigned int len)
{
    if(fh < MAXFH)
	return write(fh, buf, len);
    else
	return send(fh-MAXFH, buf, len, 0);
}


int s_close(int sd)
{
    int RetVal;

    if(sd == 1)
    {
	fprintf(stderr,"s_close: someone tries to close stdout ;-(\n");
    }
    if(sd < MAXFH)
	return close(sd);
    else
    {
#if 0
	fprintf(stderr,"s_close(%d)\n", sd -S_MAXFH);
#endif
	RetVal = CloseSocket(sd - MAXFH);
#if 0
	if(RetVal)
	{
	    perror("s_close failed\n");
	}
#endif
    }
    return RetVal;
}

int s_isatty(int sd)
{
    if(sd < MAXFH)
	return isatty(sd);
    else
    {
	return 0;
    }
}

int s_iomode(int sd, int mode)
{
    if(sd < MAXFH)
	return isatty(sd);
    else
    {
	return 1; /* not a level 1 file FIXME: call for sockets available? */
    }
}

long s_lseek(int sd, long a, int mode)
{
    if(sd < MAXFH)
	return lseek(sd, a, mode);
    else
    {
	fprintf(stderr,"s_lseek: not supported for sockets\n");
	exit(20);
    }
}

long s_accept(long s, struct sockaddr *addr, int *addrlen)
{
    int ns;

    ns = accept(s-S_MAXFH, addr, (long *) addrlen);
    if(ns>=0)
	ns += S_MAXFH;
    return ns;
}

long s_bind(long s, struct sockaddr *name, long namelen)
{
    return bind(s-S_MAXFH, name, namelen);
}

long s_CloseSocket(long sd)
{
    return CloseSocket(sd-S_MAXFH);
}

long s_connect(long s, struct sockaddr *name, long namelen)
{
    return connect(s-S_MAXFH, name, namelen);
}

long s_getpeername(long s, struct sockaddr *name, int *namelen)
{
    return getpeername(s-S_MAXFH, name, (long *) namelen);
}

long s_getsockname(long s, struct sockaddr *name, int *namelen)
{
    return getsockname(s-S_MAXFH, name, (long *) namelen);
}

long s_setsockopt(long s, long level, long optname, caddr_t optval, long optlen)
{
    return setsockopt(s-S_MAXFH, level, optname, optval, optlen);
} 

long s_listen(long s, long backlog)
{
    return listen(s-S_MAXFH, backlog);
}

long s_recv(long s, char *buf, long len, long flags)
{
    int n;

    n = recv(s-S_MAXFH, buf, len, flags);
#if 0
    if(n != -1)
    {
	fprintf(stderr,"s_recv = ");
	write(2, buf, n);
	fprintf(stderr,"\n");
    }
    else
    {
	fprintf(stderr,"recv returned -1\n");
    }
#endif
    return n;
}

long s_recvfrom(long s, char *buf, long len, long flags, 
		struct sockaddr *from, long *len2)
{
    int n;

    n = recvfrom(s-S_MAXFH, buf, len, flags, from, len2);
    return n;
}

long s_select(long nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
	      struct timeval *timeout)
{
    return select(nfds, readfds, writefds, exceptfds, timeout);
}

int s_send(int s, char *msg, int len, int flags)
{
    return send(s-S_MAXFH, msg, len, flags);
}

int s_sendto(int s, char *msg, int len, int flags,
	     struct sockaddr *to, long len2)
{
    return sendto(s-S_MAXFH, msg, len, flags, to, len2);
}

long s_shutdown(long s, long how)
{
    return shutdown(s-S_MAXFH, how);
}

long s_socket(long domain, long type, long protocol)
{
    int s;
    
    s = socket(domain, type, protocol);
    if(s>=0)
	s += S_MAXFH;

    return s;
}
