/*===========================================================================
  Copyright (C) 1995-2010 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        	tcc.c
.LANGUAGE    	C
.AUTHOR      	J.D.Ponz  IPG-ESO - Garching
.CATEGORY    	table interface (Design 2.0)
.VERSION  1.0 	25-Mar-1987   Implementation     JDP
.VERSION  1.1 	20-Oct-1987   Modify calling sequence 
.VERSION  1.2 	23-Nov-1987   New SC calling sequence
.VERSION  1.3 	11-Apr-1988   Include new routines without type conversion.
				Character string always ending with a '\0'.
				New SC calling sequence.
.VERSION  1.4 	26-Sep-1988   Modify calling sequence in TCCINI to 
                               include data types and array length.
.VERSION  3.0 	01-Jul-1990   	New Version with Arrays / Elementary IO
				Added TCCSEL routine (selection of Columns)
.VERSION  3.1 	26-Sep-1990   	Removed bug in TCCSRT 
.VERSION  3.2 	06-Dec-1990	Force New Version as soon as something modified.
.VERSION  3.3 	18-Dec-1990 FO: Take into account the number of selected rows.
.VERSION  3.4 	23-Feb-1991   	Sort always writes NULL values at the end.
.VERSION  3.5 	29-Apr-1991: 	TCCINI must work whatever the table size is ...
.VERSION  3.6 	10-Jun-1991:	Added TCCPAM (Partial Column Mapping)
.VERSION  3.7   22-Sep-1992:    treats in a proper way NULL values (i.e convex)
 
 100818		last modif 
 
 
.COMMENTS    

This module contains the routines to handle columns in a table. 
Access to the table is done through the table identifier,
returned by the routines TCTOPN and TCTINI.
Access to a column is done through the column number, given
by the routines TCCINI, TCCSER and TCLSER.

The following main functions are defined in this module:
 create a column in the table (TCCINI), 
 delete a column from the table (TCCDEL),
 map a column (TCCMAP),
 unmap a column (TCCUNM),
 table sorting according to column values (TCCSRT),
 search the column number (TCCSER).

Main arguments used by the routines are:
[alen] array length. 
            Integer number to define the number of items in a multiple 
            column.
            In the case of character strings 'alen' defines the maximum 
            length of the string.
[colref] reference to the column by label or by number.
            It is a character string containing the ':label'
            if the column is referenced by label, or '\#n' if the
            column is referenced by its sequential number.                   
[column] sequential column number. It is an integer number 
            provided by the system when a new column is created or
            defined by the user.
[dtype] column data type. 
            The system file 'midas\_def.h' in 'MID\_INCLUDE:' provides the
            symbols D\_xx\_FORMAT where 'xx' is 'I4' for integer data, 
            'R4' for float, 'R8' for double precision and 
            'C' for character. 
[form] column format. 
            Display format associated to the column. 
            The format is defined like in standard FORTRAN.
[label] column label. Character string (up to 16 characters)
            provided by the use when the column is created. 
            Valid characters are letters, digits and the underscore 
            symbol '\_'. 
            In the current version labels are NOT case independent.
[tid] table identifier. It is an integer number provided
            by the system when the table is created or opened.
[unit] column unit. Character string (up to 16 characters)
            to define the units of the column.

------------------------------------------------------------*/

#include <midas_def.h>		/* ANSI-C Prototyping 		*/
#include <computer.h>
#include <tblsys.h>		/* Table System parameters	*/
#include <tbldef.h>		/* Symbols used for Tables	*/
#include <tblerr.h>		/* List of Table Errors		*/

#include <atype.h>		/* Character classification	*/
#include <macrogen.h>		/* Classical macros		*/

#include <stdlib.h>		/* Classical macros		*/



static int dunit;		/* Not yet used	*/


#define SELW	sizeof(int)	/* Length of Selection Column	*/

char	*osmmget(), *osmmexp();
	
/*

*/

/*===================================================================
 *		Private Routines
 *===================================================================*/
#ifdef __STDC__
static int strloc(char *str, char c)
#else
static int strloc(str, c)
/*+++++++
.PURPOSE Locate the first occurence of character `c'
.RETURNS Index of `c' in str; length of str if `c' not found.
---------*/
	char *str;	/* IN: string to scan	*/
	char c;		/* IN: char to locate	*/
#endif
{
	register char *p;

  for (p=str; *p; p++)
  	if (*p == c)	break;
  return(p-str);
}

/*

*/

#ifdef __STDC__
static int find_space (TABLE *tp, int align, int bytes)
#else
static int find_space (tp, align, bytes)

/*+++++++
.PURPOSE Find an offset where a new column can be added
.RETURNS Where it can be added.
.METHOD  A template record is filled with x for used parts, zeroes
	for empty parts. It is then scanned for a suitable position.
---------*/

TABLE	*tp;	/* IN: Table Concerned	*/
int	align;	/* IN: Alignment requirement	*/
int	bytes;	/* IN: Bytes required	*/
#endif

