1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2004, 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.52 2004/03/10 16:20:33 bagder Exp $
22 ***************************************************************************/
26 #ifndef CURL_DISABLE_FILE
27 /* -- WIN32 approved -- */
33 #include <sys/types.h>
38 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
46 #ifdef HAVE_NETINET_IN_H
47 #include <netinet/in.h>
56 #ifdef HAVE_ARPA_INET_H
57 #include <arpa/inet.h>
62 #include <sys/ioctl.h>
65 #ifdef HAVE_SYS_PARAM_H
66 #include <sys/param.h>
69 #ifdef HAVE_SYS_STAT_H
80 #include <curl/curl.h>
85 #include "speedcheck.h"
87 #include "transfer.h" /* for Curl_readwrite_init() */
89 #define _MPRINTF_REPLACE /* use our functions only */
90 #include <curl/mprintf.h>
92 /* The last #include file should be: */
97 /* Emulate a connect-then-transfer protocol. We connect to the file here */
98 CURLcode Curl_file_connect(struct connectdata *conn)
100 char *real_path = curl_unescape(conn->path, 0);
101 struct FILEPROTO *file;
103 #if defined(WIN32) || defined(__EMX__)
108 file = (struct FILEPROTO *)calloc(sizeof(struct FILEPROTO), 1);
110 return CURLE_OUT_OF_MEMORY;
112 conn->proto.file = file;
114 #if defined(WIN32) || defined(__EMX__)
115 /* If the first character is a slash, and there's
116 something that looks like a drive at the beginning of
117 the path, skip the slash. If we remove the initial
118 slash in all cases, paths without drive letters end up
119 relative to the current directory which isn't how
122 Some browsers accept | instead of : as the drive letter
123 separator, so we do too.
125 On other platforms, we need the slash to indicate an
126 absolute pathname. On Windows, absolute paths start
129 actual_path = real_path;
130 if ((actual_path[0] == '/') &&
132 (actual_path[2] == ':' || actual_path[2] == '|'))
134 actual_path[2] = ':';
138 /* change path separators from '/' to '\\' for Windows and OS/2 */
139 for (i=0; actual_path[i] != '\0'; ++i)
140 if (actual_path[i] == '/')
141 actual_path[i] = '\\';
143 fd = open(actual_path, O_RDONLY | O_BINARY); /* no CR/LF translation! */
145 fd = open(real_path, O_RDONLY);
150 failf(conn->data, "Couldn't open file %s", conn->path);
151 return CURLE_FILE_COULDNT_READ_FILE;
158 #if defined(WIN32) && (SIZEOF_CURL_OFF_T > 4)
159 #define lseek(x,y,z) _lseeki64(x, y, z)
162 /* This is the do-phase, separated from the connect-phase above */
164 CURLcode Curl_file(struct connectdata *conn)
166 /* This implementation ignores the host name in conformance with
167 RFC 1738. Only local files (reachable via the standard file system)
168 are supported. This means that files on remotely mounted directories
169 (via NFS, Samba, NT sharing) can be accessed through a file:// URL
171 CURLcode res = CURLE_OK;
173 curl_off_t expected_size=0;
176 struct SessionHandle *data = conn->data;
177 char *buf = data->state.buffer;
178 curl_off_t bytecount = 0;
180 struct timeval now = Curl_tvnow();
182 Curl_readwrite_init(conn);
184 Curl_pgrsStartNow(data);
186 /* get the fd from the connection phase */
187 fd = conn->proto.file->fd;
189 /* VMS: This only works reliable for STREAMLF files */
190 if( -1 != fstat(fd, &statbuf)) {
191 /* we could stat it, then read out the size */
192 expected_size = statbuf.st_size;
196 /* If we have selected NOBODY and HEADER, it means that we only want file
197 information. Which for FILE can't be much more than the file size and
199 if(data->set.no_body && data->set.include_header && fstated) {
201 sprintf(buf, "Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
202 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
206 sprintf(buf, "Accept-ranges: bytes\r\n");
207 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
214 time_t clock = (time_t)statbuf.st_mtime;
217 tm = (struct tm *)gmtime_r(&clock, &buffer);
221 /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
222 strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n",
224 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
230 /* Added by Dolbneff A.V & Spiridonoff A.V */
231 if (conn->resume_from <= expected_size)
232 expected_size -= conn->resume_from;
234 /* Is this error code suitable in such situation? */
235 return CURLE_FTP_BAD_DOWNLOAD_RESUME;
237 if (fstated && (expected_size == 0))
240 /* The following is a shortcut implementation of file reading
241 this is both more efficient than the former call to download() and
242 it avoids problems with select() and recv() on file descriptors
245 Curl_pgrsSetDownloadSize(data, expected_size);
247 if(conn->resume_from)
248 lseek(fd, conn->resume_from, SEEK_SET);
250 Curl_pgrsTime(data, TIMER_STARTTRANSFER);
252 while (res == CURLE_OK) {
253 nread = read(fd, buf, BUFSIZE-1);
262 /* NOTE: The following call to fwrite does CR/LF translation on
263 Windows systems if the target is stdout. Use -O or -o parameters
264 to prevent CR/LF translation (this then goes to a binary mode
267 res = Curl_client_write(data, CLIENTWRITE_BODY, buf, nread);
271 Curl_pgrsSetDownloadCounter(data, bytecount);
273 if(Curl_pgrsUpdate(conn))
274 res = CURLE_ABORTED_BY_CALLBACK;
276 res = Curl_speedcheck (data, now);
278 if(Curl_pgrsUpdate(conn))
279 res = CURLE_ABORTED_BY_CALLBACK;