/*
openisis - an open implementation of the CDS/ISIS database
Version 0.8.x (patchlevel see file Version)
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
see README for more information
EOH */
/*
$Id: lio.c,v 1.43 2003/06/11 14:53:08 kripke Exp $
I/O support for the openisis library.
*/
#include <stdlib.h>
#include <stdio.h> /* vsnprintf */
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <limits.h> /* PATH_MAX */
#include <sys/types.h>
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#define NONAMELESSUNION
# include <windows.h>
# include <winsock.h>
# define SHUT_RD SD_RECEIVE
# define SHUT_WR SD_SEND
# define SHUT_RDWR SD_BOTH
# define snprintf _snprintf
# define vsnprintf _vsnprintf
# include <sys/timeb.h>
#else /* have operating system */
# define __USE_UNIX98
/*
# define _POSIX_MAPPED_FILES
# define _POSIX_SYNCHRONIZED_IO
*/
# include <fcntl.h> /* or try unixio */
# ifndef O_SYNC
# if defined( __FreeBSD__ )
# define O_SYNC O_FSYNC
# else
# define O_SYNC 0
# endif
# endif
# include <sys/stat.h>
# include <sys/file.h> /* flock */
# include <sys/mman.h>
# include <sys/socket.h>
# include <unistd.h>
#endif /* WIN32 */
#include <sys/time.h> /* gettimeofday */
#include <time.h> /* localtime */
#include "luti.h" /* logging */
#include "lses.h"
#include "lstr.h" /* log_str */
#include "lio.h"
/* ************************************************************
private data
*/
static int init;
#ifdef WIN32
/* it's not thread-safe, anyway */
static HANDLE hand[128];
#endif
/* ************************************************************
private functions
*/
#ifndef WIN32
static int lerrno ( int errno_ )
{
switch ( errno_ ) {
case EINTR:
case EAGAIN:
return ERR_OK;
case EFAULT: /* structurally bad address */
case ENAMETOOLONG:
case ELOOP:
return ERR_FAULT;
case EBADF: /* file "does not exist" */
case ENOENT:
case ENOTDIR:
return ERR_BADF;
case EIO: /* file "is not accessible" */
case EEXIST:
case EISDIR:
case EACCES:
case ENXIO:
case ENODEV:
case EROFS:
case ETXTBSY:
case ENOSPC:
case EPIPE:
case ESPIPE:
return ERR_IO;
case ENOMEM:
case EMFILE:
case ENFILE:
return ERR_NOMEM;
case EBUSY:
return ERR_BUSY;
}
return ERR_INVAL;
} /* lerrno */
/**
check unixio errno.
if harmless, return != 0 (is ok).
else return 0, possibly closing the file
*/
int unixio_ok ( int *file, int io )
{
switch ( errno ) {
case EINTR:
case EAGAIN:
return !0;
case EFAULT:
case ESPIPE:
break;
default:
log_msg( LOG_IOERR, "closing file %x for %x on %d", *file, io, errno );
lio_close( file, io );
}
return 0;
} /* unixio_err */
#endif
/**
return wether printing should be skipped
*/
static int seterrlev ( int *err, int *level, int code )
{
*level = LOG_MASK & code;
*err = ERR_MASK & code;
if ( ! code )
return 0;
if ( ! *err )
switch ( *level ) {
case LOG_FATAL: *err = ERR_IDIOT; break;
case LOG_ERROR: *err = ERR_INVAL; break;
case LOG_IOERR:
#ifndef WIN32
if ( EAGAIN == errno || EINTR == errno ) /* no error */
return !0;
case LOG_SYSERR:
*err = lerrno( errno );
#else
case LOG_SYSERR:
*err = ERR_INVAL;
#endif
break;
default:
*err = 0;
}
else if ( ! *level ) {
if ( ERR_TRASH <= *err )
*level = LOG_FATAL;
else if ( ERR_IO <= *err )
*level = LOG_SYSERR;
else if ( ERR_FAULT <= *err )
*level = LOG_ERROR;
else
*level = LOG_VERBOSE;
}
return *level > (int)log_lev;
} /* seterrlev */
/* ************************************************************
package data
*/
CLockFunc *lio_lock;
LogLevel log_lev = LOG_ERROR;
int log_flush = 1;
#ifndef WIN32
# define LIO_STATICFD( nm, flg ) int nm = flg
#else
# define LIO_STATICFD( nm, flg ) int nm
#endif
LIO_STATICFD( lio_in , LIO_IN | 0 );
LIO_STATICFD( lio_out, LIO_OUT | 1 );
LIO_STATICFD( lio_err, LIO_OUT | 2 );
/* ************************************************************
package functions
*/
static void lio_init ()
{
#ifndef WIN32
/* static */
#else
lio_in = lio_open( "CONIN$", LIO_RD );
lio_out = lio_open( "CONOUT$", LIO_WR );
lio_err = lio_open( "oisiserr.txt", LIO_WR|LIO_CREAT );
#endif
} /* lio_init */
/*
has "official" name, since visible to linker
use ld -init openIsisInit for shared object
*/
void openIsisInit ()
{
static const char inimsg[] = "openIsisInit\n";
const char *ll = getenv("OPENISIS_LOGLEVEL");
if ( init )
return;
init = 42;
lio_init();
if ( ll ) {
cLog( ll[0], 0 );
#ifndef NDEBUG
if ( LOG_DEBUG <= log_lev )
lio_write( &lio_err, inimsg, sizeof(inimsg)-1 );
#endif
}
if ( getenv("OPENISIS_LOGBUFFERED") )
log_flush = 0;
lses_init();
} /* openIsisInit */
/*
use ld -fini openIsisFini for shared object
*/
void openIsisFini ()
{
static const char finimsg[] = "openIsisFini\n";
lses_fini();
#ifndef NDEBUG
if ( LOG_DEBUG <= log_lev )
lio_write( &lio_err, finimsg, sizeof(finimsg)-1 );
#endif
} /* openIsisFini */
void linit () /* called by cOpen() */
{
if ( init ) /* we're an .so -- implicitly initialized */
return;
/* we're statically linked */
openIsisInit();
atexit( openIsisFini );
}
int lio_open ( const char *name, int flags )
{
int fd = -1;
int faillvl = (LIO_TRY == ((LIO_TRY|LIO_WR)&flags))
? (LOG_VERBOSE|ERR_BADF) : LOG_INFO|ERR_BADF /* LOG_IOERR too annoying ... */;
if ( '&' == name[0] ) {
int i = 1;
fd = 0;
while ( '0' <= name[i] && name[i] <= '9' )
fd = 10*fd + name[i++] - '0';
}
#ifndef WIN32
if ( 0 > fd ) {
int f = !(LIO_WR&flags) ? O_RDONLY
: ((LIO_RD&flags) ? O_RDWR : O_WRONLY);
if ( LIO_CREAT & flags && LIO_WR & flags ) f |= O_CREAT;
if ( LIO_NBLK & flags ) f |= O_NONBLOCK;
if ( LIO_SYNC & flags ) f |= O_SYNC;
if ( LIO_TRUNC & flags ) f |= O_TRUNC;
if ( ! (LIO_SEEK & flags) ) f |= O_APPEND;
fd = open( name, f, 00664 ); /* let umask finetune */
if ( LIO_FD < fd ) {
log_msg( LOG_ERROR, "got big fd %d", fd );
fd = -1;
}
if ( 0 > fd )
return log_msg( faillvl, "could not open '%s' %x", name, flags );
if ( LIO_FLOCK & flags ) {
const char *lck = LIO_WR&flags ? "exclusive" : "shared";
/*
we want an advisory lock, so that tail -f can read changes.
SYSV/POSIX fcntl might be mandatory, depending on files mode bits
(and filesystem). Therefore prefer BSD style flock, if available.
under linux, flock is never mandatory, while lockf/fcntl may be.
/usr/src/linux/Documentation/mandatory.txt
moreover fcntl attempts to lock over NFS, which is a very feeble idea.
*/
#ifndef LOCK_SH
/*
on solaris, LOCK_SH is declared only with the /usr/ucb/cc includes.
their flock on fcntl emulation "must not be used in MT environments".
*/
struct flock fl;
memset( &fl, 0, sizeof(fl) );
fl.l_type = LIO_WR&flags ? F_WRLCK : F_RDLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 1; /* 0 locks the whole file -- could be mandatory :( */
#endif
log_msg( LOG_VERBOSE, "attempting %s lock on '%s'", lck, name );
if (
#ifndef LOCK_SH
/* hmm .. at least this is "POSIX" */
fcntl( fd, LIO_WLOCK&flags ? F_SETLKW : F_SETLK, &fl )
#else
flock( fd,
(LIO_WR&flags ? LOCK_EX : LOCK_SH) | (LIO_WLOCK&flags ? 0 : LOCK_NB) )
#endif
) {
close( fd );
return log_msg( ERR_BADF, "could not get %s lock on '%s'", lck, name );
}
}
} else if ( -1 == fcntl( fd, F_GETFL ) ) /* check open */
return -ERR_BADF;
#else
if ( 0 > fd ) {
HANDLE h;
int acc = 0;
int shr = FILE_SHARE_READ | (LIO_FLOCK&flags ? 0 : FILE_SHARE_WRITE);
int cre = (LIO_CREAT & flags && LIO_WR & flags)
? (LIO_TRUNC & flags) ? CREATE_ALWAYS : OPEN_ALWAYS
: (LIO_TRUNC & flags) ? TRUNCATE_EXISTING : OPEN_EXISTING;
int f = (LIO_WR&flags) ? FILE_ATTRIBUTE_ARCHIVE : FILE_ATTRIBUTE_NORMAL;
if ( LIO_RD & flags ) acc |= GENERIC_READ; /* FILE_READ_DATA; */
if ( LIO_WR & flags ) acc |= GENERIC_WRITE;
/* some routines like GetFileSize are "documented" to require
GENERIC_READ/GENERIC_WRITE, so it may not be sufficient to use:
(LIO_SEEK & flags) ? FILE_WRITE_DATA : FILE_APPEND_DATA */;
/* if ( LIO_NBLK & flags ) f |= FILE_FLAG_OVERLAPPED; */
if ( LIO_SYNC & flags ) f |= FILE_FLAG_WRITE_THROUGH;
f |= (LIO_SEEK & flags) ? FILE_FLAG_RANDOM_ACCESS
: FILE_FLAG_SEQUENTIAL_SCAN;
for ( fd=0; hand[fd]; )
if ( sizeof(hand)/sizeof(hand[0]) == ++fd )
return -EMFILE;
h = CreateFile( name, acc, shr, 0, cre, f, 0 );
if ( INVALID_HANDLE_VALUE == h )
return log_msg( faillvl, "could not open '%s' %x", name, flags );
hand[fd] = h;
if ( (LIO_WR & flags) && ! (LIO_SEEK & flags) )
SetFilePointer( hand[fd], 0, 0, FILE_END );
} else if ( (int)(sizeof(hand)/sizeof(hand[0])) <= fd || ! hand[fd] )
return -ERR_BADF;
#endif
fd |= flags & LIO_WANT;
/* add status flags */
if ( LIO_RD & flags ) fd |= LIO_IN;
if ( LIO_WR & flags ) fd |= LIO_OUT;
return fd;
} /* lio_open */
int lio_close ( int *file, int flags )
{
int fd = 0xffff & *file;
int op = LIO_INOUT & *file;
int cl = LIO_INOUT & flags;
if ( ! op || ! cl )
return 0;
if ( 0 > *file ) { /* probably failed open */
*file = 0;
return 0;
}
*file &= ~cl;
if ( op & ~cl ) { /* remains partly open */
int how = (LIO_IN & cl) ? SHUT_RD : SHUT_WR;
if ( !(LIO_SOCK & fd) )
return 0;
#ifndef WIN32
if ( ! shutdown( fd, how ) )
return 0;
#else
/* TODO */
#endif
log_msg( LOG_SYSERR, "could not shutdown sock %d %d", fd, how );
return errno ? -errno : -1;
}
#ifndef WIN32
for ( fsync( fd ); close( fd ); errno = 0 )
if ( EINTR != errno ) {
log_msg( LOG_SYSERR, "could not close file %d", fd );
return errno ? -errno : -1;
}
#else
if ( (int)(sizeof(hand)/sizeof(hand[0])) <= fd )
return -ERR_BADF;
CloseHandle( hand[fd] );
hand[fd] = 0;
#endif
return 0;
} /* lio_close */
int lio_size ( int file )
{
if ( !((LIO_IN|LIO_OUT) & file) )
return log_msg( ERR_BADF, "*file 0x%x not open for stat", file );
{
int fd = LIO_FD & file;
#ifndef WIN32
struct stat s;
return fstat( fd, &s ) ? 0 : s.st_size;
#else
return GetFileSize( hand[fd], 0 );
#endif
}
} /* lio_size */
unsigned lio_time ( int file )
{
if ( !((LIO_IN|LIO_OUT) & file) )
return log_msg( ERR_BADF, "*file 0x%x not open for stat", file );
{
int fd = LIO_FD & file;
#ifndef WIN32
struct stat s;
return fstat( fd, &s ) ? 0 : (unsigned)s.st_mtime;
#else
FILETIME foo; /* time since 160101011200 UTC in hundred nanoseconds !!! */
ull bar;
if ( ! GetFileTime( hand[fd], 0, 0, &foo ) )
return 0;
bar = ((ull)foo.dwHighDateTime<<32 | (ull)foo.dwLowDateTime) / ULL(10000000);
return (unsigned)(bar - ULL(11644473600));
#endif
}
} /* lio_size */
int lio_read ( int *file, void *buf, unsigned count )
{
if ( !(LIO_IN & *file) )
return log_msg( ERR_BADF, "*file 0x%x not open for reading", *file );
{
int fd = LIO_FD & *file;
#ifndef WIN32
int got = read( fd, buf, count );
/* log_msg( LOG_ERROR, "read 0x%x got %d", *file, got ); */
if ( got )
return 0 < got ? got
: unixio_ok( file, LIO_IN ) ? 0
: log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd );
else if ( LIO_SEEK & *file ) /* special EOF treatment */
return 0;
else {
lio_close( file, LIO_IN );
return -ERR_EOF;
}
#else
DWORD got = 0;
int ok = ReadFile( hand[fd], buf, count, &got, 0 );
return ok ? got
: log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd );
#endif
}
} /* lio_read */
int lio_write ( int *file, const void *buf, unsigned count )
{
if ( !(LIO_OUT & *file) )
return log_msg( ERR_BADF, "*file 0x%x not open for writing", *file );
{
int fd = LIO_FD & *file;
#ifndef WIN32
int got = write( fd, buf, count );
return 0 <= got ? got
: unixio_ok( file, LIO_OUT ) ? 0
: log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd );
#else
DWORD got = 0;
int ok = WriteFile( hand[fd], buf, count, &got, 0 );
return ok ? got
: log_msg( LOG_IOERR, "could not read %d bytes from %d", count, fd );
#endif
}
} /* lio_write */
int lio_seek ( int *file, int offset )
{
if ( !(LIO_INOUT & *file) )
return log_msg( ERR_BADF, "*file 0x%x not open", *file );
{
int fd = LIO_FD & *file;
#ifndef WIN32
int got = (int)lseek( fd, offset, SEEK_SET );
return offset == got ? 0
: log_msg( LOG_IOERR, "could not seek to %d", offset );
#else
return 0xffffffff /* "INVALID_SET_FILE_POINTER" */
== SetFilePointer( hand[fd], offset, 0, FILE_BEGIN )
? -1 : 0;
#endif
}
} /* lio_seek */
int lio_pread ( int *file, void *buf, unsigned count, int offset )
{
if ( !(LIO_IN & *file) )
return log_msg( ERR_BADF, "*file 0x%x not open for reading", *file );
{
int fd = LIO_FD & *file;
#ifndef WIN32
int got = pread( fd, buf, count, offset );
return 0 <= got ? got
: unixio_ok( file, LIO_IN ) ? 0
: log_msg( LOG_IOERR, "could not read %d bytes from %d at %d",
count, fd, offset );
#else
return 0xffffffff /* "INVALID_SET_FILE_POINTER" */
== SetFilePointer( hand[fd], offset, 0, FILE_BEGIN )
? -1 : lio_read( file, buf, count );
#endif
}
} /* lio_pread */
int lio_pwrite ( int *file, const void *buf, unsigned count, int offset )
{
if ( !(LIO_OUT & *file) )
return log_msg( ERR_BADF, "*file 0x%x not open for writing", *file );
{
int fd = LIO_FD & *file;
#ifndef WIN32
int got = pwrite( fd, buf, count, offset );
return 0 <= got ? got
: unixio_ok( file, LIO_OUT ) ? 0
: log_msg( LOG_IOERR, "could not read %d bytes from %d at %d",
count, fd, offset );
#else
return 0xffffffff /* "INVALID_SET_FILE_POINTER" */
== SetFilePointer( hand[fd], offset, 0, FILE_BEGIN )
? -1 : lio_write( file, buf, count );
#endif
}
} /* lio_pwrite */
int lio_trunc ( int *file, int offset )
{
int fd = LIO_FD & *file;
#ifndef WIN32
ftruncate( fd, offset );
#else
/* SetFileValidData( hand[fd], offset ); XP only */
return 0xffffffff /* "INVALID_SET_FILE_POINTER" */
== SetFilePointer( hand[fd], offset, 0, FILE_BEGIN )
? -1 : SetEndOfFile( hand[fd] )
? 0 : log_msg( LOG_IOERR, "could not truncate %d to %d", fd, offset );
#endif
return 0;
} /* lio_trunc */
int lio_mmap ( int *file, void **map, int length )
{
if ( ! map )
return log_msg( ERR_INVAL, "no map" );
if ( 0 >= length && 0 >= (length = lio_size( *file )) ) {
*map = 0;
return length;
}
#ifndef WIN32
#if 0 /* solaris states it will round up itself, and linux does */
{
static int ps = 0;
if ( !ps )
ps = sysconf(_SC_PAGESIZE); /* getpagesize(); */
}
#endif
if ( file ) {
if ( *map )
return msync( *map, length, (LIO_SYNC & *file)
? (MS_SYNC|MS_INVALIDATE) : MS_ASYNC )
? log_msg( LOG_IOERR, "msync" ) : 0;
*map = mmap( 0, length, PROT_READ | ((LIO_OUT&*file) ? PROT_WRITE : 0),
MAP_SHARED, LIO_FD&*file, 0 );
if ( MAP_FAILED != *map )
return length;
*map = 0;
return log_msg( ERR_NOMEM, "mmap failed on fd %08x", *file );
}
if ( ! *map )
return log_msg( ERR_INVAL, "no *map" );
munmap( *map, length );
*map = 0;
return 0;
#else
/* awfully complicated here
-- need CreateFileMapping, MapViewOfFile
stupid piece of shrott
true mapping supported in NT family only, 9x copies to swap
*/
*map = 0;
return 0;
#endif
} /* lio_mmap */
int lio_slurp ( char **buf, int sz, const char *name, int opt )
{
int file = lio_open( name, LIO_RD|LIO_SEEK|(opt ? LIO_TRY : 0) );
int size;
int ret = -ERR_NOMEM;
char *p = *buf;
/* log_msg( LOG_IOERR, "open '%s' = %d", name, file ); */
if ( 0 > file )
return file;
size = lio_size( file );
if ( 0 >= size )
return size;
if ( size > sz )
LOG_OTO( done, ( ERR_INVAL, "file '%.30s' too big: %d > %d",
name, size, sz ) );
if ( ! p && !(p = mAlloc(size)) )
goto done;
ret = lio_read( &file, p, size );
if ( size == ret )
*buf = p;
else {
if ( ret >= 0 )
ret = log_msg( ERR_IO, "OOPS! got %d of %d bytes from '%.30s'",
ret, size, name );
if ( ! *buf )
free( p );
}
done:
lio_close( &file, LIO_INOUT );
return ret;
} /* lio_slurp */
int log_msg ( int code, const char *fmt, ... )
{
static const char toolong[] = ": message too long !!!\n";
int err, level;
va_list ap;
va_start( ap, fmt );
#if 0
ret = sMsg( MSG_VA|code, fmt, ap );
make log_msg unbuffered, use sMsg for buffered logging
#else
if ( ! seterrlev( &err, &level, code ) && (LIO_OUT & lio_err) ) {
char buf[4096];
int len = vsnprintf( (char*)buf, sizeof(buf), fmt, ap );
if ( 0 < len && len < (int)sizeof(buf) ) {
buf[len++] = '\n';
lio_write( &lio_err, buf, len );
} else {
lio_write( &lio_err, fmt, strlen(fmt) );
lio_write( &lio_err, toolong, sizeof(toolong)-1 );
}
}
#endif
va_end( ap );
return -err;
} /* log_msg */
void log_str ( LogLevel level, int *rec, const char **desc )
{
int occ = -1;
int nmbrs = LSTRFIX(*rec);
int *mbr = rec+1;
char *base = (char*)rec;
if ( level > log_lev || ! desc )
return;
sMsg( 2, "record %.20s\n", *desc++ );
/* dump the fixed part (occ==-1) and each occurrence of repeated part. */
for ( ;/* occ < LSTROCC(*dst) */; ) { /* dump one part */
int i;
for ( i=0; i<nmbrs; i++, mbr++ ) { /* dump one mbr */
if ( '\'' == *desc[i] )
sMsg( 2, "%3d.%2d %4.4s %.67s<\n",
occ, i, desc[i], base+*mbr );
else
sMsg( 2, "%3d.%2d %4.4s 0x%08x = %d\n",
occ, i, desc[i], *mbr, *mbr );
} /* for mbrs */
if ( ++occ >= LSTROCC(*rec) )
break;
if ( ! occ ) { /* was the fixed part, setup for repeated */
nmbrs = LSTRREP(*rec);
desc += i;
}
}
} /* log_str */
void log_hex ( LogLevel level, const void *mem, int len )
{
const char *p = (const char *)mem;
char buf[82];
int i = 0;
if ( level > log_lev )
return;
for ( ; i<len; i+=16, p+=16 ) {
int j = 0;
int left = len -i;
int pos = 10;
sprintf( buf, "%08x ", i );
if ( left > 16 )
left = 16;
for ( ; j < left; j++ ) {
sprintf( buf+pos, "%02x", p[j] );
pos += 2;
if ( 3 == j%4 ) { buf[pos++] = ' '; buf[pos++] = ' '; }
}
for ( ; j < 16; j++ ) {
buf[pos++] = ' '; buf[pos++] = ' ';
if ( 3 == j%4 ) { buf[pos++] = ' '; buf[pos++] = ' '; }
}
/* got 50 = 10 + 16*2 + 4*2 */
for ( j=0; j < left; j++ ) /* add up to 16 */
buf[pos++] = (0x60 & p[j]) ? p[j] : '.';
buf[pos++] = '\n';
buf[pos] = 0;
sMsg( 2, "%.*s", pos, buf );
}
} /* log_hex */
/* ************************************************************
public functions
*/
int timeUpd ( Tm *tm )
{
Tm o;
#ifdef WIN32
struct _timeb tb;
_ftime( &tb );
if ( !tm )
return tb.time;
o = *tm;
tm->millis = tb.time*LLL(1000) + tb.millitm;
#else
struct timeval tv;
gettimeofday( &tv, 0 );
if ( !tm )
return tv.tv_sec;
o = *tm;
tm->millis = tv.tv_sec*LLL(1000) + tv.tv_usec/1000;
#endif
return (int)(tm->millis - o.millis);
} /* timeUpd */
static int timeLoc ( struct tm *t, Tm *tm )
{
Tm x;
time_t tt;
if ( !tm || !tm->millis )
timeUpd( tm ? tm : (tm = &x) );
tt = (time_t)(tm->millis / 1000);
#ifdef WIN32 /* did I mention it's not threadsafe ? */
*t = *localtime( &tt );
#else
localtime_r( &tt, t );
#endif
return (int)(tm->millis % 1000);
} /* timeLoc */
char *timeGtf ( char *buf, Tm *tm )
{
struct tm t;
timeLoc( &t, tm );
snprintf( buf, 15, "%04u%02u%02u%02u%02u%02u",
1900+t.tm_year, 1+t.tm_mon, t.tm_mday,
t.tm_hour, t.tm_min, t.tm_sec );
buf[14] = 0;
return buf;
} /* timeGtf */
char *timeGtfm ( char *buf, Tm *tm )
{
struct tm t;
int millis = timeLoc( &t, tm );
snprintf( buf, 19, "%04u%02u%02u%02u%02u%02u%03u",
1900+t.tm_year, 1+t.tm_mon, t.tm_mday,
t.tm_hour, t.tm_min, t.tm_sec, millis );
buf[18] = 0;
return buf;
} /* timeGtfm */
void timeSleep ( Tm *tm )
{
#ifdef WIN32
Sleep( tm->millis );
#else
struct timespec ts;
ts.tv_sec = tm->millis / 1000;
ts.tv_nsec = 1000000 * (int)(tm->millis % 1000);
nanosleep( &ts, 0 );
#endif
} /* timeSleep */
int ioStream ( Ios *s, int op )
{
Buf *b;
switch ( op ) {
case LIO_SSIZE:
return sizeof(Ios);
case LIO_SOPEN:
s->pos = s->b.fill = s->b.done = 0;
case LIO_SCLOSE:
s->file &= ~LIO_INOUT;
return 0;
case LIO_SPURGE:
if ( !(LIO_IN & s->file) )
return -ERR_BADF;
/* try to get 4k chunk */
b = /* s->alt ? s->alt : */ &s->b;
if ( LIO_BUFSIZ/2 < b->fill && b->done ) { /* move contents downwards */
if ( b->done < b->fill )
memmove( b->c, b->c + b->done, b->fill - b->done );
s->pos += b->done;
b->fill -= b->done;
b->done = 0;
}
return LIO_BUFSIZ <= b->fill ? 0
: LIO_BUFSIZ/2 < b->fill ? LIO_BUFSIZ - b->fill
: LIO_BUFSIZ/2;
case LIO_SFILL:
case LIO_SFLUSH:
s->file &= ~LIO_INOUT;
}
return -ERR_EOF;
} /* ioStream */
int ioStdio ( Ios *s, int op )
{
Buf *b;
int ret;
switch ( op ) {
case LIO_SOPEN:
ioStream( s, op );
if ( 0 < (ret = lio_open( s->name, s->file )) )
s->file = ret;
return ret;
case LIO_SCLOSE:
if ( LIO_OUT & s->file )
s->func( s, LIO_SFLUSH );
/* don't close borrowed fd */
return '&' == *s->name ? 0 : lio_close( &s->file, LIO_INOUT );
case LIO_SFILL:
if ( !(LIO_IN & s->file) )
return -ERR_BADF;
if ( 0 < (ret = ioStream( s, LIO_SPURGE ))
&& 0 <= (ret = lio_read( &s->file, s->b.c + s->b.fill, ret ))
)
s->b.fill += ret;
log_msg( LOG_ALL, "LIO_FILL: got %d now %d", ret, s->b.fill );
return ret;
case LIO_SFLUSH:
if ( !(LIO_OUT & s->file) )
return -ERR_BADF;
b = /* s->alt ? s->alt : */ &s->b;
if ( b->fill <= b->done )
return 0;
if ( 0 < (ret = lio_write( &s->file, b->c + b->done, b->fill - b->done )) )
b->done += ret;
if ( b->fill == b->done ) {
s->pos += b->done;
b->fill = b->done = 0;
} else if ( b->done && LIO_BUFSIZ/2 < b->fill )
ioStream( s, LIO_SPURGE );
return ret;
}
return ioStream( s, op );
} /* ioStdio */
int sMsg ( int to, const char *fmt, ... )
{
SESDECL
int code = (LOG_MASK|ERR_MASK) & to, err, level;
int fd = LIO_FD & to; /* stream id uses same mask as system fd */
Ios *o = ses->io[ fd ? fd : code ? 2 : 1 ];
va_list ap;
int space;
int ret;
if ( LSES_FILE_MAX <= fd || ! o )
return -1;
if ( seterrlev( &err, &level, code ) ) /* logging */
return -err;
/* core fprintf */
if ( LIO_BUFSIZ/2 < o->b.fill )
LIO_FLUSH( o );
space = LIO_BUFSIZ - o->b.fill;
va_start( ap, fmt );
ret = vsnprintf( (char*)o->b.c + o->b.fill, space, fmt,
(MSG_VA & to) ? va_arg( ap, va_list ) : ap );
va_end( ap );
if ( ret < 0 || space < ret ) /* outta space */
ret = space;
o->b.fill += ret;
if ( ! code )
return ret;
/* logging afterburner */
if ( LOG_SYSERR == level || LOG_IOERR == level ) {
int len;
char *syserr =
#ifndef WIN32
strerror(errno);
#else
0;
char buf[256] = "";
if (
FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
0, GetLastError(), 0, buf, sizeof(buf)-1, 0 )
)
syserr = buf;
buf[ sizeof(buf)-1 ] = 0;
#endif
if ( syserr ) {
len = strlen( syserr );
if ( o->b.fill+4+len < LIO_BUFSIZ ) {
memcpy( o->b.c + o->b.fill, "\n\t: ", 4 );
memcpy( o->b.c + o->b.fill+4, syserr, len );
o->b.fill += 4+len;
}
}
}
if ( o->b.fill < LIO_BUFSIZ )
o->b.c[ o->b.fill++ ] = '\n';
if ( log_flush )
LIO_FLUSH( o );
return -err;
} /* sMsg */
int sGets ( int fd, char **ptr, char delim )
{
SESDECL
Ios *s = ses->io[ LIO_FD & fd ];
if ( s->b.fill > s->b.done || 0 < LIO_FILL( s ) ) for (;;) {
char *f = memchr( s->b.c + s->b.done, delim, s->b.fill - s->b.done );
int len;
log_msg( LOG_ALL, "sGets: done %d fill %d", s->b.done, s->b.fill );
f = memchr( s->b.c + s->b.done, delim, s->b.fill - s->b.done );
if ( ! f ) {
if ( (s->b.fill < LIO_BUFSIZ || s->b.done) /* buffer avail */
&& (LIO_IN & s->file) /* file open */
&& 0 < LIO_FILL( s )
)
continue;
else if ( s->b.done >= s->b.fill )
break;
f = (char*)s->b.c + s->b.fill;
}
len = f - (*ptr = (char*)s->b.c + s->b.done);
s->b.done += len + 1;
if ( len && '\n' == delim && '\r' == (*ptr)[len-1] )
len--;
return len;
}
*ptr = 0;
return -1;
} /* sGets */
void cLog ( int level, const char *filename )
{
if ( 0 > level ) /* no change */
;
else if ( LOG_LEVELS > level ) /* by basic number */
level <<= LOG_SHIFT;
else if ( 'z' >= level ) { /* by ascii value */
switch ( level ) {
case '-': level = LOG_NOCHANGE; break;
case 'o': level = LOG_OFF; break;
case 'f': level = LOG_FATAL; break;
case 's': level = LOG_IOERR /*LOG_SYSERR*/; break;
default:
if ( '0' <= level && level <= '9' ) {
level = (level - '0')<<LOG_SHIFT;
break;
}
case 'e': level = LOG_ERROR; break;
case 'w': level = LOG_WARN; break;
case 'i': level = LOG_INFO; break;
case 'v': level = LOG_VERBOSE; break;
case 'd': level = LOG_DEBUG; break;
case 't': level = LOG_TRACE; break;
case 'a': level = LOG_ALL; break;
}
}
if ( level >= 0 )
log_lev = (LogLevel)level;
(void)filename;
/* TODO if ( filename )
sOpen( cOpen(0), "2>"filename, 0, ioStdio ) */
} /* cLog */