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