Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - kcc-6/lib/usys/read.c
There are 10 other files named read.c in the archive. Click here to see a list.
/*
**	READ - URT low-level I/O read
**
**	(c) Copyright Ken Harrenstien 1989
**		for all changes after v.101, 11-Aug-1988
**	Copyright (C) 1986 by Ian Macky, SRI International
**	Edits for ITS:  Copyright (C) 1988 Alan Bawden
*/

#include <c-env.h>
#if SYS_T20+SYS_10X+SYS_T10+SYS_CSI+SYS_WTS+SYS_ITS	/* Systems supported for */

#include <string.h>	/* For memcpy etc */
#include <sys/usysio.h>
#include <sys/usysig.h>
#include <sys/usytty.h>
#include <errno.h>

#if SYS_T20+SYS_10X
#include <jsys.h>
#define BUFFER_SIZE(uf) UIO_BUFSIZ
static int indev();

#elif SYS_ITS
#include <sysits.h>
#define BUFFER_SIZE(uf) ((UIO_BUFSIZ / 4) * uf->uf_nbpw)
static int indev();

#elif SYS_T10+SYS_CSI+SYS_WTS
#include <muuo.h>		/* Also gets uuosym */
#include <macsym.h>
#define BUFFER_SIZE(uf) UIO_BUFSIZ
int _blkprime();		/* From write() */
static int inbuf(), inctm();
static int inblk();

#if SYS_WTS
#include <ctype.h>		/* for isdigit() */
#endif
#endif

extern int _uiobuf();		/* From open() */

