Mon Sep 1 21:42:21 2014 UTC ()
Provide real error messages.
Inspired by PR 49169 from David H. Gutteridge, but a much broader patch :-)


(dholland)
diff -r1.60 -r1.61 src/bin/date/date.c

cvs diff -r1.60 -r1.61 src/bin/date/date.c (expand / switch to unified diff)

--- src/bin/date/date.c 2011/08/27 12:55:09 1.60
+++ src/bin/date/date.c 2014/09/01 21:42:21 1.61
@@ -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
41static char sccsid[] = "@(#)date.c 8.2 (Berkeley) 4/28/95"; 41static 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
66static time_t tval; 66static time_t tval;
67static int aflag, jflag, rflag, nflag; 67static 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 *); 
72static void setthetime(const char *); 70static void setthetime(const char *);
73__dead static void usage(void); 71__dead static void usage(void);
74 72
75int 73int
76main(int argc, char *argv[]) 74main(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) {
98badarg: 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;
160bad: 164bad:
161 err(EXIT_FAILURE, "Cannot allocate format buffer"); 165 err(EXIT_FAILURE, "Cannot allocate format buffer");
162} 166}
163 167
164static void 168static void
165badformat(void) 169badcanotime(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 
171static void 
172badtime(void) 
173{ 
174 errx(EXIT_FAILURE, "illegal time"); 
175 /* NOTREACHED */ 
176} 
177 
178static void 
179badvalue(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
187static void 179static void
188setthetime(const char *p) 180setthetime(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;