Example #1
0
/*
 * Various output routines call wsetup to be sure it is safe to write,
 * because either _flags does not include __SWR, or _buf is NULL.
 * _wsetup returns 0 if OK to write, nonzero otherwise.
 */
int
__swsetup(FILE *fp)
{
	/* make sure stdio is set up */
	if (!__sdidinit)
		__sinit();

	/*
	 * If we are not writing, we had better be reading and writing.
	 */
	if ((fp->_flags & __SWR) == 0) {
		if ((fp->_flags & __SRW) == 0)
			return (EOF);
		if (fp->_flags & __SRD) {
			/* clobber any ungetc data */
			if (HASUB(fp))
				FREEUB(fp);
			fp->_flags &= ~(__SRD|__SEOF);
			fp->_r = 0;
			fp->_p = fp->_bf._base;
		}
		fp->_flags |= __SWR;
	}

	/*
	 * Make a buffer if necessary, then set _w.
	 */
	if (fp->_bf._base == NULL) {
		if ((fp->_flags & (__SSTR | __SALC)) == __SSTR)
			return (EOF);
		__smakebuf(fp);
	}
	
	if (fp->_flags & __SLBF) {
		/*
		 * It is line buffered, so make _lbfsize be -_bufsize
		 * for the putc() macro.  We will change _lbfsize back
		 * to 0 whenever we turn off __SWR.
		 */
		fp->_w = 0;
		fp->_lbfsize = -fp->_bf._size;
	} else
		fp->_w = fp->_flags & __SNBF ? 0 : fp->_bf._size;
	return (0);
}
Example #2
0
/*
 * Seek the given file to the given offset.
 * `Whence' must be one of the three SEEK_* macros.
 */