/* System-dependent routines */
static int in_filbuf(), insys(), in_slow();
static int intty(), eofp();
static int in_cbuf();
#if SYS_T20
static int in_rdln();
#endif
int
read(fd, buf, nbytes)
register int fd, nbytes;
register char *buf;
{
    register struct _ufile *uf;
    register int c, n;
    int r, slowdev;
    long old_count;

    USYS_BEG();
    if (!(uf = _UFGET(fd)) || !(uf->uf_flgs & UIOF_READ))
	USYS_RETERR(EBADF);
    if (nbytes <= 0) {		/* Check for bad count */
	if (nbytes == 0)	/* Permit 0 as no-op */
	    USYS_RET(0);
	USYS_RETERR(EINVAL);
    }

    /* Check for and handle unconverted case */
    if (!(uf->uf_flgs & UIOF_CONVERTED))	/* If not being converted */
	for (;;) {			/* Loop for interrupt continuation */
	    if ((n = insys(uf, buf, nbytes)) > 0)
		USYS_RET(n-1);		/* Normally wins - note decrement! */
	    if (n == 0)			/* Failed? */
		USYS_RETERR(EIO);

	    /* Interrupted.  n has negative of # bytes left to read.
	    ** If we've read anything already, always return OK.  Otherwise
	    ** if nothing has been read, we try to continue rather than
	    ** return EINTR.
	    */
	    if ((r = nbytes + n) > 0)	/* If anything was read */
		USYS_RET(r);		/* Return that much! */

	    /* Nothing whatsoever was read... */
	    if (USYS_END() < 0) {	/* Allow ints, see if handler done */
		errno = EINTR;		/* Yeah, fail this way */
		return -1;		/* Return failure. */
	    }
	    /* Can proceed from interrupt! */
	    USYS_BEG();			/* Disable interrupts again */
	    if (uf != _uffd[fd])	/* Ensure FD still OK */
		USYS_RETERR(EIO);	/* Fail if switcheroo happened */
	    /* Nothing needs updating since nothing was read, just re-try. */
    }

    /* Converting input.
    ** Do hairy conversion loop so input CRLF sequences are furnished as LF.
    ** Need to be careful of slow devices that could hang up; if we're about
    ** to refill our UIO buffer but some converted chars were already read
    ** into the user buffer, we just return and don't even attempt to check
    ** the system input channel.  So a refill happens only if using a fast
    ** device, or if the buffer was empty at start of the read().
    */
    slowdev = uf->uf_flgs & UIOF_CANHANG;	/* Set flag if slow */
    if ((old_count = uf->uf_rleft) <= 0)
	old_count = 0;
    else --uf->uf_cp;			/* Set up for ILDB */
    r = 1;				/* Things OK so far */
    n = nbytes;	    	    	    	/* Number of bytes to return */
    for (;;) {				/* While they want more bytes... */
	if (--uf->uf_rleft < 0) {		/* Get a char into c */
	    uf->uf_pos += old_count;	/* Buff gone, update user file pos */
	    if (r <= 0)			/* If supposed to stop now, */
		break;			/* stop. */
	    if (slowdev && n < nbytes)	/* If may hang and already got some */
		break;			/* then stop, don't try refill */
	    r = in_filbuf(uf, n < nbytes);	/* Remember result code */
	    if ((old_count = uf->uf_rleft) <= 0)	/* See if any more to cvt */
		break;			/* Nope, stop now */
	    --uf->uf_rleft;
	    c = *uf->uf_cp;
	} else c = *++uf->uf_cp;

	/* Have char, now examine it for possible conversion. */
#if SYS_T10+SYS_CSI+SYS_WTS		/* T10 systems must flush nulls */
	if (c == '\0')
	    continue;
#endif
	/* Convert CR-LF to just LF.
	** The lookahead for the LF is obscenely hairy because it has to
	** work properly even if an interrupt or EOF happens during the
	** in_filbuf that looks for the LF.  It's still more efficient than
	** setting and checking a last-char-was-CR flag.
	*/
	if (c == '\r') {		/* If a CR, see what follows. */
	    if (--uf->uf_rleft < 0) {	/* Get a char into c */
		uf->uf_pos += old_count;	/* Update user file pos */
		if (r <= 0 || (slowdev && n < nbytes)) { /* If gotta stop, */
		    uf->uf_rleft++;	/* then put CR back! */
		    uf->uf_pos--;
		    break;		/* then stop, don't try refill */
		}
		r = in_filbuf(uf, n < nbytes);
		if ((old_count = uf->uf_rleft) <= 0) {
		    /* Couldn't check next char for LF. */
		    if (r == 1) *buf = '\r', --n;	/* EOF, add the CR */
		    else if (r < 0) {
			uf->uf_cp = "\r";	/* Interrupt, back up */
			uf->uf_rleft = 1;		/* sneakily */
			uf->uf_pos--;
		    }
		    break;
		}
		--uf->uf_rleft;		/* Will always win */
		c = *uf->uf_cp;
	    } else c = *++uf->uf_cp;

	    if (c != '\n') {		/* If next char after CR is NOT LF */
		uf->uf_rleft++;		/* then put it back! */
		uf->uf_cp--;
		c = '\r';		/* (we had a CR originally) */
	    }
	}
	*buf++ = c;			/* Store the char we got */
	if (--n <= 0) {			/* If that's enough, stop. */
	    uf->uf_pos += (old_count - uf->uf_rleft);	/* Set user I/O pos */
	    uf->uf_cp++;		/* Ensure ptr points to next */
	    break;
	}
    }		/* Don't use "while" cuz "continue" mustn't bump cnt */

    /* r has stop indicator.  Pos means OK, counted out;
    ** 0 means I/O error, and -1 means interrupted.  errno is already set.
    */
    if (r > 0			/* Normal case -- return success */
      || (r < 0 && n < nbytes))	/* also win if interrupted but stuff read */
	USYS_RET(nbytes - n);	/* Win, return # bytes given to user */
    if (r == 0)			/* If error, errno is already set */
	USYS_RET(-1);
    USYS_RETERR(EINTR);		/* Otherwise was interrupted, nothing read */
}
/* IN_FILBUF
**	Fill a buffer to supply raw data for conversion routine.
**	If no buffer has been assigned yet, make one.
**	Must set uf_cp, uf_rleft, possibly other vars as needed to get
**		valid data into buffer.
**	Should not change uf_pos as caller does that.
** Returns:
**	< 0 if interrupted and must return.
**	0   if error
**	1   if EOF
**	> 0 the number of characters (PLUS ONE) ready to read.
*/
static int
in_filbuf(uf, havesome)
register struct _ufile *uf;
int havesome;			/* Flag saying whether already got input */
{
    register int n;

    uf->uf_rleft = 0;
    if (uf->uf_eof)
        return 1;

    for (;;) {
	n = in_cbuf(uf);
#if SYS_WTS
	if (uf->uf_pos == 0)	/* On WAITS check for E directory hdr */
	    n = chkedir(uf);	/* Barf bletch puke */
#endif
	if (n > 0) return n;	/* Have stuff or EOF, return success */
	if (n == 0) {		/* Error, fail immediately */
	    errno = EIO;
	    return 0;
	}
	/* Interrupted.
	** If we've read anything already, always return.  Otherwise
	** if nothing has been read, we try to continue rather than
	** return EINTR.
	*/
	if (havesome || uf->uf_rleft > 0)	/* Was anything read? */
	    return -1;		/* Yes, return immediately. */

	/* Nothing was read... */
	n = USYS_END();		/* Allow ints for a moment, check handler */
	USYS_BEG();		/* Disable interrupts again */
	if (n < 0)		/* Was a handler done? */
	    return -1;		/* Yes, return interrupt indicator */

	/* Can proceed from interrupt! */
	/* Ensure UF is still OK (int rtn may have closed/reopened) */
	if (uf->uf_nopen <= 0 || !uf->uf_bpbeg) {
	    errno = EIO;		/* Fail if switcheroo happened */
	    return 0;
	}
	/* Nothing needs updating since nothing was read, just re-try. */
    }
}


/* IN_CBUF - Fill conversion buffer auxiliary
**	(Re)Fill a buffer to supply raw data for conversion routine.
**	May create a buffer if one is needed and doesn't exist.
**	Must set uf_bpbeg, uf_cp, uf_rleft.
** Returns:
**	< 0 if interrupted, nothing was read.
**	0   if error
**	1   if EOF
**	> 0 the number of characters (PLUS ONE) slurped in.
*/
static int
in_cbuf(uf)
register struct _ufile *uf;
{
    int n;

#if SYS_T10+SYS_CSI+SYS_WTS
    /* If disk or some random buffered input channel, invoke buffer slurp */
    if (uf->uf_type == UIO_DVDSK || uf->uf_biring.br_bp)
	return inbuf(uf);
#endif
    if (!uf->uf_buf && !_uiobuf(uf))	/* If no buffer yet, get one */
	return 0;			/* Fail if can't, errno already set */

    n = insys(uf, uf->uf_bpbeg, uf->uf_blen);
    if (n > 0)
	uf->uf_rleft = n-1;
    else if (n < 0)		/* Interrupted, find # bytes we got */
	uf->uf_rleft = (n + uf->uf_blen);	/* (may be none at all) */
    else uf->uf_rleft = 0;

    /* Restore uf_pos since insys() mistakenly bumped it for us */
    uf->uf_pos -= uf->uf_rleft;
    uf->uf_cp = uf->uf_bpbeg;	/* Set pointer to start of buffer */
    uf->uf_wleft = 0;		/* Never permit writing */
    return n;
}
#if SYS_T20+SYS_10X

