1 /* $Id: time.c,v 1.34 2021-11-20 18:19:39 phil Exp $ */
2
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif /* HAVE_CONFIG_H defined */
6
7 #if defined(HAVE_CLOCK_GETTIME_REALTIME)
8 #define GETTIMESPEC(TS) (clock_gettime(CLOCK_REALTIME, TS) == 0)
9 #elif defined(HAVE_TIMESPEC_GET)
10 #define GETTIMESPEC(TS) (timespec_get(TS, TIME_UTC) == TIME_UTC)
11 #elif defined(HAVE_GETTIMEOFDAY)
12 #include <sys/time.h>
13 #endif
14
15 #include <stdlib.h> /* for free() */
16 #include <stdio.h> /* for lib.h */
17 #include <time.h> /* time_t, time(), strptime() */
18
19 #include "h.h"
20 #include "equ.h"
21 #include "snotypes.h"
22 #include "macros.h"
23 #include "module.h"
24 #include "load.h"
25 #include "lib.h" /* sleepf() */
26 #include "str.h"
27
28 SNOBOL4_MODULE(time)
29
30 #define SETINT(DP,N,VAL) (DP)[N].a.i = (VAL); (DP)[N].f = 0; (DP)[N].v = I
31 #define SETREAL(DP,N,VAL) (DP)[N].a.f = (VAL); (DP)[N].f = 0; (DP)[N].v = R
32 #define SETNULL(DP,N) (DP)[N].a.i = 0; (DP)[N].f = 0; (DP)[N].v = S
33
34 enum tv_member {
35 TV_DESCR,
36 TV_SEC,
37 TV_USEC,
38 TV_NSEC,
39 TV_COUNT /* must be last */
40 };
41
42 enum tm_member {
43 TM_DESCR,
44 TM_SEC,
45 TM_MIN,
46 TM_HOUR,
47 TM_MDAY,
48 TM_MON,
49 TM_YEAR,
50 TM_WDAY,
51 TM_YDAY,
52 TM_ISDST,
53 TM_GMTOFF,
54 TM_COUNT /* must be last */
55 };
56
57 #define COUNT(DP) ((DP)->v/DESCR+1)
58
59 /*
60 * LOAD("GETTIMEOFDAY_(TIMEVAL)", TIME_DL)
61 */
62 lret_t
GETTIMEOFDAY_(LA_ALIST)63 GETTIMEOFDAY_( LA_ALIST ) {
64 struct descr *dp = LA_PTR(0);
65 #if defined(GETTIMESPEC)
66 struct timespec ts;
67 #elif defined(HAVE_GETTIMEOFDAY)
68 struct timeval tv;
69 #endif
70
71 (void) nargs;
72 if (!dp || LA_TYPE(0) < DATSTA || COUNT(dp) != TV_COUNT)
73 RETNULL;
74 /* validate dp[TV_DESCR] */
75 #if defined(GETTIMESPEC)
76 if (!GETTIMESPEC(&ts))
77 RETFAIL;
78 SETINT(dp,TV_SEC,ts.tv_sec);
79 SETINT(dp,TV_USEC,(ts.tv_nsec+500)/1000);
80 SETINT(dp,TV_NSEC,ts.tv_nsec);
81 #elif defined(HAVE_GETTIMEOFDAY)
82 if (gettimeofday(&tv, NULL) < 0)
83 RETFAIL;
84 SETINT(dp,TV_SEC,tv.tv_sec);
85 SETINT(dp,TV_USEC,tv.tv_usec);
86 SETINT(dp,TV_NSEC,tv.tv_usec*1000);
87 #else
88 SETINT(dp,TV_SEC,time(0));
89 SETINT(dp,TV_USEC,0);
90 SETINT(dp,TV_NSEC,0);
91 #endif
92 RETNULL;
93 }
94
95 static int
tm2sno(struct tm * tmp,struct descr * dp)96 tm2sno(struct tm *tmp, struct descr *dp) {
97 if (!dp || COUNT(dp) != TM_COUNT)
98 return 0;
99 SETINT(dp,TM_SEC,tmp->tm_sec);
100 SETINT(dp,TM_MIN,tmp->tm_min);
101 SETINT(dp,TM_HOUR,tmp->tm_hour);
102 SETINT(dp,TM_MDAY,tmp->tm_mday);
103 SETINT(dp,TM_MON,tmp->tm_mon);
104 SETINT(dp,TM_YEAR,tmp->tm_year);
105 SETINT(dp,TM_WDAY,tmp->tm_wday);
106 SETINT(dp,TM_YDAY,tmp->tm_yday);
107 SETINT(dp,TM_ISDST,tmp->tm_isdst);
108 #ifdef HAVE_TM_GMTOFF
109 SETINT(dp,TM_GMTOFF,tmp->tm_gmtoff);
110 #else
111 SETNULL(dp,TM_GMTOFF);
112 #endif
113 return 1;
114 }
115
116 static int
sno2tm(struct descr * dp,struct tm * tmp)117 sno2tm(struct descr *dp, struct tm *tmp) {
118 if (!dp || COUNT(dp) != TM_COUNT)
119 return 0;
120 memset(tmp, 0, sizeof(struct tm));
121 /* accept int or empty string */
122 #define FETCH(X,Y) \
123 if (dp[Y].v == I) tmp->X = dp[Y].a.i; \
124 else if (dp[Y].v == S && dp[Y].a.ptr == 0) tmp->X = 0; \
125 else return 0
126 FETCH(tm_sec, TM_SEC);
127 FETCH(tm_min, TM_MIN);
128 FETCH(tm_hour, TM_HOUR);
129 FETCH(tm_mday, TM_MDAY);
130 FETCH(tm_mon, TM_MON);
131 FETCH(tm_year, TM_YEAR);
132 FETCH(tm_wday, TM_WDAY);
133 FETCH(tm_yday, TM_YDAY);
134 FETCH(tm_isdst, TM_ISDST);
135 #ifdef HAVE_TM_GMTOFF
136 FETCH(tm_gmtoff, TM_GMTOFF);
137 #endif
138 return 1;
139 }
140
141 /*
142 * LOAD("LOCALTIME_(INTEGER,TM)", TIME_DL)
143 */
144 lret_t
LOCALTIME_(LA_ALIST)145 LOCALTIME_( LA_ALIST ) {
146 time_t t = LA_INT(0);
147 struct tm *tmp = localtime(&t);
148 (void) nargs;
149 if (LA_TYPE(1) < DATSTA || !tm2sno(tmp, LA_PTR(1)))
150 RETFAIL;
151 RETNULL;
152 }
153
154 /*
155 * LOAD("GMTIME_(INTEGER,TM)", TIME_DL)
156 */
157 lret_t
GMTIME_(LA_ALIST)158 GMTIME_( LA_ALIST ) {
159 time_t t = LA_INT(0);
160 struct tm *tmp = gmtime(&t);
161 (void) nargs;
162 if (!tm2sno(tmp, LA_PTR(1)))
163 RETFAIL;
164 RETNULL;
165 }
166
167 #ifdef HAVE_STRFTIME
168 /*
169 * LOAD("STRFTIME(STRING,TM)STRING", TIME_DL)
170 */
171 lret_t
STRFTIME(LA_ALIST)172 STRFTIME( LA_ALIST ) {
173 char format[1024];
174 char output[1024];
175 struct tm tm;
176
177 (void) nargs;
178 getstring(LA_PTR(0), format, sizeof(format));
179 if (!sno2tm(LA_PTR(1), &tm))
180 RETFAIL;
181 strftime(output, sizeof(output), format, &tm);
182 RETSTR(output);
183 }
184 #endif
185
186 /*
187 * LOAD("MKTIME(TM)INTEGER", TIME_DL)
188 */
189
190 lret_t
MKTIME(LA_ALIST)191 MKTIME( LA_ALIST ) {
192 struct tm tm;
193 time_t ret;
194
195 (void) nargs;
196 if (!sno2tm(LA_PTR(0), &tm))
197 RETFAIL;
198 ret = mktime(&tm);
199 if (ret == -1 || !tm2sno(&tm, LA_PTR(0)))
200 RETFAIL;
201 RETINT(ret);
202 }
203
204 #ifdef HAVE_SLEEP
205 /*
206 * LOAD("SLEEP(REAL)", TIME_DL)
207 */
208 lret_t
SLEEP(LA_ALIST)209 SLEEP( LA_ALIST ) {
210 (void) nargs;
211 if (sleepf(LA_REAL(0)) < 0)
212 RETFAIL; /* did not sleep full period */
213 RETNULL;
214 }
215 #endif
216
217 #ifdef HAVE_STRPTIME
218 /*
219 * LOAD("STRPTIME_(STRING,STRING,TM)", TIME_DL)
220 */
221 lret_t
STRPTIME_(LA_ALIST)222 STRPTIME_( LA_ALIST ) {
223 struct tm tm;
224 char *ret;
225 char *format;
226 char *input = nmgetstring(LA_PTR(0));
227 (void) nargs;
228 if (!input)
229 RETFAIL;
230 format = nmgetstring(LA_PTR(1));
231 if (!format) {
232 free(input);
233 RETFAIL;
234 }
235 memset(&tm, 0, sizeof(tm)); /* sno2tm(LA_PTR(2), &tm); ? */
236 ret = strptime(input, format, &tm);
237 free(input);
238 free(format);
239 if (ret && tm2sno(&tm, LA_PTR(2)))
240 RETSTR(ret); /* return remaining string */
241 RETFAIL;
242 }
243 #endif
244
245 #ifdef HAVE_TIMEGM
246 /*
247 * LOAD("TIMEGM(TM)INTEGER", TIME_DL)
248 */
249
250 lret_t
TIMEGM(LA_ALIST)251 TIMEGM( LA_ALIST ) {
252 struct tm tm;
253 time_t ret;
254
255 (void) nargs;
256 if (!sno2tm(LA_PTR(0), &tm))
257 RETFAIL;
258 ret = timegm(&tm);
259 if (ret == -1 || !tm2sno(&tm, LA_PTR(0)))
260 RETFAIL;
261 RETINT(ret);
262 }
263 #endif
264