fixed parsing for science direct html with more than one <a href=> per one <tr>
[webpac] / openisis / lio.c
1 /*
2         openisis - an open implementation of the CDS/ISIS database
3         Version 0.8.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.  See the GNU
14         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: lio.c,v 1.43 2003/06/11 14:53:08 kripke Exp $
25         I/O support for the openisis library.
26 */
27 #include <stdlib.h>
28 #include <stdio.h> /* vsnprintf */
29 #include <stdarg.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <limits.h> /* PATH_MAX */
33 #include <sys/types.h>
34
35 #ifdef WIN32
36 #define WIN32_LEAN_AND_MEAN
37 #define NONAMELESSUNION
38 #       include <windows.h>
39 #       include <winsock.h>
40 #       define SHUT_RD SD_RECEIVE
41 #       define SHUT_WR SD_SEND
42 #       define SHUT_RDWR SD_BOTH
43 # define snprintf _snprintf
44 # define vsnprintf _vsnprintf
45 #       include <sys/timeb.h>
46 #else   /* have operating system */
47 #       define __USE_UNIX98
48 /*
49 #       define _POSIX_MAPPED_FILES
50 # define _POSIX_SYNCHRONIZED_IO
51 */
52 #       include <fcntl.h> /* or try unixio */
53 #       ifndef O_SYNC
54 #               if defined( __FreeBSD__ )
55 #                       define O_SYNC O_FSYNC
56 #               else
57 #                       define O_SYNC 0
58 #               endif
59 #       endif
60 #       include <sys/stat.h>
61 #       include <sys/file.h> /* flock */
62 #       include <sys/mman.h>
63 #       include <sys/socket.h>
64 #       include <unistd.h>
65 #endif /* WIN32 */
66 #include <sys/time.h> /* gettimeofday */
67 #include <time.h> /* localtime */
68
69 #include "luti.h" /* logging */
70 #include "lses.h"
71 #include "lstr.h" /* log_str */
72 #include "lio.h"
73
74
75
76 /* ************************************************************
77         private data
78 */
79 static int init;
80
81 #ifdef WIN32
82 /* it's not thread-safe, anyway */
83 static HANDLE hand[128];
84 #endif
85
86 /* ************************************************************
87         private functions
88 */
89 #ifndef WIN32
90 static int lerrno ( int errno_ )
91 {
92         switch ( errno_ ) {
93         case EINTR:
94         case EAGAIN:
95                 return ERR_OK;
96         case EFAULT: /* structurally bad address */
97         case ENAMETOOLONG:
98         case ELOOP:
99                 return ERR_FAULT;
100         case EBADF: /* file "does not exist" */
101         case ENOENT:
102         case ENOTDIR:
103                 return ERR_BADF;
104         case EIO: /* file "is not accessible" */
105         case EEXIST:
106         case EISDIR:
107         case EACCES:
108         case ENXIO:
109         case ENODEV:
110         case EROFS:
111         case ETXTBSY:
112         case ENOSPC:
113         case EPIPE:
114         case ESPIPE:
115                 return ERR_IO;
116         case ENOMEM:
117         case EMFILE:
118         case ENFILE:
119                 return ERR_NOMEM;
120         case EBUSY:
121                 return ERR_BUSY;
122         }
123         return ERR_INVAL;
124 }       /* lerrno */
125
126 /**
127         check unixio errno.
128         if harmless, return != 0 (is ok).
129         else return 0, possibly closing the file
130 */
131 int unixio_ok ( int *file, int io )
132 {
133         switch ( errno ) {
134         case EINTR:
135         case EAGAIN:
136                 return !0;
137         case EFAULT:
138         case ESPIPE:
139                 break;
140         default:
141                 log_msg( LOG_IOERR, "closing file %x for %x on %d", *file, io, errno );
142                 lio_close( file, io );
143         }
144         return 0;
145 }       /* unixio_err */
146 #endif
147
148
149 /**
150         return wether printing should be skipped
151 */
152 static int seterrlev ( int *err, int *level, int code )
153 {
154         *level = LOG_MASK & code;
155         *err = ERR_MASK & code;
156         if ( ! code )
157                 return 0;
158         if ( ! *err )
159                 switch ( *level ) {
160                 case LOG_FATAL: *err = ERR_IDIOT; break;
161                 case LOG_ERROR: *err = ERR_INVAL; break;
162                 case LOG_IOERR:
163 #ifndef WIN32
164                         if ( EAGAIN == errno || EINTR == errno ) /* no error */
165                                 return !0;
166                 case LOG_SYSERR:
167                         *err = lerrno( errno );
168 #else
169                 case LOG_SYSERR:
170                         *err = ERR_INVAL;
171 #endif
172                         break;
173                 default:
174                         *err = 0;
175                 }
176         else if ( ! *level ) {
177                 if ( ERR_TRASH <= *err )
178                         *level = LOG_FATAL;
179                 else if ( ERR_IO <= *err )
180                         *level = LOG_SYSERR;
181                 else if ( ERR_FAULT <= *err )
182                         *level = LOG_ERROR;
183                 else
184                         *level = LOG_VERBOSE;
185         }
186         return *level > (int)log_lev;
187 }       /* seterrlev */
188
189
190 /* ************************************************************
191         package data
192 */
193
194 CLockFunc *lio_lock;
195 LogLevel log_lev = LOG_ERROR;
196 int log_flush = 1;
197
198 #ifndef WIN32
199 #       define LIO_STATICFD( nm, flg ) int nm = flg
200 #else
201 #       define LIO_STATICFD( nm, flg ) int nm
202 #endif
203 LIO_STATICFD( lio_in , LIO_IN | 0 );
204 LIO_STATICFD( lio_out, LIO_OUT | 1 );
205 LIO_STATICFD( lio_err, LIO_OUT | 2 );
206
207 /* ************************************************************
208         package functions
209 */
210 static void lio_init ()
211 {
212 #ifndef WIN32
213         /* static */
214 #else
215         lio_in  = lio_open( "CONIN$", LIO_RD );
216         lio_out = lio_open( "CONOUT$", LIO_WR );
217         lio_err = lio_open( "oisiserr.txt", LIO_WR|LIO_CREAT );
218 #endif
219 }       /* lio_init */
220
221
222 /*
223         has "official" name, since visible to linker
224         use ld -init openIsisInit for shared object
225 */
226 void openIsisInit ()
227 {
228         static const char inimsg[] = "openIsisInit\n";
229         const char *ll = getenv("OPENISIS_LOGLEVEL");
230
231         if ( init )
232                 return;
233         init = 42;
234         lio_init();
235         if ( ll ) {
236                 cLog( ll[0], 0 );
237 #ifndef NDEBUG
238                 if ( LOG_DEBUG <= log_lev )
239                         lio_write( &lio_err, inimsg, sizeof(inimsg)-1 );
240 #endif
241         }
242         if ( getenv("OPENISIS_LOGBUFFERED") )
243                 log_flush = 0;
244         lses_init();
245 }       /* openIsisInit */
246
247
248 /*
249         use ld -fini openIsisFini for shared object
250 */
251 void openIsisFini ()
252 {
253         static const char finimsg[] = "openIsisFini\n";
254
255         lses_fini();
256 #ifndef NDEBUG
257         if ( LOG_DEBUG <= log_lev )
258                 lio_write( &lio_err, finimsg, sizeof(finimsg)-1 );
259 #endif
260 }       /* openIsisFini */
261
262
263 void linit () /* called by cOpen() */
264 {
265         if ( init ) /* we're an .so -- implicitly initialized */
266                 return;
267         /* we're statically linked */
268         openIsisInit();
269         atexit( openIsisFini );
270 }
271
272
273
274
275 int lio_open ( const char *name, int flags )
276 {
277         int fd = -1;
278         int faillvl = (LIO_TRY == ((LIO_TRY|LIO_WR)&flags))
279                 ? (LOG_VERBOSE|ERR_BADF) : LOG_INFO|ERR_BADF /* LOG_IOERR too annoying ... */;
280
281         if ( '&' == name[0] ) {
282                 int i = 1;
283                 fd = 0;
284                 while ( '0' <= name[i] && name[i] <= '9' )
285                         fd = 10*fd + name[i++] - '0';
286         }
287
288 #ifndef WIN32
289         if ( 0 > fd ) {
290                 int f = !(LIO_WR&flags) ? O_RDONLY
291                         : ((LIO_RD&flags) ? O_RDWR : O_WRONLY);
292                 if ( LIO_CREAT & flags && LIO_WR & flags ) f |= O_CREAT;
293                 if ( LIO_NBLK & flags ) f |= O_NONBLOCK;
294                 if ( LIO_SYNC & flags ) f |= O_SYNC;
295                 if ( LIO_TRUNC & flags ) f |= O_TRUNC;
296                 if ( ! (LIO_SEEK & flags) ) f |= O_APPEND;
297                 fd = open( name, f, 00664 ); /* let umask finetune */
298                 if ( LIO_FD < fd ) {
299                         log_msg( LOG_ERROR, "got big fd %d", fd );
300                         fd = -1;
301                 }
302                 if ( 0 > fd )
303                         return log_msg( faillvl, "could not open '%s' %x", name, flags );
304                 if ( LIO_FLOCK & flags ) {
305                         const char *lck = LIO_WR&flags ? "exclusive" : "shared";
306         /*
307                 we want an advisory lock, so that tail -f can read changes.
308                 SYSV/POSIX fcntl might be mandatory, depending on files mode bits
309                 (and filesystem). Therefore prefer BSD style flock, if available.
310
311                 under linux, flock is never mandatory, while lockf/fcntl may be.
312                 /usr/src/linux/Documentation/mandatory.txt
313                 moreover fcntl attempts to lock over NFS, which is a very feeble idea.
314         */
315 #ifndef LOCK_SH
316         /*
317                 on solaris, LOCK_SH is declared only with the /usr/ucb/cc includes.
318                 their flock on fcntl emulation "must not be used in MT environments".
319         */
320                         struct flock fl;
321                         memset( &fl, 0, sizeof(fl) );
322                         fl.l_type   = LIO_WR&flags ? F_WRLCK : F_RDLCK;
323                         fl.l_whence = SEEK_SET;
324                         fl.l_start  = 0;
325                         fl.l_len    = 1; /* 0 locks the whole file -- could be mandatory :( */
326 #endif
327                         log_msg( LOG_VERBOSE, "attempting %s lock on '%s'", lck, name );
328                         if (
329 #ifndef LOCK_SH
330                                 /* hmm .. at least this is "POSIX" */
331                                 fcntl( fd, LIO_WLOCK&flags ? F_SETLKW : F_SETLK, &fl )
332 #else
333                                 flock( fd,
334                                 (LIO_WR&flags ? LOCK_EX : LOCK_SH) | (LIO_WLOCK&flags ? 0 : LOCK_NB) )
335 #endif
336                         ) {
337                                 close( fd );
338                                 return log_msg( ERR_BADF, "could not get %s lock on '%s'", lck, name );
339                         }
340                 }
341         } else if ( -1 == fcntl( fd, F_GETFL ) ) /* check open */
342                 return -ERR_BADF;
343 #else
344         if ( 0 > fd ) {
345                 HANDLE h;
346                 int acc = 0;
347                 int shr = FILE_SHARE_READ | (LIO_FLOCK&flags ? 0 : FILE_SHARE_WRITE);
348                 int cre = (LIO_CREAT & flags && LIO_WR & flags)
349                         ? (LIO_TRUNC & flags) ? CREATE_ALWAYS : OPEN_ALWAYS
350                         : (LIO_TRUNC & flags) ? TRUNCATE_EXISTING : OPEN_EXISTING;
351                 int f = (LIO_WR&flags) ? FILE_ATTRIBUTE_ARCHIVE : FILE_ATTRIBUTE_NORMAL;
352
353                 if ( LIO_RD & flags ) acc |= GENERIC_READ; /* FILE_READ_DATA; */
354                 if ( LIO_WR & flags ) acc |= GENERIC_WRITE;
355                         /* some routines like GetFileSize are "documented" to require
356                                 GENERIC_READ/GENERIC_WRITE, so it may not be sufficient to use:
357                         (LIO_SEEK & flags) ? FILE_WRITE_DATA : FILE_APPEND_DATA */;
358                 /* if ( LIO_NBLK & flags ) f |= FILE_FLAG_OVERLAPPED; */
359                 if ( LIO_SYNC & flags ) f |= FILE_FLAG_WRITE_THROUGH;
360                 f |= (LIO_SEEK & flags) ? FILE_FLAG_RANDOM_ACCESS
361                         : FILE_FLAG_SEQUENTIAL_SCAN;
362
363                 for ( fd=0; hand[fd]; )
364                         if ( sizeof(hand)/sizeof(hand[0]) == ++fd )
365                                 return -EMFILE;
366                 h = CreateFile( name, acc, shr, 0, cre, f, 0 );
367                 if ( INVALID_HANDLE_VALUE == h )
368                         return log_msg( faillvl, "could not open '%s' %x", name, flags );
369                 hand[fd] = h;
370                 if ( (LIO_WR & flags) && ! (LIO_SEEK & flags) )
371                         SetFilePointer( hand[fd], 0, 0, FILE_END );
372         } else if ( (int)(sizeof(hand)/sizeof(hand[0])) <= fd || ! hand[fd] )
373                 return -ERR_BADF;
374 #endif
375         fd |= flags & LIO_WANT;
376         /* add status flags */
377         if ( LIO_RD & flags ) fd |= LIO_IN;
378         if ( LIO_WR & flags ) fd |= LIO_OUT;
379         return fd;
380 }       /* lio_open */
381
382
383 int lio_close ( int *file, int flags )
384 {
385         int fd = 0xffff & *file;
386         int op = LIO_INOUT & *file;
387         int cl = LIO_INOUT & flags;
388         if ( ! op || ! cl )
389                 return 0;
390         if ( 0 > *file ) { /* probably failed open */
391                 *file = 0;
392                 return 0;
393         }
394         *file &= ~cl;
395         if ( op & ~cl ) { /* remains partly open */
396                 int how = (LIO_IN & cl) ? SHUT_RD : SHUT_WR;
397                 if ( !(LIO_SOCK & fd) )
398                         return 0;
399 #ifndef WIN32
400                 if ( ! shutdown( fd, how ) )
401                         return 0;
402 #else
403                 /* TODO */
404 #endif
405                 log_msg( LOG_SYSERR, "could not shutdown sock %d %d", fd, how );
406                 return errno ? -errno : -1;
407         }
408 #ifndef WIN32
409         for ( fsync( fd ); close( fd ); errno = 0 ) 
410                 if ( EINTR != errno ) {
411                         log_msg( LOG_SYSERR, "could not close file %d", fd );
412                         return errno ? -errno : -1;
413                 }
414 #else
415         if ( (int)(sizeof(hand)/sizeof(hand[0])) <= fd )
416                 return -ERR_BADF;
417         CloseHandle( hand[fd] );
418         hand[fd] = 0;
419 #endif
420         return 0;
421 }       /* lio_close */
422
423
424 int lio_size ( int file )
425 {
426         if ( !((LIO_IN|LIO_OUT) & file) )
427                 return log_msg( ERR_BADF, "*file 0x%x not open for stat", file );
428         {
429         int fd = LIO_FD & file;
430 #ifndef WIN32
431         struct stat s;
432         return fstat( fd, &s ) ? 0 : s.st_size;
433 #else
434         return GetFileSize( hand[fd], 0 );
435 #endif
436         }
437 }       /* lio_size */
438
439
440 unsigned lio_time ( int file )
441 {
442         if ( !((LIO_IN|LIO_OUT) & file) )
443                 return log_msg( ERR_BADF, "*file 0x%x not open for stat", file );
444         {
445         int fd = LIO_FD & file;
446 #ifndef WIN32
447         struct stat s;
448         return fstat( fd, &s ) ? 0 : (unsigned)s.st_mtime;
449 #else
450         FILETIME foo; /* time since 160101011200 UTC in hundred nanoseconds !!! */
451         ull bar;
452         if ( ! GetFileTime( hand[fd], 0, 0, &foo ) )
453                 return 0;
454         bar = ((ull)foo.dwHighDateTime<<32 | (ull)foo.dwLowDateTime) / ULL(10000000);
455         return (unsigned)(bar - ULL(11644473600));
456 #endif
457         }
458 }       /* lio_size */
459
460
461 int lio_read ( int *file, void *buf, unsigned count )
462 {
463         if ( !(LIO_IN & *file) )
464                 return log_msg( ERR_BADF, "*file 0x%x not open for reading", *file );
465         {
466         int fd = LIO_FD & *file;
467 #ifndef WIN32
468         int got = read( fd, buf, count );
469         /* log_msg( LOG_ERROR, "read 0x%x got %d", *file, got ); */
470         if ( got )
471                 return 0 < got ? got
472                         : unixio_ok( file, LIO_IN ) ? 0
473                         : log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd );
474         else if ( LIO_SEEK & *file ) /* special EOF treatment */
475                 return 0;
476         else {
477                 lio_close( file, LIO_IN );
478                 return -ERR_EOF;
479         }
480 #else
481         DWORD got = 0;
482         int ok = ReadFile( hand[fd], buf, count, &got, 0 );
483         return ok ? got
484                 : log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd );
485 #endif
486         }
487 }       /* lio_read */
488
489
490 int lio_write ( int *file, const void *buf, unsigned count )
491 {
492         if ( !(LIO_OUT & *file) )
493                 return log_msg( ERR_BADF, "*file 0x%x not open for writing", *file );
494         {
495         int fd = LIO_FD & *file;
496 #ifndef WIN32
497         int got = write( fd, buf, count );
498         return 0 <= got ? got
499                 : unixio_ok( file, LIO_OUT ) ? 0
500                 : log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd );
501 #else
502         DWORD got = 0;
503         int ok = WriteFile( hand[fd], buf, count, &got, 0 );
504         return ok ? got
505                 : log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd );
506 #endif
507         }
508 }       /* lio_write */
509
510
511
512 int lio_seek ( int *file, int offset )
513 {
514         if ( !(LIO_INOUT & *file) )
515                 return log_msg( ERR_BADF, "*file 0x%x not open", *file );
516         {
517         int fd = LIO_FD & *file;
518 #ifndef WIN32
519         int got = (int)lseek( fd, offset, SEEK_SET );
520         return offset == got ? 0
521                 : log_msg( LOG_IOERR, "could not seek to %d", offset );
522 #else
523         return 0xffffffff /* "INVALID_SET_FILE_POINTER" */
524                 == SetFilePointer( hand[fd], offset, 0, FILE_BEGIN )
525                 ? -1 : 0;
526 #endif
527         }
528 }       /* lio_seek */
529
530
531 int lio_pread ( int *file, void *buf, unsigned count, int offset )
532 {
533         if ( !(LIO_IN & *file) )
534                 return log_msg( ERR_BADF, "*file 0x%x not open for reading", *file );
535         {
536         int fd = LIO_FD & *file;
537 #ifndef WIN32
538         int got = pread( fd, buf, count, offset );
539         return 0 <= got ? got
540                 : unixio_ok( file, LIO_IN ) ? 0
541                 : log_msg( LOG_IOERR, "could not read %d bytes from %d at %d",
542                                 count, fd, offset );
543 #else
544         return 0xffffffff /* "INVALID_SET_FILE_POINTER" */
545                 == SetFilePointer( hand[fd], offset, 0, FILE_BEGIN )
546                 ? -1 : lio_read( file, buf, count );
547 #endif
548         }
549 }       /* lio_pread */
550
551
552 int lio_pwrite ( int *file, const void *buf, unsigned count, int offset )
553 {
554         if ( !(LIO_OUT & *file) )
555                 return log_msg( ERR_BADF, "*file 0x%x not open for writing", *file );
556         {
557         int fd = LIO_FD & *file;
558 #ifndef WIN32
559         int got = pwrite( fd, buf, count, offset );
560         return 0 <= got ? got
561                 : unixio_ok( file, LIO_OUT ) ? 0
562                 : log_msg( LOG_IOERR, "could not read %d bytes from %d at %d",
563                                 count, fd, offset );
564 #else
565         return 0xffffffff /* "INVALID_SET_FILE_POINTER" */
566                 == SetFilePointer( hand[fd], offset, 0, FILE_BEGIN )
567                 ? -1 : lio_write( file, buf, count );
568 #endif
569         }
570 }       /* lio_pwrite */
571
572
573 int lio_trunc ( int *file, int offset )
574 {
575         int fd = LIO_FD & *file;
576 #ifndef WIN32
577         ftruncate( fd, offset );
578 #else
579         /* SetFileValidData( hand[fd], offset ); XP only */
580         return 0xffffffff /* "INVALID_SET_FILE_POINTER" */
581                 == SetFilePointer( hand[fd], offset, 0, FILE_BEGIN )
582                 ? -1 : SetEndOfFile( hand[fd] )
583                 ? 0 : log_msg( LOG_IOERR, "could not truncate %d to %d", fd, offset );
584 #endif
585         return 0;
586 }       /* lio_trunc */
587
588
589 int lio_mmap ( int *file, void **map, int length )
590 {
591         if ( ! map )
592                 return log_msg( ERR_INVAL, "no map" );
593         if ( 0 >= length && 0 >= (length = lio_size( *file )) ) {
594                 *map = 0;
595                 return length;
596         }
597 #ifndef WIN32
598 #if 0 /* solaris states it will round up itself, and linux does */
599         {
600                 static int ps = 0;
601                 if ( !ps )
602                         ps = sysconf(_SC_PAGESIZE); /* getpagesize(); */
603         }
604 #endif
605         if ( file ) {
606                 if ( *map )
607                         return msync( *map, length, (LIO_SYNC & *file)
608                                 ? (MS_SYNC|MS_INVALIDATE) : MS_ASYNC )
609                                 ? log_msg( LOG_IOERR, "msync" ) : 0;
610                 *map = mmap( 0, length, PROT_READ | ((LIO_OUT&*file) ? PROT_WRITE : 0),
611                         MAP_SHARED, LIO_FD&*file, 0 );
612                 if ( MAP_FAILED != *map )
613                         return length;
614                 *map = 0;
615                 return log_msg( ERR_NOMEM, "mmap failed on fd %08x", *file );
616         }
617         if ( ! *map )
618                 return log_msg( ERR_INVAL, "no *map" );
619         munmap( *map, length );
620         *map = 0;
621         return 0;
622 #else
623         /* awfully complicated here
624                 -- need  CreateFileMapping, MapViewOfFile
625                 stupid piece of shrott
626                 true mapping supported in NT family only, 9x copies to swap
627  */
628         *map = 0;
629         return 0;
630 #endif
631 }       /* lio_mmap */
632
633
634 int lio_slurp ( char **buf, int sz, const char *name, int opt )
635 {
636         int file = lio_open( name, LIO_RD|LIO_SEEK|(opt ? LIO_TRY : 0) );
637         int size;
638         int ret = -ERR_NOMEM;
639         char *p = *buf;
640         /* log_msg( LOG_IOERR, "open '%s' = %d", name, file ); */
641         if ( 0 > file )
642                 return file;
643         size = lio_size( file );
644         if ( 0 >= size )
645                 return size;
646         if ( size > sz )
647                 LOG_OTO( done, ( ERR_INVAL, "file '%.30s' too big: %d > %d",
648                         name, size, sz ) );
649         if ( ! p && !(p = mAlloc(size)) )
650                 goto done;
651         ret = lio_read( &file, p, size );
652         if ( size == ret )
653                 *buf = p;
654         else {
655                 if ( ret >= 0 )
656                         ret = log_msg( ERR_IO, "OOPS! got %d of %d bytes from '%.30s'",
657                                 ret, size, name );
658                 if ( ! *buf )
659                         free( p );
660         }
661 done:
662         lio_close( &file, LIO_INOUT );
663         return ret;
664 }       /* lio_slurp */
665
666
667 int log_msg ( int code, const char *fmt, ... )
668 {
669         static const char toolong[] = ": message too long !!!\n";
670         int err, level;
671         va_list ap;
672         va_start( ap, fmt );
673 #if 0
674         ret = sMsg( MSG_VA|code, fmt, ap );
675         make log_msg unbuffered, use sMsg for buffered logging
676 #else
677         if ( ! seterrlev( &err, &level, code ) && (LIO_OUT & lio_err) ) {
678                 char buf[4096];
679                 int len = vsnprintf( (char*)buf, sizeof(buf), fmt, ap );
680                 if ( 0 < len && len < (int)sizeof(buf) ) {
681                         buf[len++] = '\n';
682                         lio_write( &lio_err, buf, len );
683                 } else {
684                         lio_write( &lio_err, fmt, strlen(fmt) );
685                         lio_write( &lio_err, toolong, sizeof(toolong)-1 );
686                 }
687         }
688 #endif
689         va_end( ap );
690         return -err;
691 }       /* log_msg */
692
693
694 void log_str ( LogLevel level, int *rec, const char **desc )
695 {
696         int occ = -1;
697         int nmbrs  = LSTRFIX(*rec);
698         int *mbr  = rec+1;
699         char *base  = (char*)rec;
700
701         if ( level > log_lev || ! desc )
702                 return;
703
704         sMsg( 2, "record %.20s\n", *desc++ );
705         /* dump the fixed part (occ==-1) and each occurrence of repeated part. */
706         for ( ;/* occ < LSTROCC(*dst) */; ) { /* dump one part */
707                 int i;
708                 for ( i=0; i<nmbrs; i++, mbr++ ) { /* dump one mbr */
709                         if ( '\'' == *desc[i] )
710                                 sMsg( 2, "%3d.%2d %4.4s %.67s<\n",
711                                         occ, i, desc[i], base+*mbr );
712                         else
713                                 sMsg( 2, "%3d.%2d %4.4s 0x%08x = %d\n",
714                                         occ, i, desc[i], *mbr, *mbr );
715                 }       /* for mbrs */
716
717                 if ( ++occ >= LSTROCC(*rec) )
718                         break;
719                 if ( ! occ ) { /* was the fixed part, setup for repeated */
720                         nmbrs = LSTRREP(*rec);
721                         desc  += i;
722                 }
723         }
724 }       /* log_str */
725
726
727 void log_hex ( LogLevel level, const void *mem, int len )
728 {
729         const char *p = (const char *)mem;
730         char buf[82];
731         int i = 0;
732
733         if ( level > log_lev )
734                 return;
735         for ( ; i<len; i+=16, p+=16 ) {
736                 int j = 0;
737                 int left = len -i;
738                 int pos = 10;
739                 sprintf( buf, "%08x  ", i );
740                 if ( left > 16 )
741                         left = 16;
742                 for ( ; j < left; j++ ) {
743                         sprintf( buf+pos, "%02x", p[j] );
744                         pos += 2;
745                         if ( 3 == j%4 ) { buf[pos++] = ' '; buf[pos++] = ' '; }
746                 }
747                 for ( ; j < 16; j++ ) {
748                         buf[pos++] = ' '; buf[pos++] = ' ';
749                         if ( 3 == j%4 ) { buf[pos++] = ' '; buf[pos++] = ' '; }
750                 }
751                 /* got 50 = 10 + 16*2 + 4*2 */
752                 for ( j=0; j < left; j++ ) /* add up to 16 */
753                         buf[pos++] = (0x60 & p[j]) ? p[j] : '.';
754                 buf[pos++] = '\n';
755                 buf[pos] = 0;
756                 sMsg( 2, "%.*s", pos, buf );
757         }
758 }       /* log_hex */
759
760
761 /* ************************************************************
762         public functions
763 */
764 int timeUpd ( Tm *tm )
765 {
766         Tm o;
767 #ifdef WIN32
768         struct _timeb tb;
769         _ftime( &tb );
770         if ( !tm )
771                 return tb.time;
772         o = *tm;
773         tm->millis = tb.time*LLL(1000) + tb.millitm;
774 #else
775         struct timeval tv;
776         gettimeofday( &tv, 0 );
777         if ( !tm )
778                 return tv.tv_sec;
779         o = *tm;
780         tm->millis = tv.tv_sec*LLL(1000) + tv.tv_usec/1000;
781 #endif
782         return (int)(tm->millis - o.millis);
783 }       /* timeUpd */
784
785
786 static int timeLoc ( struct tm *t, Tm *tm )
787 {
788         Tm x;
789         time_t tt;
790         if ( !tm || !tm->millis )
791                 timeUpd( tm ? tm : (tm = &x) );
792         tt = (time_t)(tm->millis / 1000);
793 #ifdef WIN32 /* did I mention it's not threadsafe ? */
794         *t = *localtime( &tt );
795 #else
796         localtime_r( &tt, t );
797 #endif
798         return (int)(tm->millis % 1000);
799 }       /* timeLoc */
800
801 char *timeGtf ( char *buf, Tm *tm )
802 {
803         struct tm t;
804         timeLoc( &t, tm );
805         snprintf( buf, 15, "%04u%02u%02u%02u%02u%02u",
806                 1900+t.tm_year, 1+t.tm_mon, t.tm_mday,
807                 t.tm_hour, t.tm_min, t.tm_sec );
808         buf[14] = 0;
809         return buf;
810 }       /* timeGtf */
811
812
813 char *timeGtfm ( char *buf, Tm *tm )
814 {
815         struct tm t;
816         int millis = timeLoc( &t, tm );
817         snprintf( buf, 19, "%04u%02u%02u%02u%02u%02u%03u",
818                 1900+t.tm_year, 1+t.tm_mon, t.tm_mday,
819                 t.tm_hour, t.tm_min, t.tm_sec, millis );
820         buf[18] = 0;
821         return buf;
822 }       /* timeGtfm */
823
824
825 void timeSleep ( Tm *tm )
826 {
827 #ifdef WIN32
828         Sleep( tm->millis );
829 #else
830         struct timespec ts;
831         ts.tv_sec = tm->millis / 1000;
832         ts.tv_nsec = 1000000 * (int)(tm->millis % 1000);
833         nanosleep( &ts, 0 );
834 #endif
835 }       /* timeSleep */
836
837
838 int ioStream ( Ios *s, int op )
839 {
840         Buf *b;
841         switch ( op ) {
842         case LIO_SSIZE:
843                 return sizeof(Ios);
844         case LIO_SOPEN:
845                 s->pos = s->b.fill = s->b.done = 0;
846         case LIO_SCLOSE:
847                 s->file &= ~LIO_INOUT;
848                 return 0;
849         case LIO_SPURGE:
850                 if ( !(LIO_IN & s->file) )
851                         return -ERR_BADF;
852                 /* try to get 4k chunk */
853                 b = /* s->alt ? s->alt : */ &s->b;
854                 if ( LIO_BUFSIZ/2 < b->fill && b->done ) { /* move contents downwards */
855                         if ( b->done < b->fill )
856                                 memmove( b->c, b->c + b->done, b->fill - b->done );
857                         s->pos += b->done;
858                         b->fill -= b->done;
859                         b->done = 0;
860                 }
861                 return LIO_BUFSIZ <= b->fill ? 0
862                         : LIO_BUFSIZ/2 < b->fill ? LIO_BUFSIZ - b->fill
863                         : LIO_BUFSIZ/2;
864         case LIO_SFILL:
865         case LIO_SFLUSH:
866                 s->file &= ~LIO_INOUT;
867         }
868         return -ERR_EOF;
869 }       /* ioStream */
870
871
872 int ioStdio ( Ios *s, int op )
873 {
874         Buf *b;
875         int ret;
876         switch ( op ) {
877         case LIO_SOPEN:
878                 ioStream( s, op );
879                 if ( 0 < (ret = lio_open( s->name, s->file )) )
880                         s->file = ret;
881                 return ret;
882         case LIO_SCLOSE:
883                 if ( LIO_OUT & s->file )
884                         s->func( s, LIO_SFLUSH );
885                 /* don't close borrowed fd */
886                 return '&' == *s->name ? 0 : lio_close( &s->file, LIO_INOUT );
887         case LIO_SFILL:
888                 if ( !(LIO_IN & s->file) )
889                         return -ERR_BADF;
890                 if ( 0 < (ret = ioStream( s, LIO_SPURGE ))
891                         && 0 <= (ret = lio_read( &s->file, s->b.c + s->b.fill, ret ))
892                 )
893                         s->b.fill += ret;
894                 log_msg( LOG_ALL, "LIO_FILL: got %d now %d", ret, s->b.fill );
895                 return ret;
896         case LIO_SFLUSH:
897                 if ( !(LIO_OUT & s->file) )
898                         return -ERR_BADF;
899                 b = /* s->alt ? s->alt : */ &s->b;
900                 if ( b->fill <= b->done )
901                         return 0;
902                 if ( 0 < (ret = lio_write( &s->file, b->c + b->done, b->fill - b->done )) )
903                         b->done += ret;
904                 if ( b->fill == b->done ) {
905                         s->pos += b->done;
906                         b->fill = b->done = 0;
907                 } else if ( b->done && LIO_BUFSIZ/2 < b->fill )
908                         ioStream( s, LIO_SPURGE );
909                 return ret;
910         }
911         return ioStream( s, op );
912 }       /* ioStdio */
913
914
915 int sMsg ( int to, const char *fmt, ... )
916 {
917         SESDECL
918         int code = (LOG_MASK|ERR_MASK) & to, err, level;
919         int fd = LIO_FD & to; /* stream id uses same mask as system fd */
920         Ios *o = ses->io[ fd ? fd : code ? 2 : 1 ];
921         va_list ap;
922         int space;
923         int ret;
924         
925         if ( LSES_FILE_MAX <= fd || ! o )
926                 return -1;
927
928         if ( seterrlev( &err, &level, code ) ) /* logging */
929                 return -err;
930
931         /* core fprintf */
932         if ( LIO_BUFSIZ/2 < o->b.fill )
933                 LIO_FLUSH( o );
934         space = LIO_BUFSIZ - o->b.fill;
935         va_start( ap, fmt );
936         ret = vsnprintf( (char*)o->b.c + o->b.fill, space, fmt,
937                 (MSG_VA & to) ? va_arg( ap, va_list ) : ap );
938         va_end( ap );
939         if ( ret < 0 || space < ret ) /* outta space */
940                 ret = space;
941         o->b.fill += ret;
942         if ( ! code )
943                 return ret;
944
945         /* logging afterburner */
946         if ( LOG_SYSERR == level || LOG_IOERR == level ) {
947                 int len;
948                 char *syserr =
949 #ifndef WIN32
950                         strerror(errno);
951 #else
952                         0;
953                 char buf[256] = "";
954                 if ( 
955                         FormatMessage(
956                                 FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
957                                 0, GetLastError(), 0, buf, sizeof(buf)-1, 0 )
958                 )
959                         syserr = buf;
960                 buf[ sizeof(buf)-1 ] = 0;
961 #endif
962                 if ( syserr ) {
963                         len = strlen( syserr );
964                         if ( o->b.fill+4+len < LIO_BUFSIZ ) {
965                                 memcpy( o->b.c + o->b.fill, "\n\t: ", 4 );
966                                 memcpy( o->b.c + o->b.fill+4, syserr, len );
967                                 o->b.fill += 4+len;
968                         }
969                 }
970         }
971         if ( o->b.fill < LIO_BUFSIZ )
972                 o->b.c[ o->b.fill++ ] = '\n';
973         if ( log_flush )
974                 LIO_FLUSH( o );
975         return -err;
976 }       /* sMsg */
977
978
979 int sGets ( int fd, char **ptr, char delim )
980 {
981         SESDECL
982         Ios *s = ses->io[ LIO_FD & fd ];
983
984         if ( s->b.fill > s->b.done || 0 < LIO_FILL( s ) ) for (;;) {
985                 char *f = memchr( s->b.c + s->b.done, delim, s->b.fill - s->b.done );
986                 int len;
987                 log_msg( LOG_ALL, "sGets: done %d fill %d", s->b.done, s->b.fill );
988                 f = memchr( s->b.c + s->b.done, delim, s->b.fill - s->b.done );
989                 if ( ! f ) {
990                         if ( (s->b.fill < LIO_BUFSIZ || s->b.done) /* buffer avail */
991                                 && (LIO_IN & s->file) /* file open */
992                                 && 0 < LIO_FILL( s )
993                         )
994                                 continue;
995                         else if ( s->b.done >= s->b.fill )
996                                 break;
997                         f = (char*)s->b.c + s->b.fill;
998                 }
999                 len = f - (*ptr = (char*)s->b.c + s->b.done);
1000                 s->b.done += len + 1;
1001                 if ( len && '\n' == delim && '\r' == (*ptr)[len-1] )
1002                         len--;
1003                 return len;
1004         }
1005         *ptr = 0;
1006         return -1;
1007 }       /* sGets */
1008
1009
1010 void cLog ( int level, const char *filename )
1011 {
1012         if ( 0 > level ) /* no change */
1013                 ;
1014         else if ( LOG_LEVELS > level ) /* by basic number */
1015                 level <<= LOG_SHIFT;
1016         else if ( 'z' >= level ) { /* by ascii value */
1017                 switch ( level ) {
1018                 case '-': level = LOG_NOCHANGE; break;
1019                 case 'o': level = LOG_OFF; break;
1020                 case 'f': level = LOG_FATAL; break;
1021                 case 's': level = LOG_IOERR /*LOG_SYSERR*/; break;
1022                 default:
1023                         if ( '0' <= level && level <= '9' ) {
1024                                 level = (level - '0')<<LOG_SHIFT;
1025                                 break;
1026                         }
1027                 case 'e': level = LOG_ERROR; break;
1028                 case 'w': level = LOG_WARN; break;
1029                 case 'i': level = LOG_INFO; break;
1030                 case 'v': level = LOG_VERBOSE; break;
1031                 case 'd': level = LOG_DEBUG; break;
1032                 case 't': level = LOG_TRACE; break;
1033                 case 'a': level = LOG_ALL; break;
1034                 }
1035         }
1036         if ( level >= 0 )
1037                 log_lev = (LogLevel)level;
1038         (void)filename;
1039         /* TODO if ( filename )
1040                 sOpen( cOpen(0), "2>"filename, 0, ioStdio ) */
1041 }       /* cLog */