/* INSYS - system-dependent input.
**	Returns:
**		> 0 Won, value is # bytes read, PLUS ONE!!!
**		= 0 Lost, some kind of error.
**		< 0 Interrupted.  Value is neg of # bytes LEFT to read.
*/
static int
insys(uf, buf, cnt)
struct _ufile *uf;
int cnt;
char *buf;
{
    switch (uf->uf_type) {
	/* Superfast input devices which can do input using PMAP% to map
	** data pages directly into our address space.
	** This is only possible for DSK: really.
	** PMAP input is not yet implemented.
	*/
#if 0
	case _DVDSK:
	    return inmap(uf);
#endif

	/* Fast input devices, which should always completely satisfy the
	** input request (unless EOF or error).
	*/
	case _DVDSK:
	    return indev(SIN, uf, buf, cnt);

	/* Special TTY input devices, which may permit line editing */
	case _DVTTY:
	case _DVPTY:
	    return intty(uf, buf, cnt);

	/* Slow input devices, which may hang indefinitely and for which
	** we always want to return the available input without waiting
	** for more to fill out the count.
	*/
	default:
	    return in_slow(uf, buf, cnt);
    }
}
static int
indev(num, uf, buf, cnt)
struct _ufile *uf;
int num, cnt;
char *buf;
{
    int i, n, acs[5];

    acs[1] = uf->uf_ch;		/* JFN */
    acs[2] = (int) (buf - 1);		/* pointer to 1 before buffer */
    acs[3] = -cnt;			/* -# to read */
    i = jsys(num, acs);			/* Do it! */

    n = cnt + acs[3];			/* Find # bytes read */
    uf->uf_pos += n;			/* Update count */
    if (i > 0) {
	if (n == 0)		/* If call won, but nothing read, */
	    uf->uf_eof = -1;	/* then say we're at EOF */
	return n+1;		/* Return N+1 so 0 looks winning. */
    } else if (i < 0)		/* Interrupted? */
	return (acs[3] < 0 ?	/* Yes, see if anything left to read */
		acs[3] : n+1);	/* return -N if so, else claim we won */

    /* Call failed somehow... */
    if (eofp(uf)) {		/* Was it EOF of some kind? */
	uf->uf_eof = -1;		/* Yes, set flag */
	return n+1;			/* And return win. */
    }
    errno = EIO;
    return 0;			/* Else return failure. */
}
/* IN_SLOW - system-dependent slow device input.
**	Returns:
**		> 0 Won, value is # bytes read, PLUS ONE!!!
**		= 0 Lost, some kind of error.
**		< 0 Interrupted.  Value is neg of # bytes LEFT to read.
*/
static int
in_slow(uf, buf, cnt)
struct _ufile *uf;
int cnt;
char *buf;
{
    int n, acs[5];

    if (uf->uf_eof)
	return 1;		/* Return EOF */
    acs[1] = uf->uf_ch;	/* Get JFN */
    switch (jsys(SIBE, acs)) {	/* Anything to read? */

	case 1:
	    if (acs[2] < cnt)	/* Yes!  AC2 has # available, read minimum */
		cnt = acs[2];
	    if (cnt <= 0)	/* Just in case SIBE% screws up... */
		break;
	    return indev((int)(SIN|JSYS_OKINT), uf, buf, cnt);

	case 2:		/* No input available, ask for just one */

	default:	/* If error, we try BIN% anyway on the grounds that
			** SIBE% is poorly supported; if there is really
			** a problem with the JFN then BIN% will find it.
			*/
	    break;

    }

    n = jsys(BIN|JSYS_OKINT, acs);	/* Wait until we read 1 byte */
    if (n < 0)			/* Interrupted? */
	return -cnt;		/* Say interrupted, nothing read */

    if (n == 0) {		/* Error? */
	if (eofp(uf)) {	/* Yes, was it EOF? */
	    uf->uf_eof = -1;	/* If so, mark it */
	    return 1;		/* and return EOF */
	}
	errno = EIO;		/* Otherwise, set error # */
	return 0;		/* and return error indication */
    }

    /* Got a byte!  Update things and see if there's any more input. */
    uf->uf_pos++;
    *buf++ = acs[2];		/* Store byte */
    if (--cnt <= 0)		/* Update count */
	return 1+1;

    switch (jsys(SIBE, acs)) {	/* Want more, so check for more! */
	default:	/* If SIBE% lost, then SIBE% must be losing
			** since the BIN% just won.  Ignore the
			** failure, and act as if no more input was available.
			*/
	case 2:		/* No more input available */
	    break;
	case 1:			/* Still have more input! */
	    if (acs[2] < cnt)	/* Set # to read */
		cnt = acs[2];
	    if (cnt <= 0)	/* SIBE% sometimes returns 0?? */
		break;
	    n = indev((int)(SIN|JSYS_OKINT), uf, buf, cnt);
	    if (n > 0) n++;	/* Account for the single byte we read */
	    return n;
    }
    return 1+1;		/* Say read just 1 char (plus one) */
}
/* INTTY - system-dependent TTY input routine.
**	Return values are same as for IN_SLOW.
*/
static int
intty(uf, buf, cnt)
struct _ufile *uf;
int cnt;
char *buf;
{
    register struct _tty *tp;

    tp = &_ttys[uf->uf_dnum];		/* Get ptr to TTY struct */
    if (tp->tt_uf != uf) {		/* Cross-check... */
	errno = EIO;
	return 0;
    }
    switch (tp->sg.sg_flags & (RAW|CBREAK)) {	/* Determine mode */
	case 0:		/* Cooked mode */
	    return in_rdln(tp, uf, buf, cnt);

	case CBREAK:	/* CBREAK mode */
	default:	/* RAW mode */
	    return in_slow(uf, buf, cnt);
    }
}
struct texti {		/* Command block for TEXTI% */
	int rdcwb;	/* # words following in block */
	int rdflg;	/* Flag bits */
	union {		/* I/O designators - one word */
	    char *str;	/* Input str */
	    struct {	/* or two JFNs */
		int in : 18;
		int out: 18;
	    } jfn;
	} rdioj;
	char *rddbp;	/* Where input should go */
	int rddbc;	/* # bytes available in buffer */
};

