git: d05e43bc0d57 - main - pax: update date parsing code (from OpenBSD)

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Wed, 19 Oct 2022 17:04:17 UTC
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=d05e43bc0d5705919d057793aaa3e6d3df65f815

commit d05e43bc0d5705919d057793aaa3e6d3df65f815
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2022-10-19 17:02:45 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2022-10-19 17:02:45 +0000

    pax: update date parsing code (from OpenBSD)
    
    Sponsored by:   Klara, Inc.
    MFC after:      1 week
---
 bin/pax/pax.1      |  25 +++++++-----
 bin/pax/sel_subs.c | 116 +++++++++++++++++++++++++++++------------------------
 bin/pax/sel_subs.h |   2 +-
 3 files changed, 78 insertions(+), 65 deletions(-)

diff --git a/bin/pax/pax.1 b/bin/pax/pax.1
index abfbacee78b3..b503e1562f8a 100644
--- a/bin/pax/pax.1
+++ b/bin/pax/pax.1
@@ -33,7 +33,7 @@
 .\"	@(#)pax.1	8.4 (Berkeley) 4/18/94
 .\" $FreeBSD$
 .\"
-.Dd December 29, 2018
+.Dd October 19, 2022
 .Dt PAX 1
 .Os
 .Sh NAME
@@ -933,28 +933,31 @@ changed during a specified time range will be archived).
 A time range is made up of six different fields and each field must contain two
 digits.
 The format is:
-.Dl [yy[mm[dd[hh]]]]mm[.ss]
+.Pp
+.Dl [[[[[cc]yy]mm]dd]HH]MM[.SS]
+.Pp
 Where
-.Cm yy
+.Ar cc
+is the first two digits of the year (the century),
+.Ar yy
 is the last two digits of the year,
 the first
-.Cm mm
+.Ar mm
 is the month (from 01 to 12),
-.Cm dd
+.Ar dd
 is the day of the month (from 01 to 31),
-.Cm hh
+.Ar HH
 is the hour of the day (from 00 to 23),
-the second
-.Cm mm
+.Ar MM
 is the minute (from 00 to 59),
 and
-.Cm ss
+.Ar SS
 is the seconds (from 00 to 59).
 The minute field
-.Cm mm
+.Ar MM
 is required, while the other fields are optional and must be added in the
 following order:
-.Dl Cm hh , dd , mm , yy .
+.Ar HH , dd , mm , yy , cc .
 The
 .Cm ss
 field may be added independently of the other fields.
diff --git a/bin/pax/sel_subs.c b/bin/pax/sel_subs.c
index 12975f21946f..7ef5bec523a5 100644
--- a/bin/pax/sel_subs.c
+++ b/bin/pax/sel_subs.c
@@ -44,17 +44,20 @@ __FBSDID("$FreeBSD$");
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/stat.h>
-#include <pwd.h>
+
+#include <ctype.h>
 #include <grp.h>
+#include <pwd.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <strings.h>
-#include <stdlib.h>
+
 #include "pax.h"
 #include "sel_subs.h"
 #include "extern.h"
 
-static int str_sec(char *, time_t *);
+static int str_sec(const char *, time_t *);
 static int usr_match(ARCHD *);
 static int grp_match(ARCHD *);
 static int trng_match(ARCHD *);
@@ -309,7 +312,7 @@ grp_match(ARCHD *arcn)
  * trng_add()
  *	add a time range match to the time range list.
  *	This is a non-standard pax option. Lower and upper ranges are in the
- *	format: [yy[mm[dd[hh]]]]mm[.ss] and are comma separated.
+ *	format: [[[[[cc]yy]mm]dd]HH]MM[.SS] and are comma separated.
  *	Time ranges are based on current time, so 1234 would specify a time of
  *	12:34 today.
  * Return:
@@ -446,7 +449,7 @@ trng_add(char *str)
 	return(0);
 
     out:
-	paxwarn(1, "Time range format is: [yy[mm[dd[hh]]]]mm[.ss][/[c][m]]");
+	paxwarn(1, "Time range format is: [[[[[cc]yy]mm]dd]HH]MM[.SS][/[c][m]]");
 	return(-1);
 }
 
@@ -520,80 +523,87 @@ trng_match(ARCHD *arcn)
 
 /*
  * str_sec()
- *	Convert a time string in the format of [[[[yy[mm[dd[hh]mm[.ss] to gmt
- *	seconds. Tval already has current time loaded into it at entry.
+ *	Convert a time string in the format of [[[[[cc]yy]mm]dd]HH]MM[.SS] to
+ *	seconds UTC. Tval already has current time loaded into it at entry.
  * Return:
  *	0 if converted ok, -1 otherwise
  */
 
 static int
