Merge commit 'upstream/0.1.7a.dfsg'
[samtools.git] / faidx.c
diff --git a/faidx.c b/faidx.c
index 36366c20db595ee9ffeaac4d87628dc40c3690d2..811bdf8858ff9ef18479fb644981ad4780bf7a8d 100644 (file)
--- a/faidx.c
+++ b/faidx.c
@@ -14,8 +14,13 @@ KHASH_MAP_INIT_STR(s, faidx1_t)
 #ifndef _NO_RAZF
 #include "razf.h"
 #else
+#ifdef _WIN32
+#define ftello(fp) ftell(fp)
+#define fseeko(fp, offset, whence) fseek(fp, offset, whence)
+#else
 extern off_t ftello(FILE *stream);
 extern int fseeko(FILE *stream, off_t offset, int whence);
+#endif
 #define RAZF FILE
 #define razf_read(fp, buf, size) fread(buf, 1, size, fp)
 #define razf_open(fn, mode) fopen(fn, mode)
@@ -23,6 +28,9 @@ extern int fseeko(FILE *stream, off_t offset, int whence);
 #define razf_seek(fp, offset, whence) fseeko(fp, offset, whence)
 #define razf_tell(fp) ftello(fp)
 #endif
+#ifdef _USE_KNETFILE
+#include "knetfile.h"
+#endif
 
 struct __faidx_t {
        RAZF *rz;
@@ -134,7 +142,11 @@ void fai_save(const faidx_t *fai, FILE *fp)
                faidx1_t x;
                k = kh_get(s, fai->hash, fai->name[i]);
                x = kh_value(fai->hash, k);
+#ifdef _WIN32
+               fprintf(fp, "%s\t%d\t%ld\t%d\t%d\n", fai->name[i], (int)x.len, (long)x.offset, (int)x.line_blen, (int)x.line_len);
+#else
                fprintf(fp, "%s\t%d\t%lld\t%d\t%d\n", fai->name[i], (int)x.len, (long long)x.offset, (int)x.line_blen, (int)x.line_len);
+#endif
        }
 }
 
@@ -143,14 +155,22 @@ faidx_t *fai_read(FILE *fp)
        faidx_t *fai;
        char *buf, *p;
        int len, line_len, line_blen;
+#ifdef _WIN32
+       long offset;
+#else
        long long offset;
+#endif
        fai = (faidx_t*)calloc(1, sizeof(faidx_t));
        fai->hash = kh_init(s);
        buf = (char*)calloc(0x10000, 1);
        while (!feof(fp) && fgets(buf, 0x10000, fp)) {
                for (p = buf; *p && isgraph(*p); ++p);
                *p = 0; ++p;
+#ifdef _WIN32
+               sscanf(p, "%d%ld%d%d", &len, &offset, &line_blen, &line_len);
+#else
                sscanf(p, "%d%lld%d%d", &len, &offset, &line_blen, &line_len);
+#endif
                fai_insert_index(fai, buf, len, line_len, line_blen, offset);
        }
        free(buf);
@@ -177,15 +197,15 @@ int fai_build(const char *fn)
        sprintf(str, "%s.fai", fn);
        rz = razf_open(fn, "r");
        if (rz == 0) {
-               fprintf(stderr, "[fai_build] fail to open the FASTA file.\n");
+               fprintf(stderr, "[fai_build] fail to open the FASTA file %s\n",str);
                free(str);
                return -1;
        }
        fai = fai_build_core(rz);
        razf_close(rz);
-       fp = fopen(str, "w");
+       fp = fopen(str, "wb");
        if (fp == 0) {
-               fprintf(stderr, "[fai_build] fail to write FASTA index.\n");
+               fprintf(stderr, "[fai_build] fail to write FASTA index %s\n",str);
                fai_destroy(fai); free(str);
                return -1;
        }
@@ -196,6 +216,47 @@ int fai_build(const char *fn)
        return 0;
 }
 
