[comp.sources.unix] v15i100: Perl, version 2, Part11/15

rsalz@bbn.com (Rich Salz) (07/13/88)

Submitted-by: Larry Wall <lwall@jpl-devvax.jpl.nasa.gov>
Posting-number: Volume 15, Issue 100
Archive-name: perl2/part11

#! /bin/sh
# Make a new directory for the perl sources, cd to it, and run kits 1
# thru 15 through sh.  When all 15 kits have been run, read README.

echo "This is perl 2.0 kit 11 (of 15).  If kit 11 is complete, the line"
echo '"'"End of kit 11 (of 15)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
mkdir t x2p 2>/dev/null
echo Extracting x2p/str.c
sed >x2p/str.c <<'!STUFFY!FUNK!' -e 's/X//'
X/* $Header: str.c,v 2.0 88/06/05 00:16:02 root Exp $
X *
X * $Log:	str.c,v $
X * Revision 2.0  88/06/05  00:16:02  root
X * Baseline version 2.0.
X * 
X */
X
X#include "handy.h"
X#include "EXTERN.h"
X#include "util.h"
X#include "a2p.h"
X
Xstr_numset(str,num)
Xregister STR *str;
Xdouble num;
X{
X    str->str_nval = num;
X    str->str_pok = 0;		/* invalidate pointer */
X    str->str_nok = 1;		/* validate number */
X}
X
Xchar *
Xstr_2ptr(str)
Xregister STR *str;
X{
X    register char *s;
X
X    if (!str)
X	return "";
X    GROWSTR(&(str->str_ptr), &(str->str_len), 24);
X    s = str->str_ptr;
X    if (str->str_nok) {
X	sprintf(s,"%.20g",str->str_nval);
X	while (*s) s++;
X    }
X    *s = '\0';
X    str->str_cur = s - str->str_ptr;
X    str->str_pok = 1;
X#ifdef DEBUGGING
X    if (debug & 32)
X	fprintf(stderr,"0x%lx ptr(%s)\n",str,str->str_ptr);
X#endif
X    return str->str_ptr;
X}
X
Xdouble
Xstr_2num(str)
Xregister STR *str;
X{
X    if (!str)
X	return 0.0;
X    if (str->str_len && str->str_pok)
X	str->str_nval = atof(str->str_ptr);
X    else
X	str->str_nval = 0.0;
X    str->str_nok = 1;
X#ifdef DEBUGGING
X    if (debug & 32)
X	fprintf(stderr,"0x%lx num(%g)\n",str,str->str_nval);
X#endif
X    return str->str_nval;
X}
X
Xstr_sset(dstr,sstr)
XSTR *dstr;
Xregister STR *sstr;
X{
X    if (!sstr)
X	str_nset(dstr,No,0);
X    else if (sstr->str_nok)
X	str_numset(dstr,sstr->str_nval);
X    else if (sstr->str_pok)
X	str_nset(dstr,sstr->str_ptr,sstr->str_cur);
X    else
X	str_nset(dstr,"",0);
X}
X
Xstr_nset(str,ptr,len)
Xregister STR *str;
Xregister char *ptr;
Xregister int len;
X{
X    GROWSTR(&(str->str_ptr), &(str->str_len), len + 1);
X    bcopy(ptr,str->str_ptr,len);
X    str->str_cur = len;
X    *(str->str_ptr+str->str_cur) = '\0';
X    str->str_nok = 0;		/* invalidate number */
X    str->str_pok = 1;		/* validate pointer */
X}
X
Xstr_set(str,ptr)
Xregister STR *str;
Xregister char *ptr;
X{
X    register int len;
X
X    if (!ptr)
X	ptr = "";
X    len = strlen(ptr);
X    GROWSTR(&(str->str_ptr), &(str->str_len), len + 1);
X    bcopy(ptr,str->str_ptr,len+1);
X    str->str_cur = len;
X    str->str_nok = 0;		/* invalidate number */
X    str->str_pok = 1;		/* validate pointer */
X}
X
Xstr_chop(str,ptr)	/* like set but assuming ptr is in str */
Xregister STR *str;
Xregister char *ptr;
X{
X    if (!(str->str_pok))
X	str_2ptr(str);
X    str->str_cur -= (ptr - str->str_ptr);
X    bcopy(ptr,str->str_ptr, str->str_cur + 1);
X    str->str_nok = 0;		/* invalidate number */
X    str->str_pok = 1;		/* validate pointer */
X}
X
Xstr_ncat(str,ptr,len)
Xregister STR *str;
Xregister char *ptr;
Xregister int len;
X{
X    if (!(str->str_pok))
X	str_2ptr(str);
X    GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + len + 1);
X    bcopy(ptr,str->str_ptr+str->str_cur,len);
X    str->str_cur += len;
X    *(str->str_ptr+str->str_cur) = '\0';
X    str->str_nok = 0;		/* invalidate number */
X    str->str_pok = 1;		/* validate pointer */
X}
X
Xstr_scat(dstr,sstr)
XSTR *dstr;
Xregister STR *sstr;
X{
X    if (!(sstr->str_pok))
X	str_2ptr(sstr);
X    if (sstr)
X	str_ncat(dstr,sstr->str_ptr,sstr->str_cur);
X}
X
Xstr_cat(str,ptr)
Xregister STR *str;
Xregister char *ptr;
X{
X    register int len;
X
X    if (!ptr)
X	return;
X    if (!(str->str_pok))
X	str_2ptr(str);
X    len = strlen(ptr);
X    GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + len + 1);
X    bcopy(ptr,str->str_ptr+str->str_cur,len+1);
X    str->str_cur += len;
X    str->str_nok = 0;		/* invalidate number */
X    str->str_pok = 1;		/* validate pointer */
X}
X
Xchar *
Xstr_append_till(str,from,delim,keeplist)
Xregister STR *str;
Xregister char *from;
Xregister int delim;
Xchar *keeplist;
X{
X    register char *to;
X    register int len;
X
X    if (!from)
X	return Nullch;
X    len = strlen(from);
X    GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + len + 1);
X    str->str_nok = 0;		/* invalidate number */
X    str->str_pok = 1;		/* validate pointer */
X    to = str->str_ptr+str->str_cur;
X    for (; *from; from++,to++) {
X	if (*from == '\\' && from[1] && delim != '\\') {
X	    if (!keeplist) {
X		if (from[1] == delim || from[1] == '\\')
X		    from++;
X		else
X		    *to++ = *from++;
X	    }
X	    else if (index(keeplist,from[1]))
X		*to++ = *from++;
X	    else
X		from++;
X	}
X	else if (*from == delim)
X	    break;
X	*to = *from;
X    }
X    *to = '\0';
X    str->str_cur = to - str->str_ptr;
X    return from;
X}
X
XSTR *
Xstr_new(len)
Xint len;
X{
X    register STR *str;
X    
X    if (freestrroot) {
X	str = freestrroot;
X	freestrroot = str->str_link.str_next;
X    }
X    else {
X	str = (STR *) safemalloc(sizeof(STR));
X	bzero((char*)str,sizeof(STR));
X    }
X    if (len)
X	GROWSTR(&(str->str_ptr), &(str->str_len), len + 1);
X    return str;
X}
X
Xvoid
Xstr_grow(str,len)
Xregister STR *str;
Xint len;
X{
X    if (len && str)
X	GROWSTR(&(str->str_ptr), &(str->str_len), len + 1);
X}
X
X/* make str point to what nstr did */
X
Xvoid
Xstr_replace(str,nstr)
Xregister STR *str;
Xregister STR *nstr;
X{
X    safefree(str->str_ptr);
X    str->str_ptr = nstr->str_ptr;
X    str->str_len = nstr->str_len;
X    str->str_cur = nstr->str_cur;
X    str->str_pok = nstr->str_pok;
X    if (str->str_nok = nstr->str_nok)
X	str->str_nval = nstr->str_nval;
X    safefree((char*)nstr);
X}
X
Xvoid
Xstr_free(str)
Xregister STR *str;
X{
X    if (!str)
X	return;
X    if (str->str_len)
X	str->str_ptr[0] = '\0';
X    str->str_cur = 0;
X    str->str_nok = 0;
X    str->str_pok = 0;
X    str->str_link.str_next = freestrroot;
X    freestrroot = str;
X}
X
Xstr_len(str)
Xregister STR *str;
X{
X    if (!str)
X	return 0;
X    if (!(str->str_pok))
X	str_2ptr(str);
X    if (str->str_len)
X	return str->str_cur;
X    else
X	return 0;
X}
X
Xchar *
Xstr_gets(str,fp)
Xregister STR *str;
Xregister FILE *fp;
X{
X#ifdef STDSTDIO		/* Here is some breathtakingly efficient cheating */
X
X    register char *bp;		/* we're going to steal some values */
X    register int cnt;		/*  from the stdio struct and put EVERYTHING */
X    register STDCHAR *ptr;	/*   in the innermost loop into registers */
X    register char newline = '\n';	/* (assuming at least 6 registers) */
X    int i;
X    int bpx;
X
X    cnt = fp->_cnt;			/* get count into register */
X    str->str_nok = 0;			/* invalidate number */
X    str->str_pok = 1;			/* validate pointer */
X    if (str->str_len <= cnt)		/* make sure we have the room */
X	GROWSTR(&(str->str_ptr), &(str->str_len), cnt+1);
X    bp = str->str_ptr;			/* move these two too to registers */
X    ptr = fp->_ptr;
X    for (;;) {
X	while (--cnt >= 0) {
X	    if ((*bp++ = *ptr++) == newline)
X		if (bp <= str->str_ptr || bp[-2] != '\\')
X		    goto thats_all_folks;
X		else {
X		    line++;
X		    bp -= 2;
X		}
X	}
X	
X	fp->_cnt = cnt;			/* deregisterize cnt and ptr */
X	fp->_ptr = ptr;
X	i = _filbuf(fp);		/* get more characters */
X	cnt = fp->_cnt;
X	ptr = fp->_ptr;			/* reregisterize cnt and ptr */
X
X	bpx = bp - str->str_ptr;	/* prepare for possible relocation */
X	GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + cnt + 1);
X	bp = str->str_ptr + bpx;	/* reconstitute our pointer */
X
X	if (i == newline) {		/* all done for now? */
X	    *bp++ = i;
X	    goto thats_all_folks;
X	}
X	else if (i == EOF)		/* all done for ever? */
X	    goto thats_all_folks;
X	*bp++ = i;			/* now go back to screaming loop */
X    }
X
Xthats_all_folks:
X    fp->_cnt = cnt;			/* put these back or we're in trouble */
X    fp->_ptr = ptr;
X    *bp = '\0';
X    str->str_cur = bp - str->str_ptr;	/* set length */
X
X#else /* !STDSTDIO */	/* The big, slow, and stupid way */
X
X    static char buf[4192];
X
X    if (fgets(buf, sizeof buf, fp) != Nullch)
X	str_set(str, buf);
X    else
X	str_set(str, No);
X
X#endif /* STDSTDIO */
X
X    return str->str_cur ? str->str_ptr : Nullch;
X}
X
Xvoid
Xstr_inc(str)
Xregister STR *str;
X{
X    register char *d;
X
X    if (!str)
X	return;
X    if (str->str_nok) {
X	str->str_nval += 1.0;
X	str->str_pok = 0;
X	return;
X    }
X    if (!str->str_pok) {
X	str->str_nval = 1.0;
X	str->str_nok = 1;
X	return;
X    }
X    for (d = str->str_ptr; *d && *d != '.'; d++) ;
X    d--;
X    if (!isdigit(*str->str_ptr) || !isdigit(*d) ) {
X        str_numset(str,atof(str->str_ptr) + 1.0);  /* punt */
X	return;
X    }
X    while (d >= str->str_ptr) {
X	if (++*d <= '9')
X	    return;
X	*(d--) = '0';
X    }
X    /* oh,oh, the number grew */
X    GROWSTR(&(str->str_ptr), &(str->str_len), str->str_cur + 2);
X    str->str_cur++;
X    for (d = str->str_ptr + str->str_cur; d > str->str_ptr; d--)
X	*d = d[-1];
X    *d = '1';
X}
X
Xvoid
Xstr_dec(str)
Xregister STR *str;
X{
X    register char *d;
X
X    if (!str)
X	return;
X    if (str->str_nok) {
X	str->str_nval -= 1.0;
X	str->str_pok = 0;
X	return;
X    }
X    if (!str->str_pok) {
X	str->str_nval = -1.0;
X	str->str_nok = 1;
X	return;
X    }
X    for (d = str->str_ptr; *d && *d != '.'; d++) ;
X    d--;
X    if (!isdigit(*str->str_ptr) || !isdigit(*d) || (*d == '0' && d == str->str_ptr)) {
X        str_numset(str,atof(str->str_ptr) - 1.0);  /* punt */
X	return;
X    }
X    while (d >= str->str_ptr) {
X	if (--*d >= '0')
X	    return;
X	*(d--) = '9';
X    }
X}
X
X/* make a string that will exist for the duration of the expression eval */
X
XSTR *
Xstr_static(oldstr)
XSTR *oldstr;
X{
X    register STR *str = str_new(0);
X    static long tmps_size = -1;
X
X    str_sset(str,oldstr);
X    if (++tmps_max > tmps_size) {
X	tmps_size = tmps_max;
X	if (!(tmps_size & 127)) {
X	    if (tmps_size)
X		tmps_list = (STR**)saferealloc((char*)tmps_list,
X		    (tmps_size + 128) * sizeof(STR*) );
X	    else
X		tmps_list = (STR**)safemalloc(128 * sizeof(char*));
X	}
X    }
X    tmps_list[tmps_max] = str;
X    return str;
X}
X
XSTR *
Xstr_make(s)
Xchar *s;
X{
X    register STR *str = str_new(0);
X
X    str_set(str,s);
X    return str;
X}
X
XSTR *
Xstr_nmake(n)
Xdouble n;
X{
X    register STR *str = str_new(0);
X
X    str_numset(str,n);
X    return str;
X}
!STUFFY!FUNK!
echo Extracting malloc.c
sed >malloc.c <<'!STUFFY!FUNK!' -e 's/X//'
X/* $Header: malloc.c,v 2.0 88/06/05 00:09:16 root Exp $
X *
X * $Log:	malloc.c,v $
X * Revision 2.0  88/06/05  00:09:16  root
X * Baseline version 2.0.
X * 
X */
X
X#ifndef lint
Xstatic char sccsid[] = "@(#)malloc.c	4.3 (Berkeley) 9/16/83";
X#endif
X
X#define RCHECK
X/*
X * malloc.c (Caltech) 2/21/82
X * Chris Kingsley, kingsley@cit-20.
X *
X * This is a very fast storage allocator.  It allocates blocks of a small 
X * number of different sizes, and keeps free lists of each size.  Blocks that
X * don't exactly fit are passed up to the next larger size.  In this 
X * implementation, the available sizes are 2^n-4 (or 2^n-12) bytes long.
X * This is designed for use in a program that uses vast quantities of memory,
X * but bombs when it runs out. 
X */
X
X#include "EXTERN.h"
X#include "perl.h"
X
X/* I don't much care whether these are defined in sys/types.h--LAW */
X
X#define u_char unsigned char
X#define u_int unsigned int
X#define u_short unsigned short
X
X/*
X * The overhead on a block is at least 4 bytes.  When free, this space
X * contains a pointer to the next free block, and the bottom two bits must
X * be zero.  When in use, the first byte is set to MAGIC, and the second
X * byte is the size index.  The remaining bytes are for alignment.
X * If range checking is enabled and the size of the block fits
X * in two bytes, then the top two bytes hold the size of the requested block
X * plus the range checking words, and the header word MINUS ONE.
X */
Xunion	overhead {
X	union	overhead *ov_next;	/* when free */
X	struct {
X		u_char	ovu_magic;	/* magic number */
X		u_char	ovu_index;	/* bucket # */
X#ifdef RCHECK
X		u_short	ovu_size;	/* actual block size */
X		u_int	ovu_rmagic;	/* range magic number */
X#endif
X	} ovu;
X#define	ov_magic	ovu.ovu_magic
X#define	ov_index	ovu.ovu_index
X#define	ov_size		ovu.ovu_size
X#define	ov_rmagic	ovu.ovu_rmagic
X};
X
X#define	MAGIC		0xff		/* magic # on accounting info */
X#define OLDMAGIC	0x7f		/* same after a free() */
X#define RMAGIC		0x55555555	/* magic # on range info */
X#ifdef RCHECK
X#define	RSLOP		sizeof (u_int)
X#else
X#define	RSLOP		0
X#endif
X
X/*
X * nextf[i] is the pointer to the next free block of size 2^(i+3).  The
X * smallest allocatable block is 8 bytes.  The overhead information
X * precedes the data area returned to the user.
X */
X#define	NBUCKETS 30
Xstatic	union overhead *nextf[NBUCKETS];
Xextern	char *sbrk();
X
X#ifdef MSTATS
X/*
X * nmalloc[i] is the difference between the number of mallocs and frees
X * for a given block size.
X */
Xstatic	u_int nmalloc[NBUCKETS];
X#include <stdio.h>
X#endif
X
X#ifdef debug
X#define	ASSERT(p)   if (!(p)) botch("p"); else
Xstatic
Xbotch(s)
X	char *s;
X{
X
X	printf("assertion botched: %s\n", s);
X	abort();
X}
X#else
X#define	ASSERT(p)
X#endif
X
Xchar *
Xmalloc(nbytes)
X	register unsigned nbytes;
X{
X  	register union overhead *p;
X  	register int bucket = 0;
X  	register unsigned shiftr;
X
X	/*
X	 * Convert amount of memory requested into
X	 * closest block size stored in hash buckets
X	 * which satisfies request.  Account for
X	 * space used per block for accounting.
X	 */
X  	nbytes += sizeof (union overhead) + RSLOP;
X  	nbytes = (nbytes + 3) &~ 3; 
X  	shiftr = (nbytes - 1) >> 2;
X	/* apart from this loop, this is O(1) */
X  	while (shiftr >>= 1)
X  		bucket++;
X	/*
X	 * If nothing in hash bucket right now,
X	 * request more memory from the system.
X	 */
X  	if (nextf[bucket] == NULL)    
X  		morecore(bucket);
X  	if ((p = (union overhead *)nextf[bucket]) == NULL)
X  		return (NULL);
X	/* remove from linked list */
X	if (*((int*)p) > 0x10000000)
X	    fprintf(stderr,"Corrupt malloc ptr 0x%x at 0x%x\n",*((int*)p),p);
X  	nextf[bucket] = nextf[bucket]->ov_next;
X	p->ov_magic = MAGIC;
X	p->ov_index= bucket;
X#ifdef MSTATS
X  	nmalloc[bucket]++;
X#endif
X#ifdef RCHECK
X	/*
X	 * Record allocated size of block and
X	 * bound space with magic numbers.
X	 */
X  	if (nbytes <= 0x10000)
X		p->ov_size = nbytes - 1;
X	p->ov_rmagic = RMAGIC;
X  	*((u_int *)((caddr_t)p + nbytes - RSLOP)) = RMAGIC;
X#endif
X  	return ((char *)(p + 1));
X}
X
X/*
X * Allocate more memory to the indicated bucket.
X */
Xstatic
Xmorecore(bucket)
X	register bucket;
X{
X  	register union overhead *op;
X  	register int rnu;       /* 2^rnu bytes will be requested */
X  	register int nblks;     /* become nblks blocks of the desired size */
X	register int siz;
X
X  	if (nextf[bucket])
X  		return;
X	/*
X	 * Insure memory is allocated
X	 * on a page boundary.  Should
X	 * make getpageize call?
X	 */
X  	op = (union overhead *)sbrk(0);
X  	if ((int)op & 0x3ff)
X  		sbrk(1024 - ((int)op & 0x3ff));
X	/* take 2k unless the block is bigger than that */
X  	rnu = (bucket <= 8) ? 11 : bucket + 3;
X  	nblks = 1 << (rnu - (bucket + 3));  /* how many blocks to get */
X  	if (rnu < bucket)
X		rnu = bucket;
X	op = (union overhead *)sbrk(1 << rnu);
X	/* no more room! */
X  	if ((int)op == -1)
X  		return;
X	/*
X	 * Round up to minimum allocation size boundary
X	 * and deduct from block count to reflect.
X	 */
X  	if ((int)op & 7) {
X  		op = (union overhead *)(((int)op + 8) &~ 7);
X  		nblks--;
X  	}
X	/*
X	 * Add new memory allocated to that on
X	 * free list for this hash bucket.
X	 */
X  	nextf[bucket] = op;
X  	siz = 1 << (bucket + 3);
X  	while (--nblks > 0) {
X		op->ov_next = (union overhead *)((caddr_t)op + siz);
X		op = (union overhead *)((caddr_t)op + siz);
X  	}
X}
X
Xfree(cp)
X	char *cp;
X{   
X  	register int size;
X	register union overhead *op;
X
X  	if (cp == NULL)
X  		return;
X	op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
X#ifdef debug
X  	ASSERT(op->ov_magic == MAGIC);		/* make sure it was in use */
X#else
X	if (op->ov_magic != MAGIC) {
X		fprintf(stderr,"%s free() ignored\n",
X		    op->ov_magic == OLDMAGIC ? "Duplicate" : "Bad");
X		return;				/* sanity */
X	}
X	op->ov_magic = OLDMAGIC;
X#endif
X#ifdef RCHECK
X  	ASSERT(op->ov_rmagic == RMAGIC);
X	if (op->ov_index <= 13)
X		ASSERT(*(u_int *)((caddr_t)op + op->ov_size + 1 - RSLOP) == RMAGIC);
X#endif
X  	ASSERT(op->ov_index < NBUCKETS);
X  	size = op->ov_index;
X	op->ov_next = nextf[size];
X  	nextf[size] = op;
X#ifdef MSTATS
X  	nmalloc[size]--;
X#endif
X}
X
X/*
X * When a program attempts "storage compaction" as mentioned in the
X * old malloc man page, it realloc's an already freed block.  Usually
X * this is the last block it freed; occasionally it might be farther
X * back.  We have to search all the free lists for the block in order
X * to determine its bucket: 1st we make one pass thru the lists
X * checking only the first block in each; if that fails we search
X * ``reall_srchlen'' blocks in each list for a match (the variable
X * is extern so the caller can modify it).  If that fails we just copy
X * however many bytes was given to realloc() and hope it's not huge.
X */
Xint reall_srchlen = 4;	/* 4 should be plenty, -1 =>'s whole list */
X
Xchar *
Xrealloc(cp, nbytes)
X	char *cp; 
X	unsigned nbytes;
X{   
X  	register u_int onb;
X	union overhead *op;
X  	char *res;
X	register int i;
X	int was_alloced = 0;
X
X  	if (cp == NULL)
X  		return (malloc(nbytes));
X	op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
X	if (op->ov_magic == MAGIC) {
X		was_alloced++;
X		i = op->ov_index;
X	} else {
X		/*
X		 * Already free, doing "compaction".
X		 *
X		 * Search for the old block of memory on the
X		 * free list.  First, check the most common
X		 * case (last element free'd), then (this failing)
X		 * the last ``reall_srchlen'' items free'd.
X		 * If all lookups fail, then assume the size of
X		 * the memory block being realloc'd is the
X		 * smallest possible.
X		 */
X		if ((i = findbucket(op, 1)) < 0 &&
X		    (i = findbucket(op, reall_srchlen)) < 0)
X			i = 0;
X	}
X	onb = (1 << (i + 3)) - sizeof (*op) - RSLOP;
X	/* avoid the copy if same size block */
X	if (was_alloced &&
X	    nbytes <= onb && nbytes > (onb >> 1) - sizeof(*op) - RSLOP)
X		return(cp);
X  	if ((res = malloc(nbytes)) == NULL)
X  		return (NULL);
X  	if (cp != res)			/* common optimization */
X		bcopy(cp, res, (nbytes < onb) ? nbytes : onb);
X  	if (was_alloced)
X		free(cp);
X  	return (res);
X}
X
X/*
X * Search ``srchlen'' elements of each free list for a block whose
X * header starts at ``freep''.  If srchlen is -1 search the whole list.
X * Return bucket number, or -1 if not found.
X */
Xstatic
Xfindbucket(freep, srchlen)
X	union overhead *freep;
X	int srchlen;
X{
X	register union overhead *p;
X	register int i, j;
X
X	for (i = 0; i < NBUCKETS; i++) {
X		j = 0;
X		for (p = nextf[i]; p && j != srchlen; p = p->ov_next) {
X			if (p == freep)
X				return (i);
X			j++;
X		}
X	}
X	return (-1);
X}
X
X#ifdef MSTATS
X/*
X * mstats - print out statistics about malloc
X * 
X * Prints two lines of numbers, one showing the length of the free list
X * for each size category, the second showing the number of mallocs -
X * frees for each size category.
X */
Xmstats(s)
X	char *s;
X{
X  	register int i, j;
X  	register union overhead *p;
X  	int totfree = 0,
X  	totused = 0;
X
X  	fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
X  	for (i = 0; i < NBUCKETS; i++) {
X  		for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
X  			;
X  		fprintf(stderr, " %d", j);
X  		totfree += j * (1 << (i + 3));
X  	}
X  	fprintf(stderr, "\nused:\t");
X  	for (i = 0; i < NBUCKETS; i++) {
X  		fprintf(stderr, " %d", nmalloc[i]);
X  		totused += nmalloc[i] * (1 << (i + 3));
X  	}
X  	fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n",
X	    totused, totfree);
X}
X#endif
!STUFFY!FUNK!
echo Extracting MANIFEST
sed >MANIFEST <<'!STUFFY!FUNK!' -e 's/X//'
XAfter all the perl kits are run you should have the following files:
X
XFilename		Kit Description
X--------		--- -----------
XChanges                 13 Differences between 1.0 level 29 and 2.0 level 0
XConfigure                6 Run this first
XEXTERN.h                 6 Included before foreign .h files
XINTERN.h                15 Included before domestic .h files
XMANIFEST                11 This list of files
XMakefile.SH             13 Precursor to Makefile
XREADME                   1 The Instructions
XWishlist                 4 Some things that may or may not happen
Xarg.c                    1 Expression evaluation
Xarg.h                   12 Public declarations for the above
Xarray.c                 13 Numerically subscripted arrays
Xarray.h                 15 Public declarations for the above
Xcmd.c                   10 Command interpreter
Xcmd.h                   13 Public declarations for the above
Xconfig.H                13 Sample config.h
Xconfig.h.SH             11 Produces config.h.
Xdump.c                  12 Debugging output
Xeg/ADB                  15 An adb wrapper to put in your crash dir
Xeg/README                1 Intro to example perl scripts
Xeg/changes              15 A program to list recently changed files
Xeg/dus                  15 A program to do du -s on non-mounted dirs
Xeg/findcp               14 A find wrapper that implements a -cp switch
Xeg/findtar              15 A find wrapper that pumps out a tar file
Xeg/g/gcp                14 A program to do a global rcp
Xeg/g/gcp.man            14 Manual page for gcp
Xeg/g/ged                 1 A program to do a global edit
Xeg/g/ghosts             15 A sample /etc/ghosts file
Xeg/g/gsh                10 A program to do a global rsh
Xeg/g/gsh.man            14 Manual page for gsh
Xeg/myrup                15 A program to find lightly loaded machines
Xeg/nih                  15 Script to insert #! workaround
Xeg/rmfrom               15 A program to feed doomed filenames to
Xeg/scan/scan_df         14 Scan for filesystem anomalies
Xeg/scan/scan_last       14 Scan for login anomalies
Xeg/scan/scan_messages   13 Scan for console message anomalies
Xeg/scan/scan_passwd     15 Scan for passwd file anomalies
Xeg/scan/scan_ps         15 Scan for process anomalies
Xeg/scan/scan_sudo       14 Scan for sudo anomalies
Xeg/scan/scan_suid        8 Scan for setuid anomalies
Xeg/scan/scanner         14 An anomaly reporter
Xeg/shmkill              15 A program to remove unused shared memory
Xeg/van/empty            15 A program to empty the trashcan
Xeg/van/unvanish         14 A program to undo what vanish does
Xeg/van/vanexp           15 A program to expire vanished files
Xeg/van/vanish           14 A program to put files in a trashcan
Xeval.c                   8 The expression evaluator
Xform.c                  12 Format processing
Xform.h                  15 Public declarations for the above
Xhandy.h                 15 Handy definitions
Xhash.c                  12 Associative arrays
Xhash.h                  14 Public declarations for the above
Xlib/getopt.pl           14 Perl library supporting option parsing
Xlib/importenv.pl        15 Perl routine to get environment into variables.
Xlib/stat.pl             15 Perl library supporting stat function
Xmakedepend.SH            5 Precursor to makedepend
Xmakedir.SH              14 Precursor to makedir
Xmalloc.c                11 A version of malloc you might not want
Xpatchlevel.h            12 The current patch level of perl
Xperl.h                  12 Global declarations
Xperl.man.1               5 The manual page(s), first half
Xperl.man.2               3 The manual page(s), second half
Xperl.y                  10 Yacc grammar for perl
Xperldb                  11 Perl symbolic debugger
Xperldb.man              13 Manual page for perl debugger
Xperlsh                  15 A poor man's perl shell.
Xperly.c                  4 The perl compiler
Xregexp.c                 2 String matching
Xregexp.h                14 Public declarations for the above
Xspat.h                  14 Search pattern declarations
Xstab.c                   6 Symbol table stuff
Xstab.h                   3 Public declarations for the above
Xstr.c                    7 String handling package
Xstr.h                   14 Public declarations for the above
Xt/README                 1 Instructions for regression tests
Xt/TEST                  14 The regression tester
Xt/base.cond             15 See if conditionals work
Xt/base.if               15 See if if works
Xt/base.lex              15 See if lexical items work
Xt/base.pat              15 See if pattern matching works
Xt/base.term             15 See if various terms work
Xt/cmd.elsif             15 See if else-if works
Xt/cmd.for               15 See if for loops work
Xt/cmd.mod               15 See if statement modifiers work
Xt/cmd.subval            14 See if subroutine values work
Xt/cmd.while             14 See if while loops work
Xt/comp.cmdopt           13 See if command optimization works
Xt/comp.cpp              15 See if C preprocessor works
Xt/comp.decl             15 See if declarations work
Xt/comp.multiline        15 See if multiline strings work
Xt/comp.script           14 See if script invokation works
Xt/comp.term             15 See if more terms work
Xt/io.argv               15 See if ARGV stuff works
Xt/io.dup                15 See if >& works right
Xt/io.fs                 12 See if directory manipulations work
Xt/io.inplace            15 See if inplace editing works
Xt/io.pipe               15 See if secure pipes work
Xt/io.print              15 See if print commands work
Xt/io.tell               13 See if file seeking works
Xt/op.append             15 See if . works
Xt/op.auto               14 See if autoincrement et all work
Xt/op.chop               15 See if chop works
Xt/op.cond                5 See if conditional expressions work
Xt/op.delete             15 See if delete works
Xt/op.do                 14 See if subroutines work
Xt/op.each               14 See if associative iterators work
Xt/op.eval               14 See if eval operator works
Xt/op.exec               15 See if exec and system work
Xt/op.exp                15 See if math functions work
Xt/op.flip               15 See if range operator works
Xt/op.fork               15 See if fork works
Xt/op.goto               15 See if goto works
Xt/op.int                15 See if int works
Xt/op.join               15 See if join works
Xt/op.list               14 See if array lists work
Xt/op.magic              15 See if magic variables work
Xt/op.oct                15 See if oct and hex work
Xt/op.ord                15 See if ord works
Xt/op.pat                14 See if esoteric patterns work
Xt/op.push               15 See if push and pop work
Xt/op.regexp             15 See if regular expressions work
Xt/op.repeat             15 See if x operator works
Xt/op.sleep              15 See if sleep works
Xt/op.split               7 See if split works
Xt/op.sprintf            15 See if sprintf works
Xt/op.stat               11 See if stat works
Xt/op.study              14 See if study works
Xt/op.subst              14 See if substitutions work
Xt/op.time               14 See if time functions work
Xt/op.unshift            15 See if unshift works
Xt/re_tests              13 Input file for op.regexp
Xtoke.c                   9 The tokener
Xutil.c                   8 Utility routines
Xutil.h                  15 Public declarations for the above
Xversion.c               15 Prints version of perl
Xx2p/EXTERN.h            15 Same as above
Xx2p/INTERN.h            15 Same as above
Xx2p/Makefile.SH          4 Precursor to Makefile
Xx2p/a2p.h               13 Global declarations
Xx2p/a2p.man             12 Manual page for awk to perl translator
Xx2p/a2p.y               12 A yacc grammer for awk
Xx2p/a2py.c               9 Awk compiler, sort of
Xx2p/handy.h             15 Handy definitions
Xx2p/hash.c              13 Associative arrays again
Xx2p/hash.h              14 Public declarations for the above
Xx2p/s2p                 10 Sed to perl translator
Xx2p/s2p.man              9 Manual page for	sed to perl translator
Xx2p/str.c               11 String handling package
Xx2p/str.h               15 Public declarations for the above
Xx2p/util.c              13 Utility routines
Xx2p/util.h              15 Public declarations for the above
Xx2p/walk.c               7 Parse tree walker
!STUFFY!FUNK!
echo Extracting config.h.SH
sed >config.h.SH <<'!STUFFY!FUNK!' -e 's/X//'
Xcase $CONFIG in
X'')
X    if test ! -f config.sh; then
X	ln ../config.sh . || \
X	ln ../../config.sh . || \
X	ln ../../../config.sh . || \
X	(echo "Can't find config.sh."; exit 1)
X	echo "Using config.sh from above..."
X    fi
X    . ./config.sh
X    ;;
Xesac
Xecho "Extracting config.h (with variable substitutions)"
Xcat <<!GROK!THIS! >config.h
X/* config.h
X * This file was produced by running the config.h.SH script, which
X * gets its values from config.sh, which is generally produced by
X * running Configure.
X *
X * Feel free to modify any of this as the need arises.  Note, however,
X * that running config.h.SH again will wipe out any changes you've made.
X * For a more permanent change edit config.sh and rerun config.h.SH.
X */
X
X
X/* EUNICE:
X *	This symbol, if defined, indicates that the program is being compiled
X *	under the EUNICE package under VMS.  The program will need to handle
X *	things like files that don't go away the first time you unlink them,
X *	due to version numbering.  It will also need to compensate for lack
X *	of a respectable link() command.
X */
X/* VMS:
X *	This symbol, if defined, indicates that the program is running under
X *	VMS.  It is currently only set in conjunction with the EUNICE symbol.
X */
X#$d_eunice	EUNICE		/**/
X#$d_eunice	VMS		/**/
X
X/* CPPSTDIN:
X *	This symbol contains the first part of the string which will invoke
X *	the C preprocessor on the standard input and produce to standard
X *	output.	 Typical value of "cc -E" or "/lib/cpp".
X */
X/* CPPMINUS:
X *	This symbol contains the second part of the string which will invoke
X *	the C preprocessor on the standard input and produce to standard
X *	output.  This symbol will have the value "-" if CPPSTDIN needs a minus
X *	to specify standard input, otherwise the value is "".
X */
X#define CPPSTDIN "$cppstdin"
X#define CPPMINUS "$cppminus"
X
X/* BCOPY:
X *	This symbol, if defined, indicates that the bcopy routine is available
X *	to copy blocks of memory.  Otherwise you should probably use memcpy().
X */
X#$d_bcopy	BCOPY		/**/
X
X/* CHARSPRINTF:
X *	This symbol is defined if this system declares "char *sprintf()" in
X *	stdio.h.  The trend seems to be to declare it as "int sprintf()".  It
X *	is up to the package author to declare sprintf correctly based on the
X *	symbol.
X */
X#$d_charsprf	CHARSPRINTF 	/**/
X
X/* CRYPT:
X *	This symbol, if defined, indicates that the crypt routine is available
X *	to encrypt passwords and the like.
X */
X#$d_crypt	CRYPT		/**/
X
X/* FCHMOD:
X *	This symbol, if defined, indicates that the fchmod routine is available
X *	to change mode of opened files.  If unavailable, use chmod().
X */
X#$d_fchmod	FCHMOD		/**/
X
X/* FCHOWN:
X *	This symbol, if defined, indicates that the fchown routine is available
X *	to change ownership of opened files.  If unavailable, use chown().
X */
X#$d_fchown	FCHOWN		/**/
X
X/* GETGROUPS:
X *	This symbol, if defined, indicates that the getgroups() routine is
X *	available to get the list of process groups.  If unavailable, multiple
X *	groups are probably not supported.
X */
X#$d_getgrps	GETGROUPS		/**/
X
X/* index:
X *	This preprocessor symbol is defined, along with rindex, if the system
X *	uses the strchr and strrchr routines instead.
X */
X/* rindex:
X *	This preprocessor symbol is defined, along with index, if the system
X *	uses the strchr and strrchr routines instead.
X */
X#$d_index	index strchr	/* cultural */
X#$d_index	rindex strrchr	/*  differences? */
X
X/* KILLPG:
X *	This symbol, if defined, indicates that the killpg routine is available
X *	to kill process groups.  If unavailable, you probably should use kill
X *	with a negative process number.
X */
X#$d_killpg	KILLPG		/**/
X
X/* MEMCPY:
X *	This symbol, if defined, indicates that the memcpy routine is available
X *	to copy blocks of memory.  Otherwise you should probably use bcopy().
X *	If neither is defined, roll your own.
X */
X#$d_memcpy	MEMCPY		/**/
X
X/* RENAME:
X *	This symbol, if defined, indicates that the rename routine is available
X *	to rename files.  Otherwise you should do the unlink(), link(), unlink()
X *	trick.
X */
X#$d_rename	RENAME		/**/
X
X/* SETEGID:
X *	This symbol, if defined, indicates that the setegid routine is available
X *	to change the effective gid of the current program.
X */
X#$d_setegid	SETEGID		/**/
X
X/* SETEUID:
X *	This symbol, if defined, indicates that the seteuid routine is available
X *	to change the effective uid of the current program.
X */
X#$d_seteuid	SETEUID		/**/
X
X/* SETRGID:
X *	This symbol, if defined, indicates that the setrgid routine is available
X *	to change the real gid of the current program.
X */
X#$d_setrgid	SETRGID		/**/
X
X/* SETRUID:
X *	This symbol, if defined, indicates that the setruid routine is available
X *	to change the real uid of the current program.
X */
X#$d_setruid	SETRUID		/**/
X
X/* STATBLOCKS:
X *	This symbol is defined if this system has a stat structure declaring
X *	st_blksize and st_blocks.
X */
X#$d_statblks	STATBLOCKS 	/**/
X
X/* STDSTDIO:
X *	This symbol is defined if this system has a FILE structure declaring
X *	_ptr and _cnt in stdio.h.
X */
X#$d_stdstdio	STDSTDIO 	/**/
X
X/* STRCSPN:
X *	This symbol, if defined, indicates that the strcspn routine is available
X *	to scan strings.
X */
X#$d_strcspn	STRCSPN		/**/
X
X/* STRUCTCOPY:
X *	This symbol, if defined, indicates that this C compiler knows how
X *	to copy structures.  If undefined, you'll need to use a block copy
X *	routine of some sort instead.
X */
X#$d_strctcpy	STRUCTCOPY	/**/
X
X/* SYMLINK:
X *	This symbol, if defined, indicates that the symlink routine is available
X *	to create symbolic links.
X */
X#$d_symlink	SYMLINK		/**/
X
X/* TMINSYS:
X *	This symbol is defined if this system declares "struct tm" in
X *	in <sys/time.h> rather than <time.h>.  We can't just say
X *	-I/usr/include/sys because some systems have both time files, and
X *	the -I trick gets the wrong one.
X */
X#$d_tminsys	TMINSYS 	/**/
X
X/* vfork:
X *	This symbol, if defined, remaps the vfork routine to fork if the
X *	vfork() routine isn't supported here.
X */
X#$d_vfork	vfork fork	/**/
X
X/* VOIDSIG:
X *	This symbol is defined if this system declares "void (*signal())()" in
X *	signal.h.  The old way was to declare it as "int (*signal())()".  It
X *	is up to the package author to declare things correctly based on the
X *	symbol.
X */
X#$d_voidsig	VOIDSIG 	/**/
X
X/* GIDTYPE:
X *	This symbol has a value like gid_t, int, ushort, or whatever type is
X *	used to declare group ids in the kernel.
X */
X#define GIDTYPE $gidtype		/**/
X
X/* STDCHAR:
X *	This symbol is defined to be the type of char used in stdio.h.
X *	It has the values "unsigned char" or "char".
X */
X#define STDCHAR $stdchar	/**/
X
X/* UIDTYPE:
X *	This symbol has a value like uid_t, int, ushort, or whatever type is
X *	used to declare user ids in the kernel.
X */
X#define UIDTYPE $uidtype		/**/
X
X/* VOIDFLAGS:
X *	This symbol indicates how much support of the void type is given by this
X *	compiler.  What various bits mean:
X *
X *	    1 = supports declaration of void
X *	    2 = supports arrays of pointers to functions returning void
X *	    4 = supports comparisons between pointers to void functions and
X *		    addresses of void functions
X *
X *	The package designer should define VOIDUSED to indicate the requirements
X *	of the package.  This can be done either by #defining VOIDUSED before
X *	including config.h, or by defining defvoidused in Myinit.U.  If the
X *	level of void support necessary is not present, defines void to int.
X */
X#ifndef VOIDUSED
X#define VOIDUSED $defvoidused
X#endif
X#define VOIDFLAGS $voidflags
X#if (VOIDFLAGS & VOIDUSED) != VOIDUSED
X#$define void int		/* is void to be avoided? */
X#$define M_VOID		/* Xenix strikes again */
X#endif
X
X/* PRIVLIB:
X *	This symbol contains the name of the private library for this package.
X *	The library is private in the sense that it needn't be in anyone's
X *	execution path, but it should be accessible by the world.
X */
X#define PRIVLIB "$privlib"		/**/
X
X!GROK!THIS!
!STUFFY!FUNK!
echo Extracting perldb
sed >perldb <<'!STUFFY!FUNK!' -e 's/X//'
X#!/usr/bin/perl
X
X# $Header: perldb,v 2.0 88/06/05 00:09:45 root Exp $
X#
X# $Log:	perldb,v $
X# Revision 2.0  88/06/05  00:09:45  root
X# Baseline version 2.0.
X# 
X#
X
X$tmp = "/tmp/pdb$$";		# default temporary file, -o overrides.
X
X# parse any switches
X
Xwhile ($ARGV[0] =~ /^-/) {
X    $_ = shift;
X    /^-o$/ && ($tmp = shift,next);
X    die "Unrecognized switch: $_";
X}
X
X$filename = shift;
Xdie "Usage: perldb [-o output] scriptname arguments" unless $filename;
X
Xopen(script,$filename) || die "Can't find $filename";
X
Xopen(tmp, ">$tmp") || die "Can't make temp script";
X
X$perl = '/usr/bin/perl';
X$init = 1;
X$state = 'statement';
X
X# now translate script to contain DB calls at the appropriate places
X
Xwhile (<script>) {
X    chop;
X    if ($. == 1) {
X	if (/^#! *([^ \t]*) (-[^ \t]*)/) {
X	    $perl = $1;
X	    $switch = $2;
X	}
X	elsif (/^#! *([^ \t]*)/) {
X	    $perl = $1;
X	}
X    }
X    s/ *$//;
X    push(@script,$_);		# remember line for DBinit
X    $line = $_;
X    next if /^$/;		# blank lines are uninteresting
X    next if /^[ \t]*#/;		# likewise comment lines
X    if ($init) {
X	print tmp "do DBinit($.);"; $init = '';
X    }
X    if ($inform) {		# skip formats
X	if (/^\.$/) {
X	    $inform = '';
X	    $state = 'statement';
X	}
X	next;
X    }
X    if (/^[ \t]*format /) {
X	$inform++;
X	next;
X    }
X    if ($state eq 'statement' &&
X      !/^[ \t]*}|^[ \t]*else|^[ \t]*continue|^[ \t]*elsif/) {
X	if (s/^([ \t]*[A-Za-z_0-9]+:)//) {
X	    $label = $1;
X	}
X	else {
X	    $label = '';
X	}
X	$line = $label . "do DB($.); " . $_;	# all that work for this line
X    }
X    else {
X	$script[$#script - 1] .= ' ';	# mark line as having continuation
X    }
X    do parse();				# set $state to correct eol value
X}
Xcontinue {
X    print tmp $line,"\n";
X}
X
X# now put out our debugging subroutines.  First the one that's called all over.
X
Xprint tmp '
Xsub DB {
X    push(@DB,$. ,$@, $!, $[, $,, $/, $\ );
X    $[ = 0; $, = ""; $/ = "\n"; $\ = "";
X    $DBline=pop(@_);
X    if ($DBsingle || $DBstop[$DBline] || $DBtrace) {
X	print "$DBline:\t",$DBline[$DBline],"\n";
X	for ($DBi = $DBline; $DBline[$DBi++] =~ / $/; ) {
X	    print "$DBi:\t",$DBline[$DBi],"\n";
X	}
X    }
X    if ($DBaction[$DBline]) {
X	eval $DBaction[$DBline];  print $@;
X    }
X    if ($DBstop[$DBline] || $DBsingle) {
X	for (;;) {
X	    print "perldb> ";
X	    $DBcmd = <stdin>;
X	    last if $DBcmd =~ /^$/;
X	    if ($DBcmd =~ /^q$/) {
X		exit 0;
X	    }
X	    if ($DBcmd =~ /^h$/) {
X		print "
Xs		Single step.
Xc		Continue.
X<CR>		Repeat last s or c.
Xl min-max	List lines.
Xl line		List line.
Xl		List the whole program.
XL		List breakpoints.
Xt		Toggle trace mode.
Xb line		Set breakpoint.
Xd line		Delete breakpoint.
Xd		Delete breakpoint at this line.
Xa line command	Set an action for this line.
Xq		Quit.
Xcommand		Execute as a perl statement.
X
X";
X		next;
X	    }
X	    if ($DBcmd =~ /^t$/) {
X		$DBtrace = !$DBtrace;
X		print "Trace = $DBtrace\n";
X		next;
X	    }
X	    if ($DBcmd =~ /^l (.*)[-,](.*)/) {
X		for ($DBi = $1; $DBi <= $2; $DBi++) {
X		    print "$DBi:\t", $DBline[$DBi], "\n";
X		}
X		next;
X	    }
X	    if ($DBcmd =~ /^l (.*)/) {
X		print "$1:\t", $DBline[$1], "\n";
X		next;
X	    }
X	    if ($DBcmd =~ /^l$/) {
X		for ($DBi = 1; $DBi <= $DBmax ; $DBi++) {
X		    print "$DBi:\t", $DBline[$DBi], "\n";
X		}
X		next;
X	    }
X	    if ($DBcmd =~ /^L$/) {
X		for ($DBi = 1; $DBi <= $DBmax ; $DBi++) {
X		    print "$DBi:\t", $DBline[$DBi], "\n" if $DBstop[$DBi];
X		}
X		next;
X	    }
X	    if ($DBcmd =~ /^b (.*)/) {
X		$DBi = $1;
X		if ($DBline[$DBi-1] =~ / $/) {
X		    print "Line $DBi not breakable.\n";
X		}
X		else {
X		    $DBstop[$DBi] = 1;
X		}
X		next;
X	    }
X	    if ($DBcmd =~ /^d (.*)/) {
X		$DBstop[$1] = 0;
X		next;
X	    }
X	    if ($DBcmd =~ /^d$/) {
X		$DBstop[$DBline] = 0;
X		next;
X	    }
X	    if ($DBcmd =~ /^a ([0-9]+)[ \t]+(.*)/) {
X		$DBi = $1;
X		$DBaction = $2;
X		$DBaction .= ";" unless $DBaction =~ /[;}]$/;
X		$DBaction[$DBi] = $DBaction;
X		next;
X	    }
X	    if ($DBcmd =~ /^s$/) {
X		$DBsingle = 1;
X		last;
X	    }
X	    if ($DBcmd =~ /^c$/) {
X		$DBsingle = 0;
X		last;
X	    }
X	    chop($DBcmd);
X	    $DBcmd .= ";" unless $DBcmd =~ /[;}]$/;
X	    eval $DBcmd;
X	    print $@,"\n";
X	}
X    }
X    $\ = pop(@DB);
X    $/ = pop(@DB);
X    $, = pop(@DB);
X    $[ = pop(@DB);
X    $! = pop(@DB);
X    $@ = pop(@DB);
X    $. = pop(@DB);
X}
X
Xsub DBinit {
X    $DBstop[$_[0]] = 1;
X';
Xprint tmp "    \$0 = '$script';\n";
Xprint tmp "    \$DBmax = $.;\n";
Xprint tmp "    unlink '/tmp/pdb$$';\n";		# expected to fail on -o.
Xfor ($i = 1; $#script >= 0; $i++) {
X    $_ = shift(@script);
X    s/'/\\'/g;
X    print tmp "    \$DBline[$i] = '$_';\n";
X}
Xprint tmp '}
X';
X
Xclose tmp;
X
X# prepare to run the new script
X
Xunshift(@ARGV,$tmp);
Xunshift(@ARGV,$switch) if $switch;
Xunshift(@ARGV,$perl);
Xexec @ARGV;
X
X# This routine tokenizes one perl line good enough to tell what state we are
X# in by the end of the line, so we can tell if the next line should contain
X# a call to DB or not.
X
Xsub parse {
X    until ($_ eq '') {
X	$ord = ord($_);
X	if ($quoting) {
X	    if ($quote == $ord) {
X		$quoting--;
X	    }
X	    s/^.//			if /^[\\]/;
X	    s/^.//;
X	    last if $_ eq "\n";
X	    $state = 'term'		unless $quoting;
X	    next;
X	}
X	if ($ord > 64) {
X	    do quote(ord($1),1), next	if s/^m\b(.)//;
X	    do quote(ord($1),2), next	if s/^s\b(.)//;
X	    do quote(ord($1),2), next	if s/^y\b(.)//;
X	    do quote(ord($1),2), next	if s/^tr\b(.)//;
X	    do quote($ord,1), next	if s/^`//;
X	    next			if s/^[A-Za-z_][A-Za-z_0-9]*://;
X	    $state = 'term', next	if s/^eof\b//;
X	    $state = 'term', next	if s/^shift\b//;
X	    $state = 'term', next	if s/^split\b//;
X	    $state = 'term', next	if s/^tell\b//;
X	    $state = 'term', next	if s/^write\b//;
X	    $state = 'operator', next	if s/^[A-Za-z_][A-Za-z_0-9]*//;
X	    $state = 'operator', next	if s/^[~^|]+//;
X	    $state = 'statement', next	if s/^{//;
X	    $state = 'statement', next	if s/^}[ \t]*$//;
X	    $state = 'statement', next	if s/^}[ \t]*#/#/;
X	    $state = 'term', next	if s/^}//;
X	    $state = 'operator', next	if s/^\[//;
X	    $state = 'term', next	if s/^]//;
X	    die "Illegal character $_";
X	}
X	elsif ($ord < 33) {
X	    next if s/[ \t\n\f]+//;
X	    die "Illegal character $_";
X	}
X	else {
X	    $state = 'statement', next	if s/^;//;
X	    $state = 'term', next	if s/^\.[0-9eE]+//;
X	    $state = 'term', next	if s/^[0-9][0-9xaAbBcCddeEfF.]*//;
X	    $state = 'term', next	if s/^\$[A-Za-z_][A-Za-z_0-9]*//;
X	    $state = 'term', next	if s/^\$.//;
X	    $state = 'term', next	if s/^@[A-Za-z_][A-Za-z_0-9]*//;
X	    $state = 'term', next	if s/^@.//;
X	    $state = 'term', next	if s/^<[A-Za-z_0-9]*>//;
X	    next			if s/^\+\+//;
X	    next			if s/^--//;
X	    $state = 'operator', next	if s/^[-(!%&*=+:,.<>]//;
X	    $state = 'term', next	if s/^\)+//;
X	    do quote($ord,1), next	if s/^'//;
X	    do quote($ord,1), next	if s/^"//;
X	    if (s|^[/?]||) {
X		if ($state =~ /stat|oper/) {
X		    $state = 'term';
X		    do quote($ord,1), next;
X		}
X		$state = 'operator', next;
X	    }
X	    next			if s/^#.*//;
X	}
X    }
X}
X
Xsub quote {
X    ($quote,$quoting) = @_;
X    $state = 'quote';
X}
!STUFFY!FUNK!
echo Extracting t/op.stat
sed >t/op.stat <<'!STUFFY!FUNK!' -e 's/X//'
X#!./perl
X
X# $Header: op.stat,v 2.0 88/06/05 00:14:43 root Exp $
X
Xprint "1..56\n";
X
Xopen(foo, ">Op.stat.tmp");
X
X($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
X    $blksize,$blocks) = stat(foo);
Xif ($nlink == 1) {print "ok 1\n";} else {print "not ok 1\n";}
Xif ($mtime && $mtime == $ctime) {print "ok 2\n";} else {print "not ok 2\n";}
X
Xprint foo "Now is the time for all good men to come to.\n";
Xclose(foo);
X
X$base = time;
Xwhile (time == $base) {}
X
X`rm -f Op.stat.tmp2; ln Op.stat.tmp Op.stat.tmp2; chmod 644 Op.stat.tmp`;
X
X($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
X    $blksize,$blocks) = stat('Op.stat.tmp');
X
Xif ($nlink == 2) {print "ok 3\n";} else {print "not ok 3\n";}
Xif ($mtime && $mtime != $ctime) {print "ok 4\n";} else {print "not ok 4\n";}
Xprint "#4	:$mtime: != :$ctime:\n";
X
X`cp /dev/null Op.stat.tmp`;
X
Xif (-z 'Op.stat.tmp') {print "ok 5\n";} else {print "not ok 5\n";}
Xif (! -s 'Op.stat.tmp') {print "ok 6\n";} else {print "not ok 6\n";}
X
X`echo hi >Op.stat.tmp`;
Xif (! -z 'Op.stat.tmp') {print "ok 7\n";} else {print "not ok 7\n";}
Xif (-s 'Op.stat.tmp') {print "ok 8\n";} else {print "not ok 8\n";}
X
Xchmod 0,'Op.stat.tmp';
X$olduid = $>;		# can't test -r if uid == 0
Xeval '$> = 1;';		# so switch uid (may not be implemented)
Xif (!$> || ! -r 'Op.stat.tmp') {print "ok 9\n";} else {print "not ok 9\n";}
Xif (!$> || ! -w 'Op.stat.tmp') {print "ok 10\n";} else {print "not ok 10\n";}
Xeval '$> = $olduid;';		# switch uid back (may not be implemented)
Xif (! -x 'Op.stat.tmp') {print "ok 11\n";} else {print "not ok 11\n";}
X
Xforeach ((12,13,14,15,16,17)) {
X    print "ok $_\n";		#deleted tests
X}
X
Xchmod 0700,'Op.stat.tmp';
Xif (-r 'Op.stat.tmp') {print "ok 18\n";} else {print "not ok 18\n";}
Xif (-w 'Op.stat.tmp') {print "ok 19\n";} else {print "not ok 19\n";}
Xif (-x 'Op.stat.tmp') {print "ok 20\n";} else {print "not ok 20\n";}
X
Xif (-f 'Op.stat.tmp') {print "ok 21\n";} else {print "not ok 21\n";}
Xif (! -d 'Op.stat.tmp') {print "ok 22\n";} else {print "not ok 22\n";}
X
Xif (-d '.') {print "ok 23\n";} else {print "not ok 23\n";}
Xif (! -f '.') {print "ok 24\n";} else {print "not ok 24\n";}
X
Xif (`ls -l perl` =~ /^l.*->/) {
X    if (-l 'perl') {print "ok 25\n";} else {print "not ok 25\n";}
X}
Xelse {
X    print "ok 25\n";
X}
X
Xif (-o 'Op.stat.tmp') {print "ok 26\n";} else {print "not ok 26\n";}
X
Xif (-e 'Op.stat.tmp') {print "ok 27\n";} else {print "not ok 27\n";}
X`rm -f Op.stat.tmp Op.stat.tmp2`;
Xif (! -e 'Op.stat.tmp') {print "ok 28\n";} else {print "not ok 28\n";}
X
Xif (-c '/dev/tty') {print "ok 29\n";} else {print "not ok 29\n";}
Xif (! -c '.') {print "ok 30\n";} else {print "not ok 30\n";}
X
Xif (! -e '/dev/printer' || -S '/dev/printer')
X    {print "ok 31\n";}
Xelse
X    {print "not ok 31\n";}
Xif (! -S '.') {print "ok 32\n";} else {print "not ok 32\n";}
X
Xif (! -e '/dev/mt0' || -b '/dev/mt0')
X    {print "ok 33\n";}
Xelse
X    {print "not ok 33\n";}
Xif (! -b '.') {print "ok 34\n";} else {print "not ok 34\n";}
X
X$cnt = $uid = 0;
X
Xwhile (</usr/bin/*>) {
X    $cnt++;
X    $uid++ if -u;
X    last if $uid && $uid < $cnt;
X}
X
X# I suppose this is going to fail somewhere...
Xif ($uid > 0 && $uid < $cnt) {print "ok 35\n";} else {print "not ok 35\n";}
X
Xunless (open(tty,"/dev/tty")) {
X    print stderr "Can't open /dev/tty--run t/TEST outside of make.\n";
X}
Xif (-t tty) {print "ok 36\n";} else {print "not ok 36\n";}
Xif (-c tty) {print "ok 37\n";} else {print "not ok 37\n";}
Xclose(tty);
Xif (! -t tty) {print "ok 38\n";} else {print "not ok 38\n";}
Xopen(null,"/dev/null");
Xif (! -t null) {print "ok 39\n";} else {print "not ok 39\n";}
Xclose(null);
Xif (-t) {print "ok 40\n";} else {print "not ok 40\n";}
X
X# These aren't strictly "stat" calls, but so what?
X
Xif (-T 'op.stat') {print "ok 41\n";} else {print "not ok 41\n";}
Xif (! -B 'op.stat') {print "ok 42\n";} else {print "not ok 42\n";}
X
Xif (-B './perl') {print "ok 43\n";} else {print "not ok 43\n";}
Xif (! -T './perl') {print "ok 44\n";} else {print "not ok 44\n";}
X
Xopen(foo,'op.stat');
Xif (-T foo) {print "ok 45\n";} else {print "not ok 45\n";}
Xif (! -B foo) {print "ok 46\n";} else {print "not ok 46\n";}
X$_ = <foo>;
Xif (/perl/) {print "ok 47\n";} else {print "not ok 47\n";}
Xif (-T foo) {print "ok 48\n";} else {print "not ok 48\n";}
Xif (! -B foo) {print "ok 49\n";} else {print "not ok 49\n";}
Xclose(foo);
X
Xopen(foo,'op.stat');
X$_ = <foo>;
Xif (/perl/) {print "ok 50\n";} else {print "not ok 50\n";}
Xif (-T foo) {print "ok 51\n";} else {print "not ok 51\n";}
Xif (! -B foo) {print "ok 52\n";} else {print "not ok 52\n";}
Xseek(foo,0,0);
Xif (-T foo) {print "ok 53\n";} else {print "not ok 53\n";}
Xif (! -B foo) {print "ok 54\n";} else {print "not ok 54\n";}
Xclose(foo);
X
Xif (-T '/dev/null') {print "ok 55\n";} else {print "not ok 55\n";}
Xif (-B '/dev/null') {print "ok 56\n";} else {print "not ok 56\n";}
!STUFFY!FUNK!
echo ""
echo "End of kit 11 (of 15)"
cat /dev/null >kit11isdone
run=''
config=''
for iskit in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
    if test -f kit${iskit}isdone; then
	run="$run $iskit"
    else
	todo="$todo $iskit"
    fi
done
case $todo in
    '')
	echo "You have run all your kits.  Please read README and then type Configure."
	chmod 755 Configure
	;;
    *)  echo "You have run$run."
	echo "You still need to run$todo."
	;;
esac
: Someone might mail this, so...
exit

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.