/*===========================================================================
  Copyright (C) 1995-2013 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        tcd.c
..LANGUAGE    C
..AUTHOR      IPG-ESO Garching
..CATEGORY    table interface (Design version 2.0)

..VERSION 1.0 	25-Mar-1987   Implementation     J.D. Ponz
..VERSION  3.4 	15-Mar-1991: Corrected TCSINF  (FO)

 130702		last modif


..COMMENTS    

\begin{TeX}

This module contains the routines to handle standard table descriptors:
\begin{itemize}
\item General table information giving the current number
      of rows and columns, the number of the sorted column
      (0 by default), the column acting as refence to access
      rows by value (0 by default) and the physical 
      dimension of the table.
      The routine TCIGET gives this information.
\item Table data types. The structure of one column may be retrieved
      with TCBGET.
\item Format to define the editing format.
      The following operations are implemented:
      Get (TCFGET), put (TCFPUT).
\item Reference column. Defines the column used as reference for
      accessing the table elements by value. Possible  operations
      are define reference column (TCKPUT) and get reference column
      (TCKGET).
\item Label. Defines the name associated to the column.
      The label is used as a variable name associated to the
      column in programs and procedures. Possible operations are 
      get label (TCLGET), put label (TCLPUT) and search column
      with a given label (TCLSER). The label is initialized 
      by TCCINI.
      
      Case sensitivity for label names may be turned on via {\em
      TCOSET ("label\_case", 1)} or off 
      via {\em TCOSET ("label\_case", 0)}.
\item Various {\em options} may be retrieved (TCOGET) or setup (TCOSET).
	TCOERR writes an error.
\item Unit. Defines the units associated to a column. 
      Possible operations are get unit (TCUGET) and put unit 
      (TCUPUT). Units are initialized by TCCINI.
\item Selection flag. Defines the current subset selected
      in the table. The functions included are: get selection
      (TCSGET), put selection on/off (TCSPUT), apply selection
      from descr SELIDX (TCSDSC), count selected
      items (TCSCNT) and initialize selection (TCSINI).
\end{itemize}

Main arguments used by the routines are:
\begin{description}
\item[column] sequential column number. It is an integer 
                number provided by the system when a new column 
                is created or defined by the user.
\item[dtype] column data type. The system file 
                midas\_def.h in MID\_INCLUDE provides the symbols
                D\_xx\_FORMAT where 'xx' is 'I4' for integers,
                'R4' for float, 'R8' for double precision and
                'C'  for characters
\item[form] column display format. It is a character string 
                defining a FORTRAN compatible format.
\item[label] column label. Character string (up to 16 characters)
                defined by the user when the column is created.
                Valid characters are letters, digits and the underscore
                '\_'. In the current version labels are NOT case independent.
\item[row] sequential row number. It is an integer number
                defining the row number or the symbols FIRST, LAST,
                NEXT and PREVIOUS defined in the system file 'midas\_def.h'
                in the directory 'MID\_INCLUDE'.
                The terms row and record are equivalent in this context.
\item[tid] table identifier. It is an integer number 
                provided by the system when the table is created 
                or opened.
\item[unit] column unit. It is a character string 
                (up to 16 characters) to define the units of the column.
\end{description}

\end{TeX}
------------------------------------------------------------*/

#include <midas_def.h>		/* General MIDAS Symbols	*/
#include <tblsys.h>		/* Table System parameters	*/
#include <tbldef.h>		/* Symbols used for Tables	*/
#include <errext.h>		/* needed for OnError_xxx	*/

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

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


static int dunit;		/* Not yet used	*/
static int dnull = 0;

static unsigned char bit_mask[8] = { 0x80, 0x40, 0x20, 0x10, 
		0x08, 0x04, 0x02, 0x01};


/* Mnemonics for error handling ops */
	
#define OnError_Continue    \
{	int eflags[3];	\
	eflags[0] = ERRO_CONT; eflags[1] = ERRO_LOG; eflags[2] = ERRO_DISP; \
	ERRO_CONT = 1; ERRO_LOG = ERRO_DISP = 0; 

#define OnError_Reset 	    \
        ERRO_CONT = eflags[0]; ERRO_LOG = eflags[1]; ERRO_DISP = eflags[2]; \
}
 

/*==========================================================================
 *		Local Routines
 *==========================================================================*/
