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 c63899775d242f3cd5aae4134537ca0413f05aa0
Author: Carsten Haitzler (Rasterman) <ras...@rasterman.com>
AuthorDate: Fri Jul 5 11:40:20 2024 +0100

    mv metadata properly for custom xy,wh .. or others in future
---
 TODO.md                     |  26 +----
 src/backends/default/fs.c   |  16 +--
 src/backends/default/meta.c | 236 ++++++++++++++++++++++++++++++++++++++++----
 src/backends/default/meta.h |   8 ++
 src/backends/default/mv.c   |  35 +++----
 src/efm/efm_dnd.c           |  27 ++++-
 src/shared/sha.c            |   3 -
 src/shared/util.c           |  46 +++++++++
 src/shared/util.h           |  10 +-
 9 files changed, 332 insertions(+), 75 deletions(-)

diff --git a/TODO.md b/TODO.md
index 7d4da44..b790e72 100644
--- a/TODO.md
+++ b/TODO.md
@@ -2,33 +2,9 @@
 
 # NOTES
 
-Solve Meta clash problem:
-
-* On load of meta file
-  * Lock meta
-  * Store stat info
-  * Load fields
-  * Unlock meta
-* Add changed flags to each meta key that was changed in memory
-* On write of meta
-  * Lock meta file
-  * If a meta file exists + stat info differs
-    * Load meta file fields again but keey changed flag fields as is
-  * Write out all meta file
-  * Unlock meta file
-* On mv or cp of meta
-  * Lock src meta
-  * Lock dst meta
-  * Read src meta
-  * Read dst meta
-  * Merge in only xy meta field into src meta if it exists
-  * Write src meta to dst meta (now merged)
-  * Delete src meta
-  * Unlock src meta
-  * Unlock dst meta
-
 ## Now
 
+* Handle open command crashing (stiore null exe and don't send cmds when null)
 * View
   * Vertical icon view
   * Free x/y cleanup/grid align
diff --git a/src/backends/default/fs.c b/src/backends/default/fs.c
index 2fe1148..e352ae1 100644
--- a/src/backends/default/fs.c
+++ b/src/backends/default/fs.c
@@ -1,5 +1,5 @@
 // for copy_file_range()
-#define _GNU_SOURCE
+#include "efm_config.h"
 #define _FILE_OFFSET_BITS 64
 
 #include <Eina.h>
@@ -408,13 +408,13 @@ err_unlink:
 // duplicate mtime+atime from src down to msic/nsec if possible
 #ifdef STAT_NSEC
 #  ifdef st_mtime
-#    define STAT_NSEC_ATIME(st) (unsigned long long)((st)->st_atim.tv_nsec)
-#    define STAT_NSEC_MTIME(st) (unsigned long long)((st)->st_mtim.tv_nsec)
-#    define STAT_NSEC_CTIME(st) (unsigned long long)((st)->st_ctim.tv_nsec)
+#    define STAT_NSEC_ATIME(st) (unsigned long long)((st).st_atim.tv_nsec)
+#    define STAT_NSEC_MTIME(st) (unsigned long long)((st).st_mtim.tv_nsec)
+#    define STAT_NSEC_CTIME(st) (unsigned long long)((st).st_ctim.tv_nsec)
 #  else
-#    define STAT_NSEC_ATIME(st) (unsigned long long)((st)->st_atimensec)
-#    define STAT_NSEC_MTIME(st) (unsigned long long)((st)->st_mtimensec)
-#    define STAT_NSEC_CTIME(st) (unsigned long long)((st)->st_ctimensec)
+#    define STAT_NSEC_ATIME(st) (unsigned long long)((st).st_atimensec)
+#    define STAT_NSEC_MTIME(st) (unsigned long long)((st).st_mtimensec)
+#    define STAT_NSEC_CTIME(st) (unsigned long long)((st).st_ctimensec)
 #  endif
 #else
 #  define STAT_NSEC_ATIME(st) (unsigned long long)(0)
@@ -459,4 +459,4 @@ done:
   return res;
 }
 
