view src/util.c @ 259:b660fadc1814 noffle

[svn] * src/server.c,src/util.c,src/util.h: Recognise GMT/UTC on NNTP NEWGROUPS. Do small reorg of some of the timezone sensitive code, and introduce use of timegm(). An implementation is provided for systems without timegm().
author bears
date Wed, 26 Jun 2002 14:30:26 +0100
parents 4e69e9b722ae
children 3477050e8d10
line wrap: on
line source

/*
  util.c

  $Id: util.c 391 2002-06-26 13:30:26Z bears $
*/

#if HAVE_CONFIG_H
#include <config.h>
#endif

#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif

#include <errno.h>
#include <ctype.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include "configfile.h"
#include "log.h"
#include "portable.h"
#include "util.h"
#include "wildmat.h"

#if defined(UTIL_TEST)
#define	Log_err	printf
#endif

static const char *
nextWhiteSpace( const char *p )
{
    while ( *p && ! isspace( *p ) )
        ++p;
    return p;
}

static const char *
nextNonWhiteSpace( const char *p )
{
    while ( *p && isspace( *p ) )
        ++p;
    return p;
}

const char *
Utl_restOfLn( const char *line, unsigned int token )
{
    unsigned int i;
    const char *p;

    p = line;
    for ( i = 0; i < token; ++i )
    {
        p = nextNonWhiteSpace( p );
        p = nextWhiteSpace( p );
    }
    p = nextNonWhiteSpace( p );
    return p;
}

const char *
Utl_getLn( Str result, const char *pos )
{
    int len = 0;
    const char *p = pos;

    if ( ! p )
        return NULL;
    while ( *p != '\n' )
    {
        if ( *p == '\0' )
        {
            if ( len > 0 )
                Log_err( "Line not terminated by newline: '%s'", pos );
            return NULL;
        }
        *(result++) = *(p++);
        ++len;
        if ( len >= MAXCHAR - 1 )
        {
            *result = '\0';
            Log_err( "Utl_getLn: line too long: %s", result );
            return ++p;
        }
    }
    *result = '\0';
    return ++p;

}

const char *
Utl_ungetLn( const char *str, const char *p )
{
    if ( str == p )
        return FALSE;
    --p;
    if ( *p != '\n' )
    {
        Log_err( "Utl_ungetLn: not at beginning of line" );
        return NULL;
    }
    --p;
    while ( TRUE )
    {
        if ( p == str )
            return p;
        if ( *p == '\n' )
            return p + 1;
        --p;
    }
}

const char *
Utl_getHeaderLn( Str result, const char *p )
{
    const char * res = Utl_getLn( result, p );
    Bool not_too_long_header = TRUE;

    /* Look for followon line if this isn't a blank line. */
    if ( res != NULL && result[ 0 ] != '\0' && ! isspace( result[ 0 ] ) )
	for(;;)
	{
	    Str nextLine;
	    const char *here;

	    here = res;
	    nextLine[ 0 ] = '\0';
	    res = Utl_getLn( nextLine, res );
	    if ( res == NULL || nextLine[ 0 ] == '\0'
		 || ! isspace( nextLine[ 0 ] ) )
	    {
		res = here;
		break;
	    }
	    else
	    {
                if ( not_too_long_header &&
                     ( MAXCHAR > ( strlen( result ) + strlen( nextLine ) + 1 ) ) )
                {
                    Utl_catStr( result, "\n"   );
		    Utl_catStr( result, nextLine );
                }
                else
                {
                    Log_err( "Utl_getHeaderLn: skipped continued header: %s", nextLine );
                    not_too_long_header = FALSE;
                    /* Now let poor little noffle skip the header continuations. */
                    /* We really need to up the size limit of headers much */
                    /* higher than MAXCHAR = 2048. mliss */
                }
	    }
	}

    return res;
}

void
Utl_toLower( Str line )
{
    char *p;

    p = line;
    while ( *p )
    {
        *p = tolower( *p );
        ++p;
    }
}

char *
Utl_stripWhiteSpace( char *line )
{
    char *p;

    while ( isspace( *line ) )
        ++line;
    p = line + strlen( line ) - 1;
    while ( p >= line && isspace( *p ) )
    {
        *p = '\0';
        --p;
    }
    return line;
}

void
Utl_stripComment( char *line )
{
    for ( ; *line != '\0'; line++ )
	if ( *line =='#' )
	{
	    *line = '\0';
	    break;
	}
}

void
Utl_cpyStr( Str dst, const char *src )
{
    dst[ 0 ] = '\0';
    strncat( dst, src, MAXCHAR );
}

void
Utl_cpyStrN( Str dst, const char *src, int n )
{
    if ( n > MAXCHAR )
    	n = MAXCHAR;
    dst[ 0 ] = '\0';
    strncat( dst, src, (size_t)n );
}

void
Utl_catStr( Str dst, const char *src )
{
    strncat( dst, src, MAXCHAR - strlen( dst ) );
}

