~ubuntu-branches/ubuntu/quantal/genometools/quantal-backports

« back to all changes in this revision

Viewing changes to src/external/samtools-0.1.18/knetfile.c

  • Committer: Package Import Robot
  • Author(s): Sascha Steinbiss
  • Date: 2012-07-09 14:10:23 UTC
  • Revision ID: package-import@ubuntu.com-20120709141023-juuu4spm6chqsf9o
Tags: upstream-1.4.1
ImportĀ upstreamĀ versionĀ 1.4.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* The MIT License
 
2
 
 
3
   Copyright (c) 2008 by Genome Research Ltd (GRL).
 
4
                 2010 by Attractive Chaos <attractor@live.co.uk>
 
5
 
 
6
   Permission is hereby granted, free of charge, to any person obtaining
 
7
   a copy of this software and associated documentation files (the
 
8
   "Software"), to deal in the Software without restriction, including
 
9
   without limitation the rights to use, copy, modify, merge, publish,
 
10
   distribute, sublicense, and/or sell copies of the Software, and to
 
11
   permit persons to whom the Software is furnished to do so, subject to
 
12
   the following conditions:
 
13
 
 
14
   The above copyright notice and this permission notice shall be
 
15
   included in all copies or substantial portions of the Software.
 
16
 
 
17
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
18
   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
19
   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
20
   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 
21
   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 
22
   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 
23
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
24
   SOFTWARE.
 
25
*/
 
26
 
 
27
/* Probably I will not do socket programming in the next few years and
 
28
   therefore I decide to heavily annotate this file, for Linux and
 
29
   Windows as well.  -ac */
 
30
 
 
31
#include <time.h>
 
32
#include <stdio.h>
 
33
#include <ctype.h>
 
34
#include <stdlib.h>
 
35
#include <string.h>
 
36
#include <errno.h>
 
37
#include <unistd.h>
 
38
#include <sys/types.h>
 
39
 
 
40
#ifndef _WIN32
 
41
#include <netdb.h>
 
42
#include <arpa/inet.h>
 
43
#include <sys/socket.h>
 
44
#endif
 
45
 
 
46
#include "knetfile.h"
 
47
 
 
48
/* In winsock.h, the type of a socket is SOCKET, which is: "typedef
 
49
 * u_int SOCKET". An invalid SOCKET is: "(SOCKET)(~0)", or signed
 
50
 * integer -1. In knetfile.c, I use "int" for socket type
 
51
 * throughout. This should be improved to avoid confusion.
 
52
 *
 
53
 * In Linux/Mac, recv() and read() do almost the same thing. You can see
 
54
 * in the header file that netread() is simply an alias of read(). In
 
55
 * Windows, however, they are different and using recv() is mandatory.
 
56
 */
 
57
 
 
58
/* This function tests if the file handler is ready for reading (or
 
59
 * writing if is_read==0). */
 
60
static int socket_wait(int fd, int is_read)
 
61
{
 
62
        fd_set fds, *fdr = 0, *fdw = 0;
 
63
        struct timeval tv;
 
64
        int ret;
 
65
        tv.tv_sec = 5; tv.tv_usec = 0; // 5 seconds time out
 
66
        FD_ZERO(&fds);
 
67
        FD_SET(fd, &fds);
 
68
        if (is_read) fdr = &fds;
 
69
        else fdw = &fds;
 
70
        ret = select(fd+1, fdr, fdw, 0, &tv);
 
71
#ifndef _WIN32
 
72
        if (ret == -1) perror("select");
 
73
#else
 
74
        if (ret == 0)
 
75
                fprintf(stderr, "select time-out\n");
 
76
        else if (ret == SOCKET_ERROR)
 
77
                fprintf(stderr, "select: %d\n", WSAGetLastError());
 
78
#endif
 
79
        return ret;
 
80
}
 
81
 
 
82
#ifndef _WIN32
 
83
/* This function does not work with Windows due to the lack of
 
84
 * getaddrinfo() in winsock. It is addapted from an example in "Beej's
 
85
 * Guide to Network Programming" (http://beej.us/guide/bgnet/). */
 
86
static int socket_connect(const char *host, const char *port)
 