-str_sec(char *str, time_t *tval)
+str_sec(const char *p, time_t *tval)
 {
 	struct tm *lt;
-	char *dot = NULL;
+	const char *dot, *t;
+	size_t len;
+	int bigyear;
+	int yearset;
+
+	yearset = 0;
+	len = strlen(p);
+
+	for (t = p, dot = NULL; *t; ++t) {
+		if (isdigit((unsigned char)*t))
+			continue;
+		if (*t == '.' && dot == NULL) {
+			dot = t;
+			continue;
+		}
+		return(-1);
+	}
 
 	lt = localtime(tval);
-	if ((dot = strchr(str, '.')) != NULL) {
-		/*
-		 * seconds (.ss)
-		 */
-		*dot++ = '\0';
-		if (strlen(dot) != 2)
+
+	if (dot != NULL) {			/* .SS */
+		if (strlen(++dot) != 2)
 			return(-1);
-		if ((lt->tm_sec = ATOI2(dot)) > 61)
+		lt->tm_sec = ATOI2(dot);
+		if (lt->tm_sec > 61)
 			return(-1);
+		len -= 3;
 	} else
 		lt->tm_sec = 0;
 
-	switch (strlen(str)) {
-	case 10:
-		/*
-		 * year (yy)
-		 * watch out for year 2000
-		 */
-		if ((lt->tm_year = ATOI2(str)) < 69)
-			lt->tm_year += 100;
-		str += 2;
+	switch (len) {
+	case 12:				/* cc */
+		bigyear = ATOI2(p);
+		lt->tm_year = (bigyear * 100) - 1900;
+		yearset = 1;
 		/* FALLTHROUGH */
-	case 8:
-		/*
-		 * month (mm)
-		 * watch out months are from 0 - 11 internally
-		 */
-		if ((lt->tm_mon = ATOI2(str)) > 12)
+	case 10:				/* yy */
+		if (yearset) {
+			lt->tm_year += ATOI2(p);
+		} else {
+			lt->tm_year = ATOI2(p);
+			if (lt->tm_year < 69)		/* hack for 2000 ;-} */
+				lt->tm_year += (2000 - 1900);
+		}
+		/* FALLTHROUGH */
+	case 8:					/* mm */
+		lt->tm_mon = ATOI2(p);
+		if ((lt->tm_mon > 12) || !lt->tm_mon)
 			return(-1);
-		--lt->tm_mon;
-		str += 2;
+		--lt->tm_mon;			/* time struct is 0 - 11 */
 		/* FALLTHROUGH */
-	case 6:
-		/*
-		 * day (dd)
-		 */
-		if ((lt->tm_mday = ATOI2(str)) > 31)
+	case 6:					/* dd */
+		lt->tm_mday = ATOI2(p);
+		if ((lt->tm_mday > 31) || !lt->tm_mday)
 			return(-1);
-		str += 2;
 		/* FALLTHROUGH */
-	case 4:
-		/*
-		 * hour (hh)
-		 */
-		if ((lt->tm_hour = ATOI2(str)) > 23)
+	case 4:					/* HH */
+		lt->tm_hour = ATOI2(p);
+		if (lt->tm_hour > 23)
 			return(-1);
-		str += 2;
 		/* FALLTHROUGH */
-	case 2:
-		/*
-		 * minute (mm)
-		 */
-		if ((lt->tm_min = ATOI2(str)) > 59)
+	case 2:					/* MM */
+		lt->tm_min = ATOI2(p);
+		if (lt->tm_min > 59)
 			return(-1);
 		break;
 	default:
 		return(-1);
 	}
-	/*
-	 * convert broken-down time to GMT clock time seconds
-	 */
+
+	/* convert broken-down time to UTC clock time seconds */
 	if ((*tval = mktime(lt)) == -1)
 		return(-1);
 	return(0);
diff --git a/bin/pax/sel_subs.h b/bin/pax/sel_subs.h
index 7e1ddeedcb87..a628e6981b1a 100644
--- a/bin/pax/sel_subs.h
+++ b/bin/pax/sel_subs.h
@@ -57,7 +57,7 @@ typedef struct grpt {
  * data structure for storing user supplied time ranges (-T option)
  */
 
-#define ATOI2(s)	((((s)[0] - '0') * 10) + ((s)[1] - '0'))
+#define ATOI2(ar)	((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
 
 typedef struct time_rng {
 	time_t		low_time;	/* lower inclusive time limit */