3 #ifndef _FILE_OFFSET_BITS
4 # define _FILE_OFFSET_BITS 64
10 # define alloca __builtin_alloca
12 # define alloca __alloca
13 #elif defined _MSC_VER
15 # define alloca _alloca
21 void *alloca (size_t);
29 #include <sys/types.h>
37 #include <Ecore_File.h>
39 #include <eina_stringshare.h>
46 #define READBUFSIZE 65536
47 #define COPYBUFSIZE 16384
48 #define REMOVECHUNKSIZE 4096
50 #define FREE(p) do { if (p) {free((void *)p); p = NULL;} } while (0)
52 typedef struct _E_Fm_Op_Task E_Fm_Op_Task;
53 typedef struct _E_Fm_Op_Copy_Data E_Fm_Op_Copy_Data;
55 static E_Fm_Op_Task *_e_fm_op_task_new();
56 static void _e_fm_op_task_free(void *t);
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);
64 static Eina_Bool _e_fm_op_work_idler(void *data);
65 static Eina_Bool _e_fm_op_scan_idler(void *data);
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);
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);
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);
86 Ecore_Fd_Handler *_e_fm_op_stdin_handler = NULL;
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;
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. */
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;
102 int _e_fm_op_error_response = E_FM_OP_NONE;
103 int _e_fm_op_overwrite_response = E_FM_OP_NONE;
105 Eina_List *_e_fm_op_separator = NULL;
107 char *_e_fm_op_stdin_buffer = NULL;
123 int started, finished;
128 E_Fm_Op_Type overwrite;
133 struct _E_Fm_Op_Copy_Data
140 main(int argc, char **argv)
148 _e_fm_op_stdin_buffer = malloc(READBUFSIZE);
150 _e_fm_op_stdin_handler =
151 ecore_main_fd_handler_add(STDIN_FILENO, ECORE_FD_READ, _e_fm_op_stdin_data, NULL,
154 if (argc <= 2) return 0;
159 if (strcmp(argv[1], "cp") == 0)
161 else if (strcmp(argv[1], "mv") == 0)
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;
169 if ((type == E_FM_OP_COPY) || (type == E_FM_OP_MOVE) || (type == E_FM_OP_SYMLINK))
171 if (argc < 4) goto quit;
173 if (type == E_FM_OP_MOVE)
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;
179 if ((argc >= 4) && (ecore_file_is_dir(argv[last])))
183 int p2_len, last_len, done, total;
185 p2 = ecore_file_realpath(argv[last]);
189 last_len = strlen(argv[last]);
190 if ((last_len < 1) || (last_len + 2 >= PATH_MAX))
195 memcpy(buf, argv[last], last_len);
196 if (buf[last_len - 1] != '/')
207 for (; i < last; i++)
209 char *p = ecore_file_realpath(argv[i]);
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')))
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);
227 if ((type == E_FM_OP_MOVE) &&
228 (rename(argv[i], buf) == 0))
231 _e_fm_op_update_progress_report_simple
232 (done * 100 / total, argv[i], buf);
234 else if ((type == E_FM_OP_SYMLINK) &&
235 (symlink(argv[i], buf) == 0))
238 _e_fm_op_update_progress_report_simple
239 (done * 100 / total, argv[i], buf);
245 task = _e_fm_op_task_new();
247 task->src.name = eina_stringshare_add(argv[i]);
248 task->dst.name = eina_stringshare_add(buf);
250 _e_fm_op_scan_queue =
251 eina_list_append(_e_fm_op_scan_queue, task);
264 p = ecore_file_realpath(argv[2]);
265 p2 = ecore_file_realpath(argv[3]);
267 /* Don't move a file on top of itself. */
268 i = (strcmp(p, p2) == 0);
274 if ((type == E_FM_OP_MOVE) && (rename(argv[2], argv[3]) == 0))
276 _e_fm_op_update_progress_report_simple(100, argv[2], argv[3]);
279 else if ((type == E_FM_OP_SYMLINK) &&
280 (symlink(argv[2], argv[3]) == 0))
282 _e_fm_op_update_progress_report_simple(100, argv[2], argv[3]);
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();
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);
301 else if (type == E_FM_OP_REMOVE)
303 if (argc < 3) return 0;
309 task = _e_fm_op_task_new();
311 task->src.name = eina_stringshare_add(argv[i]);
312 _e_fm_op_scan_queue = eina_list_append(_e_fm_op_scan_queue, task);
317 _e_fm_op_set_up_idlers();
319 ecore_main_loop_begin();
325 free(_e_fm_op_stdin_buffer);
327 E_FM_OP_DEBUG("Slave quit.\n");
332 /* Create new task. */
333 static E_Fm_Op_Task *
338 t = malloc(sizeof(E_Fm_Op_Task));
340 memset(&(t->src.st), 0, sizeof(struct stat));
347 t->type = E_FM_OP_NONE;
348 t->overwrite = E_FM_OP_NONE;
356 _e_fm_op_task_free(void *t)
358 E_Fm_Op_Task *task = t;
359 E_Fm_Op_Copy_Data *data;
363 if (task->src.name) eina_stringshare_del(task->src.name);
364 if (task->dst.name) eina_stringshare_del(task->dst.name);
369 if (task->type == E_FM_OP_COPY)
371 if (data->from) fclose(data->from);
372 if (data->to) fclose(data->to);
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.
386 _e_fm_op_remove_link_task(E_Fm_Op_Task *task)
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);
401 * Handles data from STDIN.
402 * Received data must be in this format:
403 * 1) (int) magic number,
405 * 3) (int) message length.
406 * Right now message length is always 0. Id is what matters.
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.
413 _e_fm_op_stdin_data(void *data __UNUSED__, Ecore_Fd_Handler * fd_handler)
416 static char *buf = NULL;
417 static int length = 0;
422 fd = ecore_main_fd_handler_fd_get(fd_handler);
425 buf = _e_fm_op_stdin_buffer;
429 num = read(fd, buf, READBUFSIZE - length);
433 E_FM_OP_DEBUG("STDIN was closed. Abort. \n");
438 E_FM_OP_DEBUG("Error while reading from STDIN: read returned -1. (%s) Abort. \n", strerror(errno));
445 buf = _e_fm_op_stdin_buffer;
446 begin = _e_fm_op_stdin_buffer;
448 while (length >= ((int)(3 * sizeof(int))))
453 if (*((int *)buf) != E_FM_OP_MAGIC)
455 E_FM_OP_DEBUG("Error while reading from STDIN: magic is not correct!\n");
460 /* Read indentifying data. */
461 memcpy(&identity, buf, sizeof(int));
464 /* Read message length. */
465 memcpy(&msize, buf, sizeof(int));
468 if ((length - 3 * (int)sizeof(int)) < msize)
470 /* There is not enough data to read the whole message. */
474 length -= (3 * sizeof(int));
476 /* You may want to read msize bytes of data too,
477 * but currently commands here do not have any data.
484 E_FM_OP_DEBUG("Aborted.\n");
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();
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");
503 if (length > 0) memmove(_e_fm_op_stdin_buffer, begin, length);
504 buf = _e_fm_op_stdin_buffer + length;
507 return ECORE_CALLBACK_RENEW;
511 _e_fm_op_set_up_idlers()
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);
519 #define _E_FM_OP_ERROR_SEND_SCAN(_task, _e_fm_op_error_type, _fmt, ...)\
523 _e_fm_op_scan_error = 1;\
524 _e_fm_op_send_error(_task, _e_fm_op_error_type, _fmt, __VA_ARGS__, strerror(_errno));\
529 #define _E_FM_OP_ERROR_SEND_WORK(_task, _e_fm_op_error_type, _fmt, ...)\
533 _e_fm_op_work_error = 1;\
534 _e_fm_op_send_error(_task, _e_fm_op_error_type, _fmt, __VA_ARGS__, strerror(_errno));\
540 _e_fm_op_delete_idler(int *mark)
542 if (mark == &_e_fm_op_work_error)
544 ecore_idler_del(_e_fm_op_work_idler_p);
545 _e_fm_op_work_idler_p = NULL;
549 ecore_idler_del(_e_fm_op_scan_idler_p);
550 _e_fm_op_scan_idler_p = NULL;
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.
559 _e_fm_op_idler_handle_error(int *mark, Eina_List **queue, Eina_List **node, E_Fm_Op_Task *task)
561 if (_e_fm_op_overwrite)
563 if (_e_fm_op_overwrite_response != E_FM_OP_NONE)
565 task->overwrite = _e_fm_op_overwrite_response;
566 _e_fm_op_work_error = 0;
567 _e_fm_op_scan_error = 0;
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);
579 if (_e_fm_op_error_response == E_FM_OP_NONE)
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);
588 E_FM_OP_DEBUG("Got response.\n");
590 if (_e_fm_op_error_response == E_FM_OP_ERROR_RESPONSE_ABORT)
594 _e_fm_op_error_response = E_FM_OP_NONE;
595 _e_fm_op_rollback(task);
597 else if (_e_fm_op_error_response == E_FM_OP_ERROR_RESPONSE_RETRY)
600 _e_fm_op_error_response = E_FM_OP_NONE;
602 else if (_e_fm_op_error_response == E_FM_OP_ERROR_RESPONSE_IGNORE_THIS)
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;
612 else if (_e_fm_op_error_response == E_FM_OP_ERROR_RESPONSE_IGNORE_ALL)
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);
621 /* Do not clean out _e_fm_op_error_response. This way when another error occures, it would be handled automatically. */
626 else if (( _e_fm_op_work_error) || (_e_fm_op_scan_error))
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.
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.
639 _e_fm_op_work_idler(void *data __UNUSED__)
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.
649 * BTW, the same is propably right for the _e_fm_op_scan_idler().
651 static Eina_List *node = NULL;
652 E_Fm_Op_Task *task = NULL;
654 if (!node) node = _e_fm_op_work_queue;
655 task = eina_list_data_get(node);
658 node = _e_fm_op_work_queue;
659 task = eina_list_data_get(node);
664 if ((_e_fm_op_separator) &&
665 (_e_fm_op_work_queue == _e_fm_op_separator) &&
666 (!_e_fm_op_scan_idler_p))
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);
671 return ECORE_CALLBACK_RENEW;
674 if ((!_e_fm_op_scan_idler_p) && (!_e_fm_op_work_error) &&
675 (!_e_fm_op_scan_error))
676 ecore_main_loop_quit();
678 return ECORE_CALLBACK_RENEW;
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;
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);
697 _e_fm_op_work_queue = eina_list_remove_list(_e_fm_op_work_queue, node);
698 _e_fm_op_task_free(task);
704 /* So, _atom did what it whats in case of abort. Now to idler. */
705 ecore_main_loop_quit();
706 return ECORE_CALLBACK_CANCEL;
709 return ECORE_CALLBACK_RENEW;
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.
717 _e_fm_op_scan_idler(void *data __UNUSED__)
719 static Eina_List *node = NULL;
720 E_Fm_Op_Task *task = NULL;
722 static struct dirent *de = NULL;
723 static DIR *dir = NULL;
724 E_Fm_Op_Task *ntask = NULL;
726 if (!node) node = _e_fm_op_scan_queue;
727 task = eina_list_data_get(node);
730 node = _e_fm_op_scan_queue;
731 task = eina_list_data_get(node);
736 _e_fm_op_scan_idler_p = NULL;
737 return ECORE_CALLBACK_CANCEL;
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;
745 /* We're marked for abortion. */
746 ecore_main_loop_quit();
747 return ECORE_CALLBACK_CANCEL;
750 if (task->type == E_FM_OP_COPY_STAT_INFO)
752 _e_fm_op_scan_atom(task);
755 _e_fm_op_scan_queue =
756 eina_list_remove_list(_e_fm_op_scan_queue, node);
757 _e_fm_op_task_free(task);
761 else if (!dir && !task->started)
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);
767 if (S_ISDIR(task->src.st.st_mode))
769 /* If it's a dir, then look through it and add a task for each. */
771 dir = opendir(task->src.name);
773 _E_FM_OP_ERROR_SEND_SCAN(task, E_FM_OP_ERROR,
774 "Cannot open directory '%s': %s.",
780 else if (dir && !task->started)
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));
792 ntask->dst.name = eina_stringshare_add(task->dst.name);
794 ntask->dst.name = NULL;
796 if (task->type == E_FM_OP_REMOVE)
797 _e_fm_op_scan_queue =
798 eina_list_prepend(_e_fm_op_scan_queue, ntask);
800 _e_fm_op_scan_queue =
801 eina_list_append(_e_fm_op_scan_queue, ntask);
807 return ECORE_CALLBACK_RENEW;
810 if ((!strcmp(de->d_name, ".") || (!strcmp(de->d_name, ".."))))
811 return ECORE_CALLBACK_RENEW;
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);
820 snprintf(buf, sizeof(buf), "%s/%s", task->dst.name, de->d_name);
821 ntask->dst.name = eina_stringshare_add(buf);
824 ntask->dst.name = NULL;
826 if (task->type == E_FM_OP_REMOVE)
827 _e_fm_op_scan_queue = eina_list_prepend(_e_fm_op_scan_queue, ntask);
829 _e_fm_op_scan_queue = eina_list_append(_e_fm_op_scan_queue, ntask);
833 _e_fm_op_scan_atom(task);
836 _e_fm_op_scan_queue =
837 eina_list_remove_list(_e_fm_op_scan_queue, node);
838 _e_fm_op_task_free(task);
843 return ECORE_CALLBACK_RENEW;
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,
852 _e_fm_op_send_error(E_Fm_Op_Task *task __UNUSED__, E_Fm_Op_Type type, const char *fmt, ...)
855 char buffer[READBUFSIZE];
856 char *buf = &buffer[0];
857 char *str = buf + (3 * sizeof(int));
862 if (_e_fm_op_error_response == E_FM_OP_ERROR_RESPONSE_IGNORE_ALL)
870 vsnprintf(str, READBUFSIZE - 3 * sizeof(int), fmt, ap);
873 *((int *)buf) = E_FM_OP_MAGIC;
874 *((int *)(buf + sizeof(int))) = type;
875 *((int *)(buf + (2 * sizeof(int)))) = len + 1;
877 ret = write(STDOUT_FILENO, buf, (3 * sizeof(int)) + len + 1);
879 E_FM_OP_DEBUG("%s", str);
880 E_FM_OP_DEBUG(" Error sent.\n");
886 /* Unrolls task: makes a clean up and updates progress info. */
888 _e_fm_op_rollback(E_Fm_Op_Task *task)
890 E_Fm_Op_Copy_Data *data;
894 if (task->type == E_FM_OP_COPY)
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));
917 _e_fm_op_update_progress(task, -REMOVECHUNKSIZE, -REMOVECHUNKSIZE);
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)
923 const int magic = E_FM_OP_MAGIC;
924 const int id = E_FM_OP_PROGRESS;
926 int size, src_len, dst_len;
929 src_len = strlen(src);
930 dst_len = strlen(dst);
932 size = (2 * sizeof(int)) + (2 * sizeof(off_t)) + src_len + 1 + dst_len + 1;
933 data = alloca(3 * sizeof(int) + size);
937 #define P(value) memcpy(p, &(value), sizeof(int)); p += sizeof(int)
945 #define P(value) memcpy(p, &(value), sizeof(off_t)); p += sizeof(off_t)
950 #define P(value) memcpy(p, value, value ## _len + 1); p += value ## _len + 1
955 ret = write(STDOUT_FILENO, data, (3 * sizeof(int)) + size);
957 E_FM_OP_DEBUG("Time left: %d at %e\n", eta, elapsed);
958 E_FM_OP_DEBUG("Progress %d. \n", percent);
962 _e_fm_op_update_progress_report_simple(int percent, const char *src, const char *dst)
964 size_t done = (percent * REMOVECHUNKSIZE) / 100;
965 _e_fm_op_update_progress_report
966 (percent, 0, 0, done, REMOVECHUNKSIZE, src, dst);
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())
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.
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)
981 static int ppercent = -1;
983 static double ctime = 0;
984 static double stime = 0;
986 static int peta = -1;
987 static E_Fm_Op_Task *ptask = NULL;
989 _e_fm_op_done += _plus_e_fm_op_done;
990 _e_fm_op_total += _plus_e_fm_op_total;
992 /* Do not send progress until scan is done.*/
993 if (_e_fm_op_scan_idler_p) return;
995 if (_e_fm_op_total != 0)
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;
1004 if (!stime) stime = ecore_time_get();
1006 /* Update ETA once a second */
1007 if ((_e_fm_op_done) && (ecore_time_get() - ctime > 1.0 ))
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);
1014 if ((percent != ppercent) || (eta != peta) || ((task) && (task != ptask)))
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);
1026 /* We just use this code in several places. */
1028 _e_fm_op_copy_stat_info(E_Fm_Op_Task *task)
1033 if (!task->dst.name) return;
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);
1043 _e_fm_op_handle_overwrite(E_Fm_Op_Task *task)
1047 if ((task->overwrite == E_FM_OP_OVERWRITE_RESPONSE_YES_ALL)
1048 || (_e_fm_op_overwrite_response == E_FM_OP_OVERWRITE_RESPONSE_YES_ALL))
1050 _e_fm_op_overwrite = 0;
1053 else if ((task->overwrite == E_FM_OP_OVERWRITE_RESPONSE_YES)
1054 || (_e_fm_op_overwrite_response == E_FM_OP_OVERWRITE_RESPONSE_YES))
1056 _e_fm_op_overwrite_response = E_FM_OP_NONE;
1057 _e_fm_op_overwrite = 0;
1060 else if ((task->overwrite == E_FM_OP_OVERWRITE_RESPONSE_NO)
1061 || (_e_fm_op_overwrite_response == E_FM_OP_OVERWRITE_RESPONSE_NO))
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;
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))
1074 _e_fm_op_rollback(task);
1075 _e_fm_op_remove_link_task(task);
1076 _e_fm_op_overwrite = 0;
1080 if ( stat(task->dst.name, &st) == 0)
1083 if ( _e_fm_op_overwrite_response == E_FM_OP_OVERWRITE_RESPONSE_NO_ALL)
1086 _e_fm_op_rollback(task);
1091 _e_fm_op_overwrite = 1;
1092 _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_OVERWRITE, "%s", task->dst.name);
1100 _e_fm_op_copy_dir(E_Fm_Op_Task * task)
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 |
1109 if (errno == EEXIST)
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.",
1115 if (!S_ISDIR(st.st_mode))
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.",
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.",
1129 _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR,
1130 "Cannot make directory '%s': %s.",
1134 task->dst.done += task->src.st.st_size;
1135 _e_fm_op_update_progress(task, task->src.st.st_size, 0);
1137 /* Finish with this task. */
1144 _e_fm_op_copy_link(E_Fm_Op_Task *task)
1147 char path[PATH_MAX];
1149 len = readlink(task->src.name, &path[0], PATH_MAX);
1152 if (symlink(path, task->dst.name) != 0)
1154 if (errno == EEXIST)
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);
1162 _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot create link from '%s' to '%s': %s.", path, task->dst.name);
1165 task->dst.done += task->src.st.st_size;
1167 _e_fm_op_update_progress(task, task->src.st.st_size, 0);
1168 _e_fm_op_copy_stat_info(task);
1176 _e_fm_op_copy_fifo(E_Fm_Op_Task *task)
1178 if (mkfifo(task->dst.name, task->src.st.st_mode) == -1)
1180 if (errno == EEXIST)
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);
1188 _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot make FIFO at '%s': %s.", task->dst.name);
1191 _e_fm_op_copy_stat_info(task);
1193 task->dst.done += task->src.st.st_size;
1194 _e_fm_op_update_progress(task, task->src.st.st_size, 0);
1202 _e_fm_op_open_files(E_Fm_Op_Task *task)
1204 E_Fm_Op_Copy_Data *data = task->data;
1206 /* Ordinary file. */
1209 data = malloc(sizeof(E_Fm_Op_Copy_Data));
1217 data->from = fopen(task->src.name, "rb");
1219 _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot open file '%s' for reading: %s.", task->src.name);
1224 data->to = fopen(task->dst.name, "wb");
1226 _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot open file '%s' for writing: %s.", task->dst.name);
1233 _e_fm_op_copy_chunk(E_Fm_Op_Task *task)
1235 E_Fm_Op_Copy_Data *data;
1236 size_t dread, dwrite;
1237 char buf[COPYBUFSIZE];
1243 _e_fm_op_rollback(task);
1249 dread = fread(buf, 1, sizeof(buf), data->from);
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);
1260 _e_fm_op_copy_stat_info(task);
1266 _e_fm_op_update_progress(task, 0, 0);
1271 dwrite = fwrite(buf, 1, dread, data->to);
1274 _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot write data to '%s': %s.", task->dst.name);
1276 task->dst.done += dread;
1277 _e_fm_op_update_progress(task, dwrite, 0);
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.
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.
1291 * _e_fm_op_remove_atom() removes smth.
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.
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().
1298 * These functions are called repeatedly until they put task->finished = 1. After that the task is removed from queue.
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.)
1303 _e_fm_op_copy_atom(E_Fm_Op_Task * task)
1305 E_Fm_Op_Copy_Data *data;
1307 if (!task) return 1;
1311 if ((!data) || (!data->to) || (!data->from)) /* Did not touch the files yet. */
1313 E_FM_OP_DEBUG("Copy: %s --> %s\n", task->src.name, task->dst.name);
1317 /* We're marked for abortion. Don't do anything.
1318 * Just return -- abort gets handled in _idler.
1324 if (_e_fm_op_handle_overwrite(task)) return 1;
1326 if (S_ISDIR(task->src.st.st_mode))
1328 if (_e_fm_op_copy_dir(task)) return 1;
1330 else if (S_ISLNK(task->src.st.st_mode))
1332 if (_e_fm_op_copy_link(task)) return 1;
1334 else if (S_ISFIFO(task->src.st.st_mode))
1336 if (_e_fm_op_copy_fifo(task)) return 1;
1338 else if (S_ISREG(task->src.st.st_mode))
1340 if (_e_fm_op_open_files(task)) return 1;
1345 if (_e_fm_op_copy_chunk(task)) return 1;
1352 _e_fm_op_scan_atom(E_Fm_Op_Task * task)
1354 E_Fm_Op_Task *ctask, *rtask;
1356 if (!task) return 1;
1360 /* Now push a new task to the work idler. */
1362 if (task->type == E_FM_OP_COPY)
1364 _e_fm_op_update_progress(NULL, 0, task->src.st.st_size);
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));
1370 ctask->dst.name = eina_stringshare_add(task->dst.name);
1371 ctask->type = E_FM_OP_COPY;
1373 _e_fm_op_work_queue = eina_list_append(_e_fm_op_work_queue, ctask);
1375 else if (task->type == E_FM_OP_COPY_STAT_INFO)
1377 _e_fm_op_update_progress(NULL, 0, REMOVECHUNKSIZE);
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));
1383 ctask->dst.name = eina_stringshare_add(task->dst.name);
1384 ctask->type = E_FM_OP_COPY_STAT_INFO;
1386 _e_fm_op_work_queue = eina_list_append(_e_fm_op_work_queue, ctask);
1388 else if (task->type == E_FM_OP_REMOVE)
1390 _e_fm_op_update_progress(NULL, 0, REMOVECHUNKSIZE);
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));
1396 rtask->dst.name = eina_stringshare_add(task->dst.name);
1397 rtask->type = E_FM_OP_REMOVE;
1399 _e_fm_op_work_queue = eina_list_prepend(_e_fm_op_work_queue, rtask);
1401 else if (task->type == E_FM_OP_MOVE)
1404 _e_fm_op_update_progress(NULL, 0, task->src.st.st_size);
1405 ctask = _e_fm_op_task_new();
1407 ctask->src.name = eina_stringshare_add(task->src.name);
1408 memcpy(&(ctask->src.st), &(task->src.st), sizeof(struct stat));
1410 ctask->dst.name = eina_stringshare_add(task->dst.name);
1411 ctask->type = E_FM_OP_COPY;
1413 _e_fm_op_work_queue = eina_list_prepend(_e_fm_op_work_queue, ctask);
1416 _e_fm_op_update_progress(NULL, 0, REMOVECHUNKSIZE);
1417 rtask = _e_fm_op_task_new();
1419 rtask->src.name = eina_stringshare_add(task->src.name);
1420 memcpy(&(rtask->src.st), &(task->src.st), sizeof(struct stat));
1422 rtask->dst.name = eina_stringshare_add(task->dst.name);
1423 rtask->type = E_FM_OP_REMOVE;
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
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
1435 _e_fm_op_work_queue = eina_list_append_relative_list(_e_fm_op_work_queue, rtask, _e_fm_op_separator);
1437 ctask->link = eina_list_next(_e_fm_op_separator);
1439 else if (task->type == E_FM_OP_SYMLINK)
1441 _e_fm_op_update_progress(NULL, 0, REMOVECHUNKSIZE);
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));
1447 rtask->dst.name = eina_stringshare_add(task->dst.name);
1448 rtask->type = E_FM_OP_SYMLINK;
1450 _e_fm_op_work_queue = eina_list_prepend(_e_fm_op_work_queue, rtask);
1457 _e_fm_op_copy_stat_info_atom(E_Fm_Op_Task * task)
1459 E_FM_OP_DEBUG("Stat: %s --> %s\n", task->src.name, task->dst.name);
1461 _e_fm_op_copy_stat_info(task);
1463 task->dst.done += REMOVECHUNKSIZE;
1465 _e_fm_op_update_progress(task, REMOVECHUNKSIZE, 0);
1471 _e_fm_op_symlink_atom(E_Fm_Op_Task *task)
1473 if (_e_fm_op_abort) return 1;
1475 E_FM_OP_DEBUG("Symlink: %s -> %s\n", task->src.name, task->dst.name);
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);
1480 task->dst.done += REMOVECHUNKSIZE;
1481 _e_fm_op_update_progress(task, REMOVECHUNKSIZE, 0);
1488 _e_fm_op_remove_atom(E_Fm_Op_Task * task)
1490 if (_e_fm_op_abort) return 1;
1492 E_FM_OP_DEBUG("Remove: %s\n", task->src.name);
1494 if (S_ISDIR(task->src.st.st_mode))
1496 if (rmdir(task->src.name) == -1)
1498 if (errno == ENOTEMPTY)
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. */
1507 _E_FM_OP_ERROR_SEND_WORK(task, E_FM_OP_ERROR, "Cannot remove directory '%s': %s.", task->src.name);
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);
1513 task->dst.done += REMOVECHUNKSIZE;
1514 _e_fm_op_update_progress(task, REMOVECHUNKSIZE, 0);