Tizen 2.1 release
[platform/core/uifw/e17.git] / src / bin / e_fm / e_fm_ipc.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5
6 #ifndef _FILE_OFFSET_BITS
7 #define _FILE_OFFSET_BITS 64
8 #endif
9
10 #ifdef __linux__
11 #include <features.h>
12 #endif
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <sys/time.h>
20 #include <sys/param.h>
21 #include <utime.h>
22 #include <math.h>
23 #include <fnmatch.h>
24 #include <limits.h>
25 #include <ctype.h>
26 #include <time.h>
27 #include <dirent.h>
28 #include <pwd.h>
29 #include <glob.h>
30 #include <errno.h>
31 #include <signal.h>
32 #include <Ecore.h>
33 #include <Ecore_Ipc.h>
34 #include <Ecore_File.h>
35 #include <Evas.h>
36 #include <Efreet.h>
37 #include <Eet.h>
38
39 #include "e_fm_ipc.h"
40 #define E_TYPEDEFS
41 #include "e_fm_op.h"
42 #undef E_TYPEDEFS
43 #include "e_fm_main.h"
44
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
49
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;
55
56 struct _E_Dir
57 {
58    int                 id;
59    const char         *dir;
60    Ecore_File_Monitor *mon;
61    int                 mon_ref;
62    E_Dir              *mon_real;
63    Eina_List          *fq;
64    Ecore_Idler        *idler;
65    int                 dot_order;
66    int                 sync;
67    double              sync_time;
68    int                 sync_num;
69    Eina_List          *recent_mods;
70    Ecore_Timer        *recent_clean;
71    unsigned char       cleaning : 1;
72 };
73
74 struct _E_Fop
75 {
76    int           id;
77    const char   *src;
78    const char   *dst;
79    const char   *rel;
80    int           rel_to;
81    int           x, y;
82    unsigned char del_after : 1;
83    unsigned char gone_bad : 1;
84    Ecore_Idler  *idler;
85    void         *data;
86 };
87
88 struct _E_Mod
89 {
90    const char   *path;
91    double        timestamp;
92    unsigned char add : 1;
93    unsigned char del : 1;
94    unsigned char mod : 1;
95    unsigned char done : 1;
96 };
97
98 struct _e_fm_ipc_slave
99 {
100    Ecore_Exe *exe;
101    int        id;
102 };
103
104 struct _E_Fm_Task
105 {
106    int          id;
107    E_Fm_Op_Type type;
108    E_Fm_Slave  *slave;
109    const char  *src;
110    const char  *dst;
111    const char  *rel;
112    int          rel_to;
113    int          x, y;
114 };
115
116 /* local subsystem globals */
117 Ecore_Ipc_Server *_e_fm_ipc_server = NULL;
118
119 static Eina_List *_e_dirs = NULL;
120 static Eina_List *_e_fops = NULL;
121 static int _e_sync_num = 0;
122
123 static Eina_List *_e_fm_ipc_slaves = NULL;
124 static Eina_List *_e_fm_tasks = NULL;
125
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);
130
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);
140
141 static int         _e_fm_ipc_client_send(int id, E_Fm_Op_Type type, void *data, int size);
142
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);
146
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);
149
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);
156
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);
162
163 static const char *_e_fm_ipc_prepare_command(E_Fm_Op_Type type, const char *args);
164
165 /* local subsystem functions */
166 int
167 _e_fm_ipc_init(void)
168 {
169    char *sdir;
170
171    sdir = getenv("E_IPC_SOCKET");
172    if (!sdir)
173      {
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");
178         return 0;
179      }
180    _e_fm_ipc_server = ecore_ipc_server_connect(ECORE_IPC_LOCAL_SYSTEM, sdir, 0, NULL);
181    if (!_e_fm_ipc_server)
182      {
183         printf("Cannot connect to enlightenment (socket '%s') - abort\n", sdir);
184         return 0;
185      }
186
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);
190
191    return 1;
192 }
193
194 static Eina_Bool
195 _e_fm_ipc_cb_server_add(void *data __UNUSED__, int type __UNUSED__, void *event)
196 {
197    Ecore_Ipc_Event_Server_Add *e;
198
199    e = event;
200    ecore_ipc_server_send(e->server,
201                          6 /*E_IPC_DOMAIN_FM*/,
202                          E_FM_OP_HELLO,
203                          0, 0, 0, NULL, 0); /* send hello */
204    return ECORE_CALLBACK_PASS_ON;
205 }
206
207 static Eina_Bool
208 _e_fm_ipc_cb_server_del(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__)
209 {
210    /* quit now */
211    ecore_main_loop_quit();
212    return ECORE_CALLBACK_PASS_ON;
213 }
214
215 static void
216 _e_fm_ipc_monitor_start(int id, const char *path)
217 {
218    E_Fm_Task *task = malloc(sizeof(E_Fm_Task));
219
220    if (!task) return;
221
222    task->id = id;
223    task->type = E_FM_OP_MONITOR_START;
224    task->slave = NULL;
225    task->src = eina_stringshare_add(path);
226    task->dst = NULL;
227    task->rel = NULL;
228    task->rel_to = 0;
229    task->x = 0;
230    task->y = 0;
231
232    _e_fm_tasks = eina_list_append(_e_fm_tasks, task);
233
234    _e_fm_ipc_monitor_start_try(task);
235 }
236
237 static void
238 _e_fm_ipc_monitor_start_try(E_Fm_Task *task)
239 {
240    E_Dir *ed, *ped = NULL;
241    Eina_Iterator *it;
242
243    Eina_List *l;
244
245    /* look for any previous dir entries monitoring this dir */
246    EINA_LIST_FOREACH(_e_dirs, l, ed)
247      {
248         if ((ed->mon) && (!strcmp(ed->dir, task->src)))
249           {
250              /* found a previous dir - save it in ped */
251              ped = ed;
252              break;
253           }
254      }
255
256    /* open the dir to list */
257    it = eina_file_direct_ls(task->src);
258    if (!it)
259      {
260         char buf[PATH_MAX + 4096];
261
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);
264      }
265    else
266      {
267         Eina_File_Direct_Info *info;
268         Eina_List *files = NULL;
269         char *dot_order = NULL;
270
271         /* create a new dir entry */
272         ed = calloc(1, sizeof(E_Dir));
273         ed->id = task->id;
274         ed->dir = eina_stringshare_add(task->src);
275         if (!ped)
276           {
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);
280              ed->mon_ref = 1;
281           }
282         else
283           {
284              /* an existing monitor exists - ref it up */
285              ed->mon_real = ped;
286              ped->mon_ref++;
287           }
288         _e_dirs = eina_list_append(_e_dirs, ed);
289
290         /* read everything except a .order, . and .. */
291         EINA_ITERATOR_FOREACH(it, info)
292           {
293              if (!strcmp(info->path + info->name_start, ".order"))
294                {
295                   dot_order = strdup(info->path);
296                   continue;
297                }
298              files = eina_list_append(files, eina_stringshare_add(info->path + info->name_start));
299           }
300         eina_iterator_free(it);
301
302         /* if there was a .order - we need to parse it */
303         if (dot_order)
304           {
305              Eina_File *f;
306
307              f = eina_file_open(dot_order, EINA_FALSE);
308              if (f)
309                {
310                   Eina_List *f2 = NULL;
311                   char *map;
312
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,
315                    * not E17.
316                    */
317                   map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL);
318                   if (map)
319                     {
320                        const char *current = map;
321                        const char *found;
322                        size_t length = eina_file_size_get(f);
323
324                        /* inset files in order if the existed in file
325                         * list before */
326                        while ((found = memchr(current, '\n', length)))
327                          {
328                             if (found - current > 1)
329                               {
330                                  char *s = _e_str_list_remove(&files, current, found - current - 1);
331                                  if (s) f2 = eina_list_append(f2, s);
332                               }
333                             length -= found - current - 1;
334                             current = found + 1;
335                          }
336
337                        if (found == NULL && length > 0)
338                          {
339                             char *s = _e_str_list_remove(&files, current, length);
340                             if (s) f2 = eina_list_append(f2, s);
341                          }
342
343                        /* append whats left */
344                        files = eina_list_merge(f2, files);
345
346                        eina_file_map_free(f, map);
347                     }
348
349                   eina_file_close(f);
350                }
351           }
352         ed->fq = files;
353         /* FIXME: if .order file- load it, sort all items int it
354          * that are in files then just append whatever is left in
355          * alphabetical order
356          */
357         /* FIXME: maybe one day we can sort files here and handle
358          * .order file stuff here - but not today
359          */
360         /* note that we had a .order at all */
361         ed->dot_order = dot_order ? EINA_TRUE : EINA_FALSE;
362         if (dot_order)
363           {
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);
368              else
369                _e_fm_ipc_file_add(ed, dot_order, 1);
370           }
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;
376
377         free(dot_order);
378      }
379 }
380
381 static void
382 _e_fm_ipc_monitor_end(int id, const char *path)
383 {
384    E_Fm_Task *task;
385    Eina_List *l;
386    E_Dir *ed;
387
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)))
391        {
392           /* if this is not the real monitoring node - unref the
393            * real one */
394           if (ed->mon_real)
395             {
396                /* unref original monitor node */
397                ed->mon_real->mon_ref--;
398                if (ed->mon_real->mon_ref == 0)
399                  {
400                     /* original is at 0 ref - free it */
401                     _e_fm_ipc_dir_del(ed->mon_real);
402                     ed->mon_real = NULL;
403                  }
404                /* free this node */
405                _e_fm_ipc_dir_del(ed);
406             }
407           /* this is a core monitoring node - remove ref */
408           else
409             {
410                ed->mon_ref--;
411                /* we are the last ref - free */
412                if (ed->mon_ref == 0) _e_fm_ipc_dir_del(ed);
413             }
414           /* remove from dirs list anyway */
415           _e_dirs = eina_list_remove_list(_e_dirs, l);
416           break;
417        }
418
419    task = _e_fm_ipc_task_get(id);
420    if (task) _e_fm_ipc_task_remove(task);
421 }
422
423 static E_Fm_Task *
424 _e_fm_ipc_task_get(int id)
425 {
426    Eina_List *l = _e_fm_ipc_task_node_get(id);
427
428    return (E_Fm_Task *)eina_list_data_get(l);
429 }
430
431 static Eina_List *
432 _e_fm_ipc_task_node_get(int id)
433 {
434    E_Fm_Task *task;
435    Eina_List *l;
436
437    EINA_LIST_FOREACH(_e_fm_tasks, l, task)
438      if (task->id == id)
439        return l;
440
441    return NULL;
442 }
443
444 static void
445 _e_fm_ipc_task_remove(E_Fm_Task *task)
446 {
447    Eina_List *l = _e_fm_ipc_task_node_get(task->id);
448
449    switch (task->type)
450      {
451       case E_FM_OP_MONITOR_START:
452       {
453          E_Dir ted;
454
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));
458          ted.id = task->id;
459          _e_fm_ipc_file_mon_dir_del(&ted, task->src);
460       }
461       break;
462
463       default:
464         break;
465      }
466
467    _e_fm_tasks = eina_list_remove_list(_e_fm_tasks, l);
468
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);
472
473    free(task);
474 }
475
476 static void
477 _e_fm_ipc_mkdir_try(E_Fm_Task *task)
478 {
479    char buf[PATH_MAX + 4096];
480
481    if (mkdir(task->src, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
482      {
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);
485      }
486    else
487      {
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);
490      }
491 }
492
493 static void
494 _e_fm_ipc_mkdir(int id, const char *src, const char *rel, int rel_to __UNUSED__, int x, int y)
495 {
496    E_Fm_Task *task;
497
498    task = malloc(sizeof(E_Fm_Task));
499
500    task->id = id;
501    task->type = E_FM_OP_MKDIR;
502    task->slave = NULL;
503    task->src = eina_stringshare_add(src);
504    task->dst = NULL;
505    task->rel = eina_stringshare_add(rel);
506    task->x = x;
507    task->y = y;
508
509    _e_fm_tasks = eina_list_append(_e_fm_tasks, task);
510
511    _e_fm_ipc_mkdir_try(task);
512 }
513
514 static void
515 _e_fm_ipc_handle_error_response(int id, E_Fm_Op_Type type)
516 {
517    E_Fm_Task *task = _e_fm_ipc_task_get(id);
518    E_Fm_Slave *slave = NULL;
519
520    if (!task)
521      {
522         slave = _e_fm_ipc_slave_get(id);
523         if (slave) _e_fm_ipc_slave_send(slave, type, NULL, 0);
524         return;
525      }
526
527    if (type == E_FM_OP_ERROR_RESPONSE_ABORT)
528      {
529         _e_fm_ipc_task_remove(task);
530      }
531    else if (type == E_FM_OP_ERROR_RESPONSE_RETRY)
532      {
533         switch (task->type)
534           {
535            case E_FM_OP_MKDIR:
536              _e_fm_ipc_mkdir_try(task);
537              break;
538
539            case E_FM_OP_MONITOR_START:
540              _e_fm_ipc_monitor_start_try(task);
541
542            default:
543              break;
544           }
545      }
546 }
547
548 static Eina_Bool
549 _e_fm_ipc_cb_server_data(void *data __UNUSED__, int type __UNUSED__, void *event)
550 {
551    Ecore_Ipc_Event_Server_Data *e;
552
553    e = event;
554    if (e->major != 6 /*E_IPC_DOMAIN_FM*/) return ECORE_CALLBACK_PASS_ON;
555    switch (e->minor)
556      {
557       case E_FM_OP_MONITOR_START: /* monitor dir (and implicitly list) */
558       {
559          _e_fm_ipc_monitor_start(e->ref, e->data);
560       }
561       break;
562
563       case E_FM_OP_MONITOR_END: /* monitor dir end */
564       {
565 //           printf("End listing directory: %s\n", e->data);
566          _e_fm_ipc_monitor_end(e->ref, e->data);
567       }
568       break;
569
570       case E_FM_OP_REMOVE: /* fop delete file/dir */
571       {
572          _e_fm_ipc_slave_run(E_FM_OP_REMOVE, (const char *)e->data, e->ref);
573       }
574       break;
575
576       case E_FM_OP_TRASH: /* fop trash file/dir */
577       {
578          E_Fop *fop;
579
580          fop = calloc(1, sizeof(E_Fop));
581          if (fop)
582            {
583               fop->id = e->ref;
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);
587            }
588       }
589       break;
590
591       case E_FM_OP_MOVE: /* fop cp file/dir && rm file/dir */
592       {
593          _e_fm_ipc_slave_run(E_FM_OP_MOVE, (const char *)e->data, e->ref);
594       }
595       break;
596
597       case E_FM_OP_COPY: /* fop cp file/dir */
598       {
599          _e_fm_ipc_slave_run(E_FM_OP_COPY, (const char *)e->data, e->ref);
600       }
601       break;
602
603       case E_FM_OP_SYMLINK: /* fop ln -s */
604       {
605          _e_fm_ipc_slave_run(E_FM_OP_SYMLINK, (const char *)e->data, e->ref);
606       }
607       break;
608
609       case E_FM_OP_RENAME: /* fop mv */
610       {
611          _e_fm_ipc_slave_run(E_FM_OP_RENAME, (const char *)e->data, e->ref);
612       }
613       break;
614
615       case E_FM_OP_MKDIR: /* fop mkdir */
616       {
617          const char *src, *rel;
618          int rel_to, x, y;
619
620          src = e->data;
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));
625
626          _e_fm_ipc_mkdir(e->ref, src, rel, rel_to, x, y);
627       }
628       break;
629
630       case E_FM_OP_MOUNT: /* mount udi mountpoint */
631       {
632          E_Volume *v;
633          const char *udi, *mountpoint;
634          int ulen;
635
636          udi = e->data;
637          v = e_volume_find(udi);
638          if (!v) break;
639          ulen = strlen(udi);
640          if (ulen + 1 >= e->size)
641            WRN("No mount point available for %s, trying anyway", udi);
642          else
643            {
644               mountpoint = udi + strlen(udi) + 1;
645               if (mountpoint[0])
646                 eina_stringshare_replace(&v->mount_point, mountpoint);
647 //             printf("REQ M %p (find from %s -> %s)\n", v, udi, mountpoint); fflush(stdout);
648            }
649          e_volume_mount(v);
650       }
651       break;
652
653       case E_FM_OP_UNMOUNT: /* unmount udi */
654       {
655          E_Volume *v;
656          const char *udi;
657
658          udi = e->data;
659          v = e_volume_find(udi);
660          if (v)
661            {
662 //                printf("REQ UM\n"); fflush(stdout);
663               e_volume_unmount(v);
664            }
665       }
666       break;
667
668       case E_FM_OP_EJECT: /* eject udi */
669       {
670          E_Volume *v;
671          const char *udi;
672
673          udi = e->data;
674          v = e_volume_find(udi);
675          if (v)
676            e_volume_eject(v);
677       }
678       break;
679
680       case E_FM_OP_QUIT: /* quit */
681         ecore_main_loop_quit();
682         break;
683
684       case E_FM_OP_MONITOR_SYNC: /* mon list sync */
685       {
686          Eina_List *l;
687          E_Dir *ed;
688          double sync_time;
689
690          EINA_LIST_FOREACH(_e_dirs, l, ed)
691            {
692               if (ed->fq)
693                 {
694                    if (ed->sync == e->response)
695                      {
696                         sync_time = ecore_time_get() - ed->sync_time;
697                         /* try keep round trips to round trip tolerance */
698                         if
699                         (sync_time < (DEF_ROUND_TRIP - DEF_ROUND_TRIP_TOLERANCE))
700                           ed->sync_num += 1;
701                         else if
702                         (sync_time > (DEF_ROUND_TRIP + DEF_ROUND_TRIP_TOLERANCE))
703                           ed->sync_num -= 1;
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);
707                         break;
708                      }
709                 }
710            }
711       }
712       break;
713
714       case E_FM_OP_ABORT: // abort copy/move/delete operation by user
715       {
716          E_Fm_Slave *slave = _e_fm_ipc_slave_get(e->ref);
717          if (slave)
718            _e_fm_ipc_slave_send(slave, e->minor, NULL, 0);
719       }
720       break;
721
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:
726       {
727          _e_fm_ipc_handle_error_response(e->ref, e->minor);
728       }
729       break;
730
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:
735       {
736          _e_fm_ipc_slave_send(_e_fm_ipc_slave_get(e->ref), e->minor, NULL, 0);
737       }
738       break;
739
740       case E_FM_OP_REORDER:
741       {
742          const char *file, *dst, *relative;
743          int after;
744          char *p = e->data;
745
746          file = p;
747          p += strlen(file) + 1;
748
749          dst = p;
750          p += strlen(dst) + 1;
751
752          relative = p;
753          p += strlen(relative) + 1;
754
755          after = *((int *)p);
756
757          _e_fm_ipc_reorder(file, dst, relative, after);
758       }
759       break;
760
761       default:
762         break;
763      }
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*/,
770                          E_FM_OP_OK,
771                          0, e->ref, 0, NULL, 0);
772    return ECORE_CALLBACK_PASS_ON;
773 }
774
775 static int
776 _e_fm_ipc_client_send(int id, E_Fm_Op_Type type, void *data, int size)
777 {
778    return ecore_ipc_server_send(_e_fm_ipc_server,
779                                 6 /*E_IPC_DOMAIN_FM*/,
780                                 type,
781                                 id, 0, 0, data, size);
782 }
783
784 static int
785 _e_fm_ipc_slave_run(E_Fm_Op_Type type, const char *args, int id)
786 {
787    E_Fm_Slave *slave;
788    const char *command;
789
790    slave = malloc(sizeof(E_Fm_Slave));
791
792    if (!slave) return 0;
793
794    command = eina_stringshare_add(_e_fm_ipc_prepare_command(type, args));
795    if (!command)
796      {
797         free(slave);
798         return 0;
799      }
800
801    slave->id = id;
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);
804
805    eina_stringshare_del(command);
806
807    _e_fm_ipc_slaves = eina_list_append(_e_fm_ipc_slaves, slave);
808
809    return !!slave->exe;
810 }
811
812 static E_Fm_Slave *
813 _e_fm_ipc_slave_get(int id)
814 {
815    Eina_List *l;
816    E_Fm_Slave *slave;
817
818    EINA_LIST_FOREACH(_e_fm_ipc_slaves, l, slave)
819      {
820         if (slave->id == id)
821           return slave;
822      }
823
824    return NULL;
825 }
826
827 static int
828 _e_fm_ipc_slave_send(E_Fm_Slave *slave, E_Fm_Op_Type type, void *data, int size)
829 {
830    char *sdata;
831    int ssize;
832    int magic = E_FM_OP_MAGIC;
833    int result;
834
835    ssize = 3 * sizeof(int) + size;
836    sdata = malloc(ssize);
837
838    if (!sdata) return 0;
839
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));
843
844    memcpy(sdata + 2 * sizeof(int) + sizeof(E_Fm_Op_Type), data, size);
845
846    result = ecore_exe_send(slave->exe, sdata, ssize);
847
848    free(sdata);
849
850    return result;
851 }
852
853 Eina_Bool
854 _e_fm_ipc_slave_data_cb(void *data, int type __UNUSED__, void *event)
855 {
856    Ecore_Exe_Event_Data *e = event;
857    E_Fm_Slave *slave;
858    int magic, id, size;
859    char *sdata;
860    int ssize;
861
862    if (!e) return ECORE_CALLBACK_PASS_ON;
863
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;
868
869    sdata = e->data;
870    ssize = e->size;
871
872    while ((unsigned int)ssize >= 3 * sizeof(int))
873      {
874         memcpy(&magic, sdata, sizeof(int));
875         memcpy(&id, sdata + sizeof(int), sizeof(int));
876         memcpy(&size, sdata + sizeof(int) + sizeof(int), sizeof(int));
877
878         if (magic != E_FM_OP_MAGIC)
879           {
880              printf("%s:%s(%d) Wrong magic number from slave #%d. ", __FILE__, __FUNCTION__, __LINE__, slave->id);
881              break;
882           }
883
884         sdata += 3 * sizeof(int);
885         ssize -= 3 * sizeof(int);
886
887         if (id == E_FM_OP_OVERWRITE)
888           {
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);
891           }
892         else if (id == E_FM_OP_ERROR)
893           {
894              _e_fm_ipc_client_send(slave->id, E_FM_OP_ERROR, sdata, size);
895           }
896         else if (id == E_FM_OP_PROGRESS)
897           {
898              _e_fm_ipc_client_send(slave->id, E_FM_OP_PROGRESS, sdata, size);
899           }
900
901         sdata += size;
902         ssize -= size;
903      }
904
905    return ECORE_CALLBACK_PASS_ON;
906 }
907
908 Eina_Bool
909 _e_fm_ipc_slave_error_cb(void *data, int type __UNUSED__, void *event)
910 {
911    Ecore_Exe_Event_Data *e = event;
912    E_Fm_Slave *slave;
913
914    if (!e) return 1;
915
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;
920
921    printf("EFM: Data from STDERR of slave #%d: %.*s", slave->id, e->size, (char *)e->data);
922
923    return 1;
924 }
925
926 Eina_Bool
927 _e_fm_ipc_slave_del_cb(void *data, int type __UNUSED__, void *event)
928 {
929    Ecore_Exe_Event_Del *e = event;
930    E_Fm_Slave *slave;
931    Eina_List *l;
932
933    if (!e) return ECORE_CALLBACK_RENEW;
934
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);
941
942    _e_fm_ipc_slaves = eina_list_remove(_e_fm_ipc_slaves, l);
943    free(slave);
944
945    return ECORE_CALLBACK_RENEW;
946 }
947
948 static void
949 _e_fm_ipc_cb_file_monitor(void *data __UNUSED__, Ecore_File_Monitor *em __UNUSED__, Ecore_File_Event event, const char *path)
950 {
951    E_Dir *ed;
952    char *dir, *rp, *drp;
953    Eina_List *l;
954
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))
959      {
960         rp = ecore_file_realpath(dir);
961         EINA_LIST_FOREACH(_e_dirs, l, ed)
962           {
963              drp = ecore_file_realpath(ed->dir);
964              if (drp)
965                {
966                   if (!strcmp(rp, drp))
967                     _e_fm_ipc_file_add(ed, path, 0);
968                   free(drp);
969                }
970           }
971         free(rp);
972      }
973    else if ((event == ECORE_FILE_EVENT_DELETED_FILE) ||
974             (event == ECORE_FILE_EVENT_DELETED_DIRECTORY))
975      {
976         rp = ecore_file_realpath(dir);
977         EINA_LIST_FOREACH(_e_dirs, l, ed)
978           {
979              drp = ecore_file_realpath(ed->dir);
980              if (drp)
981                {
982                   if (!strcmp(rp, drp))
983                     _e_fm_ipc_file_del(ed, path);
984                   free(drp);
985                }
986           }
987         free(rp);
988      }
989    else if (event == ECORE_FILE_EVENT_MODIFIED)
990      {
991         rp = ecore_file_realpath(dir);
992         EINA_LIST_FOREACH(_e_dirs, l, ed)
993           {
994              drp = ecore_file_realpath(ed->dir);
995              if (drp)
996                {
997                   if (!strcmp(rp, drp))
998                     _e_fm_ipc_file_mod(ed, path);
999                   free(drp);
1000                }
1001           }
1002         free(rp);
1003      }
1004    else if (event == ECORE_FILE_EVENT_DELETED_SELF)
1005      {
1006         rp = ecore_file_realpath(path);
1007         EINA_LIST_FOREACH(_e_dirs, l, ed)
1008           {
1009              drp = ecore_file_realpath(ed->dir);
1010              if (drp)
1011                {
1012                   if (!strcmp(rp, drp))
1013                     _e_fm_ipc_file_mon_dir_del(ed, path);
1014                   free(drp);
1015                }
1016           }
1017         free(rp);
1018      }
1019    free(dir);
1020 }
1021
1022 static Eina_Bool
1023 _e_fm_ipc_cb_recent_clean(void *data)
1024 {
1025    E_Dir *ed;
1026    Eina_List *l, *pl;
1027    E_Mod *m;
1028    double t_now;
1029
1030    ed = data;
1031    ed->cleaning = 1;
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))
1035        {
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);
1039           free(m);
1040        }
1041    ed->cleaning = 0;
1042    if (ed->recent_mods) return ECORE_CALLBACK_RENEW;
1043    ed->recent_clean = NULL;
1044    return ECORE_CALLBACK_CANCEL;
1045 }
1046
1047 static void
1048 _e_fm_ipc_file_add_mod(E_Dir *ed, const char *path, E_Fm_Op_Type op, int listing)
1049 {
1050    struct stat st;
1051    char *lnk = NULL, *rlnk = NULL;
1052    int broken_lnk = 0;
1053    int bsz = 0;
1054    unsigned char *p, buf
1055    /* file add/change format is as follows:
1056     *
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];
1059
1060    /* FIXME: handle BACKOFF */
1061    if ((!listing) && (op == E_FM_OP_FILE_CHANGE) && (!ed->cleaning)) /* 5 == mod */
1062      {
1063         Eina_List *l;
1064         E_Mod *m;
1065         double t_now;
1066         int skip = 0;
1067
1068         t_now = ecore_time_unix_get();
1069         EINA_LIST_FOREACH(ed->recent_mods, l, m)
1070           {
1071              if ((m->mod) && (!strcmp(m->path, path)))
1072                {
1073                   if ((t_now - m->timestamp) < DEF_MOD_BACKOFF)
1074                     {
1075                        m->done = 0;
1076                        skip = 1;
1077                     }
1078                }
1079           }
1080         if (!skip)
1081           {
1082              m = calloc(1, sizeof(E_Mod));
1083              m->path = eina_stringshare_add(path);
1084              m->mod = 1;
1085              m->done = 1;
1086              m->timestamp = t_now;
1087              ed->recent_mods = eina_list_append(ed->recent_mods, m);
1088           }
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);
1091         if (skip)
1092           {
1093 //           printf("SKIP MOD %s %3.3f\n", path, t_now);
1094              return;
1095           }
1096      }
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)
1101      {
1102         if ((path[0] == 0) || (lnk)) broken_lnk = 1;
1103         else return;
1104      }
1105    if ((lnk) && (lnk[0] != '/'))
1106      {
1107         rlnk = ecore_file_realpath(path);
1108         if ((rlnk == NULL) || (rlnk[0] == 0) ||
1109             (stat(rlnk, &st) == -1))
1110           broken_lnk = 1;
1111         else
1112           broken_lnk = 0;
1113      }
1114    else if (lnk)
1115      rlnk = strdup(lnk);
1116    if (!lnk) lnk = strdup("");
1117    if (!rlnk) rlnk = strdup("");
1118
1119    p = buf;
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
1123     * change */
1124    memcpy(buf, &st, sizeof(struct stat));
1125    p += sizeof(struct stat);
1126
1127    p[0] = broken_lnk;
1128    p += 1;
1129
1130    strcpy((char *)p, path);
1131    p += strlen(path) + 1;
1132
1133    strcpy((char *)p, lnk);
1134    p += strlen(lnk) + 1;
1135
1136    strcpy((char *)p, rlnk);
1137    p += strlen(rlnk) + 1;
1138
1139    bsz = p - buf;
1140    ecore_ipc_server_send(_e_fm_ipc_server, 6 /*E_IPC_DOMAIN_FM*/, op, 0, ed->id,
1141                          listing, buf, bsz);
1142    if (lnk) free(lnk);
1143    if (rlnk) free(rlnk);
1144 }
1145
1146 static void
1147 _e_fm_ipc_file_add(E_Dir *ed, const char *path, int listing)
1148 {
1149    if (!listing)
1150      {
1151         /* FIXME: handle BACKOFF */
1152      }
1153    _e_fm_ipc_file_add_mod(ed, path, E_FM_OP_FILE_ADD, listing); /*file add*/
1154 }
1155
1156 static void
1157 _e_fm_ipc_file_del(E_Dir *ed, const char *path)
1158 {
1159    {
1160       /* FIXME: handle BACKOFF */
1161    }
1162    ecore_ipc_server_send(_e_fm_ipc_server,
1163                          6 /*E_IPC_DOMAIN_FM*/,
1164                          E_FM_OP_FILE_DEL,
1165                          0, ed->id, 0, (void *)path, strlen(path) + 1);
1166 }
1167
1168 static void
1169 _e_fm_ipc_file_mod(E_Dir *ed, const char *path)
1170 {
1171    {
1172       /* FIXME: handle BACKOFF */
1173    }
1174    _e_fm_ipc_file_add_mod(ed, path, E_FM_OP_FILE_CHANGE, 0); /*file change*/
1175 }
1176
1177 static void
1178 _e_fm_ipc_file_mon_dir_del(E_Dir *ed, const char *path)
1179 {
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);
1184 }
1185
1186 static void
1187 _e_fm_ipc_file_mon_list_sync(E_Dir *ed)
1188 {
1189    _e_sync_num++;
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);
1197 }
1198
1199 static Eina_Bool
1200 _e_fm_ipc_cb_file_mon_list_idler(void *data)
1201 {
1202    E_Dir *ed;
1203    int n = 0;
1204    char *file, buf[4096];
1205
1206    ed = data;
1207    /* FIXME: spool off files in idlers and handle sync req's */
1208    while (ed->fq)
1209      {
1210         file = eina_list_data_get(ed->fq);
1211         if (!((ed->dot_order) && (!strcmp(file, ".order"))))
1212           {
1213              if (!strcmp(ed->dir, "/"))
1214                snprintf(buf, sizeof(buf), "/%s", file);
1215              else
1216                snprintf(buf, sizeof(buf), "%s/%s", ed->dir, file);
1217              _e_fm_ipc_file_add(ed, buf, 1);
1218           }
1219         eina_stringshare_del(file);
1220         ed->fq = eina_list_remove_list(ed->fq, ed->fq);
1221         n++;
1222         if (n == ed->sync_num)
1223           {
1224              _e_fm_ipc_file_mon_list_sync(ed);
1225              ed->idler = NULL;
1226              if (!ed->fq) _e_fm_ipc_file_add(ed, "", 2);
1227              return 0;
1228           }
1229      }
1230    ed->sync_num = DEF_SYNC_NUM;
1231    ed->sync = 0;
1232    ed->sync_time = 0.0;
1233    ed->idler = NULL;
1234    if (!ed->fq) _e_fm_ipc_file_add(ed, "", 2);
1235    return ECORE_CALLBACK_CANCEL;
1236 }
1237
1238 static Eina_Bool
1239 _e_fm_ipc_cb_fop_trash_idler(void *data)
1240 {
1241    E_Fop *fop = NULL;
1242    FILE *info = NULL;
1243    const char *trash_dir = NULL;
1244    const char *filename = NULL;
1245    const char *escname = NULL;
1246    const char *dest = NULL;
1247    char buf[4096];
1248    unsigned int i = 0;
1249    struct tm *lt;
1250    time_t t;
1251
1252    /* FIXME: For now, this will only implement 'home trash'
1253     * Later, implement mount/remote/removable device trash, if wanted. */
1254
1255    fop = (E_Fop *)data;
1256    if (!fop) return 0;
1257
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;
1265
1266    filename = eina_stringshare_add(strrchr(fop->src, '/'));
1267    escname = ecore_file_escape_name(filename);
1268    eina_stringshare_del(filename);
1269
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 */
1273    do
1274      {
1275         snprintf(buf, sizeof(buf), "%s/file%s.%p.%u", trash_dir, escname,
1276                  fop, i++);
1277      }
1278    while (ecore_file_exists(buf));
1279    dest = eina_stringshare_add(buf);
1280
1281    /* Try to move the file */
1282    if (rename(fop->src, dest))
1283      {
1284         if (errno == EXDEV)
1285           {
1286              /* Move failed. Spec says delete files that can't be trashed */
1287              ecore_file_unlink(fop->src);
1288              return ECORE_CALLBACK_CANCEL;
1289           }
1290      }
1291
1292    /* Move worked. Create info file */
1293    snprintf(buf, sizeof(buf), "%s/info%s.%p.%u.trashinfo", trash_dir,
1294             escname, fop, --i);
1295    info = fopen(buf, "w");
1296    if (info)
1297      {
1298         t = time(NULL);
1299         lt = localtime(&t);
1300
1301         /* Insert info for trashed file */
1302         fprintf(info,
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);
1306         fclose(info);
1307      }
1308    else
1309      /* Could not create info file. Spec says to put orig file back */
1310      rename(dest, fop->src);
1311
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);
1317    free(fop);
1318    return ECORE_CALLBACK_CANCEL;
1319 }
1320
1321 static char *
1322 _e_str_list_remove(Eina_List **list, const char *str, int len)
1323 {
1324    Eina_List *l;
1325    char *s;
1326
1327    EINA_LIST_FOREACH(*list, l, s)
1328      if (!strncmp(s, str, len))
1329        {
1330           *list = eina_list_remove_list(*list, l);
1331           return s;
1332        }
1333
1334    return NULL;
1335 }
1336
1337 static void
1338 _e_fm_ipc_reorder(const char *file, const char *dst, const char *relative, int after)
1339 {
1340    char buffer[PATH_MAX];
1341    char order[PATH_MAX];
1342
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);
1346
1347    snprintf(order, sizeof(order), "%s/.order", dst);
1348    if (ecore_file_exists(order))
1349      {
1350         FILE *forder;
1351         Eina_List *files = NULL, *l;
1352         char *str;
1353
1354         forder = fopen(order, "r");
1355         if (forder)
1356           {
1357              int len;
1358
1359              /* inset files in order if the existed in file
1360               * list before */
1361              while (fgets(buffer, sizeof(buffer), forder))
1362                {
1363                   len = strlen(buffer);
1364                   if (len > 0) buffer[len - 1] = 0;
1365                   files = eina_list_append(files, strdup(buffer));
1366                }
1367              fclose(forder);
1368           }
1369         /* remove dest file from .order - if there */
1370         EINA_LIST_FOREACH(files, l, str)
1371           if (!strcmp(str, file))
1372             {
1373                free(str);
1374                files = eina_list_remove_list(files, l);
1375                break;
1376             }
1377         /* now insert dest into list or replace entry */
1378         EINA_LIST_FOREACH(files, l, str)
1379           {
1380              if (!strcmp(str, relative))
1381                {
1382                   if (after == 2) /* replace */
1383                     {
1384                        free(str);
1385                        l->data = strdup(file);
1386                     }
1387                   else if (after == 0) /* before */
1388                     {
1389                        files = eina_list_prepend_relative_list(files, strdup(file), l);
1390                     }
1391                   else if (after == 1) /* after */
1392                     {
1393                        files = eina_list_append_relative_list(files, strdup(file), l);
1394                     }
1395                   break;
1396                }
1397           }
1398
1399         forder = fopen(order, "w");
1400         if (forder)
1401           {
1402              EINA_LIST_FREE(files, str)
1403                {
1404                   fprintf(forder, "%s\n", str);
1405                   free(str);
1406                }
1407              fclose(forder);
1408           }
1409      }
1410 }
1411
1412 static void
1413 _e_fm_ipc_dir_del(E_Dir *ed)
1414 {
1415    void *data;
1416    E_Mod *m;
1417
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)
1423      {
1424         eina_stringshare_del(m->path);
1425         free(m);
1426      }
1427    EINA_LIST_FREE(ed->fq, data)
1428      eina_stringshare_del(data);
1429    free(ed);
1430 }
1431
1432 static const char *
1433 _e_fm_ipc_prepare_command(E_Fm_Op_Type type, const char *args)
1434 {
1435    char *buffer;
1436    const char *libdir;
1437    unsigned int length = 0;
1438    char command[4];
1439
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");
1450    else
1451      return NULL;
1452
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);
1459
1460    return buffer;
1461 }