#include "global.h"
#ifdef	ANSIPROTO
#include <stdarg.h>
#endif
#include "mbuf.h"
#include "proc.h"
#include "socket.h"
#include "usock.h"
#include "session.h"
#include "nr4.h"

static int rrecvchar __ARGS((struct usock *up,int s));

/* Higher-level receive routine, intended for connection-oriented sockets.
 * Can be used with datagram sockets, although the sender id is lost.
 */
int
recv(s,buf,len,flags)
int s;		/* Socket index */
char *buf;	/* User buffer */
int len;	/* Max length to receive */
int flags;	/* Unused; will eventually select oob data, etc */
{
	struct mbuf *bp;
	int cnt;

	if(len == 0)
		return 0;	/* Otherwise would be interp as "all" */

	cnt = recv_mbuf(s,&bp,flags,NULLCHAR,(int *)NULL);
	if(cnt > 0){
		cnt = min(cnt,len);
		pullup(&bp,buf,(int16)cnt);
		free_p(bp);
	}
	return cnt;
}
/* Higher level receive routine, intended for datagram sockets. Can also
 * be used for connection-oriented sockets, although from and fromlen are
 * ignored.
 */
int
recvfrom(s,buf,len,flags,from,fromlen)
int s;		/* Socket index */
char *buf;	/* User buffer */
int len;	/* Maximum length */
int flags;	/* Unused; will eventually select oob data, etc */
char *from;	/* Source address, only for datagrams */
int *fromlen;	/* Length of source address */
{
	struct mbuf *bp;
	register int cnt;

	cnt = recv_mbuf(s,&bp,flags,from,fromlen);
	if(cnt > 0){
		cnt = min(cnt,len);
		pullup(&bp,buf,(int16)cnt);
		free_p(bp);
	}
	return cnt;
}
/* High level send routine */
int
send(s,buf,len,flags)
int s;		/* Socket index */
char *buf;	/* User buffer */
int len;	/* Length of buffer */
int flags;	/* Unused; will eventually select oob data, etc */
{
	register struct mbuf *bp;
	char sock[MAXSOCKSIZE];
	int i = MAXSOCKSIZE;

	if(getpeername(s,sock,&i) == -1)
		return -1;
	bp = qdata(buf,(int16)len);
	return send_mbuf(s,bp,flags,sock,i);
}
/* High level send routine, intended for datagram sockets. Can be used on
 * connection-oriented sockets, but "to" and "tolen" are ignored.
 */
int
sendto(s,buf,len,flags,to,tolen)
int s;		/* Socket index */
char *buf;	/* User buffer */
int len;	/* Length of buffer */
int flags;	/* Unused; will eventually select oob data, etc */
char *to;	/* Destination, only for datagrams */
int tolen;	/* Length of destination */
{
	register struct mbuf *bp;

	bp = qdata(buf,(int16)len);
	return send_mbuf(s,bp,flags,to,tolen);
}
/* Receive a newline-terminated line from a socket, returning # chars read.
 * The end-of-line sequence is recognized and translated into a single '\n'.
 */
