/* File: tabledefsubs.c (formerly known as sprdop.c) * Author: Jean Thierry-Mieg (mieg@mrc-lmb.cam.ac.uk) * Copyright (C) J Thierry-Mieg and R Durbin, 1992 * ------------------------------------------------------------------- * Acedb is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt * ------------------------------------------------------------------- * This file is part of the ACEDB genome database package, written by * Richard Durbin (MRC LMB, UK) rd@mrc-lmb.cam.ac.uk, and * Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr * * Description: * Exported functions: * HISTORY: * Last edited: Nov 25 17:58 1999 (fw) * * May 27 16:49 1999 (fw): calculation system rewritten, so * results are written directly to TABLE type * * Mar 18 10:17 1999 (edgrif): Add proper non-valid int/float constants. * Created: Fri Dec 11 13:53:37 1992 (mieg) *------------------------------------------------------------------- */ /* $Id: tabledefsubs.c,v 1.17 2001/02/01 17:41:43 srk Exp $ */ #include "acedb.h" #include "aceio.h" #include "lex.h" #include "session.h" #include "pick.h" #include "whooks/systags.h" #include "whooks/tags.h" #include "whooks/sysclass.h" #include "query.h" #include "bindex.h" #include "java.h" #include "client.h" #include "spread_.h" BITSET_MAKE_BITFIELD /* define bitField for bitset.h ops */ /************************************************************/ TABLE* (*externalTableMaker) (char *quer) = 0 ; /* allocation */ magic_t SPREAD_MAGIC = "SPREAD_MAGIC"; /************************************************************/ typedef struct SpreadCellStruct *SPREADCELL ; struct SpreadCellStruct { BOOL empty ; KEY key, parent, grandParent ; int iCol ; COL *col ; SPREADCELL up; /* previous column */ SPREADCELL scFrom; /* from/rightof: has the correct mark */ SPREADCELL scGrandParent; /* has the correct obj */ BSunit u ; OBJ obj ; BSMARK mark ; } ; /*****************************************************/ static SPREAD spreadCreate (void); static void sortSpreadTable (TABLE *table, int sortCol); static void spreadDestroyCol (COL* c); static void spreadDestroyConditions(SPREAD spread); static void storePrecomputedTable(KEY defKey, char *internalParams, char *commandLineParams, TABLE *table) ; static int scLoopRight (SPREAD spread, SPREADCELL sc) ; static void initSpreadTable (SPREAD spread); static SPREADCELL scAlloc (void); static void scFree (SPREADCELL sc); static BOOL isParamInString (char *cp); static char* getSpreadTableTypes (Array colonnes, STORE_HANDLE handle); static char* getPrecomputationName (KEY tableKey, char *internalParams, char *commandLineParams, STORE_HANDLE handle); static Stack scStack = 0 ; /****************************************************************** ************************* public functions *********************** ******************************************************************/ SPREAD spreadCreateFromFile (char *filename, const char *spec, char *params) { SPREAD spread = NULL; ACEIN spread_in; if (!filename) messcrash ("spreadCreateFromFile() - received NULL filename"); /* first pass we read without parameter substitution */ spread_in = aceInCreateFromFile (filename, spec, NULL, 0); if (spread_in) { spread = spreadCreate(); spreadReadDef (spread, spread_in, params, FALSE); if (params) spread->extraParameters = strnew(params, 0); spread->filename = strnew(aceInGetURL(spread_in), 0); if (arrayMax (spread->colonnes) == 0) /* no column definitions - invalid table-def */ spreadDestroy (spread); aceInDestroy (spread_in); } return spread; } /* spreadCreateFromFile */ /*****************************************/ SPREAD spreadCreateFromAceIn (ACEIN spread_in, char *params) { SPREAD spread; char *filename; spread = spreadCreate(); filename = aceInGetURL(spread_in); if (filename && filename[0] == '/') /* real file-name */ spread->filename = strnew(filename, 0); spreadReadDef (spread, spread_in, params, FALSE); if (params) spread->extraParameters = strnew(params, 0); if (arrayMax (spread->colonnes) == 0) /* no column definitions - invalid table-def */ spreadDestroy (spread); return spread; } /* spreadCreateFromAceIn */ /*****************************************/ SPREAD spreadCreateFromStack (Stack defStack, char *params) { SPREAD spread; ACEIN spread_in; spread = spreadCreate(); spread_in = aceInCreateFromText (stackText(defStack,0), NULL, 0); spreadReadDef (spread, spread_in, params, FALSE); if (params) spread->extraParameters = strnew(params, 0); if (arrayMax (spread->colonnes) == 0) /* no column definitions - invalid table-def */ spreadDestroy (spread); aceInDestroy (spread_in); return spread; } /* spreadCreateFromStack */ /*****************************************/ SPREAD spreadCreateFromObj (KEY tableDefKey, char *params) { SPREAD spread = NULL; ACEIN def_io; Stack objDefStack; objDefStack = getDefinitionFromObj (tableDefKey); if (objDefStack) { spread = spreadCreate(); /* don't substitute parameters */ def_io = aceInCreateFromText (stackText(objDefStack,0), NULL, 0); spreadReadDef (spread, def_io, params, TRUE); if (params) spread->extraParameters = strnew(params, 0); spread->tableDefKey = tableDefKey; if (arrayMax (spread->colonnes) == 0) /* no column definitions - invalid table-def */ spreadDestroy (spread); aceInDestroy (def_io); stackDestroy (objDefStack); } return spread; } /* spreadCreateFromObj */ /*****************************************/ void uSpreadDestroy (SPREAD spread) /* public function */ /* DON'T call directly, use the spreadDestroy() macro */ { if (!spread) return; if (spread->magic != &SPREAD_MAGIC) messcrash ("uSpreadDestroy received corrupt spread->magic"); if (spread->tableDefKey /* the defs came from an object */ && spread->precompute /* where precomute was switched on */ && spread->valuesModified /* which has changed ... */ && sessionGainWriteAccess()) /* ... so we store the new precomputation */ { storePrecomputedTable (spread->tableDefKey, spread->paramBuffer, spread->extraParameters, spread->values); } tableDestroy (spread->values) ; spreadDestroyConditions(spread) ; spreadDestroyColonnes (spread->colonnes); stackDestroy (spread->comments) ; messfree (spread->filename); messfree (spread->extraParameters); spread->magic = 0 ; messfree (spread) ; return; } /* uSpreadDestroy */ /*****************************************************/ char *spreadGetTitle (SPREAD spread) { return spread->titleBuffer; } /* spreadGetTitle */ Array spreadGetFlipInfo (SPREAD spread) { Array isFlipped; int i; isFlipped = arrayCreate(arrayMax(spread->colonnes), BOOL); for (i = 0; i < arrayMax(spread->colonnes); i++) { if (arrp(spread->colonnes, i, COL)->flipped) array(isFlipped, i, BOOL) = TRUE; } return isFlipped; } /* spreadGetFlipInfo */ Array spreadGetWidths (SPREAD spread) { Array widthsArray; int i; widthsArray = arrayCreate(arrayMax(spread->colonnes), int); for (i = 0; i < arrayMax(spread->colonnes); i++) array(widthsArray, i, int) = arrp(spread->colonnes, i, COL)->width; return widthsArray; } /* spreadGetWidths */ /*****************************************************/ KEYSET spreadGetMasterKeyset (SPREAD spread) /* public function */ /* return the alphabetically ordered 1st column of keys */ { KEYSET ksFiltered ; KEYSET ksTmp ; COL *masterCol ; if (arrayMax(spread->colonnes) == 0) { messout("First define at least one column"); return 0; } masterCol = arrp(spread->colonnes, 0, COL) ; if (freelower(masterCol->type) != 'k') { messout ("Column 1 should be type Object") ; return 0 ; } if (!masterCol->classe) { messout ("First choose the class of column 1") ; return 0 ; } if (*masterCol->conditionBuffer && ! condCheckSyntax(messprintf(" %s", masterCol->conditionBuffer))) { messout ("First fix the syntax error in column 1's condition") ; return 0 ; } ksTmp = query (0, messprintf ("FIND %s %s", name(masterCol->classe), masterCol->conditionBuffer)); ksFiltered = keySetAlphaHeap(ksTmp, keySetMax(ksTmp)) ; keySetDestroy (ksTmp) ; return ksFiltered ; } /* spreadGetMasterKeyset */ TABLE* spreadCalculateOverKeySet (SPREAD spread, KEYSET ks, int *lastp) /* public function */ /* compute the results table, based with the given * keyset for the first column * this function can be called multiple times on the same keyset, * every time a new table is calculated with new results * for another part of the 1st column : * last - where the calculation got to last time round * RETURNS : * the position in the master column where we have to continue * from next time, or 0 if the calculation finished */ { int i, new = 0, mx = keySetMax (ks) ; KEY key ; BOOL stop ; /* fw - ?what?does?this?do? */ int nn = 200 ; SPREADCELL sc ; COL *col; int colNum; int last; if (lastp) last = *lastp; else last = 0; if (last > 0 && spread->sortColonne == 1) stop = TRUE; else stop = FALSE; if (externalServer && spread->isActiveKeySet) { /* export active keyset and compute on server side */ Stack ss = 0 ; int n; char *cp ; ACEOUT dump_out; if (!spreadCheckConditions(spread)) return 0 ; /* export active keyset was done by the filter call, * compute on server side */ ss = stackCreate (100) ; dump_out = aceOutCreateToStack (ss, 0); spreadDumpColonneDefinitions (dump_out, spread->colonnes) ; aceOutDestroy (dump_out); n = stackMark(ss) ; if (!n) { stackDestroy (ss) ; return 0 ; } pushText (ss, "Table -active -a = ") ; cp =stackText (ss, 0) ; catText (ss, cp) ; cp = stackText(ss, n) ; spread->values = externalTableMaker (cp) ; stackDestroy (ss) ; spreadDestroyConditions(spread) ; return 0 ; } if (last == 1) last = 0 ; scStack = stackReCreate (scStack, 16) ; spread->interrupt = FALSE ; /* clean up the text-stacks carried by the columns */ for (colNum = 0; colNum < arrayMax(spread->colonnes); colNum++) { col = arrp (spread->colonnes, colNum, COL) ; if (col->text) { col->text = stackReCreate (col->text, 100) ; pushText (col->text, "toto") ; /* avoid zero */ } } /* kill the old value-table */ tableDestroy (spread->values); initSpreadTable (spread); messStatus ("TableCalculation, F4 to interrupt..."); if (last > 0 || spreadCheckConditions(spread)) /* no need to re-check * if last had been set * by a previous run */ { for (i = last ; i < mx ; i++) { new = i + 1 ; /* for encore */ key = keySet(ks,i) ; sc = scAlloc () ; sc->key = key ; sc->u.k = key ; sc->parent = key ; scLoopRight (spread, sc) ; scFree(sc) ; if (spread->interrupt) break ; if (stop && --nn <= 0) /* will not stop if starts at 0 */ break ; new = 0 ; /* remains 0 at end of loop */ } } sortSpreadTable (spread->values, spread->sortColonne) ; spread->valuesModified = TRUE; if (last >= mx) new = 0 ; /* destroy */ if (!stop || !new) spreadDestroyConditions(spread) ; if (lastp) *lastp = new; return spread->values ; } /* spreadCalculateOverKeySet */ KEYSET spreadFilterKeySet (SPREAD spread, KEYSET ks) /* public function */ /* use the KEYSET ks and apply the condition of the master colonne * RETURNS: * a new keyset containing the filtered version of ks */ { KEYSET ks1 = 0, ks2 = keySetCreate () ; COL *masterCol ; if (arrayMax(spread->colonnes) == 0) messcrash("spreadFilterKeySet() called with empty spread->colonnes"); masterCol = arrp(spread->colonnes, 0, COL) ; if (!masterCol->classe) /* class of mastercolumn undefined */ return ks2 ; if (strlen(masterCol->conditionBuffer) > 0 && ! condCheckSyntax(messprintf(" %s", masterCol->conditionBuffer))) return ks2 ; ks1 = query (ks, messprintf("CLASS %s", name(masterCol->classe))) ; if (strlen(masterCol->conditionBuffer) > 0) ks2 = query (ks1, masterCol->conditionBuffer) ; else { ks2 = ks1 ; ks1 = 0 ; } keySetDestroy (ks1) ; return ks2 ; } /* spreadFilterKeySet */ /*****************************************************/ void uSpreadDestroyColonnes (Array colonnes) /* private within SpreadPackage */ /* DON'T call directly, use the spreadDestroyColonnes() macro */ /* clear all spread->colonnes */ { int i; if (arrayExists(colonnes)) { i = arrayMax(colonnes); while (i--) spreadDestroyCol(arrp(colonnes,i,COL)) ; arrayDestroy(colonnes); } return; } /* uSpreadDestroyColonnes */ /*****************************************************/ COL *spreadColInit (SPREAD spread, int colNum) /* private within SpreadPackage */ /* returns number of added colonne */ { COL *c ; c = arrayp(spread->colonnes, colNum, COL) ; c->tagStack = 0 ; c->type = 0 ; c->realType = 0 ; c->colonne = colNum ; c->width = 12 ; c->flipped = FALSE ; c->showType = SHOW_ALL ; c->nonLocal = FALSE ; c->from = 1; c->presence = -1; /* init with -1 ... */ c->extend = -1; /* ... to force change ... */ c->hidden = -1; /* ... in the functions below */ spreadColSetPresence(spread, colNum, VAL_IS_OPTIONAL); spreadColSetExtend(spread, colNum, COLEXTEND_FROM); spreadColSetHidden(spread, colNum, FALSE); memset(c->headerBuffer, 0, 60); memset(c->conditionBuffer, 0, 360); memset(c->tagTextBuffer, 0, 360); spread->isModified = TRUE ; return c; } /* spreadColInit */ /********************************/ int spreadRemoveColonne (SPREAD spread, int colNum) { COL *c, *c1 ; int i ; if (colNum <= 0) return 0; /* cannot remove master-column */ c = arrayp(spread->colonnes, colNum, COL) ; spreadDestroyCol(c) ; for (i = colNum, c = arrayp(spread->colonnes, i, COL); i < arrayMax(spread->colonnes) - 1 ; i++) { c1 = c++ ; memcpy(c1, c, sizeof(COL)) ; } arrayMax(spread->colonnes) -- ; for (i = colNum ; i < arrayMax(spread->colonnes) ; i++) { c = arrayp(spread->colonnes, i, COL) ; if (c->from > colNum + 1) c->from -- ; else if (c->from == colNum + 1) messout ("You should modify colonne %d, which is derived from the colonne you just destroyed", i + 2) ; } spread->isModified = TRUE ; return colNum - 1; } /* spreadRemoveColonne */ /*****************************************************/ TABLE *spreadCalculateOverWholeClass (SPREAD spread) { KEYSET ks; int n ; char *cp ; if (!spread->isModified && spread->precompute && spread->precomputeAvailable && spread->values) /* return the precomputed table */ { printf ("returning existing results\n"); return spread->values; } if (!spreadCheckConditions(spread)) goto abort; if (externalServer) /* compute on server side */ { Stack ss = stackCreate (1000); ACEOUT dump_out = aceOutCreateToStack (ss, 0); spreadDumpColonneDefinitions (dump_out, spread->colonnes) ; aceOutDestroy (dump_out); n = stackMark(ss) ; if (!n) { stackDestroy (ss) ; return (TABLE*)NULL ; } pushText (ss, "Table -a = ") ; cp =stackText (ss, 0) ; catText (ss, cp) ; cp = stackText(ss, n) ; spread->values = externalTableMaker (cp) ; stackDestroy (ss) ; return spread->values ; } ks = spreadGetMasterKeyset (spread) ; if (!ks) goto abort ; /* if we get here we have at least one correct colonne def */ spreadCalculateOverKeySet (spread, ks, NULL); keySetDestroy(ks) ; return spread->values; abort: return (TABLE*)NULL; } /* spreadCalculateOverWholeClass */ /********************************/ BOOL spreadCheckConditions(SPREAD spread) /* private within SpreadPackage */ { COL *colonne = arrp (spread->colonnes, 0, COL) - 1 ; COND cond = 0 ; int from, icol = 0 , max = arrayMax (spread->colonnes) ; int ctype ; while (icol++, colonne++, max--) { colonne->tagCondition = colonne->conditionCondition = 0 ; from = colonne->from - 1 ; if (icol > 1) { if (from + 1 >= icol || from < 0) { messout ("From field %d of column %d is wrong", from + 1, icol) ; return FALSE ; } ctype = arrp (spread->colonnes, from, COL)->type ; if (colonne->extend == COLEXTEND_FROM && ctype != 'k' && ctype != 'K' && ctype != 'n') { messout ("Column %d is computed from column %d which " "is not a list of objects", icol, from + 1) ; return FALSE ; } } if (colonne->type == 't' && !colonne->text) { colonne->text = stackReCreate(colonne->text, 100) ; pushText(colonne->text, "\177NULL") ; /* so 0 means undefined */ } if (colonne->tagStack && stackText(colonne->tagStack, 0)) { if (!condConstruct (stackText(colonne->tagStack,0), &cond)) { messout ("syntax error in Tag field :%s", stackText(colonne->tagStack, 0)) ; return FALSE ; } colonne->tagCondition = cond ; } else { if (!colonne->tag) cond = 0 ; else if (!condConstruct (name(colonne->tag), &cond)) { messout ("syntax error in Tag field :%s", name(colonne->tag)) ; return FALSE ; } colonne->tagCondition = cond ; } colonne->condHasParam = isParamInString (colonne->conditionBuffer); colonne->conditionCondition = 0 ; if (!colonne->condHasParam) { /****** no parameters *******/ if (colonne->conditionBuffer && *colonne->conditionBuffer) { if (!condConstruct (colonne->conditionBuffer, &cond)) { messout ("syntax error in Tag field :%s", colonne->conditionBuffer) ; return FALSE ; } else colonne->conditionCondition = cond ; } } else { /********** do parameter substitution **********/ Stack s ; int i, i1, mine, n1, n2 ; char cc, *cp = colonne->conditionBuffer, *cq ; /* must check here that substitution is compatible with syntax */ cp-- ; while (*++cp) if (*cp == '%') { cc = *(cp + 1) ; if (cc == '%') { messout("Give a value to %% before running in column %d at:\n%s", icol , cp) ; return FALSE ; } cq = cp + 1; i = 0 ; i1 = 1 ; while (*cq >= '0' && *cq <= '9') { i *= 10 ; i += (*cq - '0') ; cq++ ; } if (i <=0 || i > icol) { messout("Cannot substitute parameter in column %d at:\n%s", icol, cp) ; return FALSE ; } } s = stackCreate(500) ; pushText (s, colonne->conditionBuffer) ; n1 = stackMark(s) ; pushText (s, " ") ; for (i = 0 ; i < icol ; i++) { /* use same value as in scMatch */ switch (arrp (spread->colonnes, i, COL)->type) { case 'b': case 'k': case'n': case 'K': case't': catText (s," __toto__ ") ; break ; case 'i': case 'f': case 'c': catText (s," 1 ") ; break ; case 'd': catText (s," now ") ; break ; } } mine = freesettext (stackText(s, 0), stackText(s,n1)) ; freespecial (";\n\t/%\\") ; /* No subshells ($ removed) */ n2 = stackMark(s) ; while (freecard(mine)) pushText (s, freepos()) ; if (!condCheckSyntax(stackText(s,n2))) { messout( "After test substitution, I cannot parse condition of col %d :\n%s", icol, stackText(s,n2)) ; stackDestroy (s) ; return FALSE ; } stackDestroy (s) ; colonne->conditionCondition = 0 ; /* do not register in this case */ } } return TRUE ; } /* spreadCheckConditions */ BOOL spreadGetPrecomputation (SPREAD spread) /* find the stored table results for the tableDefinition object * and return the precomuted table. * no definition have to be loaded into 'spread' at this point. * * PARAMETERS: * spread - the table-maker definitions - * IN: ->tableDefKey needs to be set * OUT: ->values * ->tableResultKey * ->precomputeAvailable * may be initialised * * RETURNS : * TRUE - if spread->tableDefKey is a valid object * which has a 'params'-based precomputation stored * FALSE - no precomputed table could be retrieved */ { KEY resultKey = spread->tableResultKey; TABLE *table = 0; char *resultName = 0; resultName = getPrecomputationName(spread->tableDefKey, spread->paramBuffer, spread->extraParameters, 0); if (!resultName) goto unavailable; /* tableDefKey may be invalid */ lexword2key(resultName, &resultKey, _VTableResult) ; messfree(resultName); if (!resultKey) goto unavailable; table = tableGet(resultKey) ; if (!table) goto unavailable; messout ("Load precompute from TableResult : \"%s\"\n", name(resultKey)); spread->precomputeAvailable = TRUE; spread->tableResultKey = resultKey; if (spread->values) tableDestroy(spread->values); spread->values = table; return TRUE; unavailable: spread->precomputeAvailable = FALSE; return FALSE; } /* spreadGetPrecomputation */ /****************************************************************** ************************ private functions *********************** ******************************************************************/ static SPREAD spreadCreate (void) { SPREAD spread ; spread = (SPREAD) messalloc(sizeof(struct SpreadStruct)); spread->magic = &SPREAD_MAGIC; spread->colonnes = arrayCreate(6, COL) ; spread->isModified = FALSE; /* definitions changed yet */ spread->valuesModified = FALSE; /* results haven't changed yet */ /* we don't know yet were the definitions will be loaded from */ spread->tableDefKey = 0; spread->filename = NULL; spread->extraParameters = NULL; return spread ; } /* spreadCreate */ /*****************************************/ static void spreadDestroyCol (COL* c) /* clear just the specified colonne */ { stackDestroy(c->text) ; stackDestroy(c->tagStack) ; return; } /* spreadDestroyCol */ /********************************/ static void spreadDestroyConditions(SPREAD spread) { COL *colonne = arrp (spread->colonnes, 0, COL) - 1 ; int max = arrayMax (spread->colonnes) ; while (colonne++, max--) { condDestroy (colonne->tagCondition) ; condDestroy (colonne->conditionCondition) ; colonne->tagCondition = colonne->conditionCondition = 0 ; } return; } /* spreadDestroyConditions */ /********************************/ static void sortSpreadTable (TABLE *table, int sortCol) /* sort the results-table according to the sortCol */ { char *sortSpec = (char*)messalloc((table->ncol*2)+1); int i, n; if (sortCol == 0) { /* sort left to right */ for (i = 0; i < table->ncol; i++) { sortSpec[i*2] = '+'; sortSpec[i*2+1] = '1'+i; } } else { /* sortColonne has first sort priority, rest of columns in * left-right order - all ascending */ sortSpec[0] = '+'; sortSpec[1] = '1'+(sortCol-1); n = 1; for (i = 0; i < table->ncol; i++) { if (i == (sortCol-1)) continue; sortSpec[n*2] = '+'; sortSpec[n*2+1] = '1'+i; n++; } } tableSort(table, sortSpec); tableMakeUnique(table); messfree(sortSpec); return; } /* sortSpreadTable */ /**************************************************************/ /* self managed calloc */ static Stack freeScStack = 0 ; static int nScUsed = 0 , nScAlloc = 0 ; /* useful for debug */ static SPREADCELL scAlloc (void) /* self managed calloc */ { static int blocSize = 256 ; /* i need no more SPREADCELL than columns */ SPREADCELL p = 0 ; int i ; if (!freeScStack) { nScUsed = nScAlloc = 0 ; blocSize = 256 ; freeScStack = stackCreate (sizeof(SPREADCELL)*blocSize) ; } if (stackEmpty (freeScStack)) { p = (SPREADCELL) messalloc (blocSize * sizeof (struct SpreadCellStruct)) ; for (i = blocSize ; i-- ; ++p) push (freeScStack,p,SPREADCELL) ; nScAlloc += blocSize ; blocSize *= 2 ; } p = pop (freeScStack, SPREADCELL) ; memset (p, 0, sizeof (struct SpreadCellStruct)) ; nScUsed ++ ; return p ; } /* scAlloc */ /********************************/ static void scFree (SPREADCELL sc) { if (sc && freeScStack) { if (sc->obj) bsDestroy (sc->obj) ; if (sc->mark) messfree (sc->mark) ; push (freeScStack, sc, SPREADCELL) ; nScUsed-- ; } else invokeDebugger() ; return; } /* scFree */ /*****************************************************/ static BOOL isParamInString (char *cp) { if (cp) /* find '%' char in the condition-string */ while (*cp) { if (*cp++ == '%') return TRUE ; } return FALSE ; } /* isParamInString */ /**********************************************************************/ /************************ Actual calculation **************************/ /**********************************************************************/ static BOOL scMatch (SPREADCELL sc, OBJ *objp, KEY key) { static Stack s = 0 ; SPREADCELL cc ; int mine, n0, n1, n2 ; BOOL found = FALSE ; char *cp = 0 ; switch (sc->col->type) { case 'i': case 'c': cp = strnew(messprintf("%d", sc->u.i), 0) ; key = _Text ; break ; case 'f': cp = strnew(messprintf("%g", sc->u.f), 0) ; key = _Text ; break ; case 'd': cp = strnew(messprintf("\"%s\"", timeShow(sc->u.time)), 0) ; key = _Text ; break ; case 't': cp = strnew(stackText(sc->col->text, sc->u.i), 0) ; key = _Text ; break ; default: break ; } s = stackReCreate(s, 256) ; /* prepare the text of the query with param substituted */ n0 = stackMark(s) ; pushText(s, sc->col->conditionBuffer) ; n1 = stackMark(s) ; pushText (s, " ") ; /* push now, so I can then cat */ stackClear (scStack) ; cc = sc ; do { push (scStack, cc, SPREADCELL) ; } while ((cc = cc->up)) ; while (!stackEmpty(scStack)) { catText(s, " ") ; cc = pop(scStack, SPREADCELL) ; if (!cc->iCol) { catText(s, freeprotect (name(cc->u.k))) ; continue ; } if (cc->empty) /* use same value as in checkSyntax */ switch (cc->col->type) { case 'i': case 'f': case 'c': catText (s," 1 ") ; break ; case 'd': catText (s," now ") ; break ; case 'b': case 'k': case'n': case 'K': case't': default: catText (s," __toto__ ") ; break ; } else switch (cc->col->type) { case 'i': case 'c': catText (s,messprintf("%d", cc->u.i) ) ; break ; case 'f': catText (s,messprintf("%g", cc->u.f) ) ; break ; case 'd': catText (s,messprintf("\"%s\"", timeShow(cc->u.time))) ; break ; case't': catText(s, freeprotect (stackText(cc->col->text, cc->u.i))) ; break ; case 'b': case 'k': case'n': case 'K': default: catText(s, freeprotect (name(cc->u.k))) ; break ; } } /* rely on freesubs to actually substitute the param in the query */ mine = freesettext (stackText(s, n0), stackText(s,n1)) ; freespecial (";\n\t/%\\") ; /* No subshells ($ removed) */ n2 = stackMark(s) ; while (freecard(mine)) pushText (s, freepos()) ; /* evaluate the query */ if (condCheckSyntax(stackText(s,n2)) && queryFind21(objp,key,stackText(s,n2), cp)) found = TRUE ; messfree (cp) ; return found ; } /* scMatch */ /*****************************************************/ static BOOL scQuery (SPREAD spread, SPREADCELL s2, SPREADCELL sc) { KEY k = 0, direction = _bsRight ; OBJ obj = s2->obj ; int nn = 0 , xi, xxi = 0, xxi2 = 0 ; float xf = 0, xxf = 0, xxf2 = 0 ; mytime_t xtm , xxtm = 0 ; char *xt ; if (!sc->iCol) /* accept column one */ return TRUE ; if (sc->mark) { if (!obj) messcrash("Error 1 in scQuery, sorry") ; bsGoto (obj, sc->mark) ; direction = _bsDown ; } else { if (sc->col->extend == COLEXTEND_RIGHT_OF) /* start inside obj */ { if (!obj && sc->scFrom->mark) messcrash("Error 1 in scQuery, sorry") ; if (!obj && !(obj = s2->obj = bsCreate (sc->scGrandParent->key))) return FALSE ; bsGoto (obj, sc->scFrom->mark) ; } else /* start at root */ { if (obj) bsGoto (obj, 0) ; } if (!obj && sc->col->tag && !bIndexFind(s2->key, sc->col->tag)) return FALSE ; if (!queryFind3(sc->col->tagCondition, &(s2->obj), sc->scGrandParent->key)) return FALSE ; obj = s2->obj ; /* in case we just created obj */ if (!obj && !(obj = s2->obj = bsCreate (sc->scGrandParent->key))) return FALSE ; /* if queryFind3 did not create the object */ direction = _bsRight ; } /* do not iterate inside a column on tags or on arithmetic evaluators */ if (sc->mark && (sc->col->type == 'b' || sc->col->showType != SHOW_ALL)) return FALSE ; while (TRUE) { switch (sc->col->type) { case 'b': if (sc->mark || !bsGetKeyTags (obj, _bsHere, &k)) return nn ? TRUE : FALSE ; direction = _bsDown ; while (k && ((class(k) == _VComment) || (class(k) == _VUserSession)) ) { k = 0 ; bsGetKeyTags (obj, _bsHere, &k) ; } if (!k) return nn ? TRUE : FALSE ; sc->u.k = sc->key = k ; break ; case 'c': /* count */ if (sc->mark || !bsGetKeyTags (obj, direction, &k)) return FALSE ; direction = _bsDown ; nn = 0 ; do { if (class(k) != _VComment && class(k) != _VUserSession) nn++ ; } while (bsGetKeyTags (obj, direction, &k)) ; if (!nn) return FALSE ; sc->u.i = nn ; goto done ; break ; case 'k': case'n': case 'K': if (!bsGetKeyTags (obj, direction, &k)) return nn ? TRUE : FALSE ; direction = _bsDown ; while (k && ((class(k) == _VComment) || (class(k) == _VUserSession)) ) { k = 0 ; bsGetKeyTags (obj, _bsHere, &k) ; } if (!k) return nn ? TRUE : FALSE ; sc->u.k = sc->key = k ; break ; case 'i': if (!bsGetData (obj, direction, _Int, &xi)) return nn ? TRUE : FALSE ; sc->u.i = xi ; break ; case 'f': if (!bsGetData (obj, direction, _Float, &xf)) return nn ? TRUE : FALSE ; sc->u.f = xf ; break ; case 'd': if (!bsGetData (obj, direction, _DateType, &xtm)) return nn ? TRUE : FALSE ; sc->u.time = xtm ; break ; case 't': { /* this is a hack, but it seems that a Text is preceded by a cell of type Text but with no text inside it, at least sometimes */ encore: if (!bsGetData (obj, direction, _Text, &xt)) return nn ? TRUE : FALSE ; if (!xt || !*xt) { direction = _bsDown ; goto encore ; } } sc->u.i = stackMark(sc->col->text) ; pushText (sc->col->text, xt) ; break ; } /* SHOW_ALL=0, SHOW_MIN, SHOW_MAX, SHOW_AVG */ switch (sc->col->showType) { case SHOW_ALL: goto done ; case SHOW_MIN: switch (sc->col->type) { case 'i': if (!nn || xxi > xi) xxi = xi ; sc->u.i = xxi ; /* reset */ break ; case 'f': if (!nn || xxf > xf) xxf = xf ; sc->u.f = xxf ; break ; case 'd': if (!nn || xxtm > xtm) xxtm = xtm ; sc->u.time = xxtm ; break ; default: /* undefined on other types */ return FALSE ; } break ; case SHOW_MAX: switch (sc->col->type) { case 'i': if (!nn || xxi < xi) xxi = xi ; sc->u.i = xxi ; /* reset */ break ; case 'f': if (!nn || xxf < xf) xxf = xf ; sc->u.f = xxf ; break ; case 'd': if (!nn || xxtm < xtm) xxtm = xtm ; sc->u.time = xxtm ; break ; default: /* undefined on other types */ return FALSE ; } break ; case SHOW_AVG: switch (sc->col->type) { case 'i': if (!nn) xxi = 0 ; xxi += xi ; sc->u.i = xxi/(nn + 1) ; /* reset */ break ; case 'f': if (!nn) xxf = 0 ; xxf += xf ; sc->u.f = xxf/(nn + 1) ; break ; default: /* undefined on other types */ return FALSE ; } break ; case SHOW_VAR: switch (sc->col->type) { case 'i': if (!nn) { xxi = 0 ; xxi2 = 0 ; sc->u.i = 0 ; } xxi += xi ; xxi2 += xi * xi ; if (nn > 1) sc->u.i = (xxi2 -(xxi*xxi/((float)nn)))/(nn - 1.0); /* reset */ break ; case 'f': if (!nn) { xxf = 0 ; xxf2 = 0 ; sc->u.f = 0 ; } xxf += xf ; xxf2 += xf * xf ; if (nn > 1) sc->u.f = (xxf2 - xxf*xxf/nn)/(nn - 1.0) ; break ; default: /* undefined on other types */ return FALSE ; } break ; case SHOW_SUM: switch (sc->col->type) { case 'i': if (!nn) xxi = 0 ; xxi += xi ; sc->u.i = xxi ; /* reset */ break ; case 'f': if (!nn) xxf = 0 ; xxf += xf ; sc->u.f = xxf ; break ; default: /* undefined on other types */ return FALSE ; } break ; default: break ; } nn++ ; direction = _bsDown ; } done: sc->mark = bsMark (obj, sc->mark) ; sc->parent = bsParentKey(obj) ; return TRUE ; } /* scQuery */ /*****************************************************/ static BOOL scCondition (SPREAD spread, SPREADCELL sc) { BOOL found = FALSE; BOOL isCond = *sc->col->conditionBuffer ? TRUE : FALSE; BOOL hasParam = sc->col->condHasParam; OBJ obj = 0 ; KEY key ; char *cp = 0 ; if (!isCond) return TRUE ; switch (sc->col->type) { case 'b': return TRUE ; break ; case 'n': case 'k': if (!lexIsInClass(sc->key, sc->col->classe)) return FALSE ; key = sc->key ; break ; case 'K': /* no class check in case K */ key = sc->key ; break ; case 'i': case 'c': cp = strnew(messprintf("%d", sc->u.i),0 ) ; key = _Text ; break ; case 'f': cp = strnew(messprintf("%g", sc->u.f),0 ) ; key = _Text ; break ; case 'd': cp = strnew(messprintf("\"%s\"", timeShow(sc->u.time)),0) ; key = _Text ; break ; case 't': cp = strnew(stackText(sc->col->text, sc->u.i),0) ; key = _Text ; break ; default: key = _Text ; break ; } if (hasParam) found = scMatch (sc, &obj, key) ; else found = sc->col->conditionCondition && queryFind31(sc->col->conditionCondition, &obj, key, cp) ; messfree (cp) ; bsDestroy (obj) ; return found ; } /* scCondition */ /*****************************************************/ static int scLoopDown (SPREAD spread, SPREADCELL s2, SPREADCELL sc) { int n = 0; SpPresenceType mand = sc->col->presence ; if (!(sc->scFrom && sc->scFrom->empty) && !(sc->scFrom && sc->scFrom->col && sc->scFrom->col->nonLocal)) while (TRUE) /* loop on the whole column */ { if (messIsInterruptCalled()) { spread->interrupt = TRUE ; return n ; } if (!scQuery (spread, s2, sc)) break ; /* no more target */ if (mand == VAL_MUST_BE_NULL) return 1 ; /* do not export */ if (scCondition(spread, sc)) /* target acceptable */ n += scLoopRight (spread, sc) ; /* expand and export */ if (sc->col->showType != SHOW_ALL) break ; } if (n == 0 && mand != VAL_IS_REQUIRED) /* null or optional */ { sc->empty = TRUE ; n = scLoopRight (spread, sc) ; /* explore once */ } return n ; } /* scLoopDown */ /*****************************************************/ static void scExport (SPREAD spread, SPREADCELL sc) /* finished to calclulate the row for one value in the * first column - now add the resulting row of values * to the results table */ { int i = 0; COL *colonne ; stackClear (scStack) ; for (i = sc->iCol + 1, colonne = arrp (spread->colonnes, sc->iCol, COL) + 1 ; i < arrayMax(spread->colonnes) ; i++, colonne++) if (colonne->presence == VAL_IS_REQUIRED) return ; /* missing non optional column, do not export */ { COL *colonne; SPREADCELL cc ; int rowNum, colNum; rowNum = tableMax(spread->values); colNum = spread->values->ncol - 1; cc = sc; do { colonne = cc->col; if (!colonne) /* master-column has cc->col == NULL */ colonne = arrayp (spread->colonnes, 0, COL); if (colonne && colonne->type && !colonne->hidden) { if (cc->empty) tableSetEmpty(spread->values, rowNum, colNum); else switch(spread->values->type[colNum]) { case '0': tableSetEmpty(spread->values, rowNum, colNum); break; case 'i': tableInt (spread->values, rowNum, colNum) = cc->u.i; break; case 'f': tableFloat (spread->values, rowNum, colNum) = cc->u.f; break; case 'k': tableKey (spread->values, rowNum, colNum) = cc->u.k; break; case 't': tableDate (spread->values, rowNum, colNum) = cc->u.time; break; case 's': if (stackExists(cc->col->text)) tableSetString (spread->values, rowNum, colNum, stackText(cc->col->text, cc->u.i)); else tableSetEmpty (spread->values, rowNum, colNum); break; default: messcrash("scExport() - unkown table->type"); } tableParent(spread->values, rowNum, colNum) = cc->parent; tableGrandParent(spread->values, rowNum, colNum) = cc->scFrom ? cc->scFrom->key : cc->key ; colNum--; } } while ((cc = cc->up)); } return; } /* scExport */ /*****************************************************/ static int scLoopRight (SPREAD spread, SPREADCELL sc) { int n = 0, from ; SPREADCELL sr = 0, s2, srFrom = 0 ; if(!spread->interrupt && (sc->iCol == 0 || (sc->col && sc->col->type)) && sc->iCol + 1 < arrayMax(spread->colonnes)) { /* go to the right */ sr = scAlloc () ; sr->up = sc ; sr->iCol = sc->iCol + 1 ; sr->col = arrp (spread->colonnes, sr->iCol, COL) ; srFrom = sr ; from = sr->col->from - 1 ; while (srFrom->iCol != from && srFrom->up) srFrom = srFrom->up ; if (srFrom == sr || srFrom->iCol != from) { messerror ("Confusion 1 in scLoopRight") ; return 1 ; } sr->scFrom = srFrom ; s2 = sr ; while (s2 && s2->col && s2->col->extend == COLEXTEND_RIGHT_OF) s2 = s2->scFrom ; if (s2 && s2->col) s2 = s2->scFrom ; sr->scGrandParent = s2 ; if (sr->col && sr->col->type) n = scLoopDown (spread, s2, sr) ; scFree (sr) ; } if (n == 0) /* then export self */ { n = 1 ; scExport (spread, sc) ; } bsDestroy (sc->obj) ; /* release memory NOW */ return n ; } /* scLoopRight */ /********************************/ static void initSpreadTable (SPREAD spread) { COL *col; int colNum, i; char *typeString; typeString = getSpreadTableTypes(spread->colonnes, 0); spread->values = tableCreate(50, typeString); messfree (typeString); i = 0; for (colNum = 0; colNum < arrayMax(spread->colonnes); colNum++) { col = arrp(spread->colonnes, colNum, COL); if (!col->type) continue; if (col->hidden) continue; if (col->headerBuffer == NULL) messcrash("initSpreadTable() - col->headerBuffer == NULL"); tableSetColumnName(spread->values, i, col->headerBuffer); i++; } return; } /* initSpreadTable */ /******************************************************/ static char* getSpreadTableTypes (Array colonnes, STORE_HANDLE handle) { int i, colNum, colMax = arrayMax(colonnes) ; COL *col ; char *types = (char*)halloc (colMax + 1, handle) ; i = 0; for(colNum = 0 ; colNum < colMax; colNum++) { col = arrp(colonnes, colNum, COL) ; if (!col->hidden) switch (col->type) { case 'c': case 'i': types[i++] = 'i' ; break ; case 'f': types[i++] = 'f' ; break ; case 'd': types[i++] = 't' ; break ; /* dates */ case 'k': case 'K': case 'n': case 'b': types[i++] = 'k' ; break ; case 't': types[i++] = 's' ; break ; /* text */ case 0: /* type undefined (colonne definition incomplete) * -> no column in result-table here */ break; default: messcrash("getSpreadTableTypes() - invalid col->type"); break; } } types[i] = 0 ; if (i == 0) /* this means that there is no colonne with a valid type * this can happen is all column are hidden. * Since the table package barfs on zero-column tables * we fake something up. */ { types[0] = 't'; types[1] = 0; } return types ; } /* getSpreadTableTypes */ /*****************/ static char* getPrecomputationName (KEY tableKey, char *internalParams, char *commandLineParams, STORE_HANDLE handle) { char *cp; char *fullname = 0; if ((class(tableKey) == _VTable)) { Stack s = stackCreate(100); ACEIN param_in; int mark; pushText(s, name(tableKey)); catText (s, "#"); /* construct the full name of the TableResult object where the * result-table is stored. This name incorporates the parameters */ param_in = aceInCreateFromText (messprintf("%s %s", internalParams ? internalParams : "", commandLineParams ? commandLineParams : ""), NULL, 0); aceInSpecial (param_in, ""); aceInCard (param_in); mark = stackMark(s); while ((cp = aceInWord(param_in))) { if (strncmp(stackText(s, mark), "#", 1) != 0) catText(s, "#"); catText(s, cp); mark = stackMark(s); } aceInDestroy (param_in); fullname = strnew(stackText(s, 0), handle); stackDestroy (s); /* fullname = halloc ((strlen(name(tableKey)) + (params ? strlen(params) : 0) + 8), handle); cp = fullname ; cq = name(tableKey) ; while ((*cp++ = *cq++)) ; if (params) { cp-- ; *cp++ = '#' ;} cq = params ? params : "" ; while (*cq == ' ' && *cq) cq++ ; while(*cq) { if (*cq == ' ') { *cp++ = '#' ; while (*cq == ' ') cq++ ; } *cp++ = *cq++ ; } */ } return fullname; } /* getPrecomputationName */ /******************************************************/ static void storePrecomputedTable(KEY defKey, char *internalParams, char *commandLineParams, TABLE *table) { OBJ obj = 0 ; KEY _Precompute ; KEY resultKey; char *resultName; if (!defKey || (class(defKey) != _VTable)) /* can only store TableResult, if the definitions are * stored in a Table-object */ return; if (!table) /* has to have a results table */ return; if (tableMax(table) == 0) return; /* don't store empty results-tables */ /* init the TableResult object which will store the precomputed table */ resultName = getPrecomputationName (defKey, internalParams, commandLineParams, 0); lexaddkey(resultName, &resultKey, _VTableResult) ; messfree(resultName); /* add a tag to the definition object to remember * where the precomputed table was stored */ if (!(obj = bsUpdate(defKey))) return ; messout ("Store precompute in TableResult : \"%s\"\n", name(resultKey)); lexaddkey("Precompute", &_Precompute, 0) ; bsAddKey (obj, _Precompute, resultKey) ; bsSave (obj) ; /* write the table-data to the TableResult-object */ tableStore (resultKey, table) ; return; } /* storePrecomputedTable */ /******************** eof **********************************/