#ifdef __STDC__
int GetLabel(TABLE *tp, int col, int pos, int len, char *buf)
#else
int GetLabel(tp, col, pos, len, buf)
/*++++++++
..PURPOSE Interface to SCDRDC (Read Descriptor) for Label
..RETURNS Status
..REMARKS Trailing blanks are removed. Error is logged when found.
-----------*/
	TABLE 	*tp;	/* IN: Table pointer		*/
	int	col;	/* IN: Column number		*/
	int	pos;	/* IN: Position (from 1) to get	*/
	int	len;	/* IN: How many bytes to get	*/
	char	*buf;	/* OUT: Result			*/
#endif
{
	int	got, status;
	
/*
   OJO: size of char. string 'buf' must be at least 1+'len' !
*/
   
  OnError_Continue;

  if ((status = SCDRDC(tp->imno, TBL_Dlab(col),1, pos, len, 
  	&got, buf, &dunit, &dnull))) 
     {
     buf[0] = EOS;	/* Failed...	*/
     TBL_errf(status,"can't get info for column #%d, table: %s",col,tp->phname);
     }
  else  
     {		/* Remove trailing spaces, and terminate string	*/
     buf[got] = '\0';
     while (got > 0 && buf[--got] == ' ') buf[got] = '\0';
     }

  OnError_Reset;

  return(ERR_NORMAL);
}

#ifdef __STDC__
int PutLabel(TABLE *tp, int col, char *buf, int pos, int len)
#else
int PutLabel(tp, col, buf, pos, len)
/*++++++++
..PURPOSE Interface to SCDWRC (Write Descriptor) for Label
..RETURNS Status
..REMARKS Error is logged when found.
-----------*/
	TABLE 	*tp;	/* IN: Table pointer		*/
	int	col;	/* IN: Column number		*/
	char	*buf;	/* IN: What to write		*/
	int	pos;	/* IN: Position (from 1) to write*/
	int	len;	/* IN: How many bytes to get	*/
#endif
{
	int	status;
	


  if ((status = SCDWRC(tp->imno, TBL_Dlab(col),1, buf, pos,len, &dunit)))
  	TBL_errf(status, "can't write info for column #%d, table: %s", 
  	col, tp->phname);
  else	tp->tflags |= TBL__MODIFIED;

  return(status);
}
/*

*/

/*==========================================================================
 *		Public Routines
 *==========================================================================*/

int TCBGET(tid, column, dtype, items, bytes)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Reads Binary storage characteristics of Column. 
		The routine returns the data type (one of the D_xx_FORMAT), 
		the number of items (arrays), and the number of bytes 
		required for the binary storage.
..REMARKS Arrays only allowed for characters.
..RETURNS status
-------------------------------------------------------------*/
	int	tid;		/* IN : table ident	*/
	int	column;		/* IN : column number	*/
	int	*dtype;		/* OUT: data type	*/
	int	*items;		/* OUT: array size	*/
	int	*bytes;		/* OUT: storage required*/
{
	TABLE	*tp;
	int	*ptr;
	int	status;
        char ws[4]; 
                      				/*   check argument 	*/
  tp = TBL_ptr(tid);
  if ((status = CheckTable(tp)))		return(TBL_errs(tid, status,0));
  if ((status = CheckColumn(tp, column)))
				return(TBL_errs(tid, status, column));

  if (column == 0) {				/* Special Selection	*/
	*dtype = D_I4_FORMAT;
	*bytes = sizeof(int);
	return (status);
  }

					/* assign type according to code */
  ptr = tp->dtypes + column - 1;

  *items = TBL_Items(*ptr);
  switch (TBL_ElementType(*ptr)) {
    case TBL_D_I1:
    	*dtype = D_I1_FORMAT;  *bytes = 1;		break;
    case TBL_D_I2:
    	*dtype = D_I2_FORMAT;  *bytes = sizeof(short);	break;
    case TBL_D_I4:
    	*dtype = D_I4_FORMAT;  *bytes = sizeof(int);	break;
    case TBL_D_R8:
    	*dtype = D_R8_FORMAT;  *bytes = sizeof(double);	break;
    case TBL_D_R4:
    	*dtype = D_R4_FORMAT;  *bytes = sizeof(float);	break;
    case TBL_D_A1:
    	*dtype = D_C_FORMAT;   
        *bytes = *items;
        GetLabel(tp,column,39,3,ws);
        *items = atoi(ws);
        if (*items == 0) *items = 1;
        break;
    case TBL_D_A2:
    	*dtype = D_UI2_FORMAT; *bytes = sizeof(short);	break;
    case TBL_D_A4:
    	*dtype = D_L4_FORMAT;  *bytes = sizeof(int);	break;
  }
  if (*dtype != D_C_FORMAT)  *bytes *= *items; 
  return(status);
}