int
fseeko(FILE *fp, off_t offset, int whence)
{
	fpos_t (*seekfn)(void *, fpos_t, int);
	fpos_t target, curoff;
	size_t n;
	struct stat st;
	int havepos;

	/* make sure stdio is set up */
	if (!__sdidinit)
		__sinit();

	/*
	 * Have to be able to seek.
	 */
	if ((seekfn = fp->_seek) == NULL || isatty(__sfileno(fp))) {
		__sseterr(fp, ESPIPE);			/* historic practice */
		return (EOF);
	}

	/*
	 * Change any SEEK_CUR to SEEK_SET, and check `whence' argument.
	 * After this, whence is either SEEK_SET or SEEK_END.
	 */
	FLOCKFILE(fp);
	switch (whence) {

	case SEEK_CUR:
		/*
		 * In order to seek relative to the current stream offset,
		 * we have to first find the current stream offset a la
		 * ftell (see ftell for details).
		 */
		__sflush(fp);	/* may adjust seek offset on append stream */
		if (fp->_flags & __SOFF)
			curoff = fp->_offset;
		else {
			curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR);
			if (curoff == (fpos_t)-1) {
				FUNLOCKFILE(fp);
				return (EOF);
			}
		}
		if (fp->_flags & __SRD) {
			curoff -= fp->_r;
			if (HASUB(fp))
				curoff -= fp->_ur;
		} else if (fp->_flags & __SWR && fp->_p != NULL)
			curoff += fp->_p - fp->_bf._base;

		offset += curoff;
		whence = SEEK_SET;
		havepos = 1;
		break;

	case SEEK_SET:
	case SEEK_END:
		curoff = 0;		/* XXX just to keep gcc quiet */
		havepos = 0;
		break;

	default:
		FUNLOCKFILE(fp);
		__sseterr(fp, EINVAL);
		return (EOF);
	}

	/*
	 * Can only optimise if:
	 *	reading (and not reading-and-writing);
	 *	not unbuffered; and
	 *	this is a `regular' Unix file (and hence seekfn==__sseek).
	 * We must check __NBF first, because it is possible to have __NBF
	 * and __SOPT both set.
	 */
	if (fp->_bf._base == NULL)
		__smakebuf(fp);
	if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT))
		goto dumb;
	if ((fp->_flags & __SOPT) == 0) {
		if (seekfn != __sseek ||
		    fp->_file < 0 || __libc_fstat(fp->_file, &st) ||
		    (st.st_mode & S_IFMT) != S_IFREG) {
			fp->_flags |= __SNPT;
			goto dumb;
		}
		fp->_blksize = st.st_blksize;
		fp->_flags |= __SOPT;
	}

	/*
	 * We are reading; we can try to optimise.
	 * Figure out where we are going and where we are now.
	 */
	if (whence == SEEK_SET)
		target = offset;
	else {
		if (__libc_fstat(fp->_file, &st))
			goto dumb;
		target = st.st_size + offset;
	}

	if (!havepos) {
		if (fp->_flags & __SOFF)
			curoff = fp->_offset;
		else {
			curoff = (*seekfn)(fp->_cookie, (fpos_t)0, SEEK_CUR);
			if (curoff == POS_ERR)
				goto dumb;
		}
		curoff -= fp->_r;
		if (HASUB(fp))
			curoff -= fp->_ur;
	}

	/*
	 * Compute the number of bytes in the input buffer (pretending
	 * that any ungetc() input has been discarded).  Adjust current
	 * offset backwards by this count so that it represents the
	 * file offset for the first byte in the current input buffer.
	 */
	if (HASUB(fp)) {
		curoff += fp->_r;	/* kill off ungetc */
		n = fp->_up - fp->_bf._base;
		curoff -= n;
		n += fp->_ur;
	} else {
		n = fp->_p - fp->_bf._base;
		curoff -= n;
		n += fp->_r;
	}

	/*
	 * If the target offset is within the current buffer,
	 * simply adjust the pointers, clear EOF, undo ungetc(),
	 * and return.  (If the buffer was modified, we have to
	 * skip this; see fgetln.c.)
	 */
	if ((fp->_flags & __SMOD) == 0 &&
	    target >= curoff && target < curoff + n) {
		int o = target - curoff;

		fp->_p = fp->_bf._base + o;
		fp->_r = n - o;
		if (HASUB(fp))
			FREEUB(fp);
		fp->_flags &= ~__SEOF;
		FUNLOCKFILE(fp);
		return (0);
	}

	/*
	 * The place we want to get to is not within the current buffer,
	 * but we can still be kind to the kernel copyout mechanism.
	 * By aligning the file offset to a block boundary, we can let
	 * the kernel use the VM hardware to map pages instead of
	 * copying bytes laboriously.  Using a block boundary also
	 * ensures that we only read one block, rather than two.
	 */
	curoff = target & ~(fp->_blksize - 1);
	if ((*seekfn)(fp->_cookie, curoff, SEEK_SET) == POS_ERR)
		goto dumb;
	fp->_r = 0;
 	fp->_p = fp->_bf._base;
	if (HASUB(fp))
		FREEUB(fp);
	fp->_flags &= ~__SEOF;
	n = target - curoff;
	if (n) {
		if (__srefill(fp) || fp->_r < n)
			goto dumb;
		fp->_p += n;
		fp->_r -= n;
	}
	FUNLOCKFILE(fp);
	return (0);

	/*
	 * We get here if we cannot optimise the seek ... just
	 * do it.  Allow the seek function to change fp->_bf._base.
	 */
dumb:
	if (__sflush(fp) ||
	    (*seekfn)(fp->_cookie, (fpos_t)offset, whence) == POS_ERR) {
		FUNLOCKFILE(fp);
		return (EOF);
	}
	/* success: clear EOF indicator and discard ungetc() data */
	if (HASUB(fp))
		FREEUB(fp);
	fp->_p = fp->_bf._base;
	fp->_r = 0;
	/* fp->_w = 0; */	/* unnecessary (I think...) */
	fp->_flags &= ~__SEOF;
	FUNLOCKFILE(fp);
	return (0);
}
Example #3
0
File: refill.c Project: B-Rich/edk2
/*
 * Refill a stdio buffer.
 * Return EOF on eof or error, 0 otherwise.
 */
