",
hgTextName(), cartSessionId(cart));
cgiContinueHiddenVar("tbPosOrKeys");
cgiContinueHiddenVar("tbShowPasteResults");
cgiContinueHiddenVar("tbShowUploadResults");
//#*** Really need to save this off to a local file!
cgiContinueHiddenVar("tbUserKeys");
}
char *searchPosition(char *pos, char **retChrom, int *retStart, int *retEnd)
/* Use hgFind if necessary; return NULL
* if we had to display the gateway page or hgFind's selection page. */
{
if (! isGenome(pos))
{
struct hgPositions *hgp = NULL;
char *phase = cgiUsualString("phase", "table");
char retAddr[512];
saveChooseTableState();
safef(retAddr, sizeof(retAddr), "%s?phase=%s", hgTextName(), phase);
hgp = findGenomePosWeb(pos, retChrom, retStart, retEnd,
cart, TRUE, retAddr);
if ((hgp == NULL) || (hgp->singlePos == NULL))
{
cartCheckout(&cart);
return NULL;
}
}
return(pos);
}
char *getTableVar()
{
char *table = cgiOptionalString("table");
char *track = cartCgiUsualString(cart, "tbTrack", NULL);
char *ct = cartCgiUsualString(cart, "tbCustomTrack", NULL);
char *table0 = cartCgiUsualString(cart, "table0", NULL);
char *table1 = cartCgiUsualString(cart, "table1", NULL);
if (table != NULL && strcmp(table, "Choose table") == 0)
table = NULL;
if (track != NULL && strcmp(track, "Choose table") == 0)
track = NULL;
if (ct != NULL && strcmp(ct, "Choose table") == 0)
ct = NULL;
if (table0 != NULL && strcmp(table0, "Choose table") == 0)
table0 = NULL;
if (table1 != NULL && strcmp(table1, "Choose table") == 0)
table1 = NULL;
if (isNotEmpty(table))
return table;
else if (isNotEmpty(track))
return track;
else if (isNotEmpty(ct))
return ct;
else if (isNotEmpty(table0))
return table0;
else
return table1;
}
char *getTableName()
{
char *val, *ptr;
val = getTableVar();
if (val == NULL)
return val;
else if ((ptr = strchr(val, '.')) != NULL)
return ptr + 1;
else
return val;
}
char *getTableDb()
{
char *val, *ptr;
val = cloneString(getTableVar());
if (val == NULL)
return NULL;
if ((ptr = strchr(val, '.')) != NULL)
*ptr = 0;
return(val);
}
char *getTrackName()
{
char *trackName = cloneString(getTableName());
if (trackName == NULL)
return NULL;
if (startsWith("chrN_", trackName))
strcpy(trackName, trackName+strlen("chrN_"));
return trackName;
}
char *getTable2Var()
{
char *track = cartCgiUsualString(cart, "tbTrack2", NULL);
char *ct = cartCgiUsualString(cart, "tbCustomTrack2", NULL);
char *table2 = cgiOptionalString("table2");
if (track != NULL && sameString(track, "Choose table"))
track = NULL;
if (ct != NULL && sameString(ct, "Choose table"))
ct = NULL;
if ((table2 != NULL) && sameString(table2, "Choose table"))
table2 = NULL;
if (isNotEmpty(track))
return track;
else if (isNotEmpty(ct))
return ct;
else
return table2;
}
char *getTable2Name()
{
char *val, *ptr;
val = getTable2Var();
if (val == NULL)
return val;
else if ((ptr = strchr(val, '.')) != NULL)
return ptr + 1;
else
return val;
}
char *getTable2Db()
{
char *val, *ptr;
val = cloneString(getTable2Var());
if (val == NULL)
return(val);
if ((ptr = strchr(val, '.')) != NULL)
*ptr = 0;
return(val);
}
char *getTrack2Name()
{
char *trackName = cloneString(getTable2Name());
if ((trackName != NULL) && startsWith("chrN_", trackName))
strcpy(trackName, trackName+strlen("chrN_"));
return trackName;
}
void printTrackDropList(char *db, char *javascript, char *varName)
/* Print tracks for this database */
{
struct trackDb *trackList = hTrackDb(NULL), *t;
char *value = cartCgiUsualString(cart, varName, NULL);
int trackCount = slCount(trackList);
char **trackLabels, **trackNames;
char *selected = NULL;
char chrN_track[256];
char tbl[256];
int i;
AllocArray(trackLabels, trackCount+1);
AllocArray(trackNames, trackCount+1);
trackLabels[0] = "Browser tracks";
trackNames[0] = "Choose table";
for (t = trackList, i=1; t != NULL; t = t->next, ++i)
{
if (tdbIsDownloadsOnly(tdb) || tdb->table == NULL) // Don't bother with downloadsOnly for now
continue;
trackLabels[i] = t->shortLabel;
safef(chrN_track, sizeof(chrN_track), "%s_%s", hDefaultChrom(),
t->table);
if (hTableExists(chrN_track))
safef(tbl, sizeof(tbl), "%s.chrN_%s", database, t->table);
else
safef(tbl, sizeof(tbl), "%s.%s", database, t->table);
trackNames[i] = cloneString(tbl);
if (value != NULL && sameString(value, tbl))
selected = trackNames[i];
}
if (selected == NULL)
selected = trackNames[0];
cgiMakeDropListFull(varName, trackLabels, trackNames,
trackCount+1, selected, javascript);
}
boolean anyWildMatch(char *s, struct slName *wildList)
/* Return TRUE if s matches anything on wildList */
{
struct slName *wild;
for (wild = wildList; wild != NULL; wild = wild->next)
{
if (wildMatch(wild->name, s))
return TRUE;
}
return FALSE;
}
void doGateway()
/* Table Browser gateway page: select organism, db */
{
webStart(cart, "Table Browser: Choose Organism and Assembly");
if (! hDbIsActive(database))
{
database = hDefaultDb();
organism = hGenome(database);
}
-puts(
-"
"
+puts("
"
"
\n"
"
\n"
"The UCSC Table Browser was created by the \n"
"Genome Bioinformatics Group of UC Santa Cruz. \n"
" \n"
"Software Copyright (c) The Regents of the University of California.\n"
"All rights reserved.\n"
-"
\n"
-);
+ "
\n");
puts("
This tool allows you to download portions of the Genome Browser \n"
"database in several output formats. \n"
"Choose a genome and assembly, \n"
"then press the Submit button.\n");
puts("See the Table Browser "
"User Guide for more information.
\n"
"NOTE: This software has been replaced by a "
"newer version of the Table Browser. "
"This version of the tool is no longer "
"maintained or updated by UCSC; therefore, we can make no guarantees "
"about the completeness or correctness of the data returned. "
"We are happy to assist you in the transition to the new Table "
"Browser, which has many more features. Please "
"email our public mailing "
"list with questions or comments. "
"
");
puts("
");
printf(""
-"
\n"
-);
+puts(""
+ "\n");
printf("To reset all user cart settings (including custom tracks), \n"
"click here.\n",
hgTextName());
printf("");
hgPositionsHelpHtml(organism, database);
webEnd();
}
static boolean allLetters(char *s)
/* returns true if the string only has letters number and underscores */
{
int i;
for (i = 0; i < strlen(s); i++)
if (!isalnum(s[i]) && s[i] != '_')
return FALSE;
return TRUE;
}
void checkIsAlpha(char *desc, char *word)
/* make sure that the table name doesn't have anything "weird" in it */
{
if (!allLetters(word))
webAbort("Error", "Invalid %s \"%s\".", desc, word);
}
boolean isSqlStringType(char *type)
{
return(strstr(type, "char") ||
strstr(type, "text") ||
strstr(type, "blob"));
}
char *getPosition(char **retChrom, int *retStart, int *retEnd)
/* Get position from cgi (not cart); use hgFind if necessary; return NULL
* if we had to display the gateway page or hgFind's selection page. */
{
char *pos = stripCommas(cgiOptionalString("position"));
char rawPos[64];
if ((pos != NULL) && (pos[0] != 0))
{
char *newPos = searchPosition(pos, retChrom, retStart, retEnd);
if (newPos == NULL)
exit(0);
else
{
if (! isGenome(newPos))
{
snprintf(rawPos, sizeof(rawPos), "%s:%d-%d",
chrom, winStart+1, winEnd);
newPos = rawPos;
}
return(cloneString(newPos));
}
}
else
return(NULL);
}
struct customTrack *getCustomTracks()
{
if (theCtList == NULL)
theCtList = customTracksParseCart(cart, &browserLines, NULL);
return(theCtList);
}
struct customTrack *lookupCt(char *name)
{
struct customTrack *ctList = getCustomTracks();
struct customTrack *ct;
for (ct=ctList; ct != NULL; ct=ct->next)
{
if (sameString(ct->tdb->tableName, name))
return ct;
}
return NULL;
}
boolean tableExists(char *table, char *db)
{
if (sameString(customTrackPseudoDb, db))
return (lookupCt(table) != NULL);
if (sameString(database, db))
return hTableExists(table);
if (sameString(hGetDb2(), db))
return hTableExists2(table);
else
{
errAbort("Unrecognized database name: %s", db);
return FALSE;
}
}
void checkTableExists(char *table)
{
if (! tableExists(table, getTableDb()))
webAbort("No data", "Table %s (%s) does not exist in database %s.",
getTableName(), table, getTableDb());
}
void checkTable2Exists(char *table)
{
if (! tableExists(table, getTable2Db()))
webAbort("No data", "Table %s (%s) does not exist in database %s.",
getTable2Name(), table, getTable2Db());
}
boolean existsAndEqual(char *var, char *value)
/* returns true is the given CGI var exists and equals value */
{
if (cgiOptionalString(var) != 0 && sameString(cgiOptionalString(var), value))
return TRUE;
else
return FALSE;
}
static boolean existsAndStartsWith(char *var, char *value)
/* returns true is the given CGI var exists and starts with value */
{
if (cgiOptionalString(var) != 0 && startsWith(value, cgiOptionalString(var)))
return TRUE;
else
return FALSE;
}
void pasteForm()
/* Put up form that lets them paste in keys. */
{
webStart(cart, "Table Browser: Paste in Names/Accessions for Batch Query");
puts(""
"Help");
if (tableIsPositional)
{
puts("
Please paste in a list of names/accessions to match. "
"These may include * and ? wildcard characters.");
puts(" Note: name/accession matching is not supported for "
"non-positional tables.");
}
else
{
puts("
Sorry, pasting in names/accessions is not supported for "
"non-positional tables. (You can paste values into the "
"filter box of the field you would like to filter on the "
"Advanced Query page.)");
}
printf("
\n");
webEnd();
}
void uploadForm()
/* Put up upload form. */
{
webStart(cart, "Table Browser: Upload File of Names/Accessions for Batch Query");
puts(""
"Help");
printf("\n");
webEnd();
}
static void printSelectOptions(struct hashEl *optList, char *varName)
/* Print out an HTML select option for each element in a hashEl list.
* Mark as selected if it's the same as varName; strip prefix for display. */
{
struct hashEl *cur;
char *noPrefix;
char *curSetting = cartCgiUsualString(cart, varName, "");
for (cur = optList; cur != NULL; cur = cur->next)
{
if ((noPrefix = strchr(cur->name, '.')) != NULL)
noPrefix++;
else
noPrefix = cur->name;
if (sameString(curSetting, cur->name))
printf("\n",
cur->name, noPrefix);
else
printf("\n",
cur->name, noPrefix);
}
}
int compareTable(const void *elem1, const void *elem2)
/* compairs two hash element by name */
{
struct hashEl* a = *((struct hashEl **)elem1);
struct hashEl* b = *((struct hashEl **)elem2);
char *na, *nb;
if ((na = strchr(a->name, '.')) == NULL)
na = a->name;
if ((nb = strchr(b->name, '.')) == NULL)
nb = b->name;
return strcmp(na, nb);
}
boolean excludeTable(char *tbl)
/* Exclude these large tables. I think we should use a better algo. than
* hardcoding -- a count(*) cutoff? And should alert the user that the
* tables exist, but are being excluded due to size. */
{
return(sameString(tbl, "all_est") ||
sameString(tbl, "all_mrna"));
}
void getTableNames(char *db, struct sqlConnection *conn,
struct hashEl **retPosTableList,
struct hashEl **retNonposTableList)
/* separate tables in db into positional and nonpositional lists,
* with db added as a prefix to each name. */
{
struct hash *posTableHash = newHash(7);
struct hashEl *posTableList;
struct hash *nonposTableHash = newHash(7);
struct hashEl *nonposTableList;
struct sqlResult *sr;
char **row;
char query[256];
char name[128];
char chrom[32];
char post[64];
char fullName[128];
strcpy(query, "SHOW TABLES");
sr = sqlGetResult(conn, query);
while((row = sqlNextRow(sr)) != NULL)
{
if (excludeTable(row[0]))
continue;
/* if table name is of the form, chr*_random_* or chr*_*: */
if ( (sscanf(row[0], "chr%32[^_]_random_%64s", chrom, post) == 2) ||
(sscanf(row[0], "chr%32[^_]_hla_hap1_%64s", chrom, post) == 2) ||
(sscanf(row[0], "chr%32[^_]_hla_hap2_%64s", chrom, post) == 2) ||
(sscanf(row[0], "chr%32[^_]_%64s", chrom, post) == 2))
{
snprintf(name, sizeof(name), "chrN_%s", post);
// If a chrN_ table is already in the (positional) hash,
// don't bother looking up its fields.
if (hashLookup(posTableHash, name))
continue;
}
else
{
strncpy(name, row[0], sizeof(name));
}
snprintf(fullName, sizeof(fullName), "%s.%s", db, name);
if (hFindChromStartEndFieldsDb(db, row[0], query, query, query))
hashStoreName(posTableHash, cloneString(fullName));
else
hashStoreName(nonposTableHash, cloneString(fullName));
}
sqlFreeResult(&sr);
posTableList = hashElListHash(posTableHash);
slSort(&posTableList, compareTable);
nonposTableList = hashElListHash(nonposTableHash);
slSort(&nonposTableList, compareTable);
if (retPosTableList != NULL)
*retPosTableList = posTableList;
if (retNonposTableList != NULL)
*retNonposTableList = nonposTableList;
}
struct hashEl *getCustomTrackNames()
/* store custom track names in a hash (custom tracks are always positional) */
{
struct customTrack *ctList = getCustomTracks();
struct hash *ctTableHash = newHash(7);
struct hashEl *ctTableList;
struct customTrack *ct;
char fullName[128];
for (ct=ctList; ct != NULL; ct=ct->next)
{
snprintf(fullName, sizeof(fullName), "%s.%s",
customTrackPseudoDb, ct->tdb->tableName);
hashStoreName(ctTableHash, cloneString(fullName));
}
ctTableList = hashElListHash(ctTableHash);
slSort(&ctTableList, compareTable);
return(ctTableList);
}
void categorizeTables(struct hashEl **retPosTableList,
struct hashEl **retNonposTableList)
/* Return sorted lists of positional and non-positional table names
* from the current database and hgFixed. */
{
struct sqlConnection *conn;
struct hashEl *posTableList = NULL;
struct hashEl *nonposTableList = NULL;
struct hashEl *fixedPosTableList = NULL;
struct hashEl *fixedNonposTableList = NULL;
/* get table names from the database */
conn = hAllocConn(database);
getTableNames(database, conn, &posTableList, &nonposTableList);
hFreeConn(&conn);
/* get table names from hgFixed too */
conn = sqlConnect(hgFixed);
getTableNames(hgFixed, conn, &fixedPosTableList, &fixedNonposTableList);
sqlDisconnect(&conn);
/* append hgFixed db lists onto default db lists and return */
posTableList = slCat(posTableList, fixedPosTableList);
slSort(&posTableList, compareTable);
*retPosTableList = posTableList;
*retNonposTableList = slCat(nonposTableList, fixedNonposTableList);
slSort(retNonposTableList, compareTable);
}
void explainCoordSystem()
/* Our coord system is counter-intuitive to users. Warn them in advance to
* reduce the frequency with which they find this "bug" on their own and
* we have to explain it on the genome list. */
{
puts("
Note: all start coordinates in our database are 0-based, not \n"
"1-based. See explanation \n"
"here.
");
}
void doChooseTable()
/* Offer the user choice of tracks/tables, positions, actions */
{
struct hashEl *ctPosTableList = NULL;
struct hashEl *posTableList;
struct hashEl *nonposTableList;
char *keyStr = getUserKeys();
char *posOrKeys;
webStart(cart, "Table Browser: %s %s: Choose a table",
hOrganism(database), freezeName);
printf("");
explainCoordSystem();
hgPositionsHelpHtml(organism, database);
webEnd();
}
void getFullTableName(char *dest, char *newChrom, char *table)
/* given a chrom return the table name of the table selected by the user */
{
char post[64];
if (newChrom == NULL)
{
newChrom = hDefaultChrom();
}
chrom = newChrom;
if (allGenome)
{
winStart = 0;
winEnd = hChromSize(chrom);
}
if (sscanf(table, "chrN_%64s", post) == 1)
snprintf(dest, 256, "%s_%s", chrom, post);
else
strncpy(dest, table, 256);
/* make sure that the table name doesn't have anything "weird" in it */
checkIsAlpha("table name", dest);
}
void stringFilterOption(char *field, char *tableId, char *logOp)
/* Print out a table row with filter constraint options for a string/char. */
{
char name[128];
printf("
\n", logOp);
}
void filterOptionsCustomTrack(char *table, char *tableId)
/* Print out an HTML table with form inputs for constraints on custom track */
{
struct customTrack *ct = lookupCt(table);
puts("
");
if (ct->fieldCount >= 3)
{
stringFilterOption("chrom", tableId, " AND ");
numericFilterOption("chromStart", "chromStart", tableId, " AND ");
numericFilterOption("chromEnd", "chromEnd", tableId, " AND ");
}
if (ct->fieldCount >= 4)
{
stringFilterOption("name", tableId, " AND ");
}
if (ct->fieldCount >= 5)
{
numericFilterOption("score", "score", tableId, " AND ");
}
if (ct->fieldCount >= 6)
{
stringFilterOption("strand", tableId, " AND ");
}
if (ct->fieldCount >= 8)
{
numericFilterOption("thickStart", "thickStart", tableId, " AND ");
numericFilterOption("thickEnd", "thickEnd", tableId, " AND ");
}
if (ct->fieldCount >= 12)
{
numericFilterOption("blockCount", "blockCount", tableId, " AND ");
}
/* These are not bed fields, just extra constraints that we offer: */
if (ct->fieldCount >= 3)
{
numericFilterOption("chromLength", "(chromEnd - chromStart)", tableId,
(ct->fieldCount >= 8) ? " AND " : "");
}
if (ct->fieldCount >= 8)
{
numericFilterOption("thickLength", "(thickEnd - thickStart)",
tableId, " AND ");
eqFilterOption("compareStarts", "chromStart", "thickStart", tableId,
" AND ");
eqFilterOption("compareEnds", "chromEnd", "thickEnd", tableId, "");
}
puts("
");
}
void filterOptionsTableDb(char *fullTblName, char *db, char *tableId,
boolean filterWiggle)
/* Print out an HTML table with form inputs for constraints on table fields */
{
struct sqlConnection *conn = hAllocOrConnect(db);
struct sqlResult *sr;
char **row;
boolean gotFirst;
char query[256];
char name[128];
char *newVal;
snprintf(query, sizeof(query), "DESCRIBE %s", fullTblName);
sr = sqlGetResult(conn, query);
puts("
\n");
puts("
\n");
gotFirst = FALSE;
if (filterWiggle)
{
printf("
");
}
void parseNum(char *fieldName, struct kxTok **tok, struct dyString *q)
{
if (*tok == NULL)
webAbort("Error", "Parse error when reading number for field %s.",
fieldName);
if ((*tok)->type == kxtSub)
{
// unary '-': negative number, pass through to SQL.
dyStringAppend(q, (*tok)->string);
*tok = (*tok)->next;
}
if ((*tok)->type == kxtString)
{
if (! isdigit((*tok)->string[0]))
webAbort("Error", "Parse error when reading number for field %s.",
fieldName);
dyStringAppend(q, (*tok)->string);
*tok = (*tok)->next;
}
else
{
webAbort("Error", "Parse error when reading number for field %s: Incorrect token type %d for token \"%s\"",
fieldName, (*tok)->type, (*tok)->string);
}
}
void constrainNumber(char *fieldName, char *op, char *pat, char *log,
struct dyString *clause)
{
struct kxTok *tokList, *tokPtr;
int i;
boolean legit;
if (fieldName == NULL || op == NULL || pat == NULL || log == NULL)
webAbort("Error", "CGI var error: not all required vars were defined for field %s.", fieldName);
/* complain if op is not a legitimate value */
legit = FALSE;
for (i=0; i < cmpOpMenuSize; i++)
{
if (sameString(cmpOpMenu[i], op))
{
legit = TRUE;
break;
}
}
if (! legit)
webAbort("Error", "Illegal comparison operator \"%s\"", op);
/* tokenize (don't expect wildcards) */
tokPtr = tokList = kxTokenize(pat, FALSE);
if (clause->stringSize > 0)
dyStringPrintf(clause, " %s ", log);
else
dyStringAppend(clause, "(");
dyStringPrintf(clause, "(%s %s ", fieldName, op);
parseNum(fieldName, &tokPtr, clause);
dyStringAppend(clause, ")");
slFreeList(&tokList);
}
void constrainRange(char *fieldName, char *pat, char *log,
struct dyString *clause)
{
struct kxTok *tokList, *tokPtr;
if (fieldName == NULL || pat == NULL || log == NULL)
webAbort("Error", "CGI var error: not all required vars were defined for field %s.", fieldName);
/* tokenize (don't expect wildcards) */
tokPtr = tokList = kxTokenize(pat, FALSE);
if (clause->stringSize > 0)
dyStringPrintf(clause, " %s ", log);
else
dyStringAppend(clause, "(");
dyStringPrintf(clause, "((%s >= ", fieldName);
parseNum(fieldName, &tokPtr, clause);
dyStringPrintf(clause, ") && (%s <= ", fieldName);
while (tokPtr != NULL && tokPtr->type == kxtPunct)
tokPtr = tokPtr->next;
parseNum(fieldName, &tokPtr, clause);
dyStringAppend(clause, "))");
slFreeList(&tokList);
}
void constrainPattern(char *fieldName, char *dd, char *pat, char *log,
struct dyString *clause)
{
struct kxTok *tokList, *tokPtr;
boolean needOr = FALSE;
char *cmp, *or, *ptr;
int i;
boolean legit;
if (fieldName == NULL || dd == NULL || pat == NULL || log == NULL)
webAbort("Error", "CGI var error: not all required vars were defined for field %s.", fieldName);
/* complain if dd is not a legitimate value */
legit = FALSE;
for (i=0; i < ddOpMenuSize; i++)
{
if (sameString(ddOpMenu[i], dd))
{
legit = TRUE;
break;
}
}
if (! legit)
webAbort("Error", "Illegal does/doesn't value \"%s\"", dd);
/* tokenize (do allow wildcards, SQL wildcards, hyphens) */
tokList = kxTokenizeFancy(pat, TRUE, TRUE, TRUE);
/* The subterms are joined by OR if dd="does", AND if dd="doesn't" */
or = sameString(dd, "does") ? " OR " : " AND ";
cmp = sameString(dd, "does") ? "LIKE" : "NOT LIKE";
if (clause->stringSize > 0)
dyStringPrintf(clause, " %s ", log);
else
dyStringAppend(clause, "(");
dyStringAppend(clause, "(");
for (tokPtr = tokList; tokPtr != NULL; tokPtr = tokPtr->next)
{
if (tokPtr->type == kxtWildString || tokPtr->type == kxtString ||
/* Allow these types for strand matches too: */
tokPtr->type == kxtSub || tokPtr->type == kxtAdd)
{
if (needOr)
dyStringAppend(clause, or);
/* Replace normal wildcard characters with SQL: */
while ((ptr = strchr(tokPtr->string, '?')) != NULL)
*ptr = '_';
while ((ptr = strchr(tokPtr->string, '*')) != NULL)
*ptr = '%';
dyStringPrintf(clause, "(%s %s \"%s\")",
fieldName, cmp, tokPtr->string);
needOr = TRUE;
}
else if (tokPtr->type == kxtEnd)
break;
else
{
webAbort("Error", "Match pattern parse error for field %s: bad token type (%d) for this word: \"%s\".",
fieldName, tokPtr->type, tokPtr->string);
}
}
dyStringAppend(clause, ")");
slFreeList(&tokList);
}
void constrainFreeForm(char *rawQuery, struct dyString *clause)
/* Let the user type in an expression that may contain
* - field names
* - parentheses
* - comparison/arithmetic/logical operators
* - numbers
* - patterns with wildcards
* Make sure they don't use any SQL reserved words, ;'s, etc.
* Let SQL handle the actual parsing of nested expressions etc. -
* this is just a token cop. */
{
struct kxTok *tokList, *tokPtr;
char *ptr;
int numLeftParen, numRightParen;
if ((rawQuery == NULL) || (rawQuery[0] == 0))
return;
/* tokenize (do allow wildcards, and include quotes.) */
kxTokIncludeQuotes(TRUE);
tokList = kxTokenizeFancy(rawQuery, TRUE, TRUE, TRUE);
/* to be extra conservative, wrap the whole expression in parens. */
dyStringAppend(clause, "(");
numLeftParen = numRightParen = 0;
for (tokPtr = tokList; tokPtr != NULL; tokPtr = tokPtr->next)
{
if ((tokPtr->type == kxtEquals) ||
(tokPtr->type == kxtGT) ||
(tokPtr->type == kxtGE) ||
(tokPtr->type == kxtLT) ||
(tokPtr->type == kxtLE) ||
(tokPtr->type == kxtAnd) ||
(tokPtr->type == kxtOr) ||
(tokPtr->type == kxtNot) ||
(tokPtr->type == kxtAdd) ||
(tokPtr->type == kxtSub) ||
(tokPtr->type == kxtDiv))
{
dyStringAppend(clause, tokPtr->string);
}
else if (tokPtr->type == kxtOpenParen)
{
dyStringAppend(clause, tokPtr->string);
numLeftParen++;
}
else if (tokPtr->type == kxtCloseParen)
{
dyStringAppend(clause, tokPtr->string);
numRightParen++;
}
else if ((tokPtr->type == kxtWildString) ||
(tokPtr->type == kxtString))
{
char *word = cloneString(tokPtr->string);
toUpperN(word, strlen(word));
if (startsWith("SQL_", word) ||
startsWith("MYSQL_", word) ||
sameString("ALTER", word) ||
sameString("BENCHMARK", word) ||
sameString("CHANGE", word) ||
sameString("CREATE", word) ||
sameString("DELAY", word) ||
sameString("DELETE", word) ||
sameString("DROP", word) ||
sameString("FLUSH", word) ||
sameString("GET_LOCK", word) ||
sameString("GRANT", word) ||
sameString("INSERT", word) ||
sameString("KILL", word) ||
sameString("LOAD", word) ||
sameString("LOAD_FILE", word) ||
sameString("LOCK", word) ||
sameString("MODIFY", word) ||
sameString("PROCESS", word) ||
sameString("QUIT", word) ||
sameString("RELEASE_LOCK", word) ||
sameString("RELOAD", word) ||
sameString("REPLACE", word) ||
sameString("REVOKE", word) ||
sameString("SELECT", word) ||
sameString("SESSION_USER", word) ||
sameString("SHOW", word) ||
sameString("SYSTEM_USER", word) ||
sameString("UNLOCK", word) ||
sameString("UPDATE", word) ||
sameString("USE", word) ||
sameString("USER", word) ||
sameString("VERSION", word))
{
webAbort("Error", "Illegal SQL word \"%s\" in free-form query string",
tokPtr->string);
}
else if (sameString("*", tokPtr->string))
{
// special case for multiplication in a wildcard world
dyStringPrintf(clause, " %s ", tokPtr->string);
}
else
{
/* Replace normal wildcard characters with SQL: */
while ((ptr = strchr(tokPtr->string, '?')) != NULL)
*ptr = '_';
while ((ptr = strchr(tokPtr->string, '*')) != NULL)
*ptr = '%';
dyStringPrintf(clause, " %s ", tokPtr->string);
}
}
else if (tokPtr->type == kxtEnd)
{
break;
}
else
{
webAbort("Error", "Unrecognized token \"%s\" in free-form query string",
tokPtr->string);
}
}
dyStringAppend(clause, ")");
if (numLeftParen != numRightParen)
webAbort("Error", "Unequal number of left parentheses (%d) and right parentheses (%d) in free-form query expression",
numLeftParen, numRightParen);
slFreeList(&tokList);
}
char *constrainFields(char *tableId)
/* If the user specified constraints, append SQL conditions (suitable
* for a WHERE clause) to q. */
{
struct cgiVar *current;
struct dyString *freeClause = newDyString(512);
struct dyString *andClause = newDyString(512);
struct dyString *clause;
char *fieldName;
char *rawQuery;
char *rQLogOp;
char *dd, *cmp, *pat;
char varName[128];
char *ret;
int tableIndex = 0;
if (tableId == NULL)
{
tableId = "";
tableIndex = 0;
}
if (sameString(tableId,"2"))
tableIndex = 1;
dyStringClear(andClause);
for (current = cgiVarList(); current != NULL; current = current->next)
{
/* Look for pattern variable associated with each field. */
snprintf(varName, sizeof(varName), "pat%s_", tableId);
if (startsWith(varName, current->name))
{
fieldName = current->name + strlen(varName);
/* make sure that the field name doesn't have anything "weird" in it */
checkIsAlpha("field name", fieldName);
pat = current->val;
snprintf(varName, sizeof(varName), "dd%s_%s", tableId, fieldName);
dd = cgiOptionalString(varName);
snprintf(varName, sizeof(varName), "cmp%s_%s", tableId, fieldName);
cmp = cgiOptionalString(varName);
if (sameString(fieldName,"wigDataValue"))
{
if (strlen(cmp)>0 && differentWord(cmp,"ignored"))
wiggleConstraints(cmp, pat, tableIndex);
}
/* If it's a null constraint, skip it. */
if ( (dd != NULL &&
(pat == NULL || pat[0] == 0 ||
sameString(trimSpaces(pat), "*"))) ||
(cmp != NULL && sameString(cmp, "ignored")) ||
sameString(fieldName,"wigDataValue") )
continue;
/* Otherwise, expect it to be a well-formed constraint and tack
* it on to the clause. */
clause = andClause;
if (cmp != NULL && sameString(cmp, "in range"))
constrainRange(fieldName, pat, "AND", clause);
else if (cmp != NULL)
constrainNumber(fieldName, cmp, pat, "AND", clause);
else
constrainPattern(fieldName, dd, pat, "AND", clause);
}
}
if (andClause->stringSize > 0)
dyStringAppend(andClause, ")");
dyStringClear(freeClause);
snprintf(varName, sizeof(varName), "rawQuery%s", tableId);
rawQuery = cgiOptionalString(varName);
snprintf(varName, sizeof(varName), "log_rawQuery%s", tableId);
rQLogOp = cgiOptionalString(varName);
constrainFreeForm(rawQuery, freeClause);
// force rQLogOp to a legit value:
if ((rQLogOp != NULL) && (! sameString("AND", rQLogOp)))
rQLogOp = "OR";
if (freeClause->stringSize > 0 && andClause->stringSize > 0)
dyStringPrintf(freeClause, " %s ", rQLogOp);
if (freeClause->stringSize > 0)
{
dyStringAppend(freeClause, andClause->string);
}
else
{
dyStringAppend(freeClause, andClause->string);
}
ret = cloneString(freeClause->string);
freeDyString(&freeClause);
freeDyString(&andClause);
return ret;
}
void cgiToCharFilter(char *dd, char *pat, enum charFilterType *retCft,
char **retVals, boolean *retInv)
/* Given a "does/doesn't" and a (list of) literal chars from CGI, fill in
* retCft, retVals and retInv to make a filter. */
{
char *vals, *ptrs[32];
int numWords;
int i;
assert(retCft != NULL);
assert(retVals != NULL);
assert(retInv != NULL);
assert(sameString(dd, "does") || sameString(dd, "doesn't"));
/* catch null-constraint cases. ? will be treated as a literal match,
* which would make sense for bed strand and maybe other single-char things: */
if (pat == NULL)
pat = "";
pat = trimSpaces(pat);
if ((pat[0] == 0) || sameString(pat, "*"))
{
*retCft = cftIgnore;
return;
}
*retCft = cftMultiLiteral;
numWords = chopByWhite(pat, ptrs, ArraySize(ptrs));
vals = needMem((numWords+1) * sizeof(char));
for (i=0; i < numWords; i++)
vals[i] = ptrs[i][0];
vals[i] = 0;
*retVals = vals;
*retInv = sameString("doesn't", dd);
}
void cgiToStringFilter(char *dd, char *pat, enum stringFilterType *retSft,
char ***retVals, boolean *retInv)
/* Given a "does/doesn't" and a (list of) regexps from CGI, fill in
* retCft, retVals and retInv to make a filter. */
{
char **vals, *ptrs[32];
int numWords;
int i;
assert(retSft != NULL);
assert(retVals != NULL);
assert(retInv != NULL);
assert(sameString(dd, "does") || sameString(dd, "doesn't"));
/* catch null-constraint cases: */
if (pat == NULL)
pat = "";
pat = trimSpaces(pat);
if ((pat[0] == 0) || sameString(pat, "*"))
{
*retSft = sftIgnore;
return;
}
*retSft = sftMultiRegexp;
numWords = chopByWhite(pat, ptrs, ArraySize(ptrs));
vals = needMem((numWords+1) * sizeof(char *));
for (i=0; i < numWords; i++)
vals[i] = cloneString(ptrs[i]);
vals[i] = NULL;
*retVals = vals;
*retInv = sameString("doesn't", dd);
}
void cgiToIntFilter(char *cmp, char *pat, enum numericFilterType *retNft,
int **retVals)
/* Given a comparison operator and a (pair of) integers from CGI, fill in
* retNft and retVals to make a filter. */
{
char *ptrs[3];
int *vals;
int numWords;
assert(retNft != NULL);
assert(retVals != NULL);
/* catch null-constraint cases: */
if (pat == NULL)
pat = "";
pat = trimSpaces(pat);
if ((pat[0] == 0) || sameString(pat, "*") || sameString(cmp, "ignored"))
{
*retNft = nftIgnore;
return;
}
else if (sameString(cmp, "in range"))
{
*retNft = nftInRange;
numWords = chopString(pat, " \t,", ptrs, ArraySize(ptrs));
if (numWords != 2)
errAbort("For \"in range\" constraint, you must give two numbers separated by whitespace or comma.");
vals = needMem(2 * sizeof(int));
vals[0] = atoi(ptrs[0]);
vals[1] = atoi(ptrs[1]);
if (vals[0] > vals[1])
{
int tmp = vals[0];
vals[0] = vals[1];
vals[1] = tmp;
}
*retVals = vals;
}
else
{
if (sameString(cmp, "<"))
*retNft = nftLessThan;
else if (sameString(cmp, "<="))
*retNft = nftLTE;
else if (sameString(cmp, "="))
*retNft = nftEqual;
else if (sameString(cmp, "!="))
*retNft = nftNotEqual;
else if (sameString(cmp, ">="))
*retNft = nftGTE;
else if (sameString(cmp, ">"))
*retNft = nftGreaterThan;
else
errAbort("Unrecognized comparison operator %s", cmp);
vals = needMem(sizeof(int));
vals[0] = atoi(pat);
*retVals = vals;
}
}
struct bedFilter *constrainBedFields(char *tableId)
/* If the user specified constraints, then translate them to a bedFilter. */
{
struct bedFilter *bf;
struct cgiVar *current;
char *fieldName;
char *dd, *cmp, *pat;
char varName[128];
int *trash;
if (tableId == NULL)
tableId = "";
AllocVar(bf);
for (current = cgiVarList(); current != NULL; current = current->next)
{
/* Look for pattern variable associated with each field. */
snprintf(varName, sizeof(varName), "pat%s_", tableId);
if (startsWith(varName, current->name))
{
fieldName = current->name + strlen(varName);
checkIsAlpha("field name", fieldName);
pat = cloneString(current->val);
snprintf(varName, sizeof(varName), "dd%s_%s", tableId, fieldName);
dd = cgiOptionalString(varName);
snprintf(varName, sizeof(varName), "cmp%s_%s", tableId, fieldName);
cmp = cgiOptionalString(varName);
if (sameString("chrom", fieldName))
cgiToStringFilter(dd, pat, &(bf->chromFilter), &(bf->chromVals),
&(bf->chromInvert));
else if (sameString("chromStart", fieldName))
cgiToIntFilter(cmp, pat,
&(bf->chromStartFilter), &(bf->chromStartVals));
else if (sameString("chromEnd", fieldName))
cgiToIntFilter(cmp, pat,
&(bf->chromEndFilter), &(bf->chromEndVals));
else if (sameString("name", fieldName))
cgiToStringFilter(dd, pat, &(bf->nameFilter), &(bf->nameVals),
&(bf->nameInvert));
else if (sameString("score", fieldName))
cgiToIntFilter(cmp, pat,
&(bf->scoreFilter), &(bf->scoreVals));
else if (sameString("strand", fieldName))
cgiToCharFilter(dd, pat, &(bf->strandFilter), &(bf->strandVals),
&(bf->strandInvert));
else if (sameString("thickStart", fieldName))
cgiToIntFilter(cmp, pat,
&(bf->thickStartFilter), &(bf->thickStartVals));
else if (sameString("thickEnd", fieldName))
cgiToIntFilter(cmp, pat,
&(bf->thickEndFilter), &(bf->thickEndVals));
else if (sameString("blockCount", fieldName))
cgiToIntFilter(cmp, pat,
&(bf->blockCountFilter), &(bf->blockCountVals));
else if (sameString("chromLength", fieldName))
cgiToIntFilter(cmp, pat,
&(bf->chromLengthFilter), &(bf->chromLengthVals));
else if (sameString("thickLength", fieldName))
cgiToIntFilter(cmp, pat,
&(bf->thickLengthFilter), &(bf->thickLengthVals));
else if (sameString("compareStarts", fieldName))
cgiToIntFilter(cmp, pat,
&(bf->compareStartsFilter), &trash);
else if (sameString("compareEnds", fieldName))
cgiToIntFilter(cmp, pat,
&(bf->compareEndsFilter), &trash);
}
}
return(bf);
}
void preserveConstraints(char *fullTblName, char *db, char *tableId)
/* Add CGI variables for filtering constraints, so they will be passed to
* the next page. Also parse the constraints and do a null query with them
* in order to catch any syntax errors sooner rather than later. */
{
struct cgiVar *current;
char *constraints = constrainFields(tableId);
char varName[128];
if ((constraints != NULL) && (constraints[0] != 0) &&
(! sameString(customTrackPseudoDb, db)))
{
struct sqlConnection *conn = hAllocOrConnect(db);
struct sqlResult *sr;
struct dyString *query = newDyString(512);
// Null query will cause errAbort if there's a syntax error, no-op if OK.
dyStringPrintf(query, "SELECT 1 FROM %s WHERE 0 AND %s",
fullTblName, constraints);
sr = sqlGetResult(conn, query->string);
dyStringFree(&query);
sqlFreeResult(&sr);
hFreeOrDisconnect(&conn);
}
if (tableId == NULL)
tableId = "";
for (current = cgiVarList(); current != NULL; current = current->next)
{
/* Look for pattern variable associated with each field. */
snprintf(varName, sizeof(varName), "pat%s_", tableId);
if (startsWith(varName, current->name))
cgiMakeHiddenVar(current->name, current->val);
snprintf(varName, sizeof(varName), "dd%s_", tableId);
if (startsWith(varName, current->name))
cgiMakeHiddenVar(current->name, current->val);
snprintf(varName, sizeof(varName), "cmp%s_", tableId);
if (startsWith(varName, current->name))
cgiMakeHiddenVar(current->name, current->val);
snprintf(varName, sizeof(varName), "log_rawQuery%s", tableId);
if (sameString(varName, current->name))
cgiMakeHiddenVar(current->name, current->val);
snprintf(varName, sizeof(varName), "rawQuery%s", tableId);
if (sameString(varName, current->name))
{
// Replace " with ' in rawQuery; the value will be double-quoted
// in the form, and double-quotes in the value really mess it up.
subChar(current->val, '"', '\'');
cgiMakeHiddenVar(current->name, current->val);
}
}
}
void preserveTable2()
{
char *table2 = getTable2Name();
char *op = cgiOptionalString("tbIntersectOp");
if ((table2 != NULL) && (table2[0] != 0) && (op != NULL))
{
char *db2 = getTable2Db();
char fullTableName2[256];
cgiContinueHiddenVar("table2");
cgiContinueHiddenVar("tbIntersectOp");
cgiMakeHiddenVar("tbMoreThresh", cgiUsualString("tbMoreThresh", "0"));
cgiMakeHiddenVar("tbLessThresh", cgiUsualString("tbLessThresh", "100"));
cgiContinueHiddenVar("tbInvertTable");
cgiContinueHiddenVar("tbInvertTable2");
getFullTableName(fullTableName2, chrom, table2);
preserveConstraints(fullTableName2, db2, "2");
}
}
struct hTableInfo *ctToHti(struct customTrack *ct)
/* Create an hTableInfo from a customTrack. */
{
struct hTableInfo *hti;
if (ct == NULL)
return(NULL);
AllocVar(hti);
hti->rootName = cloneString(ct->tdb->tableName);
hti->isPos = TRUE;
hti->isSplit = FALSE;
hti->hasBin = FALSE;
hti->type = cloneString(ct->tdb->type);
if (ct->fieldCount >= 3)
{
strncpy(hti->chromField, "chrom", 32);
strncpy(hti->startField, "chromStart", 32);
strncpy(hti->endField, "chromEnd", 32);
}
if (ct->fieldCount >= 4)
{
strncpy(hti->nameField, "name", 32);
}
if (ct->fieldCount >= 5)
{
strncpy(hti->scoreField, "score", 32);
}
if (ct->fieldCount >= 6)
{
strncpy(hti->strandField, "strand", 32);
}
if (ct->fieldCount >= 8)
{
strncpy(hti->cdsStartField, "thickStart", 32);
strncpy(hti->cdsEndField, "thickEnd", 32);
hti->hasCDS = TRUE;
}
if (ct->fieldCount >= 12)
{
strncpy(hti->countField, "blockCount", 32);
strncpy(hti->startsField, "chromStarts", 32);
strncpy(hti->endsSizesField, "blockSizes", 32);
hti->hasBlocks = TRUE;
}
return(hti);
}
struct hTableInfo *maybeGetHti(char *db, char *table)
/* Return primary table info. */
{
struct hTableInfo *hti = NULL;
if (sameString(customTrackPseudoDb, db))
{
struct customTrack *ct = lookupCt(table);
hti = ctToHti(ct);
}
else
{
char *track;
if (startsWith("chrN_", table))
track = table + strlen("chrN_");
else
track = table;
hti = hFindTableInfoDb(db, chrom, track);
}
return(hti);
}
struct hTableInfo *getHti(char *db, char *table)
/* Return primary table info. */
{
struct hTableInfo *hti = maybeGetHti(db, table);
if (hti == NULL)
webAbort("Error", "Could not find table info for table %s in db %s",
table, db);
return(hti);
}
struct hTableInfo *getOutputHti()
/* Return effective table info for the output. If we're doing a
* base-pair-wise intersection/union of 2 tables, this will be a
* bed4 that isn't a track (unless user makes it a custom track).
* Otherwise this will just be the primary table info. */
{
char *db = getTableDb();
char *table = getTableName();
struct hTableInfo *hti = getHti(db, table);
char *table2 = getTable2Name();
char *op = cgiOptionalString("tbIntersectOp");
if ((table2 != NULL) && (table2[0] != 0) && (op != NULL))
{
char buf[128];
if (sameString("and", op) || sameString("or", op))
{
struct hTableInfo *effHti;
AllocVar(effHti);
snprintf(buf, sizeof(buf), "%s_%s_%s", table, op, table2);
effHti->rootName = cloneString(buf);
effHti->isPos = TRUE;
effHti->isSplit = FALSE;
effHti->hasBin = FALSE;
snprintf(effHti->chromField, 32, "N/A");
snprintf(effHti->startField, 32, "N/A");
snprintf(effHti->endField, 32, "N/A");
snprintf(effHti->nameField, 32, "N/A");
effHti->scoreField[0] = 0;
effHti->strandField[0] = 0;
effHti->cdsStartField[0] = 0;
effHti->cdsEndField[0] = 0;
effHti->countField[0] = 0;
effHti->startsField[0] = 0;
effHti->endsSizesField[0] = 0;
effHti->hasCDS = FALSE;
effHti->hasBlocks = FALSE;
strcpy(buf, "bed 4");
effHti->type = cloneString(buf);
return(effHti);
}
else
{
snprintf(buf, sizeof(buf), "%s_%s_%s", hti->rootName, op, table2);
hti->rootName = cloneString(buf);
}
}
return(hti);
}
boolean isWiggle(char *db, char *table)
/* Return TRUE if db.table is a wiggle. */
{
boolean typeWiggle = FALSE;
if (db != NULL && table != NULL)
{
if (sameString(customTrackPseudoDb, db))
{
struct customTrack *ct = lookupCt(table);
if (ct && ct->wiggle)
typeWiggle = FALSE; /* TEMPORARY, until we handle this case */
/* this is actually the TRUE condition */
}
else
{
struct hTableInfo *hti = maybeGetHti(db, table);
typeWiggle = (hti != NULL && HTI_IS_WIGGLE);
}
}
return(typeWiggle);
}
void doOutputOptions()
/* print out a form with output table format & filtering options */
{
struct hashEl *ctPosTableList = NULL;
struct hashEl *posTableList = NULL;
struct hashEl *nonposTableList = NULL;
char *table = getTableName();
char *db = getTableDb();
saveChooseTableState();
webStart(cart, "Table Browser: %s %s: %s", hOrganism(database),freezeName, outputOptionsPhase);
checkTableExists(fullTableName);
printf("");
webEnd();
}
void bedFilterBatch(struct bed **bedListPtr)
/* If position is batch, filter by name. */
{
if (isBatch())
{
struct bed *bedListOut = NULL;
char *keyStr = getUserKeys();
char *word;
if (cgiVarExists("tbShowUploadResults"))
{
struct hash *nameHash = newHash(18);
while ((word = nextWord(&keyStr)) != NULL)
{
hashAdd(nameHash, word, NULL);
}
bedListOut = bedFilterByNameHash(*bedListPtr, nameHash);
hashFree(&nameHash);
}
else
{
struct slName *wildNames = NULL, *wild=NULL;
while ((word = nextWord(&keyStr)) != NULL)
{
wild = slNameNew(word);
slAddHead(&wildNames, wild);
}
bedListOut = bedFilterByWildNames(*bedListPtr, wildNames);
slFreeList(&wildNames);
}
bedFreeList(bedListPtr);
*bedListPtr = bedListOut;;
}
}
struct bed *bitsToBed4List(Bits *bits, int bitSize, char *chrom, int minSize,
int rangeStart, int rangeEnd)
/* Translate ranges of set bits to bed 4 items. */
{
struct bed *bedList = NULL, *bed;
int i;
boolean thisBit, lastBit;
int start = 0;
int end;
int id = 0;
char name[128];
if (rangeStart < 0)
rangeStart = 0;
if (rangeEnd > bitSize)
rangeEnd = bitSize;
/* We depend on extra zero BYTE at end in case bitNot was used on bits. */
thisBit = FALSE;
for (i=0; i < bitSize+8; ++i)
{
lastBit = thisBit;
thisBit = bitReadOne(bits, i);
if (thisBit)
{
if (!lastBit)
start = i;
}
else
{
end = i;
if (end >= bitSize)
end = bitSize - 1;
// Lop off elements that go all the way to the beginning/end of the
// chrom... unless our range actually includes the beginning/end.
// (That can happen with the AND/OR of two NOT's...)
if (lastBit &&
((end - start) >= minSize) &&
((rangeStart == 0) || (start > 0)) &&
((rangeEnd == bitSize) || (end < bitSize)))
{
AllocVar(bed);
bed->chrom = cloneString(chrom);
bed->chromStart = start;
bed->chromEnd = end;
snprintf(name, sizeof(name), "%s.%d", chrom, ++id);
bed->name = cloneString(name);
slAddHead(&bedList, bed);
}
}
}
slReverse(&bedList);
return(bedList);
}
int countBasesOverlap(struct bed *bedItem, Bits *bits, boolean hasBlocks)
/* Return the number of bases belonging to bedItem covered by bits. */
{
int count = 0;
int i, j;
if (hasBlocks)
{
for (i=0; i < bedItem->blockCount; i++)
{
int start = bedItem->chromStart + bedItem->chromStarts[i];
int end = start + bedItem->blockSizes[i];
for (j=start; j < end; j++)
if (bitReadOne(bits, j))
count++;
}
}
else
{
for (i=bedItem->chromStart; i < bedItem->chromEnd; i++)
if (bitReadOne(bits, i))
count++;
}
return(count);
}
struct bed *getBedList(boolean ignoreConstraints, char *onlyThisChrom)
/* For any positional table output: get the features selected by the user
* and return them as a bed list. This is where table intersection happens. */
{
struct slName *chromList, *chromPtr;
struct bed *bedList = NULL, *bedListT1 = NULL, *bedListChrom = NULL;
char *db = getTableDb();
char *table = getTableName();
struct hTableInfo *hti = getHti(db, table);
char *constraints;
char *table2 = getTable2Name();
char *op = cgiOptionalString("tbIntersectOp");
char *track = getTrackName();
int i;
if (! tableExists(fullTableName, db))
return NULL;
constraints = constrainFields(NULL);
if (ignoreConstraints ||
((constraints != NULL) && (constraints[0] == 0)))
constraints = NULL;
if (onlyThisChrom != NULL)
chromList = newSlName(onlyThisChrom);
else if (allGenome)
chromList = hAllChromNames();
else
chromList = newSlName(chrom);
for (chromPtr=chromList; chromPtr != NULL; chromPtr = chromPtr->next)
{
bedListChrom = NULL;
getFullTableName(fullTableName, chromPtr->name, table);
if (! tableExists(fullTableName, db))
continue;
if (sameString(customTrackPseudoDb, db))
{
struct customTrack *ct = lookupCt(table);
struct bedFilter *bf = NULL;
if (! ignoreConstraints)
bf = constrainBedFields(NULL);
bedListT1 = bedFilterListInRange(ct->bedList, bf,
chrom, winStart, winEnd);
}
else
{
if (typeWiggle)
wigMakeBedList(db, fullTableName, chromPtr->name,
constraints, WIG_TABLE_1);
if (typeWiggle && (bedListWig[WIG_TABLE_1] != (struct bed *)NULL))
bedListT1 = bedListWig[WIG_TABLE_1];
else
bedListT1 = hGetBedRangeDb(db, fullTableName, chrom, winStart,
winEnd, constraints);
}
bedFilterBatch(&bedListT1);
/* If 2 tables are named, get their intersection. */
if ((table2 != NULL) && (table2[0] != 0) && (op != NULL))
{
struct featureBits *fbListT2 = NULL;
struct bed *bed;
Bits *bitsT2;
int moreThresh = cgiOptionalInt("tbMoreThresh", 0);
int lessThresh = cgiOptionalInt("tbLessThresh", 100);
boolean invTable = cgiBoolean("tbInvertTable");
boolean invTable2 = cgiBoolean("tbInvertTable2");
char *track2 = getTrack2Name();
char *db2 = getTable2Db();
char *constraints2 = constrainFields("2");
char fullTableName2[256];
int chromSize = hChromSize(chromPtr->name);
boolean isBpWise = (sameString("and", op) || sameString("or", op));
if ((!sameString("any", op)) &&
(!sameString("none", op)) &&
(!sameString("more", op)) &&
(!sameString("less", op)) &&
(!sameString("and", op)) &&
(!sameString("or", op)))
{
webAbort("Error", "Invalid value \"%s\" of CGI variable tbIntersectOp", op);
}
if (ignoreConstraints ||
((constraints2 != NULL) && (constraints2[0] == 0)))
constraints2 = NULL;
fprintf(stderr, "TBQuery: p=%s:%d-%d t1=%s q1=%s t2=%s q2=%s op=%s mT=%d lT=%d iT=%d iT2=%d\n",
chrom, winStart, winEnd,
table, constraints,
table2, constraints2,
op, moreThresh, lessThresh, invTable, invTable2);
getFullTableName(fullTableName2, chromPtr->name, table2);
if (! tableExists(fullTableName2, db2))
fbListT2 = NULL;
else if (sameString(customTrackPseudoDb, db2))
{
struct customTrack *ct2 = lookupCt(table2);
struct bedFilter *bf2 = NULL;
struct bed *bedListT2 = bedFilterListInRange(ct2->bedList, bf2,
chrom, winStart, winEnd);
struct hTableInfo *hti2 = ctToHti(ct2);
if (! ignoreConstraints)
bf2 = constrainBedFields("2");
fbListT2 = fbFromBed(track2, hti2, bedListT2, winStart, winEnd,
isBpWise, FALSE);
bedFreeList(&bedListT2);
}
else
{
char *db2 = getTable2Db();
struct hTableInfo *hti2 = getHti(db2, table2);
if (typeWiggle2)
wigMakeBedList(db2, table2, chromPtr->name, constraints2,
WIG_TABLE_2);
if ((typeWiggle2) && (bedListWig[WIG_TABLE_2] !=(struct bed *)NULL))
{
fbListT2 = fbFromBed(track2, hti2, bedListWig[WIG_TABLE_2],
winStart, winEnd, isBpWise, FALSE);
bedFreeList(&bedListWig[WIG_TABLE_2]);
}
else
fbListT2 = fbGetRangeQueryDb(db2, track2, chrom, winStart,
winEnd, constraints2, isBpWise, FALSE);
}
bitsT2 = bitAlloc(chromSize+8);
fbOrBits(bitsT2, chromSize, fbListT2, 0);
if (isBpWise)
{
// Base-pair-wise operation: get featureBits for primary table too
struct featureBits *fbListT1;
Bits *bitsT1;
fbListT1 = fbFromBed(track, hti, bedListT1, winStart, winEnd,
isBpWise, FALSE);
bitsT1 = bitAlloc(chromSize+8);
fbOrBits(bitsT1, chromSize, fbListT1, 0);
// invert inputs if necessary
if (invTable)
bitNot(bitsT1, chromSize);
if (invTable2)
bitNot(bitsT2, chromSize);
// do the intersection/union
if (sameString("and", op))
bitAnd(bitsT1, bitsT2, chromSize);
else
bitOr(bitsT1, bitsT2, chromSize);
// translate back to bed
bedListChrom = bitsToBed4List(bitsT1, chromSize, chrom, 1,
winStart, winEnd);
bitFree(&bitsT1);
featureBitsFreeList(&fbListT1);
}
else
{
for (bed = bedListT1; bed != NULL; bed = bed->next)
{
struct bed *newBed;
int numBasesOverlap = countBasesOverlap(bed, bitsT2,
hti->hasBlocks);
int length = 0;
double pctBasesOverlap;
if (hti->hasBlocks)
for (i=0; i < bed->blockCount; i++)
length += bed->blockSizes[i];
else
length = (bed->chromEnd - bed->chromStart);
if (length == 0)
length = 1;
pctBasesOverlap = ((numBasesOverlap * 100.0) / length);
if ((sameString("any", op) && (numBasesOverlap > 0)) ||
(sameString("none", op) && (numBasesOverlap == 0)) ||
(sameString("more", op) &&
(pctBasesOverlap >= moreThresh)) ||
(sameString("less", op) &&
(pctBasesOverlap <= lessThresh)))
{
newBed = cloneBed(bed);
slAddHead(&bedListChrom, newBed);
}
}
slReverse(&bedListChrom);
} // end foreach primary table bed item
bedFreeList(&bedListT1);
bitFree(&bitsT2);
featureBitsFreeList(&fbListT2);
}
else
{
/* Only one table was given, no intersection necessary. */
bedListChrom = bedListT1;
}
bedList = slCat(bedList, bedListChrom);
} // end foreach chromosome in position range
return(bedList);
}
struct slName *getAllFields()
/* Return a list of all field names in the primary table. */
{
struct slName *fieldList = NULL, *field;
char *db = getTableDb();
if (sameString(customTrackPseudoDb, db))
{
char *table = getTableName();
struct customTrack *ct = lookupCt(table);
if (ct->fieldCount >= 3)
{
field = newSlName("chrom");
slAddHead(&fieldList, field);
field = newSlName("chromStart");
slAddHead(&fieldList, field);
field = newSlName("chromEnd");
slAddHead(&fieldList, field);
}
if (ct->fieldCount >= 4)
{
field = newSlName("name");
slAddHead(&fieldList, field);
}
if (ct->fieldCount >= 5)
{
field = newSlName("score");
slAddHead(&fieldList, field);
}
if (ct->fieldCount >= 6)
{
field = newSlName("strand");
slAddHead(&fieldList, field);
}
if (ct->fieldCount >= 8)
{
field = newSlName("thickStart");
slAddHead(&fieldList, field);
field = newSlName("thickEnd");
slAddHead(&fieldList, field);
}
if (ct->fieldCount >= 9)
{
field = newSlName("itemRgb");
slAddHead(&fieldList, field);
}
if (ct->fieldCount >= 12)
{
field = newSlName("blockCount");
slAddHead(&fieldList, field);
field = newSlName("blockSizes");
slAddHead(&fieldList, field);
field = newSlName("chromStarts");
slAddHead(&fieldList, field);
}
}
else
{
struct sqlConnection *conn = hAllocOrConnect(db);
struct sqlResult *sr;
char **row;
char query[256];
snprintf(query, sizeof(query), "DESCRIBE %s", fullTableName);
sr = sqlGetResult(conn, query);
while ((row = sqlNextRow(sr)) != NULL)
{
field = newSlName(row[0]);
slAddHead(&fieldList, field);
}
sqlFreeResult(&sr);
hFreeOrDisconnect(&conn);
}
slReverse(&fieldList);
return(fieldList);
}
struct slName *getChosenFields(boolean allFields)
/* Return a list of chosen field names. */
{
struct slName *tableFields = getAllFields();
if (allFields)
{
return(tableFields);
}
else
{
struct slName *fieldList = NULL, *field;
struct cgiVar* varPtr;
for (varPtr = cgiVarList(); varPtr != NULL; varPtr = varPtr->next)
{
if (startsWith("field_", varPtr->name) &&
sameString("on", varPtr->val))
{
char *fieldStr = varPtr->name + strlen("field_");
checkIsAlpha("field name", fieldStr);
/* check that the field is there in the current table (and not
* just a stale CGI var) */
if (slNameInList(tableFields, fieldStr))
{
field = newSlName(fieldStr);
slAddHead(&fieldList, field);
}
}
}
slReverse(&fieldList);
return(fieldList);
}
}
boolean printTabbedResults(struct sqlResult *sr, boolean initialized)
{
struct hash *nameHash = newHash(18);
struct slName *wildNames = NULL, *wild=NULL;
struct hTableInfo *hti = NULL;
char *table = getTableName();
char *db = getTableDb();
char *keyStr = getUserKeys();
char *word;
char **row;
int i;
int numberColumns = sqlCountColumns(sr);
row = sqlNextRow(sr);
if (row == NULL)
return(initialized);
hti = getHti(db, table);
if (isBatch() && hti->nameField[0] != 0)
{
// last column is name (for batch filtering) -- don't print it out.
numberColumns = numberColumns - 1;
if (cgiVarExists("tbShowUploadResults"))
{
while ((word = nextWord(&keyStr)) != NULL)
{
hashAdd(nameHash, word, NULL);
}
}
else
{
while ((word = nextWord(&keyStr)) != NULL)
{
wild = slNameNew(word);
slAddHead(&wildNames, wild);
}
}
}
if (! initialized)
{
initialized = TRUE;
/* print the columns names */
printf("#");
for (i = 0; i < numberColumns; i++)
{
printf("%s\t", sqlFieldName(sr));
}
printf("\n");
}
/* print the data */
do
{
if ((! isBatch()) || (hti->nameField[0] == 0) ||
(cgiVarExists("tbShowUploadResults") &&
(hashLookup(nameHash, row[numberColumns]) != NULL)) ||
(anyWildMatch(row[numberColumns], wildNames)))
{
for (i = 0; i < numberColumns; i++)
printf("%s\t", row[i]);
printf("\n");
}
}
while((row = sqlNextRow(sr)) != NULL);
return(initialized);
}
boolean printTabbedBed(struct bed *bedList, struct slName *chosenFields,
boolean initialized)
/* Print out the chosen fields of a bedList. */
{
struct bed *bed;
struct slName *field;
if (bedList == NULL)
return(initialized);
if (! initialized)
{
boolean gotFirst = FALSE;
initialized = TRUE;
printf("#");
for (field=chosenFields; field != NULL; field=field->next)
{
if (! gotFirst)
gotFirst = TRUE;
else
printf("\t");
printf("%s", field->name);
}
printf("\n");
}
for (bed=bedList; bed != NULL; bed=bed->next)
{
boolean gotFirst = FALSE;
for (field=chosenFields; field != NULL; field=field->next)
{
if (! gotFirst)
gotFirst = TRUE;
else
printf("\t");
if (sameString(field->name, "chrom"))
printf("%s", bed->chrom);
else if (sameString(field->name, "chromStart"))
printf("%d", bed->chromStart);
else if (sameString(field->name, "chromEnd"))
printf("%d", bed->chromEnd);
else if (sameString(field->name, "name"))
printf("%s", bed->name);
else if (sameString(field->name, "score"))
printf("%d", bed->score);
else if (sameString(field->name, "strand"))
printf("%s", bed->strand);
else if (sameString(field->name, "thickStart"))
printf("%d", bed->thickStart);
else if (sameString(field->name, "thickEnd"))
printf("%d", bed->thickEnd);
else if (sameString(field->name, "itemRgb"))
printf("%d", bed->itemRgb);
else if (sameString(field->name, "blockCount"))
printf("%d", bed->blockCount);
else if (sameString(field->name, "blockSizes"))
{
int i;
for (i=0; i < bed->blockCount; i++)
printf("%d,", bed->blockSizes[i]);
}
else if (sameString(field->name, "chromStarts"))
{
int i;
for (i=0; i < bed->blockCount; i++)
printf("%d,", bed->chromStarts[i]);
}
else
errAbort("\nUnrecognized bed field name \"%s\"", field->name);
}
printf("\n");
}
return(initialized);
}
void showTdbInfo(struct trackDb *tdb)
/* Show interesting stuff from tdb. */
{
if (tdb != NULL)
{
puts("");
printf("
Track information for %s (%s):
\n",
tdb->shortLabel, tdb->longLabel);
printf("Track type: %s \n", tdb->type);
if (tdb->settings != NULL && tdb->settings[0] != 0)
printf("Additional settings: %s \n", tdb->settings);
printf("Track group: %s \n", tdb->grp);
if (tdb->url != NULL && tdb->url[0] != 0)
printf("Feature URL: %s \n", tdb->url);
if (tdb->html != NULL && tdb->html[0] != 0)
puts(tdb->html);
}
}
void describeCustomTrack (char *ctName)
/* Describe the contents of a custom track as well as we can. */
{
char *db = getTableDb();
char *table = getTableName();
struct hTableInfo *hti = getHti(db, table);
struct customTrack *ct = lookupCt(table);
struct bed *firstFewBed = NULL, *bed = NULL;
int i;
printf("
Custom track %s is stored locally as a bed %d file.\n",
table, ct->fieldCount);
puts("(Note: custom tracks may initially be loaded into the browser \n"
"from files in other formats such as PSL or GFF, \n"
"but upon loading they are translated to BED for local processing \n"
"and temporary storage.)");
printf("
Custom track %s has %d rows total. \n",
hti->rootName, slCount(ct->bedList));
if (ct->bedList != 0)
{
firstFewBed = NULL;
for (i=0,bed=ct->bedList; i < 3 && bed != NULL; i++,bed=bed->next)
{
struct bed *newBed = cloneBed(bed);
slAddHead(&firstFewBed, newBed);
}
slReverse(&firstFewBed);
printf ("Example rows of custom track %s (not necessarily from current position!): \n",
hti->rootName);
puts("
");
printf("",
asi->gbdAnchor);
printf("Genome Browser Database Description for %s", table);
}
}
sqlFreeResult(&sr);
}
return(gotInfo);
}
void showItemCountFirstFew(struct sqlConnection *conn, int n)
/* Show the item count and first n items of table. */
{
char *track = getTrackName();
struct slName *tableList = hSplitTableNames(track);
struct slName *tPtr = NULL;
struct sqlResult *sr = NULL;
struct dyString *query = newDyString(256);
char **row = NULL;
char *table = getTableName();
int count = 0;
int numberColumns = 0;
int i = 0;
#define NEEDED_UNTIL_GB_CDNA_INFO_CHANGE
#ifdef NEEDED_UNTIL_GB_CDNA_INFO_CHANGE
if (sameString(table, "mrna"))
{
struct slName *slNew = newSlName(table);
slFreeList(&tableList);
tableList = slNew;
}
#endif /* NEEDED_UNTIL_GB_CDNA_INFO_CHANGE */
for (tPtr=tableList; tPtr != NULL; tPtr=tPtr->next)
{
count += sqlTableSize(conn, tPtr->name);
}
printf("
Table %s has %d rows total. \n", table, count);
if (count > 0)
{
dyStringPrintf(query, "select * from %s limit %d", tableList->name, n);
sr = sqlGetResult(conn, query->string);
printf ("Example rows of table %s (not necessarily from current position!): \n",
table);
puts("
");
numberColumns = sqlCountColumns(sr);
printf("#");
for (i = 0; i < numberColumns; i++)
{
printf("%s\t", sqlFieldName(sr));
}
printf("\n");
while ((row = sqlNextRow(sr)) != NULL)
{
for (i = 0; i < numberColumns; i++)
printf("%s\t", row[i]);
printf("\n");
}
puts("
");
}
}
void descTable(boolean histButtons)
/* Print out an HTML table showing table fields and types, and optionally
* offering histograms for the text/enum fields. */
{
char *db = getTableDb();
struct sqlConnection *conn = hAllocOrConnect(db);
struct sqlResult *sr;
char **row;
boolean tooBig = (sqlTableSize(conn, fullTableName) > TOO_BIG_FOR_HISTO);
char button[64];
char query[256];
safef(query, sizeof(query), "desc %s", fullTableName);
sr = sqlGetResult(conn, query);
// For some reason BORDER=1 does not work in our web.c nested table scheme.
// So use web.c's trick of using an enclosing table to provide a border.
puts("" "\n"
"
");
puts("
");
printf("
name
SQL type
");
histButtons = (histButtons && ! tooBig);
if (histButtons)
printf("
text value histogram
");
puts("
");
while ((row = sqlNextRow(sr)) != NULL)
{
printf("
Primary table: %s\n", table);
if (constraints != NULL)
printf("
Constraints on %s: %s \n", table, constraints);
else
printf("
No additional constraints selected on fields of %s.\n", table);
if (typeWiggle)
{
if (wigConstraint[0])
{
if (sameWord(wigConstraint[0],"in range"))
printf("
data value constraint: %s [%g , %g]\n",
wigConstraint[0], wigDataConstraint[0][0],
wigDataConstraint[0][1]);
else
printf("
data value constraint: %s %g\n",
wigConstraint[0], wigDataConstraint[0][0]);
}
}
if (table2 != NULL)
{
char tableUse[128], table2Use[128];
printf("
Secondary table: %s\n", table2);
if (constraints2 != NULL)
printf("
Constraints on %s: %s\n", table2, constraints2);
else
printf("
No additional constraints selected on fields of %s.\n",
table2);
if (cgiBoolean("tbInvertTable"))
snprintf(tableUse, sizeof(tableUse), "complement of %s", table);
else
strncpy(tableUse, table, sizeof(tableUse));
if (cgiBoolean("tbInvertTable2"))
snprintf(table2Use, sizeof(table2Use), "complement of %s", table2);
else
strncpy(table2Use, table2, sizeof(table2Use));
puts("
Means of combining tables:");
if (sameString(op, "any"))
printf("Include %s records that have any overlap with %s
\n",
table, table2);
else if (sameString(op, "none"))
printf("Include %s records that have no overlap with %s
\n",
table, table2);
else if (sameString(op, "more"))
printf("Include %s records that have at least %s%% overlap with %s
\n",
table, cgiString("tbMoreThresh"), table2);
else if (sameString(op, "less"))
printf("Include %s records that have at most %s%% overlap with %s
\n",
table, cgiString("tbLessThresh"), table2);
else if (sameString(op, "and"))
printf("List positions of base pairs covered by both %s and %s
\n",
tableUse, table2Use);
else if (sameString(op, "or"))
printf("List positions of base pairs covered by either %s or %s
\n",
tableUse, table2Use);
else
errAbort("Unrecognized table combination type.");
if (typeWiggle2)
{
if (wigConstraint[1])
{
if (sameWord(wigConstraint[1],"in range"))
printf("
data value constraint: %s [%g , %g]\n",
wigConstraint[1], wigDataConstraint[1][0],
wigDataConstraint[1][1]);
else
printf("
data value constraint: %s %g\n",
wigConstraint[1], wigDataConstraint[1][0]);
}
}
}
/* Print out a big table of stats... only when not just one wiggle */
if (!(typeWiggle && (table2 == NULL)))
{
puts("
");
numCols = (numChroms > 1) ? numChroms+1 : 1;
for (chromPtr=chromList,i=1; chromPtr != NULL; chromPtr=chromPtr->next,i++)
{
struct bed *bedListConstr, *bed;
int count;
getFullTableName(fullTableName, chromPtr->name, table);
bedListConstr = getBedList(FALSE, chrom);
count = slCount(bedListConstr);
itemCounts[0] += count;
itemCounts[i] = count;
if (count > 0)
{
struct featureBits *fbList =
fbFromBed("out", hti, bedListConstr, winStart, winEnd,
FALSE, FALSE);
int chromSize = hChromSize(chrom);
Bits *bits = bitAlloc(chromSize+8);
fbOrBits(bits, chromSize, fbList, 0);
count = bitCountRange(bits, 0, chromSize);
bitFree(&bits);
}
else
count = 0;
bitCounts[0] += count;
bitCounts[i] = count;
if ((constraints != NULL)||((table2 != NULL)&&(constraints2 != NULL)))
{
struct bed *bedListUnc = getBedList(TRUE, chrom);
count = slCount(bedListUnc);
itemUncCounts[0] += count;
itemUncCounts[i] = count;
}
if (itemCounts[i] > 0)
{
chromLengthArrs[i] = needMem(itemCounts[i] * sizeof(int));
for (bed=bedListConstr, j=0; bed != NULL; bed=bed->next, j++)
chromLengthArrs[i][j] = bed->chromEnd - bed->chromStart;
intStatsFromArr(chromLengthArrs[i], itemCounts[i],
&(chromLengthStats[i]));
if (hti->scoreField[0] != 0)
{
scoreArrs[i] = needMem(itemCounts[i] * sizeof(int));
for (bed=bedListConstr, j=0; bed != NULL; bed=bed->next, j++)
scoreArrs[i][j] = bed->score;
intStatsFromArr(scoreArrs[i], itemCounts[i], &(scoreStats[i]));
}
if (hti->strandField[0] != 0)
{
for (bed=bedListConstr; bed != NULL; bed=bed->next)
{
if (bed->strand[0] == '+')
{
strandPCounts[0]++;
strandPCounts[i]++;
}
else if (bed->strand[0] == '-')
{
strandMCounts[0]++;
strandMCounts[i]++;
}
else
{
strandQCounts[0]++;
strandQCounts[i]++;
}
}
}
if (hti->hasCDS)
{
utr5Arrs[i] = needMem(itemCounts[i] * sizeof(int));
cdsArrs[i] = needMem(itemCounts[i] * sizeof(int));
utr3Arrs[i] = needMem(itemCounts[i] * sizeof(int));
getBedBaseCounts(bedListConstr, hti->hasBlocks,
utr5Arrs[i], cdsArrs[i], utr3Arrs[i]);
intStatsFromArr(utr5Arrs[i], itemCounts[i], &(utr5Stats[i]));
intStatsFromArr(cdsArrs[i], itemCounts[i], &(cdsStats[i]));
intStatsFromArr(utr3Arrs[i], itemCounts[i], &(utr3Stats[i]));
}
if (hti->hasBlocks)
{
blockCountArrs[i] = needMem(itemCounts[i] * sizeof(int));
blockSizeArrs[i] = needMem(itemCounts[i] * sizeof(int));
for (bed=bedListConstr, j=0; bed != NULL; bed=bed->next, j++)
{
int k, totalSize = 0;
if (bed->blockCount < 1)
errAbort("Illegal bed: appears to have blocks, but blockCount is %d (%s\t%d\t%d\t%s...)",
bed->blockCount, bed->chrom, bed->chromStart,
bed->chromEnd, bed->name);
blockCountArrs[i][j] = bed->blockCount;
for (k=0; k < bed->blockCount; k++)
totalSize += bed->blockSizes[k];
// we lose some granularity here, but not too much...
blockSizeArrs[i][j] = round(totalSize / bed->blockCount);
}
intStatsFromArr(blockCountArrs[i], itemCounts[i],
&(blockCountStats[i]));
intStatsFromArr(blockSizeArrs[i], itemCounts[i],
&(blockSizeStats[i]));
}
}
}
getCumulativeStats(chromLengthArrs, itemCounts, numChroms,
chromLengthStats);
if (hti->scoreField[0] != 0)
getCumulativeStats(scoreArrs, itemCounts, numChroms, scoreStats);
if (hti->hasCDS)
{
getCumulativeStats(utr5Arrs, itemCounts, numChroms, utr5Stats);
getCumulativeStats(cdsArrs, itemCounts, numChroms, cdsStats);
getCumulativeStats(utr3Arrs, itemCounts, numChroms, utr3Stats);
}
if (hti->hasBlocks)
{
getCumulativeStats(blockCountArrs,itemCounts,numChroms,blockCountStats);
getCumulativeStats(blockSizeArrs,itemCounts,numChroms,blockSizeStats);
}
}
if (typeWiggle)
{
wigDoStats(database, table, chromList, WIG_TABLE_1, constraints);
wiggleDone = TRUE;
}
if (typeWiggle2)
{
wigDoStats(db2, table2, chromList, WIG_TABLE_2, constraints2);
}
if (! wiggleDone)
{
// For some reason BORDER=1 does not work in our web.c nested table scheme.
// So use web.c's trick of using an enclosing table to provide a border.
puts("" "\n"
"
");
puts("
");
/* Use fixed-font for decimal point/integer alignment. */
/* All these non-blocking spaces are to widen the first column so that some
* row descriptions below do not get wrapped (which would mess up the
* formatting of row contents). */
puts("
statistic
");
/* These non-blocking spaces are for decimal point/integer alignment: */
puts("
total
");
if (numCols > 1)
for (chromPtr=chromList; chromPtr != NULL; chromPtr=chromPtr->next)
printf("