diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 000000000..f90c61db9 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,43 @@ +# version format. +# you can use {branch} name in version format too +# version: 1.0.{build}-{branch} +version: 'vers.{build}' + +# branches to build +branches: + # Whitelist + only: + - develop + + # Blacklist + except: + - gh-pages + +# Do not build on tags (GitHub and BitBucket) +skip_tags: true + +# Skipping commits affecting specific files (GitHub only). More details here: /docs/appveyor-yml +#skip_commits: +# files: +# - docs/* +# - '**/*.html' + +# We use Mingw/Msys, so use pacman for installs +install: + - set HOME=. + - set MSYSTEM=MINGW64 + - set PATH=C:/msys64/usr/bin;C:/msys64/mingw64/bin;%PATH% + - set MINGWPREFIX=x86_64-w64-mingw32 + - "sh -lc \"pacman -S --noconfirm --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-zlib mingw-w64-x86_64-bzip2 mingw-w64-x86_64-xz mingw-w64-x86_64-curl\"" + +build_script: + - set HOME=. + - set MSYSTEM=MINGW64 + - set PATH=C:/msys64/usr/bin;C:/msys64/mingw64/bin;%PATH% + - "sh -lc \"aclocal && autoheader && autoconf && ./configure && make -j2\"" + +#build_script: +# - make + +test_script: + - "sh -lc \"make test\"" diff --git a/.gitattributes b/.gitattributes index 173dcd329..da0ea8308 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,3 +10,8 @@ .git* export-ignore /.travis.yml export-ignore README.md export-ignore + +# Remove the text attribute from reference files, so that git doesn't convert +# line separators on Windows machines. It causes the index files to become out +# of sync with the fasta files. +*.fa* -text diff --git a/.gitignore b/.gitignore index 6972b555e..6101b215b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ *.o *.pico +*.obj *.dSYM *.exe +*.dll *.pc.tmp *-uninstalled.pc /version.h diff --git a/INSTALL b/INSTALL index 2413ba6c2..8d9c3b6ec 100644 --- a/INSTALL +++ b/INSTALL @@ -48,6 +48,12 @@ library is used. Systems that do not have CChmac will get this from libcrypto. libcrypto is part of OpenSSL or one of its derivatives (LibreSSL or BoringSSL). +On Microsoft Windows we recommend use of Mingw64/Msys2. Note that +currently for the test harness to work you will need to override the +test temporary directory with e.g.: make check TEST_OPTS="-t C:/msys64/tmp/_" +Whilst the code may work on Windows with other environments, these have +not be verified. + Building Configure ================== diff --git a/Makefile b/Makefile index 9a44b6263..96dc99c43 100644 --- a/Makefile +++ b/Makefile @@ -148,6 +148,7 @@ LIBHTS_OBJS = \ hfile.o \ hfile_net.o \ hts.o \ + hts_os.o\ md5.o \ multipart.o \ probaln.o \ @@ -208,6 +209,7 @@ config.h: echo '/* Default config.h generated by Makefile */' > $@ echo '#define HAVE_LIBBZ2 1' >> $@ echo '#define HAVE_LIBLZMA 1' >> $@ + echo '#define HAVE_DRAND48 1' >> $@ # And similarly for htslib.pc.tmp ("pkg-config template"). No dependency # on htslib.pc.in listed, as if that file is newer the usual way to regenerate @@ -235,6 +237,9 @@ lib-shared: libhts.dylib else ifeq "$(findstring CYGWIN,$(PLATFORM))" "CYGWIN" SHLIB_FLAVOUR = cygdll lib-shared: cyghts-$(LIBHTS_SOVERSION).dll +else ifeq "$(findstring MSYS,$(PLATFORM))" "MSYS" +SHLIB_FLAVOUR = dll +lib-shared: hts-$(LIBHTS_SOVERSION).dll else SHLIB_FLAVOUR = so lib-shared: libhts.so @@ -276,6 +281,9 @@ libhts.dylib: $(LIBHTS_OBJS) cyghts-$(LIBHTS_SOVERSION).dll: $(LIBHTS_OBJS) $(CC) -shared -Wl,--out-implib=libhts.dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import $(LDFLAGS) -o $@ -Wl,--whole-archive $(LIBHTS_OBJS) -Wl,--no-whole-archive $(LIBS) -lpthread +hts-$(LIBHTS_SOVERSION).dll: $(LIBHTS_OBJS) + $(CC) -shared -Wl,--out-implib=hts.dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import $(LDFLAGS) -o $@ -Wl,--whole-archive $(LIBHTS_OBJS) -Wl,--no-whole-archive $(LIBS) -lpthread + .pico.so: $(CC) -shared -Wl,-E $(LDFLAGS) -o $@ $< $(LIBS) -lpthread @@ -286,6 +294,9 @@ cyghts-$(LIBHTS_SOVERSION).dll: $(LIBHTS_OBJS) .o.cygdll: $(CC) -shared $(LDFLAGS) -o $@ $< libhts.dll.a $(LIBS) +.o.dll: + $(CC) -shared $(LDFLAGS) -o $@ $< hts.dll.a $(LIBS) + bgzf.o bgzf.pico: bgzf.c config.h $(htslib_hts_h) $(htslib_bgzf_h) $(htslib_hfile_h) $(htslib_thread_pool_h) cram/pooled_alloc.h $(htslib_khash_h) errmod.o errmod.pico: errmod.c config.h $(htslib_hts_h) $(htslib_ksort_h) @@ -348,6 +359,9 @@ tabix.o: tabix.c config.h $(htslib_tbx_h) $(htslib_sam_h) $(htslib_vcf_h) $(htsl # For tests that might use it, set $REF_PATH explicitly to use only reference # areas within the test suite (or set it to ':' to use no reference areas). +# +# If using MSYS, avoid poor shell expansion via: +# MSYS2_ARG_CONV_EXCL="*" make check check test: $(BUILT_PROGRAMS) $(BUILT_TEST_PROGRAMS) test/hts_endian test/fieldarith test/fieldarith.sam @@ -356,7 +370,7 @@ check test: $(BUILT_PROGRAMS) $(BUILT_TEST_PROGRAMS) cd test/tabix && ./test-tabix.sh tabix.tst REF_PATH=: test/sam test/ce.fa test/faidx.fa test/test-regidx - cd test && REF_PATH=: ./test.pl + cd test && REF_PATH=: ./test.pl $${TEST_OPTS:-} test/hts_endian: test/hts_endian.o $(CC) $(LDFLAGS) -o $@ test/hts_endian.o $(LIBS) @@ -446,6 +460,10 @@ install-cygdll: cyghts-$(LIBHTS_SOVERSION).dll installdirs $(INSTALL_PROGRAM) cyghts-$(LIBHTS_SOVERSION).dll $(DESTDIR)$(bindir)/cyghts-$(LIBHTS_SOVERSION).dll $(INSTALL_PROGRAM) libhts.dll.a $(DESTDIR)$(libdir)/libhts.dll.a +install-dll: hts-$(LIBHTS_SOVERSION).dll installdirs + $(INSTALL_PROGRAM) hts-$(LIBHTS_SOVERSION).dll $(DESTDIR)$(bindir)/hts-$(LIBHTS_SOVERSION).dll + $(INSTALL_PROGRAM) hts.dll.a $(DESTDIR)$(libdir)/hts.dll.a + install-dylib: libhts.dylib installdirs $(INSTALL_PROGRAM) libhts.dylib $(DESTDIR)$(libdir)/libhts.$(PACKAGE_VERSION).dylib ln -sf libhts.$(PACKAGE_VERSION).dylib $(DESTDIR)$(libdir)/libhts.dylib @@ -483,6 +501,9 @@ clean-so: clean-cygdll: -rm -f cyghts-*.dll libhts.dll.a +clean-dll: + -rm -f hts-*.dll hts.dll.a + clean-dylib: -rm -f libhts.dylib libhts.*.dylib @@ -510,4 +531,5 @@ force: .PHONY: tags test testclean .PHONY: clean-so install-so .PHONY: clean-cygdll install-cygdll +.PHONY: clean-dll install-dll .PHONY: clean-dylib install-dylib diff --git a/bgzf.c b/bgzf.c index 7e80b8cfd..0f62173d8 100644 --- a/bgzf.c +++ b/bgzf.c @@ -1038,6 +1038,9 @@ static int bgzf_check_EOF_common(BGZF *fp) off_t offset = htell(fp->fp); if (hseek(fp->fp, -28, SEEK_END) < 0) { if (errno == ESPIPE) { hclearerr(fp->fp); return 2; } +#ifdef _WIN32 + if (errno == EINVAL) { hclearerr(fp->fp); return 2; } +#endif else return -1; } if ( hread(fp->fp, buf, 28) != 28 ) return -1; @@ -1179,6 +1182,7 @@ static void *bgzf_mt_reader(void *vp) { pthread_exit(NULL); } } + return NULL; } int bgzf_thread_pool(BGZF *fp, hts_tpool *pool, int qsize) { diff --git a/bgzip.c b/bgzip.c index e078185d7..931e7a0d8 100644 --- a/bgzip.c +++ b/bgzip.c @@ -36,6 +36,11 @@ #include "htslib/bgzf.h" #include "htslib/hts.h" +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include +#endif + static const int WINDOW_SIZE = 64 * 1024; static void error(const char *format, ...) @@ -198,6 +203,9 @@ int main(int argc, char **argv) if ( index ) bgzf_index_build_init(fp); buffer = malloc(WINDOW_SIZE); +#ifdef _WIN32 + _setmode(f_src, O_BINARY); +#endif if (rebgzip){ if ( bgzf_index_load(fp, index_fname, NULL) < 0 ) error("Could not load index: %s.gzi\n", argv[optind]); @@ -319,13 +327,21 @@ int main(int argc, char **argv) if ( bgzf_index_load(fp, argv[optind], ".gzi") < 0 ) error("Could not load index: %s.gzi\n", argv[optind]); if ( bgzf_useek(fp, start, SEEK_SET) < 0 ) error("Could not seek to %d-th (uncompressd) byte\n", start); } +#ifdef _WIN32 + _setmode(f_dst, O_BINARY); +#endif while (1) { if (end < 0) c = bgzf_read(fp, buffer, WINDOW_SIZE); else c = bgzf_read(fp, buffer, (end - start > WINDOW_SIZE)? WINDOW_SIZE:(end - start)); if (c == 0) break; if (c < 0) error("Could not read %d bytes: Error %d\n", (end - start > WINDOW_SIZE)? WINDOW_SIZE:(end - start), fp->errcode); start += c; - if ( write(f_dst, buffer, c) != c ) error("Could not write %d bytes\n", c); + if ( write(f_dst, buffer, c) != c ) { +#ifdef _WIN32 + if (GetLastError() != ERROR_NO_DATA) +#endif + error("Could not write %d bytes\n", c); + } if (end >= 0 && start >= end) break; } free(buffer); diff --git a/configure.ac b/configure.ac index 308a5c1f2..33c9616b8 100644 --- a/configure.ac +++ b/configure.ac @@ -112,8 +112,8 @@ AC_ARG_ENABLE([s3], [support Amazon AWS S3 URLs])], [], [enable_s3=check]) -AC_MSG_CHECKING([shared library type]) test -n "$host_alias" || host_alias=unknown-`uname -s` +AC_MSG_CHECKING([shared library type for $host_alias]) case $host_alias in *-cygwin* | *-CYGWIN*) host_result="Cygwin DLL" @@ -125,6 +125,15 @@ case $host_alias in PLATFORM=Darwin PLUGIN_EXT=.bundle ;; + *-msys* | *-MSYS* | *-mingw* | *-MINGW*) + host_result="MSYS dll" + PLATFORM=MSYS + PLUGIN_EXT=.dll + # This also sets __USE_MINGW_ANSI_STDIO which in turn makes PRId64, + # %lld and %z printf formats work. It also enforces the snprintf to + # be C99 compliant so it returns the correct values (in kstring.c). + CPPFLAGS="$CPPCFLAGS -D_XOPEN_SOURCE=600" + ;; *) host_result="plain .so" PLATFORM=default @@ -136,7 +145,7 @@ AC_SUBST([PLATFORM]) dnl FIXME This pulls in dozens of standard header checks AC_FUNC_MMAP -AC_CHECK_FUNCS(gmtime_r) +AC_CHECK_FUNCS([gmtime_r fsync drand48]) # Darwin has a dubious fdatasync() symbol, but no declaration in AC_CHECK_DECL([fdatasync(int)], [AC_CHECK_FUNCS(fdatasync)]) @@ -183,9 +192,11 @@ FAILED. This error must be resolved in order to build HTSlib successfully.]) fi dnl connect() etc. fns are in libc on linux, but libsocket on illumos/Solaris -libsocket=unneeded -AC_SEARCH_LIBS(connect, socket, [libsocket=needed], []) - +AC_SEARCH_LIBS([recv], [socket ws2_32], [ +if test "$ac_cv_search_recv" != "none required" +then + static_LIBS="$static_LIBS $ac_cv_search_recv" +fi], [AC_MSG_ERROR([unable to find the recv() function])]) if test "$enable_bz2" != no; then bz2_devel=ok diff --git a/cram/cram_encode.c b/cram/cram_encode.c index d7f08f17c..1d9f70ce2 100644 --- a/cram/cram_encode.c +++ b/cram/cram_encode.c @@ -1935,7 +1935,7 @@ static int cram_add_insertion(cram_container *c, cram_slice *s, cram_record *r, } /* - * Encodes auxiliary data. + * Encodes auxiliary data, CRAM 1.0 format. * Returns the read-group parsed out of the BAM aux fields on success * NULL on failure or no rg present (FIXME) */ diff --git a/cram/cram_io.c b/cram/cram_io.c index 8e2f1ea58..e2b416f36 100644 --- a/cram/cram_io.c +++ b/cram/cram_io.c @@ -1446,7 +1446,7 @@ char *cram_block_method2str(enum cram_block_method m) { case RANS0: return "RANS0"; case RANS1: return "RANS1"; case GZIP_RLE: return "GZIP_RLE"; - case ERROR: break; + case BM_ERROR: break; } return "?"; } @@ -3663,7 +3663,11 @@ SAM_hdr *cram_read_SAM_hdr(cram_fd *fd) { * Out must be at least PATH_MAX bytes long. */ static void full_path(char *out, char *in) { - if (*in == '/') { + size_t in_l = strlen(in); + if (*in == '/' || + // Windows paths + (in_l > 3 && toupper(*in) >= 'A' && toupper(*in) <= 'Z' && + in[1] == ':' && (in[2] == '/' || in[2] == '\\'))) { strncpy(out, in, PATH_MAX); out[PATH_MAX-1] = 0; } else { diff --git a/cram/cram_structs.h b/cram/cram_structs.h index 5165605b6..eeba679f3 100644 --- a/cram/cram_structs.h +++ b/cram/cram_structs.h @@ -192,7 +192,7 @@ typedef struct cram_file_def { struct cram_slice; enum cram_block_method { - ERROR = -1, + BM_ERROR = -1, RAW = 0, GZIP = 1, BZIP2 = 2, diff --git a/cram/mFILE.h b/cram/mFILE.h index 05a3a88d2..a6b067a75 100644 --- a/cram/mFILE.h +++ b/cram/mFILE.h @@ -48,6 +48,11 @@ typedef struct { size_t flush_pos; } mFILE; +// Work around a clash with winuser.h +#ifdef MF_APPEND +# undef MF_APPEND +#endif + #define MF_READ 1 #define MF_WRITE 2 #define MF_APPEND 4 diff --git a/cram/os.h b/cram/os.h index b1fdca620..ba0a20753 100644 --- a/cram/os.h +++ b/cram/os.h @@ -205,27 +205,13 @@ static inline uint16_t le_int2(uint16_t x) { * Microsoft Windows running MinGW */ #if defined(__MINGW32__) -/* #define mkdir(filename,mode) mkdir((filename)) */ +#include +#define mkdir(filename,mode) mkdir((filename)) #define sysconf(x) 512 -#define ftruncate(fd,len) _chsize(fd,len) +#ifndef ftruncate +# define ftruncate(fd,len) _chsize(fd,len) +#endif #endif - -/* Generic WIN32 API issues */ -#ifdef _WIN32 -# ifndef HAVE_FSEEKO -# if __MSVCRT_VERSION__ >= 0x800 - /* if you have MSVCR80 installed then you can use these definitions: */ -# define off_t __int64 -# define fseeko _fseeki64 -# define ftello _ftelli64 -# else - /* otherwise we're stuck with 32-bit file support */ -# define off_t long -# define fseeko fseek -# define ftello ftell -# endif -# endif /* !HAVE_FSEEKO */ -#endif /* _WIN32 */ #ifdef __cplusplus } diff --git a/errmod.c b/errmod.c index ee4823b63..bb9fc28f7 100644 --- a/errmod.c +++ b/errmod.c @@ -28,6 +28,8 @@ DEALINGS IN THE SOFTWARE. */ #include #include "htslib/hts.h" #include "htslib/ksort.h" +#include "htslib/hts_os.h" // for drand48 + KSORT_INIT_GENERIC(uint16_t) struct errmod_t { diff --git a/hfile.c b/hfile.c index 57e2b8957..b0c5eba30 100644 --- a/hfile.c +++ b/hfile.c @@ -526,6 +526,18 @@ static ssize_t fd_write(hFILE *fpv, const void *buffer, size_t nbytes) n = fp->is_socket? send(fp->fd, buffer, nbytes, 0) : write(fp->fd, buffer, nbytes); } while (n < 0 && errno == EINTR); +#ifdef _WIN32 + // On windows we have no SIGPIPE. Instead write returns + // EINVAL. We check for this and our fd being a pipe. + // If so, we raise SIGTERM instead of SIGPIPE. It's not + // ideal, but I think the only alternative is extra checking + // in every single piece of code. + if (n < 0 && errno == EINVAL && + GetLastError() == ERROR_NO_DATA && + GetFileType((HANDLE)_get_osfhandle(fp->fd)) == FILE_TYPE_PIPE) { + raise(SIGTERM); + } +#endif return n; } @@ -537,12 +549,13 @@ static off_t fd_seek(hFILE *fpv, off_t offset, int whence) static int fd_flush(hFILE *fpv) { - hFILE_fd *fp = (hFILE_fd *) fpv; - int ret; + int ret = 0; do { #ifdef HAVE_FDATASYNC + hFILE_fd *fp = (hFILE_fd *) fpv; ret = fdatasync(fp->fd); -#else +#elif defined(HAVE_FSYNC) + hFILE_fd *fp = (hFILE_fd *) fpv; ret = fsync(fp->fd); #endif // Ignore invalid-for-fsync(2) errors due to being, e.g., a pipe, @@ -619,6 +632,11 @@ static hFILE *hopen_fd_fileuri(const char *url, const char *mode) else if (strncmp(url, "file:///", 8) == 0) url += 7; else { errno = EPROTONOSUPPORT; return NULL; } +#ifdef _WIN32 + // For cases like C:/foo + if (url[0] == '/' && url[2] == ':' && url[3] == '/') url++; +#endif + return hopen_fd(url, mode); } @@ -876,7 +894,8 @@ static const struct hFILE_scheme_handler *find_scheme_handler(const char *s) else if (s[i] == ':') break; else return NULL; - if (i == 0 || i >= sizeof scheme) return NULL; + // 1 byte schemes are likely windows C:/foo pathnames + if (i <= 1 || i >= sizeof scheme) return NULL; scheme[i] = '\0'; pthread_mutex_lock(&plugins_lock); diff --git a/hfile_libcurl.c b/hfile_libcurl.c index ea99aa741..461e8910b 100644 --- a/hfile_libcurl.c +++ b/hfile_libcurl.c @@ -28,7 +28,9 @@ DEALINGS IN THE SOFTWARE. */ #include #include #include -#include +#ifndef _WIN32 +# include +#endif #include "hfile_internal.h" #ifdef ENABLE_PLUGINS diff --git a/hts.c b/hts.c index 1c6ca8abe..ae033c98c 100644 --- a/hts.c +++ b/hts.c @@ -43,6 +43,7 @@ DEALINGS IN THE SOFTWARE. */ #include "htslib/hts_endian.h" #include "version.h" #include "hts_internal.h" +#include "htslib/hts_os.h" // drand48 #include "hfile_internal.h" #include "htslib/khash.h" diff --git a/hts_os.c b/hts_os.c new file mode 100644 index 000000000..eb0a5ca3a --- /dev/null +++ b/hts_os.c @@ -0,0 +1,35 @@ +/// @file hts_os.c +/// Operating System specific tweaks, for compatibility with POSIX. +/* + Copyright (C) 2017 Genome Research Ltd. + + Author: James Bonfield + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. */ + +// Windows (maybe more) lack a drand48 implementation. +#ifndef HAVE_DRAND48 +#include "win/rand.c" +#endif + +// // On Windows when using the MSYS or Cygwin terminals, isatty fails +// #ifdef _WIN32 +// #define USE_FILEEXTD +// #include "win/iscygpty.c" +// #endif diff --git a/htslib/bgzf.h b/htslib/bgzf.h index 15c76cd37..b2f41654b 100644 --- a/htslib/bgzf.h +++ b/htslib/bgzf.h @@ -92,6 +92,9 @@ typedef struct __kstring_t { * Open an existing file descriptor for reading or writing. * * @param fd file descriptor + * Note that the file must be opened in binary mode, or else + * there will be problems on platforms that make a difference + * between text and binary mode. * @param mode mode matching /[rwag][u0-9]+/: 'r' for reading, 'w' for * writing, 'a' for appending, 'g' for gzip rather than BGZF * compression (with 'w' only), and digit specifies the zlib diff --git a/htslib/hfile.h b/htslib/hfile.h index d07a755d1..fa8971842 100644 --- a/htslib/hfile.h +++ b/htslib/hfile.h @@ -66,6 +66,10 @@ hFILE *hopen(const char *filename, const char *mode, ...) HTS_RESULT_USED; /// Associate a stream with an existing open file descriptor /** @return An hFILE pointer, or `NULL` (with _errno_ set) if an error occurred. +Note that the file must be opened in binary mode, or else +there will be problems on platforms that make a difference +between text and binary mode. + For socket descriptors (on Windows), _mode_ should contain `s`. */ hFILE *hdopen(int fd, const char *mode) HTS_RESULT_USED; diff --git a/htslib/hts_defs.h b/htslib/hts_defs.h index 1602303fc..5a8d921c4 100644 --- a/htslib/hts_defs.h +++ b/htslib/hts_defs.h @@ -69,6 +69,18 @@ DEALINGS IN THE SOFTWARE. */ #define HTS_DEPRECATED(message) #endif +// On mingw the "printf" format type doesn't work. It needs "gnu_printf" +// in order to check %lld and %z, otherwise it defaults to checking against +// the Microsoft library printf format options despite linking against the +// GNU posix implementation of printf. The __MINGW_PRINTF_FORMAT macro +// expands to printf or gnu_printf as required, but obviously may not +// exist +#ifdef __MINGW_PRINTF_FORMAT +#define HTS_PRINTF_FMT __MINGW_PRINTF_FORMAT +#else +#define HTS_PRINTF_FMT printf +#endif + #if HTS_COMPILER_HAS(__format__) || HTS_GCC_AT_LEAST(3,0) #define HTS_FORMAT(type, idx, first) __attribute__((__format__ (type, idx, first))) #else diff --git a/htslib/hts_os.h b/htslib/hts_os.h new file mode 100644 index 000000000..edb23a205 --- /dev/null +++ b/htslib/hts_os.h @@ -0,0 +1,49 @@ +/// @file hts_os.h +/// Operating System specific tweaks, for compatibility with POSIX. +/* + Copyright (C) 2017 Genome Research Ltd. + + Author: James Bonfield + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. */ + +#ifndef HTSLIB_HTS_OS_H +#define HTSLIB_HTS_OS_H + +extern void srand48(long seed); +extern double drand48(void); +extern long lrand48(void); + +#ifdef _WIN32 +/* Check if the fd is a cygwin/msys's pty. */ +extern int is_cygpty(int fd); +#endif + +#if defined(__MINGW32__) +#include +#define mkdir(filename,mode) mkdir((filename)) +#endif + +#ifdef _WIN32 +#include +#define srandom srand +#define random rand +#endif + +#endif diff --git a/tabix.c b/tabix.c index 681f8a0b5..119f396ca 100644 --- a/tabix.c +++ b/tabix.c @@ -29,6 +29,7 @@ DEALINGS IN THE SOFTWARE. */ #include #include #include +#include #include #include #include @@ -69,7 +70,6 @@ static void error(const char *format, ...) int file_type(const char *fname) { int l = strlen(fname); - int strcasecmp(const char *s1, const char *s2); if (l>=7 && strcasecmp(fname+l-7, ".gff.gz") == 0) return IS_GFF; else if (l>=7 && strcasecmp(fname+l-7, ".bed.gz") == 0) return IS_BED; else if (l>=7 && strcasecmp(fname+l-7, ".sam.gz") == 0) return IS_SAM; diff --git a/test/compare_sam.pl b/test/compare_sam.pl index a241c6b68..6860c91a5 100755 --- a/test/compare_sam.pl +++ b/test/compare_sam.pl @@ -64,6 +64,8 @@ # Compare lines while ($ln1 && $ln2) { + $ln1 =~ s/\015?\012/\n/; + $ln2 =~ s/\015?\012/\n/; chomp($ln1); chomp($ln2); diff --git a/test/hfile.c b/test/hfile.c index 16ad6708a..577b8171b 100644 --- a/test/hfile.c +++ b/test/hfile.c @@ -62,8 +62,8 @@ char *slurp(const char *filename) char *text; struct stat sbuf; size_t filesize; - FILE *f = fopen(filename, "r"); - if (f == NULL) fail("fopen(\"%s\", \"r\")", filename); + FILE *f = fopen(filename, "rb"); + if (f == NULL) fail("fopen(\"%s\", \"rb\")", filename); if (fstat(fileno(f), &sbuf) != 0) fail("fstat(\"%s\")", filename); filesize = sbuf.st_size; diff --git a/test/sam.c b/test/sam.c index 736281d7d..dd8333640 100644 --- a/test/sam.c +++ b/test/sam.c @@ -43,7 +43,7 @@ DEALINGS IN THE SOFTWARE. */ int status; -static void HTS_FORMAT(printf, 1, 2) fail(const char *fmt, ...) +static void HTS_FORMAT(HTS_PRINTF_FMT, 1, 2) fail(const char *fmt, ...) { va_list args; diff --git a/test/tabix/test-tabix.sh b/test/tabix/test-tabix.sh index 358a3dd54..4a4b0456a 100755 --- a/test/tabix/test-tabix.sh +++ b/test/tabix/test-tabix.sh @@ -57,11 +57,12 @@ run_test() { y="exit_code" elif [ "x$e" != "x" -a "$e" != "." ] then - if cmp -s _out.tmp "$e" + sed -n 's/.*/&/p' _out.tmp > _out.tmp2 + if cmp -s _out.tmp2 "$e" then # Output was as expected r="P" - rm -f _out.tmp _err.tmp + rm -f _out.tmp _out.tmp2 _err.tmp else # Output differed r="F" @@ -70,7 +71,7 @@ run_test() { else # Expected zero exit code and got it. r="P" - rm -f _out.tmp _err.tmp + rm -f _out.tmp _out.tmp2 _err.tmp fi if [ "$r" = "F" ] diff --git a/test/test-bcf-sr.pl b/test/test-bcf-sr.pl index 8f78acb86..3102cddbf 100755 --- a/test/test-bcf-sr.pl +++ b/test/test-bcf-sr.pl @@ -156,6 +156,7 @@ sub check_outputs { my ($pos,@vals) = split(/\t/,$line); chomp($vals[-1]); + $vals[-1] =~ s/\r$//; push @{$out{$pos}},join("\t",@vals); } close($fh) or error("close failed: $fname_bin"); @@ -173,6 +174,7 @@ sub check_outputs while (my $line=<$fh>) { chomp($line); + $line =~ s/\r$//; push @plines,$line; } close($fh) or error("close failed: $fname_perl"); @@ -181,8 +183,8 @@ sub check_outputs @plines = sort @plines; for (my $i=0; $i<@plines; $i++) { - if ( $blines[$i] ne $plines[$i] ) - { + if ( $blines[$i] ne $plines[$i] ) + { #error("Different lines in $fname_bin vs $fname_perl:\n\t$blines[$i].\nvs\n\t$plines[$i].\n"); error("Different lines in $fname_bin vs $fname_perl:\n\t".join("\n\t",@blines)."\nvs\n\t".join("\n\t",@plines)."\n"); } diff --git a/test/test.pl b/test/test.pl index 3ce6e674a..f168135de 100755 --- a/test/test.pl +++ b/test/test.pl @@ -64,10 +64,28 @@ sub error "Options:\n", " -r, --redo-outputs Recreate expected output files.\n", " -t, --temp-dir When given, temporary files will not be removed.\n", + " -f, --fail-fast Fail-fast mode: exit as soon as a test fails.\n", " -h, -?, --help This help message.\n", "\n"; exit 1; } + +sub cygpath { + my ($path) = @_; + $path = `cygpath -m $path`; + $path =~ s/\r?\n//; + return $path +} + +sub safe_tempdir +{ + my $dir = tempdir(CLEANUP=>1); + if ($^O =~ /^msys/) { + $dir = cygpath($dir); + } + return $dir; +} + sub parse_params { my $opts = { keep_files=>0, nok=>0, nfailed=>0 }; @@ -76,14 +94,20 @@ sub parse_params my $ret = GetOptions ( 't|temp-dir:s' => \$$opts{keep_files}, 'r|redo-outputs' => \$$opts{redo_outputs}, + 'f|fail-fast' => \$$opts{fail_fast}, 'h|?|help' => \$help ); if ( !$ret or $help ) { error(); } - $$opts{tmp} = $$opts{keep_files} ? $$opts{keep_files} : tempdir(CLEANUP=>1); + $$opts{tmp} = $$opts{keep_files} ? $$opts{keep_files} : safe_tempdir(); if ( $$opts{keep_files} ) { cmd("mkdir -p $$opts{keep_files}"); } $$opts{path} = $FindBin::RealBin; $$opts{bin} = $FindBin::RealBin; $$opts{bin} =~ s{/test/?$}{}; + if ($^O =~ /^msys/) { + $$opts{path} = cygpath($$opts{path}); + $$opts{bin} = cygpath($$opts{bin}); + } + return $opts; } sub _cmd @@ -149,11 +173,13 @@ sub test_cmd { my @exp = <$fh>; $exp = join('',@exp); + $exp =~ s/\015?\012/\n/g; close($fh); } elsif ( !$$opts{redo_outputs} ) { failed($opts,$test,"$$opts{path}/$args{out}: $!"); return; } - if ( $exp ne $out ) + (my $out_lf = $out) =~ s/\015?\012/\n/g; + if ( $exp ne $out_lf ) { open(my $fh,'>',"$$opts{path}/$args{out}.new") or error("$$opts{path}/$args{out}.new"); print $fh $out; @@ -181,6 +207,9 @@ sub failed if ( defined $reason ) { print STDERR "\t$reason\n"; } print STDERR ".. failed ...\n\n"; STDERR->flush(); + if ($$opts{fail_fast}) { + die "\n"; + } } sub passed { @@ -202,7 +231,7 @@ sub is_file_newer my $test_view_failures; sub testv { - my ($cmd) = @_; + my ($opts, $cmd) = @_; print " $cmd\n"; my ($ret, $out) = _cmd($cmd); if ($ret != 0) { @@ -210,6 +239,9 @@ sub testv { print STDERR "FAILED\n$out\n"; STDERR->flush(); $test_view_failures++; + if ($$opts{fail_fast}) { + die "\n"; + } } } @@ -234,50 +266,50 @@ sub test_view $test_view_failures = 0; # SAM -> BAM -> SAM - testv "./test_view $tv_args -S -b $sam > $bam"; - testv "./test_view $tv_args $bam > $bam.sam_"; - testv "./compare_sam.pl $sam $bam.sam_"; + testv $opts, "./test_view $tv_args -S -b $sam > $bam"; + testv $opts, "./test_view $tv_args $bam > $bam.sam_"; + testv $opts, "./compare_sam.pl $sam $bam.sam_"; # SAM -> CRAM -> SAM - testv "./test_view $tv_args -t $ref -S -C $sam > $cram"; - testv "./test_view $tv_args -D $cram > $cram.sam_"; - testv "./compare_sam.pl $md $sam $cram.sam_"; + testv $opts, "./test_view $tv_args -t $ref -S -C $sam > $cram"; + testv $opts, "./test_view $tv_args -D $cram > $cram.sam_"; + testv $opts, "./compare_sam.pl $md $sam $cram.sam_"; # BAM -> CRAM -> BAM -> SAM $cram = "$bam.cram"; - testv "./test_view $tv_args -t $ref -C $bam > $cram"; - testv "./test_view $tv_args -b -D $cram > $cram.bam"; - testv "./test_view $tv_args $cram.bam > $cram.bam.sam_"; - testv "./compare_sam.pl $md $sam $cram.bam.sam_"; + testv $opts, "./test_view $tv_args -t $ref -C $bam > $cram"; + testv $opts, "./test_view $tv_args -b -D $cram > $cram.bam"; + testv $opts, "./test_view $tv_args $cram.bam > $cram.bam.sam_"; + testv $opts, "./compare_sam.pl $md $sam $cram.bam.sam_"; # SAM -> CRAM3 -> SAM $cram = "$base.tmp.cram"; - testv "./test_view $tv_args -t $ref -S -C -o VERSION=3.0 $sam > $cram"; - testv "./test_view $tv_args -D $cram > $cram.sam_"; - testv "./compare_sam.pl $md $sam $cram.sam_"; + testv $opts, "./test_view $tv_args -t $ref -S -C -o VERSION=3.0 $sam > $cram"; + testv $opts, "./test_view $tv_args -D $cram > $cram.sam_"; + testv $opts, "./compare_sam.pl $md $sam $cram.sam_"; # BAM -> CRAM3 -> BAM -> SAM $cram = "$bam.cram"; - testv "./test_view $tv_args -t $ref -C -o VERSION=3.0 $bam > $cram"; - testv "./test_view $tv_args -b -D $cram > $cram.bam"; - testv "./test_view $tv_args $cram.bam > $cram.bam.sam_"; - testv "./compare_sam.pl $md $sam $cram.bam.sam_"; + testv $opts, "./test_view $tv_args -t $ref -C -o VERSION=3.0 $bam > $cram"; + testv $opts, "./test_view $tv_args -b -D $cram > $cram.bam"; + testv $opts, "./test_view $tv_args $cram.bam > $cram.bam.sam_"; + testv $opts, "./compare_sam.pl $md $sam $cram.bam.sam_"; # CRAM3 -> CRAM2 $cram = "$base.tmp.cram"; - testv "./test_view $tv_args -t $ref -C -o VERSION=2.1 $cram > $cram.cram"; + testv $opts, "./test_view $tv_args -t $ref -C -o VERSION=2.1 $cram > $cram.cram"; # CRAM2 -> CRAM3 - testv "./test_view $tv_args -t $ref -C -o VERSION=3.0 $cram.cram > $cram"; - testv "./test_view $tv_args $cram > $cram.sam_"; - testv "./compare_sam.pl $md $sam $cram.sam_"; + testv $opts, "./test_view $tv_args -t $ref -C -o VERSION=3.0 $cram.cram > $cram"; + testv $opts, "./test_view $tv_args $cram > $cram.sam_"; + testv $opts, "./compare_sam.pl $md $sam $cram.sam_"; # Java pre-made CRAM -> SAM my $jcram = "${base}_java.cram"; if (-e $jcram) { my $jsam = "${base}_java.tmp.sam_"; - testv "./test_view $tv_args -i reference=$ref $jcram > $jsam"; - testv "./compare_sam.pl -Baux $md $sam $jsam"; + testv $opts, "./test_view $tv_args -i reference=$ref $jcram > $jsam"; + testv $opts, "./compare_sam.pl -Baux $md $sam $jsam"; } if ($test_view_failures == 0) diff --git a/test/test_bgzf.c b/test/test_bgzf.c index e34b22f01..c98382940 100644 --- a/test/test_bgzf.c +++ b/test/test_bgzf.c @@ -23,6 +23,8 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include + #include #include #include @@ -32,6 +34,7 @@ DEALINGS IN THE SOFTWARE. #include #include "htslib/bgzf.h" #include "htslib/hfile.h" +#include "hfile_internal.h" const char *bgzf_suffix = ".gz"; const char *idx_suffix = ".gzi"; @@ -113,17 +116,7 @@ static BGZF * try_bgzf_open(const char *name, const char *mode, static BGZF * try_bgzf_dopen(const char *name, const char *mode, const char *func) { BGZF *bgz = NULL; - int fd = -1; - if (strchr(mode, 'r')) { - fd = open(name, O_RDONLY); - } else if (strchr(mode, 'w')) { - fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666); - } else if (strchr(mode, 'a')) { - fd = open(name, O_WRONLY | O_CREAT | O_APPEND, 0666); - } else { - errno = EINVAL; - } - + int fd = open(name, hfile_oflags(mode), 0666); if (fd < 0) { fprintf(stderr, "%s : Failed to open %s with mode %s : %s\n", func, name, mode, strerror(errno)); diff --git a/test/test_view.c b/test/test_view.c index 7f173fd95..9d2678a53 100644 --- a/test/test_view.c +++ b/test/test_view.c @@ -35,6 +35,13 @@ DEALINGS IN THE SOFTWARE. */ #include "htslib/sam.h" +enum test_op { + READ_COMPRESSED = 1, + WRITE_COMPRESSED = 2, + READ_CRAM = 4, + WRITE_CRAM = 8 +}; + int main(int argc, char *argv[]) { samFile *in; @@ -52,30 +59,46 @@ int main(int argc, char *argv[]) int benchmark = 0; int nthreads = 0; // shared pool - while ((c = getopt(argc, argv, "IbDCSl:t:i:o:N:BZ:@:")) >= 0) { + while ((c = getopt(argc, argv, "DSIt:i:bCl:o:N:BZ:@:")) >= 0) { switch (c) { - case 'S': flag |= 1; break; - case 'b': flag |= 2; break; - case 'D': flag |= 4; break; - case 'C': flag |= 8; break; - case 'B': benchmark = 1; break; - case 'l': clevel = atoi(optarg); flag |= 2; break; - case 't': fn_ref = optarg; break; + case 'D': flag |= READ_CRAM; break; + case 'S': flag |= READ_COMPRESSED; break; case 'I': ignore_sam_err = 1; break; - case 'i': if (hts_opt_add(&in_opts, optarg)) return 1; break; + case 't': fn_ref = optarg; break; + case 'i': if (hts_opt_add(&in_opts, optarg)) return 1; break; + case 'b': flag |= WRITE_COMPRESSED; break; + case 'C': flag |= WRITE_CRAM; break; + case 'l': clevel = atoi(optarg); flag |= WRITE_COMPRESSED; break; case 'o': if (hts_opt_add(&out_opts, optarg)) return 1; break; case 'N': nreads = atoi(optarg); break; + case 'B': benchmark = 1; break; case 'Z': extra_hdr_nuls = atoi(optarg); break; case '@': nthreads = atoi(optarg); break; } } if (argc == optind) { - fprintf(stderr, "Usage: samview [-bSCSIB] [-N num_reads] [-l level] [-o option=value] [-Z hdr_nuls] || [region]\n"); + fprintf(stderr, "Usage: test_view [-DSI] [-t fn_ref] [-i option=value] [-bC] [-l level] [-o option=value] [-N num_reads] [-B] [-Z hdr_nuls] [-@ num_threads] || [region]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "-D: read CRAM format (mode 'c')\n"); + fprintf(stderr, "-S: read compressed BCF, BAM, FAI (mode 'b')\n"); + fprintf(stderr, "-I: ignore SAM parsing errors\n"); + fprintf(stderr, "-t: fn_ref: load CRAM references from the specificed fasta file instead of @SQ headers when writing a CRAM file\n"); + fprintf(stderr, "-i: option=value: set an option for CRAM input\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "-b: write compressed BCF, BAM, FAI (mode 'b')\n"); + fprintf(stderr, "-C: write CRAM format (mode 'c')\n"); + fprintf(stderr, "-l 0-9: set zlib compression level\n"); + fprintf(stderr, "-o option=value: set an option for CRAM output\n"); + fprintf(stderr, "-N: num_reads: limit the output to the first num_reads reads\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "-B: enable benchmarking\n"); + fprintf(stderr, "-Z hdr_nuls: append specified number of null bytes to the SAM header\n"); + fprintf(stderr, "-@ num_threads: use thread pool with specified number of threads\n"); return 1; } strcpy(moder, "r"); - if (flag&4) strcat(moder, "c"); - else if ((flag&1) == 0) strcat(moder, "b"); + if (flag & READ_CRAM) strcat(moder, "c"); + else if ((flag & READ_COMPRESSED) == 0) strcat(moder, "b"); in = sam_open(argv[optind], moder); if (in == NULL) { @@ -103,8 +126,8 @@ int main(int argc, char *argv[]) strcpy(modew, "w"); if (clevel >= 0 && clevel <= 9) sprintf(modew + 1, "%d", clevel); - if (flag&8) strcat(modew, "c"); - else if (flag&2) strcat(modew, "b"); + if (flag & WRITE_CRAM) strcat(modew, "c"); + else if (flag & WRITE_COMPRESSED) strcat(modew, "b"); out = hts_open("-", modew); if (out == NULL) { fprintf(stderr, "Error opening standard output\n"); @@ -112,7 +135,7 @@ int main(int argc, char *argv[]) } /* CRAM output */ - if (flag & 8) { + if (flag & WRITE_CRAM) { int ret; // Parse input header and use for CRAM output @@ -155,7 +178,7 @@ int main(int argc, char *argv[]) fprintf(stderr, "Error writing output header.\n"); exit_code = 1; } - if (optind + 1 < argc && !(flag&1)) { // BAM input and has a region + if (optind + 1 < argc && !(flag & READ_COMPRESSED)) { // BAM input and has a region int i; hts_idx_t *idx; if ((idx = sam_index_load(in, argv[optind])) == 0) { diff --git a/win/rand.c b/win/rand.c new file mode 100644 index 000000000..c8d54abb2 --- /dev/null +++ b/win/rand.c @@ -0,0 +1,98 @@ +/* rand.c -- drand48 implementation from the FreeBSD source tree. */ + +// This file is an amalgamation of the many small files in FreeBSD to do with +// drand48 and friends implementations. +// It comprises _rand48.c, rand48.h, srand48.c, drand48.c, erand48.c, lrand48.c + +/* + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + +//#include +//__FBSDID("$FreeBSD: src/lib/libc/gen/_rand48.c,v 1.2 2002/03/22 21:52:05 obrien Exp $"); + +#include +#include "win/rand.h" + +#define RAND48_SEED_0 (0x330e) +#define RAND48_SEED_1 (0xabcd) +#define RAND48_SEED_2 (0x1234) +#define RAND48_MULT_0 (0xe66d) +#define RAND48_MULT_1 (0xdeec) +#define RAND48_MULT_2 (0x0005) +#define RAND48_ADD (0x000b) + +unsigned short _rand48_seed[3] = { + RAND48_SEED_0, + RAND48_SEED_1, + RAND48_SEED_2 +}; +unsigned short _rand48_mult[3] = { + RAND48_MULT_0, + RAND48_MULT_1, + RAND48_MULT_2 +}; +unsigned short _rand48_add = RAND48_ADD; + +static void +_dorand48(unsigned short xseed[3]) +{ + unsigned long accu; + unsigned short temp[2]; + + accu = (unsigned long) _rand48_mult[0] * (unsigned long) xseed[0] + + (unsigned long) _rand48_add; + temp[0] = (unsigned short) accu; /* lower 16 bits */ + accu >>= sizeof(unsigned short) * 8; + accu += (unsigned long) _rand48_mult[0] * (unsigned long) xseed[1] + + (unsigned long) _rand48_mult[1] * (unsigned long) xseed[0]; + temp[1] = (unsigned short) accu; /* middle 16 bits */ + accu >>= sizeof(unsigned short) * 8; + accu += _rand48_mult[0] * xseed[2] + _rand48_mult[1] * xseed[1] + _rand48_mult[2] * xseed[0]; + xseed[0] = temp[0]; + xseed[1] = temp[1]; + xseed[2] = (unsigned short) accu; +} + +void +srand48(long seed) +{ + _rand48_seed[0] = RAND48_SEED_0; + _rand48_seed[1] = (unsigned short) seed; + _rand48_seed[2] = (unsigned short) (seed >> 16); + _rand48_mult[0] = RAND48_MULT_0; + _rand48_mult[1] = RAND48_MULT_1; + _rand48_mult[2] = RAND48_MULT_2; + _rand48_add = RAND48_ADD; +} + +double +erand48(unsigned short xseed[3]) +{ + _dorand48(xseed); + return ldexp((double) xseed[0], -48) + + ldexp((double) xseed[1], -32) + + ldexp((double) xseed[2], -16); +} + +double +drand48(void) +{ + return erand48(_rand48_seed); +} + +long +lrand48(void) +{ + _dorand48(_rand48_seed); + return ((long) _rand48_seed[2] << 15) + ((long) _rand48_seed[1] >> 1); +} diff --git a/win/rand.h b/win/rand.h new file mode 100644 index 000000000..1a36daaaf --- /dev/null +++ b/win/rand.h @@ -0,0 +1,23 @@ +/* rand.h -- drand48 implementation from the FreeBSD source tree. */ + +/* + * Copyright (c) 1993 Martin Birgmeier + * All rights reserved. + * + * You may redistribute unmodified or modified versions of this source + * code provided that the above copyright notice and this and the + * following conditions are retained. + * + * This software is provided ``as is'', and comes with no warranties + * of any kind. I shall in no event be liable for anything that happens + * to anyone/anything when using this software. + */ + +#ifndef HTSLIB_HTS_RAND_H +#define HTSLIB_HTS_RAND_H + +void srand48(long seed); +double drand48(void); +long lrand48(void); + +#endif /* HTSLIB_HTS_RAND_H */