Bug 30309

Summary: New FIND(1) option
Product: Base System Reporter: Nils M Holm <nmh>
Component: binAssignee: ru <ru>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 4.2-RELEASE   
Hardware: Any   
OS: Any   
Attachments:
Description Flags
file.diff none

Description Nils M Holm 2001-09-04 13:50:01 UTC
	I have added the -nt (newer than) option to FIND(1) which
	allows to search for files with at least/at most a given age.
	For example,

	find . -nt 5h30m

	will list files with an age of at least 5 hours and 30 minutes.

	I use this option for cleaning up spool directories.

	Man pages are updated.
Comment 1 dima 2001-09-04 13:58:10 UTC
Nils M Holm <nmh@t3x.org> wrote:
> >Description:
> 
> 	I have added the -nt (newer than) option to FIND(1) which
> 	allows to search for files with at least/at most a given age.
> 	For example,
> 
> 	find . -nt 5h30m
> 
> 	will list files with an age of at least 5 hours and 30 minutes.
> 
> 	I use this option for cleaning up spool directories.
> 
> 	Man pages are updated.

We have this in -current:

     -newer file
             True if the current file has a more recent last modification time
             than file.
Comment 2 ru freebsd_committer freebsd_triage 2001-09-04 13:59:30 UTC
On Tue, Sep 04, 2001 at 02:45:11PM +0200, Nils M Holm wrote:
> 
> I have added the -nt (newer than) option to FIND(1) which
> allows to search for files with at least/at most a given age.
> For example,
> 
> find . -nt 5h30m
> 
> will list files with an age of at least 5 hours and 30 minutes.
> 
> I use this option for cleaning up spool directories.
> 
> Man pages are updated.
> 
What do you think of just extending the -mtime functionality
so that it accepts time units in hours and minutes?  Then the
above would be equivalent to:

	find . ! -mtime +5h30m


Cheers,
-- 
Ruslan Ermilov		Oracle Developer/DBA,
ru@sunbay.com		Sunbay Software AG,
ru@FreeBSD.org		FreeBSD committer,
+380.652.512.251	Simferopol, Ukraine

http://www.FreeBSD.org	The Power To Serve
http://www.oracle.com	Enabling The Information Age
Comment 3 dima 2001-09-04 14:09:29 UTC
Dima Dorfman <dima@unixfreak.org> wrote:
> The following reply was made to PR bin/30309; it has been noted by GNATS.
> 
> From: Dima Dorfman <dima@unixfreak.org>
> To: nmh@t3x.org
> Cc: FreeBSD-gnats-submit@freebsd.org
> Subject: Re: bin/30309: New FIND(1) option 
> Date: Tue, 04 Sep 2001 05:58:10 -0700
> 
>  Nils M Holm <nmh@t3x.org> wrote:
>  > >Description:
>  > 
>  > 	I have added the -nt (newer than) option to FIND(1) which
>  > 	allows to search for files with at least/at most a given age.
>  > 	For example,
>  > 
>  > 	find . -nt 5h30m
>  > 
>  > 	will list files with an age of at least 5 hours and 30 minutes.
>  > 
>  > 	I use this option for cleaning up spool directories.
>  > 
>  > 	Man pages are updated.
>  
>  We have this in -current:
>  
>       -newer file
>               True if the current file has a more recent last modification time
>               than file.

Actually, as ru@ pointed out, what you propose is more like -mtime,
except that the latter doesn't support units.

