| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: date.c,v 1.60 2011/08/27 12:55:09 joerg Exp $ */ | | 1 | /* $NetBSD: date.c,v 1.61 2014/09/01 21:42:21 dholland Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 1985, 1987, 1988, 1993 | | 4 | * Copyright (c) 1985, 1987, 1988, 1993 |
5 | * The Regents of the University of California. All rights reserved. | | 5 | * The Regents of the University of California. All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
| @@ -30,98 +30,102 @@ | | | @@ -30,98 +30,102 @@ |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | #include <sys/cdefs.h> | | 32 | #include <sys/cdefs.h> |
33 | #ifndef lint | | 33 | #ifndef lint |
34 | __COPYRIGHT( | | 34 | __COPYRIGHT( |
35 | "@(#) Copyright (c) 1985, 1987, 1988, 1993\ | | 35 | "@(#) Copyright (c) 1985, 1987, 1988, 1993\ |
36 | The Regents of the University of California. All rights reserved."); | | 36 | The Regents of the University of California. All rights reserved."); |
37 | #endif /* not lint */ | | 37 | #endif /* not lint */ |
38 | | | 38 | |
39 | #ifndef lint | | 39 | #ifndef lint |
40 | #if 0 | | 40 | #if 0 |
41 | static char sccsid[] = "@(#)date.c 8.2 (Berkeley) 4/28/95"; | | 41 | static char sccsid[] = "@(#)date.c 8.2 (Berkeley) 4/28/95"; |
42 | #else | | 42 | #else |
43 | __RCSID("$NetBSD: date.c,v 1.60 2011/08/27 12:55:09 joerg Exp $"); | | 43 | __RCSID("$NetBSD: date.c,v 1.61 2014/09/01 21:42:21 dholland Exp $"); |
44 | #endif | | 44 | #endif |
45 | #endif /* not lint */ | | 45 | #endif /* not lint */ |
46 | | | 46 | |
47 | #include <sys/param.h> | | 47 | #include <sys/param.h> |
48 | #include <sys/time.h> | | 48 | #include <sys/time.h> |
49 | | | 49 | |
50 | #include <ctype.h> | | 50 | #include <ctype.h> |
51 | #include <err.h> | | 51 | #include <err.h> |
52 | #include <fcntl.h> | | 52 | #include <fcntl.h> |
53 | #include <errno.h> | | 53 | #include <errno.h> |
54 | #include <locale.h> | | 54 | #include <locale.h> |
55 | #include <stdio.h> | | 55 | #include <stdio.h> |
56 | #include <stdlib.h> | | 56 | #include <stdlib.h> |
57 | #include <string.h> | | 57 | #include <string.h> |
58 | #include <syslog.h> | | 58 | #include <syslog.h> |
59 | #include <time.h> | | 59 | #include <time.h> |
60 | #include <tzfile.h> | | 60 | #include <tzfile.h> |
61 | #include <unistd.h> | | 61 | #include <unistd.h> |
62 | #include <util.h> | | 62 | #include <util.h> |
63 | | | 63 | |
64 | #include "extern.h" | | 64 | #include "extern.h" |
65 | | | 65 | |
66 | static time_t tval; | | 66 | static time_t tval; |
67 | static int aflag, jflag, rflag, nflag; | | 67 | static int aflag, jflag, rflag, nflag; |
68 | | | 68 | |
69 | __dead static void badformat(void); | | 69 | __dead static void badcanotime(const char *, const char *, size_t); |
70 | __dead static void badtime(void); | | | |
71 | __dead static void badvalue(const char *); | | | |
72 | static void setthetime(const char *); | | 70 | static void setthetime(const char *); |
73 | __dead static void usage(void); | | 71 | __dead static void usage(void); |
74 | | | 72 | |
75 | int | | 73 | int |
76 | main(int argc, char *argv[]) | | 74 | main(int argc, char *argv[]) |
77 | { | | 75 | { |
78 | char *buf; | | 76 | char *buf; |
79 | size_t bufsiz; | | 77 | size_t bufsiz; |
80 | const char *format; | | 78 | const char *format; |
81 | int ch; | | 79 | int ch; |
82 | long long val; | | 80 | long long val; |
83 | struct tm *tm; | | 81 | struct tm *tm; |
84 | | | 82 | |
85 | setprogname(argv[0]); | | 83 | setprogname(argv[0]); |
86 | (void)setlocale(LC_ALL, ""); | | 84 | (void)setlocale(LC_ALL, ""); |
87 | | | 85 | |
88 | while ((ch = getopt(argc, argv, "ad:jnr:u")) != -1) { | | 86 | while ((ch = getopt(argc, argv, "ad:jnr:u")) != -1) { |
89 | switch (ch) { | | 87 | switch (ch) { |
90 | case 'a': /* adjust time slowly */ | | 88 | case 'a': /* adjust time slowly */ |
91 | aflag = 1; | | 89 | aflag = 1; |
92 | nflag = 1; | | 90 | nflag = 1; |
93 | break; | | 91 | break; |
94 | case 'd': | | 92 | case 'd': |
95 | rflag = 1; | | 93 | rflag = 1; |
96 | tval = parsedate(optarg, NULL, NULL); | | 94 | tval = parsedate(optarg, NULL, NULL); |
97 | if (tval == -1) | | 95 | if (tval == -1) { |
98 | badarg: errx(EXIT_FAILURE, | | 96 | errx(EXIT_FAILURE, |
99 | "Cannot parse `%s'", optarg); | | 97 | "%s: Unrecognized date format", optarg); |
| | | 98 | } |
100 | break; | | 99 | break; |
101 | case 'j': /* don't set time */ | | 100 | case 'j': /* don't set time */ |
102 | jflag = 1; | | 101 | jflag = 1; |
103 | break; | | 102 | break; |
104 | case 'n': /* don't set network */ | | 103 | case 'n': /* don't set network */ |
105 | nflag = 1; | | 104 | nflag = 1; |
106 | break; | | 105 | break; |
107 | case 'r': /* user specified seconds */ | | 106 | case 'r': /* user specified seconds */ |
| | | 107 | if (optarg[0] == '\0') { |
| | | 108 | errx(EXIT_FAILURE, "<empty>: Invalid number"); |
| | | 109 | } |
108 | errno = 0; | | 110 | errno = 0; |
109 | val = strtoll(optarg, &buf, 0); | | 111 | val = strtoll(optarg, &buf, 0); |
110 | if (optarg[0] == '\0' || *buf != '\0') | | 112 | if (errno) { |
111 | goto badarg; | | 113 | err(EXIT_FAILURE, "%s", optarg); |
112 | if (errno == ERANGE && (val == LLONG_MAX || | | 114 | } |
113 | val == LLONG_MIN)) | | 115 | if (optarg[0] == '\0' || *buf != '\0') { |
114 | err(EXIT_FAILURE, "Bad number `%s'", optarg); | | 116 | errx(EXIT_FAILURE, |
| | | 117 | "%s: Invalid number", optarg); |
| | | 118 | } |
115 | rflag = 1; | | 119 | rflag = 1; |
116 | tval = (time_t)val; | | 120 | tval = (time_t)val; |
117 | break; | | 121 | break; |
118 | case 'u': /* do everything in UTC */ | | 122 | case 'u': /* do everything in UTC */ |
119 | (void)setenv("TZ", "UTC0", 1); | | 123 | (void)setenv("TZ", "UTC0", 1); |
120 | break; | | 124 | break; |
121 | default: | | 125 | default: |
122 | usage(); | | 126 | usage(); |
123 | } | | 127 | } |
124 | } | | 128 | } |
125 | argc -= optind; | | 129 | argc -= optind; |
126 | argv += optind; | | 130 | argv += optind; |
127 | | | 131 | |
| @@ -138,177 +142,196 @@ badarg: errx(EXIT_FAILURE, | | | @@ -138,177 +142,196 @@ badarg: errx(EXIT_FAILURE, |
138 | | | 142 | |
139 | if (*argv) { | | 143 | if (*argv) { |
140 | setthetime(*argv); | | 144 | setthetime(*argv); |
141 | ++argv; | | 145 | ++argv; |
142 | } | | 146 | } |
143 | | | 147 | |
144 | if (*argv && **argv == '+') | | 148 | if (*argv && **argv == '+') |
145 | format = *argv; | | 149 | format = *argv; |
146 | | | 150 | |
147 | if ((buf = malloc(bufsiz = 1024)) == NULL) | | 151 | if ((buf = malloc(bufsiz = 1024)) == NULL) |
148 | goto bad; | | 152 | goto bad; |
149 | | | 153 | |
150 | if ((tm = localtime(&tval)) == NULL) | | 154 | if ((tm = localtime(&tval)) == NULL) |
151 | err(EXIT_FAILURE, "localtime %lld failed", (long long)tval); | | 155 | err(EXIT_FAILURE, "%lld: localtime", (long long)tval); |
152 | | | 156 | |
153 | while (strftime(buf, bufsiz, format, tm) == 0) | | 157 | while (strftime(buf, bufsiz, format, tm) == 0) |
154 | if ((buf = realloc(buf, bufsiz <<= 1)) == NULL) | | 158 | if ((buf = realloc(buf, bufsiz <<= 1)) == NULL) |
155 | goto bad; | | 159 | goto bad; |
156 | | | 160 | |
157 | (void)printf("%s\n", buf + 1); | | 161 | (void)printf("%s\n", buf + 1); |
158 | free(buf); | | 162 | free(buf); |
159 | return 0; | | 163 | return 0; |
160 | bad: | | 164 | bad: |
161 | err(EXIT_FAILURE, "Cannot allocate format buffer"); | | 165 | err(EXIT_FAILURE, "Cannot allocate format buffer"); |
162 | } | | 166 | } |
163 | | | 167 | |
164 | static void | | 168 | static void |
165 | badformat(void) | | 169 | badcanotime(const char *msg, const char *val, size_t where) |
166 | { | | 170 | { |
167 | warnx("illegal time format"); | | 171 | warnx("%s in canonical time", msg); |
168 | usage(); | | 172 | warnx("%s", val); |
169 | } | | 173 | warnx("%*s", (int)where + 1, "^"); |
170 | | | | |
171 | static void | | | |
172 | badtime(void) | | | |
173 | { | | | |
174 | errx(EXIT_FAILURE, "illegal time"); | | | |
175 | /* NOTREACHED */ | | | |
176 | } | | | |
177 | | | | |
178 | static void | | | |
179 | badvalue(const char *param) | | | |
180 | { | | | |
181 | warnx("invalid %s supplied", param); | | | |
182 | usage(); | | 174 | usage(); |
183 | } | | 175 | } |
184 | | | 176 | |
185 | #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0')) | | 177 | #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0')) |
186 | | | 178 | |
187 | static void | | 179 | static void |
188 | setthetime(const char *p) | | 180 | setthetime(const char *p) |
189 | { | | 181 | { |
190 | struct timeval tv; | | 182 | struct timeval tv; |
191 | time_t new_time; | | 183 | time_t new_time; |
192 | struct tm *lt; | | 184 | struct tm *lt; |
193 | const char *dot, *t; | | 185 | const char *dot, *t, *op; |
194 | size_t len; | | 186 | size_t len; |
195 | int yearset; | | 187 | int yearset; |
196 | | | 188 | |
197 | for (t = p, dot = NULL; *t; ++t) { | | 189 | for (t = p, dot = NULL; *t; ++t) { |
198 | if (isdigit((unsigned char)*t)) | | 190 | if (*t == '.') { |
199 | continue; | | 191 | if (dot == NULL) { |
200 | if (*t == '.' && dot == NULL) { | | 192 | dot = t; |
201 | dot = t; | | 193 | } else { |
202 | continue; | | 194 | badcanotime("Unexpected dot", p, t - p); |
| | | 195 | } |
| | | 196 | } else if (!isdigit((unsigned char)*t)) { |
| | | 197 | badcanotime("Expected digit", p, t - p); |
203 | } | | 198 | } |
204 | badformat(); | | | |
205 | } | | 199 | } |
206 | | | 200 | |
207 | if ((lt = localtime(&tval)) == NULL) | | 201 | if ((lt = localtime(&tval)) == NULL) |
208 | err(EXIT_FAILURE, "localtime %lld failed", (long long)tval); | | 202 | err(EXIT_FAILURE, "%lld: localtime", (long long)tval); |
209 | | | 203 | |
210 | lt->tm_isdst = -1; /* Divine correct DST */ | | 204 | lt->tm_isdst = -1; /* Divine correct DST */ |
211 | | | 205 | |
212 | if (dot != NULL) { /* .ss */ | | 206 | if (dot != NULL) { /* .ss */ |
213 | len = strlen(dot); | | 207 | len = strlen(dot); |
214 | if (len != 3) | | 208 | if (len > 3) { |
215 | badformat(); | | 209 | badcanotime("Unexpected digit after seconds field", |
| | | 210 | p, strlen(p) - 1); |
| | | 211 | } else if (len < 3) { |
| | | 212 | badcanotime("Expected digit in seconds field", |
| | | 213 | p, strlen(p)); |
| | | 214 | } |
216 | ++dot; | | 215 | ++dot; |
217 | lt->tm_sec = ATOI2(dot); | | 216 | lt->tm_sec = ATOI2(dot); |
218 | if (lt->tm_sec > 61) | | 217 | if (lt->tm_sec > 61) |
219 | badvalue("seconds"); | | 218 | badcanotime("Seconds out of range", p, strlen(p) - 1); |
220 | } else { | | 219 | } else { |
221 | len = 0; | | 220 | len = 0; |
222 | lt->tm_sec = 0; | | 221 | lt->tm_sec = 0; |
223 | } | | 222 | } |
224 | | | 223 | |
| | | 224 | op = p; |
225 | yearset = 0; | | 225 | yearset = 0; |
226 | switch (strlen(p) - len) { | | 226 | switch (strlen(p) - len) { |
227 | case 12: /* cc */ | | 227 | case 12: /* cc */ |
228 | lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE; | | 228 | lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE; |
229 | if (lt->tm_year < 0) | | 229 | if (lt->tm_year < 0) |
230 | badtime(); | | 230 | badcanotime("Year before 1900", op, p - op + 1); |
231 | yearset = 1; | | 231 | yearset = 1; |
232 | /* FALLTHROUGH */ | | 232 | /* FALLTHROUGH */ |
233 | case 10: /* yy */ | | 233 | case 10: /* yy */ |
234 | if (yearset) { | | 234 | if (yearset) { |
235 | lt->tm_year += ATOI2(p); | | 235 | lt->tm_year += ATOI2(p); |
236 | } else { | | 236 | } else { |
237 | yearset = ATOI2(p); | | 237 | yearset = ATOI2(p); |
238 | if (yearset < 69) | | 238 | if (yearset < 69) |
239 | lt->tm_year = yearset + 2000 - TM_YEAR_BASE; | | 239 | lt->tm_year = yearset + 2000 - TM_YEAR_BASE; |
240 | else | | 240 | else |
241 | lt->tm_year = yearset + 1900 - TM_YEAR_BASE; | | 241 | lt->tm_year = yearset + 1900 - TM_YEAR_BASE; |
242 | } | | 242 | } |
243 | /* FALLTHROUGH */ | | 243 | /* FALLTHROUGH */ |
244 | case 8: /* mm */ | | 244 | case 8: /* mm */ |
245 | lt->tm_mon = ATOI2(p); | | 245 | lt->tm_mon = ATOI2(p); |
246 | if (lt->tm_mon > 12 || lt->tm_mon == 0) | | 246 | if (lt->tm_mon > 12 || lt->tm_mon == 0) |
247 | badvalue("month"); | | 247 | badcanotime("Month out of range", op, p - op - 1); |
248 | --lt->tm_mon; /* time struct is 0 - 11 */ | | 248 | --lt->tm_mon; /* time struct is 0 - 11 */ |
249 | /* FALLTHROUGH */ | | 249 | /* FALLTHROUGH */ |
250 | case 6: /* dd */ | | 250 | case 6: /* dd */ |
251 | lt->tm_mday = ATOI2(p); | | 251 | lt->tm_mday = ATOI2(p); |
252 | switch (lt->tm_mon) { | | 252 | switch (lt->tm_mon) { |
253 | case 0: | | 253 | case 0: |
254 | case 2: | | 254 | case 2: |
255 | case 4: | | 255 | case 4: |
256 | case 6: | | 256 | case 6: |
257 | case 7: | | 257 | case 7: |
258 | case 9: | | 258 | case 9: |
259 | case 11: | | 259 | case 11: |
260 | if (lt->tm_mday > 31 || lt->tm_mday == 0) | | 260 | if (lt->tm_mday > 31 || lt->tm_mday == 0) |
261 | badvalue("day of month"); | | 261 | badcanotime("Day out of range (max 31)", |
| | | 262 | op, p - op - 1); |
262 | break; | | 263 | break; |
263 | case 3: | | 264 | case 3: |
264 | case 5: | | 265 | case 5: |
265 | case 8: | | 266 | case 8: |
266 | case 10: | | 267 | case 10: |
267 | if (lt->tm_mday > 30 || lt->tm_mday == 0) | | 268 | if (lt->tm_mday > 30 || lt->tm_mday == 0) |
268 | badvalue("day of month"); | | 269 | badcanotime("Day out of range (max 30)", |
| | | 270 | op, p - op - 1); |
269 | break; | | 271 | break; |
270 | case 1: | | 272 | case 1: |
271 | if (lt->tm_mday > 29 || lt->tm_mday == 0 || | | 273 | if (isleap(lt->tm_year + TM_YEAR_BASE)) { |
272 | (lt->tm_mday == 29 && | | 274 | if (lt->tm_mday > 29 || lt->tm_mday == 0) { |
273 | !isleap(lt->tm_year + TM_YEAR_BASE))) | | 275 | badcanotime("Day out of range " |
274 | badvalue("day of month"); | | 276 | "(max 29)", |
| | | 277 | op, p - op - 1); |
| | | 278 | } |
| | | 279 | } else { |
| | | 280 | if (lt->tm_mday > 28 || lt->tm_mday == 0) { |
| | | 281 | badcanotime("Day out of range " |
| | | 282 | "(max 28)", |
| | | 283 | op, p - op - 1); |
| | | 284 | } |
| | | 285 | } |
275 | break; | | 286 | break; |
276 | default: | | 287 | default: |
277 | badvalue("month"); | | 288 | /* |
278 | break; | | 289 | * If the month was given, it's already been |
| | | 290 | * checked. If a bad value came back from |
| | | 291 | * localtime, something's badly broken. |
| | | 292 | * (make this an assertion?) |
| | | 293 | */ |
| | | 294 | errx(EXIT_FAILURE, "localtime gave invalid month %d", |
| | | 295 | lt->tm_mon); |
279 | } | | 296 | } |
280 | /* FALLTHROUGH */ | | 297 | /* FALLTHROUGH */ |
281 | case 4: /* hh */ | | 298 | case 4: /* hh */ |
282 | lt->tm_hour = ATOI2(p); | | 299 | lt->tm_hour = ATOI2(p); |
283 | if (lt->tm_hour > 23) | | 300 | if (lt->tm_hour > 23) |
284 | badvalue("hour"); | | 301 | badcanotime("Hour out of range", op, p - op - 1); |
285 | /* FALLTHROUGH */ | | 302 | /* FALLTHROUGH */ |
286 | case 2: /* mm */ | | 303 | case 2: /* mm */ |
287 | lt->tm_min = ATOI2(p); | | 304 | lt->tm_min = ATOI2(p); |
288 | if (lt->tm_min > 59) | | 305 | if (lt->tm_min > 59) |
289 | badvalue("minute"); | | 306 | badcanotime("Minute out of range", op, p - op - 1); |
290 | break; | | 307 | break; |
291 | case 0: /* was just .sss */ | | 308 | case 0: /* was just .sss */ |
292 | if (len != 0) | | 309 | if (len != 0) |
293 | break; | | 310 | break; |
294 | /* FALLTHROUGH */ | | 311 | /* FALLTHROUGH */ |
295 | default: | | 312 | default: |
296 | badformat(); | | 313 | if (strlen(p) - len > 12) { |
| | | 314 | badcanotime("Too many digits", p, 12); |
| | | 315 | } else { |
| | | 316 | badcanotime("Not enough digits", p, strlen(p) - len); |
| | | 317 | } |
297 | } | | 318 | } |
298 | | | 319 | |
299 | /* convert broken-down time to UTC clock time */ | | 320 | /* convert broken-down time to UTC clock time */ |
300 | if ((new_time = mktime(lt)) == -1) | | 321 | if ((new_time = mktime(lt)) == -1) { |
301 | badtime(); | | 322 | /* Can this actually happen? */ |
| | | 323 | err(EXIT_FAILURE, "%s: mktime", op); |
| | | 324 | } |
302 | | | 325 | |
303 | /* if jflag is set, don't actually change the time, just return */ | | 326 | /* if jflag is set, don't actually change the time, just return */ |
304 | if (jflag) { | | 327 | if (jflag) { |
305 | tval = new_time; | | 328 | tval = new_time; |
306 | return; | | 329 | return; |
307 | } | | 330 | } |
308 | | | 331 | |
309 | /* set the time */ | | 332 | /* set the time */ |
310 | if (nflag || netsettime(new_time)) { | | 333 | if (nflag || netsettime(new_time)) { |
311 | logwtmp("|", "date", ""); | | 334 | logwtmp("|", "date", ""); |
312 | if (aflag) { | | 335 | if (aflag) { |
313 | tv.tv_sec = new_time - tval; | | 336 | tv.tv_sec = new_time - tval; |
314 | tv.tv_usec = 0; | | 337 | tv.tv_usec = 0; |