mirror of
https://bitbucket.org/cosmicvoids/vide_public.git
synced 2025-07-04 07:11:12 +00:00
2823 lines
96 KiB
C
2823 lines
96 KiB
C
/************************************************************************/
|
|
/* */
|
|
/* CFITSIO Lexical Parser */
|
|
/* */
|
|
/* This file is one of 3 files containing code which parses an */
|
|
/* arithmetic expression and evaluates it in the context of an input */
|
|
/* FITS file table extension. The CFITSIO lexical parser is divided */
|
|
/* into the following 3 parts/files: the CFITSIO "front-end", */
|
|
/* eval_f.c, contains the interface between the user/CFITSIO and the */
|
|
/* real core of the parser; the FLEX interpreter, eval_l.c, takes the */
|
|
/* input string and parses it into tokens and identifies the FITS */
|
|
/* information required to evaluate the expression (ie, keywords and */
|
|
/* columns); and, the BISON grammar and evaluation routines, eval_y.c, */
|
|
/* receives the FLEX output and determines and performs the actual */
|
|
/* operations. The files eval_l.c and eval_y.c are produced from */
|
|
/* running flex and bison on the files eval.l and eval.y, respectively. */
|
|
/* (flex and bison are available from any GNU archive: see www.gnu.org) */
|
|
/* */
|
|
/* The grammar rules, rather than evaluating the expression in situ, */
|
|
/* builds a tree, or Nodal, structure mapping out the order of */
|
|
/* operations and expression dependencies. This "compilation" process */
|
|
/* allows for much faster processing of multiple rows. This technique */
|
|
/* was developed by Uwe Lammers of the XMM Science Analysis System, */
|
|
/* although the CFITSIO implementation is entirely code original. */
|
|
/* */
|
|
/* */
|
|
/* Modification History: */
|
|
/* */
|
|
/* Kent Blackburn c1992 Original parser code developed for the */
|
|
/* FTOOLS software package, in particular, */
|
|
/* the fselect task. */
|
|
/* Kent Blackburn c1995 BIT column support added */
|
|
/* Peter D Wilson Feb 1998 Vector column support added */
|
|
/* Peter D Wilson May 1998 Ported to CFITSIO library. User */
|
|
/* interface routines written, in essence */
|
|
/* making fselect, fcalc, and maketime */
|
|
/* capabilities available to all tools */
|
|
/* via single function calls. */
|
|
/* Peter D Wilson Jun 1998 Major rewrite of parser core, so as to */
|
|
/* create a run-time evaluation tree, */
|
|
/* inspired by the work of Uwe Lammers, */
|
|
/* resulting in a speed increase of */
|
|
/* 10-100 times. */
|
|
/* Peter D Wilson Jul 1998 gtifilter(a,b,c,d) function added */
|
|
/* Peter D Wilson Aug 1998 regfilter(a,b,c,d) function added */
|
|
/* Peter D Wilson Jul 1999 Make parser fitsfile-independent, */
|
|
/* allowing a purely vector-based usage */
|
|
/* Peter D Wilson Aug 1999 Add row-offset capability */
|
|
/* Peter D Wilson Sep 1999 Add row-range capability to ffcalc_rng */
|
|
/* */
|
|
/************************************************************************/
|
|
|
|
#include <limits.h>
|
|
#include <ctype.h>
|
|
#include "eval_defs.h"
|
|
#include "region.h"
|
|
|
|
typedef struct {
|
|
int datatype; /* Data type to cast parse results into for user */
|
|
void *dataPtr; /* Pointer to array of results, NULL if to use iterCol */
|
|
void *nullPtr; /* Pointer to nulval, use zero if NULL */
|
|
long maxRows; /* Max No. of rows to process, -1=all, 0=1 iteration */
|
|
int anyNull; /* Flag indicating at least 1 undef value encountered */
|
|
} parseInfo;
|
|
|
|
/* Internal routines needed to allow the evaluator to operate on FITS data */
|
|
|
|
static void Setup_DataArrays( int nCols, iteratorCol *cols,
|
|
long fRow, long nRows );
|
|
static int find_column( char *colName, void *itslval );
|
|
static int find_keywd ( char *key, void *itslval );
|
|
static int allocateCol( int nCol, int *status );
|
|
static int load_column( int varNum, long fRow, long nRows,
|
|
void *data, char *undef );
|
|
|
|
static int DEBUG_PIXFILTER;
|
|
|
|
#define FREE(x) { if (x) free(x); else printf("invalid free(" #x ") at %s:%d\n", __FILE__, __LINE__); }
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
int fffrow( fitsfile *fptr, /* I - Input FITS file */
|
|
char *expr, /* I - Boolean expression */
|
|
long firstrow, /* I - First row of table to eval */
|
|
long nrows, /* I - Number of rows to evaluate */
|
|
long *n_good_rows, /* O - Number of rows eval to True */
|
|
char *row_status, /* O - Array of boolean results */
|
|
int *status ) /* O - Error status */
|
|
/* */
|
|
/* Evaluate a boolean expression using the indicated rows, returning an */
|
|
/* array of flags indicating which rows evaluated to TRUE/FALSE */
|
|
/*---------------------------------------------------------------------------*/
|
|
{
|
|
parseInfo Info;
|
|
int naxis, constant;
|
|
long nelem, naxes[MAXDIMS], elem;
|
|
char result;
|
|
|
|
if( *status ) return( *status );
|
|
|
|
FFLOCK;
|
|
if( ffiprs( fptr, 0, expr, MAXDIMS, &Info.datatype, &nelem, &naxis,
|
|
naxes, status ) ) {
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
if( nelem<0 ) {
|
|
constant = 1;
|
|
nelem = -nelem;
|
|
} else
|
|
constant = 0;
|
|
|
|
if( Info.datatype!=TLOGICAL || nelem!=1 ) {
|
|
ffcprs();
|
|
ffpmsg("Expression does not evaluate to a logical scalar.");
|
|
FFUNLOCK;
|
|
return( *status = PARSE_BAD_TYPE );
|
|
}
|
|
|
|
if( constant ) { /* No need to call parser... have result from ffiprs */
|
|
result = gParse.Nodes[gParse.resultNode].value.data.log;
|
|
*n_good_rows = nrows;
|
|
for( elem=0; elem<nrows; elem++ )
|
|
row_status[elem] = result;
|
|
} else {
|
|
firstrow = (firstrow>1 ? firstrow : 1);
|
|
Info.dataPtr = row_status;
|
|
Info.nullPtr = NULL;
|
|
Info.maxRows = nrows;
|
|
|
|
if( ffiter( gParse.nCols, gParse.colData, firstrow-1, 0,
|
|
parse_data, (void*)&Info, status ) == -1 )
|
|
*status = 0; /* -1 indicates exitted without error before end... OK */
|
|
|
|
if( *status ) {
|
|
|
|
/***********************/
|
|
/* Error... Do nothing */
|
|
/***********************/
|
|
|
|
} else {
|
|
|
|
/***********************************/
|
|
/* Count number of good rows found */
|
|
/***********************************/
|
|
|
|
*n_good_rows = 0L;
|
|
for( elem=0; elem<Info.maxRows; elem++ ) {
|
|
if( row_status[elem]==1 ) ++*n_good_rows;
|
|
}
|
|
}
|
|
}
|
|
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return(*status);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int ffsrow( fitsfile *infptr, /* I - Input FITS file */
|
|
fitsfile *outfptr, /* I - Output FITS file */
|
|
char *expr, /* I - Boolean expression */
|
|
int *status ) /* O - Error status */
|
|
/* */
|
|
/* Evaluate an expression on all rows of a table. If the input and output */
|
|
/* files are not the same, copy the TRUE rows to the output file. If the */
|
|
/* files are the same, delete the FALSE rows (preserve the TRUE rows). */
|
|
/* Can copy rows between extensions of the same file, *BUT* if output */
|
|
/* extension is before the input extension, the second extension *MUST* be */
|
|
/* opened using ffreopen, so that CFITSIO can handle changing file lengths. */
|
|
/*--------------------------------------------------------------------------*/
|
|
{
|
|
parseInfo Info;
|
|
int naxis, constant;
|
|
long nelem, rdlen, naxes[MAXDIMS], maxrows, nbuff, nGood, inloc, outloc;
|
|
LONGLONG ntodo, inbyteloc, outbyteloc, hsize;
|
|
long freespace;
|
|
unsigned char *buffer, result;
|
|
struct {
|
|
LONGLONG rowLength, numRows, heapSize;
|
|
LONGLONG dataStart, heapStart;
|
|
} inExt, outExt;
|
|
|
|
if( *status ) return( *status );
|
|
|
|
FFLOCK;
|
|
if( ffiprs( infptr, 0, expr, MAXDIMS, &Info.datatype, &nelem, &naxis,
|
|
naxes, status ) ) {
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
|
|
if( nelem<0 ) {
|
|
constant = 1;
|
|
nelem = -nelem;
|
|
} else
|
|
constant = 0;
|
|
|
|
/**********************************************************************/
|
|
/* Make sure expression evaluates to the right type... logical scalar */
|
|
/**********************************************************************/
|
|
|
|
if( Info.datatype!=TLOGICAL || nelem!=1 ) {
|
|
ffcprs();
|
|
ffpmsg("Expression does not evaluate to a logical scalar.");
|
|
FFUNLOCK;
|
|
return( *status = PARSE_BAD_TYPE );
|
|
}
|
|
|
|
/***********************************************************/
|
|
/* Extract various table information from each extension */
|
|
/***********************************************************/
|
|
|
|
if( infptr->HDUposition != (infptr->Fptr)->curhdu )
|
|
ffmahd( infptr, (infptr->HDUposition) + 1, NULL, status );
|
|
if( *status ) {
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
inExt.rowLength = (long) (infptr->Fptr)->rowlength;
|
|
inExt.numRows = (infptr->Fptr)->numrows;
|
|
inExt.heapSize = (infptr->Fptr)->heapsize;
|
|
if( inExt.numRows == 0 ) { /* Nothing to copy */
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
|
|
if( outfptr->HDUposition != (outfptr->Fptr)->curhdu )
|
|
ffmahd( outfptr, (outfptr->HDUposition) + 1, NULL, status );
|
|
if( (outfptr->Fptr)->datastart < 0 )
|
|
ffrdef( outfptr, status );
|
|
if( *status ) {
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
outExt.rowLength = (long) (outfptr->Fptr)->rowlength;
|
|
outExt.numRows = (outfptr->Fptr)->numrows;
|
|
if( !outExt.numRows )
|
|
(outfptr->Fptr)->heapsize = 0L;
|
|
outExt.heapSize = (outfptr->Fptr)->heapsize;
|
|
|
|
if( inExt.rowLength != outExt.rowLength ) {
|
|
ffpmsg("Output table has different row length from input");
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status = PARSE_BAD_OUTPUT );
|
|
}
|
|
|
|
/***********************************/
|
|
/* Fill out Info data for parser */
|
|
/***********************************/
|
|
|
|
Info.dataPtr = (char *)malloc( (size_t) ((inExt.numRows + 1) * sizeof(char)) );
|
|
Info.nullPtr = NULL;
|
|
Info.maxRows = (long) inExt.numRows;
|
|
if( !Info.dataPtr ) {
|
|
ffpmsg("Unable to allocate memory for row selection");
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status = MEMORY_ALLOCATION );
|
|
}
|
|
|
|
/* make sure array is zero terminated */
|
|
((char*)Info.dataPtr)[inExt.numRows] = 0;
|
|
|
|
if( constant ) { /* Set all rows to the same value from constant result */
|
|
|
|
result = gParse.Nodes[gParse.resultNode].value.data.log;
|
|
for( ntodo = 0; ntodo<inExt.numRows; ntodo++ )
|
|
((char*)Info.dataPtr)[ntodo] = result;
|
|
nGood = (long) (result ? inExt.numRows : 0);
|
|
|
|
} else {
|
|
|
|
ffiter( gParse.nCols, gParse.colData, 0L, 0L,
|
|
parse_data, (void*)&Info, status );
|
|
|
|
nGood = 0;
|
|
for( ntodo = 0; ntodo<inExt.numRows; ntodo++ )
|
|
if( ((char*)Info.dataPtr)[ntodo] ) nGood++;
|
|
}
|
|
|
|
if( *status ) {
|
|
/* Error... Do nothing */
|
|
} else {
|
|
rdlen = (long) inExt.rowLength;
|
|
buffer = (unsigned char *)malloc(maxvalue(500000,rdlen) * sizeof(char) );
|
|
if( buffer==NULL ) {
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status=MEMORY_ALLOCATION );
|
|
}
|
|
maxrows = maxvalue( (500000L/rdlen), 1);
|
|
nbuff = 0;
|
|
inloc = 1;
|
|
if( infptr==outfptr ) { /* Skip initial good rows if input==output file */
|
|
while( ((char*)Info.dataPtr)[inloc-1] ) inloc++;
|
|
outloc = inloc;
|
|
} else {
|
|
outloc = (long) (outExt.numRows + 1);
|
|
if (outloc > 1)
|
|
ffirow( outfptr, outExt.numRows, nGood, status );
|
|
}
|
|
|
|
do {
|
|
if( ((char*)Info.dataPtr)[inloc-1] ) {
|
|
ffgtbb( infptr, inloc, 1L, rdlen, buffer+rdlen*nbuff, status );
|
|
nbuff++;
|
|
if( nbuff==maxrows ) {
|
|
ffptbb( outfptr, outloc, 1L, rdlen*nbuff, buffer, status );
|
|
outloc += nbuff;
|
|
nbuff = 0;
|
|
}
|
|
}
|
|
inloc++;
|
|
} while( !*status && inloc<=inExt.numRows );
|
|
|
|
if( nbuff ) {
|
|
ffptbb( outfptr, outloc, 1L, rdlen*nbuff, buffer, status );
|
|
outloc += nbuff;
|
|
}
|
|
|
|
if( infptr==outfptr ) {
|
|
|
|
if( outloc<=inExt.numRows )
|
|
ffdrow( infptr, outloc, inExt.numRows-outloc+1, status );
|
|
|
|
} else if( inExt.heapSize && nGood ) {
|
|
|
|
/* Copy heap, if it exists and at least one row copied */
|
|
|
|
/********************************************************/
|
|
/* Get location information from the output extension */
|
|
/********************************************************/
|
|
|
|
if( outfptr->HDUposition != (outfptr->Fptr)->curhdu )
|
|
ffmahd( outfptr, (outfptr->HDUposition) + 1, NULL, status );
|
|
outExt.dataStart = (outfptr->Fptr)->datastart;
|
|
outExt.heapStart = (outfptr->Fptr)->heapstart;
|
|
|
|
/*************************************************/
|
|
/* Insert more space into outfptr if necessary */
|
|
/*************************************************/
|
|
|
|
hsize = outExt.heapStart + outExt.heapSize;
|
|
freespace = (long) (( ( (hsize + 2879) / 2880) * 2880) - hsize);
|
|
ntodo = inExt.heapSize;
|
|
|
|
if ( (freespace - ntodo) < 0) { /* not enough existing space? */
|
|
ntodo = (ntodo - freespace + 2879) / 2880; /* number of blocks */
|
|
ffiblk(outfptr, (long) ntodo, 1, status); /* insert the blocks */
|
|
}
|
|
ffukyj( outfptr, "PCOUNT", inExt.heapSize+outExt.heapSize,
|
|
NULL, status );
|
|
|
|
/*******************************************************/
|
|
/* Get location information from the input extension */
|
|
/*******************************************************/
|
|
|
|
if( infptr->HDUposition != (infptr->Fptr)->curhdu )
|
|
ffmahd( infptr, (infptr->HDUposition) + 1, NULL, status );
|
|
inExt.dataStart = (infptr->Fptr)->datastart;
|
|
inExt.heapStart = (infptr->Fptr)->heapstart;
|
|
|
|
/**********************************/
|
|
/* Finally copy heap to outfptr */
|
|
/**********************************/
|
|
|
|
ntodo = inExt.heapSize;
|
|
inbyteloc = inExt.heapStart + inExt.dataStart;
|
|
outbyteloc = outExt.heapStart + outExt.dataStart + outExt.heapSize;
|
|
|
|
while ( ntodo && !*status ) {
|
|
rdlen = (long) minvalue(ntodo,500000);
|
|
ffmbyt( infptr, inbyteloc, REPORT_EOF, status );
|
|
ffgbyt( infptr, rdlen, buffer, status );
|
|
ffmbyt( outfptr, outbyteloc, IGNORE_EOF, status );
|
|
ffpbyt( outfptr, rdlen, buffer, status );
|
|
inbyteloc += rdlen;
|
|
outbyteloc += rdlen;
|
|
ntodo -= rdlen;
|
|
}
|
|
|
|
/***********************************************************/
|
|
/* But must update DES if data is being appended to a */
|
|
/* pre-existing heap space. Edit each new entry in file */
|
|
/***********************************************************/
|
|
|
|
if( outExt.heapSize ) {
|
|
LONGLONG repeat, offset, j;
|
|
int i;
|
|
for( i=1; i<=(outfptr->Fptr)->tfield; i++ ) {
|
|
if( (outfptr->Fptr)->tableptr[i-1].tdatatype<0 ) {
|
|
for( j=outExt.numRows+1; j<=outExt.numRows+nGood; j++ ) {
|
|
ffgdesll( outfptr, i, j, &repeat, &offset, status );
|
|
offset += outExt.heapSize;
|
|
ffpdes( outfptr, i, j, repeat, offset, status );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* End of HEAP copy */
|
|
|
|
FREE(buffer);
|
|
}
|
|
|
|
FREE(Info.dataPtr);
|
|
ffcprs();
|
|
|
|
ffcmph(outfptr, status); /* compress heap, deleting any orphaned data */
|
|
FFUNLOCK;
|
|
return(*status);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
int ffcrow( fitsfile *fptr, /* I - Input FITS file */
|
|
int datatype, /* I - Datatype to return results as */
|
|
char *expr, /* I - Arithmetic expression */
|
|
long firstrow, /* I - First row to evaluate */
|
|
long nelements, /* I - Number of elements to return */
|
|
void *nulval, /* I - Ptr to value to use as UNDEF */
|
|
void *array, /* O - Array of results */
|
|
int *anynul, /* O - Were any UNDEFs encountered? */
|
|
int *status ) /* O - Error status */
|
|
/* */
|
|
/* Calculate an expression for the indicated rows of a table, returning */
|
|
/* the results, cast as datatype (TSHORT, TDOUBLE, etc), in array. If */
|
|
/* nulval==NULL, UNDEFs will be zeroed out. For vector results, the number */
|
|
/* of elements returned may be less than nelements if nelements is not an */
|
|
/* even multiple of the result dimension. Call fftexp to obtain the */
|
|
/* dimensions of the results. */
|
|
/*---------------------------------------------------------------------------*/
|
|
{
|
|
parseInfo Info;
|
|
int naxis;
|
|
long nelem1, naxes[MAXDIMS];
|
|
|
|
if( *status ) return( *status );
|
|
|
|
FFLOCK;
|
|
if( ffiprs( fptr, 0, expr, MAXDIMS, &Info.datatype, &nelem1, &naxis,
|
|
naxes, status ) ) {
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
if( nelem1<0 ) nelem1 = - nelem1;
|
|
|
|
if( nelements<nelem1 ) {
|
|
ffcprs();
|
|
ffpmsg("Array not large enough to hold at least one row of data.");
|
|
FFUNLOCK;
|
|
return( *status = PARSE_LRG_VECTOR );
|
|
}
|
|
|
|
firstrow = (firstrow>1 ? firstrow : 1);
|
|
|
|
if( datatype ) Info.datatype = datatype;
|
|
|
|
Info.dataPtr = array;
|
|
Info.nullPtr = nulval;
|
|
Info.maxRows = nelements / nelem1;
|
|
|
|
if( ffiter( gParse.nCols, gParse.colData, firstrow-1, 0,
|
|
parse_data, (void*)&Info, status ) == -1 )
|
|
*status=0; /* -1 indicates exitted without error before end... OK */
|
|
|
|
*anynul = Info.anyNull;
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int ffcalc( fitsfile *infptr, /* I - Input FITS file */
|
|
char *expr, /* I - Arithmetic expression */
|
|
fitsfile *outfptr, /* I - Output fits file */
|
|
char *parName, /* I - Name of output parameter */
|
|
char *parInfo, /* I - Extra information on parameter */
|
|
int *status ) /* O - Error status */
|
|
/* */
|
|
/* Evaluate an expression for all rows of a table. Call ffcalc_rng with */
|
|
/* a row range of 1-MAX. */
|
|
{
|
|
long start=1, end=LONG_MAX;
|
|
|
|
return ffcalc_rng( infptr, expr, outfptr, parName, parInfo,
|
|
1, &start, &end, status );
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int ffcalc_rng( fitsfile *infptr, /* I - Input FITS file */
|
|
char *expr, /* I - Arithmetic expression */
|
|
fitsfile *outfptr, /* I - Output fits file */
|
|
char *parName, /* I - Name of output parameter */
|
|
char *parInfo, /* I - Extra information on parameter */
|
|
int nRngs, /* I - Row range info */
|
|
long *start, /* I - Row range info */
|
|
long *end, /* I - Row range info */
|
|
int *status ) /* O - Error status */
|
|
/* */
|
|
/* Evaluate an expression using the data in the input FITS file and place */
|
|
/* the results into either a column or keyword in the output fits file, */
|
|
/* depending on the value of parName (keywords normally prefixed with '#') */
|
|
/* and whether the expression evaluates to a constant or a table column. */
|
|
/* The logic is as follows: */
|
|
/* (1) If a column exists with name, parName, put results there. */
|
|
/* (2) If parName starts with '#', as in #NAXIS, put result there, */
|
|
/* with parInfo used as the comment. If expression does not evaluate */
|
|
/* to a constant, flag an error. */
|
|
/* (3) If a keyword exists with name, parName, and expression is a */
|
|
/* constant, put result there, using parInfo as the new comment. */
|
|
/* (4) Else, create a new column with name parName and TFORM parInfo. */
|
|
/* If parInfo is NULL, use a default data type for the column. */
|
|
/*--------------------------------------------------------------------------*/
|
|
{
|
|
parseInfo Info;
|
|
int naxis, constant, typecode, newNullKwd=0;
|
|
long nelem, naxes[MAXDIMS], repeat, width;
|
|
int col_cnt, colNo;
|
|
Node *result;
|
|
char card[81], tform[16], nullKwd[9], tdimKwd[9];
|
|
|
|
if( *status ) return( *status );
|
|
|
|
FFLOCK;
|
|
if( ffiprs( infptr, 0, expr, MAXDIMS, &Info.datatype, &nelem, &naxis,
|
|
naxes, status ) ) {
|
|
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
if( nelem<0 ) {
|
|
constant = 1;
|
|
nelem = -nelem;
|
|
} else
|
|
constant = 0;
|
|
|
|
/* Case (1): If column exists put it there */
|
|
|
|
colNo = 0;
|
|
if( ffgcno( outfptr, CASEINSEN, parName, &colNo, status )==COL_NOT_FOUND ) {
|
|
|
|
/* Output column doesn't exist. Test for keyword. */
|
|
|
|
/* Case (2): Does parName indicate result should be put into keyword */
|
|
|
|
*status = 0;
|
|
if( parName[0]=='#' ) {
|
|
if( ! constant ) {
|
|
ffcprs();
|
|
ffpmsg( "Cannot put tabular result into keyword (ffcalc)" );
|
|
FFUNLOCK;
|
|
return( *status = PARSE_BAD_TYPE );
|
|
}
|
|
parName++;
|
|
|
|
} else if( constant ) {
|
|
|
|
/* Case (3): Does a keyword named parName already exist */
|
|
|
|
if( ffgcrd( outfptr, parName, card, status )==KEY_NO_EXIST ) {
|
|
colNo = -1;
|
|
} else if( *status ) {
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
|
|
} else
|
|
colNo = -1;
|
|
|
|
if( colNo<0 ) {
|
|
|
|
/* Case (4): Create new column */
|
|
|
|
*status = 0;
|
|
ffgncl( outfptr, &colNo, status );
|
|
colNo++;
|
|
if( parInfo==NULL || *parInfo=='\0' ) {
|
|
/* Figure out best default column type */
|
|
if( gParse.hdutype==BINARY_TBL ) {
|
|
sprintf(tform,"%ld",nelem);
|
|
switch( Info.datatype ) {
|
|
case TLOGICAL: strcat(tform,"L"); break;
|
|
case TLONG: strcat(tform,"J"); break;
|
|
case TDOUBLE: strcat(tform,"D"); break;
|
|
case TSTRING: strcat(tform,"A"); break;
|
|
case TBIT: strcat(tform,"X"); break;
|
|
case TLONGLONG: strcat(tform,"K"); break;
|
|
}
|
|
} else {
|
|
switch( Info.datatype ) {
|
|
case TLOGICAL:
|
|
ffcprs();
|
|
ffpmsg("Cannot create LOGICAL column in ASCII table");
|
|
FFUNLOCK;
|
|
return( *status = NOT_BTABLE );
|
|
case TLONG: strcpy(tform,"I11"); break;
|
|
case TDOUBLE: strcpy(tform,"D23.15"); break;
|
|
case TSTRING:
|
|
case TBIT: sprintf(tform,"A%ld",nelem); break;
|
|
}
|
|
}
|
|
parInfo = tform;
|
|
} else if( !(isdigit((int) *parInfo)) && gParse.hdutype==BINARY_TBL ) {
|
|
if( Info.datatype==TBIT && *parInfo=='B' )
|
|
nelem = (nelem+7)/8;
|
|
sprintf(tform,"%ld%s",nelem,parInfo);
|
|
parInfo = tform;
|
|
}
|
|
fficol( outfptr, colNo, parName, parInfo, status );
|
|
if( naxis>1 )
|
|
ffptdm( outfptr, colNo, naxis, naxes, status );
|
|
|
|
/* Setup TNULLn keyword in case NULLs are encountered */
|
|
|
|
ffkeyn("TNULL", colNo, nullKwd, status);
|
|
if( ffgcrd( outfptr, nullKwd, card, status )==KEY_NO_EXIST ) {
|
|
*status = 0;
|
|
if( gParse.hdutype==BINARY_TBL ) {
|
|
LONGLONG nullVal=0;
|
|
fits_binary_tform( parInfo, &typecode, &repeat, &width, status );
|
|
if( typecode==TBYTE )
|
|
nullVal = UCHAR_MAX;
|
|
else if( typecode==TSHORT )
|
|
nullVal = SHRT_MIN;
|
|
else if( typecode==TINT )
|
|
nullVal = INT_MIN;
|
|
else if( typecode==TLONG )
|
|
nullVal = LONG_MIN;
|
|
else if( typecode==TLONGLONG )
|
|
nullVal = LONGLONG_MIN;
|
|
|
|
if( nullVal ) {
|
|
ffpkyj( outfptr, nullKwd, nullVal, "Null value", status );
|
|
fits_set_btblnull( outfptr, colNo, nullVal, status );
|
|
newNullKwd = 1;
|
|
}
|
|
} else if( gParse.hdutype==ASCII_TBL ) {
|
|
ffpkys( outfptr, nullKwd, "NULL", "Null value string", status );
|
|
fits_set_atblnull( outfptr, colNo, "NULL", status );
|
|
newNullKwd = 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
} else if( *status ) {
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
} else {
|
|
|
|
/********************************************************/
|
|
/* Check if a TDIM keyword should be written/updated. */
|
|
/********************************************************/
|
|
|
|
ffkeyn("TDIM", colNo, tdimKwd, status);
|
|
ffgcrd( outfptr, tdimKwd, card, status );
|
|
if( *status==0 ) {
|
|
/* TDIM exists, so update it with result's dimension */
|
|
ffptdm( outfptr, colNo, naxis, naxes, status );
|
|
} else if( *status==KEY_NO_EXIST ) {
|
|
/* TDIM does not exist, so clear error stack and */
|
|
/* write a TDIM only if result is multi-dimensional */
|
|
*status = 0;
|
|
ffcmsg();
|
|
if( naxis>1 )
|
|
ffptdm( outfptr, colNo, naxis, naxes, status );
|
|
}
|
|
if( *status ) {
|
|
/* Either some other error happened in ffgcrd */
|
|
/* or one happened in ffptdm */
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
|
|
}
|
|
|
|
if( colNo>0 ) {
|
|
|
|
/* Output column exists (now)... put results into it */
|
|
|
|
int anyNull = 0;
|
|
int nPerLp, i;
|
|
long totaln;
|
|
|
|
ffgkyj(infptr, "NAXIS2", &totaln, 0, status);
|
|
|
|
/*************************************/
|
|
/* Create new iterator Output Column */
|
|
/*************************************/
|
|
|
|
col_cnt = gParse.nCols;
|
|
if( allocateCol( col_cnt, status ) ) {
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
|
|
fits_iter_set_by_num( gParse.colData+col_cnt, outfptr,
|
|
colNo, 0, OutputCol );
|
|
gParse.nCols++;
|
|
|
|
for( i=0; i<nRngs; i++ ) {
|
|
Info.dataPtr = NULL;
|
|
Info.maxRows = end[i]-start[i]+1;
|
|
|
|
/*
|
|
If there is only 1 range, and it includes all the rows,
|
|
and there are 10 or more rows, then set nPerLp = 0 so
|
|
that the iterator function will dynamically choose the
|
|
most efficient number of rows to process in each loop.
|
|
Otherwise, set nPerLp to the number of rows in this range.
|
|
*/
|
|
|
|
if( (Info.maxRows >= 10) && (nRngs == 1) &&
|
|
(start[0] == 1) && (end[0] == totaln))
|
|
nPerLp = 0;
|
|
else
|
|
nPerLp = Info.maxRows;
|
|
|
|
if( ffiter( gParse.nCols, gParse.colData, start[i]-1,
|
|
nPerLp, parse_data, (void*)&Info, status ) == -1 )
|
|
*status = 0;
|
|
else if( *status ) {
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
if( Info.anyNull ) anyNull = 1;
|
|
}
|
|
|
|
if( newNullKwd && !anyNull ) {
|
|
ffdkey( outfptr, nullKwd, status );
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Put constant result into keyword */
|
|
|
|
result = gParse.Nodes + gParse.resultNode;
|
|
switch( Info.datatype ) {
|
|
case TDOUBLE:
|
|
ffukyd( outfptr, parName, result->value.data.dbl, 15,
|
|
parInfo, status );
|
|
break;
|
|
case TLONG:
|
|
ffukyj( outfptr, parName, result->value.data.lng, parInfo, status );
|
|
break;
|
|
case TLOGICAL:
|
|
ffukyl( outfptr, parName, result->value.data.log, parInfo, status );
|
|
break;
|
|
case TBIT:
|
|
case TSTRING:
|
|
ffukys( outfptr, parName, result->value.data.str, parInfo, status );
|
|
break;
|
|
}
|
|
}
|
|
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int fftexp( fitsfile *fptr, /* I - Input FITS file */
|
|
char *expr, /* I - Arithmetic expression */
|
|
int maxdim, /* I - Max Dimension of naxes */
|
|
int *datatype, /* O - Data type of result */
|
|
long *nelem, /* O - Vector length of result */
|
|
int *naxis, /* O - # of dimensions of result */
|
|
long *naxes, /* O - Size of each dimension */
|
|
int *status ) /* O - Error status */
|
|
/* */
|
|
/* Evaluate the given expression and return information on the result. */
|
|
/*--------------------------------------------------------------------------*/
|
|
{
|
|
FFLOCK;
|
|
ffiprs( fptr, 0, expr, maxdim, datatype, nelem, naxis, naxes, status );
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int ffiprs( fitsfile *fptr, /* I - Input FITS file */
|
|
int compressed, /* I - Is FITS file hkunexpanded? */
|
|
char *expr, /* I - Arithmetic expression */
|
|
int maxdim, /* I - Max Dimension of naxes */
|
|
int *datatype, /* O - Data type of result */
|
|
long *nelem, /* O - Vector length of result */
|
|
int *naxis, /* O - # of dimensions of result */
|
|
long *naxes, /* O - Size of each dimension */
|
|
int *status ) /* O - Error status */
|
|
/* */
|
|
/* Initialize the parser and determine what type of result the expression */
|
|
/* produces. */
|
|
/*--------------------------------------------------------------------------*/
|
|
{
|
|
Node *result;
|
|
int i,lexpr, tstatus = 0;
|
|
int xaxis, bitpix;
|
|
long xaxes[9];
|
|
static iteratorCol dmyCol;
|
|
|
|
if( *status ) return( *status );
|
|
|
|
/* make sure all internal structures for this HDU are current */
|
|
if ( ffrdef(fptr, status) ) return(*status);
|
|
|
|
/* Initialize the Parser structure */
|
|
|
|
gParse.def_fptr = fptr;
|
|
gParse.compressed = compressed;
|
|
gParse.nCols = 0;
|
|
gParse.colData = NULL;
|
|
gParse.varData = NULL;
|
|
gParse.getData = find_column;
|
|
gParse.loadData = load_column;
|
|
gParse.Nodes = NULL;
|
|
gParse.nNodesAlloc= 0;
|
|
gParse.nNodes = 0;
|
|
gParse.hdutype = 0;
|
|
gParse.status = 0;
|
|
|
|
fits_get_hdu_type(fptr, &gParse.hdutype, status );
|
|
|
|
if (gParse.hdutype == IMAGE_HDU) {
|
|
|
|
fits_get_img_param(fptr, 9, &bitpix, &xaxis, xaxes, status);
|
|
if (*status) {
|
|
ffpmsg("ffiprs: unable to get image dimensions");
|
|
return( *status );
|
|
}
|
|
gParse.totalRows = xaxis > 0 ? 1 : 0;
|
|
for (i = 0; i < xaxis; ++i)
|
|
gParse.totalRows *= xaxes[i];
|
|
if (DEBUG_PIXFILTER)
|
|
printf("naxis=%d, gParse.totalRows=%ld\n", xaxis, gParse.totalRows);
|
|
}
|
|
else if( ffgkyj(fptr, "NAXIS2", &gParse.totalRows, 0, &tstatus) )
|
|
{
|
|
/* this might be a 1D or null image with no NAXIS2 keyword */
|
|
gParse.totalRows = 0;
|
|
}
|
|
|
|
|
|
/* Copy expression into parser... read from file if necessary */
|
|
|
|
|
|
if( expr[0]=='@' ) {
|
|
if( ffimport_file( expr+1, &gParse.expr, status ) ) return( *status );
|
|
lexpr = strlen(gParse.expr);
|
|
} else {
|
|
lexpr = strlen(expr);
|
|
gParse.expr = (char*)malloc( (2+lexpr)*sizeof(char));
|
|
strcpy(gParse.expr,expr);
|
|
}
|
|
strcat(gParse.expr + lexpr,"\n");
|
|
gParse.index = 0;
|
|
gParse.is_eobuf = 0;
|
|
|
|
/* Parse the expression, building the Nodes and determing */
|
|
/* which columns are needed and what data type is returned */
|
|
|
|
ffrestart(NULL);
|
|
if( ffparse() ) {
|
|
return( *status = PARSE_SYNTAX_ERR );
|
|
}
|
|
/* Check results */
|
|
|
|
*status = gParse.status;
|
|
if( *status ) return(*status);
|
|
|
|
if( !gParse.nNodes ) {
|
|
ffpmsg("Blank expression");
|
|
return( *status = PARSE_SYNTAX_ERR );
|
|
}
|
|
if( !gParse.nCols ) {
|
|
dmyCol.fptr = fptr; /* This allows iterator to know value of */
|
|
gParse.colData = &dmyCol; /* fptr when no columns are referenced */
|
|
}
|
|
|
|
result = gParse.Nodes + gParse.resultNode;
|
|
|
|
*naxis = result->value.naxis;
|
|
*nelem = result->value.nelem;
|
|
for( i=0; i<*naxis && i<maxdim; i++ )
|
|
naxes[i] = result->value.naxes[i];
|
|
|
|
switch( result->type ) {
|
|
case BOOLEAN:
|
|
*datatype = TLOGICAL;
|
|
break;
|
|
case LONG:
|
|
*datatype = TLONG;
|
|
break;
|
|
case DOUBLE:
|
|
*datatype = TDOUBLE;
|
|
break;
|
|
case BITSTR:
|
|
*datatype = TBIT;
|
|
break;
|
|
case STRING:
|
|
*datatype = TSTRING;
|
|
break;
|
|
default:
|
|
*datatype = 0;
|
|
ffpmsg("Bad return data type");
|
|
*status = gParse.status = PARSE_BAD_TYPE;
|
|
break;
|
|
}
|
|
gParse.datatype = *datatype;
|
|
FREE(gParse.expr);
|
|
|
|
if( result->operation==CONST_OP ) *nelem = - *nelem;
|
|
return(*status);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
void ffcprs( void ) /* No parameters */
|
|
/* */
|
|
/* Clear the parser, making it ready to accept a new expression. */
|
|
/*--------------------------------------------------------------------------*/
|
|
{
|
|
int col, node, i;
|
|
|
|
if( gParse.nCols > 0 ) {
|
|
FREE( gParse.colData );
|
|
for( col=0; col<gParse.nCols; col++ ) {
|
|
if( gParse.varData[col].undef == NULL ) continue;
|
|
if( gParse.varData[col].type == BITSTR )
|
|
FREE( ((char**)gParse.varData[col].data)[0] );
|
|
free( gParse.varData[col].undef );
|
|
}
|
|
FREE( gParse.varData );
|
|
gParse.nCols = 0;
|
|
}
|
|
|
|
if( gParse.nNodes > 0 ) {
|
|
node = gParse.nNodes;
|
|
while( node-- ) {
|
|
if( gParse.Nodes[node].operation==gtifilt_fct ) {
|
|
i = gParse.Nodes[node].SubNodes[0];
|
|
if (gParse.Nodes[ i ].value.data.ptr)
|
|
FREE( gParse.Nodes[ i ].value.data.ptr );
|
|
}
|
|
else if( gParse.Nodes[node].operation==regfilt_fct ) {
|
|
i = gParse.Nodes[node].SubNodes[0];
|
|
fits_free_region( (SAORegion *)gParse.Nodes[ i ].value.data.ptr );
|
|
}
|
|
}
|
|
gParse.nNodes = 0;
|
|
}
|
|
if( gParse.Nodes ) free( gParse.Nodes );
|
|
gParse.Nodes = NULL;
|
|
|
|
gParse.hdutype = ANY_HDU;
|
|
gParse.pixFilter = 0;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
int parse_data( long totalrows, /* I - Total rows to be processed */
|
|
long offset, /* I - Number of rows skipped at start*/
|
|
long firstrow, /* I - First row of this iteration */
|
|
long nrows, /* I - Number of rows in this iter */
|
|
int nCols, /* I - Number of columns in use */
|
|
iteratorCol *colData, /* IO- Column information/data */
|
|
void *userPtr ) /* I - Data handling instructions */
|
|
/* */
|
|
/* Iterator work function which calls the parser and copies the results */
|
|
/* into either an OutputCol or a data pointer supplied in the userPtr */
|
|
/* structure. */
|
|
/*---------------------------------------------------------------------------*/
|
|
{
|
|
int status, constant=0, anyNullThisTime=0;
|
|
long jj, kk, idx, remain, ntodo;
|
|
Node *result;
|
|
iteratorCol * outcol;
|
|
|
|
/* declare variables static to preserve their values between calls */
|
|
static void *Data, *Null;
|
|
static int datasize;
|
|
static long lastRow, repeat, resDataSize;
|
|
static LONGLONG jnull;
|
|
static parseInfo *userInfo;
|
|
static long zeros[4] = {0,0,0,0};
|
|
|
|
if (DEBUG_PIXFILTER)
|
|
printf("parse_data(total=%ld, offset=%ld, first=%ld, rows=%ld, cols=%d)\n",
|
|
totalrows, offset, firstrow, nrows, nCols);
|
|
/*--------------------------------------------------------*/
|
|
/* Initialization procedures: execute on the first call */
|
|
/*--------------------------------------------------------*/
|
|
outcol = colData + (nCols - 1);
|
|
if (firstrow == offset+1)
|
|
{
|
|
userInfo = (parseInfo*)userPtr;
|
|
userInfo->anyNull = 0;
|
|
|
|
if( userInfo->maxRows>0 )
|
|
userInfo->maxRows = minvalue(totalrows,userInfo->maxRows);
|
|
else if( userInfo->maxRows<0 )
|
|
userInfo->maxRows = totalrows;
|
|
else
|
|
userInfo->maxRows = nrows;
|
|
|
|
lastRow = firstrow + userInfo->maxRows - 1;
|
|
|
|
if( userInfo->dataPtr==NULL ) {
|
|
|
|
if( outcol->iotype == InputCol ) {
|
|
ffpmsg("Output column for parser results not found!");
|
|
return( PARSE_NO_OUTPUT );
|
|
}
|
|
/* Data gets set later */
|
|
Null = outcol->array;
|
|
userInfo->datatype = outcol->datatype;
|
|
|
|
/* Check for a TNULL/BLANK keyword for output column/image */
|
|
|
|
status = 0;
|
|
jnull = 0;
|
|
if (gParse.hdutype == IMAGE_HDU) {
|
|
if (gParse.pixFilter->blank)
|
|
jnull = (LONGLONG) gParse.pixFilter->blank;
|
|
}
|
|
else {
|
|
ffgknjj( outcol->fptr, "TNULL", outcol->colnum,
|
|
1, &jnull, (int*)&jj, &status );
|
|
|
|
if( status==BAD_INTKEY ) {
|
|
/* Probably ASCII table with text TNULL keyword */
|
|
switch( userInfo->datatype ) {
|
|
case TSHORT: jnull = (LONGLONG) SHRT_MIN; break;
|
|
case TINT: jnull = (LONGLONG) INT_MIN; break;
|
|
case TLONG: jnull = (LONGLONG) LONG_MIN; break;
|
|
}
|
|
}
|
|
}
|
|
repeat = outcol->repeat;
|
|
if (DEBUG_PIXFILTER)
|
|
printf("parse_data: using null value %ld\n", jnull);
|
|
} else {
|
|
|
|
Data = userInfo->dataPtr;
|
|
Null = (userInfo->nullPtr ? userInfo->nullPtr : zeros);
|
|
repeat = gParse.Nodes[gParse.resultNode].value.nelem;
|
|
|
|
}
|
|
|
|
/* Determine the size of each element of the returned result */
|
|
|
|
switch( userInfo->datatype ) {
|
|
case TBIT: /* Fall through to TBYTE */
|
|
case TLOGICAL: /* Fall through to TBYTE */
|
|
case TBYTE: datasize = sizeof(char); break;
|
|
case TSHORT: datasize = sizeof(short); break;
|
|
case TINT: datasize = sizeof(int); break;
|
|
case TLONG: datasize = sizeof(long); break;
|
|
case TLONGLONG: datasize = sizeof(LONGLONG); break;
|
|
case TFLOAT: datasize = sizeof(float); break;
|
|
case TDOUBLE: datasize = sizeof(double); break;
|
|
case TSTRING: datasize = sizeof(char*); break;
|
|
}
|
|
|
|
/* Determine the size of each element of the calculated result */
|
|
/* (only matters for numeric/logical data) */
|
|
|
|
switch( gParse.Nodes[gParse.resultNode].type ) {
|
|
case BOOLEAN: resDataSize = sizeof(char); break;
|
|
case LONG: resDataSize = sizeof(long); break;
|
|
case DOUBLE: resDataSize = sizeof(double); break;
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------*/
|
|
/* Main loop: process all the rows of data */
|
|
/*-------------------------------------------*/
|
|
|
|
/* If writing to output column, set first element to appropriate */
|
|
/* null value. If no NULLs encounter, zero out before returning. */
|
|
if (DEBUG_PIXFILTER)
|
|
printf("parse_data: using null value %ld\n", jnull);
|
|
|
|
|
|
if( userInfo->dataPtr == NULL ) {
|
|
/* First, reset Data pointer to start of output array */
|
|
Data = (char*) outcol->array + datasize;
|
|
|
|
switch( userInfo->datatype ) {
|
|
case TLOGICAL: *(char *)Null = 'U'; break;
|
|
case TBYTE: *(char *)Null = (char )jnull; break;
|
|
case TSHORT: *(short *)Null = (short)jnull; break;
|
|
case TINT: *(int *)Null = (int )jnull; break;
|
|
case TLONG: *(long *)Null = (long )jnull; break;
|
|
case TLONGLONG: *(LONGLONG *)Null = (LONGLONG )jnull; break;
|
|
case TFLOAT: *(float *)Null = FLOATNULLVALUE; break;
|
|
case TDOUBLE: *(double*)Null = DOUBLENULLVALUE; break;
|
|
case TSTRING: (*(char **)Null)[0] = '\1';
|
|
(*(char **)Null)[1] = '\0'; break;
|
|
}
|
|
}
|
|
|
|
/* Alter nrows in case calling routine didn't want to do all rows */
|
|
|
|
nrows = minvalue(nrows,lastRow-firstrow+1);
|
|
|
|
Setup_DataArrays( nCols, colData, firstrow, nrows );
|
|
|
|
/* Parser allocates arrays for each column and calculation it performs. */
|
|
/* Limit number of rows processed during each pass to reduce memory */
|
|
/* requirements... In most cases, iterator will limit rows to less */
|
|
/* than 2500 rows per iteration, so this is really only relevant for */
|
|
/* hk-compressed files which must be decompressed in memory and sent */
|
|
/* whole to parse_data in a single iteration. */
|
|
|
|
remain = nrows;
|
|
while( remain ) {
|
|
ntodo = minvalue(remain,2500);
|
|
Evaluate_Parser ( firstrow, ntodo );
|
|
if( gParse.status ) break;
|
|
|
|
firstrow += ntodo;
|
|
remain -= ntodo;
|
|
|
|
/* Copy results into data array */
|
|
|
|
result = gParse.Nodes + gParse.resultNode;
|
|
if( result->operation==CONST_OP ) constant = 1;
|
|
|
|
switch( result->type ) {
|
|
|
|
case BOOLEAN:
|
|
case LONG:
|
|
case DOUBLE:
|
|
if( constant ) {
|
|
char undef=0;
|
|
for( kk=0; kk<ntodo; kk++ )
|
|
for( jj=0; jj<repeat; jj++ )
|
|
ffcvtn( gParse.datatype,
|
|
&(result->value.data),
|
|
&undef, result->value.nelem /* 1 */,
|
|
userInfo->datatype, Null,
|
|
(char*)Data + (kk*repeat+jj)*datasize,
|
|
&anyNullThisTime, &gParse.status );
|
|
} else {
|
|
if ( repeat == result->value.nelem ) {
|
|
ffcvtn( gParse.datatype,
|
|
result->value.data.ptr,
|
|
result->value.undef,
|
|
result->value.nelem*ntodo,
|
|
userInfo->datatype, Null, Data,
|
|
&anyNullThisTime, &gParse.status );
|
|
} else if( result->value.nelem == 1 ) {
|
|
for( kk=0; kk<ntodo; kk++ )
|
|
for( jj=0; jj<repeat; jj++ ) {
|
|
ffcvtn( gParse.datatype,
|
|
(char*)result->value.data.ptr + kk*resDataSize,
|
|
(char*)result->value.undef + kk,
|
|
1, userInfo->datatype, Null,
|
|
(char*)Data + (kk*repeat+jj)*datasize,
|
|
&anyNullThisTime, &gParse.status );
|
|
}
|
|
} else {
|
|
int nCopy;
|
|
nCopy = minvalue( repeat, result->value.nelem );
|
|
for( kk=0; kk<ntodo; kk++ ) {
|
|
ffcvtn( gParse.datatype,
|
|
(char*)result->value.data.ptr
|
|
+ kk*result->value.nelem*resDataSize,
|
|
(char*)result->value.undef
|
|
+ kk*result->value.nelem,
|
|
nCopy, userInfo->datatype, Null,
|
|
(char*)Data + (kk*repeat)*datasize,
|
|
&anyNullThisTime, &gParse.status );
|
|
if( nCopy < repeat ) {
|
|
memset( (char*)Data + (kk*repeat+nCopy)*datasize,
|
|
0, (repeat-nCopy)*datasize);
|
|
}
|
|
}
|
|
|
|
}
|
|
if( result->operation>0 ) {
|
|
FREE( result->value.data.ptr );
|
|
}
|
|
}
|
|
if( gParse.status==OVERFLOW_ERR ) {
|
|
gParse.status = NUM_OVERFLOW;
|
|
ffpmsg("Numerical overflow while converting expression to necessary datatype");
|
|
}
|
|
break;
|
|
|
|
case BITSTR:
|
|
switch( userInfo->datatype ) {
|
|
case TBYTE:
|
|
idx = -1;
|
|
for( kk=0; kk<ntodo; kk++ ) {
|
|
for( jj=0; jj<result->value.nelem; jj++ ) {
|
|
if( jj%8 == 0 )
|
|
((char*)Data)[++idx] = 0;
|
|
if( constant ) {
|
|
if( result->value.data.str[jj]=='1' )
|
|
((char*)Data)[idx] |= 128>>(jj%8);
|
|
} else {
|
|
if( result->value.data.strptr[kk][jj]=='1' )
|
|
((char*)Data)[idx] |= 128>>(jj%8);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case TBIT:
|
|
case TLOGICAL:
|
|
if( constant ) {
|
|
for( kk=0; kk<ntodo; kk++ )
|
|
for( jj=0; jj<result->value.nelem; jj++ ) {
|
|
((char*)Data)[ jj+kk*result->value.nelem ] =
|
|
( result->value.data.str[jj]=='1' );
|
|
}
|
|
} else {
|
|
for( kk=0; kk<ntodo; kk++ )
|
|
for( jj=0; jj<result->value.nelem; jj++ ) {
|
|
((char*)Data)[ jj+kk*result->value.nelem ] =
|
|
( result->value.data.strptr[kk][jj]=='1' );
|
|
}
|
|
}
|
|
break;
|
|
case TSTRING:
|
|
if( constant ) {
|
|
for( jj=0; jj<ntodo; jj++ ) {
|
|
strcpy( ((char**)Data)[jj], result->value.data.str );
|
|
}
|
|
} else {
|
|
for( jj=0; jj<ntodo; jj++ ) {
|
|
strcpy( ((char**)Data)[jj], result->value.data.strptr[jj] );
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
ffpmsg("Cannot convert bit expression to desired type.");
|
|
gParse.status = PARSE_BAD_TYPE;
|
|
break;
|
|
}
|
|
if( result->operation>0 ) {
|
|
FREE( result->value.data.strptr[0] );
|
|
FREE( result->value.data.strptr );
|
|
}
|
|
break;
|
|
|
|
case STRING:
|
|
if( userInfo->datatype==TSTRING ) {
|
|
if( constant ) {
|
|
for( jj=0; jj<ntodo; jj++ )
|
|
strcpy( ((char**)Data)[jj], result->value.data.str );
|
|
} else {
|
|
for( jj=0; jj<ntodo; jj++ )
|
|
if( result->value.undef[jj] ) {
|
|
anyNullThisTime = 1;
|
|
strcpy( ((char**)Data)[jj],
|
|
*(char **)Null );
|
|
} else {
|
|
strcpy( ((char**)Data)[jj],
|
|
result->value.data.strptr[jj] );
|
|
}
|
|
}
|
|
} else {
|
|
ffpmsg("Cannot convert string expression to desired type.");
|
|
gParse.status = PARSE_BAD_TYPE;
|
|
}
|
|
if( result->operation>0 ) {
|
|
FREE( result->value.data.strptr[0] );
|
|
FREE( result->value.data.strptr );
|
|
}
|
|
break;
|
|
}
|
|
|
|
if( gParse.status ) break;
|
|
|
|
/* Increment Data to point to where the next block should go */
|
|
|
|
if( result->type==BITSTR && userInfo->datatype==TBYTE )
|
|
Data = (char*)Data
|
|
+ datasize * ( (result->value.nelem+7)/8 ) * ntodo;
|
|
else if( result->type==STRING )
|
|
Data = (char*)Data + datasize * ntodo;
|
|
else
|
|
Data = (char*)Data + datasize * ntodo * repeat;
|
|
}
|
|
|
|
/* If no NULLs encountered during this pass, set Null value to */
|
|
/* zero to make the writing of the output column data faster */
|
|
|
|
if( anyNullThisTime )
|
|
userInfo->anyNull = 1;
|
|
else if( userInfo->dataPtr == NULL ) {
|
|
if( userInfo->datatype == TSTRING )
|
|
memcpy( *(char **)Null, zeros, 2 );
|
|
else
|
|
memcpy( Null, zeros, datasize );
|
|
}
|
|
|
|
/*-------------------------------------------------------*/
|
|
/* Clean up procedures: after processing all the rows */
|
|
/*-------------------------------------------------------*/
|
|
|
|
/* if the calling routine specified that only a limited number */
|
|
/* of rows in the table should be processed, return a value of -1 */
|
|
/* once all the rows have been done, if no other error occurred. */
|
|
|
|
if (gParse.hdutype != IMAGE_HDU && firstrow - 1 == lastRow) {
|
|
if (!gParse.status && userInfo->maxRows<totalrows) {
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
return(gParse.status); /* return successful status */
|
|
}
|
|
|
|
static void Setup_DataArrays( int nCols, iteratorCol *cols,
|
|
long fRow, long nRows )
|
|
/***********************************************************************/
|
|
/* Setup the varData array in gParse to contain the fits column data. */
|
|
/* Then, allocate and initialize the necessary UNDEF arrays for each */
|
|
/* column used by the parser. */
|
|
/***********************************************************************/
|
|
{
|
|
int i;
|
|
long nelem, len, row, idx;
|
|
char **bitStrs;
|
|
char **sptr;
|
|
char *barray;
|
|
long *iarray;
|
|
double *rarray;
|
|
char msg[80];
|
|
|
|
gParse.firstDataRow = fRow;
|
|
gParse.nDataRows = nRows;
|
|
|
|
/* Resize and fill in UNDEF arrays for each column */
|
|
|
|
for( i=0; i<nCols; i++ ) {
|
|
|
|
iteratorCol *icol = cols + i;
|
|
DataInfo *varData = gParse.varData + i;
|
|
|
|
if( icol->iotype == OutputCol ) continue;
|
|
|
|
nelem = varData->nelem;
|
|
len = nelem * nRows;
|
|
|
|
switch ( varData->type ) {
|
|
|
|
case BITSTR:
|
|
/* No need for UNDEF array, but must make string DATA array */
|
|
len = (nelem+1)*nRows; /* Count '\0' */
|
|
bitStrs = (char**)varData->data;
|
|
if( bitStrs ) FREE( bitStrs[0] );
|
|
free( bitStrs );
|
|
bitStrs = (char**)malloc( nRows*sizeof(char*) );
|
|
if( bitStrs==NULL ) {
|
|
varData->data = varData->undef = NULL;
|
|
gParse.status = MEMORY_ALLOCATION;
|
|
break;
|
|
}
|
|
bitStrs[0] = (char*)malloc( len*sizeof(char) );
|
|
if( bitStrs[0]==NULL ) {
|
|
free( bitStrs );
|
|
varData->data = varData->undef = NULL;
|
|
gParse.status = MEMORY_ALLOCATION;
|
|
break;
|
|
}
|
|
|
|
for( row=0; row<nRows; row++ ) {
|
|
bitStrs[row] = bitStrs[0] + row*(nelem+1);
|
|
idx = (row)*( (nelem+7)/8 ) + 1;
|
|
for(len=0; len<nelem; len++) {
|
|
if( ((char*)icol->array)[idx] & (1<<(7-len%8)) )
|
|
bitStrs[row][len] = '1';
|
|
else
|
|
bitStrs[row][len] = '0';
|
|
if( len%8==7 ) idx++;
|
|
}
|
|
bitStrs[row][len] = '\0';
|
|
}
|
|
varData->undef = (char*)bitStrs;
|
|
varData->data = (char*)bitStrs;
|
|
break;
|
|
|
|
case STRING:
|
|
sptr = (char**)icol->array;
|
|
if (varData->undef)
|
|
free( varData->undef );
|
|
varData->undef = (char*)malloc( nRows*sizeof(char) );
|
|
if( varData->undef==NULL ) {
|
|
gParse.status = MEMORY_ALLOCATION;
|
|
break;
|
|
}
|
|
row = nRows;
|
|
while( row-- )
|
|
varData->undef[row] =
|
|
( **sptr != '\0' && FSTRCMP( sptr[0], sptr[row+1] )==0 );
|
|
varData->data = sptr + 1;
|
|
break;
|
|
|
|
case BOOLEAN:
|
|
barray = (char*)icol->array;
|
|
if (varData->undef)
|
|
free( varData->undef );
|
|
varData->undef = (char*)malloc( len*sizeof(char) );
|
|
if( varData->undef==NULL ) {
|
|
gParse.status = MEMORY_ALLOCATION;
|
|
break;
|
|
}
|
|
while( len-- ) {
|
|
varData->undef[len] =
|
|
( barray[0]!=0 && barray[0]==barray[len+1] );
|
|
}
|
|
varData->data = barray + 1;
|
|
break;
|
|
|
|
case LONG:
|
|
iarray = (long*)icol->array;
|
|
if (varData->undef)
|
|
free( varData->undef );
|
|
varData->undef = (char*)malloc( len*sizeof(char) );
|
|
if( varData->undef==NULL ) {
|
|
gParse.status = MEMORY_ALLOCATION;
|
|
break;
|
|
}
|
|
while( len-- ) {
|
|
varData->undef[len] =
|
|
( iarray[0]!=0L && iarray[0]==iarray[len+1] );
|
|
}
|
|
varData->data = iarray + 1;
|
|
break;
|
|
|
|
case DOUBLE:
|
|
rarray = (double*)icol->array;
|
|
if (varData->undef)
|
|
free( varData->undef );
|
|
varData->undef = (char*)malloc( len*sizeof(char) );
|
|
if( varData->undef==NULL ) {
|
|
gParse.status = MEMORY_ALLOCATION;
|
|
break;
|
|
}
|
|
while( len-- ) {
|
|
varData->undef[len] =
|
|
( rarray[0]!=0.0 && rarray[0]==rarray[len+1]);
|
|
}
|
|
varData->data = rarray + 1;
|
|
break;
|
|
|
|
default:
|
|
sprintf(msg, "SetupDataArrays, unhandled type %d\n",
|
|
varData->type);
|
|
ffpmsg(msg);
|
|
}
|
|
|
|
if( gParse.status ) { /* Deallocate NULL arrays of previous columns */
|
|
while( i-- ) {
|
|
varData = gParse.varData + i;
|
|
if( varData->type==BITSTR )
|
|
FREE( ((char**)varData->data)[0] );
|
|
FREE( varData->undef );
|
|
varData->undef = NULL;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int ffcvtn( int inputType, /* I - Data type of input array */
|
|
void *input, /* I - Input array of type inputType */
|
|
char *undef, /* I - Array of flags indicating UNDEF elems */
|
|
long ntodo, /* I - Number of elements to process */
|
|
int outputType, /* I - Data type of output array */
|
|
void *nulval, /* I - Ptr to value to use for UNDEF elements */
|
|
void *output, /* O - Output array of type outputType */
|
|
int *anynull, /* O - Any nulls flagged? */
|
|
int *status ) /* O - Error status */
|
|
/* */
|
|
/* Convert an array of any input data type to an array of any output */
|
|
/* data type, using an array of UNDEF flags to assign nulvals to */
|
|
/*--------------------------------------------------------------------------*/
|
|
{
|
|
long i;
|
|
|
|
switch( outputType ) {
|
|
|
|
case TLOGICAL:
|
|
switch( inputType ) {
|
|
case TLOGICAL:
|
|
case TBYTE:
|
|
for( i=0; i<ntodo; i++ )
|
|
if( ((unsigned char*)input)[i] )
|
|
((unsigned char*)output)[i] = 1;
|
|
else
|
|
((unsigned char*)output)[i] = 0;
|
|
break;
|
|
case TSHORT:
|
|
for( i=0; i<ntodo; i++ )
|
|
if( ((short*)input)[i] )
|
|
((unsigned char*)output)[i] = 1;
|
|
else
|
|
((unsigned char*)output)[i] = 0;
|
|
break;
|
|
case TLONG:
|
|
for( i=0; i<ntodo; i++ )
|
|
if( ((long*)input)[i] )
|
|
((unsigned char*)output)[i] = 1;
|
|
else
|
|
((unsigned char*)output)[i] = 0;
|
|
break;
|
|
case TFLOAT:
|
|
for( i=0; i<ntodo; i++ )
|
|
if( ((float*)input)[i] )
|
|
((unsigned char*)output)[i] = 1;
|
|
else
|
|
((unsigned char*)output)[i] = 0;
|
|
break;
|
|
case TDOUBLE:
|
|
for( i=0; i<ntodo; i++ )
|
|
if( ((double*)input)[i] )
|
|
((unsigned char*)output)[i] = 1;
|
|
else
|
|
((unsigned char*)output)[i] = 0;
|
|
break;
|
|
default:
|
|
*status = BAD_DATATYPE;
|
|
break;
|
|
}
|
|
for(i=0;i<ntodo;i++) {
|
|
if( undef[i] ) {
|
|
((unsigned char*)output)[i] = *(unsigned char*)nulval;
|
|
*anynull = 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TBYTE:
|
|
switch( inputType ) {
|
|
case TLOGICAL:
|
|
case TBYTE:
|
|
for( i=0; i<ntodo; i++ )
|
|
((unsigned char*)output)[i] = ((unsigned char*)input)[i];
|
|
break;
|
|
case TSHORT:
|
|
fffi2i1((short*)input,ntodo,1.,0.,0,0,0,NULL,NULL,(unsigned char*)output,status);
|
|
break;
|
|
case TLONG:
|
|
for (i = 0; i < ntodo; i++) {
|
|
if( undef[i] ) {
|
|
((unsigned char*)output)[i] = *(unsigned char*)nulval;
|
|
*anynull = 1;
|
|
} else {
|
|
if( ((long*)input)[i] < 0 ) {
|
|
*status = OVERFLOW_ERR;
|
|
((unsigned char*)output)[i] = 0;
|
|
} else if( ((long*)input)[i] > UCHAR_MAX ) {
|
|
*status = OVERFLOW_ERR;
|
|
((unsigned char*)output)[i] = UCHAR_MAX;
|
|
} else
|
|
((unsigned char*)output)[i] =
|
|
(unsigned char) ((long*)input)[i];
|
|
}
|
|
}
|
|
return( *status );
|
|
case TFLOAT:
|
|
fffr4i1((float*)input,ntodo,1.,0.,0,0,NULL,NULL,
|
|
(unsigned char*)output,status);
|
|
break;
|
|
case TDOUBLE:
|
|
fffr8i1((double*)input,ntodo,1.,0.,0,0,NULL,NULL,
|
|
(unsigned char*)output,status);
|
|
break;
|
|
default:
|
|
*status = BAD_DATATYPE;
|
|
break;
|
|
}
|
|
for(i=0;i<ntodo;i++) {
|
|
if( undef[i] ) {
|
|
((unsigned char*)output)[i] = *(unsigned char*)nulval;
|
|
*anynull = 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TSHORT:
|
|
switch( inputType ) {
|
|
case TLOGICAL:
|
|
case TBYTE:
|
|
for( i=0; i<ntodo; i++ )
|
|
((short*)output)[i] = ((unsigned char*)input)[i];
|
|
break;
|
|
case TSHORT:
|
|
for( i=0; i<ntodo; i++ )
|
|
((short*)output)[i] = ((short*)input)[i];
|
|
break;
|
|
case TLONG:
|
|
for (i = 0; i < ntodo; i++) {
|
|
if( undef[i] ) {
|
|
((short*)output)[i] = *(short*)nulval;
|
|
*anynull = 1;
|
|
} else {
|
|
if( ((long*)input)[i] < SHRT_MIN ) {
|
|
*status = OVERFLOW_ERR;
|
|
((short*)output)[i] = SHRT_MIN;
|
|
} else if ( ((long*)input)[i] > SHRT_MAX ) {
|
|
*status = OVERFLOW_ERR;
|
|
((short*)output)[i] = SHRT_MAX;
|
|
} else
|
|
((short*)output)[i] = (short) ((long*)input)[i];
|
|
}
|
|
}
|
|
return( *status );
|
|
case TFLOAT:
|
|
fffr4i2((float*)input,ntodo,1.,0.,0,0,NULL,NULL,
|
|
(short*)output,status);
|
|
break;
|
|
case TDOUBLE:
|
|
fffr8i2((double*)input,ntodo,1.,0.,0,0,NULL,NULL,
|
|
(short*)output,status);
|
|
break;
|
|
default:
|
|
*status = BAD_DATATYPE;
|
|
break;
|
|
}
|
|
for(i=0;i<ntodo;i++) {
|
|
if( undef[i] ) {
|
|
((short*)output)[i] = *(short*)nulval;
|
|
*anynull = 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TINT:
|
|
switch( inputType ) {
|
|
case TLOGICAL:
|
|
case TBYTE:
|
|
for( i=0; i<ntodo; i++ )
|
|
((int*)output)[i] = ((unsigned char*)input)[i];
|
|
break;
|
|
case TSHORT:
|
|
for( i=0; i<ntodo; i++ )
|
|
((int*)output)[i] = ((short*)input)[i];
|
|
break;
|
|
case TLONG:
|
|
for( i=0; i<ntodo; i++ )
|
|
((int*)output)[i] = ((long*)input)[i];
|
|
break;
|
|
case TFLOAT:
|
|
fffr4int((float*)input,ntodo,1.,0.,0,0,NULL,NULL,
|
|
(int*)output,status);
|
|
break;
|
|
case TDOUBLE:
|
|
fffr8int((double*)input,ntodo,1.,0.,0,0,NULL,NULL,
|
|
(int*)output,status);
|
|
break;
|
|
default:
|
|
*status = BAD_DATATYPE;
|
|
break;
|
|
}
|
|
for(i=0;i<ntodo;i++) {
|
|
if( undef[i] ) {
|
|
((int*)output)[i] = *(int*)nulval;
|
|
*anynull = 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TLONG:
|
|
switch( inputType ) {
|
|
case TLOGICAL:
|
|
case TBYTE:
|
|
for( i=0; i<ntodo; i++ )
|
|
((long*)output)[i] = ((unsigned char*)input)[i];
|
|
break;
|
|
case TSHORT:
|
|
for( i=0; i<ntodo; i++ )
|
|
((long*)output)[i] = ((short*)input)[i];
|
|
break;
|
|
case TLONG:
|
|
for( i=0; i<ntodo; i++ )
|
|
((long*)output)[i] = ((long*)input)[i];
|
|
break;
|
|
case TFLOAT:
|
|
fffr4i4((float*)input,ntodo,1.,0.,0,0,NULL,NULL,
|
|
(long*)output,status);
|
|
break;
|
|
case TDOUBLE:
|
|
fffr8i4((double*)input,ntodo,1.,0.,0,0,NULL,NULL,
|
|
(long*)output,status);
|
|
break;
|
|
default:
|
|
*status = BAD_DATATYPE;
|
|
break;
|
|
}
|
|
for(i=0;i<ntodo;i++) {
|
|
if( undef[i] ) {
|
|
((long*)output)[i] = *(long*)nulval;
|
|
*anynull = 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TLONGLONG:
|
|
switch( inputType ) {
|
|
case TLOGICAL:
|
|
case TBYTE:
|
|
for( i=0; i<ntodo; i++ )
|
|
((LONGLONG*)output)[i] = ((unsigned char*)input)[i];
|
|
break;
|
|
case TSHORT:
|
|
for( i=0; i<ntodo; i++ )
|
|
((LONGLONG*)output)[i] = ((short*)input)[i];
|
|
break;
|
|
case TLONG:
|
|
for( i=0; i<ntodo; i++ )
|
|
((LONGLONG*)output)[i] = ((long*)input)[i];
|
|
break;
|
|
case TFLOAT:
|
|
fffr4i8((float*)input,ntodo,1.,0.,0,0,NULL,NULL,
|
|
(LONGLONG*)output,status);
|
|
break;
|
|
case TDOUBLE:
|
|
fffr8i8((double*)input,ntodo,1.,0.,0,0,NULL,NULL,
|
|
(LONGLONG*)output,status);
|
|
|
|
break;
|
|
default:
|
|
*status = BAD_DATATYPE;
|
|
break;
|
|
}
|
|
for(i=0;i<ntodo;i++) {
|
|
if( undef[i] ) {
|
|
((LONGLONG*)output)[i] = *(LONGLONG*)nulval;
|
|
*anynull = 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TFLOAT:
|
|
switch( inputType ) {
|
|
case TLOGICAL:
|
|
case TBYTE:
|
|
for( i=0; i<ntodo; i++ )
|
|
((float*)output)[i] = ((unsigned char*)input)[i];
|
|
break;
|
|
case TSHORT:
|
|
for( i=0; i<ntodo; i++ )
|
|
((float*)output)[i] = ((short*)input)[i];
|
|
break;
|
|
case TLONG:
|
|
for( i=0; i<ntodo; i++ )
|
|
((float*)output)[i] = (float) ((long*)input)[i];
|
|
break;
|
|
case TFLOAT:
|
|
for( i=0; i<ntodo; i++ )
|
|
((float*)output)[i] = ((float*)input)[i];
|
|
break;
|
|
case TDOUBLE:
|
|
fffr8r4((double*)input,ntodo,1.,0.,0,0,NULL,NULL,
|
|
(float*)output,status);
|
|
break;
|
|
default:
|
|
*status = BAD_DATATYPE;
|
|
break;
|
|
}
|
|
for(i=0;i<ntodo;i++) {
|
|
if( undef[i] ) {
|
|
((float*)output)[i] = *(float*)nulval;
|
|
*anynull = 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TDOUBLE:
|
|
switch( inputType ) {
|
|
case TLOGICAL:
|
|
case TBYTE:
|
|
for( i=0; i<ntodo; i++ )
|
|
((double*)output)[i] = ((unsigned char*)input)[i];
|
|
break;
|
|
case TSHORT:
|
|
for( i=0; i<ntodo; i++ )
|
|
((double*)output)[i] = ((short*)input)[i];
|
|
break;
|
|
case TLONG:
|
|
for( i=0; i<ntodo; i++ )
|
|
((double*)output)[i] = ((long*)input)[i];
|
|
break;
|
|
case TFLOAT:
|
|
for( i=0; i<ntodo; i++ )
|
|
((double*)output)[i] = ((float*)input)[i];
|
|
break;
|
|
case TDOUBLE:
|
|
for( i=0; i<ntodo; i++ )
|
|
((double*)output)[i] = ((double*)input)[i];
|
|
break;
|
|
default:
|
|
*status = BAD_DATATYPE;
|
|
break;
|
|
}
|
|
for(i=0;i<ntodo;i++) {
|
|
if( undef[i] ) {
|
|
((double*)output)[i] = *(double*)nulval;
|
|
*anynull = 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
*status = BAD_DATATYPE;
|
|
break;
|
|
}
|
|
|
|
return ( *status );
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
int fffrwc( fitsfile *fptr, /* I - Input FITS file */
|
|
char *expr, /* I - Boolean expression */
|
|
char *timeCol, /* I - Name of time column */
|
|
char *parCol, /* I - Name of parameter column */
|
|
char *valCol, /* I - Name of value column */
|
|
long ntimes, /* I - Number of distinct times in file */
|
|
double *times, /* O - Array of times in file */
|
|
char *time_status, /* O - Array of boolean results */
|
|
int *status ) /* O - Error status */
|
|
/* */
|
|
/* Evaluate a boolean expression for each time in a compressed file, */
|
|
/* returning an array of flags indicating which times evaluated to TRUE/FALSE*/
|
|
/*---------------------------------------------------------------------------*/
|
|
{
|
|
parseInfo Info;
|
|
long alen, width;
|
|
int parNo, typecode;
|
|
int naxis, constant, nCol=0;
|
|
long nelem, naxes[MAXDIMS], elem;
|
|
char result;
|
|
|
|
if( *status ) return( *status );
|
|
|
|
fits_get_colnum( fptr, CASEINSEN, timeCol, &gParse.timeCol, status );
|
|
fits_get_colnum( fptr, CASEINSEN, parCol, &gParse.parCol , status );
|
|
fits_get_colnum( fptr, CASEINSEN, valCol, &gParse.valCol, status );
|
|
if( *status ) return( *status );
|
|
|
|
if( ffiprs( fptr, 1, expr, MAXDIMS, &Info.datatype, &nelem,
|
|
&naxis, naxes, status ) ) {
|
|
ffcprs();
|
|
return( *status );
|
|
}
|
|
if( nelem<0 ) {
|
|
constant = 1;
|
|
nelem = -nelem;
|
|
nCol = gParse.nCols;
|
|
gParse.nCols = 0; /* Ignore all column references */
|
|
} else
|
|
constant = 0;
|
|
|
|
if( Info.datatype!=TLOGICAL || nelem!=1 ) {
|
|
ffcprs();
|
|
ffpmsg("Expression does not evaluate to a logical scalar.");
|
|
return( *status = PARSE_BAD_TYPE );
|
|
}
|
|
|
|
/*******************************************/
|
|
/* Allocate data arrays for each parameter */
|
|
/*******************************************/
|
|
|
|
parNo = gParse.nCols;
|
|
while( parNo-- ) {
|
|
switch( gParse.colData[parNo].datatype ) {
|
|
case TLONG:
|
|
if( (gParse.colData[parNo].array =
|
|
(long *)malloc( (ntimes+1)*sizeof(long) )) )
|
|
((long*)gParse.colData[parNo].array)[0] = 1234554321;
|
|
else
|
|
*status = MEMORY_ALLOCATION;
|
|
break;
|
|
case TDOUBLE:
|
|
if( (gParse.colData[parNo].array =
|
|
(double *)malloc( (ntimes+1)*sizeof(double) )) )
|
|
((double*)gParse.colData[parNo].array)[0] = DOUBLENULLVALUE;
|
|
else
|
|
*status = MEMORY_ALLOCATION;
|
|
break;
|
|
case TSTRING:
|
|
if( !fits_get_coltype( fptr, gParse.valCol, &typecode,
|
|
&alen, &width, status ) ) {
|
|
alen++;
|
|
if( (gParse.colData[parNo].array =
|
|
(char **)malloc( (ntimes+1)*sizeof(char*) )) ) {
|
|
if( (((char **)gParse.colData[parNo].array)[0] =
|
|
(char *)malloc( (ntimes+1)*sizeof(char)*alen )) ) {
|
|
for( elem=1; elem<=ntimes; elem++ )
|
|
((char **)gParse.colData[parNo].array)[elem] =
|
|
((char **)gParse.colData[parNo].array)[elem-1]+alen;
|
|
((char **)gParse.colData[parNo].array)[0][0] = '\0';
|
|
} else {
|
|
free( gParse.colData[parNo].array );
|
|
*status = MEMORY_ALLOCATION;
|
|
}
|
|
} else {
|
|
*status = MEMORY_ALLOCATION;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if( *status ) {
|
|
while( parNo-- ) {
|
|
if( gParse.colData[parNo].datatype==TSTRING )
|
|
FREE( ((char **)gParse.colData[parNo].array)[0] );
|
|
FREE( gParse.colData[parNo].array );
|
|
}
|
|
return( *status );
|
|
}
|
|
}
|
|
|
|
/**********************************************************************/
|
|
/* Read data from columns needed for the expression and then parse it */
|
|
/**********************************************************************/
|
|
|
|
if( !uncompress_hkdata( fptr, ntimes, times, status ) ) {
|
|
if( constant ) {
|
|
result = gParse.Nodes[gParse.resultNode].value.data.log;
|
|
elem = ntimes;
|
|
while( elem-- ) time_status[elem] = result;
|
|
} else {
|
|
Info.dataPtr = time_status;
|
|
Info.nullPtr = NULL;
|
|
Info.maxRows = ntimes;
|
|
*status = parse_data( ntimes, 0, 1, ntimes, gParse.nCols,
|
|
gParse.colData, (void*)&Info );
|
|
}
|
|
}
|
|
|
|
/************/
|
|
/* Clean up */
|
|
/************/
|
|
|
|
parNo = gParse.nCols;
|
|
while ( parNo-- ) {
|
|
if( gParse.colData[parNo].datatype==TSTRING )
|
|
FREE( ((char **)gParse.colData[parNo].array)[0] );
|
|
FREE( gParse.colData[parNo].array );
|
|
}
|
|
|
|
if( constant ) gParse.nCols = nCol;
|
|
|
|
ffcprs();
|
|
return(*status);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
int uncompress_hkdata( fitsfile *fptr,
|
|
long ntimes,
|
|
double *times,
|
|
int *status )
|
|
/* */
|
|
/* description */
|
|
/*---------------------------------------------------------------------------*/
|
|
{
|
|
char parName[256], *sPtr[1], found[1000];
|
|
int parNo, anynul;
|
|
long naxis2, row, currelem;
|
|
double currtime, newtime;
|
|
|
|
sPtr[0] = parName;
|
|
currelem = 0;
|
|
currtime = -1e38;
|
|
|
|
parNo=gParse.nCols;
|
|
while( parNo-- ) found[parNo] = 0;
|
|
|
|
if( ffgkyj( fptr, "NAXIS2", &naxis2, NULL, status ) ) return( *status );
|
|
|
|
for( row=1; row<=naxis2; row++ ) {
|
|
if( ffgcvd( fptr, gParse.timeCol, row, 1L, 1L, 0.0,
|
|
&newtime, &anynul, status ) ) return( *status );
|
|
if( newtime != currtime ) {
|
|
/* New time encountered... propogate parameters to next row */
|
|
if( currelem==ntimes ) {
|
|
ffpmsg("Found more unique time stamps than caller indicated");
|
|
return( *status = PARSE_BAD_COL );
|
|
}
|
|
times[currelem++] = currtime = newtime;
|
|
parNo = gParse.nCols;
|
|
while( parNo-- ) {
|
|
switch( gParse.colData[parNo].datatype ) {
|
|
case TLONG:
|
|
((long*)gParse.colData[parNo].array)[currelem] =
|
|
((long*)gParse.colData[parNo].array)[currelem-1];
|
|
break;
|
|
case TDOUBLE:
|
|
((double*)gParse.colData[parNo].array)[currelem] =
|
|
((double*)gParse.colData[parNo].array)[currelem-1];
|
|
break;
|
|
case TSTRING:
|
|
strcpy( ((char **)gParse.colData[parNo].array)[currelem],
|
|
((char **)gParse.colData[parNo].array)[currelem-1] );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( ffgcvs( fptr, gParse.parCol, row, 1L, 1L, "",
|
|
sPtr, &anynul, status ) ) return( *status );
|
|
parNo = gParse.nCols;
|
|
while( parNo-- )
|
|
if( !strcasecmp( parName, gParse.varData[parNo].name ) ) break;
|
|
|
|
if( parNo>=0 ) {
|
|
found[parNo] = 1; /* Flag this parameter as found */
|
|
switch( gParse.colData[parNo].datatype ) {
|
|
case TLONG:
|
|
ffgcvj( fptr, gParse.valCol, row, 1L, 1L,
|
|
((long*)gParse.colData[parNo].array)[0],
|
|
((long*)gParse.colData[parNo].array)+currelem,
|
|
&anynul, status );
|
|
break;
|
|
case TDOUBLE:
|
|
ffgcvd( fptr, gParse.valCol, row, 1L, 1L,
|
|
((double*)gParse.colData[parNo].array)[0],
|
|
((double*)gParse.colData[parNo].array)+currelem,
|
|
&anynul, status );
|
|
break;
|
|
case TSTRING:
|
|
ffgcvs( fptr, gParse.valCol, row, 1L, 1L,
|
|
((char**)gParse.colData[parNo].array)[0],
|
|
((char**)gParse.colData[parNo].array)+currelem,
|
|
&anynul, status );
|
|
break;
|
|
}
|
|
if( *status ) return( *status );
|
|
}
|
|
}
|
|
|
|
if( currelem<ntimes ) {
|
|
ffpmsg("Found fewer unique time stamps than caller indicated");
|
|
return( *status = PARSE_BAD_COL );
|
|
}
|
|
|
|
/* Check for any parameters which were not located in the table */
|
|
parNo = gParse.nCols;
|
|
while( parNo-- )
|
|
if( !found[parNo] ) {
|
|
sprintf( parName, "Parameter not found: %-30s",
|
|
gParse.varData[parNo].name );
|
|
ffpmsg( parName );
|
|
*status = PARSE_SYNTAX_ERR;
|
|
}
|
|
return( *status );
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
int ffffrw( fitsfile *fptr, /* I - Input FITS file */
|
|
char *expr, /* I - Boolean expression */
|
|
long *rownum, /* O - First row of table to eval to T */
|
|
int *status ) /* O - Error status */
|
|
/* */
|
|
/* Evaluate a boolean expression, returning the row number of the first */
|
|
/* row which evaluates to TRUE */
|
|
/*---------------------------------------------------------------------------*/
|
|
{
|
|
int naxis, constant, dtype;
|
|
long nelem, naxes[MAXDIMS];
|
|
char result;
|
|
|
|
if( *status ) return( *status );
|
|
|
|
FFLOCK;
|
|
if( ffiprs( fptr, 0, expr, MAXDIMS, &dtype, &nelem, &naxis,
|
|
naxes, status ) ) {
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return( *status );
|
|
}
|
|
if( nelem<0 ) {
|
|
constant = 1;
|
|
nelem = -nelem;
|
|
} else
|
|
constant = 0;
|
|
|
|
if( dtype!=TLOGICAL || nelem!=1 ) {
|
|
ffcprs();
|
|
ffpmsg("Expression does not evaluate to a logical scalar.");
|
|
FFUNLOCK;
|
|
return( *status = PARSE_BAD_TYPE );
|
|
}
|
|
|
|
*rownum = 0;
|
|
if( constant ) { /* No need to call parser... have result from ffiprs */
|
|
result = gParse.Nodes[gParse.resultNode].value.data.log;
|
|
if( result ) {
|
|
/* Make sure there is at least 1 row in table */
|
|
ffgnrw( fptr, &nelem, status );
|
|
if( nelem )
|
|
*rownum = 1;
|
|
}
|
|
} else {
|
|
if( ffiter( gParse.nCols, gParse.colData, 0, 0,
|
|
ffffrw_work, (void*)rownum, status ) == -1 )
|
|
*status = 0; /* -1 indicates exitted without error before end... OK */
|
|
}
|
|
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return(*status);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
int ffffrw_work(long totalrows, /* I - Total rows to be processed */
|
|
long offset, /* I - Number of rows skipped at start*/
|
|
long firstrow, /* I - First row of this iteration */
|
|
long nrows, /* I - Number of rows in this iter */
|
|
int nCols, /* I - Number of columns in use */
|
|
iteratorCol *colData, /* IO- Column information/data */
|
|
void *userPtr ) /* I - Data handling instructions */
|
|
/* */
|
|
/* Iterator work function which calls the parser and searches for the */
|
|
/* first row which evaluates to TRUE. */
|
|
/*---------------------------------------------------------------------------*/
|
|
{
|
|
long idx;
|
|
Node *result;
|
|
|
|
Evaluate_Parser( firstrow, nrows );
|
|
|
|
if( !gParse.status ) {
|
|
|
|
result = gParse.Nodes + gParse.resultNode;
|
|
if( result->operation==CONST_OP ) {
|
|
|
|
if( result->value.data.log ) {
|
|
*(long*)userPtr = firstrow;
|
|
return( -1 );
|
|
}
|
|
|
|
} else {
|
|
|
|
for( idx=0; idx<nrows; idx++ )
|
|
if( result->value.data.logptr[idx] && !result->value.undef[idx] ) {
|
|
*(long*)userPtr = firstrow + idx;
|
|
return( -1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( gParse.status );
|
|
}
|
|
|
|
|
|
static int set_image_col_types (fitsfile * fptr, const char * name, int bitpix,
|
|
DataInfo * varInfo, iteratorCol *colIter) {
|
|
|
|
int istatus;
|
|
double tscale, tzero;
|
|
char temp[80];
|
|
|
|
switch (bitpix) {
|
|
case BYTE_IMG:
|
|
case SHORT_IMG:
|
|
case LONG_IMG:
|
|
istatus = 0;
|
|
if (fits_read_key(fptr, TDOUBLE, "BZERO", &tzero, NULL, &istatus))
|
|
tzero = 0.0;
|
|
|
|
istatus = 0;
|
|
if (fits_read_key(fptr, TDOUBLE, "BSCALE", &tscale, NULL, &istatus))
|
|
tscale = 1.0;
|
|
|
|
if (tscale == 1.0 && (tzero == 0.0 || tzero == 32768.0 )) {
|
|
varInfo->type = LONG;
|
|
colIter->datatype = TLONG;
|
|
}
|
|
else {
|
|
varInfo->type = DOUBLE;
|
|
colIter->datatype = TDOUBLE;
|
|
if (DEBUG_PIXFILTER)
|
|
printf("use DOUBLE for %s with BSCALE=%g/BZERO=%g\n",
|
|
name, tscale, tzero);
|
|
}
|
|
break;
|
|
|
|
case LONGLONG_IMG:
|
|
case FLOAT_IMG:
|
|
case DOUBLE_IMG:
|
|
varInfo->type = DOUBLE;
|
|
colIter->datatype = TDOUBLE;
|
|
break;
|
|
default:
|
|
sprintf(temp, "set_image_col_types: unrecognized image bitpix [%d]\n",
|
|
bitpix);
|
|
ffpmsg(temp);
|
|
return gParse.status = PARSE_BAD_TYPE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
|
|
Functions used by the evaluator to access FITS data
|
|
(find_column, find_keywd, allocateCol, load_column)
|
|
|
|
*************************************************************************/
|
|
|
|
static int find_column( char *colName, void *itslval )
|
|
{
|
|
FFSTYPE *thelval = (FFSTYPE*)itslval;
|
|
int col_cnt, status;
|
|
int colnum, typecode, type;
|
|
long repeat, width;
|
|
fitsfile *fptr;
|
|
char temp[80];
|
|
double tzero,tscale;
|
|
int istatus;
|
|
DataInfo *varInfo;
|
|
iteratorCol *colIter;
|
|
|
|
if (DEBUG_PIXFILTER)
|
|
printf("find_column(%s)\n", colName);
|
|
|
|
if( *colName == '#' )
|
|
return( find_keywd( colName + 1, itslval ) );
|
|
|
|
fptr = gParse.def_fptr;
|
|
|
|
status = 0;
|
|
col_cnt = gParse.nCols;
|
|
|
|
if (gParse.hdutype == IMAGE_HDU) {
|
|
int i;
|
|
if (!gParse.pixFilter) {
|
|
gParse.status = COL_NOT_FOUND;
|
|
ffpmsg("find_column: IMAGE_HDU but no PixelFilter");
|
|
return pERROR;
|
|
}
|
|
|
|
colnum = -1;
|
|
for (i = 0; i < gParse.pixFilter->count; ++i) {
|
|
if (!strcasecmp(colName, gParse.pixFilter->tag[i]))
|
|
colnum = i;
|
|
}
|
|
if (colnum < 0) {
|
|
sprintf(temp, "find_column: PixelFilter tag %s not found", colName);
|
|
ffpmsg(temp);
|
|
gParse.status = COL_NOT_FOUND;
|
|
return pERROR;
|
|
}
|
|
|
|
if( allocateCol( col_cnt, &gParse.status ) ) return pERROR;
|
|
|
|
varInfo = gParse.varData + col_cnt;
|
|
colIter = gParse.colData + col_cnt;
|
|
|
|
fptr = gParse.pixFilter->ifptr[colnum];
|
|
fits_get_img_param(fptr,
|
|
MAXDIMS,
|
|
&typecode, /* actually bitpix */
|
|
&varInfo->naxis,
|
|
&varInfo->naxes[0],
|
|
&status);
|
|
varInfo->nelem = 1;
|
|
type = COLUMN;
|
|
if (set_image_col_types(fptr, colName, typecode, varInfo, colIter))
|
|
return pERROR;
|
|
colIter->fptr = fptr;
|
|
colIter->iotype = InputCol;
|
|
}
|
|
else { /* HDU holds a table */
|
|
if( gParse.compressed )
|
|
colnum = gParse.valCol;
|
|
else
|
|
if( fits_get_colnum( fptr, CASEINSEN, colName, &colnum, &status ) ) {
|
|
if( status == COL_NOT_FOUND ) {
|
|
type = find_keywd( colName, itslval );
|
|
if( type != pERROR ) ffcmsg();
|
|
return( type );
|
|
}
|
|
gParse.status = status;
|
|
return pERROR;
|
|
}
|
|
|
|
if( fits_get_coltype( fptr, colnum, &typecode,
|
|
&repeat, &width, &status ) ) {
|
|
gParse.status = status;
|
|
return pERROR;
|
|
}
|
|
|
|
if( allocateCol( col_cnt, &gParse.status ) ) return pERROR;
|
|
|
|
varInfo = gParse.varData + col_cnt;
|
|
colIter = gParse.colData + col_cnt;
|
|
|
|
fits_iter_set_by_num( colIter, fptr, colnum, 0, InputCol );
|
|
}
|
|
|
|
/* Make sure we don't overflow variable name array */
|
|
strncpy(varInfo->name,colName,MAXVARNAME);
|
|
varInfo->name[MAXVARNAME] = '\0';
|
|
|
|
if (gParse.hdutype != IMAGE_HDU) {
|
|
switch( typecode ) {
|
|
case TBIT:
|
|
varInfo->type = BITSTR;
|
|
colIter->datatype = TBYTE;
|
|
type = BITCOL;
|
|
break;
|
|
case TBYTE:
|
|
case TSHORT:
|
|
case TLONG:
|
|
/* The datatype of column with TZERO and TSCALE keywords might be
|
|
float or double.
|
|
*/
|
|
sprintf(temp,"TZERO%d",colnum);
|
|
istatus = 0;
|
|
if(fits_read_key(fptr,TDOUBLE,temp,&tzero,NULL,&istatus)) {
|
|
tzero = 0.0;
|
|
}
|
|
sprintf(temp,"TSCAL%d",colnum);
|
|
istatus = 0;
|
|
if(fits_read_key(fptr,TDOUBLE,temp,&tscale,NULL,&istatus)) {
|
|
tscale = 1.0;
|
|
}
|
|
if (tscale == 1.0 && (tzero == 0.0 || tzero == 32768.0 )) {
|
|
varInfo->type = LONG;
|
|
colIter->datatype = TLONG;
|
|
/* Reading an unsigned long column as a long can cause overflow errors.
|
|
Treat the column as a double instead.
|
|
} else if (tscale == 1.0 && tzero == 2147483648.0 ) {
|
|
varInfo->type = LONG;
|
|
colIter->datatype = TULONG;
|
|
*/
|
|
|
|
}
|
|
else {
|
|
varInfo->type = DOUBLE;
|
|
colIter->datatype = TDOUBLE;
|
|
}
|
|
type = COLUMN;
|
|
break;
|
|
/*
|
|
For now, treat 8-byte integer columns as type double.
|
|
This can lose precision, so the better long term solution
|
|
will be to add support for TLONGLONG as a separate datatype.
|
|
*/
|
|
case TLONGLONG:
|
|
case TFLOAT:
|
|
case TDOUBLE:
|
|
varInfo->type = DOUBLE;
|
|
colIter->datatype = TDOUBLE;
|
|
type = COLUMN;
|
|
break;
|
|
case TLOGICAL:
|
|
varInfo->type = BOOLEAN;
|
|
colIter->datatype = TLOGICAL;
|
|
type = BCOLUMN;
|
|
break;
|
|
case TSTRING:
|
|
varInfo->type = STRING;
|
|
colIter->datatype = TSTRING;
|
|
type = SCOLUMN;
|
|
if ( width >= MAX_STRLEN ) {
|
|
sprintf(temp, "column %d is wider than maximum %d characters",
|
|
colnum, MAX_STRLEN-1);
|
|
ffpmsg(temp);
|
|
gParse.status = PARSE_LRG_VECTOR;
|
|
return pERROR;
|
|
}
|
|
if( gParse.hdutype == ASCII_TBL ) repeat = width;
|
|
break;
|
|
default:
|
|
if (typecode < 0) {
|
|
sprintf(temp, "variable-length array columns are not supported. typecode = %d", typecode);
|
|
ffpmsg(temp);
|
|
}
|
|
gParse.status = PARSE_BAD_TYPE;
|
|
return pERROR;
|
|
}
|
|
varInfo->nelem = repeat;
|
|
if( repeat>1 && typecode!=TSTRING ) {
|
|
if( fits_read_tdim( fptr, colnum, MAXDIMS,
|
|
&varInfo->naxis,
|
|
&varInfo->naxes[0], &status )
|
|
) {
|
|
gParse.status = status;
|
|
return pERROR;
|
|
}
|
|
} else {
|
|
varInfo->naxis = 1;
|
|
varInfo->naxes[0] = 1;
|
|
}
|
|
}
|
|
gParse.nCols++;
|
|
thelval->lng = col_cnt;
|
|
|
|
return( type );
|
|
}
|
|
|
|
static int find_keywd(char *keyname, void *itslval )
|
|
{
|
|
FFSTYPE *thelval = (FFSTYPE*)itslval;
|
|
int status, type;
|
|
char keyvalue[FLEN_VALUE], dtype;
|
|
fitsfile *fptr;
|
|
double rval;
|
|
int bval;
|
|
long ival;
|
|
|
|
status = 0;
|
|
fptr = gParse.def_fptr;
|
|
if( fits_read_keyword( fptr, keyname, keyvalue, NULL, &status ) ) {
|
|
if( status == KEY_NO_EXIST ) {
|
|
/* Do this since ffgkey doesn't put an error message on stack */
|
|
sprintf(keyvalue, "ffgkey could not find keyword: %s",keyname);
|
|
ffpmsg(keyvalue);
|
|
}
|
|
gParse.status = status;
|
|
return( pERROR );
|
|
}
|
|
|
|
if( fits_get_keytype( keyvalue, &dtype, &status ) ) {
|
|
gParse.status = status;
|
|
return( pERROR );
|
|
}
|
|
|
|
switch( dtype ) {
|
|
case 'C':
|
|
fits_read_key_str( fptr, keyname, keyvalue, NULL, &status );
|
|
type = STRING;
|
|
strcpy( thelval->str , keyvalue );
|
|
break;
|
|
case 'L':
|
|
fits_read_key_log( fptr, keyname, &bval, NULL, &status );
|
|
type = BOOLEAN;
|
|
thelval->log = bval;
|
|
break;
|
|
case 'I':
|
|
fits_read_key_lng( fptr, keyname, &ival, NULL, &status );
|
|
type = LONG;
|
|
thelval->lng = ival;
|
|
break;
|
|
case 'F':
|
|
fits_read_key_dbl( fptr, keyname, &rval, NULL, &status );
|
|
type = DOUBLE;
|
|
thelval->dbl = rval;
|
|
break;
|
|
default:
|
|
type = pERROR;
|
|
break;
|
|
}
|
|
|
|
if( status ) {
|
|
gParse.status=status;
|
|
return pERROR;
|
|
}
|
|
|
|
return( type );
|
|
}
|
|
|
|
static int allocateCol( int nCol, int *status )
|
|
{
|
|
if( (nCol%25)==0 ) {
|
|
if( nCol ) {
|
|
gParse.colData = (iteratorCol*) realloc( gParse.colData,
|
|
(nCol+25)*sizeof(iteratorCol) );
|
|
gParse.varData = (DataInfo *) realloc( gParse.varData,
|
|
(nCol+25)*sizeof(DataInfo) );
|
|
} else {
|
|
gParse.colData = (iteratorCol*) malloc( 25*sizeof(iteratorCol) );
|
|
gParse.varData = (DataInfo *) malloc( 25*sizeof(DataInfo) );
|
|
}
|
|
if( gParse.colData == NULL
|
|
|| gParse.varData == NULL ) {
|
|
if( gParse.colData ) free(gParse.colData);
|
|
if( gParse.varData ) free(gParse.varData);
|
|
gParse.colData = NULL;
|
|
gParse.varData = NULL;
|
|
return( *status = MEMORY_ALLOCATION );
|
|
}
|
|
}
|
|
gParse.varData[nCol].data = NULL;
|
|
gParse.varData[nCol].undef = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static int load_column( int varNum, long fRow, long nRows,
|
|
void *data, char *undef )
|
|
{
|
|
iteratorCol *var = gParse.colData+varNum;
|
|
long nelem,nbytes,row,len,idx;
|
|
char **bitStrs, msg[80];
|
|
unsigned char *bytes;
|
|
int status = 0, anynul;
|
|
|
|
if (gParse.hdutype == IMAGE_HDU) {
|
|
/* This test would need to be on a per varNum basis to support
|
|
* cross HDU operations */
|
|
fits_read_imgnull(var->fptr, var->datatype, fRow, nRows,
|
|
data, undef, &anynul, &status);
|
|
if (DEBUG_PIXFILTER)
|
|
printf("load_column: IMAGE_HDU fRow=%ld, nRows=%ld => %d\n",
|
|
fRow, nRows, status);
|
|
} else {
|
|
|
|
nelem = nRows * var->repeat;
|
|
|
|
switch( var->datatype ) {
|
|
case TBYTE:
|
|
nbytes = ((var->repeat+7)/8) * nRows;
|
|
bytes = (unsigned char *)malloc( nbytes * sizeof(char) );
|
|
|
|
ffgcvb(var->fptr, var->colnum, fRow, 1L, nbytes,
|
|
0, bytes, &anynul, &status);
|
|
|
|
nelem = var->repeat;
|
|
bitStrs = (char **)data;
|
|
for( row=0; row<nRows; row++ ) {
|
|
idx = (row)*( (nelem+7)/8 ) + 1;
|
|
for(len=0; len<nelem; len++) {
|
|
if( bytes[idx] & (1<<(7-len%8)) )
|
|
bitStrs[row][len] = '1';
|
|
else
|
|
bitStrs[row][len] = '0';
|
|
if( len%8==7 ) idx++;
|
|
}
|
|
bitStrs[row][len] = '\0';
|
|
}
|
|
|
|
FREE( (char *)bytes );
|
|
break;
|
|
case TSTRING:
|
|
ffgcfs(var->fptr, var->colnum, fRow, 1L, nRows,
|
|
(char **)data, undef, &anynul, &status);
|
|
break;
|
|
case TLOGICAL:
|
|
ffgcfl(var->fptr, var->colnum, fRow, 1L, nelem,
|
|
(char *)data, undef, &anynul, &status);
|
|
break;
|
|
case TLONG:
|
|
ffgcfj(var->fptr, var->colnum, fRow, 1L, nelem,
|
|
(long *)data, undef, &anynul, &status);
|
|
break;
|
|
case TDOUBLE:
|
|
ffgcfd(var->fptr, var->colnum, fRow, 1L, nelem,
|
|
(double *)data, undef, &anynul, &status);
|
|
break;
|
|
default:
|
|
sprintf(msg,"load_column: unexpected datatype %d", var->datatype);
|
|
ffpmsg(msg);
|
|
}
|
|
}
|
|
if( status ) {
|
|
gParse.status = status;
|
|
return pERROR;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int fits_pixel_filter (PixelFilter * filter, int * status)
|
|
/* Evaluate an expression using the data in the input FITS file(s) */
|
|
/*--------------------------------------------------------------------------*/
|
|
{
|
|
parseInfo Info = { 0 };
|
|
int naxis, bitpix;
|
|
long nelem, naxes[MAXDIMS];
|
|
int col_cnt;
|
|
Node *result;
|
|
int datatype;
|
|
fitsfile * infptr;
|
|
fitsfile * outfptr;
|
|
char * DEFAULT_TAGS[] = { "X" };
|
|
char msg[256];
|
|
int writeBlankKwd = 0; /* write BLANK if any output nulls? */
|
|
|
|
DEBUG_PIXFILTER = getenv("DEBUG_PIXFILTER") ? 1 : 0;
|
|
|
|
if (*status)
|
|
return (*status);
|
|
|
|
FFLOCK;
|
|
if (!filter->tag || !filter->tag[0] || !filter->tag[0][0]) {
|
|
filter->tag = DEFAULT_TAGS;
|
|
if (DEBUG_PIXFILTER)
|
|
printf("using default tag '%s'\n", filter->tag[0]);
|
|
}
|
|
|
|
infptr = filter->ifptr[0];
|
|
outfptr = filter->ofptr;
|
|
gParse.pixFilter = filter;
|
|
|
|
if (ffiprs(infptr, 0, filter->expression, MAXDIMS,
|
|
&Info.datatype, &nelem, &naxis, naxes, status)) {
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (nelem < 0) {
|
|
nelem = -nelem;
|
|
}
|
|
|
|
{
|
|
/* validate result type */
|
|
const char * type = 0;
|
|
switch (Info.datatype) {
|
|
case TLOGICAL: type = "LOGICAL"; break;
|
|
case TLONG: type = "LONG"; break;
|
|
case TDOUBLE: type = "DOUBLE"; break;
|
|
case TSTRING: type = "STRING";
|
|
*status = pERROR;
|
|
ffpmsg("pixel_filter: cannot have string image");
|
|
case TBIT: type = "BIT";
|
|
if (DEBUG_PIXFILTER)
|
|
printf("hmm, image from bits?\n");
|
|
break;
|
|
default: type = "UNKNOWN?!";
|
|
*status = pERROR;
|
|
ffpmsg("pixel_filter: unexpected result datatype");
|
|
}
|
|
if (DEBUG_PIXFILTER)
|
|
printf("result type is %s [%d]\n", type, Info.datatype);
|
|
if (*status)
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (fits_get_img_param(infptr, MAXDIMS,
|
|
&bitpix, &naxis, &naxes[0], status)) {
|
|
ffpmsg("pixel_filter: unable to read input image parameters");
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (DEBUG_PIXFILTER)
|
|
printf("input bitpix %d\n", bitpix);
|
|
|
|
if (Info.datatype == TDOUBLE) {
|
|
/* for floating point expressions, set the default output image to
|
|
bitpix = -32 (float) unless the default is already a double */
|
|
if (bitpix != DOUBLE_IMG)
|
|
bitpix = FLOAT_IMG;
|
|
}
|
|
|
|
/* override output image bitpix if specified by caller */
|
|
if (filter->bitpix)
|
|
bitpix = filter->bitpix;
|
|
if (DEBUG_PIXFILTER)
|
|
printf("output bitpix %d\n", bitpix);
|
|
|
|
if (fits_create_img(outfptr, bitpix, naxis, naxes, status)) {
|
|
ffpmsg("pixel_filter: unable to create output image");
|
|
goto CLEANUP;
|
|
}
|
|
|
|
/* transfer keycards */
|
|
{
|
|
int i, ncards, more;
|
|
if (fits_get_hdrspace(infptr, &ncards, &more, status)) {
|
|
ffpmsg("pixel_filter: unable to determine number of keycards");
|
|
goto CLEANUP;
|
|
}
|
|
|
|
for (i = 1; i <= ncards; ++i) {
|
|
|
|
int keyclass;
|
|
char card[FLEN_CARD];
|
|
|
|
if (fits_read_record(infptr, i, card, status)) {
|
|
sprintf(msg, "pixel_filter: unable to read keycard %d", i);
|
|
ffpmsg(msg);
|
|
goto CLEANUP;
|
|
}
|
|
|
|
keyclass = fits_get_keyclass(card);
|
|
if (keyclass == TYP_STRUC_KEY) {
|
|
/* output structure defined by fits_create_img */
|
|
}
|
|
else if (keyclass == TYP_COMM_KEY && i < 12) {
|
|
/* assume this is one of the FITS standard comments */
|
|
}
|
|
else if (keyclass == TYP_NULL_KEY && bitpix < 0) {
|
|
/* do not transfer BLANK to real output image */
|
|
}
|
|
else if (keyclass == TYP_SCAL_KEY && bitpix < 0) {
|
|
/* do not transfer BZERO, BSCALE to real output image */
|
|
}
|
|
else if (fits_write_record(outfptr, card, status)) {
|
|
sprintf(msg, "pixel_filter: unable to write keycard '%s' [%d]\n",
|
|
card, *status);
|
|
ffpmsg(msg);
|
|
goto CLEANUP;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (bitpix) {
|
|
case BYTE_IMG: datatype = TLONG; Info.datatype = TBYTE; break;
|
|
case SHORT_IMG: datatype = TLONG; Info.datatype = TSHORT; break;
|
|
case LONG_IMG: datatype = TLONG; Info.datatype = TLONG; break;
|
|
case FLOAT_IMG: datatype = TDOUBLE; Info.datatype = TFLOAT; break;
|
|
case DOUBLE_IMG: datatype = TDOUBLE; Info.datatype = TDOUBLE; break;
|
|
|
|
default:
|
|
sprintf(msg, "pixel_filter: unexpected output bitpix %d\n", bitpix);
|
|
ffpmsg(msg);
|
|
*status = pERROR;
|
|
goto CLEANUP;
|
|
}
|
|
|
|
if (bitpix > 0) { /* arrange for NULLs in output */
|
|
long nullVal = filter->blank;
|
|
if (!filter->blank) {
|
|
int tstatus = 0;
|
|
if (fits_read_key_lng(infptr, "BLANK", &nullVal, 0, &tstatus)) {
|
|
|
|
writeBlankKwd = 1;
|
|
|
|
if (bitpix == BYTE_IMG)
|
|
nullVal = UCHAR_MAX;
|
|
else if (bitpix == SHORT_IMG)
|
|
nullVal = SHRT_MIN;
|
|
else if (bitpix == LONG_IMG)
|
|
nullVal = LONG_MIN;
|
|
else
|
|
printf("unhandled positive output BITPIX %d\n", bitpix);
|
|
}
|
|
|
|
filter->blank = nullVal;
|
|
}
|
|
|
|
fits_set_imgnull(outfptr, filter->blank, status);
|
|
if (DEBUG_PIXFILTER)
|
|
printf("using blank %ld\n", nullVal);
|
|
|
|
}
|
|
|
|
if (!filter->keyword[0]) {
|
|
iteratorCol * colIter;
|
|
DataInfo * varInfo;
|
|
|
|
/*************************************/
|
|
/* Create new iterator Output Column */
|
|
/*************************************/
|
|
col_cnt = gParse.nCols;
|
|
if (allocateCol(col_cnt, status))
|
|
goto CLEANUP;
|
|
gParse.nCols++;
|
|
|
|
colIter = &gParse.colData[col_cnt];
|
|
colIter->fptr = filter->ofptr;
|
|
colIter->iotype = OutputCol;
|
|
varInfo = &gParse.varData[col_cnt];
|
|
set_image_col_types(colIter->fptr, "CREATED", bitpix, varInfo, colIter);
|
|
|
|
Info.maxRows = -1;
|
|
|
|
if (ffiter(gParse.nCols, gParse.colData, 0,
|
|
0, parse_data, &Info, status) == -1)
|
|
*status = 0;
|
|
else if (*status)
|
|
goto CLEANUP;
|
|
|
|
if (Info.anyNull) {
|
|
if (writeBlankKwd) {
|
|
fits_update_key_lng(outfptr, "BLANK", filter->blank, "NULL pixel value", status);
|
|
if (*status)
|
|
ffpmsg("pixel_filter: unable to write BLANK keyword");
|
|
if (DEBUG_PIXFILTER) {
|
|
printf("output has NULLs\n");
|
|
printf("wrote blank [%d]\n", *status);
|
|
}
|
|
}
|
|
}
|
|
else if (bitpix > 0) /* never used a null */
|
|
if (fits_set_imgnull(outfptr, -1234554321, status))
|
|
ffpmsg("pixel_filter: unable to reset imgnull");
|
|
}
|
|
else {
|
|
|
|
/* Put constant result into keyword */
|
|
char * parName = filter->keyword;
|
|
char * parInfo = filter->comment;
|
|
|
|
result = gParse.Nodes + gParse.resultNode;
|
|
switch (Info.datatype) {
|
|
case TDOUBLE:
|
|
ffukyd(outfptr, parName, result->value.data.dbl, 15, parInfo, status);
|
|
break;
|
|
case TLONG:
|
|
ffukyj(outfptr, parName, result->value.data.lng, parInfo, status);
|
|
break;
|
|
case TLOGICAL:
|
|
ffukyl(outfptr, parName, result->value.data.log, parInfo, status);
|
|
break;
|
|
case TBIT:
|
|
case TSTRING:
|
|
ffukys(outfptr, parName, result->value.data.str, parInfo, status);
|
|
break;
|
|
default:
|
|
sprintf(msg, "pixel_filter: unexpected constant result type [%d]\n",
|
|
Info.datatype);
|
|
ffpmsg(msg);
|
|
}
|
|
}
|
|
|
|
CLEANUP:
|
|
ffcprs();
|
|
FFUNLOCK;
|
|
return (*status);
|
|
}
|