int
__srefill(FILE *fp)
{

  _DIAGASSERT(fp != NULL);
  if(fp == NULL) {
    errno = EINVAL;
    return (EOF);
  }

  /* make sure stdio is set up */
  if (!__sdidinit)
    __sinit();

  fp->_r = 0;   /* largely a convenience for callers */

  /* SysV does not make this test; take it out for compatibility */
  if (fp->_flags & __SEOF) {
    return (EOF);
  }

  /* if not already reading, have to be reading and writing */
  if ((fp->_flags & __SRD) == 0) {
    if ((fp->_flags & __SRW) == 0) {
      errno = EBADF;
      fp->_flags |= __SERR;   //<dvm> Allows differentiation between errors and EOF
      return (EOF);
    }
    /* switch to reading */
    if (fp->_flags & __SWR) {
      if (__sflush(fp)) {
        return (EOF);
      }
      fp->_flags &= ~__SWR;
      fp->_w = 0;
      fp->_lbfsize = 0;
    }
    fp->_flags |= __SRD;
  } else {
    /*
     * We were reading.  If there is an ungetc buffer,
     * we must have been reading from that.  Drop it,
     * restoring the previous buffer (if any).  If there
     * is anything in that buffer, return.
     */
    if (HASUB(fp)) {
      FREEUB(fp);
      if ((fp->_r = fp->_ur) != 0) {
        fp->_p = fp->_up;
        return (0);
      }
    }
  }

  if (fp->_bf._base == NULL)
    __smakebuf(fp);

  /*
   * Before reading from a line buffered or unbuffered file,
   * flush all line buffered output files, per the ANSI C
   * standard.
   */
  if (fp->_flags & (__SLBF|__SNBF)) {
    rwlock_rdlock(&__sfp_lock);
    (void) _fwalk(lflush);
    rwlock_unlock(&__sfp_lock);
  }
  fp->_p = fp->_bf._base;
  fp->_r = (*fp->_read)(fp->_cookie, (char *)fp->_p, fp->_bf._size);
  fp->_flags &= ~__SMOD;  /* buffer contents are again pristine */
  if (fp->_r <= 0) {
    if (fp->_r == 0)
      fp->_flags |= __SEOF;
    else {
      fp->_r = 0;
      fp->_flags |= __SERR;
    }
    return (EOF);
  }
  return (0);
}
Example #4
0
/*
 * Seek the given file to the given offset.
 * `Whence' must be one of the three SEEK_* macros.
 */
