NVBIO
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
knetfile.c
Go to the documentation of this file.
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 "htslib/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, (void*)((char*)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 = (char*)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  int len = strlen(cmd);
237  if ( netwrite(ftp->ctrl_fd, cmd, len) != len ) return -1;
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 
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 
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 = (knetFile*)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 = (char*)calloc(l + 1, 1);
309  if (strchr(mode, 'c')) fp->no_reconnect = 1;
310  strncpy(fp->host, fn + 6, l);
311  fp->retr = (char*)calloc(strlen(p) + 8, 1);
312  sprintf(fp->retr, "RETR %s\r\n", p);
313  fp->size_cmd = (char*)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
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 = (knetFile*)calloc(1, sizeof(knetFile));
381  fp->http_host = (char*)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 
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 = (char*)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  if ( netwrite(fp->fd, buf, l) != l ) return -1;
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 ssize_t knet_read(knetFile *fp, void *buf, size_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  size_t rest = len;
519  ssize_t curr;
520  while (rest) {
521  do {
522  curr = read(fp->fd, (void*)((char*)buf + l), rest);
523  } while (curr < 0 && EINTR == errno);
524  if (curr < 0) return -1;
525  if (curr == 0) break;
526  l += curr; rest -= curr;
527  }
528  } else l = my_netread(fp->fd, buf, len);
529  fp->offset += l;
530  return l;
531 }
532 
533 off_t knet_seek(knetFile *fp, off_t off, int whence)
534 {
535  if (whence == SEEK_SET && off == fp->offset) return 0;
536  if (fp->type == KNF_TYPE_LOCAL) {
537  /* Be aware that lseek() returns the offset after seeking, while fseek() returns zero on success. */
538  off_t offset = lseek(fp->fd, off, whence);
539  if (offset == -1) return -1;
540  fp->offset = offset;
541  return fp->offset;
542  } else if (fp->type == KNF_TYPE_FTP) {
543  if (whence == SEEK_CUR) fp->offset += off;
544  else if (whence == SEEK_SET) fp->offset = off;
545  else if (whence == SEEK_END) fp->offset = fp->file_size + off;
546  else return -1;
547  fp->is_ready = 0;
548  return fp->offset;
549  } else if (fp->type == KNF_TYPE_HTTP) {
550  if (whence == SEEK_END) { // FIXME: can we allow SEEK_END in future?
551  fprintf(stderr, "[knet_seek] SEEK_END is not supported for HTTP. Offset is unchanged.\n");
552  errno = ESPIPE;
553  return -1;
554  }
555  if (whence == SEEK_CUR) fp->offset += off;
556  else if (whence == SEEK_SET) fp->offset = off;
557  else return -1;
558  fp->is_ready = 0;
559  return fp->offset;
560  }
561  errno = EINVAL;
562  fprintf(stderr,"[knet_seek] %s\n", strerror(errno));
563  return -1;
564 }
565 
567 {
568  if (fp == 0) return 0;
569  if (fp->ctrl_fd != -1) netclose(fp->ctrl_fd); // FTP specific
570  if (fp->fd != -1) {
571  /* On Linux/Mac, netclose() is an alias of close(), but on
572  * Windows, it is an alias of closesocket(). */
573  if (fp->type == KNF_TYPE_LOCAL) close(fp->fd);
574  else netclose(fp->fd);
575  }
576  free(fp->host); free(fp->port);
577  free(fp->response); free(fp->retr); // FTP specific
578  free(fp->path); free(fp->http_host); // HTTP specific
579  free(fp);
580  return 0;
581 }
582 
583 #ifdef KNETFILE_MAIN
584 int main(void)
585 {
586  char *buf;
587  knetFile *fp;
588  int type = 4, l;
589 #ifdef _WIN32
590  knet_win32_init();
591 #endif
592  buf = calloc(0x100000, 1);
593  if (type == 0) {
594  fp = knet_open("knetfile.c", "r");
595  knet_seek(fp, 1000, SEEK_SET);
596  } else if (type == 1) { // NCBI FTP, large file
597  fp = knet_open("ftp://ftp.ncbi.nih.gov/1000genomes/ftp/data/NA12878/alignment/NA12878.chrom6.SLX.SRP000032.2009_06.bam", "r");
598  knet_seek(fp, 2500000000ll, SEEK_SET);
599  l = knet_read(fp, buf, 255);
600  } else if (type == 2) {
601  fp = knet_open("ftp://ftp.sanger.ac.uk/pub4/treefam/tmp/index.shtml", "r");
602  knet_seek(fp, 1000, SEEK_SET);
603  } else if (type == 3) {
604  fp = knet_open("http://www.sanger.ac.uk/Users/lh3/index.shtml", "r");
605  knet_seek(fp, 1000, SEEK_SET);
606  } else if (type == 4) {
607  fp = knet_open("http://www.sanger.ac.uk/Users/lh3/ex1.bam", "r");
608  knet_read(fp, buf, 10000);
609  knet_seek(fp, 20000, SEEK_SET);
610  knet_seek(fp, 10000, SEEK_SET);
611  l = knet_read(fp, buf+10000, 10000000) + 10000;
612  }
613  if (type != 4 && type != 1) {
614  knet_read(fp, buf, 255);
615  buf[255] = 0;
616  printf("%s\n", buf);
617  } else write(fileno(stdout), buf, l);
618  knet_close(fp);
619  free(buf);
620  return 0;
621 }
622 #endif