-// add fs_cp() fs_rm() fs_trash() fs_rename()
\ No newline at end of file
+// add fs_cp() fs_rm() fs_trash() fs_rename()
diff --git a/src/backends/default/meta.c b/src/backends/default/meta.c
index de92471..ad1b553 100644
--- a/src/backends/default/meta.c
+++ b/src/backends/default/meta.c
@@ -5,6 +5,7 @@
 #include <Ecore_File.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#include "eina_list.h"
 #include "sha.h"
 #include "meta.h"
 #include "util.h"
@@ -32,12 +33,13 @@
 
 #define META_WRITE_TIMEOUT 0.2
 
-typedef struct _Meta_File
+struct _Meta_File
 {
-  const char *path;        // path of original file this metadata is for
-  Eina_List  *list;        // for a small mount of meta - list of Meta
-  Eina_Bool   changed : 1; // has changes to write out
-} Meta_File;
+  const char   *path;        // path of original file this metadata is for
+  Eina_List    *list;        // for a small mount of meta - list of Meta
+  Eina_Bool     changed : 1; // has changes to write out
+  Util_Modtime  modtime;     // time file modified when loaded - if it existed
+};
 
 typedef struct _Meta
 {
@@ -109,13 +111,50 @@ _meta_file_free(Meta_File *mf)
   free(mf);
 }
 
+static Eina_Bool
+_cb_meta_desktop_x_foreach_merge(const Eina_Hash *hash EINA_UNUSED,
+                                 const void *key, void *data, void *fdata)
+{
+  Meta_File *mf = fdata;
+  Meta      *m;
+  Eina_List *l;
+  const char *meta = key;
+
+  EINA_LIST_FOREACH(mf->list, l, m)
+  {
+    if (!strcmp(m->meta, meta))
+      {
+        if (m->changed) // our in-memory key has changed - keep it
+          return EINA_TRUE;
+        else
+          { // key exists - replace with new one
+            eina_stringshare_replace(&(m->data), data);
+            return EINA_TRUE;
+          }
+      }
+  }
+  // it's a new key - add it
+  m = calloc(1, sizeof(Meta));
+  if (m)
+    {
+      m->meta  = eina_stringshare_add(key /* + 6*/);
+      m->data  = ""
+      mf->list = eina_list_append(mf->list, m);
+    }
+  // yes - we don't delete keys that were removed... we're handling the x,y
+  // meta problem with this so it's ok.
+  return EINA_TRUE;
+}
+
 static void
 _meta_file_write(Meta_File *mf)
 { // write out all in memory metadata to target meta file
-  Eina_List *l;
-  Meta      *m;
-  FILE      *f;
-  char      *meta_path = NULL, *dir = NULL;
+  Util_Modtime  mt, mt2;
+  Eina_List    *l;
+  Meta         *m;
+  FILE         *f = NULL;
+  char         *meta_path = NULL, *meta_path_tmp = NULL, *dir = NULL;
+  int           tmpfd;
 
   if (!mf->changed) return;
   mf->changed = EINA_FALSE;
@@ -130,21 +169,60 @@ _meta_file_write(Meta_File *mf)
 
   if (mf->list)
     {
-      // XXX: should gain a lock!
-      // XXX: should write to tmp then rename atomically
-      f = fopen(meta_path, "w");
+merge_again:
+      // get the mod time of the target file
+      mt = util_file_modtime_get(meta_path);
+      // has the target file changed since we may have read from it?
+      if (((util_modtime_valid(mt))
+            && (util_modtime_valid(mf->modtime))
+            && (util_modtime_cmp(mt, mf->modtime) >= 0)) || (!util_modtime_valid(mf->modtime)))
+        { // file we are about to write is newer than original. we need to
+          // merge these files - keep changed keys but re-load rest. if we've
+          // picked up keys from a file + personal overlay file then we
+          // only care about the changes in the overlay. we are doing this
+          // specifically to handle x,y coords on dnd that may be written
+          // to a dest meta file before the src meta file is cp/mv'd over
+          // and thus this x,y has to be merged or tyhe x,y may be written
+          // after the src meta has mv/cp'd over
+          Efreet_Ini *ini = efreet_ini_new(meta_path);
+          if (ini)
+            {
+              if ((ini->data) && (efreet_ini_section_set(ini, "Efm Meta")))
+                eina_hash_foreach(ini->section, _cb_meta_desktop_x_foreach_merge, mf);
+              efreet_ini_free(ini);
+            }
+        }
+      meta_path_tmp = malloc(strlen(meta_path) + 7 + 1);
+      // XXX: malloc fails?
+      strcpy(meta_path_tmp, meta_path);
+      strcat(meta_path_tmp, ".XXXXXX");
+      tmpfd = mkstemp(meta_path_tmp);
+      if (tmpfd >= 0) f = fdopen(tmpfd, "w");
       if (!f)
         { // can't write to dir - write to personal meta instead
+          if (tmpfd >= 0) close(tmpfd);
           free(meta_path);
+          meta_path = NULL;
+          free(meta_path_tmp);
+          meta_path_tmp = NULL;
           meta_path = _meta_personal_overlay_file_get(mf);
-          if (!meta_path) return;
+          if (!meta_path) goto err;
           dir = ecore_file_dir_get(meta_path);
-          if (!dir) return;
+          if (!dir) goto err;
           if (!ecore_file_is_dir(dir)) ecore_file_mkpath(dir);
           free(dir);
           dir = NULL;
-          f   = fopen(meta_path, "w");
-          if (!f) goto err;
+          meta_path_tmp = malloc(strlen(meta_path) + 7 + 1);
+          // XXX: malloc fails?
+          strcpy(meta_path_tmp, meta_path);
+          strcat(meta_path_tmp, ".XXXXXX");
+          tmpfd = mkstemp(meta_path_tmp);
+          if (tmpfd >= 0) f = fdopen(tmpfd, "w");
+          if (!f)
+            {
+              if (tmpfd >= 0) close(tmpfd);
+              goto err;
+            }
         }
       fprintf(f, "[Efm Meta]\n");
       EINA_LIST_FOREACH(mf->list, l, m)
@@ -153,10 +231,23 @@ _meta_file_write(Meta_File *mf)
         m->changed = EINA_FALSE;
       }
       fclose(f);
+      mt2 = util_file_modtime_get(meta_path);
+      if (((util_modtime_valid(mt2)) && (util_modtime_valid(mt))
+           && (util_modtime_cmp(mt2, mt) != 0))
+          || ((util_modtime_valid(mt2)) && (!util_modtime_valid(mt))))
+        { // this is unusual - someone snuck in a write between now and our
+          // previous modtime check. try merge in again...
+          unlink(meta_path_tmp);
+          free(meta_path_tmp);
+          meta_path_tmp = NULL;
+          goto merge_again;
+        }
+      rename(meta_path_tmp, meta_path);
     }
   else // no meta keys - delete it
     ecore_file_unlink(meta_path);
 err:
+  free(meta_path_tmp);
   free(meta_path);
   free(dir);
 }
@@ -248,9 +339,10 @@ done:
 static Meta_File *
 _meta_file_find(const char *path)
 {
-  Meta_File  *mf;
-  Efreet_Ini *ini;
-  char       *meta_path;
+  Util_Modtime  mt;
+  Meta_File    *mf;
+  Efreet_Ini   *ini;
+  char         *meta_path;
 
   // find existing in memory meta file data and return that
   mf = eina_hash_find(_meta_hash, path);
@@ -273,6 +365,7 @@ _meta_file_find(const char *path)
         eina_hash_foreach(ini->section, _cb_meta_desktop_x_foreach, mf);
       efreet_ini_free(ini);
     }
+  mf->modtime = util_file_modtime_get(meta_path);
   free(meta_path);
 
   // load overlayed user metdata and modify meta file content based on it
@@ -287,6 +380,13 @@ _meta_file_find(const char *path)
                               mf);
           efreet_ini_free(ini);
         }
+      mt = util_file_modtime_get(meta_path);
+      // base modtime not valid use overlay anyway
+      if (!util_modtime_valid(mf->modtime)) mf->modtime = mt;
+      // base modtime is valid, so if overlay modtime is valid and newere, use it
+      else if (util_modtime_valid(mt)
+               && (util_modtime_cmp(mt, mf->modtime) >= 0))
+        mf->modtime = mt;
       free(meta_path);
     }
   // add to our hash db of meta files
