980 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			980 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #ifndef lint
 | |
| #ifndef NOID
 | |
| static char	elsieid[] = "@(#)date.c	7.45";
 | |
| /*
 | |
| ** Modified from the UCB version with the SCCS ID appearing below.
 | |
| */
 | |
| #endif /* !defined NOID */
 | |
| #endif /* !defined lint */
 | |
| 
 | |
| /*
 | |
|  * Copyright (c) 1985, 1987, 1988 The Regents of the University of California.
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms are permitted
 | |
|  * provided that the above copyright notice and this paragraph are
 | |
|  * duplicated in all such forms and that any documentation,
 | |
|  * advertising materials, and other materials related to such
 | |
|  * distribution and use acknowledge that the software was developed
 | |
|  * by the University of California, Berkeley.  The name of the
 | |
|  * University may not be used to endorse or promote products derived
 | |
|  * from this software without specific prior written permission.
 | |
|  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 | |
|  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 | |
|  * WARRANTIES OF MERCHANT[A]BILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 | |
|  */
 | |
| 
 | |
| #include <fcntl.h>	/* for O_WRONLY, O_APPEND */
 | |
| 
 | |
| #ifndef lint
 | |
| char copyright[] =
 | |
| "@(#) Copyright (c) 1985, 1987, 1988 The Regents of the University of California.\n\
 | |
|  All rights reserved.\n";
 | |
| #endif /* not lint */
 | |
| 
 | |
| #ifndef lint
 | |
| static char sccsid[] = "@(#)date.c	4.23 (Berkeley) 9/20/88";
 | |
| #endif /* not lint */
 | |
| 
 | |
| #include "private.h"
 | |
| #if HAVE_ADJTIME || HAVE_SETTIMEOFDAY
 | |
| #include "sys/time.h"	/* for struct timeval, struct timezone */
 | |
| #endif /* HAVE_ADJTIME || HAVE_SETTIMEOFDAY */
 | |
| #include "locale.h"
 | |
| #include "utmp.h"	/* for OLD_TIME (or its absence) */
 | |
| #if HAVE_UTMPX_H
 | |
| #include "utmpx.h"
 | |
| #endif
 | |
| 
 | |
| #ifndef OTIME_MSG
 | |
| #define OTIME_MSG "old time"
 | |
| #endif
 | |
| #ifndef NTIME_MSG
 | |
| #define NTIME_MSG "new time"
 | |
| #endif
 | |
| 
 | |
| /*
 | |
| ** The two things date knows about time are. . .
 | |
| */
 | |
| 
 | |
| #ifndef TM_YEAR_BASE
 | |
| #define TM_YEAR_BASE	1900
 | |
| #endif /* !defined TM_YEAR_BASE */
 | |
| 
 | |
| #ifndef SECSPERMIN
 | |
| #define SECSPERMIN	60
 | |
| #endif /* !defined SECSPERMIN */
 | |
| 
 | |
| extern char **		environ;
 | |
| #if 0
 | |
| extern double		atof();
 | |
| extern char *		getlogin();
 | |
| extern time_t		mktime();
 | |
| extern char *		optarg;
 | |
| extern int		optind;
 | |
| extern char *		strchr();
 | |
| extern time_t		time();
 | |
| extern char *		tzname[2];
 | |
| #endif
 | |
| 
 | |
| static int		retval = EXIT_SUCCESS;
 | |
| 
 | |
| static void		checkfinal P((const char *, int, time_t, time_t));
 | |
| static int		comptm P((const struct tm *, const struct tm *));
 | |
| static time_t		convert P((const char *, int, time_t));
 | |
| static void		display P((const char *));
 | |
| static void		dogmt P((void));
 | |
| static void		errensure P((void));
 | |
| static void		iffy P((time_t, time_t, const char *, const char *));
 | |
| int			main P((int, char**));
 | |
| static const char *	nondigit P((const char *));
 | |
| static void		oops P((const char *));
 | |
| static void		reset P((time_t, int));
 | |
| static void		timeout P((FILE *, const char *, const struct tm *));
 | |
| static void		usage P((void));
 | |
| static void		wildinput P((const char *, const char *,
 | |
| 				const char *));
 | |
| 
 | |
| int
 | |
| main(argc, argv)
 | |
| const int	argc;
 | |
| char *		argv[];
 | |
