/*===========================================================================
  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        	tct.c
.LANGUAGE    	C
.AUTHOR      	IPG-ESO Garching
.CATEGORY    	table interface 
.VERSION  1.0 	25-Mar-1987   Definition     J.D. Ponz
.VERSION  3.0 	01-Jul-1990   	New Version with Arrays / Elementary IO

 090406		last modif


.COMMENTS    
This module contains the routines to handle tables as a whole. 
Table initialization and open table functions return the
table identification tid, used by other table routines.

Tables are physically stored on disk in two different formats:
by records, corresponding to the natural way of storing sequentially
the rows, and transposed, where all the values of a single variable
-- column -- are stored together. 
It is the responsibility of the user to decide the physical format
when initializing the table file.

The functions provided by this module are:

Create or Initialize a table (TCTINI)
Open an existing table (TCTOPN)
Map an opened table to virtual memory (TCTMAP)
Unmap a mapped table (TCTUNM): write modified parts, unmap mapped pieces
Close a table (TCTCLO).

Main arguments used by the routines are:
allcol:	 number of words per record allocated physically in the
            table file.
allrow:	 number of records (rows) allocated physically in the
            table file.
mode:	 file opening mode. There are several modes to open a table:
               F_I\_MODE input (TBTOPN), 
               F_O\_MODE output (TBTINI), 
               F_IO\_MODE input/output (TBTOPN), 
               F_X\_MODE scratch (TBTINI), 
               F_D\_MODE descriptors only (TBTOPN).
           These symbols are provided in the file 'midas_def.h'
           in the directory 'MID_INCLUDE'.

	   The access mode may be forced to mapping with the F_MAP_FORCE
	   option, or to elementary i/o mode with the F_EIO_FORCE
	   option. The default is EIO mode for tables larger than
	   TBL_EIO_LIMIT bytes, a configuration parameter
	   which may be changed with TCOSET
name:	 table file name. It is a character string defining the
           file name of the table as follows: '[path_name]table_name[.ext]',
           where 'path_name' is the directory path name, defaulted to
           the working directory, 'table_name' is the name of the file
           and 'ext' is the file extension, defaulted to 'tbl'.
storage: physical file format. Defined as F_RECORD if the table
           is stored by records, or F_TRANS for transposed format.
           These symbols are provided in the file 'midas_def.h'
           in the directory 'MID_INCLUDE'.
tid:	 table identifier. It is an integer number provided by the
           system when the table is created or opened.
------------------------------------------------------------*/

#include <fileexts.h>		/* General MIDAS Symbols	*/

#include <tblsys.h>		/* Table System parameters	*/
#include <tbldef.h>		/* Symbols used for Tables	*/
#include <tblerr.h>		/* List of Table Errors		*/

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

#include <stdlib.h>		/* for malloc, free */
#include <stdio.h>


static int dunit=-1;		/* For future use */

static int eio_limit = TBL_EIO_LIMIT;

char	*osmmget(), *osmmexp();	/* Memory Allocation Functions	*/

/*

*/
	
/*=======================================================================
 *		Conversion of NULL values from old to New
 *=======================================================================*/

#ifdef __STDC__
static int convert_nulls(TABLE *tp)
#else
static int convert_nulls(tp)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE  Change NULL values to -Infinity
.METHOD   Assume that table is MAPPED.
.RETURNS  Number of Modified Columns
------------------------------------------------------------------*/
	TABLE	*tp;	/* MOD: Table concerned	*/
#endif
{
	int	i, j, di, o, cols;
	char	*pv;
	float	fnull;
	double	ddnull;

  TBL_toNULL((TBL_D_R4<<TBL_D_BITS)|1, (char *)&fnull);
  TBL_toNULL((TBL_D_R8<<TBL_D_BITS)|1, (char *)&ddnull);
  cols = 0;

  for (j = 0; j < tp->cols; j++) {
	if (tp->swise == F_TRANS)
		o = tp->offset[j] * tp->arows, di = tp->bytes[j];
	else	o = tp->offset[j],		di = tp->reclen;

	switch(tp->dtypes[j] >> TBL_D_BITS) {
	  default:		continue;
	  case TBL_D_R4:
		cols++;
		for (pv = tp->addres + o, i = tp->arows; --i >= 0; pv += di) {
			if (*((float *)pv) > 1.e38)	*(float *)pv = fnull;
		}
		break;
	  case TBL_D_R8:
		cols++;
		for (pv = tp->addres + o, i = tp->arows; --i >= 0; pv += di) {
			if (*((double *)pv) > 1.e38)	*(double *)pv = ddnull;
		}
		break;
	}
  }
  return(cols);
}
/*

*/

