rs@uunet.UU.NET (Rich Salz) (08/12/87)
Submitted-by: Paul Placeway <pyramid!osu-eddie!paul>
Posting-number: Volume 10, Issue 4
Archive-name: tcsh/Part04
# This is a shell archive. Remove anything before this line
# then unpack it by saving it in a file and typing "sh file"
# (Files unpacked will be owned by you and have default permissions).
# This archive contains the following files:
# ./pwprintf.c
# ./nmalloc.c
# ./tw.h
# ./tw.help.c
# ./tw.init.c
# ./tw.parse.c
#
if `test ! -s ./pwprintf.c`
then
echo "writing ./pwprintf.c"
sed 's/^x//' > ./pwprintf.c << '\Rogue\Monster\'
x/* A public-domain, minimal printf routine that prints through the putchar()
x routine. Feel free to use for anything... -- 7/17/87 Paul Placeway */
x
x#include <ctype.h>
x#include <varargs.h>
x
x/* use varargs since it's the RIGHT WAY, and assuming things about parameters
x on the stack will be wrong on a register passing machine (Pyramid) */
x
x#define INF 32766 /* should be bigger than any field to print */
x
xstatic char buf[128];
x
x/*VARARGS*/
xprintf (va_alist)
xva_dcl
x{
x va_list ap;
x register char *f, *bp;
x register long l;
x register unsigned long u;
x register int i;
x register int fmt;
x register char pad = ' ';
x int flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
x int sign = 0;
x
x va_start(ap);
x
x f = va_arg(ap, char *);
x for (; *f; f++) {
x if (*f != '%') { /* then just out the char */
x putchar (*f);
x } else {
x f++; /* skip the % */
x
x if (*f == '-') { /* minus: flush left */
x flush_left = 1;
x f++;
x }
x
x if (*f == '0') { /* padding with 0 rather than blank */
x pad = '0';
x f++;
x }
x if (*f == '*') { /* field width */
x f_width = va_arg(ap, int);
x f++;
x } else if (isdigit(*f)) {
x f_width = atoi (f);
x while (isdigit(*f)) f++; /* skip the digits */
x }
x
x if (*f == '.') { /* precision */
x f++;
x if (*f == '*') {
x prec = va_arg(ap, int);
x f++;
x } else if (isdigit(*f)) {
x prec = atoi (f);
x while (isdigit(*f)) f++; /* skip the digits */
x }
x }
x
x if (*f == '#') { /* alternate form */
x hash = 1;
x f++;
x }
x
x if (*f == 'l') { /* long format */
x do_long = 1;
x f++;
x }
x
x fmt = *f;
x if (isupper(fmt)) {
x do_long = 1;
x fmt = tolower(fmt);
x }
x bp = buf;
x switch (fmt) { /* do the format */
x case 'd':
x if (do_long)
x l = va_arg(ap, long);
x else
x l = (long) ( va_arg(ap, int) );
x if (l < 0) {
x sign = 1;
x l = -l;
x }
x do {
x *bp++ = l % 10 + '0';
x } while ((l /= 10) > 0);
x if (sign)
x *bp++ = '-';
x f_width = f_width - (bp - buf);
x if (!flush_left)
x while (f_width-- > 0)
x putchar (pad);
x for (bp--; bp >= buf; bp--)
x putchar (*bp);
x if (flush_left)
x while (f_width-- > 0)
x putchar (' ');
x break;
x
x case 'o':
x case 'x':
x case 'u':
x if (do_long)
x u = va_arg(ap, unsigned long);
x else
x u = (unsigned long) ( va_arg(ap, unsigned) );
x if (fmt == 'u') { /* unsigned decimal */
x do {
x *bp++ = u % 10 + '0';
x } while ((u /= 10) > 0);
x } else if (fmt == 'o') { /* octal */
x do {
x *bp++ = u % 8 + '0';
x } while ((u /= 8) > 0);
x if (hash)
x *bp++ = '0';
x } else if (fmt == 'x') { /* hex */
x do {
x i = u % 16;
x if (i < 10)
x *bp++ = i + '0';
x else
x *bp++ = i - 10 + 'a';
x } while ((u /= 16) > 0);
x if (hash) {
x *bp++ = 'x';
x *bp++ = '0';
x }
x }
x i = f_width - (bp - buf);
x if (!flush_left)
x while (i-- > 0)
x putchar (pad);
x for (bp--; bp >= buf; bp--)
x putchar (*bp);
x if (flush_left)
x while (i-- > 0)
x putchar (' ');
x break;
x
x
x case 'c':
x i = va_arg(ap, int);
x putchar (i);
x break;
x
x case 's':
x bp = va_arg(ap, char *);
x f_width = f_width - strlen(bp);
x if (!flush_left)
x while (f_width-- > 0)
x putchar (pad);
x for (i = 0; *bp && i < prec; i++) {
x putchar (*bp);
x bp++;
x }
x if (flush_left)
x while (f_width-- > 0)
x putchar (' ');
x
x break;
x
x case '%':
x putchar ('%');
x break;
x }
x flush_left = 0, f_width = 0, prec = INF, hash = 0, do_long = 0;
x sign = 0;
x pad = ' ';
x }
x }
x va_end(ap);
x return 0;
x}
\Rogue\Monster\
else
echo "will not over write ./pwprintf.c"
fi
if [ `wc -c ./pwprintf.c | awk '{printf $1}'` -ne 3648 ]
then
echo `wc -c ./pwprintf.c | awk '{print "Got " $1 ", Expected " 3648}'`
fi
if `test ! -s ./nmalloc.c`
then
echo "writing ./nmalloc.c"
sed 's/^x//' > ./nmalloc.c << '\Rogue\Monster\'
xstatic char RCSid[] = "$Header: nmalloc.c,v 1.1 85/02/11 21:19:46 paul Exp $";
x#define MSTATS
x
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 <sys/types.h>
x
x#define NULL 0
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 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();
xstatic char *memtop = NULL; /* PWP: top of current memory */
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#endif
x
x#include "sh.local.h"
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 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 memtop = (char *) op; /* PWP */
x if ((int)op & 0x3ff) {
x memtop = sbrk(1024 - ((int)op & 0x3ff)); /* PWP */
x memtop += 1024 - ((int)op & 0x3ff);
x }
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 memtop = sbrk(1 << rnu); /* PWP */
x op = (union overhead *) memtop;
x memtop += 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 if (cp > memtop) {
x /* printf ("free(%lx) above top of memory: %lx\n", cp, memtop); */
x return;
x }
x
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 return; /* sanity */
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 * ``realloc_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 realloc_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 ``realloc_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, realloc_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 */
xshowall(v)
x char **v;
x{
x register int i, j;
x register union overhead *p;
x int totfree = 0,
x totused = 0;
x
x printf("tcsh memory allocation statistics\nfree:\t");
x for (i = 0; i < NBUCKETS; i++) {
x for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
x ;
x printf(" %4d", j);
x totfree += j * (1 << (i + 3));
x }
x printf("\nused:\t");
x for (i = 0; i < NBUCKETS; i++) {
x printf(" %4d", nmalloc[i]);
x totused += nmalloc[i] * (1 << (i + 3));
x }
x printf("\n\tTotal in use: %d, total free: %d\n",
x totused, totfree);
x}
x#endif
\Rogue\Monster\
else
echo "will not over write ./nmalloc.c"
fi
if [ `wc -c ./nmalloc.c | awk '{printf $1}'` -ne 8882 ]
then
echo `wc -c ./nmalloc.c | awk '{print "Got " $1 ", Expected " 8882}'`
fi
if `test ! -s ./tw.h`
then
echo "writing ./tw.h"
sed 's/^x//' > ./tw.h << '\Rogue\Monster\'
x#ifdef MAKE_TWENEX
x
x#define FREE_ITEMS(items,num)\
x{\
x sighold (SIGINT);\
x free_items (items,num);\
x items = NULL;\
x sigrelse (SIGINT);\
x}
x
x#define FREE_DIR(fd)\
x{\
x sighold (SIGINT);\
x closedir (fd);\
x fd = NULL;\
x sigrelse (SIGINT);\
x}
x
x#define TRUE 1
x#define FALSE 0
x#define ON 1
x#define OFF 0
x#define FILSIZ 512 /* Max reasonable file name length */
x#define ESC '\033'
x#define equal(a, b) (strcmp(a, b) == 0)
x
x#define is_set(var) adrof(var)
x
x#define BUILTINS "/usr/local/lib/builtins/" /* fake builtin bin */
x#define SEARCHLIST "HPATH" /* Env. param for helpfile searchlist */
x#define DEFAULTLIST ":/u0/osu/man/cat1:/u0/osu/man/cat8:/u0/osu/man/cat6:/usr/man/cat1:/usr/man/cat8:/usr/man/cat6:/u0/local/man/cat1:/u0/local/man/cat8:/u0/local/man/cat6" /* if no HPATH */
x
xextern char PromptBuf[];
x
xextern char *getenv ();
x
xtypedef enum {LIST, RECOGNIZE, PRINT_HELP, SPELL} COMMAND;
x
x
xchar *getentry();
xchar GetAChar();
xchar *tilde();
xchar filetype();
x
x#define NUMCMDS_START 512 /* was 800 */
x#define NUMCMDS_INCR 256
x#define ITEMS_START 512
x#define ITEMS_INCR 256
x
x#ifndef DONT_EXTERN
x
xextern char **command_list; /* the pre-digested list of commands
x for speed and general usefullness */
xextern int numcommands;
xextern int have_sorted;
x
xextern int dirctr; /* -1 0 1 2 ... */
xextern char dirflag[5]; /* ' nn\0' - dir #s - . 1 2 ... */
x
x
x#endif
x#endif
\Rogue\Monster\
else
echo "will not over write ./tw.h"
fi
if [ `wc -c ./tw.h | awk '{printf $1}'` -ne 1398 ]
then
echo `wc -c ./tw.h | awk '{print "Got " $1 ", Expected " 1398}'`
fi
if `test ! -s ./tw.help.c`
then
echo "writing ./tw.help.c"
sed 's/^x//' > ./tw.help.c << '\Rogue\Monster\'
xstatic char RCSid[] = "$Header: tw.help.c,v 1.1 85/02/11 21:22:00 paul Exp $";
x#define MAKE_TWENEX /* flag to include definitions */
x#include "tw.h"
x#include "sh.h"
x
x
x/* actually look up and print documentation on a file. Look down the path
x for an approiate file, then print it. Note that the printing is NOT
x PAGED. This is because the function is NOT ment to look at manual pages,
x it only does so if there is no .help file to look in. */
x
xstatic int f = -1;
x
xdo_help (command)
xchar *command;
x{
x char name[FILSIZ + 1];
x char *cmd_p;
x int cleanf(), orig_intr;
x char curdir[128]; /* Current directory being looked at */
x char *getenv();
x register char *hpath; /* The environment parameter */
x char *skipslist();
x char full[128], buf[512]; /* full path name and buffer for read */
x int len; /* length of read buffer */
x
x /* copy the string to a safe place */
x copyn (name, command, sizeof (name));
x
x /* trim off the garbage that may be at the end */
x for (cmd_p = name; *cmd_p != '\0'; cmd_p++) {
x if (*cmd_p == ' ' || *cmd_p == '\t') *cmd_p = '\0';
x }
x
x /* if nothing left, return */
x if (*name == '\0') return;
x
x /* got is, now "cat" the file based on the path $HPATH */
x
x hpath = getenv(SEARCHLIST);
x if(hpath == (char *)0)
x hpath = DEFAULTLIST;
x
x while (1)
x {
x if(!*hpath)
x {
x printf("No help file for %s\n", name);
x break;
x }
x nextslist(hpath, curdir);
x hpath = skipslist(hpath);
x
x/* now make the full path name - try first /bar/foo.help, then /bar/foo.1,
x /bar/foo.8, then finally /bar/foo.6 . This is so that you don't spit
x a binary at the tty when $HPATH == $PATH. I know, I know, gotos are
x BOGUS */
x
x copyn (full, curdir, sizeof (full));
x catn (full, "/", sizeof (full));
x catn (full, name, sizeof (full));
x catn (full, ".help", sizeof (full));
x f = open (full, 0); /* try to open for reading */
x if (f != -1) goto cat_it; /* no file there */
x
x copyn (full, curdir, sizeof (full));
x catn (full, "/", sizeof (full));
x catn (full, name, sizeof (full));
x catn (full, ".1", sizeof (full));
x f = open (full, 0); /* try to open for reading */
x if (f != -1) goto cat_it; /* no file there */
x
x copyn (full, curdir, sizeof (full));
x catn (full, "/", sizeof (full));
x catn (full, name, sizeof (full));
x catn (full, ".8", sizeof (full));
x f = open (full, 0); /* try to open for reading */
x if (f != -1) goto cat_it; /* no file there */
x
x copyn (full, curdir, sizeof (full));
x catn (full, "/", sizeof (full));
x catn (full, name, sizeof (full));
x catn (full, ".6", sizeof (full));
x f = open (full, 0); /* try to open for reading */
x if (f == -1) continue; /* no file there */
x
xcat_it:
x orig_intr = (int) signal(SIGINT, cleanf);
x while ((f != -1) && (len = read (f, buf, 512)) != 0) {
x /* the file is here */
x write (SHOUT, buf, len); /* so cat it to the */
x } /* terminal */
x if (f != -1)
x close(f);
x f = (-1);
x signal(SIGINT, orig_intr);
x break;
x }
x}
x
xstatic
xcleanf()
x{
x if (f != -1)
x close(f);
x f = (-1);
x}
x
x/* these next two are stolen from CMU's man(1) command for looking down
x * paths. they are prety straight forward. */
x
x/*
x * nextslist takes a search list and copies the next path in it
x * to np. A null search list entry is expanded to ".".
x * If there are no entries in the search list, then np will point
x * to a null string.
x */
x
xnextslist(sl, np)
x register char *sl;
x register char *np;
x{
x if(!*sl)
x *np = '\000';
x else if(*sl == ':')
x {
x *np++ = '.';
x *np = '\000';
x }
x else
x {
x while(*sl && *sl != ':')
x *np++ = *sl++;
x *np = '\000';
x }
x}
x
x/*
x * skipslist returns the pointer to the next entry in the search list.
x */
x
xchar *
xskipslist(sl)
x register char *sl;
x{
x while(*sl && *sl++ != ':');
x return(sl);
x}
x
\Rogue\Monster\
else
echo "will not over write ./tw.help.c"
fi
if [ `wc -c ./tw.help.c | awk '{printf $1}'` -ne 3785 ]
then
echo `wc -c ./tw.help.c | awk '{print "Got " $1 ", Expected " 3785}'`
fi
if `test ! -s ./tw.init.c`
then
echo "writing ./tw.init.c"
sed 's/^x//' > ./tw.init.c << '\Rogue\Monster\'
x#define MAKE_TWENEX /* flag to include definitions */
x#include "tw.h"
x#include "sh.h"
x
x/*
x * Build the command name list (and print the results). This is a HACK until
x * I can get the rehash command to include its results in the command list.
x */
x
xchar *extract_dir_from_path();
xint fcompare();
xchar *malloc();
xchar *calloc();
xstatic int maxcommands = 0;
x
xstatic int tw_been_inited = 0;
x /* to avoid double reading of commands file */
x
xtw_clear_comm_list() {
x register int i;
x
x have_sorted = 0;
x if (numcommands != 0) {
x/* for (i = 0; command_list[i]; i++) { */
x for (i = 0; i < numcommands; i++) {
x if (command_list[i] == 0) {
x printf ("tried to free a null, i = %d\n", i);
x } else {
x free (command_list[i]);
x }
x command_list[i] = NULL;
x }
x numcommands = 0;
x }
x}
x
xtw_sort_comms () { /* could be re-written */
x register int i,forward;
x
x /* sort the list. */
x qsort (command_list, numcommands, sizeof (command_list[0]), fcompare);
x
x /* get rid of multiple entries */
x for (i = 0, forward = 0; i < numcommands - 1; i++) {
x if (strcmp (command_list[i], command_list[i+1]) == 0) { /* garbage */
x if (command_list[i] == 0) {
x printf ("tried to free a null, i = %d, previous = %s\n", i,
x command_list [i-1]);
x } else {
x free (command_list[i]);
x }
x forward++; /* increase the forward ref. count */
x } else if (forward) {
x command_list[i-forward] = command_list[i];
x }
x }
x numcommands -= forward;
x command_list[numcommands] = (char *)NULL;
x
x have_sorted = 1;
x}
x
xtw_add_comm_name (name) /* this is going to be called a LOT at startup */
xchar *name;
x{
x register int length;
x register long i;
x register char **ncl, **p1, **p2;
x
x if (maxcommands == 0) {
x command_list = (char **)malloc(NUMCMDS_START * sizeof (command_list[0]));
x if (command_list == NULL) {
x printf ("\nYikes! malloc ran out of memory!!!\n");
x return;
x }
x maxcommands = NUMCMDS_START;
x for (i = 0, p2 = command_list; i < maxcommands; i++)
x *p2 = NULL;
x } else if (numcommands >= maxcommands) {
x ncl = (char **)malloc((maxcommands + NUMCMDS_INCR) *
x sizeof (command_list[0]));
x if (ncl == NULL) {
x printf ("\nYikes! malloc ran out of memory!!!\n");
x return;
x }
x for (i = 0, p1 = command_list, p2 = ncl; i < numcommands; i++)
x *p2++ = *p1++;
x for (; i < maxcommands+NUMCMDS_INCR; i++)
x *p2++ = NULL;
x free(command_list);
x command_list = ncl;
x#ifdef COMMENT
x command_list = (char **)realloc(command_list, (maxcommands +
x NUMCMDS_INCR) * sizeof (command_list[0]));
x if (command_list == NULL) {
x printf ("\nYikes! realloc ran out of memory!!!\n");
x return;
x }
x#endif
x maxcommands += NUMCMDS_INCR;
x }
x
x if (name[0] == '.') return; /* no dot commands... */
x if (name[0] == '#') return; /* no Emacs buffer checkpoints */
x
x length = strlen(name) + 1;
x
x if (name[length-2] == '~') return; /* and no Emacs backups either */
x
x if ((command_list[numcommands] = (char *)malloc (length)) == NULL) {
x printf ("\nYikes!! I ran out of memory!!!\n");
x return;
x }
x
x copyn (command_list[numcommands], name, MAXNAMLEN);
x numcommands++;
x}
x
xtw_add_builtins() {
x register char *cp;
x register struct biltins *bptr;
x
x for (bptr = bfunc; cp = bptr->bname; bptr++) {
x tw_add_comm_name (cp);
x }
x}
x
xtw_add_aliases ()
x{
x register struct varent *vp;
x
x vp = &aliases;
x
x for (vp = vp->link; vp != 0; vp = vp->link) {
x tw_add_comm_name(vp->name);
x }
x
x}
\Rogue\Monster\
else
echo "will not over write ./tw.init.c"
fi
if [ `wc -c ./tw.init.c | awk '{printf $1}'` -ne 3517 ]
then
echo `wc -c ./tw.init.c | awk '{print "Got " $1 ", Expected " 3517}'`
fi
if `test ! -s ./tw.parse.c`
then
echo "writing ./tw.parse.c"
sed 's/^x//' > ./tw.parse.c << '\Rogue\Monster\'
x#define MAKE_TWENEX /* flag to include definitions */
x#include "tw.h"
x#include "sh.h"
x
xstatic int maxitems = 0;
xchar **command_list = (char **)NULL; /* the pre-digested list of commands
x for speed and general usefullness */
xint numcommands = 0;
xint have_sorted = 0;
x
xint dirctr; /* -1 0 1 2 ... */
xchar dirflag[5]; /* ' nn\0' - dir #s - . 1 2 ... */
x
x/* do the expand or list on the command line -- SHOULD BE REPLACED */
xint fcompare();
x
xextern char NeedsRedraw; /* from ed.h */
x
xtenematch (inputline, inputline_size, num_read, command)
xchar *inputline; /* match string prefix */
xint inputline_size; /* max size of string */
xint num_read; /* # actually in inputline */
xCOMMAND command; /* LIST or RECOGNIZE or PRINT_HELP */
x
x{
x static char
x *delims = " '\"\t;&<>()|^%";
x static char
x *cmd_delims = ";&(|`";
x char word [FILSIZ + 1];
x register char *str_end, *word_start, *cmd_start, *wp;
x char *cmd_st;
x int space_left;
x int is_a_cmd; /* UNIX command rather than filename */
x int search_ret; /* what search returned for debugging */
x
x str_end = &inputline[num_read];
x
x /*
x * Find LAST occurence of a delimiter in the inputline.
x * The word start is one character past it.
x */
x for (word_start = str_end; word_start > inputline; --word_start)
x if (index (delims, word_start[-1]))
x break;
x
x /* space backward looking for the beginning
x of this command */
x for (cmd_st = str_end; cmd_st > inputline; --cmd_st)
x if (index (cmd_delims, cmd_st[-1]))
x break;
x /* step forward over leading spaces */
x while (*cmd_st != '\0' && (*cmd_st == ' ' || *cmd_st == '\t'))
x cmd_st++;
x
x space_left = inputline_size - (word_start - inputline) - 1;
x
x is_a_cmd = starting_a_command (word_start, inputline);
x
x for (cmd_start = word_start, wp = word; cmd_start < str_end;
x *wp++ = *cmd_start++);
x *wp = 0;
x
x/* printf ("\ncmd_st:%s:\nword_start:%s:\n", cmd_st, word_start); */
x /* for debugging */
x if (command == RECOGNIZE) {
x search_ret = t_search (word, wp, command, space_left, is_a_cmd);
x if (InsertStr(wp) < 0) /* put it in the input buffer */
x return 2; /* error inserting */
x return search_ret;
x } else if (command == SPELL) {
x search_ret = spell_me(word, sizeof(word), is_a_cmd);
x DeleteBack(str_end-word_start); /* get rid of old word */
x if (InsertStr(word) < 0) /* insert newly spelled word */
x return 2; /* error inserting */
x if (search_ret < 0)
x return 2;
x else
x return 1; /* force a beep */
x } else if (command == PRINT_HELP) {
x do_help (cmd_st);
x return 1;
x } else { /* LIST */
x search_ret = t_search (word, wp, command, space_left, is_a_cmd);
x return search_ret;
x }
x}
x
x
x
x/*
x * return true if check items initial chars in template
x * This differs from PWB imatch in that if check is null
x * it items anything
x */
x
xstatic
xis_prefix (check, template)
xchar *check,
x *template;
x{
x register char *check_char,
x *template_char;
x
x check_char = check;
x template_char = template;
x do
x if (*check_char == 0)
x return (TRUE);
x while (*check_char++ == *template_char++);
x return (FALSE);
x}
x
x/* return true if the command starting at wordstart is a command */
x
xstarting_a_command (wordstart, inputline)
xregister char *wordstart, *inputline;
x{
x static char
x *cmdstart = ";&(|`",
x *cmdalive = " \t'\"";
x while (--wordstart >= inputline)
x {
x if (index (cmdstart, *wordstart))
x break;
x if (!index (cmdalive, *wordstart))
x return (FALSE);
x }
x if (wordstart > inputline && *wordstart == '&') /* Look for >& */
x {
x while (wordstart > inputline &&
x (*--wordstart == ' ' || *wordstart == '\t'));
x if (*wordstart == '>')
x return (FALSE);
x }
x return (TRUE);
x}
x
x
x/*
x * Object: extend what user typed up to an ambiguity.
x * Algorithm:
x * On first match, copy full entry (assume it'll be the only match)
x * On subsequent matches, shorten extended_name to the first
x * character mismatch between extended_name and entry.
x * If we shorten it back to the prefix length, stop searching.
x */
xrecognize (extended_name, entry, name_length, numitems)
xchar *extended_name, *entry;
x{
x if (numitems == 1) /* 1st match */
x copyn (extended_name, entry, MAXNAMLEN);
x else /* 2nd and subsequent matches */
x {
x register char *x, *ent;
x register int len = 0;
x for (x = extended_name, ent = entry; *x && *x == *ent++; x++, len++);
x *x = '\0'; /* Shorten at 1st char diff */
x if (len == name_length) /* Ambiguous to prefix? */
x return (-1); /* So stop now and save time */
x }
x return (0);
x}
x
x
x/*
x * Perform a RECOGNIZE or LIST command on string "word".
x */
xt_search (word, wp, command, max_word_length, looking_for_command)
xchar *word,
x *wp; /* original end-of-word */
xCOMMAND command;
x{
x register numitems,
x name_length, /* Length of prefix (file name) */
x looking_for_lognames; /* True if looking for login names */
x int showpathn; /* True if we want path number */
x struct stat
x dot_statb, /* Stat buffer for "." */
x curdir_statb; /* Stat buffer for current directory */
x int dot_scan, /* True if scanning "." */
x dot_got; /* True if have scanned dot already */
x char tilded_dir[FILSIZ + 1], /* dir after ~ expansion */
x dir[FILSIZ + 1], /* /x/y/z/ part in /x/y/z/f */
x name[MAXNAMLEN + 1], /* f part in /d/d/d/f */
x extended_name[MAXNAMLEN+1], /* the recognized (extended) name */
x *entry, /* single directory entry or logname */
x *path; /* hacked PATH environment variable */
x int next_command = 0; /* the next command to take out of */
x /* the list of commands */
x int d = 4, nd; /* distance and new distance to command for SPELL */
x
x static DIR
x *dir_fd = NULL;
x static char
x **items = NULL; /* file names when doing a LIST */
x
x if (items != NULL)
x FREE_ITEMS (items, numitems);
x numitems = 0;
x if (dir_fd != NULL)
x FREE_DIR (dir_fd);
x
x looking_for_lognames = (*word == '~') && (index (word, '/') == NULL);
x looking_for_command &= (*word != '~') && (index (word, '/') == NULL);
x
x dot_got = FALSE;
x stat (".", &dot_statb);
x
x if (looking_for_lognames) { /* Looking for login names? */
x setpwent (); /* Open passwd file */
x copyn (name, &word[1], MAXNAMLEN); /* name sans ~ */
x } else if (looking_for_command) {
x if (!numcommands) /* if we have no list of commands */
x get_tw_comm_list();
x if (!have_sorted) { /* if we haven't sorted them yet */
x tw_add_builtins();
x tw_add_aliases();
x tw_sort_comms (); /* re-build the command path for twenex.c */
x }
x } else { /* not looking for a command or logname */
x /* Open the directory */
x extract_dir_and_name (word, dir, name);
x if ((tilde (tilded_dir, dir) == 0) || /* expand ~user/... stuff */
x ((dir_fd = opendir (*tilded_dir ? tilded_dir : ".")) == NULL)) {
x printf ("\n%s unreadable\n", *tilded_dir ? tilded_dir : ".");
x NeedsRedraw = 1;
x return (0);
x }
x
x dot_scan = FALSE;
x }
x
x name_length = strlen (name);
x showpathn = looking_for_command && is_set("listpathnum");
x
x while (1) {
x if (!looking_for_command) {
x if ((entry = getentry (dir_fd, looking_for_lognames)) == NULL) {
x break;
x }
x
x /*
x * Don't match . files on null prefix match
x */
x if (name_length == 0 && entry[0] == '.' &&
x !looking_for_lognames && !is_set("showdots"))
x continue;
x
x } else {
x if (numcommands == 0) {
x dohash ();
x }
x if (next_command >= numcommands) break; /* end of list */
x if ((entry = command_list[next_command++]) == NULL)
x break;
x copyn (name, word, MAXNAMLEN); /* so it can match things */
x }
x
x if (command == SPELL) { /* correct the spelling of the last bit */
x nd = spdist(entry, name); /* test the entry against original */
x if (nd <= d && nd != 4) {
x strcpy (extended_name, entry);
x d = nd;
x if (d == 0) /* if found it exactly */
x break;
x }
x } else if (command == LIST) { /* LIST command */
x extern char *malloc ();
x register int length;
x register long i;
x register char **ni, **p1, **p2;
x
x if (!is_prefix (name, entry))
x continue;
x
x if (items == NULL || maxitems == 0) {
x items = (char **) malloc (sizeof (items[0]) * (ITEMS_START+1));
x if (items == NULL) {
x printf("\nCannot malloc items!!\n");
x NeedsRedraw = 1;
x break;
x }
x maxitems = ITEMS_START;
x for (i = 0, p2 = items; i < maxitems; i++)
x *p2++ = NULL;
x } else if (numitems >= maxitems) {
x ni = (char **)malloc((sizeof (items[0])) *
x (maxitems + ITEMS_INCR));
x if (ni == NULL) {
x printf("\nCannot realloc items!!\n");
x NeedsRedraw = 1;
x break;
x }
x for (i = 0, p1 = items, p2 = ni; i < numitems; i++)
x *p2++ = *p1++;
x for (; i < numitems+ITEMS_INCR; i++)
x *p2++ = NULL;
x free (items);
x items = ni;
x maxitems += ITEMS_INCR;
x }
x
x
x length = strlen(entry) + 1;
x if (showpathn)
x length += strlen(dirflag);
x if ((items[numitems] = malloc (length)) == NULL)
x {
x printf ("\nYikes!! I ran out of memory!!!\n");
x NeedsRedraw = 1;
x break;
x }
x copyn (items[numitems], entry, MAXNAMLEN);
x if (showpathn)
x catn (items[numitems], dirflag, MAXNAMLEN);
x numitems++;
x } else { /* RECOGNIZE command */
x if (!is_prefix (name, entry))
x continue;
x
x if (adrof ("recexact")) {
x if (strcmp (name, entry) == 0) { /* EXACT match */
x copyn (extended_name, entry, MAXNAMLEN);
x numitems = 1; /* fake into expanding */
x break;
x }
x }
x if (recognize (extended_name, entry, name_length, ++numitems))
x break;
x }
x }
x
x if (!looking_for_command) {
x if (looking_for_lognames)
x endpwent ();
x else
x FREE_DIR (dir_fd);
x }
x
x if (command == RECOGNIZE && numitems > 0)
x {
x if (looking_for_lognames)
x copyn (word, "~", 1);
x else if (looking_for_command)
x word[0] = '\0';
x else
x copyn (word, dir, max_word_length); /* put back dir part */
x catn (word, extended_name, max_word_length); /* add extended name */
x if (numitems == 1) {
x if (looking_for_lognames) { /* add / */
x catn (word, "/", max_word_length);
x }
x else {
x if (looking_for_command) { /* add space */
x catn (word, " ", max_word_length);
x }
x else {
x if (isadirectory (tilded_dir, extended_name)) {
x catn (word, "/", max_word_length);
x }
x else {
x catn (word, " ", max_word_length);
x }
x }
x }
x }
x return (numitems); /* at the end */
x } else if (command == LIST) {
x qsort (items, numitems, sizeof (items[1]), fcompare);
x print_by_column (looking_for_lognames ? NULL:tilded_dir, items,
x numitems, looking_for_command);
x if (items != NULL)
x FREE_ITEMS (items, numitems);
x return (0);
x } else if (command == SPELL) {
x if (looking_for_lognames)
x copyn (word, "~", 1);
x else if (looking_for_command)
x word[0] = '\0';
x else
x copyn (word, dir, max_word_length); /* put back dir part */
x catn (word, extended_name, max_word_length); /* add extended name */
x return d;
x }
x}
x
x
x/* stuff for general command line hacking */
x
x/*
x * Strip next directory from path; return ptr to next unstripped directory.
x */
x
xchar *extract_dir_from_path (path, dir)
xchar *path, dir[];
x{
x register char *d = dir;
x
x while (*path && (*path == ' ' || *path == ':')) path++;
x while (*path && (*path != ' ' && *path != ':')) *(d++) = *(path++);
x while (*path && (*path == ' ' || *path == ':')) path++;
x
x ++dirctr;
x if (*dir == '.')
x strcpy (dirflag, " .");
x else
x {
x dirflag[0] = ' ';
x if (dirctr <= 9)
x {
x dirflag[1] = '0' + dirctr;
x dirflag[2] = '\0';
x }
x else
x {
x dirflag[1] = '0' + dirctr / 10;
x dirflag[2] = '0' + dirctr % 10;
x dirflag[3] = '\0';
x }
x }
x *(d++) = '/';
x *d = 0;
x
x return path;
x}
x
x
xstatic
xfree_items (items, numitems)
xregister char **items;
xregister numitems;
x{
x register int i;
x/* for (i = 0; items[i] != (char *)NULL; i++) */
x for (i = 0; i < numitems; i++)
x free (items[i]);
x free (items);
x maxitems = 0;
x}
x
x
x/*
x * parse full path in file into 2 parts: directory and file names
x * Should leave final slash (/) at end of dir.
x */
xstatic
xextract_dir_and_name (path, dir, name)
xchar *path, *dir, *name;
x{
x extern char *rindex ();
x register char *p;
x p = rindex (path, '/');
x if (p == NULL)
x {
x copyn (name, path, MAXNAMLEN);
x dir[0] = '\0';
x }
x else
x {
x p++;
x copyn (name, p, MAXNAMLEN);
x copyn (dir, path, p - path);
x }
x}
x
x
xchar *
xgetentry (dir_fd, looking_for_lognames)
xDIR *dir_fd;
x{
x if (looking_for_lognames) /* Is it login names we want? */
x {
x extern struct passwd *getpwent ();
x register struct passwd *pw;
x if ((pw = getpwent ()) == NULL)
x return (NULL);
x return (pw -> pw_name);
x }
x else /* It's a dir entry we want */
x {
x register struct direct *dirp;
x if (dirp = readdir (dir_fd))
x return (dirp -> d_name);
x return (NULL);
x }
x}
x
x
x/*
x * expand "old" file name with possible tilde usage
x * ~person/mumble
x * expands to
x * home_directory_of_person/mumble
x * into string "new".
x */
x
xchar *
xtilde (new, old)
xchar *new, *old;
x{
x extern struct passwd *getpwuid (), *getpwnam ();
x
x register char *o, *p;
x register struct passwd *pw;
x static char person[40] = {0};
x
x if ((old[0] != '~') && (old[0] != '='))
x {
x strcpy (new, old);
x return (new);
x }
x
x for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++);
x *p = '\0';
x
x if (old[0] == '~') {
x if (person[0] == '\0') /* then use current uid */
x pw = getpwuid (getuid ());
x else
x pw = getpwnam (person);
x
x if (pw == NULL)
x return (NULL);
x
x strcpy (new, pw -> pw_dir);
x } else { /* '=' stack expansion */
x new[0] = '\0';
x if (!isdigit (old[1]) && old[1] != '-')
x return (NULL);
x getstakd (new, (old[1] == '-') ? -1 : old[1] - '0', 0);
x /* last "0" = don't call error */
x if (new[0] == '\0')
x return (NULL);
x }
x (void) strcat (new, o);
x return (new);
x}
x
xchar
xfiletype (dir, file) /* symbology from 4.3 ls command */
xchar *dir, *file;
x{
x if (dir)
x {
x char path[512];
x struct stat statb;
x strcpy (path, dir);
x catn (path, file, sizeof path);
x if (lstat (path, &statb) >= 0) /* if no symlinks, change to stat() */
x {
x if ((statb.st_mode & S_IFLNK) == S_IFLNK) /* Symbolic link */
x return ('@');
x if ((statb.st_mode & S_IFSOCK) == S_IFSOCK) /* Socket */
x return ('=');
x#ifdef S_IFIFO
x if ((statb.st_mode & S_IFIFO) == S_IFIFO) /* Named Pipe */
x return ('<');
x#endif
x if ((statb.st_mode & S_IFDIR) == S_IFDIR) /* normal Directory */
x return ('/');
x if (statb.st_mode & 0111)
x return ('*');
x }
x }
x return (' ');
x}
x
xisadirectory (dir, file) /* return 1 if dir/file is a directory */
xchar *dir, *file; /* uses stat rather than lstat to get dest. */
x{
x if (dir)
x {
x char path[512];
x struct stat statb;
x strcpy (path, dir);
x catn (path, file, sizeof path);
x if (stat (path, &statb) >= 0) /* if no symlinks, change to stat() */
x {
x if ((statb.st_mode & S_IFDIR) == S_IFDIR) /* normal Directory */
x return 1;
x }
x }
x return 0;
x}
x
x/*
x * Print sorted down columns
x */
xprint_by_column (dir, items, count, looking_for_command)
xregister char *dir, *items[];
x{
x register int i, rows, r, c, maxwidth = 0, columns;
x extern int TermH; /* from the editor routines */
x extern int lbuffed; /* from sh.print.c */
x
x /* lbuffed = 0; */ /* turn off line buffering */
x
x for (i = 0; i < count; i++)
x maxwidth = max (maxwidth, strlen (items[i]));
x maxwidth += looking_for_command ? 1:2; /* for the file tag and space */
x columns = (TermH+1) / maxwidth; /* PWP: terminal size change */
x if (!columns) columns = 1;
x rows = (count + (columns - 1)) / columns;
x for (r = 0; r < rows; r++)
x {
x for (c = 0; c < columns; c++)
x {
x i = c * rows + r;
x if (i < count)
x {
x register int w;
x printf("%s", items[i]);
x w = strlen (items[i]);
x /* Print filename followed by '/' or '*' or ' ' */
x if (!looking_for_command)
x putchar (filetype (dir, items[i])), w++;
x if (c < (columns - 1)) /* Not last column? */
x for (; w < maxwidth; w++)
x putchar (' ');
x }
x }
x printf ("\n");
x }
x
x /* lbuffed = 1; */ /* turn back on line buffering */
x flush();
x}
x
x/*
x * For qsort()
x */
xfcompare (file1, file2)
xchar **file1, **file2;
x{
x return (strcmp (*file1, *file2));
x}
x
x/*
x * Concatonate src onto tail of des.
x * Des is a string whose maximum length is count.
x * Always null terminate.
x */
x
xcatn (des, src, count)
xregister char *des, *src;
xregister count;
x{
x while (--count >= 0 && *des)
x des++;
x while (--count >= 0)
x if ((*des++ = *src++) == 0)
x return;
x *des = '\0';
x}
x
xmax (a, b)
x{
x if (a > b)
x return (a);
x return (b);
x}
x
x/*
x * like strncpy but always leave room for trailing \0
x * and always null terminate.
x */
xcopyn (des, src, count)
xregister char *des, *src;
xregister count;
x{
x while (--count >= 0)
x if ((*des++ = *src++) == 0)
x return;
x *des = '\0';
x}
\Rogue\Monster\
else
echo "will not over write ./tw.parse.c"
fi
if [ `wc -c ./tw.parse.c | awk '{printf $1}'` -ne 17506 ]
then
echo `wc -c ./tw.parse.c | awk '{print "Got " $1 ", Expected " 17506}'`
fi
echo "Finished archive 4 of 6"
# if you want to concatenate archives, remove anything after this line
exit
--
Rich $alz
Cronus Project, BBN Labs rsalz@bbn.com
Moderator, comp.sources.unix sources@uunet.uu.uu.w