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) 2012, Joyent, Inc. All rights reserved.
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 <ctype.h>
60 #include <utmpx.h>
61 #include <tzfile.h>
62
63 #define year_size(A) ((isleap(A)) ? 366 : 365)
64 static char buf[BUFSIZ];
65 static time_t clock_val;
66 static short month_size[12] =
67 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
68 static struct utmpx wtmpx[2] = {
69 {"", "", OTIME_MSG, 0, OLD_TIME, 0, 0, 0},
70 {"", "", NTIME_MSG, 0, NEW_TIME, 0, 0, 0}
71 };
72 static char *usage =
73 "usage:\tdate [-u] mmddHHMM[[cc]yy][.SS]\n\tdate [-Ru] [+format]\n"
74 "\tdate -a [-]sss[.fff]\n";
75 static int uflag = 0;
76 static int Rflag = 0;
77
78 static int get_adj(char *, struct timeval *);
79 static int setdate(struct tm *, char *);
80 static void fmt_extensions(char *, size_t,
81 const char *, const struct timespec *);
82
83 int
main(int argc,char ** argv)84 main(int argc, char **argv)
85 {
86 struct tm *tp, tm;
87 struct timeval tv;
88 char *fmt;
89 char fmtbuf[BUFSIZ];
90 int c, aflag = 0, illflag = 0;
91 struct timespec ts;
92
93 (void) setlocale(LC_ALL, "");
94
95 #if !defined(TEXT_DOMAIN)
96 #define TEXT_DOMAIN "SYS_TEST"
97 #endif
98 (void) textdomain(TEXT_DOMAIN);
99
100 while ((c = getopt(argc, argv, "a:uR")) != EOF)
101 switch (c) {
102 case 'a':
103 aflag++;
104 if (get_adj(optarg, &tv) < 0) {
105 (void) fprintf(stderr,
106 gettext("date: invalid argument -- %s\n"),
107 optarg);
108 illflag++;
109 }
110 break;
111 case 'u':
112 uflag++;
113 break;
114 case 'R':
115 Rflag++;
116 break;
117 default:
118 illflag++;
119 }
120
121 argc -= optind;
122 argv = &argv[optind];
123
124 /* -a is mutually exclusive with -u and -R */
125 if (uflag && aflag)
126 illflag++;
127 if (Rflag && aflag)
128 illflag++;
129
130 if (illflag) {
131 (void) fprintf(stderr, gettext(usage));
132 exit(1);
133 }
134
135 if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
136 perror(gettext("date: Failed to obtain system time"));
137 exit(1);
138 }
139 clock_val = ts.tv_sec;
140
141 if (aflag) {
142 if (adjtime(&tv, 0) < 0) {
143 perror(gettext("date: Failed to adjust date"));
144 exit(1);
145 }
146 exit(0);
147 }
148
149 if (argc > 0) {
150 if (*argv[0] == '+')
151 fmt = &argv[0][1];
152 else {
153 if (setdate(localtime(&clock_val), argv[0])) {
154 (void) fprintf(stderr, gettext(usage));
155 exit(1);
156 }
157 fmt = nl_langinfo(_DATE_FMT);
158 }
159 } else if (Rflag) {
160 fmt = "%a, %d %h %Y %H:%M:%S %z";
161 } else
162 fmt = nl_langinfo(_DATE_FMT);
163
164 fmt_extensions(fmtbuf, sizeof (fmtbuf), fmt, &ts);
165
166 if (uflag) {
167 (void) putenv("TZ=GMT0");
168 tzset();
169 tp = gmtime(&clock_val);
170 } else
171 tp = localtime(&clock_val);
172 (void) memcpy(&tm, tp, sizeof (struct tm));
173 (void) strftime(buf, BUFSIZ, fmtbuf, &tm);
174
175 (void) puts(buf);
176
177 return (0);
178 }
179
180 int
setdate(struct tm * current_date,char * date)181 setdate(struct tm *current_date, char *date)
182 {
183 int i;
184 int mm;
185 int hh;
186 int min;
187 int sec = 0;
188 char *secptr;
189 int yy;
190 int dd = 0;
191 int minidx = 6;
192 int len;
193 int dd_check;
194
195 /* Parse date string */
196 if ((secptr = strchr(date, '.')) != NULL && strlen(&secptr[1]) == 2 &&
197 isdigit(secptr[1]) && isdigit(secptr[2]) &&
198 (sec = atoi(&secptr[1])) >= 0 && sec < 60)
199 secptr[0] = '\0'; /* eat decimal point only on success */
200
201 len = strlen(date);
202
203 for (i = 0; i < len; i++) {
204 if (!isdigit(date[i])) {
205 (void) fprintf(stderr,
206 gettext("date: bad conversion\n"));
207 exit(1);
208 }
209 }
210 switch (strlen(date)) {
211 case 12:
212 yy = atoi(&date[8]);
213 date[8] = '\0';
214 break;
215 case 10:
216 /*
217 * The YY format has the following representation:
218 * 00-68 = 2000 thru 2068
219 * 69-99 = 1969 thru 1999
220 */
221 if (atoi(&date[8]) <= 68) {
222 yy = 1900 + (atoi(&date[8]) + 100);
223 } else {
224 yy = 1900 + atoi(&date[8]);
225 }
226 date[8] = '\0';
227 break;
228 case 8:
229 yy = 1900 + current_date->tm_year;
230 break;
231 case 4:
232 yy = 1900 + current_date->tm_year;
233 mm = current_date->tm_mon + 1; /* tm_mon goes from 1 to 11 */
234 dd = current_date->tm_mday;
235 minidx = 2;
236 break;
237 default:
238 (void) fprintf(stderr, gettext("date: bad conversion\n"));
239 return (1);
240 }
241
242 min = atoi(&date[minidx]);
243 date[minidx] = '\0';
244 hh = atoi(&date[minidx-2]);
245 date[minidx-2] = '\0';
246
247 if (!dd) {
248 /*
249 * if dd is 0 (not between 1 and 31), then
250 * read the value supplied by the user.
251 */
252 dd = atoi(&date[2]);
253 date[2] = '\0';
254 mm = atoi(&date[0]);
255 }
256
257 if (hh == 24)
258 hh = 0, dd++;
259
260 /* Validate date elements */
261 dd_check = 0;
262 if (mm >= 1 && mm <= 12) {
263 dd_check = month_size[mm - 1]; /* get days in this month */
264 if (mm == 2 && isleap(yy)) /* adjust for leap year */
265 dd_check++;
266 }
267 if (!((mm >= 1 && mm <= 12) && (dd >= 1 && dd <= dd_check) &&
268 (hh >= 0 && hh <= 23) && (min >= 0 && min <= 59))) {
269 (void) fprintf(stderr, gettext("date: bad conversion\n"));
270 return (1);
271 }
272
273 /* Build date and time number */
274 for (clock_val = 0, i = 1970; i < yy; i++)
275 clock_val += year_size(i);
276 /* Adjust for leap year */
277 if (isleap(yy) && mm >= 3)
278 clock_val += 1;
279 /* Adjust for different month lengths */
280 while (--mm)
281 clock_val += (time_t)month_size[mm - 1];
282 /* Load up the rest */
283 clock_val += (time_t)(dd - 1);
284 clock_val *= 24;
285 clock_val += (time_t)hh;
286 clock_val *= 60;
287 clock_val += (time_t)min;
288 clock_val *= 60;
289 clock_val += sec;
290
291 if (!uflag) {
292 /* convert to GMT assuming standard time */
293 /* correction is made in localtime(3C) */
294
295 /*
296 * call localtime to set up "timezone" variable applicable
297 * for clock_val time, to support Olson timezones which
298 * can allow timezone rules to change.
299 */
300 (void) localtime(&clock_val);
301
302 clock_val += (time_t)timezone;
303
304 /* correct if daylight savings time in effect */
305
306 if (localtime(&clock_val)->tm_isdst)
307 clock_val = clock_val - (time_t)(timezone - altzone);
308 }
309
310 (void) time(&wtmpx[0].ut_xtime);
311 if (stime(&clock_val) < 0) {
312 perror("date");
313 return (1);
314 }
315 #if defined(i386)
316 /* correct the kernel's "gmt_lag" and the PC's RTC */
317 (void) system("/usr/sbin/rtc -c > /dev/null 2>&1");
318 #endif
319 (void) time(&wtmpx[1].ut_xtime);
320 (void) pututxline(&wtmpx[0]);
321 (void) pututxline(&wtmpx[1]);
322 (void) updwtmpx(WTMPX_FILE, &wtmpx[0]);
323 (void) updwtmpx(WTMPX_FILE, &wtmpx[1]);
324 return (0);
325 }
326
327 int
get_adj(char * cp,struct timeval * tp)328 get_adj(char *cp, struct timeval *tp)
329 {
330 register int mult;
331 int sign;
332
333 /* arg must be [-]sss[.fff] */
334
335 tp->tv_sec = tp->tv_usec = 0;
336 if (*cp == '-') {
337 sign = -1;
338 cp++;
339 } else {
340 sign = 1;
341 }
342
343 while (*cp >= '0' && *cp <= '9') {
344 tp->tv_sec *= 10;
345 tp->tv_sec += *cp++ - '0';
346 }
347 if (*cp == '.') {
348 cp++;
349 mult = 100000;
350 while (*cp >= '0' && *cp <= '9') {
351 tp->tv_usec += (*cp++ - '0') * mult;
352 mult /= 10;
353 }
354 }
355 /*
356 * if there's anything left in the string,
357 * the input was invalid.
358 */
359 if (*cp) {
360 return (-1);
361 } else {
362 tp->tv_sec *= sign;
363 tp->tv_usec *= sign;
364 return (0);
365 }
366 }
367
368 /*
369 * Extensions that cannot be interpreted by strftime are interpreted here.
370 */
371 void
fmt_extensions(char * fmtbuf,size_t len,const char * fmt,const struct timespec * tsp)372 fmt_extensions(char *fmtbuf, size_t len,
373 const char *fmt, const struct timespec *tsp)
374 {
375 const char *p;
376 char *q;
377
378 for (p = fmt, q = fmtbuf; *p != '\0' && q < fmtbuf + len; ++p) {
379 if (*p == '%') {
380 switch (*(p + 1)) {
381 case 'N':
382 ++p;
383 q += snprintf(q, len - (q - fmtbuf),
384 "%09lu", tsp->tv_nsec);
385 continue;
386 }
387 }
388 *q++ = *p;
389 }
390
391 if (q < fmtbuf + len)
392 *q = '\0';
393 else
394 fmtbuf[len - 1] = '\0';
395 }
396