int
_fseeko(FILE *fp, off_t offset, int whence, int ltest)
{
	fpos_t (*seekfn)(void *, fpos_t, int);
	fpos_t target, curoff, ret;
	size_t n;
	struct stat st;
	int havepos;

	/*
	 * Have to be able to seek.
	 */
	if ((seekfn = fp->_seek) == NULL) {
		errno = ESPIPE;		/* historic practice */
		return (-1);
	}

	/*
	 * Change any SEEK_CUR to SEEK_SET, and check `whence' argument.
	 * After this, whence is either SEEK_SET or SEEK_END.
	 */
	switch (whence) {

	case SEEK_CUR:
		/*
		 * In order to seek relative to the current stream offset,
		 * we have to first find the current stream offset via
		 * ftell (see ftell for details).
		 */
		if (_ftello(fp, &curoff))
			return (-1);
		if (curoff < 0) {
			/* Unspecified position because of ungetc() at 0 */
			errno = ESPIPE;
			return (-1);
		}
		if (offset > 0 && curoff > OFF_MAX - offset) {
			errno = EOVERFLOW;
			return (-1);
		}
		offset += curoff;
		if (offset < 0) {
			errno = EINVAL;
			return (-1);
		}
		if (ltest && offset > LONG_MAX) {
			errno = EOVERFLOW;
			return (-1);
		}
		whence = SEEK_SET;
		havepos = 1;
		break;

	case SEEK_SET:
		if (offset < 0) {
			errno = EINVAL;
			return (-1);
		}
	case SEEK_END:
		curoff = 0;		/* XXX just to keep gcc quiet */
		havepos = 0;
		break;

	default:
		errno = EINVAL;
		return (-1);
	}

	/*
	 * Can only optimise if:
	 *	reading (and not reading-and-writing);
	 *	not unbuffered; and
	 *	this is a `regular' Unix file (and hence seekfn==__sseek).
	 * We must check __NBF first, because it is possible to have __NBF
	 * and __SOPT both set.
	 */
	if (fp->_bf._base == NULL)
		__smakebuf(fp);
	if (fp->pub._flags & (__SWR | __SRW | __SNBF | __SNPT))
		goto dumb;
	if ((fp->pub._flags & __SOPT) == 0) {
		if (seekfn != __sseek ||
		    fp->pub._fileno < 0 || _fstat(fp->pub._fileno, &st) ||
		    (st.st_mode & S_IFMT) != S_IFREG) {
			fp->pub._flags |= __SNPT;
			goto dumb;
		}
		fp->_blksize = st.st_blksize;
		fp->pub._flags |= __SOPT;
	}

	/*
	 * We are reading; we can try to optimise.
	 * Figure out where we are going and where we are now.
	 */
	if (whence == SEEK_SET)
		target = offset;
	else {
		if (_fstat(fp->pub._fileno, &st))
			goto dumb;
		if (offset > 0 && st.st_size > OFF_MAX - offset) {
			errno = EOVERFLOW;
			return (-1);
		}
		target = st.st_size + offset;
		if ((off_t)target < 0) {
			errno = EINVAL;
			return (-1);
		}
		if (ltest && (off_t)target > LONG_MAX) {
			errno = EOVERFLOW;
			return (-1);
		}
	}

	if (!havepos && _ftello(fp, &curoff))
		goto dumb;

	/*
	 * (If the buffer was modified, we have to
	 * skip this; see fgetln.c.)
	 */
	if (fp->pub._flags & __SMOD)
		goto abspos;

	/*
	 * Compute the number of bytes in the input buffer (pretending
	 * that any ungetc() input has been discarded).  Adjust current
	 * offset backwards by this count so that it represents the
	 * file offset for the first byte in the current input buffer.
	 */
	if (HASUB(fp)) {
		curoff += fp->pub._r;	/* kill off ungetc */
		n = fp->_up - fp->_bf._base;
		curoff -= n;
		n += fp->_ur;
	} else {
		n = fp->pub._p - fp->_bf._base;
		curoff -= n;
		n += fp->pub._r;
	}

	/*
	 * If the target offset is within the current buffer,
	 * simply adjust the pointers, clear EOF, undo ungetc(),
	 * and return.
	 */
	if (target >= curoff && target < curoff + n) {
		size_t o = target - curoff;

		fp->pub._p = fp->_bf._base + o;
		fp->pub._r = n - o;
		if (HASUB(fp))
			FREEUB(fp);
		fp->pub._flags &= ~__SEOF;
		memset(WCIO_GET(fp), 0, sizeof(struct wchar_io_data));
		return (0);
	}

abspos:
	/*
	 * The place we want to get to is not within the current buffer,
	 * but we can still be kind to the kernel copyout mechanism.
	 * By aligning the file offset to a block boundary, we can let
	 * the kernel use the VM hardware to map pages instead of
	 * copying bytes laboriously.  Using a block boundary also
	 * ensures that we only read one block, rather than two.
	 */
	curoff = target & ~(fp->_blksize - 1);
	if (_sseek(fp, curoff, SEEK_SET) == POS_ERR)
		goto dumb;
	fp->pub._r = 0;
	fp->pub._p = fp->_bf._base;
	if (HASUB(fp))
		FREEUB(fp);
	n = target - curoff;
	if (n) {
		if (__srefill(fp) || fp->pub._r < n)
			goto dumb;
		fp->pub._p += n;
		fp->pub._r -= n;
	}
	fp->pub._flags &= ~__SEOF;
	memset(WCIO_GET(fp), 0, sizeof(struct wchar_io_data));
	return (0);

	/*
	 * We get here if we cannot optimise the seek ... just
	 * do it.  Allow the seek function to change fp->_bf._base.
	 */
dumb:
	if (__sflush(fp) ||
	    (ret = _sseek(fp, (fpos_t)offset, whence)) == POS_ERR)
		return (-1);
	if (ltest && ret > LONG_MAX) {
		fp->pub._flags |= __SERR;
		errno = EOVERFLOW;
		return (-1);
	}
	/* success: clear EOF indicator and discard ungetc() data */
	if (HASUB(fp))
		FREEUB(fp);
	fp->pub._p = fp->_bf._base;
	fp->pub._r = 0;
	/* fp->pub._w = 0; */	/* unnecessary (I think...) */
	fp->pub._flags &= ~__SEOF;
	memset(WCIO_GET(fp), 0, sizeof(struct wchar_io_data));
	return (0);
}
Example #5
0
/*
 * Refill a stdio buffer.
 * Return EOF on eof or error, 0 otherwise.
 */
