/* SDBSRT - sort routines */

#include "sdbio.h"

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

/* local variables */
static struct skey *skeys;
static struct scan *inscan,*outscan;
static int intnum,outtnum;

/* get_skeys - get sort key list */
static struct skey *get_skeys(sptr)
  struct scan *sptr;
{
    struct skey *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;
{
    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 ((inscan = db_ropen(dbv_tstring)) == NULL)
	return (FALSE);
    if ((outscan = db_ropen(dbv_tstring)) == NULL) {
    	db_rclose(inscan);
	return (FALSE);
    }

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

    /* do the sort */
    result = sortit(inscan);

    /* close the relation */
    db_rclose(inscan);
    db_rclose(outscan);

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

    return (result);
}

/* free_skeys - free a list of sort keys */
static free_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));
}

/* getrec - get a record for the sort */
static int getrec(buf)
  char *buf;
{
    if (intnum <= inscan->sc_relation->rl_tcnt)
	return (db_rget(inscan,intnum++,buf));
    else
	return (FALSE);
}

/* putrec - put a record for the sort */
static int putrec(buf)
  char *buf;
{
    return (db_rput(outscan,outtnum++,buf));
}

/* cmprec - compare two tuples */
static int cmprec(rec1,rec2)
  char *rec1,*rec2;
{
    struct skey *cskey;
    int result;

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

    return (result);
}

/* cattr - compare two attributes */
static int cattr(cskey,rec1,rec2)
  struct skey *cskey; char *rec1,*rec2;
{
    int astart,aend,i;

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

    for (i = astart; i < aend; i++)
    	if (rec1[i] != rec2[i])
    	    break;

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

    if (rec1[i] < rec2[i])
    	if (cskey->sk_type == ASCENDING)
    	    return (-1);
    	else
    	    return (1);
    else
    	if (cskey->sk_type == ASCENDING)
    	    return (1);
    	else
    	    return (-1);
}

/* sortit - sort the relation */
static int sortit(sptr)
  struct scan *sptr;
{
    intnum = outtnum = 1;
    sort(getrec,putrec,cmprec,sptr->sc_relation->rl_size);
    return (TRUE);
}
