f57fc11d951807e19b20b5960e735fa50eaea279
braney
  Fri Jun 12 13:10:00 2026 -0700
Fix more warnings exposed by -O3 build across hg utils and errAbort

Continuation of the -O3 cleanup: a full clean rebuild surfaced warnings in
many programs whose objects had not been recompiled before.  Most are the same
mechanical patterns as the first batch (strncpy -> safecpy/memcpy, sprintf ->
safef), plus a few that needed more thought:

- errAbort.h: mark errAbort/vaErrAbort/errnoAbort/noWarnAbort as noreturn.
They provably never return (longjmp or exit; the existing "to make compiler
happy" exit(-1) in noWarnAbort confirms the intent), and this lets GCC prune
the impossible null paths after an errAbort guard, fixing false-positive
null-deref / overread warnings in mafAddIRows, mafAddIRowsStream and
phyloPlace with no source change to those files.
- altSplice.c (hgGene): real one-element stack buffer overflow.  makeGrayShades
writes shadesOfGray[maxShade+1], but the caller declared shadesOfGray[9] with
maxShade=8.  Grow the array to [10] (maxShade stays 8); behavior unchanged.
- hgc.c bedPrintPos: ~60 callers pass a track-specific struct cast to
(struct bed *) and read only its bed-compatible leading fields.  At -O3
-Warray-bounds flags the casts because the real object is smaller than
struct bed; the reads are safe by the bed-layout convention, so suppress
-Warray-bounds around just that function.
- mafsInRegion.c: chromFromSrc returns strchr(src,'.')+1, which GCC mis-sizes
as a 0-byte region when handed to strcmp via sameString/differentString;
suppress the false-positive -Wstringop-overread around extractMafs.
- sanger22gtf.c, bottleneck.c: put the printf/fprintf in the else of the
NULL guard so -Wformat-overflow (which runs before the noreturn-based
pruning) can see the argument is non-null.

safecpy/memcpy/safef conversions: basicBed already done earlier; here
haplotypes (memcpy of the original pointer pun), gbToFaRa, motifSig,
hgClonePos, featureBits, libScan, hgGoldGapGl, hgSoftPromoter, mafClick,
mafAddQRows, hgc.c, stanToBedAndExpRecs, bedUp, faSplit, trfBig,
splitFaIntoContigs, aladdin, ameme.

A full clean tree now builds with no warnings at -O3.

refs #37761

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c
index bc3905e0bc2..5c0bfcd230a 100644
--- src/hg/hgc/hgc.c
+++ src/hg/hgc/hgc.c
@@ -802,57 +802,64 @@
 void samplePrintPos(struct sample *smp, int smpSize)
 /* Print first three fields of a sample 9 type structure in
  * standard format. */
 {
 if ( smpSize != 9 )
     errAbort("Invalid sample entry!\n It has %d fields instead of 9\n",
              smpSize);
 
 printf("<B>Item:</B> %s<BR>\n", smp->name);
 printf("<B>Score:</B> %d<BR>\n", smp->score);
 printf("<B>Strand:</B> %s<BR>\n", smp->strand);
 printPos(smp->chrom, smp->chromStart, smp->chromEnd, NULL, TRUE, smp->name);
 }
 
 
+// Many callers pass a track-specific struct cast to (struct bed *), relying on
+// its bed-compatible leading fields (only the first bedSize fields are read).
+// At -O3 GCC's -Warray-bounds flags those casts because the real object is
+// smaller than struct bed; the accesses are safe by the bed-layout convention.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
 void bedPrintPos(struct bed *bed, int bedSize, struct trackDb *tdb)
 /* Print first bedSize fields of a bed type structure in
  * standard format. */
 {
 char *strand = NULL;
 if (bedSize >= 4 && bed->name[0] != 0)
     {
     char *label = "Item", *tdbLabel = NULL;
     if (tdb && ((tdbLabel = trackDbSetting(tdb, "bedNameLabel")) != NULL))
 	label = tdbLabel;
     printf("<B>%s:</B> %s<BR>\n", label, bed->name);
     }
 if (bedSize >= 5)
     {
     if (!tdb || !trackDbSetting(tdb, "noScoreFilter"))
         {
         char *scoreLabel = trackDbSettingOrDefault(tdb, "scoreLabel", "Score");
 	printf("<B>%s:</B> %d<BR>\n", scoreLabel, bed->score);
         }
     }
 if (bedSize >= 6)
    {
    strand = bed->strand;
    }
 printPos(bed->chrom, bed->chromStart, bed->chromEnd, strand, TRUE, bed->name);
 
 }
+#pragma GCC diagnostic pop
 
 void genericHeader(struct trackDb *tdb, char *item)
 /* Put up generic track info. */
 {
 if (item != NULL && item[0] != 0)
     cartWebStart(cart, database, "%s: %s (%s)", genome, tdb->longLabel, item);
 else
     cartWebStart(cart, database, "%s: %s", genome, tdb->longLabel);
 
 // QA noticed that clicking the +- buttons to collapse item detail tables was
 // generating messages in the Apache log if you went directly to an item page
 // without first visiting hgTracks. Clicking those buttons causes a cartDump
 // in order to save the state of visibility of the table, which in
 // turn needs an hgsid in order to save the state correctly. However, because
 // we aren't in a form, we have never saved the hgsid to a hidden
@@ -13896,31 +13903,31 @@
 printAlignments(pslList, start, "htcCdnaAli", "HInvGeneMrna", item);
 
 printTrackHtml(tdb);
 hFreeConn(&conn);
 }
 
 char *getGi(char *ncbiFaHead)
 /* Get GI number from NCBI FA format header. */
 {
 char *s;
 static char gi[64];
 
 if (!startsWith("gi|", ncbiFaHead))
     return NULL;
 ncbiFaHead += 3;
-strncpy(gi, ncbiFaHead, sizeof(gi));
+safecpy(gi, sizeof(gi), ncbiFaHead);
 s = strchr(gi, '|');
 if (s != NULL)
     *s = 0;
 return trimSpaces(gi);
 }
 
 void showSAM_T02(char *itemName)
 {
 char query2[256];
 struct sqlResult *sr2;
 char **row2;
 struct sqlConnection *conn2 = hAllocConn(database);
 char cond_str[256];
 char *predFN;
 char *homologID;
@@ -15841,37 +15848,37 @@
     sqlSafef(query, sizeof(query),
              "select * from %s where name = '%s' and chrom = '%s' ",
              table, name, qChrom
              );
     sr = sqlGetResult(conn2, query);
     if ((row = sqlNextRow(sr)) != NULL)
         gp = genePredLoad(row + hasBin);
     sqlFreeResult(&sr);
     }
 if (gp == NULL)
     errAbort("htcPseudoGene: Could not locate gene prediction (db=%s, table=%s, name=%s, in range %s:%d-%d) %s",
              db2, table, name, qChrom, qStart+1, qEnd, query);
 
 /* extract nib directory from nibfile */
 if (strrchr(nibFile,'/') != NULL)
-    strncpy(tNibDir, nibFile, strlen(nibFile)-strlen(strrchr(nibFile,'/')));
+    memcpy(tNibDir, nibFile, strlen(nibFile)-strlen(strrchr(nibFile,'/')));
 else
     errAbort("Cannot find nib directory for %s\n",nibFile);
 tNibDir[strlen(nibFile)-strlen(strrchr(nibFile,'/'))] = '\0';
 
 if (strrchr(qNibFile,'/') != NULL)
-    strncpy(qNibDir, qNibFile, strlen(qNibFile)-strlen(strrchr(qNibFile,'/')));
+    memcpy(qNibDir, qNibFile, strlen(qNibFile)-strlen(strrchr(qNibFile,'/')));
 else
     errAbort("Cannot find nib directory for %s\n",qNibFile);
 qNibDir[strlen(qNibFile)-strlen(strrchr(qNibFile,'/'))] = '\0';
 
 safef(path, sizeof path, "%s/%s.nib", tNibDir, chrom);
 
 /* load chain */
 if (sameString(database,db2))
     {
     track = "selfChain";
     if (!hTableExists(database, "chr1_selfChain"))
         track = "chainSelf";
     }
 else
     {
@@ -25067,31 +25074,31 @@
 char *table = tdb->table;
 struct dvBed dvBed;
 struct dv *dv;
 struct dvXref2 *dvXref2;
 struct omimTitle *omimTitle;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr, *sr2, *sr3, *sr4;
 char **row;
 char query[256], query2[256], query3[256], query4[256];
 
 int rowOffset = hOffsetPastBin(database, seqName, table);
 int start = cartInt(cart, "o");
 
 genericHeader(tdb, itemName);
 
-printf("<B>Item:</B> %s <BR>\n", itemName);
+printf("<B>Item:</B> %s <BR>\n", naForNull(itemName));
 printf("<B>Outside Link:</B> ");
 printf("<A HREF=");
 printSwissProtVariationUrl(stdout, itemName);
 printf(" Target=_blank> %s </A> <BR>\n", itemName);
 
 sqlSafef(query, sizeof(query),
       "select * from %s where chrom = '%s' and "
       "chromStart=%d and name = '%s'", table, seqName, start, itemName);
 sr = sqlGetResult(conn, query);
 while ((row = sqlNextRow(sr)) != NULL)
     {
     dvBedStaticLoad(row+rowOffset, &dvBed);
     bedPrintPos((struct bed *)&dvBed, 3, tdb);
     }
 sqlFreeResult(&sr);