/*===========================================================================
  Copyright (C) 1995-2009 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.TYPE        Module
.NAME        tbassoap.c
.LANGUAGE    C
.AUTHOR      IPG-ESO Garching
.CATEGORY    Data Organizer utilities
.COMMENTS       This module implements the following Midas commands:
             ASSOCIATE/IMA table columns exptype table_rule outtab

.VERSION  1.0   15-Mar-1993   Definition     M. Peron

 090828		last modif
-----------------------------------------------------------*/

#include <atype.h>
#include <tbldef.h>
#include <tblsys.h>
#include <tblerr.h>
#include <str.h>
#include <midas_def.h>
#include <macrogen.h>
#include <proto_tbl.h>
#include <computer.h>

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



#define issign(c)   ((c == '+') || (c == '-'))
#define ATTSIZE 49
#define BUFM 30				/* synchronize with tbcomsel !! */
#define COUNSIZE 8
#define RANGESIZE 16
#define COLS 10
#define STRINGLEN 256

double tdtrue,tdfalse;
char *line,*token;
int first,nochar,nline,nsci,associate,*rownumber;
int refrow,token_type,action,exist,sort_off,nconst[BUFM];
int number_of_rows;
int tmno[BUFM],tmnoc[BUFM];

extern int find_string(), stucmp(), stsnum(), get_token();
extern int level00(), fidelim();


void Qsorta(), Qsort8(), assoone(), decrypt();

int diff_a();


/*

*/

void associate_ima(intabdat,exptype,incol,intabrul,outtab,fc,imaname,ocolname,
icount)
char intabdat[60],intabrul[60],exptype[10],outtab[60],incol[80];
char ocolname[TBL_LABLEN+1],fc[2],imaname[60];
int icount;