+#ifdef _USE_KNETFILE
+FILE *download_and_open(const char *fn)
+{
+    const int buf_size = 1 * 1024 * 1024;
+    uint8_t *buf;
+    FILE *fp;
+    knetFile *fp_remote;
+    const char *url = fn;
+    const char *p;
+    int l = strlen(fn);
+    for (p = fn + l - 1; p >= fn; --p)
+        if (*p == '/') break;
+    fn = p + 1;
+
+    // First try to open a local copy
+    fp = fopen(fn, "r");
+    if (fp)
+        return fp;
+
+    // If failed, download from remote and open
+    fp_remote = knet_open(url, "rb");
+    if (fp_remote == 0) {
+        fprintf(stderr, "[download_from_remote] fail to open remote file %s\n",url);
+        return NULL;
+    }
+    if ((fp = fopen(fn, "wb")) == 0) {
+        fprintf(stderr, "[download_from_remote] fail to create file in the working directory %s\n",fn);
+        knet_close(fp_remote);
+        return NULL;
+    }
+    buf = (uint8_t*)calloc(buf_size, 1);
+    while ((l = knet_read(fp_remote, buf, buf_size)) != 0)
+        fwrite(buf, 1, l, fp);
+    free(buf);
+    fclose(fp);
+    knet_close(fp_remote);
+
+    return fopen(fn, "r");
+}
+#endif
+
 faidx_t *fai_load(const char *fn)
 {
        char *str;
@@ -203,20 +264,36 @@ faidx_t *fai_load(const char *fn)
        faidx_t *fai;
        str = (char*)calloc(strlen(fn) + 5, 1);
        sprintf(str, "%s.fai", fn);
-       fp = fopen(str, "r");
+
+#ifdef _USE_KNETFILE
+    if (strstr(fn, "ftp://") == fn || strstr(fn, "http://") == fn)
+    {
+        fp = download_and_open(str);
+        if ( !fp )
+        {
+            fprintf(stderr, "[fai_load] failed to open remote FASTA index %s\n", str);
+            free(str);
+            return 0;
+        }
+    }
+    else
+#endif
+        fp = fopen(str, "rb");
        if (fp == 0) {
                fprintf(stderr, "[fai_load] build FASTA index.\n");
                fai_build(fn);
-               fp = fopen(str, "r");
+               fp = fopen(str, "rb");
                if (fp == 0) {
                        fprintf(stderr, "[fai_load] fail to open FASTA index.\n");
                        free(str);
                        return 0;
                }
        }
+
        fai = fai_read(fp);
        fclose(fp);
-       fai->rz = razf_open(fn, "r");
+
+       fai->rz = razf_open(fn, "rb");
        free(str);
        if (fai->rz == 0) {
                fprintf(stderr, "[fai_load] fail to open FASTA file.\n");
@@ -270,7 +347,7 @@ char *fai_fetch(const faidx_t *fai, const char *str, int *len)
        l = 0;
        s = (char*)malloc(end - beg + 2);
        razf_seek(fai->rz, val.offset + beg / val.line_blen * val.line_len + beg % val.line_blen, SEEK_SET);
-       while (razf_read(fai->rz, &c, 1) == 1 && l < end - beg)
+       while (razf_read(fai->rz, &c, 1) == 1 && l < end - beg && !fai->rz->z_err)
                if (isgraph(c)) s[l++] = c;
        s[l] = '\0';
        *len = l;
@@ -306,6 +383,40 @@ int faidx_main(int argc, char *argv[])
        return 0;
 }
 
+int faidx_fetch_nseq(const faidx_t *fai) 
+{
+       return fai->n;
+}
+
+char *faidx_fetch_seq(const faidx_t *fai, char *c_name, int p_beg_i, int p_end_i, int *len)
+{
+       int l;
+       char c;
+    khiter_t iter;
+    faidx1_t val;
+       char *seq=NULL;
+
+    // Adjust position
+    iter = kh_get(s, fai->hash, c_name);
+    if(iter == kh_end(fai->hash)) return 0;
+    val = kh_value(fai->hash, iter);
+       if(p_end_i < p_beg_i) p_beg_i = p_end_i;
+    if(p_beg_i < 0) p_beg_i = 0;
+    else if(val.len <= p_beg_i) p_beg_i = val.len - 1;
+    if(p_end_i < 0) p_end_i = 0;
+    else if(val.len <= p_end_i) p_end_i = val.len - 1;
+
+    // Now retrieve the sequence 
+       l = 0;
+       seq = (char*)malloc(p_end_i - p_beg_i + 2);
+       razf_seek(fai->rz, val.offset + p_beg_i / val.line_blen * val.line_len + p_beg_i % val.line_blen, SEEK_SET);
+       while (razf_read(fai->rz, &c, 1) == 1 && l < p_end_i - p_beg_i + 1)
+               if (isgraph(c)) seq[l++] = c;
+       seq[l] = '\0';
+       *len = l;
+       return seq;
+}
+
 #ifdef FAIDX_MAIN
 int main(int argc, char *argv[]) { return faidx_main(argc, argv); }
 #endif