/* File: table.c * Author: Richard Durbin (rd@sanger.ac.uk) * Copyright (C) J Thierry-Mieg and R Durbin, 1996 * ------------------------------------------------------------------- * 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@sanger.ac.uk, and * Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr * * SCCS: $Id: table.c,v 1.70 2004/04/09 22:24:24 mieg Exp $ * Description: * Exported functions: * HISTORY: * Last edited: Jul 9 12:03 2003 (rnc) * * Jun 15 18:17 2000 (rdurbin): -x, -X, -y, -Y options for tableOut() produce XML (-x for simple HTML) * * Jun 15 18:16 2000 (rdurbin): -k, -K options as -j/-J variants * * Oct 20 16:06 1999 (fw): dumping of tables to ACEOUT inst. of freeOut * * Aug 26 18:19 1998 (fw): proper tableSort over multiple columns * * Aug 25 14:25 1998 (fw): tableOut now writes NULL for '0' column-values * * Aug 14 12:07 1998 (fw): fixed silly bug in tableCreateFromKeySet() * * Jul 15 16:48 1998 (fw): fixed silly bug in tableMax() * Created: Thu Oct 17 14:29:36 1996 (rd) *------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for lexstrcmp */ BITSET_MAKE_BITFIELD /* define bitField for bitset.h ops */ static magic_t TABLE_MAGIC = "TABLE"; static BOOL tableExists (TABLE *tt); static void tableFinalise (void *block); static TABLE *tableDoDoParse (ACEIN parse_io, char *table_name, char *format_type, Array oldClass, char **errtext); static char* tableParseFormat (ACEIN parse_io, Array oldClass); static BOOL setColumnVisibility (TABLE *t, int colNum, int on_off); /****************************************************************** ************************* public functions *********************** ******************************************************************/ TABLE *tableHandleCreate (int size, char *type, STORE_HANDLE parent) /* type-string is 0-terminated string with format-char for each * column. possibilities are "kgbifst" * upper-case type-character indicates that column is hidden */ { TABLE *t ; int i, ncol ; /* first check type is OK */ if (!type || !*type) messcrash ("tableCreate: received null or empty type string") ; for (i = 0 ; i < strlen(type); ++i) switch (ACEIN_LOWER[(int)type[i]]) { case 'k': case 'g': case 'b': case 'i': case 'f': case 's': case 't': case 'a': case '0': break ; default: messcrash ("tableCreate: bad type %c for column %d", type[i], i+1) ; } if (sizeof(float) != sizeof (KEY) || sizeof(int) != sizeof (KEY) || sizeof(TABLETYPE) != sizeof (KEY) || sizeof(mytime_t) != sizeof (KEY)) messcrash ("%s\n%s", "On this platform different TABLETYPEs have different byte size", " Rewrite the table.h pointer macros tabXxxp ! ") ; /* make the table and deal with handles */ t = (TABLE *)halloc(sizeof(TABLE), parent) ; blockSetFinalise (t, tableFinalise) ; /* initialise TABLE */ t->magic = &TABLE_MAGIC ; /* do not allocate the internal handle on the given handle, * the internal handle is killed by the finalisation function, * and this way allows free-floating allocation with a null-handle */ t->handle = handleCreate () ; t->ncol = ncol = strlen(type) ; t->type = strnew(type, t->handle) ; t->name = (TABLETYPE*) halloc(ncol*sizeof(int), t->handle) ; t->visibility = (BOOL*) halloc(ncol*sizeof(int), t->handle) ; t->col = (Array*) halloc(ncol*sizeof(Array), t->handle) ; t->parents = (KEYSET*) halloc(ncol*sizeof(KEYSET), t->handle); t->grandParents = (KEYSET*) halloc(ncol*sizeof(KEYSET), t->handle); t->emptyBits = (BitSet*) halloc(ncol*sizeof(BitSet), t->handle) ; t->s = stackHandleCreate (4096, t->handle) ; t->s->textOnly = TRUE; pushText (t->s, type) ; /* ! occupy mark zero in a most useful way */ if (size < 64) size = 64 ; /* initialise each column */ for (i = 0 ; i < ncol ; ++i) { /* make each character in the type-string lower-case * but preserve the visibility info given by upper-case * chars in an extra array */ if (isupper((int)t->type[i])) { t->type[i] = ACEIN_LOWER[(int)t->type[i]]; t->visibility[i] = FALSE; } else t->visibility[i] = TRUE; t->name[i].i = stackMark (t->s) ; pushText (t->s, "") ; t->col[i] = arrayHandleCreate (size, TABLETYPE, t->handle) ; t->parents[i] = arrayHandleCreate(size, KEY, t->handle); t->grandParents[i] = arrayHandleCreate(size, KEY, t->handle); t->emptyBits[i] = bitSetCreate (size, t->handle) ; } if (!tableExists(t)) messcrash("tableHandleCreate() - just created invalid TABLE\n" " something seriously screwy - look at tableExists()"); return t ; } /* tableHandleCreate */ /************************************************************/ void uTableDestroy (TABLE *t) /* call via tableDestroy macro */ { messfree (t) ; /* trigger tableFinalise() */ return; } /* uTableDestroy */ /************************************************************/ TABLE *tableCreateFromKeySet (KEYSET keySet, STORE_HANDLE handle) /* make a table of KEYs from a given KEYSET (fw-980805) */ { TABLE *table; int row; if (!keySet) return 0; table = tableHandleCreate (keySetMax(keySet), "k", handle); if (keySetMax(keySet)) { tableKey(table, keySetMax(keySet)-1, 0) = 0; /* set last item to extend size */ for (row = 0; row < keySetMax(keySet); row++) tabKey(table, row, 0) = arr(keySet, row, KEY); } return table; } /* tableCreateFromKeySet */ KEYSET keySetCreateFromTable (TABLE *table, STORE_HANDLE handle) /* makes a keyset from the KEY-type table column (fw-980805) */ { int c, i, keyCol=-1; KEYSET newKeySet; for (c = 0; c < table->ncol; ++c) if (table->type[c] == 'k') keyCol = c; if (keyCol == -1) return 0; /* table contains no column of type KEY */ newKeySet = keySetHandleCreate(handle); if (tableMax(table)) { array(newKeySet, tabMax(table,keyCol)-1, KEY) = 0; /* set last item to extend size */ for (i = 0 ; i < tabMax(table,keyCol) ; ++i) arr(newKeySet,i,KEY) = tabKey(table,i,keyCol) ; } keySetSort(newKeySet) ; keySetCompress(newKeySet) ; return newKeySet; } /* keySetCreateFromTable */ /************************************************************/ TABLE *tableCopy (TABLE *old, STORE_HANDLE handle) { TABLE *new; int colNum; if (!tableExists (old)) messcrash("tableCopy() - received invalid TABLE* pointer"); new = tableHandleCreate(tableMax(old), old->type, handle); /* kill the empty arrays that have been initialised and replace * them with copies of the data in the old table */ stackDestroy(new->s); new->s = stackCopy(old->s, new->handle); for (colNum = 0; colNum < old->ncol; colNum++) { arrayDestroy(new->col[colNum]); new->col[colNum] = arrayHandleCopy(old->col[colNum], new->handle); arrayDestroy(new->parents[colNum]); new->parents[colNum] = arrayHandleCopy(old->parents[colNum], new->handle); arrayDestroy(new->col[colNum]); new->col[colNum] = arrayHandleCopy(old->col[colNum], new->handle); bitSetDestroy(new->emptyBits[colNum]); new->emptyBits[colNum] = bitSetCopy(old->emptyBits[colNum], new->handle); new->name[colNum].i = old->name[colNum].i; new->visibility[colNum] = old->visibility[colNum]; } return new; } /* tableCopy */ /************************************************************/ int tableMax (TABLE *t) /* return the largest number of rows over all columns */ { int colNum, j = 0, max = 0 ; if (!tableExists (t)) messcrash ("tableMax() - received invalid TABLE* pointer"); for (colNum = 0; colNum < t->ncol; colNum++) { j = arrayMax(t->col[colNum]) ; if (j > max) max = j ; } return max ; } /* tableMax */ /************************************************************/ char *tableTypes (TABLE *t) { if (!tableExists (t)) messcrash("tableTypes() - received invalid TABLE* pointer"); return t->type; } /* tableTypes */ /************************************************************/ char *tableGetColumnName (TABLE *t, int i) { if (!tableExists (t)) messcrash("tableGetColumnName() - received invalid TABLE* pointer"); if (i >= 0 && i < t->ncol) return stackText (t->s, t->name[i].i) ; return (char*)NULL; } /* tableGetColumnName */ /************************************************************/ char *tableSetColumnName (TABLE *t, int i, char *name) { char *old ; if (!tableExists (t)) messcrash("tableSetColumnName() - received invalid TABLE* pointer"); if (i < 0 || i >= t->ncol) messcrash ("tableSetColumnName() - col %d is out of bounds 0-%d", i, t->ncol-1) ; if (name == NULL) messcrash ("tableSetColumnName() - name is NULL") ; old = stackText (t->s, t->name[i].i); t->name[i].i = stackMark(t->s) ; pushText (t->s, name) ; return old ; } /* tableSetColumnName */ /************************************************************/ BOOL tableSetColumnHidden (TABLE *t, int colNum) { return setColumnVisibility (t, colNum, FALSE); } BOOL tableSetColumnVisible (TABLE *t, int colNum) { return setColumnVisibility (t, colNum, TRUE); } BOOL tableColumnIsVisible (TABLE *t, int colNum) { return setColumnVisibility (t, colNum, -1); /* don't set, just enquire */ } /* tableColumnIsVisible */ /************************************************************/ void tableSetString (TABLE *t, int i, int j, char *string) { if (!tableExists (t)) messcrash ("tableSetString() - received invalid TABLE* pointer"); if (string && strlen(string) > 0) { tableInt(t,i,j) = stackMark(t->s) ; pushText (t->s, string) ; } else tableInt(t,i,j) = 0 ; tableUnSetEmpty (t, i, j); /* mark table field as non-NULL */ return; } /* tableSetString */ /************************************************************/ BOOL tabValueNull (TABLE *t, int i, int j) { if (!tableExists (t)) messcrash ("tableValueNull() - received invalid TABLE* pointer"); if (i > tabMax(t,j)) return TRUE ; switch (t->type[j]) { case 'k': case 'g': case 's': case 't': if (!tabKey(t,i,j)) return TRUE ; case 'b': if (tabBool(t,i,j) == NON_BOOL) return TRUE ; break ; case 'a': if (tabFlag(t,i,j) == NON_FLAG) return TRUE ; break ; case 'i': if (tabInt(t,i,j) == NON_INT) return TRUE ; break ; case 'f': if (tabFloat(t,i,j) == NON_FLOAT) return TRUE ; break ; } return FALSE ; } /* tabValueNull */ /************************************************************/ void tableCheckEmpty (TABLE *t, int column) /* set all values in column to NULL */ { int i ; if (!tableExists (t)) messcrash ("tableCheckEmpty() - received invalid TABLE* pointer"); for (i = 0 ; i < tabMax(t,column) ; ++i) if (tabValueNull (t, i, column)) tableSetEmpty(t,i,column) ; else tableUnSetEmpty(t,i,column) ; return; } /* tableCheckEmpty */ /************************************************************/ void tableClear (TABLE *t, int n) /* clear all values of the given table and reinitialise * to max-row-number of 'n' */ { int i ; if (!tableExists (t)) messcrash ("tableClear() - received invalid TABLE* pointer"); for (i = 0 ; i < t->ncol ; ++i) { t->col[i] = arrayReCreate (t->col[i], n, TABLETYPE) ; t->parents[i] = arrayReCreate (t->parents[i], n, KEY) ; t->grandParents[i] = arrayReCreate (t->grandParents[i], n, KEY) ; t->emptyBits[i] = bitSetReCreate (t->emptyBits[i], n) ; } return; } /* tableClear */ /************************************************************/ BOOL tableSetTypes (TABLE *t, char *types) /* can only re-set '0' typed columns returns FALSE if one or more columns already had a type */ { int i ; if (!tableExists (t)) messcrash ("tableSetTypes() - received invalid TABLE* pointer"); for (i = 0 ; i < t->ncol ; ++i) /* check first */ if (types[i] != '0' && types[i] != t->type[i] && t->type[i] != '0') return FALSE ; for (i = 0 ; i < t->ncol ; ++i) if (types[i] != '0' && types[i] != t->type[i]) t->type[i] = types[i] ; strcpy (stackText(t->s, 0), t->type) ; return TRUE ; } /* tableSetTypes */ /*********************************************************/ static TABLE *sortTable = 0 ; static Array sortSpecList = 0; static int tableCompare (const void* va, const void* vb) /* comparison function used for qsort() in tableSort, that also rearranges an index array of row numbers, requires static sortSpec and sortTable to be assigned */ { register int a = *(int*)va ; register int b = *(int*)vb ; register int i, sortCol, sortSpec; for (i = 0; i < arrayMax(sortSpecList); ++i) { sortSpec = arr(sortSpecList,i,int); /* get the true sort column in C-numbers */ if (sortSpec < 0) /* descending */ sortCol = -sortSpec - 1; else /* sortSpec > 0 -> ascending */ sortCol = sortSpec - 1; /* we make NULL the smallest value ever */ if (tabEmpty(sortTable, a, sortCol)) { if (tabEmpty(sortTable, b, sortCol)) continue ; return sortSpec>0 ? -1 : 1 ; } if (tabEmpty(sortTable, b, sortCol)) return sortSpec>0 ? 1 : -1 ; if (tabKey(sortTable, a, sortCol) == tabKey(sortTable, b, sortCol)) continue ; switch (sortTable->type[sortCol]) { case 'k': case 'g': { KEY xk = tabKey(sortTable, a, sortCol), yk = tabKey(sortTable, b, sortCol) ; int r = keySetAlphaOrder(&xk, &yk); if (r != 0) return sortSpec>0 ? r : -r ; } break; case 'b': { BOOL ba = tabBool(sortTable, a, sortCol), bb = tabBool(sortTable, b, sortCol) ; int r = ba ? (bb ? -1 : 0 ) : ( bb ? 0 : 1 ); if (r != 0) return sortSpec>0 ? r : -r ; } break; case 'i': { int ba = tabInt(sortTable, a, sortCol), bb = tabInt(sortTable, b, sortCol) ; int r = ba - bb; if (r != 0) return sortSpec>0 ? r : -r ; } break; case 'f': { float ba = tabFloat(sortTable, a, sortCol), bb = tabFloat(sortTable, b, sortCol) ; float rr = ba - bb; int r = ((rr > 0)? 1: -1); if (rr != 0) return sortSpec>0 ? r : -r ; } break; case 's': { char *ba = tabString(sortTable, a, sortCol), *bb = tabString(sortTable, b, sortCol) ; int r = lexstrcmp(ba,bb) ; if (r != 0) return sortSpec>0 ? r : -r ; } break; case 't': { mytime_t ba = tabDate(sortTable, a, sortCol), bb = tabDate(sortTable, b, sortCol) ; int r = ba - bb; if (r != 0) return sortSpec>0 ? r : -r ; } break; } /* end switch */ } /* default keep original order */ if (a > b) return +1 ; return -1 ; } /* tableCompare */ /************************************************************/ BOOL tableSort (TABLE *t, char *spec) /* sorts elements in the table according to the sort specifier : spec should look like "-2+4+1" which would specify first priority column 2 reversed, then column 4, then column 1. returns FALSE if table is wrong, or spec is out of range */ { STORE_HANDLE scopeHandle; int i, numRows, specLen, sortCol; int *indexArray ; if (!tableExists (t)) messcrash ("tableSort() - received invalid TABLE* pointer"); if (!spec) messcrash ("tableSort() - received NULL sort specifier"); specLen = strlen(spec); if ((specLen % 2)) /* has to have an even number of chars */ return FALSE; sortSpecList = arrayReCreate(sortSpecList, (specLen / 2), int); for (i = 0; i < specLen/2; i++) { sscanf(spec+(i*2)+1, "%1d", &sortCol); if (sortCol < 1 || sortCol > t->ncol) /* column numbers have to range from 1 to colNum */ return FALSE; if (spec[i*2] == '+') array(sortSpecList, i, int) = sortCol; else if (spec[i*2] == '-') array(sortSpecList, i, int) = -sortCol; else return FALSE; /* only + or - qualifier allowed */ } numRows = tableMax(t); if (numRows <= 1) return TRUE; /* 1 or no elements are always sorted */ /***************************************/ scopeHandle = handleCreate(); /* create the indices that keep track of which row went where during comparative sorting */ indexArray = (int*)halloc (numRows * sizeof(int), scopeHandle); /* fill the indices with row numbers */ for (i = 0; i < numRows; ++i) indexArray[i] = i; /* set global table-var used by compare() to be this one */ sortTable = t; /* rearrange lines */ qsort (indexArray, numRows, sizeof(int), tableCompare) ; /**** use index to rearrange column by column ****/ #ifdef NEW_RICHARD_CODE /* warning this new code is not adapted to deal * with parents/grandParents */ { TABLETYPE tmp ; BitSet bs=0 ; int k, j; for (i = 0 ; i < t->ncol ; ++i) { bs = bitSetReCreate (bs, tabMax(t,i)); for (j = 0 ; j < tabMax(t,i) ; ++j) if (!bit(bs,j)) /* not done yet */ if (indexArray[j] != j) { tmp = arr((t)->col[i],j,TABLETYPE) ; for (k = j ; !bitSet(bs, indexArray[k]) ; k = indexArray[k]) { arr((t)->col[i],k,TABLETYPE) = arr((t)->col[i], indexArray[k],TABLETYPE) ; bitSet(bs,k) ; } arr((t)->col[i],k,TABLETYPE) = tmp ; bitSet(bs,k) ; } else bitSet(bs,j) ; } bitSetDestroy (bs); } #else /* !NEW_RICHARD_CODE */ /****** still use previuos code, until we get the new one to work ****/ { Array colArray, oldColArray ; KEYSET parentKeys, oldParentKeys, grandParentKeys, oldGrandParentKeys; BitSet bb, oldbb ; int ii, j, j1; for (ii = 0 ; ii < t->ncol ; ii++) { oldColArray = t->col[ii] ; colArray = t->col[ii] = arrayHandleCreate (numRows, TABLETYPE, t->handle) ; array(colArray,numRows-1,TABLETYPE).k = 0 ; /* make room */ oldParentKeys = t->parents[ii]; parentKeys = t->parents[ii] = arrayHandleCreate(numRows, KEY, t->handle); array(parentKeys, numRows-1, KEY) = 0; /* make room */ oldGrandParentKeys = t->grandParents[ii]; grandParentKeys = t->grandParents[ii] = arrayHandleCreate(numRows, KEY, t->handle); array(grandParentKeys, numRows-1, KEY) = 0; /* make room */ oldbb = t->emptyBits[ii] ; bb = t->emptyBits[ii] = bitSetCreate (numRows, t->handle) ; /* copy values and parent/empty info over in the order * of the indexArray, created by the sorting function */ for (j = 0; j < numRows ; j++) { j1 = indexArray[j] ; arr(colArray,j,TABLETYPE).k = array(oldColArray, j1, TABLETYPE).k ; arr(parentKeys, j, KEY) = array(oldParentKeys, j1, KEY); arr(grandParentKeys, j, KEY) = array(oldGrandParentKeys, j1, KEY); if (bit(oldbb,j1)) bitSet(bb,j) ; } arrayDestroy(oldColArray) ; arrayDestroy(oldParentKeys) ; arrayDestroy(oldGrandParentKeys) ; bitSetDestroy (oldbb) ; } } #endif /* !NEW_RICHARD_CODE */ handleDestroy (scopeHandle); return TRUE; } /* tableSort */ /************************************************************/ void tableMakeUnique (TABLE *t) /* assumes that table has previously been sorted */ { int i1, ii, j, mx, nCol; if (!tableExists (t)) messcrash("tableMakeUnique() - received invalid TABLE* pointer"); mx = tableMax(t); nCol = t->ncol; if (mx < 2 || !nCol) return ; i1 = 0; for (ii=1 ; ii < mx ; ii++ ) { /* dry pass */ for (j=0; jtype[j] == 's') { if(strcmp(tabString(t,ii,j), tabString(t,i1,j)) != 0) goto different ; } else { /* any other type we just compare the value * if it was an integer - enough to reveal differences * for ints, bools, even floats */ if(tabKey(t,ii,j) != tabKey(t,i1,j)) goto different ; } } continue ; /* line ii == line i1, drop line ii */ different: i1++ ; /* accept line ii */ if (i1 < ii) /* copy if needed */ { for (j=0; jemptyBits[j],i1) ; else bitUnSet(t->emptyBits[j],i1) ; } } } for (j=0; jcol[j]) = i1 + 1 ; return; } /* tableMakeUnique */ /************************************************************/ /************************************************/ /******* table operator utility functions *******/ /************************************************/ /* Function to check whether two tables are compatible, that they can be used for the subsequent table operators together. */ /* NOTE: Once this check is performed on both tables the short version tabXXX() of the operations can be used. In case it is only a one-off statement that uses a table operation, the tableXXXX() routines should be used to include the type checking from this function */ BOOL tableIsColumnCompatible (TABLE *leftTable, TABLE *rightTable) { int colCount; if (leftTable == NULL || rightTable == NULL) return FALSE; /* we check that the value-types of the columns match in both tables */ if (strcmp(leftTable->type, rightTable->type) != 0) return FALSE; /* check for unevaluated columns, ideally we'd just like to exclude them */ for (colCount = 0; colCount < leftTable->ncol; colCount++) { if (leftTable->type[colCount] == '0' || rightTable->type[colCount] == '0') return FALSE; } return TRUE; } /* tableIsColumnCompatible */ /************************************************************/ /* function to compare a complete row in a table with a row in a second table. */ /* Returns FALSE if tables are incompatible, type mismatch or uninitialised ONLY returns TRUE if full compatability and if all values in the two rows match */ BOOL tableIsRowEqual (int rowCountLeft, TABLE *leftTable, int rowCountRight, TABLE *rightTable) { if (!tableIsColumnCompatible(leftTable, rightTable)) return FALSE; return (tabIsRowEqual(rowCountLeft, leftTable, rowCountRight, rightTable)); } /* tableIsRowEqual */ /************************************************************/ /* simple function to compare a complete row in a table with a row in a second table. */ /* NOTE: no type checking for column compatability is performed this function is to be used for quick access in long loops, for sporadic occasional row-comparison that includes all type checking please use tableIsRowEqual(). */ BOOL tabIsRowEqual (int rowCountLeft, TABLE *leftTable, int rowCountRight, TABLE *rightTable) { int colCount; BOOL isValueDifferent = FALSE; for (colCount = 0; colCount < leftTable->ncol; colCount++) { switch (leftTable->type[colCount]) { case 'i': if (tabInt(leftTable, rowCountLeft, colCount) != tabInt(rightTable, rowCountRight, colCount)) isValueDifferent = TRUE; break; case 'f': if (tabFloat(leftTable, rowCountLeft, colCount) != tabFloat(rightTable, rowCountRight, colCount)) isValueDifferent = TRUE; break; case 'b': if (tabBool(leftTable, rowCountLeft, colCount) != tabBool(rightTable, rowCountRight, colCount)) isValueDifferent = TRUE; break; case 't': if (tabDate(leftTable, rowCountLeft, colCount) != tabDate(rightTable, rowCountRight, colCount)) isValueDifferent = TRUE; break; case 'k': /* keys */ case 'g': /* tags */ if (tabKey(leftTable, rowCountLeft, colCount) != tabKey(rightTable, rowCountRight, colCount)) isValueDifferent = TRUE; break; case 's': if (strcmp(tabString(leftTable, rowCountLeft, colCount), tabString(rightTable, rowCountRight, colCount)) != 0) isValueDifferent = TRUE; break; } } if (!isValueDifferent) return TRUE; return FALSE; } /* tabIsRowEqual */ /************************************************************/ STORE_HANDLE globalTableCreateHandle; /* function to copy all values in a row of the srcTable to the end of the destTable */ /* The destTable is created if it is not existent. The function returns FALSE, if an existing destTable is not type-compatible with the source table. Otherwise, the values in the specified row are appended to the end of the destTable and TRUE is returned. */ BOOL tableCopyRow (int rowCountSrc, TABLE *srcTable, TABLE *destTable) { if (!destTable) { /* we have to initialise the destTable to be of type and value of srcTable */ destTable = tableHandleCreate (tableMax(srcTable), tableTypes(srcTable), globalTableCreateHandle) ; } else { /* a destination table exists, but it is compatible ? */ if (!tableIsColumnCompatible(srcTable, destTable)) return FALSE; } tabCopyRow (rowCountSrc, srcTable, destTable); return TRUE; } /* tableCopyRow */ /**********************************************************************/ /* simple function to copy all values in a row of the srcTable to the end of the destTable */ /* NOTE: (1) No type checking for column compatability is performed. (2) The destTable is assumed to be initialised with the same types as the srcTable. This function is to be used for quick access in long loops, for sporadic occasional row-copy that includes all type checking and initialisation please use tableCopyRow(). */ void tabCopyRow (int rowCountSrc, TABLE *srcTable, TABLE *destTable) { int colCount; int rowCountDest = tableMax(destTable); for (colCount = 0; colCount < srcTable->ncol; colCount++) { switch (srcTable->type[colCount]) { case 'i': tableInt(destTable, rowCountDest, colCount) = tabInt(srcTable, rowCountSrc, colCount); break; case 'f': tableFloat(destTable, rowCountDest, colCount) = tabFloat(srcTable, rowCountSrc, colCount); break; case 'b': tableBool(destTable, rowCountDest, colCount) = tabBool(srcTable, rowCountSrc, colCount); break; case 't': tableDate(destTable, rowCountDest, colCount) = tabDate(srcTable, rowCountSrc, colCount); break; case 'k': tableKey(destTable, rowCountDest, colCount) = tabKey(srcTable, rowCountSrc, colCount); break; case 's': tableSetString(destTable, rowCountDest, colCount, tabString(srcTable, rowCountSrc, colCount)); break; case '0': tableSetEmpty(destTable, rowCountDest, colCount); break; } } } /* tabCopyRow */ /************************************************************/ /* produce a combined table that contains rows from both tables but no duplicates */ /* fails with return code FALSE, if the two tables are incombinable or in case an existing table pointer was passed, this has already been assigned column types, otherwise a new table will be returned in *destTable */ BOOL tableUnion (TABLE *leftTable, TABLE *rightTable, TABLE **destTable) { int rowCountLeft, rowCountRight; int lengthLeft, lengthRight; BOOL isInLeftTable; if (!destTable) /* has to be called with a pointer to TABLE* in order to return results */ messcrash ("table.c:tableUnion() - destTable pointer to TABLE* is NULL"); if (!tableIsColumnCompatible(leftTable, rightTable)) return FALSE; lengthLeft = tableMax(leftTable); lengthRight = tableMax(rightTable); if (!*destTable) /* table it points to is still NULL */ { *destTable = tableHandleCreate((lengthLeft + lengthRight/2), leftTable->type, globalTableCreateHandle); } else /* we got passed a pointer of an existing table */ { if (strlen(leftTable->type) != strlen((*destTable)->type)) messcrash ("table.c:tableUnion() - previously created destTable has incorrect column number"); if (!tableSetTypes(*destTable, leftTable->type)) return FALSE; /* fails if types were already set */ } /********/ /* include all rows from leftTable and the ones from rightTable we haven't got already */ /********/ /* copy all rows from the left table */ for (rowCountLeft = 0; rowCountLeft < lengthLeft; rowCountLeft++) { tabCopyRow(rowCountLeft, leftTable, *destTable); } /* only copy rows from right table that we haven't got already */ for (rowCountRight = 0; rowCountRight < lengthRight; rowCountRight++) { /* look if we have this row already */ isInLeftTable = FALSE; for (rowCountLeft = 0; rowCountLeft < lengthLeft; rowCountLeft++) if (tabIsRowEqual(rowCountLeft, leftTable, rowCountRight, rightTable)) { isInLeftTable = TRUE; break; } if (!isInLeftTable) tabCopyRow(rowCountRight, rightTable, *destTable); } return TRUE; } /* tableUnion */ /************************************************************/ /* produce a table with rows that are in the first but not the second table */ /* fails with return code FALSE, if the two tables are incombinable or in case an existing table pointer was passed, this has already been assigned column types, otherwise a new table will be returned in *destTable */ BOOL tableDiff (TABLE *leftTable, TABLE *rightTable, TABLE **destTable) { int rowCountLeft, rowCountRight; int lengthLeft, lengthRight; BOOL isInRightTable; if (!destTable) /* has to be called with a pointer to TABLE* in order to return results */ messcrash ("tableDiff() - destTable pointer to TABLE* is NULL"); if (!tableIsColumnCompatible(leftTable, rightTable)) return FALSE; lengthLeft = tableMax(leftTable); lengthRight = tableMax(rightTable); if (!*destTable) /* table it points to is still NULL */ { *destTable = tableHandleCreate((lengthLeft - lengthRight/2), leftTable->type, globalTableCreateHandle); } else /* we got passed a pointer of an existing table */ { if (strlen(leftTable->type) != strlen((*destTable)->type)) messcrash ("tableDiff() - previously created destTable has incorrect column number"); if (!tableSetTypes(*destTable, leftTable->type)) return FALSE; /* fails if types were already set */ } /********/ /* only include rows from the leftTable that aren't in the rightTable */ /********/ for (rowCountLeft = 0; rowCountLeft < lengthLeft; rowCountLeft++) { /* look for this row in the rightTable */ isInRightTable = FALSE; for (rowCountRight = 0; rowCountRight < lengthRight; rowCountRight++) if (tabIsRowEqual(rowCountLeft, leftTable, rowCountRight, rightTable)) { isInRightTable = TRUE; break; } if (!isInRightTable) /* not found -> copy left row into destinationTable */ tabCopyRow(rowCountLeft, leftTable, *destTable); } return TRUE; } /* tableDiff */ /************************************************************/ /* produce a table with rows that are in the first but not the second table */ /* fails with return code FALSE, if the two tables are incombinable or in case an existing table pointer was passed, this has already been assigned column types, otherwise a new table will be returned in *destTable */ BOOL tableIntersect (TABLE *leftTable, TABLE *rightTable, TABLE **destTable) { int rowCountLeft, rowCountRight; int lengthLeft, lengthRight; BOOL isInRightTable; if (!destTable) /* has to be called with a pointer to TABLE* in order to return results */ messcrash ("tableIntersect() - destTable pointer to TABLE* is NULL"); if (!tableIsColumnCompatible(leftTable, rightTable)) return FALSE; lengthLeft = tableMax(leftTable); lengthRight = tableMax(rightTable); if (!*destTable) /* table it points to is still NULL */ { *destTable = tableHandleCreate((lengthLeft/2 + lengthRight/2), leftTable->type, globalTableCreateHandle); } else /* we got passed a pointer of an existing table */ { if (strlen(leftTable->type) != strlen((*destTable)->type)) messcrash ("tableIntersect() - previously created destTable has incorrect column number"); if (!tableSetTypes(*destTable, leftTable->type)) return FALSE; /* fails if types were already set */ } /********/ /* only include rows that are shared between leftTable and rightTable */ /********/ for (rowCountLeft = 0; rowCountLeft < lengthLeft; rowCountLeft++) { /* look for this row in the rightTable */ isInRightTable = FALSE; for (rowCountRight = 0; rowCountRight < lengthRight; rowCountRight++) if (tabIsRowEqual(rowCountLeft, leftTable, rowCountRight, rightTable)) { isInRightTable = TRUE; break; } if (isInRightTable) /* left was found in right -> copy left row into destinationTable */ tabCopyRow(rowCountLeft, leftTable, *destTable); } return TRUE; } /* tableIntersect */ /************************************************************/ /************************************************************/ /********** functions to output/export tables ***************/ /************************************************************/ int tableOut (ACEOUT dump_out, TABLE *t, char separator, char style) /* write whole table from row 0 till the end including headers */ { int n ; tableHeaderOut (dump_out, t, style) ; n = tableSliceOut (dump_out, 0, tableMax(t), t, separator, style) ; tableFooterOut (dump_out, t, style) ; return n ; } /* tableOut */ /************************************************************/ /* This routine was torn out of tableSliceOut -- see how it is called from there, for how to use it. -- JCGS 20010911 */ void tableOutSetup(TABLE *t, struct tableOutControl *taboutcontrol) { DICT **colFlagDict = taboutcontrol->colFlagDict; char **colFlagSet = taboutcontrol->colFlagSet; Array oldClass = taboutcontrol->oldClass; int i; char *cp; /* preparation for flags */ /* RD 000615 - this section was protected by 'if (doShowHeaders)' but that is nonsense. It has nothing to do with headers. It is required if there are 'a' (flag) type columns to be shown. I bet that 'a' columns are not used. */ for (i = 0 ; i < t->ncol ; ++i) { if (t->type[i] == 'a') { if (!(cp = tableGetColumnName(t,i))) cp = "Default" ; if (strncmp (cp, "Flag", 4) == 0 || strncmp (cp, "flag", 4) == 0) { cp += 4 ; if (*cp == 's') ++cp ; /* remove "flags" as well as "flag" */ if (!*cp) cp = "Default" ; } colFlagSet[i] = cp ; colFlagDict[i] = flagSetDict (cp) ; } /* RD 000615 - this looks wrong to me should always be -1 so you write class first time so I am commenting it out if ((t->type[i] == 'k' || t->type[i] == 'g') && tabMax(t,i) && !tabEmpty(t,0,i)) array (oldClass, i, int) = class(tabKey(t,0,i)); else */ array (oldClass, i, int) = -1 ; } } /************************************************************/ /* This routine was torn out of tableSliceOut -- see how it is called from there, for how to use it. -- JCGS 20010911 */ /* It might be good to set up a closure structure containing the control arguments to this, but on the other hand, I have no great motivation to do so, other than the sheer number of little fiddly arguments. */ void tableRowOut (ACEOUT dump_out, int row, TABLE *t, struct tableOutControl *taboutcontrol) { int column; char sepString[2] ; KEY xk ; int xi ; float xf ; mytime_t xt ; char *cp ; char style = taboutcontrol->style; char bufvoid[1] = {'v'} ; char bufinteger[1] = {'i'} ; char buffloat[1] = {'f'} ; char bufdate[1] = {'d'} ; char buftext[1] = {'t'} ; char bufk[2] = {'k', 0} ; char bufK[2] = {'K', 0} ; char buftag[1] = {'g'} ; sepString[0] = taboutcontrol->separator ; sepString[1] = 0 ; if (style == 'x' || style == 'X' || style == 'y' || style == 'Y') aceOutPrint(dump_out, " ") ; for (column = 0 ; column < t->ncol ; ++column) { #if 0 aceOutPrint(dump_out, "tableRowOut(row=%d, column=%d)\n", row, column); #endif if (!tableColumnIsVisible(t, column)) continue; if (style == 'C') { aceOutBinary(dump_out, taboutcontrol->cStylePrefix, 1); taboutcontrol->cStylePrefix[0] = '>' ; } else if (column > 0) aceOutPrint (dump_out, "%s", sepString) ; if (row >= tabMax(t,column) || tabEmpty(t,row,column) || t->type[column] == '0') { if (taboutcontrol->showEmpty) switch (style) { case 'j': case 'J': case 'k': case 'K': aceOutPrint (dump_out,"???") ; break ; case 'x': case 'y': case 'X': /* xml with content, like html */ aceOutPrint (dump_out," ") ; break ; case'Y': /* xml without content */ aceOutPrint (dump_out,"") ; break ; case 'C': aceOutBinary(dump_out, bufvoid, 1) ; break; default: aceOutPrint (dump_out,"NULL") ; break ; } } else { #if 0 aceOutPrint(dump_out, "t->type[%d]=%c\n", column, t->type[column]); #endif switch (t->type[column]) { case 'k': case 'g': xk = tabKey(t,row,column) ; switch (style) { case 'h': aceOutPrint (dump_out, "%s", name(xk)) ; break ; case 'C': switch (t->type[column]) { case 'g': aceOutBinary ( dump_out, buftag,1) ; aceOutBinary ( dump_out, (char*)&xk, 4) ; break ; case 'k': if (iskey (xk) == 2) { bufK[1] = class (xk) ; aceOutBinary ( dump_out,bufK,2) ; } else { bufk[1] = class (xk) ; aceOutBinary ( dump_out, bufk,2) ; } cp = name(xk) ; aceOutBinary ( dump_out, cp, strlen(cp) + 1) ; break ; } break; case 'j': case 'k': aceOutPrint (dump_out, "?%s?%s?", className(xk), freejavaprotect(name(xk))) ; break ; case 'J': case 'K': if (column > taboutcontrol->javatypeWatermark || class(xk) != array (taboutcontrol->oldClass, column, int)) aceOutPrint (dump_out, "?%s?%s?", className(xk), freejavaprotect(name(xk))) ; else aceOutPrint (dump_out, "?%s?", freejavaprotect(name(xk))) ; array (taboutcontrol->oldClass, column, int) = class(xk) ; break ; case 'a': if (class(xk) == array (taboutcontrol->oldClass, column, int)) { aceOutPrint (dump_out, "%s", freeprotect(name(xk))) ; break ; } array (taboutcontrol->oldClass, column, int) = class(xk) ; /* else fall through */ case 'A': aceOutPrint (dump_out, "%s:%s", className(xk), freeprotect(name(xk))) ; break ; case 'x': aceOutPrint (dump_out, "%s", freeXMLprotect (name(xk))) ; break ; case 'X': aceOutPrint (dump_out, "%s", class(xk) ? className(xk) : "#Tag", freeXMLprotect (name(xk))) ; break ; case 'y': aceOutPrint (dump_out, "%s", class(xk) ? className(xk) : "#Tag", freeXMLprotect (name(xk)), freeXMLprotect (name(xk))) ; break ; case 'Y': aceOutPrint (dump_out, "", class(xk) ? className(xk) : "#Tag", freeXMLprotect (name(xk))) ; break ; default: aceOutPrint (dump_out, "%s", freeprotect(name(xk))) ; break ; } break ; case 'a': cp = flagNameFromDict(taboutcontrol->colFlagDict[column], tabFlag(t,row,column)) ; switch (style) { case 'j': case 'k': aceOutPrint (dump_out, "?flag%s?%s?", taboutcontrol->colFlagSet[column], cp) ; break ; case 'J': case 'K': if (column > taboutcontrol->javatypeWatermark) aceOutPrint (dump_out, "flag%s", taboutcontrol->colFlagSet[column]) ; aceOutPrint (dump_out, "?%s?", cp) ; break ; case 'C': aceOutBinary ( dump_out, buftext,1) ; aceOutBinary ( dump_out, cp, strlen(cp) + 1) ; break ; case 'x': aceOutPrint (dump_out, "%s", cp) ; break ; case 'X': aceOutPrint (dump_out, "%s", taboutcontrol->colFlagSet[column], cp) ; break ; case 'y': aceOutPrint (dump_out, "%s", taboutcontrol->colFlagSet[column], cp, cp) ; break ; case 'Y': aceOutPrint (dump_out, "", taboutcontrol->colFlagSet[column], cp) ; break ; default: aceOutPrint (dump_out, "%s", cp) ; break ; } break ; case 'b': cp = tabBool(t,row,column) ? "TRUE" : "FALSE" ; switch (style) { case 'j': aceOutPrint (dump_out, "?BOOL?%s?", cp) ; break ; case 'J': if (column > taboutcontrol->javatypeWatermark) aceOutPrint (dump_out, "BOOL") ; aceOutPrint (dump_out, "?%s?", cp) ; break ; case 'C': aceOutBinary ( dump_out, bufinteger,1) ; if (tabBool(t,row,column)) xi = 1 ; else xi = 0 ; aceOutBinary ( dump_out, (char*)&xi, 4) ; break ; case 'k': aceOutPrint (dump_out, "?#Bool?%s?", cp) ; break ; case 'K': if (column > taboutcontrol->javatypeWatermark) aceOutPrint (dump_out, "#Bool") ; aceOutPrint (dump_out, "?%s?", cp) ; break ; case 'x': aceOutPrint (dump_out, "%s", cp) ; break ; case 'X': aceOutPrint (dump_out, "%s", cp) ; break ; case 'y': aceOutPrint (dump_out, "%s", cp, cp) ; break ; case 'Y': aceOutPrint (dump_out, "", cp) ; break ; default: aceOutPrint (dump_out, "%s", cp) ; break ; } break ; case 'i': xi = tabInt(t,row,column) ; switch (style) { case 'j': aceOutPrint (dump_out, "?int?%d?", xi) ; break ; case 'J': if (column > taboutcontrol->javatypeWatermark) aceOutPrint (dump_out, "int") ; aceOutPrint (dump_out, "?%d?", xi) ; break ; case 'C': aceOutBinary ( dump_out, bufinteger,1) ; aceOutBinary ( dump_out, (char*)&xi, 4) ; break ; case 'k': aceOutPrint (dump_out, "?#Int?%d?", xi) ; break ; case 'K': if (column > taboutcontrol->javatypeWatermark) aceOutPrint (dump_out, "#Int") ; aceOutPrint (dump_out, "?%d?", xi) ; break ; case 'x': aceOutPrint (dump_out, "%d", xi) ; break ; case 'X': aceOutPrint (dump_out, "%d", xi) ; break ; case 'y': aceOutPrint (dump_out, "%d", xi, xi) ; break ; case 'Y': aceOutPrint (dump_out, "", xi) ; break ; case 'A': aceOutPrint (dump_out, "Int:%d", xi) ; break ; default: aceOutPrint (dump_out, "%d", xi) ; break ; } break ; case 'f': xf = tabFloat (t,row,column) ; switch (style) { case 'j': aceOutPrint (dump_out, "?float?%g?", xf) ; break ; case 'J': if (column > taboutcontrol->javatypeWatermark) aceOutPrint (dump_out, "float") ; aceOutPrint (dump_out, "?%g?", xf) ; break ; case 'C': aceOutBinary ( dump_out, buffloat,1) ; aceOutBinary ( dump_out, (char*)&xf, 4) ; break ; case 'k': aceOutPrint (dump_out, "?#Float?%g?", xf) ; break ; case 'K': if (column > taboutcontrol->javatypeWatermark) aceOutPrint (dump_out, "#Float") ; aceOutPrint (dump_out, "?%g?", xf) ; break ; case 'x': aceOutPrint (dump_out, "%g", xf) ; break ; case 'X': aceOutPrint (dump_out, "%g", xf) ; break ; case 'y': aceOutPrint (dump_out, "%g", xf, xf) ; break ; case 'Y': aceOutPrint (dump_out, "", xf) ; break ; case 'A': aceOutPrint (dump_out, "Float:%g", xf) ; break ; default: aceOutPrint (dump_out, "%g", xf) ; break ; } break ; case 's': cp = tabString(t,row,column) ; switch (style) { case 'j': aceOutPrint (dump_out, "?txt?%s?", freejavaprotect(cp)) ; break ; case 'J': if (column > taboutcontrol->javatypeWatermark) aceOutPrint (dump_out, "txt") ; aceOutPrint (dump_out, "?%s?", freejavaprotect(cp)) ; break ; case 'C': aceOutBinary (dump_out, buftext,1) ; aceOutBinary (dump_out, cp, strlen(cp) + 1) ; break ; case 'k': aceOutPrint (dump_out, "?#Text?%s?", freejavaprotect(cp)) ; break ; case 'K': if (column > taboutcontrol->javatypeWatermark) aceOutPrint (dump_out, "#Text") ; aceOutPrint (dump_out, "?%s?", freejavaprotect(cp)) ; break ; case 'h': aceOutPrint (dump_out, "%s", cp) ; break ; case 'A': aceOutPrint (dump_out, "Text:%s", freeprotect(cp)) ; break ; case 'x': aceOutPrint (dump_out, "%s", freeXMLprotect(cp)) ; break ; case 'X': aceOutPrint (dump_out, "%s", freeXMLprotect(cp)) ; break ; case 'y': aceOutPrint (dump_out, "%s", freeXMLprotect(cp), freeXMLprotect(cp)) ; break ; case 'Y': aceOutPrint (dump_out, "", freeXMLprotect(cp)) ; break ; default: aceOutPrint (dump_out, "%s", freeprotect(cp)) ; break ; } break ; case 't': xt = tabDate(t,row,column) ; switch (style) { case 'j': aceOutPrint (dump_out, "?date?%s?", timeShowJava(xt)) ; break ; case 'J': aceOutPrint (dump_out, "date") ; aceOutPrint (dump_out, "?%s?", timeShowJava(xt)) ; break ; case 'C': aceOutBinary (dump_out, bufdate,1) ; aceOutBinary (dump_out, (char*)&xt, 4) ; break ; case 'k': aceOutPrint (dump_out, "?#Date?%s?", timeShow(xt)) ; break ; case 'K': aceOutPrint (dump_out, "#Date") ; aceOutPrint (dump_out, "?%s?", timeShow(xt)) ; break ; case 'h': aceOutPrint (dump_out, "%s", timeShow(xt)) ; break ; case 'A': aceOutPrint (dump_out, "Date:%s", timeShow(xt)) ; break ; case 'x': aceOutPrint (dump_out, "%s", timeShow(xt)) ; break ; case 'X': aceOutPrint (dump_out, "%s", timeShow(xt)) ; break ; case 'y': aceOutPrint (dump_out, "%s", timeShow(xt), timeShow(xt)) ; break ; case 'Y': aceOutPrint (dump_out, "", timeShow(xt)) ; break ; default: aceOutPrint (dump_out, freeprotect(timeShow(xt))) ; break ; } break ; default: messcrash ("tableRowOut() - invalid column type %c", t->type[column]) ; break ; } if (column > taboutcontrol->javatypeWatermark) taboutcontrol->javatypeWatermark = column ; /* column's type has now been exported to java */ } } switch (style) { case 'x': case 'X': case 'y': case 'Y': aceOutPrint (dump_out, "\n") ; break; case 'C': { char bfret[3]; bfret[0] = 'l'; xi = t->ncol-1 ; while (xi > 0 && xi > 255) { bfret[1]= 255; aceOutBinary( dump_out, bfret, 2) ; xi -= 255; } bfret[1] = xi ; bfret[2] = '\n' ; aceOutBinary( dump_out, bfret, 3) ; taboutcontrol->cStylePrefix[0] = '.'; } break; default: aceOutPrint (dump_out, "\n") ; } } /************************************************************/ int tableSliceOut (ACEOUT dump_out, int begin, int count, TABLE *t, char separator, char style) { int row = 0, nMax, end; struct tableOutControl outcontrol; outcontrol.showEmpty = FALSE ; if (style == 'a' || style == 'j' || style == 'J' || style == 'x' || style == 'X' || style == 'y' || style == 'Y' || style == 'k' || style == 'K' || style == 'C' ) outcontrol.showEmpty = TRUE ; outcontrol.style = style; if (!t) return 0; if (t->magic != &TABLE_MAGIC) messcrash ("tableSliceOut() received non-magic TABLE* pointer") ; if (t->ncol <= 0) return 0 ; nMax = tableMax(t); /* limit the begin-value to be within bounds */ begin = (begin < 0) ? 0 : begin; if (nMax) begin = (begin > nMax-1) ? nMax-1 : begin; /* limit the number of rows to be within bounds */ count = (count < 0) ? 0 : count; /* calc the end of the table */ end = begin + count; end = (end > nMax) ? nMax : end; outcontrol.oldClass = arrayCreate (t->ncol, int) ; outcontrol.colFlagDict = (DICT**) messalloc (t->ncol * sizeof(DICT*)) ; outcontrol.colFlagSet = (char**) messalloc (t->ncol * sizeof(char*)) ; outcontrol.separator = separator; outcontrol.cStylePrefix [0] = '>' ; tableOutSetup(t, &outcontrol); /* now dump the rows */ outcontrol.javatypeWatermark = -1 ; for (row = begin ; row < end ; row++) { tableRowOut(dump_out, row, t, &outcontrol); } if (style == 'C') { /* * We are now at the end of an encore block. At the beginning of the * next encore block, we will move the cursor right one before depositing * data, so here we move the cursor left to get it back to column -1. */ aceOutBinary ( dump_out, "l\001.",3); } messfree (outcontrol.colFlagSet) ; messfree (outcontrol.colFlagDict) ; arrayDestroy (outcontrol.oldClass) ; return row ; /* last row written */ } /* tableSliceOut */ /************************************************************/ /* * bug: returns a BOOL but nobody ever looks at it */ BOOL tableHeaderOut (ACEOUT dump_out, TABLE *table, char style) { int i, j ; KEY key ; if (!table || ! tableMax(table)) return FALSE ; switch (style) { case 'a': case 'h': aceOutPrint (dump_out, "Format ") ; break ; case 'x': case 'X': case 'y': case 'Y': aceOutPrint (dump_out, "\n") ; aceOutPrint (dump_out, "\n") ; return TRUE ; } /* tableHeaderOut */ void tableFooterOut (ACEOUT dump_out, TABLE *t, char style) { switch (style) { case 'x': case 'X': case 'y': case 'Y': aceOutPrint (dump_out, "
") ; break ; case 'C': return FALSE; default: return FALSE ; /* NOTE return HERE */ } for(j = 0 ; j < table->ncol; j++) { if (!tableColumnIsVisible(table, j)) continue; switch (tableTypes(table)[j]) { case 'k': case 'g': for (i = 0 ; i < tabMax(table, j) ; i++) if (!tabEmpty(table,i,j) && class(tabKey(table,i,j))) { key = tabKey(table,i,j) ; aceOutPrint (dump_out, " %s ", class(key) ? className(key) : "Tag") ; break ; } if (i == tabMax(table,j)) /* nothing found */ aceOutPrint (dump_out, " Key ") ; continue ; case 'b': aceOutPrint (dump_out, " Bool ") ; continue ; case 'i': aceOutPrint (dump_out, " Int ") ; continue ; case 'f': aceOutPrint (dump_out, " Float ") ; continue ; case 's': aceOutPrint (dump_out, " Text ") ; continue ; case 't': aceOutPrint (dump_out, " Date ") ; continue ; case 'a': aceOutPrint (dump_out, " Flag ") ; continue ; } } if (style == 'x' || style == 'X' || style == 'y' || style == 'Y') aceOutPrint (dump_out, "
\n") ; } } int tablePartOut (ACEOUT dump_out, int *lastp, TABLE *t, char separator, char style) /* then *lastp will be set to the row-number that was last written * if output is finished *lastp is set to 0 */ /* global for now, should go by chunk */ { int last_row; *lastp = 0 ; /* global dump for now - 0 means output was finished */ last_row = tableOut (dump_out, t, separator, style); return last_row; } /* tablePartOut */ BOOL tableResultDump (ACEOUT dump_out, KEY k) /* DumpFuncType for class _VTableResult */ { TABLE *table = tableGet(k) ; if (!table || ! tableMax(table)) return FALSE ; tableOut (dump_out, table, '\t', 'a') ; aceOutPrint (dump_out, "\n") ; tableDestroy (table) ; return TRUE ; } /* tableResultDump */ static TABLE *tableDoDoParse (ACEIN parse_io, char *table_name, char *format_type, Array oldClass, char **err_text) { char *cp, *cq, *ctype ; TABLE *tt = 0 ; int xi ; float xf ; mytime_t xt ; KEY xk ; int ii, nn ; char *errText ; ctype = format_type - 1 ; ii = -1 ; while (ii++, *++ctype) switch (*ctype) { case 'k': case 'g': case 'b': case 'i': case 'f': case 's': case 't': continue ; case 'a': errText = "tableDoDoParse cannot parse a type: flags" ; goto abort ; default: *err_text = NULL ; return NULL ; } tt = tableHandleCreate (100, format_type, 0); nn = -1 ; /* line number */ while (aceInCard(parse_io)) { nn++ ; ctype = format_type - 1 ; ii = -1 ; while (ii++, *++ctype) { aceInNext (parse_io) ; switch (*ctype) { case 'k': case 'g': cp = aceInWord(parse_io) ; if (!cp) { if (ii) { errText = "missing object name" ; goto abort ; } else return tt ; } if (strcasecmp(cp,"NULL") == 0) { tableKey (tt, nn,ii) = 0 ; break ; } cq = cp ; while (*cq && *cq != ':') cq++ ; if (*cq == ':') { *cq = 0 ; if ((xi = pickWord2Class(cp)) && !pickList[xi].protected) { *cq = ':' ; array(oldClass, ii, int) = xi ; /* but beware of "" around name */ aceInBack(parse_io) ; cp = aceInPos(parse_io) ; while(*cp != ':') *cp++ = ' ' ; /* garanteed to work */ *cp = ' ' ; aceInNext(parse_io) ; cp = aceInWord(parse_io) ; } else *cq = ':' ; } if (array(oldClass,ii,int) == -1) { errText = "missing or unrecognized or protected class name" ; goto abort ; } if (!lexIsGoodName(cp)) { errText = "missing object name" ; goto abort ; } if (!array(oldClass,ii,int) && !lexword2key (cp, &xk,array(oldClass,ii,int))) { errText = "Unrecognized Tag name" ; goto abort ; } if (pickList[array(oldClass,ii,int)].known && !lexword2key (cp, &xk,array(oldClass,ii,int))) { errText = "Unrecognized Tag name" ; goto abort ; } lexaddkey(cp, &xk, array(oldClass,ii,int)) ; tableKey(tt,nn, ii) = xk ; break ; case 'b': cp = aceInWord(parse_io) ; if (!cp) { if (ii) { errText = "missing bool data" ; goto abort ; } else return tt ; } if (strcasecmp(cp,"true") == 0) tableBool(tt, nn,ii) = TRUE ; else if (strcasecmp(cp,"false") == 0) tableBool(tt, nn,ii) = FALSE ; else if (strcasecmp(cp,"NULL") == 0) tableBool(tt, nn,ii) = NON_BOOL ; else { errText = "Wrong Bool value, should be TRUE or FALSE" ; goto abort ; } break ; case 'i': if (aceInInt(parse_io, &xi)) tableInt(tt,nn, ii) = xi ; else if ((cp = aceInWord(parse_io)) && strcasecmp(cp,"NULL") == 0) tableInt(tt,nn, ii) = NON_INT ; else if (ii || cp) { errText = "wrong or missing Int value" ; goto abort ; } else return tt ; break ; case 'f': if (aceInFloat (parse_io, &xf)) tableFloat(tt,nn, ii) = xf ; else if ((cp = aceInWord(parse_io)) && strcasecmp(cp,"NULL") == 0) tableFloat(tt,nn, ii) = NON_FLOAT ; else if (ii || cp) { errText = "wrong or missing Float value" ; goto abort ; } else return tt ; break ; case 's': cp = aceInWord(parse_io) ; if (!cp) { if (ii) { errText = "Missing text" ; goto abort ; } else return tt ; } if (strcasecmp(cp,"NULL") == 0) tableDate(tt,nn,ii) = 0 ; else tableSetString (tt, nn, ii, cp) ; break ; case 't': cp = aceInWord(parse_io) ; if (!cp) { if (ii) { errText = "missing date" ; goto abort ; } else return tt ; } if (strcasecmp(cp,"NULL") == 0) tableDate(tt,nn,ii) = 0 ; else if ((xt = timeParse(cp))) tableDate (tt,nn, ii) = xt ; else { errText = "wrong Date value" ; goto abort ; } break ; } } } return tt ; abort: *err_text = messprintf(" TableResult parse error at line %7d column %d in %.25s : %s\n", aceInStreamLine(parse_io) -1, ii + 1, table_name, errText) ; tableDestroy(tt) ; return NULL ; } /* tableDoDoParse */ /* Currently this is only called from within this module or from xclient.c */ /* in wrpc and wsocket. The interface is a hack, it can end up returning */ /* NULL + errtext which means an error */ /* NULL + no errtext, which means there was no data in parse_io */ /* or a valid tt. */ /* Need to know about the "no data" for parse result reporting, see func */ /* tableResultParse() */ TABLE *tableCreateFromAceIn(ACEIN parse_io, char *table_name, char **errtext) { TABLE *tt = NULL ; char *format_type ; Array oldClass = arrayCreate(12, int) ; format_type = tableParseFormat(parse_io, oldClass) ; if (format_type && strlen(format_type) > 0) tt = tableDoDoParse(parse_io, table_name, format_type, oldClass, errtext); arrayDestroy (oldClass) ; return tt ; } /* tableCreateFromAceIn */ ParseFuncReturn tableResultParse(ACEIN parse_io, KEY key, char **errtext) /* ParseFuncType */ { TABLE *tt ; ParseFuncReturn result ; tt = tableCreateFromAceIn (parse_io, name(key), errtext) ; if (tt) { tableStore (key, tt) ; tableDestroy (tt) ; result = PARSEFUNC_OK ; } else if (*errtext == NULL) result = PARSEFUNC_EMPTY ; else result = PARSEFUNC_ERR ; return result ; } /* tableResultParse */ /*************************************************/ /****************** Get/Store ******************/ /*************************************************/ BOOL tableStore (KEY tKey, TABLE *t) { int i, max ; Array aa = 0 ; TABLETYPE *uu, *vv; if (t->magic != &TABLE_MAGIC) messcrash ("tableStore() received non-magic TABLE* pointer"); if(t->ncol <= 0) messcrash ("tableStore() received TABLE with no columns"); if (!tKey || pickType(tKey) != 'A') messcrash ("tableStore() called with a non-valid key") ; max = tableMax(t) ; aa = arrayCreate ((max+1) * t->ncol, TABLETYPE) ; uu = arrayp (aa, (max+1) * t->ncol - 1, TABLETYPE) ; /* make room */ memcpy (arrp(aa, 0, TABLETYPE), t->name, t->ncol*sizeof(TABLETYPE)) ; if (max) for (i = 0 ; i < t->ncol ; i++) { uu = arrp (aa, t->ncol + i * max, TABLETYPE) ; vv = arrp(t->col[i], 0, TABLETYPE) ; memcpy (uu, vv, tabMax(t,i) * sizeof(TABLETYPE)) ; } arrayStackStore (tKey, aa, "a", t->s) ; /* crashes or succeeds */ arrayDestroy (aa) ; return TRUE ; } /* tableStore */ /*************************************************/ TABLE *tableHandleGet (KEY tKey, STORE_HANDLE h) { Stack s = 0 ; Array aa = 0 ; int i = 0, max, ncol, maxBitArray = 0 ; TABLETYPE *uu, *vv ; TABLE *t = 0 ; char *types ; int version ; char *vs ; if (!tKey || pickType(tKey) != 'A') messcrash ("tableHandleGet called with a non-valid key") ; if (!arrayStackHandleGet (tKey, &aa, "a", &s, h)) goto abort ; if (!aa || !s || !arrayMax(aa) || !stackMark(s)) messcrash ("missing data in tableHandleGet") ; types = stackText (s, 0) ; if (!types || !(ncol = strlen(types))) goto abort ; if (arrayMax(aa) % ncol) messcrash ("inconsistent array size in tableHandleGet") ; max = arrayMax(aa)/ncol - 1 ; /* 1 for the names */ version = 0 ; if (!stackAtEnd(s) && (vs = stackNextText(s)) && *vs == '\177') version = vs[1] ; /* 256 possible versions */ if (version > 0) { maxBitArray = 1 + (max-1)/(1 + 32) ; max -= maxBitArray ; if (sizeof(TABLETYPE) != 32) messcrash ("sizeof(TABLETYPE) = %d != 32 in tableGet: rethink!", sizeof(TABLETYPE)) ; } t = tableHandleCreate (max, types, h) ; stackDestroy (t->s) ; t->s = s ; memcpy (t->name, arrp(aa, 0, TABLETYPE), ncol*sizeof(TABLETYPE)) ; if (max) for (i = 0 ; i < ncol ; i++) { uu = arrp (aa, ncol + i * max, TABLETYPE) ; array(t->col[i], max-1, TABLETYPE).i = 0 ; /* make room */ array(t->parents[i], max-1, KEY) = 0; /* make room */ array(t->grandParents[i], max-1, KEY) = 0; /* make room */ vv = arrp(t->col[i], 0, TABLETYPE) ; memcpy (vv, uu, max*sizeof(TABLETYPE)) ; } if (version > 0) for (i = 0 ; i < ncol ; i++) { uu = arrp (aa, ncol*(1+max) + i*maxBitArray , TABLETYPE) ; bitUnSet(t->emptyBits[i], max-1) ; /* make room */ vv = arrp(t->emptyBits[i], 0, TABLETYPE) ; memcpy (vv, uu, maxBitArray*sizeof(TABLETYPE)) ; } else for (i = 0 ; i < ncol ; i++) tableCheckEmpty (t,i) ; arrayDestroy (aa) ; return t ; abort: stackDestroy (s) ; arrayDestroy (aa) ; tableDestroy (t) ; return 0 ; } /****************************************************************** ************************ private functions *********************** ******************************************************************/ static BOOL tableExists (TABLE *tt) { if (tt && tt->magic == &TABLE_MAGIC && stackExists (tt->s)) return TRUE; return FALSE; } /* tableExists */ static void tableFinalise (void *block) { TABLE *t = (TABLE*)block; if (!tableExists (t)) messcrash ("tableFinalise() received non-magic TABLE* pointer") ; t->magic = 0 ; handleDestroy (t->handle) ; return; } /* tableFinalise */ /************************************************************/ static BOOL setColumnVisibility (TABLE *t, int colNum, int on_off) /* TRUE/FALSE to set * other value to enquire */ { BOOL old; char *type_p; if (!t) messcrash("setColumnVisibility() - received NULL TABLE* pointer"); if (colNum < 0 || colNum >= t->ncol) messcrash ("setColumnVisibility() - col %d is out of bounds 0-%d", colNum, t->ncol-1) ; old = t->visibility[colNum]; if (on_off == 0 || on_off == 1) /* set the new value */ { t->visibility[colNum] = on_off; /* the type-string for the table is preserved * at the first stack position, it is also used to preserve * the visibility info for each column, by using upper-case * format characters for hidden columns. * Here we manipulate the stack to switch those characters, * the visibility info gets stored retrieved properly. */ type_p = stackText(t->s, 0); type_p += colNum; if (on_off) *type_p = ACEIN_LOWER[(int)*type_p]; else *type_p = ACEIN_UPPER[(int)*type_p]; } return old; } /* setColumnVisibility */ /************************************************************/ static char* tableParseFormat (ACEIN parse_io, Array oldClass) { char *cp ; static char type[1000] ; int nn, i = 1000 ; while(i--) type[i] = 0 ; if (!aceInCard(parse_io)) return 0 ; cp = aceInWord(parse_io) ; if (!cp || strcasecmp("format", cp) != 0) return 0 ; i = -1 ; while (i < 1000 && (cp = aceInWord(parse_io))) { i++ ; array(oldClass,i, int) = 0 ; if (strcasecmp(cp,"bool") == 0) type[i] = 'b' ; else if (strcasecmp(cp,"int") == 0) type[i] = 'i' ; else if (strcasecmp(cp,"float") == 0) type[i] = 'f' ; else if (strcasecmp(cp,"text") == 0) type[i] = 's' ; else if (strcasecmp(cp,"date") == 0) type[i] = 't' ; else if (strcasecmp(cp,"key") == 0) { type[i] = 'k' ; array(oldClass,i, int) = -1 ; } else if (strcasecmp(cp,"tag") == 0) /* tag means _VSystem */ { type[i] = 'k' ; array(oldClass,i, int) = 0 ; } else if ((nn = pickWord2Class (cp))) { type[i] = 'k' ; array(oldClass,i, int) = nn ; } else return 0 ; } return type ; } /* tableParseFormat */ /*************************************************/ /****************** end of file ******************/ /*************************************************/