/*

qsort.c

Author:	DTJ

Interface to the C library function qsort.  Contains the numeric and ASCII
comparison

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/*
  Global variables, set by the qsorter_ interface, to be read by the
  numeric comparison routines.
*/

static int	nKeys;		/* Number of keys to sort on */
static int	Descend;	/* TRUE to sort in descending order */
static short	Offset;		/* For single numeric comparison functions */

static short	*Offsets;	/* Bytes each key field is from the top */
static short	*Widths;	/* Format widths of each field */
static char	*Formats;	/* Second characters of format digraphs */

typedef int (*CMP_FUNC)(const void *,const void*);

/************************** Comparison Routines ***********************/

int ASCcompare(char *pch1, char *pch2)
{
  int		stat;

  stat = strncmp(pch1 + Offset, pch2 + Offset, Widths[0]);
  return (Descend ? -1 * stat : stat);
}



/*
  Macros to ease the maintenance of the numeric comparison functions.
  These functions are all very much alike, but must have hard-coded type
  names for the compiler.  In order to reduce the number of checks within
  each comparison function to a minimum, a different comparison function
  is used for each data type.

  Every function will have ptra and ptrb as the input arguments.
  a and b are the translated forms.
*/

#define PREAMBLE(name)  int name (char *ptra, char *ptrb)
#define DECLARE(type)   { int stat; \
			  type a, b; \
				a = *(type *)(ptra + Offset); \
				b = *(type *)(ptrb + Offset);
#define FULLCOMPARE		if (a == b)	stat = 0; \
				else if (a > b)	stat = 1; \
				else		stat = -1;
#define RETURN 		  return ( Descend ? -1 * stat : stat ); }


PREAMBLE(SDcompare)
DECLARE(double)
FULLCOMPARE
RETURN

PREAMBLE(SFcompare)
DECLARE(float)
FULLCOMPARE
RETURN

PREAMBLE(SLcompare)
DECLARE(long)
FULLCOMPARE
RETURN

PREAMBLE(SIcompare)
DECLARE(int)
FULLCOMPARE
RETURN

PREAMBLE(SScompare)
DECLARE(short)
FULLCOMPARE
RETURN

PREAMBLE(SBcompare)
DECLARE(char)
FULLCOMPARE
RETURN


/* MultiKeyCompare

For doing a multiple-key comparison.

*/

int	MultiKeyCompare(char *ptra, char *ptrb)
{
  int	i, stat;
  double	d1, d2;
  float		f1, f2;
  int           inttest;
  
  for (i = 0; i < nKeys; i++) {
    switch (Formats[i]) {
      case 'D' :
        d1 = *(double *)(ptra + Offsets[i]);
        d2 = *(double *)(ptrb + Offsets[i]);
	if (d1 == d2)
	  stat = 0;
	else if (d1 > d2)
	  stat = 1;
	else
	  stat = -1;
	break;
      case 'F' :
	f1 = *(float *)(ptra + Offsets[i]);
	f2 = *(float *)(ptrb + Offsets[i]);
	if (f1 == f2)
	  stat = 0;
	else if (f1 > f2)
	  stat = 1;
	else
	  stat = -1;
	break;
      case 'L' : 
        if (Widths[i] > sizeof(inttest))
          stat = *(long *)(ptra + Offsets[i]) - *(long *)(ptrb + Offsets[i]);
        else
          stat = *(int *)(ptra + Offsets[i]) - *(int *)(ptrb + Offsets[i]);
	break;
      case 'I' : stat = *(short *)(ptra + Offsets[i]) -
			*(short *)(ptrb + Offsets[i]);
		 break;
      case 'B' : stat = *(char *)(ptra + Offsets[i])  -
			*(char *)(ptrb + Offsets[i]);
		 break;
      case 'A' :
	stat = strncmp(ptra + Offsets[i], ptrb + Offsets[i], Widths[i]);
	break;
    }
    if (stat != 0) return (Descend ? -1 * stat : stat);
  }	/* for all keys */
  return(stat);
}  /* MultiKeyCompare */


/**************************** End Comparisons **************************/


#if vaxc
#define	qsorter_	qsorter
#endif

void    qsorter_ (int *descend, double *buf, int *elements,
          int *width,
          int *keys, short *offsets, short *fieldwidths, char *formats)
{
  int		(*comparefunc)();
  int           inttest;

  Descend = *descend;
  Offsets = offsets;
  Formats = formats;
  Widths  = fieldwidths;
  nKeys   = *keys;

  if (nKeys > 1) {
    qsort((char *)buf, *elements, *width,(CMP_FUNC) MultiKeyCompare);
  }
  else {
    Offset = Offsets[0];
    switch (Formats[0]) {
      case 'D' : comparefunc = SDcompare; break;
      case 'F' : comparefunc = SFcompare; break;
      case 'L' :
        if (*Widths > sizeof(inttest)) comparefunc = SLcompare; 
        else comparefunc = SIcompare; 
        break;
      case 'I' : comparefunc = SScompare; break;
      case 'B' : comparefunc = SBcompare; break;
      case 'A' : comparefunc = ASCcompare; break;
    }

    qsort((char *)buf, *elements, *width,(CMP_FUNC) (*comparefunc));
  } /* if there is only 1 key */
}
