6 #ifndef _FILE_OFFSET_BITS
7 #define _FILE_OFFSET_BITS 64
17 #include <sys/types.h>
20 #include <sys/param.h>
33 #include <Ecore_Ipc.h>
34 #include <Ecore_File.h>
43 #include "e_fm_main.h"
45 #define DEF_SYNC_NUM 8
46 #define DEF_ROUND_TRIP 0.05
47 #define DEF_ROUND_TRIP_TOLERANCE 0.01
48 #define DEF_MOD_BACKOFF 0.2
50 typedef struct _E_Dir E_Dir;
51 typedef struct _E_Fop E_Fop;
52 typedef struct _E_Mod E_Mod;
53 typedef struct _e_fm_ipc_slave E_Fm_Slave;
54 typedef struct _E_Fm_Task E_Fm_Task;
60 Ecore_File_Monitor *mon;
69 Eina_List *recent_mods;
70 Ecore_Timer *recent_clean;
71 unsigned char cleaning : 1;
82 unsigned char del_after : 1;
83 unsigned char gone_bad : 1;
92 unsigned char add : 1;
93 unsigned char del : 1;
94 unsigned char mod : 1;
95 unsigned char done : 1;
98 struct _e_fm_ipc_slave
116 /* local subsystem globals */
117 Ecore_Ipc_Server *_e_fm_ipc_server = NULL;
119 static Eina_List *_e_dirs = NULL;
120 static Eina_List *_e_fops = NULL;
121 static int _e_sync_num = 0;
123 static Eina_List *_e_fm_ipc_slaves = NULL;
124 static Eina_List *_e_fm_tasks = NULL;
126 /* local subsystem functions */
127 static Eina_Bool _e_fm_ipc_cb_server_add(void *data, int type, void *event);
128 static Eina_Bool _e_fm_ipc_cb_server_del(void *data, int type, void *event);
129 static Eina_Bool _e_fm_ipc_cb_server_data(void *data, int type, void *event);
131 static void _e_fm_ipc_monitor_start(int id, const char *path);
132 static void _e_fm_ipc_monitor_start_try(E_Fm_Task *task);
133 static void _e_fm_ipc_monitor_end(int id, const char *path);
134 static E_Fm_Task *_e_fm_ipc_task_get(int id);
135 static Eina_List *_e_fm_ipc_task_node_get(int id);
136 static void _e_fm_ipc_task_remove(E_Fm_Task *task);
137 static void _e_fm_ipc_mkdir_try(E_Fm_Task *task);
138 static void _e_fm_ipc_mkdir(int id, const char *src, const char *rel, int rel_to, int x, int y);
139 static void _e_fm_ipc_handle_error_response(int id, E_Fm_Op_Type type);
141 static int _e_fm_ipc_client_send(int id, E_Fm_Op_Type type, void *data, int size);
143 static int _e_fm_ipc_slave_run(E_Fm_Op_Type type, const char *args, int id);
144 static E_Fm_Slave *_e_fm_ipc_slave_get(int id);
145 static int _e_fm_ipc_slave_send(E_Fm_Slave *slave, E_Fm_Op_Type type, void *data, int size);
147 static void _e_fm_ipc_cb_file_monitor(void *data, Ecore_File_Monitor *em, Ecore_File_Event event, const char *path);
148 static Eina_Bool _e_fm_ipc_cb_recent_clean(void *data);
150 static void _e_fm_ipc_file_add_mod(E_Dir *ed, const char *path, E_Fm_Op_Type op, int listing);
151 static void _e_fm_ipc_file_add(E_Dir *ed, const char *path, int listing);
152 static void _e_fm_ipc_file_del(E_Dir *ed, const char *path);
153 static void _e_fm_ipc_file_mod(E_Dir *ed, const char *path);
154 static void _e_fm_ipc_file_mon_dir_del(E_Dir *ed, const char *path);
155 static void _e_fm_ipc_file_mon_list_sync(E_Dir *ed);
157 static Eina_Bool _e_fm_ipc_cb_file_mon_list_idler(void *data);
158 static Eina_Bool _e_fm_ipc_cb_fop_trash_idler(void *data);
159 static char *_e_str_list_remove(Eina_List **list, const char *str, int len);
160 static void _e_fm_ipc_reorder(const char *file, const char *dst, const char *relative, int after);
161 static void _e_fm_ipc_dir_del(E_Dir *ed);
163 static const char *_e_fm_ipc_prepare_command(E_Fm_Op_Type type, const char *args);
165 /* local subsystem functions */
171 sdir = getenv("E_IPC_SOCKET");
174 printf("The E_IPC_SOCKET environment variable is not set. This is\n"
175 "exported by Enlightenment to all processes it launches.\n"
176 "This environment variable must be set and must point to\n"
177 "Enlightenment's IPC socket file (minus port number).\n");
180 _e_fm_ipc_server = ecore_ipc_server_connect(ECORE_IPC_LOCAL_SYSTEM, sdir, 0, NULL);
181 if (!_e_fm_ipc_server)
183 printf("Cannot connect to enlightenment (socket '%s') - abort\n", sdir);
187 ecore_event_handler_add(ECORE_IPC_EVENT_SERVER_ADD, _e_fm_ipc_cb_server_add, NULL);
188 ecore_event_handler_add(ECORE_IPC_EVENT_SERVER_DEL, _e_fm_ipc_cb_server_del, NULL);
189 ecore_event_handler_add(ECORE_IPC_EVENT_SERVER_DATA, _e_fm_ipc_cb_server_data, NULL);
195 _e_fm_ipc_cb_server_add(void *data __UNUSED__, int type __UNUSED__, void *event)
197 Ecore_Ipc_Event_Server_Add *e;
200 ecore_ipc_server_send(e->server,
201 6 /*E_IPC_DOMAIN_FM*/,
203 0, 0, 0, NULL, 0); /* send hello */
204 return ECORE_CALLBACK_PASS_ON;
208 _e_fm_ipc_cb_server_del(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__)
211 ecore_main_loop_quit();
212 return ECORE_CALLBACK_PASS_ON;
216 _e_fm_ipc_monitor_start(int id, const char *path)
218 E_Fm_Task *task = malloc(sizeof(E_Fm_Task));
223 task->type = E_FM_OP_MONITOR_START;
225 task->src = eina_stringshare_add(path);
232 _e_fm_tasks = eina_list_append(_e_fm_tasks, task);
234 _e_fm_ipc_monitor_start_try(task);
238 _e_fm_ipc_monitor_start_try(E_Fm_Task *task)
240 E_Dir *ed, *ped = NULL;
245 /* look for any previous dir entries monitoring this dir */
246 EINA_LIST_FOREACH(_e_dirs, l, ed)
248 if ((ed->mon) && (!strcmp(ed->dir, task->src)))
250 /* found a previous dir - save it in ped */
256 /* open the dir to list */
257 it = eina_file_direct_ls(task->src);
260 char buf[PATH_MAX + 4096];
262 snprintf(buf, sizeof(buf), "Cannot open directory '%s': %s.", task->src, strerror(errno));
263 _e_fm_ipc_client_send(task->id, E_FM_OP_ERROR_RETRY_ABORT, buf, strlen(buf) + 1);
267 Eina_File_Direct_Info *info;
268 Eina_List *files = NULL;
269 char *dot_order = NULL;
271 /* create a new dir entry */
272 ed = calloc(1, sizeof(E_Dir));
274 ed->dir = eina_stringshare_add(task->src);
277 /* if no previous monitoring dir exists - this one
278 * becomes the master monitor enty */
279 ed->mon = ecore_file_monitor_add(ed->dir, _e_fm_ipc_cb_file_monitor, ed);
284 /* an existing monitor exists - ref it up */
288 _e_dirs = eina_list_append(_e_dirs, ed);
290 /* read everything except a .order, . and .. */
291 EINA_ITERATOR_FOREACH(it, info)
293 if (!strcmp(info->path + info->name_start, ".order"))
295 dot_order = strdup(info->path);
298 files = eina_list_append(files, eina_stringshare_add(info->path + info->name_start));
300 eina_iterator_free(it);
302 /* if there was a .order - we need to parse it */
307 f = eina_file_open(dot_order, EINA_FALSE);
310 Eina_List *f2 = NULL;
313 /* This piece of code should really become generic and work like an iterator.
314 * I plan to add this feature in eina, but that would be for next generation of E,
317 map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL);
320 const char *current = map;
322 size_t length = eina_file_size_get(f);
324 /* inset files in order if the existed in file
326 while ((found = memchr(current, '\n', length)))
328 if (found - current > 1)
330 char *s = _e_str_list_remove(&files, current, found - current - 1);
331 if (s) f2 = eina_list_append(f2, s);
333 length -= found - current - 1;
337 if (found == NULL && length > 0)
339 char *s = _e_str_list_remove(&files, current, length);
340 if (s) f2 = eina_list_append(f2, s);
343 /* append whats left */
344 files = eina_list_merge(f2, files);
346 eina_file_map_free(f, map);
353 /* FIXME: if .order file- load it, sort all items int it
354 * that are in files then just append whatever is left in
357 /* FIXME: maybe one day we can sort files here and handle
358 * .order file stuff here - but not today
360 /* note that we had a .order at all */
361 ed->dot_order = dot_order ? EINA_TRUE : EINA_FALSE;
364 /* if we did - tell the E about this FIRST - it will
365 * decide what to do if it first sees a .order or not */
366 if (eina_list_count(files) == 1)
367 _e_fm_ipc_file_add(ed, dot_order, 2);
369 _e_fm_ipc_file_add(ed, dot_order, 1);
371 /* send empty file - indicate empty dir */
372 if (!files) _e_fm_ipc_file_add(ed, "", 2);
373 /* and in an idler - list files, statting them etc. */
374 ed->idler = ecore_idler_add(_e_fm_ipc_cb_file_mon_list_idler, ed);
375 ed->sync_num = DEF_SYNC_NUM;
382 _e_fm_ipc_monitor_end(int id, const char *path)
388 EINA_LIST_FOREACH(_e_dirs, l, ed)
389 /* look for the dire entry to stop monitoring */
390 if ((id == ed->id) && (!strcmp(ed->dir, path)))
392 /* if this is not the real monitoring node - unref the
396 /* unref original monitor node */
397 ed->mon_real->mon_ref--;
398 if (ed->mon_real->mon_ref == 0)
400 /* original is at 0 ref - free it */
401 _e_fm_ipc_dir_del(ed->mon_real);
405 _e_fm_ipc_dir_del(ed);
407 /* this is a core monitoring node - remove ref */
411 /* we are the last ref - free */
412 if (ed->mon_ref == 0) _e_fm_ipc_dir_del(ed);
414 /* remove from dirs list anyway */
415 _e_dirs = eina_list_remove_list(_e_dirs, l);
419 task = _e_fm_ipc_task_get(id);
420 if (task) _e_fm_ipc_task_remove(task);
424 _e_fm_ipc_task_get(int id)
426 Eina_List *l = _e_fm_ipc_task_node_get(id);
428 return (E_Fm_Task *)eina_list_data_get(l);
432 _e_fm_ipc_task_node_get(int id)
437 EINA_LIST_FOREACH(_e_fm_tasks, l, task)
445 _e_fm_ipc_task_remove(E_Fm_Task *task)
447 Eina_List *l = _e_fm_ipc_task_node_get(task->id);
451 case E_FM_OP_MONITOR_START:
455 /* we can't open the dir - tell E the dir is deleted as
456 * * we can't look in it */
457 memset(&ted, 0, sizeof(E_Dir));
459 _e_fm_ipc_file_mon_dir_del(&ted, task->src);
467 _e_fm_tasks = eina_list_remove_list(_e_fm_tasks, l);
469 if (task->src) eina_stringshare_del(task->src);
470 if (task->dst) eina_stringshare_del(task->dst);
471 if (task->rel) eina_stringshare_del(task->rel);
477 _e_fm_ipc_mkdir_try(E_Fm_Task *task)
479 char buf[PATH_MAX + 4096];
481 if (mkdir(task->src, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
483 snprintf(buf, sizeof(buf), "Cannot make directory '%s': %s.", task->src, strerror(errno));
484 _e_fm_ipc_client_send(task->id, E_FM_OP_ERROR_RETRY_ABORT, buf, strlen(buf) + 1);
488 _e_fm_ipc_reorder(ecore_file_file_get(task->src), ecore_file_dir_get(task->src), task->rel, task->rel_to);
489 _e_fm_ipc_task_remove(task);
494 _e_fm_ipc_mkdir(int id, const char *src, const char *rel, int rel_to __UNUSED__, int x, int y)
498 task = malloc(sizeof(E_Fm_Task));
501 task->type = E_FM_OP_MKDIR;
503 task->src = eina_stringshare_add(src);
505 task->rel = eina_stringshare_add(rel);
509 _e_fm_tasks = eina_list_append(_e_fm_tasks, task);
511 _e_fm_ipc_mkdir_try(task);
515 _e_fm_ipc_handle_error_response(int id, E_Fm_Op_Type type)
517 E_Fm_Task *task = _e_fm_ipc_task_get(id);
518 E_Fm_Slave *slave = NULL;
522 slave = _e_fm_ipc_slave_get(id);
523 if (slave) _e_fm_ipc_slave_send(slave, type, NULL, 0);
527 if (type == E_FM_OP_ERROR_RESPONSE_ABORT)
529 _e_fm_ipc_task_remove(task);
531 else if (type == E_FM_OP_ERROR_RESPONSE_RETRY)
536 _e_fm_ipc_mkdir_try(task);
539 case E_FM_OP_MONITOR_START:
540 _e_fm_ipc_monitor_start_try(task);
549 _e_fm_ipc_cb_server_data(void *data __UNUSED__, int type __UNUSED__, void *event)
551 Ecore_Ipc_Event_Server_Data *e;
554 if (e->major != 6 /*E_IPC_DOMAIN_FM*/) return ECORE_CALLBACK_PASS_ON;
557 case E_FM_OP_MONITOR_START: /* monitor dir (and implicitly list) */
559 _e_fm_ipc_monitor_start(e->ref, e->data);
563 case E_FM_OP_MONITOR_END: /* monitor dir end */
565 // printf("End listing directory: %s\n", e->data);
566 _e_fm_ipc_monitor_end(e->ref, e->data);
570 case E_FM_OP_REMOVE: /* fop delete file/dir */
572 _e_fm_ipc_slave_run(E_FM_OP_REMOVE, (const char *)e->data, e->ref);
576 case E_FM_OP_TRASH: /* fop trash file/dir */
580 fop = calloc(1, sizeof(E_Fop));
584 fop->src = eina_stringshare_add(e->data);
585 _e_fops = eina_list_append(_e_fops, fop);
586 fop->idler = ecore_idler_add(_e_fm_ipc_cb_fop_trash_idler, fop);
591 case E_FM_OP_MOVE: /* fop cp file/dir && rm file/dir */
593 _e_fm_ipc_slave_run(E_FM_OP_MOVE, (const char *)e->data, e->ref);
597 case E_FM_OP_COPY: /* fop cp file/dir */
599 _e_fm_ipc_slave_run(E_FM_OP_COPY, (const char *)e->data, e->ref);
603 case E_FM_OP_SYMLINK: /* fop ln -s */
605 _e_fm_ipc_slave_run(E_FM_OP_SYMLINK, (const char *)e->data, e->ref);
609 case E_FM_OP_RENAME: /* fop mv */
611 _e_fm_ipc_slave_run(E_FM_OP_RENAME, (const char *)e->data, e->ref);
615 case E_FM_OP_MKDIR: /* fop mkdir */
617 const char *src, *rel;
621 rel = src + strlen(src) + 1;
622 memcpy(&rel_to, rel + strlen(rel) + 1, sizeof(int));
623 memcpy(&x, rel + strlen(rel) + 1 + sizeof(int), sizeof(int));
624 memcpy(&y, rel + strlen(rel) + 1 + sizeof(int), sizeof(int));
626 _e_fm_ipc_mkdir(e->ref, src, rel, rel_to, x, y);
630 case E_FM_OP_MOUNT: /* mount udi mountpoint */
633 const char *udi, *mountpoint;
637 v = e_volume_find(udi);
640 if (ulen + 1 >= e->size)
641 WRN("No mount point available for %s, trying anyway", udi);
644 mountpoint = udi + strlen(udi) + 1;
646 eina_stringshare_replace(&v->mount_point, mountpoint);
647 // printf("REQ M %p (find from %s -> %s)\n", v, udi, mountpoint); fflush(stdout);
653 case E_FM_OP_UNMOUNT: /* unmount udi */
659 v = e_volume_find(udi);
662 // printf("REQ UM\n"); fflush(stdout);
668 case E_FM_OP_EJECT: /* eject udi */
674 v = e_volume_find(udi);
680 case E_FM_OP_QUIT: /* quit */
681 ecore_main_loop_quit();
684 case E_FM_OP_MONITOR_SYNC: /* mon list sync */
690 EINA_LIST_FOREACH(_e_dirs, l, ed)
694 if (ed->sync == e->response)
696 sync_time = ecore_time_get() - ed->sync_time;
697 /* try keep round trips to round trip tolerance */
699 (sync_time < (DEF_ROUND_TRIP - DEF_ROUND_TRIP_TOLERANCE))
702 (sync_time > (DEF_ROUND_TRIP + DEF_ROUND_TRIP_TOLERANCE))
704 /* always sync at least 1 file */
705 if (ed->sync_num < 1) ed->sync_num = 1;
706 ed->idler = ecore_idler_add(_e_fm_ipc_cb_file_mon_list_idler, ed);
714 case E_FM_OP_ABORT: // abort copy/move/delete operation by user
716 E_Fm_Slave *slave = _e_fm_ipc_slave_get(e->ref);
718 _e_fm_ipc_slave_send(slave, e->minor, NULL, 0);
722 case E_FM_OP_ERROR_RESPONSE_IGNORE_THIS:
723 case E_FM_OP_ERROR_RESPONSE_IGNORE_ALL:
724 case E_FM_OP_ERROR_RESPONSE_ABORT:
725 case E_FM_OP_ERROR_RESPONSE_RETRY:
727 _e_fm_ipc_handle_error_response(e->ref, e->minor);
731 case E_FM_OP_OVERWRITE_RESPONSE_NO:
732 case E_FM_OP_OVERWRITE_RESPONSE_NO_ALL:
733 case E_FM_OP_OVERWRITE_RESPONSE_YES:
734 case E_FM_OP_OVERWRITE_RESPONSE_YES_ALL:
736 _e_fm_ipc_slave_send(_e_fm_ipc_slave_get(e->ref), e->minor, NULL, 0);
740 case E_FM_OP_REORDER:
742 const char *file, *dst, *relative;
747 p += strlen(file) + 1;
750 p += strlen(dst) + 1;
753 p += strlen(relative) + 1;
757 _e_fm_ipc_reorder(file, dst, relative, after);
764 /* always send back an "OK" for each request so e can basically keep a
765 * count of outstanding requests - maybe for balancing between fm
766 * slaves later. ref_to is set to the the ref id in the request to
767 * allow for async handling later */
768 ecore_ipc_server_send(_e_fm_ipc_server,
769 6 /*E_IPC_DOMAIN_FM*/,
771 0, e->ref, 0, NULL, 0);
772 return ECORE_CALLBACK_PASS_ON;
776 _e_fm_ipc_client_send(int id, E_Fm_Op_Type type, void *data, int size)
778 return ecore_ipc_server_send(_e_fm_ipc_server,
779 6 /*E_IPC_DOMAIN_FM*/,
781 id, 0, 0, data, size);
785 _e_fm_ipc_slave_run(E_Fm_Op_Type type, const char *args, int id)
790 slave = malloc(sizeof(E_Fm_Slave));
792 if (!slave) return 0;
794 command = eina_stringshare_add(_e_fm_ipc_prepare_command(type, args));
802 slave->exe = ecore_exe_pipe_run(command, ECORE_EXE_PIPE_WRITE | ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_ERROR, slave);
803 // printf("EFM command: %s\n", command);
805 eina_stringshare_del(command);
807 _e_fm_ipc_slaves = eina_list_append(_e_fm_ipc_slaves, slave);
813 _e_fm_ipc_slave_get(int id)
818 EINA_LIST_FOREACH(_e_fm_ipc_slaves, l, slave)
828 _e_fm_ipc_slave_send(E_Fm_Slave *slave, E_Fm_Op_Type type, void *data, int size)
832 int magic = E_FM_OP_MAGIC;
835 ssize = 3 * sizeof(int) + size;
836 sdata = malloc(ssize);
838 if (!sdata) return 0;
840 memcpy(sdata, &magic, sizeof(int));
841 memcpy(sdata + sizeof(int), &type, sizeof(E_Fm_Op_Type));
842 memcpy(sdata + sizeof(int) + sizeof(E_Fm_Op_Type), &size, sizeof(int));
844 memcpy(sdata + 2 * sizeof(int) + sizeof(E_Fm_Op_Type), data, size);
846 result = ecore_exe_send(slave->exe, sdata, ssize);
854 _e_fm_ipc_slave_data_cb(void *data, int type __UNUSED__, void *event)
856 Ecore_Exe_Event_Data *e = event;
862 if (!e) return ECORE_CALLBACK_PASS_ON;
864 slave = ecore_exe_data_get(e->exe);
865 if (!slave) return ECORE_CALLBACK_RENEW;
866 if (data) return ECORE_CALLBACK_PASS_ON; /* ipc handlers have NULL data */
867 if (!eina_list_data_find(_e_fm_ipc_slaves, slave)) return ECORE_CALLBACK_PASS_ON;
872 while ((unsigned int)ssize >= 3 * sizeof(int))
874 memcpy(&magic, sdata, sizeof(int));
875 memcpy(&id, sdata + sizeof(int), sizeof(int));
876 memcpy(&size, sdata + sizeof(int) + sizeof(int), sizeof(int));
878 if (magic != E_FM_OP_MAGIC)
880 printf("%s:%s(%d) Wrong magic number from slave #%d. ", __FILE__, __FUNCTION__, __LINE__, slave->id);
884 sdata += 3 * sizeof(int);
885 ssize -= 3 * sizeof(int);
887 if (id == E_FM_OP_OVERWRITE)
889 _e_fm_ipc_client_send(slave->id, E_FM_OP_OVERWRITE, sdata, size);
890 printf("%s:%s(%d) Overwrite sent to client from slave #%d.\n", __FILE__, __FUNCTION__, __LINE__, slave->id);
892 else if (id == E_FM_OP_ERROR)
894 _e_fm_ipc_client_send(slave->id, E_FM_OP_ERROR, sdata, size);
896 else if (id == E_FM_OP_PROGRESS)
898 _e_fm_ipc_client_send(slave->id, E_FM_OP_PROGRESS, sdata, size);
905 return ECORE_CALLBACK_PASS_ON;
909 _e_fm_ipc_slave_error_cb(void *data, int type __UNUSED__, void *event)
911 Ecore_Exe_Event_Data *e = event;
916 slave = ecore_exe_data_get(e->exe);
917 if (!slave) return ECORE_CALLBACK_RENEW;
918 if (data) return ECORE_CALLBACK_PASS_ON; /* ipc handlers have NULL data */
919 if (!eina_list_data_find(_e_fm_ipc_slaves, slave)) return ECORE_CALLBACK_PASS_ON;
921 printf("EFM: Data from STDERR of slave #%d: %.*s", slave->id, e->size, (char *)e->data);
927 _e_fm_ipc_slave_del_cb(void *data, int type __UNUSED__, void *event)
929 Ecore_Exe_Event_Del *e = event;
933 if (!e) return ECORE_CALLBACK_RENEW;
935 slave = ecore_exe_data_get(e->exe);
936 if (!slave) return ECORE_CALLBACK_RENEW;
937 if (data) return ECORE_CALLBACK_PASS_ON; /* ipc handlers have NULL data */
938 l = eina_list_data_find_list(_e_fm_ipc_slaves, slave);
939 if (!l) return ECORE_CALLBACK_PASS_ON;
940 _e_fm_ipc_client_send(slave->id, E_FM_OP_QUIT, NULL, 0);
942 _e_fm_ipc_slaves = eina_list_remove(_e_fm_ipc_slaves, l);
945 return ECORE_CALLBACK_RENEW;
949 _e_fm_ipc_cb_file_monitor(void *data __UNUSED__, Ecore_File_Monitor *em __UNUSED__, Ecore_File_Event event, const char *path)
952 char *dir, *rp, *drp;
955 dir = ecore_file_dir_get(path);
956 /* FIXME: get no create events if dir is empty */
957 if ((event == ECORE_FILE_EVENT_CREATED_FILE) ||
958 (event == ECORE_FILE_EVENT_CREATED_DIRECTORY))
960 rp = ecore_file_realpath(dir);
961 EINA_LIST_FOREACH(_e_dirs, l, ed)
963 drp = ecore_file_realpath(ed->dir);
966 if (!strcmp(rp, drp))
967 _e_fm_ipc_file_add(ed, path, 0);
973 else if ((event == ECORE_FILE_EVENT_DELETED_FILE) ||
974 (event == ECORE_FILE_EVENT_DELETED_DIRECTORY))
976 rp = ecore_file_realpath(dir);
977 EINA_LIST_FOREACH(_e_dirs, l, ed)
979 drp = ecore_file_realpath(ed->dir);
982 if (!strcmp(rp, drp))
983 _e_fm_ipc_file_del(ed, path);
989 else if (event == ECORE_FILE_EVENT_MODIFIED)
991 rp = ecore_file_realpath(dir);
992 EINA_LIST_FOREACH(_e_dirs, l, ed)
994 drp = ecore_file_realpath(ed->dir);
997 if (!strcmp(rp, drp))
998 _e_fm_ipc_file_mod(ed, path);
1004 else if (event == ECORE_FILE_EVENT_DELETED_SELF)
1006 rp = ecore_file_realpath(path);
1007 EINA_LIST_FOREACH(_e_dirs, l, ed)
1009 drp = ecore_file_realpath(ed->dir);
1012 if (!strcmp(rp, drp))
1013 _e_fm_ipc_file_mon_dir_del(ed, path);
1023 _e_fm_ipc_cb_recent_clean(void *data)
1032 t_now = ecore_time_unix_get();
1033 EINA_LIST_FOREACH_SAFE (ed->recent_mods, pl, l, m)
1034 if ((m->mod) && ((t_now - m->timestamp) >= DEF_MOD_BACKOFF))
1036 ed->recent_mods = eina_list_remove_list(ed->recent_mods, pl);
1037 if (!m->done) _e_fm_ipc_file_add_mod(ed, m->path, 5, 0);
1038 eina_stringshare_del(m->path);
1042 if (ed->recent_mods) return ECORE_CALLBACK_RENEW;
1043 ed->recent_clean = NULL;
1044 return ECORE_CALLBACK_CANCEL;
1048 _e_fm_ipc_file_add_mod(E_Dir *ed, const char *path, E_Fm_Op_Type op, int listing)
1051 char *lnk = NULL, *rlnk = NULL;
1054 unsigned char *p, buf
1055 /* file add/change format is as follows:
1057 * stat_info[stat size] + broken_link[1] + path[n]\0 + lnk[n]\0 + rlnk[n]\0 */
1058 [sizeof(struct stat) + 1 + 4096 + 4096 + 4096];
1060 /* FIXME: handle BACKOFF */
1061 if ((!listing) && (op == E_FM_OP_FILE_CHANGE) && (!ed->cleaning)) /* 5 == mod */
1068 t_now = ecore_time_unix_get();
1069 EINA_LIST_FOREACH(ed->recent_mods, l, m)
1071 if ((m->mod) && (!strcmp(m->path, path)))
1073 if ((t_now - m->timestamp) < DEF_MOD_BACKOFF)
1082 m = calloc(1, sizeof(E_Mod));
1083 m->path = eina_stringshare_add(path);
1086 m->timestamp = t_now;
1087 ed->recent_mods = eina_list_append(ed->recent_mods, m);
1089 if ((!ed->recent_clean) && (ed->recent_mods))
1090 ed->recent_clean = ecore_timer_add(DEF_MOD_BACKOFF, _e_fm_ipc_cb_recent_clean, ed);
1093 // printf("SKIP MOD %s %3.3f\n", path, t_now);
1097 // printf("MOD %s %3.3f\n", path, ecore_time_unix_get());
1098 lnk = ecore_file_readlink(path);
1099 memset(&st, 0, sizeof(struct stat));
1100 if (stat((lnk && lnk[0]) ? lnk : path, &st) == -1)
1102 if ((path[0] == 0) || (lnk)) broken_lnk = 1;
1105 if ((lnk) && (lnk[0] != '/'))
1107 rlnk = ecore_file_realpath(path);
1108 if ((rlnk == NULL) || (rlnk[0] == 0) ||
1109 (stat(rlnk, &st) == -1))
1116 if (!lnk) lnk = strdup("");
1117 if (!rlnk) rlnk = strdup("");
1120 /* NOTE: i am NOT converting this data to portable arch/os independent
1121 * format. i am ASSUMING e_fm_main and e are local and built together
1122 * and thus this will work. if this ever changes this here needs to
1124 memcpy(buf, &st, sizeof(struct stat));
1125 p += sizeof(struct stat);
1130 strcpy((char *)p, path);
1131 p += strlen(path) + 1;
1133 strcpy((char *)p, lnk);
1134 p += strlen(lnk) + 1;
1136 strcpy((char *)p, rlnk);
1137 p += strlen(rlnk) + 1;
1140 ecore_ipc_server_send(_e_fm_ipc_server, 6 /*E_IPC_DOMAIN_FM*/, op, 0, ed->id,
1143 if (rlnk) free(rlnk);
1147 _e_fm_ipc_file_add(E_Dir *ed, const char *path, int listing)
1151 /* FIXME: handle BACKOFF */
1153 _e_fm_ipc_file_add_mod(ed, path, E_FM_OP_FILE_ADD, listing); /*file add*/
1157 _e_fm_ipc_file_del(E_Dir *ed, const char *path)
1160 /* FIXME: handle BACKOFF */
1162 ecore_ipc_server_send(_e_fm_ipc_server,
1163 6 /*E_IPC_DOMAIN_FM*/,
1165 0, ed->id, 0, (void *)path, strlen(path) + 1);
1169 _e_fm_ipc_file_mod(E_Dir *ed, const char *path)
1172 /* FIXME: handle BACKOFF */
1174 _e_fm_ipc_file_add_mod(ed, path, E_FM_OP_FILE_CHANGE, 0); /*file change*/
1178 _e_fm_ipc_file_mon_dir_del(E_Dir *ed, const char *path)
1180 ecore_ipc_server_send(_e_fm_ipc_server,
1181 6 /*E_IPC_DOMAIN_FM*/,
1182 E_FM_OP_MONITOR_END,
1183 0, ed->id, 0, (void *)path, strlen(path) + 1);
1187 _e_fm_ipc_file_mon_list_sync(E_Dir *ed)
1190 if (_e_sync_num == 0) _e_sync_num = 1;
1191 ed->sync = _e_sync_num;
1192 ed->sync_time = ecore_time_get();
1193 ecore_ipc_server_send(_e_fm_ipc_server,
1194 6 /*E_IPC_DOMAIN_FM*/,
1195 E_FM_OP_MONITOR_SYNC,
1196 0, ed->id, ed->sync, NULL, 0);
1200 _e_fm_ipc_cb_file_mon_list_idler(void *data)
1204 char *file, buf[4096];
1207 /* FIXME: spool off files in idlers and handle sync req's */
1210 file = eina_list_data_get(ed->fq);
1211 if (!((ed->dot_order) && (!strcmp(file, ".order"))))
1213 if (!strcmp(ed->dir, "/"))
1214 snprintf(buf, sizeof(buf), "/%s", file);
1216 snprintf(buf, sizeof(buf), "%s/%s", ed->dir, file);
1217 _e_fm_ipc_file_add(ed, buf, 1);
1219 eina_stringshare_del(file);
1220 ed->fq = eina_list_remove_list(ed->fq, ed->fq);
1222 if (n == ed->sync_num)
1224 _e_fm_ipc_file_mon_list_sync(ed);
1226 if (!ed->fq) _e_fm_ipc_file_add(ed, "", 2);
1230 ed->sync_num = DEF_SYNC_NUM;
1232 ed->sync_time = 0.0;
1234 if (!ed->fq) _e_fm_ipc_file_add(ed, "", 2);
1235 return ECORE_CALLBACK_CANCEL;
1239 _e_fm_ipc_cb_fop_trash_idler(void *data)
1243 const char *trash_dir = NULL;
1244 const char *filename = NULL;
1245 const char *escname = NULL;
1246 const char *dest = NULL;
1252 /* FIXME: For now, this will only implement 'home trash'
1253 * Later, implement mount/remote/removable device trash, if wanted. */
1255 fop = (E_Fop *)data;
1258 /* Check that 'home trash' and subsequesnt dirs exists, create if not */
1259 snprintf(buf, sizeof(buf), "%s/Trash", efreet_data_home_get());
1260 trash_dir = eina_stringshare_add(buf);
1261 snprintf(buf, sizeof(buf), "%s/files", trash_dir);
1262 if (!ecore_file_mkpath(buf)) return 0;
1263 snprintf(buf, sizeof(buf), "%s/info", trash_dir);
1264 if (!ecore_file_mkpath(buf)) return 0;
1266 filename = eina_stringshare_add(strrchr(fop->src, '/'));
1267 escname = ecore_file_escape_name(filename);
1268 eina_stringshare_del(filename);
1270 /* Find path for info file. Pointer address is part of the filename to
1271 * alleviate some of the looping in case of multiple filenames with the
1272 * same name. Also use the name of the file to help */
1275 snprintf(buf, sizeof(buf), "%s/file%s.%p.%u", trash_dir, escname,
1278 while (ecore_file_exists(buf));
1279 dest = eina_stringshare_add(buf);
1281 /* Try to move the file */
1282 if (rename(fop->src, dest))
1286 /* Move failed. Spec says delete files that can't be trashed */
1287 ecore_file_unlink(fop->src);
1288 return ECORE_CALLBACK_CANCEL;
1292 /* Move worked. Create info file */
1293 snprintf(buf, sizeof(buf), "%s/info%s.%p.%u.trashinfo", trash_dir,
1295 info = fopen(buf, "w");
1301 /* Insert info for trashed file */
1303 "[Trash Info]\nPath=%s\nDeletionDate=%04u%02u%02uT%02u:%02u:%02u",
1304 fop->src, lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday,
1305 lt->tm_hour, lt->tm_min, lt->tm_sec);
1309 /* Could not create info file. Spec says to put orig file back */
1310 rename(dest, fop->src);
1312 if (dest) eina_stringshare_del(dest);
1313 if (trash_dir) eina_stringshare_del(trash_dir);
1314 eina_stringshare_del(fop->src);
1315 eina_stringshare_del(fop->dst);
1316 _e_fops = eina_list_remove(_e_fops, fop);
1318 return ECORE_CALLBACK_CANCEL;
1322 _e_str_list_remove(Eina_List **list, const char *str, int len)
1327 EINA_LIST_FOREACH(*list, l, s)
1328 if (!strncmp(s, str, len))
1330 *list = eina_list_remove_list(*list, l);
1338 _e_fm_ipc_reorder(const char *file, const char *dst, const char *relative, int after)
1340 char buffer[PATH_MAX];
1341 char order[PATH_MAX];
1343 if (!file || !dst || !relative) return;
1344 if (after != 0 && after != 1 && after != 2) return;
1345 // printf("%s:%s(%d) Reorder:\n\tfile = %s\n\tdst = %s\n\trelative = %s\n\tafter = %d\n", __FILE__, __FUNCTION__, __LINE__, file, dst, relative, after);
1347 snprintf(order, sizeof(order), "%s/.order", dst);
1348 if (ecore_file_exists(order))
1351 Eina_List *files = NULL, *l;
1354 forder = fopen(order, "r");
1359 /* inset files in order if the existed in file
1361 while (fgets(buffer, sizeof(buffer), forder))
1363 len = strlen(buffer);
1364 if (len > 0) buffer[len - 1] = 0;
1365 files = eina_list_append(files, strdup(buffer));
1369 /* remove dest file from .order - if there */
1370 EINA_LIST_FOREACH(files, l, str)
1371 if (!strcmp(str, file))
1374 files = eina_list_remove_list(files, l);
1377 /* now insert dest into list or replace entry */
1378 EINA_LIST_FOREACH(files, l, str)
1380 if (!strcmp(str, relative))
1382 if (after == 2) /* replace */
1385 l->data = strdup(file);
1387 else if (after == 0) /* before */
1389 files = eina_list_prepend_relative_list(files, strdup(file), l);
1391 else if (after == 1) /* after */
1393 files = eina_list_append_relative_list(files, strdup(file), l);
1399 forder = fopen(order, "w");
1402 EINA_LIST_FREE(files, str)
1404 fprintf(forder, "%s\n", str);
1413 _e_fm_ipc_dir_del(E_Dir *ed)
1418 eina_stringshare_del(ed->dir);
1419 if (ed->idler) ecore_idler_del(ed->idler);
1420 if (ed->recent_clean)
1421 ecore_timer_del(ed->recent_clean);
1422 EINA_LIST_FREE(ed->recent_mods, m)
1424 eina_stringshare_del(m->path);
1427 EINA_LIST_FREE(ed->fq, data)
1428 eina_stringshare_del(data);
1433 _e_fm_ipc_prepare_command(E_Fm_Op_Type type, const char *args)
1437 unsigned int length = 0;
1440 if (type == E_FM_OP_MOVE)
1441 strcpy(command, "mv");
1442 else if (type == E_FM_OP_REMOVE)
1443 strcpy(command, "rm");
1444 else if (type == E_FM_OP_COPY)
1445 strcpy(command, "cp");
1446 else if (type == E_FM_OP_SYMLINK)
1447 strcpy(command, "lns");
1448 else if (type == E_FM_OP_RENAME)
1449 strcpy(command, "mvf");
1453 libdir = getenv("E_LIB_DIR") ?: PACKAGE_LIB_DIR;
1454 length = 256 + strlen(libdir) + strlen(args);
1455 buffer = malloc(length);
1456 snprintf(buffer, length,
1457 "%s/enlightenment/utils/enlightenment_fm_op %s %s",
1458 getenv("E_LIB_DIR"), command, args);