static int
in_rdln(tp, uf, buf, cnt)
struct _ufile *uf;
struct _tty *tp;
int cnt;
char *buf;
{
    static struct texti			/* for TEXTI */
	textib = {4, RD_BEL|RD_CRF|RD_JFN|RD_BRK };
    int i, acs[5];

    if (uf->uf_eof)
	return 1;		/* EOF, punt */
    textib.rdioj.jfn.in  = uf->uf_ch;
    textib.rdioj.jfn.out = uf->uf_ch;
    textib.rddbp = buf-1;
    textib.rddbc = cnt;
    acs[1] = (int) &textib;
    i = jsys(TEXTI|JSYS_OKINT, acs);
    if (i == 0) {
	if (eofp(uf)) {
	    uf->uf_eof = -1;
	    return (cnt - textib.rddbc)+1;	/* Return # read, plus 1 */
	}
	errno = EIO;
	return 0;
    }
    if (i < 0) {	/* Interrupted? */
	if (textib.rddbc)		/* If any chars unread, */
	    return -textib.rddbc;	/* return # unread. */
    }					/* Else drop thru for normal return! */
    /* Won normally */
    if (*textib.rddbp == ('Z'&037)) {	/* Stopped on ^Z? */
	textib.rddbc++;			/* Yes, don't count as input */
	uf->uf_eof = -1;		/* Say EOF seen */
    }
    return (cnt - textib.rddbc)+1;	/* Return # chars read, plus 1 */
}
/* EOFP(jfn)
 *	given a JFN in AC1 return 0 (in AC1) if there's no EOF on that
 *	JFN, else 1 if there is.
 */

static int
eofp(uf)
struct _ufile *uf;
{
    int acs[5];
    acs[1] = uf->uf_ch;
    jsys(GTSTS, acs);		/* Always succeeds */
    return acs[2]&GS_EOF;	/* Return state of EOF bit */
}
#endif	/* T20/10X */
#if SYS_T10+SYS_CSI+SYS_WTS

/* INSYS - system-dependent input for TOPS-10/WAITS.
**	Returns:
**		> 0 Won, value is # bytes read, PLUS ONE!!!
**		= 0 Lost, some kind of error.
**		< 0 Interrupted.  Value is neg of # bytes LEFT to read.
*/
static int
insys(uf, buf, cnt)
struct _ufile *uf;
char *buf;
int cnt;
{
    switch (uf->uf_type) {
	/* Fast block devices, which must read entire blocks at a time,
	** and which should always completely satisfy the input request
	** (unless hit EOF or error).
	*/
	case UIO_DVDSK:
	    return inblk(uf, buf, cnt);

	/* TTY input devices, which may permit line editing */
	case UIO_DVTTY:
	    return intty(uf, buf, cnt);

	/* Slow input devices, which may hang indefinitely and for which
	** we always want to return the available input without waiting
	** for more to fill out the count.
	*/
	default:
	    return in_slow(uf, buf, cnt);

#if 0	/* No such devices known yet */
	/* Fast character input devices, which should always completely
	** satisfy the input request (unless EOF or error).
	*/
	case UIO_DVxxx:
	    return indev(uf, buf, cnt);
#endif
    }
}
/* INBLK - Read data from block device (normally disk).
**	Always reads complete amount unless error or EOF, never interrupted.
**	Return value is same as for INSYS.
*/
static int
inblk(uf, buf, nbytes)
struct _ufile *uf;
char *buf;
int nbytes;
{
    register int n;
    long begpos = uf->uf_pos;		/* Remember starting user pos */

    for (;;) {
	if ((n = uf->uf_rleft) <= 0) {	/* Any bytes left in buffer? */
	    if (!_blkprime(uf, 0))	/* No, get more input */
		return 0;		/* Error */
	    if ((n = uf->uf_rleft) <= 0)	/* Got some, see how much */
		break;			/* EOF, stop loop */
	}
	if (nbytes < n) n = nbytes;
	memcpy(buf, uf->uf_cp, n);	/* Copy to user space */

	/* Now update vars */
	uf->uf_pos += n;
	uf->uf_cp += n;
	uf->uf_wleft -= n;
	uf->uf_rleft -= n;
	if ((nbytes -= n) <= 0)		/* If request done, return. */
	    break;
	buf += n;			/* More bytes to do, update user ptr */
    }
    return 1 + uf->uf_pos - begpos;	/* Return total+1 bytes given user */
}