{
int status,tid1,tid2,tid3,nrow,nrow2,natt,ncand,oldnrow;
int null, dummy,clos,ibuf[7];
int coltype,colname;
int colfunct,colran1,colran2,colweight;
int outcol1,outcol2,outcol3,outrow,icol[COLS],flag[COLS];
int nc,ec,el,ed,new,sel,nsel,flagone;
int imarow,ifexist,outmast;
int i,j,k,mlen;
static int one = 1;
static int zero = 0;

char exposure[256],qual[15];
char range_1[256],range_2[256],*func_att;
char ws[4];
char *linesave,*linesecond;
char mastname[60];
char *string[BUFM],*cdata[BUFM];
				/* length of filenames in input table! */
char fname_1[80],assoname[80];

float tblsel;

double *data[BUFM],*range1[BUFM],*range2[BUFM];
double *metric,rank[BUFM];
char  *filename, *tempcrit;



clos = 1;
natt = 0;
action = 1;
for (i=0; i<BUFM;i++) {
    tmno[i] = -1;
    tmnoc[i] =-1;
    }
TCMCON(&tblsel,&tdtrue,&tdfalse);
for (i=0 ; i<BUFM; i++) {
      range1[i] = (double *)0;
      range2[i] = (double *)0;
      data[i] = (double *)0;
      cdata[i] = (char *)0;
      string[i] = (char *)0;
      }
ibuf[3] = -1;
ibuf[4] = -1;
ibuf[5] = -1;
ibuf[6] = -1;

func_att = osmmget(STRINGLEN * 8);
line = osmmget(STRINGLEN+2);
linesave = osmmget(STRINGLEN+2);
linesecond = osmmget(STRINGLEN+2);
token = osmmget(STRINGLEN+2);
(void) TCTOPN(intabdat,F_IO_MODE,&tid1);
(void) TCIGET(tid1,&dummy,&nrow,&dummy,&dummy,&dummy);
if (incol[0] == '+') nc = 0;
else (void) TCCSEL(tid1,incol,COLS,icol,flag,&nc);
(void) TCTOPN(intabrul,F_IO_MODE,&tid2);
(void) TCLSER(tid2,"FUNCTION",&colfunct);
if (colfunct < 0) {
     SCTPUT("Column FUNCTION not found in the input rule table");
     SCSEPI();
     }
status = TCLSER(tid2,"RANGE_1",&colran1);
if (colran1 < 0) {
     SCTPUT("Column RANGE_1 not found in the input rule table");
     SCSEPI();
     }
status = TCLSER(tid2,"RANGE_2",&colran2);
if (colran2 < 0) {
     SCTPUT("Column RANGE_2 not found in the input rule table");
     SCSEPI();
     }
status = TCLSER(tid2,"WEIGHT",&colweight);
if (colweight < 0) {
     SCTPUT("Column WEIGHT not found in the input rule table");
     SCSEPI();
     }
ibuf[0] = tid1;
ibuf[2] = nrow;
metric = (double *)osmmget(2 * nrow * sizeof(double));
filename = osmmget(nrow * 60 * sizeof(char));
status = TCIGET(tid2,&dummy,&nrow2,&dummy,&dummy,&dummy);
SCECNT("GET",&ec,&el,&ed);
SCECNT("PUT",&one,&zero,&zero);
new = 0;
if (fc[0] == 'C' || fc[0] == 'c') {
       status = TCSCNT(tid1,&nsel);
       status = TCTINI(outtab,F_TRANS,F_O_MODE,2,nsel,&tid3);
       new = 1;
       }
else 
       (void) TCTOPN(outtab,F_IO_MODE,&tid3);
SCECNT("PUT",&ec,&el,&ed);
status = TCCSER(tid3,":SC",&outcol1);
if (outcol1 <0)
status = TCCINI(tid3,D_C_FORMAT,60,"A20","unitless","SC",&outcol1);
if (ocolname[0] == '\0') {
        status = TCLSER(tid3,exptype,&outcol2);
        if (outcol2 < 0) {
            status = TCCINI(tid3,D_C_FORMAT,60 * icount,"A20","unitless",
                     exptype,&outcol2);
            TCAPUT(tid3,outcol2,icount);
            }
        sprintf(mastname,"%s_MAST",exptype);
        sprintf(qual,"QUAL_%s",exptype);
        }
else {
        status = TCLSER(tid3,ocolname,&outcol2);
        if (outcol2 < 0) {
            status = TCCINI(tid3,D_C_FORMAT,60 * icount,"A20","unitless",
                     ocolname,&outcol2);
            TCAPUT(tid3,outcol2,icount);
            }
        sprintf(qual,"QUAL_%s",ocolname);
        sprintf(mastname,"%s_MAST",ocolname);
        }

status = TCLSER(tid3,qual,&outcol3);
if (outcol3 <0) 
status = TCCINI(tid3,D_I4_FORMAT,icount,"I1","unitless",qual,&outcol3);
status = TCLSER(tid3,mastname,&outmast);
if (outmast <0) 
status = TCCINI(tid3,D_C_FORMAT,60,"A20","unitless",mastname,&outmast);
status = TCCSER(tid1,":EXPTYPE",&coltype);
status = TCCSER(tid1,":FILENAME",&colname);
if (imaname[0] != '\0') {
 TCESRC(tid1, colname,imaname,1,strlen(imaname),1,&imarow); 
 flagone = 1;
 }
else flagone = 0;
outrow = 0;
status = TCIGET(tid3,&dummy,&oldnrow,&dummy,&dummy,&dummy);
tempcrit = osmmget(256);
oscfill(line,256,'\0');
strcpy(line,":EXPTYPE.EQ.\"~");
strcat(line,exptype);
strcat(line,"\"");
for (i=0; i< nc; i++) {
    strcat(line,".AND.");
    sprintf(ws,"#%d",icol[i]);
    strcat(line,ws); 
    strcat(line,".EQ.REFVAL(");
    strcat(line,ws);
    strcat(line,")");
    }
strcpy(linesecond,line);
for (i=0; i<nrow2; i++) {
   status = TCERDC(tid2,i+1,colfunct,func_att + i * STRINGLEN,&null);
   if (null) continue;
   status = TCERDC(tid2,i+1,colran1,range_1,&null);
   mlen = strlen(range_1);
   for (k = mlen; k<256; k++) range_1[k] = '\0';
   status = TCERDC(tid2,i+1,colran2,range_2,&null);
   mlen = strlen(range_2);
   for (k = mlen; k<256; k++) range_2[k] = '\0';
   range1[i] = (double *)osmmget(2 * sizeof(double));
   range2[i] = (double *)osmmget(2 * sizeof(double));
   status = TCERDD(tid2,i+1,colweight,&rank[i],&null);
   if (line[0] != '\0') strcat(line,".AND.");
   if (linesecond[0] != '\0') strcat(linesecond,".AND.");
   *range1[i] = *(range1[i]+1) = 0;
   *range2[i] = *(range2[i]+1) = 0;
   decrypt(func_att +i * STRINGLEN,range_1,line,tempcrit,range1[i]);
   decrypt(func_att +i * STRINGLEN,range_2,linesecond,tempcrit,range2[i]);
   } 
nline = strlen(line);
strcpy(linesave,line);
nsci = 0;
associate = 0;
if (flagone) {
   TCERDC(tid1,imarow,coltype,exposure,&null);
   if (stumatch(exposure,"SC") == 2) {
     TCERDC(tid1,imarow,colname,fname_1,&null);
     TCIGET(tid3,&dummy,&outrow,&dummy,&dummy,&dummy);
     if (outrow >0 )
     TCESRC(tid3, outcol1,fname_1,1,strlen(fname_1),1,&ifexist); 
     if (ifexist <=0 ) {
       nsci = outrow;
       outrow++;
       TCEWRC(tid3,outrow,outcol1,fname_1);
     }
     else {
        outrow = ifexist-1;
        nsci = outrow;
        }
     flagone = 2;
     refrow = imarow;
     assoone(tid1,tid3,nrow,nrow2,colname,exptype,outcol2,outcol3,filename,
         outmast,refrow,outrow,icount,linesecond,range1,
         range2,rank,func_att,metric,data,cdata,string,flagone);
     strcpy(line,linesave);
     nline = strlen(line);
     }
   else if (stucmp(exposure,exptype) == 0) {
     associate = 1;
     number_of_rows = 1;
     rownumber = (int *)osmmget((icount + 1 ) * sizeof(int));
     *rownumber = imarow;
     for (i=1; i<oldnrow; i++) {
        number_of_rows = 1;
        TCERDC(tid3,i,outcol1,fname_1,&null);
        TCESRC(tid1, colname,fname_1,1,strlen(fname_1),1,&refrow);
        for (j=0; j<icount; j++) {
           TCARDS(tid3,i,outcol2,j+1,assoname);
           if (assoname[0] == '\0') continue;
           TCESRC(tid1, colname,assoname,1,strlen(assoname),1,rownumber+
                  number_of_rows); 
           number_of_rows++;
           }    
     for (j=number_of_rows; j<icount; j++) *(rownumber+j) = 0;
     for(j=0; j<nrow * 2; j++) *(metric+j) = 0;
      ncand = 0;
     outrow = i-1;
     nsci = outrow;
     assoone(tid1,tid3,nrow,nrow2,colname,exptype,outcol2,outcol3,filename,
         outmast,refrow,outrow,icount,linesecond,range1,
         range2,rank,func_att,metric,data,cdata,string,flagone);
     strcpy(line,linesave);
     nline = strlen(line);
     }
 }
}
else  for (i=0; i<nrow; i++) {
    TCSGET(tid1,i+1,&sel);
    if (!sel) continue;
    for(j=0; j<nrow * 2; j++) *(metric+j) = 0;
    ncand = 0;
    TCERDC(tid1,i+1,coltype,exposure,&null);
    if (stumatch(exposure,"SC") == 2) {
      nsci++;
      TCERDC(tid1,i+1,colname,fname_1,&null);
      outrow = nsci;
      TCEWRC(tid3,outrow,outcol1,fname_1);
      refrow = i+1;
      printf("filename: %s\n",fname_1);
      assoone(tid1,tid3,nrow,nrow2,colname,exptype,outcol2,outcol3,filename,
         outmast,refrow,outrow,icount,linesecond,range1,
         range2,rank,func_att,metric,data,cdata,string,flagone);
      strcpy(line,linesave);
      nline = strlen(line);
      }
}

status = TCTCLO(tid3);
}
/*

*/