87
{
 
88
#define __err_connect(func) do { perror(func); freeaddrinfo(res); return -1; } while (0)
 
89
 
 
90
        int on = 1, fd;
 
91
        struct linger lng = { 0, 0 };
 
92
        struct addrinfo hints, *res = 0;
 
93
        memset(&hints, 0, sizeof(struct addrinfo));
 
94
        hints.ai_family = AF_UNSPEC;
 
95
        hints.ai_socktype = SOCK_STREAM;
 
96
        /* In Unix/Mac, getaddrinfo() is the most convenient way to get
 
97
         * server information. */
 
98
        if (getaddrinfo(host, port, &hints, &res) != 0) __err_connect("getaddrinfo");
 
99
        if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) __err_connect("socket");
 
100
        /* The following two setsockopt() are used by ftplib
 
101
         * (http://nbpfaus.net/~pfau/ftplib/). I am not sure if they
 
102
         * necessary. */
 
103
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) __err_connect("setsockopt");
 
104
        if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1) __err_connect("setsockopt");
 
105
        if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) __err_connect("connect");
 
106
        freeaddrinfo(res);
 
107
        return fd;
 
108
}
 
109
#else
 
110
/* MinGW's printf has problem with "%lld" */
 
111
char *int64tostr(char *buf, int64_t x)
 
112
{
 
113
        int cnt;
 
114
        int i = 0;
 
115
        do {
 
116
                buf[i++] = '0' + x % 10;
 
117
                x /= 10;
 
118
        } while (x);
 
119
        buf[i] = 0;
 
120
        for (cnt = i, i = 0; i < cnt/2; ++i) {
 
121
                int c = buf[i]; buf[i] = buf[cnt-i-1]; buf[cnt-i-1] = c;
 
122
        }
 
123
        return buf;
 
124
}
 
125
 
 
126
int64_t strtoint64(const char *buf)
 
127
{
 
128
        int64_t x;
 
129
        for (x = 0; *buf != '\0'; ++buf)
 
130
                x = x * 10 + ((int64_t) *buf - 48);
 
131
        return x;
 
132
}
 
133
/* In windows, the first thing is to establish the TCP connection. */
 
134
int knet_win32_init()
 
135
{
 
136
        WSADATA wsaData;
 
137
        return WSAStartup(MAKEWORD(2, 2), &wsaData);
 
138
}
 
139
void knet_win32_destroy()
 
140
{
 
141
        WSACleanup();
 
142
}
 
143
/* A slightly modfied version of the following function also works on
 
144
 * Mac (and presummably Linux). However, this function is not stable on
 
145
 * my Mac. It sometimes works fine but sometimes does not. Therefore for
 
146
 * non-Windows OS, I do not use this one. */
 
147
static SOCKET socket_connect(const char *host, const char *port)
 
148
{
 
149
#define __err_connect(func)                                                                             \
 
150
        do {                                                                                                            \
 
151
                fprintf(stderr, "%s: %d\n", func, WSAGetLastError());   \
 
152
                return -1;                                                                                              \
 
153
        } while (0)
 
154
 
 
155
        int on = 1;
 
156
        SOCKET fd;
 
157
        struct linger lng = { 0, 0 };
 
158
        struct sockaddr_in server;
 
159
        struct hostent *hp = 0;
 
160
        // open socket
 
161
        if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) __err_connect("socket");
 
162
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)) == -1) __err_connect("setsockopt");
 
163
        if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&lng, sizeof(lng)) == -1) __err_connect("setsockopt");
 
164
        // get host info
 
165
        if (isalpha(host[0])) hp = gethostbyname(host);
 
166
        else {
 
167
                struct in_addr addr;
 
168
                addr.s_addr = inet_addr(host);
 
169
                hp = gethostbyaddr((char*)&addr, 4, AF_INET);
 
170
        }
 
171
        if (hp == 0) __err_connect("gethost");
 
172
        // connect
 
173
        server.sin_addr.s_addr = *((unsigned long*)hp->h_addr);
 
174
        server.sin_family= AF_INET;
 
175
        server.sin_port = htons(atoi(port));
 
176
        if (connect(fd, (struct sockaddr*)&server, sizeof(server)) != 0) __err_connect("connect");
 
177
        // freehostent(hp); // strangely in MSDN, hp is NOT freed (memory leak?!)
 
178
        return fd;
 
179
}
 
180
#endif
 