#if 0	/* Not needed at moment */

/* INDEV - always slurps and copies specified amount unless error or EOF. */
static int
indev(uf, buf, cnt)
struct _ufile *uf;
int cnt;
char *buf;
{
    register int n;
    int totcnt = 1;

    while ((n = in_slow(uf, buf, cnt)) > 0) {
	totcnt += --n;		/* Update # chars read */
	if (!n || (cnt -= n) <= 0)	/* If EOF or request done, */
	    return totcnt;	/* return total+1. */
	buf += n;
    }
    return n;			/* Error or interrupted */
}
#endif

/* IN_SLOW - attempts to avoid hanging up while reading.
**	Will only transfer whatever is currently in buffer ring, and
**	avoids using IN UUO unless there is no input available at all.
*/
static int
in_slow(uf, buf, cnt)
struct _ufile *uf;
int cnt;
char *buf;
{
    register int n;

    if ((n = inbuf(uf)) <= 0)		/* Get stuff in buff, or new buff */
	return n == 0 ? 0 : -cnt;	/* Error or interrupted */
    if (--n > cnt)			/* Have more input than desired? */
	n = cnt;			/* Yeah, limit to this much */
    memcpy(buf, uf->uf_cp, n);		/* Copy */

    /* Now update vars */
    uf->uf_pos += n;			/* Bump user position in file */
    uf->uf_wleft -= n;			/* May become negative, that's OK */
    if ((uf->uf_rleft -= n) > 0)	/* Only update pointer if needed, */
	uf->uf_cp += n;			/* because it's kinda slow */
    return n+1;
}

/* INBUF - auxiliary that gets some (any) data into a buffer.
**	Either returns amount remaining in current buffer, or
**	gets a new bufferful and returns that.
**	returns > 0	# bytes, plus 1 (thus 1 is EOF)
**		0	Error
**		< 0	Interrupted, nothing was read.
*/
static int
inbuf(uf)
struct _ufile *uf;
{
    register int n;

    if ((n = uf->uf_rleft) > 0)
	return n+1;			/* Already stuff in buffer! */

    /* Must get another input buffer.  Check to see if seek needed. */
    if (uf->uf_type == UIO_DVDSK) {	/* Seeks only possible on disk */
	if (!_blkprime(uf, 0))		/* Prime block device buffer! */
	    return 0;			/* Error of some kind */
	return uf->uf_rleft+1;		/* Won, return # bytes available */
    }

    /* Buffered character device, slurp stuff in. */
    uf->uf_bpos = uf->uf_pos;	/* Set buffer pos to current pos */
    uf->uf_rleft = 0;		/* Nothing in it yet */
    uf->uf_wleft = 0;		/* Don't permit writing */
    if (!_filopuse) n = !MUUO_IO("IN", uf->uf_ch, 0);
    else {
	int arg = XWD(uf->uf_ch, uuosym(".FOINP"));
	n = MUUO_AC("FILOP.", XWD(1,(int)&arg));
    }
    if (n <= 0) {
	if (n < 0) return -1;	/* Interrupted? */
	if (eofp(uf))		/* No was failure due to EOF? */
	    return 1;		/* Mark and say EOF hit */
	errno = EIO;		/* Otherwise, set error # */
	return 0;		/* and return error indication */
    }
    if ((n = uf->uf_biring.br_cnt) <= 0) {
	errno = EIO;		/* Ugh, UUO failed to fix count */
	return 0;		/* Return error indication */
    }
    uf->uf_cp = ++(uf->uf_biring.br_bp); /* Make aligned, barf! */

    /* Got buffer, uf_cp is set and "n" has # valid bytes now in buffer */
    uf->uf_cp += n;
    return (uf->uf_rleft = n) + 1;
}

static int
eofp(uf)
struct _ufile *uf;
{
    int status;
    if (!_filopuse) MUUO_IO("GETSTS", uf->uf_ch, &status);
    else {
	status = XWD(uf->uf_ch, uuosym(".FOGET"));
	MUUO_ACVAL("FILOP.", XWD(1,(int)&status), &status);
    }
    return status & uuosym("IO.EOF");
}
/* TOPS-10 TTY input routines */

