mirror of
				git://git.code.sf.net/p/zsh/code
				synced 2025-10-31 06:00:54 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			315 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			315 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * datetime.c - parameter and command interface to date and time utilities
 | |
|  *
 | |
|  * This file is part of zsh, the Z shell.
 | |
|  *
 | |
|  * Copyright (c) 2002 Peter Stephenson, Clint Adams
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Permission is hereby granted, without written agreement and without
 | |
|  * license or royalty fees, to use, copy, modify, and distribute this
 | |
|  * software and to distribute modified versions of this software for any
 | |
|  * purpose, provided that the above copyright notice and the following
 | |
|  * two paragraphs appear in all copies of this software.
 | |
|  *
 | |
|  * In no event shall Peter Stephenson, Clint Adams or the Zsh Development Group
 | |
|  * be liable to any party for direct, indirect, special, incidental, or
 | |
|  * consequential damages arising out of the use of this software and its
 | |
|  * documentation, even if Peter Stephenson, Clint Adams and the Zsh
 | |
|  * Development Group have been advised of the possibility of such damage.
 | |
|  *
 | |
|  * Peter Stephenson, Clint Adams and the Zsh Development Group specifically
 | |
|  * disclaim any warranties, including, but not limited to, the implied
 | |
|  * warranties of merchantability and fitness for a particular purpose.
 | |
|  * The software provided hereunder is on an "as is" basis, and Peter
 | |
|  * Stephenson, Clint Adams and the Zsh Development Group have no obligation
 | |
|  * to provide maintenance, support, updates, enhancements, or modifications.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #ifdef __CYGWIN__
 | |
| #define _XOPEN_SOURCE
 | |
| #endif
 | |
| #include "datetime.mdh"
 | |
| #include "datetime.pro"
 | |
| #include <time.h>
 | |
| 
 | |
| #ifndef HAVE_MKTIME
 | |
| #ifdef HAVE_TIMELOCAL
 | |
| #define	mktime(x)	timelocal(x)
 | |
| #define HAVE_MKTIME	1
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| static int
 | |
| reverse_strftime(char *nam, char **argv, char *scalar, int quiet)
 | |
