/*--------------------------------------------------------------------------- * 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 #include "my-rusage.h" #include #include #ifdef HAVE_SYS_TIME_H #include #endif #include #include #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 according to the format * string . 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 secs: 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 (of length ) in * (of length) and return a pointer to it. * A needle of length 0 is always found at . * 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 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 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 */ /***************************************************************************/