fb50a1cca3a117bb964c43196671142484b13bf9
braney
  Fri Jun 12 12:10:47 2026 -0700
lib jkOwnLib hg/lib: fix warnings exposed by -O3 build

At -O3 GCC does more inlining and interprocedural range analysis, which
surfaces several warnings that -O -g never triggered.  With -Werror these
break the build, so fix them ahead of switching the default to -O3.

- bamFile.c: move setenv() into the else of its NULL guard so it is not
reachable with a NULL argument (-Wnonnull, seen via inlining of bamFetch).
- basicBed.c, wormdna.c, bedDetail.c, customFactory.c, hdb.c, qaSeq.c:
replace strncpy() with safecpy() where the result must be terminated
(-Wstringop-truncation).
- htmshell.c: use memcpy() for the intentional exact-length, non-terminated
copy (-Wstringop-truncation).
- obscure.c: compute the comma groups in sprintLongWithCommas() with % 1000
so the compiler can see each %03lld argument is bounded (-Wformat-overflow).
- crudeali.c: do the endian type-pun through a union so strict-aliasing
analysis no longer reports the value as uninitialized (-Wuninitialized).
- jksql.c: wrap a possibly-NULL database name in naForNull() before passing
it to %s (-Wformat-overflow).

refs #37761

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

diff --git src/hg/lib/customFactory.c src/hg/lib/customFactory.c
index 43298a48155..58dfe192696 100644
--- src/hg/lib/customFactory.c
+++ src/hg/lib/customFactory.c
@@ -644,31 +644,31 @@
 if (bed->chromEnd < 1)
     lineFileAbort(lf, "chromEnd less than 1 (%d)", bed->chromEnd);
 if (bed->chromEnd < bed->chromStart)
     lineFileAbort(lf, "chromStart after chromEnd (%d > %d)",
     	bed->chromStart, bed->chromEnd);
 int chromSize = hChromSize(db, bed->chrom);
 if (bed->chromEnd > chromSize)
     lineFileAbort(lf, "chromEnd larger than chrom %s size (%d > %d)",
     	bed->chrom, bed->chromEnd, chromSize);
 if (wordCount > 3)
      bed->name = cloneString(row[3]);
 if (wordCount > 4)
      bed->score = lineFileNeedNum(lf, row, 4);
 if (wordCount > 5)
      {
-     strncpy(bed->strand, row[5], sizeof(bed->strand));
+     safecpy(bed->strand, sizeof(bed->strand), row[5]);
      if (bed->strand[0] != '+' && bed->strand[0] != '-' && bed->strand[0] != '.')
 	  lineFileAbort(lf, "Expecting + or - in strand");
      }
 if (wordCount > 6)
      bed->thickStart = lineFileNeedNum(lf, row, 6);
 else
      bed->thickStart = bed->chromStart;
 if (wordCount > 7)
      {
      bed->thickEnd = lineFileNeedNum(lf, row, 7);
      if (bed->thickEnd < bed->thickStart)
 	 lineFileAbort(lf, "thickStart after thickEnd");
      if ((bed->thickStart != 0) &&
 	 ((bed->thickStart < bed->chromStart) ||
 	  (bed->thickStart > bed->chromEnd)))
@@ -1969,31 +1969,31 @@
 if (count <= 0)
     return defaultScore;
 
 /* Calculate and return average score. */
 for (line = group->lineList; line != NULL; line = line->next)
     cumScore += line->score;
 return cumScore / count;
 }
 
 static char *niceGeneName(char *name)
 /* Return a nice version of name. */
 {
 static char buf[128];
 char *e;
 
-strncpy(buf, name, sizeof(buf));
+safecpy(buf, sizeof(buf), name);
 if ((e = strchr(buf, ';')) != NULL)
     *e = 0;
 eraseWhiteSpace(buf);
 stripChar(buf, '%');
 stripChar(buf, '\'');
 stripChar(buf, '"');
 return buf;
 }
 
 static struct bed *gffHelperFinish(struct gffFile *gff, struct hash *chromHash)
 /* Create bedList from gffHelper. */
 {
 struct genePred *gp;
 struct bed *bedList = NULL, *bed;
 struct gffGroup *group;