mirror of
git://git.psyced.org/git/psyclpc
synced 2024-08-15 03:20:16 +00:00
425 lines
12 KiB
C
425 lines
12 KiB
C
/*---------------------------------------------------------------------------
|
|
* Portability code.
|
|
*
|
|
*---------------------------------------------------------------------------
|
|
* Implementation of missing system functions, as well as wrappers to
|
|
* system functions with known bugs.
|
|
*
|
|
* The functions in here should be host-independent, though at the moment
|
|
* some aren't.
|
|
*---------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "driver.h"
|
|
|
|
#include <ctype.h>
|
|
#include "my-rusage.h"
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_SYS_TIME_H
|
|
#include <sys/time.h>
|
|
#endif
|
|
#include <locale.h>
|
|
#include <time.h>
|
|
|
|
#include "backend.h"
|
|
#include "main.h"
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
char current_time_stamp[21] = { 0 };
|
|
/* Static buffer for the last timestamp computed by time_stamp().
|
|
* This is for use only by those functions which must avoid memory
|
|
* operations - all other functions should use time_stamp() itself.
|
|
*/
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
mp_int
|
|
get_current_time (void)
|
|
|
|
/* The function time() can't really be trusted to return an integer.
|
|
* But this game uses the 'current_time', which is an integer number
|
|
* of seconds. To make this more portable, the following functions
|
|
* should be defined in such a way as to return the number of seconds since
|
|
* some chosen year. The old behaviour of time() is to return the number
|
|
* of seconds since 1970.
|
|
*
|
|
* On a SUN Sparc I, a negative time offset of 22 seconds between two
|
|
* sucessive calls to time() has been observed. Similar discrepancies can
|
|
* occur whenever a system clock is set, e.g. by automatic synchronisation
|
|
* via ntp. These negative time offsets can mess up the call_out tables. Since
|
|
* they also could mess up the mudlib, completely hide them by forcing the
|
|
* visible time to continue to run in positive direction.
|
|
*/
|
|
|
|
{
|
|
/* Don't write ever to total_alarms outside the interrupt, to avoid
|
|
* race conditions.
|
|
*/
|
|
static mp_int last_time = 0;
|
|
static mp_int noted_alarms = 0;
|
|
|
|
mp_int offset;
|
|
mp_int now;
|
|
mp_int total_alarms_now = total_alarms;
|
|
|
|
offset = (total_alarms_now - noted_alarms) >> 1;
|
|
noted_alarms = total_alarms_now;
|
|
/* The division by two forces last_time to run at about 1/4 real time
|
|
* so that the computer clock can catch up eventually. Furthermore it
|
|
* allows to miss one single alarm to race conditions (total_alarms is
|
|
* incremented in time, but after we read it) without causing a time
|
|
* anomaly.
|
|
*/
|
|
last_time += offset;
|
|
now = (mp_int)time(NULL); /* Just use the old time() for now */
|
|
if (now >= last_time) {
|
|
last_time = now;
|
|
return now;
|
|
}
|
|
/* Happens when the system clock is modified while this program is
|
|
* running, like some radical ntp resync or timezone fix.
|
|
*/
|
|
debug_message("Time anomaly, %ld seconds.\n", (long)(last_time - now));
|
|
return last_time;
|
|
} /* get_current_time() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
char *
|
|
time_fstring (mp_int t, const char* str, Bool localized)
|
|
|
|
/* Return a textual representation of the time <t> according to the format
|
|
* string <str>. Doesn't cache because it would be necessary to
|
|
* save the format string and compare.
|
|
* If localized is true, this function sets the locale according to the
|
|
* environment variable before calling strftime and resets it afterwards.
|
|
* TODO: It would be nicer to allocate the result buffer dynamically
|
|
* TODO::for using longer format strings. */
|
|
{
|
|
static char result[512];
|
|
struct tm *tm; // broken-down time struct
|
|
time_t ti = (time_t)t;
|
|
tm = localtime(&ti);
|
|
if (!localized) {
|
|
setlocale(LC_TIME, "C");
|
|
strftime(result, sizeof(result)-1, str, tm);
|
|
setlocale(LC_TIME, "");
|
|
}
|
|
else
|
|
strftime(result, sizeof(result)-1, str, tm);
|
|
|
|
return result;
|
|
} /* time_fstring() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
char *
|
|
utime_string (mp_int t, mp_int ut)
|
|
|
|
/* Return a textual representation of the time <t> secs:<ut> microseconds. */
|
|
|
|
{
|
|
static char result[80];
|
|
struct tm *tm;
|
|
size_t len;
|
|
|
|
time_t ti = (time_t)t;
|
|
tm = localtime(&ti);
|
|
len = strftime(result, sizeof(result)-1, "%a %b %d %H:%M:%S:", tm);
|
|
sprintf(result+len, "%06"PRIdMPINT, ut);
|
|
strftime(result+len+6, sizeof(result)-7-len, " %Y", tm);
|
|
|
|
return result;
|
|
} /* utime_string() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
char *
|
|
time_stamp (void)
|
|
|
|
/* Return a textual representation of the current time
|
|
* in the form "YYYY.MM.DD HH:MM:SS".
|
|
* Result is a pointer curent_time_stamp[].
|
|
*
|
|
* Putting this function in strfuns is not a good idea, because
|
|
* it is need by almost every module anyway.
|
|
*/
|
|
|
|
{
|
|
mp_int t;
|
|
struct tm *tm;
|
|
|
|
static mp_int last_time = -1;
|
|
|
|
t = get_current_time();
|
|
if (t != last_time)
|
|
{
|
|
time_t ti = (time_t)t;
|
|
last_time = t;
|
|
tm = localtime(&ti);
|
|
strftime( current_time_stamp, sizeof(current_time_stamp),
|
|
"%Y.%m.%d %H:%M:%S", tm);
|
|
}
|
|
return current_time_stamp;
|
|
} /* time_stamp() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
char *
|
|
xmemmem ( const char *haystack, size_t haystacklen
|
|
, const char *needle, size_t needlelen
|
|
)
|
|
|
|
/* Find the first occurance of <needle> (of length <needlelen>) in
|
|
* <haystack> (of <haystacklen> length) and return a pointer to it.
|
|
* A needle of length 0 is always found at <haystack>.
|
|
* If not found, return NULL.
|
|
*
|
|
#ifndef HAVE_MEMMEM
|
|
* This function is a GNU/Linux extension, but up to and including
|
|
* glibc 2 it wasn't implemented correctly. Since it is also used
|
|
* only in the get_dir() implementation, we don't even bother to
|
|
* use the glibc implementation.
|
|
#endif
|
|
*/
|
|
|
|
{
|
|
mp_int i;
|
|
|
|
i = (mp_int)(haystacklen - needlelen);
|
|
if (i >= 0) do {
|
|
if ( !memcmp(needle, haystack, needlelen) )
|
|
return (char *)haystack;
|
|
haystack++;
|
|
} while (--i >= 0);
|
|
return 0;
|
|
} /* xmemmem() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
/* Some UNIX functions which are not supported on all platforms. */
|
|
|
|
#if defined(__EMX__) || defined(OS2)
|
|
int socketpair (int a, int b, int c, int *d)
|
|
{
|
|
errno = EPERM;
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
#ifdef STRTOL_BROKEN
|
|
|
|
#define DIGIT(x) (isdigit(x) ? (x) - '0' : \
|
|
islower(x) ? (x) + 10 - 'a' : (x) + 10 - 'A')
|
|
#define MBASE ('z' - 'a' + 1 + 10)
|
|
|
|
#ifdef STRTOL_CONST_CHARP
|
|
long
|
|
strtol(register const char *str, char **ptr, register int base)
|
|
#else
|
|
long
|
|
strtol(register char *str, char **ptr, register int base)
|
|
#endif
|
|
{
|
|
register long val;
|
|
register int c;
|
|
int xx, neg = 0;
|
|
|
|
if (ptr != (char **)0)
|
|
*ptr = (char*)str; /* in case no number is formed */
|
|
if (base < 0 || base > MBASE)
|
|
return (0); /* base is invalid -- should be a fatal error */
|
|
if (!isalnum(c = *str)) {
|
|
while (isspace(c))
|
|
c = *++str;
|
|
switch (c) {
|
|
case '-':
|
|
neg++;
|
|
case '+': /* fall-through */
|
|
c = *++str;
|
|
}
|
|
}
|
|
if (base == 0)
|
|
if (c != '0')
|
|
base = 10;
|
|
else if (str[1] == 'x' || str[1] == 'X')
|
|
base = 16;
|
|
else
|
|
base = 8;
|
|
/*
|
|
* for any base > 10, the digits incrementally following
|
|
* 9 are assumed to be "abc...z" or "ABC...Z"
|
|
*/
|
|
if (!isalnum(c) || (xx = DIGIT(c)) >= base)
|
|
return (0); /* no number formed */
|
|
if (base == 10) {
|
|
/* accumulate neg avoids surprises near MAXLONG */
|
|
for (val = '0'-c; isdigit(c = *++str); )
|
|
/* multiplication with a constant can be optimized */
|
|
val = 10 * val +'0'-c;
|
|
} else if (base == 16) {
|
|
/* str[1] might be '0', thus we must not access str[2] without check. */
|
|
if (c == '0' && (str[1] == 'x' || str[1] == 'X') && isxdigit(str[2]))
|
|
c = *(str += 2); /* skip over leading "0x" or "0X" */
|
|
for (val = -DIGIT(c); isalnum(c = *++str) && (xx = DIGIT(c)) < 16; )
|
|
val = (val << 4) - xx;
|
|
} else {
|
|
for (val = -DIGIT(c); isalnum(c = *++str) && (xx = DIGIT(c)) < base; )
|
|
val = base * val - xx;
|
|
}
|
|
if (ptr != (char **)0)
|
|
*ptr = (char *)str;
|
|
return (neg ? val : -val);
|
|
}
|
|
#endif /* STRTOL_BROKEN */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
#ifndef HAVE_STRCSPN
|
|
|
|
size_t
|
|
strcspn(const char *s, const char *set)
|
|
{
|
|
register char *t, *s, c, d;
|
|
|
|
s = start;
|
|
while (c = *s)
|
|
{
|
|
t = set;
|
|
while (d = *t++) {
|
|
if (c == d)
|
|
return s - start;
|
|
}
|
|
s++;
|
|
}
|
|
}
|
|
#endif /* !HAVE_STRCSPN */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
#ifndef HAVE_STRDUP
|
|
|
|
char *
|
|
strdup (const char *str)
|
|
|
|
/* Copy <str> into a freshly allocated memory block and return that one.
|
|
*/
|
|
|
|
{
|
|
char *copy = malloc(strlen(str)+1);
|
|
if (!copy)
|
|
fatal("strdup failed\n");
|
|
strcpy(copy, str);
|
|
return copy;
|
|
}
|
|
|
|
#endif /* !HAVE_STRDUP */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
#ifndef HAVE_MEMSET
|
|
|
|
char *
|
|
memset (char *s, int c, size_t n)
|
|
{
|
|
#ifdef HAVE_BZERO
|
|
if(c == 0)
|
|
bzero(s, n);
|
|
#endif
|
|
else {
|
|
while(--n >= 0)
|
|
*s++ = c;
|
|
}
|
|
}
|
|
|
|
#endif /* !HAVE_MEMSET */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
#if !defined(HAVE_MEMMOVE) && !defined(OVERLAPPING_BCOPY)
|
|
|
|
void
|
|
move_memory (char *dest, char *src, size_t n)
|
|
{
|
|
if (!n)
|
|
return;
|
|
if (dest > src) {
|
|
dest += n;
|
|
src += n;
|
|
do
|
|
*--dest = *--src;
|
|
while (--n);
|
|
} else {
|
|
do
|
|
*dest++ = *src++;
|
|
while (--n);
|
|
}
|
|
}
|
|
#endif /* !HAVE_MEMMOVE */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
#if (!defined(HAVE_CRYPT) && !defined(HAVE__CRYPT)) || !defined(USE_SYSTEM_CRYPT)
|
|
#include "hosts/crypt.c"
|
|
#endif
|
|
|
|
#if 0 /* If you can't get crypt to compile, you can use this dummy */
|
|
char * crypt(char *pass, char *salt)
|
|
{
|
|
return pass;
|
|
}
|
|
#endif
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
#if !defined(HAVE_GETRUSAGE)
|
|
|
|
#include <sys/times.h>
|
|
int
|
|
getrusage (int who, struct rusage *rusage)
|
|
{
|
|
struct tms buffer;
|
|
|
|
if (who != RUSAGE_SELF) {
|
|
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if (times(&buffer)==-1) {
|
|
/* pass errno */
|
|
return -1;
|
|
}
|
|
rusage->ru_utime = buffer.tms_utime;
|
|
rusage->ru_stime = buffer.tms_stime;
|
|
return 0;
|
|
}
|
|
#endif /* getrusage implemented using times() */
|
|
|
|
#if defined(CYGWIN) || defined(__EMX__) || defined(OS2)
|
|
/*-----------------------------------------------------------------------
|
|
** void init_rusage (void)
|
|
** int getrusage (int who, struct rusage *rusage)
|
|
**
|
|
** Get information about resource utilization, well, at least some
|
|
** idea of the time spent in the gamedriver.
|
|
** Reason is that some wiz use the efun getrusage() to measure times
|
|
** in subseconds.
|
|
*/
|
|
|
|
static clock_t first_clock;
|
|
|
|
void
|
|
init_rusage (void) {
|
|
first_clock = clock();
|
|
}
|
|
|
|
int
|
|
getrusage (int who, struct rusage *rusage) {
|
|
if (who != RUSAGE_SELF) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
memset (rusage, 0, sizeof (struct rusage));
|
|
rusage->ru_utime.tv_sec = (clock() - first_clock) / CLK_TCK;
|
|
rusage->ru_utime.tv_usec =
|
|
((clock() - first_clock) % CLK_TCK) * (1000000 / CLK_TCK);
|
|
return 0;
|
|
}
|
|
|
|
#endif /* CYGWIN */
|
|
|
|
/***************************************************************************/
|
|
|