void assoone(tid1,tid3,nrow,nrow2,colname,exptype,outcol2,outcol3,filename,
    outmast,refrow,outrow,icount,linesecond,range1,
    range2,rank,func_att,metric,data,cdata,string,flagone)

int tid1,tid3,refrow,outrow,colname;
int nrow,nrow2,outcol2,outcol3,icount,flagone,outmast;
char  *exptype, *linesecond,*filename;
char *string[BUFM],*cdata[BUFM],*func_att;
double *metric,*data[BUFM],*range1[BUFM],*range2[BUFM],rank[BUFM];

{
int ibuf[7],ncand,*indx,*indx1,i,j,k,l,m,*perm,null; 
int outrow_1,clen;
 
double consta[BUFM],quality;
 
char **temp,**tempmast,*filetemp,*sort_start;
char *c3val,cval[60];

 

ibuf[3] = -1;
ibuf[4] = -1;
ibuf[5] = -1;
ibuf[6] = -1;
ibuf[0] = tid1;

      for (i=0; i< BUFM; i++) {
        if (tmno[i] != -1 ) {
          SCFCLO(tmno[i]);
          data[i] = (double *) 0;
          tmno[i] = -1;
          }
        if (tmnoc[i] != -1 ) {
          SCFCLO(tmnoc[i]);
          cdata[i] = (char *)0;
          } 
        }
      c3val = osmmget(icount*60);

      if (associate) ibuf[2] = number_of_rows;
      else ibuf[2] = nrow;
      indx = (int *)osmmget(nrow * sizeof(int));
      indx1 = (int *)osmmget(nrow * sizeof(int));
      temp = (char  **)osmmget(nrow * sizeof(char *));
      tempmast = (char  **)osmmget(icount * sizeof(char *));
      filetemp = osmmget(nrow * 60 * sizeof(char));
      get_token();
      level00(ibuf,data,cdata,consta,nconst,string);
      for (j=0; j< ibuf[2]; j++) *(metric+j) = *(data[0]+j);
      line -= nline;
      nline = strlen(linesecond);
      strcpy(line,linesecond);
      for (i=0; i<=ibuf[4]; i++) osmmfree(string[i]);
      ibuf[3] = -1;       
      ibuf[4] = -1;      
      ibuf[5] = -1;     
      ibuf[6] = -1;  

for (i=0; i<BUFM;i++) {
   if (tmnoc[i] != -1 ) {
          SCFCLO(tmnoc[i]);
          cdata[i] = (char *)0;
          } 
   }

      get_token();
      level00(ibuf,data,cdata,consta,nconst,string);
      for (j=0; j< ibuf[2]; j++) *(metric+j) = *(metric+j) + *(data[0]+j);
      ncand = 0;
      if (!flagone || flagone == 2) {
      for (j=0; j< ibuf[2]; j++) 
         if (*(metric+j)) {
            *(metric+ncand) = *(metric+j);
            *(indx+ncand) = j;
            *(indx1+ncand) = j;
            ncand++;
            }
       }
       else{
      for (j=0; j< ibuf[2]; j++) 
         if (*(metric+j)) {
            *(metric+ncand) = *(metric+j);
            *(indx+ncand) = *(rownumber+j);
            *(indx1+ncand) = j;
            ncand++;
            }
       }
       for (j=ncand; j<ibuf[2]; j++) *(metric+j) = 0;
       if (ncand != 0) {
       for (l=0; l< nrow2; l++) {
        line -= nline;
        oscfill(line,nline,'\0');
        strcpy(line,func_att + l * STRINGLEN);
        nline = strlen(line);
        exist = 0;
        for (i=0; i<=ibuf[4]; i++) osmmfree(string[i]);
        ibuf[3] = -1;       
        ibuf[4] = -1;      
        ibuf[5] = -1;     
        ibuf[6] = -1;  
for (i=0; i<BUFM;i++) {
   if (tmnoc[i] != -1 ) {
          SCFCLO(tmnoc[i]);
          cdata[i] = (char *)0;
          } 
   }
        get_token();
        level00(ibuf,data,cdata,consta,nconst,string);
        k = 0;
        for (j=0; j<ncand; j++)  
            if (*(metric+j) == 1 && ( *range2[l] || *(range2[l]+1)))
              *(metric+j+ncand) += rank[l] * ABSOLUTE(*(data[0]+ *(indx1 + j))/
                (*(range2[l])-*(range2[l]+1)));
            else  if ( *range1[l] || *(range1[l]+1)) 
               *(metric+j+ncand)  += rank[l] * ABSOLUTE(*(data[0]+ *(indx1 +j)) 
                /(*(range1[l])-*(range1[l]+1)));

         
      }
      if (!flagone || flagone == 2) 
      for (j=0; j< ncand; j++) {
              temp[j] = (char *)(metric+j);
              TCERDC(tid1,*(indx+j)+1,colname,filetemp + 60 *j,&null);
              }
      else
      for (j=0; j< ncand; j++) {
              temp[j] = (char *)(metric+j);
              TCERDC(tid1,*(indx+j),colname,filetemp + 60 *j,&null);
              }
      sort_start = temp[0];
      sort_off = ncand * 8;
      Qsort8(temp,0,ncand-1);
      for (j=0, perm = (int *)temp; j<ncand; j++, perm++)
          *perm = (temp[j]-sort_start)/8;
      perm = (int *)temp;
      for (j=0; j<ncand; j++)
          strncpy(filename + 60 * j, filetemp + 60 * perm[j], 60);
      k = 0;
       if (flagone) outrow_1 = nsci+1;
       else outrow_1 = nsci;
      for (j=0; j<ncand; j++) {
          k++;
          TCAWRS(tid3,outrow_1,outcol2,j+1,filename+60*j);
          quality = 3 - *(metric+perm[j]);
          TCAWRD(tid3,outrow_1,outcol3,j+1,1,&quality);
          if (k == icount) break;
         }
      if (k == icount) {
         tempmast[icount-1] = filename+60*(icount-1);
        for (j=icount-2; j>=0; j--) {
         tempmast[j] = filename+60*j;
         strcat(tempmast[j],tempmast[j+1]);
         }
         sort_start = tempmast[0];
         sort_off = icount * 60;
         Qsorta(tempmast,0,icount-1);
         for (l=0,perm = (int *)tempmast; l<icount; l++, perm++)
              *perm = (tempmast[l]-sort_start)/60;
         perm = (int *)tempmast;
         for (m=0; !isdigit(filename[m]) ; m++);
         strncpy(c3val,filename,m);
         for (l=0; l<icount; l++) {
             strcat(c3val,"_");
             strncpy(cval,filename + 60*perm[l],60);
             for (m=1; !isdigit(cval[m]) || cval[m] == '0'; m++);
             clen = atoi(cval+m);
             sprintf(c3val,"%s%d",c3val,clen);
             } 
         TCEWRC(tid3,outrow_1,outmast,c3val);
        } 
        
      }

      for (i=0; i<=ibuf[4]; i++) osmmfree(string[i]);
      ibuf[3] = -1;       
      ibuf[4] = -1;      
      ibuf[5] = -1;     
      ibuf[6] = -1;  
for (i=0; i<BUFM;i++) {
   if (tmnoc[i] != -1 ) {
          SCFCLO(tmnoc[i]);
          cdata[i] = (char *)0;
          } 
   }
      line -= nline;
      osmmfree(temp);
      osmmfree(indx); 
      osmmfree(indx1); 
      osmmfree(filetemp);
      osmmfree(c3val);
      osmmfree(tempmast);
     }