int dummyPUT(imno,size)

int  imno, size;

{
int  status;

char  *fpt;

struct FCT_STRUCT  *fctpntr;






fctpntr = FCT.ENTRIES + imno;
fctpntr->FILTYP = 22;

fpt = osmmget((unsigned int) (size * sizeof(float)));	/* already set to 0 */
status = SCFPUT(imno,1,size,fpt);

fctpntr->FILTYP = 2;
osmmfree(fpt);

return (status);
}
/*

*/

/*=======================================================================
 *		Private Routines
 *=======================================================================*/

#ifdef __STDC__
static int TBL_setcols(TABLE *tp)
#else
static int TBL_setcols(tp)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE	Set up configuration of the arrays with physical columns
.ALGORITHM	Allocate memory, and compute offset + length, set formats
.RETURNS        length of 1 record
------------------------------------------------------------------*/
TABLE	*tp;	/* MOD: Table concerned	*/
#endif

{
int	n, len1, icode, ncur, i;




/* Compute derived quantities		*/

tp->reclen = (tp->acols + 1) * 4;
tp->wsize = (tp->acols + 1) * tp->arows;

/* Store correct locations of arrays 	*/

n = tp->colitems;
tp->bytes  = (int *)osmmget(n*2*sizeof(int));
tp->abytes = tp->bytes  + n;	
tp->label  = osmmget(n*(2+TBL_FORLEN+TBL_LABLEN));
tp->format = tp->label + n*(1+TBL_LABLEN);

/* Compute length + offset of each column */

ncur = 4;
for (i=0; i<tp->cols; i++) 
   {
   icode = tp->dtypes[i];
   
   /*  we don't check this anymore - KB 040121

   if (icode < 0) 
      {				/. Convert OLD definitions to new ones	./
      switch (icode)	
         {  
	 default:
	 case -4:	 icode = TBL_D_R4; 	break;
	 case -8:	 icode = TBL_D_R8; 	break;
	 case -11:	 icode = TBL_D_I1; 	break;
	 case -12:	 icode = TBL_D_I2; 	break;
	 case -14:	 icode = TBL_D_I4; 	break;
	 }
      icode = (icode << TBL_D_BITS) | 1;
      tp->dtypes[i] = icode;
      }
   */

   len1 = TBL_ElementSize(icode);
   n = TBL_Items(icode);

   if (tp->version == 0)	
      {				/* BEWARE ! (MP) Old to New implies
				   true len of CHAR is multiple of 4 */
      n = ((3+len1*n)>>2)<<2;     /* n is now Multiple of 4 */
      n /= len1;   
      tp->offset[i] = ncur;
      if (!(icode>>TBL_D_BITS)) tp->dtypes[i] = n; /* MP 070291 */
      }

   tp->bytes[i] = len1*n;
   ncur += tp->bytes[i];
  }


/* Zero unused values	*/

for (; i < tp->colitems; i++) 
   tp->dtypes[i] = tp->offset[i] = tp->bytes[i] = 0;
  	

/* Formats not loaded	*/

len1 = 1+TBL_FORLEN;
n = 0;
for (i=0; i < tp->colitems; i++) 
   {
   tp->format[n] = '\0';
   n += len1;
   }
  	

/* Labels not loaded	*/

len1 = 1+TBL_LABLEN;
n = 0;
for (i=0; i < tp->colitems; i++)
   {
   tp->label[n] = '\0';
   n += len1;
   }
  	
return (ncur);
}
/*

*/

#ifdef __STDC__
static int do_map(TABLE *tp, int mode)
#else
static int do_map(tp, mode)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Choose the mode for file access (EIO / Mapping / Simulated Mapping)
.RETURNS status
------------------------------------------------------------------*/
	TABLE	*tp;	/* IN: Table to map	*/
	int	mode;	/* IN: Mode for Mapping	*/