static int
intty(uf, buf, cnt)
struct _ufile *uf;
char *buf;
int cnt;
{
#if SYS_CSI
    register struct _tty *tp;
    struct _toblk to;

    if (!_trmopuse)
#endif
    if (uf->uf_ch == UIO_CH_CTTRM)	/* Controlling TTY? */
	return inctm(uf, buf, cnt);	/* Yes - do specially! */
    else return in_slow(uf, buf, cnt);

#if SYS_CSI
    if (uf->uf_eof)		/* TTY must check this specially. */
	return 1;
    tp = &_ttys[uf->uf_dnum];	/* Get TTY struct for this UF */
    to.to_fnc = _TOINP;		/* Function: input byte string */
#if (UIO_CH_CTTRM != -1)	/* Make sure this the convenient value */
#error Must fix CSI intty() for UIO_CH_CTTRM!
#endif
    to.to_udx = XWD(-1,uf->uf_ch);	/* Specify which TTY */
    to.to_arg.io.bp = buf-1;		/* Set up IDPB byte pointer */
    to.to_arg.wd[0] = XWD(0,cnt);	/* Clear LH, put # bytes into RH */
    to.to_arg.io.timeout = 0;

    if (tp->sg.sg_flags & RAW)		/* If raw mode, always use 8 bits */
	to.to_arg.wd[0] |= _TOFLG_8BIT;
    if (tp->sg.sg_flags & CBREAK)	/* If CBREAK mode, break on any char */
	to.to_arg.wd[0] |= _TOFLG_CHRMODE;

    switch (MUUO_AC("TRMOP.", XWD(5,(int)&to))) {
	case -1: return -(cnt - to.to_arg.io.cnt);	/* Interrupt */
	case 0:
	    errno = EIO;		/* Ugh, random I/O error! */
	    return 0;
    }
    if (!(to.to_arg.wd[0] & _TOFLG_8BIT)	/* If ASCII mode, */
      && (to.to_arg.wd[0] & _TOFLG_BREAK))	/* Check for ^Z */
	uf->uf_eof = -1;			/* Set EOF if so */
    return 1 + (cnt - to.to_arg.io.cnt);
#endif	/* CSI */
}

/* INCTM - Input from Controlling Terminal.
**	May be interrupted.
**	Return value is same as for INSYS.
*/
static int
inctm(uf, buf, cnt)
struct _ufile *uf;
register char *buf;
int cnt;
{
    register struct _tty *tp;
    register int n, incnt = 0;
    int inchar, eofch;

    if (uf->uf_eof)		/* TTY must check this specially. */
	return 1;

    tp = &_ttys[0];		/* Get ptr to controlling TTY struct */
    if (tp->tt_uf != uf) {	/* Cross-check... */
	errno = EIO;
	return 0;
    }
    switch (tp->sg.sg_flags & (RAW|CBREAK)) {	/* Determine mode */
	case 0:		/* Cooked mode */
	    n = MUUO_TTY("INCHWL", &inchar);	/* Wait for line */
	    if (!n) n = 1;	/* INCHWL never skips, fake out loop below */
	    for (;;) {		/* Now return as much as possible */
		if (n < 0)	/* If interrupted, return count left */
		    return -cnt;
		if (n == 0)	/* No more chars available at moment? */
		    break;	/* Return # we read, plus 1 */
		if (inchar == ('Z'&037)) {	/* EOF char? */
		    uf->uf_eof = -1;		/* Yep, say EOF seen */
		    break;
		}
		*buf++ = inchar;	/* Won, deposit char */
		++incnt;
		if (--cnt <= 0)		/* Update buffer countdown */
		    break;
		n = MUUO_TTY("INCHSL", &inchar);
	    }
	    return incnt+1;		/* Return # chars read, plus 1 */

	case CBREAK:	/* CBREAK mode only */
	    if (1) eofch = 'Z'&037;
	    else {
	default:	/* RAW mode */
		eofch = -1;
	    }
	    /* Same loop as above, but uses INCHRW/INCHRS instead.
	    ** Also, RAW mode doesn't check for EOF.
	    */
	    n = MUUO_TTY("INCHRW", &inchar);	/* Wait for char */
	    if (!n) n = 1;	/* INCHRW never skips, fake out loop below */
	    for (;;) {		/* Now return as much as possible */
		if (n < 0)	/* If interrupted, return count left */
		    return -cnt;
		if (n == 0)	/* No more chars available at moment? */
		    break;	/* Return # we read, plus 1 */
		if (inchar == eofch) {	/* EOF char? */
		    uf->uf_eof = -1;		/* Yep, say EOF seen */
		    break;
		}
		*buf++ = inchar;	/* Won, deposit char */
		++incnt;
		if (--cnt <= 0)		/* Update buffer countdown */
		    break;
		n = MUUO_TTY("INCHSL", &inchar);
	    }
	    return incnt+1;		/* Return # chars read, plus 1 */
    }
}

#endif /* T10+CSI+WAITS */
#if SYS_WTS	/* Special hackery for WAITS */

/* CHKEDIR - Called whenever about to read the first block of a
**	file using converted input.  If it looks like the start of
**	an E editor directory page, input is flushed up to but not including
**	the first formfeed, so that a formfeed is the first thing the user
**	program reads.
**	An E page starts with:
**		"COMMENT \026 xxVALID ddddd PAGES"
**	where xx is either "  " or "IN" and the 'd's are any digits.
**
** Returns # bytes available in current buffer (0 if EOF hit).
*/
static char cmpstr[] = "COMMENT \26 \1\2VALID \3\3\3\3\3 PAGES";

static int
chkedir(uf)
struct _ufile *uf;
{
    register char *s, *cp;
    register int n;

    n = uf->uf_rleft;
    if (uf->uf_type != UIO_DVDSK	/* Not a disk file? */
      || n < sizeof(cmpstr))		/* or too small? */
	return n;
    if (*(cp = uf->uf_cp) != cmpstr[0])	/* Check 1st char */
	return n;
    s = cmpstr;

