f1163d8171b3533eda88cb95e101e9ff770c6c59
[framework/uifw/e17.git] / src / bin / e_fm_op.c
1 #include "config.h"
2
3 #ifndef _FILE_OFFSET_BITS
4 # define _FILE_OFFSET_BITS  64
5 #endif
6
7 #ifdef HAVE_ALLOCA_H
8 # include <alloca.h>
9 #elif defined __GNUC__
10 # define alloca __builtin_alloca
11 #elif defined _AIX
12 # define alloca __alloca
13 #elif defined _MSC_VER
14 # include <malloc.h>
15 # define alloca _alloca
16 #else
17 # include <stddef.h>
18 # ifdef  __cplusplus
19 extern "C"
20 # endif
21 void *alloca (size_t);
22 #endif
23
24 #include <unistd.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <dirent.h>
32 #include <utime.h>
33 #include <errno.h>
34 #include <limits.h>
35
36 #include <Ecore.h>
37 #include <Ecore_File.h>
38
39 #include <eina_stringshare.h>
40
41 #define E_TYPEDEFS
42 #include "e_fm_op.h"
43 #undef E_TYPEDEFS
44 #include "e_fm_op.h"
45
46 #define READBUFSIZE 65536
47 #define COPYBUFSIZE 16384
48 #define REMOVECHUNKSIZE 4096
49
50 #define FREE(p) do { if (p) {free((void *)p); p = NULL;} } while (0)
51
52 typedef struct _E_Fm_Op_Task E_Fm_Op_Task;
53 typedef struct _E_Fm_Op_Copy_Data E_Fm_Op_Copy_Data;
54
55 static E_Fm_Op_Task *_e_fm_op_task_new();
56 static void _e_fm_op_task_free(void *t);
57
58 static void _e_fm_op_remove_link_task(E_Fm_Op_Task *task);
59 static Eina_Bool _e_fm_op_stdin_data(void *data, Ecore_Fd_Handler * fd_handler);
60 static void _e_fm_op_set_up_idlers();
61 static void _e_fm_op_delete_idler(int *mark);
62 static int _e_fm_op_idler_handle_error(int *mark, Eina_List **queue, Eina_List **node, E_Fm_Op_Task *task);
63
64 static Eina_Bool _e_fm_op_work_idler(void *data);
65 static Eina_Bool _e_fm_op_scan_idler(void *data);
66
67 static void _e_fm_op_send_error(E_Fm_Op_Task * task, E_Fm_Op_Type type, const char *fmt, ...);
68 static void _e_fm_op_rollback(E_Fm_Op_Task * task);
69 static void _e_fm_op_update_progress_report_simple(int percent, const char *src, const char *dst);
70 static void  _e_fm_op_update_progress(E_Fm_Op_Task *task, off_t _plus_e_fm_op_done, off_t _plus_e_fm_op_total);
71 static void _e_fm_op_copy_stat_info(E_Fm_Op_Task *task);
72 static int _e_fm_op_handle_overwrite(E_Fm_Op_Task *task);
73
74 static int _e_fm_op_copy_dir(E_Fm_Op_Task * task);
75 static int _e_fm_op_copy_link(E_Fm_Op_Task *task);
76 static int _e_fm_op_copy_fifo(E_Fm_Op_Task *task);
77 static int _e_fm_op_open_files(E_Fm_Op_Task *task);
78 static int _e_fm_op_copy_chunk(E_Fm_Op_Task *task);
79
80 static int _e_fm_op_copy_atom(E_Fm_Op_Task * task);
81 static int _e_fm_op_scan_atom(E_Fm_Op_Task * task);
82 static int _e_fm_op_copy_stat_info_atom(E_Fm_Op_Task * task);
83 static int _e_fm_op_symlink_atom(E_Fm_Op_Task * task);
84 static int _e_fm_op_remove_atom(E_Fm_Op_Task * task);
85
86 Ecore_Fd_Handler *_e_fm_op_stdin_handler = NULL;
87
88 Eina_List *_e_fm_op_work_queue = NULL, *_e_fm_op_scan_queue = NULL;
89 Ecore_Idler *_e_fm_op_work_idler_p = NULL, *_e_fm_op_scan_idler_p = NULL;
90
91 off_t _e_fm_op_done, _e_fm_op_total; /* Type long long should be 64 bits wide everywhere,
92                                             this means that it's max value is 2^63 - 1, which 
93                                             is 8 388 608 terabytes, and this should be enough.
94                                             Well, we'll be multipling _e_fm_op_done by 100, but 
95                                             still, it is big enough. */
96
97 int _e_fm_op_abort = 0; /* Abort mark. */
98 int _e_fm_op_scan_error = 0;
99 int _e_fm_op_work_error = 0;
100 int _e_fm_op_overwrite = 0;
101
102 int _e_fm_op_error_response = E_FM_OP_NONE;
103 int _e_fm_op_overwrite_response = E_FM_OP_NONE;
104
105 Eina_List *_e_fm_op_separator = NULL;
106
107 char *_e_fm_op_stdin_buffer = NULL;
108
109 struct _E_Fm_Op_Task
110 {
111    struct
112    {
113       const char *name;
114       struct stat st;
115    } src;
116
117    struct
118    {
119       const char *name;
120       size_t done;
121    } dst;
122
123    int started, finished;
124
125    void *data;
126
127    E_Fm_Op_Type type;
128    E_Fm_Op_Type overwrite;
129
130    Eina_List *link;
131 };
132
133 struct _E_Fm_Op_Copy_Data
134 {
135    FILE *from;
136    FILE *to;
137 };
138
139 int
140 main(int argc, char **argv)
141 {
142    int i, last;
143    E_Fm_Op_Type type;
144
145    ecore_init();
146    eina_init();
147
148    _e_fm_op_stdin_buffer = malloc(READBUFSIZE);
149
150    _e_fm_op_stdin_handler =
151      ecore_main_fd_handler_add(STDIN_FILENO, ECORE_FD_READ, _e_fm_op_stdin_data, NULL,
152                                NULL, NULL);
153
154    if (argc <= 2) return 0;
155
156    last = argc - 1;
157    i = 2;
158
159    if (strcmp(argv[1], "cp") == 0)
160      type = E_FM_OP_COPY;
161    else if (strcmp(argv[1], "mv") == 0)
162      type = E_FM_OP_MOVE;
163    else if (strcmp(argv[1], "rm") == 0)
164      type = E_FM_OP_REMOVE;
165    else if (strcmp(argv[1], "lns") == 0)
166      type = E_FM_OP_SYMLINK;
167    else return 0;
168
169    if ((type == E_FM_OP_COPY) || (type == E_FM_OP_MOVE) || (type == E_FM_OP_SYMLINK))
170      {
171         if (argc < 4) goto quit;
172
173         if (type == E_FM_OP_MOVE)
174           {
175              _e_fm_op_work_queue = eina_list_append(_e_fm_op_work_queue, NULL);
176              _e_fm_op_separator = _e_fm_op_work_queue;
177           }
178
179         if ((argc >= 4) && (ecore_file_is_dir(argv[last])))
180           {
181              char buf[PATH_MAX];
182              char *p2, *p3;
183              int p2_len, last_len, done, total;
184
185              p2 = ecore_file_realpath(argv[last]);
186              if (!p2) goto quit;
187              p2_len = strlen(p2);
188
189              last_len = strlen(argv[last]);
190              if ((last_len < 1) || (last_len + 2 >= PATH_MAX))
191                {
192                   free(p2);
193                   goto quit;
194                }
195              memcpy(buf, argv[last], last_len);
196              if (buf[last_len - 1] != '/')
197                {
198                   buf[last_len] = '/';
199                   last_len++;
200                }
201
202              p3 = buf + last_len;
203
204              done = 0;
205              total = last - 2;
206
207              for (; i < last; i++)
208                {
209                   char *p = ecore_file_realpath(argv[i]);
210                   const char *name;
211                   int name_len;
212
213                   if (!p) continue;
214
215                   /* Don't move a dir into itself */
216                   if (ecore_file_is_dir(p) &&
217                       (strncmp(p, p2, PATH_MAX) == 0) &&
218                       ((p[p2_len] == '/') || (p[p2_len] == '\0')))
219                     goto skip_arg;
220
221                   name = ecore_file_file_get(p);
222                   if (!name) goto skip_arg;
223                   name_len = strlen(name);
224                   if (p2_len + name_len >= PATH_MAX) goto skip_arg;
225                   memcpy(p3, name, name_len + 1);
226
227                   if ((type == E_FM_OP_MOVE) &&
228                       (rename(argv[i], buf) == 0))
229                     {
230                        done++;
231                        _e_fm_op_update_progress_report_simple
232                          (done * 100 / total, argv[i], buf);
233                     }
234                   else if ((type == E_FM_OP_SYMLINK) &&
235                            (symlink(argv[i], buf) == 0))
236                     {
237                        done++;
238                        _e_fm_op_update_progress_report_simple
239                          (done * 100 / total, argv[i], buf);
240                     }
241                   else
242                     {
243                        E_Fm_Op_Task *task;
244
245                        task = _e_fm_op_task_new();
246                        task->type = type;
247                        task->src.name = eina_stringshare_add(argv[i]);
248                        task->dst.name = eina_stringshare_add(buf);
249
250                        _e_fm_op_scan_queue =
251                          eina_list_append(_e_fm_op_scan_queue, task);
252                     }
253
254                skip_arg:
255                   free(p);
256                }
257
258              free(p2);
259           }
260         else if (argc == 4)
261           {
262              char *p, *p2;
263
264              p = ecore_file_realpath(argv[2]);
265              p2 = ecore_file_realpath(argv[3]);
266
267              /* Don't move a file on top of itself. */
268              i = (strcmp(p, p2) == 0);
269              free(p);
270              free(p2);
271              if (i) goto quit;
272
273              /* Try a rename */
274              if ((type == E_FM_OP_MOVE) && (rename(argv[2], argv[3]) == 0))
275                {
276                   _e_fm_op_update_progress_report_simple(100, argv[2], argv[3]);
277                   goto quit;
278                }
279              else if ((type == E_FM_OP_SYMLINK) &&
280                       (symlink(argv[2], argv[3]) == 0))
281                {
282                   _e_fm_op_update_progress_report_simple(100, argv[2], argv[3]);
283                   goto quit;
284                }
285              else
286                {
287                   E_Fm_Op_Task *task;
288
289                   /* If that doesn't work, setup a copy and delete operation.
290                      It's not atomic, but it's the best we can do. */
291                   task = _e_fm_op_task_new();
292                   task->type = type;
293                   task->src.name = eina_stringshare_add(argv[2]);
294                   task->dst.name = eina_stringshare_add(argv[3]);
295                   _e_fm_op_scan_queue = eina_list_append(_e_fm_op_scan_queue, task);
296                }
297           }
298         else
299           goto quit;
300      }
301    else if (type == E_FM_OP_REMOVE)
302      {
303         if (argc < 3) return 0;
304
305         while (i <= last)
306           {
307              E_Fm_Op_Task *task;
308
309              task = _e_fm_op_task_new();
310              task->type = type;
311              task->src.name = eina_stringshare_add(argv[i]);
312              _e_fm_op_scan_queue = eina_list_append(_e_fm_op_scan_queue, task);
313              i++;
314           }
315      }
316
317    _e_fm_op_set_up_idlers();
318
319    ecore_main_loop_begin();
320
321 quit:
322    eina_shutdown();
323    ecore_shutdown();
324
325    free(_e_fm_op_stdin_buffer);
326
327    E_FM_OP_DEBUG("Slave quit.\n");
328
329    return 0;
330 }
331
332 /* Create new task. */
333 static E_Fm_Op_Task *
334 _e_fm_op_task_new()
335 {
336    E_Fm_Op_Task *t;
337
338    t = malloc(sizeof(E_Fm_Op_Task));
339    t->src.name = NULL;
340    memset(&(t->src.st), 0, sizeof(struct stat));
341
342    t->dst.name = NULL;
343    t->dst.done = 0;
344    t->started = 0;
345    t->finished = 0;
346    t->data = NULL;
347    t->type = E_FM_OP_NONE;
348    t->overwrite = E_FM_OP_NONE;
349    t->link = NULL;
350
351    return t;
352 }
353
354 /* Free task. */
355 static void
356 _e_fm_op_task_free(void *t)
357 {
358    E_Fm_Op_Task *task = t;
359    E_Fm_Op_Copy_Data *data;
360
361    if (!task) return;
362
363    if (task->src.name) eina_stringshare_del(task->src.name);
364    if (task->dst.name) eina_stringshare_del(task->dst.name);
365
366    if (task->data)
367      {
368         data = task->data;
369         if (task->type == E_FM_OP_COPY)
370           {
371              if (data->from) fclose(data->from);
372              if (data->to) fclose(data->to);
373          }
374         FREE(task->data);
375      }
376    FREE(task);
377 }
378
379 /* Removes link task from work queue.
380  * Link task is not NULL in case of MOVE. Then two tasks are created: copy and remove.
381  * Remove task is a link task for the copy task. If copy task is aborted (e.g. error 
382  * occurred and user chooses to ignore this), then the remove task is removed from
383  * queue with this functions.
384  */
385 static void 
386 _e_fm_op_remove_link_task(E_Fm_Op_Task *task)
387 {
388    E_Fm_Op_Task *ltask;
389    
390    if (task->link)
391      {
392         ltask = eina_list_data_get(task->link);
393         _e_fm_op_work_queue = 
394           eina_list_remove_list(_e_fm_op_work_queue, task->link);
395         _e_fm_op_task_free(ltask);
396         task->link = NULL;
397      }
398 }
399
400 /*
401  * Handles data from STDIN.
402  * Received data must be in this format:
403  * 1) (int) magic number,
404  * 2) (int) id,
405  * 3) (int) message length.
406  * Right now message length is always 0. Id is what matters.
407  *
408  * This function uses a couple of static variables and a global 
409  * variable _e_fm_op_stdin_buffer to deal with a situation, when read() 
410  * did not actually read enough data.
411  */
412 static Eina_Bool
413 _e_fm_op_stdin_data(void *data __UNUSED__, Ecore_Fd_Handler * fd_handler)
414 {
415    int fd;
416    static char *buf = NULL;
417    static int length = 0;
418    char *begin = NULL;
419    ssize_t num = 0;
420    int msize, identity;
421
422    fd = ecore_main_fd_handler_fd_get(fd_handler);
423    if (!buf) 
424      {
425         buf = _e_fm_op_stdin_buffer;
426         length = 0;
427      }
428
429    num = read(fd, buf, READBUFSIZE - length);
430
431    if (num == 0)
432      {
433         E_FM_OP_DEBUG("STDIN was closed. Abort. \n");
434         _e_fm_op_abort = 1;
435      }
436    else if (num < 0)
437      {
438         E_FM_OP_DEBUG("Error while reading from STDIN: read returned -1. (%s) Abort. \n", strerror(errno));
439         _e_fm_op_abort = 1;
440      }
441    else
442      {
443         length += num;
444
445         buf = _e_fm_op_stdin_buffer;
446         begin = _e_fm_op_stdin_buffer;
447
448         while (length >= ((int)(3 * sizeof(int))))
449           {
450              begin = buf;
451
452              /* Check magic. */
453              if (*((int *)buf) != E_FM_OP_MAGIC)
454                {
455                   E_FM_OP_DEBUG("Error while reading from STDIN: magic is not correct!\n");
456                   break;
457                }
458              buf += sizeof(int);
459
460              /* Read indentifying data. */
461              memcpy(&identity, buf, sizeof(int));
462              buf += sizeof(int);
463
464              /* Read message length. */
465              memcpy(&msize, buf, sizeof(int));
466              buf += sizeof(int);
467
468              if ((length - 3 * (int)sizeof(int)) < msize)
469                {
470                   /* There is not enough data to read the whole message. */
471                   break;
472                }
473
474              length -= (3 * sizeof(int));
475
476              /* You may want to read msize bytes of data too,
477               * but currently commands here do not have any data.
478               * msize is always 0.
479               */
480              switch (identity)
481                {
482                case E_FM_OP_ABORT:
483                   _e_fm_op_abort = 1;
484                   E_FM_OP_DEBUG("Aborted.\n");
485                   break;
486                case E_FM_OP_ERROR_RESPONSE_ABORT:
487                case E_FM_OP_ERROR_RESPONSE_IGNORE_THIS:
488                case E_FM_OP_ERROR_RESPONSE_IGNORE_ALL:
489                case E_FM_OP_ERROR_RESPONSE_RETRY:
490                   _e_fm_op_error_response = identity;
491                   _e_fm_op_set_up_idlers();
492                   break;
493                case E_FM_OP_OVERWRITE_RESPONSE_NO:
494                case E_FM_OP_OVERWRITE_RESPONSE_NO_ALL:
495                case E_FM_OP_OVERWRITE_RESPONSE_YES:
496                case E_FM_OP_OVERWRITE_RESPONSE_YES_ALL:
497                   _e_fm_op_overwrite_response = identity;
498                   _e_fm_op_set_up_idlers();
499                   E_FM_OP_DEBUG("Overwrite response set.\n");
500                   break;
501                }
502           }
503         if (length > 0) memmove(_e_fm_op_stdin_buffer, begin, length);
504         buf = _e_fm_op_stdin_buffer + length;
505      }
506
507    return ECORE_CALLBACK_RENEW;
508 }
509
510 static void 
511 _e_fm_op_set_up_idlers()
512 {
513    if (!_e_fm_op_scan_idler_p)
514      _e_fm_op_scan_idler_p = ecore_idler_add(_e_fm_op_scan_idler, NULL);
515    if (!_e_fm_op_work_idler_p)
516      _e_fm_op_work_idler_p = ecore_idler_add(_e_fm_op_work_idler, NULL);
517 }
518
519 #define _E_FM_OP_ERROR_SEND_SCAN(_task, _e_fm_op_error_type, _fmt, ...)\
520    do\
521      {\
522         int _errno = errno;\
523         _e_fm_op_scan_error = 1;\
524         _e_fm_op_send_error(_task, _e_fm_op_error_type, _fmt, __VA_ARGS__, strerror(_errno));\
525         return 1;\
526      }\
527    while (0)
528
529 #define _E_FM_OP_ERROR_SEND_WORK(_task, _e_fm_op_error_type, _fmt, ...)\
530     do\
531       {\
532          int _errno = errno;\
533          _e_fm_op_work_error = 1;\
534          _e_fm_op_send_error(_task, _e_fm_op_error_type, _fmt, __VA_ARGS__, strerror(_errno));\
535          return 1;\
536       }\
537     while (0)
538
539 static void 
540 _e_fm_op_delete_idler(int *mark)
541 {
542    if (mark == &_e_fm_op_work_error)
543      {
544         ecore_idler_del(_e_fm_op_work_idler_p);
545         _e_fm_op_work_idler_p = NULL;
546      }
547    else
548      {
549         ecore_idler_del(_e_fm_op_scan_idler_p);
550         _e_fm_op_scan_idler_p = NULL;
551      }
552 }
553
554 /* Code to deal with overwrites and errors in idlers.
555  * Basically, it checks if we got a response. 
556  * Returns 1 if we did; otherwise checks it and does what needs to be done.
557  */
558 static int 
559 _e_fm_op_idler_handle_error(int *mark, Eina_List **queue, Eina_List **node, E_Fm_Op_Task *task)
560 {
561    if (_e_fm_op_overwrite)
562      {
563         if (_e_fm_op_overwrite_response != E_FM_OP_NONE)
564           {
565              task->overwrite = _e_fm_op_overwrite_response;
566              _e_fm_op_work_error = 0;
567              _e_fm_op_scan_error = 0;
568           }
569         else
570           {
571              /* No response yet. */
572              /* So, delete this idler. It'll be added back when response is there. */
573              _e_fm_op_delete_idler(mark);
574              return 1;
575           }
576      }
577    else if (*mark)
578      {
579         if (_e_fm_op_error_response == E_FM_OP_NONE)
580           { 
581              /* No response yet. */
582              /* So, delete this idler. It'll be added back when response is there. */
583              _e_fm_op_delete_idler(mark);
584              return 1;
585           }
586         else
587           {
588              E_FM_OP_DEBUG("Got response.\n");
589              /* Got response. */ 
590              if (_e_fm_op_error_response == E_FM_OP_ERROR_RESPONSE_ABORT)
591                {
592                   /* Mark as abort. */
593                   _e_fm_op_abort = 1;
594                   _e_fm_op_error_response = E_FM_OP_NONE;
595                   _e_fm_op_rollback(task);
596                }
597              else if (_e_fm_op_error_response == E_FM_OP_ERROR_RESPONSE_RETRY)
598                {
599                   *mark = 0;
600                   _e_fm_op_error_response = E_FM_OP_NONE;
601                }
602              else if (_e_fm_op_error_response == E_FM_OP_ERROR_RESPONSE_IGNORE_THIS)
603                {
604                   _e_fm_op_rollback(task);
605                   _e_fm_op_remove_link_task(task);
606                   *queue = eina_list_remove_list(*queue, *node);
607                   _e_fm_op_error_response = E_FM_OP_NONE;
608                   *mark = 0;
609                   *node = NULL;
610                   return 1;
611                }
612              else if (_e_fm_op_error_response == E_FM_OP_ERROR_RESPONSE_IGNORE_ALL)
613                {
614                   E_FM_OP_DEBUG("E_Fm_Op_Task '%s' --> '%s' was automatically aborted.\n",
615                                 task->src.name, task->dst.name);
616                   _e_fm_op_rollback(task);
617                   _e_fm_op_remove_link_task(task);
618                   *queue = eina_list_remove_list(*queue, *node);
619                   *node = NULL;
620                   *mark = 0;
621                   /* Do not clean out _e_fm_op_error_response. This way when another error occures, it would be handled automatically. */ 
622                   return 1; 
623                }
624           }
625      }
626    else if (( _e_fm_op_work_error) || (_e_fm_op_scan_error))
627      return 1;
628    return 0;
629
630
631 /* This works very simple. Take a task from queue and run appropriate _atom() on it.
632  * If after _atom() is done, task->finished is 1 remove the task from queue. Otherwise,
633  * run _atom() on the same task on next call.
634  *
635  * If we have an abort (_e_fm_op_abort = 1), then _atom() should recognize it and do smth. 
636  * After this, just finish everything.
637  */
638 static Eina_Bool
639 _e_fm_op_work_idler(void *data __UNUSED__)
640 {
641    /* E_Fm_Op_Task is marked static here because _e_fm_op_work_queue can be populated with another
642     * tasks between calls. So it is possible when a part of file is copied and then 
643     * another task is pushed into _e_fm_op_work_queue and the new one if performed while 
644     * the first one is not finished yet. This is not cool, so we make sure one task 
645     * is performed until it is finished. Well, this can be an issue with removing 
646     * directories. For example, if we are trying to remove a non-empty directory, 
647     * then this will go into infinite loop. But this should never happen. 
648     *
649     * BTW, the same is propably right for the _e_fm_op_scan_idler().
650     */
651    static Eina_List *node = NULL;
652    E_Fm_Op_Task *task = NULL;
653
654    if (!node) node = _e_fm_op_work_queue;
655    task = eina_list_data_get(node);
656    if (!task) 
657      {
658         node = _e_fm_op_work_queue;
659         task = eina_list_data_get(node);
660      }
661
662    if (!task)
663      {
664         if ((_e_fm_op_separator) && 
665             (_e_fm_op_work_queue == _e_fm_op_separator) && 
666             (!_e_fm_op_scan_idler_p))
667           {
668              /* You may want to look at the comment in _e_fm_op_scan_atom() about this separator thing. */
669              _e_fm_op_work_queue = eina_list_remove_list(_e_fm_op_work_queue, _e_fm_op_separator);
670              node = NULL;
671              return ECORE_CALLBACK_RENEW;
672           }
673
674         if ((!_e_fm_op_scan_idler_p) && (!_e_fm_op_work_error) && 
675             (!_e_fm_op_scan_error))
676           ecore_main_loop_quit();
677
678         return ECORE_CALLBACK_RENEW;
679      }
680
681    if (_e_fm_op_idler_handle_error(&_e_fm_op_work_error, &_e_fm_op_work_queue, &node, task)) 
682      return ECORE_CALLBACK_RENEW;
683
684    task->started = 1;
685
686    if (task->type == E_FM_OP_COPY)
687      _e_fm_op_copy_atom(task);
688    else if (task->type == E_FM_OP_REMOVE)
689      _e_fm_op_remove_atom(task);
690    else if (task->type == E_FM_OP_COPY_STAT_INFO)
691      _e_fm_op_copy_stat_info_atom(task);
692    else if (task->type == E_FM_OP_SYMLINK)
693      _e_fm_op_symlink_atom(task);
694
695    if (task->finished)
696      {
697        _e_fm_op_work_queue = eina_list_remove_list(_e_fm_op_work_queue, node);
698        _e_fm_op_task_free(task);
699        node = NULL;
700      }
701
702    if (_e_fm_op_abort)
703      {
704         /* So, _atom did what it whats in case of abort. Now to idler. */
705         ecore_main_loop_quit();
706         return ECORE_CALLBACK_CANCEL;
707      }
708
709    return ECORE_CALLBACK_RENEW;
710 }
711
712 /* This works pretty much the same as _e_fm_op_work_idler(), except that 
713  * if this is a dir, then look into its contents and create a task 
714  * for those files. And we don't have _e_fm_op_separator here.
715  */
716 Eina_Bool
717 _e_fm_op_scan_idler(void *data __UNUSED__)
718 {
719    static Eina_List *node = NULL;
720    E_Fm_Op_Task *task = NULL;
721    char buf[PATH_MAX];
722    static struct dirent *de = NULL;
723    static DIR *dir = NULL;
724    E_Fm_Op_Task *ntask = NULL;
725
726    if (!node) node = _e_fm_op_scan_queue;
727    task = eina_list_data_get(node);
728    if (!task) 
729      {
730         node = _e_fm_op_scan_queue;
731         task = eina_list_data_get(node);
732      }
733
734    if (!task)
735      {
736         _e_fm_op_scan_idler_p = NULL;
737         return ECORE_CALLBACK_CANCEL;
738      }
739
740    if (_e_fm_op_idler_handle_error(&_e_fm_op_scan_error, &_e_fm_op_scan_queue, &node, task)) 
741      return ECORE_CALLBACK_RENEW;
742
743    if (_e_fm_op_abort)
744      {
745         /* We're marked for abortion. */
746         ecore_main_loop_quit();
747         return ECORE_CALLBACK_CANCEL;
748      }
749
750    if (task->type == E_FM_OP_COPY_STAT_INFO)
751      {
752         _e_fm_op_scan_atom(task);
753         if (task->finished)
754           {
755              _e_fm_op_scan_queue = 
756                eina_list_remove_list(_e_fm_op_scan_queue, node);
757              _e_fm_op_task_free(task);
758              node = NULL;
759           }
760      }
761    else if (!dir && !task->started)
762      {
763         if (lstat(task->src.name, &(task->src.st)) < 0)
764           _E_FM_OP_ERROR_SEND_SCAN(task, E_FM_OP_ERROR, 
765                                    "Cannot lstat '%s': %s.", task->src.name);
766
767         if (S_ISDIR(task->src.st.st_mode))
768           {
769              /* If it's a dir, then look through it and add a task for each. */
770
771              dir = opendir(task->src.name);
772              if (!dir)
773                _E_FM_OP_ERROR_SEND_SCAN(task, E_FM_OP_ERROR, 
774                                         "Cannot open directory '%s': %s.", 
775                                         task->dst.name);
776           }
777         else
778           task->started = 1;
779      }
780    else if (dir && !task->started)
781      {
782         de = readdir(dir);
783
784         if (!de)
785           {
786              ntask = _e_fm_op_task_new();
787              ntask->type = E_FM_OP_COPY_STAT_INFO;
788              ntask->src.name = eina_stringshare_add(task->src.name);
789              memcpy(&(ntask->src.st), &(task->src.st), sizeof(struct stat));
790
791              if (task->dst.name)
792                ntask->dst.name = eina_stringshare_add(task->dst.name);
793              else
794                ntask->dst.name = NULL;
795
796              if (task->type == E_FM_OP_REMOVE)
797                _e_fm_op_scan_queue = 
798                eina_list_prepend(_e_fm_op_scan_queue, ntask);
799              else
800                _e_fm_op_scan_queue = 
801                eina_list_append(_e_fm_op_scan_queue, ntask);
802
803              task->started = 1;
804              closedir(dir);
805              dir = NULL;
806              node = NULL;
807              return ECORE_CALLBACK_RENEW;
808           }
809
810         if ((!strcmp(de->d_name, ".") || (!strcmp(de->d_name, ".."))))
811           return ECORE_CALLBACK_RENEW;
812
813         ntask = _e_fm_op_task_new();
814         ntask->type = task->type;
815         snprintf(buf, sizeof(buf), "%s/%s", task->src.name, de->d_name);
816         ntask->src.name = eina_stringshare_add(buf);
817
818         if (task->dst.name)
819           {
820              snprintf(buf, sizeof(buf), "%s/%s", task->dst.name, de->d_name);
821              ntask->dst.name = eina_stringshare_add(buf);
822           }
823         else
824           ntask->dst.name = NULL;
825
826         if (task->type == E_FM_OP_REMOVE)
827           _e_fm_op_scan_queue = eina_list_prepend(_e_fm_op_scan_queue, ntask);
828         else
829           _e_fm_op_scan_queue = eina_list_append(_e_fm_op_scan_queue, ntask);
830      }
831    else
832      {
833         _e_fm_op_scan_atom(task);
834         if (task->finished)
835           {
836              _e_fm_op_scan_queue = 
837                eina_list_remove_list(_e_fm_op_scan_queue, node);
838              _e_fm_op_task_free(task);
839              node = NULL;
840           }
841      }
842
843    return ECORE_CALLBACK_RENEW;
844 }
845
846 /* Packs and sends an error to STDOUT.
847  * type is either E_FM_OP_ERROR or E_FM_OP_OVERWRITE.
848  * fmt is a printf format string, the other arguments 
849  * are for this format string,
850  */
851 static void
852 _e_fm_op_send_error(E_Fm_Op_Task *task __UNUSED__, E_Fm_Op_Type type, const char *fmt, ...)
853 {
854    va_list ap;
855    char buffer[READBUFSIZE];
856    char *buf = &buffer[0];
857    char *str = buf + (3 * sizeof(int));
858    int len = 0;
859
860    va_start(ap, fmt);
861
862    if (_e_fm_op_error_response == E_FM_OP_ERROR_RESPONSE_IGNORE_ALL)
863      {
864         /* Do nothing. */
865      }
866    else
867      {
868         int ret = 0;
869
870         vsnprintf(str, READBUFSIZE - 3 * sizeof(int), fmt, ap);
871         len = strlen(str);
872
873         *((int *)buf) = E_FM_OP_MAGIC;
874         *((int *)(buf + sizeof(int))) = type;
875         *((int *)(buf + (2 * sizeof(int)))) = len + 1;
876
877         ret = write(STDOUT_FILENO, buf, (3 * sizeof(int)) + len + 1);
878
879         E_FM_OP_DEBUG("%s", str);
880         E_FM_OP_DEBUG(" Error sent.\n");
881      }
882
883    va_end(ap);
884 }
885
886 /* Unrolls task: makes a clean up and updates progress info. */
887 static void
888 _e_fm_op_rollback(E_Fm_Op_Task *task)
889 {
890    E_Fm_Op_Copy_Data *data;
891
892    if (!task) return;
893
894    if (task->type == E_FM_OP_COPY)
895      {
896         data = task->data;
897         if (data)
898           {
899              if (data->from)
900                {
901                   fclose(data->from);
902                   data->from = NULL;
903                }
904              if (data->to)
905                {
906                   fclose(data->to);
907                   data->to = NULL;
908                }
909           }
910         FREE(task->data);
911      }
912
913    if (task->type == E_FM_OP_COPY)
914      _e_fm_op_update_progress(task, -task->dst.done, 
915                               -task->src.st.st_size - (task->link ? REMOVECHUNKSIZE : 0));
916    else
917      _e_fm_op_update_progress(task, -REMOVECHUNKSIZE, -REMOVECHUNKSIZE);
918 }
919
920 static void
921 _e_fm_op_update_progress_report(int percent, int eta, double elapsed, off_t done, off_t total, const char *src, const char *dst)
922 {
923    const int magic = E_FM_OP_MAGIC;
924    const int id = E_FM_OP_PROGRESS;
925    char *p, *data;
926    int size, src_len, dst_len;
927    int ret = 0;
928
929    src_len = strlen(src);
930    dst_len = strlen(dst);
931
932    size = (2 * sizeof(int)) + (2 * sizeof(off_t)) + src_len + 1 + dst_len + 1;
933    data = alloca(3 * sizeof(int) + size);
934    if (!data) return;
935    p = data;
936
937 #define P(value) memcpy(p, &(value), sizeof(int)); p += sizeof(int)
938    P(magic);
939    P(id);
940    P(size);
941    P(percent);
942    P(eta);
943 #undef P
944
945 #define P(value) memcpy(p, &(value), sizeof(off_t)); p += sizeof(off_t)
946    P(done);
947    P(total);
948 #undef P
949
950 #define P(value) memcpy(p, value, value ## _len + 1); p += value ## _len + 1
951    P(src);
952    P(dst);
953 #undef P
954
955    ret = write(STDOUT_FILENO, data, (3 * sizeof(int)) + size);
956
957    E_FM_OP_DEBUG("Time left: %d at %e\n", eta, elapsed);
958    E_FM_OP_DEBUG("Progress %d. \n", percent);
959 }
960
961 static void
962 _e_fm_op_update_progress_report_simple(int percent, const char *src, const char *dst)
963 {
964    size_t done = (percent * REMOVECHUNKSIZE) / 100;
965    _e_fm_op_update_progress_report
966      (percent, 0, 0, done, REMOVECHUNKSIZE, src, dst);
967 }
968
969 /* Updates progress.
970  * _plus_data is how much more works is done and _plus_e_fm_op_total 
971  * is how much more work we found out needs to be done 
972  * (it is not zero primarily in _e_fm_op_scan_idler())
973  *
974  * It calculates progress in percent. And once a second calculates eta.
975  * If either of them changes from their previuos values, then the are 
976  * packed and written to STDOUT. 
977  */
978 static void
979 _e_fm_op_update_progress(E_Fm_Op_Task *task, off_t _plus_e_fm_op_done, off_t _plus_e_fm_op_total)
980 {
981    static int ppercent = -1;
982    int percent;
983    static double ctime = 0;
984    static double stime = 0;
985    double eta = 0;
986    static int peta = -1;
987    static E_Fm_Op_Task *ptask = NULL;
988
989    _e_fm_op_done += _plus_e_fm_op_done;
990    _e_fm_op_total += _plus_e_fm_op_total;
991
992    /* Do not send progress until scan is done.*/
993    if (_e_fm_op_scan_idler_p) return;
994
995    if (_e_fm_op_total != 0)
996      {
997         /* % 101 is for the case when somehow work queue works faster 
998          than scan queue. _e_fm_op_done * 100 should not cause arithmetic 
999          overflow, since long long can hold really big values. */
1000         percent = _e_fm_op_done * 100 / _e_fm_op_total % 101;   
1001
1002         eta = peta;
1003
1004         if (!stime) stime = ecore_time_get();
1005
1006         /* Update ETA once a second */
1007         if ((_e_fm_op_done) && (ecore_time_get() - ctime > 1.0 )) 
1008           {
1009              ctime = ecore_time_get();
1010              eta = (ctime - stime) * (_e_fm_op_total - _e_fm_op_done) / _e_fm_op_done;
1011              eta = (int) (eta + 0.5);
1012           }
1013
1014         if ((percent != ppercent) || (eta != peta) || ((task) && (task != ptask)))
1015           {
1016              ppercent = percent;
1017              peta = eta;
1018              ptask = task;
1019              _e_fm_op_update_progress_report(percent, eta, ctime - stime,
1020                                              _e_fm_op_done, _e_fm_op_total,
1021                                              task->src.name, task->dst.name);
1022           }
1023      }
1024 }
1025
1026 /* We just use this code in several places. */
1027 static void 
1028 _e_fm_op_copy_stat_info(E_Fm_Op_Task *task)
1029 {
1030    struct utimbuf ut;
1031    int ret = 0;
1032
1033    if (!task->dst.name) return;
1034
1035    chmod(task->dst.name, task->src.st.st_mode);
1036    ret = chown(task->dst.name, task->src.st.st_uid, task->src.st.st_gid);
1037    ut.actime = task->src.st.st_atime;
1038    ut.modtime = task->src.st.st_mtime;
1039    utime(task->dst.name, &ut);
1040 }
1041
1042 static int
1043 _e_fm_op_handle_overwrite(E_Fm_Op_Task *task)
1044 {
1045    struct stat st;
1046
1047    if ((task->overwrite == E_FM_OP_OVERWRITE_RESPONSE_YES_ALL) 
1048        || (_e_fm_op_overwrite_response == E_FM_OP_OVERWRITE_RESPONSE_YES_ALL))
1049      {
1050         _e_fm_op_overwrite = 0;
1051         return 0;
1052      }
1053    else if ((task->overwrite == E_FM_OP_OVERWRITE_RESPONSE_YES) 
1054             || (_e_fm_op_overwrite_response == E_FM_OP_OVERWRITE_RESPONSE_YES))
1055      {
1056         _e_fm_op_overwrite_response = E_FM_OP_NONE;
1057         _e_fm_op_overwrite = 0;
1058         return 0;
1059      }
1060    else if ((task->overwrite == E_FM_OP_OVERWRITE_RESPONSE_NO) 
1061             || (_e_fm_op_overwrite_response == E_FM_OP_OVERWRITE_RESPONSE_NO))
1062      {
1063         task->finished = 1;
1064         _e_fm_op_rollback(task);
1065         _e_fm_op_remove_link_task(task);
1066         _e_fm_op_overwrite_response = E_FM_OP_NONE;
1067         _e_fm_op_overwrite = 0;
1068         return 1;
1069      }
1070    else if ((task->overwrite == E_FM_OP_OVERWRITE_RESPONSE_NO_ALL) 
1071             || (_e_fm_op_overwrite_response == E_FM_OP_OVERWRITE_RESPONSE_NO_ALL))
1072      {
1073         task->finished = 1;
1074         _e_fm_op_rollback(task);
1075         _e_fm_op_remove_link_task(task);
1076         _e_fm_op_overwrite = 0;
1077         return 1;
1078      }
1079
1080    if ( stat(task->dst.name, &st) == 0)
1081      {
1082         /* File exists. */
1083         if ( _e_fm_op_overwrite_response == E_FM_OP_OVERWRITE_RESPONSE_NO_ALL)
1084           {
1085              task->finished = 1;
1086              _e_fm_op_rollback(task);
1087              return 1;
1088           }
1089         else
1090           {
1091              _e_fm_op_overwrite = 1;
1092              _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_OVERWRITE, "%s", task->dst.name);
1093           }
1094      }
1095
1096    return 0;
1097 }
1098
1099 static int
1100 _e_fm_op_copy_dir(E_Fm_Op_Task * task)
1101 {
1102    struct stat st;
1103
1104    /* Directory. Just create one in destatation. */
1105    if (mkdir(task->dst.name,
1106              S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH |
1107              S_IXOTH) == -1)
1108      {
1109         if (errno == EEXIST)
1110           {
1111              if (lstat(task->dst.name, &st) < 0)
1112                _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, 
1113                                         "Cannot lstat '%s': %s.", 
1114                                         task->dst.name);
1115              if (!S_ISDIR(st.st_mode))
1116                {
1117                   /* Let's try to delete the file and create a dir */
1118                   if (unlink(task->dst.name) == -1)
1119                     _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, 
1120                                              "Cannot unlink '%s': %s.", 
1121                                              task->dst.name);
1122                   if (mkdir(task->dst.name, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1)
1123                     _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, 
1124                                              "Cannot make directory '%s': %s.", 
1125                                              task->dst.name);
1126                }
1127           }
1128         else
1129           _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, 
1130                                    "Cannot make directory '%s': %s.", 
1131                                    task->dst.name);
1132      }
1133
1134    task->dst.done += task->src.st.st_size;
1135    _e_fm_op_update_progress(task, task->src.st.st_size, 0);
1136
1137    /* Finish with this task. */
1138    task->finished = 1;
1139
1140    return 0;
1141 }
1142
1143 static int
1144 _e_fm_op_copy_link(E_Fm_Op_Task *task)
1145 {
1146    size_t len;
1147    char path[PATH_MAX];
1148
1149    len = readlink(task->src.name, &path[0], PATH_MAX);
1150    path[len] = 0;
1151
1152    if (symlink(path, task->dst.name) != 0)
1153      {
1154         if (errno == EEXIST)
1155           {
1156              if (unlink(task->dst.name) == -1)
1157                _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot unlink '%s': %s.", task->dst.name);
1158              if (symlink(path, task->dst.name) == -1)
1159                _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot create link from '%s' to '%s': %s.", path, task->dst.name);
1160           }
1161         else
1162           _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot create link from '%s' to '%s': %s.", path, task->dst.name);
1163      }
1164
1165    task->dst.done += task->src.st.st_size;
1166
1167    _e_fm_op_update_progress(task, task->src.st.st_size, 0);
1168    _e_fm_op_copy_stat_info(task);
1169
1170    task->finished = 1;
1171
1172    return 0;
1173 }
1174
1175 static int
1176 _e_fm_op_copy_fifo(E_Fm_Op_Task *task)
1177 {
1178    if (mkfifo(task->dst.name, task->src.st.st_mode) == -1)
1179      {
1180         if (errno == EEXIST)
1181           {
1182              if (unlink(task->dst.name) == -1)
1183                _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot unlink '%s': %s.", task->dst.name);
1184              if (mkfifo(task->dst.name, task->src.st.st_mode) == -1)
1185                _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot make FIFO at '%s': %s.", task->dst.name);
1186           }
1187         else
1188           _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot make FIFO at '%s': %s.", task->dst.name);
1189      }
1190
1191    _e_fm_op_copy_stat_info(task);
1192
1193    task->dst.done += task->src.st.st_size;
1194    _e_fm_op_update_progress(task, task->src.st.st_size, 0);
1195
1196    task->finished = 1;
1197
1198    return 0;
1199 }
1200
1201 static int
1202 _e_fm_op_open_files(E_Fm_Op_Task *task)
1203 {
1204    E_Fm_Op_Copy_Data *data = task->data;
1205
1206    /* Ordinary file. */
1207    if (!data)
1208      {
1209         data = malloc(sizeof(E_Fm_Op_Copy_Data));
1210         task->data = data;
1211         data->to = NULL;
1212         data->from = NULL;
1213      }
1214
1215    if (!data->from) 
1216      {
1217         data->from = fopen(task->src.name, "rb");
1218         if (!data->from)
1219           _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot open file '%s' for reading: %s.", task->src.name);
1220      }
1221
1222    if (!data->to)
1223      {
1224         data->to = fopen(task->dst.name, "wb");
1225         if (!data->to)
1226           _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot open file '%s' for writing: %s.", task->dst.name);
1227      }
1228
1229    return 0;
1230 }
1231
1232 static int
1233 _e_fm_op_copy_chunk(E_Fm_Op_Task *task)
1234 {
1235    E_Fm_Op_Copy_Data *data;
1236    size_t dread, dwrite;
1237    char buf[COPYBUFSIZE];
1238
1239    data = task->data;
1240
1241    if (_e_fm_op_abort)
1242      {
1243         _e_fm_op_rollback(task);
1244         
1245         task->finished = 1;
1246         return 1;
1247      }
1248
1249    dread = fread(buf, 1, sizeof(buf), data->from);
1250    if (dread <= 0)
1251      {
1252         if (!feof(data->from))
1253           _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot read data from '%s': %s.", task->dst.name);
1254
1255         fclose(data->from);
1256         fclose(data->to);
1257         data->from = NULL;
1258         data->from = NULL;
1259
1260         _e_fm_op_copy_stat_info(task);
1261
1262         FREE(task->data);
1263
1264         task->finished = 1;
1265
1266         _e_fm_op_update_progress(task, 0, 0);
1267
1268         return 1;
1269      }
1270
1271    dwrite = fwrite(buf, 1, dread, data->to);
1272
1273    if (dwrite < dread)
1274      _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot write data to '%s': %s.", task->dst.name);
1275
1276    task->dst.done += dread;
1277    _e_fm_op_update_progress(task, dwrite, 0);
1278
1279    return 0;
1280 }
1281
1282 /*
1283  * _e_fm_op_copy_atom(), _e_fm_op_remove_atom() and _e_fm_op_scan_atom() are functions that 
1284  * perform very small operations.
1285  *
1286  * _e_fm_op_copy_atom(), for example, makes one of three things:
1287  * 1) opens files for writing and reading. This may take several calls -- until overwrite issues are not resolved.
1288  * 2) reads some bytes from one file and writes them to the other.
1289  * 3) closes both files if there is nothing more to read.
1290  *
1291  * _e_fm_op_remove_atom() removes smth.
1292  *
1293  * _e_fm_op_scan_atom() pushes new tasks for the _e_fm_op_work_idler(). One task for cp&rm and two tasks for mv.
1294  *
1295  * _e_fm_op_copy_atom() and _e_fm_op_remove_atom() are called from _e_fm_op_work_idler().
1296  * _e_fm_op_scan_atom() is called from _e_fm_op_scan_idler().
1297  *
1298  * These functions are called repeatedly until they put task->finished = 1. After that the task is removed from queue.
1299  *
1300  * Return value does not matter. It's there only to _E_FM_OP_ERROR_SEND macro to work correctly. (Well, it works fine, just don't want GCC to produce a warning.)
1301  */
1302 static int
1303 _e_fm_op_copy_atom(E_Fm_Op_Task * task)
1304 {
1305    E_Fm_Op_Copy_Data *data;
1306
1307    if (!task) return 1;
1308
1309    data = task->data;
1310
1311    if ((!data) || (!data->to) || (!data->from))         /* Did not touch the files yet. */
1312      {
1313         E_FM_OP_DEBUG("Copy: %s --> %s\n", task->src.name, task->dst.name);
1314
1315         if (_e_fm_op_abort)
1316           {
1317              /* We're marked for abortion. Don't do anything. 
1318               * Just return -- abort gets handled in _idler. 
1319               */
1320              task->finished = 1;
1321              return 1;
1322           }
1323
1324         if (_e_fm_op_handle_overwrite(task)) return 1;
1325
1326         if (S_ISDIR(task->src.st.st_mode))
1327           {
1328              if (_e_fm_op_copy_dir(task)) return 1;
1329           }
1330         else if (S_ISLNK(task->src.st.st_mode))
1331           {
1332              if (_e_fm_op_copy_link(task)) return 1;
1333           }
1334         else if (S_ISFIFO(task->src.st.st_mode))
1335           {
1336              if (_e_fm_op_copy_fifo(task)) return 1;
1337           }
1338         else if (S_ISREG(task->src.st.st_mode))
1339           {
1340              if (_e_fm_op_open_files(task)) return 1;
1341           }
1342      }
1343    else
1344      {
1345         if (_e_fm_op_copy_chunk(task)) return 1;
1346      }
1347
1348    return 1;
1349 }
1350
1351 static int
1352 _e_fm_op_scan_atom(E_Fm_Op_Task * task)
1353 {
1354    E_Fm_Op_Task *ctask, *rtask;
1355
1356    if (!task) return 1;
1357
1358    task->finished = 1;
1359
1360    /* Now push a new task to the work idler. */
1361
1362    if (task->type == E_FM_OP_COPY)
1363      {
1364         _e_fm_op_update_progress(NULL, 0, task->src.st.st_size);
1365
1366         ctask = _e_fm_op_task_new();
1367         ctask->src.name = eina_stringshare_add(task->src.name);
1368         memcpy(&(ctask->src.st), &(task->src.st), sizeof(struct stat));
1369         if (task->dst.name)
1370           ctask->dst.name = eina_stringshare_add(task->dst.name);
1371         ctask->type = E_FM_OP_COPY;
1372
1373         _e_fm_op_work_queue = eina_list_append(_e_fm_op_work_queue, ctask);
1374      }
1375    else if (task->type == E_FM_OP_COPY_STAT_INFO)
1376      {
1377         _e_fm_op_update_progress(NULL, 0, REMOVECHUNKSIZE);
1378
1379         ctask = _e_fm_op_task_new();
1380         ctask->src.name = eina_stringshare_add(task->src.name);
1381         memcpy(&(ctask->src.st), &(task->src.st), sizeof(struct stat));
1382         if (task->dst.name)
1383           ctask->dst.name = eina_stringshare_add(task->dst.name);
1384         ctask->type = E_FM_OP_COPY_STAT_INFO;
1385
1386         _e_fm_op_work_queue = eina_list_append(_e_fm_op_work_queue, ctask);
1387      }
1388    else if (task->type == E_FM_OP_REMOVE)
1389      {
1390         _e_fm_op_update_progress(NULL, 0, REMOVECHUNKSIZE);
1391
1392         rtask = _e_fm_op_task_new();
1393         rtask->src.name = eina_stringshare_add(task->src.name);
1394         memcpy(&(rtask->src.st), &(task->src.st), sizeof(struct stat));
1395         if (task->dst.name)
1396           rtask->dst.name = eina_stringshare_add(task->dst.name);
1397         rtask->type = E_FM_OP_REMOVE;
1398
1399         _e_fm_op_work_queue = eina_list_prepend(_e_fm_op_work_queue, rtask);
1400      }
1401    else if (task->type == E_FM_OP_MOVE)
1402      {
1403         /* Copy task. */
1404         _e_fm_op_update_progress(NULL, 0, task->src.st.st_size);
1405         ctask = _e_fm_op_task_new();
1406
1407         ctask->src.name = eina_stringshare_add(task->src.name);
1408         memcpy(&(ctask->src.st), &(task->src.st), sizeof(struct stat));
1409         if (task->dst.name)
1410           ctask->dst.name = eina_stringshare_add(task->dst.name);
1411         ctask->type = E_FM_OP_COPY;
1412
1413         _e_fm_op_work_queue = eina_list_prepend(_e_fm_op_work_queue, ctask);
1414
1415         /* Remove task. */
1416         _e_fm_op_update_progress(NULL, 0, REMOVECHUNKSIZE);
1417         rtask = _e_fm_op_task_new();
1418
1419         rtask->src.name = eina_stringshare_add(task->src.name);
1420         memcpy(&(rtask->src.st), &(task->src.st), sizeof(struct stat));
1421         if (task->dst.name)
1422           rtask->dst.name = eina_stringshare_add(task->dst.name);
1423         rtask->type = E_FM_OP_REMOVE;
1424
1425         /* We put remove task after the separator. Work idler won't go 
1426          * there unless scan is done and this means that all tasks for 
1427          * copy are already in queue. And they will be performed before 
1428          * the delete tasks.
1429          *
1430          * If we don't use this separator trick, then there easily can be 
1431          * a situation when remove task is performed before all files are 
1432          * copied.
1433          */
1434
1435         _e_fm_op_work_queue = eina_list_append_relative_list(_e_fm_op_work_queue, rtask, _e_fm_op_separator);
1436
1437         ctask->link = eina_list_next(_e_fm_op_separator);
1438      }
1439    else if (task->type == E_FM_OP_SYMLINK)
1440      {
1441         _e_fm_op_update_progress(NULL, 0, REMOVECHUNKSIZE);
1442
1443         rtask = _e_fm_op_task_new();
1444         rtask->src.name = eina_stringshare_add(task->src.name);
1445         memcpy(&(rtask->src.st), &(task->src.st), sizeof(struct stat));
1446         if (task->dst.name)
1447           rtask->dst.name = eina_stringshare_add(task->dst.name);
1448         rtask->type = E_FM_OP_SYMLINK;
1449
1450         _e_fm_op_work_queue = eina_list_prepend(_e_fm_op_work_queue, rtask);
1451      }
1452
1453    return 1;
1454 }
1455
1456 static int 
1457 _e_fm_op_copy_stat_info_atom(E_Fm_Op_Task * task)
1458 {
1459    E_FM_OP_DEBUG("Stat: %s --> %s\n", task->src.name, task->dst.name);
1460
1461    _e_fm_op_copy_stat_info(task);
1462    task->finished = 1;
1463    task->dst.done += REMOVECHUNKSIZE;
1464
1465    _e_fm_op_update_progress(task, REMOVECHUNKSIZE, 0);
1466
1467    return 0;
1468 }
1469
1470 static int
1471 _e_fm_op_symlink_atom(E_Fm_Op_Task *task)
1472 {
1473    if (_e_fm_op_abort) return 1;
1474
1475    E_FM_OP_DEBUG("Symlink: %s -> %s\n", task->src.name, task->dst.name);
1476
1477    if (symlink(task->src.name, task->dst.name) != 0)
1478      _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot create link from '%s' to '%s': %s.", task->src.name, task->dst.name);
1479
1480    task->dst.done += REMOVECHUNKSIZE;
1481    _e_fm_op_update_progress(task, REMOVECHUNKSIZE, 0);
1482    task->finished = 1;
1483
1484    return 0;
1485 }
1486
1487 static int
1488 _e_fm_op_remove_atom(E_Fm_Op_Task * task)
1489 {
1490    if (_e_fm_op_abort) return 1;
1491
1492    E_FM_OP_DEBUG("Remove: %s\n", task->src.name);
1493
1494    if (S_ISDIR(task->src.st.st_mode))
1495      {
1496         if (rmdir(task->src.name) == -1)
1497           {
1498              if (errno == ENOTEMPTY)
1499                {
1500                   E_FM_OP_DEBUG("Attempt to remove non-empty directory.\n");
1501                   /* This should never happen due to way tasks are added to the work queue. If this happens (for example new files were created after the scan was complete), implicitly delete everything. */
1502                   ecore_file_recursive_rm(task->src.name);
1503                   task->finished = 1; /* Make sure that task is removed. */
1504                   return 1;
1505                }
1506              else
1507                _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot remove directory '%s': %s.", task->src.name);
1508           }
1509      }
1510    else if (unlink(task->src.name) == -1)
1511      _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot remove file '%s': %s.", task->src.name);
1512
1513    task->dst.done += REMOVECHUNKSIZE;
1514    _e_fm_op_update_progress(task, REMOVECHUNKSIZE, 0);
1515
1516    task->finished = 1;
1517
1518    return 1;
1519 }