V10/cmd/odist/pax/ship/shipop.c
/*
* Glenn Fowler
* AT&T Bell Laboratories
*
* ship support -- a single file for easy bootstrap
*
* shipop newer [ reference [ file ... ] ]
*
* true if reference file is newer than file ...
* false if any of the files do not exist
*
* shipop seal [ file ... ]
*
* generate 32 bit multiplicative congruential PRG checksum for files
*
* shipop state reference [ file ... | <files ]
*
* generate mam state file
*
* shipop time [ file ]
*
* generate seconds-since-epoch time in hex for today [ file ]
*
* shipop xap [ file ... ]
*
* restore pax self-delta files extracted by non-pax cpio
*/
#if !lint
static char id[] = "\n@(#)shipop (AT&T Bell Laboratories) 02/11/91\0\n";
#endif
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#define elements(x) (sizeof(x)/sizeof(x[0]))
struct optab
{
char* name;
int (*func)();
};
static char* cmd = "shipop";
static int shipnewer();
static int shipseal();
static int shipstate();
static int shiptime();
static int shipxap();
static struct optab ops[] =
{
"newer", shipnewer,
"seal", shipseal,
"state", shipstate,
"time", shiptime,
"xap", shipxap,
};
static char* op;
static int status;
static void error();
main(argc, argv)
int argc;
char** argv;
{
register char* s;
register int i;
if (!(s = *++argv))
exit(0);
for (i = 0; i < elements(ops); i++)
if (!strcmp(s, ops[i].name))
{
op = ops[i].name;
exit((*ops[i].func)(argv + 1));
}
fprintf(stderr, "Usage: %s %s", cmd, ops[0].name);
for (i = 1; i < elements(ops); i++)
fprintf(stderr, " | %s", ops[i].name);
fprintf(stderr, " [ file ... ]\n");
exit(2);
}
static void
error(level, file, msg)
char* file;
char* msg;
{
fprintf(stderr, "%s: %s", cmd, op);
if (level == 1) fprintf(stderr, ": warning");
if (file) fprintf(stderr, ": %s", file);
if (msg) fprintf(stderr, ": %s", msg);
fprintf(stderr, "\n");
if (level > status)
{
if (level > 2) exit(level - 2);
status = level;
}
}
/*
* shipop newer [ reference [ file ... ] ]
*/
static int
shipnewer(argv)
register char** argv;
{
unsigned long ref;
struct stat st;
if (!*argv || stat(*argv, &st)) return(1);
ref = (unsigned long)st.st_mtime;
while (*++argv)
if (stat(*argv, &st) || ref < (unsigned long)st.st_mtime) return(1);
return(0);
}
/*
* shipop seal [ file ... ]
*/
#define HASHPART(h,c) (h = (h) * 987654321L + 123456879L + (c))
static char buf[4096];
static unsigned long sealfile();
static int
shipseal(argv)
register char** argv;
{
register char* f;
register int fd;
unsigned long s;
s = 0;
if (!*argv) s = sealfile("/dev/stdin", 0, s);
else while (f = *argv++)
{
if (*f == '-' && !*(f + 1)) s = sealfile("/dev/stdin", 0, s);
else if ((fd = open(f, 0)) < 0) error(2, f, "cannot read");
else
{
s = sealfile(f, fd, s);
(void)close(fd);
}
}
printf("%08x\n", s);
return(status);
}
static unsigned long
sealfile(file, fd, s)
char* file;
int fd;
unsigned long s;
{
register unsigned char* b;
register unsigned char* e;
register int n;
HASHPART(s, 0);
while ((n = read(fd, buf, sizeof(buf))) > 0)
{
b = (unsigned char*)buf;
e = b + n;
while (b < e) HASHPART(s, *b++);
}
if (n < 0) error(2, file, "read error");
return(s);
}
/*
* shipop state reference [ file ... | <file-list ]
*/
static int
shipstate(argv)
register char** argv;
{
register char* s;
register int c;
long ref;
struct stat st;
if (!(s = *argv++) || stat(s, &st)) error(3, (char*)0, "reference file omitted");
ref = (long)st.st_mtime;
if (s = *argv++) do
{
if (!stat(s, &st))
printf("%s %ld\n", s, (long)st.st_mtime - ref);
} while (s = *argv++);
else do
{
s = buf;
while ((c = getchar()) != EOF && c != ' ' && c != '\n')
if (s < buf + sizeof(buf) - 1) *s++ = c;
if (s > buf)
{
*s = 0;
if (!stat(buf, &st))
printf("%s %ld\n", buf, (long)st.st_mtime - ref);
}
} while (c != EOF);
return(status);
}
/*
* shipop time [ file ]
*/
static int
shiptime(argv)
register char** argv;
{
struct stat st;
time_t date;
extern time_t time();
if (*argv && !stat(*argv, &st)) date = st.st_mtime;
else time(&date);
printf("%08x\n", (long)date);
return(status);
}
/*
* shipop xap [ file ... ]
*/
#define XAPHEADER 1024
#define round(x) (((x)+(XAPHEADER*8)-1)&~((XAPHEADER*8)-1))
static void xapfile();
static int isdelta();
extern char* malloc();
/* ----- enough of sfio and delta/update to do update() ----- */
#define reg register
#define NIL(t) ((t)0)
typedef struct
{
unsigned char* buf;
unsigned char* nxt;
unsigned char* end;
long siz;
} Sfile_t;
#define sfgetc(f) ((f)->nxt<(f)->end?*(f)->nxt++:(-1))
#define sfgetu(f) ((_Sfi = sfgetc(f)) < 0 ? -1 : \
((_Sfi&SF_MORE) ? _sfgetu(f) : (unsigned long)_Sfi))
#define sfread(f,b,n) (((f)->nxt+=(n))<=(f)->end?(n):(-1))
#define sfseek(f,o,w) ((f)->nxt=(f)->buf+(o),(o))
#define sfsync(f)
#define SFUVALUE(v) ((v)&(SF_MORE-1))
#define SF_UBITS 7
#define SF_MORE (1<<SF_UBITS)
static long _Sfi;
static unsigned long
_sfgetu(f)
Sfile_t* f;
{
register int c;
register unsigned long v;
v = SFUVALUE(_Sfi);
do
{
if ((c = sfgetc(f)) < 0) return(-1);
v = (v << SF_UBITS) | SFUVALUE(c);
} while (c & SF_MORE);
return(v);
}
static int
sfmove(fr, fw, n)
Sfile_t* fr;
Sfile_t* fw;
register int n;
{
register char* r;
register char* w;
register char* e;
r = (char*)fr->nxt;
w = (char*)fw->nxt;
e = w + n;
while (w < e) *w++ = *r++;
fr->nxt = (unsigned char*)r;
fw->nxt = (unsigned char*)w;
return(n);
}
#define CODE_BIT 8
#define DELTA_ADD (0)
#define DELTA_MOVE (1<<(CODE_BIT-1))
#define M_MIN 4
#define C_BITS (3)
#define C_SIZE ((1<<C_BITS)-3)
#define A_SIZE (1 << CODE_BIT)
#define C_ADDRS (C_SIZE)
#define C_CADDR (C_SIZE+1)
#define C_RADDR (C_SIZE+2)
#define C_SIZEOF(addr,indx) (indx == C_ADDRS ? 1 : sfulen(addr))
#define C_INIT(c,cache,addrs) \
{ cache[c=0] = 128; while((c += 1) < C_SIZE) cache[c] = (c+1)*128; \
addrs[c=0] = 256; while((c += 1) < A_SIZE) addrs[c] = c+256; \
}
#define C_SET(real,indx,cache,c,addrs) \
{ if((c += 1) >= C_SIZE) c = 0; cache[c] = real; \
if(indx != C_ADDRS) addrs[real&(A_SIZE-1)] = real; \
}
#define C_REAL(real,addr,indx,caddr,cache,addrs) \
{ real = indx == C_RADDR ? addr : \
indx == C_ADDRS ? addrs[addr] : \
indx == C_CADDR ? caddr-addr : addr+cache[indx]; \
}
#define A_BITS (CODE_BIT-3)
#define A_HERE(i) ((i) & ((1<<A_BITS)-2))
#define A_LOCAL ((1<<A_BITS)-1)
#define A_read(f,i,s) ((s) = A_HERE(i) ? ((i)&A_LOCAL)-1 : sfgetu(f)+A_LOCAL )
#define M_BITS (CODE_BIT-(C_BITS+1))
#define M_LOCAL ((1<<M_BITS) + M_MIN-1)
#define M_HERE(i) ((i) & ((1<<M_BITS)-1))
#define M_sread(f,i,s) ((s) = ((s) = M_HERE(i)) ? \
(s)+(M_MIN-1) : sfgetu(f)+M_LOCAL )
#define M_cread(i,c) ((c) = ((i)>>M_BITS) & ((1<<C_BITS)-1) )
#define M_aread(f,c,a) ((a) = (c) == C_ADDRS ? sfgetc(f) : sfgetu(f) )
#define M_READ(f,i,a,c,s) \
((M_cread(i,c)<0 || M_aread(f,c,a)<0 || M_sread(f,i,s)<0) ? -1 : 0)
#define T_MBITS (CODE_BIT - (A_BITS+1))
#define T_ABITS (CODE_BIT - (T_MBITS+C_BITS+1))
#define T_TINY(i) ((i) & (((1<<T_MBITS)-1) << (C_BITS+T_ABITS)) )
#define T_MREAD(f,i,a,c,s) \
(((c) = ((i)>>T_ABITS) & ((1<<C_BITS)-1)), \
((s) = (((i)>>(C_BITS+T_ABITS))&((1<<T_MBITS)-1)) + M_MIN-1), \
M_aread(f,c,a) )
#define T_AREAD(i,s) ((s) = ((i) & ((1<<T_ABITS)-1)) + 1)
#define A_READ(f,i,s) (T_TINY(i) ? T_AREAD(i,s) : A_read(f,i,s))
/* now the untouched libdelta/update.c */
/*
** Reconstruct a target file from a source file and a delta file.
** The delta file contain block move instructions computed by delta().
**
** Written by Kiem-Phong Vo, 03/27/90
*/
static long supdate(fsrc,fdel,wtar,rtar,sbase,tbase)
Sfile_t *fsrc; /* source file */
Sfile_t *fdel; /* delta instruction file */
Sfile_t *wtar; /* target file for writing */
Sfile_t *rtar; /* target file for reading */
reg long *sbase, *tbase; /* current bases */
{
reg int inst;
reg int c;
reg long caddr;
long cache[C_SIZE], addrs[A_SIZE];
long n_src, n_tar;
#ifdef DEBUG
int n_inst = 0;
#endif
if((n_src = sfgetu(fdel)) < 0 || (n_tar = sfgetu(fdel)) < 0)
return -1;
C_INIT(c,cache,addrs);
caddr = n_src;
while((inst = sfgetc(fdel)) >= 0)
{
reg long addr, real, size;
reg int indx;
#ifdef DEBUG
n_inst++;
#endif
if(inst == 0)
{
if(sbase)
*sbase += n_src;
if(tbase)
*tbase += n_tar;
return caddr == (n_tar+n_src) ? n_tar : -1L;
}
if((inst&DELTA_MOVE) == 0)
{ /* do add */
if(A_READ(fdel,inst,size) < 0)
return -1L;
if(sfmove(fdel,wtar,size) < 0)
return -1L;
caddr += size;
if(T_TINY(inst))
{ /* read the merged move instruction */
if(T_MREAD(fdel,inst,addr,indx,size) < 0)
return -1;
goto do_move;
}
}
else
{ /* move instruction */
if(M_READ(fdel,inst,addr,indx,size) < 0)
return -1;
do_move:
C_REAL(real,addr,indx,caddr,cache,addrs);
C_SET(real,indx,cache,c,addrs);
if(real >= n_src || !sbase)
{ /* self-move */
sfsync(wtar);
real = (real-n_src) + *tbase;
if(sfseek(rtar,real,0) < 0)
return -1L;
if(sfmove(rtar,wtar,size) < 0)
return -1L;
}
else
{ /* source-move */
if(sbase)
real += *sbase;
if(sfseek(fsrc,real,0) < 0)
return -1L;
if(sfmove(fsrc,wtar,size) < 0)
return -1L;
}
caddr += size;
}
}
/* should never get here */
return -1L;
}
long update(fsrc,fdel,wtar,rtar)
Sfile_t *fsrc; /* source stream */
Sfile_t *fdel; /* delta stream */
Sfile_t *wtar; /* write stream for target file */
Sfile_t *rtar; /* read stream for target file */
{
reg long nsrc, ntar, header, t, tar;
char magic[4];
long sbase, tbase;
extern long supdate();
if(sfread(fdel,magic,4) != 4 || sfgetc(fdel) < 0)
return -1L;
if((nsrc = sfgetu(fdel)) < 0 || (ntar = sfgetu(fdel)) < 0 ||
(header = sfgetu(fdel)) < 0)
return -1L;
/* ----- cheat here to setup wtar and rtar ----- */
if (wtar->siz < ntar)
{
if (wtar->buf) free(wtar->buf);
wtar->siz = round(ntar);
if (!(wtar->buf = (unsigned char*)malloc(wtar->siz)))
error(3, (char*)0, "out of space");
rtar->siz = wtar->siz;
}
rtar->nxt = rtar->buf = wtar->nxt = wtar->buf;
/* ----- */
for(tbase = sbase = tar = 0; ;)
{ /* process all windows */
if(header > 0)
t = supdate(fsrc,fdel,wtar,rtar,NIL(long*),&tbase);
else t = supdate(fsrc,fdel,wtar,rtar,&sbase,&tbase);
if(t >= 0)
{
if((tar += t) == ntar)
break;
else if(tar < ntar)
continue;
}
/* error happened */
ntar = -1L;
break;
}
return ntar;
}
/* ----- */
static int
shipxap(argv)
char** argv;
{
char* file;
umask(0);
while (file = *argv++) xapfile(file);
return(status);
}
static void
xapfile(file)
char* file;
{
int fd;
long n;
int m;
struct stat st;
static Sfile_t fdel;
static Sfile_t rtar;
static Sfile_t wtar;
if ((fd = open(file, 0)) < 0)
{
error(2, file, "cannot read");
return;
}
if (fstat(fd, &st))
{
error(2, file, "cannot stat");
close(fd);
return;
}
n = st.st_size;
if (n > fdel.siz)
{
if (fdel.buf) free(fdel.buf);
fdel.siz = round(n);
if (!(fdel.buf = (unsigned char*)malloc(fdel.siz)))
error(3, file, "out of space");
}
m = n > XAPHEADER ? XAPHEADER : n;
if (read(fd, fdel.buf, m) != m)
{
error(2, file, "cannot read header");
close(fd);
return;
}
fdel.nxt = fdel.buf;
fdel.end = fdel.buf + m;
if (!isdelta(&fdel))
{
close(fd);
return;
}
if ((n -= m) > 0)
{
if (read(fd, fdel.buf + m, n) != n)
{
error(2, file, "cannot read");
close(fd);
return;
}
fdel.end += n;
}
close(fd);
if ((n = update((Sfile_t*)0, &fdel, &wtar, &rtar)) < 0)
{
error(2, file, "cannot update");
return;
}
if ((fd = creat(file, st.st_mode&0777)) < 0)
{
if (chmod(file, st.st_mode | 0200) || (fd = creat(file, st.st_mode&0777)) < 0)
{
error(2, file, "cannot open for writing");
return;
}
chmod(file, st.st_mode);
}
if (write(fd, wtar.buf, n) != n)
error(2, file, "cannot write");
close(fd);
}
static int
isdelta(f)
register Sfile_t* f;
{
register int b;
register int c;
if (sfgetc(f) != 'c' || sfgetc(f) != 'A') return(0);
b = 0;
for (;;)
{
if ((c = sfgetc(f)) < 0) return(0);
if (c) b = 0;
else
{
if (b) return(1);
b = 1;
}
}
}