#include <sm/gen.h>
SM_RCSID("@(#)$Id: fseek.c,v 1.47 2005/06/14 23:07:20 ca Exp $")
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <setjmp.h>
#include <sm/time.h>
#include <sm/signal.h>
#include <sm/io.h>
#include <sm/assert.h>
#include <sm/clock.h>
#include "local.h"
#define POS_ERR (-(off_t)1)
static void seekalrm __P((int));
static jmp_buf SeekTimeOut;
static void
seekalrm(sig)
int sig;
{
longjmp(SeekTimeOut, 1);
}
int
sm_io_seek(fp, timeout, offset, whence)
register SM_FILE_T *fp;
int SM_NONVOLATILE timeout;
long SM_NONVOLATILE offset;
int SM_NONVOLATILE whence;
{
bool havepos;
off_t target, curoff;
size_t n;
struct stat st;
int ret;
SM_EVENT *evt = NULL;
register off_t (*seekfn) __P((SM_FILE_T *, off_t, int));
SM_REQUIRE_ISA(fp, SmFileMagic);
if (!Sm_IO_DidInit)
sm_init();
if ((seekfn = fp->f_seek) == NULL)
{
errno = ESPIPE;
return -1;
}
if (timeout == SM_TIME_DEFAULT)
timeout = fp->f_timeout;
if (timeout == SM_TIME_IMMEDIATE)
{
errno = EAGAIN;
return -1;
}
#define SM_SET_ALARM() \
if (timeout != SM_TIME_FOREVER) \
{ \
if (setjmp(SeekTimeOut) != 0) \
{ \
errno = EAGAIN; \
return -1; \
} \
evt = sm_seteventm(timeout, seekalrm, 0); \
}
switch (whence)
{
case SM_IO_SEEK_CUR:
sm_flush(fp, (int *) &timeout);
SM_SET_ALARM();
if (fp->f_flags & SMOFF)
curoff = fp->f_lseekoff;
else
{
curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR);
if (curoff == -1L)
{
ret = -1;
goto clean;
}
}
if (fp->f_flags & SMRD)
{
curoff -= fp->f_r;
if (HASUB(fp))
curoff -= fp->f_ur;
}
else if (fp->f_flags & SMWR && fp->f_p != NULL)
curoff += fp->f_p - fp->f_bf.smb_base;
offset += curoff;
whence = SM_IO_SEEK_SET;
havepos = true;
break;
case SM_IO_SEEK_SET:
case SM_IO_SEEK_END:
SM_SET_ALARM();
curoff = 0;
havepos = false;
break;
default:
errno = EINVAL;
return -1;
}
if (fp->f_bf.smb_base == NULL)
sm_makebuf(fp);
if (fp->f_flags & (SMWR | SMRW | SMNBF | SMNPT))
goto dumb;
if ((fp->f_flags & SMOPT) == 0)
{
if (seekfn != sm_stdseek ||
fp->f_file < 0 || fstat(fp->f_file, &st) ||
(st.st_mode & S_IFMT) != S_IFREG)
{
fp->f_flags |= SMNPT;
goto dumb;
}
fp->f_blksize = st.st_blksize;
fp->f_flags |= SMOPT;
}
if (whence == SM_IO_SEEK_SET)
target = offset;
else
{
if (fstat(fp->f_file, &st))
goto dumb;
target = st.st_size + offset;
}
if (!havepos)
{
if (fp->f_flags & SMOFF)
curoff = fp->f_lseekoff;
else
{
curoff = (*seekfn)(fp, (off_t) 0, SM_IO_SEEK_CUR);
if (curoff == POS_ERR)
goto dumb;
}
curoff -= fp->f_r;
if (HASUB(fp))
curoff -= fp->f_ur;
}
if (HASUB(fp))
{
curoff += fp->f_r;
n = fp->f_up - fp->f_bf.smb_base;
curoff -= n;
n += fp->f_ur;
}
else
{
n = fp->f_p - fp->f_bf.smb_base;
curoff -= n;
n += fp->f_r;
}
if (target >= curoff && target < curoff + (off_t) n)
{
register int o = target - curoff;
fp->f_p = fp->f_bf.smb_base + o;
fp->f_r = n - o;
if (HASUB(fp))
FREEUB(fp);
fp->f_flags &= ~SMFEOF;
ret = 0;
goto clean;
}
curoff = target & ~(fp->f_blksize - 1);
if ((*seekfn)(fp, curoff, SM_IO_SEEK_SET) == POS_ERR)
goto dumb;
fp->f_r = 0;
fp->f_p = fp->f_bf.smb_base;
if (HASUB(fp))
FREEUB(fp);
fp->f_flags &= ~SMFEOF;
n = target - curoff;
if (n)
{
if (sm_refill(fp, SM_TIME_FOREVER) || fp->f_r < (int) n)
goto dumb;
fp->f_p += n;
fp->f_r -= n;
}
ret = 0;
clean:
if (evt != NULL)
sm_clrevent(evt);
return ret;
dumb:
ret = SM_TIME_FOREVER;
if (sm_flush(fp, &ret) != 0 ||
(*seekfn)(fp, (off_t) offset, whence) == POS_ERR)
{
ret = -1;
goto clean;
}
if (HASUB(fp))
FREEUB(fp);
fp->f_p = fp->f_bf.smb_base;
fp->f_r = 0;
fp->f_flags &= ~SMFEOF;
ret = 0;
goto clean;
}