xref: /openbsd-src/bin/date/date.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: date.c,v 1.17 2001/03/26 15:22:16 millert Exp $	*/
2 /*	$NetBSD: date.c,v 1.11 1995/09/07 06:21:05 jtc Exp $	*/
3 
4 /*
5  * Copyright (c) 1985, 1987, 1988, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 static char copyright[] =
39 "@(#) Copyright (c) 1985, 1987, 1988, 1993\n\
40 	The Regents of the University of California.  All rights reserved.\n";
41 #endif /* not lint */
42 
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)date.c	8.2 (Berkeley) 4/28/95";
46 #else
47 static char rcsid[] = "$OpenBSD: date.c,v 1.17 2001/03/26 15:22:16 millert Exp $";
48 #endif
49 #endif /* not lint */
50 
51 #include <sys/param.h>
52 #include <sys/time.h>
53 
54 #include <ctype.h>
55 #include <err.h>
56 #include <fcntl.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <locale.h>
61 #include <syslog.h>
62 #include <time.h>
63 #include <tzfile.h>
64 #include <unistd.h>
65 #include <util.h>
66 
67 #include "extern.h"
68 
69 time_t tval;
70 int retval, nflag;
71 
72 static void setthetime __P((char *));
73 static void badformat __P((void));
74 static void usage __P((void));
75 
76 int
77 main(argc, argv)
78 	int argc;
79 	char **argv;
80 {
81 	struct timezone tz;
82 	int ch, rflag;
83 	char *format, buf[1024];
84 
85 	setlocale(LC_ALL, "");
86 
87 	tz.tz_dsttime = tz.tz_minuteswest = 0;
88 	rflag = 0;
89 	while ((ch = getopt(argc, argv, "d:nr:ut:")) != -1)
90 		switch((char)ch) {
91 		case 'd':		/* daylight saving time */
92 			tz.tz_dsttime = atoi(optarg) ? 1 : 0;
93 			break;
94 		case 'n':		/* don't set network */
95 			nflag = 1;
96 			break;
97 		case 'r':		/* user specified seconds */
98 			rflag = 1;
99 			tval = atol(optarg);
100 			break;
101 		case 'u':		/* do everything in UTC */
102 			if (setenv("TZ", "GMT0", 1) == -1)
103 				err(1, "cannot unsetenv TZ");
104 			break;
105 		case 't':		/* minutes west of GMT */
106 					/* error check; don't allow "PST" */
107 			if (isdigit(*optarg)) {
108 				tz.tz_minuteswest = atoi(optarg);
109 				break;
110 			}
111 			/* FALLTHROUGH */
112 		default:
113 			usage();
114 		}
115 	argc -= optind;
116 	argv += optind;
117 
118 	/*
119 	 * If -d or -t, set the timezone or daylight saving time; this
120 	 * doesn't belong here, the kernel should not know about either.
121 	 */
122 	if ((tz.tz_minuteswest || tz.tz_dsttime) &&
123 	    settimeofday(NULL, &tz))
124 		err(1, "settimeofday");
125 
126 	if (!rflag && time(&tval) == -1)
127 		err(1, "time");
128 
129 	format = "%a %b %e %H:%M:%S %Z %Y";
130 
131 	/* allow the operands in any order */
132 	if (*argv && **argv == '+') {
133 		format = *argv + 1;
134 		++argv;
135 	}
136 
137 	if (*argv) {
138 		setthetime(*argv);
139 		++argv;
140 	}
141 
142 	if (*argv && **argv == '+')
143 		format = *argv + 1;
144 
145 	(void)strftime(buf, sizeof(buf), format, localtime(&tval));
146 	(void)printf("%s\n", buf);
147 	exit(retval);
148 }
149 
150 #define	ATOI2(ar)	((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
151 void
152 setthetime(p)
153 	register char *p;
154 {
155 	register struct tm *lt;
156 	struct timeval tv;
157 	char *dot, *t;
158 	int bigyear;
159 	int yearset = 0;
160 
161 	for (t = p, dot = NULL; *t; ++t) {
162 		if (isdigit(*t))
163 			continue;
164 		if (*t == '.' && dot == NULL) {
165 			dot = t;
166 			continue;
167 		}
168 		badformat();
169 	}
170 
171 	lt = localtime(&tval);
172 
173 	lt->tm_isdst = -1;			/* correct for DST */
174 
175 	if (dot != NULL) {			/* .SS */
176 		*dot++ = '\0';
177 		if (strlen(dot) != 2)
178 			badformat();
179 		lt->tm_sec = ATOI2(dot);
180 		if (lt->tm_sec > 61)
181 			badformat();
182 	} else
183 		lt->tm_sec = 0;
184 
185 	switch (strlen(p)) {
186 	case 12:				/* cc */
187 		bigyear = ATOI2(p);
188 		lt->tm_year = bigyear * 100 - TM_YEAR_BASE;
189 		yearset = 1;
190 		/* FALLTHROUGH */
191 	case 10:				/* yy */
192 		if (yearset) {
193 			lt->tm_year += ATOI2(p);
194 		} else {
195 			lt->tm_year = ATOI2(p);
196 			if (lt->tm_year < 69)		/* hack for 2000 ;-} */
197 				lt->tm_year += (2000 - TM_YEAR_BASE);
198 			else
199 				lt->tm_year += (1900 - TM_YEAR_BASE);
200 		}
201 		/* FALLTHROUGH */
202 	case 8:					/* mm */
203 		lt->tm_mon = ATOI2(p);
204 		if ((lt->tm_mon > 12) || !lt->tm_mon)
205 			badformat();
206 		--lt->tm_mon;			/* time struct is 0 - 11 */
207 		/* FALLTHROUGH */
208 	case 6:					/* dd */
209 		lt->tm_mday = ATOI2(p);
210 		if ((lt->tm_mday > 31) || !lt->tm_mday)
211 			badformat();
212 		/* FALLTHROUGH */
213 	case 4:					/* HH */
214 		lt->tm_hour = ATOI2(p);
215 		if (lt->tm_hour > 23)
216 			badformat();
217 		/* FALLTHROUGH */
218 	case 2:					/* MM */
219 		lt->tm_min = ATOI2(p);
220 		if (lt->tm_min > 59)
221 			badformat();
222 		break;
223 	default:
224 		badformat();
225 	}
226 
227 	/* convert broken-down time to UTC clock time */
228 	if ((tval = mktime(lt)) < 0)
229 		errx(1, "specified date is outside allowed range");
230 
231 	/* set the time */
232 	if (nflag || netsettime(tval)) {
233 		logwtmp("|", "date", "");
234 		tv.tv_sec = tval;
235 		tv.tv_usec = 0;
236 		if (settimeofday(&tv, NULL))
237 			errx(1, "settimeofday");
238 		logwtmp("{", "date", "");
239 	}
240 
241 	if ((p = getlogin()) == NULL)
242 		p = "???";
243 	syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p);
244 }
245 
246 static void
247 badformat()
248 {
249 	warnx("illegal time format");
250 	usage();
251 }
252 
253 static void
254 usage()
255 {
256 	(void)fprintf(stderr,
257 	    "usage: date [-nu] [-d dst] [-r seconds] [-t west] [+format]\n");
258 	(void)fprintf(stderr, "            [[[[[[cc]yy]mm]dd]HH]MM[.SS]]\n");
259 	exit(1);
260 }
261