Sorry about that.
Comment 4 ru freebsd_committer freebsd_triage 2001-09-04 14:19:59 UTC
On Tue, Sep 04, 2001 at 04:12:10PM +0300, Peter Pentchev wrote:
> On Tue, Sep 04, 2001 at 06:10:01AM -0700, Ruslan Ermilov wrote:
> > The following reply was made to PR bin/30309; it has been noted by GNATS.
> > 
> > From: Ruslan Ermilov <ru@FreeBSD.ORG>
> > To: Nils M Holm <nmh@t3x.org>
> > Cc: FreeBSD-gnats-submit@FreeBSD.ORG
> > Subject: Re: bin/30309: New FIND(1) option
> > Date: Tue, 4 Sep 2001 15:59:30 +0300
> > 
> >  On Tue, Sep 04, 2001 at 02:45:11PM +0200, Nils M Holm wrote:
> >  > 
> >  > I have added the -nt (newer than) option to FIND(1) which
> >  > allows to search for files with at least/at most a given age.
> >  > For example,
> >  > 
> >  > find . -nt 5h30m
> >  > 
> >  > will list files with an age of at least 5 hours and 30 minutes.
> >  > 
> >  > I use this option for cleaning up spool directories.
> >  > 
> >  > Man pages are updated.
> >  > 
> >  What do you think of just extending the -mtime functionality
> >  so that it accepts time units in hours and minutes?  Then the
> >  above would be equivalent to:
> >  
> >  	find . ! -mtime +5h30m
> 
> I was considering such a reply; however, it would seem that
> the semantics of the -[acm]time option are not quite the same
> as those of the proposed -nt option; -[acm]time only returns
> true if the time of the file is *exactly* so many days old;
> -nt would return true if the file was *at most* so many days old.
> 
Don't you see the plus (``+'') sign above?

: All primaries which take a numeric argument allow the number
: to be preceded by a plus sign (``+'') or a minus sign (``-'').
: A preceding plus sign means ``more than n'', a preceding minus
: sign means ``less than n'' and neither means ``exactly n''.


-- 
Ruslan Ermilov		Oracle Developer/DBA,
ru@sunbay.com		Sunbay Software AG,
ru@FreeBSD.org		FreeBSD committer,
+380.652.512.251	Simferopol, Ukraine

http://www.FreeBSD.org	The Power To Serve
http://www.oracle.com	Enabling The Information Age
Comment 5 Nils M Holm 2001-09-04 14:48:04 UTC
On Tue, 4 Sep 2001, Ruslan Ermilov wrote:
> What do you think of just extending the -mtime functionality
> so that it accepts time units in hours and minutes?  Then the
> above would be equivalent to:
> 
> 	find . ! -mtime +5h30m

Sounds like a good idea. I'll probably do that as soon as I find the
time.

Nils.

-- 
Nils M Holm <nmh@t3x.org> -- http://www.t3x.org
PLEASE ask before sending attachments >10K bytes.
Comment 6 Nils M Holm 2001-09-06 14:17:48 UTC
> On Tue, Sep 04, 2001 at 02:45:11PM +0200, Nils M Holm wrote:
> [...]
> What do you think of just extending the -mtime functionality
> so that it accepts time units in hours and minutes?

I have extended the -mtime (and -ctime, -atime) syntax to accept units,
as suggested. In addition to hours and minutes, it also accepts seconds,
days, weeks, months, and years.

When no units are specified, the original semantics are used.

Man pages are updated (but should be proof-read).

I still haven't figured out how to send proper follow-up's to
bug reports, so I send the new diff as a reply.

Nils.

-----[ diff follows ]-----
diff -u /usr/src/usr.bin/find.old/find.1 /usr/src/usr.bin/find/find.1
--- /usr/src/usr.bin/find.old/find.1	Tue Sep  4 14:07:04 2001
+++ /usr/src/usr.bin/find/find.1	Thu Sep  6 15:04:54 2001
@@ -420,6 +420,40 @@
 preceded by a plus sign (``+'') or a minus sign (``\-'').
 A preceding plus sign means ``more than n'', a preceding minus sign means
 ``less than n'' and neither means ``exactly n'' .
+.Pp
+The
+.Ic -atime ,
+.Ic -ctime ,
+and
+.Ic -mtime
+primaries support the following units:
+.Pp
+.Bl -tag -width flag -offset indent -compact
+.It Cm s
+seconds
+.It Cm m
+minutes (60s)
+.It Cm h
+hours (60m)
+.It Cm D
+days (24h)
+.It Cm W
+weeks (7D)
+.It Cm M
+months (30D)
+.It Cm Y
+months (365Y)
+.El
+.Pp
+When a unit is specified, the primaries do not round their argument
+values. Hence
+.Ic -mtime Ar +1
+is not euqal to
+.Ic -mtime Ar +1D .
+Units are most useful when used together with the ``+'' and ``-''
+modifiers. For example,
+.Ic -mtime Ar +5m30s
+is true for all files which are at least 5 minutes and 30 seconds old.
 .Sh OPERATORS
 The primaries may be combined using the following operators.
 The operators are listed in order of decreasing precedence.
