#include "sfdchdr.h"
#define UNSEEKABLE 1
typedef struct _file_s
{ Sfio_t* f;
Sfoff_t lower;
} File_t;
typedef struct _union_s
{
Sfdisc_t disc;
short type;
short c;
short n;
Sfoff_t here;
File_t f[1];
} Union_t;
#if __STD_C
static ssize_t unwrite(Sfio_t* f, const Void_t* buf, size_t n, Sfdisc_t* disc)
#else
static ssize_t unwrite(f, buf, n, disc)
Sfio_t* f;
Void_t* buf;
size_t n;
Sfdisc_t* disc;
#endif
{
return -1;
}
#if __STD_C
static ssize_t unread(Sfio_t* f, Void_t* buf, size_t n, Sfdisc_t* disc)
#else
static ssize_t unread(f, buf, n, disc)
Sfio_t* f;
Void_t* buf;
size_t n;
Sfdisc_t* disc;
#endif
{
reg Union_t* un;
reg ssize_t r, m;
un = (Union_t*)disc;
m = n;
f = un->f[un->c].f;
while(1)
{ if((r = sfread(f,buf,m)) < 0 || (r == 0 && un->c == un->n-1) )
break;
m -= r;
un->here += r;
if(m == 0)
break;
buf = (char*)buf + r;
if(sfeof(f) && un->c < un->n-1)
f = un->f[un->c += 1].f;
}
return n-m;
}
#if __STD_C
static Sfoff_t unseek(Sfio_t* f, Sfoff_t addr, int type, Sfdisc_t* disc)
#else
static Sfoff_t unseek(f, addr, type, disc)
Sfio_t* f;
Sfoff_t addr;
int type;
Sfdisc_t* disc;
#endif
{
reg Union_t* un;
reg int i;
reg Sfoff_t extent, s;
un = (Union_t*)disc;
if(un->type&UNSEEKABLE)
return -1L;
if(type == 2)
{ extent = 0;
for(i = 0; i < un->n; ++i)
extent += (sfsize(un->f[i].f) - un->f[i].lower);
addr += extent;
}
else if(type == 1)
addr += un->here;
if(addr < 0)
return -1;
extent = 0;
for(i = 0; i < un->n-1; ++i)
{ s = sfsize(un->f[i].f) - un->f[i].lower;
if(addr < extent + s)
break;
extent += s;
}
s = (addr-extent) + un->f[i].lower;
if(sfseek(un->f[i].f,s,0) != s)
return -1;
un->c = i;
un->here = addr;
for(i += 1; i < un->n; ++i)
sfseek(un->f[i].f,un->f[i].lower,0);
return addr;
}
#if __STD_C
static int unexcept(Sfio_t* f, int type, Void_t* data, Sfdisc_t* disc)
#else
static int unexcept(f,type,data,disc)
Sfio_t* f;
int type;
Void_t* data;
Sfdisc_t* disc;
#endif
{
if(type == SF_FINAL || type == SF_DPOP)
free(disc);
return 0;
}
#if __STD_C
int sfdcunion(Sfio_t* f, Sfio_t** array, int n)
#else
int sfdcunion(f, array, n)
Sfio_t* f;
Sfio_t** array;
int n;
#endif
{
reg Union_t* un;
reg int i;
if(n <= 0)
return -1;
if(!(un = (Union_t*)malloc(sizeof(Union_t)+(n-1)*sizeof(File_t))) )
return -1;
memset(un, 0, sizeof(*un));
un->disc.readf = unread;
un->disc.writef = unwrite;
un->disc.seekf = unseek;
un->disc.exceptf = unexcept;
un->n = n;
for(i = 0; i < n; ++i)
{ un->f[i].f = array[i];
if(!(un->type&UNSEEKABLE))
{ un->f[i].lower = sfseek(array[i],(Sfoff_t)0,1);
if(un->f[i].lower < 0)
un->type |= UNSEEKABLE;
}
}
if(sfdisc(f,(Sfdisc_t*)un) != (Sfdisc_t*)un)
{ free(un);
return -1;
}
return 0;
}