sl@0: /* FSEEK.C sl@0: * sl@0: * Portions Copyright (c) 1990-2004 Nokia Corporation and/or its subsidiary(-ies). sl@0: * All rights reserved. sl@0: */ sl@0: sl@0: /* sl@0: * Copyright (c) 1990 The Regents of the University of California. sl@0: * All rights reserved. sl@0: * sl@0: * Redistribution and use in source and binary forms are permitted sl@0: * provided that the above copyright notice and this paragraph are sl@0: * duplicated in all such forms and that any documentation, sl@0: * advertising materials, and other materials related to such sl@0: * distribution and use acknowledge that the software was developed sl@0: * by the University of California, Berkeley. The name of the sl@0: * University may not be used to endorse or promote products derived sl@0: * from this software without specific prior written permission. sl@0: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR sl@0: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED sl@0: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. sl@0: */ sl@0: sl@0: /* sl@0: FUNCTION sl@0: <<fseek>>---set file position sl@0: sl@0: INDEX sl@0: fseek sl@0: sl@0: ANSI_SYNOPSIS sl@0: #include <stdio.h> sl@0: int fseek(FILE *<[fp]>, long <[offset]>, int <[whence]>) sl@0: sl@0: TRAD_SYNOPSIS sl@0: #include <stdio.h> sl@0: int fseek(<[fp]>, <[offset]>, <[whence]>) sl@0: FILE *<[fp]>; sl@0: long <[offset]>; sl@0: int <[whence]>; sl@0: sl@0: DESCRIPTION sl@0: Objects of type <<FILE>> can have a ``position'' that records how much sl@0: of the file your program has already read. Many of the <<stdio>> functions sl@0: depend on this position, and many change it as a side effect. sl@0: sl@0: You can use <<fseek>> to set the position for the file identified by sl@0: <[fp]>. The value of <[offset]> determines the new position, in one sl@0: of three ways selected by the value of <[whence]> (defined as macros sl@0: in `<<stdio.h>>'): sl@0: sl@0: <<SEEK_SET>>---<[offset]> is the absolute file position (an offset sl@0: from the beginning of the file) desired. <[offset]> must be positive. sl@0: sl@0: <<SEEK_CUR>>---<[offset]> is relative to the current file position. sl@0: <[offset]> can meaningfully be either positive or negative. sl@0: sl@0: <<SEEK_END>>---<[offset]> is relative to the current end of file. sl@0: <[offset]> can meaningfully be either positive (to increase the size sl@0: of the file) or negative. sl@0: sl@0: See <<ftell>> to determine the current file position. sl@0: sl@0: RETURNS sl@0: <<fseek>> returns <<0>> when successful. If <<fseek>> fails, the sl@0: result is <<EOF>>. The reason for failure is indicated in <<errno>>: sl@0: either <<ESPIPE>> (the stream identified by <[fp]> doesn't support sl@0: repositioning) or <<EINVAL>> (invalid file position). sl@0: sl@0: PORTABILITY sl@0: ANSI C requires <<fseek>>. sl@0: sl@0: Supporting OS subroutines required: <<close>>, <<fstat>>, <<isatty>>, sl@0: <<lseek>>, <<read>>, <<sbrk>>, <<write>>. sl@0: */ sl@0: sl@0: #include <stdio_r.h> sl@0: #include <time.h> sl@0: #include <fcntl.h> sl@0: #include <stdlib_r.h> sl@0: #include <errno.h> sl@0: #include <sys/stat.h> sl@0: #include "LOCAL.H" sl@0: sl@0: #define POS_ERR (-(fpos_t)1) sl@0: sl@0: /* sl@0: * Seek the given file to the given offset. sl@0: * `Whence' must be one of the three SEEK_* macros. sl@0: */ sl@0: sl@0: /** sl@0: Reposition stream's position indicator. sl@0: @return If successful the function returns 0. Otherwise it returns nonzero. sl@0: @param fp Pointer to an open file. sl@0: @param offset Number of bytes from origin. sl@0: @param whence Initial position from where offset is applied. sl@0: */ sl@0: EXPORT_C int sl@0: fseek (register FILE *fp,long offset,int whence) sl@0: { sl@0: struct _reent *ptr; sl@0: fpos_t (*seekfn)(void *, fpos_t, int); sl@0: fpos_t target, curoff = -1; sl@0: size_t n; sl@0: struct stat st; sl@0: int havepos; sl@0: sl@0: /* Make sure stdio is set up. */ sl@0: sl@0: CHECK_INIT (fp); sl@0: ptr = fp->_data; sl@0: sl@0: /* If we've been doing some writing, and we're in append mode sl@0: then we don't really know where the filepos is. */ sl@0: sl@0: if (fp->_flags & __SAPP && fp->_flags & __SWR) sl@0: { sl@0: /* So flush the buffer and seek to the end. */ sl@0: fflush (fp); sl@0: } sl@0: sl@0: /* Have to be able to seek. */ sl@0: sl@0: if ((seekfn = fp->_seek) == NULL) sl@0: { sl@0: ptr->_errno = ESPIPE; /* ??? */ sl@0: return EOF; sl@0: } sl@0: sl@0: /* sl@0: * Change any SEEK_CUR to SEEK_SET, and check `whence' argument. sl@0: * After this, whence is either SEEK_SET or SEEK_END. sl@0: */ sl@0: sl@0: switch (whence) sl@0: { sl@0: case SEEK_CUR: sl@0: /* sl@0: * In order to seek relative to the current stream offset, sl@0: * we have to first find the current stream offset a la sl@0: * ftell (see ftell for details). sl@0: */ sl@0: if (fp->_flags & __SOFF) sl@0: curoff = fp->_offset; sl@0: else sl@0: { sl@0: curoff = (*seekfn) (fp->_cookie, (fpos_t) 0, SEEK_CUR); sl@0: if (curoff == -1L) sl@0: return EOF; sl@0: } sl@0: if (fp->_flags & __SRD) sl@0: { sl@0: curoff -= fp->_r; sl@0: if (HASUB (fp)) sl@0: curoff -= fp->_ur; sl@0: } sl@0: else if (fp->_flags & __SWR && fp->_p != NULL) sl@0: curoff += fp->_p - fp->_bf._base; sl@0: sl@0: offset += curoff; sl@0: whence = SEEK_SET; sl@0: havepos = 1; sl@0: break; sl@0: sl@0: case SEEK_SET: sl@0: case SEEK_END: sl@0: havepos = 0; sl@0: break; sl@0: sl@0: default: sl@0: ptr->_errno = EINVAL; sl@0: return (EOF); sl@0: } sl@0: sl@0: /* sl@0: * Can only optimise if: sl@0: * reading (and not reading-and-writing); sl@0: * not unbuffered; and sl@0: * this is a `regular' Unix file (and hence seekfn==__sseek). sl@0: * We must check __NBF first, because it is possible to have __NBF sl@0: * and __SOPT both set. sl@0: */ sl@0: sl@0: if (fp->_bf._base == NULL) sl@0: __smakebuf (fp); sl@0: if (fp->_flags & (__SWR | __SRW | __SNBF | __SNPT)) sl@0: goto dumb; sl@0: if ((fp->_flags & __SOPT) == 0) sl@0: { sl@0: if (seekfn != __sseek sl@0: || fp->_file < 0 sl@0: || _fstat_r (ptr, fp->_file, &st) sl@0: || (st.st_mode & S_IFMT) != S_IFREG) sl@0: { sl@0: fp->_flags |= __SNPT; sl@0: goto dumb; sl@0: } sl@0: #ifdef HAVE_BLKSIZE sl@0: fp->_blksize = st.st_blksize; sl@0: #else sl@0: fp->_blksize = 1024; sl@0: #endif sl@0: fp->_flags |= __SOPT; sl@0: } sl@0: sl@0: /* sl@0: * We are reading; we can try to optimise. sl@0: * Figure out where we are going and where we are now. sl@0: */ sl@0: sl@0: if (whence == SEEK_SET) sl@0: target = offset; sl@0: else sl@0: { sl@0: if (_fstat_r (ptr, fp->_file, &st)) sl@0: goto dumb; sl@0: target = st.st_size + offset; sl@0: } sl@0: sl@0: if (!havepos) sl@0: { sl@0: if (fp->_flags & __SOFF) sl@0: curoff = fp->_offset; sl@0: else sl@0: { sl@0: curoff = (*seekfn) (fp->_cookie, 0L, SEEK_CUR); sl@0: if (curoff == POS_ERR) sl@0: goto dumb; sl@0: } sl@0: curoff -= fp->_r; sl@0: if (HASUB (fp)) sl@0: curoff -= fp->_ur; sl@0: } sl@0: sl@0: /* sl@0: * Compute the number of bytes in the input buffer (pretending sl@0: * that any ungetc() input has been discarded). Adjust current sl@0: * offset backwards by this count so that it represents the sl@0: * file offset for the first byte in the current input buffer. sl@0: */ sl@0: sl@0: if (HASUB (fp)) sl@0: { sl@0: n = fp->_up - fp->_bf._base; sl@0: curoff -= n; sl@0: n += fp->_ur; sl@0: } sl@0: else sl@0: { sl@0: n = fp->_p - fp->_bf._base; sl@0: curoff -= n; sl@0: n += fp->_r; sl@0: } sl@0: sl@0: /* sl@0: * If the target offset is within the current buffer, sl@0: * simply adjust the pointers, clear EOF, undo ungetc(), sl@0: * and return. (If the buffer was modified, we have to sl@0: * skip this; see fgetline.c.) sl@0: */ sl@0: sl@0: if ((fp->_flags & __SMOD) == 0 && sl@0: target >= curoff && target < (fpos_t)(curoff + n)) sl@0: { sl@0: register int o = target - curoff; sl@0: sl@0: fp->_p = fp->_bf._base + o; sl@0: fp->_r = n - o; sl@0: if (HASUB (fp)) sl@0: FREEUB (fp); sl@0: fp->_flags &= ~__SEOF; sl@0: return 0; sl@0: } sl@0: sl@0: /* sl@0: * The place we want to get to is not within the current buffer, sl@0: * but we can still be kind to the kernel copyout mechanism. sl@0: * By aligning the file offset to a block boundary, we can let sl@0: * the kernel use the VM hardware to map pages instead of sl@0: * copying bytes laboriously. Using a block boundary also sl@0: * ensures that we only read one block, rather than two. sl@0: */ sl@0: sl@0: curoff = target & ~(fp->_blksize - 1); sl@0: if ((*seekfn) (fp->_cookie, curoff, SEEK_SET) == POS_ERR) sl@0: goto dumb; sl@0: fp->_r = 0; sl@0: if (HASUB (fp)) sl@0: FREEUB (fp); sl@0: fp->_flags &= ~__SEOF; sl@0: n = target - curoff; sl@0: if (n) sl@0: { sl@0: if (__srefill (fp) || fp->_r < (fpos_t)n) sl@0: goto dumb; sl@0: fp->_p += n; sl@0: fp->_r -= n; sl@0: } sl@0: return 0; sl@0: sl@0: /* sl@0: * We get here if we cannot optimise the seek ... just sl@0: * do it. Allow the seek function to change fp->_bf._base. sl@0: */ sl@0: sl@0: dumb: sl@0: if (fflush (fp) || (*seekfn) (fp->_cookie, offset, whence) == POS_ERR) sl@0: return EOF; sl@0: /* success: clear EOF indicator and discard ungetc() data */ sl@0: if (HASUB (fp)) sl@0: FREEUB (fp); sl@0: fp->_p = fp->_bf._base; sl@0: fp->_r = 0; sl@0: /* fp->_w = 0; *//* unnecessary (I think...) */ sl@0: fp->_flags &= ~__SEOF; sl@0: return 0; sl@0: }