Article View: mod.amiga.sources
Article #25sdb Database (2 of 3)
From: doc
Date: Fri, 22 Aug 1986 22:46
Date: Fri, 22 Aug 1986 22:46
2129 lines
52837 bytes
52837 bytes
# This is a shell archive.
# Remove everything above and including the cut line.
# Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
# Run the following text with /bin/sh to create:
# int.c
# io.c
# scn.c
# cmd.c
# This archive created: Fri Aug 22 17:39:48 1986
# By: Craig Norborg (Purdue University Computing Center)
cat << \SHAR_EOF > int.c
/* SDB - boolean expression evaluator */
#include "sdbio.h"
static struct operand *stack[STACKMAX],**sptr;
static union codecell *cptr;
/* db_interpret - interpret a boolean expression */
int db_interpret(slptr)
struct sel *slptr;
{
struct operand *result;
int r;
/* check for empty where clause */
if ((cptr = slptr->sl_where) == NULL)
return (TRUE);
/* setup stack */
sptr = stack;
/* execute the code */
while ((*(*cptr++).c_operator)())
;
/* get the result from the top of stack */
result = *--sptr;
r = result->o_value.ov_boolean;
if (result->o_type == TEMP)
free(result);
/* make sure the stack is empty */
while (sptr != stack) {
if ((*sptr)->o_type == TEMP)
free(*sptr);
sptr -= 1;
}
/* return result */
return (r);
}
int db_xstop()
{
return (FALSE);
}
int db_xpush()
{
*sptr++ = (*cptr++).c_operand;
}
int db_xand()
{
return (boolean('&'));
}
int db_xor()
{
return (boolean('|'));
}
static int boolean(opr)
{
struct operand *lval,*rval,*result;
int lv,rv,r;
rval = *--sptr; lval = *--sptr;
lv = lval->o_value.ov_boolean;
rv = rval->o_value.ov_boolean;
if ((result = malloc(sizeof(struct operand))) == NULL)
return (db_ferror(INSMEM));
result->o_type = TEMP;
switch (opr) {
case '&': r = (lv && rv);
break;
case '|': r = (lv || rv);
break;
}
result->o_value.ov_boolean = r;
*sptr++ = result;
if (lval->o_type == TEMP)
free(lval);
if (rval->o_type == TEMP)
free(rval);
return (TRUE);
}
int db_xnot()
{
struct operand *val,*result;
val = *--sptr;
if ((result = malloc(sizeof(struct operand))) == NULL)
return (db_ferror(INSMEM));
result->o_type = TEMP;
result->o_value.ov_boolean = !val->o_value.ov_boolean;
*sptr++ = result;
if (val->o_type == TEMP)
free(val);
return (TRUE);
}
int db_xlss()
{
return (compare(LSS));
}
int db_xleq()
{
return (compare(LEQ));
}
int db_xeql()
{
return (compare(EQL));
}
int db_xgeq()
{
return (compare(GEQ));
}
int db_xgtr()
{
return (compare(GTR));
}
int db_xneq()
{
return (compare(NEQ));
}
static int compare(cmp)
{
struct operand *lval,*rval,*result;
int i;
rval = *--sptr; lval = *--sptr;
if ((result = malloc(sizeof(struct operand))) == NULL)
return (db_ferror(INSMEM));
result->o_type = TEMP;
if (lval->o_value.ov_char.ovc_type == TCHAR)
i = comp(lval,rval);
else
i = ncomp(lval,rval);
switch (cmp) {
case LSS: i = (i < 0);
break;
case LEQ: i = (i <= 0);
break;
case EQL: i = (i == 0);
break;
case GEQ: i = (i >= 0);
break;
case GTR: i = (i > 0);
break;
case NEQ: i = (i != 0);
break;
}
result->o_value.ov_boolean = i;
*sptr++ = result;
if (lval->o_type == TEMP)
free(lval);
if (rval->o_type == TEMP)
free(rval);
return (TRUE);
}
static int comp(lval,rval)
struct operand *lval,*rval;
{
char *lptr,*rptr; int lctr,rctr;
int len;
lptr = lval->o_value.ov_char.ovc_string;
lctr = lval->o_value.ov_char.ovc_length;
rptr = rval->o_value.ov_char.ovc_string;
rctr = rval->o_value.ov_char.ovc_length;
while (lctr > 0 && (lptr[lctr-1] == 0 || lptr[lctr-1] == ' '))
lctr--;
while (rctr > 0 && (rptr[rctr-1] == 0 || rptr[rctr-1] == ' '))
rctr--;
if (lctr < rctr)
len = lctr;
else
len = rctr;
while ((len--) > 0) {
if (*lptr++ != *rptr++)
if (*--lptr < *--rptr)
return (-1);
else
return (1);
}
if (lctr == rctr)
return (0);
else if (lctr < rctr)
return (-1);
else
return (1);
}
static int ncomp(lval,rval)
struct operand *lval,*rval;
{
char lstr[NUMBERMAX+1],rstr[NUMBERMAX+1];
int len;
strncpy(lstr,lval->o_value.ov_char.ovc_string,
(len = lval->o_value.ov_char.ovc_length)); lstr[len] = EOS;
strncpy(rstr,rval->o_value.ov_char.ovc_string,
(len = rval->o_value.ov_char.ovc_length)); rstr[len] = EOS;
return (db_cmp(lstr,rstr));
}
SHAR_EOF
cat << \SHAR_EOF > io.c
/* SDB - relation file I/O routines */
#include <stdio.h>
#include "sdbio.h"
/* global error code variable */
int dbv_errcode;
/* list of currently loaded relation definitions */
static struct relation *relations = NULL;
/* rfind - find the specified relation */
static struct relation *rfind(rname)
char *rname;
{
int fd;
char filename[RNSIZE+5];
struct relation *rptr;
/* look for relation in list currently loaded */
for (rptr = relations; rptr != NULL; rptr = rptr->rl_next)
if (db_sncmp(rname,rptr->rl_name,RNSIZE) == 0)
return (rptr);
/* create a file name */
make_fname(filename,rname);
/* lookup the relation file */
#ifdef Lattice
if ((fd = open(filename,0x8000)) == -1) /*dns*/
#else
if ((fd = open(filename,0)) == -1)
#endif
return (db_nerror(RELFNF));
/* allocate a new relation structure */
if ((rptr = malloc(sizeof(struct relation))) == NULL) {
close(fd);
return (db_nerror(INSMEM));
}
/* initialize the relation structure */
rptr->rl_scnref = 0;
/* read the header block */
if ( read(fd,&rptr->rl_header,512) != 512) {
free(rptr);
close(fd);
return (db_nerror(BADHDR));
}
/* close the relation file */
close(fd);
/* extract header information */
rptr->rl_tcnt = db_cvword(rptr->rl_header.hd_tcnt);
rptr->rl_tmax = db_cvword(rptr->rl_header.hd_tmax);
rptr->rl_data = db_cvword(rptr->rl_header.hd_data);
rptr->rl_size = db_cvword(rptr->rl_header.hd_size);
/* store the relation name */
strncpy(rptr->rl_name,rname,RNSIZE);
/* link new relation into relation list */
rptr->rl_next = relations;
relations = rptr;
/* return the new relation structure pointer */
return (rptr);
}
/* db_ropen - open a relation file */
struct scan *db_ropen(rname)
char *rname;
{
struct relation *rptr;
struct scan *sptr;
char filename[RNSIZE+5];
/* find the relation definition */
if ((rptr = rfind(rname)) == NULL)
return (NULL);
/* allocate a new scan structure */
if ((sptr = malloc(sizeof(struct scan))) == NULL)
return (db_nerror(INSMEM));
/* allocate a tuple buffer */
if ((sptr->sc_tuple = malloc(rptr->rl_size)) == NULL) {
free(sptr);
return (db_nerror(INSMEM));
}
/* initialize the scan structure */
sptr->sc_relation = rptr; /* store the relation struct addrs */
sptr->sc_dtnum = 0; /* desired tuple (non-existant) */
sptr->sc_atnum = 0; /* actual tuple (non-existant) */
sptr->sc_store = FALSE; /* no store done since open */
/* open relation file if necessary */
if (rptr->rl_scnref++ == 0) {
/* create the relation file name */
make_fname(filename,rname);
/* open the relation file */
#ifdef Lattice
if ((rptr->rl_fd = open(filename,0x8002)) == -1) { /*dns*/
#else
if ((rptr->rl_fd = open(filename,2)) == -1) {
#endif
rptr->rl_scnref--;
free(sptr->sc_tuple); free(sptr);
return (db_nerror(RELFNF));
}
}
/* return the new scan structure pointer */
return (sptr);
}
/* db_rclose - close the relation file */
int db_rclose(sptr)
struct scan *sptr;
{
struct relation *rptr,*lastrptr;
/* close relation file if this is the last reference */
if (--sptr->sc_relation->rl_scnref == 0) {
/* rewrite header if any stores took place */
if (sptr->sc_store) {
/* store the tuple count back in the header */
db_cvbytes(sptr->sc_relation->rl_tcnt,
sptr->sc_relation->rl_header.hd_tcnt);
/* write the header block */
lseek(sptr->sc_relation->rl_fd,0L,0);
if (write(sptr->sc_relation->rl_fd,
&sptr->sc_relation->rl_header,512) != 512) {
close(sptr->sc_relation->rl_fd);
free(sptr->sc_tuple); free(sptr);
return (db_ferror(BADHDR));
}
}
/* close the relation file */
close(sptr->sc_relation->rl_fd);
/* free the relation header */
lastrptr = NULL;
for (rptr = relations; rptr != NULL; rptr = rptr->rl_next) {
if (rptr == sptr->sc_relation) {
if (lastrptr == NULL)
relations = rptr->rl_next;
else
lastrptr->rl_next = rptr->rl_next;
}
lastrptr = rptr;
}
free(sptr->sc_relation);
}
/* free the scan structure */
free(sptr->sc_tuple); free(sptr);
/* return successfully */
return (TRUE);
}
/* db_rcompress - compress a relation file */
int db_rcompress(sptr)
struct scan *sptr;
{
unsigned int next,nextfree,tcnt;
/* get the last used tuple */
tcnt = sptr->sc_relation->rl_tcnt;
/* loop through all of the tuples */
for (next = nextfree = 1; next <= tcnt; next++) {
/* read the tuple */
seek(sptr,next);
if (read(sptr->sc_relation->rl_fd,
sptr->sc_tuple,sptr->sc_relation->rl_size)
!= sptr->sc_relation->rl_size)
return (db_ferror(TUPINP));
/* rewrite the tuple if it is active */
if (sptr->sc_tuple[0] == ACTIVE) {
/* rewrite it only if it must move */
if (next != nextfree) {
/* write the tuple */
seek(sptr,nextfree);
if (write(sptr->sc_relation->rl_fd,
sptr->sc_tuple,sptr->sc_relation->rl_size)
!= sptr->sc_relation->rl_size)
return (db_ferror(TUPOUT));
}
/* update the next free tuple number */
nextfree += 1;
}
}
/* update the tuple count */
sptr->sc_relation->rl_tcnt = nextfree - 1;
/* remember which tuple is in the buffer */
sptr->sc_atnum = sptr->sc_relation->rl_tcnt;
/* reset the desired tuple */
sptr->sc_dtnum = 0;
/* remember that the index needs rewriting */
sptr->sc_store = TRUE;
/* return successfully */
return (TRUE);
}
/* db_rbegin - begin scan at first tuple in relation */
db_rbegin(sptr)
struct scan *sptr;
{
/* begin with the first tuple in the file */
sptr->sc_dtnum = 0;
}
/* db_rfetch - fetch the next tuple from the relation file */
int db_rfetch(sptr)
struct scan *sptr;
{
/* look for an active tuple */
while (TRUE) {
/* check for this being the last tuple */
if (!db_rget(sptr,sptr->sc_dtnum + 1))
return (FALSE);
/* increment the tuple number */
sptr->sc_dtnum += 1;
/* return if the tuple found is active */
if (sptr->sc_tuple[0] == ACTIVE)
return (TRUE);
}
}
/* db_rupdate - update the current tuple */
int db_rupdate(sptr)
struct scan *sptr;
{
/* make sure the status byte indicates an active tuple */
sptr->sc_tuple[0] = ACTIVE;
/* write the tuple */
return (db_rput(sptr,sptr->sc_atnum));
}
/* db_rdelete - delete the current tuple */
int db_rdelete(sptr)
struct scan *sptr;
{
/* make sure the status byte indicates a deleted tuple */
sptr->sc_tuple[0] = DELETED;
/* write the tuple */
return (db_rput(sptr,sptr->sc_atnum));
}
/* db_rstore - store a new tuple */
int db_rstore(sptr)
struct scan *sptr;
{
/* make sure there's room for this tuple */
if (sptr->sc_relation->rl_tcnt == sptr->sc_relation->rl_tmax)
return (db_ferror(RELFUL));
/* make sure the status byte indicates an active tuple */
sptr->sc_tuple[0] = ACTIVE;
/* write the tuple */
if (!db_rput(sptr,sptr->sc_relation->rl_tcnt + 1))
return (FALSE);
/* update the tuple count */
sptr->sc_relation->rl_tcnt += 1;
/* remember that a tuple was stored */
sptr->sc_store = TRUE;
/* return successfully */
return (TRUE);
}
/* db_rget - get a tuple from the relation file */
int db_rget(sptr,tnum)
struct scan *sptr; unsigned int tnum;
{
/* check to see if the tuple is already in the buffer */
if (tnum == sptr->sc_atnum)
return (TRUE);
/* check for this being beyond the last tuple */
if (tnum > sptr->sc_relation->rl_tcnt)
return (db_ferror(TUPINP));
/* read the tuple */
seek(sptr,tnum);
if (read(sptr->sc_relation->rl_fd,
sptr->sc_tuple,sptr->sc_relation->rl_size)
!= sptr->sc_relation->rl_size)
return (db_ferror(TUPINP));
/* remember which tuple is in the buffer */
sptr->sc_atnum = tnum;
/* return successfully */
return (TRUE);
}
/* db_rput - put a tuple to a relation file */
int db_rput(sptr,tnum)
struct scan *sptr; unsigned int tnum;
{
/* check for this being beyond the maximum tuple */
if (tnum > sptr->sc_relation->rl_tmax)
return (db_ferror(TUPOUT));
/* write the tuple */
seek(sptr,tnum);
if (write(sptr->sc_relation->rl_fd,
sptr->sc_tuple,sptr->sc_relation->rl_size)
!= sptr->sc_relation->rl_size)
return (db_ferror(TUPOUT));
/* remember which tuple is in the buffer */
sptr->sc_atnum = tnum;
/* return successfully */
return (TRUE);
}
/* seek - seek a tuple in a relation file */
static seek(sptr,tnum)
struct scan *sptr; unsigned int tnum;
{
long offset;
offset = (long) sptr->sc_relation->rl_data +
((long) (tnum - 1) * (long) sptr->sc_relation->rl_size);
lseek(sptr->sc_relation->rl_fd,offset,0);
}
/* make_fname - make a relation name into a file name */
static make_fname(fname,rname)
char *fname,*rname;
{
strncpy(fname,rname,RNSIZE); fname[RNSIZE] = 0;
strcat(fname,".sdb");
}
/* db_nerror - store the error code and return NULL */
int db_nerror(errcode)
int errcode;
{
dbv_errcode = errcode;
return (NULL);
}
/* db_ferror - store the error code and return FALSE */
int db_ferror(errcode)
int errcode;
{
dbv_errcode = errcode;
return (FALSE);
}
/* db_cvword - convert 2 bytes to a word */
int db_cvword(bytes)
char bytes[2];
{
return (((bytes[1] & 0377) << 8) + (bytes[0] & 0377));
}
/* db_cvbytes - convert a word to 2 bytes */
db_cvbytes(word,bytes)
int word; char bytes[2];
{
bytes[0] = word;
bytes[1] = word >> 8;
}
SHAR_EOF
cat << \SHAR_EOF > scn.c
/* SDB - token scanning routines */
#include <stdio.h>
#include "sdbio.h"
int dbv_token; /* current token */
int dbv_tvalue; /* integer token value */
char dbv_tstring[STRINGMAX+1]; /* string token value */
struct ifile *dbv_ifp; /* indirect file context */
struct macro *dbv_macros; /* macro definitions */
int dbv_fold; /* case fold alpha comparisons */
static char *iprompt,*cprompt; /* input prompts */
static char cmdline[LINEMAX+2],*lptr; /* current line and pointer */
static int atbol; /* flag indicating at bol */
static int savech; /* lookahead character */
static int savetkn; /* lookahead token */
static char *keywords[] = { /* keyword table */
"ascending",
"by",
"char",
"compress",
"create",
"define",
"delete",
"descending",
"exit",
"export",
"extract",
"from",
"help",
"insert",
"import",
"into",
"num",
"print",
"quit",
"select",
"set",
"show",
"sort",
"update",
"using",
"where",
NULL
};
static int keytokens[] = { /* token values for each keyword */
ASCENDING,
BY,
CHAR,
COMPRESS,
CREATE,
DEFINE,
DELETE,
DESCENDING,
EXIT,
EXPORT,
EXTRACT,
FROM,
HELP,
INSERT,
IMPORT,
INTO,
NUM,
PRINT,
EXIT,
SELECT,
SET,
SHOW,
SORT,
UPDATE,
USING,
WHERE,
NULL
};
/* db_sinit - initialize the scanner */
db_sinit()
{
/* at beginning of line */
atbol = TRUE;
/* make the command line null */
lptr = NULL;
/* no lookahead yet */
savech = EOS;
savetkn = NULL;
/* no indirect command files */
dbv_ifp = NULL;
/* no macros defined */
dbv_macros = NULL;
/* fold alpha comparisons */
dbv_fold = TRUE;
}
/* db_prompt(ip,cp) - initialize prompt strings */
db_prompt(ip,cp)
char *ip,*cp;
{
/* save initial and continuation prompt strings */
iprompt = ip;
cprompt = cp;
}
/* db_scan(fmt,args) - initiate line scan command parsing */
db_scan(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
{
/* convert the command line and arguments */
sprintf(cmdline,fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10);
/* start at the beginning of the command line */
lptr = cmdline;
iprompt = NULL;
dbv_ifp = NULL;
/* no lookahead yet */
savech = EOS;
savetkn = NULL;
/* fold alpha comparisons */
dbv_fold = TRUE;
}
/* db_flush - flush the current input line */
int db_flush()
{
while (savech != '\n')
if (savech > ' ')
return (db_ferror(SYNTAX));
else
savech = getchx();
savech = EOS;
atbol = TRUE;
return (TRUE);
}
/* db_gline - get a line from the current input */
char *db_gline(buf)
char *buf;
{
int ch,i;
for (i = 0; (ch = getch()) != '\n' && ch != -1; )
if (i < LINEMAX)
buf[i++] = ch;
else {
printf("*** line too long ***\nRetype> ");
i = 0;
}
buf[i] = EOS;
return (buf);
}
/* db_ifile - setup an indirect command file */
int db_ifile(fname)
char *fname;
{
struct ifile *new_ifp;
if ((new_ifp = malloc(sizeof(struct ifile))) == NULL)
return (db_ferror(INSMEM));
else if ((new_ifp->if_fp = fopen(fname,"r")) == NULL) {
free(new_ifp);
return (db_ferror(INDFNF));
}
new_ifp->if_mtext = NULL;
new_ifp->if_savech = savech;
new_ifp->if_lptr = lptr;
new_ifp->if_next = dbv_ifp;
dbv_ifp = new_ifp;
/* return successfully */
return (TRUE);
}
/* db_kill - kill indirect command file input */
db_kill()
{
struct ifile *old_ifp;
while ((old_ifp = dbv_ifp) != NULL) {
dbv_ifp = old_ifp->if_next;
if (old_ifp->if_fp != NULL)
fclose(old_ifp->if_fp);
savech = old_ifp->if_savech;
lptr = old_ifp->if_lptr;
free(old_ifp);
}
while (savech != '\n')
savech = getchx();
savech = EOS;
savetkn = NULL;
atbol = TRUE;
}
/* db_token - return the current input token */
int db_token()
{
struct macro *mptr;
struct ifile *new_ifp;
/* find a token that's not a macro call */
while (db_xtoken() == ID) {
/* check for a macro call */
for (mptr = dbv_macros; mptr != NULL; mptr = mptr->mc_next)
if (db_scmp(dbv_tstring,mptr->mc_name) == 0) {
if ((new_ifp = malloc(sizeof(struct ifile))) == NULL)
printf("*** error expanding macro: %s ***\n",dbv_tstring);
else {
new_ifp->if_fp = NULL;
new_ifp->if_mtext = mptr->mc_mtext->mt_next;
new_ifp->if_lptr = lptr; lptr = mptr->mc_mtext->mt_text;
new_ifp->if_savech = savech; savech = EOS;
new_ifp->if_next = dbv_ifp;
dbv_ifp = new_ifp;
}
savetkn = NULL;
break;
}
if (mptr == NULL)
break;
}
return (dbv_token);
}
/* db_xtoken - return the current input token */
int db_xtoken()
{
int ch;
/* check for a saved token */
if ((dbv_token = savetkn) != NULL)
return (dbv_token);
/* get the next non-blank character */
ch = nextch();
/* check type of character */
if (isalpha(ch)) /* identifier or keyword */
get_id();
else if (isdigit(ch)) /* number */
get_number();
else if (ch == '"') /* string */
get_string();
else if (get_rel()) /* relational operator */
;
else /* single character token */
dbv_token = getch();
/* save the lookahead token */
savetkn = dbv_token;
/* return the token */
return (dbv_token);
}
/* db_ntoken - get next token (after skipping the current one) */
int db_ntoken()
{
/* get the current token */
db_token();
/* make sure another is read on next call */
savetkn = NULL;
/* return the current token */
return (dbv_token);
}
/* db_xntoken - get next token (after skipping the current one) */
int db_xntoken()
{
/* get the current token */
db_xtoken();
/* make sure another is read on next call */
savetkn = NULL;
/* return the current token */
return (dbv_token);
}
/* db_scmp - compare two strings */
int db_scmp(str1,str2)
char *str1,*str2;
{
if (dbv_fold)
return (scmp(str1,str2));
else
return (strcmp(str1,str2));
}
/* db_sncmp - compare two strings with a maximum length */
int db_sncmp(str1,str2,len)
char *str1,*str2; int len;
{
if (dbv_fold)
return (sncmp(str1,str2,len));
else
return (strncmp(str1,str2,len));
}
/* scmp - compare two strings with alpha case folding */
static int scmp(str1,str2)
char *str1,*str2;
{
int ch1,ch2;
/* compare each character */
while (*str1 && *str2) {
/* fold the character from the first string */
if (isupper(*str1))
ch1 = tolower(*str1++);
else
ch1 = *str1++;
/* fold the character from the second string */
if (isupper(*str2))
ch2 = tolower(*str2++);
else
ch2 = *str2++;
/* compare the characters */
if (ch1 != ch2)
if (ch1 < ch2)
return (-1);
else
return (1);
}
/* check for strings of different lengths */
if (*str1 == *str2)
return (0);
else if (*str1 == 0)
return (-1);
else
return (1);
}
/* sncmp - compare two strings with alpha case folding and a maximum length */
static int sncmp(str1,str2,len)
char *str1,*str2; int len;
{
int ch1,ch2;
/* compare each character */
while (*str1 && *str2 && len > 0) {
/* fold the character from the first string */
if (isupper(*str1))
ch1 = tolower(*str1++);
else
ch1 = *str1++;
/* fold the character from the second string */
if (isupper(*str2))
ch2 = tolower(*str2++);
else
ch2 = *str2++;
/* compare the characters */
if (ch1 != ch2)
if (ch1 < ch2)
return (-1);
else
return (1);
/* decrement the string length */
len--;
}
/* check for strings of different lengths */
if (len == 0 || *str1 == *str2)
return (0);
else if (*str1 == 0)
return (-1);
else
return (1);
}
/* get_id - get a keyword or a user identifier */
static get_id()
{
int ch,nchars,i;
/* input letters and digits */
ch = nextch();
nchars = 0;
while (isalpha(ch) || isdigit(ch)) {
if (nchars < KEYWORDMAX)
dbv_tstring[nchars++] = ch;
getch(); ch = thisch();
}
/* terminate the keyword */
dbv_tstring[nchars] = EOS;
/* assume its an identifier */
dbv_token = ID;
/* check for keywords */
for (i = 0; keywords[i] != NULL; i++)
if (db_scmp(dbv_tstring,keywords[i]) == 0)
dbv_token = keytokens[i];
}
/* get_number - get a number */
static get_number()
{
int ch,ndigits,nodot;
/* read digits and at most one decimal point */
ch = nextch();
ndigits = 0; nodot = TRUE;
while (isdigit(ch) || (nodot && ch == '.')) {
if (ch == '.')
nodot = FALSE;
if (ndigits < NUMBERMAX)
dbv_tstring[ndigits++] = ch;
getch(); ch = thisch();
}
/* terminate the number */
dbv_tstring[ndigits] = EOS;
/* get the value of the number */
sscanf(dbv_tstring,"%d",&dbv_tvalue);
/* token is a number */
dbv_token = NUMBER;
}
/* get_string - get a string */
static get_string()
{
int ch,nchars;
/* skip the opening quote */
getch();
/* read characters until a closing quote is found */
ch = thisch();
nchars = 0;
while (ch && ch != '"') {
if (nchars < STRINGMAX)
dbv_tstring[nchars++] = ch;
getch(); ch = thisch();
}
/* terminate the string */
dbv_tstring[nchars] = EOS;
/* skip the closing quote */
getch();
/* token is a string */
dbv_token = STRING;
}
/* get_rel - get a relational operator */
static int get_rel()
{
int ch;
switch (ch = nextch()) {
case '=':
getch();
dbv_token = EQL;
return (TRUE);;
case '<':
getch(); ch = nextch();
if (ch == '>') {
getch();
dbv_token = NEQ;
}
else if (ch == '=') {
getch();
dbv_token = LEQ;
}
else
dbv_token = LSS;
return (TRUE);;
case '>':
getch(); ch = nextch();
if (ch == '=') {
getch();
dbv_token = GEQ;
}
else
dbv_token = GTR;
return (TRUE);;
default:
return (FALSE);
}
}
/* getch - get the next character */
static int getch()
{
char fname[STRINGMAX+1];
int ch,i;
/* return the lookahead character if there is one */
if (savech != EOS) {
ch = savech;
savech = EOS;
return (ch);
}
/* get a character */
ch = getchx();
/* skip spaces at the beginning of a command */
if (atbol && iprompt != NULL)
while (ch <= ' ')
ch = getchx();
/* use continuation prompt next time */
iprompt = NULL;
/* check for indirect command file */
while (ch == '@') {
for (i = 0; (savech = getchx()) > ' '; )
if (i < STRINGMAX)
fname[i++] = savech;
fname[i] = 0;
if (db_ifile(fname) != TRUE)
printf("*** error opening command file: %s ***\n",fname);
ch = getchx();
}
/* return the character */
return (ch);
}
/* getchx - get the next character */
static int getchx()
{
struct ifile *old_ifp;
int ch;
/* check for input from buffer */
if (lptr != NULL) {
while (*lptr == EOS)
if (dbv_ifp != NULL)
if (dbv_ifp->if_mtext == NULL) {
old_ifp = dbv_ifp;
ch = dbv_ifp->if_savech; savech = EOS;
lptr = dbv_ifp->if_lptr;
dbv_ifp = dbv_ifp->if_next;
free(old_ifp);
if (ch != EOS)
return (ch);
if (lptr == NULL)
break;
}
else {
lptr = dbv_ifp->if_mtext->mt_text;
dbv_ifp->if_mtext = dbv_ifp->if_mtext->mt_next;
}
else
return (EOS);
if (lptr != NULL)
return (*lptr++);
}
/* print prompt if necessary */
if (atbol && dbv_ifp == NULL) { /*dns*/
if (iprompt != NULL)
printf("%s",iprompt);
else if (cprompt != NULL)
printf("%s",cprompt);
#ifdef Lattice
fflush(stdout); /*dns*/
#endif
} /*dns*/
if (dbv_ifp == NULL)
if ((ch = getcx(stdin)) == '\n')
atbol = TRUE;
else
atbol = FALSE;
else {
if ((ch = getcx(dbv_ifp->if_fp)) == -1) {
old_ifp = dbv_ifp;
ch = dbv_ifp->if_savech; savech = EOS;
lptr = dbv_ifp->if_lptr;
dbv_ifp = dbv_ifp->if_next;
fclose(old_ifp->if_fp);
free(old_ifp);
}
}
/* return the character */
return (ch);
}
/* thisch - get the current character */
static int thisch()
{
/* get a lookahead character */
if (savech == EOS)
savech = getch();
/* return lookahead character */
return (savech);
}
/* nextch - get the next non-blank character */
static int nextch()
{
int ch;
/* skip blank characters */
while ((ch = thisch()) <= ' ' && ch != EOS)
getch();
/* return the first non-blank */
return (ch);
}
SHAR_EOF
cat << \SHAR_EOF > cmd.c
/* SDB - command parser */
#include <stdio.h>
#include "sdbio.h"
extern int dbv_token;
extern char dbv_tstring[];
extern int dbv_tvalue;
extern struct ifile *dbv_ifp;
extern struct macro *dbv_macros;
extern int dbv_fold;
#ifdef Lattice
int _fmode = 0; /*dns*/
#endif
/* db_parse - parse a command */
int db_parse(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
char *fmt;
{
int sts;
/* check for a command line */
if (fmt != NULL)
db_scan(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);
/* determine the statement type */
switch (db_ntoken()) {
case ';': sts = TRUE;
break;
case COMPRESS:
sts = db_squeeze(NULL);
break;
case CREATE:
sts = create();
break;
case DEFINE:
sts = mc_define();
break;
case DELETE:
sts = delete();
break;
case EXIT:
exit();
case EXPORT:
sts = db_export(NULL);
break;
case EXTRACT:
sts = db_extract(NULL);
break;
case HELP:
sts = help();
break;
case IMPORT:
sts = db_import(NULL);
break;
case INSERT:
sts = insert();
break;
case PRINT:
sts = print();
break;
case SELECT:
sts = select();
break;
case SET:
sts = set();
break;
case SHOW:
sts = mc_show();
break;
case SORT:
sts = db_sort(NULL);
break;
case UPDATE:
sts = update();
break;
default:
return (db_ferror(SYNTAX));
}
return (sts);
}
/* help - print a short help message */
static int help()
{
FILE *fp;
int ch;
if ((fp = fopen("sdb.hlp","r")) != NULL) {
/* while ((ch = agetc(fp)) != EOF) dns */
while ((ch = getc(fp)) != EOF)
putchar(ch);
fclose(fp);
}
else
printf("No online help available. Read the manual\n");
/* return successfully */
return (TRUE);
}
/* create - create a new relation */
static int create()
{
struct relation *rptr;
char aname[STRINGMAX+1];
int atype;
/* get relation name */
if (db_ntoken() != ID)
return (db_ferror(SYNTAX));
/* start relation creation */
if ((rptr = db_rcreate(dbv_tstring)) == NULL)
return (FALSE);
/* check for attribute list */
if (db_ntoken() != '(') {
free(rptr);
return (db_ferror(SYNTAX));
}
/* parse the attributes */
while (TRUE) {
/* get the attribute name */
if (db_ntoken() != ID) {
free(rptr);
return (db_ferror(SYNTAX));
}
strcpy(aname,dbv_tstring);
/* get the attribute type */
db_ntoken();
if (dbv_token == CHAR)
atype = TCHAR;
else if (dbv_token == NUM)
atype = TNUM;
else {
free(rptr);
return (db_ferror(SYNTAX));
}
/* get the attribute size */
if (db_ntoken() != NUMBER) {
free(rptr);
return (db_ferror(SYNTAX));
}
/* add the attribute */
if (!db_rcattr(rptr,aname,atype,dbv_tvalue)) {
free(rptr);
return (FALSE);
}
/* check for end of attributes */
if (db_token() != ID)
break;
}
/* check for attribute list end */
if (db_ntoken() != ')') {
free(rptr);
return (db_ferror(SYNTAX));
}
/* check for relation size */
if (db_ntoken() != NUMBER) {
free(rptr);
return (db_ferror(SYNTAX));
}
/* finish relation creation */
if (!db_rcheader(rptr))
return (FALSE);
if (!db_rctuples(rptr,dbv_tvalue))
return (FALSE);
if (!db_rcdone(rptr))
return (FALSE);
/* return successfully */
return (TRUE);
}
/* insert - insert a tuple into a relation */
static int insert()
{
struct scan *sptr;
struct attribute *aptr;
char aname[ANSIZE+1],avalue[STRINGMAX+1];
int tcnt,astart,i;
/* get relation name */
if (db_token() == ID)
db_ntoken();
else
strcpy(dbv_tstring,"sdbcur");
/* make sure that the rest of the line is blank */
if (!db_flush())
return (FALSE);
/* open the relation */
if ((sptr = db_ropen(dbv_tstring)) == NULL)
return (FALSE);
/* insert tuples */
for (tcnt = 0; ; tcnt++) {
/* print separator if not the first tuple */
if (tcnt != 0)
printf("----\n");
/* get attribute values */
astart = 1;
for (i = 0; i < NATTRS; i++) {
/* get a pointer to the current attribute */
aptr = &sptr->sc_relation->rl_header.hd_attrs[i];
/* check for the last attribute */
if (aptr->at_name[0] == 0)
break;
/* get the attribute name */
strncpy(aname,aptr->at_name,ANSIZE); aname[ANSIZE] = 0;
/* setup null prompt strings */
db_prompt(NULL,NULL);
/* prompt and input attribute value */
while (TRUE) {
if (dbv_ifp == NULL)
if (strlen(aname) < 8)
printf("%s\t\t: ",aname);
else
printf("%s\t: ",aname);
if (db_gline(avalue) != NULL)
break;
}
/* check for last insert */
if (i == 0 && avalue[0] == EOS)
break;
/* store the attribute value */
db_aput(aptr,&sptr->sc_tuple[astart],avalue);
/* update the attribute start */
astart += aptr->at_size;
}
/* check for last insert */
if (avalue[0] == EOS)
break;
/* store the new tuple */
if (!db_rstore(sptr)) {
db_rclose(sptr);
return (FALSE);
}
}
/* close the relation */
db_rclose(sptr);
/* check number of tuples inserted */
if (tcnt != 0) {
/* print tuple count */
printf("[ %d inserted ]\n",tcnt);
}
else
printf("[ none inserted ]\n");
/* return successfully */
return (TRUE);
}
/* delete - delete tuples from a relation */
static int delete()
{
struct sel *slptr;
struct srel *srptr;
int tcnt;
/* parse the retrieval clause */
if ((slptr = db_retrieve(NULL)) == NULL)
return (FALSE);
/* loop through the retrieved tuples */
for (tcnt = 0; db_fetch(slptr); tcnt++)
/* delete the retrieved tuples */
for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next)
if (!db_rdelete(srptr->sr_scan)) {
db_done(slptr);
return (FALSE);
}
/* finish the retrieval */
db_done(slptr);
/* check number of tuples deleted */
if (tcnt != 0) {
/* print tuple count */
printf("[ %d deleted ]\n",tcnt);
}
else
printf("[ none deleted ]\n");
/* return successfully */
return (TRUE);
}
/* update - update tuples from a relation */
static int update()
{
struct sel *slptr;
struct sattr *saptr;
struct attribute *aptr;
char aname[ANSIZE+1],avalue[STRINGMAX+1],*ap;
int tcnt;
/* parse the selection clause */
if ((slptr = db_select(NULL)) == NULL)
return (FALSE);
/* make sure that the rest of the line is blank */
if (!db_flush()) {
db_done(slptr);
return (FALSE);
}
/* loop through the selected tuples */
for (tcnt = 0; db_fetch(slptr); tcnt++) {
/* print separator if not the first tuple */
if (tcnt != 0)
printf("----\n");
/* loop through the selected attributes */
for (saptr = slptr->sl_attrs; saptr != NULL; saptr = saptr->sa_next) {
/* get the attribute pointer */
aptr = saptr->sa_attr;
/* get the attribute name */
strncpy(aname,aptr->at_name,ANSIZE); aname[ANSIZE] = 0;
/* get the attribute value */
db_aget(aptr,saptr->sa_aptr,avalue);
for (ap = avalue; isspace(*ap); ap++)
;
/* print it */
if (strlen(aname) < 8)
printf("%s\t\t: %s\n",aname,ap);
else
printf("%s\t: %s\n",aname,ap);
/* setup null prompt strings */
db_prompt(NULL,NULL);
/* prompt and input attribute value */
while (TRUE) {
if (strlen(aname) < 8)
printf("%s\t\t: ",aname);
else
printf("%s\t: ",aname);
if (db_gline(avalue) != NULL)
break;
}
/* store the attribute value */
if (avalue[0] != EOS) {
db_aput(aptr,saptr->sa_aptr,avalue);
saptr->sa_srel->sr_update = TRUE;
}
}
/* update the tuples */
db_update(slptr);
}
/* finish the selection */
db_done(slptr);
/* check number of tuples updated */
if (tcnt != 0) {
/* print tuple count */
printf("[ %d updated ]\n",tcnt);
}
else
printf("[ none updated ]\n");
/* return successfully */
return (TRUE);
}
/* print - print tuples from a set of relations */
static int print()
{
struct sel *slptr;
FILE *ffp,*ofp;
int tcnt;
/* parse the using clause */
if (!using(&ffp,".frm"))
return (FALSE);
/* parse the select clause */
if ((slptr = db_select(NULL)) == NULL)
return (FALSE);
/* parse the into clause */
if (!db_to(&ofp,".txt")) {
db_done(slptr);
return (FALSE);
}
/* check for normal or formated output */
if (ffp == NULL)
tcnt = table(ofp,slptr);
else
tcnt = form(ofp,slptr,ffp);
/* finish the selection */
db_done(slptr);
/* close the form definition file */
if (ffp != NULL)
fclose(ffp);
/* close the output file */
if (ofp != stdout)
fclose(ofp);
/* check number of tuples selected */
if (tcnt != 0)
printf("[ %d found ]\n",tcnt);
else
printf("[ none found ]\n");
/* return successfully */
return (TRUE);
}
/* select - select tuples from a set of relations */
static int select()
{
struct sel *slptr;
struct relation *rptr;
struct sattr *saptr;
char *aname,*tbuf;
int tcnt,abase,i;
/* parse the select clause */
if ((slptr = db_select(NULL)) == NULL)
return (FALSE);
/* create a new relation */
if ((rptr = db_rcreate("sdbcur")) == NULL) {
db_done(slptr);
return (FALSE);
}
/* create the selected attributes */
for (saptr = slptr->sl_attrs; saptr != NULL; saptr = saptr->sa_next) {
/* decide which attribute name to use */
if ((aname = saptr->sa_name) == NULL)
aname = saptr->sa_aname;
/* add the attribute */
if (!db_rcattr(rptr,aname,saptr->sa_attr->at_type,
saptr->sa_attr->at_size)) {
free(rptr);
db_done(slptr);
return (FALSE);
}
}
/* create the relation header */
if (!db_rcheader(rptr)) {
db_done(slptr);
return (FALSE);
}
/* allocate and initialize a tuple buffer */
if ((tbuf = calloc(1,rptr->rl_size)) == NULL) {
db_rcdone(rptr);
return (db_ferror(INSMEM));
}
tbuf[0] = ACTIVE;
/* loop through the selected tuples */
for (tcnt = 0; db_fetch(slptr); tcnt++) {
/* create the tuple from the selected attributes */
abase = 1;
for (saptr = slptr->sl_attrs; saptr != NULL; saptr = saptr->sa_next) {
for (i = 0; i < saptr->sa_attr->at_size; i++)
tbuf[abase + i] = saptr->sa_aptr[i];
abase += i;
}
/* write the tuple */
if (write(rptr->rl_fd,tbuf,rptr->rl_size) != rptr->rl_size) {
db_rcdone(rptr);
free(tbuf);
return (db_ferror(INSBLK));
}
rptr->rl_tcnt++;
rptr->rl_tmax++;
}
/* finish the selection */
db_done(slptr);
/* finish relation creation */
if (!db_rcdone(rptr))
return (FALSE);
/* check number of tuples selected */
if (tcnt != 0)
printf("[ %d found ]\n",tcnt);
else
printf("[ none found ]\n");
/* return successfully */
return (TRUE);
}
/* mc_define - define a macro */
static int mc_define()
{
struct macro *mptr,*mlast;
struct mtext *tptr,*tlast;
char textline[LINEMAX+1];
/* get macro name */
if (db_xntoken() != ID)
return (db_ferror(SYNTAX));
/* make sure that the rest of the line is blank */
if (!db_flush())
return (FALSE);
/* find the macro in the macro table and free it */
for (mptr = dbv_macros, mlast = NULL; mptr != NULL; mlast = mptr, mptr = mptr->mc_next)
if (db_scmp(mptr->mc_name,dbv_tstring) == 0) {
if (mlast == NULL)
dbv_macros = mptr->mc_next;
else
mlast->mc_next = mptr->mc_next;
mc_free(mptr);
}
/* allocate and initialize a macro structure */
if ((mptr = malloc(sizeof(struct macro))) == NULL)
return (db_ferror(INSMEM));
if ((mptr->mc_name = malloc(strlen(dbv_tstring)+1)) == NULL) {
free(mptr);
return (db_ferror(INSMEM));
}
strcpy(mptr->mc_name,dbv_tstring);
mptr->mc_mtext = NULL;
/* setup null prompt strings */
db_prompt(NULL,"SDB-DEF> ");
/* get definition text */
for (tlast = NULL; ; tlast = tptr) {
/* get a line */
db_gline(textline);
if (textline[0] == EOS || textline[0] == '\n')
break;
/* allocate a macro text structure */
if ((tptr = malloc(sizeof(struct mtext))) == NULL) {
mc_free(mptr);
return (db_ferror(INSMEM));
}
if ((tptr->mt_text = malloc(strlen(textline)+1)) == NULL) {
mc_free(mptr);
return (db_ferror(INSMEM));
}
strcpy(tptr->mt_text,textline);
tptr->mt_next = NULL;
/* link it into the macro list */
if (tlast == NULL)
mptr->mc_mtext = tptr;
else
tlast->mt_next = tptr;
}
/* link the new macro into the macro list */
if (tlast == NULL)
mc_free(mptr);
else {
mptr->mc_next = dbv_macros;
dbv_macros = mptr;
}
/* return successfully */
return (TRUE);
}
/* mc_show - show a macro */
static int mc_show()
{
struct macro *mptr;
struct mtext *tptr;
/* get macro name */
if (db_xntoken() != ID)
return (db_ferror(SYNTAX));
/* find the macro in the macro table */
for (mptr = dbv_macros; mptr != NULL; mptr = mptr->mc_next)
if (db_scmp(mptr->mc_name,dbv_tstring) == 0) {
for (tptr = mptr->mc_mtext; tptr != NULL; tptr = tptr->mt_next)
printf("\t%s\n",tptr->mt_text);
break;
}
/* check for successful search */
if (mptr == NULL)
printf("*** no macro named: %s ***\n",dbv_tstring);
/* return successfully */
return (TRUE);
}
/* mc_free - free a macro definition */
static mc_free(mptr)
struct macro *mptr;
{
struct mtext *tptr;
while ((tptr = mptr->mc_mtext) != NULL) {
mptr->mc_mtext = tptr->mt_next;
free(tptr->mt_text);
free(tptr);
}
free(mptr->mc_name);
free(mptr);
}
/* db_to - redirect output into a file */
int db_to(pfp,ext)
FILE **pfp; char *ext;
{
#ifdef vms
int fd;
#endif
/* assume no into clause */
*pfp = stdout;
/* check for "into <fname>" */
if (db_token() != INTO)
return (TRUE);
db_ntoken();
if (db_ntoken() == ID)
strcat(dbv_tstring,ext);
else if (dbv_token != STRING)
return (db_ferror(SYNTAX));
/* open the output file */
#ifdef vms
if ((fd = creat(dbv_tstring,0,"rfm=var","rat=cr")) == -1)
return (db_ferror(OUTCRE));
*pfp = fdopen(fd,"w");
#else
#ifdef Lattice
_fmode = 0x8000; /*dns*/
#endif
*pfp = fopen(dbv_tstring,"w"); /*dns*/
#ifdef Lattice
_fmode = 0; /*dns*/
#endif
if (*pfp == NULL) /*dns*/
return (db_ferror(OUTCRE)); /*dns*/
#endif
/* return successfully */
return (TRUE);
}
/* using - get form definition file spec */
static int using(pfp,ext)
FILE **pfp; char *ext;
{
/* assume no using clause */
*pfp = NULL;
/* check for "using <fname>" */
if (db_token() != USING)
return (TRUE);
db_ntoken();
if (db_ntoken() == ID)
strcat(dbv_tstring,ext);
else if (dbv_token != STRING)
return (db_ferror(SYNTAX));
/* open the input file */
if ((*pfp = fopen(dbv_tstring,"r")) == NULL)
return (db_ferror(INPFNF));
/* return successfully */
return (TRUE);
}
/* table - output a relation table */
static int table(fp,slptr)
FILE *fp; struct sel *slptr;
{
int tcnt;
/* loop through the selected tuples */
for (tcnt = 0; db_fetch(slptr); tcnt++) {
/* print table head on first tuple selected */
if (tcnt == 0)
db_thead(fp,slptr);
/* print the tuple */
db_tentry(fp,slptr);
}
/* print table foot */
if (tcnt != 0)
db_tfoot(fp,slptr);
/* return the tuple count */
return (tcnt);
}
/* form - process a form */
static int form(ofp,slptr,ffp)
FILE *ofp; struct sel *slptr; FILE *ffp;
{
char aname[ANSIZE+1];
int ch,tcnt,pad;
/* loop through the selected tuples */
for (tcnt = 0; db_fetch(slptr); tcnt++) {
/* reposition the form definition file */
fseek(ffp,0L,0);
/* process the form */
while ((ch = getc(ffp)) != -1)
if (ch == '<') {
get_aname(ffp,aname,&pad);
put_avalue(ofp,slptr,aname,pad);
}
else
putc(ch,ofp);
}
/* return the tuple count */
return (tcnt);
}
/* get_aname - get an attribute name (and padding flag [mpk]) */
static get_aname(fp,aname,pad)
FILE *fp; char *aname; int *pad;
{
int ch;
if ((ch = getc(fp)) != '$') { /* mpk */
ungetc(ch,fp); /* put it back! */
*pad = 1;
}
else
*pad = 0;
while ((ch = getc(fp)) != '>')
if (!isspace(ch))
*aname++ = ch;
*aname = 0;
}
/* put_avalue - output attribute value */
static put_avalue(fp,slptr,aname,pad)
FILE *fp; struct sel *slptr; char *aname; int pad;
{
struct sattr *saptr;
char *saname;
int i;
/* loop through the selected attributes */
for (saptr = slptr->sl_attrs; saptr != NULL; saptr = saptr->sa_next) {
/* check the selected attribute name */
if ((saname = saptr->sa_name) == NULL)
saname = saptr->sa_aname;
if (db_scmp(saname,aname) == 0)
break;
}
if (saptr == NULL) {
fprintf(fp,"<error>");
return;
}
/* get the attribute value */
for (i = 0; i < saptr->sa_attr->at_size; i++)
if (saptr->sa_aptr[i] != 0)
putc(saptr->sa_aptr[i],fp);
else if (pad) /* MPK 25-Mar-1986 */
putc(' ',fp);
}
/* set - set internal parameters */
static int set()
{
int value;
/* process each set request */
while (db_token() == ID) {
/* skip the identifier */
db_ntoken();
/* check for "no" */
if (db_scmp(dbv_tstring,"no") == 0) {
value = FALSE;
if (db_token() != ID)
return (db_ferror(BADSET));
db_ntoken();
}
else
value = TRUE;
/* check for parameter to set */
if (db_scmp(dbv_tstring,"fold") == 0)
dbv_fold = value;
else
return (db_ferror(BADSET));
}
/* return successfully */
return (TRUE);
}
SHAR_EOF
# End of shell archive
exit 0
Technical Details
Message-ID:
<1851@j.cc.purdue.edu>Path:
arc1-ca.pugleaf.net!utzoo!mnetor!seismo!gatech!akgua!mhuxv!mhuxf!mhuxm!mhuxn!ihnp4!inuxc!pur-ee!j.cc.purdue.edu!docShow Full Headers
Complete Article Headers
Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: arc1-ca.pugleaf.net!utzoo!mnetor!seismo!gatech!akgua!mhuxv!mhuxf!mhuxm!mhuxn!ihnp4!inuxc!pur-ee!j.cc.purdue.edu!doc From: doc@pucc-j.UUCP Newsgroups: mod.amiga.sources Subject: sdb Database (2 of 3) Message-ID: <1851@j.cc.purdue.edu> Date: Fri, 22-Aug-86 18:46:03 EDT Article-I.D.: j.1851 Posted: Fri Aug 22 18:46:03 1986 Date-Received: Sun, 24-Aug-86 09:03:49 EDT Sender: doc@j.cc.purdue.edu Organization: Purdue University Computing Center Lines: 2129 Approved: doc@pucc-j.UUCP