1 /*
2     Copyright (C) 2000 Masanao Izumo <mo@goice.co.jp>
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18 
19 #include "config.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #ifndef NO_STRING_H
23 #include <string.h>
24 #else
25 #include <strings.h>
26 #endif
27 #include <fcntl.h>
28 
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif /* HAVE_UNISTD_H */
32 
33 #ifdef __W32__
34 #include <windows.h>
35 #endif /* __W32__ */
36 
37 
38 #ifdef HAVE_MMAP
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/mman.h>
42 #ifndef MAP_FAILED
43 #define MAP_FAILED ((caddr_t)-1)
44 #endif /* MAP_FAILED */
45 
46 #else
47 /* mmap is not supported */
48 #ifdef __W32__
49 #define try_mmap(fname, size_ret) w32_mmap(fname, size_ret, &hFile, &hMap)
50 #define munmap(addr, size)        w32_munmap(addr, size, hFile, hMap)
51 #else
52 #define try_mmap(dmy1, dmy2) NULL
53 #define munmap(addr, size) /* Do nothing */
54 #endif /* __W32__ */
55 #endif
56 
57 #include "libarc/url.h"
58 #ifdef __MACOS__
59 #include "libarc/mblock.h"
60 #endif
61 
62 #if !defined(__W32__) && !defined(O_BINARY)
63 #define O_BINARY 0
64 #endif
65 
66 typedef struct _URL_file
67 {
68     char common[sizeof(struct _URL)];
69 
70     char *mapptr;		/* Non NULL if mmap is success */
71     long mapsize;
72     long pos;
73 
74 #ifdef __W32__
75     HANDLE hFile, hMap;
76 #endif /* __W32__ */
77 
78     FILE *fp;			/* Non NULL if mmap is failure */
79 } URL_file;
80 
81 static int name_file_check(char *url_string);
82 static long url_file_read(URL url, void *buff, long n);
83 static char *url_file_gets(URL url, char *buff, int n);
84 static int url_file_fgetc(URL url);
85 static long url_file_seek(URL url, long offset, int whence);
86 static long url_file_tell(URL url);
87 static void url_file_close(URL url);
88 
89 struct URL_module URL_module_file =
90 {
91     URL_file_t,			/* type */
92     name_file_check,		/* URL checker */
93     NULL,			/* initializer */
94     url_file_open,		/* open */
95     NULL			/* must be NULL */
96 };
97 
name_file_check(char * s)98 static int name_file_check(char *s)
99 {
100     int i;
101 
102     if(IS_PATH_SEP(s[0]))
103 	return 1;
104 
105     if(strncasecmp(s, "file:", 5) == 0)
106 	return 1;
107 
108 #ifdef __W32__
109     /* [A-Za-z]: (for Windows) */
110     if((('A' <= s[0] && s[0] <= 'Z') ||
111 	('a' <= s[0] && s[0] <= 'z')) &&
112        s[1] == ':')
113 	return 1;
114 #endif /* __W32__ */
115 
116     for(i = 0; s[i] && s[i] != ':' && s[i] != '/'; i++)
117 	;
118     if(s[i] == ':' && s[i + 1] == '/')
119 	return 0;
120 
121     return 1;
122 }
123 
124 #ifdef HAVE_MMAP
try_mmap(char * path,long * size)125 static char *try_mmap(char *path, long *size)
126 {
127     int fd;
128     char *p;
129     struct stat st;
130 
131     errno = 0;
132     fd = open(path, O_RDONLY | O_BINARY);
133     if(fd < 0)
134 	return NULL;
135 
136     if(fstat(fd, &st) < 0)
137     {
138 	int save_errno = errno;
139 	close(fd);
140 	errno = save_errno;
141 	return NULL;
142     }
143 
144     p = (char *)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
145     if(p == (char *)MAP_FAILED)
146     {
147 	int save_errno = errno;
148 	close(fd);
149 	errno = save_errno;
150 	return NULL;
151     }
152     close(fd);
153     *size = (long)st.st_size;
154     return p;
155 }
156 #elif defined(__W32__)
w32_mmap(char * fname,long * size_ret,HANDLE * hFilePtr,HANDLE * hMapPtr)157 static void *w32_mmap(char *fname, long *size_ret, HANDLE *hFilePtr, HANDLE *hMapPtr)
158 {
159     void *map;
160 
161     *hFilePtr = CreateFile(fname, GENERIC_READ, 0, NULL,
162 			   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
163     if(*hFilePtr == INVALID_HANDLE_VALUE)
164 	return NULL;
165     *size_ret = GetFileSize(*hFilePtr, NULL);
166     if(*size_ret == 0xffffffff)
167     {
168 	CloseHandle(*hFilePtr);
169 	return NULL;
170     }
171     *hMapPtr = CreateFileMapping(*hFilePtr, NULL, PAGE_READONLY,
172 				 0, 0, NULL);
173     if(*hMapPtr == NULL)
174     {
175 	CloseHandle(*hFilePtr);
176 	return NULL;
177     }
178     map = MapViewOfFile(*hMapPtr, FILE_MAP_READ, 0, 0, 0);
179     if(map == NULL)
180     {
181 	CloseHandle(*hMapPtr);
182 	CloseHandle(*hFilePtr);
183 	return NULL;
184     }
185     return map;
186 }
w32_munmap(void * ptr,long size,HANDLE hFile,HANDLE hMap)187 static void w32_munmap(void *ptr, long size, HANDLE hFile, HANDLE hMap)
188 {
189     UnmapViewOfFile(ptr);
190     CloseHandle(hMap);
191     CloseHandle(hFile);
192 }
193 #endif /* HAVE_MMAP */
194 
url_file_open(char * fname)195 URL url_file_open(char *fname)
196 {
197     URL_file *url;
198     char *mapptr;		/* Non NULL if mmap is success */
199     long mapsize;
200     FILE *fp;			/* Non NULL if mmap is failure */
201 #ifdef __W32__
202     HANDLE hFile, hMap;
203 #endif /* __W32__ */
204 
205 #ifdef DEBUG
206     printf("url_file_open(%s)\n", fname);
207 #endif /* DEBUG */
208 
209     if(!strcmp(fname, "-"))
210     {
211 	mapptr = NULL;
212 	mapsize = 0;
213 	fp = stdin;
214 	goto done;
215     }
216 
217     if(strncasecmp(fname, "file:", 5) == 0)
218 	fname += 5;
219     if(*fname == '\0')
220     {
221 	url_errno = errno = ENOENT;
222 	return NULL;
223     }
224     fname = url_expand_home_dir(fname);
225 
226     fp = NULL;
227     mapsize = 0;
228     errno = 0;
229     mapptr = try_mmap(fname, &mapsize);
230     if(errno == ENOENT || errno == EACCES)
231     {
232 	url_errno = errno;
233 	return NULL;
234     }
235 
236 #ifdef DEBUG
237     if(mapptr != NULL)
238 	printf("mmap - success. size=%d\n", mapsize);
239 #ifdef HAVE_MMAP
240     else
241 	printf("mmap - failure.\n");
242 #endif
243 #endif /* DEBUG */
244 
245 
246     if(mapptr == NULL)
247     {
248 #ifdef __MACOS__
249 	char *cnvname;
250 	MBlockList pool;
251 	init_mblock(&pool);
252 	cnvname = (char *)strdup_mblock(&pool, fname);
253 	mac_TransPathSeparater(fname, cnvname);
254 	fp = fopen(cnvname, "rb");
255 	reuse_mblock(&pool);
256 	if( fp==NULL ){ /*try original name*/
257 		fp = fopen(fname, "rb");
258 	}
259 #else
260 	fp = fopen(fname, "rb");
261 #endif
262 	if(fp == NULL)
263 	{
264 	    url_errno = errno;
265 	    return NULL;
266 	}
267     }
268 
269   done:
270     url = (URL_file *)alloc_url(sizeof(URL_file));
271     if(url == NULL)
272     {
273 	url_errno = errno;
274 	if(mapptr)
275 	    munmap(mapptr, mapsize);
276 	if(fp && fp != stdin)
277 	    fclose(fp);
278 	errno = url_errno;
279 	return NULL;
280     }
281 
282     /* common members */
283     URLm(url, type)      = URL_file_t;
284     URLm(url, url_read)  = url_file_read;
285     URLm(url, url_gets)  = url_file_gets;
286     URLm(url, url_fgetc) = url_file_fgetc;
287     URLm(url, url_close) = url_file_close;
288     if(fp == stdin)
289     {
290 	URLm(url, url_seek) = NULL;
291 	URLm(url, url_tell) = NULL;
292     }
293     else
294     {
295 	URLm(url, url_seek) = url_file_seek;
296 	URLm(url, url_tell) = url_file_tell;
297     }
298 
299     /* private members */
300     url->mapptr = mapptr;
301     url->mapsize = mapsize;
302     url->pos = 0;
303     url->fp = fp;
304 #ifdef __W32__
305     url->hFile = hFile;
306     url->hMap = hMap;
307 #endif /* __W32__ */
308 
309     return (URL)url;
310 }
311 
url_file_read(URL url,void * buff,long n)312 static long url_file_read(URL url, void *buff, long n)
313 {
314     URL_file *urlp = (URL_file *)url;
315 
316     if(urlp->mapptr != NULL)
317     {
318 	if(urlp->pos + n > urlp->mapsize)
319 	    n = urlp->mapsize - urlp->pos;
320 	memcpy(buff, urlp->mapptr + urlp->pos, n);
321 	urlp->pos += n;
322     }
323     else
324     {
325 	if((n = (long)fread(buff, 1, n, urlp->fp)) == 0)
326 	{
327 	    if(ferror(urlp->fp))
328 	    {
329 		url_errno = errno;
330 		return -1;
331 	    }
332 	    return 0;
333 	}
334     }
335     return n;
336 }
337 
url_file_gets(URL url,char * buff,int n)338 char *url_file_gets(URL url, char *buff, int n)
339 {
340     URL_file *urlp = (URL_file *)url;
341 
342     if(urlp->mapptr != NULL)
343     {
344 	long s;
345 	char *nlp, *p;
346 
347 	if(urlp->mapsize == urlp->pos)
348 	    return NULL;
349 	if(n <= 0)
350 	    return buff;
351 	if(n == 1)
352 	{
353 	    *buff = '\0';
354 	    return buff;
355 	}
356 	n--; /* for '\0' */
357 	s = urlp->mapsize - urlp->pos;
358 	if(s > n)
359 	    s = n;
360 	p = urlp->mapptr + urlp->pos;
361 	nlp = (char *)memchr(p, url_newline_code, s);
362 	if(nlp != NULL)
363 	    s = nlp - p + 1;
364 	memcpy(buff, p, s);
365 	buff[s] = '\0';
366 	urlp->pos += s;
367 	return buff;
368     }
369 
370     return fgets(buff, n, urlp->fp);
371 }
372 
url_file_fgetc(URL url)373 int url_file_fgetc(URL url)
374 {
375     URL_file *urlp = (URL_file *)url;
376 
377     if(urlp->mapptr != NULL)
378     {
379 	if(urlp->mapsize == urlp->pos)
380 	    return EOF;
381 	return urlp->mapptr[urlp->pos++] & 0xff;
382     }
383 
384 #ifdef getc
385     return getc(urlp->fp);
386 #else
387     return fgetc(urlp->fp);
388 #endif /* getc */
389 }
390 
url_file_close(URL url)391 static void url_file_close(URL url)
392 {
393     URL_file *urlp = (URL_file *)url;
394 
395     if(urlp->mapptr != NULL)
396     {
397 #ifdef __W32__
398 	HANDLE hFile = urlp->hFile;
399 	HANDLE hMap = urlp->hMap;
400 #endif /* __W32__ */
401 	munmap(urlp->mapptr, urlp->mapsize);
402     }
403     if(urlp->fp != NULL)
404     {
405 	if(urlp->fp == stdin)
406 	    rewind(stdin);
407 	else
408 	    fclose(urlp->fp);
409     }
410     free(url);
411 }
412 
url_file_seek(URL url,long offset,int whence)413 static long url_file_seek(URL url, long offset, int whence)
414 {
415     URL_file *urlp = (URL_file *)url;
416     long ret;
417 
418     if(urlp->mapptr == NULL)
419 	return fseek(urlp->fp, offset, whence);
420     ret = urlp->pos;
421     switch(whence)
422     {
423       case SEEK_SET:
424 	urlp->pos = offset;
425 	break;
426       case SEEK_CUR:
427 	urlp->pos += offset;
428 	break;
429       case SEEK_END:
430 	  urlp->pos = urlp->mapsize + offset;
431 	break;
432     }
433     if(urlp->pos > urlp->mapsize)
434 	urlp->pos = urlp->mapsize;
435     else if(urlp->pos < 0)
436 	urlp->pos = 0;
437 
438     return ret;
439 }
440 
url_file_tell(URL url)441 static long url_file_tell(URL url)
442 {
443     URL_file *urlp = (URL_file *)url;
444 
445     return urlp->mapptr ? urlp->pos : ftell(urlp->fp);
446 }
447