int TCDGET(tid, store)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Reads table storage format 
..RETURNS status
-------------------------------------------------------------*/
	int	tid;	/* IN : table id	*/
	int	*store;	/* OUT: table storage format, as F_RECORD or F_TRANS */
{
	TABLE	*tp;
  	int	status;

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

  *store  = tp->swise;
  return(status);
}

int TCFGET(tid, column, form, len, dtype)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Reads column format.
		The routine returns the column format, 
		the length of the column field when edited (bytes), 
		and the data type.
..RETURNS status
-------------------------------------------------------------*/
	int	tid;	/* IN : table ident	*/
	int	column;	/* IN : column number	*/
	char	*form;	/* OUT: column format	*/
	int	*len;	/* OUT: field length 	*/
	int	*dtype;	/* OUT: data type	*/
{
	TABLE	*tp;
  	char	*f;
  	int	j, col, tdtype, items;
  	int	status;
  	char	ws[10],ws1[4];

  *len = 0;
                      				/*   check argument 	*/
  tp = TBL_ptr(tid);
  if ((status = CheckTable(tp)))		return(TBL_errs(tid, status,0));
  if ((status = CheckColumn(tp, column)))
				return(TBL_errs(tid, status, column));

  if (column == 0) {			/* Special Selection	*/
	strcpy(form,"I8 ");
	*len   = 8;
	*dtype = D_I4_FORMAT;
	return (status);
  }
  col = column - 1;

  					/* Is already loaded ?	*/
  f = tp->format + (1+TBL_FORLEN)*col;
  if (*f == '\0') {			/* read descriptor	*/
	if ((status = GetLabel(tp, column, 33, 9, ws)))	return(status);
	for (j=0; j<TBL_FORLEN; j++) f[j] = ws[j];
	f[TBL_FORLEN] = '\0';
                                        /* compute length of the field 	*/
	for (j=0; isalpha(f[j]); j++) ;
	tp->abytes[col] = atoi(&f[j]);
  }

					/* Copy format string to user's */
  strcpy (form, f);

  tdtype = tp->dtypes[col];
  items  = TBL_Items(tdtype);
  if ((tdtype & (~TBL_D_MASK)) == 0) {
        GetLabel(tp,column,39,3,ws1);
        ws1[3] = '\0';
        items = atoi(ws1);
        if (items == 0) items = 1;
        }
 
					/* assign type according to code */
  switch (TBL_ElementType(tdtype)) {
    case TBL_D_A1:       *dtype = D_C_FORMAT;   break;
    case TBL_D_I1:       *dtype = D_I1_FORMAT;	break;
    case TBL_D_A2:       *dtype = D_UI2_FORMAT;	break;
    case TBL_D_I2:       *dtype = D_I2_FORMAT; 	break;
    case TBL_D_A4:       *dtype = D_L4_FORMAT;	break;
    case TBL_D_I4:       *dtype = D_I4_FORMAT;	break;
    case TBL_D_R8:       *dtype = D_R8_FORMAT;	break;
    case TBL_D_R4:       *dtype = D_R4_FORMAT;	break;
  }
					/* Column Width		*/
  *len   = items * tp->abytes[col] + items - 1;

  return(status);
}

int TCFPUT(tid, column, form)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Writes column format.
		Defines or changes the format of the column.
                The format is defined in FORTRAN compatible form.
..RETURNS status
-------------------------------------------------------------*/
	int	tid;	/* IN : table id	*/
	int	column;	/* IN : column number	*/
	char	*form;	/* IN : column format	*/
{
	TABLE	*tp;
  	char	ws[10], *f;
  	int	status, i, j;

                      				/*   check argument 	*/
  tp = TBL_ptr(tid);
  for (i=0; i< 10; i++) ws[i] = '\0';
  if ((status = CheckTable(tp)))		return(TBL_errs(tid, status,0));
  if ((status = CheckTrueColumn(tp, column)))
  					return(TBL_errs(tid, status, column));

						/* check intrinsic type */
  j = column-1;
  if ((status = TBL_FMTCHK (form, tp->dtypes[j], ws)))	return(status);

						/* Add Format		*/
  f = tp->format + (1+TBL_FORLEN)*j;
  for (i=0; i<TBL_FORLEN; i++) f[i] = ws[i];
  f[TBL_FORLEN] = '\0';
					/* compute length of the field 	*/  
  for (i=0; isalpha(f[i]); i++) ;
  tp->abytes[j] = atoi(&f[i]);

						/* write descriptor	*/
  if_not((tp->tflags & TBL__READONLY))
  	status = PutLabel(tp, column, ws, 33,6);

  return (status);
}