/*

*/

int diff8 (s1, s2, order)
/*++++++++++++++++
.PURPOSE Compute difference between two doubles
.RETURNS s1-s2 (negative value if s1<s2, positive if s1>s2, null if s1=s2)
.REMARKS NULL values always at end
-----------------*/
        double *s1, *s2;
        int order;
{
        int     i ;

   for (i = 1; --i >= 0; s1++, s2++){
        if (*s1 == *s2)         continue;
        if (*s1 > *s2)  return (order);
        if (*s1 < *s2)  return (-order);
   }
   return(0);
}

int diff_m8(s1,s2)
    char *s1,*s2;
{
    int result;
    
    result = diff8(s1,s2,-1);
    if (result == 0) 
          result = diff8(s1+sort_off,s2+sort_off,1);
    return(result);
}



void Qsort8(data, first, last)
/*++++++++++++++++
.PURPOSE        Quick Sort
.METHOD
                Quick sort modified with insert sort for small partitions.
                The routine sorts the array of pointers to actual data.

                The comparison routine is external.
.RETURNS        ---
-----------------*/
        char **data;            /* MOD: The arrays of pointers to sort  */
        int first, last;       /* IN: Indexes of first / last pointer  */
{
        char *px, *t;
        int i,j;

  i = first; j = last;
  px = data[(first+last)/2];

  do {
        while ( (i < last ) && (diff_m8(data[i], px) < 0))      i++;
        while ( (j > first) && (diff_m8(data[j], px) > 0))      j--;
        if (i <= j) {
                if (i < j)
                        t = data[i], data[i] = data[j], data[j] = t;
                i++; j--;
        }
  } while (i <= j);

  if (first < j)        Qsort8 (data, first, j);
  if (i < last)         Qsort8 (data, i, last );
}