{
static char *record;
char *ptr;

int	lim, totbytes;
register int  ir, jr;
static int alloc_size=0;



totbytes = tp->reclen;
if (alloc_size < totbytes)
   {
   if (alloc_size > 0) free(record);
   alloc_size = totbytes;
   ptr = record = (char *) malloc((size_t) alloc_size);
   for (ir=0; ir<4; ir++) *ptr++ = 'x';		/* The Selection Column	*/
   }

memset ((void *) (record+4),0,(size_t) (totbytes-4));

for (ir=0; ir<tp->cols; ir++)	
   memset((void *)(record + tp->offset[ir]),'x',(size_t)tp->bytes[ir]);
  
if (align == 1)
   {
   for (ir=4; ir<totbytes; ir++) 		/* 1st 4 bytes always set... */
      {
      if (record[ir]) continue;

      lim = ir + bytes;
      if (lim > totbytes) lim = totbytes;
   
      for (jr=ir; (jr<lim) && (record[jr]==0); jr++) ;
      if (jr == lim) break;
      ir = jr;
      }
   }

else
   {
   for (ir=4; ir<totbytes; ir++) 
      {
      if (record[ir]) continue;
      if (ir%align) continue;

      lim = ir + bytes;
      if (lim > totbytes) lim = totbytes;
   
      for (jr=ir; (jr<lim) && (record[jr]==0); jr++) ;
      if (jr == lim) break;
      ir = jr;
      }
   }

  
return(ir);
}
/*

*/

/*===================================================================
 *		Public Routines
 *===================================================================*/
 
int TCCDEL(tid, column, ncol)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Delete column in a  table
.RETURNS status
.REMARKS This implementation FORCES the table version to TBL_VERSION.
.METHOD	 Just modify Descriptors.
-------------------------------------------------------------*/
int	tid;			/* IN: table id number 		*/
int	column;			/* IN: column number		*/
int	*ncol;			/* OUT:actual number of columns */

{
TABLE	*tp;

int	status, i, i1, lenl, lenf, got;

char  	buf[TBL_DESLAB+1];


                       				/* checks arguments	*/


tp = TBL_ptr(tid);
if ((status = CheckTable(tp)))
   return(TBL_errs(tid, status,0));

if ((status = CheckTrueColumn(tp, column)))
   return(TBL_errs(tid, status, column));


/* Force New Version	*/

if (tp->version == 0)	tp->version = TBL_VERSION;
  

/* Modifs in Memory	*/

lenl = 1+TBL_LABLEN; 
lenf = 1+TBL_FORLEN; 

i1 = column - 1;
for (i=column; i<tp->cols; i++) 
   {
   tp->dtypes[i1] = tp->dtypes[i];
   tp->bytes[i1]  = tp->bytes[i];
   tp->offset[i1] = tp->offset[i];
   tp->abytes[i1] = tp->abytes[i];
   oscopy (tp->label+(i1*lenl),tp->label+(i*lenl),lenl);
   oscopy (tp->format+(i1*lenf),tp->format+(i*lenf),lenf);
   i1 ++;
   }
  
/* Modify sorting & ref column	*/

if (tp->scol == column)	
   tp->scol = 0;
else if (tp->scol > column)
   tp->scol -= 1;

if (tp->kcol == column)	
   tp->kcol = 0;
else if (tp->kcol > column)
   tp->kcol -= 1;

*ncol = tp->cols - 1;

if (tp->tflags & TBL__READONLY)
   {
   tp->cols --;
   return(status);
   }
  
/* Update Descriptors	*/

dunit = -1;
for (i=column; i<tp->cols; i++) 
   {
   if ((status = SCDGETC(tp->imno,TBL_Dlab(i+1),1,TBL_DESLAB,&got,buf)))
      goto error;
   if ((status = SCDWRC(tp->imno,TBL_Dlab(i),1,buf,1,got,&dunit)))
      goto error;
  }

/* finally delete last descriptor, and update table information */

if ((status = SCDDEL(tp->imno,TBL_Dlab(tp->cols)))) goto error;

tp->dtypes[--tp->cols] = 0;
tp->offset[tp->cols] = 0;

if ((status = SCDWRI(tp->imno,TBL_Ddtypes,tp->dtypes,1,tp->colitems,&dunit)))
   goto error;
if ((status = SCDWRI(tp->imno,TBL_Doffset,tp->offset,1,tp->colitems,&dunit)))
   goto error;
if ((status = SCDWRI(tp->imno,TBL_Dmain, &tp->acols,1,TBL_Dmain_SIZE,&dunit)))
   goto error;

return(status);


error:					/* error in SC routines	*/
TBL_errs(tid, status, 0);
return (status);
}
/*

*/

int TCCINI(tid, dtype, alen, form, unit, label, column)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Initializes table column. Elements in the column are
        initialized as undefined (null values).
.METHOD  Check if column has been already  defined,
        otherwise add the new column descriptors.
        
        The routine provides an overflow mechanism when the
        new column exceeds the allocated space. 
