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