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
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.
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.
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
20 see README for more information
24 $Id: lio.c,v 1.43 2003/06/11 14:53:08 kripke Exp $
25 I/O support for the openisis library.
28 #include <stdio.h> /* vsnprintf */
32 #include <limits.h> /* PATH_MAX */
33 #include <sys/types.h>
36 #define WIN32_LEAN_AND_MEAN
37 #define NONAMELESSUNION
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 */
49 # define _POSIX_MAPPED_FILES
50 # define _POSIX_SYNCHRONIZED_IO
52 # include <fcntl.h> /* or try unixio */
54 # if defined( __FreeBSD__ )
55 # define O_SYNC O_FSYNC
60 # include <sys/stat.h>
61 # include <sys/file.h> /* flock */
62 # include <sys/mman.h>
63 # include <sys/socket.h>
66 #include <sys/time.h> /* gettimeofday */
67 #include <time.h> /* localtime */
69 #include "luti.h" /* logging */
71 #include "lstr.h" /* log_str */
76 /* ************************************************************
82 /* it's not thread-safe, anyway */
83 static HANDLE hand[128];
86 /* ************************************************************
90 static int lerrno ( int errno_ )
96 case EFAULT: /* structurally bad address */
100 case EBADF: /* file "does not exist" */
104 case EIO: /* file "is not accessible" */
128 if harmless, return != 0 (is ok).
129 else return 0, possibly closing the file
131 int unixio_ok ( int *file, int io )
141 log_msg( LOG_IOERR, "closing file %x for %x on %d", *file, io, errno );
142 lio_close( file, io );
150 return wether printing should be skipped
152 static int seterrlev ( int *err, int *level, int code )
154 *level = LOG_MASK & code;
155 *err = ERR_MASK & code;
160 case LOG_FATAL: *err = ERR_IDIOT; break;
161 case LOG_ERROR: *err = ERR_INVAL; break;
164 if ( EAGAIN == errno || EINTR == errno ) /* no error */
167 *err = lerrno( errno );
176 else if ( ! *level ) {
177 if ( ERR_TRASH <= *err )
179 else if ( ERR_IO <= *err )
181 else if ( ERR_FAULT <= *err )
184 *level = LOG_VERBOSE;
186 return *level > (int)log_lev;
190 /* ************************************************************
195 LogLevel log_lev = LOG_ERROR;
199 # define LIO_STATICFD( nm, flg ) int nm = flg
201 # define LIO_STATICFD( nm, flg ) int nm
203 LIO_STATICFD( lio_in , LIO_IN | 0 );
204 LIO_STATICFD( lio_out, LIO_OUT | 1 );
205 LIO_STATICFD( lio_err, LIO_OUT | 2 );
207 /* ************************************************************
210 static void lio_init ()
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 );
223 has "official" name, since visible to linker
224 use ld -init openIsisInit for shared object
228 static const char inimsg[] = "openIsisInit\n";
229 const char *ll = getenv("OPENISIS_LOGLEVEL");
238 if ( LOG_DEBUG <= log_lev )
239 lio_write( &lio_err, inimsg, sizeof(inimsg)-1 );
242 if ( getenv("OPENISIS_LOGBUFFERED") )
249 use ld -fini openIsisFini for shared object
253 static const char finimsg[] = "openIsisFini\n";
257 if ( LOG_DEBUG <= log_lev )
258 lio_write( &lio_err, finimsg, sizeof(finimsg)-1 );
263 void linit () /* called by cOpen() */
265 if ( init ) /* we're an .so -- implicitly initialized */
267 /* we're statically linked */
269 atexit( openIsisFini );
275 int lio_open ( const char *name, int flags )
278 int faillvl = (LIO_TRY == ((LIO_TRY|LIO_WR)&flags))
279 ? (LOG_VERBOSE|ERR_BADF) : LOG_INFO|ERR_BADF /* LOG_IOERR too annoying ... */;
281 if ( '&' == name[0] ) {
284 while ( '0' <= name[i] && name[i] <= '9' )
285 fd = 10*fd + name[i++] - '0';
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 */
299 log_msg( LOG_ERROR, "got big fd %d", 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";
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.
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.
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".
321 memset( &fl, 0, sizeof(fl) );
322 fl.l_type = LIO_WR&flags ? F_WRLCK : F_RDLCK;
323 fl.l_whence = SEEK_SET;
325 fl.l_len = 1; /* 0 locks the whole file -- could be mandatory :( */
327 log_msg( LOG_VERBOSE, "attempting %s lock on '%s'", lck, name );
330 /* hmm .. at least this is "POSIX" */
331 fcntl( fd, LIO_WLOCK&flags ? F_SETLKW : F_SETLK, &fl )
334 (LIO_WR&flags ? LOCK_EX : LOCK_SH) | (LIO_WLOCK&flags ? 0 : LOCK_NB) )
338 return log_msg( ERR_BADF, "could not get %s lock on '%s'", lck, name );
341 } else if ( -1 == fcntl( fd, F_GETFL ) ) /* check open */
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;
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;
363 for ( fd=0; hand[fd]; )
364 if ( sizeof(hand)/sizeof(hand[0]) == ++fd )
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 );
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] )
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;
383 int lio_close ( int *file, int flags )
385 int fd = 0xffff & *file;
386 int op = LIO_INOUT & *file;
387 int cl = LIO_INOUT & flags;
390 if ( 0 > *file ) { /* probably failed open */
395 if ( op & ~cl ) { /* remains partly open */
396 int how = (LIO_IN & cl) ? SHUT_RD : SHUT_WR;
397 if ( !(LIO_SOCK & fd) )
400 if ( ! shutdown( fd, how ) )
405 log_msg( LOG_SYSERR, "could not shutdown sock %d %d", fd, how );
406 return errno ? -errno : -1;
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;
415 if ( (int)(sizeof(hand)/sizeof(hand[0])) <= fd )
417 CloseHandle( hand[fd] );
424 int lio_size ( int file )
426 if ( !((LIO_IN|LIO_OUT) & file) )
427 return log_msg( ERR_BADF, "*file 0x%x not open for stat", file );
429 int fd = LIO_FD & file;
432 return fstat( fd, &s ) ? 0 : s.st_size;
434 return GetFileSize( hand[fd], 0 );
440 unsigned lio_time ( int file )
442 if ( !((LIO_IN|LIO_OUT) & file) )
443 return log_msg( ERR_BADF, "*file 0x%x not open for stat", file );
445 int fd = LIO_FD & file;
448 return fstat( fd, &s ) ? 0 : (unsigned)s.st_mtime;
450 FILETIME foo; /* time since 160101011200 UTC in hundred nanoseconds !!! */
452 if ( ! GetFileTime( hand[fd], 0, 0, &foo ) )
454 bar = ((ull)foo.dwHighDateTime<<32 | (ull)foo.dwLowDateTime) / ULL(10000000);
455 return (unsigned)(bar - ULL(11644473600));
461 int lio_read ( int *file, void *buf, unsigned count )
463 if ( !(LIO_IN & *file) )
464 return log_msg( ERR_BADF, "*file 0x%x not open for reading", *file );
466 int fd = LIO_FD & *file;
468 int got = read( fd, buf, count );
469 /* log_msg( LOG_ERROR, "read 0x%x got %d", *file, 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 */
477 lio_close( file, LIO_IN );
482 int ok = ReadFile( hand[fd], buf, count, &got, 0 );
484 : log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd );
490 int lio_write ( int *file, const void *buf, unsigned count )
492 if ( !(LIO_OUT & *file) )
493 return log_msg( ERR_BADF, "*file 0x%x not open for writing", *file );
495 int fd = LIO_FD & *file;
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 );
503 int ok = WriteFile( hand[fd], buf, count, &got, 0 );
505 : log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd );
512 int lio_seek ( int *file, int offset )
514 if ( !(LIO_INOUT & *file) )
515 return log_msg( ERR_BADF, "*file 0x%x not open", *file );
517 int fd = LIO_FD & *file;
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 );
523 return 0xffffffff /* "INVALID_SET_FILE_POINTER" */
524 == SetFilePointer( hand[fd], offset, 0, FILE_BEGIN )
531 int lio_pread ( int *file, void *buf, unsigned count, int offset )
533 if ( !(LIO_IN & *file) )
534 return log_msg( ERR_BADF, "*file 0x%x not open for reading", *file );
536 int fd = LIO_FD & *file;
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",
544 return 0xffffffff /* "INVALID_SET_FILE_POINTER" */
545 == SetFilePointer( hand[fd], offset, 0, FILE_BEGIN )
546 ? -1 : lio_read( file, buf, count );
552 int lio_pwrite ( int *file, const void *buf, unsigned count, int offset )
554 if ( !(LIO_OUT & *file) )
555 return log_msg( ERR_BADF, "*file 0x%x not open for writing", *file );
557 int fd = LIO_FD & *file;
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",
565 return 0xffffffff /* "INVALID_SET_FILE_POINTER" */
566 == SetFilePointer( hand[fd], offset, 0, FILE_BEGIN )
567 ? -1 : lio_write( file, buf, count );
573 int lio_trunc ( int *file, int offset )
575 int fd = LIO_FD & *file;
577 ftruncate( fd, offset );
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 );
589 int lio_mmap ( int *file, void **map, int length )
592 return log_msg( ERR_INVAL, "no map" );
593 if ( 0 >= length && 0 >= (length = lio_size( *file )) ) {
598 #if 0 /* solaris states it will round up itself, and linux does */
602 ps = sysconf(_SC_PAGESIZE); /* getpagesize(); */
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 )
615 return log_msg( ERR_NOMEM, "mmap failed on fd %08x", *file );
618 return log_msg( ERR_INVAL, "no *map" );
619 munmap( *map, length );
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
634 int lio_slurp ( char **buf, int sz, const char *name, int opt )
636 int file = lio_open( name, LIO_RD|LIO_SEEK|(opt ? LIO_TRY : 0) );
638 int ret = -ERR_NOMEM;
640 /* log_msg( LOG_IOERR, "open '%s' = %d", name, file ); */
643 size = lio_size( file );
647 LOG_OTO( done, ( ERR_INVAL, "file '%.30s' too big: %d > %d",
649 if ( ! p && !(p = mAlloc(size)) )
651 ret = lio_read( &file, p, size );
656 ret = log_msg( ERR_IO, "OOPS! got %d of %d bytes from '%.30s'",
662 lio_close( &file, LIO_INOUT );
667 int log_msg ( int code, const char *fmt, ... )
669 static const char toolong[] = ": message too long !!!\n";
674 ret = sMsg( MSG_VA|code, fmt, ap );
675 make log_msg unbuffered, use sMsg for buffered logging
677 if ( ! seterrlev( &err, &level, code ) && (LIO_OUT & lio_err) ) {
679 int len = vsnprintf( (char*)buf, sizeof(buf), fmt, ap );
680 if ( 0 < len && len < (int)sizeof(buf) ) {
682 lio_write( &lio_err, buf, len );
684 lio_write( &lio_err, fmt, strlen(fmt) );
685 lio_write( &lio_err, toolong, sizeof(toolong)-1 );
694 void log_str ( LogLevel level, int *rec, const char **desc )
697 int nmbrs = LSTRFIX(*rec);
699 char *base = (char*)rec;
701 if ( level > log_lev || ! desc )
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 */
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 );
713 sMsg( 2, "%3d.%2d %4.4s 0x%08x = %d\n",
714 occ, i, desc[i], *mbr, *mbr );
717 if ( ++occ >= LSTROCC(*rec) )
719 if ( ! occ ) { /* was the fixed part, setup for repeated */
720 nmbrs = LSTRREP(*rec);
727 void log_hex ( LogLevel level, const void *mem, int len )
729 const char *p = (const char *)mem;
733 if ( level > log_lev )
735 for ( ; i<len; i+=16, p+=16 ) {
739 sprintf( buf, "%08x ", i );
742 for ( ; j < left; j++ ) {
743 sprintf( buf+pos, "%02x", p[j] );
745 if ( 3 == j%4 ) { buf[pos++] = ' '; buf[pos++] = ' '; }
747 for ( ; j < 16; j++ ) {
748 buf[pos++] = ' '; buf[pos++] = ' ';
749 if ( 3 == j%4 ) { buf[pos++] = ' '; buf[pos++] = ' '; }
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] : '.';
756 sMsg( 2, "%.*s", pos, buf );
761 /* ************************************************************
764 int timeUpd ( Tm *tm )
773 tm->millis = tb.time*LLL(1000) + tb.millitm;
776 gettimeofday( &tv, 0 );
780 tm->millis = tv.tv_sec*LLL(1000) + tv.tv_usec/1000;
782 return (int)(tm->millis - o.millis);
786 static int timeLoc ( struct tm *t, Tm *tm )
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 );
796 localtime_r( &tt, t );
798 return (int)(tm->millis % 1000);
801 char *timeGtf ( char *buf, Tm *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 );
813 char *timeGtfm ( char *buf, Tm *tm )
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 );
825 void timeSleep ( Tm *tm )
831 ts.tv_sec = tm->millis / 1000;
832 ts.tv_nsec = 1000000 * (int)(tm->millis % 1000);
838 int ioStream ( Ios *s, int op )
845 s->pos = s->b.fill = s->b.done = 0;
847 s->file &= ~LIO_INOUT;
850 if ( !(LIO_IN & s->file) )
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 );
861 return LIO_BUFSIZ <= b->fill ? 0
862 : LIO_BUFSIZ/2 < b->fill ? LIO_BUFSIZ - b->fill
866 s->file &= ~LIO_INOUT;
872 int ioStdio ( Ios *s, int op )
879 if ( 0 < (ret = lio_open( s->name, s->file )) )
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 );
888 if ( !(LIO_IN & s->file) )
890 if ( 0 < (ret = ioStream( s, LIO_SPURGE ))
891 && 0 <= (ret = lio_read( &s->file, s->b.c + s->b.fill, ret ))
894 log_msg( LOG_ALL, "LIO_FILL: got %d now %d", ret, s->b.fill );
897 if ( !(LIO_OUT & s->file) )
899 b = /* s->alt ? s->alt : */ &s->b;
900 if ( b->fill <= b->done )
902 if ( 0 < (ret = lio_write( &s->file, b->c + b->done, b->fill - b->done )) )
904 if ( b->fill == b->done ) {
906 b->fill = b->done = 0;
907 } else if ( b->done && LIO_BUFSIZ/2 < b->fill )
908 ioStream( s, LIO_SPURGE );
911 return ioStream( s, op );
915 int sMsg ( int to, const char *fmt, ... )
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 ];
925 if ( LSES_FILE_MAX <= fd || ! o )
928 if ( seterrlev( &err, &level, code ) ) /* logging */
932 if ( LIO_BUFSIZ/2 < o->b.fill )
934 space = LIO_BUFSIZ - o->b.fill;
936 ret = vsnprintf( (char*)o->b.c + o->b.fill, space, fmt,
937 (MSG_VA & to) ? va_arg( ap, va_list ) : ap );
939 if ( ret < 0 || space < ret ) /* outta space */
945 /* logging afterburner */
946 if ( LOG_SYSERR == level || LOG_IOERR == level ) {
956 FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
957 0, GetLastError(), 0, buf, sizeof(buf)-1, 0 )
960 buf[ sizeof(buf)-1 ] = 0;
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 );
971 if ( o->b.fill < LIO_BUFSIZ )
972 o->b.c[ o->b.fill++ ] = '\n';
979 int sGets ( int fd, char **ptr, char delim )
982 Ios *s = ses->io[ LIO_FD & fd ];
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 );
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 );
990 if ( (s->b.fill < LIO_BUFSIZ || s->b.done) /* buffer avail */
991 && (LIO_IN & s->file) /* file open */
995 else if ( s->b.done >= s->b.fill )
997 f = (char*)s->b.c + s->b.fill;
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] )
1010 void cLog ( int level, const char *filename )
1012 if ( 0 > level ) /* no change */
1014 else if ( LOG_LEVELS > level ) /* by basic number */
1015 level <<= LOG_SHIFT;
1016 else if ( 'z' >= level ) { /* by ascii value */
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;
1023 if ( '0' <= level && level <= '9' ) {
1024 level = (level - '0')<<LOG_SHIFT;
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;
1037 log_lev = (LogLevel)level;
1039 /* TODO if ( filename )
1040 sOpen( cOpen(0), "2>"filename, 0, ioStdio ) */