diff -u /usr/src/usr.bin/find.old/find.h /usr/src/usr.bin/find/find.h
--- /usr/src/usr.bin/find.old/find.h	Tue Sep  4 14:07:04 2001
+++ /usr/src/usr.bin/find/find.h	Thu Sep  6 14:39:25 2001
@@ -64,6 +64,7 @@
 #define	F_ANY		2			/* perm */
 	int flags;				/* private flags */
 	enum ntype type;			/* plan node type */
+	int t_units;				/* time units flag */
 	union {
 		gid_t _g_data;			/* gid */
 		ino_t _i_data;			/* inode */
diff -u /usr/src/usr.bin/find.old/function.c /usr/src/usr.bin/find/function.c
--- /usr/src/usr.bin/find.old/function.c	Tue Sep  4 14:07:04 2001
+++ /usr/src/usr.bin/find/function.c	Thu Sep  6 14:52:09 2001
@@ -58,6 +58,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <ctype.h>
 #include <unistd.h>
 
 #include "find.h"
@@ -121,6 +122,86 @@
 }
 
 /*
+ * find_parsetime --
+ *	Parse a string of the form [+-]([0-9][hmsYMWD])* and return the value.
+ */
+static time_t
+find_parsetime(plan, option, vp)
+	PLAN *plan;
+	char *option, *vp;
+{
+	time_t nsec, a;
+	char *s;
+
+	/* Determine comparison from leading + or -. */
+	s = vp;
+	switch (*s) {
+	case '+':
+		++s;
+		plan->flags = F_GREATER;
+		break;
+	case '-':
+		++s;
+		plan->flags = F_LESSTHAN;
+		break;
+	default:
+		plan->flags = F_EQUAL;
+		break;
+	}
+
+	if (!isdigit(*s))
+		errx(1, "%s: %s: illegal time value", option, vp);
+
+	plan->t_units = 0;
+	nsec = 0L;
+	a = 0L;
+	for (; *s; s++) {
+		if (isdigit(*s)) {
+			a = a*10L + *s-'0';
+		}
+		else if (*s == 's') {	/* seconds */
+			nsec += a;
+			a = 0L;
+			plan->t_units = 1;
+		}
+		else if (*s == 'm') {	/* minutes */
+			nsec += a*60L;
+			a = 0L;
+			plan->t_units = 1;
+		}
+		else if (*s == 'h') {	/* hours */
+			nsec += a*3600L;
+			a = 0L;
+			plan->t_units = 1;
+		}
+		else if (*s == 'D') {	/* days */
+			nsec += a*86400L;
+			a = 0L;
+			plan->t_units = 1;
+		}
+		else if (*s == 'W') {	/* weeks */
+			nsec += a*604800L;
+			a = 0L;
+			plan->t_units = 1;
+		}
+		else if (*s == 'M') {	/* months (30D)*/
+			nsec += a*2592000L;
+			a = 0L;
+			plan->t_units = 1;
+		}
+		else if (*s == 'Y') {	/* years (365D)*/
+			nsec += a*31536000L;
+			a = 0L;
+			plan->t_units = 1;
+		}
+		else {
+			errx(1, "%s: %s: bad unit", option, vp);
+		}
+	}
+	return nsec+a;
+}
+
+/*
  * The value of n for the inode times (atime, ctime, and mtime) is a range,
  * i.e. n matches from (n - 1) to n 24 hour periods.  This interacts with
  * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
@@ -175,8 +256,13 @@
 {
 	extern time_t now;
 
-	COMPARE((now - entry->fts_statp->st_atime +
-	    86400 - 1) / 86400, plan->t_data);
+	if (plan->t_units) {
+		COMPARE(now - entry->fts_statp->st_atime, plan->t_data);
+	}
+	else {
+		COMPARE((now - entry->fts_statp->st_atime +
+			86400 - 1) / 86400, plan->t_data);
+	}
 }
 
 PLAN *
@@ -188,7 +274,7 @@
 	ftsoptions &= ~FTS_NOSTAT;
 
 	new = palloc(N_ATIME, f_atime);
-	new->t_data = find_parsenum(new, "-atime", arg, NULL);
+	new->t_data = find_parsetime(new, "-atime", arg);
 	TIME_CORRECT(new, N_ATIME);
 	return (new);
 }
@@ -238,8 +324,13 @@
 {
 	extern time_t now;
 
-	COMPARE((now - entry->fts_statp->st_ctime +
-	    86400 - 1) / 86400, plan->t_data);
+	if (plan->t_units) {
+		COMPARE(now - entry->fts_statp->st_ctime, plan->t_data);
+	}
+	else {
+		COMPARE((now - entry->fts_statp->st_ctime +
+			86400 - 1) / 86400, plan->t_data);
+	}
 }
 
 PLAN *
@@ -251,7 +342,7 @@
 	ftsoptions &= ~FTS_NOSTAT;
 
 	new = palloc(N_CTIME, f_ctime);
-	new->t_data = find_parsenum(new, "-ctime", arg, NULL);
+	new->t_data = find_parsetime(new, "-ctime", arg);
 	TIME_CORRECT(new, N_CTIME);
 	return (new);
 }
@@ -792,8 +883,13 @@
 {
 	extern time_t now;
 
-	COMPARE((now - entry->fts_statp->st_mtime + 86400 - 1) /
-	    86400, plan->t_data);
+	if (plan->t_units) {
+		COMPARE(now - entry->fts_statp->st_mtime, plan->t_data);
+	}
+	else {
+		COMPARE((now - entry->fts_statp->st_mtime +
+			86400 - 1) / 86400, plan->t_data);
+	}
 }
 
 PLAN *
@@ -805,7 +901,7 @@
 	ftsoptions &= ~FTS_NOSTAT;
 
 	new = palloc(N_MTIME, f_mtime);
-	new->t_data = find_parsenum(new, "-mtime", arg, NULL);
+	new->t_data = find_parsetime(new, "-mtime", arg);
 	TIME_CORRECT(new, N_MTIME);
 	return (new);
 }
--------------------------

-- 
Nils M Holm <nmh@t3x.org> -- http://www.t3x.org
PLEASE ask before sending attachments >10K bytes.
Comment 7 ru freebsd_committer freebsd_triage 2001-09-06 18:05:16 UTC
On Thu, Sep 06, 2001 at 03:17:48PM +0200, Nils M Holm wrote:
> > On Tue, Sep 04, 2001 at 02:45:11PM +0200, Nils M Holm wrote:
> > [...]
> > What do you think of just extending the -mtime functionality
> > so that it accepts time units in hours and minutes?
> 
> I have extended the -mtime (and -ctime, -atime) syntax to accept units,
> as suggested. In addition to hours and minutes, it also accepts seconds,
> days, weeks, months, and years.
> 
> When no units are specified, the original semantics are used.
> 
Uh oh, this already applies to 4.2-RELEASE sources, which have
since been heavily modified in -CURRENT and -STABLE.

How about the following patch instead (without the manpage bit)?

Index: find.h
===================================================================
RCS file: /home/ncvs/src/usr.bin/find/find.h,v
retrieving revision 1.12
diff -u -p -r1.12 find.h
--- find.h	2001/09/04 16:09:01	1.12
+++ find.h	2001/09/06 17:04:29
@@ -69,6 +69,7 @@ typedef	struct _plandata *creat_f(struct
 #define	F_MTTYPE	0x00001000
 #define	F_MTUNKNOWN	0x00002000
 #define	F_IGNCASE	0x00010000	/* iname ipath iregex */
