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