This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch master
in repository efm2.

View the commit online.

commit de7f9a21a3bb96f61ec0787f919d3b24a59e668f
Author: Carsten Haitzler (Rasterman) <ras...@rasterman.com>
AuthorDate: Thu Apr 11 10:01:45 2024 +0100

    more work on mv - file count scan, status infra etc
    
    also clean up some #includes we dont need... get this into git so it
    isnt lost
---
 src/backends/default/meta.c   |   1 -
 src/backends/default/mv.c     | 183 ++++++++++++++++++++++-------
 src/backends/default/open.c   |   1 -
 src/backends/default/status.c | 266 ++++++++++++++++++++++++++++++++++++++++++
 src/backends/default/status.h |  32 +++++
 src/efm/efm_graph.c           |   1 -
 6 files changed, 440 insertions(+), 44 deletions(-)

diff --git a/src/backends/default/meta.c b/src/backends/default/meta.c
index 6ac8ce8..46fddb9 100644
--- a/src/backends/default/meta.c
+++ b/src/backends/default/meta.c
@@ -4,7 +4,6 @@
 #include <Ecore.h>
 #include <Ecore_File.h>
 #include <fcntl.h>
-#include "eina_types.h"
 #include "sha.h"
 #include "meta.h"
 
diff --git a/src/backends/default/mv.c b/src/backends/default/mv.c
index abc9873..bda92dd 100644
--- a/src/backends/default/mv.c
+++ b/src/backends/default/mv.c
@@ -5,35 +5,163 @@
 #include <Efreet_Mime.h>
 #include <Eet.h>
 
-#include <asm-generic/errno-base.h>
 #include <asm-generic/errno.h>
+#include <errno.h>
 #include <sys/types.h>
 #include <pwd.h>
 #include <grp.h>
+#include <unistd.h>
 
-#include "cmd.h"
 #include "eina_strbuf.h"
+#include "eina_thread.h"
 #include "eina_types.h"
-#include "sha.h"
 #include "meta.h"
 #include "status.h"
 
 static const char *config_dir = NULL;
 