    /* OK, start hairy match loop */
    for (;;) {
	switch (*++s) {
	    default:
		if (*s == *++cp)	/* Char must match */
	    case '\2':			/* Ignore placeholder */
		    continue;
		break;
	    case '\1':			/* Must match "  " or "IN" */
		if (*++cp == ' ') {
		    if (*++cp == ' ')
			continue;
		} else if (*cp == 'I') {
		    if (*++cp == 'N')
			continue;
		}
		break;
	    case '\3':
		if (isdigit(*++cp))
		    continue;
		break;

	    case '\0':			/* End of match, won */
		break;
	}
	break;
    }
    if (*s)
	return n;			/* Failed */

    /* Won, now must scan up to first formfeed */
    n -= sizeof(cmpstr)-1;
    for (;;) {
	while (--n >= 0)
	    if (*++cp == '\014') {
		uf->uf_cp = cp;
		uf->uf_rleft = ++n;
		return n+1;
	    }
	/* Ran out of room on this buffer, get another and keep going */
	uf->uf_pos += uf->uf_rleft;	/* Update file pos */
	uf->uf_rleft = 0;			/* Ensure get more */
	n = in_cbuf(uf);
	if (n <= 1) return n;		/* EOF or error? */
	cp = uf->uf_cp;			/* Set up new ptr */
	n = uf->uf_rleft;
    }
}
#endif /* SYS_WTS */
#if SYS_ITS

/* INSYS - ITS system-dependent input.
**	Returns:
**		> 0 Won, value is # bytes read, PLUS ONE!!!
**		= 0 Lost, some kind of error.
**		< 0 Interrupted.  Value is neg of # bytes LEFT to read.
*/
static int
insys(uf, buf, cnt)
register struct _ufile *uf;
int cnt;
char *buf;
{
    switch (uf->uf_type) {
	/* case _DVxxx:		   Non-blocking devices */
	case _DVTTY:		/* Interactive streams */
	    /* hack rubouts later, fall through until then */
	default:		/* DSK-like devices */
	    if (uf->uf_flgs & UIOF_HANDPACK)
		return inblock(uf, buf, cnt);
	    else if (uf->uf_bsize == 7)
		return inpadded(uf, buf, cnt);
	    else
		return insiot(uf, buf, cnt);
    }
}

/* IN_CBUF - ITS Fill conversion buffer
**	Fill a buffer to supply raw data for conversion routine.
**	Must set uf_cp, uf_rleft.
** Returns:
**	< 0 if interrupted, nothing was read.
**	0   if error
**	1   if EOF
**	> 0 the number of characters (PLUS ONE) slurped in.
*/
#if 0	/* Combined with T20/10X routine */
static int
in_cbuf(uf)
register struct _ufile *uf;
{
}
#endif
/* ITS Block mode */
#define IOT_BUFSIZ 300		/* in words */

static int inblock(uf, buf, nbytes)
  struct _ufile *uf;
  int nbytes;
  char *buf;
{
    char *ptr = buf;
    int cnt = nbytes;
    int words, bytes, iotptr, val, n;

    /* First, give him any bytes saved from last time */
    while (uf->uf_zcnt > 0 && cnt > 0) {
	*ptr++ = uf->uf_zbuf[--uf->uf_zcnt];
	cnt--;
	uf->uf_pos++;
    }

    words = cnt / uf->uf_nbpw;	/* He wants this many words */
    bytes = cnt % uf->uf_nbpw;	/* Plus this many bytes */

    /* If his byte pointer is aligned, and has the right size, then we */
    /* can IOT words directly into his buffer */
    if (words > 0 && ptr == ALIGNED_BYTE_PTR(uf->uf_bsize, ptr)) {

	n = (int) (int *) ptr;				/* N: base address */
	iotptr = (((- words) << 18) | n);
	val = SYSCALL2("iot", uf->uf_ch, &iotptr);
	n = ((iotptr & 0777777) - n) * uf->uf_nbpw;	/* N: # bytes read */
	uf->uf_pos += n;
	if (val > 0) {				/* Error */
	    errno = EIO;
	    return 0;
	}
	cnt -= n;
	if (cnt <= 0) return nbytes + 1;	/* Done */
	if (val < 0) return -cnt;		/* Interrupted */
	if (iotptr < 0) {			/* EOF */
	    uf->uf_eof = -1;
	    return nbytes + 1 - cnt;
	}
	words = 0;		/* No more words */
	ptr += n;
    }

    ptr--;			/* IDPB from now on */
    if (bytes > 0) words++;	/* # words to read into internal buffer */

    while (words > 0) {
	int iotbuffer[IOT_BUFSIZ];	/* internal buffer */
	char *bptr;			/* buffer pointer */

	iotptr = (((- (words > IOT_BUFSIZ ? IOT_BUFSIZ : words)) << 18) |
		  ((int) iotbuffer));
	val = SYSCALL2("iot", uf->uf_ch, &iotptr);
	n = ((iotptr & 0777777) - ((int) iotbuffer));	/* N: # words read */
	words -= n;
	n *= uf->uf_nbpw;				/* N: # bytes read */
	bptr = ALIGNED_BYTE_PTR(uf->uf_bsize, iotbuffer);

	/* Save any extra for later */
	while (n > cnt) uf->uf_zbuf[uf->uf_zcnt++] = bptr[--n];

	/* Now give him everything we got */
	cnt -= n;
	uf->uf_pos += n;
	bptr--;
	while (n-- > 0) *++ptr = *++bptr;

	if (val > 0) {				/* Error */
	    errno = EIO;
	    return 0;
	}
	if (cnt <= 0) return nbytes + 1;	/* Done */
	if (val < 0) return -cnt;		/* Interrupted */
	if (iotptr < 0) {			/* EOF */
	    uf->uf_eof = -1;
	    return nbytes + 1 - cnt;
	}
    }

    return nbytes + 1;
}
/* ITS Unit mode, unpadded */
static int insiot(uf, buf, cnt)
  struct _ufile *uf;
  int cnt;
  char *buf;
{
    int val;
    char *ptr = buf - 1;
    int n = cnt;

    val = SYSCALL3("siot", uf->uf_ch, &ptr, &n);
    n = cnt - n;		/* n = number of bytes read */
    uf->uf_pos += n;		/* update pos */
    if (!val) {			/* nothing unusual? */
	if (n < cnt) uf->uf_eof = -1;	/* now at eof? */
	return n + 1;		/* return */
    }

    /* There is no way for an interrupt to happen yet, because */
    /* we haven't provided a way to give permission, but here is the */
    /* code to handle it anyway: */
    if (val < 0) {		/* interrupted? */
	n = n - cnt;		/* n = - bytes to go */
	return (n ? n : cnt + 1);	/* if none to go, ignore interrupt */
    }

    errno = EIO;
    return 0;
}

