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