int TCIGET(tid, column, row, nsort, allcol, allrow)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Reads table size information.
..RETURNS status
-------------------------------------------------------------*/
	int	tid;		/* IN : table id	*/
	int	*column;	/* OUT: number of column*/
	int	*row;		/* OUT: number of rows	*/
	int	*nsort;		/* OUT: sorted column	*/
	int	*allcol;	/* OUT: number of allocated words per record */
	int	*allrow;	/* OUT: number of allocated rows	*/
{
	TABLE	*tp;
	int	status;

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

    *column  = tp->cols;
    *row     = tp->rows;
    *nsort   = tp->scol;
    *allcol  = tp->acols;
    *allrow  = tp->arows;

    return(status);
}

int TCIPUT(tid, column, row)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Writes number of table columns and rows.
                This routine has to be used in connection with mapping
                facility.
..RETURNS status
..REMARKS Should this really be allowed ????
-------------------------------------------------------------*/
	int	tid;		/* IN : table id	*/
	int	column;		/* IN: number of columns */
	int	row;		/* IN: number of rows	*/
{
	TABLE	*tp;
  	int	status;
		
                      				/*   check argument 	*/
  tp = TBL_ptr(tid);
  if ((status = CheckTable(tp)))		return(TBL_errs(tid, status,0));

  if (column <= 0)		column = tp->cols;
  if (row    < 0)		row    = tp->rows;
  if (row > tp->arows)		return(TBL_errs(tid, ERR_TBLROW, row));
  if (column > tp->cols)	return(TBL_errs(tid, ERR_TBLCOL, row));

	                         	/* assign arguments	*/
  tp->cols = column;
  tp->rows = row;

  return(status);
}

int TCKGET(tid, column)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Gets the sequential number of the reference column.
..RETURNS status
-------------------------------------------------------------*/
	int	tid;		/* IN : table id	*/
	int	*column;	/* OUT: number of column*/
{
	TABLE	*tp;
	int	status;

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

  *column  = tp->kcol;

  return(status);
}

int TCKPUT(tid, column)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Defines the column to be used as reference to access 
                rows by value.
..RETURNS status
-------------------------------------------------------------*/
	int	tid;	/* IN : table id	*/
	int	column;	/* IN : column number   */
{
	TABLE	*tp;
  	int	status;
  	
                      				/*   check argument 	*/
  tp = TBL_ptr(tid);
  if ((status = CheckTable(tp)))		return(TBL_errs(tid, status,0));
  if ((status = CheckColumn(tp, column)))	
			return(TBL_errs(tid, status, column));

  tp->kcol = column;
  tp->tflags |= TBL__MODIFIED;    /* MP 080291 */

  return(status);
}

int TCLGET(tid, column, label)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Reads column label.
..RETURNS status
-------------------------------------------------------------*/
	int	tid;	/* IN : table id	*/
	int	column;	/* IN : column number	*/
	char	*label;	/* OUT: label name	*/
{
	TABLE	*tp;
  	int	status;
  	char	*lab;

                      				/*   check argument 	*/
  tp = TBL_ptr(tid);
  if ((status = CheckTable(tp)))		return(TBL_errs(tid, status,0));
  if ((status = CheckColumn(tp, column)))	
				return(TBL_errs(tid, status, column));

  if (column == 0) {
   	strcpy(label,"Sequence  ");
	return (status);
  }

  lab = tp->label + (1+TBL_LABLEN)*(column-1);
  
					/* read descriptor	*/
  if (*lab == '\0') 
	status = GetLabel(tp, column, 1,TBL_LABLEN, lab);
  if (status)	return(status);
  strcpy (label, lab);

  return (status);
}

int TCLPUT(tid, column, label)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Writes column label.
..RETURNS status 
-------------------------------------------------------------*/
int	tid;	/* IN : table id	*/
int	column;	/* IN : column number	*/
char	*label;	/* IN : label name	*/

{
TABLE	*tp;
 
int	i, j, status;
 
char	ws[1+TBL_LABLEN];
char    *chptr;
char    check[TBL_LABLEN+4];            /* LABLEN .ge. UNILEN, FORLEN */



 
                      			/*   check table ID */
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));

j = (int) strlen(label);	/* convert 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;

if ((status = TBL_LABCHK(chptr,ws)))	return(status);
  ws[TBL_LABLEN] = '\0';

					/* write descriptor	*/
  if ((status = PutLabel(tp, column, ws, 1,TBL_LABLEN)))
  	return(status);
  

					/* force reading TCLGET	*/
  tp->label[(1+TBL_LABLEN)*(column-1)] = '\0';

  return(status);
}