void
Utl_catStrN( Str dst, const char *src, int n )
{
    size_t un;

    ASSERT( n >= 0 );
    un = (size_t)n;
    if ( un > MAXCHAR - strlen( dst ) )
    	 un = MAXCHAR - strlen( dst );
    strncat( dst, src, un );
}

void
Utl_stamp( Str file )
{
    FILE *f;
    time_t t;
    Str tmpfname;

    snprintf( tmpfname, MAXCHAR, "%s/.#%d.stamp.update",
              Cfg_spoolDir(), (int) getpid() );
    time( &t );
    if ( ! ( f = fopen( tmpfname, "w" ) ) )
    {
        Log_err( "Could not open %s for writing (%s)",
                 tmpfname, strerror( errno ) );
        return;
    }
    fprintf( f, "%lu\n", t );
    if ( fclose( f ) != 0 )
    {
         Log_err( "Error stamping into file %s: %s",
		 tmpfname, strerror( errno ) );

    }
    else
    {
        if ( rename( tmpfname, file ) < 0 )
            Log_err( "Rename of stamp file %s to %s failed: %s",
		     tmpfname, file, strerror( errno ) );
    }
}

Bool
Utl_getStamp( time_t *result, Str file )
{
    FILE *f;

    if ( ! ( f = fopen( file, "r" ) ) )
        return FALSE;
    if ( fscanf( f, "%lu", result ) != 1 )
    {
        Log_err( "File %s corrupted", file );
        fclose( f );
        return FALSE;
    }
    fclose( f );
    return TRUE;
}

static const char *DOTW[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
			      "Sat", NULL };
static const char *MON[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
			     "Aug", "Sep", "Oct", "Nov", "Dec", NULL };

/*
 * Calculate the difference between local time and GMT at GMT time t.
 *
 * This is INN's 'always-works' method. It assumes the time differences
 * is < 24hrs. Sounds reasonable to me. It also assumes it can ignore seconds.
 * Returns localtime - GMT seconds. It will also trash the localtime/
 * gmtime/etc. static buffer.
 */
static int
localTimeDiff( time_t t )
{
    time_t now;
    struct tm local, gmt, *tm;
    static time_t nextCalc = 0;
    static int res = 0;

    now = time( NULL );
    if ( now < nextCalc )
	return res;
    
    tm = localtime( &t );
    if ( tm == NULL )
	return 0;
    local = *tm;
    tm = gmtime( &t );
    if ( tm == NULL )
	return 0;
    gmt = *tm;

    res = local.tm_yday - gmt.tm_yday;
    if ( res < -1 )
	res = -1;		/* Year rollover? */
    else if ( res > 1 )
	res = 1;

    res *= 24;
    res += local.tm_hour - gmt.tm_hour;
    res *= 60;
    res += local.tm_min - gmt.tm_min;
    res *= 60;

    /* Need to recalc at start of next hour */
    nextCalc = now + ( 60 - local.tm_sec ) + 60 * ( 59 - local.tm_min );
    
    return res;
}

time_t
Utl_mktimeGMT( struct tm *t )
{
#if HAVE_TIMEGM
    return timegm( t );
#else    
    /*
     * Based on the portable implementation suggested in the glibc man page
     * for timegm.
     */
    time_t ret;
    char *tz;

    tz = getenv("TZ");
    setenv("TZ", "", 1);
    tzset();
    ret = mktime(t);
    if (tz)
	setenv("TZ", tz, 1);
    else
	unsetenv("TZ");
    tzset();
    return ret;
#endif    
}

void
Utl_newsDate( time_t t, Str res )
{    
    struct tm *local;
    long tzdiff, hoffset, moffset;

    tzdiff = localTimeDiff( t ) / 60;

    local = localtime( &t );
    if ( local == NULL )
    {
	Utl_cpyStr( res, "** localtime failure **" );
	return;
    }
    
    hoffset = tzdiff / 60;
    moffset = tzdiff % 60;
    if ( moffset < 0 )
	moffset = - moffset;

    sprintf( res, "%s, %d %s %4d %02d:%02d:%02d %+03ld%02ld",
	     DOTW[local->tm_wday], local->tm_mday,
	     MON[local->tm_mon], local->tm_year + 1900,
	     local->tm_hour, local->tm_min, local->tm_sec,
	     hoffset, moffset );
}