+// this scans a tree to build a potential operation progress count. it may
+// not be 100% right as the fs can change while the scan happens and after
+// so any move that devolves into a cp + rm isn't going to be atomic and
+// handle a changing fs while it works anyway
+static Eina_Bool
+fs_scan(const char *src)
+{
+  Eina_Iterator *it;
+  const char *s;
+
+  if (strlen(src) < 1) return EINA_FALSE;
+  struct stat st;
+
+  if (lstat(src, &st) != 0)
+    {
+      switch (errno)
+        {
+        case EACCES:
+          status_error(src, NULL, "Move - Permission denied for source");
+          return EINA_FALSE;
+          break;
+        case EFAULT:
+          status_error(src, NULL, "Move - Memory Fault");
+          return EINA_FALSE;
+          break;
+        case ELOOP:
+          status_error(src, NULL, "Move - Too many symlinks");
+          return EINA_FALSE;
+          break;
+        case ENAMETOOLONG:
+          status_error(src, NULL, "Move - Name too long");
+          return EINA_FALSE;
+          break;
+        case ENOENT: // ignore this error - file removed during scan ?
+          return EINA_TRUE;
+          break;
+        case ENOMEM:
+          status_error(src, NULL, "Move - Out of memory");
+          return EINA_FALSE;
+          break;
+        case ENOTDIR:
+          status_error(src, NULL, "Move - Source path component is not a directory");
+          return EINA_FALSE;
+          break;
+        case EOVERFLOW:
+          status_error(src, NULL, "Move - Overflow");
+          return EINA_FALSE;
+          break;
+        default:
+          break;
+        }
+    }
+  if (S_ISDIR(st.st_mode))
+    { // it's a dir - scan this recursively
+      it = eina_file_ls(src);
+      if (it)
+        {
+          Eina_Strbuf *buf;
+
+          buf = eina_strbuf_new();
+          EINA_ITERATOR_FOREACH(it, s)
+          {
+            // build new path "src/s"
+            eina_strbuf_reset(buf);
+            eina_strbuf_append(buf, src);
+            // add / if src doesn't end in /
+            if (src[strlen(src) - 1] != '/') eina_strbuf_append(buf, "/");
+            eina_strbuf_append(buf, s);
+            eina_stringshare_del(s);
+            // fs_scan this file (or dir)
+            // yes - this returns. we make leak our eina_file_ls data, strbuf etc.
+            // dont care - we're going to exit the whole process in a bit
+            if (!fs_scan(eina_strbuf_string_get(buf))) return EINA_FALSE;
+          }
+          eina_strbuf_free(buf);
+        }
+    }
+  else
+    {
+      // the file itself count as 1 progress item - useful if it's a symlink
+      // or a char or block device etc.
+      status_count(1, ecore_file_file_get(src));
+      // in addition each byte in the file count as a progress item too
+      if (st.st_size > 0)
+        status_count(st.st_size, ecore_file_file_get(src));
+    }
+  return EINA_TRUE;  
+}
+
+static Eina_Bool
+fs_cp_rm(const char *src, const char *dst, Eina_Bool report_err)
+{
+  Eina_Bool res = EINA_TRUE;
+
+  if (!fs_scan(src)) return EINA_FALSE;
+
+  // if src is dir
+  //   fs_mkdir(dst)
+  //   for all files in src
+  //     fs_cp_rm(src/file, dst/file, report_err)
+  //     fs_utimes(src, dst)
+  //     fs_chmod(src, dst)
+  //     fs_chown(src, dst)
+  //   fs_utimes(src, dst)
+  //   fs_chmod(src, dst)
+  //   fs_chown(src, dst)
+  //   fs_rmdir(src)
+  // else
+  //   if (src is file)
+  //     fs_cp(src, dst, report_err)
+  //     fs_cp_xattr(src, dst)
+  //   else if (src is symlink)
+  //     fs_ln(src, dst)
+  //   else if (src is fifo)
+  //     fs_mkfifo(dst)
+  //   else if (src is socket)
+  //     fs_touch(dst)
+  //   else if (src is chrdev)
+  //     fs_chrdevdup(src, dst)
+  //   else if (src is blkdev)
+  //     fs_blkdevdup(src, dst)
+  //   fs_utimes(src, dst)
+  //   fs_chmod(src, dst)
+  //   fs_chown(src, dst)
+  //   fs_rm(src)
+  //   fs_cp(src_meta, dst_meta)
+  //   fs_utimes(src_meta, dst_meta)
+  //   fs_chmod(src_meta, dst_meta)
+  //   fs_chown(src_meta, dst_meta)
+  //   fs_rm(src_meta)
+  return res;
+}
+
 static Eina_Bool
 fs_mv(const char *src, const char *dst, Eina_Bool report_err)
 {
-  // void status_begin(void);
-  // void status_end(void);
-  // void status_count(unsigned long long num_inc, const char *str);
-  // void status_pos(unsigned long long pos_inc, const char *str));
-  // void status_error(const char *path, const char *str);
   Eina_Bool res = EINA_TRUE;
   int ret;
 
-  // XXX: write out status file and update it
   // XXX: ecore_file_mv() ? <- look at it
   // XXX: utime()/utimes() -> utimensat()
+  status_op("mv");
   status_count(1, src);
   ret = rename(src, dst);
   if (ret == 0) return res;
@@ -117,10 +245,7 @@ fs_mv(const char *src, const char *dst, Eina_Bool report_err)
 
 int
 main(int argc, char **argv)
-{
-  // mv [src] [dst]         if (report_error) status_error(src, dst, "Move - Too
-  // many symlinks");
-
+{ // mv [src] [dst]
   const char *src, *dst, *fname, *home_dir;
   Eina_Strbuf *buf = NULL;
 
@@ -156,27 +281,10 @@ main(int argc, char **argv)
   //     mkdir   DDIR/.efm
   //     mv      SDIR/.efm/SFILE.efm DDIR/.efm/SFILE.efm
 
-  // status in
-  //   EFMDIR/status/uuid.status
-  //
-  // status file:
-  // uuid (UUID) # uuid repeated from filename
-  // pid (PID) @ pid of task
-  // type (operation type - mv, cp, rm, ...) # op type
-  // busy (0/1) # show a busy spinner instead of progress
-  // progress (0-100) # show a progress bar 0-100 percent
-  // title (some title string to show)
-  // label (somt string to show) # show this string
-  // icon (some icon name or full path to file)
-  // ---
-  //
-  // check /proc/PID/cmdline so argv[1] is same uuid
-  // write status in one write() as it'll be less than 4k
-
   fname = ecore_file_file_get(src);
   if (!fname) goto err;
   buf = eina_strbuf_new();
-  // XXX: if buf is null?
+  if (!buf) goto err;
   eina_strbuf_append(buf, dst);
   eina_strbuf_append(buf, "/");
   eina_strbuf_append(buf, fname);
@@ -187,8 +295,6 @@ main(int argc, char **argv)
       const char *dstfile;
       char *src_meta, *dst_meta;
 
-      fprintf(stderr, "MV: OK\n");
-
       dstfile = eina_strbuf_string_get(buf);
       
       src_can_write = meta_path_can_write(src);
@@ -199,10 +305,7 @@ main(int argc, char **argv)
       if (dst_can_write) dst_meta = meta_path_find(dstfile, "meta.efm");
       else dst_meta = meta_path_user_find(dstfile, "meta.efm");
       if ((src_meta) && (dst_meta) && (meta_path_prepare(dstfile)))
-        {
-          // XXX: use mv() rename wrapper than will cp
-          fs_mv(src_meta, dst_meta, EINA_FALSE);
-        }
+        fs_mv(src_meta, dst_meta, EINA_FALSE);
       free(src_meta);
       free(dst_meta);
       if (src_can_write) src_meta = meta_path_find(src, "thumb.efm");
@@ -210,13 +313,11 @@ main(int argc, char **argv)
       if (dst_can_write) dst_meta = meta_path_find(dstfile, "thumb.efm");
       else dst_meta = meta_path_user_find(dstfile, "thumb.efm");
       if ((src_meta) && (dst_meta) && (meta_path_prepare(dstfile)))
-        {
-          // XXX: use mv() rename wrapper than will cp
-          fs_mv(src_meta, dst_meta, EINA_FALSE);
-        }
+        fs_mv(src_meta, dst_meta, EINA_FALSE);
       free(src_meta);
       free(dst_meta);
     }
+  else pause(); // wait here - status thread will exit for us
 err:
   if (buf) eina_strbuf_free(buf);
   status_end();
diff --git a/src/backends/default/open.c b/src/backends/default/open.c
index 531ccb6..6e97f87 100644
--- a/src/backends/default/open.c
+++ b/src/backends/default/open.c
@@ -18,7 +18,6 @@
 #include <grp.h>
 
 #include "cmd.h"
-#include "eina_strbuf.h"
 #include "sha.h"
 #include "meta.h"
 #include "thumb_check.h"
diff --git a/src/backends/default/status.c b/src/backends/default/status.c
new file mode 100644
index 0000000..aff152e
--- /dev/null
+++ b/src/backends/default/status.c
@@ -0,0 +1,266 @@
+// enable eina_vpath ...
+#define EFL_BETA_API_SUPPORT 1
+// for vasprintf()
+#define _GNU_SOURCE
+#include "status.h"
+#include <Eina.h>
+#include <Ecore_File.h>
+#include <fcntl.h>
+
+typedef enum
+{
+  MSG_OP,
+  MSG_COUNT,
+  MSG_POS,
+  MSG_ERR,
+  MSG_END
+} Msg_Type;
+
+typedef struct
+{
+#define MSG_HEAD              \
+  Eina_Thread_Queue_Msg head; \
+  Msg_Type              type
+  MSG_HEAD;
+  void *dummy;
+} Msg;
+
+typedef struct
+{
+  MSG_HEAD;
+  char *op;
+} Msg_Op;
+
+typedef struct
+{
+  MSG_HEAD;
+  unsigned long long inc;
+  char              *str;
+} Msg_Count;
+
+typedef struct
+{
+  MSG_HEAD;
+  unsigned long long inc;
+  char              *str;
+} Msg_Pos;
+
+typedef struct
+{
+  MSG_HEAD;
+} Msg_End;
+
+typedef struct
+{
+  MSG_HEAD;
+  char *src;
+  char *dst;
+  char *str;
+} Msg_Err;
+
+static unsigned long long  op_count      = 0;
+static unsigned long long  op_pos        = 0;
+static unsigned long long  op_count_prev = 0;
+static unsigned long long  op_pos_prev   = 0;
+static Eina_Bool           op_end        = EINA_FALSE;
+static char               *op_str        = NULL;
+static int                 status_fd     = -1;
+static char               *status_file   = NULL;
+static Eina_Thread         status_thread;
+static Eina_Thread_Queue  *status_thq    = NULL;
+
+static void
+_fd_printf(int fd, const char *fmt, ...)
+{
+  va_list args;
+  char *str;
+  size_t  len;
+
+  va_start(args, fmt);
+  len = vasprintf(&str, fmt, args);
+  va_end(args);
+
+  if ((len == 0) || (!str)) return;
+  write(fd, str, len);
+  free(str);
+}
+
+static void
+_status_flush(void)
+{
+  if ((op_count == op_count_prev) && (op_pos == op_pos_prev)) return;
+  op_count_prev = op_count;
+  op_pos_prev = op_pos;
+  _fd_printf(status_fd, "CMD progress pos=%llu count=%llu\n", op_pos,
+             op_count);
+  if (op_str) _fd_printf(status_fd, "CMD progress str=%s\n", op_str);
+  if (op_end) _fd_printf(status_fd, "CMD end\n");
+  free(op_str);
+  op_str = NULL;
+}
+
+static void *
+_cb_status_thread(void *data EINA_UNUSED, Eina_Thread t EINA_UNUSED)
+{
+  Msg *msg;
+  void *ref;
+
+  for (;;)
+    { // loop and poll every 100ms (0.1s) for messages
+      msg = eina_thread_queue_poll(status_thq, &ref);
+      if (!msg)
+        {
+          // flush out write of last state - rate limit to 100ms
+          _status_flush();
+          usleep(100 * 1000); // wait for 100ms if no messages
+          continue;
+        }
+      switch (msg->type)
+        {
+        case MSG_OP:
+          {
+            Msg_Op *msg2 = (Msg_Op *)msg;
+            // write this out immediately
+            _fd_printf(status_fd, "CMD op %s\n", msg2->op);
+            _fd_printf(status_fd, "CMD pid %i\n", (int)getpid());
+            free(msg2->op);
+          }
+          break;
+        case MSG_COUNT:
+          {
+            Msg_Count *msg2 = (Msg_Count *)msg;
+            op_count += msg2->inc;
+            free(op_str);
+            op_str = msg2->str;
+          }
+          break;
+        case MSG_POS:
+          {
+            Msg_Pos *msg2 = (Msg_Pos *)msg;
+            op_pos += msg2->inc;
+            free(op_str);
+            op_str = msg2->str;
+          }
+          break;
+        case MSG_ERR:
+          {
+            Msg_Err *msg2 = (Msg_Err *)msg;
+            // write this out immediately
+            _fd_printf(status_fd, "CMD error err=%s\n", msg2->str);
+            if (msg2->src) _fd_printf(status_fd, "CMD error src="" msg2->src);
+            if (msg2->dst) _fd_printf(status_fd, "CMD error dst=%s\n", msg2->dst);
+            _fd_printf(status_fd, "CMD error done\n");
+            free(msg2->src);
+            free(msg2->dst);
+            free(msg2->str);
+            eina_thread_queue_wait_done(status_thq, ref);
+            exit(1);
+          }
+          break;
+        case MSG_END:
+          {
+            if (status_fd >= 0)
+              {
+                close(status_fd);
+                status_fd = -1;
+              }
+            if (status_file)
+              {
+                unlink(status_file);
+                free(status_file);
+                status_file = NULL;
+              }
+            eina_thread_queue_wait_done(status_thq, ref);
+            return NULL;
+          }
+          break;
+        default:
+          break;
+        }
+      eina_thread_queue_wait_done(status_thq, ref);
+    }
+  return NULL;
+}
+
+void
+status_begin(void)
+{
+  Eina_Bool ok;
+  char buf[PATH_MAX];
+
+  op_count = op_pos = 0;
+  eina_vpath_resolve_snprintf(buf, sizeof(buf), "(:usr.run:)/efm/ops");
+  ecore_file_mkpath(buf);
+  eina_vpath_resolve_snprintf(buf, sizeof(buf), "(:usr.run:)/efm/ops/%i.status",
+                              (int)getpid());
+  status_file = strdup(buf);
+  status_fd   = open(status_file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+  status_thq  = eina_thread_queue_new();
+  ok          = eina_thread_create(&status_thread, EINA_THREAD_BACKGROUND, -1,
+                                   _cb_status_thread, NULL);
+  if (!ok) abort();
+}
+
+void
+status_end(void)
+{
+  Msg_End *msg;
+  void    *ref;
+
+  msg       = eina_thread_queue_send(status_thq, sizeof(Msg_End), &ref);
+  msg->type = MSG_END;
+  eina_thread_queue_send_done(status_thq, ref);
+  eina_thread_join(status_thread);
+}
+
+void
+status_op(const char *op)
+{
+  Msg_Op *msg;
+  void   *ref;
+
+  msg       = eina_thread_queue_send(status_thq, sizeof(Msg_Op), &ref);
+  msg->type = MSG_OP;
+  msg->op   = op ? strdup(op) : NULL;
+  eina_thread_queue_send_done(status_thq, ref);
+}
+
+void
+status_count(unsigned long long num_inc, const char *str)
+{
+  Msg_Count *msg;
+  void      *ref;
+
+  msg       = eina_thread_queue_send(status_thq, sizeof(Msg_Count), &ref);
+  msg->type = MSG_COUNT;
+  msg->inc  = num_inc;
+  msg->str  = str ? strdup(str) : NULL;
+  eina_thread_queue_send_done(status_thq, ref);
+}
+
+void
+status_pos(unsigned long long pos_inc, const char *str)
+{
+  Msg_Pos *msg;
+  void    *ref;
+
+  msg       = eina_thread_queue_send(status_thq, sizeof(Msg_Pos), &ref);
+  msg->type = MSG_POS;
+  msg->inc  = pos_inc;
+  msg->str  = str ? strdup(str) : NULL;
+  eina_thread_queue_send_done(status_thq, ref);
+}
+
+void
+status_error(const char *src, const char *dst, const char *str)
+{
+  Msg_Err *msg;
+  void    *ref;
+
+  msg       = eina_thread_queue_send(status_thq, sizeof(Msg_Err), &ref);
+  msg->type = MSG_ERR;
+  msg->src  = "" ? strdup(src) : NULL;
+  msg->dst  = dst ? strdup(dst) : NULL;
+  msg->str  = str ? strdup(str) : NULL;
+  eina_thread_queue_send_done(status_thq, ref);
+}
diff --git a/src/backends/default/status.h b/src/backends/default/status.h
new file mode 100644
index 0000000..4fd21b2
--- /dev/null
+++ b/src/backends/default/status.h
@@ -0,0 +1,32 @@
+#ifndef STATUS_H
+#define STATUS_H
+#include <Eina.h>
+
+// status reporting writes status to a log file USERRUNTIME/efm/ops/PID.status
+// where USERRUNTIME might for example be /var/run/user/UID .
+//
+// this will be rate limited and thus not write a line for every change but
+// the last line or few lines in the file will be the latest status with a
+// small time lag so can be relied on to monitor and read new lines from
+// as they get appended and then display as needed. if everything succeeds
+// and the op completes the file will be delted. if there is an error, the
+// file is left there with the last few lines having some error information
+// so this can be displayed to the user
+//
+// lines in the status file are of the format
+// CMD command [arg1] [arg2] ...
+// where the first 4 chars are "CMD " and then a non-whitespace containing
+// command string, then possibly a space char and then arguments until a
+// a newline "\n" char for end of line. arguments mabay just be a set of
+// whitespace delimited words (so a string until the newline) or perhaps
+// of the form key=value depending on the command
+
+void status_begin(void);
+void status_end(void);
+void status_op(const char *op);
+void status_count(unsigned long long num_inc, const char *str);
+void status_pos(unsigned long long pos_inc, const char *str);
+// this call will cause the status thread to report the error then exit(1)
+void status_error(const char *src, const char *dst, const char *str);
+
+#endif
\ No newline at end of file
diff --git a/src/efm/efm_graph.c b/src/efm/efm_graph.c
index 5b3bc50..c3ba5e7 100644
--- a/src/efm/efm_graph.c
+++ b/src/efm/efm_graph.c
@@ -1,5 +1,4 @@
 #include "efm_graph.h"
-#include "eina_stringshare.h"
 
 typedef struct _Smart_Data Smart_Data;
 

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to