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 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 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 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 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