181
 
 
182
static off_t my_netread(int fd, void *buf, off_t len)
 
183
{
 
184
        off_t rest = len, curr, l = 0;
 
185
        /* recv() and read() may not read the required length of data with
 
186
         * one call. They have to be called repeatedly. */
 
187
        while (rest) {
 
188
                if (socket_wait(fd, 1) <= 0) break; // socket is not ready for reading
 
189
                curr = netread(fd, buf + l, rest);
 
190
                /* According to the glibc manual, section 13.2, a zero returned
 
191
                 * value indicates end-of-file (EOF), which should mean that
 
192
                 * read() will not return zero if EOF has not been met but data
 
193
                 * are not immediately available. */
 
194
                if (curr == 0) break;
 
195
                l += curr; rest -= curr;
 
196
        }
 
197
        return l;
 
198
}
 
199
 
 
200
/*************************
 
201
 * FTP specific routines *
 
202
 *************************/
 
203
 
 
204
static int kftp_get_response(knetFile *ftp)
 
205
{
 
206
#ifndef _WIN32
 
207
        unsigned char c;
 
208
#else
 
209
        char c;
 
210
#endif
 
211
        int n = 0;
 
212
        char *p;
 
213
        if (socket_wait(ftp->ctrl_fd, 1) <= 0) return 0;
 
214
        while (netread(ftp->ctrl_fd, &c, 1)) { // FIXME: this is *VERY BAD* for unbuffered I/O
 
215
                //fputc(c, stderr);
 
216
                if (n >= ftp->max_response) {
 
217
                        ftp->max_response = ftp->max_response? ftp->max_response<<1 : 256;
 
218
                        ftp->response = realloc(ftp->response, ftp->max_response);
 
219
                }
 
220
                ftp->response[n++] = c;
 
221
                if (c == '\n') {
 
222
                        if (n >= 4 && isdigit(ftp->response[0]) && isdigit(ftp->response[1]) && isdigit(ftp->response[2])
 
223
                                && ftp->response[3] != '-') break;
 
224
                        n = 0;
 
225
                        continue;
 
226
                }
 
227
        }
 
228
        if (n < 2) return -1;
 
229
        ftp->response[n-2] = 0;
 
230
        return strtol(ftp->response, &p, 0);
 
231
}
 
232
 
 
233
static int kftp_send_cmd(knetFile *ftp, const char *cmd, int is_get)
 
234
{
 
235
        if (socket_wait(ftp->ctrl_fd, 0) <= 0) return -1; // socket is not ready for writing
 
236
        netwrite(ftp->ctrl_fd, cmd, strlen(cmd));
 
237
        return is_get? kftp_get_response(ftp) : 0;
 
238
}
 
239
 
 
240
static int kftp_pasv_prep(knetFile *ftp)
 
241
{
 
242
        char *p;
 
243
        int v[6];
 
244
        kftp_send_cmd(ftp, "PASV\r\n", 1);
 
245
        for (p = ftp->response; *p && *p != '('; ++p);
 
246
        if (*p != '(') return -1;
 
247
        ++p;
 
248
        sscanf(p, "%d,%d,%d,%d,%d,%d", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]);
 
249
        memcpy(ftp->pasv_ip, v, 4 * sizeof(int));
 
250
        ftp->pasv_port = (v[4]<<8&0xff00) + v[5];
 
251
        return 0;
 
252
}
 
253
 
 
254
 
 
255
static int kftp_pasv_connect(knetFile *ftp)
 
256
{
 
257
        char host[80], port[10];
 
258
        if (ftp->pasv_port == 0) {
 
259
                fprintf(stderr, "[kftp_pasv_connect] kftp_pasv_prep() is not called before hand.\n");
 
260
                return -1;
 
261
        }
 
262
        sprintf(host, "%d.%d.%d.%d", ftp->pasv_ip[0], ftp->pasv_ip[1], ftp->pasv_ip[2], ftp->pasv_ip[3]);
 
263
        sprintf(port, "%d", ftp->pasv_port);
 
264
        ftp->fd = socket_connect(host, port);
 
265
        if (ftp->fd == -1) return -1;
 
266
        return 0;
 
267
}
 
268
 
 
269
int kftp_connect(knetFile *ftp)
 
