[mod.amiga.sources] sdb Database

doc@pucc-j.UUCP (08/23/86)

    [ moderators note:  Makefile included should work with Aztec C.  The file
    pcjunk.c was for IBM pc stuff.  It should not be needed, but it put in
    for completeness...  Note it is not included in OBJ in makefile ]


I found this on the Spring 1984 DECUS VAX SIG tape.  I compiled it
on my Amiga with Lattice C version 3.03, encountering no problems
that I can remember (but it's been a few months since then...).  Since
it was written to run on a 16-bit machine to start with, there shouldn't
be any real problems compiling it with Manx Aztec using 16-bit ints.

I have made one change to what I found on the tape: when printing out
a tuple using a form (.FRM) definition, you can use a dollar sign at
the beginning of an attribute to indicate that no padding should be
done. This helps when preparing things like form letters, where you
definitely *don't* want the records to be padded.

To build SDB, compile *.c into *.o, then ALink WITH sdb.lnk.

Enjoy!

Mic Kaczmarczik
User Services Digital Support Group
University of Texas at Austin Computation Center

UUCP:	...!ihnp4!seismo!ut-sally!ut-ngp!mic
ARPA:	mic@ngp.cc.utexas.edu
	cc.kaczmarczik@a20.cc.utexas.edu

#	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:
#	readme
#	readme.too
#	aaareadme.txt
#	Makefile
#	sdb.lnk
#	sdb.c
#	sdb.hlp
#	pcjunk.c
#	junk.c
#	tbl.c
#	iex.c
#	mth.c
#	sdbio.h
#	srt.c
#	com.c
# This archive created: Fri Aug 22 17:39:24 1986
# By:	Craig Norborg (Purdue University Computing Center)
cat << \SHAR_EOF > readme


     SDB was obtained from the CNODE bulletin board at 617-470-2548
     in Amherst, Mass.  It was converted to the IBM PC using the Lattice
     C Compiler.

     To try some other compiler or system, see comments and settings in
     the front of SDBIO.H and things in PCJUNK.C.

     David N. Smith
     44 Ole Musket Lane
     Danbury, CT 06810

     24 January 1984

SHAR_EOF
cat << \SHAR_EOF > readme.too
I found this on the Spring 1984 DECUS VAX SIG tape.  I compiled it
on my Amiga with Lattice C version 3.03, encountering no problems
that I can remember (but it's been a few months since then...).  Since
it was written to run on a 16-bit machine to start with, there shouldn't
be any real problems compiling it with Manx Aztec using 16-bit ints.

I have made one change to what I found on the tape: when printing out
a tuple using a form (.FRM) definition, you can use a dollar sign at
the beginning of an attribute to indicate that no padding should be
done. This helps when preparing things like form letters, where you
definitely *don't* want the records to be padded.

To build SDB, compile *.c into *.o, then ALink WITH sdb.lnk.

Enjoy!

Mic Kaczmarczik
User Services Digital Support Group
University of Texas at Austin Computation Center

UUCP:	...!ihnp4!seismo!ut-sally!ut-ngp!mic
ARPA:	mic@ngp.cc.utexas.edu
	cc.kaczmarczik@a20.cc.utexas.edu
SHAR_EOF
cat << \SHAR_EOF > aaareadme.txt
This area contains a simple DBMS system in C. Originally for VAX, it
has been somewhat modified for IBMPC but since full sources are
here, it shouldn't be too hard to change back if anyone needs it.
	From a PC-sig disk (#147).
SHAR_EOF
cat << \SHAR_EOF > Makefile
SRC =   cmd.c com.c cre.c err.c iex.c int.c io.c junk.c mth.c pcjunk.c \
	scn.c sdb.c sel.c srt.c tbl.c
OBJ =   cmd.o com.o cre.o err.o iex.o int.o io.o junk.o mth.o \
	scn.o sdb.o sel.o srt.o tbl.o
HDR =	sdbio.h
FILES = aaareadme.txt readme readme.too sdb.hlp sdb.lnk sdb.mem

all: sdb

sdb: $(OBJ)
	cc $(CFLAGS) $(OBJ) -o sdb

cmd.o: cmd.c sdbio.h

com.o: com.c sdbio.h

cre.o: cre.c sdbio.h

err.o: err.c sdbio.h

iex.o: iex.c sdbio.h

int.o: int.c sdbio.h

io.o: io.c sdbio.h

junk.o: junk.c sdbio.h

mth.o: mth.c

pcjunk.o: pcjunk.c sdbio.h

scn.o: scn.c sdbio.h

sdb.o: sdb.c sdbio.h

sel.o: sdbio.h sel.c

srt.o: sdbio.h srt.c

tbl.o: sdbio.h tbl.c
SHAR_EOF
cat << \SHAR_EOF > sdb.lnk
FROM df0:lib/Lstartup.obj,*
int.o,*
cmd.o,*
sel.o,*
tbl.o,*
mth.o,*
scn.o,*
err.o,*
cre.o,*
iex.o,*
io.o,*
srt.o,*
com.o,*
junk.o,*
sdb.o
TO SDB
LIBRARY df0:lib/lc.lib,df0:lib/amiga.lib
SHAR_EOF
cat << \SHAR_EOF > sdb.c
/* SDB - main routine */

#include <stdio.h>
#include "sdbio.h"

extern int dbv_errcode;

main()
{
    printf("SDB - version 2.0\n");
    db_sinit();
    db_ifile("sdb.ini");

    while (TRUE) {
        db_prompt("SDB> ","\t> ");
        if (!db_parse(NULL)) {
            printf("** error: %s ***\n",db_ertxt(dbv_errcode));
            db_kill();
        }
    }
}

SHAR_EOF
cat << \SHAR_EOF > sdb.hlp
create <rname> ( <adefs> ) <size>       - create a new relation
insert <rname>                          - insert tuples into a relation
delete <rse> ;                          - delete tuples from relations
update <anames> from <rse> ;            - update tuples within a relation
print [<u>] <anames> from <rse> [<i>] ; - print tuples
sort <rname> by <snames> ;              - sort a relation file
import <fname> into <rname>             - import tuples from a file
export <rname> [<i>] ;                  - export tuples to a file
extract <rname> [<i>] ;                 - extract definition to a file
compress <rname>                        - compress a relation file
define <mname>                          - define a macro
exit                                    - exit SDB

<u>             ::=     using <fname>
<i>             ::=     into <fname>
<adefs>         ::=     <a list of attribute definitions>
<rse>           ::=     <rnames> [where <boolean-expression>]
<rname>         ::=     <a relation name>
<rnames>        ::=     <a comma separated list of <rname>s>
<aname>         ::=     [<rname> .] <an attribute name>
<anames>        ::=     * | <a comma separated list of <aname>s>
<sname>         ::=     <aname> { ascending | descending }
<snames>        ::=     <a comma separated list of <sname>s>
SHAR_EOF
cat << \SHAR_EOF > pcjunk.c
/*  PCJUNK.C     Misc. procedures */

#include "stdio.h"
#include "sdbio.h"

/*    Replaces JUNK.C for the IBM/PC version            dns */


char *alloc(n)
int n;
{
   return (char*) malloc(n);
}


int getcx(fp)
  FILE *fp;
{
    static char buf[LINEMAX] = {0};
    static char *pbuf = buf;
    int ch, i;

    if (fp!=stdin)
       if ((ch = getc(fp)) == '\r')
          return getc(fp);
       else
          return ch;

    if (*pbuf > 0)
       return *pbuf++;

    pbuf = buf;
    for (i = 0; (ch = getc(fp)) != -1; )
        if (i < LINEMAX)  {
            if (ch == ESC)     { i=0;  putchar('\n'); fflush(stdout); }  else
            if (ch == BS)      { if (i>0)  i--;                       }  else
               buf[i++] = ch;
            if (ch == '\n')    break;
            }
        else {
            printf("*** line too long ***\nRetype> ");
            i = 0;
        }
    buf[i] = EOS;
    return getcx(fp);
}



/*    string copy up to n characters    */
strncpy(to, from, n)
char *to, *from;
int n;
{
  char *cp;

  for( cp=to; n-- && (*cp++ = *from++);  ) ;
  if( n<0 )   *cp = 0;
  return to;
}


/*    string compare up to n characters     */
strncmp(s1, s2, n)
char *s1, *s2;
int n;
{

  for( ;n-- && (*s1==*s2); s2++ )
     if( !*s1++ )
        return 0;
  if( n < 0 )
     return 0;
  if( *s1 < *s2 )
     return -1;
  return 1;
}
SHAR_EOF
cat << \SHAR_EOF > junk.c
/*  PCJUNK.C     Misc. procedures */

#include <stdio.h>
#include "sdbio.h"

/*    Replaces JUNK.C for the IBM/PC version            dns */


char *alloc(n)
int n;
{
   return (char*) malloc(n);
}


int getcx(fp)
  FILE *fp;
{
#ifdef Lattice
    static char buf[LINEMAX] = {0};
    static char *pbuf = buf;
    int ch, i;

    if (fp!=stdin)
       if ((ch = getc(fp)) == '\r')
          return getc(fp);
       else
          return ch;

    if (*pbuf > 0)
       return *pbuf++;

    pbuf = buf;
    for (i = 0; (ch = getc(fp)) != -1; )
        if (i < LINEMAX)  {
            if (ch == ESC)     { i=0;  putchar('\n'); fflush(stdout); }  else
            if (ch == BS)      { if (i>0)  i--;                       }  else
               buf[i++] = ch;
            if (ch == '\n')    break;
            }
        else {
            printf("*** line too long ***\nRetype> ");
            i = 0;
        }
    buf[i] = EOS;
    return getcx(fp);
#else
    return(getc(fp));
#endif
}



/*    string copy up to n characters    */
#ifdef Lattice
strncpy(to, from, n)
char *to, *from;
int n;
{
  char *cp;

  for( cp=to; n-- && (*cp++ = *from++);  ) ;
  if( n<0 )   *cp = 0;
  return to;
}


/*    string compare up to n characters     */
strncmp(s1, s2, n)
char *s1, *s2;
int n;
{

  for( ;n-- && (*s1==*s2); s2++ )
     if( !*s1++ )
        return 0;
  if( n < 0 )
     return 0;
  if( *s1 < *s2 )
     return -1;
  return 1;
}
#endif


SHAR_EOF
cat << \SHAR_EOF > tbl.c
/* SDB - table output routines */

#include <stdio.h>
#include "sdbio.h"

static char buffer[TABLEMAX+1];
int bndx;

/* db_thead - print a table header */
db_thead(fp,slptr)
  FILE *fp; struct sel *slptr;
{
    struct sattr *saptr;
    int twidth,fwidth,i;
    char *aname;

    /* compute the table width */
    twidth = 1;
    for (saptr = slptr->sl_attrs; saptr != NULL; saptr = saptr->sa_next)
        twidth += saptr->sa_attr->at_size + 3;

    /* print the top line of the table */
    bstart();
    for (i = 0; i < twidth; i++)
        binsert('-');
    bprint(fp);

    /* print the label line of the table */
    bstart();
    for (saptr = slptr->sl_attrs; saptr != NULL; saptr = saptr->sa_next) {
        fwidth = saptr->sa_attr->at_size;
        binsert('|'); binsert(' ');
        if ((aname = saptr->sa_name) == NULL)
            aname = saptr->sa_aname;
        for (i = 0; i < fwidth; i++)
            if (*aname != 0)
                binsert(*aname++);
            else
                binsert(' ');
        binsert(' ');
    }
    binsert('|');
    bprint(fp);

    /* print the line under the labels */
    bstart();
    for (i = 0; i < twidth; i++)
        binsert('-');
    bprint(fp);
}

/* db_tfoot - print a table foot */
db_tfoot(fp,slptr)
  FILE *fp; struct sel *slptr;
{
    struct sattr *saptr;
    int twidth,i;

    /* compute the table width */
    twidth = 1;
    for (saptr = slptr->sl_attrs; saptr != NULL; saptr = saptr->sa_next)
        twidth += saptr->sa_attr->at_size + 3;

    /* print the line at the foot of the table */
    bstart();
    for (i = 0; i < twidth; i++)
        binsert('-');
    bprint(fp);
}

/* db_tentry - print a table entry */
db_tentry(fp,slptr)
  FILE *fp; struct sel *slptr;
{
    struct sattr *saptr;
    int fwidth,i;

    /* print a table entry */
    bstart();
    for (saptr = slptr->sl_attrs; saptr != NULL; saptr = saptr->sa_next) {
        fwidth = saptr->sa_attr->at_size;
        binsert('|'); binsert(' ');
        for (i = 0; i < fwidth; i++)
            if (saptr->sa_aptr[i] != 0)
                binsert(saptr->sa_aptr[i]);
            else
                binsert(' ');
        binsert(' ');
    }
    binsert('|');
    bprint(fp);
}

/* bstart - start building a line */
static bstart()
{
    bndx = 0;
}

/* binsert - insert a character into the buffer */
static binsert(ch)
  int ch;
{
    if (bndx < TABLEMAX)
        buffer[bndx++] = ch;
}

/* bprint - print the current line */
static bprint(fp)
  FILE *fp;
{
    buffer[bndx] = EOS;
    fprintf(fp,"%s\n",buffer);
}
SHAR_EOF
cat << \SHAR_EOF > iex.c
/* SDB - import/export command routines */

#include <stdio.h>
#include "sdbio.h"

extern int dbv_token;
extern char dbv_tstring[];
extern int dbv_tvalue;

/* db_import - import tuples from a file */
int *db_import(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
  char *fmt;
{
    struct scan *sptr;
    struct attribute *aptr;
    char fname[STRINGMAX+1],avalue[STRINGMAX+1];
    int tcnt,astart,i,eofile;
    FILE *fp;

    /* check for a command line */
    if (fmt != NULL)
        db_scan(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);

    /* checks for "<filename> into <relation-name>" */
    if (db_ntoken() == ID)
        strcat(dbv_tstring,".dat");
    else if (dbv_token != STRING)
        return (db_ferror(SYNTAX));
    strcpy(fname,dbv_tstring);
    if (db_ntoken() != INTO)
        return (db_ferror(SYNTAX));
    if (db_ntoken() != ID)
        return (db_ferror(SYNTAX));

    /* open the relation */
    if ((sptr = db_ropen(dbv_tstring)) == NULL)
        return (FALSE);

    /* open the input file */
    if ((fp = fopen(fname,"r")) == NULL)
        return (db_ferror(INPFNF));

    /* import tuples */
    eofile = FALSE;
    for (tcnt = 0; ; tcnt++) {

        /* 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;

            /* input the tuple */
            if (fgets(avalue,STRINGMAX,fp) == 0) {
                eofile = TRUE;
                break;
            }
            avalue[strlen(avalue)-1] = EOS;

            /* store the attribute value */
            db_aput(aptr,&sptr->sc_tuple[astart],avalue);

            /* update the attribute start */
            astart += aptr->at_size;
        }

        /* store the new tuple */
        if (!eofile) {
            if (!db_rstore(sptr)) {
                db_rclose(sptr);
                return (FALSE);
            }
        }
        else
            break;
    }

    /* close the relation */
    db_rclose(sptr);

    /* close the input file */
    fclose(fp);

    /* check number of tuples imported */
    if (tcnt != 0) {

        /* print tuple count */
        printf("[ %d imported ]\n",tcnt);
    }
    else
        printf("[ none imported ]\n");

    /* return successfully */
    return (TRUE);
}

/* db_export - export tuples to a file */
int *db_export(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
  char *fmt;
{
    struct scan *sptr;
    struct attribute *aptr;
    char rname[STRINGMAX+1],avalue[STRINGMAX+1];
    int tcnt,astart,i;
    FILE *fp;

    /* check for a command line */
    if (fmt != NULL)
        db_scan(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);

    /* checks for "<relation-name> [ into <filename> ]" */
    if (db_ntoken() != ID)
        return (db_ferror(SYNTAX));
    strcpy(rname,dbv_tstring);
    if (!db_to(&fp,".dat"))
        return (FALSE);

    /* open the relation */
    if ((sptr = db_ropen(rname)) == NULL)
        return (FALSE);

    /* export tuples */
    for (tcnt = 0; db_rfetch(sptr); tcnt++) {

        /* 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 value */
            db_aget(aptr,&sptr->sc_tuple[astart],avalue);

            /* output the tuple */
            fprintf(fp,"%s\n",avalue);

            /* update the attribute start */
            astart += aptr->at_size;
        }
    }

    /* close the relation */
    db_rclose(sptr);

    /* close the output file */
    if (fp != stdout)
        fclose(fp);

    /* check number of tuples exported */
    if (tcnt != 0) {

        /* print tuple count */
        printf("[ %d exported ]\n",tcnt);
    }
    else
        printf("[ none exported ]\n");

    /* return successfully */
    return (TRUE);
}

/* db_squeeze - squeeze deleted tuples from a relation file */
int *db_squeeze(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
  char *fmt;
{
    struct scan *sptr;

    /* check for a command line */
    if (fmt != NULL)
        db_scan(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);

    /* checks for "<relation-name>" */
    if (db_ntoken() != ID)
        return (db_ferror(SYNTAX));

    /* open the relation */
    if ((sptr = db_ropen(dbv_tstring)) == NULL)
        return (FALSE);

    /* compress the relation file */
    if (!db_rcompress(sptr)) {
        db_rclose(sptr);
        return (FALSE);
    }

    /* close the relation */
    db_rclose(sptr);

    /* return successfully */
    return (TRUE);
}

/* db_extract - extract a relation definition */
int *db_extract(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
  char *fmt;
{
    struct scan *sptr;
    struct attribute *aptr;
    char rname[STRINGMAX+1],aname[ANSIZE+1],*atype;
    int i;
    FILE *fp;

    /* check for a command line */
    if (fmt != NULL)
        db_scan(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);

    /* checks for "<relation-name> [ into <filename> ]" */
    if (db_ntoken() != ID)
        return (db_ferror(SYNTAX));
    strcpy(rname,dbv_tstring);
    if (!db_to(&fp,".def"))
        return (FALSE);

    /* open the relation */
    if ((sptr = db_ropen(rname)) == NULL)
        return (FALSE);

    /* output the relation definition */
    fprintf(fp,"create %s (\n",rname);

    /* get attribute values */
    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;

        /* determine the attribute type */
        switch (aptr->at_type) {
        case TCHAR:
                atype = "char";
                break;
        case TNUM:
                atype = "num";
                break;
        default:
                atype = "<error>";
                break;
        }

        /* output the attribute definition */
        if (strlen(aname) < 8)
            fprintf(fp,"\t%s\t\t%s\t%d\n",aname,atype,aptr->at_size);
        else
            fprintf(fp,"\t%s\t%s\t%d\n",aname,atype,aptr->at_size);
    }

    /* output the relation size */
    fprintf(fp,") %d\n",sptr->sc_relation->rl_tmax);

    /* close the relation */
    db_rclose(sptr);

    /* close the output file */
    if (fp != stdout)
        fclose(fp);

    /* return successfully */
    return (TRUE);
}
SHAR_EOF
cat << \SHAR_EOF > mth.c
/* SDB - string arithmetic routines */

#include <ctype.h>  /*dns*/

/* useful definitions */
#define TRUE            1
#define FALSE           0
#define NUMBERMAX       99
#define EOS             0

/* db_cmp - compare two signed numeric strings */
int db_cmp(arg1,arg2)
  char *arg1,*arg2;
{
    int sign1,sign2;

    /* get the signs of the arguments */
    sign1 = getsign(&arg1);
    sign2 = getsign(&arg2);

    /* compute the result */
    if (sign1 == sign2) {
        if (sign1 == 1)
            return (compare(arg1,arg2));
        else
            return (compare(arg2,arg1));
    }

    /* signs are different */
    return (sign1);
}

/* db_add - add two signed numeric strings */
db_add(result,arg1,arg2)
  char *result,*arg1,*arg2;
{
    int signr,sign1,sign2;
    char rtmp[NUMBERMAX+2],*rptr;

    /* get the signs of the arguments */
    sign1 = getsign(&arg1);
    sign2 = getsign(&arg2);

    /* compute the result */
    if (sign1 == sign2) {
        signr = sign1;
        add(rtmp,arg1,arg2);
    }
    else
        switch (compare(arg1,arg2)) {
        case -1:
                signr = sign2;
                sub(rtmp,arg2,arg1);
                break;
        case 0:
                strcpy(result,"0");
                return;
        case 1:
                signr = sign1;
                sub(rtmp,arg1,arg2);
                break;
        }

    /* insert the sign */
    if (signr == -1)
        strcpy(result,"-");
    else
        result[0] = EOS;

    /* eliminate possible leading zeros */
    for (rptr = rtmp; *rptr == '0'; rptr++)
        ;

    /* copy the result */
    if (*rptr != EOS)
        strcat(result,rptr);
    else
        strcat(result,"0");
}

/* db_sub - subtract two signed numeric strings */
db_sub(result,arg1,arg2)
  char *result,*arg1,*arg2;
{
    int signr,sign1,sign2;
    char rtmp[NUMBERMAX+2],*rptr;

    /* get the signs of the arguments */
    sign1 = getsign(&arg1);
    sign2 = getsign(&arg2);

    /* invert the sign of arg2 */
    sign2 = -sign2;

    /* compute the result */
    if (sign1 == sign2) {
        signr = sign1;
        add(rtmp,arg1,arg2);
    }
    else
        switch (compare(arg1,arg2)) {
        case -1:
                signr = sign2;
                sub(rtmp,arg2,arg1);
                break;
        case 0:
                strcpy(result,"0");
                return;
        case 1:
                signr = sign1;
                sub(rtmp,arg1,arg2);
                break;
        }

    /* insert the sign */
    if (signr == -1)
        strcpy(result,"-");
    else
        result[0] = EOS;

    /* eliminate a possible leading zero */
    for (rptr = rtmp; *rptr == '0'; rptr++)
        ;

    /* copy the result */
    if (*rptr != EOS)
        strcat(result,rptr);
    else
        strcat(result,"0");
}

/* add - add two unsigned numeric strings */
static add(result,arg1,arg2)
  char *result,*arg1,arg2;
{
    char *vr,*v1,*v2,*vx;
    int carry,i,nmin,nmax,nd1,nd2;


    /* get number of digits in each argument */
    nd1 = getlen(arg1);
    nd2 = getlen(arg2);

    /* compute the address of the last digit + 1 */
    v1 = arg1 + nd1;
    v2 = arg2 + nd2;

    /* compute minimum and maximum of nd1 and nd2 */
    if (nd1 < nd2) {
        nmin = -nd1;
        nmax = -nd2;
        vx = v2;
    }
    else {
        nmin = -nd2;
        nmax = -nd1;
        vx = v1;
    }

    /* compute the address of the last result digit + 1 */
    vr = result - nmax; vr[0] = EOS;

    /* initialize the carry */
    carry = 0;

    /* add the digits */
    for (i = -1; i >= nmin; i--) {

        /* check for decimal point */
        if (v1[i] == '.')
            vr[i] = '.';
        else {

            /* add the current pair of digits with the carry */
            vr[i] = v1[i] + v2[i] + carry - '0';

            /* check for carry */
            if (vr[i] > '9') {
                vr[i] -= 10;
                carry = 1;
            }
            else
                carry = 0;
        }
    }

    /* add the remaining digits */
    for (; i >= nmax; i--) {

        /* add the current digit with the carry */
        vr[i] = vx[i] + carry;

        /* check for carry */
        if (vr[i] > '9') {
            vr[i] -= 10;
            carry = 1;
        }
        else
            carry = 0;
    }

    /* check for a carry out and shift digits if necessary */
    if (carry == 1) {

        /* shift the digits */
        for (i = -nmax; i >= 0; i--)
            result[i+1] = result[i];
        result[0] = '1';
    }
}

/* sub - subtract two unsigned numeric strings */
static sub(result,arg1,arg2)
  char *result,*arg1,arg2;
{
    char *vr,*v1,*v2,*vx;
    int borrow,i,nmin,nmax,nd1,nd2;

    /* get number of digits in each argument */
    nd1 = getlen(arg1);
    nd2 = getlen(arg2);

    /* compute the address of the last digit + 1 */
    v1 = arg1 + nd1;
    v2 = arg2 + nd2;

    /* compute minimum and maximum of nd1 and nd2 */
    if (nd1 < nd2) {
        nmin = -nd1;
        nmax = -nd2;
        vx = v2;
    }
    else {
        nmin = -nd2;
        nmax = -nd1;
        vx = v1;
    }

    /* compute the address of the last result digit + 1 */
    vr = result - nmax; vr[0] = EOS;

    /* initialize the borrow */
    borrow = 0;

    /* subtract the digits */
    for (i = -1; i >= nmin; i--) {

        /* check for decimal point */
        if (v1[i] == '.')
            vr[i] = '.';
        else {

            /* subtract the current pair of digits with the borrow */
            vr[i] = v1[i] - v2[i] - borrow + '0';

            /* check for borrow */
            if (vr[i] < '0') {
                vr[i] += 10;
                borrow = 1;
            }
            else
                borrow = 0;
        }
    }

    /* subtract the remaining digits */
    for (; i >= nmax; i--) {

        /* subtract the current digit with the borrow */
        vr[i] = vx[i] - borrow;

        /* check for borrow */
        if (vr[i] < '0') {
            vr[i] += 10;
            borrow = 1;
        }
        else
            borrow = 0;
    }
}

/* getsign - get the sign of a numeric string */
static int getsign(parg)
  char **parg;
{
    int sign;
    char *p;

    /* eliminate leading spaces */
    while (isspace(**parg))
        *parg += 1;

    /* get the sign */
    switch (**parg) {
    case '+':   *parg += 1;
                sign = 1;
                break;
    case '-':   *parg += 1;
                sign = -1;
                break;
    default:    sign = 1;
                break;
    }

    /* eliminate leading spaces and zeros */
    while (isspace(**parg) || **parg == '0')
        *parg += 1;

    /* if the value is zero, make sure that the sign is positive */
    switch (**parg) {
    case EOS:   sign = 1;
                break;
    case '.':   for (p = *parg + 1; *p == '0'; p++)
                    ;
                if (*p == EOS)
                    sign = 1;
                break;
    }

    /* return the sign */
    return (sign);
}

/* getlen - get the length of a numeric string */
static int getlen(arg)
  char *arg;
{
    int len;

    /* get the length of the string */
    len = strlen(arg);

    /* eliminate trailing spaces */
    while (isspace(arg[len-1]))
        len -= 1;

    /* return the length */
    return (len);
}

/* compare - compare two unsigned numeric strings */
static int compare(arg1,arg2)
  char *arg1,*arg2;
{
    int len1,len2;

    /* compare lengths */
    if ((len1 = getlen(arg1)) != (len2 = getlen(arg2)))
        if (len1 < len2)
            return (-1);
        else
            return (1);

    /* compare strings */
    return (strcmp(arg1,arg2));
}

SHAR_EOF
cat << \SHAR_EOF > sdbio.h
/* SDB - definition file */

#include <ctype.h>  /*dns*/

/* compiler specific stuff  (dns) */
/* #define Lattice */

/* useful definitions */
#define TRUE            1
#define FALSE           0
#ifndef NULL
#define NULL            0
#endif

/* Character definitions   (dns) */
#define BS  0x8       /*dns*/
#define CR  0xD       /*dns*/
#define FF  0xC       /*dns*/
#define ESC 0x1B      /*dns*/

/* program limits */
#define LINEMAX         132     /* maximum input line length */
#define TABLEMAX        132     /* maximum table output line */
#define KEYWORDMAX      10      /* maximum keyword length */
#define NUMBERMAX       20      /* maximum number length */
#define STRINGMAX       132     /* maximum string length */
#define CODEMAX         100     /* maximum length of code array */
#define STACKMAX        20      /* maximum interpreter stack size */

/* token definitions */
#define EOS             0
#define LSS             -1
#define LEQ             -2
#define EQL             -3
#define NEQ             -4
#define GEQ             -5
#define GTR             -6
#define SELECT          -7
#define FROM            -8
#define WHERE           -9
#define CREATE          -10
#define DELETE          -11
#define INSERT          -12
#define EXIT            -13
#define CHAR            -14
#define NUM             -15
#define ID              -16
#define STRING          -17
#define NUMBER          -18
#define UPDATE          -19
#define PRINT           -20
#define IMPORT          -21
#define EXPORT          -22
#define INTO            -23
#define HELP            -24
#define COMPRESS        -25
#define EXTRACT         -26
#define DEFINE          -27
#define SHOW            -28
#define USING           -29
#define SORT            -30
#define BY              -31
#define ASCENDING       -32
#define DESCENDING      -33
#define SET             -34

/* operand types */
#define LITERAL 1
#define ATTR    2
#define TEMP    3

/* attribute data types */
#define TCHAR   1
#define TNUM    2

/* tuple status codes */
#define UNUSED  0
#define ACTIVE  1
#define DELETED 2

/* relation header page format definitions */
#define RNSIZE          10      /* size of a relation name */
#define HSIZE           16      /* size of a relation entry */
#define ASIZE           16      /* size of a attribute entry */
#define ANSIZE          10      /* size of a attribute name */
#define NATTRS          31      /* number of attributes in header block */

/* error code definitions */
#define END             0       /* end of retrieval set */
#define INSMEM          1       /* insufficient memory */
#define RELFNF          2       /* relation file not found */
#define BADHDR          3       /* bad relation file header */
#define TUPINP          4       /* tuple input error */
#define TUPOUT          5       /* tuple output error */
#define RELFUL          6       /* relation file full */
#define RELCRE          7       /* error creating relation file */
#define DUPATT          8       /* duplicate attribute on relation create */
#define MAXATT          9       /* too many attributes on relation create */
#define INSBLK          10      /* insufficient disk blocks */
#define SYNTAX          11      /* command syntax error */
#define ATUNDF          12      /* attribute name undefined */
#define ATAMBG          13      /* attribute name ambiguous */
#define RLUNDF          14      /* relation name undefined */
#define CDSIZE          15      /* boolean expression code too big */
#define INPFNF          16      /* input file not found */
#define OUTCRE          17      /* output file creation error */
#define INDFNF          18      /* indirect command file not found */
#define BADSET          19      /* bad set parameter */

struct attribute {
    char at_name[ANSIZE];       /* attribute name */
    char at_type;               /* attribute type */
    char at_size;               /* attribute size in bytes */
    char at_scale;              /* attribute scale factor (for numeric only) */
    char at_unused[ASIZE-ANSIZE-3];     /* unused space */
};

struct header {         /* sizeof(struct header) must be 512 bytes */
    char hd_tcnt[2];            /* # of tuples in relation */
    char hd_tmax[2];            /* maximum # of tuples */
    char hd_data[2];            /* offset to first data byte */
    char hd_size[2];            /* size of each tuple in bytes */
    char hd_unused[HSIZE-8];    /* unused space */
    struct attribute hd_attrs[NATTRS];  /* table of attributes */
};

struct relation {
    char rl_name[RNSIZE];       /* relation name */
    unsigned int rl_tcnt;       /* # of tuples in relation (from hd_tcnt) */
    unsigned int rl_tmax;       /* maximum # of tuples (from hd_tmax) */
    int rl_data;                /* offset to first data byte (from hd_data) */
    int rl_size;                /* size of eachtuple in bytes (from hd_size) */
    int rl_store;               /* flag indicating a store happened */
    int rl_fd;                  /* file descriptor for relation file */
    int rl_scnref;              /* number of scans referencing this relation */
    struct header rl_header;    /* the relation file header block */
    struct relation *rl_next;   /* pointer to next relation */
};

struct scan {
    struct relation *sc_relation;       /* pointer to relation definition */
    unsigned int sc_dtnum;              /* desired tuple number */
    unsigned int sc_atnum;              /* actual tuple number */
    int sc_store;                       /* flag indicating a store happened */
    char *sc_tuple;                     /* tuple buffer */
};

struct srel {
    char *sr_name;                      /* alternate relation name */
    struct scan *sr_scan;               /* relation scan structure ptr */
    int sr_ctuple;                      /* current tuple flag */
    int sr_update;                      /* updated tuple flag */
    struct srel *sr_next;               /* next selected relation in list */
};

struct sattr {
    char *sa_rname;                     /* relation name */
    char *sa_aname;                     /* attribute name */
    char *sa_name;                      /* alternate attribute name */
    char *sa_aptr;                      /* pointer to attr in tuple buffer */
    struct srel *sa_srel;               /* pointer to the selected relation */
    struct attribute *sa_attr;          /* attribute structure ptr */
    struct sattr *sa_next;              /* next selected attribute in list */
};

struct operand {
    int o_type;
    union  {
        struct {
            int ovc_type;
            char *ovc_string;
            int ovc_length;
        } ov_char;
        int ov_boolean;
    } o_value;
};

union codecell {
    int (*c_operator)();
    struct operand *c_operand;
};

struct binding {
    struct attribute *bd_attr;          /* bound attribute */
    char *bd_vtuple;                    /* pointer to value in tuple */
    char *bd_vuser;                     /* pointer to user buffer */
    struct binding *bd_next;            /* next binding */
};

struct sel {
    struct srel *sl_rels;               /* selected relations */
    struct sattr *sl_attrs;             /* selected attributes */
    union codecell *sl_where;           /* where clause */
    struct binding *sl_bindings;        /* user variable bindings */
};

struct mtext {
    char *mt_text;
    struct mtext *mt_next;
};

struct macro {
    char *mc_name;
    struct mtext *mc_mtext;
    struct macro *mc_next;
};

struct ifile {
    char *if_fp;
    struct mtext *if_mtext;
    char *if_cmdline;
    int if_savech;
    char *if_lptr;
    struct ifile *if_next;
};

struct skey {
    int sk_type;
    struct attribute *sk_aptr;
    int sk_start;
    struct skey *sk_next;
};

SHAR_EOF
cat << \SHAR_EOF > srt.c
/* SDB - sort routines */

#include <stdio.h>
#include "sdbio.h"

extern int dbv_token;
extern char dbv_tstring[];
extern int dbv_tvalue;

/* get_skeys - get sort key list */
static struct skey *get_skeys(sptr)
  struct scan *sptr;
{
    struct skey *skeys,*newskey,*lastskey;

    /* parse a list of attribute names */
    skeys = lastskey = NULL;
    while (TRUE) {

        /* get attribute name */
        if (db_ntoken() != ID)
            return (db_nerror(SYNTAX));

        /* allocate a sort key structure */
        if ((newskey = malloc(sizeof(struct skey))) == NULL)
            return (db_nerror(INSMEM));

        /* initialize the sort key structure */
        newskey->sk_next = NULL;

        /* lookup the attribute name */
        if (!find_attr(sptr,newskey,dbv_tstring)) {
            free(newskey);
            return (NULL);
        }

        /* check for ascending or descending */
        if (db_token() == ASCENDING || dbv_token == DESCENDING) {
            newskey->sk_type = dbv_token;
            db_ntoken();
        }
        else
            newskey->sk_type = ASCENDING;

        /* link the sort key structure into the list */
        if (lastskey == NULL)
            skeys = newskey;
        else
            lastskey->sk_next = newskey;
        lastskey = newskey;

        /* check for more attributes */
        if (db_token() != ',')
            break;
        db_ntoken();
    }

    /* return successfully */
    return (skeys);
}

/* db_sort - sort tuples in a relation */
int *db_sort(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
  char *fmt;
{
    struct scan *sptr1,*sptr2,*sptr3;   /*dns*/
    struct skey *skeys;
    int result;

    /* check for a command line */
    if (fmt != NULL)
        db_scan(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);

    /* checks for relation name */
    if (db_token() == ID)
        db_ntoken();
    else
        strcpy(dbv_tstring,"sdbcur");

    /* open the relation */
    if ((sptr1 = db_ropen(dbv_tstring)) == NULL)
        return (FALSE);
    if ((sptr2 = db_ropen(dbv_tstring)) == NULL) {
        db_rclose(sptr1);
        return (FALSE);
        }
    if ((sptr3 = db_ropen(dbv_tstring)) == NULL) {   /*dns*/
        db_rclose(sptr1);                            /*dns*/
        db_rclose(sptr2);                            /*dns*/
        return (FALSE);                              /*dns*/
        }

    /* checks for "<relation-name> by <sort-list>" */
    if (db_ntoken() != BY)
        return (db_ferror(SYNTAX));
    if ((skeys = get_skeys(sptr1)) == NULL) {
        db_rclose(sptr1);
        db_rclose(sptr2);
        db_rclose(sptr3);  /*dns*/
        return (FALSE);
       }

    /* do the sort */
    result = sort(skeys,sptr1,sptr2,sptr3);  /*dns*/

    /* close the relation */
    db_rclose(sptr1);
    db_rclose(sptr2);
    db_rclose(sptr3);     /*dns*/

    /* free the sort keys */
    free_skeys(skeys);

    return (result);
}

/* free_skeys - free a list of sort keys */
static free_skeys(skeys)
  struct skey *skeys;
{
    struct skey *thisskey;

    for (thisskey = skeys; skeys != NULL; thisskey = skeys) {
        skeys = skeys->sk_next;
        free(thisskey);
    }
}

/* find_attr - find an attribute */
static int find_attr(sptr,newskey,aname)
  struct scan *sptr; struct skey *newskey; char *aname;
{
    struct attribute *aptr;
    int i,astart;

    /* loop through each attribute within the relation */
    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 last attribute */
        if (aptr->at_name[0] == 0)
            break;

        /* check for attribute name match */
        if (db_sncmp(aname,aptr->at_name,ANSIZE) == 0) {
            newskey->sk_start = astart;
            newskey->sk_aptr = aptr;
            return (TRUE);
        }

        /* update the attribute start */
        astart += aptr->at_size;
    }

    /* attribute name not found */
    return (db_ferror(ATUNDF));
}

/* sort - sort the relation */
static int sort(skeys,sptr1,sptr2,sptr3)
  struct skey *skeys; struct scan *sptr1,*sptr2,*sptr3;
{
/*  unsigned int j,k,l,r;          dns */
    long int passes,swaps;           /*dns*/
    int i, j, m, n;         /*dns*/
    int rec1 = 0;           /*dns*/
    int rec2 = 0;           /*dns*/
    int rec3 = 0;           /*dns*/
    int dns = 0;            /*dns*/
    FILE *test;             /*dns*/

    passes = 0L;
    swaps = 0L;

    /*dns --->   */
    test = fopen("sort.dat", "w");
    n = sptr1->sc_relation->rl_tcnt;
    m = n;

    while( m>1 )   {
       passes++;
       if ((m/=3.14159) < 1)  m = 1;
       for ( j=1; j<=n-m; j++ )  {
          if( rec1 != j+m )  {
             if(dns) fprintf(test,"Read1: %d\n", j+m);
             if (!db_rget(sptr1, rec1=j+m))  return (FALSE);
             }
          for ( i=j; i>=1; i-=m ) {
             if( rec2 != i )   {
                if(dns) fprintf(test,"Read2: %d\n", i);
                if (!db_rget(sptr2, rec2=i))  return (FALSE);
                }
             if (compare(skeys,sptr1,sptr2) > 0)
                break;
             if(rec3 != i+m)  {
                if(dns) fprintf(test,"Read3: %d\n", i+m);
                if (!db_rget(sptr3, rec3=i+m))  return (FALSE);
                }
             if(dns) fprintf(test,"Write 3,2: %d from %d\n", i+m, i);
             assign( sptr3, sptr2 );
             swaps++;
             }
          if(rec1 != i+m)  {
             if(rec3 != i+m)  {
                if(dns) fprintf(test,"Read 3: %d\n", i+m);
                if (!db_rget(sptr3, rec3=i+m))  return (FALSE);
                }
             if(dns) fprintf(test,"Write 3,1: %d from %d\n", i+m, j+m);
             assign( sptr3, sptr1 );
             swaps++;
             }
          }
       }
       fclose(test);

/*
    l = 2;
    r = sptr1->sc_relation->rl_tcnt;
    k = r;

    do {
        for (j = r; j >= l; j--) {
            if (!db_rget(sptr1,j-1))
                return (FALSE);
            if (!db_rget(sptr2,j))
                return (FALSE);
            if (compare(skeys,sptr1,sptr2) > 0) {
                swap(sptr1,sptr2);
                k = j;
                swaps++;
            }
        }
        l = k + 1;
        for (j = l; j <= r; j++) {
            if (!db_rget(sptr1,j-1))
                return (FALSE);
            if (!db_rget(sptr2,j))
                return (FALSE);
            if (compare(skeys,sptr1,sptr2) > 0) {
                swap(sptr1,sptr2);
                k = j;
                swaps++;
            }
        }
        r = k - 1;
        passes++;
    } while (l <= r);
*/

    printf("[ Passes: %ld  Swaps: %ld ]\n",passes,swaps);

    return (TRUE);
}

/* compare - compare two tuples */
static int compare(skeys,sptr1,sptr2)
  struct skey *skeys; struct scan *sptr1,*sptr2;
{
    struct skey *cskey;
    int result;

    for (cskey = skeys; cskey != NULL; cskey = cskey->sk_next)
        if ((result = cattr(cskey,sptr1,sptr2)) != 0)
            break;

    return (result);
}

/* cattr - compare two attributes */
static int cattr(cskey,sptr1,sptr2)
  struct skey *cskey; struct scan *sptr1,*sptr2;
{
    int astart,aend,i;

    astart = cskey->sk_start;
    aend = astart + cskey->sk_aptr->at_size;

    for (i = astart; i < aend; i++)
        if (sptr1->sc_tuple[i] != sptr2->sc_tuple[i])
            break;

    if (i == aend)
        return (0);

    if (sptr1->sc_tuple[i] < sptr2->sc_tuple[i])
        if (cskey->sk_type == ASCENDING)
            return (-1);
        else
            return (1);
    else
        if (cskey->sk_type == ASCENDING)
            return (1);
        else
            return (-1);
}

/* swap - swap two tuples */
/* dns
static int swap(sptr1,sptr2)
  struct scan *sptr1,*sptr2;
{
    unsigned int tnum1,tnum2;

    tnum1 = sptr1->sc_atnum;
    tnum2 = sptr2->sc_atnum;

    if (!db_rput(sptr1,tnum2))
        return (FALSE);
    if (!db_rput(sptr2,tnum1))
        return (FALSE);

    return (TRUE);
}
  dns  */


/* assign - assign one tupple to another */
static int assign(sptr1,sptr2)
  struct scan *sptr1,*sptr2;
{
    unsigned int tnum1,tnum2;

    tnum1 = sptr1->sc_atnum;

    if (!db_rput(sptr2,tnum1))
        return (FALSE);

    return (TRUE);
}
SHAR_EOF
cat << \SHAR_EOF > com.c
/* SDB - expression compiler
    syntax:
        <expr>          ::= <lor> <EOS>
        <lor>           ::= <land> { '|' <land> }
        <land>          ::= <relat> { '&' <relat> }
        <relat>         ::= <primary> { <relop> <primary> }
        <primary>       ::= <factor> | <unop> <unary>
        <factor>        ::= <number> | <string> | '(' <query> ')'
        <number>        ::= <digit> | <number> <digit>
        <string>        ::= '"' <chars> '"'
        <chars>         ::= nil | <chars> <character>
        <relop>         ::= '=' | '<>' | '<' | '>' | '<=' | '>='
        <unop>          ::= '+' | '-' | '~'
*/

#include "sdbio.h"

extern int dbv_token;
extern char dbv_tstring[];
extern int dbv_tvalue;

extern int db_xand();
extern int db_xor();
extern int db_xnot();
extern int db_xlss();
extern int db_xleq();
extern int db_xeql();
extern int db_xgeq();
extern int db_xgtr();
extern int db_xneq();
extern int db_xpush();
extern int db_xstop();

static union codecell code[CODEMAX+1];
static int cndx;
static struct sel *selptr;

/* compile - compile a boolean expression */
int db_compile(slptr)
  struct sel *slptr;
{
    int result,i;
    union codecell *cptr;
#ifdef Lattice
    int (*dns)();  /*dns*/
#endif

    /* save the selection structure pointer */
    selptr = slptr;

    /* initialize the code array index */
    cndx = 0;

    /* parse the boolean expression */
    if (!expr(&result)) {
        code[cndx++].c_operator = db_xstop;
        freelit(code);
        return (FALSE);
    }

    /* terminate the code */
    code[cndx++].c_operator = db_xstop;

    /* allocate space for the code array */
    if ((cptr = malloc(sizeof(union codecell) * cndx)) == NULL) {
        freelit(code);
        return (FALSE);
    }

    /* store the code into the code array */
    slptr->sl_where = cptr;
    for (i = 0; i < cndx; i++) {
        (*cptr++).c_operator = code[i].c_operator;
#ifndef Lattice
        if (code[i].c_operator == db_xpush)
#else
        if ( code[i].c_operator == (dns=db_xpush) )  /*dns*/
#endif
            (*cptr++).c_operand = code[++i].c_operand;
    }

    /* return successfully */
    return (TRUE);
}

/* db_fcode - free a code array */
db_fcode(slptr)
  struct sel *slptr;
{
    /* make sure there is a where clause */
    if (slptr->sl_where == NULL)
        return;

    /* free the literals */
    freelit(slptr->sl_where);

    /* free the code array */
    free(slptr->sl_where);
}

/* operator - insert an operator into the code array */
static int operator(opr)
  int (*opr)();
{
    /* insert the operator */
    if (cndx < CODEMAX)
        code[cndx++].c_operator = opr;
    else
        return (db_ferror(CDSIZE));

    /* return successfully */
    return (TRUE);
}

/* operand - insert an operand into the code array */
static int operand(opr)
  struct operand *opr;
{
    /* insert the push operator */
    if (!operator(db_xpush))
        return (FALSE);

    /* insert the operand */
    if (cndx < CODEMAX)
        code[cndx++].c_operand = opr;
    else
        return (db_ferror(CDSIZE));

    /* return successfully */
    return (TRUE);
}

/* expr - compile an expression */
static int expr(result)
  int *result;
{
    int lval,rval;

    if (!land(&lval))
        return (FALSE);
    while (db_token() == '|') {
        db_ntoken();
        if (!land(&rval))
            return (FALSE);
        if (!operator(db_xor))
            return (FALSE);
    }
    *result = lval;
    return (TRUE);
}

static int land(result)
  int *result;
{
    int lval,rval;

    if (!relat(&lval))
        return (FALSE);
    while (db_token() == '&') {
        db_ntoken();
        if (!relat(&rval))
            return (FALSE);
        if (!operator(db_xand))
            return (FALSE);
    }
    *result = lval;
    return (TRUE);
}

static int relat(result)
  int *result;
{
    int lval,rval;
    int tkn;

    if (!primary(&lval))
        return (FALSE);
    while (db_token() <= LSS && dbv_token >= GTR) {
        tkn = db_ntoken();
        if (!primary(&rval))
            return (FALSE);
        switch (tkn) {
        case LSS:
                if (!operator(db_xlss))
                    return (FALSE);
                break;
        case LEQ:
                if (!operator(db_xleq))
                    return (FALSE);
                break;
        case EQL:
                if (!operator(db_xeql))
                    return (FALSE);
                break;
        case NEQ:
                if (!operator(db_xneq))
                    return (FALSE);
                break;
        case GEQ:
                if (!operator(db_xgeq))
                    return (FALSE);
                break;
        case GTR:
                if (!operator(db_xgtr))
                    return (FALSE);
                break;
        }
    }
    *result = lval;
    return (TRUE);
}

static int primary(result)
  int *result;
{
    int val;
    int tkn;

    if (db_token() == '~') {
        tkn = db_ntoken();
        if (!primary(&val))
            return (FALSE);
        switch (tkn) {
        case '~':
                if (!operator(db_xnot))
                    return (FALSE);
                break;
        }
    }
    else
        if (!factor(&val))
            return (FALSE);
    *result = val;
    return (TRUE);
}

static int factor(result)
  int *result;
{
    int val;

    if (db_token() == '(') {
        db_ntoken();
        if (!expr(&val))
            return (FALSE);
        if (db_token() != ')')
            return (db_ferror(SYNTAX));
        db_ntoken();
    }
    else
        if (!get_operand(&val))
            return (FALSE);
    *result = val;
    return (TRUE);
}

/* get_operand - get an operand (number, string, or attribute) */
static int get_operand(result)
  int *result;
{
    /* determine operand type */
    if (db_ntoken() == NUMBER)
        return (get_number(result));
    else if (dbv_token == ID)
        return (get_attr(result));
    else if (dbv_token == STRING)
        return (get_string(result));
    else
        return (db_ferror(SYNTAX));
}

/* get_attr - get an attribute argument */
static int get_attr(result)
  int *result;
{
    struct operand *opr;
    char rname[RNSIZE+1],aname[ANSIZE+1];
    char *aptr; int atype,alen;

    /* save the attribute name */
    strncpy(aname,dbv_tstring,ANSIZE); aname[ANSIZE] = EOS;

    /* check for a "." indicating a qualified attribute name */
    if (db_token() == '.') {
        db_ntoken();

        /* the previous ID was really a relation name */
        strcpy(rname,aname);

        /* check for the real attribute name */
        if (db_ntoken() != ID)
            return (db_ferror(SYNTAX));

        /* save the attribute name */
        strncpy(aname,dbv_tstring,ANSIZE); aname[ANSIZE] = EOS;

        /* lookup the attribute name */
        if (!db_sattr(selptr,rname,aname,&atype,&aptr,&alen))
            return (FALSE);
    }
    else
        if (!db_sattr(selptr,NULL,aname,&atype,&aptr,&alen))
            return (FALSE);

    /* get a new operand structure */
    if ((opr = malloc(sizeof(struct operand))) == NULL)
        return (db_ferror(INSMEM));

    /* initialize the new operand structure */
    opr->o_type = ATTR;
    opr->o_value.ov_char.ovc_type = atype;
    opr->o_value.ov_char.ovc_string = aptr;
    opr->o_value.ov_char.ovc_length = alen;

    /* insert the operand into the code array */
    if (!operand(opr)) {
        free(opr);
        return (FALSE);
    }

    /* store operand type */
    *result = atype;

    /* return successfully */
    return (TRUE);
}

/* get_number - get a numeric operand */
static int get_number(result)
  int *result;
{
    struct operand *opr;

    /* get a new operand structure */
    if ((opr = malloc(sizeof(struct operand))) == NULL)
        return (db_ferror(INSMEM));

    /* initialize the new operand structure */
    opr->o_type = LITERAL;
    if ((opr->o_value.ov_char.ovc_string =
                malloc(strlen(dbv_tstring)+1)) == NULL) {
        free(opr);
        return (db_ferror(INSMEM));
    }
    opr->o_value.ov_char.ovc_type = TNUM;
    strcpy(opr->o_value.ov_char.ovc_string,dbv_tstring);
    opr->o_value.ov_char.ovc_length = strlen(dbv_tstring);

    /* insert the operand into the code array */
    if (!operand(opr)) {
        free(opr->o_value.ov_char.ovc_string); free(opr);
        return (FALSE);
    }

    /* operand type is number */
    *result = TNUM;

    /* return successfully */
    return (TRUE);
}

/* get_string - get a string operand */
static int get_string(result)
  int *result;
{
    struct operand *opr;

    /* get a new operand structure */
    if ((opr = malloc(sizeof(struct operand))) == NULL)
        return (db_ferror(INSMEM));

    /* initialize the new operand structure */
    opr->o_type = LITERAL;
    if ((opr->o_value.ov_char.ovc_string =
                malloc(strlen(dbv_tstring)+1)) == NULL) {
        free(opr);
        return (db_ferror(INSMEM));
    }
    opr->o_value.ov_char.ovc_type = TCHAR;
    strcpy(opr->o_value.ov_char.ovc_string,dbv_tstring);
    opr->o_value.ov_char.ovc_length = strlen(dbv_tstring);

    /* insert the operand into the code array */
    if (!operand(opr)) {
        free(opr->o_value.ov_char.ovc_string); free(opr);
        return (FALSE);
    }

    /* operand type is character */
    *result = TCHAR;

    /* return successfully */
    return (TRUE);
}

/* freelit - free the literals in a code array */
static freelit(cptr)
  union codecell *cptr;
{
#ifdef Lattice
    int (*dns)();  /*dns*/
    for (; (*cptr).c_operator != (dns=db_xstop); cptr++)  /*dns*/
        if ((*cptr).c_operator == (dns=db_xpush) )        /*dns*/
#else
    for (; (*cptr).c_operator != db_xstop; cptr++)
        if ((*cptr).c_operator == db_xpush )
#endif
            if ((*++cptr).c_operand->o_type == LITERAL)
                free((*cptr).c_operand->o_value.ov_char.ovc_string);
}
SHAR_EOF
#	End of shell archive
exit 0

doc@pucc-j.UUCP (08/23/86)

#	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

doc@pucc-j.UUCP (08/23/86)

#	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:
#	err.c
#	cre.c
#	sel.c
#	sdb.mem
# This archive created: Fri Aug 22 17:40:03 1986
# By:	Craig Norborg (Purdue University Computing Center)
cat << \SHAR_EOF > err.c
/* SDB - error messages */

#include "sdbio.h"

char *db_ertxt(msg)
  int msg;
{
    char *txt;

    /* select the appropriate message text */
    switch (msg) {
    case INSMEM:
        txt = "insufficient memory";
        break;
    case RELFNF:
        txt = "relation file not found";
        break;
    case BADHDR:
        txt = "bad relation header";
        break;
    case TUPINP:
        txt = "tuple input error";
        break;
    case TUPOUT:
        txt = "tuple output error";
        break;
    case RELFUL:
        txt = "relation file full";
        break;
    case RELCRE:
        txt = "error creating relation file";
        break;
    case DUPATT:
        txt = "duplicate attribute";
        break;
    case MAXATT:
        txt = "too many attributes";
        break;
    case INSBLK:
        txt = "insufficient disk space";
        break;
    case SYNTAX:
        txt = "syntax error";
        break;
    case ATUNDF:
        txt = "undefined attribute";
        break;
    case ATAMBG:
        txt = "ambiguous attribute";
        break;
    case RLUNDF:
        txt = "undefined relation";
        break;
    case CDSIZE:
        txt = "boolean expression too complex";
        break;
    case INPFNF:
        txt = "input file not found";
        break;
    case OUTCRE:
        txt = "error creating output file";
        break;
    case INDFNF:
        txt = "indirect command file not found";
        break;
    case BADSET:
        txt = "bad set parameter";
        break;
    default:
        txt = "undefined error";
        break;
    }

    /* return the message text */
    return (txt);
}
SHAR_EOF
cat << \SHAR_EOF > cre.c
/* SDB - relation creation routines */

#include "sdbio.h"

/* db_rcreate(rname) - begin the creation of a new relation */
struct relation *db_rcreate(rname)
  char *rname;
{
    struct relation *rptr;

    /* allocate the relation structure */
    if ((rptr = calloc(1,sizeof(struct relation))) == NULL)
        return (db_nerror(INSMEM));

    /* initialize the relation structure */
    strncpy(rptr->rl_name,rname,RNSIZE);
    rptr->rl_tcnt = 0;
    rptr->rl_tmax = 0;
    rptr->rl_data = 512;
    rptr->rl_size = 1;
    rptr->rl_header.hd_attrs[0].at_name[0] = 0;

    /* return the new relation structure pointer */
    return (rptr);
}

/* db_rcheader - create the relation header */
int db_rcheader(rptr)
  struct relation *rptr;
{
    char rname[RNSIZE+1],filename[RNSIZE+13];

    /* initialize the relation file header */
    db_cvbytes(rptr->rl_tcnt,rptr->rl_header.hd_tcnt);
    db_cvbytes(rptr->rl_tmax,rptr->rl_header.hd_tmax);
    db_cvbytes(rptr->rl_data,rptr->rl_header.hd_data);
    db_cvbytes(rptr->rl_size,rptr->rl_header.hd_size);

    /* create the relation file name */
    strncpy(rname,rptr->rl_name,RNSIZE); rname[RNSIZE] = 0;
    sprintf(filename,"%s.sdb",rname);

    /* create the relation file */
    if ((rptr->rl_fd = creat(filename,0)) == -1) {
        free(rptr);
        return (db_ferror(RELCRE));
    }

    /* write the header to the relation file */
    if (write(rptr->rl_fd,&rptr->rl_header,512) != 512) {
        close(rptr->rl_fd);
        free(rptr);
        return (db_ferror(BADHDR));
    }

    /* return successfully */
    return (TRUE);
}

/* db_rctuples - create the relation tuples */
int db_rctuples(rptr,tcnt)
  struct relation *rptr; unsigned int tcnt;
{
    unsigned int i;
    char *tbuf;

    /* store the number of tuples */
    rptr->rl_tmax = tcnt;

    /* allocate a tuple buffer */
    if ((tbuf = calloc(1,rptr->rl_size)) == NULL)
        return (db_ferror(INSMEM));

    /* write null tuples into the file */
    for (i = 0; i < tcnt; i++)
        if (write(rptr->rl_fd,tbuf,rptr->rl_size) != rptr->rl_size) {
            free(tbuf);
            return (db_ferror(INSBLK));
        }

    /* free the tuple buffer */
    free(tbuf);

    /* return successfully */
    return (TRUE);
}

/* db_rcdone(rptr) - finish the creation of a new relation */
int db_rcdone(rptr)
  struct relation *rptr;
{
    /* initialize the relation file header */
    db_cvbytes(rptr->rl_tcnt,rptr->rl_header.hd_tcnt);
    db_cvbytes(rptr->rl_tmax,rptr->rl_header.hd_tmax);

    /* write the header to the relation file */
    lseek(rptr->rl_fd,0L,0);
    if (write(rptr->rl_fd,&rptr->rl_header,512) != 512) {
        close(rptr->rl_fd);
        free(rptr);
        return (db_ferror(BADHDR));
    }

   /* close the relation file */
    close(rptr->rl_fd);

    /* free the relation structure */
    free(rptr);

    /* return successfully */
    return (TRUE);
}

/* db_rcattr(rptr,aname,type,size) - add an attribute to relation being created */
int db_rcattr(rptr,aname,type,size)
  struct relation *rptr; char *aname; int type,size;
{
    int i;

    /* look for attribute name */
    for (i = 0; i < NATTRS; i++)
        if (rptr->rl_header.hd_attrs[i].at_name[0] == 0)
            break;
        else if (db_sncmp(aname,rptr->rl_header.hd_attrs[i].at_name,ANSIZE) == 0)
            return (db_ferror(DUPATT));

    /* check for too many attributes */
    if (i == NATTRS)
        return (db_ferror(MAXATT));

    /* store the new attribute */
    strncpy(rptr->rl_header.hd_attrs[i].at_name,aname,ANSIZE);
    rptr->rl_header.hd_attrs[i].at_type = type;
    rptr->rl_header.hd_attrs[i].at_size = size;

    /* terminate the attribute table */
    if (++i != NATTRS)
        rptr->rl_header.hd_attrs[i].at_name[0] = 0;

    /* update the tuple size */
    rptr->rl_size += size;

    /* return successfully */
    return (TRUE);
}
SHAR_EOF
cat << \SHAR_EOF > sel.c
/* SDB - select data from the database */

#include "sdbio.h"

extern int dbv_token;
extern char dbv_tstring[];
extern int dbv_tvalue;

/* db_select - select a set of tuples from a set of relations */
struct sel *db_select(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
  char *fmt;
{
    struct sel *slptr;

    /* check for a command line */
    if (fmt != NULL)
        db_scan(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);

    /* allocate a sel structure */
    if ((slptr = malloc(sizeof(struct sel))) == NULL)
        return (db_nerror(INSMEM));

    /* initialize the structure */
    slptr->sl_rels = NULL;
    slptr->sl_attrs = NULL;
    slptr->sl_where = NULL;
    slptr->sl_bindings = NULL;

    /* parse the list of selected attributes */
    if (!get_sattrs(slptr)) {
        db_done(slptr);
        return (NULL);
    }

    /* check for "from" clause */
    if (db_token() == FROM) {
        db_ntoken();
        if (!get_srels(slptr)) {
            db_done(slptr);
            return (NULL);
        }
    }
    else {
        if (!srelation(slptr,"sdbcur",NULL)) {
            db_done(slptr);
            return (NULL);
        }
    }

    /* check the list of selected attributes */
    if (!check_attrs(slptr)) {
        db_done(slptr);
        return (NULL);
    }

    /* check for the existance of a "where" clause */
    if (db_token() == WHERE) {
        db_ntoken();

        /* parse the boolean expression */
        if (!db_compile(slptr)) {
            db_done(slptr);
            return (FALSE);
        }
    }

    /* return the new selection structure */
    return (slptr);
}

/* db_retrieve - retrieve a set of tuples from a set of relations */
struct sel *db_retrieve(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
  char *fmt;
{
    struct sel *slptr;

    /* check for a command line */
    if (fmt != NULL)
        db_scan(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);

    /* allocate a sel structure */
    if ((slptr = malloc(sizeof(struct sel))) == NULL)
        return (db_nerror(INSMEM));

    /* initialize the structure */
    slptr->sl_rels = NULL;
    slptr->sl_attrs = NULL;
    slptr->sl_where = NULL;
    slptr->sl_bindings = NULL;

    /* check for selected relations clause */
    if (db_token() == ID) {
        if (!get_srels(slptr)) {
            db_done(slptr);
            return (NULL);
        }
    }
    else {
        if (!srelation(slptr,"sdbcur",NULL)) {
            db_done(slptr);
            return (NULL);
        }
    }

    /* check the list of selected attributes */
    if (!check_attrs(slptr)) {
        db_done(slptr);
        return (NULL);
    }

    /* check for the existance of a "where" clause */
    if (db_token() == WHERE) {
        db_ntoken();

        /* parse the boolean expression */
        if (!db_compile(slptr)) {
            db_done(slptr);
            return (FALSE);
        }
    }

    /* return the new selection structure */
    return (slptr);
}

/* db_done(slptr) - finish a selection */
db_done(slptr)
  struct sel *slptr;
{
    struct sattr *saptr,*nxtsa;
    struct srel *srptr,*nxtsr;
    struct binding *bdptr,*nxtbd;

    /* free the selected attribute blocks */
    for (saptr = slptr->sl_attrs; saptr != NULL; saptr = nxtsa) {
        nxtsa = saptr->sa_next;
        if (saptr->sa_rname != NULL)
            free(saptr->sa_rname);
        free(saptr->sa_aname);
        if (saptr->sa_name != NULL)
            free(saptr->sa_name);
        free(saptr);
    }

    /* close the scans and free the selected relation blocks */
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = nxtsr) {
        nxtsr = srptr->sr_next;
        if (srptr->sr_name != NULL)
            free(srptr->sr_name);
        db_rclose(srptr->sr_scan);
        free(srptr);
    }

    /* free the where clause */
    db_fcode(slptr);

    /* free the user bindings */
    for (bdptr = slptr->sl_bindings; bdptr != NULL; bdptr = nxtbd) {
        nxtbd = bdptr->bd_next;
        free(bdptr);
    }

    /* free the selection structure */
    free(slptr);
}

/* db_fetch(slptr) - fetch the next tuple from a selection */
int db_fetch(slptr)
  struct sel *slptr;
{
    struct srel *srptr;
    struct binding *bdptr;

    /* clear the update flags */
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next)
        srptr->sr_update = FALSE;

    /* find a matching tuple */
    while (process(slptr->sl_rels))
        if (db_interpret(slptr)) {
            for (bdptr = slptr->sl_bindings; bdptr != NULL; bdptr = bdptr->bd_next)
                db_aget(bdptr->bd_attr,bdptr->bd_vtuple,bdptr->bd_vuser);
            return (TRUE);
        }

    /* no matches, failure return */
    return (FALSE);
}

/* db_update - update modified tuples */
int db_update(slptr)
  struct sel *slptr;
{
    struct srel *srptr;

    /* check each selected relation for updates */
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next)
        if (srptr->sr_update)
            if (!db_rupdate(srptr->sr_scan))
                return (FALSE);

    /* return successfully */
    return (TRUE);
}

/* db_store - store tuples */
int db_store(slptr)
  struct sel *slptr;
{
    struct srel *srptr;

    /* check each selected relation for stores */
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next)
        if (srptr->sr_update)
            if (!db_rstore(srptr->sr_scan))
                return (FALSE);

    /* return successfully */
    return (TRUE);
}

/* db_bind - bind a user buffer to the value of an attribute */
int db_bind(slptr,rname,aname,avalue)
  struct sel *slptr; char *rname,*aname,*avalue;
{
    struct binding *newbd;
    struct srel *srptr;

    /* allocate and initialize a binding structure */
    if ((newbd = malloc(sizeof(struct binding))) == NULL)
        return (db_ferror(INSMEM));
    newbd->bd_vuser = avalue;

    /* find the attribute */
    if (!find_attr(slptr,rname,aname,&newbd->bd_vtuple,&srptr,&newbd->bd_attr))
        return (FALSE);

    /* link the new binding into the binding list */
    newbd->bd_next = slptr->sl_bindings;
    slptr->sl_bindings = newbd;

    /* return successfully */
    return (TRUE);
}

/* db_get - get the value of an attribute */
int db_get(slptr,rname,aname,avalue)
  struct sel *slptr; char *rname,*aname,*avalue;
{
    struct srel *srptr;
    struct attribute *aptr;
    char *vptr;

    /* find the attribute */
    if (!find_attr(slptr,rname,aname,&vptr,&srptr,&aptr))
        return (FALSE);

    /* get the attribute value */
    db_aget(aptr,vptr,avalue);

    /* return successfully */
    return (TRUE);
}

/* db_put - put the value of an attribute */
int db_put(slptr,rname,aname,avalue)
  struct sel *slptr; char *rname,*aname,*avalue;
{
    struct srel *srptr;
    struct attribute *aptr;
    char *vptr;

    /* find the attribute */
    if (!find_attr(slptr,rname,aname,&vptr,&srptr,&aptr))
        return (FALSE);

    /* put the attribute value */
    db_aput(aptr,vptr,avalue);

    /* mark the tuple as updated */
    srptr->sr_update = TRUE;

    /* return successfully */
    return (TRUE);
}

/* db_sattr - get selected attribute type, pointer, and length */
int db_sattr(slptr,rname,aname,ptype,pptr,plen)
  struct sel *slptr; char *rname,*aname;
  int *ptype; char **pptr; int *plen;
{
    struct srel *srptr;
    struct attribute *aptr;

    if (!find_attr(slptr,rname,aname,pptr,&srptr,&aptr))
        return (FALSE);
    *ptype = aptr->at_type;
    *plen = aptr->at_size;
    return (TRUE);
}

/* get_sattrs(slptr) - get selected attributes */
static get_sattrs(slptr)
  struct sel *slptr;
{
    struct sattr *newsattr,*lastsattr;

    /* check for "*" or blank field meaning all attributes are selected */
    if (db_token() == '*') {
        db_ntoken();
        return (TRUE);
    }
    else if (db_token() != ID)
        return (TRUE);

    /* parse a list of attribute names */
    lastsattr = NULL;
    while (TRUE) {

        /* get attribute name */
        if (db_ntoken() != ID)
            return (db_ferror(SYNTAX));

        /* allocate a selected attribute structure */
        if ((newsattr = malloc(sizeof(struct sattr))) == NULL)
            return (db_ferror(INSMEM));

        /* initialize the selected attribute structure */
        newsattr->sa_next = NULL;

        /* save the attribute name */
        if ((newsattr->sa_aname = malloc(strlen(dbv_tstring)+1)) == NULL) {
            free(newsattr);
            return (db_ferror(INSMEM));
        }
        strcpy(newsattr->sa_aname,dbv_tstring);

        /* check for "." meaning "<rel-name>.<att-name>" */
        if (db_token() == '.') {
            db_ntoken();

            /* the previous ID was really the relation name */
            newsattr->sa_rname = newsattr->sa_aname;

            /* check for attribute name */
            if (db_ntoken() != ID) {
                free(newsattr->sa_aname); free(newsattr);
                return (db_ferror(SYNTAX));
            }

            /* save the attribute name */
            if ((newsattr->sa_aname = malloc(strlen(dbv_tstring)+1)) == NULL) {
                free(newsattr->sa_aname); free(newsattr);
                return (db_ferror(INSMEM));
            }
            strcpy(newsattr->sa_aname,dbv_tstring);
        }
        else
            newsattr->sa_rname = NULL;

        /* check for alternate attribute name */
        if (db_token() == ID) {
            db_ntoken();

            /* allocate space for the alternate name */
            if ((newsattr->sa_name = malloc(strlen(dbv_tstring)+1)) == NULL) {
                if (newsattr->sa_rname != NULL)
                    free(newsattr->sa_rname);
                free(newsattr->sa_aname);
                free(newsattr);
                return (db_ferror(INSMEM));
            }
            strcpy(newsattr->sa_name,dbv_tstring);
        }
        else
            newsattr->sa_name = NULL;

        /* link the selected attribute structure into the list */
        if (lastsattr == NULL)
            slptr->sl_attrs = newsattr;
        else
            lastsattr->sa_next = newsattr;
        lastsattr = newsattr;

        /* check for more attributes */
        if (db_token() != ',')
            break;
        db_ntoken();
    }

    /* return successfully */
    return (TRUE);
}

/* get_srels(slptr) - get selected relations */
static get_srels(slptr)
  struct sel *slptr;
{
    char rname[KEYWORDMAX+1],*aname;

    /* get the list of selected relations */
    while (TRUE) {

        /* check for relation name */
        if (db_ntoken() != ID)
            return (db_ferror(SYNTAX));
        strcpy(rname,dbv_tstring);

        /* check for alternate relation name */
        if (db_token() == ID) {
            db_ntoken();
            aname = dbv_tstring;
        }
        else
            aname = NULL;

        /* add the relation name to the list */
        if (!srelation(slptr,rname,aname))
            return (FALSE);

        /* check for more selected relations */
        if (db_token() != ',')
            break;
        db_ntoken();
    }

    /* return successfully */
    return (TRUE);
}

/* srelation - select a relation */
static srelation(slptr,rname,aname)
  struct sel *slptr; char *rname,*aname;
{
    struct srel *srptr,*newsrel;

    /* allocate a new selected relation structure */
    if ((newsrel = malloc(sizeof(struct srel))) == NULL)
        return (db_ferror(INSMEM));

    /* initialize the new selected relation structure */
    newsrel->sr_ctuple = FALSE;
    newsrel->sr_update = FALSE;
    newsrel->sr_next = NULL;

    /* open the relation */
    if ((newsrel->sr_scan = db_ropen(rname)) == NULL) {
        free(newsrel);
        return (FALSE);
    }

    /* check for alternate relation name */
    if (aname != NULL) {

        /* allocate space for the alternate name */
        if ((newsrel->sr_name = malloc(strlen(aname)+1)) == NULL) {
            free(newsrel);
            return (db_ferror(INSMEM));
        }
        strcpy(newsrel->sr_name,aname);
    }
    else
        newsrel->sr_name = NULL;

    /* find the end of the list of relation names */
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next)
        if (srptr->sr_next == NULL)
            break;

    /* link the new selected relation structure into the list */
    if (srptr == NULL)
        slptr->sl_rels = newsrel;
    else
        srptr->sr_next = newsrel;

    /* return successfully */
    return (TRUE);
}

/* check_attrs(slptr) - check the list of selected attributes */
static int check_attrs(slptr)
  struct sel *slptr;
{
    struct sattr *saptr;

    /* check for all attributes selected */
    if (slptr->sl_attrs == NULL)
        return (all_attrs(slptr));

    /* check each selected attribute */
    for (saptr = slptr->sl_attrs; saptr != NULL; saptr = saptr->sa_next)
        if (!find_attr(slptr,saptr->sa_rname,saptr->sa_aname,
                        &saptr->sa_aptr,&saptr->sa_srel,&saptr->sa_attr))
            return (FALSE);

    /* return successfully */
    return (TRUE);
}

/* all_attrs(slptr) - create a list of all attributes */
static int all_attrs(slptr)
  struct sel *slptr;
{
    struct sattr *newsattr,*lastsattr;
    struct srel *srptr;
    struct attribute *aptr;
    int i,astart;

    /* loop through each selected relation */
    lastsattr = NULL;
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next) {

        /* loop through each attribute within the relation */
        astart = 1;
        for (i = 0; i < NATTRS; i++) {

            /* get a pointer to the current attribute */
            aptr = &srptr->sr_scan->sc_relation->rl_header.hd_attrs[i];

            /* check for last attribute */
            if (aptr->at_name[0] == 0)
                break;

            /* allocate a new selected attribute structure */
            if ((newsattr = malloc(sizeof(struct sattr))) == NULL)
                return (db_ferror(INSMEM));

            /* initialize the new selected attribute structure */
            newsattr->sa_name = NULL;
            newsattr->sa_srel = srptr;
            newsattr->sa_aptr = srptr->sr_scan->sc_tuple + astart;
            newsattr->sa_attr = aptr;
            newsattr->sa_next = NULL;

            /* save the relation name */
            if ((newsattr->sa_rname = malloc(RNSIZE+1)) == NULL) {
                free(newsattr);
                return (db_ferror(INSMEM));
            }
            strncpy(newsattr->sa_rname,
                    srptr->sr_scan->sc_relation->rl_name,
                    RNSIZE);
            newsattr->sa_rname[RNSIZE] = 0;

            /* save the attribute name */
            if ((newsattr->sa_aname = malloc(ANSIZE+1)) == NULL) {
                free(newsattr->sa_rname);
                free(newsattr);
                return (db_ferror(INSMEM));
            }
            strncpy(newsattr->sa_aname,
                    srptr->sr_scan->sc_relation->rl_header.hd_attrs[i].at_name,
                    ANSIZE);
            newsattr->sa_aname[ANSIZE] = 0;

            /* link the selected attribute into the list */
            if (lastsattr == NULL)
                slptr->sl_attrs = newsattr;
            else
                lastsattr->sa_next = newsattr;
            lastsattr = newsattr;

            /* update the attribute start */
            astart += aptr->at_size;
        }
    }

    /* return successfully */
    return (TRUE);
}