@@ -448,3 +548,101 @@ err:
   free(dir);
   return ret;
 }
+
+Meta_File *
+meta_file_load(const char *path)
+{
+  return _meta_file_find(path);
+}
+
+void
+meta_file_free(Meta_File *mf)
+{
+  if (mf->changed)
+    _meta_writes = eina_list_remove(_meta_writes, mf);
+  eina_hash_del(_meta_hash, mf->path, mf);
+}
+
+Meta_File *
+meta_file_copy(Meta_File *mf, const char *path)
+{
+  Meta_File *mf2;
+  Eina_List *l, *l2;
+  Meta *m, *m2;
+
+  mf2 = eina_hash_find(_meta_hash, path);
+  if (mf2)
+    {
+      EINA_LIST_FOREACH(mf->list, l, m)
+      {
+        EINA_LIST_FOREACH(mf2->list, l2, m2)
+        {
+          if (!strcmp(m->meta, m2->meta))
+            {
+              if ((m->changed) && (!m2->changed))
+                {
+                  if (m2->data) eina_stringshare_del(m2->data);
+                  m2->data = ""
+                  if (m->data) m2->data = ""
+                }
+            }
+        }
+        if (!l2) // key in mf doesnt exist in mf2
+          {
+            m2 = calloc(1, sizeof(Meta));
+            if (m2)
+              {
+                m2->meta = eina_stringshare_add(m->meta);
+                if (m->data) m2->data = ""
+                m2->changed = m->changed;
+                mf2->list   = eina_list_append(mf2->list, m2);
+              }
+          }
+      }
+    }
+  else
+    {
+      mf2 = calloc(1, sizeof(Meta_File));
+      if (!mf2) return NULL;
+      mf2->modtime = util_file_modtime_get(NULL); // invalid time for NULL
+      mf2->path = eina_stringshare_add(path);
+      eina_hash_add(_meta_hash, mf2->path, mf2);
+      EINA_LIST_FOREACH(mf->list, l, m)
+      {
+        m2 = calloc(1, sizeof(Meta));
+        if (m2)
+          {
+            m2->meta = eina_stringshare_add(m->meta);
+            if (m->data) m2->data = ""
+            m2->changed = m->changed;
+            mf2->list   = eina_list_append(mf2->list, m2);
+          }
+      }
+    }
+  _meta_file_write_queue(mf2); // queue writes for later
+  return mf2;
+}
+
+void
+meta_file_del(Meta_File *mf)
+{
+  char *meta_file;
+
+  if ((meta_file = meta_path_find(mf->path, "meta.efm")))
+    {
+      unlink(meta_file);
+      free(meta_file);
+    }
+  if ((meta_file = meta_path_user_find(mf->path, "meta.efm")))
+    {
+      unlink(meta_file);
+      free(meta_file);
+    }
+}
+
+void
+meta_file_save(Meta_File *mf)
+{
+  _meta_writes = eina_list_remove(_meta_writes, mf);
+  _meta_file_write(mf);
+}
\ No newline at end of file
diff --git a/src/backends/default/meta.h b/src/backends/default/meta.h
index 87dd91a..8f8a979 100644
--- a/src/backends/default/meta.h
+++ b/src/backends/default/meta.h
@@ -2,6 +2,8 @@
 #define META_H
 #include <Eina.h>
 
+typedef struct _Meta_File Meta_File;
+
 void meta_init(const char *config_dir);
 void meta_shutdown(void);
 
@@ -13,4 +15,10 @@ char     *meta_path_user_find(const char *path, const char *extn);
 Eina_Bool meta_path_prepare(const char *path);
 Eina_Bool meta_path_can_write(const char *path);
 
+Meta_File *meta_file_load(const char *path);
+void       meta_file_free(Meta_File *mf);
+Meta_File *meta_file_copy(Meta_File *mf, const char *path);
+void       meta_file_del(Meta_File *mf);
+void       meta_file_save(Meta_File *mf);
+
 #endif
