a63596012d328a6333a06bdd640666dd336d065a
chmalee
  Mon May 23 11:30:49 2022 -0700
Add a type line check to hubCheck, refs #28350, #13428

diff --git src/hg/utils/hubCheck/hubCheck.c src/hg/utils/hubCheck/hubCheck.c
index 0454788..ebb2be8 100644
--- src/hg/utils/hubCheck/hubCheck.c
+++ src/hg/utils/hubCheck/hubCheck.c
@@ -749,30 +749,62 @@
             (tdb->parent != NULL &&
             (tdbIsComposite(tdb->parent) || tdbIsCompositeView(tdb->parent))))
         {
         errAbort("Track \"%s\" is declared container multiWig and has parent \"%s\"."
             " Container multiWig tracks cannot be children of composites or views",
             tdb->track, tdb->parent->track);
         }
     }
 else if (tdb->subtracks != NULL)
     {
     errAbort("Track \"%s\" has children tracks (e.g: \"%s\"), but is not a "
         "compositeTrack, container, view or superTrack", tdb->track, tdb->subtracks->track);
     }
 }
 
+boolean checkTypeLine(struct trackHubGenome *genome, struct trackDb *tdb, struct dyString *errors, struct trackHubCheckOptions *options)
+{
+boolean retVal = FALSE;
+struct errCatch *errCatch = errCatchNew();
+if (errCatchStart(errCatch))
+    {
+    char *type = trackDbRequiredSetting(tdb, "type");
+    char *splitType[4];
+    int numWords = chopByWhite(cloneString(type), splitType, sizeof(splitType));
+    if (sameString(splitType[0], "bigBed"))
+        {
+        if (numWords > 1 && (strchr(splitType[1], '+') || strchr(splitType[1], '.')))
+            {
+            errAbort("error in type line \"%s\" for track \"%s\". "
+                "A space is needed after the \"+\" or \".\" character.", type, tdb->track);
+            }
+        if (numWords > 2 && (!sameString(splitType[2], "+") && !sameString(splitType[2], ".")))
+            {
+            errAbort("error in type line \"%s\" for track \"%s\". "
+                "Only \"+\" or \".\" is allowed after bigBed numFields setting.", type, tdb->track);
+            }
+        }
+    }
+errCatchEnd(errCatch);
+if (errCatch->gotError)
+    {
+    trackDbErr(errors, errCatch->message->string, genome, tdb, options->htmlOut);
+    retVal = TRUE;
+    }
+return retVal;
+}
+
 int hubCheckTrack(struct trackHub *hub, struct trackHubGenome *genome, struct trackDb *tdb,
                         struct trackHubCheckOptions *options, struct dyString *errors)
 /* Check track settings and optionally, files */
 {
 int retVal = 0;
 int trackDbErrorCount = 0;
 
 if (options->checkSettings && options->settings)
     {
     //verbose(3, "Found %d settings to check to spec\n", slCount(settings));
     verbose(3, "Checking track: %s\n", tdb->shortLabel);
     verbose(3, "Found %d settings to check to spec\n", hashNumEntries(tdb->settingsHash));
     struct hashEl *hel;
     struct hashCookie cookie = hashFirst(tdb->settingsHash);
     while ((hel = hashNext(&cookie)) != NULL)
@@ -815,32 +847,41 @@
 
 if (options->htmlOut)
     {
     dyStringPrintf(errors, "trackData['%s'] = [", idName);
     }
 
 if (errCatchStart(errCatch))
     {
     hubCheckParentsAndChildren(tdb);
     if (trackIsContainer)
         retVal |= hubCheckCompositeSettings(genome, tdb, errors, options);
 
     if (tdbIsSubtrack(tdb))
         retVal |= hubCheckSubtrackSettings(genome, tdb, errors, options);
 
-    if (options->checkFiles)
+    // check that type line is syntactically correct regardless of
+    // if we actually want to check the data file itself
+    boolean foundTypeError = checkTypeLine(genome, tdb, errors, options);
+
+    // No point in checking the data file if the type setting is incorrect,
+    // since hubCheckBigDataUrl will error out early with a less clear message
+    // if the type line is messed up. This has the added benefit of providing
+    // consistent messaging on command line interface vs web interface
+    if (!foundTypeError && options->checkFiles)
         hubCheckBigDataUrl(hub, genome, tdb);
+
     if (!sameString(tdb->track, "cytoBandIdeo"))
         {
         trackHubAddDescription(genome->trackDbFile, tdb);
         if (!tdb->html)
             warn("warning: missing description page for track: '%s'", tdb->track);
         }
     }
 errCatchEnd(errCatch);
 if (errCatch->gotError || errCatch->gotWarning)
     {
     retVal = 1;
     trackDbErr(errors, errCatch->message->string, genome, tdb, options->htmlOut);
     if (errCatch->gotError)
         trackDbErrorCount += 1;
     }