X-Git-Url: http://woldlab.caltech.edu/gitweb/?p=samtools.git;a=blobdiff_plain;f=knetfile.c;h=af091465862c238aefeb3d0562b03f75320b6c1d;hp=e110aa720f6577e7f8dee34fddc32cb6220d7154;hb=5075acdfc387c56ca8411cde3e419e867da9804d;hpb=4a17fa7e1f91b2fe04ad334a63fc2b0d5e859d8a diff --git a/knetfile.c b/knetfile.c index e110aa7..af09146 100644 --- a/knetfile.c +++ b/knetfile.c @@ -1,6 +1,7 @@ /* The MIT License - Copyright (c) 2008 Genome Research Ltd (GRL). + Copyright (c) 2008 by Genome Research Ltd (GRL). + 2010 by Attractive Chaos Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -23,23 +24,20 @@ SOFTWARE. */ -/* Contact: Heng Li */ - /* Probably I will not do socket programming in the next few years and therefore I decide to heavily annotate this file, for Linux and - Windows as well. -lh3 */ + Windows as well. -ac */ #include #include #include #include #include +#include #include #include -#ifdef _WIN32 -#include -#else +#ifndef _WIN32 #include #include #include @@ -70,7 +68,14 @@ static int socket_wait(int fd, int is_read) if (is_read) fdr = &fds; else fdw = &fds; ret = select(fd+1, fdr, fdw, 0, &tv); +#ifndef _WIN32 if (ret == -1) perror("select"); +#else + if (ret == 0) + fprintf(stderr, "select time-out\n"); + else if (ret == SOCKET_ERROR) + fprintf(stderr, "select: %d\n", WSAGetLastError()); +#endif return ret; } @@ -84,7 +89,7 @@ static int socket_connect(const char *host, const char *port) int on = 1, fd; struct linger lng = { 0, 0 }; - struct addrinfo hints, *res; + struct addrinfo hints, *res = 0; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; @@ -103,16 +108,28 @@ static int socket_connect(const char *host, const char *port) } #else /* MinGW's printf has problem with "%lld" */ -char *uint64tostr(char *buf, uint64_t x) +char *int64tostr(char *buf, int64_t x) { - int i, cnt; - for (i = 0; x; x /= 10) buf[i++] = '0' + x%10; + int cnt; + int i = 0; + do { + buf[i++] = '0' + x % 10; + x /= 10; + } while (x); buf[i] = 0; for (cnt = i, i = 0; i < cnt/2; ++i) { int c = buf[i]; buf[i] = buf[cnt-i-1]; buf[cnt-i-1] = c; } return buf; } + +int64_t strtoint64(const char *buf) +{ + int64_t x; + for (x = 0; *buf != '\0'; ++buf) + x = x * 10 + ((int64_t) *buf - 48); + return x; +} /* In windows, the first thing is to establish the TCP connection. */ int knet_win32_init() { @@ -129,7 +146,11 @@ void knet_win32_destroy() * non-Windows OS, I do not use this one. */ static SOCKET socket_connect(const char *host, const char *port) { -#define __err_connect(func) do { perror(func); return -1; } while (0) +#define __err_connect(func) \ + do { \ + fprintf(stderr, "%s: %d\n", func, WSAGetLastError()); \ + return -1; \ + } while (0) int on = 1; SOCKET fd; @@ -182,7 +203,11 @@ static off_t my_netread(int fd, void *buf, off_t len) static int kftp_get_response(knetFile *ftp) { +#ifndef _WIN32 unsigned char c; +#else + char c; +#endif int n = 0; char *p; if (socket_wait(ftp->ctrl_fd, 1) <= 0) return 0; @@ -259,10 +284,11 @@ int kftp_reconnect(knetFile *ftp) ftp->ctrl_fd = -1; } netclose(ftp->fd); + ftp->fd = -1; return kftp_connect(ftp); } -// initialize ->type, ->host and ->retr +// initialize ->type, ->host, ->retr and ->size knetFile *kftp_parse_url(const char *fn, const char *mode) { knetFile *fp; @@ -283,25 +309,42 @@ knetFile *kftp_parse_url(const char *fn, const char *mode) strncpy(fp->host, fn + 6, l); fp->retr = calloc(strlen(p) + 8, 1); sprintf(fp->retr, "RETR %s\r\n", p); - fp->seek_offset = -1; + fp->size_cmd = calloc(strlen(p) + 8, 1); + sprintf(fp->size_cmd, "SIZE %s\r\n", p); + fp->seek_offset = 0; return fp; } // place ->fd at offset off int kftp_connect_file(knetFile *fp) { int ret; + long long file_size; if (fp->fd != -1) { netclose(fp->fd); if (fp->no_reconnect) kftp_get_response(fp); } kftp_pasv_prep(fp); - if (fp->offset) { + kftp_send_cmd(fp, fp->size_cmd, 1); +#ifndef _WIN32 + if ( sscanf(fp->response,"%*d %lld", &file_size) != 1 ) + { + fprintf(stderr,"[kftp_connect_file] %s\n", fp->response); + return -1; + } +#else + const char *p = fp->response; + while (*p != ' ') ++p; + while (*p < '0' || *p > '9') ++p; + file_size = strtoint64(p); +#endif + fp->file_size = file_size; + if (fp->offset>=0) { char tmp[32]; #ifndef _WIN32 sprintf(tmp, "REST %lld\r\n", (long long)fp->offset); #else strcpy(tmp, "REST "); - uint64tostr(tmp + 5, fp->offset); + int64tostr(tmp + 5, fp->offset); strcat(tmp, "\r\n"); #endif kftp_send_cmd(fp, tmp, 1); @@ -319,6 +362,7 @@ int kftp_connect_file(knetFile *fp) return 0; } + /************************** * HTTP specific routines * **************************/ @@ -354,7 +398,7 @@ knetFile *khttp_parse_url(const char *fn, const char *mode) } fp->type = KNF_TYPE_HTTP; fp->ctrl_fd = fp->fd = -1; - fp->seek_offset = -1; + fp->seek_offset = 0; return fp; } @@ -366,8 +410,7 @@ int khttp_connect_file(knetFile *fp) fp->fd = socket_connect(fp->host, fp->port); buf = calloc(0x10000, 1); // FIXME: I am lazy... But in principle, 64KB should be large enough. l += sprintf(buf + l, "GET %s HTTP/1.0\r\nHost: %s\r\n", fp->path, fp->http_host); - if (fp->offset) - l += sprintf(buf + l, "Range: bytes=%lld-\r\n", (long long)fp->offset); + l += sprintf(buf + l, "Range: bytes=%lld-\r\n", (long long)fp->offset); l += sprintf(buf + l, "\r\n"); netwrite(fp->fd, buf, l); l = 0; @@ -383,7 +426,7 @@ int khttp_connect_file(knetFile *fp) return -1; } ret = strtol(buf + 8, &p, 0); // HTTP return code - if (ret == 200 && fp->offset) { // 200 (complete result); then skip beginning of the file + if (ret == 200 && fp->offset>0) { // 200 (complete result); then skip beginning of the file off_t rest = fp->offset; while (rest) { off_t l = rest < 0x10000? rest : 0x10000; @@ -473,7 +516,10 @@ off_t knet_read(knetFile *fp, void *buf, off_t len) if (fp->type == KNF_TYPE_LOCAL) { // on Windows, the following block is necessary; not on UNIX off_t rest = len, curr; while (rest) { - curr = read(fp->fd, buf + l, rest); + do { + curr = read(fp->fd, buf + l, rest); + } while (curr < 0 && EINTR == errno); + if (curr < 0) return -1; if (curr == 0) break; l += curr; rest -= curr; } @@ -482,7 +528,7 @@ off_t knet_read(knetFile *fp, void *buf, off_t len) return l; } -int knet_seek(knetFile *fp, off_t off, int whence) +off_t knet_seek(knetFile *fp, int64_t off, int whence) { if (whence == SEEK_SET && off == fp->offset) return 0; if (fp->type == KNF_TYPE_LOCAL) { @@ -490,20 +536,40 @@ int knet_seek(knetFile *fp, off_t off, int whence) * while fseek() returns zero on success. */ off_t offset = lseek(fp->fd, off, whence); if (offset == -1) { - perror("lseek"); + // Be silent, it is OK for knet_seek to fail when the file is streamed + // fprintf(stderr,"[knet_seek] %s\n", strerror(errno)); return -1; } fp->offset = offset; return 0; - } else if (fp->type == KNF_TYPE_FTP || fp->type == KNF_TYPE_HTTP) { - if (whence != SEEK_SET) { // FIXME: we can surely allow SEEK_CUR and SEEK_END in future - fprintf(stderr, "[knet_seek] only SEEK_SET is supported for FTP/HTTP. Offset is unchanged.\n"); + } + else if (fp->type == KNF_TYPE_FTP) + { + if (whence==SEEK_CUR) + fp->offset += off; + else if (whence==SEEK_SET) + fp->offset = off; + else if ( whence==SEEK_END) + fp->offset = fp->file_size+off; + fp->is_ready = 0; + return 0; + } + else if (fp->type == KNF_TYPE_HTTP) + { + if (whence == SEEK_END) { // FIXME: can we allow SEEK_END in future? + fprintf(stderr, "[knet_seek] SEEK_END is not supported for HTTP. Offset is unchanged.\n"); + errno = ESPIPE; return -1; } - fp->offset = off; + if (whence==SEEK_CUR) + fp->offset += off; + else if (whence==SEEK_SET) + fp->offset = off; fp->is_ready = 0; return 0; } + errno = EINVAL; + fprintf(stderr,"[knet_seek] %s\n", strerror(errno)); return -1; }