.RETURNS Status 
------------------------------------------------------------------*/
int	tid;		/* IN: table id number 		*/
int	dtype;		/* IN: column data type (Midas)	*/
int	alen;		/* IN: no. of array items       */
char	*form;		/* IN: column form		*/
char	*unit;		/* IN: column unit              */
char	*label;		/* IN: column label		*/
int	*column;	/* OUT:column number		*/

{
TABLE	*tp;	

int	status, align, where;
int	ttype, nbytes, new_cols, i, j ,maxrows, mapped_rows, incr;

char   	buf[TBL_DESLAB+1];
char	*x, *p;
char    *chptr;
char    check[TBL_LABLEN+4];		/* LABLEN .ge. UNILEN, FORLEN */



tp = TBL_ptr(tid);
if ((status = CheckTable(tp))	)
   return(TBL_errs(tid, status, 0));

if (tp->version == 0)
   tp->version = TBL_VERSION;		/* Force New Version */


/* check if column overflow */ 

if (tp->cols >= tp->colitems) 
   {
   i = tp->colitems;
   tp->colitems = tp->cols + 4;

   tp->dtypes = (int *)osmmexp((char *)tp->dtypes,2*sizeof(int)*tp->colitems);
   tp->offset = tp->dtypes + tp->colitems;
   oscopy ((char *)tp->offset, (char *)(tp->dtypes + i), i*sizeof(int));

   tp->bytes = (int *)osmmexp((char *)tp->bytes,2*sizeof(int)*tp->colitems);
   tp->abytes = tp->bytes + tp->colitems;
   oscopy ((char *)tp->abytes,(char *)(tp->bytes + i),i*sizeof(int));
   for (j=i; j<tp->colitems; j++)
      {
      tp->dtypes[j] = tp->offset[j] = tp->bytes[j] = tp->abytes[j] = 0;
      }
   tp->label = osmmexp(tp->label,(2+TBL_LABLEN+TBL_FORLEN)*tp->colitems);
   tp->format = tp->label + tp->colitems*(1+TBL_LABLEN);
   oscopy (tp->format, tp->label + i*(1+TBL_LABLEN), i*(1+TBL_FORLEN));
   }

					/* check type of column */
if ((status = TBL_TYPCHK(dtype,alen,&ttype)))
   return(status);
                                        /* check column label */
j = (int) strlen(label);
i = TBL_LABLEN;
if (*label != ':') i --;		/* without ':' => 1 char. less */

if (j > i)
   {					/* truncate if necessary */
   (void) strncpy(check,label,i);
   check[i] = '\0';
   chptr = check;
   (void) SCTPUT("(I/W) - column label truncated..");
   }
else
   chptr = label;

p = buf;
if ((status = TBL_LABCHK(chptr,p)))
   return(status);

                                        /* Check Unit		*/
j = (int) strlen(unit);
if (j > TBL_UNILEN)
   {					/* truncate if necessary */
   (void) strncpy(check,unit,TBL_UNILEN);
   check[TBL_UNILEN] = '\0';
   chptr = check;
   (void) SCTPUT("(I/W) - column unit truncated..");
   }
else
   chptr = label;

p += TBL_LABLEN;
if ((status = TBL_UNICHK(unit,p)))
   return(status);

					/* check format 	*/
p += TBL_UNILEN;
if ((status = TBL_FMTCHK(form, ttype, p)))
   return(status);


/* Adjust parameters	*/

nbytes  = TBL_ElementSize(ttype);
  					/**** NEW: Align Offset	*/
align = (tp->swise == F_RECORD ? nbytes : 1);
nbytes *= TBL_Items(ttype);
where = find_space(tp,align,nbytes);

*column = tp->cols + 1;
					/* allocate column(s) if overflow */
new_cols = (where + nbytes + 3)/4 - 1;
if (new_cols > tp->acols) 
   {
   if ((status = TBL_ALLOCOL(tid, new_cols))) return(status);
   tp = TBL_ptr(tid);
  }

/* force NULL terminated string,  write descriptor 	*/

buf[TBL_DESLAB] = '\0';
if ((status = SCDWRC(tp->imno,TBL_Dlab(*column),1,buf,1,TBL_DESLAB,&dunit)))
   return (TBL_errf(status, "table %s", tp->phname));
  
/* Update Header Info	*/

i = tp->cols;
tp->dtypes[i] = ttype;
tp->bytes[i]  = nbytes;
tp->offset[i] = where;
tp->abytes[i] = -1;				/* uninitialized...*/
tp->label [i*(1+TBL_LABLEN)] = '\0';
tp->format[i*(1+TBL_FORLEN)] = '\0';

tp->cols = *column;


/* Initialize to NULL values	*/

incr = TBL_offset (tp, 2, *column) - TBL_offset (tp, 1, *column);
maxrows = TBL_EIO_LIMIT / incr;
maxrows = MAX (maxrows, 1);

for (i=0; i<tp->arows; i+=maxrows)
   {
   mapped_rows = MIN ((tp->arows - i), maxrows);
   if_not (x = TBL_RDF (tp, TBL_offset(tp, i+1, *column),incr*mapped_rows, 1))
      return(TBL_RDst()); 
   TBL_toNULL (ttype, x);			/* NULL first element	*/
   for (p = x + incr; --mapped_rows > 0; p += incr) 
      oscopy(p ,x, nbytes); 
   }

return (status);
}
/*

*/