270
{
 
271
        ftp->ctrl_fd = socket_connect(ftp->host, ftp->port);
 
272
        if (ftp->ctrl_fd == -1) return -1;
 
273
        kftp_get_response(ftp);
 
274
        kftp_send_cmd(ftp, "USER anonymous\r\n", 1);
 
275
        kftp_send_cmd(ftp, "PASS kftp@\r\n", 1);
 
276
        kftp_send_cmd(ftp, "TYPE I\r\n", 1);
 
277
        return 0;
 
278
}
 
279
 
 
280
int kftp_reconnect(knetFile *ftp)
 
281
{
 
282
        if (ftp->ctrl_fd != -1) {
 
283
                netclose(ftp->ctrl_fd);
 
284
                ftp->ctrl_fd = -1;
 
285
        }
 
286
        netclose(ftp->fd);
 
287
        ftp->fd = -1;
 
288
        return kftp_connect(ftp);
 
289
}
 
290
 
 
291
// initialize ->type, ->host, ->retr and ->size
 
292
knetFile *kftp_parse_url(const char *fn, const char *mode)
 
293
{
 
294
        knetFile *fp;
 
295
        char *p;
 
296
        int l;
 
297
        if (strstr(fn, "ftp://") != fn) return 0;
 
298
        for (p = (char*)fn + 6; *p && *p != '/'; ++p);
 
299
        if (*p != '/') return 0;
 
300
        l = p - fn - 6;
 
301
        fp = calloc(1, sizeof(knetFile));
 
302
        fp->type = KNF_TYPE_FTP;
 
303
        fp->fd = -1;
 
304
        /* the Linux/Mac version of socket_connect() also recognizes a port
 
305
         * like "ftp", but the Windows version does not. */
 
306
        fp->port = strdup("21");
 
307
        fp->host = calloc(l + 1, 1);
 
308
        if (strchr(mode, 'c')) fp->no_reconnect = 1;
 
309
        strncpy(fp->host, fn + 6, l);
 
310
        fp->retr = calloc(strlen(p) + 8, 1);
 
311
        sprintf(fp->retr, "RETR %s\r\n", p);
 
312
    fp->size_cmd = calloc(strlen(p) + 8, 1);
 
313
    sprintf(fp->size_cmd, "SIZE %s\r\n", p);
 
314
        fp->seek_offset = 0;
 
315
        return fp;
 
316
}
 
317
// place ->fd at offset off
 
318
int kftp_connect_file(knetFile *fp)
 
319
{
 
320
        int ret;
 
321
        long long file_size;
 
322
        if (fp->fd != -1) {
 
323
                netclose(fp->fd);
 
324
                if (fp->no_reconnect) kftp_get_response(fp);
 
325
        }
 
326
        kftp_pasv_prep(fp);
 
327
    kftp_send_cmd(fp, fp->size_cmd, 1);
 
328
#ifndef _WIN32
 
329
    if ( sscanf(fp->response,"%*d %lld", &file_size) != 1 )
 
330
    {
 
331
        fprintf(stderr,"[kftp_connect_file] %s\n", fp->response);
 
332
        return -1;
 
333
    }
 
334
#else
 
335
        const char *p = fp->response;
 
336
        while (*p != ' ') ++p;
 
337
        while (*p < '0' || *p > '9') ++p;
 
338
        file_size = strtoint64(p);
 
339
#endif
 
340
        fp->file_size = file_size;
 
341
        if (fp->offset>=0) {
 
342
                char tmp[32];
 
343
#ifndef _WIN32
 
344
                sprintf(tmp, "REST %lld\r\n", (long long)fp->offset);
 
345
#else
 
346
                strcpy(tmp, "REST ");
 
347
                int64tostr(tmp + 5, fp->offset);
 
348
                strcat(tmp, "\r\n");
 
349
#endif
 
350
                kftp_send_cmd(fp, tmp, 1);
 
351
        }
 
352
        kftp_send_cmd(fp, fp->retr, 0);
 
353
        kftp_pasv_connect(fp);
 
354
        ret = kftp_get_response(fp);
 
355
        if (ret != 150) {
 
356
                fprintf(stderr, "[kftp_connect_file] %s\n", fp->response);
 
357
                netclose(fp->fd);
 
358
                fp->fd = -1;
 
359
                return -1;
 
360
        }
 
361
        fp->is_ready = 1;
 
362
        return 0;
 
363
}
 
