/** * \file funtime.c * * \brief Time functions for mushcode. * * */ #include "copyrite.h" #include "config.h" #include <string.h> #include <ctype.h> #if (defined(__GNUC__) || defined(__LCC__)) && !defined(__USE_XOPEN_EXTENDED) /* Required to get the getdate() prototype on glibc. */ #define __USE_XOPEN_EXTENDED #endif #include <time.h> #include <errno.h> #include "conf.h" #include "externs.h" #include "parse.h" #include "dbdefs.h" #include "log.h" #include "match.h" #include "confmagic.h" int do_convtime(const char *str, struct tm *ttm); void do_timestring(char *buff, char **bp, const char *format, unsigned long secs); extern char valid_timefmt_codes[256]; FUNCTION(fun_timefmt) { char s[BUFFER_LEN]; struct tm *ttm; time_t tt; int len, n; if (!args[0] || !*args[0]) return; /* No field? Bad user. */ if (nargs == 2) { /* This is silly, but time_t is signed on several platforms, * so we can't assign an unsigned int to it safely */ if (!is_integer(args[1])) { safe_str(T(e_uint), buff, bp); return; } tt = parse_integer(args[1]); if (errno == ERANGE) { safe_str(T(e_range), buff, bp); return; } if (tt < 0) { safe_str(T(e_uint), buff, bp); return; } } else tt = mudtime; ttm = localtime(&tt); len = arglens[0]; for (n = 0; n < len; n++) { if (args[0][n] == '%') args[0][n] = 0x5; else if (args[0][n] == '$') { args[0][n] = '%'; n++; if (args[0][n] == '$') args[0][n] = '%'; else if (!valid_timefmt_codes[(unsigned char) args[0][n]]) { safe_format(buff, bp, T("#-1 INVALID ESCAPE CODE '$%c'"), args[0][n] ? args[0][n] : ' '); return; } } } len = strftime(s, BUFFER_LEN, args[0], ttm); if (len == 0) { /* Problem. Either the output from strftime would be over * BUFFER_LEN characters, or there wasn't any output at all. * In the former case, what's in s is indeterminate. Instead of * trying to figure out which of the two cases happened, just * return an empty string. */ return; } for (n = 0; n < len; n++) if (s[n] == '%') s[n] = '$'; else if (s[n] == 0x5) s[n] = '%'; safe_str(s, buff, bp); } /* ARGSUSED */ FUNCTION(fun_time) { int utc = 0; int mytime; double tz = 0; mytime = mudtime; if (nargs == 1) { if (!strcasecmp("UTC", args[0])) { utc = 1; } else if (args[0] && *args[0] && is_strict_number(args[0])) { utc = 1; tz = strtod(args[0], NULL); if (tz < -24.0 || tz > 24.0) { safe_str(T("#-1 INVALID TIME ZONE"), buff, bp); return; } mytime += (int) (tz * 3600); } else if (args[0] && *args[0]) { dbref thing; ATTR *a; char *ptr; utc = 1; thing = match_thing(executor, args[0]); if (!GoodObject(thing)) { safe_str(T(e_notvis), buff, bp); return; } /* Always make time(player) return a time, * even if player's TZ is unset or wonky */ a = atr_get(thing, "TZ"); if (a && is_strict_number(ptr = atr_value(a))) { tz = strtod(ptr, NULL); if (tz >= -24.0 || tz <= 24.0) { mytime += (int) (tz * 3600); } } } } else if (!strcmp("UTCTIME", called_as)) { utc = 1; } safe_str(show_time(mytime, utc), buff, bp); } /* ARGSUSED */ FUNCTION(fun_secs) { safe_integer(mudtime, buff, bp); } /* ARGSUSED */ FUNCTION(fun_convsecs) { /* converts seconds to a time string */ time_t tt; struct tm *ttm; int utc = 0; if (strcmp(called_as, "CONVUTCSECS") == 0 || (nargs == 2 && strcasecmp("UTC", args[1]) == 0)) utc = 1; if (!is_integer(args[0])) { safe_str(T(e_int), buff, bp); return; } tt = parse_integer(args[0]); if (errno == ERANGE) { safe_str(T(e_range), buff, bp); return; } if (tt < 0) { safe_str(T(e_uint), buff, bp); return; } if (utc) ttm = gmtime(&tt); else ttm = localtime(&tt); safe_str(show_tm(ttm), buff, bp); } /* ARGSUSED */ FUNCTION(fun_etimefmt) { unsigned long secs; if (!is_uinteger(args[1])) { safe_str(e_uint, buff, bp); return; } secs = parse_uinteger(args[1]); do_timestring(buff, bp, args[0], secs); } /* ARGSUSED */ FUNCTION(fun_timestring) { /* Convert seconds to #d #h #m #s * If pad > 0, pad with 0's (i.e. 0d 0h 5m 1s) * If pad > 1, force all #'s to be 2 digits */ unsigned int secs, pad; unsigned int days, hours, mins; if (!is_uinteger(args[0])) { safe_str(T(e_uints), buff, bp); return; } if (nargs == 1) pad = 0; else { if (!is_uinteger(args[1])) { safe_str(T(e_uints), buff, bp); return; } pad = parse_uinteger(args[1]); } secs = parse_uinteger(args[0]); days = secs / 86400; secs %= 86400; hours = secs / 3600; secs %= 3600; mins = secs / 60; secs %= 60; if (pad || (days > 0)) { if (pad == 2) safe_format(buff, bp, "%02ud %02uh %02um %02us", days, hours, mins, secs); else safe_format(buff, bp, "%ud %2uh %2um %2us", days, hours, mins, secs); } else if (hours > 0) safe_format(buff, bp, "%2uh %2um %2us", hours, mins, secs); else if (mins > 0) safe_format(buff, bp, "%2um %2us", mins, secs); else safe_format(buff, bp, "%2us", secs); } #ifdef HAS_GETDATE int do_convtime_gd(const char *str, struct tm *ttm); /** Convert a time string to a struct tm using getdate(). * Formats for the time string are taken from the file referenced in * the DATEMSK environment variable. * \param str a time string. * \param ttm pointer to a struct tm to fill. * \retval 1 success. * \retval 0 failure. */ int do_convtime_gd(const char *str, struct tm *ttm) { /* converts time string to a struct tm. Returns 1 on success, 0 on fail. * Formats of the time string are taken from the file listed in the * DATEMSK env variable */ struct tm *tc; tc = getdate(str); if (tc == NULL) { #ifdef NEVER if (getdate_err <= 7) do_rawlog(LT_ERR, "getdate returned error code %d for %s", getdate_err, str); #endif return 0; } memcpy(ttm, tc, sizeof(struct tm)); ttm->tm_isdst = -1; return 1; } #endif /* do_convtime for systems without getdate(). Will probably break if in a non en_US locale */ static const char *month_table[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; /** Convert a time string to a struct tm, without getdate(). * \param mystr a time string. * \param ttm pointer to a struct tm to fill. * \retval 1 success. * \retval 0 failure. */ int do_convtime(const char *mystr, struct tm *ttm) { /* converts time string to a struct tm. Returns 1 on success, 0 on fail. * Time string format is always 24 characters long, in format * Ddd Mmm DD HH:MM:SS YYYY */ char *p, *q; char str[25]; int i; if (strlen(mystr) != 24) return 0; strcpy(str, mystr); /* move over the day of week and truncate. Will always be 3 chars. * we don't need this, so we can ignore it. */ if (!(p = strchr(str, ' '))) return 0; *p++ = '\0'; if (strlen(str) != 3) return 0; /* get the month (3 chars), and convert it to a number */ if (!(q = strchr(p, ' '))) return 0; *q++ = '\0'; if (strlen(p) != 3) return 0; for (i = 0; (i < 12) && strcmp(month_table[i], p); i++) ; if (i == 12) /* not found */ return 0; else ttm->tm_mon = i; /* get the day of month */ p = q; while (isspace((unsigned char) *p)) /* skip leading space */ p++; if (!(q = strchr(p, ' '))) return 0; *q++ = '\0'; ttm->tm_mday = atoi(p); /* get hours */ if (!(p = strchr(q, ':'))) return 0; *p++ = '\0'; ttm->tm_hour = atoi(q); /* get minutes */ if (!(q = strchr(p, ':'))) return 0; *q++ = '\0'; ttm->tm_min = atoi(p); /* get seconds */ if (!(p = strchr(q, ' '))) return 0; *p++ = '\0'; ttm->tm_sec = atoi(q); /* get year */ ttm->tm_year = atoi(p) - 1900; ttm->tm_isdst = -1; return 1; } /* ARGSUSED */ FUNCTION(fun_convtime) { /* converts time string to seconds */ struct tm ttm; if (do_convtime(args[0], &ttm) #ifdef HAS_GETDATE || do_convtime_gd(args[0], &ttm) #endif ) { #ifdef SUN_OS safe_integer(timelocal(&ttm), buff, bp); #else safe_integer(mktime(&ttm), buff, bp); #endif /* SUN_OS */ } else { safe_str("-1", buff, bp); } } #ifdef WIN32 #pragma warning( disable : 4761) /* NJG: disable warning re conversion */ #endif /* ARGSUSED */ FUNCTION(fun_isdaylight) { struct tm *ltime; ltime = localtime(&mudtime); safe_boolean(ltime->tm_isdst > 0, buff, bp); } /** Convert seconds to a formatted time string. * \verbatim * Format codes: * $s - Seconds. $S - Seconds, force 2 digits. * $m - Minutes. $M - Minutes, force 2 digits. * $h - Hours. $H - Hours, force 2 digits. * $d - Days. $D - Days, force 2 digits. * $$ - Literal $. * \endverbatim * \param buff string to store the result in. * \param bp pointer into end of buff. * \param format format code string. * \param secs seconds to convert. */ void do_timestring(char *buff, char **bp, const char *format, unsigned long secs) { int days, hours, mins; int pad = 0; int width; const char *c; char *w; int include_suffix, even_if_0, in_format_flags; days = secs / 86400; secs %= 86400; hours = secs / 3600; secs %= 3600; mins = secs / 60; secs %= 60; for (c = format; c && *c; c++) { if (*c == '$') { c++; width = strtol(c, &w, 10); if (c == w) pad = 0; else pad = 1; if (width < 0) width = 0; else if (width >= BUFFER_LEN) width = BUFFER_LEN - 1; even_if_0 = in_format_flags = 1; include_suffix = 0; while (in_format_flags) { switch (*w) { case 'x': case 'X': include_suffix = 1; w++; break; case 'z': case 'Z': even_if_0 = 0; w++; break; case '$': in_format_flags = 0; if (pad) safe_format(buff, bp, "%*c", width, '$'); else safe_chr('$', buff, bp); break; case 's': in_format_flags = 0; if (secs || even_if_0) { if (pad) safe_format(buff, bp, "%*lu", width, secs); else safe_uinteger(secs, buff, bp); if (include_suffix) safe_chr('s', buff, bp); } else if (pad) safe_fill(' ', width + (include_suffix ? 1 : 0), buff, bp); break; case 'S': in_format_flags = 0; if (secs || even_if_0) { if (pad) safe_format(buff, bp, "%0*lu", width, secs); else safe_format(buff, bp, "%0lu", secs); if (include_suffix) safe_chr('s', buff, bp); } else if (pad) safe_fill(' ', width + (include_suffix ? 1 : 0), buff, bp); break; case 'm': in_format_flags = 0; if (mins || even_if_0) { if (pad) safe_format(buff, bp, "%*d", width, mins); else safe_integer(mins, buff, bp); if (include_suffix) safe_chr('m', buff, bp); } else if (pad) safe_fill(' ', width + (include_suffix ? 1 : 0), buff, bp); break; case 'M': in_format_flags = 0; if (mins || even_if_0) { if (pad) safe_format(buff, bp, "%0*d", width, mins); else safe_format(buff, bp, "%0d", mins); if (include_suffix) safe_chr('m', buff, bp); } else if (pad) safe_fill(' ', width + (include_suffix ? 1 : 0), buff, bp); break; case 'h': in_format_flags = 0; if (hours || even_if_0) { if (pad) safe_format(buff, bp, "%*d", width, hours); else safe_integer(hours, buff, bp); if (include_suffix) safe_chr('h', buff, bp); } else if (pad) safe_fill(' ', width + (include_suffix ? 1 : 0), buff, bp); break; case 'H': in_format_flags = 0; if (hours || even_if_0) { if (pad) safe_format(buff, bp, "%0*d", width, hours); else safe_format(buff, bp, "%0d", hours); if (include_suffix) safe_chr('h', buff, bp); } else if (pad) safe_fill(' ', width + (include_suffix ? 1 : 0), buff, bp); break; case 'd': in_format_flags = 0; if (days || even_if_0) { if (pad) safe_format(buff, bp, "%*d", width, days); else safe_integer(days, buff, bp); if (include_suffix) safe_chr('d', buff, bp); } else if (pad) safe_fill(' ', width + (include_suffix ? 1 : 0), buff, bp); break; case 'D': in_format_flags = 0; if (days || even_if_0) { if (pad) safe_format(buff, bp, "%0*d", width, days); else safe_format(buff, bp, "%0d", days); if (include_suffix) safe_chr('d', buff, bp); } else if (pad) safe_fill(' ', width + (include_suffix ? 1 : 0), buff, bp); break; default: in_format_flags = 0; safe_chr('$', buff, bp); for (; c != w; c++) safe_chr(*c, buff, bp); safe_chr(*c, buff, bp); } } c = w; } else safe_chr(*c, buff, bp); } } #ifdef WIN32 #pragma warning( default : 4761) /* NJG: enable warning re conversion */ #endif