int TCCMAP(tid, col, address)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
  	Return the address of the column in the table.
.METHOD 
        Finds the position of the physical column and get address
        via the virtual memory mechanism.

        Note: This routine only works on tables stored by columns
        (transposed format, parameter F_TRANS in TCTINI).
.RETURNS
	status
------------------------------------------------------------------*/
	int	tid;		/* IN: table id number 		*/
	int	col;		/* IN: column number		*/
	char	**address;	/* OUT:column address           */
{
	TABLE	*tp;
	int	status;
	int	ic, len;

  tp = TBL_ptr(tid);
  if ((status = CheckTable(tp)))  return(TBL_errs(tid, status, 0));
  if ((status = CheckColumn(tp, col)))	return(TBL_errs(tid, status, col));

  if (tp->swise != F_TRANS)	
  	return (TBL_errf(ERR_TBLMAP, "Can't MAP column on RECORD table: %s",
  		tp->phname));
  else {
  	ic  = TBL_offset(tp, 1, col);
  	len = TBL_offset(tp, tp->arows, col) -ic + ColumnWidth(tp, col);
	if_not(*address = TBL_RDF (tp, ic, len, TBL__MAPPED))
		return (TBL_RDst());
	if (col == 0)	tp->selected = -1;	/* Unknown */
  }

  return (status);
}

int TCCPAM(tid, col, starting_row, asked_rows, mapped_rows, address)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Map a part of a table, namely up to asked_row from row # starting_row 
.REMARKS A value of zero for asked_rows provide some default.
.RETURNS status
------------------------------------------------------------------*/
	int	tid;		/* IN: table id number 		*/
	int	col;		/* IN: column number		*/
	int	starting_row;	/* IN: First row to map (from 1)*/
	int 	asked_rows;	/* IN: Number of rows to map,
                                   0 to map as many rows as possible 	*/
	int 	*mapped_rows;	/* OUT: Number of rows mapped	*/
	char	**address;	/* OUT: address of mapped first row */
{
	TABLE	*tp;
	int	status;
	int	i,ic, len;
         static unsigned char bit_mask[8] = { 0x80, 0x40, 0x20, 0x10,
                0x08, 0x04, 0x02, 0x01};


  tp = TBL_ptr(tid);
  *mapped_rows = 0;		/* Default return */
  if ((status = CheckTable(tp)))		return(TBL_errs(tid, status, 0));

  if (tp->swise != F_TRANS)	
  	return (TBL_errf(ERR_TBLMAP, "Can't MAP column on RECORD table: %s",
  		tp->phname));

  if ((status = CheckColumn(tp, col)))	return(TBL_errs(tid, status, col));
  if ((status = CheckRow(tp, starting_row)))
	return(TBL_errs(tid, status, starting_row));

  if (asked_rows <= 0) {	/* Compute Default number of Rows */
	len = ColumnWidth(tp, col);
	asked_rows = TBL_EIO_LIMIT/len/2;
	if (asked_rows < 1)	asked_rows = 1;
  }

				/* Check the number of rows	*/
  if ((starting_row + asked_rows) > tp->arows)
	asked_rows = 1 + tp->arows - starting_row;

  if (col == 0 && tp->usname != '\0') {
      *address = osmmget(asked_rows * sizeof(int));
      for (i=starting_row; i < starting_row+asked_rows; i++) {
           ic = (tp->vsel[i>>3] & bit_mask[i&7]);
           *(int *) (*address + (i-starting_row) *sizeof(int)) = (ic != 0);
           } 
      }
  else {				/* Do now the mapping		*/
      len = ColumnWidth(tp, col) * asked_rows;
      ic  = TBL_offset(tp, starting_row, col);
      if_not(*address = TBL_RDF (tp, ic, len, TBL__MAPPED))
      return (TBL_RDst());
      }			
  
				/* Write output parameters	*/
  *mapped_rows = asked_rows;
  if (col == 0)	tp->selected = -1;	/* Unknown */

  return (status);
}

int TCCSEL(tid, text, max_cols, cols, flags, found_cols)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Searchs a set of columns column by references (their name).
.METHOD  A set of columns has the general syntax 
		col[(flag)][,col]...
		
	where "col" may be #number or :label.
	The Sequence column may be designated by # or :
