44ccfacbe3a3d4b300f80d48651c77837a4b571e
galt
  Tue Apr 26 11:12:02 2022 -0700
SQL INJECTION Prevention Version 2 - this improves our methods by making subclauses of SQL that get passed around be both easy and correct to use. The way that was achieved was by getting rid of the obscure and not well used functions sqlSafefFrag and sqlDyStringPrintfFrag and replacing them with the plain versions of those functions, since these are not needed anymore. The new version checks for NOSQLINJ in unquoted %-s which is used to include SQL clauses, and will give an error the NOSQLINJ clause is not present, and this will automatically require the correct behavior by developers. sqlDyStringPrint is a very useful function, however because it was not enforced, users could use various other dyString functions and they operated without any awareness or checking for SQL correct use. Now those dyString functions are prohibited and it will produce an error if you try to use a dyString function on a SQL string, which is simply detected by the presence of the NOSQLINJ prefix.

diff --git src/hg/hgTracks/sampleTracks.c src/hg/hgTracks/sampleTracks.c
index 7dd585c..c5aeb34 100644
--- src/hg/hgTracks/sampleTracks.c
+++ src/hg/hgTracks/sampleTracks.c
@@ -1,932 +1,929 @@
 /* sampleTracks - Ryan Weber's wiggle tracks. */
 
 /* Copyright (C) 2013 The Regents of the University of California 
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #include "common.h"
 #include "hash.h"
 #include "linefile.h"
 #include "jksql.h"
 #include "hdb.h"
 #include "hgTracks.h"
 #include "sample.h"
 
 int sampleUpdateY( char *name, char *nextName, int lineHeight )
 /* only increment height when name root (minus the period if
  * there is one) is different from previous one.
   *This assumes that the entries are sorted by name as they would
   *be if loaded by hgLoadSample*/
 {
 int ret;
 char *tempstr = NULL;
 char *tempstr2 = NULL;
 
 tempstr = cloneString( name );
 if( strstr( name, "." ) != NULL )
    strtok( tempstr, "." );
 
 tempstr2 = cloneString( nextName );
 if( strstr( nextName, "." ) != NULL )
    strtok( tempstr2, "." );
 
 if( !sameString( tempstr, tempstr2 )) 
    ret = lineHeight; 
 else 
    ret = 0;
 
 freeMem(tempstr);
 freeMem(tempstr2);
 
 return(ret);
 }
 
 void samplePrintYAxisLabel( struct hvGfx *hvg, int y, struct track *track, char *labelString,
         double min0, double max0 )
 /*print a label for a horizontal y-axis line*/
 {
 double tmp;
 int fontHeight = tl.fontHeight;
 double ymin = y - (track->heightPer / 2) + fontHeight;
 int itemHeight0 = track->itemHeight(track, track->items);
 int inWid = insideX-gfxBorder*3;
 
 tmp = -whichSampleBin( atof(labelString), min0, max0, 1000 );
 tmp = (int)((double)ymin+((double)tmp)*(double)track->heightPer/1000.0+(double)track->heightPer)-fontHeight/2.0;
 if( !withCenterLabels ) tmp -= fontHeight;
 hvGfxTextRight(hvg, gfxBorder, tmp, inWid-1, itemHeight0, track->ixColor, tl.font, labelString );
 }
 
 
 static int sampleTotalHeight(struct track *tg, 
 	enum trackVisibility vis)
 /* Wiggle track will use this to figure out the height they use
 as defined in the cart */
 {
 struct slList *item;
 int lines = 0;
 
 int heightFromCart;
 char o1[128];
 
 safef( o1, 128, "%s.heightPer", tg->track);
 
 heightFromCart = atoi(cartUsualString(cart, o1, "50"));
 
 tg->lineHeight = max(tl.fontHeight+1, heightFromCart);
 tg->heightPer = tg->lineHeight - 1;
 switch (vis)
     {
     case tvFull:
 	lines = 1;
 	for (item = tg->items; item != NULL; item = item->next)
 	    {
 	    if( item->next != NULL )
 		if( sampleUpdateY( tg->itemName(tg, item), tg->itemName(tg, item->next), 1 ))
 		    lines++;
 	    }
 	tg->height = lines * tg->lineHeight;
 	break;
     case tvPack:
     case tvSquish:
         errAbort("Sorry can't handle pack in sampleTotalHeight (%s)", tg->track);
 	break;
     case tvDense:
 	tg->height = tg->lineHeight;
 	break;
     default:
         break;
     }
 return tg->height;
 }
 
 int consTotalHeight(struct track *tg, enum trackVisibility vis)
 /* Human/mouse conservation total height. */
 {
 if( vis == tvDense )
     return tg->height = tg->lineHeight = 10;
 else
     return sampleTotalHeight(tg, vis); 
 }
 
 struct linkedFeatures *lfFromSample(struct sample *sample)
 /* Return a linked feature from a full sample (wiggle) track. */
 {
 struct linkedFeatures *lf;
 struct simpleFeature *sf, *sfList = NULL;
 int grayIx = grayInRange(sample->score, 0, 1000);
 int start, i;
 unsigned *X = sample->samplePosition;
 int *Y = sample->sampleHeight;
 unsigned sampleCount = sample->sampleCount;
 
 assert(X != NULL && Y != NULL && sampleCount > 0);
 AllocVar(lf);
 lf->grayIx = grayIx;
 lf->name = cloneString(sample->name);
 lf->orientation = orientFromChar(sample->strand[0]);
 
 for (i=0; i<sampleCount; ++i)
     {
     AllocVar(sf);
     start = X[i] + sample->chromStart;
     sf->start = start;
 
     if( Y[i] < 0 )      /*hack for negative values not loading correctly*/
         sf->end = start;
     else if( Y[i] == 0 )
         sf->end = start + 1;
     else
         sf->end = start + Y[i];
 
     sf->grayIx = grayIx;
     slAddHead(&sfList, sf);
     }
 slReverse(&sfList);
 lf->components = sfList;
 linkedFeaturesBoundsAndGrays(lf);
 lf->start = sample->chromStart;
 lf->end = sample->chromEnd;
 return lf;
 }
 
 
 int whichSampleBin( double num, double thisMin, double thisMax, 
 	double binCount )
 /* Get bin value from num. */
 {
 return (num - thisMin) * binCount / (thisMax - thisMin);
 }
 
 double whichSampleNum( double bin, double thisMin, double thisMax, 
 	double binCount )
 /* gets range nums. from bin values*/
 {
 return( (thisMax - thisMin) / binCount * bin + thisMin );
 }
 
 
 int basePositionToXAxis( int base, int seqStart, int seqEnd, int
                 width, int xOff  ) 
 {
 double scale = scaleForPixels(width);
 double x1 = round((double)((int)base-seqStart)*scale) + xOff; 
 return(x1);
 }
 
 int humMusZoomLevel( void )
 {
 int zoom1 = 80000, zoom2 = 5000, zoom3 = 300; /* bp per data point */      
 int pixPerBase = (winEnd - winStart)/ tl.picWidth;
 if(pixPerBase >= zoom1)
     return(1);
 else if( pixPerBase >= zoom2 ) 
     return(2);
 else if(pixPerBase >= zoom3)
     return(3);
 else    
     return(0);
 }
 
 
 static void drawWiggleHorizontalLine( struct hvGfx *hvg, 
 	double where, double min0, double max0, 
 	int binCount, int y, double hFactor, int heightPer, 
 	Color lineColor )
 /* Draws a blue horizontal line on a wiggle track at a specified
  * location based on the range and number of bins*/
 {
 int bin;
 double y1;
 bin = -whichSampleBin( where, min0, max0, binCount);
 y1 = (int)((double)y+((double)bin)*hFactor+(double)heightPer);
 hvGfxBox(hvg, 0, y1, hvg->width, 1, lineColor);
 }
 
 static void wiggleLinkedFeaturesDraw(struct track *tg, 
     int seqStart, int seqEnd,
     struct hvGfx *hvg, int xOff, int yOff, int width, 
     MgFont *font, Color color, enum trackVisibility vis)
 /* Currently this routine is adapted from Terry's 
  * linkedFeatureSeriesDraw() routine.
  * It is called for 'sample' tracks as specified in the trackDb.ra.
  * and it looks at the cart to decide whether to interpolate, fill blocks,
  * and use anti-aliasing.*/
 {
 int i;
 struct linkedFeatures *lf;
 struct simpleFeature *sf;
 int y = yOff;
 int heightPer = tg->heightPer;
 int lineHeight = tg->lineHeight;
 int x1,x2;
 boolean isFull = (vis == tvFull);
 Color bColor = tg->ixAltColor;
 double scale = scaleForPixels(width);
 int prevX = -1;
 int gapPrevX = -1;
 double prevY = -1;
 double y1 = -1, y2;
 int ybase;
 int sampleX, sampleY; /* A sample in sample coordinates. 
                        * Sample X coordinate is chromosome coordinate.
 		       * Sample Y coordinate is usually 0-1000 */
 int binCount = 1.0/tg->scaleRange;   /* Maximum sample Y coordinate. */
 int bin;	      /* Sample Y coordinates are first converted to
                        * bin coordinates, and then to pixels.  I'm not
 		       * totally sure why.  */
 
 
 
 int currentX, currentXEnd, currentWidth;
 
 int leftSide, rightSide;
 
 int noZoom = 1;
 enum wiggleOptEnum wiggleType;
 char *interpolate = NULL;
 int fill; 
 int lineGapSize;
 double min0, max0;
 
 char o1[128]; /* Option 1 - linear interp */
 char o2[128]; /* Option 2 - anti alias */
 char o3[128]; /* Option 3 - fill */
 char o4[128]; /* Option 4 - minimum vertical range cutoff of plot */	
 char o5[128]; /* Option 5 - maximum vertical range cutoff of plot */
 char o6[128]; /* Option 6 - max gap where interpolation is still done */
 char cartStr[64];
 char *fillStr;
 
 double hFactor;
 double minRange, maxRange;
 double minRangeCutoff, maxRangeCutoff;
 
 
 Color gridColor = hvGfxFindRgb(hvg, &guidelineColor); /* for horizontal lines*/
 
 lf=tg->items;    
 if(lf==NULL) return;
 
 //take care of cart options
 safef( o1, 128,"%s.linear.interp", tg->track);
 safef( o2, 128, "%s.anti.alias", tg->track);
 safef( o3, 128,"%s.fill", tg->track);
 safef( o4, 128,"%s.min.cutoff", tg->track);
 safef( o5, 128,"%s.max.cutoff", tg->track);
 safef( o6, 128,"%s.interp.gap", tg->track);
 
 interpolate = cartUsualString(cart, o1, "Linear Interpolation");
 wiggleType = wiggleStringToEnum(interpolate);
 
 //don't fill gcPercent track by default (but fill others)
 if(sameString( tg->table, "pGC") && sameString(database,"zooHuman3"))
 {
     fillStr = cartUsualString(cart, o3, "0");
 }
 else
 {
     fillStr = cartUsualString(cart, o3, "1");
 }
 fill = atoi(fillStr);
 cartSetString(cart, o3, fillStr );
 
 //the 0.1 is so the label doesn't get truncated with integer valued user input min
 //display range.
 minRangeCutoff = max( atof(cartUsualString(cart,o4,"0.0"))-0.1, tg->minRange );
 maxRangeCutoff = min( atof(cartUsualString(cart,o5,"1000.0"))+0.1, tg->maxRange);
 
 lineGapSize = atoi(cartUsualString(cart, o6, "200"));
 
 //update cart settings to reflect truncated range cutoff values
 cartSetString( cart, "win", "F" );
 safef( cartStr, 64, "%g", minRangeCutoff );
 cartSetString( cart, o4, cartStr );
 safef( cartStr, 64, "%g", maxRangeCutoff );
 cartSetString( cart, o5, cartStr );
 
 heightPer = tg->heightPer+1;
 hFactor = (double)heightPer*tg->scaleRange;
 
 //errAbort( "min=%g, max=%g\n", minRangeCutoff, maxRangeCutoff );
 
 
 if( sameString( tg->table, "zoo" ) || sameString( tg->table, "zooNew" ) )
     binCount = binCount - 100;    //save some space at top, between each zoo species
 
 minRange = whichSampleBin( minRangeCutoff, tg->minRange, tg->maxRange, binCount );
 maxRange = whichSampleBin( maxRangeCutoff, tg->minRange, tg->maxRange, binCount );
 
 //errAbort( "(%g,%g) cutoff=(%g,%g)\n", tg->minRange, tg->maxRange, minRangeCutoff, maxRangeCutoff );
 
 
 if( sameString( tg->table, "zoo" ) || sameString( tg->table, "zooNew" ) )
     {
     /*Always interpolate zoo track (since gaps are explicitly defined*/
     lineGapSize = -1;
     }
 else if( tg->minRange == 0 && tg->maxRange == 8 )    //range for all L-score tracks
     {
     if( isFull )
     {
         min0 = whichSampleNum( minRange, tg->minRange, tg->maxRange, binCount );
         max0 = whichSampleNum( maxRange, tg->minRange, tg->maxRange,  binCount );
         for( i=1; i<=6; i++ )
             drawWiggleHorizontalLine(hvg, (double)i, min0, max0,
 	            binCount, y, hFactor, heightPer, gridColor );
         }
     }
 
 for(lf = tg->items; lf != NULL; lf = lf->next) 
     {
     gapPrevX = -1;
     prevX = -1;
     ybase = (int)((double)y+hFactor+(double)heightPer);
 
 
     for (sf = lf->components; sf != NULL; sf = sf->next)
 	{
 	sampleX = sf->start;
 	sampleY = sf->end - sampleX;	// Stange encoding but so it is. 
 					// It is to deal with the fact that
 					// for a BED: sf->end = sf->start + length
 					// but in our case length = height (or y-value)
 					// so to recover height we take
 					// height = sf->end - sf->start.
 					// Otherwise another sf variable would 
 					// be needed.
  
 
 	/*mapping or sequencing gap*/
 	if (sampleY == 0)
 	    {
 	    bin = -whichSampleBin( (int)((maxRange - minRange)/5.0+minRange), 
 	    	minRange, maxRange, binCount );
 	    y1 = (int)((double)y+((double)bin)* hFactor+(double)heightPer);
 	    if( gapPrevX >= 0 )
 		drawScaledBox(hvg, sampleX, gapPrevX, scale, 
 			xOff, (int)y1, (int)(.10*heightPer), shadesOfGray[2]);
 	    gapPrevX = sampleX;
 	    prevX = -1; /*connect next point with gray bar too*/
 	    continue;
 	    }
 	if (sampleY > maxRange)
 	    sampleY = maxRange;
 	if (sampleY < minRange)
 	    sampleY = minRange;
 	bin = -whichSampleBin( sampleY, minRange, maxRange, binCount );
 
 
 	x1 = round((double)(sampleX-winStart)*scale) + xOff;
 	y1 = (int)((double)y+((double)bin)* hFactor+(double)heightPer);
 
 	
 
 	if (prevX > 0)
 	    {
 	    y2 = prevY;
 	    x2 = round((double)(prevX-winStart)*scale) + xOff;
 	    if( wiggleType == wiggleLinearInterpolation ) 
 	    /*connect samples*/
 		{
 		if( lineGapSize < 0 || prevX - sampleX <= lineGapSize )   
 		    /*don't interpolate over large gaps*/
 		    {
 		    if (fill)
 			hvGfxFillUnder(hvg, x1,y1, x2,y2, ybase, bColor);
 		    else
 			hvGfxLine(hvg, x1,y1, x2,y2, color);
 		    }
 		}
 	    }
 
 	//if( x1 < 0 || x1 > tl.picWidth )
 	//printf("x1 = %d, sampleX=%d, winStart = %d\n<br>", x1, sampleX, winStart );
 	if( x1 >= 0 && x1 <= tl.picWidth )
 	{
 	/* Draw the points themselves*/
 	drawScaledBox(hvg, sampleX, sampleX+1, scale, xOff, (int)y1-1, 3, color);
 	if( fill )
 		drawScaledBox(hvg, sampleX, sampleX+1, scale, xOff, (int)y1+2, 
 			      ybase-y1-2, bColor);
 	}
 
 	prevX = gapPrevX = sampleX;
 	prevY = y1;
 	}
 
     leftSide = max( tg->itemStart(tg,lf), winStart );
     rightSide = min(  tg->itemEnd(tg,lf), winEnd );
 
     currentX =  round((double)((int)leftSide-winStart)*scale) + xOff;
     currentXEnd =  round((double)((int)rightSide-winStart)*scale) + xOff;
     currentWidth = currentXEnd - currentX;
 
     if( noZoom && isFull )
 	{
 	mapBoxHc(hvg, lf->start, lf->end, currentX ,y, currentWidth,
 	    heightPer, tg->track, tg->mapItemName(tg, lf), tg->itemName(tg, lf));
 
 	if( lf->next != NULL )
 	    y += sampleUpdateY( lf->name, lf->next->name, lineHeight );
 	else
 	    y += lineHeight;
 	}
 
     }
 }
 
 struct track *sortGroupList = NULL; /* Used temporarily for sample sorting. */
 
 int lfNamePositionCmp(const void *va, const void *vb)
 /* Compare based on name, then chromStart, used for
    sorting sample based tracks. */
 {
 const struct linkedFeatures *a = *((struct linkedFeatures **)va);
 const struct linkedFeatures *b = *((struct linkedFeatures **)vb);
 int dif;
 char *tgName = NULL;
 if(sortGroupList != NULL)
     tgName = sortGroupList->shortLabel;
 if(tgName != NULL)
     {
     if(sameString(a->name, tgName) && differentString(b->name, tgName))
 	return -1;
     if(sameString(b->name, tgName) && differentString(a->name, tgName))
 	return 1;
     }
 dif = strcmp(a->name, b->name);
 if (dif == 0)
     dif = a->start - b->start;
 return dif;
 }
 
 
 void loadSampleIntoLinkedFeature(struct track *tg)
 /* Convert sample info in window to linked feature. */
 {
 int maxWiggleTrackHeight = 2500;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 int rowOffset;
 struct sample *sample;
 struct linkedFeatures *lfList = NULL, *lf;
 char *hasDense = NULL;
 char *where = NULL;
 char query[256];
 
 /*see if we have a summary table*/
 sqlSafef(query, sizeof(query), 
 	"select name from %s where name = '%s' limit 1", 
 	tg->table, tg->shortLabel);
 //errAbort( "%s", query );
 hasDense = sqlQuickQuery(conn, query, query, sizeof(query));
 
 /* If we're in dense mode and have a summary table load it. */
 if(tg->visibility == tvDense)
     {
     if(hasDense != NULL)
 	{
-	sqlSafefFrag(query, sizeof(query), " name = '%s' ", tg->shortLabel);
+	sqlSafef(query, sizeof(query), " name = '%s' ", tg->shortLabel);
 	where = cloneString(query);
 	}
     }
 
 sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd, where, &rowOffset);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     sample = sampleLoad(row + rowOffset);
     lf = lfFromSample(sample);
     slAddHead(&lfList, lf);
     sampleFree(&sample);
     }
 if(where != NULL)
     freez(&where);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 slReverse(&lfList);
 
 /* sort to bring items with common names to the same line
 but only for tracks with a summary table (with name=shortLabel) in
 dense mode*/
 
 if( hasDense != NULL )
     {
     sortGroupList = tg; /* used to put track name at top of sorted list. */
     slSort(&lfList, lfNamePositionCmp);
     sortGroupList = NULL;
     }
 tg->items = lfList;
 
 /*turn off full mode if there are too many rows or each row is too
  * large. A total of maxWiggleTrackHeight is allowed for number of
  * rows times the rowHeight*/
 if( tg->visibility == tvFull && sampleTotalHeight( tg, tvFull ) > maxWiggleTrackHeight  )
     {
     tg->limitedVisSet = TRUE;
     tg->limitedVis = tvDense;
     }
 }
 
 
 
 void sampleLinkedFeaturesMethods(struct track *tg)
 /* Fill in track methods for 'sample' tracks. */
 {
 linkedFeaturesMethods(tg);
 tg->drawItems = wiggleLinkedFeaturesDraw;
 tg->totalHeight = sampleTotalHeight;
 tg->itemHeight = tgFixedItemHeight;
 }
 
 
 void sampleMethods(struct track *track, struct trackDb *tdb, int wordCount, char *words[])
 /* Load up methods for a generic sample type track. */
 {
 track->scaleRange = 0.001;
 track->minRange = 0.0;
 track->maxRange = 1000.0;
 if (wordCount > 1)     /*sample minRange maxRange*/
     track->minRange = atof(words[1]);
 if (wordCount > 2)
     track->maxRange = atof(words[2]);
 if (wordCount > 3)
     track->scaleRange = atof(words[3]);
 track->bedSize = 9;
 track->subType = lfSubSample;     /*make subType be "sample" (=2)*/
 sampleLinkedFeaturesMethods(track);
 track->loadItems = loadSampleIntoLinkedFeature;
 }
 
 void loadHumMusL(struct track *tg)
 /* Load humMusL track with 2 zoom levels and one normal level. 
  * Also used for loading the musHumL track (called Human Cons) 
  * on the mouse browser. It decides which of 4 tables to
  * load based on how large of a window the user is looking at*/
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 int rowOffset;
 struct sample *sample;
 struct linkedFeatures *lfList = NULL, *lf;
 char *hasDense = NULL;
