Module Name:    src
Committed By:   dholland
Date:           Mon Sep  1 21:42:21 UTC 2014

Modified Files:
        src/bin/date: date.c

Log Message:
Provide real error messages.
Inspired by PR 49169 from David H. Gutteridge, but a much broader patch :-)


To generate a diff of this commit:
cvs rdiff -u -r1.60 -r1.61 src/bin/date/date.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/bin/date/date.c
diff -u src/bin/date/date.c:1.60 src/bin/date/date.c:1.61
--- src/bin/date/date.c:1.60	Sat Aug 27 12:55:09 2011
+++ src/bin/date/date.c	Mon Sep  1 21:42:21 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: date.c,v 1.60 2011/08/27 12:55:09 joerg Exp $ */
+/* $NetBSD: date.c,v 1.61 2014/09/01 21:42:21 dholland Exp $ */
 
 /*
  * Copyright (c) 1985, 1987, 1988, 1993
@@ -40,7 +40,7 @@ __COPYRIGHT(
 #if 0
 static char sccsid[] = "@(#)date.c	8.2 (Berkeley) 4/28/95";
 #else
-__RCSID("$NetBSD: date.c,v 1.60 2011/08/27 12:55:09 joerg Exp $");
+__RCSID("$NetBSD: date.c,v 1.61 2014/09/01 21:42:21 dholland Exp $");
 #endif
 #endif /* not lint */
 