time_t
Utl_parseNewsDate( const char *s )
{
    struct tm tm;
    int wday, offset, tzoffset;
    char *p;
    time_t res;

    memset( &tm, 0, sizeof( tm ) );
    wday = -1;
    tm.tm_isdst = -1;
    
    s = nextNonWhiteSpace( s );

    /* Is this the day number, or a weekday? */
    if ( ! isdigit( *s ) )
    {
	if ( strlen( s ) < 4 )
	    return (time_t) -1;
	
	for ( wday = 0; DOTW[ wday ] != NULL; wday++ )
	    if ( strncasecmp( DOTW[ wday ], s, 3 ) == 0 )
		break;

	if( DOTW[ wday ] == NULL || s[3] != ',' )
	    return (time_t) -1;

	s += 4;
    }
    
    /* Get the day number */
    tm.tm_mday = (int) strtol( s, &p, 10 );
    if ( p == s )
	return (time_t) -1;
    s = p;

    /* Look for month name */
    s = nextNonWhiteSpace( s );
    if ( strlen( s ) < 4 )
	return (time_t) -1;
    for ( tm.tm_mon = 0; MON[ tm.tm_mon ] != NULL; tm.tm_mon++ )
	if ( strncasecmp( MON[ tm.tm_mon ], s, 3 ) == 0 )
	    break;

    if ( MON[ tm.tm_mon ] == NULL )
	return (time_t) -1;
    s += 3;

    /* Year next */
    tm.tm_year = (int) strtol( s, &p, 10 );
    if ( p == s || ( tm.tm_year >= 100 && tm.tm_year < 1900  ) )
	return (time_t) -1;
    if ( tm.tm_year >= 1900 )
	tm.tm_year -= 1900;
    s = p;  

    /* Hours */
    tm.tm_hour = (int) strtol( s, &p, 10 );
    if ( p == s || *p != ':' )
	return (time_t) -1;
    s = ++p;

    /* Minutes */
    tm.tm_min = (int) strtol( s, &p, 10 );
    if ( p == s || ( *p != ':' && *p != ' ' ) )
	return (time_t) -1;
    s = p;

    /* Seconds */
    if ( *s == ':' )
    {
	s++;
	tm.tm_sec = (int) strtol( s, &p, 10 );
	if ( p == s )
	    return (time_t) -1;
	s = p;
    }

    /* GMT/UT or timezone offset */
    tzoffset = 0;
    s = nextNonWhiteSpace( s );
    if ( strncasecmp( s, "GMT", 3) == 0 )
	s += 3;
    else if ( strncasecmp( s, "UT", 2 ) == 0 )
	s += 2;
    else
    {
	offset = (int) strtol( s, &p, 10 );
	if ( p == s )
	    return (time_t) -1;
	s = p;
	tzoffset = ( offset / 100 ) * 60 + ( offset % 100 );
    }

    /* Check for following junk */
    if ( *s != '\0' && ! isspace( *s ) )
	return (time_t) -1;

    res = Utl_mktimeGMT( &tm );
    if ( res == (time_t) -1 )
	return res;
    
    if ( wday >= 0 && wday != tm.tm_wday )
	return (time_t) -1;

    /* And now adjust for tzoffset */
    res -= tzoffset * 60;
    
    return res;
}

void
Utl_allocAndCpy( char **dst, const char *src )
{
    int len = strlen( src );
    if ( ! ( *dst = malloc( (size_t)len + 1 ) ) )
    {
        Log_err( "Cannot allocate string with length %lu", strlen( src ) );
        exit( EXIT_FAILURE );
    }
    memcpy( *dst, src, (size_t)len + 1 );
}

sig_t
Utl_installSignalHandler( int sig, sig_t handler )
{
    struct sigaction act, oldAct;

    act.sa_handler = handler;
    sigemptyset( &act.sa_mask );
    act.sa_flags = 0;
    if ( sig != SIGALRM )
        act.sa_flags |= SA_RESTART;
    if ( sigaction( sig, &act, &oldAct ) < 0 )
        return SIG_ERR;
    return oldAct.sa_handler;
}

static Bool
getHostFQDN( Str result )
{
    struct hostent *myHostEnt;
    struct utsname myName;
    
    if ( uname( &myName ) >= 0
         && ( myHostEnt = gethostbyname( myName.nodename ) ) )
    {
        Utl_cpyStr( result, myHostEnt->h_name );
        return TRUE;
    }
    return FALSE;
}

Bool
Utl_getFQDN( Str result )
{
    /* get hostname from Cfg-File */
    Utl_cpyStr( result, Cfg_hostnameMsgId() );
    if ( strlen( result ) != 0 )
	return TRUE;
    return getHostFQDN( result );
}

#if defined(UTIL_TEST)

/* Test code borrowed from wildmat.c. Yep, still uses gets(). */
extern char	*gets();

int
main()
{
    Str line;
    time_t t;

    printf( "Util date tester.  Enter date to test.\n" );    
    printf( "A blank line exits the program.\n" );

    for ( ; ; )
    {
	t = time( NULL );
	Utl_newsDate( t, line );
	printf( "\n(%s) Enter date:  ", line );
	(void) fflush( stdout );
	if ( gets( line ) == NULL || line[0] == '\0' )
	    break;

	t = Utl_parseNewsDate( line );
	if ( t == (time_t) -1 )
	    printf( "Date parse failed\n" );
	else
	{
	    Utl_newsDate( t, line );
	    printf( "Utl_newsDate -> '%s'\n", line );
	}
    }

    exit(0);
    /* NOTREACHED */
}
#endif