date.c revision 99109
1139969Simp/*
21556Srgrimes * Copyright (c) 1985, 1987, 1988, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * Redistribution and use in source and binary forms, with or without
61556Srgrimes * modification, are permitted provided that the following conditions
71556Srgrimes * are met:
81556Srgrimes * 1. Redistributions of source code must retain the above copyright
91556Srgrimes *    notice, this list of conditions and the following disclaimer.
101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111556Srgrimes *    notice, this list of conditions and the following disclaimer in the
121556Srgrimes *    documentation and/or other materials provided with the distribution.
131556Srgrimes * 3. All advertising materials mentioning features or use of this software
141556Srgrimes *    must display the following acknowledgement:
151556Srgrimes *	This product includes software developed by the University of
161556Srgrimes *	California, Berkeley and its contributors.
171556Srgrimes * 4. Neither the name of the University nor the names of its contributors
181556Srgrimes *    may be used to endorse or promote products derived from this software
191556Srgrimes *    without specific prior written permission.
201556Srgrimes *
211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311556Srgrimes * SUCH DAMAGE.
321556Srgrimes */
3390153Smarkm
341556Srgrimes#ifndef lint
3527967Sstevestatic char const copyright[] =
3690153Smarkm"@(#) Copyright (c) 1985, 1987, 1988, 1993\n\
3727967Ssteve	The Regents of the University of California.  All rights reserved.\n";
3899109Sobrien#endif /* not lint */
3999109Sobrien
401556Srgrimes#ifndef lint
411556Srgrimes#if 0
421556Srgrimesstatic char sccsid[] = "@(#)date.c	8.2 (Berkeley) 4/28/95";
43106371Stjr#endif
441556Srgrimes#endif /* not lint */
451556Srgrimes#include <sys/cdefs.h>
461556Srgrimes__FBSDID("$FreeBSD: head/bin/date/date.c 99109 2002-06-30 05:13:54Z obrien $");
471556Srgrimes
4874566Sache#include <sys/param.h>
49129719Spjd#include <sys/time.h>
501556Srgrimes
51225847Sed#include <ctype.h>
521556Srgrimes#include <err.h>
531556Srgrimes#include <locale.h>
5491212Sbde#include <libutil.h>
551556Srgrimes#include <stdio.h>
5661294Sache#include <stdlib.h>
5761294Sache#include <string.h>
5861294Sache#include <syslog.h>
5961294Sache#include <unistd.h>
6061294Sache
611556Srgrimes#include "extern.h"
621556Srgrimes#include "vary.h"
631556Srgrimes
641556Srgrimes#ifndef	TM_YEAR_BASE
65114583Smarkm#define	TM_YEAR_BASE	1900
66202945Sjh#endif
67105780Smarkm
6890110Simptime_t tval;
6990110Simpint retval;
7090110Simp
7161321Sachestatic void setthetime(const char *, const char *, int, int);
7290110Simpstatic void badformat(void);
7390110Simpstatic void usage(void);
7461321Sache
75196712Straszint
761556Srgrimesmain(int argc, char *argv[])
771556Srgrimes{
781556Srgrimes	struct timezone tz;
7961268Sjoe	int ch, rflag;
8061178Sjoe	int jflag, nflag;
8161178Sjoe	const char *format;
8288602Sjoe	char buf[1024];
8388602Sjoe	char *endptr, *fmt;
8488602Sjoe	char *tmp;
8588602Sjoe	int set_timezone;
8688602Sjoe	struct vary *v;
8788602Sjoe	const struct vary *badv;
8888602Sjoe	struct tm lt;
8988602Sjoe
9088602Sjoe	v = NULL;
9188602Sjoe	fmt = NULL;
9288602Sjoe	(void) setlocale(LC_TIME, "");
9388602Sjoe	tz.tz_dsttime = tz.tz_minuteswest = 0;
9488602Sjoe	rflag = 0;
9588602Sjoe	jflag = nflag = 0;
9688586Sjoe	set_timezone = 0;
9761178Sjoe	while ((ch = getopt(argc, argv, "d:f:jnr:t:uv:")) != -1)
9890150Smarkm		switch((char)ch) {
9961178Sjoe		case 'd':		/* daylight savings time */
10088583Sjoe			tz.tz_dsttime = strtol(optarg, &endptr, 10) ? 1 : 0;
10188583Sjoe			if (endptr == optarg || *endptr != '\0')
10288586Sjoe				usage();
10388586Sjoe			set_timezone = 1;
10488583Sjoe			break;
10561268Sjoe		case 'f':
10661178Sjoe			fmt = optarg;
1071556Srgrimes			break;
108114583Smarkm		case 'j':
1091556Srgrimes			jflag = 1;	/* don't set time */
11088594Sjoe			break;
1111556Srgrimes		case 'n':		/* don't set network */
1121556Srgrimes			nflag = 1;
1131556Srgrimes			break;
1141556Srgrimes		case 'r':		/* user specified seconds */
1151556Srgrimes			rflag = 1;
1161556Srgrimes			tval = strtoq(optarg, &tmp, 0);
1171556Srgrimes			if (*tmp != 0)
1181556Srgrimes				usage();
1191556Srgrimes			break;
12062597Sassar		case 't':		/* minutes west of UTC */
12162597Sassar					/* error check; don't allow "PST" */
12262597Sassar			tz.tz_minuteswest = strtol(optarg, &endptr, 10);
123105390Stjr			if (endptr == optarg || *endptr != '\0')
12490110Simp				usage();
12562597Sassar			set_timezone = 1;
12662597Sassar			break;
12762597Sassar		case 'u':		/* do everything in UTC */
12862597Sassar			(void)setenv("TZ", "UTC0", 1);
12962597Sassar			break;
13062597Sassar		case 'v':
131128823Stjr			v = vary_append(v, optarg);
13262597Sassar			break;
13362597Sassar		default:
1341556Srgrimes			usage();
135114583Smarkm		}
1361556Srgrimes	argc -= optind;
1371556Srgrimes	argv += optind;
13888594Sjoe
13988594Sjoe	/*
14088594Sjoe	 * If -d or -t, set the timezone or daylight savings time; this
14161292Sache	 * doesn't belong here; the kernel should not know about either.
14288594Sjoe	 */
14361292Sache	if (set_timezone && settimeofday((struct timeval *)NULL, &tz))
1441556Srgrimes		err(1, "settimeofday (timezone)");
145130236Sdas
146130236Sdas	if (!rflag && time(&tval) == -1)
1471556Srgrimes		err(1, "time");
148130236Sdas
1491556Srgrimes	format = "%+";
1501556Srgrimes
1511556Srgrimes	/* allow the operands in any order */
1521556Srgrimes	if (*argv && **argv == '+') {
1531556Srgrimes		format = *argv + 1;
1541556Srgrimes		++argv;
155241014Smdf	}
156241014Smdf
1571556Srgrimes	if (*argv) {
158114583Smarkm		setthetime(fmt, *argv, jflag, nflag);
1591556Srgrimes		++argv;
16090150Smarkm	} else if (fmt != NULL)
161196712Strasz		usage();
1621556Srgrimes
163177942Simp	if (*argv && **argv == '+')
1641556Srgrimes		format = *argv + 1;
1651556Srgrimes
1661556Srgrimes	lt = *localtime(&tval);
1671556Srgrimes	badv = vary_apply(v, &lt);
168105832Srwatson	if (badv) {
169105832Srwatson		fprintf(stderr, "%s: Cannot apply date adjustment\n",
1701556Srgrimes			badv->arg);
171202945Sjh		vary_destroy(v);
1721556Srgrimes		usage();
17388591Sjoe	}
1741556Srgrimes	vary_destroy(v);
1751556Srgrimes	(void)strftime(buf, sizeof(buf), format, &lt);
176157098Sjhb	(void)printf("%s\n", buf);
177157098Sjhb	exit(retval);
1781556Srgrimes}
1791556Srgrimes
1801556Srgrimes#define	ATOI2(s)	((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
1811556Srgrimes
18261268Sjoevoid
18361178Sjoesetthetime(const char *fmt, const char *p, int jflag, int nflag)
18461291Sache{
18561268Sjoe	struct tm *lt;
18662597Sassar	struct timeval tv;
18761268Sjoe	const char *dot, *t;
18861291Sache	int century;
18961321Sache
19061268Sjoe	if (fmt != NULL) {
1911556Srgrimes		lt = localtime(&tval);
1921556Srgrimes		t = strptime(p, fmt, lt);
1931556Srgrimes		if (t == NULL) {
1941556Srgrimes			fprintf(stderr, "Failed conversion of ``%s''"
1951556Srgrimes				" using format ``%s''\n", p, fmt);
1961556Srgrimes			badformat();
1971556Srgrimes		} else if (*t != '\0')
1981556Srgrimes			fprintf(stderr, "Warning: Ignoring %ld extraneous"
1991556Srgrimes				" characters in date string (%s)\n",
200114583Smarkm				(long) strlen(t), t);
20196892Stjr	} else {
20296892Stjr		for (t = p, dot = NULL; *t; ++t) {
20396892Stjr			if (isdigit(*t))
20496892Stjr				continue;
20596892Stjr			if (*t == '.' && dot == NULL) {
20696892Stjr				dot = t;
20796892Stjr				continue;
208128823Stjr			}
20996892Stjr			badformat();
21096892Stjr		}
21196892Stjr
21296892Stjr		lt = localtime(&tval);
21396892Stjr
21496892Stjr		if (dot != NULL) {			/* .ss */
21596892Stjr			dot++; /* *dot++ = '\0'; */
21696892Stjr			if (strlen(dot) != 2)
21796892Stjr				badformat();
21896892Stjr			lt->tm_sec = ATOI2(dot);
21996892Stjr			if (lt->tm_sec > 61)
22096892Stjr				badformat();
22196892Stjr		} else
22296892Stjr			lt->tm_sec = 0;
223177907Sgrog
22496892Stjr		century = 0;
225114583Smarkm		/* if p has a ".ss" field then let's pretend it's not there */
2261556Srgrimes		switch (strlen(p) - ((dot != NULL) ? 3 : 0)) {
2271556Srgrimes		case 12:				/* cc */
2281556Srgrimes			lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE;
22988594Sjoe			century = 1;
230121124Stjr			/* FALLTHROUGH */
23188594Sjoe		case 10:				/* yy */
23288594Sjoe			if (century)
23388594Sjoe				lt->tm_year += ATOI2(p);
23488594Sjoe			else {				/* hack for 2000 ;-} */
23588594Sjoe				lt->tm_year = ATOI2(p);
23688594Sjoe				if (lt->tm_year < 69)
23788594Sjoe					lt->tm_year += 2000 - TM_YEAR_BASE;
23888594Sjoe				else
23988594Sjoe					lt->tm_year += 1900 - TM_YEAR_BASE;
24088594Sjoe			}
24188594Sjoe			/* FALLTHROUGH */
2421556Srgrimes		case 8:					/* mm */
24337932Shoek			lt->tm_mon = ATOI2(p);
24437932Shoek			if (lt->tm_mon > 12)
24537932Shoek				badformat();
24637932Shoek			--lt->tm_mon;		/* time struct is 0 - 11 */
24737932Shoek			/* FALLTHROUGH */
2481556Srgrimes		case 6:					/* dd */
2491556Srgrimes			lt->tm_mday = ATOI2(p);
2501556Srgrimes			if (lt->tm_mday > 31)
2511556Srgrimes				badformat();
2521556Srgrimes			/* FALLTHROUGH */
253121124Stjr		case 4:					/* HH */
2541556Srgrimes			lt->tm_hour = ATOI2(p);
2551556Srgrimes			if (lt->tm_hour > 23)
2561556Srgrimes				badformat();
257121124Stjr			/* FALLTHROUGH */
2581556Srgrimes		case 2:					/* MM */
259121124Stjr			lt->tm_min = ATOI2(p);
260121124Stjr			if (lt->tm_min > 59)
2611556Srgrimes				badformat();
2621556Srgrimes			break;
2631556Srgrimes		default:
2641556Srgrimes			badformat();
2651556Srgrimes		}
2661556Srgrimes	}
2671556Srgrimes
2681556Srgrimes	/* Let mktime() decide whether summer time is in effect. */
2691556Srgrimes	lt->tm_isdst = -1;
2701556Srgrimes
2711556Srgrimes	/* convert broken-down time to GMT clock time */
2721556Srgrimes	if ((tval = mktime(lt)) == -1)
2731556Srgrimes		errx(1, "nonexistent time");
27437932Shoek
2751556Srgrimes	if (!jflag) {
2761556Srgrimes		/* set the time */
2771556Srgrimes		if (nflag || netsettime(tval)) {
2781556Srgrimes			logwtmp("|", "date", "");
2791556Srgrimes			tv.tv_sec = tval;
2801556Srgrimes			tv.tv_usec = 0;
2811556Srgrimes			if (settimeofday(&tv, (struct timezone *)NULL))
2821556Srgrimes				err(1, "settimeofday (timeval)");
2831556Srgrimes			logwtmp("{", "date", "");
284130236Sdas		}
285130236Sdas
2861556Srgrimes		if ((p = getlogin()) == NULL)
287130236Sdas			p = "???";
28896892Stjr		syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p);
289102577Skeramida	}
2901556Srgrimes}
2911556Srgrimes
29296892Stjrstatic void
29396892Stjrbadformat(void)
29496892Stjr{
2951556Srgrimes	warnx("illegal time format");
2961556Srgrimes	usage();
29796892Stjr}
29896892Stjr
29996892Stjrstatic void
30096892Stjrusage(void)
30196892Stjr{
3021556Srgrimes	(void)fprintf(stderr, "%s\n%s\n",
30337932Shoek	    "usage: date [-jnu] [-d dst] [-r seconds] [-t west] "
30488595Sjoe	    "[-v[+|-]val[ymwdHMS]] ... ",
30596892Stjr	    "            "
30696892Stjr	    "[-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format]");
30737932Shoek	exit(1);
3081556Srgrimes}
3091556Srgrimes