c99b62d7865a828f8f41c6e82dd36cbd2dd26780
markd
  Wed Jul 1 21:45:43 2020 -0700
hgBlat mostly works

diff --git src/gfServer/gfServer.c src/gfServer/gfServer.c
index 032ed0e..d62be7d 100644
--- src/gfServer/gfServer.c
+++ src/gfServer/gfServer.c
@@ -1008,167 +1008,179 @@
 	{
 	printf("%s\n", netRecieveString(sd, buf));
 	}
     }
 close(sd);
 }
 
 static void buildIndex(char *gfxFile, int fileCount, char *seqFiles[])
 /* build pre-computed index for seqFiles and write to gfxFile */
 {
 struct genoFindIndex *gfIdx = genoFindIndexBuild(fileCount, seqFiles, minMatch, maxGap, tileSize,
                                                  repMatch, doTrans, NULL, allowOneMismatch, doMask, stepSize, noSimpRepMask);
 genoFindIndexWrite(gfIdx, gfxFile);
 }
 
-
-static void dynErrorVa(char* msg, va_list args)
-/* send back an error response and exit */
+static void dynWarnErrorVa(char* msg, va_list args)
+/* warnHandler to log and send back an error response */
 {
 char buf[4096];
 int msgLen = vsnprintf(buf, sizeof(buf) - 1, msg, args);
 buf[msgLen] = '\0';
-logDebug("Error: %s", buf);
-printf("Error: %s\n", msg);
-exit(1);
-}
-
-static void dynError(char* msg, ...)
-/* send back an error response and exit */
-{
-va_list args;
-va_start(args, msg);
-dynErrorVa(msg, args);
-va_end(args);
-exit(1);
+logError("%s", buf);
+printf("Error: %s\n", buf);
 }
 
 static void dynReadBytes(char *buf, int bufSize)
 /* read pending bytes */
 {
 int readSize = read(STDIN_FILENO, buf, bufSize-1);
 if (readSize < 0)
-    dynError("EOF from client");
+    errAbort("EOF from client");
 buf[readSize] = '\0';
 }
 
 
 static void dynReadQuery(char **commandRet, int *qsizeRet, char **genomeNameRet)
 /* read query request from stdin, same as server expect includes database
  * Format is:
  *  signature command qsize genome
  */
 {
 char buf[256];
 dynReadBytes(buf, sizeof(buf));
 logDebug("query: %s", buf);
 
 if (!startsWith(gfSignature(), buf))
-    dynError("query does not start with signature, got '%s'", buf);
+    errAbort("query does not start with signature, got '%s'", buf);
 
 static int nwords = 3;
 char *words[nwords];
 int numWords = chopByWhite(buf, words, nwords);
 if (numWords != nwords)
-    dynError("expected %d words in request, got %d", nwords, numWords);
+    errAbort("expected %d words in request, got %d", nwords, numWords);
 char *command = buf + strlen(gfSignature());
 if (!(sameString("query", command) || 
       sameString("protQuery", command) || sameString("transQuery", command)))
-    dynError("invalid command '%s'", command);
+    errAbort("invalid command '%s'", command);
 *commandRet = cloneString(command);
 *qsizeRet = atoi(words[1]);
 *genomeNameRet = cloneString(words[2]);
 }
 
 static struct dnaSeq* dynReadQuerySeq(int qSize, boolean isTrans, boolean queryIsProt)
 /* read the DNA sequence from the query, filtering junk  */
 {
 struct dnaSeq *seq;
 AllocVar(seq);
 seq->size = qSize;
 seq->dna = needLargeMem(qSize+1);
 if (gfReadMulti(STDIN_FILENO, seq->dna, qSize) != qSize)
-    dynError("read of %d bytes of query sequence failed", qSize);
+    errAbort("read of %d bytes of query sequence failed", qSize);
 
 if (queryIsProt)
     {
     seq->size = aaFilteredSize(seq->dna);
     aaFilter(seq->dna, seq->dna);
     }
 else
     {
     seq->size = dnaFilteredSize(seq->dna);
     dnaFilter(seq->dna, seq->dna);
     }
 int maxSize = (isTrans ? maxAaSize : maxNtSize);
 if (seq->size > maxSize)
     {
     seq->size = maxSize;
     seq->dna[maxSize] = 0;
     }
 
 return seq;
 }
 
 static void dynGetDataFiles(char *rootDir, char* genomeName, boolean isTrans, char seqFile[PATH_LEN], char gfIdxFile[PATH_LEN])
 /* get paths for sequence files to handle requests and validate they exist */
 {
 safef(seqFile, PATH_LEN, "%s/%s/%s.2bit", rootDir, genomeName, genomeName);
 if (!fileExists(seqFile))
-    dynError("sequence file for %s does not exist: %s", genomeName, seqFile);
+    errAbort("sequence file for %s does not exist: %s", genomeName, seqFile);
 safef(gfIdxFile, PATH_LEN, "%s/%s/%s.%s.gfidx", rootDir, genomeName, genomeName, isTrans ? "trans" : "untrans");
 if (!fileExists(gfIdxFile))
-    dynError("gf index file for %s does not exist: %s", genomeName, gfIdxFile);
+    errAbort("gf index file for %s does not exist: %s", genomeName, gfIdxFile);
 }
 
