tclcompat.tcl: minor comment updates
[jimtcl.git] / jim-clock.c
blobe1c2d76954d567d985463d44d87a0207d5ef7f76
1 /*
2 * jim-clock.c
4 * Implements the clock command
5 */
7 #include "jimautoconf.h"
9 /* For strptime() - currently nothing sets this */
10 #ifdef STRPTIME_NEEDS_XOPEN_SOURCE
11 #ifndef _XOPEN_SOURCE
12 #define _XOPEN_SOURCE 500
13 #endif
14 #endif
16 /* For timegm() */
17 #ifndef _GNU_SOURCE
18 #define _GNU_SOURCE
19 #endif
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <time.h>
26 #include <jim-subcmd.h>
28 #ifdef HAVE_SYS_TIME_H
29 #include <sys/time.h>
30 #endif
32 struct clock_options {
33 int gmt;
34 const char *format;
37 /* Parses the options ?-format string? ?-gmt boolean? and fills in *opts.
38 * Any options not present are not set.
39 * argc must be even.
41 * Returns JIM_OK or JIM_ERR and sets an error result.
43 static int parse_clock_options(Jim_Interp *interp, int argc, Jim_Obj *const *argv, struct clock_options *opts)
45 static const char * const options[] = { "-gmt", "-format", NULL };
46 enum { OPT_GMT, OPT_FORMAT, };
47 int i;
49 for (i = 0; i < argc; i += 2) {
50 int option;
51 if (Jim_GetEnum(interp, argv[i], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
52 return JIM_ERR;
54 switch (option) {
55 case OPT_GMT:
56 if (Jim_GetBoolean(interp, argv[i + 1], &opts->gmt) != JIM_OK) {
57 return JIM_ERR;
59 break;
60 case OPT_FORMAT:
61 opts->format = Jim_String(argv[i + 1]);
62 break;
65 return JIM_OK;
68 static int clock_cmd_format(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
70 /* How big is big enough? */
71 char buf[100];
72 time_t t;
73 jim_wide seconds;
74 struct clock_options options = { 0, "%a %b %d %H:%M:%S %Z %Y" };
75 struct tm *tm;
77 if (Jim_GetWide(interp, argv[0], &seconds) != JIM_OK) {
78 return JIM_ERR;
80 if (argc % 2 == 0) {
81 return -1;
83 if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
84 return JIM_ERR;
87 t = seconds;
88 tm = options.gmt ? gmtime(&t) : localtime(&t);
90 if (tm == NULL || strftime(buf, sizeof(buf), options.format, tm) == 0) {
91 Jim_SetResultString(interp, "format string too long or invalid time", -1);
92 return JIM_ERR;
95 Jim_SetResultString(interp, buf, -1);
97 return JIM_OK;
100 #ifdef HAVE_STRPTIME
101 #ifndef HAVE_TIMEGM
102 /* Implement a basic timegm() for system's that don't have it */
103 static time_t timegm(struct tm *tm)
105 time_t t;
106 const char *tz = getenv("TZ");
107 setenv("TZ", "", 1);
108 tzset();
109 t = mktime(tm);
110 if (tz) {
111 setenv("TZ", tz, 1);
113 else {
114 unsetenv("TZ");
116 tzset();
117 return t;
119 #endif
121 static int clock_cmd_scan(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
123 char *pt;
124 struct tm tm;
125 /*time_t now = time(NULL);*/
126 /* No default format */
127 struct clock_options options = { 0, NULL };
129 if (argc % 2 == 0) {
130 return -1;
133 if (parse_clock_options(interp, argc - 1, argv + 1, &options) == JIM_ERR) {
134 return JIM_ERR;
136 if (options.format == NULL) {
137 return -1;
140 /* Set unspecified fields to 0, e.g. HH:MM becomes 00:00 */
141 memset(&tm, 0, sizeof(tm));
142 /* But this is 1-based */
143 tm.tm_mday = 1;
145 pt = strptime(Jim_String(argv[0]), options.format, &tm);
146 if (pt == 0 || *pt != 0) {
147 Jim_SetResultString(interp, "Failed to parse time according to format", -1);
148 return JIM_ERR;
151 /* Now convert into a time_t */
152 Jim_SetResultInt(interp, options.gmt ? timegm(&tm) : mktime(&tm));
154 return JIM_OK;
156 #endif
158 static int clock_cmd_seconds(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
160 Jim_SetResultInt(interp, time(NULL));
162 return JIM_OK;
165 static int clock_cmd_micros(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
167 struct timeval tv;
169 gettimeofday(&tv, NULL);
171 Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000000 + tv.tv_usec);
173 return JIM_OK;
176 static int clock_cmd_millis(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
178 struct timeval tv;
180 gettimeofday(&tv, NULL);
182 Jim_SetResultInt(interp, (jim_wide) tv.tv_sec * 1000 + tv.tv_usec / 1000);
184 return JIM_OK;
187 static const jim_subcmd_type clock_command_table[] = {
188 { "clicks",
189 NULL,
190 clock_cmd_micros,
193 /* Description: Returns the current time in 'clicks' */
195 { "format",
196 "seconds ?-format string? ?-gmt boolean?",
197 clock_cmd_format,
200 /* Description: Format the given time */
202 { "microseconds",
203 NULL,
204 clock_cmd_micros,
207 /* Description: Returns the current time in microseconds */
209 { "milliseconds",
210 NULL,
211 clock_cmd_millis,
214 /* Description: Returns the current time in milliseconds */
216 #ifdef HAVE_STRPTIME
217 { "scan",
218 "str -format format ?-gmt boolean?",
219 clock_cmd_scan,
222 /* Description: Determine the time according to the given format */
224 #endif
225 { "seconds",
226 NULL,
227 clock_cmd_seconds,
230 /* Description: Returns the current time as seconds since the epoch */
232 { NULL }
235 int Jim_clockInit(Jim_Interp *interp)
237 if (Jim_PackageProvide(interp, "clock", "1.0", JIM_ERRMSG))
238 return JIM_ERR;
240 Jim_CreateCommand(interp, "clock", Jim_SubCmdProc, (void *)clock_command_table, NULL);
241 return JIM_OK;