364
 
 
365
 
 
366
/**************************
 
367
 * HTTP specific routines *
 
368
 **************************/
 
369
 
 
370
knetFile *khttp_parse_url(const char *fn, const char *mode)
 
371
{
 
372
        knetFile *fp;
 
373
        char *p, *proxy, *q;
 
374
        int l;
 
375
        if (strstr(fn, "http://") != fn) return 0;
 
376
        // set ->http_host
 
377
        for (p = (char*)fn + 7; *p && *p != '/'; ++p);
 
378
        l = p - fn - 7;
 
379
        fp = calloc(1, sizeof(knetFile));
 
380
        fp->http_host = calloc(l + 1, 1);
 
381
        strncpy(fp->http_host, fn + 7, l);
 
382
        fp->http_host[l] = 0;
 
383
        for (q = fp->http_host; *q && *q != ':'; ++q);
 
384
        if (*q == ':') *q++ = 0;
 
385
        // get http_proxy
 
386
        proxy = getenv("http_proxy");
 
387
        // set ->host, ->port and ->path
 
388
        if (proxy == 0) {
 
389
                fp->host = strdup(fp->http_host); // when there is no proxy, server name is identical to http_host name.
 
390
                fp->port = strdup(*q? q : "80");
 
391
                fp->path = strdup(*p? p : "/");
 
392
        } else {
 
393
                fp->host = (strstr(proxy, "http://") == proxy)? strdup(proxy + 7) : strdup(proxy);
 
394
                for (q = fp->host; *q && *q != ':'; ++q);
 
395
                if (*q == ':') *q++ = 0; 
 
396
                fp->port = strdup(*q? q : "80");
 
397
                fp->path = strdup(fn);
 
398
        }
 
399
        fp->type = KNF_TYPE_HTTP;
 
400
        fp->ctrl_fd = fp->fd = -1;
 
401
        fp->seek_offset = 0;
 
402
        return fp;
 
403
}
 
404
 
 
405
int khttp_connect_file(knetFile *fp)
 
406
{
 
407
        int ret, l = 0;
 
408
        char *buf, *p;
 
409
        if (fp->fd != -1) netclose(fp->fd);
 
410
        fp->fd = socket_connect(fp->host, fp->port);
 
411
        buf = calloc(0x10000, 1); // FIXME: I am lazy... But in principle, 64KB should be large enough.
 
412
        l += sprintf(buf + l, "GET %s HTTP/1.0\r\nHost: %s\r\n", fp->path, fp->http_host);
 
413
    l += sprintf(buf + l, "Range: bytes=%lld-\r\n", (long long)fp->offset);
 
414
        l += sprintf(buf + l, "\r\n");
 
415
        netwrite(fp->fd, buf, l);
 
416
        l = 0;
 
417
        while (netread(fp->fd, buf + l, 1)) { // read HTTP header; FIXME: bad efficiency
 
418
                if (buf[l] == '\n' && l >= 3)
 
419
                        if (strncmp(buf + l - 3, "\r\n\r\n", 4) == 0) break;
 
420
                ++l;
 
421
        }
 
422
        buf[l] = 0;
 
423
        if (l < 14) { // prematured header
 
424
                netclose(fp->fd);
 
425
                fp->fd = -1;
 
426
                return -1;
 
427
        }
 
428
        ret = strtol(buf + 8, &p, 0); // HTTP return code
 
429
        if (ret == 200 && fp->offset>0) { // 200 (complete result); then skip beginning of the file
 
430
                off_t rest = fp->offset;
 
431
                while (rest) {
 
432
                        off_t l = rest < 0x10000? rest : 0x10000;
 
433
                        rest -= my_netread(fp->fd, buf, l);
 
434
                }
 
435
        } else if (ret != 206 && ret != 200) {
 
436
                free(buf);
 
437
                fprintf(stderr, "[khttp_connect_file] fail to open file (HTTP code: %d).\n", ret);
 
438
                netclose(fp->fd);
 
439
                fp->fd = -1;
 
440
                return -1;
 
441
        }
 
442
        free(buf);
 
443
        fp->is_ready = 1;
 
444
        return 0;
 
445
}
 
446
 
 
447
/********************
 
448
 * Generic routines *
 
449
 ********************/
 
