1/*- 2 * Copyright (c) 1985, 1987, 1988, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#ifndef lint 31static char const copyright[] = 32"@(#) Copyright (c) 1985, 1987, 1988, 1993\n\ 33 The Regents of the University of California. All rights reserved.\n"; 34#endif /* not lint */ 35 36#if 0 37#ifndef lint 38static char sccsid[] = "@(#)date.c 8.2 (Berkeley) 4/28/95"; 39#endif /* not lint */ 40#endif 41 42#include <sys/cdefs.h> 43__FBSDID("$FreeBSD: src/bin/date/date.c,v 1.47 2005/01/10 08:39:21 imp Exp $"); 44 45#include <sys/param.h> 46#include <sys/time.h> 47 48#include <ctype.h> 49#include <err.h> 50#include <locale.h> 51#ifndef __APPLE__ 52#include <libutil.h> 53#endif /* !__APPLE__ */ 54#include <stdio.h> 55#include <stdlib.h> 56#include <string.h> 57#include <syslog.h> 58#include <unistd.h> 59#include <utmpx.h> 60 61#ifdef __APPLE__ 62#include <get_compat.h> 63#include <util.h> 64#else 65#define COMPAT_MODE(a,b) (1) 66#endif /* __APPLE__ */ 67 68#include "extern.h" 69#include "vary.h" 70 71#ifndef TM_YEAR_BASE 72#define TM_YEAR_BASE 1900 73#endif 74 75static time_t tval; 76int retval; 77int unix2003_std = 0; /* to determine legacy vs std mode */ 78 79static void setthetime(const char *, const char *, int, int); 80static void badformat(void); 81static void usage(void); 82 83int 84main(int argc, char *argv[]) 85{ 86 struct timezone tz; 87 int ch, rflag; 88 int jflag, nflag; 89 const char *format; 90 char buf[1024]; 91 char *endptr, *fmt; 92 char *tmp; 93 int set_timezone; 94 struct vary *v; 95 const struct vary *badv; 96 struct tm lt; 97 98 unix2003_std = COMPAT_MODE("bin/date", "unix2003"); /* Determine the STD */ 99 100 v = NULL; 101 fmt = NULL; 102 (void) setlocale(LC_TIME, ""); 103 tz.tz_dsttime = tz.tz_minuteswest = 0; 104 rflag = 0; 105 jflag = nflag = 0; 106 set_timezone = 0; 107 while ((ch = getopt(argc, argv, "d:f:jnr:t:uv:")) != -1) 108 switch((char)ch) { 109 case 'd': /* daylight savings time */ 110 tz.tz_dsttime = strtol(optarg, &endptr, 10) ? 1 : 0; 111 if (endptr == optarg || *endptr != '\0') 112 usage(); 113 set_timezone = 1; 114 break; 115 case 'f': 116 fmt = optarg; 117 break; 118 case 'j': 119 jflag = 1; /* don't set time */ 120 break; 121 case 'n': /* don't set network */ 122 nflag = 1; 123 break; 124 case 'r': /* user specified seconds */ 125 rflag = 1; 126 tval = strtoq(optarg, &tmp, 0); 127 if (*tmp != 0) 128 usage(); 129 break; 130 case 't': /* minutes west of UTC */ 131 /* error check; don't allow "PST" */ 132 tz.tz_minuteswest = strtol(optarg, &endptr, 10); 133 if (endptr == optarg || *endptr != '\0') 134 usage(); 135 set_timezone = 1; 136 break; 137 case 'u': /* do everything in UTC */ 138 (void)setenv("TZ", "UTC0", 1); 139 break; 140 case 'v': 141 v = vary_append(v, optarg); 142 break; 143 default: 144 usage(); 145 } 146 argc -= optind; 147 argv += optind; 148 149 /* 150 * If -d or -t, set the timezone or daylight savings time; this 151 * doesn't belong here; the kernel should not know about either. 152 */ 153 if (set_timezone && settimeofday((struct timeval *)NULL, &tz)) 154 err(1, "settimeofday (timezone)"); 155 156 if (!rflag && time(&tval) == -1) 157 err(1, "time"); 158 159 format = "%+"; 160 161 /* allow the operands in any order */ 162 if (*argv && **argv == '+') { 163 format = *argv + 1; 164 ++argv; 165 } 166 167 if (*argv) { 168 setthetime(fmt, *argv, jflag, nflag); 169 ++argv; 170 } else if (fmt != NULL) 171 usage(); 172 173 if (*argv && **argv == '+') 174 format = *argv + 1; 175 176#ifdef __APPLE__ 177 /* 7999711 */ 178 struct tm *ltp = localtime(&tval); 179 if (ltp == NULL) { 180 err(1, "localtime"); 181 } 182 lt = *ltp; 183#else 184 lt = *localtime(&tval); 185#endif 186 badv = vary_apply(v, <); 187 if (badv) { 188 fprintf(stderr, "%s: Cannot apply date adjustment\n", 189 badv->arg); 190 vary_destroy(v); 191 usage(); 192 } 193 vary_destroy(v); 194 (void)strftime(buf, sizeof(buf), format, <); 195 (void)printf("%s\n", buf); 196 if (fflush(stdout)) 197 err(1, "stdout"); 198 /* 199 * If date/time could not be set/notified in the other hosts as 200 * determined by netsetval(), a return value 2 is set, which is 201 * only propagated back to shell in legacy mode. 202 */ 203 if (unix2003_std) 204 exit(0); 205 exit(retval); 206} 207 208#define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0')) 209#define ATOI2_OFFSET(s, o) (((s)[o] - '0') * 10 + ((s)[o + 1] - '0')) 210 211static void 212setthetime(const char *fmt, const char *p, int jflag, int nflag) 213{ 214 struct tm *lt; 215 const char *dot, *t; 216 int century; 217 size_t length; 218 219 if (fmt != NULL) { 220 lt = localtime(&tval); 221 t = strptime(p, fmt, lt); 222 if (t == NULL) { 223 fprintf(stderr, "Failed conversion of ``%s''" 224 " using format ``%s''\n", p, fmt); 225 badformat(); 226 } else if (*t != '\0') 227 fprintf(stderr, "Warning: Ignoring %ld extraneous" 228 " characters in date string (%s)\n", 229 (long) strlen(t), t); 230 } else { 231 for (t = p, dot = NULL; *t; ++t) { 232 if (isdigit(*t)) 233 continue; 234 if (*t == '.' && dot == NULL) { 235 dot = t; 236 continue; 237 } 238 badformat(); 239 } 240 241 lt = localtime(&tval); 242 243 if (dot != NULL) { /* .ss */ 244 dot++; /* *dot++ = '\0'; */ 245 if (strlen(dot) != 2) 246 badformat(); 247 lt->tm_sec = ATOI2(dot); 248 if (lt->tm_sec > 61) 249 badformat(); 250 } else 251 lt->tm_sec = 0; 252 253 century = 0; 254 /* if p has a ".ss" field then let's pretend it's not there */ 255 switch (length = strlen(p) - ((dot != NULL) ? 3 : 0)) { 256 case 12: /* cc */ 257 lt->tm_year = (unix2003_std ? ATOI2_OFFSET(p, length - 4) : ATOI2(p)) * 100 - TM_YEAR_BASE; 258 century = 1; 259 /* FALLTHROUGH */ 260 case 10: /* yy */ 261 if (century) 262 lt->tm_year += (unix2003_std ? ATOI2_OFFSET(p, length - 2) : ATOI2(p)); 263 else { 264 lt->tm_year = (unix2003_std ? ATOI2_OFFSET(p, length - 2) : ATOI2(p)); 265 if (lt->tm_year < 69) /* hack for 2000 ;-} */ 266 lt->tm_year += 2000 - TM_YEAR_BASE; 267 else 268 lt->tm_year += 1900 - TM_YEAR_BASE; 269 } 270 /* FALLTHROUGH */ 271 case 8: /* mm */ 272 lt->tm_mon = ATOI2(p); 273 if (lt->tm_mon > 12) 274 badformat(); 275 --lt->tm_mon; /* time struct is 0 - 11 */ 276 /* FALLTHROUGH */ 277 case 6: /* dd */ 278 lt->tm_mday = ATOI2(p); 279 if (lt->tm_mday > 31) 280 badformat(); 281 /* FALLTHROUGH */ 282 case 4: /* HH */ 283 lt->tm_hour = ATOI2(p); 284 if (lt->tm_hour > 23) 285 badformat(); 286 /* FALLTHROUGH */ 287 case 2: /* MM */ 288 lt->tm_min = ATOI2(p); 289 if (lt->tm_min > 59) 290 badformat(); 291 break; 292 default: 293 badformat(); 294 } 295 } 296 297 /* Let mktime() decide whether summer time is in effect. */ 298 lt->tm_isdst = -1; 299 300 /* convert broken-down time to GMT clock time */ 301 if ((tval = mktime(lt)) == -1) 302 errx(1, "nonexistent time"); 303 304 if (!jflag) { 305 /* set the time */ 306 if (nflag || netsettime(tval)) { 307 struct utmpx ut; 308 bzero(&ut, sizeof(ut)); 309 ut.ut_type = OLD_TIME; 310 (void)gettimeofday(&ut.ut_tv, NULL); 311 pututxline(&ut); 312 ut.ut_tv.tv_sec = tval; 313 ut.ut_tv.tv_usec = 0; 314 if (settimeofday(&ut.ut_tv, NULL)) 315 err(1, "settimeofday (timeval)"); 316 ut.ut_type = NEW_TIME; 317 pututxline(&ut); 318 } 319 320 if ((p = getlogin()) == NULL) 321 p = "???"; 322 syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p); 323 } 324} 325 326static void 327badformat(void) 328{ 329 warnx("illegal time format"); 330 usage(); 331} 332 333static void 334usage(void) 335{ 336 (void)fprintf(stderr, "%s\n%s\n", 337 "usage: date [-jnu] [-d dst] [-r seconds] [-t west] " 338 "[-v[+|-]val[ymwdHMS]] ... ", 339 unix2003_std ? " " 340 "[-f fmt date | [[[mm]dd]HH]MM[[cc]yy][.ss]] [+format]" : 341 " " 342 "[-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format]"); 343 exit(1); 344} 345