ENH: one more fix for HAIKU
[cmake.git] / Utilities / cmcurl / file.c
blob57e70b62edce60ead87c9a7acadc4090051aa99d
1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2007, 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.2 2007-03-15 19:22:13 andy 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>
33 #ifdef HAVE_SYS_TYPES_H
34 #include <sys/types.h>
35 #endif
36 #ifdef HAVE_SYS_STAT_H
37 #include <sys/stat.h>
38 #endif
40 #ifdef WIN32
41 #include <time.h>
42 #include <io.h>
43 #include <fcntl.h>
44 #else
45 #ifdef HAVE_SYS_SOCKET_H
46 #include <sys/socket.h>
47 #endif
48 #ifdef HAVE_NETINET_IN_H
49 #include <netinet/in.h>
50 #endif
51 #ifdef HAVE_SYS_TIME_H
52 #include <sys/time.h>
53 #endif
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif
57 #ifdef HAVE_NETDB_H
58 #include <netdb.h>
59 #endif
60 #ifdef HAVE_ARPA_INET_H
61 #include <arpa/inet.h>
62 #endif
63 #ifdef HAVE_NET_IF_H
64 #include <net/if.h>
65 #endif
66 #include <sys/ioctl.h>
67 #include <signal.h>
69 #ifdef HAVE_SYS_PARAM_H
70 #include <sys/param.h>
71 #endif
73 #ifdef HAVE_FCNTL_H
74 #include <fcntl.h>
75 #endif
77 #endif
79 #include "urldata.h"
80 #include <curl/curl.h>
81 #include "progress.h"
82 #include "sendf.h"
83 #include "escape.h"
84 #include "file.h"
85 #include "speedcheck.h"
86 #include "getinfo.h"
87 #include "transfer.h"
88 #include "url.h"
89 #include "memory.h"
90 #include "parsedate.h" /* for the week day and month names */
92 #define _MPRINTF_REPLACE /* use our functions only */
93 #include <curl/mprintf.h>
95 /* The last #include file should be: */
96 #include "memdebug.h"
99 * Curl_file_connect() gets called from Curl_protocol_connect() to allow us to
100 * do protocol-specific actions at connect-time. We emulate a
101 * connect-then-transfer protocol and "connect" to the file here
103 CURLcode Curl_file_connect(struct connectdata *conn)
105 char *real_path = curl_easy_unescape(conn->data, conn->data->reqdata.path, 0, NULL);
106 struct FILEPROTO *file;
107 int fd;
108 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
109 int i;
110 char *actual_path;
111 #endif
113 if(!real_path)
114 return CURLE_OUT_OF_MEMORY;
116 file = (struct FILEPROTO *)calloc(sizeof(struct FILEPROTO), 1);
117 if(!file) {
118 free(real_path);
119 return CURLE_OUT_OF_MEMORY;
122 if (conn->data->reqdata.proto.file) {
123 free(conn->data->reqdata.proto.file);
126 conn->data->reqdata.proto.file = file;
128 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
129 /* If the first character is a slash, and there's
130 something that looks like a drive at the beginning of
131 the path, skip the slash. If we remove the initial
132 slash in all cases, paths without drive letters end up
133 relative to the current directory which isn't how
134 browsers work.
136 Some browsers accept | instead of : as the drive letter
137 separator, so we do too.
139 On other platforms, we need the slash to indicate an
140 absolute pathname. On Windows, absolute paths start
141 with a drive letter.
143 actual_path = real_path;
144 if ((actual_path[0] == '/') &&
145 actual_path[1] &&
146 (actual_path[2] == ':' || actual_path[2] == '|'))
148 actual_path[2] = ':';
149 actual_path++;
152 /* change path separators from '/' to '\\' for DOS, Windows and OS/2 */
153 for (i=0; actual_path[i] != '\0'; ++i)
154 if (actual_path[i] == '/')
155 actual_path[i] = '\\';
157 fd = open(actual_path, O_RDONLY | O_BINARY); /* no CR/LF translation! */
158 file->path = actual_path;
159 #else
160 fd = open(real_path, O_RDONLY);
161 file->path = real_path;
162 #endif
163 file->freepath = real_path; /* free this when done */
165 file->fd = fd;
166 if(!conn->data->set.upload && (fd == -1)) {
167 failf(conn->data, "Couldn't open file %s", conn->data->reqdata.path);
168 Curl_file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
169 return CURLE_FILE_COULDNT_READ_FILE;
172 return CURLE_OK;
175 CURLcode Curl_file_done(struct connectdata *conn,
176 CURLcode status, bool premature)
178 struct FILEPROTO *file = conn->data->reqdata.proto.file;
179 (void)status; /* not used */
180 (void)premature; /* not used */
181 Curl_safefree(file->freepath);
183 if(file->fd != -1)
184 close(file->fd);
186 return CURLE_OK;
189 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
190 #define DIRSEP '\\'
191 #else
192 #define DIRSEP '/'
193 #endif
195 static CURLcode file_upload(struct connectdata *conn)
197 struct FILEPROTO *file = conn->data->reqdata.proto.file;
198 char *dir = strchr(file->path, DIRSEP);
199 FILE *fp;
200 CURLcode res=CURLE_OK;
201 struct SessionHandle *data = conn->data;
202 char *buf = data->state.buffer;
203 size_t nread;
204 size_t nwrite;
205 curl_off_t bytecount = 0;
206 struct timeval now = Curl_tvnow();
209 * Since FILE: doesn't do the full init, we need to provide some extra
210 * assignments here.
212 conn->fread = data->set.fread;
213 conn->fread_in = data->set.in;
214 conn->data->reqdata.upload_fromhere = buf;
216 if(!dir)
217 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
219 if(!dir[1])
220 return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
222 fp = fopen(file->path, "wb");
223 if(!fp) {
224 failf(data, "Can't open %s for writing", file->path);
225 return CURLE_WRITE_ERROR;
228 if(-1 != data->set.infilesize)
229 /* known size of data to "upload" */
230 Curl_pgrsSetUploadSize(data, data->set.infilesize);
232 while (res == CURLE_OK) {
233 int readcount;
234 res = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
235 if(res)
236 break;
238 if (readcount <= 0) /* fix questionable compare error. curlvms */
239 break;
241 nread = (size_t)readcount;
243 /* write the data to the target */
244 nwrite = fwrite(buf, 1, nread, fp);
245 if(nwrite != nread) {
246 res = CURLE_SEND_ERROR;
247 break;
250 bytecount += nread;
252 Curl_pgrsSetUploadCounter(data, bytecount);
254 if(Curl_pgrsUpdate(conn))
255 res = CURLE_ABORTED_BY_CALLBACK;
256 else
257 res = Curl_speedcheck(data, now);
259 if(!res && Curl_pgrsUpdate(conn))
260 res = CURLE_ABORTED_BY_CALLBACK;
262 fclose(fp);
264 return res;
268 * Curl_file() is the protocol-specific function for the do-phase, separated
269 * from the connect-phase above. Other protocols merely setup the transfer in
270 * the do-phase, to have it done in the main transfer loop but since some
271 * platforms we support don't allow select()ing etc on file handles (as
272 * opposed to sockets) we instead perform the whole do-operation in this
273 * function.
275 CURLcode Curl_file(struct connectdata *conn, bool *done)
277 /* This implementation ignores the host name in conformance with
278 RFC 1738. Only local files (reachable via the standard file system)
279 are supported. This means that files on remotely mounted directories
280 (via NFS, Samba, NT sharing) can be accessed through a file:// URL
282 CURLcode res = CURLE_OK;
283 struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
284 Windows version to have a different struct without
285 having to redefine the simple word 'stat' */
286 curl_off_t expected_size=0;
287 bool fstated=FALSE;
288 ssize_t nread;
289 struct SessionHandle *data = conn->data;
290 char *buf = data->state.buffer;
291 curl_off_t bytecount = 0;
292 int fd;
293 struct timeval now = Curl_tvnow();
295 *done = TRUE; /* unconditionally */
297 Curl_readwrite_init(conn);
298 Curl_initinfo(data);
299 Curl_pgrsStartNow(data);
301 if(data->set.upload)
302 return file_upload(conn);
304 /* get the fd from the connection phase */
305 fd = conn->data->reqdata.proto.file->fd;
307 /* VMS: This only works reliable for STREAMLF files */
308 if( -1 != fstat(fd, &statbuf)) {
309 /* we could stat it, then read out the size */
310 expected_size = statbuf.st_size;
311 fstated = TRUE;
314 /* If we have selected NOBODY and HEADER, it means that we only want file
315 information. Which for FILE can't be much more than the file size and
316 date. */
317 if(conn->bits.no_body && data->set.include_header && fstated) {
318 CURLcode result;
319 snprintf(buf, sizeof(data->state.buffer),
320 "Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
321 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
322 if(result)
323 return result;
325 result = Curl_client_write(conn, CLIENTWRITE_BOTH,
326 (char *)"Accept-ranges: bytes\r\n", 0);
327 if(result)
328 return result;
330 if(fstated) {
331 struct tm *tm;
332 time_t clock = (time_t)statbuf.st_mtime;
333 #ifdef HAVE_GMTIME_R
334 struct tm buffer;
335 tm = (struct tm *)gmtime_r(&clock, &buffer);
336 #else
337 tm = gmtime(&clock);
338 #endif
339 /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
340 snprintf(buf, BUFSIZE-1,
341 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
342 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
343 tm->tm_mday,
344 Curl_month[tm->tm_mon],
345 tm->tm_year + 1900,
346 tm->tm_hour,
347 tm->tm_min,
348 tm->tm_sec);
349 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
351 return result;
354 if (data->reqdata.resume_from <= expected_size)
355 expected_size -= data->reqdata.resume_from;
356 else {
357 failf(data, "failed to resume file:// transfer");
358 return CURLE_BAD_DOWNLOAD_RESUME;
361 if (fstated && (expected_size == 0))
362 return CURLE_OK;
364 /* The following is a shortcut implementation of file reading
365 this is both more efficient than the former call to download() and
366 it avoids problems with select() and recv() on file descriptors
367 in Winsock */
368 if(fstated)
369 Curl_pgrsSetDownloadSize(data, expected_size);
371 if(data->reqdata.resume_from) {
372 if(data->reqdata.resume_from !=
373 lseek(fd, data->reqdata.resume_from, SEEK_SET))
374 return CURLE_BAD_DOWNLOAD_RESUME;
377 Curl_pgrsTime(data, TIMER_STARTTRANSFER);
379 while (res == CURLE_OK) {
380 nread = read(fd, buf, BUFSIZE-1);
382 if ( nread > 0)
383 buf[nread] = 0;
385 if (nread <= 0)
386 break;
388 bytecount += nread;
390 res = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
391 if(res)
392 return res;
394 Curl_pgrsSetDownloadCounter(data, bytecount);
396 if(Curl_pgrsUpdate(conn))
397 res = CURLE_ABORTED_BY_CALLBACK;
398 else
399 res = Curl_speedcheck(data, now);
401 if(Curl_pgrsUpdate(conn))
402 res = CURLE_ABORTED_BY_CALLBACK;
404 return res;
407 #endif