450
 
 
451
knetFile *knet_open(const char *fn, const char *mode)
 
452
{
 
453
        knetFile *fp = 0;
 
454
        if (mode[0] != 'r') {
 
455
                fprintf(stderr, "[kftp_open] only mode \"r\" is supported.\n");
 
456
                return 0;
 
457
        }
 
458
        if (strstr(fn, "ftp://") == fn) {
 
459
                fp = kftp_parse_url(fn, mode);
 
460
                if (fp == 0) return 0;
 
461
                if (kftp_connect(fp) == -1) {
 
462
                        knet_close(fp);
 
463
                        return 0;
 
464
                }
 
465
                kftp_connect_file(fp);
 
466
        } else if (strstr(fn, "http://") == fn) {
 
467
                fp = khttp_parse_url(fn, mode);
 
468
                if (fp == 0) return 0;
 
469
                khttp_connect_file(fp);
 
470
        } else { // local file
 
471
#ifdef _WIN32
 
472
                /* In windows, O_BINARY is necessary. In Linux/Mac, O_BINARY may
 
473
                 * be undefined on some systems, although it is defined on my
 
474
                 * Mac and the Linux I have tested on. */
 
475
                int fd = open(fn, O_RDONLY | O_BINARY);
 
476
#else           
 
477
                int fd = open(fn, O_RDONLY);
 
478
#endif
 
479
                if (fd == -1) {
 
480
                        perror("open");
 
481
                        return 0;
 
482
                }
 
483
                fp = (knetFile*)calloc(1, sizeof(knetFile));
 
484
                fp->type = KNF_TYPE_LOCAL;
 
485
                fp->fd = fd;
 
486
                fp->ctrl_fd = -1;
 
487
        }
 
488
        if (fp && fp->fd == -1) {
 
489
                knet_close(fp);
 
490
                return 0;
 
491
        }
 
492
        return fp;
 
493
}
 
494
 
 
495
knetFile *knet_dopen(int fd, const char *mode)
 
496
{
 
497
        knetFile *fp = (knetFile*)calloc(1, sizeof(knetFile));
 
498
        fp->type = KNF_TYPE_LOCAL;
 
499
        fp->fd = fd;
 
500
        return fp;
 
501
}
 
502
 
 
503
off_t knet_read(knetFile *fp, void *buf, off_t len)
 
504
{
 
505
        off_t l = 0;
 
506
        if (fp->fd == -1) return 0;
 
507
        if (fp->type == KNF_TYPE_FTP) {
 
508
                if (fp->is_ready == 0) {
 
509
                        if (!fp->no_reconnect) kftp_reconnect(fp);
 
510
                        kftp_connect_file(fp);
 
511
                }
 
512
        } else if (fp->type == KNF_TYPE_HTTP) {
 
513
                if (fp->is_ready == 0)
 
514
                        khttp_connect_file(fp);
 
515
        }
 
516
        if (fp->type == KNF_TYPE_LOCAL) { // on Windows, the following block is necessary; not on UNIX
 
517
                off_t rest = len, curr;
 
518
                while (rest) {
 
519
                        do {
 
520
                                curr = read(fp->fd, buf + l, rest);
 
521
                        } while (curr < 0 && EINTR == errno);
 
522
                        if (curr < 0) return -1;
 
523
                        if (curr == 0) break;
 
524
                        l += curr; rest -= curr;
 
525
                }
 
526
        } else l = my_netread(fp->fd, buf, len);
 
527
        fp->offset += l;
 
528
        return l;
 
529
}
 
530
 
 
531
off_t knet_seek(knetFile *fp, int64_t off, int whence)
 