int TCLSER(tid, label, column)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Searchs the sequential number of the column having the
                given label, case insensitive.
		Column is set to -1 when name is not found.
..RETURNS status. 
-------------------------------------------------------------*/
int	tid;		/* IN : table id	*/
char	*label;		/* IN : label name	*/
int	*column;	/* OUT: column number	*/

{
TABLE	*tp;

char	labcol[TBL_LABLEN+1], alabel[TBL_LABLEN+1];

int	i, j, status;



/*   check argument 	*/
tp = TBL_ptr(tid);
if ((status = CheckTable(tp)))	
   {
   return(TBL_errs(tid, status,0));
   }

/* sequential label search*/

for (j=0; (j<TBL_LABLEN) && isgraph(label[j]); j++) 
   {
   labcol[j] = label[j];
   }
labcol[j] = '\0';

*column = -1;
for (i = 1; i <= tp->cols; i++) 
   {
   if ((status = TCLGET (tid, i, alabel)))
      {
      return(status);
      }

   if (TBL_sdiff(labcol,alabel) == 0)	
      { 
      *column = i; 
      break; 
      }
   }

return(status);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
 typedef int (*FUNCTION)();
 struct option {
	char	*name;
	FUNCTION op_set;
 };

 int TBL_eset();	/* Function that set error display */
 int TBL_labcase();
 static struct option options[] = {
	{ "label_case", TBL_labcase},
	{ "display_errors", TBL_eset},
 };

static FUNCTION what_to_do(name)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE  Convert the option string to a function 
..RETURNS  The function to call / NULL if error
..REMARKS  Valid parameters are in the static options.
-------------------------------------------------------------*/
	char	*name;	/* IN: Name to check */
{
	struct option *op;
	int 	 i;

   for (i = ITEMS(options), op = options; --i >= 0; op++)  {
	if (TBL_sdiff (name, op->name) == 0)	break;
   }
   return (i >= 0 ? op->op_set : (FUNCTION)0);
}

int TCOERR (tid, row, col, status, text)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE  Write an error
..RETURNS  Status
..REMARKS  
-------------------------------------------------------------*/
	int	tid;	/* IN : table id	*/
	int	row;	/* IN: row number	*/
	int	col;	/* IN: column number	*/
	int	status;	/* IN: Status		*/
	char	*text;	/* IN: Text to add	*/
{
TABLE	*tp;


TBL_eclear();
tp = TBL_ptr(tid);
return(status);
}

int TCOGET(op_name, value)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE  Gets various options; 
..RETURNS  Status
..REMARKS  Valid parameters are:
	"label_case"	sensitivity to case of labels
	"display_error" to display the errors
-------------------------------------------------------------*/
	char	*op_name;	/* IN : Name of option	*/
	int	*value;		/* OUT: Option's value	*/
{
	int	got, status;
	FUNCTION f;
	
  if ((f = what_to_do(op_name))) {
	status = ERR_NORMAL;
	got = (*f)(0);
  	(*f)(got);		/* Reset the old value	*/
  	*value = got;
  }
  else	status = ERR_TBLKEY;
  return(status);
}

int TCOSET(op_name, value)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Set various options.
..RETURNS status
..REMARKS See TCOGET for possible names of op_name.
-------------------------------------------------------------*/
	char	*op_name;	/* IN : Name of option	*/
	int	value;		/* IN : Option's value	*/
{
	int	status;
	FUNCTION f;
	
  if ((f = what_to_do(op_name))) {
	status = ERR_NORMAL;
  	(*f)(value);	
  }
  else	status = ERR_TBLKEY,
	TBL_errf(status, "Unknown option %s", op_name);
  return(status);
}

int TCSGET(tid, row, value)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Reads row selection flag.
..METHOD Check if ALL selection flag in on. Otherwise, read
		selection flag.
..RETURNS status
-------------------------------------------------------------*/
int	tid;	/* IN : table id	*/
int	row;	/* IN : row number 	*/
int	*value;	/* OUT: selection flag (TRUE if selected) */

{
TABLE	*tp;

int	status;
int	*x, ic;

char	seleline[1+TBL_Dselect_SIZE];


/*   check argument 	*/

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

if ((status = CheckRow(tp, row)))	
   return(TBL_errs(tid, status, row));
	
/* Is it a view ?	*/

if (tp->vsize != 0) 
   {
   ic = (tp->vsel[row>>3] & bit_mask[row&7]);
   *value = (ic != 0);
   return (status);
   }

if (tp->select != '\0')			/* TSELTABL not read yet */
   {
   status = TCSINF (tid, seleline);	
   if (status) return(status);
   }

ic = TBL_offset (tp, row, 0L);

if (tp->select == '\1')			/* means ALL selected	*/
   *value = (row <= tp->rows);
else if ((x = (int *)TBL_RD(tp, ic, sizeof(int)))) 
   *value = *x;
else	
   status = TBL_RDst(); 

return (status);
}

int TCSPUT(tid, row, value)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Writes row selection flag:
		Writes the value (TRUE/FALSE) to indicate
                selection/not-selection of the row in the table.
..RETURNS status
-------------------------------------------------------------*/
	int	tid;	/* IN : table id	*/
	int	row;	/* IN : row number 	*/
	int	*value;	/* IN: selection flag	*/
{
	TABLE	*tp;
	int	status;
	int	*x, ic;
	unsigned char *vx;
		/* Table for selected rows modifications */
	static int incr[4] = { 0, 1, -1, 0};
	
                      			/*   check argument 	*/
  tp = TBL_ptr(tid);
  if ((status = CheckTable(tp)))		return(TBL_errs(tid, status,0));
  if ((status = CheckRow(tp, row)))	return(TBL_errs(tid, status, row));

					/* Is it a view ?	*/
    if (tp->vsize != 0) {
	ic = bit_mask[row&7];
	vx = &tp->vsel[row>>3];
	if (*value)	*vx |= ic;
	else		*vx &= ~ic;
	return (status);
  }

				/* "ALL" selection may become false... 	*/
  if ((!*value) && (tp->select == '\1'))  
	tp->select = '\0', tp->selected = -1;

  ic = TBL_offset (tp, row, 0L);
  if_not((x = (int *)TBL_RDF(tp, ic, sizeof(int), 1))) return(TBL_RDst());
  if (tp->selected >= 0)  {	/* The count is modified. */
	ic = (*value ? 1 : 0);
	ic |= (*x    ? 2 : 0);
	tp->selected += incr[ic];
  }
  *x = *value;

  return (status);
}

int TCSCNT(tid,count)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Count selected entries.
..METHOD	 Sequential scan of the selection flags.		
..RETURNS status
-------------------------------------------------------------*/
	int	tid;	/* IN : table id	*/
        int    *count;	/* out: number of selected entries */
{
	TABLE	*tp;
  	int	status;
  	int  	*pntr, i, iav, nw, ic ;
  	char	seleline[8];

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

					/* read TSELTABL descriptor */
  if (tp->vsize != 0) {
     *count = 0;
     for (i=1; i<tp->rows; i++) {
        ic = (tp->vsel[i>>3] & bit_mask[i&7]);
        if (ic != 0) *count += 1;
        }
     return(status);
  }  


  if (!tp->select){
  	status = SCDRDC(tp->imno, TBL_Dselect, 1, 1, 4, &iav, 
  		        seleline, &dunit, &dnull);   /* only need 1st char */

  	if (status)	return(TBL_errf(status, "can't access on table: %s",
  			tp->phname));
  	tp->select = seleline[0];
  	if (tp->select == '-') tp->select ='\1';
  }
  if (tp->selected >= 0)
	*count = tp->selected;
  else	{			/* Unknown. Must count it...	*/
  	*count = 0;
  	nw = (tp->swise == F_TRANS ? 1 : tp->acols + 1);
  	if_not (pntr = (int *)TBL_RD(tp, 0L, nw*sizeof(int)*tp->arows))
  		return(TBL_RDst()); 
  	for (i=0; i < tp->rows; i++, pntr += nw) 
       		if(*pntr) *count += 1;
  }

		/* Update the number of selected rows */
  if (tp->selected != *count)
  	tp->selected = *count, tp->tflags |= TBL__MODIFIED;
  return (status);
}

int TCSINI(tid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Initializes selection flag.
..RETURNS status
-------------------------------------------------------------*/
int	tid;	/* IN : table id	*/

{
TABLE	*tp;

int	status, i, nw, maxrows, mapped_rows;
int  	*pntr;

char	seleline[1+TBL_Dselect_SIZE];


/*   check argument 	*/
	

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

/* define selection line */

(void) memset((void *)seleline,32,(size_t)TBL_Dselect_SIZE);
seleline[0] = '-';
seleline[TBL_Dselect_SIZE] = '\0';	/* force Null terminated string */

/* write descriptor TSELTABL*/

status = SCDWRC(tp->imno,TBL_Dselect,1,seleline,1,TBL_Dselect_SIZE,&dunit);
if (status)
   {
   return(TBL_errf(status, "can't access on table: %s", tp->phname));
   }

/* Initialize Selection flag */

nw = (tp->swise == F_TRANS ? 1 : tp->acols + 1);
maxrows = TBL_EIO_LIMIT / sizeof(int) / nw;
maxrows = MAX (maxrows, 1);
for (i=0; i<tp->arows; i += maxrows) 
   {
   mapped_rows = tp->arows - i;
   mapped_rows = MIN (mapped_rows, maxrows);
   if_not (pntr = (int *)TBL_RDF (tp, TBL_offset(tp, i+1, 0L), 
	   nw*sizeof(int)*mapped_rows, 1))
      {
      return(TBL_RDst()); 
      }
   while (--mapped_rows >= 0)
      {
      *pntr = 1;
      pntr += nw;
      }
   }

tp->select = '\1';			/* All Rows Selected	*/
if (tp->selected != tp->rows) 
   {
   tp->selected = tp->rows, tp->tflags |= TBL__MODIFIED;
   }

return (status);
}


int TCSINF(tid, line)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Get selection information.
..RETURNS status
-------------------------------------------------------------*/
int	tid;	/* IN : table id	*/
char    *line;	/* OUT: selection info	*/

{
TABLE	*tp;

int	status, iav, j, k, tidview;

register char  cr;
	


/*   check argument 	*/

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

/* define selection line */

OnError_Continue;
if (tp->usname) 
   {
   status = SCFOPN(tp->usname,D_R4_FORMAT,F_O_MODE,F_TBL_TYPE, &tidview);
   status = SCDRDC(tidview, TBL_Dselect, 1, 1, TBL_Dselect_SIZE, &iav, 
                   line, &dunit, &dnull);
   status = SCFCLO(tidview);
   } 
else 
   {
   status = SCDRDC(tp->imno, TBL_Dselect, 1, 1, TBL_Dselect_SIZE, &iav, 
                   line, &dunit, &dnull);
   }
OnError_Reset;

if (status != ERR_NORMAL) 
   {
   line[0] = '-', line[1] = '\0';
   }
status = ERR_NORMAL;
tp->select = line[0];			/* This byte is changed to \1 if ALL */


/* Remove Trailing Blanks */

k = 1;
for (j=iav; j>0; j--)	
   {
   cr = line[j];
   if ((cr != ' ') && (cr != '\t'))
      {
      k = j;
      break;
      }
   }
line[k] = '\0'; 
  
if ((line[0] == '-') && (line[1] == '\0'))	
   {
   tp->select = '\1', tp->selected = tp->rows;
   }

return (status);
}

int TCSDSC(tid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Set selection flag according to descr. SELIDX
	layout of SELIDX: 1st elem = no. of selections (-1 = all selected)
        all other elements indices of selected rows (in increasing order!)
..RETURNS no. of selections
-------------------------------------------------------------*/
int tid;        /* IN: table id */

{
int i, selflag, iav, k, j, uni, nsel;
int nrow, nextsel, *isele, *seladr;

char  seltab[TBL_Dselect_SIZE+1];



OnError_Continue;

nsel = -1;
seltab[0] = '-';
(void) SCDRDI(tid,"SELIDX",1,1,&iav,&nsel,&k,&uni);
if (nsel < 1) return -99;

/* clear string */

(void) memset((void *)seltab,32,(size_t)TBL_Dselect_SIZE);
(void) SCDRDC(tid,"XTSELTABL",1,1,TBL_Dselect_SIZE,&iav,seltab,&k,&uni);
if (seltab[0] == '-') return -98;

/* get no. of rows */

(void) TCIGET(tid,&i,&nrow,&i,&i,&i);
if (nrow < 1) return -96;

isele = seladr = (int*) malloc((size_t)(nsel*sizeof(int)));
if (isele == (int *)0)
   {
   SCTPUT("WARNING: Not enough memory to create selection array!");
   return -97;
   }

/* copy selection criterion to official descr. */

(void) SCDWRC(tid,"TSELTABL",1,seltab,1,TBL_Dselect_SIZE,&uni);

(void) SCDRDI(tid,"SELIDX",2,nsel,&iav,isele,&k,&uni);
nextsel = *isele++;
j = 1;                               /* counter in SELIDX */

/* loop through table rows */

for (i=1; i<=nrow; i++)
   {
   if (i == nextsel)
      {
      selflag = 1;
      if (j < nsel)
         {                           /* move to next SELIDX elem */
         nextsel = *isele++;         /* always increasing... */
         j ++;
         }
      else
         {
         (void) TCSPUT(tid,i,&selflag);      /* do this selection */
         selflag = 0;                        /* clear all following rows */
         for ( k=i+1; k<=nrow; k++)
            {
            (void) TCSPUT(tid,k,&selflag);
            }
         goto free_sele;
         }
      }
   else
      selflag = 0;

   (void) TCSPUT(tid,i,&selflag);
   }

free_sele:
free (seladr);

if (j != nsel) 
   SCTPUT("selefromDescr() out of sync!");
else
   (void) TCSCNT(tid,&i);			/* needed !! */

OnError_Reset;

return nsel;
}

int TCSSET(tid, line)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Set selection information. Note that the number of
	selected rows is not modified, unless line is blank
	which asks for an ALL selection.
..RETURNS status
-------------------------------------------------------------*/
int	tid;	/* IN : table id	*/
char    *line;	/* IN : selection info	*/

{
TABLE	*tp;

int	status, len;

char	local_line[TBL_Dselect_SIZE+1];

	
/* pad line with blanks */

len = (int) strlen(line);
if (len > TBL_Dselect_SIZE)
   {
   len = TBL_Dselect_SIZE;
   }

(void) memset((void *)local_line,32,(size_t)TBL_Dselect_SIZE);
(void) strncpy(local_line, line,(size_t)len);

/* check the ALL selection */

if ((local_line[0] == ' ') || 
    ((local_line[0] == '-') && (local_line[1] == ' ')))
   return(TCSINI(tid));

/* check argument 	*/

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

status = SCDWRC(tp->imno,TBL_Dselect,1,local_line,1,TBL_Dselect_SIZE,&dunit);
  
return (status);
}

int TCUGET(tid, column, unit)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Reads column unit.
..RETURNS status
-------------------------------------------------------------*/
	int	tid;	/* IN : table id	*/
	int	column;	/* IN : column number	*/
	char	*unit;	/* OUT: unit name	*/
{
	TABLE	*tp;
  	int 	status;

                      			/*   check argument 	*/
  tp = TBL_ptr(tid);
  if ((status = CheckTable(tp)))		return(TBL_errs(tid, status,0));
  if ((status = CheckColumn(tp, column)))
				return(TBL_errs(tid, status, column));

  /* read descriptor; note that  GetLabel takes care of non-existant units */
  status = GetLabel(tp, column, 17, TBL_LABLEN, unit);
  return(status);
}

int TCUPUT(tid, column, unit)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Writes column units.
..RETURNS status
-------------------------------------------------------------*/
	int	tid;	/* IN : table id	*/
	int	column;	/* IN : column number	*/
	char	*unit;	/* IN : unit name	*/
{
	TABLE	*tp;
	char	ws[TBL_UNILEN+1];
  	int	status;

                      			/*   check argument 	*/
  tp = TBL_ptr(tid);
  if ((status = CheckTable(tp)))		return(TBL_errs(tid, status,0));
  if ((status = CheckColumn(tp, column)))
				return(TBL_errs(tid, status, column));

  if ((status = TBL_UNICHK(unit,ws)))	return(status);

						/* write descriptor	*/
  if ((status = PutLabel(tp, column, ws, 17,TBL_UNILEN)))
  	return(status);

  return (status);
}

int TCVERS(tid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE Reads table Version
..RETURNS ERR_NORMAL if Correct Version / -n for Obsolete Versions
-------------------------------------------------------------*/
	int	tid;	/* IN : table id	*/
{
	TABLE	*tp;
  	int	status;

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

  return (tp->version - TBL_VERSION);	/* Gives the difference. 0 of OK */
}

int TCAPUT(tid,column,items)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..PURPOSE changes the structure of a character column  into an array
..RETURNS ERR_NORMAL if Correct Version / -n for Obsolete Versions
-------------------------------------------------------------*/
int   tid;	          /* IN : table id	*/
int   column;             /* IN : column number */
int   items;              /* IN : column items */

{
TABLE   *tp;

int dtype,nrow,ic,nbytes,knull;
int status,i;

char ws[4],*x;



tp = TBL_ptr(tid);

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

dtype = tp->dtypes[column];
if (dtype & (~TBL_D_MASK)) 
   {
   status = ERR_TBLCOL;
   TBL_errf(status,"Can't change the structure of column #%d",column);
   return(status);
   }
nrow = tp->rows;
nbytes = tp->bytes[column-1];
for (i=1; i<=nrow; i++) 
   {
   ic = TBL_offset(tp,i,column);
   x = TBL_RD(tp,ic,nbytes);
   if (!(knull = TBL_isNULL(dtype,(int *)x)))
      {
      status = ERR_TBLCOL;
      TBL_errf(status,"Can't change the structure of column  #%d",column);
      return(status);
      }
   } 
(void) sprintf(ws,"%3d",items);
status = PutLabel(tp,column,ws,39,3);

return (status);
}