\ No newline at end of file
diff --git a/src/backends/default/mv.c b/src/backends/default/mv.c
index d655bc9..d2022a0 100644
--- a/src/backends/default/mv.c
+++ b/src/backends/default/mv.c
@@ -196,25 +196,26 @@ main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
       { // it worked so deal with meta/thumbs
         char *src_meta, *dst_meta;
 
-        // metadata file for the base target file
-        if (src_can_write) src_meta = meta_path_find(src, "meta.efm");
-        else src_meta = meta_path_user_find(src, "meta.efm");
-        if (dst_can_write) dst_meta = meta_path_find(dst, "meta.efm");
-        else dst_meta = meta_path_user_find(dst, "meta.efm");
-        if ((src_meta) && (dst_meta) && (meta_path_prepare(dst)))
+        // XXX: use meta api to:
+        // 1. meta_load src (new api that takes path + ret handle)
+        // 2. meta_dup src to dst (new api with new path dst path, ret dst hnd)
+        // 3. meta_save the duplicated meta (bew api)
+        // 4. meta_del the src meta (new api)
+        // 5. meta_free src + dst handles (new api)
+        Meta_File *mfsrc, *mfdst;
+
+        mfsrc = meta_file_load(src);
+        if (mfsrc)
           {
-            status_count(1, src_meta);
-            fs_mv(src_meta, dst_meta, EINA_FALSE);
-            // XXX: how do we force a re-read of the meta file? also how to
-            // merge? e.g. we dnd an icon into a dir - we may or may not at that
-            // point write x,y to the meta file - but we have other fields we
-            // will want to merge in ... so we don't want to mv here - we want
-            // to merge sensibly... so ugh... bug already here ... we want
-            // to also trigger an update of meta data for any efm backends
-            // telling the front-ends...
+            mfdst = meta_file_copy(mfsrc, dst);
+            if (mfdst)
+              {
+                meta_file_save(mfdst);
+                meta_file_del(mfsrc);
+                meta_file_free(mfsrc);
+                meta_file_free(mfdst);
+              }
           }
-        free(src_meta);
-        free(dst_meta);
 
         // thumbnail file for the base target file
         if (src_can_write) src_meta = meta_path_find(src, "thumb.efm");
diff --git a/src/efm/efm_dnd.c b/src/efm/efm_dnd.c
index d476573..f67a6e9 100644
--- a/src/efm/efm_dnd.c
+++ b/src/efm/efm_dnd.c
@@ -214,13 +214,14 @@ _cb_drop(void *data, Evas_Object *o EINA_UNUSED, Elm_Selection_Data *ev)
           printf("XXX: DROP FILE: [%s]\n", esc);
           if (sd->config.view_mode == EFM_VIEW_MODE_ICONS_CUSTOM)
             {
+              Eina_Bool found = EINA_FALSE;
+              char str[128];
+
               dropicons = _icons_path_find(esc);
               EINA_LIST_FREE(dropicons, icon)
               {
                 if (icon->selected)
                   {
-                    char str[128];
-
                     if (icon->sd == sd)
                       {
                         icon->geom.x += delta_x;
@@ -240,8 +241,30 @@ _cb_drop(void *data, Evas_Object *o EINA_UNUSED, Elm_Selection_Data *ev)
                              (int)(icon->geom.y / _scale_get(icon->sd)));
                     cmd_strbuf_append(buf, "xy", str);
                     cmd_strbuf_exe_consume(buf, icon->sd->exe_open);
+                    found = EINA_TRUE;
                   }
               }
+              if (!found)
+                {
+                  Eina_Strbuf *buf2 = eina_strbuf_new();
+
+                  if (buf2)
+                    {
+                      printf("XXX: ->     DROPEXTICON: [%s]  %i   %i\n",
+                             esc, sd->dnd_x, sd->dnd_y);
+                      buf = cmd_strbuf_new("meta-set");
+
+                      eina_strbuf_append(buf2, sd->config.path);
+                      eina_strbuf_append(buf2, ecore_file_file_get(esc));
+                      cmd_strbuf_append(buf, "path", eina_strbuf_string_get(buf2));
+                      snprintf(str, sizeof(str), "%i,%i",
+                               (int)(sd->dnd_x / _scale_get(sd)),
+                               (int)(sd->dnd_y / _scale_get(sd)));
+                      cmd_strbuf_append(buf, "xy", str);
+                      cmd_strbuf_exe_consume(buf, sd->exe_open);
+                      eina_strbuf_free(buf2);
+                    }
+                }
             }
           free(esc);
         }