532
{
 
533
        if (whence == SEEK_SET && off == fp->offset) return 0;
 
534
        if (fp->type == KNF_TYPE_LOCAL) {
 
535
                /* Be aware that lseek() returns the offset after seeking,
 
536
                 * while fseek() returns zero on success. */
 
537
                off_t offset = lseek(fp->fd, off, whence);
 
538
                if (offset == -1) {
 
539
            // Be silent, it is OK for knet_seek to fail when the file is streamed
 
540
            // fprintf(stderr,"[knet_seek] %s\n", strerror(errno));
 
541
                        return -1;
 
542
                }
 
543
                fp->offset = offset;
 
544
                return 0;
 
545
        }
 
546
    else if (fp->type == KNF_TYPE_FTP) 
 
547
    {
 
548
        if (whence==SEEK_CUR)
 
549
            fp->offset += off;
 
550
        else if (whence==SEEK_SET)
 
551
            fp->offset = off;
 
552
        else if ( whence==SEEK_END)
 
553
            fp->offset = fp->file_size+off;
 
554
                fp->is_ready = 0;
 
555
                return 0;
 
556
        } 
 
557
    else if (fp->type == KNF_TYPE_HTTP) 
 
558
    {
 
559
                if (whence == SEEK_END) { // FIXME: can we allow SEEK_END in future?
 
560
                        fprintf(stderr, "[knet_seek] SEEK_END is not supported for HTTP. Offset is unchanged.\n");
 
561
                        errno = ESPIPE;
 
562
                        return -1;
 
563
                }
 
564
        if (whence==SEEK_CUR)
 
565
            fp->offset += off;
 
566
        else if (whence==SEEK_SET)
 
567
            fp->offset = off;
 
568
                fp->is_ready = 0;
 
569
                return 0;
 
570
        }
 
571
        errno = EINVAL;
 
572
    fprintf(stderr,"[knet_seek] %s\n", strerror(errno));
 
573
        return -1;
 
574
}
 
575
 
 
576
int knet_close(knetFile *fp)
 
577
{
 
578
        if (fp == 0) return 0;
 
579
        if (fp->ctrl_fd != -1) netclose(fp->ctrl_fd); // FTP specific
 
580
        if (fp->fd != -1) {
 
581
                /* On Linux/Mac, netclose() is an alias of close(), but on
 
582
                 * Windows, it is an alias of closesocket(). */
 
583
                if (fp->type == KNF_TYPE_LOCAL) close(fp->fd);
 
584
                else netclose(fp->fd);
 
585
        }
 
586
        free(fp->host); free(fp->port);
 
587
        free(fp->response); free(fp->retr); // FTP specific
 
588
        free(fp->path); free(fp->http_host); // HTTP specific
 
589
        free(fp);
 
590
        return 0;
 
591
}
 
592
 
 
593
#ifdef KNETFILE_MAIN
 
594
int main(void)
 
595
{
 
596
        char *buf;
 
597
        knetFile *fp;
 
598
        int type = 4, l;
 
599
#ifdef _WIN32
 
600
        knet_win32_init();
 
601
#endif
 
602
        buf = calloc(0x100000, 1);
 
603
        if (type == 0) {
 
604
                fp = knet_open("knetfile.c", "r");
 
605
                knet_seek(fp, 1000, SEEK_SET);
 
606
        } else if (type == 1) { // NCBI FTP, large file
 
607
                fp = knet_open("ftp://ftp.ncbi.nih.gov/1000genomes/ftp/data/NA12878/alignment/NA12878.chrom6.SLX.SRP000032.2009_06.bam", "r");
 
608
                knet_seek(fp, 2500000000ll, SEEK_SET);
 
609
                l = knet_read(fp, buf, 255);
 
610
        } else if (type == 2) {
 
611
                fp = knet_open("ftp://ftp.sanger.ac.uk/pub4/treefam/tmp/index.shtml", "r");
 
612
                knet_seek(fp, 1000, SEEK_SET);
 
613
        } else if (type == 3) {
 
614
                fp = knet_open("http://www.sanger.ac.uk/Users/lh3/index.shtml", "r");
 
615
                knet_seek(fp, 1000, SEEK_SET);
 
616
        } else if (type == 4) {
 
617
                fp = knet_open("http://www.sanger.ac.uk/Users/lh3/ex1.bam", "r");
 
618
                knet_read(fp, buf, 10000);
 
619
                knet_seek(fp, 20000, SEEK_SET);
 
620
                knet_seek(fp, 10000, SEEK_SET);
 
621
                l = knet_read(fp, buf+10000, 10000000) + 10000;
 
622
        }
 
623
        if (type != 4 && type != 1) {
 
624
                knet_read(fp, buf, 255);
 
625
                buf[255] = 0;
 
626
                printf("%s\n", buf);
 
627
        } else write(fileno(stdout), buf, l);
 
628
        knet_close(fp);
 
629
        free(buf);
 
630
        return 0;
 
631
}
 
632
#endif