+static void dynWriteTrailer(struct genoFindIndex *gfIdx)
+/* write trailer information, which is a subset of what status would return.
+ * This avoids the need to reconnect and reload index.
+ */
+{
+char buf[256];
+struct genoFind *gf = gfIdx->isTrans ? gfIdx->transGf[0][0] : gfIdx->untransGf;
+sprintf(buf, "version %s", gfVersion);
+netSendString(STDOUT_FILENO, buf);
+sprintf(buf, "type %s", (gfIdx->isTrans ? "translated" : "nucleotide"));
+netSendString(STDOUT_FILENO, buf);
+sprintf(buf, "tileSize %d", gf->tileSize);
+netSendString(STDOUT_FILENO, buf);
+sprintf(buf, "stepSize %d", gf->stepSize);
+netSendString(STDOUT_FILENO, buf);
+sprintf(buf, "minMatch %d", gf->minMatch);
+netSendString(STDOUT_FILENO, buf);
+netSendString(STDOUT_FILENO, "trailerEnd");
+}
 
 void dynamicServer(char* rootDir)
 /* dynamic server for inetd. Read query from stdin, open index, query, respond, exit.
  * only one query at a time */
 {
+// make sure error is logged, protocol doesn't allow reporting error to user
+pushWarnHandler(dynWarnErrorVa);
+
 char *command, *genomeName;
 int qSize;
 dynReadQuery(&command, &qSize, &genomeName);
 
 boolean isTrans = sameString("protQuery", command) || sameString("transQuery", command);
 boolean queryIsProt = sameString(command, "protQuery");
 
 char seqFile[PATH_LEN];
 char *seqFiles[1] = {seqFile};  // functions expect list of files
 char gfIdxFile[PATH_LEN];
 dynGetDataFiles(rootDir, genomeName, isTrans, seqFiles[0], gfIdxFile);
 
 
 struct genoFindIndex *gfIdx = genoFindIndexLoad(gfIdxFile, isTrans);
 mustWriteFd(STDOUT_FILENO, "Y", 1);
 
 struct dnaSeq* seq = dynReadQuerySeq(qSize, isTrans, queryIsProt);
 if (isTrans)
     {
     if (queryIsProt)
         transQuery(gfIdx->transGf, seq, STDOUT_FILENO);
     else
         transTransQuery(gfIdx->transGf, seq, STDOUT_FILENO);
     }
 else
     {
     struct hash *perSeqMaxHash = maybePerSeqMax(1, seqFiles);
     dnaQuery(gfIdx->untransGf, seq, STDOUT_FILENO, perSeqMaxHash);
     }
-logDebug("query done");
 netSendString(STDOUT_FILENO, "end");
+dynWriteTrailer(gfIdx);
+logDebug("query done");
 }
+
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 char *command;
 
 gfCatchPipes();
 dnaUtilOpen();
 optionInit(&argc, argv, optionSpecs);
 command = argv[1];
 if (optionExists("trans"))
     {
     doTrans = TRUE;
     tileSize = 4;
     minMatch = 3;
     maxGap = 0;