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