int
__srefill(FILE *fp)
{

	/* make sure stdio is set up */
	if (!__sdidinit)
		__sinit();

	ORIENT(fp, -1);

	fp->_r = 0;		/* largely a convenience for callers */

	/* SysV does not make this test; take it out for compatibility */
	if (fp->_flags & __SEOF)
		return (EOF);

	/* if not already reading, have to be reading and writing */
	if ((fp->_flags & __SRD) == 0) {
		if ((fp->_flags & __SRW) == 0) {
			errno = EBADF;
			fp->_flags |= __SERR;
			return (EOF);
		}
		/* switch to reading */
		if (fp->_flags & __SWR) {
			if (__sflush(fp))
				return (EOF);
			fp->_flags &= ~__SWR;
			fp->_w = 0;
			fp->_lbfsize = 0;
		}
		fp->_flags |= __SRD;
	} else {
		/*
		 * We were reading.  If there is an ungetc buffer,
		 * we must have been reading from that.  Drop it,
		 * restoring the previous buffer (if any).  If there
		 * is anything in that buffer, return.
		 */
		if (HASUB(fp)) {
			FREEUB(fp);
			if ((fp->_r = fp->_ur) != 0) {
				fp->_p = fp->_up;
				return (0);
			}
		}
	}

	if (fp->_bf._base == NULL)
		__smakebuf(fp);

	/*
	 * Before reading from a line buffered or unbuffered file,
	 * flush all line buffered output files, per the ANSI C
	 * standard.
	 */
	if (fp->_flags & (__SLBF|__SNBF)) {
		/* Ignore this file in _fwalk to avoid potential deadlock. */
		fp->_flags |= __SIGN;
		(void) _fwalk(lflush);
		fp->_flags &= ~__SIGN;

		/* Now flush this file without locking it. */
		if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR))
			__sflush(fp);
	}
	fp->_p = fp->_bf._base;
	fp->_r = _sread(fp, (char *)fp->_p, fp->_bf._size);
	fp->_flags &= ~__SMOD;	/* buffer contents are again pristine */
	if (fp->_r <= 0) {
		if (fp->_r == 0)
			fp->_flags |= __SEOF;
		else {
			fp->_r = 0;
			fp->_flags |= __SERR;
		}
		return (EOF);
	}
	return (0);
}
Example #6
0
int
__srefill (register FILE * fp)
{
  /* make sure stdio is set up */

  CHECK_INIT (fp);

  fp->_r = 0;			/* largely a convenience for callers */

  /* SysV does not make this test; take it out for compatibility */
  if (fp->_flags & __SEOF)
    return EOF;

  /* if not already reading, have to be reading and writing */
  if ((fp->_flags & __SRD) == 0)
    {
      if ((fp->_flags & __SRW) == 0)
	return EOF;
      /* switch to reading */
      if (fp->_flags & __SWR)
	{
	  if (fflush (fp))
	    return EOF;
	  fp->_flags &= ~__SWR;
	  fp->_w = 0;
	  fp->_lbfsize = 0;
	}
      fp->_flags |= __SRD;
    }
  else
    {
      /*
       * We were reading.  If there is an ungetc buffer,
       * we must have been reading from that.  Drop it,
       * restoring the previous buffer (if any).  If there
       * is anything in that buffer, return.
       */
      if (HASUB (fp))
	{
	  FREEUB (fp);
	  if ((fp->_r = fp->_ur) != 0)
	    {
	      fp->_p = fp->_up;
	      return 0;
	    }
	}
    }

  if (fp->_bf._base == NULL)
    __smakebuf (fp);

  /*
   * Before reading from a line buffered or unbuffered file,
   * flush all line buffered output files, per the ANSI C
   * standard.
   */

  if (fp->_flags & (__SLBF | __SNBF))
    (void) _fwalk (fp->_data, lflush);
  fp->_p = fp->_bf._base;
  fp->_r = (*fp->_read) (fp->_cookie, (char *) fp->_p, fp->_bf._size);
  fp->_flags &= ~__SMOD;	/* buffer contents are again pristine */
  if (fp->_r <= 0)
    {
      if (fp->_r == 0)
	fp->_flags |= __SEOF;
      else
	{
	  fp->_r = 0;
	  fp->_flags |= __SERR;
	}
      return EOF;
    }
  return 0;
}
Example #7
0
int fseek
    (
    FAST FILE *	fp,		/* stream */
    long	offset,		/* offset from <whence> */
    int		whence		/* position to offset from: */
				/* SEEK_SET = beginning */
				/* SEEK_CUR = current position */
				/* SEEK_END = end-of-file */
    )
    {
    fpos_t	target;
    fpos_t	curoff;
    size_t	n;
    struct stat st;
    int		havepos;
    BOOL	doStat;

    if (OBJ_VERIFY (fp, fpClassId) != OK)
	return (ERROR);

    /*
     * Change any SEEK_CUR to SEEK_SET, and check `whence' argument.
     * After this, whence is either SEEK_SET or SEEK_END.
     */

    switch (whence) 
	{
	case SEEK_CUR:
		/*
		 * In order to seek relative to the current stream offset,
		 * we have to first find the current stream offset a la
		 * ftell (see ftell for details).
		 */
		if (fp->_flags & __SOFF)
		    curoff = fp->_offset;
		else 
		    {
		    curoff = __sseek (fp, (fpos_t)0, SEEK_CUR);
		    if (curoff == -1L)
			return (EOF);
		    }

		if (fp->_flags & __SRD) 
		    {
		    curoff -= fp->_r;
		    if (HASUB(fp))
			curoff -= fp->_ur;
		    }
		else if (fp->_flags & __SWR && fp->_p != NULL)
		    curoff += fp->_p - fp->_bf._base;

		offset += curoff;
		whence	= SEEK_SET;
		havepos = 1;
		break;

	case SEEK_SET:
	case SEEK_END:
		curoff = 0;		/* XXX just to keep gcc quiet */
		havepos = 0;
		break;

	default:
		errno = EINVAL;
		return (EOF);
	}

    /*
     * Can only optimise if:
     *	reading (and not reading-and-writing);
     *	not unbuffered; and
     *	this is a `regular' Unix file (and hence seekfn==__sseek).
     * We must check __NBF first, because it is possible to have __NBF
     * and __SOPT both set.
     */

    if (fp->_bf._base == NULL)
	__smakebuf (fp);

    if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT))
	goto dumb;


    doStat = ioctl (fp->_file, FIOFSTATGET, (int)&st);

    if ((fp->_flags & __SOPT) == 0) 
	{
	if ((fp->_file < 0 || (doStat) ||
	    (st.st_mode & S_IFMT) != S_IFREG)) 
	    {
	    fp->_flags |= __SNPT;
	    goto dumb;
	    }

	fp->_blksize 	 = st.st_blksize;
	fp->_flags	|= __SOPT;
	}

    /*
     * We are reading; we can try to optimise.
     * Figure out where we are going and where we are now.
     */

    if (whence == SEEK_SET)
	target = offset;
    else 
	{
	if (doStat)
	    goto dumb;

	target = st.st_size + offset;
	}

    if (!havepos) 
	{
	if (fp->_flags & __SOFF)
	    curoff = fp->_offset;
	else 
	    {
	    curoff = __sseek (fp, 0L, SEEK_CUR);

	    if (curoff == POS_ERR)
		goto dumb;
	    }

	curoff -= fp->_r;

	if (HASUB(fp))
	    curoff -= fp->_ur;
	}

    /*
     * Compute the number of bytes in the input buffer (pretending
     * that any ungetc() input has been discarded).  Adjust current
     * offset backwards by this count so that it represents the
     * file offset for the first byte in the current input buffer.
     */

    if (HASUB(fp)) 
	{
	n	= fp->_up - fp->_bf._base;
	curoff -= n;
	n      += fp->_ur;
	} 
    else 
	{
	n	= fp->_p - fp->_bf._base;
	curoff -= n;
	n      += fp->_r;
	}

    /*
     * If the target offset is within the current buffer,
     * simply adjust the pointers, clear EOF, undo ungetc(),
     * and return.  (If the buffer was modified, we have to
     * skip this; see fgetline.c.)
     */

    if (((fp->_flags & __SMOD) == 0) && 
	(target >= curoff) && 
	(target < (curoff + n))) 
	{
	FAST int o = target - curoff;

	fp->_p = fp->_bf._base + o;
	fp->_r = n - o;

	if (HASUB(fp))
	    FREEUB(fp);

	fp->_flags &= ~__SEOF;

	return (0);
	}

    /*
     * The place we want to get to is not within the current buffer,
     * but we can still be kind to the kernel copyout mechanism.
     * By aligning the file offset to a block boundary, we can let
     * the kernel use the VM hardware to map pages instead of
     * copying bytes laboriously.  Using a block boundary also
     * ensures that we only read one block, rather than two.
     */

    curoff = target & ~(fp->_blksize - 1);

    if (__sseek (fp, curoff, SEEK_SET) == POS_ERR)
	goto dumb;

    fp->_r = 0;

    if (HASUB(fp))
	FREEUB(fp);

    fp->_flags &= ~__SEOF;
    n = target - curoff;

    if (n) 
	{
	if (__srefill (fp) || fp->_r < n)
	    goto dumb;
	fp->_p += n;
	fp->_r -= n;
	}

    return (0);

    /*
     * We get here if we cannot optimise the seek ... just
     * do it.  Allow the seek function to change fp->_bf._base.
     */
dumb:
    if ((__sflush (fp)) || (__sseek (fp, offset, whence) == POS_ERR)) 
	return (EOF);

    /* success: clear EOF indicator and discard ungetc() data */

    if (HASUB(fp))
	FREEUB(fp);

    fp->_p = fp->_bf._base;
    fp->_r = 0;

    /* fp->_w = 0; */	/* unnecessary (I think...) */

    fp->_flags &= ~__SEOF;

    return (0);
    }