Skip to content

Commit ff1604d

Browse files
committed
Release 1.23.1
2 parents 56e72e4 + db566e7 commit ff1604d

50 files changed

Lines changed: 277 additions & 216 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ endif
109109

110110
include config.mk
111111

112-
PACKAGE_VERSION = 1.23
112+
PACKAGE_VERSION = 1.23.1
113113

114114
# If building from a Git repository, replace $(PACKAGE_VERSION) with the Git
115115
# description of the working tree: either a release tag with the same value
@@ -231,7 +231,7 @@ bcftools_h = bcftools.h $(htslib_hts_defs_h) $(htslib_vcf_h) $(htslib_synced_bcf
231231
call_h = call.h $(htslib_vcf_h) $(htslib_synced_bcf_reader_h) vcmp.h
232232
variantkey_h = variantkey.h hex.h
233233
convert_h = convert.h $(htslib_vcf_h)
234-
tsv2vcf_h = tsv2vcf.h $(htslib_vcf_h)
234+
tsv2vcf_h = tsv2vcf.h $(htslib_vcf_h) $(bcftools_h)
235235
filter_h = filter.h $(htslib_vcf_h)
236236
gvcf_h = gvcf.h $(bcftools_h)
237237
khash_str2str_h = khash_str2str.h $(htslib_khash_h)

NEWS

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
## Release 1.23.1 (18th March 2026)
2+
3+
4+
Changes affecting the whole of bcftools, or multiple commands:
5+
6+
* This release bundles HTSlib 1.23.1, which includes many important
7+
bug fixes. Please see htslib/NEWS for details.
8+
9+
* Fix silent output truncation due to missing checks for read errors in
10+
the annotate, cnv, concat, convert, consensus, csq, filter, gtcheck,
11+
isec, merge, norm, query and stats commands; and all plugins. They
12+
will now print an error and return a non-zero exit code if an error
13+
is encountered when reading input data. (#2503)
14+
115
## Release 1.23 (16th December 2025)
216

317

abuf.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ static inline int _is_acgtn(char *seq)
721721
{
722722
while ( *seq )
723723
{
724-
char c = toupper(*seq);
724+
char c = toupper_c(*seq);
725725
if ( c!='A' && c!='C' && c!='G' && c!='T' && c!='N' ) return 0;
726726
seq++;
727727
}

bam2bcf_indel.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ DEALINGS IN THE SOFTWARE. */
3636
#include <htslib/ksort.h>
3737
KSORT_INIT_GENERIC(uint32_t)
3838

39+
// Avoid having to include all of bcftools.h
40+
static inline char toupper_c(char c) { return toupper((unsigned char) c); }
41+
3942
#define MINUS_CONST 0x10000000
4043

4144
#define MAX_TYPES 64
@@ -89,8 +92,8 @@ inline int est_indelreg(int pos, const char *ref, int l, char *ins4)
8992
int i, j, max = 0, max_i = pos, score = 0;
9093
l = abs(l);
9194
for (i = pos + 1, j = 0; ref[i]; ++i, ++j) {
92-
if (ins4) score += (toupper(ref[i]) != "ACGTN"[(int)ins4[j%l]])? -10 : 1;
93-
else score += (toupper(ref[i]) != toupper(ref[pos+1+j%l]))? -10 : 1;
95+
if (ins4) score += (toupper_c(ref[i]) != "ACGTN"[(int)ins4[j%l]])? -10 : 1;
96+
else score += (toupper_c(ref[i]) != toupper_c(ref[pos+1+j%l]))? -10 : 1;
9497
if (score < 0) break;
9598
if (max < score) max = score, max_i = i;
9699
}

bam_sample.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -295,18 +295,18 @@ int bam_smpl_add_samples(bam_smpl_t *bsmpl, char *list, int is_file)
295295
while ( *ptr )
296296
{
297297
if ( *ptr=='\\' && !escaped ) { escaped = 1; ptr++; continue; }
298-
if ( isspace(*ptr) && !escaped ) break;
298+
if ( isspace_c(*ptr) && !escaped ) break;
299299
kputc(*ptr, &ori);
300300
escaped = 0;
301301
ptr++;
302302
}
303303
if ( *ptr )
304304
{
305-
while ( *ptr && isspace(*ptr) ) ptr++;
305+
while ( *ptr && isspace_c(*ptr) ) ptr++;
306306
while ( *ptr )
307307
{
308308
if ( *ptr=='\\' && !escaped ) { escaped = 1; ptr++; continue; }
309-
if ( isspace(*ptr) && !escaped ) break;
309+
if ( isspace_c(*ptr) && !escaped ) break;
310310
kputc(*ptr, &ren);
311311
escaped = 0;
312312
ptr++;
@@ -343,30 +343,30 @@ int bam_smpl_add_readgroups(bam_smpl_t *bsmpl, char *list, int is_file)
343343
while ( *ptr )
344344
{
345345
if ( *ptr=='\\' && !escaped ) { escaped = 1; ptr++; continue; }
346-
if ( isspace(*ptr) && !escaped ) break;
346+
if ( isspace_c(*ptr) && !escaped ) break;
347347
kputc(*ptr, &fld1);
348348
escaped = 0;
349349
ptr++;
350350
}
351351
if ( *ptr )
352352
{
353-
while ( *ptr && isspace(*ptr) ) ptr++;
353+
while ( *ptr && isspace_c(*ptr) ) ptr++;
354354
while ( *ptr )
355355
{
356356
if ( *ptr=='\\' && !escaped ) { escaped = 1; ptr++; continue; }
357-
if ( isspace(*ptr) && !escaped ) break;
357+
if ( isspace_c(*ptr) && !escaped ) break;
358358
kputc(*ptr, &fld2);
359359
escaped = 0;
360360
ptr++;
361361
}
362362
}
363363
if ( *ptr )
364364
{
365-
while ( *ptr && isspace(*ptr) ) ptr++;
365+
while ( *ptr && isspace_c(*ptr) ) ptr++;
366366
while ( *ptr )
367367
{
368368
if ( *ptr=='\\' && !escaped ) { escaped = 1; ptr++; continue; }
369-
if ( isspace(*ptr) && !escaped ) break;
369+
if ( isspace_c(*ptr) && !escaped ) break;
370370
kputc(*ptr, &fld3);
371371
escaped = 0;
372372
ptr++;

bcftools.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ THE SOFTWARE. */
3131
#include <htslib/synced_bcf_reader.h>
3232
#include <htslib/kfunc.h>
3333
#include <math.h>
34+
#include <ctype.h>
3435

3536
#define FT_TAB_TEXT 0 // custom tab-delimited text file
3637
#define FT_GZ 1
@@ -184,4 +185,20 @@ static inline int get_unseen_allele(bcf1_t *line)
184185
return 0;
185186
}
186187

188+
// <ctype.h> wrappers, borrowed from htslib's textutils_internal.h
189+
// The <ctype.h> functions operate on ints such as are returned by fgetc(),
190+
// i.e., characters represented as unsigned-char-valued ints, or EOF.
191+
// To operate on plain chars (and to avoid warnings on some platforms),
192+
// technically one must cast to unsigned char everywhere (see CERT STR37-C)
193+
// or less painfully use these *_c() functions that operate on plain chars
194+
// (but not EOF, which must be considered separately where it is applicable).
195+
static inline int isalnum_c(char c) { return isalnum((unsigned char) c); }
196+
static inline int isalpha_c(char c) { return isalpha((unsigned char) c); }
197+
static inline int isdigit_c(char c) { return isdigit((unsigned char) c); }
198+
static inline int isprint_c(char c) { return isprint((unsigned char) c); }
199+
static inline int ispunct_c(char c) { return ispunct((unsigned char) c); }
200+
static inline int isspace_c(char c) { return isspace((unsigned char) c); }
201+
static inline char tolower_c(char c) { return tolower((unsigned char) c); }
202+
static inline char toupper_c(char c) { return toupper((unsigned char) c); }
203+
187204
#endif

configure.ac

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,8 @@ AS_IF([test "$enable_perl_filters" != "no" ], [dnl
247247
fi
248248
AS_CASE([$PERL_LIBS],
249249
[*redhat/redhat-hardened-ld*],[[
250-
PERL_CCOPTS="`echo $PERL_CCOPTS | sed 's,-specs=[a-z/]*/redhat/redhat-hardened-[a-z0-9]* *,,g'`"
251-
PERL_LIBS="`echo $PERL_LIBS | sed 's,-specs=[a-z/]*/redhat/redhat-hardened-[a-z0-9]* *,,g'`"]
250+
PERL_CCOPTS="`echo $PERL_CCOPTS | sed 's,-specs=[a-z/]*/redhat/redhat-hardened-[a-z0-9-]* *,,g'`"
251+
PERL_LIBS="`echo $PERL_LIBS | sed 's,-specs=[a-z/]*/redhat/redhat-hardened-[a-z0-9-]* *,,g'`"]
252252
AC_MSG_NOTICE([removing -specs=...redhat-hardened... from Perl options])])
253253
AC_SUBST([PERL_CCOPTS])
254254
AC_SUBST([PERL_LIBS])

consensus.c

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ static void destroy_data(args_t *args)
339339
static void init_region(args_t *args, char *line)
340340
{
341341
char *ss, *se = line;
342-
while ( *se && !isspace(*se) && *se!=':' ) se++;
342+
while ( *se && !isspace_c(*se) && *se!=':' ) se++;
343343
hts_pos_t from = 0, to = 0;
344344
char tmp = 0, *tmp_ptr = NULL;
345345
if ( *se )
@@ -353,7 +353,7 @@ static void init_region(args_t *args, char *line)
353353
from--;
354354
ss = ++se;
355355
to = strtol(ss,&se,10);
356-
if ( ss==se || (*se && !isspace(*se)) ) { from = 0; to = 0; }
356+
if ( ss==se || (*se && !isspace_c(*se)) ) { from = 0; to = 0; }
357357
else to--;
358358
}
359359
}
@@ -411,6 +411,7 @@ static bcf1_t **next_vcf_line(args_t *args)
411411
}
412412
return &args->files->readers[0].buffer[0];
413413
}
414+
if ( args->files->errnum ) error("Error: %s\n", bcf_sr_strerror(args->files->errnum));
414415
return NULL;
415416
}
416417
static void unread_vcf_line(args_t *args, bcf1_t **rec_ptr)
@@ -500,9 +501,9 @@ static void mark_ins(char *ref, char *alt, char mark)
500501
{
501502
int i, nref = strlen(ref), nalt = strlen(alt);
502503
if ( mark==TO_LOWER )
503-
for (i=nref; i<nalt; i++) alt[i] = tolower(alt[i]);
504+
for (i=nref; i<nalt; i++) alt[i] = tolower_c(alt[i]);
504505
else if ( mark==TO_UPPER )
505-
for (i=nref; i<nalt; i++) alt[i] = toupper(alt[i]);
506+
for (i=nref; i<nalt; i++) alt[i] = toupper_c(alt[i]);
506507
else if ( mark )
507508
for (i=nref; i<nalt; i++) alt[i] = mark;
508509
}
@@ -513,22 +514,17 @@ static void mark_snv(char *ref, char *alt, char mark)
513514
if ( mark==TO_LOWER )
514515
{
515516
for (i=0; i<n; i++)
516-
if ( tolower(ref[i])!=tolower(alt[i]) ) alt[i] = tolower(alt[i]);
517+
if ( tolower_c(ref[i])!=tolower_c(alt[i]) ) alt[i] = tolower_c(alt[i]);
517518
}
518519
else if ( mark==TO_UPPER)
519520
{
520521
for (i=0; i<n; i++)
521-
if ( tolower(ref[i])!=tolower(alt[i]) ) alt[i] = toupper(alt[i]);
522-
}
523-
else if ( mark==TO_UPPER)
524-
{
525-
for (i=0; i<n; i++)
526-
if ( tolower(ref[i])!=tolower(alt[i]) ) alt[i] = toupper(alt[i]);
522+
if ( tolower_c(ref[i])!=tolower_c(alt[i]) ) alt[i] = toupper_c(alt[i]);
527523
}
528524
else if ( mark )
529525
{
530526
for (i=0; i<n; i++)
531-
if ( tolower(ref[i])!=tolower(alt[i]) ) alt[i] = mark;
527+
if ( tolower_c(ref[i])!=tolower_c(alt[i]) ) alt[i] = mark;
532528
}
533529
}
534530
static void iupac_init(args_t *args, bcf1_t *rec)
@@ -943,7 +939,7 @@ static void apply_variant(args_t *args, bcf1_t *rec)
943939
// one base overlap
944940

945941
int fail = 1;
946-
if ( args->prev_base_pos==rec->pos && toupper(ref_allele[0])==toupper(args->prev_base) )
942+
if ( args->prev_base_pos==rec->pos && toupper_c(ref_allele[0])==toupper_c(args->prev_base) )
947943
{
948944
if ( rec->rlen==1 ) fail = 0;
949945
else if ( !strncasecmp(ref_allele+1,args->fa_buf.s+idx+1,rec->rlen-1) ) fail = 0;
@@ -992,11 +988,11 @@ static void apply_variant(args_t *args, bcf1_t *rec)
992988
}
993989

994990
int safe_idx = idx<0 ? 0 : idx; // idx can be negative in case of overlapping deletion
995-
args->fa_case = toupper(args->fa_buf.s[safe_idx])==args->fa_buf.s[safe_idx] ? TO_UPPER : TO_LOWER;
991+
args->fa_case = toupper_c(args->fa_buf.s[safe_idx])==args->fa_buf.s[safe_idx] ? TO_UPPER : TO_LOWER;
996992
if ( args->fa_case==TO_UPPER )
997-
for (i=0; i<alen; i++) alt_allele[i] = toupper(alt_allele[i]);
993+
for (i=0; i<alen; i++) alt_allele[i] = toupper_c(alt_allele[i]);
998994
else
999-
for (i=0; i<alen; i++) alt_allele[i] = tolower(alt_allele[i]);
995+
for (i=0; i<alen; i++) alt_allele[i] = tolower_c(alt_allele[i]);
1000996

1001997
if ( args->mark_ins && len_diff>0 )
1002998
mark_ins(ref_allele, alt_allele, args->mark_ins);
@@ -1080,9 +1076,9 @@ static void mask_region(args_t *args, char *seq, int len)
10801076
if ( idx_start < 0 ) idx_start = 0;
10811077
if ( idx_end >= len ) idx_end = len - 1;
10821078
if ( mask->with==MASK_UC )
1083-
for (j=idx_start; j<=idx_end; j++) seq[j] = toupper(seq[j]);
1079+
for (j=idx_start; j<=idx_end; j++) seq[j] = toupper_c(seq[j]);
10841080
else if ( mask->with==MASK_LC )
1085-
for (j=idx_start; j<=idx_end; j++) seq[j] = tolower(seq[j]);
1081+
for (j=idx_start; j<=idx_end; j++) seq[j] = tolower_c(seq[j]);
10861082
else
10871083
for (j=idx_start; j<=idx_end; j++) seq[j] = mask->with;
10881084
}
@@ -1124,7 +1120,7 @@ static void consensus(args_t *args)
11241120
args->fa_src_pos += str.l;
11251121

11261122
// determine if uppercase or lowercase is used in this fasta file
1127-
if ( args->fa_case==-1 ) args->fa_case = toupper(str.s[0])==str.s[0] ? 1 : 0;
1123+
if ( args->fa_case==-1 ) args->fa_case = toupper_c(str.s[0])==str.s[0] ? 1 : 0;
11281124

11291125
if ( args->mask ) mask_region(args, str.s, str.l);
11301126
kputs(str.s, &args->fa_buf);

convert.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,7 +1461,7 @@ static int parse_subscript(char **p)
14611461
char *q = *p;
14621462
if ( *q!='{' ) return -1;
14631463
q++;
1464-
while ( *q && *q!='}' && isdigit(*q) ) q++;
1464+
while ( *q && *q!='}' && isdigit_c(*q) ) q++;
14651465
if ( *q!='}' ) return -1;
14661466
int idx = atoi((*p)+1);
14671467
*p = q+1;
@@ -1474,7 +1474,7 @@ static char *parse_tag(convert_t *convert, char *p, int is_gtf)
14741474
if ( is_vcf_column ) p++;
14751475

14761476
char *q = ++p;
1477-
while ( *q && (isalnum(*q) || *q=='_' || *q=='.') ) q++;
1477+
while ( *q && (isalnum_c(*q) || *q=='_' || *q=='.') ) q++;
14781478
kstring_t str = {0,0,0};
14791479
if ( q-p==0 ) error("Could not parse format string: %s\n", convert->format_str);
14801480
kputsn(p, q-p, &str);
@@ -1517,7 +1517,7 @@ static char *parse_tag(convert_t *convert, char *p, int is_gtf)
15171517
}
15181518
p = ++q;
15191519
str.l = 0;
1520-
while ( *q && (isalnum(*q) || *q=='_' || *q=='.') ) q++;
1520+
while ( *q && (isalnum_c(*q) || *q=='_' || *q=='.') ) q++;
15211521
if ( q-p==0 ) error("Could not parse format string: %s\n", convert->format_str);
15221522
kputsn(p, q-p, &str);
15231523
fmt_t *fmt = register_tag(convert, str.s, is_gtf, T_INFO);
@@ -1567,7 +1567,7 @@ static char *parse_tag(convert_t *convert, char *p, int is_gtf)
15671567
{
15681568
p = ++q;
15691569
str.l = 0;
1570-
while ( *q && (isalnum(*q) || *q=='_' || *q=='.') ) q++;
1570+
while ( *q && (isalnum_c(*q) || *q=='_' || *q=='.') ) q++;
15711571
if ( q-p==0 ) error("Could not parse format string: %s\n", convert->format_str);
15721572
kputsn(p, q-p, &str);
15731573
fmt_t *fmt = register_tag(convert, str.s, is_gtf, T_INFO);

csq.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2844,7 +2844,7 @@ static int sanity_check_ref(args_t *args, gf_tscript_t *tr, bcf1_t *rec)
28442844
int i = 0;
28452845
while ( ref[i] && vcf[i] )
28462846
{
2847-
if ( ref[i]!=vcf[i] && toupper(ref[i])!=toupper(vcf[i]) )
2847+
if ( ref[i]!=vcf[i] && toupper_c(ref[i])!=toupper_c(vcf[i]) )
28482848
{
28492849
if ( !args->force )
28502850
error("Error: the fasta reference does not match the VCF REF allele at %s:%"PRId64" .. fasta=%c vcf=%c\n",
@@ -3918,6 +3918,7 @@ int main_csq(int argc, char *argv[])
39183918
{
39193919
process(args, &args->sr->readers[0].buffer[0]);
39203920
}
3921+
if ( args->sr->errnum ) error("Error: %s\n", bcf_sr_strerror(args->sr->errnum));
39213922
process(args,NULL);
39223923

39233924
destroy_data(args);

0 commit comments

Comments
 (0)