/* find_attr - find a named attribute */
static int find_attr(slptr,rname,aname,paptr,psrel,pattr)
  struct sel *slptr; char *rname,*aname;
  char **paptr; struct attribute **pattr;
{
    /* check for unqualified or qualified attribute names */
    if (rname == NULL)
        return (uattr(slptr,aname,paptr,psrel,pattr));
    else
        return (qattr(slptr,rname,aname,paptr,psrel,pattr));
}

/* uattr - find an unqualified attribute name */
static int uattr(slptr,aname,paptr,psrel,pattr)
  struct sel *slptr; char *aname;
  char **paptr; struct srel **psrel; struct attribute **pattr;
{
    struct srel *srptr;
    struct attribute *aptr;
    int i,astart;

    /* loop through each selected relation */
    *pattr = NULL;
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next) {

        /* loop through each attribute within the relation */
        astart = 1;
        for (i = 0; i < NATTRS; i++) {

            /* get a pointer to the current attribute */
            aptr = &srptr->sr_scan->sc_relation->rl_header.hd_attrs[i];

            /* check for last attribute */
            if (aptr->at_name[0] == 0)
                break;

            /* check for attribute name match */
            if (db_sncmp(aname,aptr->at_name,ANSIZE) == 0) {
                if (*pattr != NULL)
                    return (db_ferror(ATAMBG));
                *paptr = srptr->sr_scan->sc_tuple + astart;
                *psrel = srptr;
                *pattr = aptr;
            }

            /* update the attribute start */
            astart += aptr->at_size;
        }
    }

    /* check whether attribute was found */
    if (*pattr == NULL)
        return (db_ferror(ATUNDF));

    /* return successfully */
    return (TRUE);
}