| {
 | |
| 	register const char *	format;
 | |
| 	register const char *	value;
 | |
| 	register const char *	cp;
 | |
| 	register int		ch;
 | |
| 	register int		dousg;
 | |
| 	register int		aflag = 0;
 | |
| 	register int		dflag = 0;
 | |
| 	register int		nflag = 0;
 | |
| 	register int		tflag = 0;
 | |
| 	register int		minuteswest;
 | |
| 	register int		dsttime;
 | |
| 	register double		adjust;
 | |
| 	time_t			now;
 | |
| 	time_t			t;
 | |
| 
 | |
| 	INITIALIZE(dousg);
 | |
| 	INITIALIZE(minuteswest);
 | |
| 	INITIALIZE(dsttime);
 | |
| 	INITIALIZE(adjust);
 | |
| 	INITIALIZE(t);
 | |
| #ifdef LC_ALL
 | |
| 	(void) setlocale(LC_ALL, "");
 | |
| #endif /* defined(LC_ALL) */
 | |
| #if HAVE_GETTEXT
 | |
| #ifdef TZ_DOMAINDIR
 | |
| 	(void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
 | |
| #endif /* defined(TEXTDOMAINDIR) */
 | |
| 	(void) textdomain(TZ_DOMAIN);
 | |
| #endif /* HAVE_GETTEXT */
 | |
| 	(void) time(&now);
 | |
| 	format = value = NULL;
 | |
| 	while ((ch = getopt(argc, argv, "ucnd:t:a:")) != EOF && ch != -1) {
 | |
| 		switch (ch) {
 | |
| 		default:
 | |
| 			usage();
 | |
| 		case 'u':		/* do it in UTC */
 | |
| 		case 'c':
 | |
| 			dogmt();
 | |
| 			break;
 | |
| 		case 'n':		/* don't set network */
 | |
| 			nflag = 1;
 | |
| 			break;
 | |
| 		case 'd':		/* daylight saving time */
 | |
| 			if (dflag) {
 | |
| 				(void) fprintf(stderr,
 | |
| 					_("date: error: multiple -d's used"));
 | |
| 				usage();
 | |
| 			}
 | |
| 			dflag = 1;
 | |
| 			cp = optarg;
 | |
| 			dsttime = atoi(cp);
 | |
| 			if (*cp == '\0' || *nondigit(cp) != '\0')
 | |
| 				wildinput(_("-t value"), optarg,
 | |
| 					_("must be a non-negative number"));
 | |
| 			break;
 | |
| 		case 't':		/* minutes west of UTC */
 | |
| 			if (tflag) {
 | |
| 				(void) fprintf(stderr,
 | |
| 					_("date: error: multiple -t's used"));
 | |
| 				usage();
 | |
| 			}
 | |
| 			tflag = 1;
 | |
| 			cp = optarg;
 | |
| 			minuteswest = atoi(cp);
 | |
| 			if (*cp == '+' || *cp == '-')
 | |
| 				++cp;
 | |
| 			if (*cp == '\0' || *nondigit(cp) != '\0')
 | |
| 				wildinput(_("-d value"), optarg,
 | |
| 					_("must be a number"));
 | |
| 			break;
 | |
| 		case 'a':		/* adjustment */
 | |
| 			if (aflag) {
 | |
| 				(void) fprintf(stderr,
 | |
| 					_("date: error: multiple -a's used"));
 | |
| 				usage();
 | |
| 			}
 | |
| 			aflag = 1;
 | |
| 			cp = optarg;
 | |
| 			adjust = atof(cp);
 | |
| 			if (*cp == '+' || *cp == '-')
 | |
| 				++cp;
 | |
| 			if (*cp == '\0' || strcmp(cp, ".") == 0)
 | |
| 				wildinput(_("-a value"), optarg,
 | |
| 					_("must be a number"));
 | |
| 			cp = nondigit(cp);
 | |
| 			if (*cp == '.')
 | |
| 				++cp;
 | |
| 			if (*nondigit(cp) != '\0')
 | |
| 				wildinput(_("-a value"), optarg,
 | |
| 					_("must be a number"));
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	while (optind < argc) {
 | |
| 		cp = argv[optind++];
 | |
| 		if (*cp == '+')
 | |
| 			if (format == NULL)
 | |
| 				format = cp + 1;
 | |
| 			else {
 | |
| 				(void) fprintf(stderr,
 | |
| _("date: error: multiple formats in command line\n"));
 | |
| 				usage();
 | |
| 			}
 | |
| 		else	if (value == NULL)
 | |
| 				value = cp;
 | |
| 			else {
 | |
| 				(void) fprintf(stderr,
 | |
| _("date: error: multiple values in command line\n"));
 | |
| 				usage();
 | |
| 			}
 | |
| 	}
 | |
| 	if (value != NULL) {
 | |
| 		/*
 | |
| 		** This order ensures that "reasonable" twelve-digit inputs
 | |
| 		** (such as 120203042006) won't be misinterpreted
 | |
| 		** even if time_t's range all the way back to the thirteenth
 | |
| 		** century.  Do not change the order.
 | |
| 		*/
 | |
| 		t = convert(value, (dousg = TRUE), now);
 | |
| 		if (t == -1)
 | |
| 			t = convert(value, (dousg = FALSE), now);
 | |
| 		if (t == -1) {
 | |
| 			/*
 | |
| 			** Out of range values,
 | |
| 			** or time that falls in a DST transition hole?
 | |
| 			*/
 | |
| 			if ((cp = strchr(value, '.')) != NULL) {
 | |
| 				/*
 | |
| 				** Ensure that the failure of
 | |
| 				**	TZ=America/New_York date 8712312359.60
 | |
| 				** doesn't get misdiagnosed.  (It was
 | |
| 				**	TZ=America/New_York date 8712311859.60
 | |
| 				** when the leap second was inserted.)
 | |
| 				** The normal check won't work since
 | |
| 				** the given time is valid in UTC.
 | |
| 				*/
 | |
| 				if (atoi(cp + 1) >= SECSPERMIN)
 | |
| 					wildinput(_("time"), value,
 | |
| 					    _("out of range seconds given"));
 | |
| 			}
 | |
| 			dogmt();
 | |
| 			t = convert(value, FALSE, now);
 | |
| 			if (t == -1)
 | |
| 				t = convert(value, TRUE, now);
 | |
| 			wildinput(_("time"), value,
 | |
| 				(t == -1) ?
 | |
| 				_("out of range value given") :
 | |
| 				_("time skipped when clock springs forward"));
 | |
| 		}
 | |
| 	}
 | |
| 	/*
 | |
| 	** Entire command line has now been checked.
 | |
| 	*/
 | |
| 	if (aflag) {
 | |
| #if HAVE_ADJTIME
 | |
| 		struct timeval	tv;
 | |
| 
 | |
| 		tv.tv_sec = (int) adjust;
 | |
| 		tv.tv_usec = (int) ((adjust - tv.tv_sec) * 1000000L);
 | |
| 		if (adjtime(&tv, (struct timeval *) NULL) != 0)
 | |
| 			oops("adjtime");
 | |
| #endif /* HAVE_ADJTIME */
 | |
| #if !HAVE_ADJTIME
 | |
| 		reset((time_t) (now + adjust), nflag);
 | |
| #endif /* !HAVE_ADJTIME */
 | |
| 		/*
 | |
| 		** Sun silently ignores everything else; we follow suit.
 | |
| 		*/
 | |
| 		exit(retval);
 | |
| 	}
 | |
| 	if (dflag || tflag) {
 | |
| #if HAVE_SETTIMEOFDAY == 2
 | |
| 		struct timezone	tz;
 | |
| 
 | |
| 		if (!dflag || !tflag)
 | |
| 			if (gettimeofday((struct timeval *) NULL, &tz) != 0)
 | |
| 				oops("gettimeofday");
 | |
| 		if (dflag)
 | |
| 			tz.tz_dsttime = dsttime;
 | |
| 		if (tflag)
 | |
| 			tz.tz_minuteswest = minuteswest;
 | |
| 		if (settimeofday((struct timeval *) NULL, &tz) != 0)
 | |
| 			oops("settimeofday");
 | |
| #endif /* HAVE_SETTIMEOFDAY == 2 */
 | |
| #if HAVE_SETTIMEOFDAY != 2
 | |
| 		(void) fprintf(stderr,
 | |
| _("date: warning: kernel doesn't keep -d/-t information, option ignored\n"));
 | |
| #endif /* HAVE_SETTIMEOFDAY != 2 */
 | |
| 	}
 | |
| 
 | |
| 	if (value == NULL)
 | |
| 		display(format);
 | |
| 
 | |
| 	reset(t, nflag);
 | |
| 
 | |
| 	checkfinal(value, dousg, t, now);
 | |
| 
 | |
| #ifdef EBUG
 | |
| 	{
 | |
| 		struct tm	tm;
 | |
| 
 | |
| 		tm = *localtime(&t);
 | |
| 		timeout(stdout, "%c\n", &tm);
 | |
| 		exit(retval);
 | |
| 	}
 | |
| #endif /* defined EBUG */
 | |
| 
 | |
| 	display(format);
 | |
| 
 | |
| 	/* gcc -Wall pacifier */
 | |
| 	for ( ; ; )
 | |
| 		continue;
 | |
| }
 | |
| 
 | |
| static void
 | |
| dogmt()
 | |
| {
 | |
| 	static char **	fakeenv;
 | |
| 
 | |
| 	if (fakeenv == NULL) {
 | |
| 		register int	from;
 | |
| 		register int	to;
 | |
| 		register int	n;
 | |
| 		static char	tzegmt0[] = "TZ=GMT0";
 | |
| 
 | |
| 		for (n = 0;  environ[n] != NULL;  ++n)
 | |
| 			continue;
 | |
| 		fakeenv = (char **) malloc((size_t) (n + 2) * sizeof *fakeenv);
 | |
| 		if (fakeenv == NULL) {
 | |
| 			(void) perror(_("Memory exhausted"));
 | |
| 			errensure();
 | |
| 			exit(retval);
 | |
| 		}
 | |
| 		to = 0;
 | |
| 		fakeenv[to++] = tzegmt0;
 | |
| 		for (from = 1; environ[from] != NULL; ++from)
 | |
| 			if (strncmp(environ[from], "TZ=", 3) != 0)
 | |
| 				fakeenv[to++] = environ[from];
 | |
| 		fakeenv[to] = NULL;
 | |
| 		environ = fakeenv;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| #ifdef OLD_TIME
 | |
| 
 | |
| /*
 | |
| ** We assume we're on a System-V-based system,
 | |
| ** should use stime,
 | |
| ** should write System-V-format utmp entries,
 | |
| ** and don't have network notification to worry about.
 | |
| */
 | |
| 
 | |
| /*ARGSUSED*/
 | |
| static void
 | |
| #if __STDC__
 | |
| reset(const time_t newt, const int nflag)
 | |
| #else /* !__STDC__ */
 | |
| reset(newt, nflag)
 | |
| const time_t	newt;
 | |
| const int	nflag;
 | |
| #endif /* !__STDC__ */
 | |
| {
 | |
| 	register int		fid;
 | |
| 	time_t			oldt;
 | |
| 	static struct {
 | |
| 		struct utmp	before;
 | |
| 		struct utmp	after;
 | |
| 	} s;
 | |
| #if HAVE_UTMPX_H
 | |
| 	static struct {
 | |
| 		struct utmpx	before;
 | |
| 		struct utmpx	after;
 | |
| 	} sx;
 | |
| #endif
 | |
| 
 | |
| 	/*
 | |
| 	** Wouldn't it be great if stime returned the old time?
 | |
| 	*/
 | |
| 	(void) time(&oldt);
 | |
| 	if (stime(&newt) != 0)
 | |
| 		oops("stime");
 | |
| 	s.before.ut_type = OLD_TIME;
 | |
| 	s.before.ut_time = oldt;
 | |
| 	(void) strcpy(s.before.ut_line, OTIME_MSG);
 | |
| 	s.after.ut_type = NEW_TIME;
 | |
| 	s.after.ut_time = newt;
 | |
| 	(void) strcpy(s.after.ut_line, NTIME_MSG);
 | |
| 	fid = open(WTMP_FILE, O_WRONLY | O_APPEND);
 | |
| 	if (fid < 0)
 | |
| 		oops(_("log file open"));
 | |
| 	if (write(fid, (char *) &s, sizeof s) != sizeof s)
 | |
| 		oops(_("log file write"));
 | |
| 	if (close(fid) != 0)
 | |
| 		oops(_("log file close"));
 | |
| #if !HAVE_UTMPX_H
 | |
| 	pututline(&s.before);
 | |
| 	pututline(&s.after);
 | |
| #endif /* !HAVE_UTMPX_H */
 | |
| #if HAVE_UTMPX_H
 | |
| 	sx.before.ut_type = OLD_TIME;
 | |
| 	sx.before.ut_tv.tv_sec = oldt;
 | |
| 	(void) strcpy(sx.before.ut_line, OTIME_MSG);
 | |
| 	sx.after.ut_type = NEW_TIME;
 | |
| 	sx.after.ut_tv.tv_sec = newt;
 | |
| 	(void) strcpy(sx.after.ut_line, NTIME_MSG);
 | |
| #if !SUPPRESS_WTMPX_FILE_UPDATE
 | |
| 	/* In Solaris 2.5 (and presumably other systems),
 | |
| 	   `date' does not update /var/adm/wtmpx.
 | |
| 	   This must be a bug.  If you'd like to reproduce the bug,
 | |
| 	   define SUPPRESS_WTMPX_FILE_UPDATE to be nonzero.  */
 | |
| 	fid = open(WTMPX_FILE, O_WRONLY | O_APPEND);
 | |
| 	if (fid < 0)
 | |
| 		oops(_("log file open"));
 | |
| 	if (write(fid, (char *) &sx, sizeof sx) != sizeof sx)
 | |
| 		oops(_("log file write"));
 | |
| 	if (close(fid) != 0)
 | |
| 		oops(_("log file close"));
 | |
| #endif /* !SUPPRESS_WTMPX_FILE_UPDATE */
 | |
| 	pututxline(&sx.before);
 | |
| 	pututxline(&sx.after);
 | |
| #endif /* HAVE_UTMPX_H */
 | |
| }
 | |
| 
 | |
| #endif /* defined OLD_TIME */
 | |
| #ifndef OLD_TIME
 | |
| 
 | |
| /*
 | |
| ** We assume we're on a BSD-based system,
 | |
| ** should use settimeofday,
 | |
| ** should write BSD-format utmp entries (using logwtmp),
 | |
| ** and may get to worry about network notification.
 | |
| ** The "time name" changes between 4.3-tahoe and 4.4;
 | |
| ** we include sys/param.h to determine which we should use.
 | |
| */
 | |
| 
 | |
| #ifndef TIME_NAME
 | |
| #include "sys/param.h"
 | |
| #ifdef BSD4_4
 | |
| #define TIME_NAME	"date"
 | |
| #endif /* defined BSD4_4 */
 | |
| #ifndef BSD4_4
 | |
| #define TIME_NAME	""
 | |
| #endif /* !defined BSD4_4 */
 | |
| #endif /* !defined TIME_NAME */
 | |
| 
 | |
| #include "syslog.h"
 | |
| #include "sys/socket.h"
 | |
| #include "netinet/in.h"
 | |
| #include "netdb.h"
 | |
| #define TSPTYPES
 | |
| 
 | |
| #if 0
 | |
| extern int		logwtmp();
 | |
| #endif
 | |
| 
 | |
| #if HAVE_SETTIMEOFDAY == 1
 | |
| #define settimeofday(t, tz) (settimeofday)(t)
 | |
| #endif /* HAVE_SETTIMEOFDAY == 1 */
 | |
| 
 | |
| #ifndef TSP_SETDATE
 | |
| /*ARGSUSED*/
 | |
| #endif /* !defined TSP_SETDATE */
 | |
| static void
 | |
| reset(newt, nflag)
 | |
| const time_t	newt;
 | |
| const int	nflag;
 | |
| {
 | |
| 	register const char *	username;
 | |
| 	static struct timeval	tv;	/* static so tv_usec is 0 */
 | |
| 
 | |
| #ifdef EBUG
 | |
| 	return;
 | |
| #endif /* defined EBUG */
 | |
| 	username = getlogin();
 | |
| 	if (username == NULL || *username == '\0') /* single-user or no tty */
 | |
| 		username = "root";
 | |
| 	tv.tv_sec = newt;
 | |
| #ifdef TSP_SETDATE
 | |
| 	if (nflag || !netsettime(tv))
 | |
| #endif /* defined TSP_SETDATE */
 | |
| 	{
 | |
| 		/*
 | |
| 		** "old" entry is always written, for compatibility.
 | |
| 		*/
 | |
| 		logwtmp("|", TIME_NAME, "");
 | |
| 		if (settimeofday(&tv, (struct timezone *) NULL) == 0) {
 | |
| 			logwtmp("{", TIME_NAME, "");	/* } */
 | |
| 			syslog(LOG_AUTH | LOG_NOTICE, _("date set by %s"),
 | |
| 				username);
 | |
| 		} else	oops("settimeofday");
 | |
| 	}
 | |
| }
 | |
| 
 | |
| #endif /* !defined OLD_TIME */
 | |
| 
 | |
| static void
 | |
| wildinput(item, value, reason)
 | |
| const char * const	item;
 | |
| const char * const	value;
 | |
| const char * const	reason;
 | |
| {
 | |
| 	(void) fprintf(stderr,
 | |
| 		_("date: error: bad command line %s \"%s\", %s\n"),
 | |
| 		item, value, reason);
 | |
| 	usage();
 | |
| }
 | |
| 
 | |
| static void
 | |
| errensure P((void))
 | |
| {
 | |
| 	if (retval == EXIT_SUCCESS)
 | |
| 		retval = EXIT_FAILURE;
 | |
| }
 | |
| 
 | |
| static const char *
 | |
| nondigit(cp)
 | |
| register const char *	cp;
 | |
| {
 | |
| 	while (is_digit(*cp))
 | |
| 		++cp;
 | |
| 	return cp;
 | |
| }
 | |
| 
 | |
| static void
 | |
| usage P((void))
 | |
| {
 | |
| 	(void) fprintf(stderr, _("date: usage is date [-u] [-c] [-n] [-d dst] \
 | |
| [-t min-west] [-a sss.fff] [[yyyy]mmddhhmm[yyyy][.ss]] [+format]\n"));
 | |
| 	errensure();
 | |
| 	exit(retval);
 | |
| }
 | |
| 
 | |
| static void
 | |
| oops(string)
 | |
| const char * const	string;
 | |
| {
 | |
| 	int		e = errno;
 | |
| 
 | |
| 	(void) fprintf(stderr, _("date: error: "));
 | |
| 	errno = e;
 | |
| 	(void) perror(string);
 | |
| 	errensure();
 | |
| 	display((char *) NULL);
 | |
| }
 | |
| 
 | |
| static void
 | |
| display(format)
 | |
| const char * const	format;
 | |
| {
 | |
| 	struct tm	tm;
 | |
| 	time_t		now;
 | |
| 
 | |
| 	(void) time(&now);
 | |
| 	tm = *localtime(&now);
 | |
| 	timeout(stdout, format ? format : "%+", &tm);
 | |
| 	(void) putchar('\n');
 | |
| 	(void) fflush(stdout);
 | |
| 	(void) fflush(stderr);
 | |
| 	if (ferror(stdout) || ferror(stderr)) {
 | |
| 		(void) fprintf(stderr,
 | |
| 			_("date: error: couldn't write results\n"));
 | |
| 		errensure();
 | |
| 	}
 | |
| 	exit(retval);
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| extern size_t	strftime();
 | |
| #endif
 | |
| 
 | |
| #define INCR	1024
 | |
| 
 | |
| static void
 | |
| timeout(fp, format, tmp)
 | |
| FILE * const		fp;
 | |
| const char * const	format;
 | |
| const struct tm * const	tmp;
 | |
| {
 | |
| 	char *	cp;
 | |
| 	size_t	result;
 | |
| 	size_t	size;
 | |
| 
 | |
| 	if (*format == '\0')
 | |
| 		return;
 | |
| 	size = INCR;
 | |
| 	cp = malloc((size_t) size);
 | |
| 	for ( ; ; ) {
 | |
| 		if (cp == NULL) {
 | |
| 			(void) fprintf(stderr,
 | |
| 				_("date: error: can't get memory\n"));
 | |
| 			errensure();
 | |
| 			exit(retval);
 | |
| 		}
 | |
| 		cp[0] = '\1';
 | |
| 		result = strftime(cp, size, format, tmp);
 | |
| 		if (result != 0 || cp[0] == '\0')
 | |
| 			break;
 | |
| 		size += INCR;
 | |
| 		cp = realloc(cp, (size_t) size);
 | |
| 	}
 | |
| 	(void) fwrite(cp, 1, result, fp);
 | |
| 	free(cp);
 | |
| }
 | |
| 
 | |
| static int
 | |
| comptm(atmp, btmp)
 | |
| register const struct tm * const atmp;
 | |
| register const struct tm * const btmp;
 | |
| {
 | |
| 	register int	result;
 | |
| 
 | |
| 	if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
 | |
| 		(result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
 | |
| 		(result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
 | |
| 		(result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
 | |
| 		(result = (atmp->tm_min - btmp->tm_min)) == 0)
 | |
| 			result = atmp->tm_sec - btmp->tm_sec;
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| /*
 | |
| ** convert --
 | |
| **	convert user's input into a time_t.
 | |
| */
 | |
| 
 | |
| #define ATOI2(ar)	(ar[0] - '0') * 10 + (ar[1] - '0'); ar += 2;
 | |
| 
 | |
| static time_t
 | |
| #if __STDC__
 | |
| convert(register const char * const value, const int dousg, const time_t t)
 | |
| #else /* !__STDC__ */
 | |
| convert(value, dousg, t)
 | |
| register const char * const	value;
 | |
| const int			dousg;
 | |
| const time_t			t;
 | |
| #endif /* !__STDC__ */
 | |
| {
 | |
| 	register const char *	cp;
 | |
| 	register const char *	dotp;
 | |
| 	register int	cent, year_in_cent, month, hour, day, mins, secs;
 | |
| 	struct tm	tm, outtm;
 | |
| 	time_t		outt;
 | |
| 
 | |
| 	tm = *localtime(&t);
 | |
| #define DIVISOR	100
 | |
| 	year_in_cent = tm.tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
 | |
| 	cent = tm.tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
 | |
| 		year_in_cent / DIVISOR;
 | |
| 	year_in_cent %= DIVISOR;
 | |
| 	if (year_in_cent < 0) {
 | |
| 		year_in_cent += DIVISOR;
 | |
| 		--cent;
 | |
| 	}
 | |
| 	month = tm.tm_mon + 1;
 | |
| 	day = tm.tm_mday;
 | |
| 	hour = tm.tm_hour;
 | |
| 	mins = tm.tm_min;
 | |
| 	secs = 0;
 | |
| 
 | |
| 	dotp = strchr(value, '.');
 | |
| 	for (cp = value; *cp != '\0'; ++cp)
 | |
| 		if (!is_digit(*cp) && cp != dotp)
 | |
| 			wildinput(_("time"), value, _("contains a nondigit"));
 | |
| 
 | |
| 	if (dotp == NULL)
 | |
| 		dotp = strchr(value, '\0');
 | |
| 	else {
 | |
| 		cp = dotp + 1;
 | |
| 		if (strlen(cp) != 2)
 | |
| 			wildinput(_("time"), value,
 | |
| 				_("seconds part is not two digits"));
 | |
| 		secs = ATOI2(cp);
 | |
| 	}
 | |
| 
 | |
| 	cp = value;
 | |
| 	switch (dotp - cp) {
 | |
| 		default:
 | |
| 			wildinput(_("time"), value,
 | |
| 				_("main part is wrong length"));
 | |
| 		case 12:
 | |
| 			if (!dousg) {
 | |
| 				cent = ATOI2(cp);
 | |
| 				year_in_cent = ATOI2(cp);
 | |
| 			}
 | |
| 			month = ATOI2(cp);
 | |
| 			day = ATOI2(cp);
 | |
| 			hour = ATOI2(cp);
 | |
| 			mins = ATOI2(cp);
 | |
| 			if (dousg) {
 | |
| 				cent = ATOI2(cp);
 | |
| 				year_in_cent = ATOI2(cp);
 | |
| 			}
 | |
| 			break;
 | |
| 		case 8:	/* mmddhhmm */
 | |
| 			month = ATOI2(cp);
 | |
| 			/* fall through to. . . */
 | |
| 		case 6:	/* ddhhmm */
 | |
| 			day = ATOI2(cp);
 | |
| 			/* fall through to. . . */
 | |
| 		case 4:	/* hhmm */
 | |
| 			hour = ATOI2(cp);
 | |
| 			mins = ATOI2(cp);
 | |
| 			break;
 | |
| 		case 10:
 | |
| 			if (!dousg) {
 | |
| 				year_in_cent = ATOI2(cp);
 | |
| 			}
 | |
| 			month = ATOI2(cp);
 | |
| 			day = ATOI2(cp);
 | |
| 			hour = ATOI2(cp);
 | |
| 			mins = ATOI2(cp);
 | |
| 			if (dousg) {
 | |
| 				year_in_cent = ATOI2(cp);
 | |
| 			}
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	tm.tm_year = cent * 100 + year_in_cent - TM_YEAR_BASE;
 | |
| 	tm.tm_mon = month - 1;
 | |
| 	tm.tm_mday = day;
 | |
| 	tm.tm_hour = hour;
 | |
| 	tm.tm_min = mins;
 | |
| 	tm.tm_sec = secs;
 | |
| 	tm.tm_isdst = -1;
 | |
| 	outtm = tm;
 | |
| 	outt = mktime(&outtm);
 | |
| 	return (comptm(&tm, &outtm) == 0) ? outt : -1;
 | |
| }
 | |
| 
 | |
| /*
 | |
| ** Code from here on out is either based on code provided by UCB
 | |
| ** or is only called just before the program exits.
 | |
| */
 | |
| 
 | |
| /*
 | |
| ** Check for iffy input.
 | |
| */
 | |
| 
 | |
| static void
 | |
| #if __STDC__
 | |
| checkfinal(const char * const	value,
 | |
| 	   const int		didusg,
 | |
| 	   const time_t		t,
 | |
| 	   const time_t		oldnow)
 | |
| #else /* !__STDC__ */
 | |
| checkfinal(value, didusg, t, oldnow)
 | |
| const char * const	value;
 | |
| const int		didusg;
 | |
| const time_t		t;
 | |
| const time_t		oldnow;
 | |
| #endif /* !__STDC__ */
 | |
| {
 | |
| 	time_t		othert;
 | |
| 	struct tm	tm;
 | |
| 	struct tm	othertm;
 | |
| 	register int	pass;
 | |
| 	register long	offset;
 | |
| 
 | |
| 	/*
 | |
| 	** See if there's both a USG and a BSD interpretation.
 | |
| 	*/
 | |
| 	othert = convert(value, !didusg, oldnow);
 | |
| 	if (othert != -1 && othert != t)
 | |
| 		iffy(t, othert, value, _("year could be at start or end"));
 | |
| 	/*
 | |
| 	** See if there's both a DST and a STD version.
 | |
| 	*/
 | |
| 	tm = *localtime(&t);
 | |
| 	othertm = tm;
 | |
| 	othertm.tm_isdst = !tm.tm_isdst;
 | |
| 	othert = mktime(&othertm);
 | |
| 	if (othert != -1 && othertm.tm_isdst != tm.tm_isdst &&
 | |
| 		comptm(&tm, &othertm) == 0)
 | |
| 			iffy(t, othert, value,
 | |
| 			    _("both standard and summer time versions exist"));
 | |
| /*
 | |
| ** Final check.
 | |
| **
 | |
| ** If a jurisdiction shifts time *without* shifting whether time is
 | |
| ** summer or standard (as Hawaii, the United Kingdom, and Saudi Arabia
 | |
| ** have done), routine checks for iffy times may not work.
 | |
| ** So we perform this final check, deferring it until after the time has
 | |
| ** been set--it may take a while, and we don't want to introduce an unnecessary
 | |
| ** lag between the time the user enters their command and the time that
 | |
| ** stime/settimeofday is called.
 | |
| **
 | |
| ** We just check nearby times to see if any have the same representation
 | |
| ** as the time that convert returned.  We work our way out from the center
 | |
| ** for quick response in solar time situations.  We only handle common cases--
 | |
| ** offsets of at most a minute, and offsets of exact numbers of minutes
 | |
| ** and at most an hour.
 | |
| */
 | |
| 	for (offset = 1; offset <= 60; ++offset)
 | |
| 		for (pass = 1; pass <= 4; ++pass) {
 | |
| 			if (pass == 1)
 | |
| 				othert = t + offset;
 | |
| 			else if (pass == 2)
 | |
| 				othert = t - offset;
 | |
| 			else if (pass == 3)
 | |
| 				othert = t + 60 * offset;
 | |
| 			else	othert = t - 60 * offset;
 | |
| 			othertm = *localtime(&othert);
 | |
| 			if (comptm(&tm, &othertm) == 0)
 | |
| 				iffy(t, othert, value,
 | |
| 					_("multiple matching times exist"));
 | |
| 		}
 | |
| }
 | |
| 
 | |
| static void
 | |
| #if __STDC__
 | |
| iffy(const time_t thist, const time_t thatt,
 | |
| 	const char * const value, const char * const reason)
 | |
| #else /* !__STDC__ */
 | |
| iffy(thist, thatt, value, reason)
 | |
| const time_t		thist;
 | |
| const time_t		thatt;
 | |
| const char * const	value;
 | |
| const char * const	reason;
 | |
| #endif /* !__STDC__ */
 | |
| {
 | |
| 	struct tm	tm;
 | |
| 
 | |
| 	(void) fprintf(stderr, _("date: warning: ambiguous time \"%s\", %s.\n"),
 | |
| 		value, reason);
 | |
| 	tm = *gmtime(&thist);
 | |
| 	/*
 | |
| 	** Avoid running afoul of SCCS!
 | |
| 	*/
 | |
| 	timeout(stderr, _("Time was set as if you used\n\tdate -u %m%d%H\
 | |
| %M\
 | |
| %Y.%S\n"), &tm);
 | |
| 	tm = *localtime(&thist);
 | |
| 	timeout(stderr, _("to get %c"), &tm);
 | |
| 	(void) fprintf(stderr, _(" (%s).  Use\n"),
 | |
| 		tm.tm_isdst ? _("summer time") : _("standard time"));
 | |
| 	tm = *gmtime(&thatt);
 | |
| 	timeout(stderr, _("\tdate -u %m%d%H\
 | |
| %M\
 | |
| %Y.%S\n"), &tm);
 | |
| 	tm = *localtime(&thatt);
 | |
| 	timeout(stderr, _("to get %c"), &tm);
 | |
| 	(void) fprintf(stderr, _(" (%s).\n"),
 | |
| 		tm.tm_isdst ? _("summer time") : _("standard time"));
 | |
| 	errensure();
 | |
| 	exit(retval);
 | |
| }
 | |
| 
 | |
| #ifdef TSP_SETDATE
 | |
| #define WAITACK		2	/* seconds */
 | |
| #define WAITDATEACK	5	/* seconds */
 | |
| 
 | |
| /*
 | |
|  * Set the date in the machines controlled by timedaemons
 | |
|  * by communicating the new date to the local timedaemon.
 | |
|  * If the timedaemon is in the master state, it performs the
 | |
|  * correction on all slaves.  If it is in the slave state, it
 | |
|  * notifies the master that a correction is needed.
 | |
|  * Returns 1 on success, 0 on failure.
 | |
|  */
 | |
| netsettime(ntv)
 | |
| 	struct timeval ntv;
 | |
| {
 | |
| 	int s, length, port, timed_ack, found, err;
 | |
| 	long waittime;
 | |
| 	fd_set ready;
 | |
| 	char hostname[MAXHOSTNAMELEN];
 | |
| 	struct timeval tout;
 | |
| 	struct servent *sp;
 | |
| 	struct tsp msg;
 | |
| 	struct sockaddr_in sin, dest, from;
 | |
| 
 | |
| 	sp = getservbyname("timed", "udp");
 | |
| 	if (sp == 0) {
 | |
| 		fputs(_("udp/timed: unknown service\n"), stderr);
 | |
| 		retval = 2;
 | |
| 		return (0);
 | |
| 	}
 | |
| 	dest.sin_port = sp->s_port;
 | |
| 	dest.sin_family = AF_INET;
 | |
| 	dest.sin_addr.s_addr = htonl((u_long)INADDR_ANY);
 | |
| 	s = socket(AF_INET, SOCK_DGRAM, 0);
 | |
| 	if (s < 0) {
 | |
| 		if (errno != EPROTONOSUPPORT)
 | |
| 			perror("date: socket");
 | |
| 		goto bad;
 | |
| 	}
 | |
| 	bzero((char *)&sin, sizeof (sin));
 | |
| 	sin.sin_family = AF_INET;
 | |
| 	for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
 | |
| 		sin.sin_port = htons((u_short)port);
 | |
| 		if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) >= 0)
 | |
| 			break;
 | |
| 		if (errno != EADDRINUSE) {
 | |
| 			if (errno != EADDRNOTAVAIL)
 | |
| 				perror("date: bind");
 | |
| 			goto bad;
 | |
| 		}
 | |
| 	}
 | |
| 	if (port == IPPORT_RESERVED / 2) {
 | |
| 		fputs(_("date: All ports in use\n"), stderr);
 | |
| 		goto bad;
 | |
| 	}
 | |
| 	msg.tsp_type = TSP_SETDATE;
 | |
| 	msg.tsp_vers = TSPVERSION;
 | |
| 	if (gethostname(hostname, sizeof (hostname))) {
 | |
| 		perror("gethostname");
 | |
| 		goto bad;
 | |
| 	}
 | |
| 	(void) strncpy(msg.tsp_name, hostname, sizeof (hostname));
 | |
| 	msg.tsp_seq = htons((u_short)0);
 | |
| 	msg.tsp_time.tv_sec = htonl((u_long)ntv.tv_sec);
 | |
| 	msg.tsp_time.tv_usec = htonl((u_long)ntv.tv_usec);
 | |
| 	length = sizeof (struct sockaddr_in);
 | |
| 	if (connect(s, &dest, length) < 0) {
 | |
| 		perror("date: connect");
 | |
| 		goto bad;
 | |
| 	}
 | |
| 	if (send(s, (char *)&msg, sizeof (struct tsp), 0) < 0) {
 | |
| 		if (errno != ECONNREFUSED)
 | |
| 			perror("date: send");
 | |
| 		goto bad;
 | |
| 	}
 | |
| 	timed_ack = -1;
 | |
| 	waittime = WAITACK;
 | |
| loop:
 | |
| 	tout.tv_sec = waittime;
 | |
| 	tout.tv_usec = 0;
 | |
| 	FD_ZERO(&ready);
 | |
| 	FD_SET(s, &ready);
 | |
| 	found = select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout);
 | |
| 	length = sizeof err;
 | |
| 	if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)&err, &length) == 0
 | |
| 	    && err) {
 | |
| 		errno = err;
 | |
| 		if (errno != ECONNREFUSED)
 | |
| 			perror(_("date: send (delayed error)"));
 | |
| 		goto bad;
 | |
| 	}
 | |
| 	if (found > 0 && FD_ISSET(s, &ready)) {
 | |
| 		length = sizeof (struct sockaddr_in);
 | |
| 		if (recvfrom(s, (char *)&msg, sizeof (struct tsp), 0, &from,
 | |
| 		    &length) < 0) {
 | |
| 			if (errno != ECONNREFUSED)
 | |
| 				perror("date: recvfrom");
 | |
| 			goto bad;
 | |
| 		}
 | |
| 		msg.tsp_seq = ntohs(msg.tsp_seq);
 | |
| 		msg.tsp_time.tv_sec = ntohl(msg.tsp_time.tv_sec);
 | |
| 		msg.tsp_time.tv_usec = ntohl(msg.tsp_time.tv_usec);
 | |
| 		switch (msg.tsp_type) {
 | |
| 
 | |
| 		case TSP_ACK:
 | |
| 			timed_ack = TSP_ACK;
 | |
| 			waittime = WAITDATEACK;
 | |
| 			goto loop;
 | |
| 
 | |
| 		case TSP_DATEACK:
 | |
| 			(void)close(s);
 | |
| 			return (1);
 | |
| 
 | |
| 		default:
 | |
| 			fprintf(stderr,
 | |
| 				_("date: Wrong ack received from timed: %s\n"),
 | |
| 				tsptype[msg.tsp_type]);
 | |
| 			timed_ack = -1;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	if (timed_ack == -1)
 | |
| 		fputs(_("date: Can't reach time daemon, time set locally.\n"),
 | |
| 			stderr);
 | |
| bad:
 | |
| 	(void)close(s);
 | |
| 	retval = 2;
 | |
| 	return (0);
 | |
| }
 | |
| #endif /* defined TSP_SETDATE */
 | 
