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 * 3. 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 * @(#)date.c 8.2 (Berkeley) 4/28/95 30 * $FreeBSD: src/bin/date/date.c,v 1.47 2005/01/10 08:39:21 imp Exp $ 31 */ 32 33 #include <sys/param.h> 34 #include <sys/time.h> 35 36 #include <ctype.h> 37 #include <err.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <syslog.h> 42 #include <unistd.h> 43 #include <locale.h> 44 #include <libutil.h> 45 #include <utmpx.h> 46 47 #include "vary.h" 48 49 #ifndef TM_YEAR_BASE 50 #define TM_YEAR_BASE 1900 51 #endif 52 53 static time_t tval; 54 55 static void badformat(void); 56 static void setthetime(const char *, const char *, int); 57 static void usage(void); 58 59 static const struct iso8601_fmt { 60 const char *refname; 61 const char *format; 62 } iso8601_fmts[] = { 63 { "date", "%Y-%m-%d" }, 64 { "hours", "%Y-%m-%dT%H" }, 65 { "minutes", "%Y-%m-%dT%H:%M" }, 66 { "seconds", "%Y-%m-%dT%H:%M:%S" }, 67 }; 68 static const struct iso8601_fmt *iso8601_selected; 69 70 static const char *rfc2822_format = "%a, %d %b %Y %T %z"; 71 72 int 73 main(int argc, char **argv) 74 { 75 int ch, rflag; 76 int Iflag, jflag, Rflag; 77 const char *format; 78 char buf[1024], tzbuf[8]; 79 char *fmt; 80 char *tmp; 81 struct vary *v; 82 const struct vary *badv; 83 struct tm lt; 84 size_t i; 85 86 v = NULL; 87 fmt = NULL; 88 setlocale(LC_TIME, ""); 89 rflag = 0; 90 Iflag = jflag = Rflag = 0; 91 while ((ch = getopt(argc, argv, "d:f:I::jnRr:uv:")) != -1) { 92 switch (ch) { 93 case 'f': 94 fmt = optarg; 95 break; 96 case 'I': /* ISO 8601 datetime format */ 97 if (Rflag) 98 errx(1, "multiple output formats specified"); 99 Iflag = 1; 100 if (optarg == NULL) { 101 iso8601_selected = iso8601_fmts; 102 break; 103 } 104 for (i = 0; i < nitems(iso8601_fmts); i++) { 105 if (strncmp(optarg, iso8601_fmts[i].refname, 106 strlen(optarg)) == 0) 107 break; 108 } 109 if (i == nitems(iso8601_fmts)) 110 errx(1, "invalid argument '%s' for -I", optarg); 111 iso8601_selected = &iso8601_fmts[i]; 112 break; 113 case 'j': 114 jflag = 1; /* don't set time */ 115 break; 116 case 'n': /* don't set network */ 117 break; 118 case 'R': /* RFC 2822 datetime format */ 119 if (Iflag) 120 errx(1, "multiple output formats specified"); 121 Rflag = 1; 122 break; 123 case 'r': /* user specified seconds */ 124 rflag = 1; 125 tval = strtoll(optarg, &tmp, 0); 126 if (*tmp != 0) 127 usage(); 128 break; 129 case 'u': /* do everything in UTC */ 130 if (setenv("TZ", "UTC0", 1) != 0) 131 err(1, "setenv: cannot set TZ=UTC0"); 132 break; 133 case 'd': 134 case 'v': 135 if (strncmp(optarg, "TZ=", 3) == 0) { 136 tmp = strdup(optarg); 137 size_t tzlen; 138 139 for (tzlen = 0; !isspace(tmp[tzlen]); ++tzlen) 140 ; 141 if (tmp[0] == '"' && tmp[tzlen - 1] == '"') { 142 tmp[tzlen - 1] = 0; 143 setenv("TZ", tmp + 1, 1); 144 } else { 145 tmp[tzlen] = 0; 146 setenv("TZ", tmp, 1); 147 } 148 while (isspace(tmp[tzlen])) 149 ++tzlen; 150 if (tmp[tzlen]) 151 v = vary_append(v, optarg + tzlen); 152 } else { 153 v = vary_append(v, optarg); 154 } 155 break; 156 default: 157 usage(); 158 } 159 } 160 argc -= optind; 161 argv += optind; 162 163 if (!rflag && time(&tval) == -1) 164 err(1, "time"); 165 166 format = "%+"; 167 168 if (Rflag) 169 format = rfc2822_format; 170 if (Iflag) 171 format = iso8601_selected->format; 172 173 /* allow the operands in any order */ 174 if (*argv && **argv == '+') { 175 if (Iflag || Rflag) 176 errx(1, "multiple output formats specified"); 177 format = *argv + 1; 178 ++argv; 179 } 180 181 if (*argv) { 182 setthetime(fmt, *argv, jflag); 183 ++argv; 184 } else if (fmt != NULL) 185 usage(); 186 187 if (*argv && **argv == '+') { 188 if (Iflag || Rflag) 189 errx(1, "multiple output formats specified"); 190 format = *argv + 1; 191 } 192 193 badv = vary_apply(v, tval, <); 194 if (badv) { 195 fprintf(stderr, "%s: Cannot apply date adjustment\n", 196 badv->arg); 197 vary_destroy(v); 198 usage(); 199 } 200 vary_destroy(v); 201 202 if (format == rfc2822_format) { 203 /* 204 * When using RFC 2822 datetime format, don't honor the 205 * locale. 206 */ 207 setlocale(LC_TIME, "C"); 208 } 209 210 strftime(buf, sizeof(buf), format, <); 211 if (Iflag && iso8601_selected > iso8601_fmts) { 212 strftime(tzbuf, sizeof(tzbuf), "%z", <); 213 memmove(&tzbuf[4], &tzbuf[3], 3); 214 tzbuf[3] = ':'; 215 strlcat(buf, tzbuf, sizeof(buf)); 216 } 217 printf("%s\n", buf); 218 if (fflush(stdout) != 0) 219 err(1, "stdout"); 220 exit(EXIT_SUCCESS); 221 } 222 223 #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0')) 224 225 static void 226 setthetime(const char *fmt, const char *p, int jflag) 227 { 228 struct tm *lt; 229 struct timeval tv; 230 const char *dot, *t; 231 int century; 232 233 if (fmt != NULL) { 234 lt = localtime(&tval); 235 t = strptime(p, fmt, lt); 236 if (t == NULL) { 237 fprintf(stderr, "Failed conversion of ``%s''" 238 " using format ``%s''\n", p, fmt); 239 badformat(); 240 } else if (*t != '\0') 241 fprintf(stderr, "Warning: Ignoring %ld extraneous" 242 " characters in date string (%s)\n", 243 (long) strlen(t), t); 244 } else { 245 for (t = p, dot = NULL; *t; ++t) { 246 if (isdigit(*t)) 247 continue; 248 if (*t == '.' && dot == NULL) { 249 dot = t; 250 continue; 251 } 252 badformat(); 253 } 254 255 lt = localtime(&tval); 256 257 if (dot != NULL) { /* .ss */ 258 dot++; /* *dot++ = '\0'; */ 259 if (strlen(dot) != 2) 260 badformat(); 261 lt->tm_sec = ATOI2(dot); 262 if (lt->tm_sec > 61) 263 badformat(); 264 } else 265 lt->tm_sec = 0; 266 267 century = 0; 268 /* if p has a ".ss" field then let's pretend it's not there */ 269 switch (strlen(p) - ((dot != NULL) ? 3 : 0)) { 270 case 12: /* cc */ 271 lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE; 272 century = 1; 273 /* FALLTHROUGH */ 274 case 10: /* yy */ 275 if (century) 276 lt->tm_year += ATOI2(p); 277 else { /* hack for 2000 ;-} */ 278 lt->tm_year = ATOI2(p); 279 if (lt->tm_year < 69) 280 lt->tm_year += 2000 - TM_YEAR_BASE; 281 else 282 lt->tm_year += 1900 - TM_YEAR_BASE; 283 } 284 /* FALLTHROUGH */ 285 case 8: /* mm */ 286 lt->tm_mon = ATOI2(p); 287 if (lt->tm_mon > 12) 288 badformat(); 289 --lt->tm_mon; /* time struct is 0 - 11 */ 290 /* FALLTHROUGH */ 291 case 6: /* dd */ 292 lt->tm_mday = ATOI2(p); 293 if (lt->tm_mday > 31) 294 badformat(); 295 /* FALLTHROUGH */ 296 case 4: /* HH */ 297 lt->tm_hour = ATOI2(p); 298 if (lt->tm_hour > 23) 299 badformat(); 300 /* FALLTHROUGH */ 301 case 2: /* MM */ 302 lt->tm_min = ATOI2(p); 303 if (lt->tm_min > 59) 304 badformat(); 305 break; 306 default: 307 badformat(); 308 } 309 } 310 311 /* Let mktime() decide whether summer time is in effect. */ 312 lt->tm_isdst = -1; 313 314 /* convert broken-down time to GMT clock time */ 315 if ((tval = mktime(lt)) == -1) 316 errx(1, "nonexistent time"); 317 318 if (!jflag) { 319 logwtmpx("|", "date", "", 0, OLD_TIME); 320 tv.tv_sec = tval; 321 tv.tv_usec = 0; 322 if (settimeofday(&tv, NULL)) 323 err(1, "settimeofday (timeval)"); 324 logwtmpx("{", "date", "", 0, NEW_TIME); 325 326 if ((p = getlogin()) == NULL) 327 p = "???"; 328 syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p); 329 } 330 } 331 332 static void 333 badformat(void) 334 { 335 warnx("illegal time format"); 336 usage(); 337 } 338 339 static void 340 usage(void) 341 { 342 fprintf(stderr, "%s\n%s\n", 343 "usage: date [-jnRu] [-r seconds] [-v[+|-]val[ymwdHMS]] ... ", 344 " " 345 "[-I [date | hours | minutes | seconds]]" 346 " " 347 "[-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format]"); 348 exit(1); 349 } 350