-char *where = NULL;
 char tableName[256];
 int z;
 
 if(tl.picWidth == 0)
     errAbort("hgTracks.c::loadHumMusL() - can't have pixel width of 0");
 
 /* Determine zoom level. */
  if (!strstr(tg->table,"HMRConservation"))
    z = humMusZoomLevel();
  else z=0;
 
 
 if(z == 1 )
     safef(tableName, sizeof(tableName), "%s_%s", "zoom1",
 	    tg->table);
 else if( z == 2)
     safef(tableName, sizeof(tableName), "%s_%s", "zoom50",
 	    tg->table);
 else if(z == 3)
     safef(tableName, sizeof(tableName), "%s_%s",
 	    "zoom2500", tg->table);
 else
     safef(tableName, sizeof(tableName), "%s", tg->table);
 
 //printf("(%s)", tableName );
 
 sr = hRangeQuery(conn, tableName, chromName, winStart, winEnd,
-    where, &rowOffset);
+    NULL, &rowOffset);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     sample = sampleLoad(row+rowOffset);
     lf = lfFromSample(sample);
     slAddHead(&lfList, lf);
     sampleFree(&sample);
     }
-if(where != NULL)
-    freez(&where);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 slReverse(&lfList);
 
 /* sort to bring items with common names to the same line
    but only for tracks with a summary table (with name=shortLabel)
    in
    dense mode*/
 if( hasDense != NULL )
     {
     sortGroupList = tg; /* used to put track name at
 	                 * top of sorted list. */
     slSort(&lfList, lfNamePositionCmp);
     sortGroupList = NULL;
     }
 tg->items = lfList;
 }
 
 void humMusLMethods( struct track *tg )
 /* Overide the humMusL load function to look for zoomed out tracks. */
 {
 tg->loadItems = loadHumMusL;
 tg->totalHeight = consTotalHeight;
 }
 
 struct hash *zooSpeciesHash = NULL;
 
 void zooSpeciesHashInit()
 /* Function to init list of zoo species */
 {
 char *name = NULL;
 char *val = NULL;
 
 if (zooSpeciesHash != NULL)
     return;
 zooSpeciesHash = hashNew(6);
 
 name = cloneString("Human");
 val = cloneString("1");
 hashAdd(zooSpeciesHash, name, val);
 cartSetBoolean( cart, "zooSpecies.Human", TRUE );
 
 name = cloneString("Chimpanzee");
 val = cloneString("2");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Baboon");
 val = cloneString("3");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Orangutan");
 val = cloneString("4");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Macaque");
 val = cloneString("5");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Vervet");
 val = cloneString("6");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Lemur");
 val = cloneString("7");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Cat");
 val = cloneString("8");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Dog");
 val = cloneString("9");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Cow");
 val = cloneString("10");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Pig");
 val = cloneString("11");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Horse");
 val = cloneString("12");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Rabbit");
 val = cloneString("13");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Hedgehog");
 val = cloneString("14");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("ajBat");
 val = cloneString("15");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("cpBat");
 val = cloneString("16");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Rat");
 val = cloneString("17");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Mouse");
 val = cloneString("18");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Platypus");
 val = cloneString("19");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Opossum");
 val = cloneString("20");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Dunnart");
 val = cloneString("21");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Chicken");
 val = cloneString("22");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Fugu");
 val = cloneString("23");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Tetraodon");
 val = cloneString("24");
 hashAdd(zooSpeciesHash, name, val);
 
 name = cloneString("Zebrafish");
 val = cloneString("25");
 hashAdd(zooSpeciesHash, name, val);
 }
 
 
 int lfZooCmp(const void *va, const void *vb)
 /* Compare based on name, then chromStart, used for
    sorting sample based tracks. */
 {
 struct linkedFeatures *a = *((struct linkedFeatures **)va);
 struct linkedFeatures *b = *((struct linkedFeatures **)vb);
 char *aVal =  hashFindVal(zooSpeciesHash, a->name);
 char *bVal =  hashFindVal(zooSpeciesHash, b->name);
 int aV = atoi(aVal);
 int bV = atoi(bVal);
 return aV - bV;
 }
 
 void loadSampleZoo(struct track *tg)
 /* Convert sample info in window to linked feature. */
 {
 int maxWiggleTrackHeight = 2500;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 int rowOffset;
 struct sample *sample;
 struct linkedFeatures *lfList = NULL, *lf;
 char *hasDense = NULL;
 char *where = NULL;
 char query[256];
 char option[64];
 
 zooSpeciesHashInit();
 
 /*see if we have a summary table*/
 sqlSafef(query, sizeof(query), "select name from %s where name = '%s' limit 1", tg->table, tg->shortLabel);
 //errAbort( "%s", query );
 hasDense = sqlQuickQuery(conn, query, query, sizeof(query));
 
 /* If we're in dense mode and have a summary table load it. */
 if(tg->visibility == tvDense)
     {
     if(hasDense != NULL)
 	{
-	sqlSafefFrag(query, sizeof(query), " name = '%s' ", tg->shortLabel);
+	sqlSafef(query, sizeof(query), " name = '%s' ", tg->shortLabel);
 	where = cloneString(query);
 	}
     }
 
 sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd, where, &rowOffset);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     sample = sampleLoad(row + rowOffset);
     lf = lfFromSample(sample);
     safef( option, sizeof(option), "zooSpecies.%s", sample->name );
     if( cartUsualBoolean(cart, option, TRUE ))
     slAddHead(&lfList, lf);
     sampleFree(&sample);
     }
 if(where != NULL)
     freez(&where);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 slReverse(&lfList);
 
 /* sort to bring items with common names to the same line
  * but only for tracks with a summary table 
  * (with name=shortLabel) in dense mode */
 if( hasDense != NULL )
     {
     sortGroupList = tg; /* used to put track name at top of sorted list. */
     slSort(&lfList, lfNamePositionCmp);
     sortGroupList = NULL;
     }
 
 /* Sort in species phylogenetic order */
 slSort(&lfList, lfZooCmp);
 
 
 tg->items = lfList;
 
 /*turn off full mode if there are too many rows or each row is too
  * large. A total of maxWiggleTrackHeight is allowed for number of
  * rows times the rowHeight*/
 if( tg->visibility == tvFull && sampleTotalHeight( tg, tvFull ) > maxWiggleTrackHeight  )
     {
     tg->limitedVisSet = TRUE;
     tg->limitedVis = tvDense;
     }
 }
 
 void zooMethods( struct track *tg )
 /* Overide the zoo sample type load function to look for zoomed out tracks. */
 {
 tg->loadItems = loadSampleZoo;
 }
 
 
 void loadAffyTranscriptome(struct track *tg)
 /* Convert sample info in window to linked feature. */
 {
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr;
 char **row;
 int rowOffset;
 struct sample *sample;
 struct linkedFeatures *lfList = NULL, *lf;
 char *hasDense = NULL;
 char *where = NULL;
 char query[256];
 char tableName[256];
 int zoom1 = 23924, zoom2 = 2991; /* bp per data point */
 float pixPerBase = 0;
 /* Set it up so we don't have linear interpretation.. */
 char *noLinearInterpString = wiggleEnumToString(wiggleNoInterpolation);
 cartSetString(cart, "affyTranscriptome.linear.interp", noLinearInterpString);
 
 if(tl.picWidth == 0)
     errAbort("hgTracks.c::loadAffyTranscriptome() - can't have pixel width of 0");
 pixPerBase = (winEnd - winStart)/ tl.picWidth;
 
 
 /* Determine zoom level. */
 if(pixPerBase >= zoom1)
     safef(tableName, sizeof(tableName), "%s_%s", "zoom1", tg->table);
 else if(pixPerBase >= zoom2)
     safef(tableName, sizeof(tableName), "%s_%s", "zoom2", tg->table);
 else 
     safef(tableName, sizeof(tableName), "%s", tg->table);
 
 /*see if we have a summary table*/
 if(hTableExists(database, tableName))
     sqlSafef(query, sizeof(query), "select name from %s where name = '%s' limit 1",  tableName, tg->shortLabel);
 else
     {
     warn("<p>Couldn't find table %s<br><br>", tableName);
     sqlSafef(query, sizeof(query), "select name from %s where name = '%s' limit 1",  tg->table, tg->shortLabel);
     safef(tableName, sizeof(tableName), "%s", tg->table);
     }
 
 hasDense = sqlQuickQuery(conn, query, query, sizeof(query));
 
 /* If we're in dense mode and have a summary table load it. */
 if(tg->visibility == tvDense)
     {
     if(hasDense != NULL)
 	{
-	sqlSafefFrag(query, sizeof(query), " name = '%s' ", tg->shortLabel);
+	sqlSafef(query, sizeof(query), " name = '%s' ", tg->shortLabel);
 	where = cloneString(query);
 	}
     }
 
 sr = hRangeQuery(conn, tableName, chromName, winStart, winEnd, where, &rowOffset);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     sample = sampleLoad(row+rowOffset);
     lf = lfFromSample(sample);
     slAddHead(&lfList, lf);
     sampleFree(&sample);
     }
 if(where != NULL)
     freez(&where);
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 slReverse(&lfList);
 
 /* sort to bring items with common names to the same line
 but only for tracks with a summary table (with name=shortLabel) in
 dense mode*/
 if( hasDense != NULL )
     {
     sortGroupList = tg; /* used to put track name at top of sorted list. */
     slSort(&lfList, lfNamePositionCmp);
     sortGroupList = NULL;
     }
 tg->items = lfList;
 }
 
 
 void affyTranscriptomeMethods(struct track *tg)
 /* Overide the load function to look for zoomed out tracks. */
 {
 tg->loadItems = loadAffyTranscriptome;
 }