Skip to content

Commit fc9aeb6

Browse files
committed
Add internal isdigit_c()/etc functions for plain chars
Using `char c; ... isdigit(c)` is technically incorrect (as the <ctype.h> functions expect an int such as is returned by fgetc()) and produces a warning on Windows. See also CERT STR37-C's explanation. Add our own *_c() functions operating on plain chars to hts_internal.h, which also allows us to in future reimplement them directly to make them immune to locales. Use *_c() instead of casting to unsigned char in hts.c and vcf.c.
1 parent eb3481f commit fc9aeb6

4 files changed

Lines changed: 26 additions & 4 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ hfile_irods.o hfile_irods.pico: hfile_irods.c config.h $(hfile_internal_h) $(hts
258258
hfile_libcurl.o hfile_libcurl.pico: hfile_libcurl.c config.h $(hfile_internal_h) $(htslib_hts_h) $(htslib_kstring_h)
259259
hfile_net.o hfile_net.pico: hfile_net.c config.h $(hfile_internal_h) $(htslib_knetfile_h)
260260
hts.o hts.pico: hts.c config.h version.h $(htslib_hts_h) $(htslib_bgzf_h) $(cram_h) $(htslib_hfile_h) $(htslib_khash_h) $(htslib_kseq_h) $(htslib_ksort_h) $(hts_internal_h)
261-
vcf.o vcf.pico: vcf.c config.h $(htslib_vcf_h) $(htslib_bgzf_h) $(htslib_tbx_h) $(htslib_hfile_h) $(htslib_khash_h) $(htslib_kseq_h) $(htslib_kstring_h) $(htslib_khash_str2int_h)
261+
vcf.o vcf.pico: vcf.c config.h $(htslib_vcf_h) $(htslib_bgzf_h) $(htslib_tbx_h) $(htslib_hfile_h) $(hts_internal_h) $(htslib_khash_h) $(htslib_kseq_h) $(htslib_kstring_h) $(htslib_khash_str2int_h)
262262
sam.o sam.pico: sam.c config.h $(htslib_sam_h) $(htslib_bgzf_h) $(cram_h) $(hts_internal_h) $(htslib_hfile_h) $(htslib_khash_h) $(htslib_kseq_h) $(htslib_kstring_h)
263263
tbx.o tbx.pico: tbx.c config.h $(htslib_tbx_h) $(htslib_bgzf_h) $(htslib_khash_h)
264264
faidx.o faidx.pico: faidx.c config.h $(htslib_bgzf_h) $(htslib_faidx_h) $(htslib_hfile_h) $(htslib_khash_h) $(htslib_kstring_h)

hts.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ scan_keyword(const char *str, char delim, char *buf, size_t buflen)
431431
{
432432
size_t i = 0;
433433
while (*str && *str != delim) {
434-
if (i < buflen-1) buf[i++] = tolower((unsigned char) *str);
434+
if (i < buflen-1) buf[i++] = tolower_c(*str);
435435
str++;
436436
}
437437

hts_internal.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* hts_internal.h -- internal functions; not part of the public API.
22
3-
Copyright (C) 2015 Genome Research Ltd.
3+
Copyright (C) 2015-2016 Genome Research Ltd.
44
55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal
@@ -24,13 +24,34 @@ DEALINGS IN THE SOFTWARE. */
2424
#define HTSLIB_HTS_INTERNAL_H
2525

2626
#include <stddef.h>
27+
#include <ctype.h>
2728

2829
#include "htslib/hts.h"
2930

3031
#ifdef __cplusplus
3132
extern "C" {
3233
#endif
3334

35+
// The <ctype.h> functions operate on ints such as are returned by fgetc(),
36+
// i.e., characters represented as unsigned-char-valued ints, or EOF.
37+
// To operate on plain chars (and to avoid warnings on some platforms),
38+
// technically one must cast to unsigned char everywhere (see CERT STR37-C)
39+
// or less painfully use these *_c() functions that operate on plain chars
40+
// (but not EOF, which must be considered separately where it is applicable).
41+
// TODO We may eventually wish to implement these functions directly without
42+
// using their <ctype.h> equivalents, and thus make them immune to locales.
43+
static inline int isalnum_c(char c) { return isalnum((unsigned char) c); }
44+
static inline int isalpha_c(char c) { return isalpha((unsigned char) c); }
45+
static inline int isdigit_c(char c) { return isdigit((unsigned char) c); }
46+
static inline int isgraph_c(char c) { return isgraph((unsigned char) c); }
47+
static inline int islower_c(char c) { return islower((unsigned char) c); }
48+
static inline int isprint_c(char c) { return isprint((unsigned char) c); }
49+
static inline int isspace_c(char c) { return isspace((unsigned char) c); }
50+
static inline int isupper_c(char c) { return isupper((unsigned char) c); }
51+
static inline char tolower_c(char c) { return tolower((unsigned char) c); }
52+
static inline char toupper_c(char c) { return toupper((unsigned char) c); }
53+
54+
3455
struct cram_fd;
3556

3657
char *hts_idx_getfn(const char *fn, const char *ext);

vcf.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ DEALINGS IN THE SOFTWARE. */
3939
#include "htslib/tbx.h"
4040
#include "htslib/hfile.h"
4141
#include "htslib/khash_str2int.h"
42+
#include "hts_internal.h"
4243

4344
#include "htslib/khash.h"
4445
KHASH_MAP_INIT_STR(vdict, bcf_idinfo_t)
@@ -1741,7 +1742,7 @@ static int vcf_parse_format(kstring_t *s, const bcf_hdr_t *h, bcf1_t *v, char *p
17411742
t++;
17421743
}
17431744
else {
1744-
fprintf(stderr,"[E::%s] Invalid character '%c' in '%s' FORMAT field at %s:%d\n", __FUNCTION__, isprint((unsigned char) *t)? *t : '?', h->id[BCF_DT_ID][z->key].key, bcf_seqname(h,v), v->pos+1);
1745+
fprintf(stderr,"[E::%s] Invalid character '%c' in '%s' FORMAT field at %s:%d\n", __FUNCTION__, isprint_c(*t)? *t : '?', h->id[BCF_DT_ID][z->key].key, bcf_seqname(h,v), v->pos+1);
17451746
// TODO Set v->errcode appropriately
17461747
return -1;
17471748
}

0 commit comments

Comments
 (0)