+#define	F_EXACTTIME	F_IGNCASE	/* -[acm]time units syntax */
 
 /* node definition */
 typedef struct _plandata {
Index: function.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/find/function.c,v
retrieving revision 1.33
diff -u -p -r1.33 function.c
--- function.c	2001/09/04 16:09:01	1.33
+++ function.c	2001/09/06 17:04:31
@@ -67,7 +67,7 @@ static const char rcsid[] =
 
 time_t get_date __P((char *date, struct timeb *now));
 
-#define	COMPARE(a, b) {							\
+#define	COMPARE(a, b) do {						\
 	switch (plan->flags & F_ELG_MASK) {				\
 	case F_EQUAL:							\
 		return (a == b);					\
@@ -78,7 +78,7 @@ time_t get_date __P((char *date, struct 
 	default:							\
 		abort();						\
 	}								\
-}
+} while(0)
 
 static PLAN *
 palloc(option)
@@ -138,6 +138,82 @@ find_parsenum(plan, option, vp, endch)
 }
 
 /*
+ * find_parsetime --
+ *	Parse a string of the form [+-]([0-9]+[smhdw]?)+ and return the value.
+ */
+static long long
+find_parsetime(plan, option, vp)
+	PLAN *plan;
+	char *option, *vp;
+{
+	long long secs, value;
+	char *str, *unit;	/* Pointer to character ending conversion. */
+
+	/* Determine comparison from leading + or -. */
+	str = vp;
+	switch (*str) {
+	case '+':
+		++str;
+		plan->flags |= F_GREATER;
+		break;
+	case '-':
+		++str;
+		plan->flags |= F_LESSTHAN;
+		break;
+	default:
+		plan->flags |= F_EQUAL;
+		break;
+	}
+
+	value = strtoq(str, &unit, 10);
+	if (value == 0 && unit == str) {
+		errx(1, "%s: %s: illegal time value", option, vp);
+		/* NOTREACHED */
+	}
+	if (*unit == '\0')
+		return value;
+
+	/* Units syntax. */
+	secs = 0;
+	for (;;) {
+		switch(*unit) {
+		case 's':	/* seconds */
+			secs += value;
+			break;
+		case 'm':	/* minutes */
+			secs += value * 60;
+			break;
+		case 'h':	/* hours */
+			secs += value * 3600;
+			break;
+		case 'd':	/* days */
+			secs += value * 86400;
+			break;
+		case 'w':	/* weeks */
+			secs += value * 604800;
+			break;
+		default:
+			errx(1, "%s: %s: bad unit '%c'", option, vp, *unit);
+			/* NOTREACHED */
+		}
+		str = unit + 1;
+		if (*str == '\0')	/* EOS */
+			break;
+		value = strtoq(str, &unit, 10);
+		if (value == 0 && unit == str) {
+			errx(1, "%s: %s: illegal time value", option, vp);
+			/* NOTREACHED */
+		}
+		if (*unit == '\0') {
+			errx(1, "%s: %s: missing trailing unit", option, vp);
+			/* NOTREACHED */
+		}
+	}
+	plan->flags |= F_EXACTTIME;
+	return secs;
+}
+
+/*
  * nextarg --
  *	Check that another argument still exists, return a pointer to it,
  *	and increment the argument vector pointer.
@@ -226,16 +302,31 @@ f_Xtime(plan, entry)
 	FTSENT *entry;
 {
 	extern time_t now;
+	int exact_time;
+
+	exact_time = plan->flags & F_EXACTTIME;
 
 	if (plan->flags & F_TIME_C) {
-		COMPARE((now - entry->fts_statp->st_ctime +
-		    86400 - 1) / 86400, plan->t_data);
+		if (exact_time)
+			COMPARE(now - entry->fts_statp->st_ctime,
+			    plan->t_data);
+		else
+			COMPARE((now - entry->fts_statp->st_ctime +
+			    86400 - 1) / 86400, plan->t_data);
 	} else if (plan->flags & F_TIME_A) {
-		COMPARE((now - entry->fts_statp->st_atime +
-		    86400 - 1) / 86400, plan->t_data);
+		if (exact_time)
+			COMPARE(now - entry->fts_statp->st_atime,
+			    plan->t_data);
+		else
+			COMPARE((now - entry->fts_statp->st_atime +
+			    86400 - 1) / 86400, plan->t_data);
 	} else {
-		COMPARE((now - entry->fts_statp->st_mtime +
-		    86400 - 1) / 86400, plan->t_data);
+		if (exact_time)
+			COMPARE(now - entry->fts_statp->st_mtime,
+			    plan->t_data);
+		else
+			COMPARE((now - entry->fts_statp->st_mtime +
+			    86400 - 1) / 86400, plan->t_data);
 	}
 }
 
@@ -244,15 +335,16 @@ c_Xtime(option, argvp)
 	OPTION *option;
 	char ***argvp;
 {
-	char *ndays;
+	char *value;
 	PLAN *new;
 
-	ndays = nextarg(option, argvp);
+	value = nextarg(option, argvp);
 	ftsoptions &= ~FTS_NOSTAT;
 
 	new = palloc(option);
-	new->t_data = find_parsenum(new, option->name, ndays, NULL);
-	TIME_CORRECT(new);
+	new->t_data = find_parsetime(new, option->name, value);
+	if (!(new->flags & F_EXACTTIME))
+		TIME_CORRECT(new);
 	return new;
 }
 

Cheers,
-- 
Ruslan Ermilov		Oracle Developer/DBA,
ru@sunbay.com		Sunbay Software AG,
ru@FreeBSD.org		FreeBSD committer,
+380.652.512.251	Simferopol, Ukraine

http://www.FreeBSD.org	The Power To Serve
http://www.oracle.com	Enabling The Information Age
Comment 8 Nils M Holm 2001-09-06 19:46:46 UTC
On Thu, 6 Sep 2001, Ruslan Ermilov wrote:
> Uh oh, this already applies to 4.2-RELEASE sources, which have
> since been heavily modified in -CURRENT and -STABLE.

I would have loved to patch the 4.3 sources instead, but I am still
waiting for my CDROM from the subscription. Well, time for another
complaint...

> How about the following patch instead (without the manpage bit)?

Looks good. Removing YM and replacing DW with dw is a nice idea.

Nils.

-- 
Nils M Holm <nmh@t3x.org> -- http://www.t3x.org
PLEASE ask before sending attachments >10K bytes.
Comment 9 ru freebsd_committer freebsd_triage 2001-09-07 07:40:05 UTC
On Thu, Sep 06, 2001 at 08:46:46PM +0200, Nils M Holm wrote:
> On Thu, 6 Sep 2001, Ruslan Ermilov wrote:
> > Uh oh, this already applies to 4.2-RELEASE sources, which have
> > since been heavily modified in -CURRENT and -STABLE.
> 
> I would have loved to patch the 4.3 sources instead, but I am still
> waiting for my CDROM from the subscription. Well, time for another
> complaint...
> 
> > How about the following patch instead (without the manpage bit)?
> 
> Looks good. Removing YM and replacing DW with dw is a nice idea.
> 
I have removed Y and M because they are not exact.


Cheers,
-- 
Ruslan Ermilov		Oracle Developer/DBA,
ru@sunbay.com		Sunbay Software AG,
ru@FreeBSD.org		FreeBSD committer,
+380.652.512.251	Simferopol, Ukraine

http://www.FreeBSD.org	The Power To Serve
http://www.oracle.com	Enabling The Information Age
Comment 10 Nils M Holm 2001-09-07 10:54:36 UTC
On Fri, 7 Sep 2001, Ruslan Ermilov wrote:
> I have removed Y and M because they are not exact.

Good point, too. I may have removed them for esthetic reasons.

Nils.

-- 
Nils M Holm <nmh@t3x.org> -- http://www.t3x.org
PLEASE ask before sending attachments >10K bytes.
Comment 11 ru freebsd_committer freebsd_triage 2001-09-14 13:50:40 UTC
State Changed
From-To: open->closed

An adopted patch committed to 5.0-CURRENT, thanks! 


Comment 12 ru freebsd_committer freebsd_triage 2001-09-14 13:50:40 UTC
Responsible Changed
From-To: freebsd-bugs->ru