| {
 | |
| #if defined(HAVE_STRPTIME) && defined(HAVE_MKTIME)
 | |
|     struct tm tm;
 | |
|     zlong mytime;
 | |
|     char *endp;
 | |
| 
 | |
|     /*
 | |
|      * Initialise all parameters to zero; there's no floating point
 | |
|      * so memset() will do the trick.  The exception is that tm_isdst
 | |
|      * is set to -1 which, if not overridden, will cause mktime()
 | |
|      * to use the current timezone.  This is probably the best guess;
 | |
|      * it's the one that will cause dates and times output by strftime
 | |
|      * without the -r option and without an explicit timezone to be
 | |
|      * converted back correctly. Additionally, tm_mday is set to 1
 | |
|      * as that and not 0 corresponds to the first of the month.
 | |
|      */
 | |
|     (void)memset(&tm, 0, sizeof(tm));
 | |
|     tm.tm_isdst = -1;
 | |
|     tm.tm_mday = 1;
 | |
|     endp = strptime(argv[1], argv[0], &tm);
 | |
| 
 | |
|     if (!endp) {
 | |
| 	/* Conversion failed completely. */
 | |
| 	if (!quiet)
 | |
| 	    zwarnnam(nam, "format not matched");
 | |
| 	return 1;
 | |
|     }
 | |
| 
 | |
|     mytime = (zlong)mktime(&tm);
 | |
| 
 | |
|     if (scalar)
 | |
| 	setiparam(scalar, mytime);
 | |
|     else {
 | |
| 	char buf[DIGBUFSIZE];
 | |
| 	convbase(buf, mytime, 10);
 | |
| 	printf("%s\n", buf);
 | |
|     }
 | |
| 
 | |
|     if (*endp && !quiet) {
 | |
| 	/*
 | |
| 	 * Not everything in the input string was converted.
 | |
| 	 * This is probably benign, since the format has been satisfied,
 | |
| 	 * but issue a warning unless the quiet flag is set.
 | |
| 	 */
 | |
| 	zwarnnam(nam, "warning: input string not completely matched");
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| #else
 | |
|     if (!quiet)
 | |
| 	zwarnnam(nam, "not implemented on this system");
 | |
|     return 2;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static int
 | |
| output_strftime(char *nam, char **argv, Options ops, UNUSED(int func))
 | |
| {
 | |
|     int bufsize, x, len;
 | |
|     char *endptr = NULL, *scalar = NULL, *buffer;
 | |
|     time_t secs;
 | |
|     struct tm *t;
 | |
| 
 | |
|     if (OPT_ISSET(ops,'s')) {
 | |
| 	scalar = OPT_ARG(ops, 's');
 | |
| 	if (!isident(scalar)) {
 | |
| 	    zwarnnam(nam, "not an identifier: %s", scalar);
 | |
| 	    return 1;
 | |
| 	}
 | |
|     }
 | |
|     if (OPT_ISSET(ops, 'r'))
 | |
| 	return reverse_strftime(nam, argv, scalar, OPT_ISSET(ops, 'q'));
 | |
| 
 | |
|     errno = 0;
 | |
|     secs = (time_t)strtoul(argv[1], &endptr, 10);
 | |
|     if (errno != 0) {
 | |
| 	zwarnnam(nam, "%s: %e", argv[1], errno);
 | |
| 	return 1;
 | |
|     } else if (*endptr != '\0') {
 | |
| 	zwarnnam(nam, "%s: invalid decimal number", argv[1]);
 | |
| 	return 1;
 | |
|     }
 | |
| 
 | |
|     t = localtime(&secs);
 | |
|     if (!t) {
 | |
| 	zwarnnam(nam, "%s: unable to convert to time", argv[1]);
 | |
| 	return 1;
 | |
|     }
 | |
|     bufsize = strlen(argv[0]) * 8;
 | |
|     buffer = zalloc(bufsize);
 | |
| 
 | |
|     len = 0;
 | |
|     for (x=0; x < 4; x++) {
 | |
|         if ((len = ztrftime(buffer, bufsize, argv[0], t, 0L)) >= 0)
 | |
| 	    break;
 | |
| 	buffer = zrealloc(buffer, bufsize *= 2);
 | |
|     }
 | |
|     DPUTS(len < 0, "bad output from ztrftime");
 | |
| 
 | |
|     if (scalar) {
 | |
| 	setsparam(scalar, metafy(buffer, len, META_DUP));
 | |
|     } else {
 | |
| 	fwrite(buffer, 1, len, stdout);
 | |
| 	putchar('\n');
 | |
|     }
 | |
|     zfree(buffer, bufsize);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| bin_strftime(char *nam, char **argv, Options ops, int func)
 | |
| {
 | |
|     int result = 1;
 | |
|     char *tz = getsparam("TZ");
 | |
| 
 | |
|     startparamscope();
 | |
|     if (tz && *tz) {
 | |
| 	Param pm = createparam("TZ", PM_LOCAL|PM_SCALAR|PM_EXPORTED);
 | |
| 	if (pm)
 | |
| 	    pm->level = locallevel; /* because createparam() doesn't */
 | |
| 	setsparam("TZ", ztrdup(tz));
 | |
|     }
 | |
|     result = output_strftime(nam, argv, ops, func);
 | |
|     endparamscope();
 | |
| 
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| static zlong
 | |
| getcurrentsecs(UNUSED(Param pm))
 | |
| {
 | |
|     return (zlong) time(NULL);
 | |
| }
 | |
| 
 | |
| static double
 | |
| getcurrentrealtime(Param pm)
 | |
| {
 | |
| #ifdef HAVE_CLOCK_GETTIME
 | |
|     struct timespec now;
 | |
| 
 | |
|     if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
 | |
| 	zwarn("%s: unable to retrieve time: %e", pm->node.nam, errno);
 | |
| 	return (double)0.0;
 | |
|     }
 | |
| 
 | |
|     return (double)now.tv_sec + (double)now.tv_nsec * 1e-9;
 | |
| #else
 | |
|     struct timeval now;
 | |
|     struct timezone dummy_tz;
 | |
| 
 | |
|     (void)pm;
 | |
|     gettimeofday(&now, &dummy_tz);
 | |
| 
 | |
|     return (double)now.tv_sec + (double)now.tv_usec * 1e-6;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static char **
 | |
| getcurrenttime(Param pm)
 | |
| {
 | |
|     char **arr;
 | |
|     char buf[DIGBUFSIZE];
 | |
| 
 | |
| #ifdef HAVE_CLOCK_GETTIME
 | |
|     struct timespec now;
 | |
| 
 | |
|     if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
 | |
| 	zwarn("%s: unable to retrieve time: %e", pm->node.nam, errno);
 | |
| 	return NULL;
 | |
|     }
 | |
| 
 | |
|     arr = (char **)zhalloc(3 * sizeof(*arr));
 | |
|     sprintf(buf, "%ld", (long)now.tv_sec);
 | |
|     arr[0] = dupstring(buf);
 | |
|     sprintf(buf, "%ld", now.tv_nsec);
 | |
|     arr[1] = dupstring(buf);
 | |
|     arr[2] = NULL;
 | |
| 
 | |
|     return arr;
 | |
| #else
 | |
|     struct timeval now;
 | |
|     struct timezone dummy_tz;
 | |
| 
 | |
|     (void)pm;
 | |
|     gettimeofday(&now, &dummy_tz);
 | |
| 
 | |
|     arr = (char **)zhalloc(3 * sizeof(*arr));
 | |
|     sprintf(buf, "%ld", (long)now.tv_sec);
 | |
|     arr[0] = dupstring(buf);
 | |
|     sprintf(buf, "%ld", (long)now.tv_usec * 1000);
 | |
|     arr[1] = dupstring(buf);
 | |
|     arr[2] = NULL;
 | |
| 
 | |
|     return arr;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static struct builtin bintab[] = {
 | |
|     BUILTIN("strftime",    0, bin_strftime,    2,   2, 0, "qrs:", NULL),
 | |
| };
 | |
| 
 | |
| static const struct gsu_integer epochseconds_gsu =
 | |
| { getcurrentsecs, NULL, stdunsetfn };
 | |
| 
 | |
| static const struct gsu_float epochrealtime_gsu =
 | |
| { getcurrentrealtime, NULL, stdunsetfn };
 | |
| 
 | |
| static const struct gsu_array epochtime_gsu =
 | |
| { getcurrenttime, NULL, stdunsetfn };
 | |
| 
 | |
| static struct paramdef patab[] = {
 | |
|     SPECIALPMDEF("EPOCHSECONDS", PM_INTEGER|PM_READONLY,
 | |
| 		 &epochseconds_gsu, NULL, NULL),
 | |
|     SPECIALPMDEF("EPOCHREALTIME", PM_FFLOAT|PM_READONLY,
 | |
| 		 &epochrealtime_gsu, NULL, NULL),
 | |
|     SPECIALPMDEF("epochtime", PM_ARRAY|PM_READONLY,
 | |
| 		 &epochtime_gsu, NULL, NULL)
 | |
| };
 | |
| 
 | |
| static struct features module_features = {
 | |
|     bintab, sizeof(bintab)/sizeof(*bintab),
 | |
|     NULL, 0,
 | |
|     NULL, 0,
 | |
|     patab, sizeof(patab)/sizeof(*patab),
 | |
|     0
 | |
| };
 | |
| 
 | |
| /**/
 | |
| int
 | |
| setup_(UNUSED(Module m))
 | |
| {
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| features_(Module m, char ***features)
 | |
| {
 | |
|     *features = featuresarray(m, &module_features);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| enables_(Module m, int **enables)
 | |
| {
 | |
|     return handlefeatures(m, &module_features, enables);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| boot_(UNUSED(Module m))
 | |
| {
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| cleanup_(Module m)
 | |
| {
 | |
|     return setfeatureenables(m, &module_features, NULL);
 | |
| }
 | |
| 
 | |
| /**/
 | |
| int
 | |
| finish_(UNUSED(Module m))
 | |
| {
 | |
|     return 0;
 | |
| }
 |