.RETURNS  Status
-------------------------------------------------------------*/
int	tid;		/* IN : table id	*/
char	*text;		/* IN : textual designation */
int	max_cols;	/* IN: Size of cols & flags arrays */
int	*cols;		/* OUT: column numbers	*/
int	*flags;		/* OUT: column flags (in brackets) */
int	*found_cols;	/* OUT: How many columns were found */

{     
int    mm,status;
int    k, j, ic, ib, id;
int    temp,dummy;

char   *p, b, c, d;




status = ERR_NORMAL;
if (*text == '\0') 
   {
   status = TCIGET(tid,found_cols,&dummy,&dummy,&dummy,&dummy);
   for (j=1; j <= *found_cols; j++) cols[j-1] = j;
   return(status);
   }

				/* Tokenize from commas, then from brackets */
for (j=0, p=text; (*p) && (j < max_cols) && (status == ERR_NORMAL); j++) 
   {
   ic = strloc (p, ','); c = p[ic]; p[ic] = '\0';
   id = strloc(p,'.'); 
   if ( p[id] && p[id+1] == '.')  
      { 
      d = p[id]; 
      p[id] = '\0';
      }
   else d = '\0';

   ib = strloc (p, '('); b = p[ib]; p[ib] = '\0';
   flags[j] = 1;				/* Default Flag	*/
   if ((status = TCCSER (tid, p, &cols[j]))) continue;   /* Get Label*/

   if (cols[j] < 0) 
      {
      status = ERR_TBLCOL;
      TBL_errf(status, "unknown column %s", p);
      }

   if (b) 
      {
      p += ib; ic -= ib;
      *p = b;	 p++; ic--;
      flags[j] = atoi(p);
      if (flags[j] == 0) flags[j] = (*p == '-' ? -1 : 1);
      }

   if (d) 
      {					/* column interval: #m..n */ 
      p += id+1; 
      *p = '#';
      ic -= ib+1;
      if ((status = TCCSER (tid, p, &temp))) continue;

      if (temp < 0) {
         status = ERR_TBLCOL;
         TBL_errf(status,"unknown column %s",p);
         }

      temp = temp-cols[j];		/* no. of cols in interval */
      mm = max_cols - j - 1;		/* no. of cols still available */
/*
printf("temp = %d, cols[%d] = %d, mm = %d\n",temp,j,cols[j],mm);
*/
      if (temp > mm) temp = mm;
      for (k=1; k<= temp; k++) *(cols+j+k) = *(cols+j)+k;     

      j = j + temp; 			/* last filled column */
      *p = d;
      }    

   p += ic;  *p = c;
   if (c) p++;
   }

					/* Check if not too many cols */
if ( (j >= max_cols) && (isgraph(*p)) && (status == ERR_NORMAL) )
   TBL_errf (-1, "list truncated to %d items: %s", max_cols, text);

  
*found_cols = j;
return (status);
}



int TCCSER(tid, colref, column)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Searchs column by reference (its name).
	The column may be designated as #number or :name or name 
	The Sequence column may be designated by # or :
.METHOD Sequential search for the column label.
	The routine returns the column number as the last
	argument or -1 if the column is not found.
.RETURNS Status
-------------------------------------------------------------*/
int	tid;		/* IN : table id	*/
char	*colref;	/* IN : column reference */
int	*column;	/* OUT: column number	*/

{     
TABLE	*tp;	
int	status, i;
 
char	*p, *q;
char   *chptr, check[TBL_LABLEN+4];




tp = TBL_ptr(tid);
if ((status = CheckTable(tp)))
   return(TBL_errs(tid, status, 0));

					/* init variables */
p = colref;
*column = -1;

switch (*p)
   {                  
   case '#' : 				/* column by number */
	p++;
	for (i = 0; isdigit(p[i]); i++) ;
	if (isalpha(p[i]))
           {
	   i = -1;
	   status = ERR_TBLCOL;
	   TBL_errf(status, "invalid number #'%s', table: %s", p, 
			tp->phname);
	   }
	else	
           {
	   i = atoi(p);
	   if ((i > tp->cols) || (i < 0)) 
	      {
              status = ERR_TBLCOL;
              TBL_errs(tid, status, i);
              }
	   else	
              *column = i;
	   }
	break;

   case 's' : case 'S' :               /* column sequence */
        q = p + 1;		       /* check at least for SEQ */
        if ((*q != 'e') && (*q != 'E')) break; 
        q++;
        if ((*q == 'q') || (*q == 'Q')) *column = 0;
        break;				

   case ':' : 				/* column by label */
	p++;

   default :				/* assume it's name */
	if (isspace(*p) || (*p == '\0'))
           *column = 0;			/* use sequence no. */
	else
           {
           i = TBL_LABLEN - 1;			/* :name is checked... */
	   if ((int) strlen(p) > i)
   	      {					/* truncate if necessary */
   	      (void) strncpy(check,p,i);
   	      check[i] = '\0';
   	      chptr = check;
   	      }
	   else
   	      {
              chptr = p;
              }

           status = TCLSER (tid, chptr, column);	/* without : */
           }
	break;



  }

  return (status);
}
/*

*/