int
recvline(s,buf,len)
int s;		/* Socket index */
char *buf;	/* User buffer */
unsigned len;	/* Length of buffer */
{
	int c;
	int cnt = 0;

	while(len-- > 1){
		if((c = recvchar(s)) == EOF){
			cnt = -1;
			break;
		}
		if(buf != NULLCHAR)
			*buf++ = c;
		cnt++;
		if(uchar(c) == '\n')
			break;
	}
	if(buf != NULLCHAR)
		*buf = '\0';
	return cnt;
}
#if	defined(ANSIPROTO)
/* Do printf on a user socket */
int
usprintf(int s,char *fmt,...)
{
	va_list args;
	int len;

	va_start(args,fmt);
	len = usvprintf(s,fmt,args);
	va_end(args);
	return len;
}
/* Printf on standard output socket */
int
tprintf(char *fmt,...)
{
	va_list args;
	int len;

	va_start(args,fmt);
	len = usvprintf(Curproc->output,fmt,args);
	va_end(args);
	return len;
}
/* The guts of printf, uses variable arg version of sprintf */
int
usvprintf(int s,char *fmt, va_list args)
{
	int len,withargs;
	register int i;
	char *buf;
	register char *cp;

	if(strchr(fmt,'%') == NULLCHAR){
		/* No args, so we don't need vsprintf() */
		withargs = 0;
		buf = fmt;
		len = strlen(fmt);
	} else {
		/* Use a default value that is hopefully longer than the
		 * biggest output string we'll ever print (!)
		 */
		withargs = 1;
		buf = mallocw(SOBUF);
		vsprintf(buf,fmt,args);
		len = strlen(buf);
	}
	cp = buf;
	i = len;
	while(i-- > 0)
		usputc(s,*cp++);
	if(withargs)
		free(buf);
	return len;
}
#else
/*VARARGS*/
/* Printf to standard output socket */
int
tprintf(fmt,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12)
char *fmt;		/* Message format */
int arg1,arg2,arg3;	/* Arguments */
int arg4,arg5,arg6;
int arg7,arg8,arg9;
int arg10,arg11,arg12;
{
	return usprintf(Curproc->output,fmt,arg1,arg2,arg3,arg4,arg5,arg6,
		arg7,arg8,arg9,arg10,arg11,arg12);
}
/* Printf to socket. Doesn't use ANSI vsprintf */
int
usprintf(s,fmt,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12)
int s;			/* Socket index */
char *fmt;		/* Message format */
int arg1,arg2,arg3;	/* Arguments */
int arg4,arg5,arg6;
int arg7,arg8,arg9;
int arg10,arg11,arg12;
{
	int len,withargs;
	register int i;
	char *buf;
	register char *cp;

	if(strchr(fmt,'%') == NULLCHAR){
		/* No args, so we don't need vsprintf() */
		withargs = 0;
		buf = fmt;
		len = strlen(fmt);
	} else {
		/* Use a default value that is hopefully longer than the
		 * biggest output string we'll ever print (!)
		 */
		withargs = 1;
		buf = mallocw(SOBUF);
		sprintf(buf,fmt,arg1,arg2,arg3,arg4,arg5,arg6,arg7,
		 arg8,arg9,arg10,arg11,arg12);
		len = strlen(buf);
	}
	cp = buf;
	i = len;
	while(i-- > 0)
		usputc(s,*cp++);
	if(withargs)
		free(buf);
	return len;
}
#endif
/* Buffered putchar to a socket */
int
usputc(s,c)
int s;
char c;
{
	struct usock *up;
	register struct mbuf *bp;
	char *cp;

	if((up = itop(s)) == NULLUSOCK){
		errno = EBADF;
		return -1;
	}
	if(up->obuf == NULLBUF){
		/* Allocate a buffer of appropriate size */
		switch(up->type){
		case TYPE_NETROML4:
			up->obuf = alloc_mbuf(NR4MAXINFO);
			break;
		default:
			up->obuf = alloc_mbuf(BUFSIZ);
			break;
		}
	}
	bp = up->obuf;
	if(c == '\n' && up->eol[0] != '\0'){
		/* Translate into appropriate end-of-line sequence */
		for(cp = up->eol;*cp != '\0';cp++)
			bp->data[bp->cnt++] = *cp;
	} else {
		bp->data[bp->cnt++] = c;
	}
	if((c == up->flush && up->flush != -1) || bp->cnt >= bp->size-2)
		usflush(s);

	return (int)uchar(c);
}
/* Put a character to standard output socket */
int
tputc(c)
char c;
{
	return usputc(Curproc->output,c);
}
int
usputs(s,x)
int s;
register char *x;
{
	while(*x != '\0')
		if(usputc(s,*x++) == EOF)
			return EOF;
	return 0;
}

/* Put a string to standard output socket */
int
tputs(s)
char *s;
{
	return usputs(Curproc->output,s);
}

/* Read a raw character from a socket with stream buffering. */
static int
rrecvchar(up,s)
register struct usock *up;
int s;			/* Socket index */
{
	char c;

	/* Replenish if necessary */
	if(up->ibuf == NULLBUF && recv_mbuf(s,&up->ibuf,0,NULLCHAR,0) <= 0)
		return EOF;

	if(pullup(&up->ibuf,&c,1) == 1)
		return (int)uchar(c);
	else
		return EOF;
}
/* This function recognizes the end-of-line sequence for the stream
 * and translates it into a single '\n'.
 */
int
recvchar(s)
int s;			/* Socket index */
{
	int c;
	register struct usock *up;

	if((up = itop(s)) == NULLUSOCK)
		return EOF;

	c = rrecvchar(up,s);

	if(c != up->eol[0] || up->eol[0] == '\0')
		return (int)c;

	/* This is the first char of a eol sequence. If the eol sequence is
	 * more than one char long, eat the next character in the input stream.
	 */
	if(up->eol[1] != '\0'){
		(void)rrecvchar(up,s);
	}
	return '\n';
}
/* Flush output on a socket stream */
void
usflush(s)
int s;
{
	register struct usock *up;
	struct mbuf *bp;

	if((up = itop(s)) == NULLUSOCK)
		return;

	if(up->obuf != NULLBUF){
		bp = up->obuf;
		up->obuf = NULLBUF;
		send_mbuf(s,bp,0,NULLCHAR,0);
	}
}
/* Flush output socket */
void
tflush()
{
	usflush(Current->output);
}

/* Print prompt and read one character */
int
keywait(prompt,flush)
char *prompt;	/* Optional prompt */
int flush;	/* Flush queued input? */
{
	int c;

	if(flush && socklen(Curproc->input,1) != 0)
		recv_mbuf(Curproc->input,NULLBUFP,0,NULLCHAR,0); /* flush */
	if(prompt != NULLCHAR)
		tprintf(prompt);
	else
		tprintf("Hit enter to continue");
	tflush();
	if((c = recvchar(Curproc->input)) == EOF)
		return -1;
	tprintf("\r%75s\r"," ");
	tflush();
	return (int)c;
}

/* Set the end-of-line sequence on a socket */
int
seteol(s,seq)
int s;
char *seq;
{
	register struct usock *up;

	if((up = itop(s)) == NULLUSOCK)
		return -1;

	if(seq != NULLCHAR)
		strncpy(up->eol,seq,sizeof(up->eol));
	else
		*up->eol = '\0';
	return 0;
}
/* Specify the character to trigger automatic output buffer
 * flushing, or -1 to disable it
 */
int
setflush(s,c)
int s;
int c;
{
	register struct usock *up;
	int old;

	if((up = itop(s)) == NULLUSOCK)
		return -1;

	old = up->flush;
	up->flush = c;
	return old;
}