/*

*/

void decrypt(column,rule,selecrit,tempcrit,range)
char *rule,*selecrit,*column,*tempcrit;
double *range;
{
int i,k,str;
str = 0;
if (!*rule) return;
oscfill(tempcrit,256,'\0');
strcat(selecrit,column);
if (!*rule) return;
else  {
      if (find_string(rule,"~*[=")) { 

/* strcat(selecrit,".EQ."); */

	   if (*rule == '=') rule++;
           }
      else if ((*rule == '!') && (*(rule+1) == '=')) {
              rule += 2;
              str = 1;
              strcat(selecrit,".NE.");
              }
      else if (*rule == '<') {
              str = 1;
	      rule++;
	      if (*rule == '=') {
		  strcat(selecrit,".LE.");
		  rule++;
		  }
	      else strcat(selecrit,".LT.");
	      }
      else if (*rule == '>') {
              str = 1;
	      rule++;
	      if (*rule == '=') {
                  strcat(selecrit,".GE.");
                  rule++;
                  }
	      else strcat(selecrit,".GT.");
	      }
/* else strcat(selecrit,".EQ."); */
      i = 0;
      while(!fidelim(rule)) {
          *(tempcrit+i) = *rule++;
	  i++;
          }
      k = strindex(tempcrit,"..");
      if (tempcrit[k]) {
	   strcat(selecrit,".GE.");
	   strncat(selecrit,tempcrit,k);
           *range = atof(tempcrit);
           strcat(selecrit,".AND.");
           strcat(selecrit,column);
	   tempcrit = tempcrit+k+2;
           *(range+1) = atof(tempcrit);
	   strcat(selecrit,".LE.");
	   strcat(selecrit,tempcrit);
	   }
      else {
           if (!str) strcat(selecrit,".EQ.");
           if (stsnum(tempcrit) <= 0) {
               if (stumatch(tempcrit,"REFVAL(") != 7) strcat(selecrit,"\"");
               strcat(selecrit,tempcrit);
               if (stumatch(tempcrit,"REFVAL(") != 7) strcat(selecrit,"\"");
               }
           else {
               *range = atof(tempcrit);
               strcat(selecrit,tempcrit);
               }
           }
      if (*rule == '|')  strcat(selecrit,".OR.");
      else if (*rule == '&') strcat(selecrit,".AND.");
      rule++;
      decrypt(column,rule,selecrit,tempcrit,range);
      }
}

