1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26 /*
27 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
28 */
29 /*
30 * Copyright (c) 2017, Joyent, Inc.
31 */
32 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
33 /* All Rights Reserved */
34
35 /*
36 * University Copyright- Copyright (c) 1982, 1986, 1988
37 * The Regents of the University of California
38 * All Rights Reserved
39 *
40 * University Acknowledgment- Portions of this document are derived from
41 * software developed by the University of California, Berkeley, and its
42 * contributors.
43 */
44
45 /*
46 * date - with format capabilities and international flair
47 */
48
49 #include <locale.h>
50 #include <fcntl.h>
51 #include <langinfo.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <time.h>
56 #include <unistd.h>
57 #include <sys/time.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <ctype.h>
61 #include <errno.h>
62 #include <utmpx.h>
63 #include <tzfile.h>
64
65 #define year_size(A) ((isleap(A)) ? 366 : 365)
66 static char buf[BUFSIZ];
67 static time_t clock_val;
68 static short month_size[12] =
69 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
70 static struct utmpx wtmpx[2] = {
71 {"", "", OTIME_MSG, 0, OLD_TIME, 0, 0, 0},
72 {"", "", NTIME_MSG, 0, NEW_TIME, 0, 0, 0}
73 };
74 static char *usage =
75 "usage:\tdate [-u] mmddHHMM[[cc]yy][.SS]\n"
76 "\tdate [-Ru] [-r seconds | filename] [+format]\n"
77 "\tdate -a [-]sss[.fff]\n";
78 static int uflag = 0;
79 static int Rflag = 0;
80 static int rflag = 0;
81
82 static int get_adj(char *, struct timeval *);
83 static int setdate(struct tm *, char *);
84 static void fmt_extensions(char *, size_t,
85 const char *, const struct timespec *);
86
87 int
main(int argc,char ** argv)88 main(int argc, char **argv)
89 {
90 struct tm *tp, tm;
91 struct timeval tv;
92 char *fmt, *eptr;
93 char fmtbuf[BUFSIZ];
94 int c, aflag = 0, illflag = 0;
95 struct timespec ts;
96
97 (void) setlocale(LC_ALL, "");
98
99 #if !defined(TEXT_DOMAIN)
100 #define TEXT_DOMAIN "SYS_TEST"
101 #endif
102 (void) textdomain(TEXT_DOMAIN);
103
104 while ((c = getopt(argc, argv, "a:uRr:")) != EOF)
105 switch (c) {
106 case 'a':
107 aflag++;
108 if (get_adj(optarg, &tv) < 0) {
109 (void) fprintf(stderr,
110 gettext("date: invalid argument -- %s\n"),
111 optarg);
112 illflag++;
113 }
114 break;
115 case 'u':
116 uflag++;
117 break;
118 case 'R':
119 Rflag++;
120 break;
121 case 'r':
122
123 /*
124 * BSD originally used -r to specify a unix time. GNU
125 * used -r to specify a reference to a file. Now, like
126 * some BSDs we attempt to parse the time. If we can,
127 * then we use that, otherwise we fall back and treat it
128 * like GNU.
129 */
130 rflag++;
131 errno = 0;
132 ts.tv_sec = strtol(optarg, &eptr, 0);
133 if (errno == EINVAL || *eptr != '\0') {
134 struct stat st;
135 if (stat(optarg, &st) == 0) {
136 ts.tv_sec = st.st_mtime;
137 } else {
138 (void) fprintf(stderr,
139 gettext("date: failed to get stat "
140 "information about %s: %s\n"),
141 optarg, strerror(errno));
142 exit(1);
143 }
144 } else if (errno != 0) {
145 (void) fprintf(stderr,
146 gettext("date: failed to parse -r "
147 "argument: %s\n"), optarg);
148 exit(1);
149 }
150 break;
151 default:
152 illflag++;
153 }
154
155 argc -= optind;
156 argv = &argv[optind];
157
158 /* -a is mutually exclusive with -u, -R, and -r */
159 if (uflag && aflag)
160 illflag++;
161 if (Rflag && aflag)
162 illflag++;
163 if (rflag && aflag)
164 illflag++;
165
166 if (illflag) {
167 (void) fprintf(stderr, gettext(usage));
168 exit(1);
169 }
170
171 if (rflag == 0) {
172 if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
173 perror(gettext("date: Failed to obtain system time"));
174 exit(1);
175 }
176 }
177 clock_val = ts.tv_sec;
178
179 if (aflag) {
180 if (adjtime(&tv, 0) < 0) {
181 perror(gettext("date: Failed to adjust date"));
182 exit(1);
183 }
184 exit(0);
185 }
186
187 if (argc > 0) {
188 if (*argv[0] == '+')
189 fmt = &argv[0][1];
190 else {
191 if (setdate(localtime(&clock_val), argv[0])) {
192 (void) fprintf(stderr, gettext(usage));
193 exit(1);
194 }
195 fmt = nl_langinfo(_DATE_FMT);
196 }
197 } else if (Rflag) {
198 fmt = "%a, %d %h %Y %H:%M:%S %z";
199 } else
200 fmt = nl_langinfo(_DATE_FMT);
201
202 fmt_extensions(fmtbuf, sizeof (fmtbuf), fmt, &ts);
203
204 if (uflag) {
205 (void) putenv("TZ=GMT0");
206 tzset();
207 tp = gmtime(&clock_val);
208 } else
209 tp = localtime(&clock_val);
210 (void) memcpy(&tm, tp, sizeof (struct tm));
211 (void) strftime(buf, BUFSIZ, fmtbuf, &tm);
212
213 (void) puts(buf);
214
215 return (0);
216 }
217
218 int
setdate(struct tm * current_date,char * date)219 setdate(struct tm *current_date, char *date)
220 {
221 int i;
222 int mm;
223 int hh;
224 int min;
225 int sec = 0;
226 char *secptr;
227 int yy;
228 int dd = 0;
229 int minidx = 6;
230 int len;
231 int dd_check;
232
233 /* Parse date string */
234 if ((secptr = strchr(date, '.')) != NULL && strlen(&secptr[1]) == 2 &&
235 isdigit(secptr[1]) && isdigit(secptr[2]) &&
236 (sec = atoi(&secptr[1])) >= 0 && sec < 60)
237 secptr[0] = '\0'; /* eat decimal point only on success */
238
239 len = strlen(date);
240
241 for (i = 0; i < len; i++) {
242 if (!isdigit(date[i])) {
243 (void) fprintf(stderr,
244 gettext("date: bad conversion\n"));
245 exit(1);
246 }
247 }
248 switch (strlen(date)) {
249 case 12:
250 yy = atoi(&date[8]);
251 date[8] = '\0';
252 break;
253 case 10:
254 /*
255 * The YY format has the following representation:
256 * 00-68 = 2000 thru 2068
257 * 69-99 = 1969 thru 1999
258 */
259 if (atoi(&date[8]) <= 68) {
260 yy = 1900 + (atoi(&date[8]) + 100);
261 } else {
262 yy = 1900 + atoi(&date[8]);
263 }
264 date[8] = '\0';
265 break;
266 case 8:
267 yy = 1900 + current_date->tm_year;
268 break;
269 case 4:
270 yy = 1900 + current_date->tm_year;
271 mm = current_date->tm_mon + 1; /* tm_mon goes from 1 to 11 */
272 dd = current_date->tm_mday;
273 minidx = 2;
274 break;
275 default:
276 (void) fprintf(stderr, gettext("date: bad conversion\n"));
277 return (1);
278 }
279
280 min = atoi(&date[minidx]);
281 date[minidx] = '\0';
282 hh = atoi(&date[minidx-2]);
283 date[minidx-2] = '\0';
284
285 if (!dd) {
286 /*
287 * if dd is 0 (not between 1 and 31), then
288 * read the value supplied by the user.
289 */
290 dd = atoi(&date[2]);
291 date[2] = '\0';
292 mm = atoi(&date[0]);
293 }
294
295 if (hh == 24)
296 hh = 0, dd++;
297
298 /* Validate date elements */
299 dd_check = 0;
300 if (mm >= 1 && mm <= 12) {
301 dd_check = month_size[mm - 1]; /* get days in this month */
302 if (mm == 2 && isleap(yy)) /* adjust for leap year */
303 dd_check++;
304 }
305 if (!((mm >= 1 && mm <= 12) && (dd >= 1 && dd <= dd_check) &&
306 (hh >= 0 && hh <= 23) && (min >= 0 && min <= 59))) {
307 (void) fprintf(stderr, gettext("date: bad conversion\n"));
308 return (1);
309 }
310
311 /* Build date and time number */
312 for (clock_val = 0, i = 1970; i < yy; i++)
313 clock_val += year_size(i);
314 /* Adjust for leap year */
315 if (isleap(yy) && mm >= 3)
316 clock_val += 1;
317 /* Adjust for different month lengths */
318 while (--mm)
319 clock_val += (time_t)month_size[mm - 1];
320 /* Load up the rest */
321 clock_val += (time_t)(dd - 1);
322 clock_val *= 24;
323 clock_val += (time_t)hh;
324 clock_val *= 60;
325 clock_val += (time_t)min;
326 clock_val *= 60;
327 clock_val += sec;
328
329 if (!uflag) {
330 /* convert to GMT assuming standard time */
331 /* correction is made in localtime(3C) */
332
333 /*
334 * call localtime to set up "timezone" variable applicable
335 * for clock_val time, to support Olson timezones which
336 * can allow timezone rules to change.
337 */
338 (void) localtime(&clock_val);
339
340 clock_val += (time_t)timezone;
341
342 /* correct if daylight savings time in effect */
343
344 if (localtime(&clock_val)->tm_isdst)
345 clock_val = clock_val - (time_t)(timezone - altzone);
346 }
347
348 (void) time(&wtmpx[0].ut_xtime);
349 if (stime(&clock_val) < 0) {
350 perror("date");
351 return (1);
352 }
353 #if defined(i386)
354 /* correct the kernel's "gmt_lag" and the PC's RTC */
355 (void) system("/usr/sbin/rtc -c > /dev/null 2>&1");
356 #endif
357 (void) time(&wtmpx[1].ut_xtime);
358 (void) pututxline(&wtmpx[0]);
359 (void) pututxline(&wtmpx[1]);
360 (void) updwtmpx(WTMPX_FILE, &wtmpx[0]);
361 (void) updwtmpx(WTMPX_FILE, &wtmpx[1]);
362 return (0);
363 }
364
365 int
get_adj(char * cp,struct timeval * tp)366 get_adj(char *cp, struct timeval *tp)
367 {
368 register int mult;
369 int sign;
370
371 /* arg must be [-]sss[.fff] */
372
373 tp->tv_sec = tp->tv_usec = 0;
374 if (*cp == '-') {
375 sign = -1;
376 cp++;
377 } else {
378 sign = 1;
379 }
380
381 while (*cp >= '0' && *cp <= '9') {
382 tp->tv_sec *= 10;
383 tp->tv_sec += *cp++ - '0';
384 }
385 if (*cp == '.') {
386 cp++;
387 mult = 100000;
388 while (*cp >= '0' && *cp <= '9') {
389 tp->tv_usec += (*cp++ - '0') * mult;
390 mult /= 10;
391 }
392 }
393 /*
394 * if there's anything left in the string,
395 * the input was invalid.
396 */
397 if (*cp) {
398 return (-1);
399 } else {
400 tp->tv_sec *= sign;
401 tp->tv_usec *= sign;
402 return (0);
403 }
404 }
405
406 /*
407 * Extensions that cannot be interpreted by strftime are interpreted here.
408 */
409 void
fmt_extensions(char * fmtbuf,size_t len,const char * fmt,const struct timespec * tsp)410 fmt_extensions(char *fmtbuf, size_t len,
411 const char *fmt, const struct timespec *tsp)
412 {
413 const char *p;
414 char *q;
415
416 for (p = fmt, q = fmtbuf; *p != '\0' && q < fmtbuf + len; ++p) {
417 if (*p == '%') {
418 switch (*(p + 1)) {
419 case 'N':
420 ++p;
421 q += snprintf(q, len - (q - fmtbuf),
422 "%09lu", tsp->tv_nsec);
423 continue;
424 }
425 }
426 *q++ = *p;
427 }
428
429 if (q < fmtbuf + len)
430 *q = '\0';
431 else
432 fmtbuf[len - 1] = '\0';
433 }
434