/* qattr - find a qualified attribute name */
static int qattr(slptr,rname,aname,paptr,psrel,pattr)
  struct sel *slptr; char *rname,*aname;
  char **paptr; struct srel **psrel; struct attribute **pattr;
{
    struct srel *srptr;
    struct attribute *aptr;
    char *crname;
    int i,astart;

    /* loop through each selected relation */
    for (srptr = slptr->sl_rels; srptr != NULL; srptr = srptr->sr_next) {

        /* get relation name */
        if ((crname = srptr->sr_name) == NULL)
            crname = srptr->sr_scan->sc_relation->rl_name;

        /* check for relation name match */
        if (db_sncmp(rname,crname,RNSIZE) == 0) {

            /* loop through each attribute within the relation */
            astart = 1;
            for (i = 0; i < NATTRS; i++) {

                /* get a pointer to the current attribute */
                aptr = &srptr->sr_scan->sc_relation->rl_header.hd_attrs[i];

                /* check for last attribute */
                if (aptr->at_name[0] == 0)
                    break;

                /* check for attribute name match */
                if (db_sncmp(aname,aptr->at_name,ANSIZE) == 0) {
                    *paptr = srptr->sr_scan->sc_tuple + astart;
                    *psrel = srptr;
                    *pattr = aptr;
                    return (TRUE);
                }

                /* update the attribute start */
                astart += aptr->at_size;
            }

            /* attribute name not found */
            return (db_ferror(ATUNDF));
        }
    }

    /* relation name not found */
    return (db_ferror(RLUNDF));
}

