1 |
/* |
2 |
The Malete project - the Z39.2/Z39.50 database framework of OpenIsis. |
3 |
Version 0.9.x (patchlevel see file Version) |
4 |
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org |
5 |
|
6 |
This library is free software; you can redistribute it and/or |
7 |
modify it under the terms of the GNU Lesser General Public |
8 |
License as published by the Free Software Foundation; either |
9 |
version 2.1 of the License, or (at your option) any later version. |
10 |
|
11 |
This library is distributed in the hope that it will be useful, |
12 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
14 |
See the GNU Lesser General Public License for more details. |
15 |
|
16 |
You should have received a copy of the GNU Lesser General Public |
17 |
License along with this library; if not, write to the Free Software |
18 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 |
|
20 |
see README for more information |
21 |
EOH */ |
22 |
|
23 |
/* |
24 |
$Id: sys.c,v 1.29 2004/11/12 09:38:02 kripke Exp $ |
25 |
I/O support for the openisis library. |
26 |
*/ |
27 |
#include <stdlib.h> |
28 |
#include <stdarg.h> |
29 |
#include <string.h> |
30 |
#include <errno.h> |
31 |
#include <limits.h> /* PATH_MAX */ |
32 |
#include <sys/types.h> |
33 |
|
34 |
#ifdef WIN32 |
35 |
#define WIN32_LEAN_AND_MEAN |
36 |
#define NONAMELESSUNION |
37 |
# include <windows.h> |
38 |
# include <sys/timeb.h> |
39 |
#else /* have operating system */ |
40 |
# define __USE_UNIX98 |
41 |
/* |
42 |
# define _POSIX_MAPPED_FILES |
43 |
# define _POSIX_SYNCHRONIZED_IO |
44 |
*/ |
45 |
# include <fcntl.h> /* or try unixio */ |
46 |
# ifndef O_SYNC |
47 |
# if defined( __FreeBSD__ ) |
48 |
# define O_SYNC O_FSYNC |
49 |
# else |
50 |
# define O_SYNC 0 |
51 |
# endif |
52 |
# endif |
53 |
# include <sys/stat.h> |
54 |
# include <sys/file.h> /* flock */ |
55 |
#if defined(__linux__) && !defined(__USE_GNU) |
56 |
# define __USE_GNU /* weird trigger to declare MREMAP_MAYMOVE */ |
57 |
#endif |
58 |
# include <sys/mman.h> |
59 |
# include <sys/socket.h> |
60 |
# include <unistd.h> |
61 |
#endif /* WIN32 */ |
62 |
|
63 |
#include <sys/time.h> /* gettimeofday */ |
64 |
#include <time.h> /* localtime */ |
65 |
|
66 |
|
67 |
#include "../core/core.h" |
68 |
|
69 |
|
70 |
|
71 |
/* ************************************************************ |
72 |
private data |
73 |
*/ |
74 |
|
75 |
|
76 |
/* ************************************************************ |
77 |
private functions |
78 |
*/ |
79 |
|
80 |
static void *memordie ( int size ) |
81 |
{ |
82 |
void *p = malloc( size ); |
83 |
if ( p ) |
84 |
return p; |
85 |
exit( 71/*EX_OSERR*/ ); /* exit handlers MUST NOT alloc memory */ |
86 |
return 0; |
87 |
} |
88 |
|
89 |
/* ************************************************************ |
90 |
private functions |
91 |
*/ |
92 |
|
93 |
|
94 |
/* ************************************************************ |
95 |
public data |
96 |
*/ |
97 |
Env env; |
98 |
|
99 |
|
100 |
|
101 |
/* ************************************************************ |
102 |
public functions |
103 |
*/ |
104 |
|
105 |
|
106 |
void *mAlloc ( int size ) |
107 |
{ |
108 |
void *p = memordie( size ); |
109 |
memset(p, 0, size); |
110 |
return p; |
111 |
} /* mAlloc */ |
112 |
|
113 |
|
114 |
void mFree ( void *mem ) |
115 |
{ |
116 |
if ( mem ) |
117 |
free( mem ); |
118 |
} /* mFree */ |
119 |
|
120 |
|
121 |
void *mDup ( const void *str, int sz ) |
122 |
{ |
123 |
void *m = memordie( 0<=sz ? sz : (sz = strlen( str ) + 1) ); |
124 |
memcpy( m, str, sz ); |
125 |
return m; |
126 |
} /* mDup */ |
127 |
|
128 |
|
129 |
char *mDupz ( const char *str, int sz ) |
130 |
{ |
131 |
char *m = memordie( sz+1 ); |
132 |
memcpy( m, str, sz ); |
133 |
m[sz] = 0; |
134 |
return m; |
135 |
} /* mDupz */ |
136 |
|
137 |
|
138 |
LBlk *mBlkAlloc ( int size ) |
139 |
{ |
140 |
LBlk *b = memordie( (((char*)(&((LBlk*)0)->byt))-(char*)0) + size ); |
141 |
b->nxt = 0; |
142 |
b->siz = size; |
143 |
memset(b->byt, 0, size); |
144 |
return b; |
145 |
} /* mBlkAlloc */ |
146 |
|
147 |
|
148 |
List *mListAlloc ( const char *name ) |
149 |
{ |
150 |
return lInit(memordie(sizeof(List)), name); |
151 |
} /* mListAlloc */ |
152 |
|
153 |
|
154 |
int tUpd ( lolo *tm ) |
155 |
{ |
156 |
lolo o; |
157 |
#ifdef WIN32 |
158 |
struct _timeb tb; |
159 |
_ftime( &tb ); |
160 |
if ( !tm ) |
161 |
return tb.time; |
162 |
o = *tm; |
163 |
*tm = tb.time*LOLO(1000) + tb.millitm; |
164 |
#else |
165 |
struct timeval tv; |
166 |
gettimeofday( &tv, 0 ); |
167 |
if ( !tm ) |
168 |
return tv.tv_sec; |
169 |
o = *tm; |
170 |
*tm = tv.tv_sec*LOLO(1000) + tv.tv_usec/1000; |
171 |
#endif |
172 |
return (int)(*tm - o); |
173 |
} /* tUpd */ |
174 |
|
175 |
|
176 |
static int tLoc ( struct tm *t, lolo *tm ) |
177 |
{ |
178 |
lolo x; |
179 |
time_t tt; |
180 |
if ( !tm || !*tm ) |
181 |
tUpd( tm ? tm : (tm = &x) ); |
182 |
tt = (time_t)(*tm / 1000); |
183 |
#ifdef WIN32 /* did I mention it's not threadsafe ? */ |
184 |
*t = *localtime( &tt ); |
185 |
#else |
186 |
localtime_r( &tt, t ); |
187 |
#endif |
188 |
return (int)(*tm % 1000); |
189 |
} /* tLoc */ |
190 |
|
191 |
|
192 |
/* logically belongs to uti.c; here so tGtf may consider inlining */ |
193 |
void u2a0 ( char *p, unsigned u, unsigned n ) |
194 |
{ |
195 |
for ( ; n--; u/=10 ) |
196 |
p[n] = '0' + (u % 10); |
197 |
} /* u2a0 */ |
198 |
|
199 |
|
200 |
int tGtf ( char *buf, lolo *tm ) |
201 |
{ |
202 |
struct tm t; |
203 |
int millis = tLoc( &t, tm ); |
204 |
|
205 |
u2a0(buf, 1900+t.tm_year, 1900+t.tm_year); |
206 |
u2a0(buf+4, 1+t.tm_mon, 2); |
207 |
u2a0(buf+6, t.tm_mday, 2); |
208 |
u2a0(buf+8, t.tm_hour, 2); |
209 |
u2a0(buf+10, t.tm_min, 2); |
210 |
u2a0(buf+12, t.tm_sec, 2); |
211 |
buf[14] = 0; |
212 |
return millis; |
213 |
} /* tGtf */ |
214 |
|
215 |
|
216 |
char *tGtfm ( char *buf, lolo *tm ) |
217 |
{ |
218 |
u2a0(buf+14, tGtf(buf, tm), 3); |
219 |
buf[18] = 0; |
220 |
return buf; |
221 |
} /* tGtfm */ |
222 |
|
223 |
|
224 |
#if 0 /* unused */ |
225 |
void tSleep ( lolo tm ) |
226 |
{ |
227 |
#ifdef WIN32 |
228 |
Sleep( tm ); |
229 |
#else |
230 |
struct timespec ts; |
231 |
ts.tv_sec = tm / 1000; |
232 |
ts.tv_nsec = 1000000 * (int)(tm % 1000); |
233 |
nanosleep( &ts, 0 ); |
234 |
#endif |
235 |
} /* tSleep */ |
236 |
#endif |
237 |
|
238 |
|
239 |
#ifndef WIN32 |
240 |
static const char *lmode (int f) |
241 |
{ |
242 |
switch ( (FIL_BLOCK|FIL_TLOCK|FIL_WR) & f ) { |
243 |
case FIL_BLOCK|FIL_WR: return "wait ex"; |
244 |
case FIL_BLOCK: return "wait sh"; |
245 |
case FIL_TLOCK|FIL_WR: return "test ex"; |
246 |
case FIL_TLOCK: return "test sh"; |
247 |
} |
248 |
return "unlock"; |
249 |
} |
250 |
#endif |
251 |
|
252 |
|
253 |
int fOpen ( file *fil, const char *name, int flags ) |
254 |
{ |
255 |
int faillvl = FIL_TRY&flags ? LOG_VERBOSE : ERR_NO; |
256 |
|
257 |
#ifndef WIN32 |
258 |
int fd = -1; |
259 |
int f = !(FIL_WR&flags) ? O_RDONLY : ((FIL_RD&flags) ? O_RDWR : O_WRONLY); |
260 |
|
261 |
*fil = FIL_NONE; |
262 |
if (!name) { /* tempfile */ |
263 |
const char tpl[] = "/tmp/maleteXXXXXX"; |
264 |
char nam[sizeof tpl]; |
265 |
memcpy(nam, tpl, sizeof tpl); |
266 |
fd = mkstemp(nam); |
267 |
if (0 > fd) |
268 |
return eRr(ERR_NO, "could not create tmpfile"); |
269 |
eRr(LOG_VERBOSE, "tempfile '%s'", nam); |
270 |
unlink(nam); |
271 |
*fil = fd; |
272 |
return 0; |
273 |
} |
274 |
if ( FIL_CREAT & flags && FIL_WR & flags ) f |= O_CREAT; |
275 |
if ( FIL_SYNC & flags ) f |= O_SYNC; |
276 |
if ( FIL_TRUNC & flags ) f |= O_TRUNC; |
277 |
fd = open( name, f, 00664 ); /* let umask finetune */ |
278 |
if ( 0 > fd ) |
279 |
return eRr( faillvl, "could not open '%s' %x", name, flags ); |
280 |
if ( FIL_FLOCK & flags ) { |
281 |
struct flock fl; |
282 |
memset(&fl, 0, sizeof(fl)); |
283 |
fl.l_type = FIL_WR&flags ? F_WRLCK : F_RDLCK; |
284 |
fl.l_whence = SEEK_SET; |
285 |
fl.l_start = 0; |
286 |
fl.l_len = 0; |
287 |
eRr(LOG_VERBOSE, "%s perm lock on '%s'", lmode(flags), name); |
288 |
if ( fcntl(fd, FIL_BLOCK&flags ? F_SETLKW : F_SETLK, &fl) ) { |
289 |
close(fd); |
290 |
return eRr(ERR_NO, "err %s perm lock on '%s'", lmode(flags), name); |
291 |
} |
292 |
} |
293 |
*fil = fd; |
294 |
#else |
295 |
HANDLE h; |
296 |
int acc = 0; |
297 |
int shr = FILE_SHARE_READ | (FIL_FLOCK&flags ? 0 : FILE_SHARE_WRITE); |
298 |
int cre = (FIL_CREAT & flags && FIL_WR & flags) |
299 |
? (FIL_TRUNC & flags) ? CREATE_ALWAYS : OPEN_ALWAYS |
300 |
: (FIL_TRUNC & flags) ? TRUNCATE_EXISTING : OPEN_EXISTING; |
301 |
int f = (FIL_WR&flags) ? FILE_ATTRIBUTE_ARCHIVE : FILE_ATTRIBUTE_NORMAL; |
302 |
char nam[128]; |
303 |
|
304 |
*fil = FIL_NONE; |
305 |
if (!name) { /* tempfile */ |
306 |
GetTempPath(sizeof nam, nam); |
307 |
GetTempFileName(nam, "mtp", 0, nam); |
308 |
name = nam; |
309 |
eRr(LOG_VERBOSE, "tempfile '%s'", nam); |
310 |
acc = GENERIC_READ|GENERIC_WRITE; |
311 |
shr = 0; |
312 |
cre = TRUNCATE_EXISTING; |
313 |
f = FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE; |
314 |
} else { |
315 |
if ( FIL_RD & flags ) acc |= GENERIC_READ; /* FILE_READ_DATA; */ |
316 |
if ( FIL_WR & flags ) acc |= GENERIC_WRITE; |
317 |
if ( FIL_SYNC & flags ) f |= FILE_FLAG_WRITE_THROUGH; |
318 |
} |
319 |
f |= FILE_FLAG_RANDOM_ACCESS; |
320 |
|
321 |
h = CreateFile( name, acc, shr, 0, cre, f, 0 ); |
322 |
if ( INVALID_HANDLE_VALUE == h ) |
323 |
return eRr( faillvl, "could not open '%s' %x", name, flags ); |
324 |
*fil = h; |
325 |
#endif |
326 |
return 0; |
327 |
} /* fOpen */ |
328 |
|
329 |
|
330 |
int fClose ( file *f ) |
331 |
{ |
332 |
if ( FIL_NONE != *f ) { |
333 |
#ifndef WIN32 |
334 |
for ( fsync( *f ); close( *f ); errno = 0 ) |
335 |
if ( EINTR != errno ) { |
336 |
eRr( LOG_SYSERR, "could not close file %d", *f ); |
337 |
return errno ? -errno : -1; |
338 |
} |
339 |
#else |
340 |
CloseHandle( (HANDLE)*f ); |
341 |
#endif |
342 |
*f = FIL_NONE; |
343 |
} |
344 |
return 0; |
345 |
} /* fClose */ |
346 |
|
347 |
|
348 |
int fSize ( file f ) |
349 |
{ |
350 |
#ifndef WIN32 |
351 |
{ |
352 |
struct stat s; |
353 |
return fstat( f, &s ) ? 0 : s.st_size; |
354 |
} |
355 |
#else |
356 |
return GetFileSize( (HANDLE)f, 0 ); |
357 |
#endif |
358 |
} /* fSize */ |
359 |
|
360 |
|
361 |
unsigned fTime ( file f ) |
362 |
{ |
363 |
#ifndef WIN32 |
364 |
struct stat s; |
365 |
return fstat( f, &s ) ? 0 : (unsigned)s.st_mtime; |
366 |
#else |
367 |
FILETIME foo; /* time since 160101011200 UTC in hundred nanoseconds !!! */ |
368 |
lulu bar; |
369 |
if ( ! GetFileTime( (HANDLE)f, 0, 0, &foo ) ) |
370 |
return 0; |
371 |
bar = ((lulu)foo.dwHighDateTime<<32 | (lulu)foo.dwLowDateTime) / LULU(10000000); |
372 |
return (unsigned)(bar - LULU(11644473600)); |
373 |
#endif |
374 |
} /* fTime */ |
375 |
|
376 |
|
377 |
int fRead ( file *f, void *buf, unsigned count ) |
378 |
{ |
379 |
#ifndef WIN32 |
380 |
int got = read( *f, buf, count ); |
381 |
return 0 <= got ? got |
382 |
: eRr( ERR_NO, "could not read %d bytes from %d", count, *f ); |
383 |
#else |
384 |
DWORD got = 0; |
385 |
int ok = ReadFile( (HANDLE)*f, buf, count, &got, 0 ); |
386 |
return ok ? got : eRr( ERR_NO, "could not read %d bytes", count ); |
387 |
#endif |
388 |
} /* fRead */ |
389 |
|
390 |
|
391 |
int fWrite ( file *f, const void *buf, unsigned count ) |
392 |
{ |
393 |
{ |
394 |
#ifndef WIN32 |
395 |
int got = write( *f, buf, count ); |
396 |
return 0 <= got ? got |
397 |
: eRr( ERR_NO, "could not write %d bytes to %d", count, *f ); |
398 |
#else |
399 |
DWORD got = 0; |
400 |
int ok = WriteFile( (HANDLE)*f, buf, count, &got, 0 ); |
401 |
return ok ? got |
402 |
: eRr( ERR_NO, "could not write %d bytes", count ); |
403 |
#endif |
404 |
} |
405 |
} /* fWrite */ |
406 |
|
407 |
|
408 |
|
409 |
int fSeek ( file *f, unsigned offset ) |
410 |
{ |
411 |
{ |
412 |
#ifndef WIN32 |
413 |
int got = (int)lseek( *f, offset, SEEK_SET ); |
414 |
return (int)offset == got ? 0 |
415 |
: eRr( ERR_NO, "could not seek to %d", offset ); |
416 |
#else |
417 |
return 0xffffffff /* "INVALID_SET_FILE_POINTER" */ |
418 |
== SetFilePointer( (HANDLE)*f, offset, 0, FILE_BEGIN ) |
419 |
? -1 : 0; |
420 |
#endif |
421 |
} |
422 |
} /* fSeek */ |
423 |
|
424 |
|
425 |
int fPread ( file *f, void *buf, unsigned count, unsigned offset ) |
426 |
{ |
427 |
{ |
428 |
#ifndef WIN32 |
429 |
int got = pread( *f, buf, count, offset ); |
430 |
return 0 <= got ? got |
431 |
: eRr( ERR_NO, "could not read %d bytes from %d at %d", |
432 |
count, *f, offset ); |
433 |
#else |
434 |
return 0xffffffff /* "INVALID_SET_FILE_POINTER" */ |
435 |
== SetFilePointer( (HANDLE)*f, offset, 0, FILE_BEGIN ) |
436 |
? -1 : fRead( f, buf, count ); |
437 |
#endif |
438 |
} |
439 |
} /* fPread */ |
440 |
|
441 |
|
442 |
int fPwrite ( file *f, const void *buf, unsigned count, unsigned offset ) |
443 |
{ |
444 |
{ |
445 |
#ifndef WIN32 |
446 |
int got = pwrite( *f, buf, count, offset ); |
447 |
return 0 <= got ? got |
448 |
: eRr( ERR_NO, "could not write %d bytes to %d at %d", |
449 |
count, *f, offset ); |
450 |
#else |
451 |
return 0xffffffff /* "INVALID_SET_FILE_POINTER" */ |
452 |
== SetFilePointer( (HANDLE)*f, offset, 0, FILE_BEGIN ) |
453 |
? -1 : fWrite( f, buf, count ); |
454 |
#endif |
455 |
} |
456 |
} /* fPwrite */ |
457 |
|
458 |
|
459 |
int fTrunc ( file *f, unsigned length ) |
460 |
{ |
461 |
#ifndef WIN32 |
462 |
ftruncate( *f, length ); |
463 |
if ( length && (int)length != fSize(*f) ) { |
464 |
/* Solaris and FreeBSD guarantee that ftruncate can extend files, |
465 |
but Linux does not. |
466 |
*/ |
467 |
static char nosh = 0; |
468 |
fPwrite(f, &nosh, 1, length-1); |
469 |
} |
470 |
#else |
471 |
/* SetFileValidData( (HANDLE)*f, length ); XP only */ |
472 |
return 0xffffffff /* "INVALID_SET_FILE_POINTER" */ |
473 |
== SetFilePointer( (HANDLE)*f, length, 0, FILE_BEGIN ) |
474 |
? -1 : SetEndOfFile( (HANDLE)*f ) |
475 |
? 0 : eRr( ERR_NO, "could not truncate %d", length ); |
476 |
#endif |
477 |
return 0; |
478 |
} /* fTrunc */ |
479 |
|
480 |
|
481 |
#ifdef BUILD_SHMODE |
482 |
int fLock ( file f, unsigned n, int flg ) |
483 |
{ |
484 |
struct flock fl; |
485 |
|
486 |
memset( &fl, 0, sizeof(fl) ); |
487 |
fl.l_type = FIL_WR&flg ? F_WRLCK : flg ? F_RDLCK : F_UNLCK; |
488 |
fl.l_whence = SEEK_SET; |
489 |
fl.l_start = n; |
490 |
fl.l_len = 1; |
491 |
eRr( LOG_VERBOSE, "%s tmp lock %d", lmode(flg), n ); |
492 |
return fcntl(f, FIL_BLOCK&flg ? F_SETLKW : F_SETLK, &fl) |
493 |
? eRr(ERR_NO, "err %s tmp lock %d", lmode(flg), n) : 0; |
494 |
} /* fLock */ |
495 |
#endif |
496 |
|
497 |
|
498 |
int fMOpen ( FMap *fm, const char *name, int flags ) |
499 |
{ |
500 |
int ret; |
501 |
fm->map = 0; |
502 |
fm->npg = 0; |
503 |
if ( (ret = fOpen(&fm->fil, name, flags)) ) |
504 |
fm->flg = 0; |
505 |
else { |
506 |
#ifdef WIN32 /* save mapping name */ |
507 |
static const char pfx[] = "Malete"; |
508 |
int l = strlen(name); |
509 |
char *c; |
510 |
fm->nam = mAlloc(sizeof(pfx)+l); |
511 |
memcpy(fm->nam, pfx, sizeof(pfx)-1); |
512 |
memcpy(c = fm->nam+sizeof(pfx)-1, name, l+1); |
513 |
for (; *c; c++) if ( '\\' == *c ) *c = '/'; /* must not contain \ */ |
514 |
#endif |
515 |
fm->flg = flags; |
516 |
} |
517 |
return ret; |
518 |
} /* fMOpen */ |
519 |
|
520 |
|
521 |
int fMClose ( FMap *fm ) |
522 |
{ |
523 |
if ( fm->map ) |
524 |
fMap(fm, 0); |
525 |
#ifdef WIN32 /* save mapping name */ |
526 |
if ( fm->nam ) { |
527 |
mFree(fm->nam); |
528 |
fm->nam = 0; |
529 |
} |
530 |
#endif |
531 |
return fClose(&fm->fil); |
532 |
} /* fMClose */ |
533 |
|
534 |
|
535 |
int fMap ( FMap *fm, unsigned npg ) |
536 |
{ |
537 |
if ( npg > fm->lim && fm->npg == (npg = fm->lim) ) |
538 |
return npg; |
539 |
#ifndef WIN32 |
540 |
if ((FIL_WR & fm->flg) && npg && (((int)npg<<env.psh) > fSize(fm->fil))) |
541 |
fTrunc(&fm->fil, npg<<env.psh); |
542 |
if ( fm->map ) { /* unmap or remap */ |
543 |
#ifdef __linux__ |
544 |
if ( npg ) { |
545 |
fm->map = mremap(fm->map, fm->npg<<env.psh, npg<<env.psh, MREMAP_MAYMOVE); |
546 |
fm->npg = 0; |
547 |
goto done; |
548 |
} |
549 |
#endif |
550 |
munmap(fm->map, fm->npg<<env.psh); |
551 |
} |
552 |
fm->map = 0; |
553 |
fm->npg = 0; |
554 |
if (!npg) |
555 |
return 0; |
556 |
/* get new map */ |
557 |
fm->map = mmap( 0, npg<<env.psh, |
558 |
PROT_READ | ((FIL_WR&fm->flg) ? PROT_WRITE : 0), MAP_SHARED, fm->fil, 0 ); |
559 |
#ifdef __linux__ |
560 |
done: |
561 |
#endif |
562 |
LOG_DBG(LOG_DEBUG, "mapped %d pages at %8x", npg, fm->map); |
563 |
if ( MAP_FAILED != fm->map ) |
564 |
return (int)(fm->npg = npg); |
565 |
fm->map = 0; |
566 |
return eRr(ERR_NO, "mmap failed"); |
567 |
#else |
568 |
/* awfully complicated here |
569 |
-- need CreateFileMapping, MapViewOfFile |
570 |
stupid piece of shrott |
571 |
true mapping supported in NT family only, 9x copies to swap |
572 |
*/ |
573 |
if ( fm->map ) { |
574 |
UnmapViewOfFile(fm->map); |
575 |
CloseHandle(fm->hdl); |
576 |
} |
577 |
fm->map = 0; |
578 |
fm->npg = 0; |
579 |
fm->hdl = 0; |
580 |
if (!npg) |
581 |
return 0; |
582 |
/* |
583 |
note that shared writing of a mapping does not work, |
584 |
because to be consistent, it needs to be the same mapping object, |
585 |
which we can not grow. |
586 |
However, windoze doesn't support shared writing anyway. |
587 |
*/ |
588 |
{ |
589 |
int size = fSize(fm->fil); |
590 |
if (FIL_WR&fm->flg || size > npg<<env.psh) |
591 |
size = npg<<env.psh; |
592 |
else |
593 |
npg = (size + env.psz-1)>>env.psh; |
594 |
if ( (fm->hdl = CreateFileMapping(fm->fil,0, |
595 |
(FIL_WR&fm->flg)?PAGE_READWRITE:PAGE_READONLY,0,size,fm->nam)) ) { |
596 |
if ( (fm->map = MapViewOfFile(fm->hdl, |
597 |
(FIL_WR&fm->flg)?FILE_MAP_WRITE:FILE_MAP_READ,0,0,size)) |
598 |
) |
599 |
return fm->npg = npg; |
600 |
CloseHandle(fm->hdl); |
601 |
fm->hdl = 0; |
602 |
return eRr(ERR_NO, "MapViewOfFile %d failed", npg); |
603 |
} |
604 |
} |
605 |
return eRr(ERR_NO, "CreateFileMapping failed"); |
606 |
#endif |
607 |
} /* fMap */ |
608 |
|
609 |
|
610 |
int fMSync ( FMap *fm, unsigned page ) |
611 |
{ |
612 |
char * buf = fm->map + (page<<env.psh); |
613 |
LOG_DBG(LOG_DEBUG, "msync %d", page); |
614 |
#ifndef WIN32 |
615 |
return msync( buf, env.psz, ( |
616 |
#ifdef BUILD_SHMODE |
617 |
ENV_SHARED == env.wri ? MS_INVALIDATE : |
618 |
#endif |
619 |
0) | (FIL_SYNC&fm->flg ? MS_SYNC : MS_ASYNC) ) |
620 |
? eRr( ERR_NO, "msync" ) : 0; |
621 |
#else |
622 |
return FlushViewOfFile(buf, env.psz) ? 0 : eRr(ERR_NO, "msync"); |
623 |
#endif |
624 |
} /* fMSync */ |
625 |
|
626 |
|
627 |
int fSlurp ( char **buf, int sz, const char *name, int opt ) |
628 |
{ |
629 |
int size; |
630 |
int ret; |
631 |
char *p = *buf; |
632 |
file f; |
633 |
|
634 |
if ( (ret = fOpen( &f, name, FIL_RD|(opt ? FIL_TRY : 0) )) ) |
635 |
return ret; |
636 |
size = fSize( f ); |
637 |
if ( 0 >= size ) |
638 |
return size; |
639 |
if ( size > sz ) { |
640 |
eRr(ERR_INVAL, "file '%.*s' too big: %d > %d", 30, name, size, sz); |
641 |
goto done; |
642 |
} |
643 |
if ( ! p && !(p = mAlloc(size)) ) { |
644 |
ret = ERR_NOMEM; |
645 |
goto done; |
646 |
} |
647 |
ret = fRead( &f, p, size ); |
648 |
if ( size == ret ) |
649 |
*buf = p; |
650 |
else { |
651 |
if ( ret >= 0 ) |
652 |
ret = eRr( ERR_IO, "OOPS! got %d of %d bytes from '%.*s'", |
653 |
ret, size, 30, name ); |
654 |
if ( ! *buf ) |
655 |
free( p ); |
656 |
} |
657 |
done: |
658 |
fClose( &f ); |
659 |
return ret; |
660 |
} /* fSlurp */ |
661 |
|
662 |
|
663 |
int fGets ( FBuf *b ) |
664 |
{ |
665 |
char *c; |
666 |
int n; |
667 |
if ( b->l ) { |
668 |
b->p += b->l; |
669 |
b->o += b->l; |
670 |
b->l = 0; |
671 |
} |
672 |
if ( b->m ) { |
673 |
b->p++; |
674 |
b->o++; |
675 |
b->m--; |
676 |
b->n++; |
677 |
} |
678 |
for (;;) { /* now have b->m bytes at b->p+b->l */ |
679 |
if ( b->m ) { /* add all or part of this b->l */ |
680 |
if ( (c = memchr(b->p+b->l, LF, b->m)) ) { |
681 |
b->m -= c - b->p - b->l; |
682 |
b->l = c - b->p; |
683 |
return !0; |
684 |
} |
685 |
b->l += b->m; |
686 |
b->m = 0; |
687 |
if ( b->l == b->s ) /* buffer exhausted */ |
688 |
return !0; |
689 |
if ( b->p != b->b ) |
690 |
memmove(b->b, b->p, b->l); |
691 |
} |
692 |
b->p = b->b; |
693 |
if ( 0 >= (n = fRead(&b->f, b->p+b->l, b->s-b->l)) ) /* eof */ |
694 |
return b->l; /* but had line */ |
695 |
b->m = n; |
696 |
} |
697 |
} /* fGets */ |
698 |
|
699 |
|
700 |
int fGetr ( List *l, FBuf *fb ) |
701 |
{ |
702 |
if ( !fGets(fb) ) |
703 |
return 0; |
704 |
while ( fb->l ) { |
705 |
int tag; |
706 |
int n = a2il(fb->p, fb->l, &tag); |
707 |
if ( !l->fld->tag ) { /* init rec */ |
708 |
if ( n ) /* this is a data field, add blank header */ |
709 |
LPREF(l, -1, 0); |
710 |
else /* use this one as header */ |
711 |
tag = -1; |
712 |
} |
713 |
if ( n < (int)fb->l && TAB == fb->p[n] ) |
714 |
n++; |
715 |
LADD(l, tag, fb->p+n, fb->l-n); |
716 |
if ( !fGets(fb) ) |
717 |
break; |
718 |
} |
719 |
return !0; |
720 |
} /* fGetr */ |
721 |
|
722 |
|
723 |
|
724 |
static void fSinkf (Sink *lo, int eor, int full) |
725 |
{ |
726 |
Fld *f = lo->lst.fld, *e = f + RLEN(f); |
727 |
file to = (file)lo->dst; |
728 |
|
729 |
if ( FIL_NONE == to ) goto reset; |
730 |
for ( f++; f < e; f++ ) { |
731 |
char buf[8192]; |
732 |
int l; |
733 |
if ( full ) { |
734 |
l = i2a(buf, f->tag); |
735 |
buf[l++] = '\t'; |
736 |
} else { |
737 |
if ( !f->len ) |
738 |
continue; |
739 |
l = 0; |
740 |
} |
741 |
if ( f->len < sizeof(buf)-l ) { |
742 |
memcpy(buf+l, f->val, f->len); |
743 |
buf[l += f->len] = '\n'; |
744 |
fWrite(&to, buf, l+1); |
745 |
} else { |
746 |
if ( l ) |
747 |
fWrite(&to, buf, l); |
748 |
if ( f->len ) |
749 |
fWrite(&to, f->val, f->len); |
750 |
buf[0] = '\n'; |
751 |
fWrite(&to, buf, 1); |
752 |
} |
753 |
} |
754 |
if ( eor ) |
755 |
fWrite(&to, "\n", 1); |
756 |
reset: |
757 |
lReset(&lo->lst); |
758 |
} /* fSinkf */ |
759 |
|
760 |
void fSinkl (Sink *lo, int eor) |
761 |
{ |
762 |
fSinkf(lo, eor, 0); |
763 |
} /* fSinkl */ |
764 |
|
765 |
void fSinkr (Sink *lo, int eor) |
766 |
{ |
767 |
Fld *f = lo->lst.fld; |
768 |
file to = (file)lo->dst; |
769 |
char buf[8192]; |
770 |
int l; |
771 |
|
772 |
if ( FIL_NONE == to ) goto reset; |
773 |
if ( f->tag ) { /* not empty */ |
774 |
if ( !lo->off && f->len ) { /* skip header if off or empty */ |
775 |
if ( 10>b36val[(unsigned)*f->val] ) {/* starts with digit */ |
776 |
buf[0] = 'W'; |
777 |
buf[1] = TAB; |
778 |
l = 2; |
779 |
} else |
780 |
l = 0; |
781 |
if ( f->len < sizeof(buf)-l ) { |
782 |
memcpy(buf+l, f->val, f->len); |
783 |
buf[l += f->len] = '\n'; |
784 |
fWrite(&to, buf, l+1); |
785 |
} else { |
786 |
if ( l ) |
787 |
fWrite(&to, buf, l); |
788 |
if ( f->len ) |
789 |
fWrite(&to, f->val, f->len); |
790 |
fWrite(&to, "\n", 1); |
791 |
} |
792 |
} |
793 |
} |
794 |
fSinkf(lo, eor, 1); |
795 |
if ( ! eor ) { |
796 |
lo->off += f->tag; /* no of fields passed by */ |
797 |
return; |
798 |
} |
799 |
reset: |
800 |
lo->off = 0; |
801 |
lClr(&lo->lst); |
802 |
} /* fSinkr */ |
803 |
|
804 |
|
805 |
int eOut ( int tag, const char *fmt, ... ) |
806 |
{ |
807 |
if ( env.out ) { |
808 |
va_list ap; |
809 |
va_start( ap, fmt ); |
810 |
lOut(&env.out->lst, tag, 0, fmt, ap); |
811 |
va_end( ap ); |
812 |
SINK(env.out); |
813 |
} |
814 |
return tag; |
815 |
} /* eOut */ |
816 |
|
817 |
|
818 |
int eRr ( int tag, const char *fmt, ... ) |
819 |
{ |
820 |
va_list ap; |
821 |
|
822 |
if ( ERR_NO == tag ) |
823 |
#ifndef WIN32 |
824 |
switch ( errno ) { |
825 |
case EINTR: case EAGAIN: |
826 |
tag = ERR_AGAIN; break; |
827 |
case EFAULT: case ENAMETOOLONG: case ELOOP: |
828 |
tag = ERR_FAULT; break; /* structurally bad address */ |
829 |
case EBADF: case ENOENT: case ENOTDIR: |
830 |
tag = ERR_BADF; break; /* file "does not exist" */ |
831 |
case EIO: case EEXIST: case EISDIR: case EACCES: case ENXIO: |
832 |
case ENODEV: case EROFS: case ETXTBSY: case ENOSPC: |
833 |
case EPIPE: case ESPIPE: |
834 |
tag = ERR_IO; break; /* file "is not accessible" */ |
835 |
case ENOMEM: case EMFILE: case ENFILE: |
836 |
tag = ERR_NOMEM; break; |
837 |
case EBUSY: |
838 |
tag = ERR_BUSY; break; |
839 |
default: |
840 |
tag = ERR_INVAL; |
841 |
} |
842 |
#else |
843 |
tag = ERR_INVAL; |
844 |
#endif |
845 |
if ( env.log > tag ) |
846 |
return tag; |
847 |
|
848 |
if ( !env.err ) |
849 |
return tag; |
850 |
va_start( ap, fmt ); |
851 |
lOut(&env.err->lst, -tag, 0, fmt, ap); |
852 |
va_end( ap ); |
853 |
|
854 |
/* logging afterburner */ |
855 |
if ( ERR_NOMEM >= tag && tag >= ERR_AGAIN ) { |
856 |
char *syserr = |
857 |
#ifndef WIN32 |
858 |
errno ? strerror(errno) : 0; |
859 |
#else |
860 |
0; |
861 |
char buf[256] = ""; |
862 |
if ( |
863 |
FormatMessage( |
864 |
FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, |
865 |
0, GetLastError(), 0, buf, sizeof(buf)-1, 0 ) |
866 |
) |
867 |
syserr = buf; |
868 |
buf[ sizeof(buf)-1 ] = 0; |
869 |
#endif |
870 |
if ( syserr ) { |
871 |
LAPP(&env.err->lst, ": ", 2); |
872 |
LAPPS(&env.err->lst, syserr); |
873 |
} |
874 |
} |
875 |
SINK(env.err); /* just trigger the sink, buffering should be handled there */ |
876 |
return tag; |
877 |
} /* eRr */ |
878 |
|
879 |
|
880 |
|
881 |
/* |
882 |
NOTE: no shared lib support |
883 |
*/ |
884 |
void cFini () |
885 |
{ |
886 |
dCloseAll(); |
887 |
/* close env's buffers */ |
888 |
if ( env.out ) env.out->snk(env.out, 1); |
889 |
if ( env.err ) env.err->snk(env.err, 1); |
890 |
} /* cFini */ |
891 |
|
892 |
|
893 |
void cInit ( List *opts, Sink *out, Sink *err ) |
894 |
{ |
895 |
Sink **oe; |
896 |
Fld opt = { 0, 0, 0 }; |
897 |
int i; |
898 |
|
899 |
if ( env.out ) |
900 |
return; |
901 |
memset(&env, 0, sizeof(env)); |
902 |
env.opt = opts; |
903 |
#ifndef WIN32 |
904 |
env.in = 0; |
905 |
#else |
906 |
if ( INVALID_HANDLE_VALUE == (env.in = GetStdHandle(STD_INPUT_HANDLE)) ) |
907 |
env.in = FIL_NONE; |
908 |
#endif |
909 |
env.out = out; |
910 |
env.err = err; |
911 |
oe = (Sink**)&env.out; |
912 |
for ( i=0; i<2; i++ ) { |
913 |
oe = i ? &env.err : &env.out; |
914 |
if ( *oe ) |
915 |
continue; |
916 |
*oe = (Sink*)mAlloc(sizeof(Sink)); |
917 |
#ifndef WIN32 |
918 |
(*oe)->dst = (void*)(i + 1); |
919 |
#else |
920 |
if ( INVALID_HANDLE_VALUE == |
921 |
((*oe)->dst = GetStdHandle(i?STD_ERROR_HANDLE:STD_OUTPUT_HANDLE)) |
922 |
) |
923 |
fOpen((file*)&(*oe)->dst, i ? "err.txt" : "out.txt", FIL_WR|FIL_CREAT); |
924 |
#endif |
925 |
if ( i ) { |
926 |
lInit(&env.err->lst, "err"); |
927 |
env.err->snk = fSinkl; |
928 |
} else { |
929 |
lInit(&env.out->lst, 0); |
930 |
env.out->snk = fSinkr; |
931 |
} |
932 |
} |
933 |
|
934 |
/* check pagesize */ |
935 |
#ifndef WIN32 |
936 |
env.psz = sysconf(_SC_PAGESIZE); /* getpagesize(); */ |
937 |
#else |
938 |
{ |
939 |
SYSTEM_INFO si; |
940 |
GetSystemInfo(&si); |
941 |
env.psz = si.dwPageSize; /* usually also 4k */ |
942 |
} |
943 |
#endif |
944 |
{ |
945 |
int t = env.psz; |
946 |
env.psh = 0; |
947 |
while (t >>= 1) env.psh++; |
948 |
if ( env.psz != (unsigned)1<<env.psh || env.psh > CPU_PAGE_SHIFT ) { |
949 |
eRr(ERR_TRASH, "weirdness! pagesize %d shift %d max %d", |
950 |
env.psz, env.psh, CPU_PAGE_SHIFT); |
951 |
exit(1); |
952 |
} |
953 |
if ( env.psh < 12 ) { /* but using multiples is ok */ |
954 |
eRr(LOG_WARN, "page_shift is %d !!!", env.psh); |
955 |
env.psz = 1<<(env.psh = 12); |
956 |
} |
957 |
} |
958 |
/* defaults */ |
959 |
env.log = LOG_WARN; |
960 |
env.wri = ENV_EXCL; |
961 |
env.qml = env.rml = 1<<(30-env.psh); /* map 1 GB at most */ |
962 |
|
963 |
if ( opts ) for ( opt.val = 0; vGet(&opt, opts->fld, "vw"); ) |
964 |
switch (opt.tag) { |
965 |
case 'v': |
966 |
/* |
967 |
logging: |
968 |
first char selects level, a 'b' as second sets buffering |
969 |
*/ |
970 |
if ( opt.len ) { |
971 |
switch ( *opt.val ) { |
972 |
case '0': case 'o': env.log = 0; break; |
973 |
case '1': case 'f': env.log = LOG_FATAL; break; |
974 |
case '2': case 's': env.log = LOG_IOERR /*LOG_SYSERR*/; break; |
975 |
case '3': case 'e': env.log = LOG_ERROR; break; |
976 |
case '4': case 'w': env.log = LOG_WARN; break; |
977 |
case '5': case 'i': env.log = LOG_INFO; break; |
978 |
case '6': case 'v': env.log = LOG_VERBOSE; break; |
979 |
case '7': case 'd': env.log = LOG_DEBUG; break; |
980 |
case '8': case 't': env.log = LOG_TRACE; break; |
981 |
case '9': case 'a': env.log = LOG_ALL; break; |
982 |
} |
983 |
if ( 1 < opt.len && 'b' == opt.val[1] ) |
984 |
env.flg |= ENV_BUFFER; |
985 |
} |
986 |
break; |
987 |
case 'w': /* write/concurrency mode */ |
988 |
if ( opt.len ) |
989 |
switch ( *opt.val ) { |
990 |
case '0': env.wri = ENV_RO; break; |
991 |
case 's': |
992 |
#ifdef BUILD_SHMODE |
993 |
env.wri = ENV_SHARED; |
994 |
env.flg |= ENV_MSYNC; |
995 |
#else |
996 |
eRr(ERR_INVAL, "no shared mode build"); |
997 |
#endif |
998 |
break; |
999 |
} |
1000 |
} /* if (in) for switch */ |
1001 |
|
1002 |
atexit( cFini ); |
1003 |
} /* cInit */ |