1/*
2 * datetime.c - parameter and command interface to date and time utilities
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 2002 Peter Stephenson, Clint Adams
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Peter Stephenson, Clint Adams or the Zsh Development Group
16 * be liable to any party for direct, indirect, special, incidental, or
17 * consequential damages arising out of the use of this software and its
18 * documentation, even if Peter Stephenson, Clint Adams and the Zsh
19 * Development Group have been advised of the possibility of such damage.
20 *
21 * Peter Stephenson, Clint Adams and the Zsh Development Group specifically
22 * disclaim any warranties, including, but not limited to, the implied
23 * warranties of merchantability and fitness for a particular purpose.
24 * The software provided hereunder is on an "as is" basis, and Peter
25 * Stephenson, Clint Adams and the Zsh Development Group have no obligation
26 * to provide maintenance, support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "datetime.mdh"
31#include "datetime.pro"
32#include <time.h>
33
34#ifndef HAVE_MKTIME
35#ifdef HAVE_TIMELOCAL
36#define	mktime(x)	timelocal(x)
37#define HAVE_MKTIME	1
38#endif
39#endif
40
41static int
42reverse_strftime(char *nam, char **argv, char *scalar, int quiet)
43{
44#if defined(HAVE_STRPTIME) && defined(HAVE_MKTIME)
45    struct tm tm;
46    zlong mytime;
47    char *endp;
48
49    /*
50     * Initialise all parameters to zero; there's no floating point
51     * so memset() will do the trick.  The exception is that tm_isdst
52     * is set to -1 which, if not overridden, will cause mktime()
53     * to use the current timezone.  This is probably the best guess;
54     * it's the one that will cause dates and times output by strftime
55     * without the -r option and without an explicit timezone to be
56     * converted back correctly.
57     */
58    (void)memset(&tm, 0, sizeof(tm));
59    tm.tm_isdst = -1;
60    endp = strptime(argv[1], argv[0], &tm);
61
62    if (!endp) {
63	/* Conversion failed completely. */
64	if (!quiet)
65	    zwarnnam(nam, "format not matched");
66	return 1;
67    }
68
69    mytime = (zlong)mktime(&tm);
70
71    if (scalar)
72	setiparam(scalar, mytime);
73    else {
74	char buf[DIGBUFSIZE];
75	convbase(buf, mytime, 10);
76	printf("%s\n", buf);
77    }
78
79    if (*endp && !quiet) {
80	/*
81	 * Not everything in the input string was converted.
82	 * This is probably benign, since the format has been satisfied,
83	 * but issue a warning unless the quiet flag is set.
84	 */
85	zwarnnam(nam, "warning: input string not completely matched");
86    }
87
88    return 0;
89#else
90    if (!quiet)
91	zwarnnam(nam, "not implemented on this system");
92    return 2;
93#endif
94}
95
96static int
97bin_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
98{
99    int bufsize, x;
100    char *endptr = NULL, *scalar = NULL, *buffer;
101    time_t secs;
102    struct tm *t;
103
104    if (OPT_ISSET(ops,'s')) {
105	scalar = OPT_ARG(ops, 's');
106	if (!isident(scalar)) {
107	    zwarnnam(nam, "not an identifier: %s", scalar);
108	    return 1;
109	}
110    }
111    if (OPT_ISSET(ops, 'r'))
112	return reverse_strftime(nam, argv, scalar, OPT_ISSET(ops, 'q'));
113
114    errno = 0;
115    secs = (time_t)strtoul(argv[1], &endptr, 10);
116    if (errno != 0) {
117	zwarnnam(nam, "%s: %e", argv[1], errno);
118	return 1;
119    } else if (*endptr != '\0') {
120	zwarnnam(nam, "%s: invalid decimal number", argv[1]);
121	return 1;
122    }
123
124    t = localtime(&secs);
125    if (!t) {
126	zwarnnam(nam, "%s: unable to convert to time", argv[1]);
127	return 1;
128    }
129    bufsize = strlen(argv[0]) * 8;
130    buffer = zalloc(bufsize);
131
132    for (x=0; x < 4; x++) {
133        if (ztrftime(buffer, bufsize, argv[0], t) >= 0)
134	    break;
135	buffer = zrealloc(buffer, bufsize *= 2);
136    }
137
138    if (scalar) {
139	setsparam(scalar, metafy(buffer, -1, META_DUP));
140    } else {
141	printf("%s\n", buffer);
142    }
143    zfree(buffer, bufsize);
144
145    return 0;
146}
147
148static zlong
149getcurrentsecs(UNUSED(Param pm))
150{
151    return (zlong) time(NULL);
152}
153
154static double
155getcurrentrealtime(Param pm)
156{
157#ifdef HAVE_CLOCK_GETTIME
158    struct timespec now;
159
160    if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
161	zwarn("%s: unable to retrieve time: %e", pm->node.nam, errno);
162	return (double)0.0;
163    }
164
165    return (double)now.tv_sec + (double)now.tv_nsec * 1e-9;
166#else
167    struct timeval now;
168    struct timezone dummy_tz;
169
170    (void)pm;
171    gettimeofday(&now, &dummy_tz);
172
173    return (double)now.tv_sec + (double)now.tv_usec * 1e-6;
174#endif
175}
176
177static char **
178getcurrenttime(Param pm)
179{
180    char **arr;
181    char buf[DIGBUFSIZE];
182
183#ifdef HAVE_CLOCK_GETTIME
184    struct timespec now;
185
186    if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
187	zwarn("%s: unable to retrieve time: %e", pm->node.nam, errno);
188	return NULL;
189    }
190
191    arr = (char **)zhalloc(3 * sizeof(*arr));
192    sprintf(buf, "%ld", (long)now.tv_sec);
193    arr[0] = dupstring(buf);
194    sprintf(buf, "%ld", now.tv_nsec);
195    arr[1] = dupstring(buf);
196    arr[2] = NULL;
197
198    return arr;
199#else
200    struct timeval now;
201    struct timezone dummy_tz;
202
203    (void)pm;
204    gettimeofday(&now, &dummy_tz);
205
206    arr = (char **)zhalloc(3 * sizeof(*arr));
207    sprintf(buf, "%ld", (long)now.tv_sec);
208    arr[0] = dupstring(buf);
209    sprintf(buf, "%ld", (long)now.tv_usec * 1000);
210    arr[1] = dupstring(buf);
211    arr[2] = NULL;
212
213    return arr;
214#endif
215}
216
217static struct builtin bintab[] = {
218    BUILTIN("strftime",    0, bin_strftime,    2,   2, 0, "qrs:", NULL),
219};
220
221static const struct gsu_integer epochseconds_gsu =
222{ getcurrentsecs, NULL, stdunsetfn };
223
224static const struct gsu_float epochrealtime_gsu =
225{ getcurrentrealtime, NULL, stdunsetfn };
226
227static const struct gsu_array epochtime_gsu =
228{ getcurrenttime, NULL, stdunsetfn };
229
230static struct paramdef patab[] = {
231    SPECIALPMDEF("EPOCHSECONDS", PM_INTEGER|PM_READONLY,
232		 &epochseconds_gsu, NULL, NULL),
233    SPECIALPMDEF("EPOCHREALTIME", PM_FFLOAT|PM_READONLY,
234		 &epochrealtime_gsu, NULL, NULL),
235    SPECIALPMDEF("epochtime", PM_ARRAY|PM_READONLY,
236		 &epochtime_gsu, NULL, NULL)
237};
238
239static struct features module_features = {
240    bintab, sizeof(bintab)/sizeof(*bintab),
241    NULL, 0,
242    NULL, 0,
243    patab, sizeof(patab)/sizeof(*patab),
244    0
245};
246
247/**/
248int
249setup_(UNUSED(Module m))
250{
251    return 0;
252}
253
254/**/
255int
256features_(Module m, char ***features)
257{
258    *features = featuresarray(m, &module_features);
259    return 0;
260}
261
262/**/
263int
264enables_(Module m, int **enables)
265{
266    return handlefeatures(m, &module_features, enables);
267}
268
269/**/
270int
271boot_(Module m)
272{
273    return 0;
274}
275
276/**/
277int
278cleanup_(Module m)
279{
280    return setfeatureenables(m, &module_features, NULL);
281}
282
283/**/
284int
285finish_(UNUSED(Module m))
286{
287    return 0;
288}
289