/* process(srptr) - process each tuple in a relation cross-product */
static int process(srptr)
  struct srel *srptr;
{
    /* always get a new tuple if this is the last relation in the list */
    if (srptr->sr_next == NULL) {

        /* check for beginning of new scan */
        if (!srptr->sr_ctuple)
            db_rbegin(srptr->sr_scan);

        /* return the next tuple in the relation */
        return (srptr->sr_ctuple = db_rfetch(srptr->sr_scan));
    }

    /* check for beginning of new scan */
    if (!srptr->sr_ctuple) {
        db_rbegin(srptr->sr_scan);

        /* get the first tuple */
        if (!db_rfetch(srptr->sr_scan))
            return (FALSE);
    }

    /* look for a match with the remaining relations in list */
    while (!process(srptr->sr_next))

        /* get the next tuple in the scan */
        if (!db_rfetch(srptr->sr_scan))
            return (srptr->sr_ctuple = FALSE);

    /* found a match at this level */
    return (srptr->sr_ctuple = TRUE);
}

/* db_aget - get the value of an attribute */
db_aget(aptr,vptr,avalue)
  struct attribute *aptr; char *vptr,*avalue;
{
    int i;

    /* get the attribute value */
    for (i = 0; i < aptr->at_size; i++)
        *avalue++ = vptr[i];
    *avalue = EOS;
}

