c29d92c458b3d21a19caf72954b91e949bf72886
chmalee
  Fri May 24 16:17:22 2019 -0700
Fixing segfault in subtrack table printing for composite tracks with a sortOrder but no subgroups or missing subgroups, refs #23556

diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index f9a3c60..05cafc2 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -4733,51 +4733,51 @@
 
     // A hidden field to keep track of subtrack order if it could change
     if (sortOrder != NULL || useDragAndDrop)
 	{
 	safef(buffer, sizeof(buffer), "%s.priority", subtrack->track);
 	float priority = (float)cartUsualDouble(cart, buffer, subtrack->priority);
 	printf("<INPUT TYPE=HIDDEN NAME='%s' class='trPos' VALUE=\"%.0f\">",
 	       buffer, priority); // keeing track of priority
 	}
 
     // The checkbox has identifying classes including subCB and the tag for each dimension
     //  (e.g. class='subCB GM12878 CTCF Peak')
     dyStringClear(dyHtml);
     dyStringAppend(dyHtml, "subCB"); // always first
     int di;
-    if (membersForAll->dimensions)
+    if (membersForAll->dimensions && membership != NULL)
 	{
 	for (di=dimX;di<membersForAll->dimMax;di++)
 	    {
 	    if (membersForAll->members[di] && -1 !=
 				(ix = stringArrayIx(membersForAll->members[di]->groupTag,
 						    membership->subgroups, membership->count)))
 		dyStringPrintf(dyHtml," %s",membership->membership[ix]);
 	    }
 	}
-    else if (membersForAll->abcCount) // "dimensions" don't exist but may be subgroups anyway
+    else if (membersForAll->abcCount && membership != NULL) // "dimensions" don't exist but may be subgroups anyway
 	{
 	for (di=dimA;di<membersForAll->dimMax;di++)
 	    {
 	    if (membersForAll->members[di] && -1 !=
 				(ix = stringArrayIx(membersForAll->members[di]->groupTag,
 						    membership->subgroups, membership->count)))
 		dyStringPrintf(dyHtml," %s",membership->membership[ix]);
 	    }
 	}
-    if (membersForAll->members[dimV] && -1 !=
+    if (membersForAll->members[dimV] && membership != NULL && -1 !=
 				(ix = stringArrayIx(membersForAll->members[dimV]->groupTag,
 						    membership->subgroups, membership->count)))
 	dyStringPrintf(dyHtml, " %s",membership->membership[ix]);  // Saved view for last
 
     // And finally the checkBox is made!
     safef(buffer, sizeof(buffer), "%s_sel", subtrack->track);
     if (!enabledCB)
 	{
 	dyStringAppend(dyHtml, " disabled");
 	cgiMakeCheckBoxFourWay(buffer,checkedCB,enabledCB,id,dyStringContents(dyHtml),
 		"style='cursor:pointer' title='view is hidden'");
 	jsOnEventById("click", id, "matSubCbClick(this);");
 	}
     else
 	{
@@ -4816,33 +4816,45 @@
 	    safef(id, sizeof id, "%s_toggle", subtrack->track);
 	    #define SUBTRACK_CFG_WRENCH "<span id='%s' class='clickable%s' " \
 					"title='Configure this subtrack'><img src='../images/wrench.png'></span>\n"
 	    printf(SUBTRACK_CFG_WRENCH,id,(visibleCB ? "":" disabled"));
 	    jsOnEventByIdF("click", id, "return subCfg.cfgToggle(this,\"%s\");", subtrack->track);
 	    }
 	}
     printf("</TD>");
 
     // If sortable, then there must be a column per sortable dimension
     if (sortOrder != NULL)
         {
         int sIx=0;
         for (sIx=0; sIx <sortOrder->count; sIx++)
             {
+            ix = -1;
             char *col = sortOrder->column[sIx];
+            if (membership)
                 ix = stringArrayIx(col, membership->subgroups, membership->count);
                 // TODO: Sort needs to expand from subGroups to labels as well
+
+            // only print the warning message for trackDb errors and not for the
+            // default sortable columns of trackName and dateUnrestricted
+            if ( (!membership || (membership && ix == -1) ) &&
+                !(sameString(col, "trackName") || sameString(col, "dateUnrestricted")) )
+                {
+                printf("<TD><span style=\"color:red\">Missing subgroup</span></TD>");
+                }
+            else
+                {
                 if (ix >= 0)
                     {
                     char *term = membership->membership[ix];
                     char *title = membership->titles[ix];
                     char *titleRoot=NULL;
                     if (cvTermIsEmpty(col, title))
                         titleRoot = cloneString(" &nbsp;");
                     else
                         titleRoot = labelRoot(title, NULL);
                     // Each sortable column requires hidden goop (in the "abbr" field currently)
                     // which is the actual sort on value
                     printf("<TD id='%s_%s' abbr='%s' align='left'>", subtrack->track, col, term);
                     printf("&nbsp");
                     char *link = NULL;
                     if (vocabHash)
@@ -4853,30 +4865,31 @@
                         }
                     printf("%s", link ? link : titleRoot);
                     puts("</TD>");
                     freeMem(titleRoot);
                     }
                 else if (sameString(col, SUBTRACK_COLOR_SUBGROUP))
                     {
                     char *hue = subtrackColorToCompare(subtrack);
                     printf("<TD id='%s_%s' abbr='%s' bgcolor='#%02X%02X%02X'>"
                         "&nbsp;&nbsp;&nbsp;&nbsp;</TD>",
                         subtrack->track, col, hue, 
                         subtrack->colorR, subtrack->colorG, subtrack->colorB);
                     }
                 }
             }
+        }
     else  // Non-sortable tables do not have sort by columns but will display a short label
 	{ // (which may be a configurable link)
 	if (settings->colorPatch)
 	    {
 	    printf("<TD BGCOLOR='#%02X%02X%02X'>&nbsp;&nbsp;&nbsp;&nbsp;</TD>",
 			   subtrack->colorR, subtrack->colorG, subtrack->colorB);
 
 	    }
 	printf("<TD>&nbsp;");
 	hierarchy_t *hierarchy = hierarchySettingGet(parentTdb);
 	indentIfNeeded(hierarchy,membership);
 	hierarchyFree(&hierarchy);
 	printf("%s",subtrack->shortLabel);
 	puts("</TD>");
 	}