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