]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/curl/lib/file.c
hello world
[icculus/iodoom3.git] / neo / curl / lib / file.c
1 /***************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
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.
13  * 
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.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * $Id: file.c,v 1.52 2004/03/10 16:20:33 bagder Exp $
22  ***************************************************************************/
23
24 #include "setup.h"
25
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 #include <sys/types.h>
34 #include <sys/stat.h>
35
36 #include <errno.h>
37
38 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
39 #include <time.h>
40 #include <io.h>
41 #include <fcntl.h>
42 #else
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
45 #endif
46 #ifdef HAVE_NETINET_IN_H
47 #include <netinet/in.h>
48 #endif
49 #include <sys/time.h>
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53 #ifdef HAVE_NETDB_H
54 #include <netdb.h>
55 #endif
56 #ifdef HAVE_ARPA_INET_H
57 #include <arpa/inet.h>
58 #endif
59 #ifdef HAVE_NET_IF_H
60 #include <net/if.h>
61 #endif
62 #include <sys/ioctl.h>
63 #include <signal.h>
64
65 #ifdef HAVE_SYS_PARAM_H
66 #include <sys/param.h>
67 #endif
68
69 #ifdef HAVE_SYS_STAT_H
70 #include <sys/stat.h>
71 #endif
72 #ifdef HAVE_FCNTL_H
73 #include <fcntl.h>
74 #endif
75
76
77 #endif
78
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" /* for Curl_readwrite_init() */
88
89 #define _MPRINTF_REPLACE /* use our functions only */
90 #include <curl/mprintf.h>
91
92 /* The last #include file should be: */
93 #ifdef CURLDEBUG
94 #include "memdebug.h"
95 #endif
96
97 /* Emulate a connect-then-transfer protocol. We connect to the file here */
98 CURLcode Curl_file_connect(struct connectdata *conn)
99 {
100   char *real_path = curl_unescape(conn->path, 0);
101   struct FILEPROTO *file;
102   int fd;
103 #if defined(WIN32) || defined(__EMX__)
104   int i;
105   char *actual_path;
106 #endif
107
108   file = (struct FILEPROTO *)calloc(sizeof(struct FILEPROTO), 1);
109   if(!file)
110     return CURLE_OUT_OF_MEMORY;
111
112   conn->proto.file = file;
113
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
120      browsers work.
121
122      Some browsers accept | instead of : as the drive letter
123      separator, so we do too.
124
125      On other platforms, we need the slash to indicate an
126      absolute pathname.  On Windows, absolute paths start
127      with a drive letter.
128   */
129   actual_path = real_path;
130   if ((actual_path[0] == '/') &&
131       actual_path[1] &&
132       (actual_path[2] == ':' || actual_path[2] == '|'))
133   {
134     actual_path[2] = ':';
135     actual_path++;
136   }
137
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] = '\\';
142
143   fd = open(actual_path, O_RDONLY | O_BINARY);  /* no CR/LF translation! */
144 #else
145   fd = open(real_path, O_RDONLY);
146 #endif
147   free(real_path);
148
149   if(fd == -1) {
150     failf(conn->data, "Couldn't open file %s", conn->path);
151     return CURLE_FILE_COULDNT_READ_FILE;
152   }
153   file->fd = fd;
154
155   return CURLE_OK;
156 }
157
158 #if defined(WIN32) && (SIZEOF_CURL_OFF_T > 4)
159 #define lseek(x,y,z) _lseeki64(x, y, z)
160 #endif
161
162 /* This is the do-phase, separated from the connect-phase above */
163
164 CURLcode Curl_file(struct connectdata *conn)
165 {
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
170   */
171   CURLcode res = CURLE_OK;
172   struct stat statbuf;
173   curl_off_t expected_size=0;
174   bool fstated=FALSE;
175   ssize_t nread;
176   struct SessionHandle *data = conn->data;
177   char *buf = data->state.buffer;
178   curl_off_t bytecount = 0;
179   int fd;
180   struct timeval now = Curl_tvnow();
181
182   Curl_readwrite_init(conn);
183   Curl_initinfo(data);
184   Curl_pgrsStartNow(data);
185
186   /* get the fd from the connection phase */
187   fd = conn->proto.file->fd;
188
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;
193     fstated = TRUE;
194   }
195
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
198      date. */
199   if(data->set.no_body && data->set.include_header && fstated) {
200     CURLcode result;
201     sprintf(buf, "Content-Length: %" FORMAT_OFF_T "\r\n", expected_size);
202     result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
203     if(result)
204       return result;
205
206     sprintf(buf, "Accept-ranges: bytes\r\n");
207     result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
208     if(result)
209       return result;
210
211 #ifdef HAVE_STRFTIME
212     if(fstated) {
213       struct tm *tm;
214       time_t clock = (time_t)statbuf.st_mtime;
215 #ifdef HAVE_GMTIME_R
216       struct tm buffer;
217       tm = (struct tm *)gmtime_r(&clock, &buffer);
218 #else
219       tm = gmtime(&clock);
220 #endif
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",
223                tm);
224       result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
225     }
226 #endif
227     return result;
228   }
229
230   /* Added by Dolbneff A.V & Spiridonoff A.V */
231   if (conn->resume_from <= expected_size)
232     expected_size -= conn->resume_from;
233   else
234     /* Is this error code suitable in such situation? */
235     return CURLE_FTP_BAD_DOWNLOAD_RESUME;
236
237   if (fstated && (expected_size == 0))
238     return CURLE_OK;
239
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
243      in Winsock */
244   if(fstated)
245     Curl_pgrsSetDownloadSize(data, expected_size);
246
247   if(conn->resume_from)
248     lseek(fd, conn->resume_from, SEEK_SET);
249
250   Curl_pgrsTime(data, TIMER_STARTTRANSFER);
251
252   while (res == CURLE_OK) {
253     nread = read(fd, buf, BUFSIZE-1);
254
255     if ( nread > 0)
256       buf[nread] = 0;
257
258     if (nread <= 0)
259       break;
260
261     bytecount += nread;
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
265        file descriptor). */
266
267     res = Curl_client_write(data, CLIENTWRITE_BODY, buf, nread);
268     if(res)
269       return res;
270
271     Curl_pgrsSetDownloadCounter(data, bytecount);
272
273     if(Curl_pgrsUpdate(conn))
274       res = CURLE_ABORTED_BY_CALLBACK;
275     else
276       res = Curl_speedcheck (data, now);
277   }
278   if(Curl_pgrsUpdate(conn))
279     res = CURLE_ABORTED_BY_CALLBACK;
280
281   close(fd);
282
283   return res;
284 }
285 #endif