From c191288d6384c5cf1fe7fa0d7d9b381386fa6928 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Archer=20St=C3=A9phane?=
 <2981437+stephane-archer@users.noreply.github.com>
Date: Thu, 22 Dec 2022 12:44:45 -0300
Subject: [PATCH] add smart_rename feature

---
 src/mv.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 84 insertions(+), 2 deletions(-)

diff --git a/src/mv.c b/src/mv.c
index 53b9c1300..a3e23eade 100644
--- a/src/mv.c
+++ b/src/mv.c
@@ -22,6 +22,7 @@
 #include <sys/types.h>
 #include <assert.h>
 #include <selinux/label.h>
+#include <unistd.h>
 
 #include "system.h"
 #include "backupfile.h"
@@ -63,6 +64,7 @@ static struct option const long_options[] =
   {"suffix", required_argument, NULL, 'S'},
   {"target-directory", required_argument, NULL, 't'},
   {"update", no_argument, NULL, 'u'},
+  {"smartrename", no_argument, NULL, 's'},
   {"verbose", no_argument, NULL, 'v'},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
@@ -282,6 +284,67 @@ If you specify more than one of -i, -f, -n, only the final one takes effect.\n\
   exit (status);
 }
 
+bool check_file_exists (const char *fname)
+{
+	return access(fname, F_OK) == 0;
+}
+
+unsigned int find_dot(char *str)
+{
+	unsigned int i = 0;
+	while (str[i] != '\0')
+	{
+		if (str[i] == '.')
+		{
+			return i;
+		}
+		++i;
+	}
+	return -1;
+}
+
+char *create_smart_name(char *basename, char *number, unsigned int dot_index)
+{
+	unsigned int len = strlen(basename) + 1 + strlen(number);// +1 for the separation_char
+								 // is there a way to avoid malloc?
+	char *result = malloc(len + 1); // +1 for the null-terminator
+	if (dot_index == -1)
+	{
+		strcpy(result, basename);
+	}
+	else
+	{
+		strlcpy(result, basename, dot_index + 1);
+	}
+	strcat(result, "_");
+	strcat(result, number);
+	if (dot_index != -1)
+	{
+		strcat(result, basename + dot_index);
+	}
+	return result;
+}
+
+char *find_smart_name (char *dest)
+{
+      if (!check_file_exists(dest))
+      {
+	      return dest;
+      }
+      unsigned int i = 0;
+      char is[100]; // What len should I put?
+      sprintf(is, "%d", i);
+      char *smart_name = create_smart_name(dest, is, find_dot(dest));
+      while (check_file_exists(smart_name))
+      {
+	      free(smart_name);
+	      ++i;
+	      sprintf(is, "%d", i);
+	      smart_name = create_smart_name(dest, is, find_dot(dest));
+      }
+      return smart_name;
+}
+
 int
 main (int argc, char **argv)
 {
@@ -294,6 +357,7 @@ main (int argc, char **argv)
   bool remove_trailing_slashes = false;
   char const *target_directory = NULL;
   bool no_target_directory = false;
+  bool smart_rename = false;
   int n_files;
   char **file;
   bool selinux_enabled = (0 < is_selinux_enabled ());
@@ -311,7 +375,7 @@ main (int argc, char **argv)
   /* Try to disable the ability to unlink a directory.  */
   priv_set_remove_linkdir ();
 
-  while ((c = getopt_long (argc, argv, "bfint:uvS:TZ", long_options, NULL))
+  while ((c = getopt_long (argc, argv, "bfint:uvsS:TZ", long_options, NULL))
          != -1)
     {
       switch (c)
@@ -330,6 +394,9 @@ main (int argc, char **argv)
         case 'n':
           x.interactive = I_ALWAYS_NO;
           break;
+	case 's':
+	  smart_rename = true;
+	  break;
         case STRIP_TRAILING_SLASHES_OPTION:
           remove_trailing_slashes = true;
           break;
@@ -480,6 +547,12 @@ main (int argc, char **argv)
       ok = true;
       for (int i = 0; i < n_files; ++i)
         {
+		if (smart_rename)
+		{
+			error (0, 0,
+				_("options -s is not yet supported when target directory is specify"));
+			usage (EXIT_FAILURE);
+		}
           x.last_file = i + 1 == n_files;
           char const *source = file[i];
           char const *source_basename = last_component (source);
@@ -494,7 +567,16 @@ main (int argc, char **argv)
   else
     {
       x.last_file = true;
-      ok = do_move (file[0], file[1], AT_FDCWD, file[1], &x);
+      char *dest = file[1];
+      if (smart_rename)
+      {
+	      dest = find_smart_name (dest);
+      }
+      ok = do_move (file[0], dest, AT_FDCWD, dest, &x);
+      if (smart_rename)
+      {
+	      free(dest);
+      }
     }
 
   main_exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
-- 
2.37.1 (Apple Git-137.1)