#endif
{
	int	status;
	int	dummy, i;
	float	null_float;
	



  status = ERR_NORMAL;
  tp->cbuf = tp->nbuf = 0;
  tp->tbuf = (TABLE_BUF *)0;
  tp->sbuf = 0;
  tp->addres = (char *)0;
  tp->loaded_map = (unsigned char *)0;
  tp->vsel       = (unsigned char *)0;
  tp->vsize      = 0;
  
	/* Check, for old versions, of possible bad NULL values.
	   Changing NULL values also implies loding the table,
	   and therefore the F_MAP_FORCE is set.
	   Note that this test is only temporarly required, until
	   no table with old format exists any more...
	*/

  if (tp->version == 0) {
	TBL_toNULL((TBL_D_R4<<TBL_D_BITS)|1, (char *)&null_float);
    /* if (null_float < -1.e38) tp->tflags |= TBL__BADNULLS; */
        tp->tflags |= TBL__BADNULLS;
	mode = F_MAP_FORCE|F_IO_MODE;

  }

  	/* Choose the mode: TBL__MAPPED, or TBL__EIO.
  	   If no option, use TBL_EIO if size > TBL_EIO_LIMIT	*/

  if (mode & F_MAP_FORCE)	tp->tflags |= TBL__MAPPED;
  else if (mode & F_EIO_FORCE)	tp->tflags |= TBL__EIO;
  if ((tp->tflags& (TBL__MAPPED|TBL__EIO)) == 0) {
	if (sizeof(int)*tp->acols*tp->arows > eio_limit)
		tp->tflags |= TBL__EIO;
  }
  if (tp->tflags & TBL__MAPPED) {
  	status = SCFMAP(tp->imno, mode&0xf, 1,0,&dummy,&tp->addres);
	if_not(tp->tflags & TBL__READONLY)	tp->tflags |= TBL__MODIFIED;
  }
  else	if (tp->tflags & TBL__EIO) {	/* Elementary Mode. Define Buffers */
	/* Let's assume at least one buffer per column (in case the table
	   is stored columnwise and accessed recordwise) + Selection + another
	*/
	tp->nbuf = tp->acols + 2;
	if (tp->nbuf < TBL_TBUFS)	tp->nbuf = TBL_TBUFS;
  	i = tp->nbuf * sizeof(TABLE_BUF);
	tp->tbuf = (TABLE_BUF *)osmmget(i);
	oscfill ((char *)tp->tbuf, i, 0);
  }
  else {			/* BitMap contains size/64k bytes	*/
  	i = sizeof(float)*(tp->acols + 1) * tp->arows;
  	tp->addres = osmmget(i);
	i = (1 + (i|0xffff)) >> 16;
  	tp->loaded_map   = (unsigned char *)osmmget(2*i);
  	tp->modified_map = tp->loaded_map + i;
  	oscfill ((char *)tp->loaded_map, 2*i, 0);
  }
  if (status)	TBL_errf(status, "can't map table: %s", tp->phname);
  return(status);
}

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