/*====================================================================
 *		Internal Routines for Sort
 *====================================================================*/

/* When several sort parameters are given, the address of the other
   parameters to test are computed from the address of the first parameter
   Let @  be the address of line i of the first parameter,
       @0    the address of line 1 of the first parameter
   the address @n of the 2nd parameter of line i can be computed via
   @n = (@-@0)*a/l0 + Cte where   a = difference of widths
   				  l0= width of first parameter column

   The parameters are stored as 	Cte: in sort_offset
					a  : in sort_factor
					l0 : in sort_width
  */
 

typedef int	(*FUNCTION)();	/* Just to simplify the declarations....*/

static char	*sort_astart;	/* Origin of 1st sort parameter		*/
static int	sort_width;	/* Width of first column to be sorted	*/
static int 	sort_order;	/* 1 for ascending, -1 for descending	*/
static int 	sort_len;	/* Number of items in Table		*/
static FUNCTION	diff;
				/* Several parameters used for sorting 	*/
static FUNCTION	sort_list[SORT_MAX];
static int 	sort_array[SORT_MAX],  sort_items[SORT_MAX];
static int	sort_offset[SORT_MAX], sort_factor[SORT_MAX];
static int	sort_keys = 0;

/*

*/
static int diff_m(s1, s2)
/*++++++++++++++++
.PURPOSE Compute differences when several keys are used.
.RETURNS Negative value if s1<s2, positive value if s1>s2, null if s1=s2)
-----------------*/
	char *s1, *s2;
{
	int	result, i;
	FUNCTION what_to_do;

  sort_order = sort_array[0];
  sort_len   = sort_items[0];
  what_to_do = sort_list[0];

  result = (*what_to_do)(s1, s2);

  for (i = 1; (i < sort_keys) && (result == 0); i++) {
	sort_order = sort_array[i];
	sort_len   = sort_items[i];
	what_to_do = sort_list[i];
	result = (*what_to_do)(
		s1+sort_offset[i] + sort_factor[i]*(s1-sort_astart)/sort_width,
		s2+sort_offset[i] + sort_factor[i]*(s2-sort_astart)/sort_width);
   }
   return(result);
}

static int diffi1 (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;
	
   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);
}
static int diffi2 (s1, s2)
/*++++++++++++++++
.PURPOSE Compute difference between two short arrays
.RETURNS s1-s2 (negative value if s1<s2, positive if s1>s2, null if s1=s2)
.REMARKS NULL values always at end
-----------------*/
	short *s1, *s2;
{
	int	i;
	
   for (i = sort_len; --i >= 0; s1++, s2++){
	if (*s1 == *s2)		continue;
	if (*s1 == NULL2)	return(1);	/* NULL value at end */
	if (*s2 == NULL2)	return(-1);	/* NULL value at end */
	if (*s1 > *s2) 	return (sort_order);
	if (*s1 < *s2) 	return (-sort_order);
   }
   return(0);
}
static int diffi4 (s1, s2)
/*++++++++++++++++
.PURPOSE Compute difference between two int arrays
.RETURNS s1-s2 (negative value if s1<s2, positive if s1>s2, null if s1=s2)
.REMARKS NULL values always at end
-----------------*/
	int *s1, *s2;
{
	int	i;
	
   for (i = sort_len; --i >= 0; s1++, s2++){
	if (*s1 == *s2)		continue;
	if (*s1 == NULL4)	return(1);	/* NULL value at end */
	if (*s2 == NULL4)	return(-1);	/* NULL value at end */
	if (*s1 > *s2) 	return (sort_order);
	if (*s1 < *s2) 	return (-sort_order);
   }
   return(0);
}

static int diffa1 (s1, s2)
/*++++++++++++++++
.PURPOSE Compute difference between two unsigned chars
.RETURNS s1-s2 (negative value if s1<s2, positive if s1>s2, null if s1=s2)
.REMARKS No NULL values...
-----------------*/
	unsigned char *s1, *s2;
{
	int	i;
	
   for (i = sort_len; --i >= 0; s1++, s2++){
	if (*s1 > *s2) 	return (sort_order);
	if (*s1 < *s2) 	return (-sort_order);
   }
   return(0);
}
static int diffa2 (s1, s2)
/*++++++++++++++++
.PURPOSE Compute difference between two unsigned shorts
.RETURNS s1-s2 (negative value if s1<s2, positive if s1>s2, null if s1=s2)
-----------------*/
	unsigned short *s1, *s2;
{
	int	i;
	
   for (i = sort_len; --i >= 0; s1++, s2++){
	if (*s1 > *s2) 	return (sort_order);
	if (*s1 < *s2) 	return (-sort_order);
   }
   return(0);
}
static int diffa4 (s1, s2)
/*++++++++++++++++
.PURPOSE Compute difference between two unsigned ints
.RETURNS s1-s2 (negative value if s1<s2, positive if s1>s2, null if s1=s2)
-----------------*/
	unsigned int *s1, *s2;
{
	int	i;
	
   for (i = sort_len; --i >= 0; s1++, s2++){
	if (*s1 > *s2) 	return (sort_order);
	if (*s1 < *s2) 	return (-sort_order);
   }
   return(0);
}

