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/rmskJoinedTrack.c src/hg/hgTracks/rmskJoinedTrack.c
index 9bde434..716a9fb 100644
--- src/hg/hgTracks/rmskJoinedTrack.c
+++ src/hg/hgTracks/rmskJoinedTrack.c
@@ -1,1313 +1,1313 @@
 /* joinedRmskTrack - A comprehensive RepeatMasker visualization track
  *                   handler. This is an extension of the original
  *                   rmskTrack.c written by UCSC.
  *
  *  Written by Robert Hubley 10/2012-01/2015
  *
  *  Modifications:
  *
  *  1/2015
  *     Request to revert back to only Full/Dense modes.
  *
  *  11/2014
  *     Request for traditional pack/squish modes.
  *
  *  7/2014
  *     With the help of Jim Kent we modified the
  *     glyph design to more closely resemble the
  *     existing browser styles.
  */
 
 /* Copyright (C) 2014 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 "rmskJoined.h"
 
 
 /* DETAIL_VIEW_MAX_SCALE: The size ( max bp ) at which the
  * detailed view of repeats is turned on, otherwise it reverts
  * back to the original UCSC glyph.
  */
 #define DETAIL_VIEW_MAX_SCALE 30000
 
 /* MAX_UNALIGNED_PIXEL_LEN: The maximum size of unaligned sequence
  * to draw before switching to the unscaled view: ie. "----/###/---"
  * This is measured in pixels *not* base pairs due to the
  * mixture of scaled ( lines ) and unscaled ( text ) glyph
  * components.
  */
 #define MAX_UNALIGNED_PIXEL_LEN 150
 
 // TODO: Document
 #define LABEL_PADDING 5
 #define MINHEIGHT 24
 
 // TODO: Document
 static float pixelsPerBase = 1.0;
 
 
 struct windowGlobals
 {
 struct windowGlobals *next;
 struct window *window;
 /* These are the static globals needed for each window: */
 struct hash *classHash;
 struct repeatItem *otherRepeatItem;
 struct hash *subTracksHash;
 };
 
 static struct windowGlobals *wgWindows = NULL;
 static struct windowGlobals *wgWindow = NULL;
 static struct windowGlobals *wgWindowOld = NULL;
 
 static bool setWgWindow()
 /* scan genoCache windows. create new one if not found */
 {
 if (wgWindow && wgWindow->window == currentWindow)
     return FALSE;
 wgWindowOld = wgWindow;
 for (wgWindow = wgWindows; wgWindow; wgWindow =  wgWindow->next)
     {
     if (wgWindow->window == currentWindow)
 	{
 	return TRUE;
 	}
     }
 AllocVar(wgWindow);
 wgWindow->window = currentWindow;
 slAddTail(&wgWindows, wgWindow);
 return TRUE;
 }
 
 // per window static vars act like local global vars, need one per window
 
 /* Hash of the repeatItems ( tg->items ) for this track.
  *   This is used to hold display names for the lines of
  *   the track, the line heights, and class colors.
  */
 struct hash *classHash = NULL;
 static struct repeatItem *otherRepeatItem = NULL;
 
 /* Hash of subtracks
  *   The joinedRmskTrack is designed to be used as a composite track.
  *   When rmskJoinedLoadItems is called this hash is populated with the
  *   results of one or more table queries
  */
 struct hash *subTracksHash = NULL;
 
 
 static void setWg()
 /* set up per-window globals for the current window */
 {
 if (setWgWindow())
     {
     if (wgWindowOld)
 	{
 	wgWindowOld->classHash       = classHash;
 	wgWindowOld->otherRepeatItem = otherRepeatItem;
     	wgWindowOld->subTracksHash   = subTracksHash;
 	}
     classHash       = wgWindow->classHash;
     otherRepeatItem = wgWindow->otherRepeatItem;
     subTracksHash   = wgWindow->subTracksHash;
     }
 
 }
 
 
 
 struct subTrack
 {
   // The number of display levels used in levels[]
 int levelCount;
   // The rmskJoined records from table query
 struct rmskJoined **levels;
 };
 
 
 // Repeat Classes: Display names
 static char *rptClassNames[] = {
 "SINE", "LINE", "LTR", "DNA", "Simple", "Low Complexity", "Satellite",
 "RNA", "Other", "Unknown",
 };
 
 // Repeat Classes: Data names
 static char *rptClasses[] = {
 "SINE", "LINE", "LTR", "DNA", "Simple_repeat", "Low_complexity",
 "Satellite", "RNA", "Other", "Unknown",
 };
 
 
 /* Repeat class to color mappings.
  *   - Currently borrowed from snakePalette.
  *
  *  NOTE: If these are changed, do not forget to update the
  *        help table in joinedRmskTrack.html
  */
 static Color rmskJoinedClassColors[] = {
 0xff1f77b4,			// SINE - red
 0xffff7f0e,			// LINE - lime
 0xff2ca02c,			// LTR - maroon
 0xffd62728,			// DNA - fuchsia
 0xff9467bd,			// Simple - yellow
 0xff8c564b,			// LowComplex - olive
 0xffe377c2,			// Satellite - blue
 0xff7f7f7f,			// RNA - green
 0xffbcbd22,			// Other - teal
 0xff17becf,			// Unknown - aqua
 };
 
 
 static void getExtents(struct rmskJoined *rm, int *pStart, int *pEnd)
 /* Calculate the glyph extents in genome coordinate
  * space ( ie. bp )
  *
  * It is not straightforward to determine the extent
  * of this track's glyph.  This is due to the mixture
  * of graphic and text elements within the glyph.
  *
  */
 {
 int start;
 int end;
 char lenLabel[20];
 
 // Start position is anchored by the alignment start
 // coordinates.  Then we subtract from that space for
 // the label.
 start = rm->alignStart -
     (int) ((mgFontStringWidth(tl.font, rm->name) +
 	    LABEL_PADDING) / pixelsPerBase);
 
 // Now subtract out space for the unaligned sequence
 // bar starting off the graphics portion of the glyph.
 if ((rm->blockSizes[0] * pixelsPerBase) > MAX_UNALIGNED_PIXEL_LEN)
     {
     // Unaligned sequence bar needs length label -- account for that.
     safef(lenLabel, sizeof(lenLabel), "%d", rm->blockSizes[0]);
     start -= (int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase) +
 	(int) ((mgFontStringWidth(tl.font, lenLabel) +
 		LABEL_PADDING) / pixelsPerBase);
     }
 else
     {
     start -= rm->blockSizes[0];
     }
 
 end = rm->alignEnd;
 
 if ((rm->blockSizes[rm->blockCount - 1] * pixelsPerBase) >
     MAX_UNALIGNED_PIXEL_LEN)
     {
     safef(lenLabel, sizeof(lenLabel), "%d",
 	   rm->blockSizes[rm->blockCount - 1]);
     end +=
 	(int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase) +
 	(int) ((mgFontStringWidth(tl.font, lenLabel) +
 		LABEL_PADDING) / pixelsPerBase);
     }
 else
     {
     end += rm->blockSizes[rm->blockCount - 1];
     }
 
 *pStart = start;
 *pEnd = end;
 }
 
 // A better way to organize the display
 static int cmpRepeatDiv(const void *va, const void *vb)
 /* Sort repeats by divergence. TODO: Another way to do this
    would be to sort by containment.  How many annotations
    are contained within the repeat.  This would require
    a "contained_count" be precalculated for each annotation.
  */
 {
 struct rmskJoined *a = *((struct rmskJoined **) va);
 struct rmskJoined *b = *((struct rmskJoined **) vb);
 
 return (b->score - a->score);
 }
 
 struct repeatItem *classRIList = NULL;
 struct repeatItem *fullRIList = NULL;
 
 static struct repeatItem * makeJRepeatItems()
 /* Initialize the track */
 {
 setWg();
 int i;
 struct repeatItem *ri = NULL;
 
 // Build a permanent hash for mapping
 // class names to colors.  Used by glyph
 // drawing routine.  Also build the
 // classRIList primarily for use in tvDense
 // mode.
 if (!classHash)
     {
     classHash = newHash(6);
     int numClasses = ArraySize(rptClasses);
     for (i = 0; i < numClasses; ++i)
         {
         AllocVar(ri);
         ri->class = rptClasses[i];
         ri->className = rptClassNames[i];
         // New color attribute
         ri->color = rmskJoinedClassColors[i];
         slAddHead(&classRIList, ri);
         // Hash now prebuilt to hold color attributes
         hashAdd(classHash, ri->class, ri);
         if (sameString(rptClassNames[i], "Other"))
             otherRepeatItem = ri;
         }
     slReverse(&classRIList);
     }
 return classRIList;
 }
 
 
 
 static void rmskJoinedLoadItems(struct track *tg)
 /* We do the query(ies) here so we can report how deep the track(s)
  * will be when rmskJoinedTotalHeight() is called later on.
  */
 {
   /*
    * Initialize the subtracks hash - This will eventually contain
    * all the repeat data for each displayed subtrack.
    */
 
 setWg();
 
 if (!subTracksHash)
     subTracksHash = newHash(20);
 
 //tg->items = makeJRepeatItems();
  makeJRepeatItems();
 
 int baseWidth = winEnd - winStart;
 pixelsPerBase = (float) insideWidth / (float) baseWidth;
 //Disabled
 //if ((tg->visibility == tvFull || tg->visibility == tvSquish ||
 //     tg->visibility == tvPack) && baseWidth <= DETAIL_VIEW_MAX_SCALE)
 if (tg->visibility == tvFull && baseWidth <= DETAIL_VIEW_MAX_SCALE)
     {
     struct repeatItem *ri = NULL;
     struct subTrack *st = NULL;
     AllocVar(st);
     st->levelCount = 0;
     struct rmskJoined *rm = NULL;
     char **row;
     int rowOffset;
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr = hRangeQuery(conn, tg->table, chromName,
                                         winStart, winEnd, NULL,
                                         &rowOffset);
     struct rmskJoined *detailList = NULL;
     while ((row = sqlNextRow(sr)) != NULL)
         {
         rm = rmskJoinedLoad(row + rowOffset);
         slAddHead(&detailList, rm);
         }
     slSort(&detailList, cmpRepeatDiv);
 
     sqlFreeResult(&sr);
     hFreeConn(&conn);
 
     if (detailList)
 	AllocArray( st->levels, slCount(detailList));
 
     while (detailList)
         {
         st->levels[st->levelCount++] = detailList;
 
         struct rmskJoined *cr = detailList;
         detailList = detailList->next;
         cr->next = NULL;
         int crChromStart, crChromEnd;
         int rmChromStart, rmChromEnd;
         struct rmskJoined *prev = NULL;
         rm = detailList;
 
         getExtents(cr, &crChromStart, &crChromEnd);
 
         if ( tg->visibility == tvFull )
 	    {
 	    AllocVar(ri);
 	    ri->className = cr->name;
 	    slAddHead(&fullRIList, ri);
 	    }
 
         //Disabled
         //if ( tg->visibility == tvSquish || tg->visibility == tvPack )
         if ( tg->visibility == tvFull )
              {
              while (rm)
                  {
                  getExtents(rm, &rmChromStart, &rmChromEnd);
 
                  if (rmChromStart > crChromEnd)
                      {
                      cr->next = rm;
                      cr = rm;
                      crChromEnd = rmChromEnd;
                      if (prev)
                          prev->next = rm->next;
                      else
                          detailList = rm->next;
 
                      rm = rm->next;
                      cr->next = NULL;
                      }
                  else
                      {
                      // Save for a lower level
                      prev = rm;
                      rm = rm->next;
                      }
                  } // while ( rm )
              } // else if ( tg->visibility == tvSquish ...
         } // while ( detailList )
     // Create Hash Entry
     hashReplace(subTracksHash, tg->table, st);
     slReverse(&fullRIList);
     tg->items = fullRIList;
     } // if ((tg->visibility == tvFull || ...
 else
    tg->items = classRIList;
 }
 
 static void rmskJoinedFreeItems(struct track *tg)
 /* Free up rmskJoinedMasker items. */
 {
 slFreeList(&tg->items);
 }
 
 static char * rmskJoinedName(struct track *tg, void *item)
 /* Return name of rmskJoined item track. */
 {
 struct repeatItem *ri = item;
   /*
    * In detail view mode the items represent different packing
    * levels.  No need to display a label at each level.  Instead
    * Just return a label for the first level.
    */
 //Disabled
 //if ((tg->visibility == tvSquish ||
 //     tg->visibility == tvPack) && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
 if (tg->visibility == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
     {
     if (strcmp(ri->className, "SINE") == 0)
 	return "Repeats";
     else
 	return "";
     }
 return ri->className;
 }
 
 
 int rmskJoinedItemHeight(struct track *tg, void *item)
 {
   // Are we in full view mode and at the scale needed to display
   // the detail view?
 //Disabled
 //if (tg->limitedVis == tvSquish && winBaseCount <= DETAIL_VIEW_MAX_SCALE )
 //    {
 //    if ( tg->heightPer < (MINHEIGHT/2) )
 //      return (MINHEIGHT/2);
 //    else
 //      return tg->heightPer;
 //    }
 //else if ((tg->limitedVis == tvFull || tg->limitedVis == tvPack) && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
 if (tg->limitedVis == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
     {
     return max(tg->heightPer, MINHEIGHT);
     }
 else
     {
       return tgFixedItemHeight(tg, item);
     }
 }
 
 int rmskJoinedTotalHeight(struct track *tg, enum trackVisibility vis)
 {
 setWg();
   // Are we in full view mode and at the scale needed to display
   // the detail view?
 //Disabled
 //if ((tg->limitedVis == tvFull || tg->limitedVis == tvSquish ||
 //     tg->limitedVis == tvPack) && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
 if (tg->limitedVis == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
     {
     // Lookup the depth of this subTrack and report it
     struct subTrack *st = hashFindVal(subTracksHash, tg->table);
     if (st)
         {
         tg->height = (st->levelCount * rmskJoinedItemHeight(tg, NULL));
         return tg->height;
         }
     else
         {
         tg->height = rmskJoinedItemHeight(tg, NULL);
         return tg->height;	// Just display one line
         }
     }
 else
     {
     if ( vis == tvDense )
       {
       tg->height = tgFixedTotalHeightNoOverflow(tg, tvDense );
       return tgFixedTotalHeightNoOverflow(tg, tvDense );
       }
     else
       {
       tg->height = tgFixedTotalHeightNoOverflow(tg, tvFull );
       return tgFixedTotalHeightNoOverflow(tg, tvFull );
       }
     }
 }
 
 static void drawDashedHorizLine(struct hvGfx *hvg, int x1, int x2,
 		     int y, int dashLen, int gapLen, Color lineColor)
 // ie.    - - - - - - - - - - - - - - - -
 {
 int cx1 = x1;
 int cx2;
 while (1)
     {
     cx2 = cx1 + dashLen;
     if (cx2 > x2)
         cx2 = x2;
     hvGfxLine(hvg, cx1, y, cx2, y, lineColor);
     cx1 += (dashLen + gapLen);
     if (cx1 > x2)
         break;
     }
 }
 
 static void drawDashedHorizLineWHash(struct hvGfx *hvg, int x1, int x2,
 			  int y, int dashLen, int gapLen, Color lineColor,
 			  int unalignedLen)
 // ie.    - - - - - - -\234\- - - - - - - - -
 {
 int cx1 = x1;
 int cx2;
 
 char lenLabel[20];
 safef(lenLabel, sizeof(lenLabel), "%d", unalignedLen);
 MgFont *font = tl.font;
 int fontHeight = tl.fontHeight;
 int stringWidth = mgFontStringWidth(font, lenLabel) + LABEL_PADDING;
 
 int glyphWidth = x2 - x1;
 
 if (glyphWidth < stringWidth + 6 + (2 * dashLen))
     stringWidth = 0;
 
 int midX = ((glyphWidth) / 2) + x1;
 int startHash = midX - (stringWidth * 0.5);
 int midPointDrawn = 0;
 
   /*
    * Degrade Gracefully:
    *   Too little space to draw dashes or even
    *   hash marks, give up.
    */
 if (glyphWidth < 6 + dashLen)
     {
     hvGfxLine(hvg, x1, y, x2, y, lineColor);
     return;
     }
 if (glyphWidth < 6 + (2 * dashLen))
     {
     midX -= 3;
     hvGfxLine(hvg, x1, y, midX, y, lineColor);
     hvGfxLine(hvg, midX, y - 3, midX + 3, y + 3, lineColor);
     hvGfxLine(hvg, midX + 3, y - 3, midX + 6, y + 3, lineColor);
     hvGfxLine(hvg, midX + 6, y, x2, y, lineColor);
     return;
     }
 
 while (1)
     {
     cx2 = cx1 + dashLen;
     if (cx2 > x2)
         cx2 = x2;
 
     if (!midPointDrawn && cx2 > startHash)
 	{
 	// Draw double slashes "\\" instead of dash
 	hvGfxLine(hvg, cx1, y - 3, cx1 + 3, y + 3, lineColor);
 	if (stringWidth)
 	    {
 	    hvGfxTextCentered(hvg, cx1 + 3, y - (fontHeight/2), stringWidth,
 			       fontHeight, MG_BLACK, font, lenLabel);
 	    cx1 += stringWidth;
 	    }
 	hvGfxLine(hvg, cx1 + 3, y - 3, cx1 + 6, y + 3, lineColor);
 	cx1 += 6;
 	midPointDrawn = 1;
 	}
     else
 	{
 	// Draw a dash
 	hvGfxLine(hvg, cx1, y, cx2, y, lineColor);
 	cx1 += dashLen;
 	}
 
     if (!midPointDrawn && cx1 + gapLen > midX)
 	{
 	// Draw double slashes "\\" instead of gap
 	hvGfxLine(hvg, cx1, y - 3, cx1 + 3, y + 3, lineColor);
 	if (stringWidth)
 	    {
 	    hvGfxTextCentered(hvg, cx1 + 3, y - 3, stringWidth,
 			       fontHeight, MG_BLACK, font, lenLabel);
 	    cx1 += stringWidth;
 	    }
 	hvGfxLine(hvg, cx1 + 3, y - 3, cx1 + 6, y + 3, lineColor);
 	cx1 += 6;
 	midPointDrawn = 1;
 	}
     else
 	{
 	cx1 += gapLen;
 	}
 
     if (cx1 > x2)
         break;
     }
 }
 
 static void drawNormalBox(struct hvGfx *hvg, int x1, int y1,
 	       int width, int height, Color fillColor, Color outlineColor)
 {
 struct gfxPoly *poly = gfxPolyNew();
 int y2 = y1 + height;
 int x2 = x1 + width;
   /*
    *     1,5--------------2
    *       |              |
    *       |              |
    *       |              |
    *       4--------------3
    */
 gfxPolyAddPoint(poly, x1, y1);
 gfxPolyAddPoint(poly, x2, y1);
 gfxPolyAddPoint(poly, x2, y2);
 gfxPolyAddPoint(poly, x1, y2);
 gfxPolyAddPoint(poly, x1, y1);
 hvGfxDrawPoly(hvg, poly, fillColor, TRUE);
 hvGfxDrawPoly(hvg, poly, outlineColor, FALSE);
 gfxPolyFree(&poly);
 }
 
 static void drawBoxWChevrons(struct hvGfx *hvg, int x1, int y1,
 		  int width, int height, Color fillColor, Color outlineColor,
 		  char strand)
 {
 drawNormalBox(hvg, x1, y1, width, height, fillColor, outlineColor);
 static Color white = 0xffffffff;
 int dir = (strand == '+' ? 1 : -1);
 int midY = y1 + ((height) >> 1);
 clippedBarbs(hvg, x1 + 1, midY, width, ((height >> 1) - 2), 5,
 	      dir, white, TRUE);
 }
 
 static void drawRMGlyph(struct hvGfx *hvg, int y, int heightPer,
 	    int width, int baseWidth, int xOff, struct rmskJoined *rm,
             enum trackVisibility vis)
 /*
  *  Draw a detailed RepeatMasker annotation glyph given
  *  a single rmskJoined structure.
  *
  *  A couple of caveats about the use of the rmskJoined
  *  structure to hold a RepeatMasker annotation.
  *
  *  chromStart/chromEnd:  These represent genomic coordinates
  *                        that mark the extents of graphics
  *                        annotation on the genome.  I.e
  *                        aligned blocks + unaligned consensus
  *                        blocks.  The code below may not
  *                        draw to these extents in some cases.
  *
  *  score:  This represents the average divergence of the
  *          individual aligned blocks.  It is a percentage
  *          * 100.  Ie. 23.11 % is encoded as the integer 2311.
  *
  *  blockRelStarts:  Two types of blocks are interwoven in this
  *                format.  Aligned and unaligned blocks.  Aligned
  *                blocks will have a value >= 0 representing the
  *                relative position from chromStart for this
  *                start of this block.  A value of -1 for chromStart
  *                is indicative of unaligned block and starts from
  *                the end of the last aligned block on the chromosome
  *                or from chromStart if it's the first block in the list.
  *
  *  name:  Stores the id#class/family
  *
  *  rgb:  Unused at this time
  *
  *  description: Unused but the intention would be to store
  *               the RepeatMasker *.out lines which make up
  *               all the aligned sections of this joined
  *               annotation.
  *
  *  blockSizes:   As you would expect.
  *
  *  Here is an example:
  *                ie.
  *                A forward strand RM annotation from chr1:186-196
  *                which is aligned to a 100 bp repeat from 75-84
  *                in the consensus would be represented as:
  *
  *                chromStart: 111
  *                chromEnd: 212
  *                blockRelStarts: -1, 75, -1
  *                blockSizes: 75, 10, 16
  *
  */
 {
 setWg();
 int idx;
 int lx1, lx2;
 int cx1, cx2;
 int w;
 struct repeatItem *ri;
   /*
    * heightPer is the God given height of a single
    * track item...respect your overlord.
    */
 int alignedBlockHeight = heightPer * 0.5;
 int alignedBlockOffset = heightPer - alignedBlockHeight;
 int unalignedBlockHeight = heightPer * 0.75;
 int unalignedBlockOffset = heightPer - unalignedBlockHeight;
 
 Color black = hvGfxFindColorIx(hvg, 0, 0, 0);
 Color fillColor = shadesOfGray[5];
 Color outlineColor = rmskJoinedClassColors[0];
 
   // Break apart the name and get the class of the
   // repeat.
 char class[256];
   // Simplify repClass for lookup: strip trailing '?',
   // simplify *RNA to RNA:
 char *poundPtr = index(rm->name, '#');
 if (poundPtr)
     {
     safecpy(class, sizeof(class), poundPtr + 1);
     char *slashPtr = index(class, '/');
     if (slashPtr)
         *slashPtr = '\0';
     }
 else
     {
     safecpy(class, sizeof(class), "Unknown");
     }
 char *p = &(class[strlen(class) - 1]);
 if (*p == '?')
     *p = '\0';
 if (endsWith(class, "RNA"))
     safecpy(class, sizeof(class), "RNA");
 
   // Lookup the class to get the color scheme
 ri = hashFindVal(classHash, class);
 if (ri == NULL)
     ri = otherRepeatItem;
 
   // Pick the fill color based on the divergence
 int percId = 10000 - rm->score;
 int grayLevel = grayInRange(percId, 6000, 10000);
 fillColor = shadesOfGray[grayLevel];
 outlineColor = ri->color;
 
   // Draw from left to right
 for (idx = 0; idx < rm->blockCount; idx++)
     {
     int fragGStart = rm->blockRelStarts[idx];
 
     /*
      *  Assumptions about blockCount
      *   - first aligned block = 1, the block 0 is
      *     the unaligned consnesus either 5' or 3' of
      *     this block.
      *   - The last aligned block is blockCount - 1;
      */
     if (fragGStart > -1)
 	{
 	// Aligned Block
 	int fragSize = rm->blockSizes[idx];
 	fragGStart += rm->chromStart;
 	int fragGEnd = fragGStart + fragSize;
 	lx1 = roundingScale(fragGStart - winStart, width, baseWidth) + xOff;
 	lx1 = max(lx1, 0);
 	lx2 = roundingScale(fragGEnd - winStart, width, baseWidth) + xOff;
 	w = lx2 - lx1;
 	if (w <= 0)
 	    w = 1;
 
 	if (idx == 1 && rm->blockCount == 3)
 	    {
 	    // One and only one aligned block
 	    drawBoxWChevrons(hvg, lx1, y + alignedBlockOffset, w,
 			      alignedBlockHeight, fillColor,
 			      outlineColor, rm->strand[0]);
 	    }
 	else if (idx == 1)
 	    {
 	    // First block
 	    if (rm->strand[0] == '-')
 		{
 		// First block on negative strand is the point block
 		drawBoxWChevrons(hvg, lx1,
 				  y + alignedBlockOffset, w,
 				  alignedBlockHeight, fillColor,
 				  outlineColor, rm->strand[0]);
 		}
 	    else
 		{
 		// First block on the positive strand is the tail block
 		drawBoxWChevrons(hvg, lx1,
 				  y + alignedBlockOffset, w,
 				  alignedBlockHeight, fillColor,
 				  outlineColor, rm->strand[0]);
 
 		}
 	    }
 	else if (idx == (rm->blockCount - 2))
 	    {
 	    // Last block
 	    if (rm->strand[0] == '-')
 		{
 		// Last block on the negative strand is the tail block
 		drawBoxWChevrons(hvg, lx1,
 				  y + alignedBlockOffset, w,
 				  alignedBlockHeight, fillColor,
 				  outlineColor, rm->strand[0]);
 
 		}
 	    else
 		{
 		// Last block on the positive strand is the poitn block
 		drawBoxWChevrons(hvg, lx1,
 				  y + alignedBlockOffset, w,
 				  alignedBlockHeight, fillColor,
 				  outlineColor, rm->strand[0]);
 
 		}
 	    }
 	else
 	    {
 	    // Intermediate aligned blocks are drawn as rectangles
 	    drawBoxWChevrons(hvg, lx1, y + alignedBlockOffset, w,
 			      alignedBlockHeight, fillColor,
 			      outlineColor, rm->strand[0]);
 	    }
 
 	}
     else
 	{
 	// Unaligned Block
 	int relStart = 0;
 	if (idx == 0)
 	    {
 	    /*
 	     * Unaligned sequence at the start of an annotation
 	     * Draw as:
 	     *     |-------------     or |------//------
 	     *                  |                      |
 	     *                  >>>>>>>                >>>>>>>>
 	     */
 	    lx2 = roundingScale(rm->chromStart +
 				 rm->blockRelStarts[idx + 1] -
 				 winStart, width, baseWidth) + xOff;
 	    if ((rm->blockSizes[idx] * pixelsPerBase) >
 		MAX_UNALIGNED_PIXEL_LEN)
 		{
 		lx1 = roundingScale(rm->chromStart +
 				     (rm->blockRelStarts[idx + 1] -
 				      (int)
 				      (MAX_UNALIGNED_PIXEL_LEN /
 				       pixelsPerBase)) -
 				     winStart, width, baseWidth) + xOff;
 		// Line Across
 		drawDashedHorizLineWHash(hvg, lx1, lx2,
 					  y +
 					  unalignedBlockOffset,
 					  5, 5, black, rm->blockSizes[idx]);
 		}
 	    else
 		{
 		lx1 = roundingScale(rm->chromStart - winStart,
 				     width, baseWidth) + xOff;
 		// Line Across
 		drawDashedHorizLine(hvg, lx1, lx2,
 				     y + unalignedBlockOffset, 5, 5, black);
 		}
 	    // Line down
 	    hvGfxLine(hvg, lx2, y + alignedBlockOffset, lx2,
 		       y + unalignedBlockOffset, black);
 	    hvGfxLine(hvg, lx1, y + unalignedBlockOffset - 3, lx1,
 		       y + unalignedBlockOffset + 3, black);
 
 	    // Draw labels
 	    //Disabled
 	    //if ( vis != tvSquish && vis != tvFull )
             //     {
 	         MgFont *font = tl.font;
 	         int fontHeight = tl.fontHeight;
 	         int stringWidth =
 	     	     mgFontStringWidth(font, rm->name) + LABEL_PADDING;
 	         hvGfxTextCentered(hvg, lx1 - stringWidth,
 			       heightPer - fontHeight + y,
 			       stringWidth, fontHeight, MG_BLACK, font,
 			       rm->name);
              //    }
 
 
 
 
 	    }
 	else if (idx == (rm->blockCount - 1))
 	    {
 	    /*
 	     * Unaligned sequence at the end of an annotation
 	     * Draw as:
 	     *       -------------|   or        ------//------|
 	     *       |                          |
 	     *  >>>>>                    >>>>>>>
 	     */
 	    lx1 = roundingScale(rm->chromStart +
 				 rm->blockRelStarts[idx - 1] +
 				 rm->blockSizes[idx - 1] - winStart,
 				 width, baseWidth) + xOff;
 	    if ((rm->blockSizes[idx] * pixelsPerBase) >
 		MAX_UNALIGNED_PIXEL_LEN)
 		{
 		lx2 = roundingScale(rm->chromStart +
 				     rm->blockRelStarts[idx -
 							1] +
 				     rm->blockSizes[idx - 1] +
 				     (int)
 				     (MAX_UNALIGNED_PIXEL_LEN /
 				      pixelsPerBase) - winStart,
 				     width, baseWidth) + xOff;
 		// Line Across
 		drawDashedHorizLineWHash(hvg, lx1, lx2,
 					  y +
 					  unalignedBlockOffset,
 					  5, 5, black, rm->blockSizes[idx]);
 		}
 	    else
 		{
 		lx2 = roundingScale(rm->chromStart +
 				     rm->blockRelStarts[idx -
 							1] +
 				     rm->blockSizes[idx - 1] +
 				     rm->blockSizes[idx] -
 				     winStart, width, baseWidth) + xOff;
 		// Line Across
 		drawDashedHorizLine(hvg, lx1, lx2,
 				     y + unalignedBlockOffset, 5, 5, black);
 		}
 	    // Line down
 	    hvGfxLine(hvg, lx1, y + alignedBlockOffset, lx1,
 		       y + unalignedBlockOffset, black);
 	    hvGfxLine(hvg, lx2, y + unalignedBlockOffset - 3, lx2,
 		       y + unalignedBlockOffset + 3, black);
 	    }
 	else
 	    {
 	    /*
 	     * Middle Unaligned
 	     * Draw unaligned sequence between aligned fragments
 	     * as:       .........
 	     *          /         \
 	     *     >>>>>           >>>>>>
 	     *
 	     * or:
 	     *         .............
 	     *         \           /
 	     *     >>>>>           >>>>>>
 	     *
 	     * Also use ....//.... to indicate not to scale if
 	     * necessary.
 	     *
 	     */
 	    int alignedGapSize = rm->blockRelStarts[idx + 1] -
 		(rm->blockRelStarts[idx - 1] + rm->blockSizes[idx - 1]);
 	    int unaSize = rm->blockSizes[idx];
 
 	    int alignedOverlapSize = unaSize - alignedGapSize;
 
 	    if (unaSize < 0)
 		{
 		relStart =
 		    rm->blockRelStarts[idx - 1] +
 		    rm->blockSizes[idx - 1] - (alignedOverlapSize / 2);
 
 		if (abs(unaSize) > rm->blockSizes[idx - 1])
 		    unaSize = -rm->blockSizes[idx - 1];
 
 		lx1 = roundingScale(rm->chromStart +
 				     rm->blockRelStarts[idx -
 							1] +
 				     rm->blockSizes[idx - 1] +
 				     unaSize - winStart, width,
 				     baseWidth) + xOff;
 		lx1 = max(lx1, 0);
 		lx2 = roundingScale(rm->chromStart +
 				     rm->blockRelStarts[idx +
 							1] -
 				     winStart, width, baseWidth) + xOff;
 
 		int midPoint = ((lx2 - lx1) / 2) + lx1;
 
 		// Block Intersection Line
 		hvGfxLine(hvg, lx1, y + alignedBlockOffset, lx1,
 			   y + alignedBlockOffset + alignedBlockHeight,
 			   black);
 
 		// Line Up
 		hvGfxLine(hvg, lx1, y + alignedBlockOffset,
 			   midPoint, y + unalignedBlockOffset, black);
 
 		// Line Down
 		hvGfxLine(hvg, lx2, y + alignedBlockOffset,
 			   midPoint, y + unalignedBlockOffset, black);
 
 		}
 	    else if (alignedOverlapSize > 0 &&
 		     ((alignedOverlapSize * 0.5) >
 		      (0.3 * rm->blockSizes[idx - 1])
 		      || (alignedOverlapSize * 0.5) >
 		      (0.3 * rm->blockSizes[idx + 1])))
 		{
 		// Need to shorten unaligned length
 		int smallOverlapLen = 0;
 		smallOverlapLen = (0.3 * rm->blockSizes[idx - 1]);
 		if (smallOverlapLen > (0.3 * rm->blockSizes[idx + 1]))
 		    smallOverlapLen = (0.3 * rm->blockSizes[idx + 1]);
 		unaSize = (smallOverlapLen * 2) + alignedGapSize;
 		relStart =
 		    rm->blockRelStarts[idx - 1] +
 		    rm->blockSizes[idx - 1] - smallOverlapLen;
 		lx1 = roundingScale(relStart + rm->chromStart -
 				     winStart, width, baseWidth) + xOff;
 		lx1 = max(lx1, 0);
 		lx2 = roundingScale(relStart + rm->chromStart +
 				     unaSize - winStart, width,
 				     baseWidth) + xOff;
 		// Line Up
 		cx1 = roundingScale(rm->chromStart +
 				     rm->blockRelStarts[idx -
 							1] +
 				     rm->blockSizes[idx - 1] -
 				     winStart, width, baseWidth) + xOff;
 		hvGfxLine(hvg, cx1, y + alignedBlockOffset, lx1,
 			   y + unalignedBlockOffset, black);
 
 		// Line Across
 		drawDashedHorizLineWHash(hvg, lx1, lx2,
 					  y +
 					  unalignedBlockOffset,
 					  5, 5, black, rm->blockSizes[idx]);
 		// Line Down
 		cx2 = roundingScale(rm->chromStart +
 				     rm->blockRelStarts[idx +
 							1] -
 				     winStart, width, baseWidth) + xOff;
 		hvGfxLine(hvg, cx2, y + alignedBlockOffset, lx2,
 			   y + unalignedBlockOffset, black);
 		}
 	    else
 		{
 		// Adequate to display full length
 		relStart =
 		    rm->blockRelStarts[idx - 1] +
 		    rm->blockSizes[idx - 1] - (alignedOverlapSize / 2);
 		lx1 = roundingScale(relStart + rm->chromStart -
 				     winStart, width, baseWidth) + xOff;
 		lx1 = max(lx1, 0);
 		lx2 = roundingScale(relStart + rm->chromStart +
 				     unaSize - winStart, width,
 				     baseWidth) + xOff;
 		// Line Up
 		int cx1 =
 		    roundingScale(rm->chromStart +
 				   rm->blockRelStarts[idx - 1] +
 				   rm->blockSizes[idx - 1] - winStart,
 				   width, baseWidth) + xOff;
 		hvGfxLine(hvg, cx1, y + alignedBlockOffset, lx1,
 			   y + unalignedBlockOffset, black);
 		drawDashedHorizLine(hvg, lx1, lx2,
 				     y + unalignedBlockOffset, 5, 5, black);
 		// Line Down
 		cx2 = roundingScale(rm->chromStart +
 				     rm->blockRelStarts[idx +
 							1] -
 				     winStart, width, baseWidth) + xOff;
 		hvGfxLine(hvg, cx2, y + alignedBlockOffset, lx2,
 			   y + unalignedBlockOffset, black);
 		}
 	    }
 	}
     }
 }
 
 static void origRepeatDraw(struct track *tg, int seqStart, int seqEnd,
 		struct hvGfx *hvg, int xOff, int yOff, int width,
 		MgFont * font, Color color, enum trackVisibility vis)
 /* This is the original repeat drawing routine, modified
  * to handle the new rmskJoined table structure.
  */
 {
 setWg();
 int baseWidth = seqEnd - seqStart;
 struct repeatItem *ri;
 int y = yOff;
 int heightPer = tg->heightPer;
 int lineHeight = tg->lineHeight;
 int x1, x2, w;
 Color col;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 int rowOffset;
 
 //Disabled
 //if (vis == tvFull || vis == tvSquish || vis == tvPack)
 if (vis == tvFull)
     {
     /*
      * Do grayscale representation spread out among tracks.
      */
     struct hash *hash = newHash(6);
     struct rmskJoined *ro;
     int percId;
     int grayLevel;
 
     for (ri = tg->items; ri != NULL; ri = ri->next)
 	{
 	ri->yOffset = y;
 	y += lineHeight;
 	hashAdd(hash, ri->class, ri);
 	}
     sr = hRangeQuery(conn, tg->table, chromName, winStart, winEnd, NULL,
 		      &rowOffset);
     while ((row = sqlNextRow(sr)) != NULL)
 	{
 	ro = rmskJoinedLoad(row + rowOffset);
 	char class[256];
 	// Simplify repClass for lookup: strip trailing '?',
 	// simplify *RNA to RNA:
 	char *poundPtr = index(ro->name, '#');
 	if (poundPtr)
 	    {
 	    safecpy(class, sizeof(class), poundPtr + 1);
 	    char *slashPtr = index(class, '/');
 	    if (slashPtr)
 	        *slashPtr = '\0';
 	    }
 	else
 	    {
 	    safecpy(class, sizeof(class), "Unknown");
 	    }
 	char *p = &(class[strlen(class) - 1]);
 	if (*p == '?')
 	    *p = '\0';
 	if (endsWith(class, "RNA"))
 	    safecpy(class, sizeof(class), "RNA");
 	ri = hashFindVal(hash, class);
 	if (ri == NULL)
 	    ri = otherRepeatItem;
 	percId = 10000 - ro->score;
 	grayLevel = grayInRange(percId, 6000, 10000);
 	col = shadesOfGray[grayLevel];
 
 	int idx = 0;
 	for (idx = 0; idx < ro->blockCount; idx++)
 	    {
 	    if (ro->blockRelStarts[idx] > 0)
 		{
 		int blockStart = ro->chromStart + ro->blockRelStarts[idx];
 		int blockEnd =
 		    ro->chromStart + ro->blockRelStarts[idx] +
 		    ro->blockSizes[idx];
 
 		x1 = roundingScale(blockStart - winStart, width,
 				    baseWidth) + xOff;
 		x1 = max(x1, 0);
 		x2 = roundingScale(blockEnd - winStart, width,
 				    baseWidth) + xOff;
 		w = x2 - x1;
 		if (w <= 0)
 		    w = 1;
 		hvGfxBox(hvg, x1, ri->yOffset, w, heightPer, col);
 		}
 	    }
 	rmskJoinedFree(&ro);
 	}
     freeHash(&hash);
     }
 else
     {
     char table[HDB_MAX_TABLE_STRING];
     boolean hasBin;
-    struct dyString *query = newDyString(1024);
+    struct dyString *query = dyStringNew(1024);
     /*
      * Do black and white on single track.  Fetch less than
      * we need from database.
      */
     if (hFindSplitTable(database, chromName, tg->table, table, sizeof table, &hasBin))
 	{
 	sqlDyStringPrintf(query,
 			"select chromStart,blockCount,blockSizes,"
 			"blockRelStarts from %s where ", table);
 	if (hasBin)
 	    hAddBinToQuery(winStart, winEnd, query);
 	sqlDyStringPrintf(query, "chromStart<%u and chromEnd>%u ",
 			winEnd, winStart);
 	/*
 	 * if we're using a single rmsk table, add chrom to the where clause
 	 */
 	if (startsWith("rmskJoined", table))
 	    sqlDyStringPrintf(query, " and chrom = '%s' ", chromName);
 	sr = sqlGetResult(conn, query->string);
 	while ((row = sqlNextRow(sr)) != NULL)
 	    {
 	    int idx = 0;
 	    int blockCount = sqlSigned(row[1]);
 	    int sizeOne;
 	    int *blockSizes;
 	    int *blockRelStarts;
 	    int chromStart = sqlUnsigned(row[0]);
 	    sqlSignedDynamicArray(row[2], &blockSizes, &sizeOne);
 	    assert(sizeOne == blockCount);
 	    sqlSignedDynamicArray(row[3], &blockRelStarts, &sizeOne);
 	    assert(sizeOne == blockCount);
 
 	    for (idx = 1; idx < blockCount - 1; idx++)
 		{
 		if (blockRelStarts[idx] >= 0)
 		    {
 		    int blockStart = chromStart + blockRelStarts[idx];
 		    int blockEnd =
 			chromStart + blockRelStarts[idx] + blockSizes[idx];
 
 		    x1 = roundingScale(blockStart - winStart,
 					width, baseWidth) + xOff;
 		    x1 = max(x1, 0);
 		    x2 = roundingScale(blockEnd - winStart, width,
 					baseWidth) + xOff;
 		    w = x2 - x1;
 		    if (w <= 0)
 		        w = 1;
 		    hvGfxBox(hvg, x1, yOff, w, heightPer, MG_BLACK);
 		    }
 		}
 	    }
 	}
     dyStringFree(&query);
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 }
 
 /* Main callback for displaying this track in the viewport
  * of the browser.
  */
 static void rmskJoinedDrawItems(struct track *tg, int seqStart, int seqEnd,
 	     struct hvGfx *hvg, int xOff, int yOff, int width,
 	     MgFont * font, Color color, enum trackVisibility vis)
 {
 setWg();
 int baseWidth = seqEnd - seqStart;
 
 // TODO: Document
 pixelsPerBase = (float) width / (float) baseWidth;
   /*
    * Its unclear to me why heightPer is not updated to the
    * value set in rmskJoinedItemHeight() at the time this callback
    * is invoked.  Getting the correct value myself.
    * was:
    * int heightPer = tg->heightPer;
    */
 int heightPer = rmskJoinedItemHeight(tg, NULL);
 //boolean isFull = (vis == tvFull);
 struct rmskJoined *rm;
 
   // If we are in full view mode and the scale is sufficient,
   // display the new visualization.
 //Disabled
 //if ((vis == tvFull || vis == tvSquish || vis == tvPack) && baseWidth <= DETAIL_VIEW_MAX_SCALE)
 if (vis == tvFull && baseWidth <= DETAIL_VIEW_MAX_SCALE)
     {
     int level = yOff;
     struct subTrack *st = hashFindVal(subTracksHash, tg->table);
     if (!st)
 	return;
 
     int lidx = st->levelCount;
     int currLevel = 0;
     for (currLevel = 0; currLevel < lidx; currLevel++)
 	{
 	rm = st->levels[currLevel];
 	while (rm)
 	    {
 	    drawRMGlyph(hvg, level, heightPer, width, baseWidth, xOff, rm, vis );
 
 	    char statusLine[128];
 	    int ss1 = roundingScale(rm->alignStart - winStart,
 				     width, baseWidth) + xOff;
 
 	    safef(statusLine, sizeof(statusLine), "%s", rm->name);
 
 	    int x1 = roundingScale(rm->alignStart - winStart, width,
 				    baseWidth) + xOff;
 	    x1 = max(x1, 0);
 	    int x2 = roundingScale(rm->alignEnd - winStart, width,
 				    baseWidth) + xOff;
 	    int w = x2 - x1;
 	    if (w <= 0)
 	        w = 1;
 
 	    mapBoxHc(hvg, rm->alignStart, rm->alignEnd, ss1, level,
    	             w, heightPer, tg->track, rm->id, statusLine);
 	    rm = rm->next;
 	    }
 	level += heightPer;
 	rmskJoinedFreeList(&(st->levels[currLevel]));
         }
 
     }
 else
     {
     // Draw the stereotypical view
     origRepeatDraw(tg, seqStart, seqEnd,
 		    hvg, xOff, yOff, width, font, color, vis);
     }
 }
 
 void rmskJoinedMethods(struct track *tg)
 {
 tg->loadItems = rmskJoinedLoadItems;
 tg->freeItems = rmskJoinedFreeItems;
 tg->drawItems = rmskJoinedDrawItems;
 tg->colorShades = shadesOfGray;
 tg->itemName = rmskJoinedName;
 tg->mapItemName = rmskJoinedName;
 tg->totalHeight = rmskJoinedTotalHeight;
 tg->itemHeight = rmskJoinedItemHeight;
 tg->itemStart = tgItemNoStart;
 tg->itemEnd = tgItemNoEnd;
 tg->mapsSelf = TRUE;
 //tg->canPack = TRUE;
 }