/* inzbuf() is like insiot(), except it pops characters from _uiozbuf */
/* first.  If this indicates error, interrupt, or EOF, _uiozbuf will be */
/* empty. */
static int inzbuf(uf, buf, cnt)
  struct _ufile *uf;
  int cnt;
  char *buf;
{
    int val;
    char *ptr = buf;
    int nread = 0;

    while (uf->uf_zcnt > 0 && nread < cnt) {
	*ptr++ = uf->uf_zbuf[--uf->uf_zcnt];
	nread++;
	uf->uf_pos++;
    }

    if (nread == cnt) return cnt + 1;

    val = insiot(uf, ptr, cnt - nread);

    if (val > 0) return val + nread;

    else return val;		/* error or interrupt */

}

/* push some stuff into _uiozbuf */
static void unzbuf(uf, buf, cnt)
  struct _ufile *uf;
  int cnt;
  char *buf;
{
    int i;
    i = (uf->uf_zcnt += cnt);
    while (cnt-- > 0) uf->uf_zbuf[--i] = *buf++;
}
/* ITS Unit mode, 7-bit bytes (=> padded) */

#define PADMAX 4		/* Maximum number of pads */

#if PADMAX > UIO_ZBUFSIZ
#error _uiozbuf isn't large enough!
#endif

/* count potential padding characters */
static int padcount(buf, cnt)
  int cnt;
  char *buf;
{
    int c, pads = 0;
    while (pads < PADMAX && cnt > 0) {
	c = buf[--cnt];
	if (!(c == 3 || c == 0)) break;
	pads++;
    }
    return pads;
}

static int inpadded(uf, buf, cnt)
  struct _ufile *uf;
  int cnt;
  char *buf;
{
    int val, nread, pads, xcnt;
    char xbuf[PADMAX];

    if (!(val = inzbuf(uf, buf, cnt))) return 0;

    /* nread = ((val > 0) ? val - 1 : val + cnt); */
    if (val > 0) nread = val - 1;
    else nread = val + cnt;

    pads = padcount(buf, nread);

    if (!pads) return val;	/* No padding?  All done! */

    if (nread < cnt) {
	/* buf wasn't filled => interrupt or EOF */
	/* It must be the case that _uiozbuf is empty */
	if (val < 0) {		/* Interrupted => save padding */
	    unzbuf(uf, buf + (nread - pads), pads);
	}			/* EOF => flush padding */
	uf->uf_pos -= pads;
	return val - pads;
    }

    /* Screw.  buf was filled, but ends with padding. */

    /* First, ask for enough to certainly empty _uiozbuf: */
    if (!(val = inzbuf(uf, xbuf, PADMAX))) return 0;

    if (val == (PADMAX + 1)) {		/* No funny stuff? */
	unzbuf(uf, xbuf, PADMAX);	/* Good, just save chars for later. */
	uf->uf_pos -= PADMAX;
	return cnt + 1;
    }

    /* So how many did we read into xbuf? */
    /* nread = ((val > 0) ? val - 1 : val + PADMAX); */
    if (val > 0) nread = val - 1;
    else nread = val + PADMAX;

    /* This might change our opinion about the pads at the end of buf */
    /* Note that this cannot set pads <= 0 */
    if (pads + nread > PADMAX) pads = PADMAX - nread;

    if (val < 0) {		/* Interrupted?  What a nightmare! */
	unzbuf(uf, xbuf, nread);	/* save stuff */
	unzbuf(uf, buf + (cnt - pads), pads);	/* all of it */
	uf->uf_pos -= (nread + pads);
	return -pads;		/* Must be negative */
    }

    /* Must have been EOF */
    /* So how much stuff in xbuf is for real? */
    xcnt = (nread - padcount(xbuf, nread));
    if (!xcnt) {		/* None of it */
	uf->uf_pos -= (nread + pads);	/* Flush it all */
	return (cnt - pads) + 1;
    }

    /* Real stuff in xbuf */
    unzbuf(uf, xbuf, xcnt);	/* Save it for next time */
    uf->uf_pos -= nread;
    uf->uf_eof = 0;		/* learn about EOF again the hard way */
    return cnt + 1;
}

#endif /* SYS_ITS */

#endif /* T20+10X+T10+CSI+WAITS+ITS */