summaryrefslogtreecommitdiffstats
path: root/funtools/funtext.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2016-10-27 17:38:41 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2016-10-27 17:38:41 (GMT)
commit5b44fb0d6530c4ff66a446afb69933aa8ffd014f (patch)
treee059f66d1f612e21fe9d83f9620c8715530353ec /funtools/funtext.c
parentda2e3d212171bbe64c1af39114fd067308656990 (diff)
parent23c7930d27fe11c4655e1291a07a821dbbaba78d (diff)
downloadblt-5b44fb0d6530c4ff66a446afb69933aa8ffd014f.zip
blt-5b44fb0d6530c4ff66a446afb69933aa8ffd014f.tar.gz
blt-5b44fb0d6530c4ff66a446afb69933aa8ffd014f.tar.bz2
Merge commit '23c7930d27fe11c4655e1291a07a821dbbaba78d' as 'funtools'
Diffstat (limited to 'funtools/funtext.c')
-rw-r--r--funtools/funtext.c1194
1 files changed, 1194 insertions, 0 deletions
diff --git a/funtools/funtext.c b/funtools/funtext.c
new file mode 100644
index 0000000..8189a26
--- /dev/null
+++ b/funtools/funtext.c
@@ -0,0 +1,1194 @@
+/*
+ * Copyright (c) 1999-2005 Smithsonian Astrophysical Observatory
+ */
+
+#include <funtoolsP.h>
+
+/*
+ *
+ * private routines
+ *
+ */
+
+/* struct for getting column info from headers */
+typedef struct _hcstruct{
+ int args;
+ int eflag;
+ int clen;
+ int maxlen;
+ char *fmt;
+ char *expr;
+ char mbuf[SZ_LINE];
+ char tbuf[SZ_LINE];
+} *HC, HCRec;
+
+/* parser characteristics struct */
+typedef struct _ptyperec{
+ char *delims;
+ char *comchars;
+ char *eot;
+ int nullvalue;
+ int whitespace;
+ int units;
+ int comeot;
+ int lazyeot;
+ char *hcolfmt;
+} *PType, PTypeRec;
+
+static PTypeRec ptype[] =
+ {{" \t,\n", "#\n", NULL, 0, 0, 0, 1, 1, NULL}, /* funtools */
+ {" \t\n", "#\n", NULL, 0, 0, 0, 1, 1, NULL}, /* spaces */
+ {",\n", "#\n", NULL, 1, 0, 0, 1, 1, NULL}, /* commas */
+ {"\t\n", "#\n", "\f\n", 1, 0, 0, 1, 0, NULL}, /* rdb */
+ {"\t\n", "#\n", NULL, 1, 0, 1, 1, 1, "Column $col ($fmt)"}, /* VizieR */
+ {"|;\n", "#\n", NULL, 1, 0, 0, 1, 1, NULL}}; /* other */
+
+/* macro return buffer */
+static char macrobuf[SZ_LINE];
+
+/*
+ *
+ * _HCCB -- callback for macro expansion
+ *
+ */
+#ifdef ANSI_FUNC
+static char *_HCCB(char *name, void *client_data)
+#else
+static char *_HCCB(name, client_data)
+ char *name;
+ void *client_data;
+#endif
+{
+ HC hc = (HC)client_data;
+
+ /* perform macro replacement */
+ if( !strcmp(name, "name") || !strcmp(name, "col") ){
+ if( strchr(hc->mbuf, 'n') ){
+ hc->eflag = 1;
+ gerror(stderr,
+ "$%s can only be specified once in headcol format\n", name);
+ return NULL;
+ }
+ strncpy(macrobuf, "%s", SZ_LINE);
+ strncat(hc->mbuf, "n", SZ_LINE);
+ hc->args++;
+ return macrobuf;
+ }
+ else if( !strcmp(name, "format") || !strcmp(name, "fmt") ){
+ if( strchr(hc->mbuf, 'f') ){
+ hc->eflag = 1;
+ gerror(stderr,
+ "$%s can only be specified once in headcol format\n", name);
+ return NULL;
+ }
+ strncpy(macrobuf, "%[a-zA-Z0-9.]", SZ_LINE);
+ strncat(hc->mbuf, "f", SZ_LINE);
+ hc->args++;
+ return macrobuf;
+ }
+ else if( !strcmp(name, "skip") ){
+ strncpy(macrobuf, "%*s", SZ_LINE);
+ return macrobuf;
+ }
+ else if( !strcmp(name, "skipi") ){
+ strncpy(macrobuf, "%*d", SZ_LINE);
+ return macrobuf;
+ }
+ else if( !strcmp(name, "skipf") ){
+ strncpy(macrobuf, "%*f", SZ_LINE);
+ return macrobuf;
+ }
+ else{
+ return NULL;
+ }
+}
+
+/*
+ *
+ * HCFree -- free up headercolumn struct
+ *
+ */
+#ifdef ANSI_FUNC
+static void
+HCFree(HC hc)
+#else
+static void
+HCFree(hc)
+ HC hc;
+#endif
+{
+ /* sanity check */
+ if( !hc ) return;
+ if( hc->fmt ) xfree(hc->fmt);
+ if( hc->expr ) xfree(hc->expr);
+ xfree(hc);
+}
+
+/*
+ *
+ * HCNew -- allocate header/column struct and generate scanf expression
+ *
+ */
+#ifdef ANSI_FUNC
+static HC
+HCNew(char *s)
+#else
+static HC
+HCNew(s)
+ char *s;
+#endif
+{
+ HC hc=NULL;
+
+ /* sanity check */
+ if( !s ) return NULL;
+
+ /* allocate record */
+ if( !(hc=xcalloc(1, sizeof(HCRec))) ) return NULL;
+ /* expand format specification to make a scanf format */
+ if( !(hc->fmt = ExpandMacro(s, NULL, NULL, 0, _HCCB, hc)) ||
+ hc->eflag ){
+ HCFree(hc);
+ return NULL;
+ }
+ return hc;
+}
+
+/*
+ *
+ * HCProcess -- process a header line to see if it contains column info
+ *
+ */
+#ifdef ANSI_FUNC
+static char *
+HCProcess(HC hc, char *s)
+#else
+static char *
+HCProcess(hc, s)
+ HC hc;
+ char *s;
+#endif
+{
+ int got;
+ int len;
+ char tbuf[SZ_LINE];
+ char tbuf1[SZ_LINE];
+ char tbuf2[SZ_LINE];
+
+ /* sanity check */
+ if( !hc || !*hc->mbuf || !hc->fmt || !s ) return NULL;
+
+ /* clear buffers */
+ *tbuf = '\0';
+ *tbuf1 = '\0';
+ *tbuf2 = '\0';
+
+ /* skip leading white space */
+ while( isspace((int)*s) ) s++;
+
+ /* try to recognize a type specification */
+ if( !strcmp(hc->mbuf, "n") ){
+ if( (got = sscanf(s, hc->fmt, tbuf1)) != hc->args ) return NULL;
+ strncpy(tbuf, tbuf1, SZ_LINE);
+ }
+ else if( !strcmp(hc->mbuf, "f") ){
+ if( (got = sscanf(s, hc->fmt, tbuf1)) != hc->args ) return NULL;
+ strncpy(tbuf, tbuf1, SZ_LINE);
+ }
+ else if( !strcmp(hc->mbuf, "nf") ){
+ if( (got = sscanf(s, hc->fmt, tbuf1, tbuf2)) != hc->args ) return NULL;
+ switch(*tbuf2){
+ case 'I':
+ snprintf(tbuf, SZ_LINE, "%s:J", tbuf1);
+ break;
+ case 'F':
+ snprintf(tbuf, SZ_LINE, "%s:D", tbuf1);
+ break;
+ case 'A':
+ snprintf(tbuf, SZ_LINE, "%s:%sA", tbuf1, &tbuf2[1]?&tbuf2[1]:"");
+ break;
+ default:
+ snprintf(tbuf, SZ_LINE, "%s:%s", tbuf1, tbuf2);
+ break;
+ }
+ }
+ else if( !strcmp(hc->mbuf, "fn") ){
+ if( (got = sscanf(s, hc->fmt, tbuf1, tbuf2)) != hc->args ) return NULL;
+ switch(*tbuf2){
+ case 'I':
+ snprintf(tbuf, SZ_LINE, "%s:J", tbuf2);
+ break;
+ case 'F':
+ snprintf(tbuf, SZ_LINE, "%s:D", tbuf2);
+ break;
+ case 'A':
+ snprintf(tbuf, SZ_LINE, "%s:%sA", tbuf2, &tbuf1[1]?&tbuf1[1]:"");
+ break;
+ default:
+ snprintf(tbuf, SZ_LINE, "%s:%s", tbuf2, tbuf1);
+ break;
+ }
+ }
+ else{
+ *tbuf = '\0';
+ }
+
+ /* add to expression */
+ if( *hc->tbuf ){
+ len = strlen(tbuf);
+ /* make sure we have ennough room */
+ if( (len + hc->clen + 1) >= hc->maxlen ){
+ while( (len + hc->clen +1) > hc->maxlen ){
+ hc->maxlen += SZ_LINE;
+ }
+ if( hc->clen == 0 )
+ hc->expr = (char *)xcalloc(hc->maxlen, sizeof(char));
+ else
+ hc->expr = (char *)xrealloc(hc->expr, hc->maxlen);
+ }
+ /* eventdef syntax requires a comma between column arguments */
+ if( *hc->expr ) strncat(hc->expr, ",", SZ_LINE-1);
+ /* add new column */
+ strncat(hc->expr, tbuf, SZ_LINE-1);
+ }
+
+ /* return what we just got */
+ return hc->tbuf;
+}
+
+/*
+ *
+ * _FunTextGetLine -- get a line of text (using existing line if possible)
+ *
+ */
+#ifdef ANSI_FUNC
+static int
+_FunTextGetLine(Fun fun, char *iline, char *lbuf, int llen)
+#else
+static int _FunTextGetLine(fun, iline, lbuf, llen)
+ Fun fun;
+ char *iline;
+ char *lbuf;
+ int llen;
+#endif
+{
+ int len;
+ char *lptr=NULL;
+
+ /* initialize */
+ *lbuf = '\0';
+
+ /* seed with previously-read partial line, if necessary */
+ if( iline && iline[0] ){
+ strncpy(lbuf, iline, llen-1);
+ lbuf[llen-1] = '\0';
+ iline[0] = '\0';
+ /* get actual length of string */
+ len = strlen(lbuf);
+ /* check for complete line (EOL at end of string) */
+ if( lbuf[len-1] == '\n' )
+ lptr = NULL;
+ /* otherwise prepare to read rest of line */
+ else
+ lptr = &lbuf[len];
+ }
+ /* need a whole line */
+ else{
+ lptr = lbuf;
+ }
+ /* read more if we need it */
+ if( lptr ){
+ /* get next line, error if none */
+ if( !ggets(fun->gio, lptr, llen) ){
+ return 0;
+ }
+ }
+ return strlen(lbuf);
+}
+
+/*
+ *
+ * semi-public routines, used by other modules
+ *
+ */
+
+/*
+ *
+ * FunTextParam -- parse a line, looking for a valid parameter
+ *
+ */
+#ifdef ANSI_FUNC
+int
+FunTextParam(char *pdelims,
+ char *lbuf, char *kbuf, char *vbuf, char *cbuf, int maxlen)
+#else
+int FunTextParam(pdelims, lbuf, kbuf, vbuf, cbuf, maxlen)
+ char *pdelims;
+ char *lbuf, *kbuf, *vbuf, *cbuf;
+ int maxlen;
+#endif
+{
+ int i;
+ int hstate=0;
+ int got=0;
+ int kgot=0;
+ int len=0;
+ int docom=0;
+ int dtable[PARSE_TABLE_SIZE];
+ char *s;
+ char *kptr, *vptr, *cptr;
+ char *tptr;
+ char *tbuf;
+ char quote='\0';
+
+ kptr = kbuf;
+ vptr = vbuf;
+ cptr = cbuf;
+ *kptr = '\0';
+ *vptr = '\0';
+ *cptr = '\0';
+
+ /* set up delim table for removing enclosing chars from keyword strings */
+ memset(dtable, 0, PARSE_TABLE_SIZE*sizeof(int));
+ if( pdelims && *pdelims ){
+ /* set the delim table */
+ for(s=pdelims; s && *s; s++){
+ if( (i=(int)*s) == '\\' ){
+ s++;
+ if( *s == 'n' ) i = '\n';
+ else if( *s == 't' ) i = '\t';
+ else if( *s == 'r' ) i = '\r';
+ else if( *s == 'f' ) i = '\014';
+ }
+ dtable[i] = 1;
+ }
+ }
+ else{
+ dtable[(int)'='] = 1;
+ dtable[(int)':'] = 1;
+ }
+
+ /* get modifiable string */
+ tbuf = xstrdup(lbuf);
+ nocr(tbuf);
+ nowhite(tbuf, tbuf);
+
+ /* process it */
+ for(tptr=tbuf; *tptr;){
+ switch(hstate){
+ /* gather up keyword */
+ case 0:
+ if( (*tptr == ' ') || (*tptr == '\t') || dtable[(int)*tptr] ){
+ /* check for FITS-style comment */
+ if( *tptr == '=' ) docom=1;
+ hstate = 1;
+ len = 0;
+ }
+ else{
+ *kptr++ = *tptr;
+ *kptr = '\0';
+ if( ++len >= maxlen ) goto done;
+ }
+ tptr++;
+ break;
+ /* process whitespace, including = and : */
+ case 1:
+ /* skip past whitespace before value */
+ if( (*tptr == ' ') || (*tptr == '\t') )
+ tptr++;
+ else if( dtable[(int)*tptr] ){
+ /* check for FITS-style comment */
+ if( *tptr == '=' ) docom=1;
+ /*only one = or : allowed */
+ kgot++;
+ if( kgot > 1 ){
+ hstate = 2;
+ len = 0;
+ }
+ else{
+ tptr++;
+ }
+ }
+ else{
+ hstate = 2;
+ len = 0;
+ }
+ break;
+ /* check for quoted string */
+ case 2:
+ if( *tptr == '"' ){
+ quote = '"';
+ docom = 1;
+ hstate = 3;
+ len = 0;
+ }
+ else if( *tptr == '\'' ){
+ quote = '\'';
+ docom = 1;
+ hstate = 3;
+ len = 0;
+ }
+ else{
+ *vptr++ = *tptr;
+ *vptr = '\0';;
+ if( ++len >= maxlen ) goto done;
+ hstate = 4;
+ len = 0;
+ }
+ tptr++;
+ break;
+ /* gather up value in quoted string */
+ case 3:
+ if( *tptr == quote ){
+ hstate = 5;
+ len = 0;
+ }
+ else{
+ *vptr++ = *tptr;
+ *vptr = '\0';
+ if( ++len >= maxlen ) goto done;
+ }
+ tptr++;
+ break;
+ /* gather value up to whitespace */
+ case 4:
+ /* gather up value */
+ if( (*tptr == ' ') || (*tptr == '\t') ){
+ hstate = 5;
+ len = 0;
+ }
+ else{
+ *vptr++ = *tptr;
+ *vptr = '\0';
+ if( ++len >= maxlen ) goto done;
+ }
+ tptr++;
+ break;
+ /* skip past whitespace before possible comment */
+ case 5:
+ if( docom ){
+ if( (*tptr == ' ') || (*tptr == '\t') ){
+ tptr++;
+ }
+ else{
+ hstate = 6;
+ len = 0;
+ }
+ }
+ /* if comments are not wanted, then everything is value */
+ else{
+ *vptr++ = *tptr;
+ *vptr = '\0';
+ tptr++;
+ if( ++len >= maxlen ) goto done;
+ }
+ break;
+ /* look for comment */
+ case 6:
+ if( *tptr == '/' ){
+ tptr++;
+ hstate = 7;
+ len = 0;
+ }
+ /* extra chars but no comment char */
+ else{
+ got = 4;
+ goto done2;
+ }
+ break;
+ /* skip past whitespace before possible comment */
+ case 7:
+ if( (*tptr == ' ') || (*tptr == '\t') ){
+ tptr++;
+ }
+ else{
+ hstate = 8;
+ len = 0;
+ }
+ break;
+ /* gather up comment to end of line */
+ case 8:
+ *cptr++ = *tptr++;
+ *cptr = '\0';
+ if( ++len >= maxlen ) goto done;
+ break;
+ default:
+ gerror(stderr, "unknown state (%d) processing text header\n", hstate);
+ break;
+ }
+ }
+
+ /* result code depends on what we gathered up */
+done:
+ if( *cbuf )
+ got = 3;
+ else if( *vbuf )
+ got = 2;
+ else if( *kbuf )
+ got = 1;
+ else
+ got = 0;
+
+done2:
+ if( tbuf ) xfree(tbuf);
+ return got;
+}
+
+
+/*
+ *
+ * FunTextParamHeader -- put param into header
+ *
+ */
+#ifdef ANSI_FUNC
+void
+FunTextParamHeader(FITSHead theader,
+ char *lbuf, char *key, char *val, char *com, int pgot)
+#else
+void FunTextParamHeader(theader, lbuf, key, val, com, pgot)
+ FITSHead theader;
+ char *lbuf, *key, *val, *com;
+ int pgot;
+#endif
+{
+ char *lptr;
+ longlong ival;
+ int dtype;
+ double dval;
+
+ switch(pgot){
+ case -1:
+ gerror(stderr, "internal text parser error: processing params\n");
+ break;
+ case 0:
+ break;
+ case 1:
+ ft_headapps(theader, "COMMENT", 0, key, NULL);
+ break;
+ case 2:
+ case 3:
+ dtype = ParseDataType(val, &dval, &ival);
+ switch( dtype ){
+ case PARSE_COMMENT:
+ case PARSE_DASH:
+ case PARSE_EOL:
+ case PARSE_NULL:
+ case PARSE_EOT:
+ break;
+ case PARSE_FLOAT:
+ ft_headsetr(theader, key, 0, dval, 7, com, 1);
+ break;
+ case PARSE_HEXINT:
+ case PARSE_INTEGER:
+ ft_headseti(theader, key, 0, (int)ival, com, 1);
+ break;
+ case PARSE_STRING:
+ ft_headsets(theader, key, 0, val, com, 1);
+ break;
+ }
+ break;
+ case 4:
+ /* clean up line */
+ nocr(lbuf);
+ nowhite(lbuf, lbuf);
+ for(lptr=lbuf; *lptr; lptr++)
+ if( *lptr == '\t' ) *lptr = ' ';
+ ft_headapps(theader, "COMMENT", 0, lbuf, NULL);
+ break;
+ }
+}
+
+/*
+ *
+ * FunTextOpen -- open a ascii text events file,
+ * set up binning and filtering parameters
+ *
+ */
+#ifdef ANSI_FUNC
+Fun
+FunTextOpen(char *fname, char *mode, char *iline, GIO ifd)
+#else
+Fun FunTextOpen(fname, mode, iline, ifd)
+ char *fname;
+ char *mode;
+ char *iline;
+ GIO ifd;
+#endif
+{
+ int i=0, p=0, q=0, t=0;
+ int got;
+ int state;
+ int alen=0;
+ int indx=0;
+ int pgot=0;
+ int nheader=0;
+ int ntheader=0;
+ char *s;
+ char *delim=NULL, *comchars=NULL, *eot=NULL, *textp=NULL;
+ char **hcolfmts=NULL;
+ char tdelims[SZ_LINE];
+ char tdebug[SZ_LINE];
+ char pdelims[SZ_LINE];
+ char tcomchars[SZ_LINE];
+ char thcolfmt[SZ_LINE];
+ char teot[SZ_LINE];
+ char tnull1[SZ_LINE];
+ char lbuf[SZ_LINE];
+ char tbuf[SZ_LINE];
+ char tbuf2[SZ_LINE];
+ char pmode[SZ_LINE];
+ char key[SZ_LINE];
+ char val[SZ_LINE];
+ char com[SZ_LINE];
+ char eventdef[SZ_LINE];
+ char file[SZ_LINE];
+ char extn[SZ_LINE];
+ char textparams[SZ_LINE];
+ char tail[SZ_LINE];
+ char ttail[SZ_LINE];
+ char tname[SZ_LINE];
+ Parse parser;
+ Parse fakep=NULL;
+ ParsedLine line=NULL, header=NULL, data1=NULL;
+ FITSHead theader=NULL, tmerge=NULL;
+ Fun fun=NULL;
+ HC *hc=NULL;
+
+ /* intialize */
+ *tdebug = '\0';
+ *tdelims = '\0';
+ *pdelims = '\0';
+ *tcomchars = '\0';
+ *thcolfmt = '\0';
+ *eventdef = '\0';
+ *teot = '\0';
+ *tnull1 = '\0';
+ *tbuf = '\0';
+ *extn = '\0';
+ *textparams = '\0';
+ *tail = '\0';
+ *ttail = '\0';
+ *tname = '\0';
+ *file = '\0';
+
+ /* allocate a fun record */
+ if( !(fun = _FunNew()) )
+ return NULL;
+
+ /* save filename and mode */
+ fun->fname = xstrdup(fname);
+ fun->mode = xstrdup(mode);
+
+ /* parse filename and get extension, index and tail */
+ ft_parsefilename(fname, file, extn, SZ_LINE, &indx, ttail, SZ_LINE);
+ /* if TEXT() specified, strip it out and place in textparams */
+ if( !_FunSpecialFile(ttail, "TEXT", tname, tail, textparams, SZ_LINE) ){
+ strncpy(tail, ttail, SZ_LINE-1);
+ }
+
+ /* use passed-in fd or try to open the file */
+ if( ifd )
+ fun->gio = ifd;
+ else if( !(fun->gio=gopen(file, mode)) )
+ goto error;
+
+ /* process section and keywords (but not mode keywords) */
+ textp = textparams;
+ _FunKeyword(textp, "debug", "TEXT_DEBUG", tdebug, SZ_LINE);
+ _FunKeyword(textp, "delims", "TEXT_DELIMS", tdelims, SZ_LINE);
+ _FunKeyword(textp, "pdelims", "TEXT_DELIMS", pdelims, SZ_LINE);
+ _FunKeyword(textp, "comchars", "TEXT_COMCHARS", tcomchars, SZ_LINE);
+ _FunKeyword(textp, "hcolfmt", "TEXT_HCOLFMT", thcolfmt, SZ_LINE);
+ _FunKeyword(textp, "cols", "TEXT_COLUMNS", eventdef, SZ_LINE);
+ _FunKeyword(textp, "eot", "TEXT_EOT", teot, SZ_LINE);
+ _FunKeyword(textp, "null1", "TEXT_NULL1", tnull1, SZ_LINE);
+ _FunKeyword(textp, "alen", "TEXT_ALEN", tbuf, SZ_LINE);
+ if( *tbuf ) alen = atoi(tbuf);
+ if( *textparams ){
+ if( strlen(textp) && !strchr(textp, '=') ){
+ /* the whole string is the column spec */
+ strncpy(eventdef, textp, SZ_LINE-1);
+ textp = NULL;
+ }
+ }
+
+ /* make sure CR is in the delim table */
+ if(*tdelims && !strstr(tdelims, "\\n")) strncat(tdelims, "\\n", SZ_LINE-1);
+
+ /* create parsers */
+ if( *tdelims ){
+ fun->nparser = 1;
+ fun->parsers = (Parse *)xcalloc(fun->nparser, sizeof(Parse));
+ hcolfmts = xcalloc(fun->nparser, sizeof(char *));
+ hc = xcalloc(fun->nparser, sizeof(HCRec));
+ delim = tdelims;
+ comchars = (*tcomchars ? tcomchars : PARSE_DEFAULT_COMCHARS);
+ hcolfmts[0] = (*thcolfmt ? thcolfmt : NULL);
+ hc[0] = HCNew(hcolfmts[0]);
+ eot = (*teot ? teot : NULL);
+ *pmode = '\0';
+ if( textp && *textp ){
+ strncat(pmode, textp, SZ_LINE-1);
+ strncat(pmode, ",", SZ_LINE-1);
+ }
+ strncat(pmode, PARSE_DEFAULT_NULLVALUES, SZ_LINE-1);
+ strncat(pmode, ",", SZ_LINE-1);
+ strncat(pmode, PARSE_DEFAULT_WHITESPACE, SZ_LINE-1);
+ strncat(pmode, ",", SZ_LINE-1);
+ strncat(pmode, PARSE_DEFAULT_UNITS, SZ_LINE-1);
+ strncat(pmode, ",", SZ_LINE-1);
+ strncat(pmode, PARSE_DEFAULT_COMEOT, SZ_LINE-1);
+ strncat(pmode, ",", SZ_LINE-1);
+ strncat(pmode, PARSE_DEFAULT_LAZYEOT, SZ_LINE-1);
+ if( !(fun->parsers[0] = ParseNew(delim, comchars, eot, pmode)) ){
+ gwarning(stderr, "could not create parser #%d (%s)\n", p, delim);
+ return NULL;
+ }
+ }
+ else{
+ fun->nparser = sizeof(ptype)/sizeof(PTypeRec);
+ fun->parsers = (Parse *)xcalloc(fun->nparser, sizeof(Parse));
+ hcolfmts = xcalloc(fun->nparser, sizeof(char *));
+ hc = xcalloc(fun->nparser, sizeof(HCRec));
+ for(p=0; p<fun->nparser; p++){
+ delim = ptype[p].delims;
+ comchars = (*tcomchars ? tcomchars : ptype[p].comchars);
+ hcolfmts[p] = (*thcolfmt ? thcolfmt : ptype[p].hcolfmt);
+ hc[p] = HCNew(hcolfmts[p]);
+ eot = (*teot ? teot : ptype[p].eot);
+ *pmode = '\0';
+ if( textp && *textp ){
+ strncat(pmode, textp, SZ_LINE-1);
+ }
+ if( *pmode ) strncat(pmode, ",", SZ_LINE-1);
+ if( ptype[p].nullvalue ){
+ strncat(pmode, "nullvalues=true", SZ_LINE-1);
+ }
+ else{
+ strncat(pmode, "nullvalues=false", SZ_LINE-1);
+ }
+ if( *pmode ) strncat(pmode, ",", SZ_LINE-1);
+ if( ptype[p].whitespace ){
+ strncat(pmode, "whitespace=true", SZ_LINE-1);
+ }
+ else{
+ strncat(pmode, "whitespace=false", SZ_LINE-1);
+ }
+ if( *pmode ) strncat(pmode, ",", SZ_LINE-1);
+ if( ptype[p].units ){
+ strncat(pmode, "units=true", SZ_LINE-1);
+ }
+ else{
+ strncat(pmode, "units=false", SZ_LINE-1);
+ }
+ if( *pmode ) strncat(pmode, ",", SZ_LINE-1);
+ snprintf(tbuf, SZ_LINE-1, "comeot=%d", ptype[p].comeot);
+ strncat(pmode, tbuf, SZ_LINE-1);
+ if( *pmode ) strncat(pmode, ",", SZ_LINE-1);
+ snprintf(tbuf, SZ_LINE-1, "lazyeot=%d", ptype[p].lazyeot);
+ strncat(pmode, tbuf, SZ_LINE-1);
+ if( *tdebug ){
+ if( *pmode ) strncat(pmode, ",", SZ_LINE-1);
+ if( istrue(tdebug) ) strcpy(tdebug, "1");
+ else if( isfalse(tdebug) ) strcpy(tdebug, "0");
+ snprintf(tbuf, SZ_LINE-1, "debug=%d", atoi(tdebug));
+ strncat(pmode, tbuf, SZ_LINE-1);
+ }
+ if( !(fun->parsers[p] = ParseNew(delim, comchars, eot, pmode)) ){
+ gwarning(stderr, "could not create text parser #%d (%s)\n", p, delim);
+ goto error;
+ }
+ }
+ }
+
+ /* look for section specification in environment, if none was specified */
+ if ( !*extn && (indx <=0) ) {
+ if ( (s = (char *)getenv("TEXT_EXTNAME")) ) {
+ strncpy(extn, s, SZ_LINE-1);
+ extn[SZ_LINE-1] = '\0';
+ }
+ if ( (s = (char *)getenv("TEXT_EXTNUM")) ) {
+ indx = atoi(s);
+ }
+ }
+
+ /* skip to the specified table -- loop requires explicit break, which
+ we do through a goto to the header processing section or to error */
+ i = 0;
+ext:
+ i++;
+ /* if no extension was specified, we are at the right place already */
+ if( !*extn && (indx <=0) ) goto ext2;
+ /* index was found, break out and process header */
+ if( i == indx ) goto ext2;
+ /* grab next table: get next line, error if none */
+ while( 1 ){
+ if( !_FunTextGetLine(fun, iline, lbuf, SZ_LINE) ){
+ gwarning(stderr,
+ "text parser failure looking for extension: %s/%d\n",
+ *extn?extn:"unnamed", (indx!=-1)?indx:1);
+ goto error;
+ }
+ /* analyze line and make sure one parser succeeded (even if its EOT) */
+ if( !ParseAnalyze(fun->parsers, fun->nparser, lbuf) ){
+ gwarning(stderr, "text parser failure analyzing line:\n%s", lbuf);
+ goto error;
+ }
+ /* look for that valid parser */
+ for(got=0, parser=NULL, p=0; p<fun->nparser; p++){
+ if( fun->parsers[p]->state & PARSE_STATE_BAD) continue;
+ if( fun->parsers[p]->state & PARSE_STATE_EOT){
+ for(q=0; q<fun->nparser; q++){
+ if( fun->parsers[q]->state & PARSE_STATE_BAD) continue;
+ /* a parser finding eot is still valid */
+ if( fun->parsers[q]->state & PARSE_STATE_EOT) continue;
+ /* all other parsers missed this eot, so they are "bad" */
+ fun->parsers[q]->state = PARSE_STATE_BADMATCH;
+ }
+ got++;
+ parser = NULL;
+ break;
+ }
+ else{
+ parser = fun->parsers[p];
+ }
+ }
+ /* check for extn, if necessary */
+ if( parser ){
+ if( *extn ){
+ if( parser->types[0] == PARSE_COMMENT ){
+ if( FunTextParam(pdelims, &lbuf[1], key, val, com, SZ_LINE) >=2 ){
+ /* initialize fitsy header, if necessary */
+ if( !theader ) theader = ft_headinit(NULL, 0);
+ /* add to temp header */
+ FunTextParamHeader(theader, &lbuf[1], key, val, com, pgot);
+ /* see if this is the right extension */
+ nowhite(val, val);
+ if( !strcasecmp(key, "extname") && !strcasecmp(val, extn) ){
+ goto ext2;
+ }
+ }
+ }
+ }
+ }
+ /* found another EOT */
+ else{
+ /* did not find named extension, free temp header for this section */
+ if( *extn ){
+ ft_headfree(theader, 1);
+ theader = NULL;
+ }
+ /* if we still have parsers, we can continue with the next section */
+ if( got ){
+ /* bad parsers are kept bad, but others are reset */
+ for(p=0; p<fun->nparser; p++){
+ if( fun->parsers[p]->state & PARSE_STATE_BAD) continue;
+ /* if we read and analyzed into the next table, back up a bit */
+ if( fun->parsers[p]->state & PARSE_STATE_NEXTLINE ){
+ line = ParseLineDup(fun->parsers[p], fun->parsers[p]->cur);
+ state = fun->parsers[p]->state;
+ state &= ~(PARSE_STATE_NEXTLINE|PARSE_STATE_EOT);
+ ParseReset(fun->parsers[p], line, state);
+ }
+ /* for comments, we just reprocess the line */
+ else if( fun->parsers[p]->state & PARSE_STATE_REDOLINE ){
+ ParseReset(fun->parsers[p], NULL, 0);
+ strncpy(tbuf, lbuf, SZ_LINE-1);
+ iline = tbuf;
+ }
+ else{
+ ParseReset(fun->parsers[p], NULL, 0);
+ }
+ }
+ /* go back to the outermost loop and look for next extension */
+ goto ext;
+ }
+ else{
+ gwarning(stderr,
+ "text parser failure looking for data table %d\n",
+ indx);
+ goto error;
+ }
+ }
+ }
+ext2:
+
+/* look for a header */
+header:
+ /* get line, seeding with previously-read partial line, if necessary */
+ if( !_FunTextGetLine(fun, iline, lbuf, SZ_LINE) ){
+ /* if we ran out of file but eventdef was passed, we have an empty table */
+ if( *eventdef ){
+ goto evdef;
+ }
+ /* otherwise we're probably in trouble */
+ /* we'll make some last-ditch checks for a header, which should be in
+ parse.c but would be really hard to add there */
+ else{
+ /* it might be an empty table with a proper header */
+ if( !nheader ) goto headguess;
+ /* or, if all strings have ':' characters, it might be a header line */
+ for(nheader=0, p=0; p<fun->nparser; p++){
+ if( fun->parsers[p]->state & PARSE_STATE_BAD ) continue;
+ if( fun->parsers[p]->types[0] == PARSE_STRING ){
+ for(t=0; t<fun->parsers[p]->ntoken; t++){
+ if( !strchr(fun->parsers[p]->tokens[t].sval, ':') ){
+ nheader++;
+ break;
+ }
+ }
+ /* flag that we are past the header and into the data */
+ fun->parsers[p]->state = PARSE_STATE_DATA;
+ fun->parsers[p]->header =
+ ParseLineDup(fun->parsers[p], fun->parsers[p]->cur);
+ }
+ else{
+ nheader++;
+ break;
+ }
+ }
+ if( !nheader ) goto headguess;
+ /* give up */
+ gwarning(stderr, "text parser failure looking for header (section %d)\n",
+ indx);
+ goto error;
+ }
+ }
+ /* parse and analyze line */
+ if( !ParseAnalyze(fun->parsers, fun->nparser, lbuf) ){
+ gwarning(stderr, "text parser failure analyzing header\n");
+ goto error;
+ }
+ /* see if all are in data state, meaning we have found the header */
+ for(p=0, nheader=0, ntheader=0; p<fun->nparser; p++){
+ if( fun->parsers[p]->state & PARSE_STATE_BAD ) continue;
+ /* process comment into a header parameter */
+ if( fun->parsers[p]->types[0] == PARSE_COMMENT ){
+ if( strlen(lbuf) ){
+ /* try to process parameter as a header column spec */
+ if( !hc[p] || !HCProcess(hc[p], &lbuf[1]) ){
+ /* if not, process as an ordinary parameter (first time only) */
+ if( !ntheader ){
+ pgot = FunTextParam(pdelims, &lbuf[1], key, val, com, SZ_LINE);
+ /* initialize fitsy header, if necessary */
+ if( !theader ) theader = ft_headinit(NULL, 0);
+ FunTextParamHeader(theader, &lbuf[1], key, val, com, pgot);
+ ntheader++;
+ }
+ }
+ }
+ }
+ if( !(fun->parsers[p]->state & PARSE_STATE_DATA) ) nheader++;
+ }
+ /* if we're not all in the data state, to back for more */
+ if( nheader ) goto header;
+
+ /* first line of data tells data types */
+ for(p=0; p<fun->nparser; p++){
+ if( !(fun->parsers[p]->state & PARSE_STATE_BAD) ){
+ if( fun->parsers[p]->data1 ){
+ data1 = fun->parsers[p]->data1;
+ break;
+ }
+ }
+ }
+ /* make sure we read enough to get data */
+ if( !data1 ) goto header;
+
+ /* if the eventdef was not specified, make a guess */
+headguess:
+ if( !*eventdef ){
+ /* see if we got the evendef from the header */
+ for(p=0; p<fun->nparser; p++){
+ if( hc[p] && hc[p]->expr ){
+ /* if it has type information, use it */
+ if( strchr(hc[p]->expr, ':') ){
+ strncpy(eventdef, hc[p]->expr, SZ_LINE-1);
+ goto evdef;
+ }
+ else{
+ /* if we have no header info, we should be able to get it here */
+ if( !fun->parsers[p]->header ){
+ if( (fakep = ParseNew(",\n", NULL, NULL, NULL)) ){
+ ParseLine(fakep, hc[p]->expr, NULL);
+ if( fakep->cur ){
+ fun->parsers[p]->header = ParseLineDup(fakep, fakep->cur);
+ }
+ ParseFree(fakep);
+ }
+ }
+ }
+ }
+ }
+ /* a header tells column names (or else we have to make them up) */
+ for(p=0; p<fun->nparser; p++){
+ if( !(fun->parsers[p]->state & PARSE_STATE_BAD) ){
+ if( fun->parsers[p]->header ){
+ header = fun->parsers[p]->header;
+ break;
+ }
+ }
+ }
+ /* fake header line, if necessary */
+ if( !header ){
+ *tbuf = '\0';
+ *lbuf = '\0';
+ if( !data1 ){
+ gwarning(stderr,
+ "internal text parser error: no data available to fake text header\n");
+ goto error;
+ }
+ for(t=0; t<data1->ntoken; t++){
+ snprintf(tbuf, SZ_LINE-1, "col%d", t+1);
+ if( *lbuf )
+ strncat(lbuf, " ", SZ_LINE-1);
+ strncat(lbuf, tbuf, SZ_LINE-1);
+ }
+ if( !(fakep = ParseNew(" \n", NULL, NULL, NULL)) ) goto error;
+ if( !ParseAnalyze(&fakep, 1, lbuf) ) goto error;
+ header = fakep->cur;
+ }
+
+ /* make up the event definition */
+ for(t=0; t<header->ntoken; t++){
+ *tbuf = '\0';
+ strncpy(tbuf, header->tokens[t].sval, SZ_LINE-1);
+ if( !strchr(header->tokens[t].sval, ':') ) {
+ if( !data1 ){
+ gwarning(stderr,
+ "an empty table must specify data types in the column header\n");
+ goto error;
+ }
+ switch(data1->tokens[t].type){
+ case PARSE_FLOAT:
+ strncat(tbuf, ":1D", SZ_LINE-1);
+ break;
+ case PARSE_HEXINT:
+ strncat(tbuf, ":32X", SZ_LINE-1);
+ break;
+ case PARSE_INTEGER:
+ strncat(tbuf, ":1J", SZ_LINE-1);
+ break;
+ case PARSE_STRING:
+ if( alen <=0 )
+ alen = MAX(strlen(data1->tokens[t].sval), PARSE_DEFAULT_ALEN);
+ snprintf(tbuf2, SZ_LINE-1, ":%dA", alen);
+ strncat(tbuf, tbuf2, SZ_LINE-1);
+ break;
+ case PARSE_NULL:
+ if( *tnull1 ){
+ snprintf(tbuf, SZ_LINE-1, "%s:%s", header->tokens[t].sval, tnull1);
+ }
+ else{
+ gwarning(stderr,
+ "a NULL value in row1,col%d requires null1='[I|J|E|D|A]' or cols='...'\n",
+ t+1);
+ goto error;
+ }
+ break;
+ /* ignore everything else */
+ case PARSE_DASH:
+ case PARSE_COMMENT:
+ case PARSE_EOL:
+ break;
+ default:
+ gwarning(stderr,
+ "internal text parser error: invalid type in text header: '%c'\n",
+ data1->tokens[t].type);
+ goto error;
+ }
+ }
+ if( *tbuf ){
+ if( *eventdef ) strncat(eventdef, ",", SZ_LINE-1);
+ strncat(eventdef, tbuf, SZ_LINE-1);
+ }
+ }
+ }
+
+evdef:
+ /* create a fake table header from the event description */
+ if( !(fun->header = _FunRawEvHeader(fun, NULL, tail, eventdef)) )
+ goto error;
+
+ /* its a valid event file */
+ fun->type = FUN_EVENTS;
+ /* no blanks for events */
+ fun->doblank = 0;
+
+ /* data will not need conversion */
+ fun->endian = is_bigendian();
+
+ /* look for indication of bitpix */
+ if( _FunKeyword(tail, "bitpix", "TEXT_BITPIX", tbuf, SZ_LINE) )
+ fun->bitpix = atoi(tbuf);
+ /* else assume a safe value */
+ else
+ fun->bitpix = 32;
+
+ /* no header on text files */
+ fun->skip = 0;
+
+ /* determine whether we are only processing specific rows */
+ _FunRowNum(fun, tail, NULL);
+ /* get the key for binning */
+ _FunTableBinCols(fun, tail, "TEXT_BINCOLS");
+ /* get the key for the value column for binning */
+ _FunTableValCol(fun, tail, "TEXT_VCOL");
+ /* calculate the image length including padding */
+ _FunImageSize(fun);
+ /* now parse the section specification */
+ _FunParseSection(fun, tail,
+ &(fun->x0), &(fun->x1), &(fun->y0), &(fun->y1),
+ &(fun->block), &(fun->btype), tail, SZ_LINE);
+ /* get maxbufsize for table access */
+ _FunMaxBufSize(fun, tail);
+ /* what's left in the tail is the filter */
+ fun->filter = xstrdup(tail);
+ /* fill in the default selected columns */
+ FunColumnSelect(fun, 0, "copy=reference", NULL);
+
+ /* skip events header, if necessary */
+ if( fun->skip )
+ gskip(fun->gio, (off_t)fun->skip);
+
+ /* merge in comments as params */
+ if( theader && fun->header ){
+ tmerge = ft_headmerge(fun->header, theader, 1);
+ ft_headfree(fun->header, 1);
+ ft_headfree(theader, 1);
+ fun->header = tmerge;
+ }
+
+ /* common code */
+ if( !_FunOpenCommon(fun) ) goto error;
+
+ /* make sure we turned indexing off */
+ fun->idx = -1;
+
+ /* free up tem space */
+ if( fakep ) ParseFree(fakep);
+ if( hcolfmts) xfree(hcolfmts);
+ if( hc ){
+ for(i=0; i<fun->nparser; i++){
+ if( hc[i] ) HCFree(hc[i]);
+ }
+ xfree(hc);
+ }
+ /* return completed struct */
+ return fun;
+
+error:
+ /* free up temp space */
+ if( fakep ) ParseFree(fakep);
+ if( theader ) ft_headfree(theader, 1);
+ if( hcolfmts) xfree(hcolfmts);
+ if( hc ){
+ for(i=0; i<fun->nparser; i++){
+ if( hc[i] ) HCFree(hc[i]);
+ }
+ xfree(hc);
+ }
+ _FunFree(fun, 1);
+ return NULL;
+}
+