Mercurial > noffle
view src/util.c @ 279:49b452b667a6 noffle
[svn] * src/util.c: localTimeDiff() cached its value and recalculated it every
hour of clock time, regardless of the time the calculated was based on.
This is potentially dangerous at daylight saving changes. So instead
use the cached last result only when the new request is to be based on
a time in the same hour as the cached result.
* src/util.c: Replace the alternate Utl_mktimeGMT() implementation used when
timegm() is not available. The previous version, as suggested by the
glibc timegm() man page, used setenv() and unsetenv() for changing the
environment. These aren't POSIX function, and the POSIX putenv()
(a) is tricky to manage if the same var is being constantly update and
memory isn't to leak, and (b) provides no way to remove an environment
entry. So change to an implementation Wget uses. This should compile on
not glibc systems - the previous version failed to build on Solaris.
author | bears |
---|---|
date | Sun, 17 Nov 2002 15:18:19 +0000 |
parents | 3477050e8d10 |
children | 5eece4dfd945 |
line wrap: on
line source
/* util.c $Id: util.c 411 2002-11-17 15:18:19Z 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. * * Do some basic caching by returning the previous result if the new request * is in the same hour as the previous one. */ static int localTimeDiff( time_t t ) { struct tm local, gmt, *tm; static time_t resHourStart = 0; static int res = 0; if ( labs( t - resHourStart ) < 60 ) return res; /* Calculating for new hour */ resHourStart = ( t / 60 ) * 60; 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; return res; } time_t Utl_mktimeGMT( struct tm *t ) { #if HAVE_TIMEGM return timegm( t ); #else /* This comment and implmentation is taken from Wget's mktime_from_utc(). * We could * * Converts struct tm to time_t, assuming the data in tm is UTC rather * than local timezone. * * mktime is similar but assumes struct tm, also known as the * "broken-down" form of time, is in local time zone. mktime_from_utc * uses mktime to make the conversion understanding that an offset * will be introduced by the local time assumption. * * mktime_from_utc then measures the introduced offset by applying * gmtime to the initial result and applying mktime to the resulting * "broken-down" form. The difference between the two mktime results * is the measured offset which is then subtracted from the initial * mktime result to yield a calendar time which is the value returned. * * tm_isdst in struct tm is set to 0 to force mktime to introduce a * consistent offset (the non DST offset) since tm and tm+o might be * on opposite sides of a DST change. * * Some implementations of mktime return -1 for the nonexistent * localtime hour at the beginning of DST. In this event, use * mktime(tm - 1hr) + 3600. * * Schematically * mktime(tm) --> t+o * gmtime(t+o) --> tm+o * mktime(tm+o) --> t+2o * t+o - (t+2o - t+o) = t * * Note that glibc contains a function of the same purpose named * `timegm' (reverse of gmtime). But obviously, it is not universally * available, and unfortunately it is not straightforwardly * extractable for use here. Perhaps configure should detect timegm * and use it where available. * * Contributed by Roger Beeman <beeman@cisco.com>, with the help of * Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. * Further improved by Roger with assistance from Edward J. Sabol * based on input by Jamie Zawinski. */ time_t tl, tb; struct tm *tg; tl = mktime (t); if (tl == -1) { t->tm_hour--; tl = mktime (t); if (tl == -1) return -1; /* can't deal with output from strptime */ tl += 3600; } tg = gmtime (&tl); tg->tm_isdst = 0; tb = mktime (tg); if (tb == -1) { tg->tm_hour--; tb = mktime (tg); if (tb == -1) return -1; /* can't deal with output from gmtime */ tb += 3600; } return (tl - (tb - tl)); #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 ); } SignalHandler Utl_installSignalHandler( int sig, SignalHandler 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