diff --git a/src/shared/sha.c b/src/shared/sha.c
index a2a6492..3ca5b12 100644
--- a/src/shared/sha.c
+++ b/src/shared/sha.c
@@ -13,14 +13,11 @@ sha1_stat(const struct stat *st, unsigned char dst[20])
 #ifdef STAT_NSEC
 #  ifdef st_mtime
 #    define STAT_NSEC_MTIME(st) (unsigned long long)((st)->st_mtim.tv_nsec)
-#    define STAT_NSEC_CTIME(st) (unsigned long long)((st)->st_ctim.tv_nsec)
 #  else
 #    define STAT_NSEC_MTIME(st) (unsigned long long)((st)->st_mtimensec)
-#    define STAT_NSEC_CTIME(st) (unsigned long long)((st)->st_ctimensec)
 #  endif
 #else
 #  define STAT_NSEC_MTIME(st) (unsigned long long)(0)
-#  define STAT_NSEC_CTIME(st) (unsigned long long)(0)
 #endif
 
   snprintf(buf, sizeof(buf), "%llu %llu %llu",
diff --git a/src/shared/util.c b/src/shared/util.c
index 8460430..f9871ae 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -1,4 +1,5 @@
 #include "util.h"
+#include "efm_config.h"
 #include <sys/stat.h>
 #include <unistd.h>
 #include <string.h>
@@ -24,4 +25,49 @@ util_file_mode_parent_copy(const char *file, Eina_Bool is_dir)
     }
 err:
   free(dir_parent);
+}
+
+Util_Modtime
+util_file_modtime_get(const char *file)
+{
+  struct stat st;
+  Util_Modtime mt = { ~(0ull), ~(0ull) }; // default - ivalid all bits 1
+
+  if (!file) return mt;
+  if (stat(file, &st) == 0)
+    {
+      mt.sec = st.st_mtime;
+#ifdef STAT_NSEC
+#  ifdef st_mtime
+#    define STAT_NSEC_MTIME(st) (unsigned long long)((st).st_mtim.tv_nsec)
+#  else
+#    define STAT_NSEC_MTIME(st) (unsigned long long)((st).st_mtimensec)
+#  endif
+#else
+#  define STAT_NSEC_MTIME(st) (unsigned long long)(0)
+#endif
+      mt.nsec = STAT_NSEC_MTIME(st);
+    }
+  return mt;
+}
+
+int
+util_modtime_cmp(Util_Modtime t1, Util_Modtime t2)
+{
+  if (t1.sec > t2.sec) return 1; // t1 > t2
+  else if (t1.sec < t2.sec) return -1; // t1 < t2
+  // seconds equal - check nsec
+  if (t1.nsec > t2.nsec) return 1; // t1 > t2
+  else if (t1.nsec < t2.nsec) return -1; // t1 < t2
+  // they are both equal
+  return 0; // t1 == t2
+}
+
+Eina_Bool
+util_modtime_valid(Util_Modtime t)
+{
+  const Util_Modtime mt = { ~(0ull), ~(0ull) }; // default - ivalid all bits 1
+
+  if ((t.sec == mt.sec) && (t.nsec == mt.nsec)) return EINA_FALSE;
+  return EINA_TRUE;
 }
\ No newline at end of file
diff --git a/src/shared/util.h b/src/shared/util.h
index 4a7abdf..38f4ec8 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -3,6 +3,14 @@
 
 #include <Eina.h>
 
-void util_file_mode_parent_copy(const char *file, Eina_Bool is_dir);
+typedef struct
+{
+  unsigned long long sec, nsec;
+} Util_Modtime;
+
+void         util_file_mode_parent_copy(const char *file, Eina_Bool is_dir);
+Util_Modtime util_file_modtime_get(const char *file);
+int          util_modtime_cmp(Util_Modtime t1, Util_Modtime t2);
+Eina_Bool    util_modtime_valid(Util_Modtime t);
 
 #endif
\ No newline at end of file

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

Reply via email to