bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] glob: add shopt globmtimesort to sort globs by mtime


From: Evan Gates
Subject: [PATCH] glob: add shopt globmtimesort to sort globs by mtime
Date: Mon, 3 Oct 2022 12:56:42 -0600

---

There is currently no good way to sort files by mtime in the shell.
It's possible to do so with an ls that supports -t, but parsing ls is
problematic.  It's possible using GNU find's printf %T and nul separated
lists with sort -z.  Neither is a great option.  This patch adds the
ability to sort globs by mtime with shopt -s globmtimesort.  I don't
think it's worth adding atime and ctime, but I know someone will want
it so it's coded in a way that should make that easy to implement once
you figure out the shopt interface for it.

I feel like it would may better to stat files while globbing, but that's
a much larger change.  This seemed like the smallest addition to get
this working.

I'm not familiar with the bash codebase so there's a good chance I got
parts of this wrong.  Is this the right place for the code?  What should
happen if stat fails?  Did I code stuff in an unportable way?

Looking forward to feedback.

 builtins/shopt.def |  2 ++
 pathexp.c          | 56 +++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/builtins/shopt.def b/builtins/shopt.def
index 33d61d4c..48fd7dc0 100644
--- a/builtins/shopt.def
+++ b/builtins/shopt.def
@@ -89,6 +89,7 @@ extern int check_jobs_at_exit;
 extern int autocd;
 extern int glob_star;
 extern int glob_asciirange;
+extern int glob_mtimesort;
 extern int glob_always_skip_dot_and_dotdot;
 extern int lastpipe_opt;
 extern int inherit_errexit;
@@ -211,6 +212,7 @@ static struct {
   { "force_fignore", &force_fignore, (shopt_set_func_t *)NULL },
 #endif
   { "globasciiranges", &glob_asciirange, (shopt_set_func_t *)NULL },
+  { "globmtimesort", &glob_mtimesort, (shopt_set_func_t *)NULL },
   { "globskipdots", &glob_always_skip_dot_and_dotdot, (shopt_set_func_t *)NULL 
},
   { "globstar", &glob_star, (shopt_set_func_t *)NULL },
   { "gnu_errfmt", &gnu_error_format, (shopt_set_func_t *)NULL },
diff --git a/pathexp.c b/pathexp.c
index 379128e7..97f9a7f3 100644
--- a/pathexp.c
+++ b/pathexp.c
@@ -33,6 +33,9 @@
 #include "pathexp.h"
 #include "flags.h"
 
+#include "posixstat.h"
+#include "stat-time.h"
+
 #include "shmbutil.h"
 #include "bashintl.h"
 
@@ -53,6 +56,9 @@ int extended_glob = EXTGLOB_DEFAULT;
 /* Control enabling special handling of `**' */
 int glob_star = 0;
 
+/* Control whether globs are sorted by mtime. */
+int glob_mtimesort = 0;
+
 /* Return nonzero if STRING has any unquoted special globbing chars in it.
    This is supposed to be called when pathname expansion is performed, so
    it implements the rules in Posix 2.13.3, specifically that an unquoted
@@ -402,6 +408,52 @@ quote_globbing_chars (string)
   return temp;
 }
 
+struct glob_time {
+  char *name;
+  struct timespec time;
+};
+
+static int
+glob_time_cmp(g1, g2)
+     struct glob_time *g1;
+     struct glob_time *g2;
+{
+  return -timespec_cmp(g1->time, g2->time);
+}
+
+static void
+glob_time_sort (array, get_stat_time)
+     char **array;
+     struct timespec (*get_stat_time)(struct stat const *);
+{
+  struct glob_time *gt_array;
+  int len, i;
+  struct stat buf;
+
+  len = strvec_len (array);
+
+  gt_array = (struct glob_time*)xmalloc (len * sizeof (struct glob_time));
+
+  for (i = 0; i < len; i++)
+    {
+      gt_array[i].name = array[i];
+      if (!stat (array[i], &buf))
+       gt_array[i].time = get_stat_time (&buf);
+      else
+       {
+         gt_array[i].time.tv_sec = -1;
+         gt_array[i].time.tv_nsec = -1;
+       }
+    }
+
+  qsort (gt_array, len, sizeof (struct glob_time), (QSFUNC *)glob_time_cmp);
+
+  for (i = 0; i < len; i++)
+    array[i] = gt_array[i].name;
+
+  free (gt_array);
+}
+
 /* Call the glob library to do globbing on PATHNAME. */
 char **
 shell_glob_filename (pathname, qflags)
@@ -422,7 +474,9 @@ shell_glob_filename (pathname, qflags)
     {
       if (should_ignore_glob_matches ())
        ignore_glob_matches (results);
-      if (results && results[0])
+      if (results && results[0] && glob_mtimesort)
+       glob_time_sort (results, get_stat_mtime);
+      else if (results && results[0])
        strvec_sort (results, 1);               /* posix sort */
       else
        {
-- 
2.36.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]