int fidelim(s)
char *s;
{
if (find_string(s,"|&") || *s==9 || *s=='\r' || *s==0) return 1;

   return 0;
}
/*

*/

void Qsorta(data, first, last)
/*++++++++++++++++
.PURPOSE        Quick Sort
.METHOD
                Quick sort modified with insert sort for small partitions.
                The routine sorts the array of pointers to actual data.

                The comparison routine is external.
.RETURNS        ---
-----------------*/
        char **data;            /* MOD: The arrays of pointers to sort  */
        int first, last;       /* IN: Indexes of first / last pointer  */
{
        char *px, *t;
        int i,j;

  i = first; j = last;
  px = data[(first+last)/2];

  do {
        while ( (i < last ) && (diff_a(data[i], px) < 0))      i++;
        while ( (j > first) && (diff_a(data[j], px) > 0))      j--;
        if (i <= j) {
                if (i < j)
                        t = data[i], data[i] = data[j], data[j] = t;
                i++; j--;
        }
  } while (i <= j);

  if (first < j)        Qsorta (data, first, j);
  if (i < last)         Qsorta (data, i, last );
}

int diffa(s1,s2)
/*++++++++++++++++
.PURPOSE Compute difference between two char strings
.RETURNS s1-s2 (negative value if s1<s2, positive if s1>s2, null if s1=s2)
.REMARKS NULL values always at end
-----------------*/
        char *s1, *s2;
{
        int     i;
        int sort_len,sort_order;

int diff_a();

   sort_len = sort_off;
   sort_order = 1;
   for (i = sort_len; --i >= 0; s1++, s2++){
        if (*s1 == *s2)         continue;
        if (*s1 == NULL1)       return(1);      /* NULL value at end */
        if (*s2 == NULL1)       return(-1);     /* NULL value at end */
        if (*s1 > *s2)  return (sort_order);
        if (*s1 < *s2)  return (-sort_order);
   }
   return(0);
}

int diff_a(s1,s2)
    unsigned char *s1,*s2;
{
int sort_len,sort_order,i;

sort_len = sort_off;
sort_order = 1;
for (i = sort_len; --i >= 0; s1++, s2++){
   if (*s1 > *s2)  return (sort_order);
   if (*s1 < *s2)  return (-sort_order);
   }
return sort_order;
}