static int diffr4 (s1, s2)
/*++++++++++++++++
.PURPOSE Compute difference between two floats
.RETURNS s1-s2 (negative value if s1<s2, positive if s1>s2, null if s1=s2)
.REMARKS NULL values always at end
-----------------*/
	float *s1, *s2;
{
	int	i;
	
   for (i = sort_len; --i >= 0; s1++, s2++){
	if (*s1 == *s2)		continue;
	if (isNULLF(s1))	return(1);	/* NULL value at end */
	if (isNULLF(s2))	return(-1);	/* NULL value at end */
	if (*s1 > *s2) 	return (sort_order);
	if (*s1 < *s2) 	return (-sort_order);
   }
   return(0);
}

static int diffr8 (s1, s2)
/*++++++++++++++++
.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	i;
	
   for (i = sort_len; --i >= 0; s1++, s2++){
	if (*s1 == *s2)		continue;
	if (isNULLD(s1))	return(1);	/* NULL value at end */
	if (isNULLD(s2))	return(-1);	/* NULL value at end */
	if (*s1 > *s2) 	return (sort_order);
	if (*s1 < *s2) 	return (-sort_order);
   }
   return(0);
}

/*===================================================================*/

static void Qsort(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)(data[i], px) < 0))	i++;
	while ( (j > first) && ((*diff)(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)	Qsort (data, first, j);
  if (i < last)		Qsort (data, i, last );
}

int TCCSRT(tid, nc, column, sortfl)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE  Sort table.
.METHOD   Use Quick sort algorithm to sort the table data
                in ascending (sort flag > 0) or descending 
                (sort flag < 0) sequence.
.RETURNS  Status
-------------------------------------------------------------*/
int	tid;		/* IN : table id	*/
int	nc;		/* IN : number of columns*/
int	column[];	/* IN : column numbers	*/
int	sortfl[];	/* IN : sort flags	*/

{
TABLE	*tp;
int	status;
int	i, j, len, icol, ic, dtype, incr, mapped_bytes;
long int  lolo;

char	**data;
char	*x, *copy, *root[SORT_MAX];



tp = TBL_ptr(tid);
if ((status = CheckTable(tp)))	return(TBL_errs(tid, status,0));

if (tp->rows <= 1) 	return (status);  

data = (char **)0;

/* Check the list of sort keys	*/

sort_keys = nc;
if (sort_keys < 1) 
   {
   TBL_errf(-1, "value %d of SORT parameters assumed to be 1, table: %s",
	  	sort_keys, tp->phname);
   sort_keys = 1;
   }

if (sort_keys > SORT_MAX) 
   {
   TBL_errf(-1, "Number %d of SORT parameters truncated to %d, table: %s",
	  	sort_keys, SORT_MAX, tp->phname);
   sort_keys = SORT_MAX;
   }
	

/* check column numbers */

for (i=0; i<sort_keys; i++) 
   {
   icol = column[i];
   if (icol > tp->cols || icol <= 0) 
      return (TBL_errs (ERR_TBLCOL, tid, icol));
   }

icol = column[0];
sort_width = ColumnWidth(tp, icol);


/* All existing data will be modified	*/

for (i=0,ic=0; i<tp->cols; i++) 
   {
   if (tp->offset[i] > tp->offset[ic])	ic = i;
   }
ic += 1;

if (tp->swise == F_TRANS) 
   {
   int offset, *perm;
   int 	*asInt;
   short int *asShort;

   char	*from;


   offset = 0;
   for (i=0; i<sort_keys;i++)
      {
      icol = column[i];
      sort_offset[i] = offset;
      mapped_bytes = ColumnWidth(tp,icol)*tp->rows;
      offset = offset + mapped_bytes;
      if_not (root[i] = TBL_RDF (tp, TBL_offset(tp,1,icol), mapped_bytes, 2))
              return(TBL_RDst());
      }

   lolo = (long int) offset;
   copy = osmmget(lolo);
   ic = TBL_offset (tp, 1, column[0]);
   for (i=0; i<sort_keys; i++) 
      {
      icol = column[i];
      dtype  = ColumnType (tp, icol);
      oscopy(copy+sort_offset[i],root[i],ColumnWidth(tp,icol)*tp->rows);
      sort_array[i] = sortfl[i];
      sort_items[i] = TBL_Items(dtype);
      sort_factor[i]= ColumnWidth(tp, icol) - sort_width;

      /* Choose the comparison function	*/  
      switch (TBL_ElementType(dtype)) 
         {
         case TBL_D_I1:	diff = diffi1;	break;
         case TBL_D_I2:	diff = diffi2;	break;
 	 case TBL_D_I4:	diff = diffi4;	break;
 	 case TBL_D_A1:	diff = diffa1;	break;
 	 case TBL_D_A2:	diff = diffa2;	break;
 	 case TBL_D_A4:	diff = diffa4;	break;
 	 case TBL_D_R4:	diff = diffr4;	break;
 	 case TBL_D_R8:	diff = diffr8;	break;
 	 default:	diff = diffr4;
  	 }
  	 sort_list[i] = diff;
      }
  
   /* Take Values if sort_keys == 1*/

   icol   = column[0];
   sort_order = sort_array[0];
   sort_len   = sort_items[0];
   if (sort_keys > 1)	diff = diff_m;

   /* Create the array of pointers to data */

   lolo = (long int)(sizeof(char *) * tp->rows);
   data = (char **)osmmget(lolo);
   incr = TBL_offset (tp, 2, icol) - ic;
   for (i=0, x = copy ; i < tp->rows; i++, x += incr) data[i] = x;
   sort_astart = data[0];	

   /* Sort the data array	*/
   Qsort (data,0,tp->rows-1);


   /* Follow the permutation	*/
   for (i = 0, perm = (int *)data; i < tp->rows; i++, perm++)
      *perm = (data[i]-sort_astart)/sort_width;

   perm = (int *)data;

   len = SELW; incr = 0; 
   for (j = 0; j < tp->cols; j++) 
      {
      len = tp->bytes[j];
      incr= tp->offset[j]*tp->arows;

      mapped_bytes = ColumnWidth(tp,j+1)*tp->rows;
      root[0] = TBL_RDF (tp, TBL_offset(tp,1,j+1), mapped_bytes, 1);
      lolo = (long int) mapped_bytes;
      copy = osmmget(lolo);
      oscopy (copy, root[0], mapped_bytes);
      from = copy ;	
      x = root[0] ;	
      asShort = (short *)from; 	asInt = (int *)from;
      for (i=0; i < tp->rows; i++, x += len) switch (len) 
         {
	 case sizeof(char):  *x = from[perm[i]]; continue;
	 case sizeof(short): *(short *)x = asShort[perm[i]]; continue;
	 case sizeof(int):  *(int *)x  = asInt[perm[i]]; continue;
	 default: oscopy (x, from + len*perm[i], len); continue;
	 }
      osmmfree(copy);
      }
   }	
else 
   {
   mapped_bytes = tp->reclen * tp->rows;
   if_not(root[0] = TBL_RDF (tp, 0, mapped_bytes, 1))   return (TBL_RDst());

   /* Copy a table to a new piece  */
   lolo = (long int) mapped_bytes;
   copy = osmmget (lolo);
   oscopy (copy, root[0], mapped_bytes);
   ic = TBL_offset (tp, 1, icol);

   for (i=0; i<sort_keys; i++) 
      {
      icol = column[i];
      dtype  = ColumnType (tp, icol);
      sort_offset[i] = TBL_offset (tp, 1, icol) - ic;
      sort_array[i]  = sortfl[i];
      sort_items[i]  = TBL_Items(dtype);
      sort_factor[i] = ColumnWidth(tp, icol) - sort_width;

      /* Choose the comparison function       */
      switch (TBL_ElementType(dtype)) 
         {
         case TBL_D_I1:       diff = diffi1;  break;
         case TBL_D_I2:       diff = diffi2;  break;
         case TBL_D_I4:       diff = diffi4;  break;
         case TBL_D_A1:       diff = diffa1;  break;
         case TBL_D_A2:       diff = diffa2;  break;
         case TBL_D_A4:       diff = diffa4;  break;
         case TBL_D_R4:       diff = diffr4;  break;
         case TBL_D_R8:       diff = diffr8;  break;
         default: diff = diffr4;
         }
      sort_list[i] = diff;
      }
   icol   = column[0];
   sort_order = sort_array[0];
   sort_len   = sort_items[0];
   if (sort_keys > 1)    diff = diff_m;

   /* Create the array of pointers to data */
   lolo = (long int) (sizeof(char *) * tp->rows);
   data = (char **)osmmget(lolo);
   incr = TBL_offset (tp, 2, icol) - ic;
   for (i=0, x=copy + ic; i<tp->rows; i++, x += incr)  data[i] = x;

   sort_astart = data[0];
   Qsort (data,0,tp->rows-1);
   len = tp->reclen;
   for (i=0, x=root[0]; i<tp->rows; i++, x += len) 
	oscopy (x, data[i] - ic, len); 
   osmmfree (copy);
   }

/* update table descriptors */

tp->scol = icol;

osmmfree ((char *)data);
return (status);
}

int TCCUNM(tid, address)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE	Unmap the part of a file that was mapped before.
.METHOD 	Identical to TCEUNM
.RETURNS	status (-1 if address not found)
------------------------------------------------------------------*/
	int	tid;		/* IN: table id number 		*/
	char	*address;	/* IN: column address           */
{
  return (TCEUNM(tid, address));
}

