date.c revision 1.46
1/* $NetBSD: date.c,v 1.46 2006/11/15 03:10:01 jdarrow Exp $ */ 2 3/* 4 * Copyright (c) 1985, 1987, 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#ifndef lint 34__COPYRIGHT( 35"@(#) Copyright (c) 1985, 1987, 1988, 1993\n\ 36 The Regents of the University of California. All rights reserved.\n"); 37#endif /* not lint */ 38 39#ifndef lint 40#if 0 41static char sccsid[] = "@(#)date.c 8.2 (Berkeley) 4/28/95"; 42#else 43__RCSID("$NetBSD: date.c,v 1.46 2006/11/15 03:10:01 jdarrow Exp $"); 44#endif 45#endif /* not lint */ 46 47#include <sys/param.h> 48#include <sys/time.h> 49 50#include <ctype.h> 51#include <err.h> 52#include <fcntl.h> 53#include <locale.h> 54#include <stdio.h> 55#include <stdlib.h> 56#include <string.h> 57#include <syslog.h> 58#include <time.h> 59#include <tzfile.h> 60#include <unistd.h> 61#include <util.h> 62 63#include "extern.h" 64 65static time_t tval; 66static int aflag, jflag, rflag, nflag; 67int retval; 68 69static void badformat(void); 70static void badtime(void); 71static void badvalue(const char *); 72static void setthetime(const char *); 73static void usage(void); 74 75int 76main(int argc, char *argv[]) 77{ 78 char *buf; 79 size_t bufsiz; 80 const char *format; 81 int ch; 82 83 setprogname(argv[0]); 84 (void)setlocale(LC_ALL, ""); 85 86 while ((ch = getopt(argc, argv, "ajnr:u")) != -1) { 87 switch (ch) { 88 case 'a': /* adjust time slowly */ 89 aflag = 1; 90 nflag = 1; 91 break; 92 case 'j': /* don't set time */ 93 jflag = 1; 94 break; 95 case 'n': /* don't set network */ 96 nflag = 1; 97 break; 98 case 'r': /* user specified seconds */ 99 rflag = 1; 100 tval = strtol(optarg, NULL, 0); 101 break; 102 case 'u': /* do everything in UTC */ 103 (void)putenv("TZ=UTC0"); 104 break; 105 default: 106 usage(); 107 } 108 } 109 argc -= optind; 110 argv += optind; 111 112 if (!rflag && time(&tval) == -1) 113 err(EXIT_FAILURE, "time"); 114 115 format = "%a %b %e %H:%M:%S %Z %Y"; 116 117 /* allow the operands in any order */ 118 if (*argv && **argv == '+') { 119 format = *argv + 1; 120 ++argv; 121 } 122 123 if (*argv) { 124 setthetime(*argv); 125 ++argv; 126 } 127 128 if (*argv && **argv == '+') 129 format = *argv + 1; 130 131 if ((buf = malloc(bufsiz = 1024)) == NULL) 132 goto bad; 133 while (strftime(buf, bufsiz, format, localtime(&tval)) == 0) 134 if ((buf = realloc(buf, bufsiz <<= 1)) == NULL) 135 goto bad; 136 (void)printf("%s\n", buf); 137 free(buf); 138 return 0; 139bad: 140 err(1, "Cannot allocate format buffer"); 141} 142 143static void 144badformat(void) 145{ 146 warnx("illegal time format"); 147 usage(); 148} 149 150static void 151badtime(void) 152{ 153 errx(EXIT_FAILURE, "illegal time"); 154 /* NOTREACHED */ 155} 156 157static void 158badvalue(const char *param) 159{ 160 warnx("invalid %s supplied", param); 161 usage(); 162} 163 164#define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0')) 165 166static void 167setthetime(const char *p) 168{ 169 struct timeval tv; 170 time_t new_time; 171 struct tm *lt; 172 const char *dot, *t; 173 int len, yearset; 174 175 for (t = p, dot = NULL; *t; ++t) { 176 if (isdigit((unsigned char)*t)) 177 continue; 178 if (*t == '.' && dot == NULL) { 179 dot = t; 180 continue; 181 } 182 badformat(); 183 } 184 185 lt = localtime(&tval); 186 187 lt->tm_isdst = -1; /* Divine correct DST */ 188 189 if (dot != NULL) { /* .ss */ 190 len = strlen(dot); 191 if (len != 3) 192 badformat(); 193 ++dot; 194 lt->tm_sec = ATOI2(dot); 195 if (lt->tm_sec > 61) 196 badvalue("seconds"); 197 } else { 198 len = 0; 199 lt->tm_sec = 0; 200 } 201 202 yearset = 0; 203 switch (strlen(p) - len) { 204 case 12: /* cc */ 205 lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE; 206 if (lt->tm_year < 0) 207 badtime(); 208 yearset = 1; 209 /* FALLTHROUGH */ 210 case 10: /* yy */ 211 if (yearset) { 212 lt->tm_year += ATOI2(p); 213 } else { 214 yearset = ATOI2(p); 215 if (yearset < 69) 216 lt->tm_year = yearset + 2000 - TM_YEAR_BASE; 217 else 218 lt->tm_year = yearset + 1900 - TM_YEAR_BASE; 219 } 220 /* FALLTHROUGH */ 221 case 8: /* mm */ 222 lt->tm_mon = ATOI2(p); 223 if (lt->tm_mon > 12 || lt->tm_mon == 0) 224 badvalue("month"); 225 --lt->tm_mon; /* time struct is 0 - 11 */ 226 /* FALLTHROUGH */ 227 case 6: /* dd */ 228 lt->tm_mday = ATOI2(p); 229 switch (lt->tm_mon) { 230 case 0: 231 case 2: 232 case 4: 233 case 6: 234 case 7: 235 case 9: 236 case 11: 237 if (lt->tm_mday > 31 || lt->tm_mday == 0) 238 badvalue("day of month"); 239 break; 240 case 3: 241 case 5: 242 case 8: 243 case 10: 244 if (lt->tm_mday > 30 || lt->tm_mday == 0) 245 badvalue("day of month"); 246 break; 247 case 1: 248 if (lt->tm_mday > 29 || lt->tm_mday == 0 || 249 (lt->tm_mday == 29 && 250 !isleap(lt->tm_year + TM_YEAR_BASE))) 251 badvalue("day of month"); 252 break; 253 default: 254 badvalue("month"); 255 break; 256 } 257 /* FALLTHROUGH */ 258 case 4: /* hh */ 259 lt->tm_hour = ATOI2(p); 260 if (lt->tm_hour > 23) 261 badvalue("hour"); 262 /* FALLTHROUGH */ 263 case 2: /* mm */ 264 lt->tm_min = ATOI2(p); 265 if (lt->tm_min > 59) 266 badvalue("minute"); 267 break; 268 case 0: /* was just .sss */ 269 if (len != 0) 270 break; 271 /* FALLTHROUGH */ 272 default: 273 badformat(); 274 } 275 276 /* convert broken-down time to UTC clock time */ 277 if ((new_time = mktime(lt)) == -1) 278 badtime(); 279 280 /* if jflag is set, don't actually change the time, just return */ 281 if (jflag) { 282 tval = new_time; 283 return; 284 } 285 286 /* set the time */ 287 if (nflag || netsettime(new_time)) { 288 logwtmp("|", "date", ""); 289 if (aflag) { 290 tv.tv_sec = new_time - tval; 291 tv.tv_usec = 0; 292 if (adjtime(&tv, NULL)) 293 err(EXIT_FAILURE, "adjtime"); 294 } else { 295 tval = new_time; 296 tv.tv_sec = tval; 297 tv.tv_usec = 0; 298 if (settimeofday(&tv, NULL)) 299 err(EXIT_FAILURE, "settimeofday"); 300 } 301 logwtmp("{", "date", ""); 302 } 303 304 if ((p = getlogin()) == NULL) 305 p = "???"; 306 syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p); 307} 308 309static void 310usage(void) 311{ 312 (void)fprintf(stderr, 313 "usage: %s [-ajnu] [-r seconds] [+format]", getprogname()); 314 (void)fprintf(stderr, " [[[[[[CC]yy]mm]dd]HH]MM[.SS]]\n"); 315 exit(EXIT_FAILURE); 316 /* NOTREACHED */ 317} 318