int TCTCLO(tid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE 	Closes table file .
.ALGORITHM	First Unmap, then release used memory.
.RETURNS	status
------------------------------------------------------------------*/
	int	tid;	/* IN : table id	*/
{
TABLE	*tp;

int	status, i;

char    save_name[100];

 



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

if (tp->intlFITS == 'F') 
   {					/* SCFCLO may still need FITS data */
   if ((status = TCTUNMF(tid,0))) return(status);

   status =  SCFCLO(tp->imno);
   (void) TCTUNMF(tid,1);		/* now, also free table data memory */
   goto free_memory;
   }

if ((status = TCTUNM(tid))) return(status);

status = SCFCLO(tp->imno);
if (status == -99)			/* we have to convert to FITS */
   (void) strcpy(save_name,tp->phname);


/* Free allocated memory of table */

free_memory:
if (tp->phname != (char *) 0) osmmfree(tp->phname); 
if (tp->loaded_map != (unsigned char *) 0)   osmmfree((char *)tp->loaded_map); 
if (tp->usname != (char *) 0) osmmfree(tp->usname); 

if (!(tp->tflags&TBL__MAPPED)) 
   {
   if (tp->addres != (char *) 0) osmmfree(tp->addres);
   }

if (tp->label != (char *) 0) osmmfree(tp->label); 
  
if (tp->dtypes != (int *) 0)           osmmfree((char *)tp->dtypes); 
if (tp->bytes  != (int *) 0)           osmmfree((char *)tp->bytes); 
if (tp->tbuf   != (TABLE_BUF *) 0)     osmmfree((char *)tp->tbuf); 
if (tp->vsel   != (unsigned char *) 0) osmmfree((char *)tp->vsel); 
TBL_kill(tid);


if (status == -99)			/* we have to convert to FITS */
   {
   char   temp[12], delname[80];

   delname[0] = '\0';
   status = TCTOPN(save_name,F_I_MODE,&i); 
   if (status == 0)
      {
      tid = i;
      status = SCFSAV(tid,2);		/* convert table to FITS */
      if (status != ERR_NORMAL) return (status);

      status = SCFCLO(tid);
      if (status == ERR_NORMAL) 
         {
         status = osfrename("midFITS.mt",save_name);
         if (status != 0)
            {
            status = ERR_INPINV;
            (void) sprintf(temp,"TCTCLO/osfrename: %s ",save_name);
            MID_ERROR("MID",temp,status,0);
            return (status);
            }

         tp = TBL_ptr(tid);
         goto free_memory;
         }
      }
   }

return (status);
}

int TCTCRV(name, refer_name, mode)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
  	Create a view (a table with only a Selection Column)
  	taking columns from another (existing!) table.
.ALGORITHM
  	The view is ONLY CREATED. It must later be opened ...
.RETURNS
        Status
------------------------------------------------------------------*/
	char	*name;		/* IN : Name of view to create	*/
	char	*refer_name;	/* IN : Name of related table	*/
	int	mode;		/* IN : creation mode (future)	*/
{
  	int	status;
	int	vid;
	TABLE	*vp;


	/* Operations:
	   1- TCTOPN refer_table in read-only mode; check.
	   2- Create file. Set all bits to 1.
	   3- Copy column definitions
	   4- Copy selections ? 
	*/



                      
  if ((status = TCTOPN (refer_name, F_I_MODE | F_EIO_FORCE, &vid))) 
	return (status);

  vp = TBL_ptr (vid);
  if (vp->swise & F_B_VIEW) {	
	TCTCLO (vid);
	return (TBL_errf(ERR_TBLENT, "can't create view of the view: %s",
		refer_name));
  }
  vp->swise |= F_B_VIEW;
  vp->vsize = 4 + (vp->rows>>3) ; 
  vp->vsel  = (unsigned char *)osmmget( vp->vsize );
  oscfill ((char *)vp->vsel, vp->vsize, ~0);

  if ((status = SCFCRE (name, D_R4_FORMAT, F_O_MODE, F_TBL_TYPE, 
  	vp->vsize>>2, &vp->vno)))	goto error;

  status = SCFPUT(vp->vno,1,vp->vsize,(char *)vp->vsel);
					/* Add refer_table as descriptor */
  status = SCDWRC(vp->vno, TBL_Dview, 1, refer_name, 1, strlen(refer_name), &dunit);
  if (status)	goto error;
					/* copy descriptors */
  if ((status = SCDCOP(vp->imno, vp->vno, 1, " ")))
	goto error;
					/* Rewrite Main Descriptor	*/
  if ((status = SCDWRI(vp->vno, TBL_Dmain, &vp->acols,
		1,TBL_Dmain_SIZE,&dunit)))	goto error;
	
  SCFCLO(vp->vno);
  error:					/* error in SC routines	*/
  TCTCLO (vid);
  if (status) 	TBL_errs(vid, status, 0);
  return ( status );
}
/*

*/

int TCTUNMF(tid,flag)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE 	Unmap (Flush) FITS table file in 2 calls
.ALGORITHM	1st call: write back modified parts 
                2nd call: release used memory
.RETURNS	status
------------------------------------------------------------------*/
int	tid;	/* IN : table id	*/
int	flag;	/* IN : 0 = do 1st part of TCTUNM
		        1 = do 2nd part of TCTUNM */

{
TABLE	*tp;
TABLE_BUF *bp;

int    status, i, newtid;

 


tp = TBL_ptr(tid);
status = ERR_NORMAL;

if (flag == 0)
   {
   if (tp->phname == (char *) 0) return ERR_NORMAL;	/* nothing to do */


   if (tp->vsize != 0 && tp->usname)
      {
      if (tp->tflags & TBL__VREADONLY)
         ;
      else
         {
         status = SCFOPN(tp->usname,D_R4_FORMAT,F_O_MODE,F_TBL_TYPE, &newtid);
         status = SCFPUT (newtid, 1, tp->vsize, (char *)tp->vsel);
         status = SCFCLO(newtid);
         }
      }


   if ((tp->tflags & (TBL__READONLY|TBL__MODIFIED)) == TBL__MODIFIED)
      {
      if_not (tp->tflags & TBL__KEEPVERS) 
             tp->version = TBL_VERSION;

      /* If ALL is selected, correct number of selected rows       */

      if ((tp->selected < 0) && (tp->select == '\1'))
          tp->selected = tp->rows;
      }

   if (tp->tflags != TBL__READONLY)     /* if_not (tp->tflags & TBL__MAPPED) */
      status = TBL_WR (tp);
   }

else
   {
   if (tp->addres != (char *) 0)
      {
      osmmfree(tp->addres);
      tp->addres = (char *) 0;    /* MP 920909 */
      }

   for (i=0; i<tp->nbuf; i++)
      {
      bp = tp->tbuf+i;
      if (bp && bp->buf) osmmfree(bp->buf);
      }
   }

return (status);
}

/*

*/
int TCTFIX(tid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE 	free allocated memory
.RETURNS	status

added by KB  980428
 990624
------------------------------------------------------------------*/
int     tid;    /* IN : table id        */

{
        TABLE   *tp;
        int     status;




  tp = TBL_ptr(tid);
  if ((status = TCTUNM(tid))) return(status);

                        /* Free allocated memory        */
  osmmfree(tp->phname);
  tp->phname = (char *) 0;
  					/* tp->usname is NOT used currently */
  osmmfree((char *)tp->dtypes);
  tp->dtypes = (int *) 0;

  osmmfree((char *)tp->bytes);
  tp->bytes = (int *) 0;

  osmmfree(tp->label);
  tp->label = (char *) 0;

  if (!(tp->tflags&TBL__MAPPED))
     {
     if (tp->addres != (char *) 0)
        {
        osmmfree(tp->addres);
        tp->addres = (char *) 0;
        }
     }
  osmmfree((char *)tp->tbuf);
  tp->tbuf = (TABLE_BUF *) 0;

  osmmfree((char *)tp->loaded_map);
  tp->loaded_map = (unsigned char *) 0;

  osmmfree((char *)tp->vsel);
  tp->vsel = (unsigned char *) 0;

  return (status);
}

int TCTUNM(tid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE 	Unmap (Flush) table file
.ALGORITHM	Writes back modified parts and releases used memory.
.RETURNS	status
------------------------------------------------------------------*/
	int	tid;	/* IN : table id	*/
{
	TABLE	*tp;
        TABLE_BUF *bp;
	int	status;
	int	i,newtid;
 


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

if (tp->vsize != 0 && tp->usname)
   {
   if (tp->tflags & TBL__VREADONLY)	
      ;
   else
      {
      status = SCFOPN(tp->usname,D_R4_FORMAT,F_O_MODE,F_TBL_TYPE, &newtid);
      status = SCFPUT (newtid, 1, tp->vsize, (char *)tp->vsel);
      status = SCFCLO(newtid);
      }
   } 


if ((tp->tflags & (TBL__READONLY|TBL__MODIFIED)) == TBL__MODIFIED) 
   {
   if_not (tp->tflags & TBL__KEEPVERS) tp->version = TBL_VERSION;
	
   /* If ALL is selected, correct number of selected rows 	*/

   if ((tp->selected < 0) && (tp->select == '\1')) tp->selected = tp->rows;

   /* write descriptors	*/

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

if_not  (tp->tflags & TBL__MAPPED) 
   {						/* Must write everything */
   if ((status = TBL_WR (tp)))       return(status);

   osmmfree(tp->addres);
   tp->addres = (char *) 0;    /* MP 920909 */
   }

for (i=0; i<tp->nbuf; i++)
   {
   bp = tp->tbuf+i;
   if (bp && bp->buf) osmmfree(bp->buf);
   }

error:					/* error in SC routines	*/
if (status)
   {
   TBL_enter("TCTUNM problems");
   TBL_errs(tid, status, 0);
   }
return (status);
}

int TCTID(name)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Find number from Name
.RETURNS Corresponding tid / -1 if not found
--------------------------------------------------------------------*/
	char *name;	/* IN: Name to look for */
{
  return (TBL_tid(name));
}

int TCTINI(name, storage, mode, allcol, allrow, tid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
  	Initializes table file on disk and in memory
.ALGORITHM
  	Creates output file and opens it
        according to the desired storage option and mode. 
        The storage option can be by records (F_RECORD) or
        transposed (F_TRANS). 
        The openning mode can be
        F_O_MODE for output or F_X_MODE for scratch. 
        
        The mode may include modifiers F_MAP_FORCE to force the
        mapping, F_EIO_FORCE to force an elementary i/o mode,
        F_ALL_FORCE to force exact numbers of lines / cols. 
        In this last case (F_ALL_FORCE), the number of physical
        columns to allocate may be given in the 2 leftmost bytes of mode,
        i.e. mode = (physical_columns*2**16 + F_ALL_FORCE + mode)
.RETURNS
        Status
------------------------------------------------------------------*/
	char	*name;		/* IN : table name              */
	int    storage;		/* IN : physical structure on disk */
	int	mode;		/* IN : opening mode           */
	int	allcol;		/* IN : number of words per record alloc.*/
	int	allrow;		/* IN : number of rows allocated */
	int	*tid;		/* MOD: table identifier (try to keep it) */

{
TABLE	*tp;

int	status, dummy;
int	imno, len, acol, arow, colitems;

struct FCT_STRUCT  *fctpntr;





tp = (TABLE *)0;
acol = (allcol <= 0) ? TBL_DALCOL : allcol ;
arow = (allrow <= 0) ? TBL_DALROW : allrow ;

			/* Restrictions: Rows multiple of 8, columns odd.
			 * This ensures alignments on 8-byte boundaries	*/
colitems = 0;
if (mode & F_ALL_FORCE)
   {
   colitems = mode >> 16;
   }
else
   {
   arow = ((arow+7)>>3)<<3;
   if (!(acol&1)) acol++;
   }
if (colitems == 0) colitems = MIN (acol,COLS_MAX);

            
len  = arow * (acol + 1);
imno = 0;
  
if (mode == F_FO_MODE)			/* set in fitsrhd() */
   {
   dummy = 1;
   mode = F_O_MODE;
   status = SCFCRE(name,D_R4_FORMAT,mode&0xf,F_FTBL_TYPE,len,&imno);
   }
else
   {
   dummy = 0;
   status = SCFCRE(name,D_R4_FORMAT,mode&0xf,F_TBL_TYPE,len,&imno);
   }
if (status != ERR_NORMAL) goto error;
                                            /* initialize control variables */
if (TBL_new(imno) < 0)	
   return(TBL_errf(ERR_TBLFUL, "too many tables, can't create %s", name));

*tid = imno;
tp = TBL_ptr(imno);

fctpntr = FCT.ENTRIES + imno;
tp->phname = TBL_ssave(fctpntr->NAME);		/* use cleaned name */
tp->usname = (char *)0;
tp->imno   = imno;
tp->acols = acol;
tp->arows = arow;
tp->cols = 0;
tp->rows = 0;
tp->kcol = 0;
tp->scol = 0;
tp->swise = storage;
tp->version= TBL_VERSION;
tp->colitems = colitems;
tp->selected = -1;				/* unknown */
tp->tflags = 0;
tp->select = 0;
if (dummy == 1)
   tp->intlFITS = 'F';			/* internal FITS table */
else
   tp->intlFITS = ' ';
					/* initialize columns for dtype	*/
tp->dtypes = (int *)osmmget(2*sizeof(int)*tp->colitems);
tp->offset = tp->dtypes + tp->colitems;
TBL_setcols (tp);
					/* write basic descriptors */
status = SCDWRI(tp->imno, TBL_Ddtypes, tp->dtypes, 1,tp->colitems, &dunit);
if (status != ERR_NORMAL) goto error;

status = SCDWRI(tp->imno, TBL_Doffset, tp->offset, 1,tp->colitems, &dunit);
if (status != ERR_NORMAL) goto error;

status = SCDWRI(tp->imno, TBL_Dmain, &tp->acols,1,TBL_Dmain_SIZE,&dunit);
if (status != ERR_NORMAL) goto error;

status = do_map(tp, mode);		/* map file */
if (status != ERR_NORMAL) goto error;

if (tp->intlFITS == 'F')		/* we have to write some data */
   {					/* because TCSINI uses SCFGET ... */
   status = dummyPUT(imno,len);
   if (status != ERR_NORMAL) goto error;
   }

TCSINI (*tid);

return (status);


error:
if (tp) osmmfree (tp->phname),TBL_kill(tp->imno);
if (status < ERR_TBLFUL)			/* SC error	*/
   MID_ERROR("MIDAS","TCTINI: ",status,1);

return (status);
}
/*

*/


int TCTMAP(tid, addr)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Maps complete Table
.RETURNS status
------------------------------------------------------------------*/
	int	tid;	/* IN : table id		*/
	char	**addr;	/* OUT: Where table Mapped	*/
{
	TABLE_BUF *bp;		/* MOD: The buffer concerned	*/
	TABLE	*tp;
	unsigned char *pmap;
	int	status;
	int	i;


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

  if (tp->tflags & TBL__MAPPED) {	/* Already Done	*/
	*addr = tp->addres;
  	return(status);
  }
  if (tp->tflags & TBL__EIO) {		/* Verify all empty */
  	for (i = tp->nbuf, bp = tp->tbuf; (--i >= 0) && (bp->size == 0); bp++);
  } else {				/* Just check bitmap	*/
  	i = sizeof(float)*(tp->acols + 1) * tp->arows;
	i = (1 + (i|0xffff)) >> 16;
	for (pmap = tp->loaded_map; (--i >= 0) && (*pmap == 0); pmap++) ;
  }

  if (i >= 0)	status = ERR_TBLMAP;
  if (status) {
	TBL_errf(status, "Table partly mapped. First Unmap table %s", 
		tp->phname);
	return(status);
  }
			/* Free allocated memory for Buffers	*/
  osmmfree((char *)tp->tbuf); 
  osmmfree((char *)tp->loaded_map); 
  osmmfree((char *)tp->vsel); 
	
  i = F_MAP_FORCE | (tp->tflags & TBL__READONLY ? F_I_MODE : F_IO_MODE);
  status = do_map (tp, i);

  return (status);
}

int TCTOPN(name,mode,tid)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
  	Reads  table file from disk.
.ALGORITHM
  	Opens table file 
        according to the desired mode. The openning mode can be
        F_I_MODE for input, F_D_MODE for descriptors only, 
        F_IO_MODE for update. 

        The mode may include modifiers F_MAP_FORCE to force the
        mapping, or F_EIO_FORCE to force an elementary i/o mode.
.RETURNS
        Status
------------------------------------------------------------------*/
	char	*name;		/* IN : table name              */
	int	mode;		/* IN : openning mode    */
	int	*tid;		/* MOD: table identifier (try to keep it) */
{
TABLE	*tp;

int  	imno, maxval;
int  dnull=-1;
int  status, flags[3];



					/* opens file   		*/
                      
flags[0] = ERRO_CONT;			/* continue always, and */
flags[1] = ERRO_LOG;			/* log nothing, and */
flags[2] = ERRO_DISP;			/* display nothing */
ERRO_CONT = 1;;
ERRO_LOG = 0;
ERRO_DISP = 0;

  imno = *tid;			/* in case a specific `index' is wanted... */
  if ((status = SCFOPN(name, D_R4_FORMAT, 0, F_TBL_TYPE, &imno)))
     {
     SCECNT("PUT", flags, flags+1, flags+2);
     return(TBL_errf(status,"Error opening Table %s",name));
     }

  if (TBL_new(imno) < 0) 	/* Not too many opened tables?	*/
     {
     TBL_errf(ERR_TBLFUL, "too many tables, can't open %s", name);
     return (ERR_TBLFUL);
     }
		
  *tid = imno;
  tp = TBL_ptr(imno);
  tp->phname = TBL_ssave(name);
  tp->usname = (char *)0;
  tp->tflags = ((mode&0xf) == F_I_MODE ? TBL__READONLY : 0);	
  tp->imno   = imno;
  tp->addres = (char *)0;
  tp->select = 0;
						/* get control variables */
  status = SCDRDI(tp->imno, TBL_Dmain,1,TBL_Dmain_SIZE,
                  &maxval,&tp->acols,&dunit,&dnull);
  if (status != ERR_NORMAL) goto error;

  if (maxval < 8)	tp->version = 0;
  if (maxval < 9)	tp->colitems = MAX (tp->acols, tp->cols);
  if (maxval <10)	tp->selected = -1;	/* Unknown */
  if (tp->selected == -1) tp->selected = tp->rows;

  if (tp->swise & F_B_VIEW) 
     {
	int	vid, oldtid, kk;
	TABLE *vp, *oldtp;
	char	view[1+TBL_Dview_SIZE];

  	status = SCDRDC(tp->imno, TBL_Dview, 1,1,TBL_Dview_SIZE, &maxval,
                        view, &dunit, &dnull);
	if (status != ERR_NORMAL) 
           {
	   TBL_errf (status, "Bad View: %s", name);
	   goto error;
	   }

	view[maxval] = '\0';
	if (osfdate(view) > osfdate(name))
	   TBL_errf (-1, "table '%s' refered by view '%s' was modified ?",
		     view, name);

        oldtp = tp;		/* save table pointer */
        oldtid = *tid;

        ERRO_CONT = flags[0];
        ERRO_LOG = flags[1];                   /* reset error flags */
        ERRO_DISP = flags[2];			/* reset error flags */
	vid = -1;
        status = TCTOPN (view, F_I_MODE, &vid);
	if (status != ERR_NORMAL) 	goto error;

	vp = TBL_ptr(vid);
	vp->vno    = oldtp->imno;

	if (oldtp->tflags & TBL__READONLY)	vp->tflags |= TBL__VREADONLY;

					/* Read Selection Table	*/
	kk = sizeof(float);
        vp->vsize = 1 + (vp->rows>>3);
        if (vp->vsize < kk) vp->vsize = kk;	/* space for at least 1 value */
	vp->vsel = (unsigned char *)osmmget( vp->vsize );
        vp->usname = TBL_ssave(name);

  	kk = tp->vsize>>2;	/* really tp->vsize, not vp->vsize? KB 050603 */
        if (kk > vp->vsize)
           {
           char mesg[80];
           (void) sprintf(mesg,
                  "tp->vsize>>2 = %d, vp->vsize = %d, so we truncate...",
                  kk,vp->vsize);
           SCTPUT(mesg);
           kk = vp->vsize;
           }
        /* status = SCFGET (vp->vno,1,tp->vsize>>2,&maxval,(char *)vp->vsel); */
        status = SCFGET (vp->vno, 1, kk, &maxval, (char *)vp->vsel);

        oldtp->tflags |= TBL__MAPPED;	/* to avoid going through TBL_WR() */
        TCTCLO(oldtid);
 
	*tid = vid;			/* return the new table id */
	goto error;
  }
	
						/* Map File		*/
  status = do_map(tp, mode);
  if (status != ERR_NORMAL) goto error;
						/* Get Column datatypes	*/
  tp->dtypes = (int *)osmmget(2*sizeof(int)*tp->colitems);
  tp->offset = tp->dtypes + tp->colitems;

  status = SCDRDI(tp->imno, TBL_Ddtypes,1,tp->colitems,&maxval,
          tp->dtypes,&dunit,&dnull);
  if (status != ERR_NORMAL) goto error;

  if (tp->version)  status = SCDRDI(tp->imno, TBL_Doffset,1,tp->colitems,
  		&maxval,tp->offset,&dunit,&dnull);
				 		/* layout of physical columns */
  TBL_setcols (tp);

			/* Convert NULL values if required */
  if (tp->tflags & TBL__BADNULLS) 
     {
     convert_nulls(tp);
     tp->tflags &= ~TBL__BADNULLS;
     }



  error:
  ERRO_CONT = flags[0];				/* reset error flags */
  ERRO_LOG = flags[1]; 
  ERRO_DISP = flags[2];	
  if ((status) && (status < ERR_TBLFUL))		/* SC error	*/
	TBL_errs(imno, status, 0);
  return ( status );
}
/*

*/

int TCTVIS(tid,name)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
  	Reads  table file from disk.
.ALGORITHM
  	Opens table file 
        according to the desired mode. The openning mode can be
        F_I_MODE for input, F_D_MODE for descriptors only, 
        F_IO_MODE for update. 

        The mode may include modifiers F_MAP_FORCE to force the
        mapping, or F_EIO_FORCE to force an elementary i/o mode.
.RETURNS
        Status
------------------------------------------------------------------*/
	int	tid;		/* MOD: table identifier (try to keep it) */
	char	*name;		/* IN : table name              */
{
	TABLE	*tp;

        
  tp = TBL_ptr(tid);
  if (tp->usname) {
         strncpy(name,tp->usname,60);
         return(1);
         }
  else return(0);
}
