Daniel Egger made CURLOPT_RANGE work on file:// URLs the very same way it
authorDaniel Stenberg <daniel@haxx.se>
Fri, 11 Jan 2008 14:20:41 +0000 (14:20 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 11 Jan 2008 14:20:41 +0000 (14:20 +0000)
already worked for FTP:// URLs

CHANGES
RELEASE-NOTES
docs/curl.1
docs/libcurl/curl_easy_setopt.3
lib/file.c
tests/data/Makefile.am
tests/data/test1016 [new file with mode: 0644]
tests/data/test1017 [new file with mode: 0644]
tests/data/test1018 [new file with mode: 0644]
tests/data/test1019 [new file with mode: 0644]
tests/data/test1020 [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index 032aac0..203d946 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,9 @@
                                   Changelog
 
 Daniel S (11 Jan 2008)
+- Daniel Egger made CURLOPT_RANGE work on file:// URLs the very same way it
+  already worked for FTP:// URLs.
+
 - I made the curl tool switch from using CURLOPT_IOCTLFUNCTION to now use the
   spanking new CURLOPT_SEEKFUNCTION simply to take advantage of the improved
   performance for the upload resume cases where you want to upload the last
index 8013284..49f540f 100644 (file)
@@ -49,6 +49,7 @@ This release includes the following bugfixes:
  o time zone offsets from -1400 to +1400 are now accepted by the date parser
  o allows more spaces in WWW/Proxy-Authenticate: headers
  o curl-config --libs skips /usr/lib64
+ o range support for file:// transfers
 
 This release includes the following known bugs:
 
index 2f6d399..0babaaa 100644 (file)
@@ -974,9 +974,9 @@ This option can be used multiple times.
 random data. The data is used to seed the random engine for SSL connections.
 See also the \fI--egd-file\fP option.
 .IP "-r/--range <range>"
-(HTTP/FTP)
-Retrieve a byte range (i.e a partial document) from a HTTP/1.1 or FTP
-server. Ranges can be specified in a number of ways.
+(HTTP/FTP/FILE) Retrieve a byte range (i.e a partial document) from a
+HTTP/1.1, FTP server or a local FILE. Ranges can be specified in a number of
+ways.
 .RS
 .TP 10
 .B 0-499
index 56b763f..dec4371 100644 (file)
@@ -1119,6 +1119,8 @@ transfers also support several intervals, separated with commas as in
 \fI"X-Y,N-M"\fP. Using this kind of multiple intervals will cause the HTTP
 server to send the response document in pieces (using standard MIME separation
 techniques). Pass a NULL to this option to disable the use of ranges.
+
+Ranges work on HTTP, FTP and FILE (since 7.18.0) transfers only.
 .IP CURLOPT_RESUME_FROM
 Pass a long as parameter. It contains the offset in number of bytes that you
 want the transfer to start from. Set this option to 0 to make the transfer
index 8582264..cbc9f4c 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -70,6 +70,7 @@
 
 #endif
 
+#include "strtoofft.h"
 #include "urldata.h"
 #include <curl/curl.h>
 #include "progress.h"
@@ -119,6 +120,61 @@ const struct Curl_handler Curl_handler_file = {
   PROT_FILE                             /* protocol */
 };
 
+
+ /*
+  Check if this is a range download, and if so, set the internal variables
+  properly. This code is copied from the FTP implementation and might as
+  well be factored out.
+ */
+static CURLcode file_range(struct connectdata *conn)
+{
+  curl_off_t from, to;
+  curl_off_t totalsize=-1;
+  char *ptr;
+  char *ptr2;
+  struct SessionHandle *data = conn->data;
+
+  if(data->state.use_range && data->state.range) {
+    from=curlx_strtoofft(data->state.range, &ptr, 0);
+    while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
+      ptr++;
+    to=curlx_strtoofft(ptr, &ptr2, 0);
+    if(ptr == ptr2) {
+      /* we didn't get any digit */
+      to=-1;
+    }
+    if((-1 == to) && (from>=0)) {
+      /* X - */
+      data->state.resume_from = from;
+      DEBUGF(infof(data, "RANGE %" FORMAT_OFF_T " to end of file\n",
+                   from));
+    }
+    else if(from < 0) {
+      /* -Y */
+      totalsize = -from;
+      data->req.maxdownload = -from;
+      data->state.resume_from = from;
+      DEBUGF(infof(data, "RANGE the last %" FORMAT_OFF_T " bytes\n",
+                   totalsize));
+    }
+    else {
+      /* X-Y */
+      totalsize = to-from;
+      data->req.maxdownload = totalsize+1; /* include last byte */
+      data->state.resume_from = from;
+      DEBUGF(infof(data, "RANGE from %" FORMAT_OFF_T
+                   " getting %" FORMAT_OFF_T " bytes\n",
+                   from, data->req.maxdownload));
+    }
+    DEBUGF(infof(data, "range-download from %" FORMAT_OFF_T
+                 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
+                 from, to, data->req.maxdownload));
+  }
+  else
+    data->req.maxdownload = -1;
+  return CURLE_OK;
+}
+
 /*
  * file_connect() gets called from Curl_protocol_connect() to allow us to
  * do protocol-specific actions at connect-time.  We emulate a
@@ -287,8 +343,8 @@ static CURLcode file_upload(struct connectdata *conn)
     Curl_pgrsSetUploadSize(data, data->set.infilesize);
 
   /* treat the negative resume offset value as the case of "-" */
-  if(data->state.resume_from < 0){
-    if(stat(file->path, &file_stat)){
+  if(data->state.resume_from < 0) {
+    if(stat(file->path, &file_stat)) {
       fclose(fp);
       failf(data, "Can't get the size of %s", file->path);
       return CURLE_WRITE_ERROR;
@@ -434,6 +490,20 @@ static CURLcode file_do(struct connectdata *conn, bool *done)
     return result;
   }
 
+  /* Check whether file range has been specified */
+  file_range(conn);
+
+  /* Adjust the start offset in case we want to get the N last bytes
+   * of the stream iff the filesize could be determined */
+  if(data->state.resume_from < 0) {
+    if(!fstated) {
+      failf(data, "Can't get the size of file.");
+      return CURLE_READ_ERROR;
+    }
+    else
+      data->state.resume_from += (curl_off_t)statbuf.st_size;
+  }
+
   if(data->state.resume_from <= expected_size)
     expected_size -= data->state.resume_from;
   else {
@@ -441,6 +511,10 @@ static CURLcode file_do(struct connectdata *conn, bool *done)
     return CURLE_BAD_DOWNLOAD_RESUME;
   }
 
+  /* A high water mark has been specified so we obey... */
+  if (data->req.maxdownload > 0)
+    expected_size = data->req.maxdownload;
+
   if(fstated && (expected_size == 0))
     return CURLE_OK;
 
@@ -460,15 +534,20 @@ static CURLcode file_do(struct connectdata *conn, bool *done)
   Curl_pgrsTime(data, TIMER_STARTTRANSFER);
 
   while(res == CURLE_OK) {
-    nread = read(fd, buf, BUFSIZE-1);
+    /* Don't fill a whole buffer if we want less than all data */
+    if (expected_size < BUFSIZE-1)
+      nread = read(fd, buf, expected_size);
+    else
+      nread = read(fd, buf, BUFSIZE-1);
 
     if( nread > 0)
       buf[nread] = 0;
 
-    if(nread <= 0)
+    if (nread <= 0 || expected_size == 0)
       break;
 
     bytecount += nread;
+    expected_size -= nread;
 
     res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
     if(res)
index 4c214bc..87a41e4 100644 (file)
@@ -28,26 +28,26 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46         \
  test194 test195 test196 test197 test198 test515 test516 test517 test518   \
  test210 test211 test212 test220 test221 test222 test223 test224 test206   \
  test207 test208 test209 test213 test240 test241 test242 test519 test214   \
- test215 test216 test217 test218 test199 test225 test226 test227          \
- test228 test229 test233 test234 test235 test236 test520                  \
- test237 test238 test239 test243 test245 test246 test247 test248 test249   \
- test250 test251 test252 test253 test254 test255 test521 test522 test523   \
- test256 test257 test258 test259 test260 test261 test262 test263 test264   \
- test265 test266 test267 test268 test269 test270 test271 test272 test273   \
- test274 test275 test524 test525 test276 test277 test526 test527 test528   \
- test530 DISABLED test278 test279 test531 test280 test529 test532 test533  \
- test534 test535 test281 test537 test282 test283 test284 test538 test285   \
- test286 test307 test308 test287 test400 test288 test600 test601 test602   \
- test603 test401 test402 test290 test291 test292 test293 test403 test404   \
- test405 test604 test605 test606 test607 test608 test609 test294 test295   \
- test296 test297 test298 test610 test611 test612 test406 test407 test408   \
- test409 test613 test614 test700 test701 test702 test704 test705 test703   \
- test706 test707 test350 test351 test352 test353 test289 test540 test354   \
- test231 test1000 test1001 test1002 test1003 test1004 test1005 test1006    \
- test615 test1007 test541 test1010 test1011 test1012 test542 test543       \
- test536 test1008 test1009 test2000 test2001 test2002 test2003 test35      \
- test544 test545 test2004 test546 test1013 test1014 test1015 \
- test547 test548 test549 test550 test551 test552
+ test215 test216 test217 test218 test199 test225 test226 test227 test228   \
+ test229 test233 test234 test235 test236 test520 test237 test238 test239   \
+ test243 test245 test246 test247 test248 test249 test250 test251 test252   \
+ test253 test254 test255 test521 test522 test523 test256 test257 test258   \
+ test259 test260 test261 test262 test263 test264 test265 test266 test267   \
+ test268 test269 test270 test271 test272 test273 test274 test275 test524   \
+ test525 test276 test277 test526 test527 test528 test530 DISABLED test278  \
+ test279 test531 test280 test529 test532 test533 test534 test535 test281   \
+ test537 test282 test283 test284 test538 test285 test286 test307 test308   \
+ test287 test400 test288 test600 test601 test602 test603 test401 test402   \
+ test290 test291 test292 test293 test403 test404 test405 test604 test605   \
+ test606 test607 test608 test609 test294 test295 test296 test297 test298   \
+ test610 test611 test612 test406 test407 test408 test409 test613 test614   \
+ test700 test701 test702 test704 test705 test703 test706 test707 test350   \
+ test351 test352 test353 test289 test540 test354 test231 test1000 test1001 \
+ test1002 test1003 test1004 test1005 test1006 test615 test1007 test541    \
+ test1010 test1011 test1012 test542 test543 test536 test1008 test1009     \
+ test2000 test2001 test2002 test2003 test35 test544 test545 test2004      \
+ test546 test1013 test1014 test1015 test547 test548 test549 test550       \
+ test551 test552 test1016 test1017 test1018 test1019 test1020
 
 filecheck:
        @mkdir test-place; \
diff --git a/tests/data/test1016 b/tests/data/test1016
new file mode 100644 (file)
index 0000000..4b8fc62
--- /dev/null
@@ -0,0 +1,35 @@
+<testcase>
+<info>
+<keywords>
+FILE
+</keywords>
+</info>
+
+<reply>
+<data>
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+none
+</server>
+ <name>
+X-Y range on a file:// URL to stdout
+ </name>
+ <command>
+-r 1-4 file://localhost/%PWD/log/test1016.txt 
+</command>
+<file name="log/test1016.txt">
+1234567890
+</file>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<stdout nonewline="yes">
+2345
+</stdout>
+</verify>
+</testcase>
diff --git a/tests/data/test1017 b/tests/data/test1017
new file mode 100644 (file)
index 0000000..8c4c0a8
--- /dev/null
@@ -0,0 +1,35 @@
+<testcase>
+<info>
+<keywords>
+FILE
+</keywords>
+</info>
+# Server-side
+<reply>
+<data>
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+none
+</server>
+ <name>
+0-Y range on a file:// URL to stdout
+ </name>
+ <command>
+-r 0-3 file://localhost/%PWD/log/test1017.txt 
+</command>
+<file name="log/test1017.txt">
+1234567890
+</file>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<stdout nonewline="yes">
+1234
+</stdout>
+</verify>
+</testcase>
diff --git a/tests/data/test1018 b/tests/data/test1018
new file mode 100644 (file)
index 0000000..c66dc66
--- /dev/null
@@ -0,0 +1,35 @@
+<testcase>
+<info>
+<keywords>
+FILE
+</keywords>
+</info>
+
+<reply>
+<data>
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+none
+</server>
+ <name>
+X-X range on a file:// URL to stdout
+ </name>
+ <command>
+-r 4-4 file://localhost/%PWD/log/test1018.txt 
+</command>
+<file name="log/test1018.txt">
+1234567890
+</file>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<stdout nonewline="yes">
+5
+</stdout>
+</verify>
+</testcase>
diff --git a/tests/data/test1019 b/tests/data/test1019
new file mode 100644 (file)
index 0000000..f5ab151
--- /dev/null
@@ -0,0 +1,37 @@
+<testcase>
+<info>
+<keywords>
+FILE
+</keywords>
+</info>
+# Server-side
+<reply>
+<data>
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+none
+</server>
+ <name>
+X- range on a file:// URL to stdout
+ </name>
+ <command>
+-r 7- file://localhost/%PWD/log/test1019.txt 
+</command>
+<file name="log/test1019.txt">
+1234567890
+1234567890
+</file>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<stdout>
+890
+1234567890
+</stdout>
+</verify>
+</testcase>
diff --git a/tests/data/test1020 b/tests/data/test1020
new file mode 100644 (file)
index 0000000..fe834d2
--- /dev/null
@@ -0,0 +1,37 @@
+<testcase>
+<info>
+<keywords>
+FILE
+</keywords>
+</info>
+# Server-side
+<reply>
+<data>
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+none
+</server>
+ <name>
+-Y range on a file:// URL to stdout
+ </name>
+ <command>
+-r -9 file://localhost/%PWD/log/test1020.txt 
+</command>
+<file name="log/test1020.txt">
+1234567890
+1234567890
+</file>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<stdout>
+34567890
+</stdout>
+</verify>
+</testcase>
+