/* db_aput - put the value of an attribute */
db_aput(aptr,vptr,avalue)
  struct attribute *aptr; char *vptr,*avalue;
{
    int i;

    /* initialize counter */
    i = 0;

    /* right justify numbers */
    if (aptr->at_type == TNUM)
        for (; i < aptr->at_size - strlen(avalue); i++)
            vptr[i] = ' ';

    /* put the attribute value */
    for (; i < aptr->at_size; i++)
        if (*avalue == 0)
            vptr[i] = 0;
        else
            vptr[i] = *avalue++;
}
SHAR_EOF
cat << \SHAR_EOF > sdb.mem




                       SDB - a Simple Database System

                              by David Betz
                            114 Davenport Ave.
                           Manchester, NH 03103
                              (603) 625-4691

                        Converted to the IBM/PC by
                              David N. Smith
                            44 Ole Musket Lane
                            Danbury, CT  06810
                              (203) 748-5934


        1.0  INTRODUCTION

        SDB is a simple database manager for small systems.  It  was
        developed  to  provide  a relatively low overhead system for
        storing data  on  machines  with  limited  disk  and  memory
        resources.   The current version runs on a PDT-11/150 with 2
        RX01 floppy disk drives and 60K bytes of  memory  under  the
        RT-11 operating system.  (it also runs on the VAX under VMS)

        SDB was originally intended  to  be  a  relational  database
        system, so many of the terms used in describing it are taken
        from the relational database literature.  Within the context
        of SDB the user can safely make the following associations:

             1.  RELATION can be taken to mean FILE

             2.  TUPLE can be taken to mean RECORD

             3.  ATTRIBUTE can be taken to mean FIELD

        It should be noted that SDB is not a  relationally  complete
        system.   It  provides  the relational operations of SELECT,
        PROJECT, and JOIN, but does not provide the  set  operations
        of  UNION,  INTERSECTION,  or  DIFFERENCE  as  well  as some
        others.


        2.0  RELATION FILE FORMATS

        SDB maintains a separate file for  each  relation  that  the
        user  creates.  This file contains a header block containing
        the definition of the relation including the names and types
        of  all  of the relation's attributes.  The remainder of the
        file contains fixed length records each containing one tuple
        from the relation.

        Tuples can be of three types:

             1.  active - tuples that contain actual active data

             2.  deleted - tuples that have been deleted

             3.  unused - tuples that haven't been used yet

        SDB - a Simple Database System                        Page 2


        Initially, all tuples are  unused.   When  a  new  tuple  is
        stored  into  a  relation,  the  first unused tuple is found
        (they are all contiguous at the end of the  relation  file).
        The new tuple is stored as an active tuple.

        When a tuple is deleted, it is marked as  such.   The  space
        previously  allocated  to  the  deleted tuple is left unused
        until the relation is compressed.

        It is possible that when attempting to store a new tuple, no
        unused  tuple can be found even though the relation contains
        fewer than the maximum active  tuples.   This  happens  when
        tuples  have  been  deleted since the time the relation file
        was last compressed.

        The compress function  allows  all  of  the  space  lost  by
        deleting tuples to be regained.  It does this by copying all
        of the active tuples as far backward in the file as possible
        leaving  all  of  the  available space toward the end of the
        file.



        3.0  SELECTION EXPRESSIONS

        A selection expression specifies a set of tuples over  which
        some  SDB  operation  is  to  be executed.  The syntax for a
        selection expression is:

        <rse>           ::= <rnames> [ where <boolean> ]
        <rnames>        ::= <rname> [ , <rname> ] ...
        <rname>         ::= <relation-name> [ <alias> ]

        When a single relation name  is  specified  in  a  selection
        expression,  each  tuple  within  that  relation  becomes  a
        candidate for selection.

        When more than one relation name is  specified,  the  tuples
        are  formed  by  taking  the  cross product of all specified
        relations.  If a relation is to be crossed with  itself,  an
        alias must be given to one or both of the occurances of that
        relation name in the selection expression.  This allows  SDB
        to determine which relation occurance is being refered to in
        the boolean part of the selection expression.

        After the set of candidate tuples is determined, the boolean
        expression  is evaluated for each candidate.  The candidates
        for which the boolean expression evaluates  to  TRUE  become
        the selected tuples.

        SDB - a Simple Database System                        Page 3


        4.0  INITIALIZATION FILE AND COMMAND FILES

        When SDB is first run,  it  attempts  to  read  and  process
        commands  from  a  file  named "SDB.INI".  This file usually
        contains macro definitions, but can contain  any  valid  SDB
        command.   In  addition,  it  is possible to process command
        files from within SDB.   This  is  done  by  typing  an  '@'
        followed by the command file name after the SDB prompt.



        5.0  FILE NAMES

        Whenever a file name is allowed in the syntax for a command,
        it  is  possible  to  use  either  an identifier or a quoted
        string.  An identifier is interpreted as the file name and a
        string  is  interpreted  as  a full file specification.  The
        string form allows for the  specification  of  an  alternate
        device or extension.



        6.0  FORM DEFINITION FILES

        A form  definition  file  contains  a  template  into  which
        attribute  values  are substituted during a print operation.
        There are two types of information that can be included in a
        form definition:

             1.  Literal text

             2.  Attribute references

        Attribute references are indicated by placing  the  name  of
        the  attribute  being  referenced  between  a  pair of angle
        brackets.  Literal text is anything that is not enclosed  in
        angle brackets.

        SDB - a Simple Database System                        Page 4


        Example:
        ________

        print using test amount,category from checks;

        Where test.frm contains:

        Amount: <amount>
        Category: <category>

        (MPK) For ease in generating English text, a $ character  may
        be placed before  the  attribute  name  inside  an  attribute
        reference, in which case no trailing blanks  will  appear  in
        the output for that reference.  For example:

        print using test number,category from checks;

	Where test.frm contains:

	Check #<$number> was spent on <$category


        7.0  ALIASES FOR RELATIONS AND ATTRIBUTES

        When a relation or attribute name is specified  in  a  print
        statement,  it  is possible to provide an alternate name for
        that relation or attribute.  This is useful  for  relations,
        when  it  is  necessary to join a relation to itself.  It is
        useful for attributes when it is  desired  that  the  column
        headers  in  a  table be different from the actual attribute
        names.  Also, alternate  attribute  names  can  be  used  in
        references  to that attribute in the where clause as well as
        in a  form  definition  file.   The  syntax  for  specifying
        aliases is:

            <name> <alias>


        Example:
        ________

        print using test amount a,category c from checks;

        Where test.frm contains:

        Amount: <a>
        Category: <c>

        SDB - a Simple Database System                        Page 5


        8.0  BOOLEAN EXPRESSIONS

        The syntax for boolean expressions:

        <expr>          ::= <land> [ '|' <land> ]
        <land>          ::= <relat> [ '&' <relat> ]
        <relat>         ::= <primary> [ <relop> <primary> ]
        <primary>       ::= <term> [ <addop> <term> ]
        <term>          ::= <unary> [ <mulop> <unary> ]
        <unary>         ::= <factor> | <unop> <unary>
        <factor>        ::= <operand> | '(' <expr> ')'
        <operand>       ::= <number> | <string> | <attribute>
        <attribute>     ::= [ <rname> . ] <aname>
        <relop>         ::= '=' | '<>' | '<' | '>' | '<=' | '>='
        <addop>         ::= '+' | '-'
        <mulop>         ::= '*' | '/'
        <unop>          ::= '+' | '-' | '~'


        Operators:

             1.  '=' - equal

             2.  '<>' - not equal

             3.  '<' - less than

             4.  '>' - greater than

             5.  '<=' - less than or equal

             6.  '>=' - greater than or equal

             7.  '+' - addition or unary plus (not implemented)

             8.  '-' - subraction or unary minus (not implemented)

             9.  '*' - multiplication (not implemented)

            10.  '/' - division (not implemented)

            11.  '&' - logical and

            12.  '|' - logical or

            13.  '~' - logical not

        Operands:

             1.  number - a string of digits containing at most  one
                 decimal point

             2.  string - a string of characters enclosed in  double
                 quotes

        SDB - a Simple Database System                        Page 6


             3.  attribute - an attribute name optionally  qualified
                 by a relation name


        SDB - a Simple Database System                        Page 7


        9.0  INTERACTIVE COMMAND DESCRIPTIONS

        Function:
        _________

        Create a relation file


        Format:
        _______

        create <rname> ( <alist> ) <size>


        Rules:
        ______

             1.  <rname> is the name of the relation file

             2.  <alist> is a list of attribute definitions  of  the
                 form:

                   <aname> { char | num } <size>

                 where:

                 1.  <aname> is the name of the attribute

                 2.  the type of the attribute is either  "char"  or
                     "num"

                 3.  <size> is the number of bytes allocated to  the
                     attribute value


             3.  <size> is the maximum number of tuples the file  is
                 to hold



        Example:
        ________

        create checks (
            number      num     4
            date        char    8
            payee       char    20
            amount      num     8
            category    char    5
        ) 200

        This command creates a relation file named "checks.sdb" with
        attributes   "number",   "date",   "payee",   "amount",  and
        "category" and space to store 200 tuples.

        SDB - a Simple Database System                        Page 8


        Function:
        _________

        Insert tuples into a relation


        Format:
        _______

        insert <rname>


        Rules:
        ______

             1.  <rname> is the name of a relation

             2.  the user will be prompted for  the  values  of  the
                 attributes for the tuple to be inserted

             3.  a  null  response  to  an  attribute  prompt   will
                 terminate tuple entry

             4.  if a null value is desired, a single space  can  be
                 entered


        SDB - a Simple Database System                        Page 9


        Function:
        _________

        Delete tuples from a set of relations


        Format:
        _______

        delete <rse> ;


        Rules:
        ______

             1.  <rse> is a tuple selection expression

             2.  selected tuples are deleted



        Example:
        ________

        delete checks where category = "junk";

        SDB - a Simple Database System                       Page 10



        Function:
        _________

        Update the values of selected attributes in selected tuples


        Format:
        _______

        update { <attrs> | * } from <rse> ;


        Rules:
        ______

             1.  <attrs> is a list of attribute names to be updated

             2.  * means all attributes

             3.  <rse> is a tuple selection expression

             4.  for each  set  of  selected  tuples,  the  user  is
                 prompted for new values for the selected attributes

             5.  a null response to an attribute prompt will  retain
                 the previous attribute value

             6.  if a null value is desired, a single space  can  be
                 entered



        Example:
        ________

        update amount,category from checks where number > 10;

        SDB - a Simple Database System                       Page 11


        Function:
        _________

        Print a table of values of selected attributes


        Format:
        _______

        print [ using <fname> ] { <attrs> | * } from  <rse>  [  into
        <fname> ] ;


        Rules:
        ______

             1.  using  <fname>  indicates  output  using   a   form
                 definition file (.FRM)

             2.  <attrs> is a list of attribute names to be printed

             3.  * means all attributes

             4.  <rse> is a tuple selection expression

             5.  <fname> is the name of an file to which  the  table
                 will be output (.TXT)

             6.  if the output file name is omitted,  output  is  to
                 the terminal

             7.  for each set of selected tuples, a table  entry  is
                 printed containing the selected attributes



        Example:
        ________

        print payee,amount from checks where category = "junk";

        SDB - a Simple Database System                       Page 12


        Function:
        _________

        Import tuples from a file into a relation


        Format:
        _______

        import <fname> into <rname>


        Rules:
        ______

             1.  <fname> is the name of the input file (.DAT)

             2.  the input file contains the  values  of  the  tuple
                 attributes with each on a separate line

             3.  <rname> is the name of a relation

             4.  tuples are appended to the named relation


        SDB - a Simple Database System                       Page 13


        Function:
        _________

        Export tuples from a relation into a file


        Format:
        _______

        export <rname> [ into <fname> ] ;


        Rules:
        ______

             1.  <rname> is the name of a relation

             2.  <fname> is the name of the output file (.DAT)

             3.  if the output file name is omitted,  output  is  to
                 the terminal

             4.  tuples are written to  the  output  file  with  one
                 attribute value per line


        SDB - a Simple Database System                       Page 14


        Function:
        _________

        Extract the definition of a relation into a file


        Format:
        _______

        extract <rname> [ into <fname> ] ;


        Rules:
        ______

             1.  <rname> is the name of a relation

             2.  <fname> is the name of the output file (.DEF)

             3.  if the output file name is omitted,  output  is  to
                 the terminal

             4.  the definition of the relation is  written  to  the
                 output file


        SDB - a Simple Database System                       Page 15


        Function:
        _________

        Compress a relation file


        Format:
        _______

        compress <rname>


        Rules:
        ______

             1.  <rname> is the name of a relation file

             2.  tuples are copied toward the front of the  relation
                 file  such  that  any  space  freed  by  previously
                 deleted tuples becomes adjacent to the  free  space
                 at the end of the file, thus becoming available for
                 use in inserting new tuples


        SDB - a Simple Database System                       Page 16


        Function:
        _________

        Sort a relation file


        Format:
        _______

        sort <rname> by <sname> { , <sname } ...  ;


        Rules:
        ______

             1.  <rname> is the name of a relation file

             2.  <sname> is the name of  an  attribute  to  sort  on
                 followed optionally by "ascending" or "descending"

             3.  if a sort order  is  not  specified,  ascending  is
                 assumed

             4.  tuples within the  relation  are  sorted  in  place
                 using the attributes indicated


        SDB - a Simple Database System                       Page 17


        Function:
        _________

        Define a macro


        Format:
        _______

        define <mname>


        Rules:
        ______

             1.  <mname> is the name of the macro being defined

             2.  if a macro with the specified name already  exists,
                 it is replaced

             3.  after entering the define command, definition  mode
                 is entered

             4.  definition  mode  is  indicated   by   the   prompt
                 "SDB-DEF>"

             5.  all lines typed in definition mode are added to the
                 macro definition

             6.  a blank line terminates definition mode

             7.  a macro can be deleted by entering a blank line  as
                 the only line in the definition

             8.  after a macro is defined, every  occurance  of  the
                 macro name is replaced by the macro definition


        SDB - a Simple Database System                       Page 18


        Function:
        _________

        Show a macro definition


        Format:
        _______

        show <mname>


        Rules:
        ______

             1.  <mname> is the name of a macro whose definition  is
                 to be shown


        SDB - a Simple Database System                       Page 19


        Function:
        _________

        Print a short help message



        Format:
        _______

        help


        Rules:
        ______

             1.  (none)


        SDB - a Simple Database System                       Page 20


        Function:
        _________

        Exit from SDB


        Format:
        _______

        exit


        Rules:
        ______

             1.  (none)


        SDB - a Simple Database System                       Page 21


        10.0  PROGRAM INTERFACE

        SDB provides a callable program interface to allow  programs
        written  in  DECUS-C  to access relation files.  In order to
        use the call interface, the users program should  be  linked
        with  the SDBUSR.OBJ object library.  Also, additional stack
        space should be allocated at link  time  using  the  /BOTTOM
        qualifier  on  the link command.  /BOTTOM:3000 seems to work
        well, but it is probably possible to get away with less.

        Example:
        ________

        #include <stdio.h>
        #include "sdb.h"

        main()
        {
            DB_SEL *sptr;
            char payee[100],amount[100];

            /* setup retrieval */
            if ((sptr = db_retrieve("checks where amount > 25.00")) == NULL) {
                printf("*** error: %s ***\n",db_ertxt(dbv_errcode));
                exit();
            }

            /* bind user variables to attributes */
            db_bind(sptr,"checks","payee",payee);
            db_bind(sptr,"checks","amount",amount);

            /* loop through selection */
            while (db_fetch(sptr))
                printf("%s\t%s\n",payee,amount);

            /* finish selection */
            db_done(sptr);
        }

        SDB - a Simple Database System                       Page 22


        Function:
        _________

        Setup a tuple retrieval context


        Format:
        _______

        dbptr = db_retrieve(sexpr [ ,arg ]...)


        Rules:
        ______

             1.  sexpr is a pointer to a string containing an rse

             2.  arg is a "printf" argument

             3.  dbptr is a database context pointer

             4.  db_retrieve returns NULL on errors

             5.  on errors, the error code is in dbv_errcode


        SDB - a Simple Database System                       Page 23


        Function:
        _________

        Fetch the next set of tuples from a retrieval context


        Format:
        _______

        db_fetch(dbptr)


        Rules:
        ______

             1.  dbptr is a database context pointer

             2.  updates the values of all bound user variables

             3.  db_fetch returns FALSE if no more tuples  match  or
                 if an error occurs

             4.  on errors, the error code is in dbv_errcode


        SDB - a Simple Database System                       Page 24


        Function:
        _________

        Update the current tuple within a retrieval context


        Format:
        _______

        db_update(dbptr)


        Rules:
        ______

             1.  dbptr is a database context pointer

             2.  db_update returns FALSE if an error occurs

             3.  on errors, the error code is in dbv_errcode


        SDB - a Simple Database System                       Page 25


        Function:
        _________

        Store a new tuple within a retrieval context


        Format:
        _______

        db_store(dbptr)


        Rules:
        ______

             1.  dbptr is a database context pointer

             2.  db_store returns FALSE if an error occurs

             3.  on errors, the error code is in dbv_errcode


        SDB - a Simple Database System                       Page 26


        Function:
        _________

        Bind a user variable to  the  value  of  a  tuple  attribute
        within a retrieval context


        Format:
        _______

        db_bind(dbptr,rname,aname,value)


        Rules:
        ______

             1.  dbptr is a database context pointer

             2.  rname is a pointer to the relation name

             3.  aname is a pointer to the attribute name

             4.  value is a pointer to a character array to  receive
                 the attribute value

             5.  db_bind returns FALSE if an error occurs

             6.  on errors, the error code is in dbv_errcode


        SDB - a Simple Database System                       Page 27


        Function:
        _________

        Get the value  of  a  tuple  attribute  within  a  retrieval
        context


        Format:
        _______

        db_get(dbptr,rname,aname,value)


        Rules:
        ______

             1.  dbptr is a database context pointer

             2.  rname is a pointer to the relation name

             3.  aname is a pointer to the attribute name

             4.  value is a pointer to a character array to  receive
                 the attribute value

             5.  db_get returns FALSE if an error occurs

             6.  on errors, the error code is in dbv_errcode


        SDB - a Simple Database System                       Page 28


        Function:
        _________

        Put the value  of  a  tuple  attribute  within  a  retrieval
        context


        Format:
        _______

        db_put(dbptr,rname,aname,value)


        Rules:
        ______

             1.  dbptr is a database context pointer

             2.  rname is a pointer to the relation name

             3.  aname is a pointer to the attribute name

             4.  value is a pointer to the new value

             5.  db_put returns FALSE if an error occurs

             6.  on errors, the error code is in dbv_errcode


        SDB - a Simple Database System                       Page 29


        Function:
        _________

        Discontinue usage of a retrieval context



        Format:
        _______

        db_done(dbptr)


        Rules:
        ______

             1.  dbptr is a database context pointer


        SDB - a Simple Database System                       Page 30


        Function:
        _________

        Translate an error code to an error message text


        Format:
        _______

        db_ertxt(errcode)


        Rules:
        ______

             1.  errcode is an SDB error code

SHAR_EOF
#	End of shell archive
exit 0