@@ -66,9 +66,7 @@ __RCSID("$NetBSD: date.c,v 1.60 2011/08/
 static time_t tval;
 static int aflag, jflag, rflag, nflag;
 
-__dead static void badformat(void);
-__dead static void badtime(void);
-__dead static void badvalue(const char *);
+__dead static void badcanotime(const char *, const char *, size_t);
 static void setthetime(const char *);
 __dead static void usage(void);
 
@@ -94,9 +92,10 @@ main(int argc, char *argv[])
 		case 'd':
 			rflag = 1;
 			tval = parsedate(optarg, NULL, NULL);
-			if (tval == -1) 
-badarg:				 errx(EXIT_FAILURE,
-				    "Cannot parse `%s'", optarg);
+			if (tval == -1) {
+				errx(EXIT_FAILURE,
+				    "%s: Unrecognized date format", optarg);
+			}
 			break;
 		case 'j':		/* don't set time */
 			jflag = 1;
@@ -105,13 +104,18 @@ badarg:				 errx(EXIT_FAILURE,
 			nflag = 1;
 			break;
 		case 'r':		/* user specified seconds */
+			if (optarg[0] == '\0') {
+				errx(EXIT_FAILURE, "<empty>: Invalid number");
+			}
 			errno = 0;
 			val = strtoll(optarg, &buf, 0);
-			if (optarg[0] == '\0' || *buf != '\0')
-				goto badarg;
-			if (errno == ERANGE && (val == LLONG_MAX ||
-			    val == LLONG_MIN))
-				err(EXIT_FAILURE, "Bad number `%s'", optarg);
+			if (errno) {
+				err(EXIT_FAILURE, "%s", optarg);
+			}
+			if (optarg[0] == '\0' || *buf != '\0') {
+				errx(EXIT_FAILURE,
+				    "%s: Invalid number", optarg);
+			}
 			rflag = 1;
 			tval = (time_t)val;
 			break;
@@ -148,7 +152,7 @@ badarg:				 errx(EXIT_FAILURE,
 		goto bad;
 
 	if ((tm = localtime(&tval)) == NULL)
-		err(EXIT_FAILURE, "localtime %lld failed", (long long)tval);
+		err(EXIT_FAILURE, "%lld: localtime", (long long)tval);
 
 	while (strftime(buf, bufsiz, format, tm) == 0)
 		if ((buf = realloc(buf, bufsiz <<= 1)) == NULL)
@@ -162,23 +166,11 @@ bad:
 }
 
 static void
-badformat(void)
+badcanotime(const char *msg, const char *val, size_t where)
 {
-	warnx("illegal time format");
-	usage();
-}
-
-static void
-badtime(void)
-{
-	errx(EXIT_FAILURE, "illegal time");
-	/* NOTREACHED */
-}
-
-static void
-badvalue(const char *param)
-{
-	warnx("invalid %s supplied", param);
+	warnx("%s in canonical time", msg);
+	warnx("%s", val);
+	warnx("%*s", (int)where + 1, "^");
 	usage();
 }
 
@@ -190,44 +182,52 @@ setthetime(const char *p)
 	struct timeval tv;
 	time_t new_time;
 	struct tm *lt;
-	const char *dot, *t;
+	const char *dot, *t, *op;
 	size_t len;
 	int yearset;
 
 	for (t = p, dot = NULL; *t; ++t) {
-		if (isdigit((unsigned char)*t))
-			continue;
-		if (*t == '.' && dot == NULL) {
-			dot = t;
-			continue;
+		if (*t == '.') {
+			if (dot == NULL) {
+				dot = t;
+			} else {
+				badcanotime("Unexpected dot", p, t - p);
+			}
+		} else if (!isdigit((unsigned char)*t)) {
+			badcanotime("Expected digit", p, t - p);
 		}
-		badformat();
 	}
 
 	if ((lt = localtime(&tval)) == NULL)
-		err(EXIT_FAILURE, "localtime %lld failed", (long long)tval);
+		err(EXIT_FAILURE, "%lld: localtime", (long long)tval);
 
 	lt->tm_isdst = -1;			/* Divine correct DST */
 
 	if (dot != NULL) {			/* .ss */
 		len = strlen(dot);
-		if (len != 3)
-			badformat();
+		if (len > 3) {
+			badcanotime("Unexpected digit after seconds field",
+				    p, strlen(p) - 1);
+		} else if (len < 3) {
+			badcanotime("Expected digit in seconds field",
+				    p, strlen(p));
+		}
 		++dot;
 		lt->tm_sec = ATOI2(dot);
 		if (lt->tm_sec > 61)
-			badvalue("seconds");
+			badcanotime("Seconds out of range", p, strlen(p) - 1);
 	} else {
 		len = 0;
 		lt->tm_sec = 0;
 	}
 
+	op = p;
 	yearset = 0;
 	switch (strlen(p) - len) {
 	case 12:				/* cc */
 		lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE;
 		if (lt->tm_year < 0)
-			badtime();
+			badcanotime("Year before 1900", op, p - op + 1);
 		yearset = 1;
 		/* FALLTHROUGH */
 	case 10:				/* yy */
@@ -244,7 +244,7 @@ setthetime(const char *p)
 	case 8:					/* mm */
 		lt->tm_mon = ATOI2(p);
 		if (lt->tm_mon > 12 || lt->tm_mon == 0)
-			badvalue("month");
+			badcanotime("Month out of range", op, p - op - 1);
 		--lt->tm_mon;			/* time struct is 0 - 11 */
 		/* FALLTHROUGH */
 	case 6:					/* dd */
@@ -258,47 +258,70 @@ setthetime(const char *p)
 		case 9:
 		case 11:
 			if (lt->tm_mday > 31 || lt->tm_mday == 0)
-				badvalue("day of month");
+				badcanotime("Day out of range (max 31)",
+					    op, p - op - 1);
 			break;
 		case 3:
 		case 5:
 		case 8:
 		case 10:
 			if (lt->tm_mday > 30 || lt->tm_mday == 0)
-				badvalue("day of month");
+				badcanotime("Day out of range (max 30)",
+					    op, p - op - 1);
 			break;
 		case 1:
-			if (lt->tm_mday > 29 || lt->tm_mday == 0 ||
-			    (lt->tm_mday == 29 &&
-			     !isleap(lt->tm_year + TM_YEAR_BASE)))
-				badvalue("day of month");
+			if (isleap(lt->tm_year + TM_YEAR_BASE)) {
+				if (lt->tm_mday > 29 || lt->tm_mday == 0) {
+					badcanotime("Day out of range "
+						    "(max 29)",
+						    op, p - op - 1);
+				}
+			} else {
+				if (lt->tm_mday > 28 || lt->tm_mday == 0) {
+					badcanotime("Day out of range "
+						    "(max 28)",
+						    op, p - op - 1);
+				}
+			}
 			break;
 		default:
-			badvalue("month");
-			break;
+			/*
+			 * If the month was given, it's already been
+			 * checked.  If a bad value came back from
+			 * localtime, something's badly broken.
+			 * (make this an assertion?)
+			 */
+			errx(EXIT_FAILURE, "localtime gave invalid month %d",
+			    lt->tm_mon);
 		}
 		/* FALLTHROUGH */
 	case 4:					/* hh */
 		lt->tm_hour = ATOI2(p);
 		if (lt->tm_hour > 23)
-			badvalue("hour");
+			badcanotime("Hour out of range", op, p - op - 1);
 		/* FALLTHROUGH */
 	case 2:					/* mm */
 		lt->tm_min = ATOI2(p);
 		if (lt->tm_min > 59)
-			badvalue("minute");
+			badcanotime("Minute out of range", op, p - op - 1);
 		break;
 	case 0:					/* was just .sss */
 		if (len != 0)
 			break;
 		/* FALLTHROUGH */
 	default:
-		badformat();
+	    if (strlen(p) - len > 12) {
+		    badcanotime("Too many digits", p, 12);
+	    } else {
+		    badcanotime("Not enough digits", p, strlen(p) - len);
+	    }
 	}
 
 	/* convert broken-down time to UTC clock time */
-	if ((new_time = mktime(lt)) == -1)
-		badtime();
+	if ((new_time = mktime(lt)) == -1) {
+		/* Can this actually happen? */
+		err(EXIT_FAILURE, "%s: mktime", op);
+	}
 
 	/* if jflag is set, don't actually change the time, just return */
 	if (jflag) {

Reply via email to