Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Utilities / cmcurl-7.19.0 / lib / file.c
blob21853fa00592738b611b3fdb86bba74ad42e67e8
1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: file.c,v 1.1.1.1 2008-09-23 16:32:05 hoffman Exp $
22 ***************************************************************************/
24 #include "setup.h"
26 #ifndef CURL_DISABLE_FILE
27 /* -- WIN32 approved -- */
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <ctype.h>
34 #ifdef WIN32
35 #include <time.h>
36 #include <io.h>
37 #include <fcntl.h>
38 #else
39 #ifdef HAVE_SYS_SOCKET_H
40 #include <sys/socket.h>
41 #endif
42 #ifdef HAVE_NETINET_IN_H
43 #include <netinet/in.h>
44 #endif
45 #ifdef HAVE_SYS_TIME_H
46 #include <sys/time.h>
47 #endif
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51 #ifdef HAVE_NETDB_H
52 #include <netdb.h>
53 #endif
54 #ifdef HAVE_ARPA_INET_H
55 #include <arpa/inet.h>
56 #endif
57 #ifdef HAVE_NET_IF_H
58 #include <net/if.h>
59 #endif
60 #include <sys/ioctl.h>
61 #include <signal.h>
63 #ifdef HAVE_SYS_PARAM_H
64 #include <sys/param.h>
65 #endif
67 #ifdef HAVE_FCNTL_H
68 #include <fcntl.h>
69 #endif
71 #endif /* WIN32 */
73 #include "strtoofft.h"
74 #include "urldata.h"
75 #include <curl/curl.h>
76 #include "progress.h"
77 #include "sendf.h"
78 #include "escape.h"
79 #include "file.h"
80 #include "speedcheck.h"
81 #include "getinfo.h"
82 #include "transfer.h"
83 #include "url.h"
84 #include "memory.h"
85 #include "parsedate.h" /* for the week day and month names */
87 #define _MPRINTF_REPLACE /* use our functions only */
88 #include <curl/mprintf.h>
90 /* The last #include file should be: */
91 #include "memdebug.h"
93 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || defined(__SYMBIAN32__)
94 #define DOS_FILESYSTEM 1
95 #endif
98 * Forward declarations.
101 static CURLcode file_do(struct connectdata *, bool *done);
102 static CURLcode file_done(struct connectdata *conn,
103 CURLcode status, bool premature);
104 static CURLcode file_connect(struct connectdata *conn, bool *done);
107 * FILE scheme handler.
110 const struct Curl_handler Curl_handler_file = {
111 "FILE", /* scheme */
112 ZERO_NULL, /* setup_connection */
113 file_do, /* do_it */
114 file_done, /* done */
115 ZERO_NULL, /* do_more */
116 file_connect, /* connect_it */
117 ZERO_NULL, /* connecting */
118 ZERO_NULL, /* doing */
119 ZERO_NULL, /* proto_getsock */
120 ZERO_NULL, /* doing_getsock */
121 ZERO_NULL, /* disconnect */
122 0, /* defport */
123 PROT_FILE /* protocol */
128 Check if this is a range download, and if so, set the internal variables
129 properly. This code is copied from the FTP implementation and might as
130 well be factored out.
132 static CURLcode file_range(struct connectdata *conn)
134 curl_off_t from, to;
135 curl_off_t totalsize=-1;
136 char *ptr;
137 char *ptr2;
138 struct SessionHandle *data = conn->data;
140 if(data->state.use_range && data->state.range) {
141 from=curlx_strtoofft(data->state.range, &ptr, 0);
142 while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
143 ptr++;
144 to=curlx_strtoofft(ptr, &ptr2, 0);
145 if(ptr == ptr2) {
146 /* we didn't get any digit */
147 to=-1;
149 if((-1 == to) && (from>=0)) {
150 /* X - */
151 data->state.resume_from = from;
152 DEBUGF(infof(data, "RANGE %" FORMAT_OFF_T " to end of file\n",
153 from));
155 else if(from < 0) {
156 /* -Y */
157 totalsize = -from;
158 data->req.maxdownload = -from;
159 data->state.resume_from = from;
160 DEBUGF(infof(data, "RANGE the last %" FORMAT_OFF_T " bytes\n",
161 totalsize));
163 else {
164 /* X-Y */
165 totalsize = to-from;
166 data->req.maxdownload = totalsize+1; /* include last byte */
167 data->state.resume_from = from;
168 DEBUGF(infof(data, "RANGE from %" FORMAT_OFF_T
169 " getting %" FORMAT_OFF_T " bytes\n",
170 from, data->req.maxdownload));
172 DEBUGF(infof(data, "range-download from %" FORMAT_OFF_T
173 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
174 from, to, data->req.maxdownload));
176 else
177 data->req.maxdownload = -1;
178 return CURLE_OK;
182 * file_connect() gets called from Curl_protocol_connect() to allow us to
183 * do protocol-specific actions at connect-time. We emulate a
184 * connect-then-transfer protocol and "connect" to the file here
186 static CURLcode file_connect(struct connectdata *conn, bool *done)
188 struct SessionHandle *data = conn->data;
189 char *real_path = curl_easy_unescape(data, data->state.path, 0, NULL);
190 struct FILEPROTO *file;
191 int fd;
192 #ifdef DOS_FILESYSTEM
193 int i;
194 char *actual_path;
195 #endif
197 if(!real_path)
198 return CURLE_OUT_OF_MEMORY;
200 /* If there already is a protocol-specific struct allocated for this
201 sessionhandle, deal with it */
202 Curl_reset_reqproto(conn);
204 if(!data->state.proto.file) {
205 file = (struct FILEPROTO *)calloc(sizeof(struct FILEPROTO), 1);
206 if(!file) {
207 free(real_path);
208 return CURLE_OUT_OF_MEMORY;
210 data->state.proto.file = file;
212 else {
213 /* file is not a protocol that can deal with "persistancy" */
214 file = data->state.proto.file;
215 Curl_safefree(file->freepath);
216 if(file->fd != -1)
217 close(file->fd);
218 file->path = NULL;
219 file->freepath = NULL;
220 file->fd = -1;
223 #ifdef DOS_FILESYSTEM
224 /* If the first character is a slash, and there's
225 something that looks like a drive at the beginning of
226 the path, skip the slash. If we remove the initial
227 slash in all cases, paths without drive letters end up
228 relative to the current directory which isn't how
229 browsers work.
231 Some browsers accept | instead of : as the drive letter
232 separator, so we do too.
234 On other platforms, we need the slash to indicate an
235 absolute pathname. On Windows, absolute paths start
236 with a drive letter.
238 actual_path = real_path;
239 if((actual_path[0] == '/') &&
240 actual_path[1] &&
241 (actual_path[2] == ':' || actual_path[2] == '|'))
243 actual_path[2] = ':';
244 actual_path++;
247 /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
248 for (i=0; actual_path[i] != '\0'; ++i)
249 if(actual_path[i] == '/')
250 actual_path[i] = '\\';
252 fd = open(actual_path, O_RDONLY | O_BINARY); /* no CR/LF translation! */
253 file->path = actual_path;
254 #else
255 fd = open(real_path, O_RDONLY);
256 file->path = real_path;
257 #endif
258 file->freepath = real_path; /* free this when done */
260 file->fd = fd;
261 if(!data->set.upload && (fd == -1)) {
262 failf(data, "Couldn't open file %s", data->state.path);
263 file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
264 return CURLE_FILE_COULDNT_READ_FILE;
266 *done = TRUE;
268 return CURLE_OK;
271 static CURLcode file_done(struct connectdata *conn,
272 CURLcode status, bool premature)
274 struct FILEPROTO *file = conn->data->state.proto.file;
275 (void)status; /* not used */
276 (void)premature; /* not used */
277 Curl_safefree(file->freepath);
279 if(file->fd != -1)
280 close(file->fd);
282 return CURLE_OK;
285 #ifdef DOS_FILESYSTEM
286 #define DIRSEP '\\'
287 #else
288 #define DIRSEP '/'
289 #endif
291 static CURLcode file_upload(struct connectdata *conn)
293 struct FILEPROTO *file = conn->data->state.proto.file;
294 const char *dir = strchr(file->path, DIRSEP);
295 FILE *fp;
296 CURLcode res=CURLE_OK;
297 struct SessionHandle *data = conn->data;
298 char *buf = data->state.buffer;
299 size_t nread;
300 size_t nwrite;
301 curl_off_t bytecount = 0;
302 struct timeval now = Curl_tvnow();
303 struct_stat file_stat;
304 const char* buf2;
307 * Since FILE: doesn't do the full init, we need to provide some extra
308 * assignments here.
310 conn->fread_func = data->set.fread_func;
311 conn->fread_in = data->set.in;
312 conn->data->req.upload_fromhere = buf;
314 if(!dir)
315 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
317 if(!dir[1])
318 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
320 if(data->state.resume_from)
321 fp = fopen( file->path, "ab" );
322 else {
323 int fd;
325 #ifdef DOS_FILESYSTEM
326 fd = open(file->path, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,
327 conn->data->set.new_file_perms);
328 #else
329 fd = open(file->path, O_WRONLY|O_CREAT|O_TRUNC,
330 conn->data->set.new_file_perms);
331 #endif
332 if(fd < 0) {
333 failf(data, "Can't open %s for writing", file->path);
334 return CURLE_WRITE_ERROR;
336 fp = fdopen(fd, "wb");
339 if(!fp) {
340 failf(data, "Can't open %s for writing", file->path);
341 return CURLE_WRITE_ERROR;
344 if(-1 != data->set.infilesize)
345 /* known size of data to "upload" */
346 Curl_pgrsSetUploadSize(data, data->set.infilesize);
348 /* treat the negative resume offset value as the case of "-" */
349 if(data->state.resume_from < 0) {
350 if(stat(file->path, &file_stat)) {
351 fclose(fp);
352 failf(data, "Can't get the size of %s", file->path);
353 return CURLE_WRITE_ERROR;
355 else
356 data->state.resume_from = (curl_off_t)file_stat.st_size;
359 while(res == CURLE_OK) {
360 int readcount;
361 res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
362 if(res)
363 break;
365 if(readcount <= 0) /* fix questionable compare error. curlvms */
366 break;
368 nread = (size_t)readcount;
370 /*skip bytes before resume point*/
371 if(data->state.resume_from) {
372 if( (curl_off_t)nread <= data->state.resume_from ) {
373 data->state.resume_from -= nread;
374 nread = 0;
375 buf2 = buf;
377 else {
378 buf2 = buf + data->state.resume_from;
379 nread -= (size_t)data->state.resume_from;
380 data->state.resume_from = 0;
383 else
384 buf2 = buf;
386 /* write the data to the target */
387 nwrite = fwrite(buf2, 1, nread, fp);
388 if(nwrite != nread) {
389 res = CURLE_SEND_ERROR;
390 break;
393 bytecount += nread;
395 Curl_pgrsSetUploadCounter(data, bytecount);
397 if(Curl_pgrsUpdate(conn))
398 res = CURLE_ABORTED_BY_CALLBACK;
399 else
400 res = Curl_speedcheck(data, now);
402 if(!res && Curl_pgrsUpdate(conn))
403 res = CURLE_ABORTED_BY_CALLBACK;
405 fclose(fp);
407 return res;
411 * file_do() is the protocol-specific function for the do-phase, separated
412 * from the connect-phase above. Other protocols merely setup the transfer in
413 * the do-phase, to have it done in the main transfer loop but since some
414 * platforms we support don't allow select()ing etc on file handles (as
415 * opposed to sockets) we instead perform the whole do-operation in this
416 * function.
418 static CURLcode file_do(struct connectdata *conn, bool *done)
420 /* This implementation ignores the host name in conformance with
421 RFC 1738. Only local files (reachable via the standard file system)
422 are supported. This means that files on remotely mounted directories
423 (via NFS, Samba, NT sharing) can be accessed through a file:// URL
425 CURLcode res = CURLE_OK;
426 struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
427 Windows version to have a different struct without
428 having to redefine the simple word 'stat' */
429 curl_off_t expected_size=0;
430 bool fstated=FALSE;
431 ssize_t nread;
432 size_t bytestoread;
433 struct SessionHandle *data = conn->data;
434 char *buf = data->state.buffer;
435 curl_off_t bytecount = 0;
436 int fd;
437 struct timeval now = Curl_tvnow();
439 *done = TRUE; /* unconditionally */
441 Curl_initinfo(data);
442 Curl_pgrsStartNow(data);
444 if(data->set.upload)
445 return file_upload(conn);
447 /* get the fd from the connection phase */
448 fd = conn->data->state.proto.file->fd;
450 /* VMS: This only works reliable for STREAMLF files */
451 if( -1 != fstat(fd, &statbuf)) {
452 /* we could stat it, then read out the size */
453 expected_size = statbuf.st_size;
454 fstated = TRUE;
457 /* If we have selected NOBODY and HEADER, it means that we only want file
458 information. Which for FILE can't be much more than the file size and
459 date. */
460 if(data->set.opt_no_body && data->set.include_header && fstated) {
461 CURLcode result;
462 snprintf(buf, sizeof(data->state.buffer),
463 "Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
464 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
465 if(result)
466 return result;
468 result = Curl_client_write(conn, CLIENTWRITE_BOTH,
469 (char *)"Accept-ranges: bytes\r\n", 0);
470 if(result)
471 return result;
473 if(fstated) {
474 const struct tm *tm;
475 time_t filetime = (time_t)statbuf.st_mtime;
476 #ifdef HAVE_GMTIME_R
477 struct tm buffer;
478 tm = (const struct tm *)gmtime_r(&filetime, &buffer);
479 #else
480 tm = gmtime(&filetime);
481 #endif
482 /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
483 snprintf(buf, BUFSIZE-1,
484 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
485 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
486 tm->tm_mday,
487 Curl_month[tm->tm_mon],
488 tm->tm_year + 1900,
489 tm->tm_hour,
490 tm->tm_min,
491 tm->tm_sec);
492 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
494 return result;
497 /* Check whether file range has been specified */
498 file_range(conn);
500 /* Adjust the start offset in case we want to get the N last bytes
501 * of the stream iff the filesize could be determined */
502 if(data->state.resume_from < 0) {
503 if(!fstated) {
504 failf(data, "Can't get the size of file.");
505 return CURLE_READ_ERROR;
507 else
508 data->state.resume_from += (curl_off_t)statbuf.st_size;
511 if(data->state.resume_from <= expected_size)
512 expected_size -= data->state.resume_from;
513 else {
514 failf(data, "failed to resume file:// transfer");
515 return CURLE_BAD_DOWNLOAD_RESUME;
518 /* A high water mark has been specified so we obey... */
519 if (data->req.maxdownload > 0)
520 expected_size = data->req.maxdownload;
522 if(fstated && (expected_size == 0))
523 return CURLE_OK;
525 /* The following is a shortcut implementation of file reading
526 this is both more efficient than the former call to download() and
527 it avoids problems with select() and recv() on file descriptors
528 in Winsock */
529 if(fstated)
530 Curl_pgrsSetDownloadSize(data, expected_size);
532 if(data->state.resume_from) {
533 if(data->state.resume_from !=
534 lseek(fd, data->state.resume_from, SEEK_SET))
535 return CURLE_BAD_DOWNLOAD_RESUME;
538 Curl_pgrsTime(data, TIMER_STARTTRANSFER);
540 while(res == CURLE_OK) {
541 /* Don't fill a whole buffer if we want less than all data */
542 bytestoread = (expected_size < BUFSIZE-1)?(size_t)expected_size:BUFSIZE-1;
543 nread = read(fd, buf, bytestoread);
545 if( nread > 0)
546 buf[nread] = 0;
548 if (nread <= 0 || expected_size == 0)
549 break;
551 bytecount += nread;
552 expected_size -= nread;
554 res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
555 if(res)
556 return res;
558 Curl_pgrsSetDownloadCounter(data, bytecount);
560 if(Curl_pgrsUpdate(conn))
561 res = CURLE_ABORTED_BY_CALLBACK;
562 else
563 res = Curl_speedcheck(data, now);
565 if(Curl_pgrsUpdate(conn))
566 res = CURLE_ABORTED_BY_CALLBACK;
568 return res;
571 #endif