/* Higher level user subroutines built on top of the socket primitives * Copyright 1991 Phil Karn, KA9Q */ #include "global.h" #ifdef ANSIPROTO #include #endif #include "mbuf.h" #include "proc.h" #include "socket.h" #include "usock.h" #include "session.h" #include "nr4.h" /* 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; char *buf; if(strchr(fmt,'%') == NULLCHAR){ /* Common case optimization: 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); } if(usputs(s,buf) == EOF) len = -1; 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; char *buf; 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); } if(usputs(s,buf) == EOF) len = -1; 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; int newline,len; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return -1; } if(c == '\n' && (up->flag & SOCK_ASCII)){ newline = 1; len = strlen(up->eol); } else { newline = 0; len = 1; } /* Make sure there's room in the current buffer, if any */ if((bp = up->obuf) != NULLBUF){ if((bp->cnt >= bp->size - len) && usflush(s) == -1) return EOF; } if(up->obuf == NULLBUF){ /* Allocate a buffer of appropriate size */ switch(up->type){ case TYPE_NETROML4: up->obuf = ambufw(NR4MAXINFO); break; default: up->obuf = ambufw(BUFSIZ); break; } } /* Note: the buffer must be larger than the end-of-line sequence! */ bp = up->obuf; cp = &bp->data[bp->cnt]; if(newline){ /* Translate into appropriate end-of-line sequence */ strncpy(cp,up->eol,len); } else { *cp = c; } bp->cnt += len; /* Flush if necessary */ if(c == up->flush && up->flush != -1 && usflush(s) == -1) return -1; return (int)uchar(c); } /* Put a character to standard output socket */ int tputc(c) char c; { return usputc(Curproc->output,c); } #ifndef oldusputs /* Buffered puts to a socket */ int usputs(s,buf) int s; char *buf; { register struct usock *up; register struct mbuf *bp; char *cp,*wp; int16 len,clen; int doflush; int eol_len; int16 flushpt; int ascii; if((up = itop(s)) == NULLUSOCK){ errno = EBADF; return EOF; } ascii = up->flag & SOCK_ASCII; if(ascii) eol_len = strlen(up->eol); doflush = (up->flush != -1) && (strchr(buf,up->flush) != NULLCHAR); len = strlen(buf); while(len != 0){ if(up->obuf == NULLBUF){ /* Allocate a buffer of appropriate size */ switch(up->type){ case TYPE_NETROML4: clen = NR4MAXINFO; break; default: clen = BUFSIZ; break; } up->obuf = ambufw(clen); } /* Note: the buffer must be larger than the end-of-line sequence! */ bp = up->obuf; wp = &bp->data[bp->cnt]; if(ascii && (cp = strchr(buf,'\n')) != NULLCHAR){ /* Copy up to, but not including, newline */ clen = cp - buf; } else { /* Copy whole thing */ clen = len; } /* ...but no more than the room available */ clen = min(clen,bp->size - bp->cnt); if(clen != 0){ memcpy(wp,buf,clen); wp += clen; bp->cnt += clen; buf += clen; len -= clen; } /* Set flush threshold to allow for eol, if enabled */ if(ascii) flushpt = bp->size - eol_len; else flushpt = bp->size; if(ascii && *buf == '\n' && bp->cnt < flushpt){ /* Add appropriate end-of-line sequence */ strncpy(wp,up->eol,eol_len); wp += eol_len; bp->cnt += eol_len; buf++; /* Skip newline in buffer */ len--; } if(bp->cnt >= flushpt){ /* Buffer full, flush and get another */ if(usflush(s) == -1) return EOF; } } if(doflush && usflush(s) == -1) return EOF; return 0; } #else int usputs(s,x) int s; register char *x; { while(*x != '\0') if(usputc(s,*x++) == EOF) return EOF; return 0; } #endif /* 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. */ int rrecvchar(s) int s; /* Socket index */ { register struct usock *up; if((up = itop(s)) == NULLUSOCK){ return EOF; } /* Replenish if necessary */ if(up->ibuf == NULLBUF && recv_mbuf(s,&up->ibuf,0,NULLCHAR,0) <= 0) return EOF; return PULLCHAR(&up->ibuf); /* Returns -1 if 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(s); if(c != up->eol[0] || !(up->flag & SOCK_ASCII)) return 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(s); } return '\n'; } /* Flush output on a socket stream */ int usflush(s) int s; { register struct usock *up; struct mbuf *bp; if((up = itop(s)) == NULLUSOCK) return -1; if(up->obuf != NULLBUF){ bp = up->obuf; up->obuf = NULLBUF; return send_mbuf(s,bp,0,NULLCHAR,0); } return 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; int i; if(flush && socklen(Curproc->input,1) != 0) recv_mbuf(Curproc->input,NULLBUFP,0,NULLCHAR,0); /* flush */ if(prompt == NULLCHAR) prompt = "Hit enter to continue"; tprintf(prompt); tflush(); c = recvchar(Curproc->input); /* Get rid of the prompt */ for(i=strlen(prompt);i != 0;i--) tputc('\b'); for(i=strlen(prompt);i != 0;i--) tputc(' '); for(i=strlen(prompt);i != 0;i--) tputc('\b'); 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; } /* Enable/disable eol translation, return previous state */ int sockmode(s,mode) int s,mode; { struct usock *up; int prev; if((up = itop(s)) == NULLUSOCK) return -1; usflush(s); prev = up->flag; switch(mode){ case SOCK_BINARY: case SOCK_ASCII: up->flag = mode; break; default: break; } return prev; } /* Specify the character to trigger automatic output buffer * flushing, or -1 to disable it. Return the previous setting. */ 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; }