PennMUSH Community

root/1.8.3/trunk/src/funtime.c

Revision 919, 15.4 kB (checked in by shawnw, 1 year ago)

1.8.3p3

Line 
1 /**
2  * \file funtime.c
3  *
4  * \brief Time functions for mushcode.
5  *
6  *
7  */
8 #include "copyrite.h"
9
10 #include "config.h"
11 #include <string.h>
12 #include <ctype.h>
13 #if  (defined(__GNUC__) || defined(__LCC__)) && !defined(__USE_XOPEN_EXTENDED)
14 /* Required to get the getdate() prototype on glibc. */
15 #define __USE_XOPEN_EXTENDED
16 #endif
17 #include <time.h>
18 #include <errno.h>
19 #include "conf.h"
20 #include "externs.h"
21 #include "parse.h"
22 #include "dbdefs.h"
23 #include "log.h"
24 #include "match.h"
25 #include "attrib.h"
26 #include "confmagic.h"
27
28 int do_convtime(const char *str, struct tm *ttm);
29 void do_timestring(char *buff, char **bp, const char *format,
30                    unsigned long secs);
31
32 extern char valid_timefmt_codes[256];
33
34 FUNCTION(fun_timefmt)
35 {
36   char s[BUFFER_LEN];
37   struct tm *ttm;
38   time_t tt;
39   size_t len, n;
40
41   if (!args[0] || !*args[0])
42     return;                     /* No field? Bad user. */
43
44   if (nargs == 2) {
45     /* This is silly, but time_t is signed on several platforms,
46      * so we can't assign an unsigned int to it safely
47      */
48     if (!is_integer(args[1])) {
49       safe_str(T(e_uint), buff, bp);
50       return;
51     }
52     tt = parse_integer(args[1]);
53     if (errno == ERANGE) {
54       safe_str(T(e_range), buff, bp);
55       return;
56     }
57     if (tt < 0) {
58       safe_str(T(e_uint), buff, bp);
59       return;
60     }
61   } else
62     tt = mudtime;
63
64   ttm = localtime(&tt);
65   len = arglens[0];
66   for (n = 0; n < len; n++) {
67     if (args[0][n] == '%')
68       args[0][n] = 0x5;
69     else if (args[0][n] == '$') {
70       args[0][n] = '%';
71       n++;
72       if (args[0][n] == '$')
73         args[0][n] = '%';
74       else if (!valid_timefmt_codes[(unsigned char) args[0][n]]) {
75         safe_format(buff, bp, T("#-1 INVALID ESCAPE CODE '$%c'"),
76                     args[0][n] ? args[0][n] : ' ');
77         return;
78       }
79     }
80   }
81   len = strftime(s, BUFFER_LEN, args[0], ttm);
82   if (len == 0) {
83     /* Problem. Either the output from strftime would be over
84      * BUFFER_LEN characters, or there wasn't any output at all.
85      * In the former case, what's in s is indeterminate. Instead of
86      * trying to figure out which of the two cases happened, just
87      * return an empty string.
88      */
89     safe_str(T("#-1 COULDN'T FORMAT TIME"), buff, bp);
90     return;
91   }
92   for (n = 0; n < len; n++)
93     if (s[n] == '%')
94       s[n] = '$';
95     else if (s[n] == 0x5)
96       s[n] = '%';
97   safe_str(s, buff, bp);
98 }
99
100 /* ARGSUSED */
101 FUNCTION(fun_time)
102 {
103   int utc = 0;
104   int mytime;
105   double tz = 0;
106
107   mytime = mudtime;
108
109   if (nargs == 1) {
110     if (!strcasecmp("UTC", args[0])) {
111       utc = 1;
112     } else if (args[0] && *args[0] && is_strict_number(args[0])) {
113       utc = 1;
114       tz = strtod(args[0], NULL);
115       if (tz < -24.0 || tz > 24.0) {
116         safe_str(T("#-1 INVALID TIME ZONE"), buff, bp);
117         return;
118       }
119       mytime += (int) (tz * 3600);
120     } else if (args[0] && *args[0]) {
121       dbref thing;
122       ATTR *a;
123       char *ptr;
124       utc = 1;
125       thing = match_thing(executor, args[0]);
126       if (!GoodObject(thing)) {
127         safe_str(T(e_notvis), buff, bp);
128         return;
129       }
130       /* Always make time(player) return a time,
131        * even if player's TZ is unset or wonky */
132       a = atr_get(thing, "TZ");
133       if (a && is_strict_number(ptr = atr_value(a))) {
134         tz = strtod(ptr, NULL);
135         if (tz >= -24.0 && tz <= 24.0) {
136           mytime += (int) (tz * 3600);
137         }
138       }
139     }
140   } else if (!strcmp("UTCTIME", called_as)) {
141     utc = 1;
142   }
143
144   safe_str(show_time(mytime, utc), buff, bp);
145 }
146
147 /* ARGSUSED */
148 FUNCTION(fun_secs)
149 {
150   safe_integer(mudtime, buff, bp);
151 }
152
153 /* ARGSUSED */
154 FUNCTION(fun_convsecs)
155 {
156   /* converts seconds to a time string */
157
158   time_t tt;
159   struct tm *ttm;
160   int utc = 0;
161
162   if (strcmp(called_as, "CONVUTCSECS") == 0 ||
163       (nargs == 2 && strcasecmp("UTC", args[1]) == 0))
164     utc = 1;
165
166   if (!is_integer(args[0])) {
167     safe_str(T(e_int), buff, bp);
168     return;
169   }
170   tt = parse_integer(args[0]);
171   if (errno == ERANGE) {
172     safe_str(T(e_range), buff, bp);
173     return;
174   }
175   if (tt < 0) {
176     safe_str(T(e_uint), buff, bp);
177     return;
178   }
179
180   if (utc)
181     ttm = gmtime(&tt);
182   else
183     ttm = localtime(&tt);
184
185   safe_str(show_tm(ttm), buff, bp);
186 }
187
188 /* ARGSUSED */
189 FUNCTION(fun_etimefmt)
190 {
191   unsigned long secs;
192
193   if (!is_uinteger(args[1])) {
194     safe_str(e_uint, buff, bp);
195     return;
196   }
197
198   secs = strtoul(args[1], NULL, 10);
199
200   do_timestring(buff, bp, args[0], secs);
201
202 }
203
204 /* ARGSUSED */
205 FUNCTION(fun_stringsecs)
206 {
207   int secs;
208   if (etime_to_secs(args[0], &secs))
209     safe_integer(secs, buff, bp);
210   else
211     safe_str(T("#-1 INVALID TIMESTRING"), buff, bp);
212 }
213
214 /** Convert an elapsed time string (3d 2h 1m 10s) to seconds.
215  * \param str1 a time string.
216  * \param secs pointer to an int to fill with number of seconds.
217  * \retval 1 success.
218  * \retval 0 failure.
219  */
220 int
221 etime_to_secs(char *str1, int *secs)
222 {
223   /* parse the result from timestring() back into a number of seconds */
224   char str2[BUFFER_LEN];
225   int i;
226
227   *secs = 0;
228   while (str1 && *str1) {
229     while (*str1 == ' ')
230       str1++;
231     i = 0;
232     while (isdigit((unsigned char) *str1)) {
233       str2[i] = *str1;
234       str1++;
235       i++;
236     }
237     if (i == 0) {
238       return 0;                 /* No numbers given */
239     }
240     str2[i] = '\0';
241     if (!*str1) {
242       *secs += parse_integer(str2);     /* no more chars, just add seconds and stop */
243       break;
244     }
245     switch (*str1) {
246     case 'd':
247     case 'D':
248       *secs += (parse_integer(str2) * 86400);   /* days */
249       break;
250     case 'h':
251     case 'H':
252       *secs += (parse_integer(str2) * 3600);    /* hours */
253       break;
254     case 'm':
255     case 'M':
256       *secs += (parse_integer(str2) * 60);      /* minutes */
257       break;
258     case 's':
259     case 'S':
260     case ' ':
261       *secs += parse_integer(str2);     /* seconds */
262       break;
263     default:
264       return 0;
265     }
266     str1++;                     /* move past the time char */
267   }
268   return 1;
269 }
270
271 /* ARGSUSED */
272 FUNCTION(fun_timestring)
273 {
274   /* Convert seconds to #d #h #m #s
275    * If pad > 0, pad with 0's (i.e. 0d 0h 5m 1s)
276    * If pad > 1, force all #'s to be 2 digits
277    */
278   unsigned int secs, pad;
279   unsigned int days, hours, mins;
280
281   if (!is_uinteger(args[0])) {
282     safe_str(T(e_uints), buff, bp);
283     return;
284   }
285   if (nargs == 1)
286     pad = 0;
287   else {
288     if (!is_uinteger(args[1])) {
289       safe_str(T(e_uints), buff, bp);
290       return;
291     }
292     pad = parse_uinteger(args[1]);
293   }
294
295   secs = parse_uinteger(args[0]);
296
297   days = secs / 86400;
298   secs %= 86400;
299   hours = secs / 3600;
300   secs %= 3600;
301   mins = secs / 60;
302   secs %= 60;
303   if (pad || (days > 0)) {
304     if (pad == 2)
305       safe_format(buff, bp, "%02ud %02uh %02um %02us", days, hours, mins, secs);
306     else
307       safe_format(buff, bp, "%ud %2uh %2um %2us", days, hours, mins, secs);
308   } else if (hours > 0)
309     safe_format(buff, bp, "%2uh %2um %2us", hours, mins, secs);
310   else if (mins > 0)
311     safe_format(buff, bp, "%2um %2us", mins, secs);
312   else
313     safe_format(buff, bp, "%2us", secs);
314 }
315
316 #ifdef HAS_GETDATE
317 int do_convtime_gd(const char *str, struct tm *ttm);
318 /** Convert a time string to a struct tm using getdate().
319  * Formats for the time string are taken from the file referenced in
320  * the DATEMSK environment variable.
321  * \param str a time string.
322  * \param ttm pointer to a struct tm to fill.
323  * \retval 1 success.
324  * \retval 0 failure.
325  */
326 int
327 do_convtime_gd(const char *str, struct tm *ttm)
328 {
329   /* converts time string to a struct tm. Returns 1 on success, 0 on fail.
330    * Formats of the time string are taken from the file listed in the
331    * DATEMSK env variable
332    */
333   struct tm *tc;
334
335   tc = getdate(str);
336
337   if (tc == NULL) {
338 #ifdef NEVER
339     if (getdate_err <= 7)
340       do_rawlog(LT_ERR, "getdate returned error code %d for %s", getdate_err,
341                 str);
342 #endif
343     return 0;
344   }
345
346   memcpy(ttm, tc, sizeof(struct tm));
347   ttm->tm_isdst = -1;
348
349   return 1;
350 }
351
352 #endif
353 /* do_convtime for systems without getdate(). Will probably break if in
354          a non en_US locale */
355 static const char *month_table[] = {
356   "Jan",
357   "Feb",
358   "Mar",
359   "Apr",
360   "May",
361   "Jun",
362   "Jul",
363   "Aug",
364   "Sep",
365   "Oct",
366   "Nov",
367   "Dec",
368 };
369
370 /** Convert a time string to a struct tm, without getdate().
371  * \param mystr a time string.
372  * \param ttm pointer to a struct tm to fill.
373  * \retval 1 success.
374  * \retval 0 failure.
375  */
376 int
377 do_convtime(const char *mystr, struct tm *ttm)
378 {
379   /* converts time string to a struct tm. Returns 1 on success, 0 on fail.
380    * Time string format is always 24 characters long, in format
381    * Ddd Mmm DD HH:MM:SS YYYY
382    */
383
384   char *p, *q;
385   char str[25];
386   int i;
387
388   if (strlen(mystr) != 24)
389     return 0;
390   strcpy(str, mystr);
391
392   /* move over the day of week and truncate. Will always be 3 chars.
393    * we don't need this, so we can ignore it.
394    */
395   if (!(p = strchr(str, ' ')))
396     return 0;
397   *p++ = '\0';
398   if (strlen(str) != 3)
399     return 0;
400
401   /* get the month (3 chars), and convert it to a number */
402   if (!(q = strchr(p, ' ')))
403     return 0;
404   *q++ = '\0';
405   if (strlen(p) != 3)
406     return 0;
407   for (i = 0; (i < 12) && strcmp(month_table[i], p); i++) ;
408   if (i == 12)                  /* not found */
409     return 0;
410   else
411     ttm->tm_mon = i;
412
413   /* get the day of month */
414   p = q;
415   while (isspace((unsigned char) *p))   /* skip leading space */
416     p++;
417   if (!(q = strchr(p, ' ')))
418     return 0;
419   *q++ = '\0';
420   ttm->tm_mday = atoi(p);
421
422   /* get hours */
423   if (!(p = strchr(q, ':')))
424     return 0;
425   *p++ = '\0';
426   ttm->tm_hour = atoi(q);
427
428   /* get minutes */
429   if (!(q = strchr(p, ':')))
430     return 0;
431   *q++ = '\0';
432   ttm->tm_min = atoi(p);
433
434   /* get seconds */
435   if (!(p = strchr(q, ' ')))
436     return 0;
437   *p++ = '\0';
438   ttm->tm_sec = atoi(q);
439
440   /* get year */
441   ttm->tm_year = atoi(p) - 1900;
442
443   ttm->tm_isdst = -1;
444
445   return 1;
446 }
447
448 /* ARGSUSED */
449 FUNCTION(fun_convtime)
450 {
451   /* converts time string to seconds */
452
453   struct tm ttm;
454
455   if (do_convtime(args[0], &ttm)
456 #ifdef HAS_GETDATE
457       || do_convtime_gd(args[0], &ttm)
458 #endif
459     ) {
460 #ifdef SUN_OS
461     safe_integer(timelocal(&ttm), buff, bp);
462 #else
463     safe_integer(mktime(&ttm), buff, bp);
464 #endif                          /* SUN_OS */
465   } else {
466     safe_str("-1", buff, bp);
467   }
468 }
469
470 #ifdef WIN32
471 #pragma warning( disable : 4761)        /* NJG: disable warning re conversion */
472 #endif
473 /* ARGSUSED */
474 FUNCTION(fun_isdaylight)
475 {
476   struct tm *ltime;
477
478   ltime = localtime(&mudtime);
479
480   safe_boolean(ltime->tm_isdst > 0, buff, bp);
481 }
482
483 /** Convert seconds to a formatted time string.
484  * \verbatim
485  * Format codes:
486  *       $s - Seconds. $S - Seconds, force 2 digits.
487  *       $m - Minutes. $M - Minutes, force 2 digits.
488  *       $h - Hours.   $H - Hours, force 2 digits.
489  *       $d - Days.    $D - Days, force 2 digits.
490  * $$ - Literal $.
491  * \endverbatim
492  * \param buff string to store the result in.
493  * \param bp pointer into end of buff.
494  * \param format format code string.
495  * \param secs seconds to convert.
496  */
497 void
498 do_timestring(char *buff, char **bp, const char *format, unsigned long secs)
499 {
500   unsigned long days, hours, mins;
501   int pad = 0;
502   int width;
503   const char *c;
504   char *w;
505   bool include_suffix, in_format_flags, even_if_0;
506
507   days = secs / 86400;
508   secs %= 86400;
509   hours = secs / 3600;
510   secs %= 3600;
511   mins = secs / 60;
512   secs %= 60;
513
514   for (c = format; c && *c; c++) {
515     if (*c == '$') {
516       c++;
517       width = parse_int(c, &w, 10);
518       if (c == w)
519         pad = 0;
520       else
521         pad = 1;
522       if (width < 0)
523         width = 0;
524       else if (width >= BUFFER_LEN)
525         width = BUFFER_LEN - 1;
526       even_if_0 = in_format_flags = 1;
527       include_suffix = 0;
528       while (in_format_flags) {
529         switch (*w) {
530         case 'x':
531         case 'X':
532           include_suffix = 1;
533           w++;
534           break;
535         case 'z':
536         case 'Z':
537           even_if_0 = 0;
538           w++;
539           break;
540         case '$':
541           in_format_flags = 0;
542           if (pad)
543             safe_format(buff, bp, "%*c", width, '$');
544           else
545             safe_chr('$', buff, bp);
546           break;
547         case 's':
548           in_format_flags = 0;
549           if (secs || even_if_0) {
550             if (pad)
551               safe_format(buff, bp, "%*lu", width, secs);
552             else
553               safe_uinteger(secs, buff, bp);
554             if (include_suffix)
555               safe_chr('s', buff, bp);
556           } else if (pad)
557             safe_fill(' ', width + (include_suffix ? 1 : 0), buff, bp);
558           break;
559         case 'S':
560           in_format_flags = 0;
561           if (secs || even_if_0) {
562             if (pad)